scummvm/engines/chamber/cga.cpp
yigithanyigit 15602e77c7 CHAMBERS: Refactor code for HGA compatibility
Refactor the existing CGA rendering code to make it compatible with HGA rendering. The main changes include the introduction of engine variables and a move away from macros.
2024-04-25 00:42:36 +02:00

1595 lines
40 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "graphics/paletteman.h"
#include "chamber/chamber.h"
#include "chamber/common.h"
#include "chamber/input.h"
#include "chamber/resdata.h"
#include "chamber/cga.h"
#include "common/debug.h"
namespace Chamber {
/* Calc screen offset from packed pixel coordinates
Out:
screen offset
*/
uint16 HGA_CalcXY_p(uint16 x, uint16 y) {
uint16 ofs = HGA_CENTERED_BASE_OFS; /*initial offset for centering*/
#if 0
ofs += HGA_CALCXY_RAW(x * 4 * 2, y);
#else
/*optimized version of the above*/
if (y & 1)
ofs |= HGA_NEXT_LINES_OFS;
if (y & 2)
ofs |= HGA_NEXT_LINES_OFS * 2;
y /= 4;
ofs += y * HGA_BYTES_PER_LINE;
ofs += x; /*one unit of x represent 4 pixels, but one unit of ofs is 8 pixels, so x is implicitly multiplied by 2 here*/
#endif
return ofs;
}
/* Calc screen offset from normal pixel coordinates
Out:
screen offset
*/
uint16 HGA_CalcXY(uint16 x, uint16 y) {
return HGA_CalcXY_p(x / 4, y);
}
extern byte backbuffer[0xB800]; ///< CGA: 0x4000, HGS: 0xB800
byte CGA_SCREENBUFFER[0xB800];
byte scrbuffer[720*348]; ///< Screen buffer for blitting, it is intentionally left larger instead of dynamic memory allocation
extern byte *scratch_mem2;
byte char_draw_coords_x;
byte char_draw_coords_y;
byte *char_xlat_table;
byte string_ended;
byte char_draw_max_width;
byte char_draw_max_height;
/*pixels order reverse in a byte*/
byte cga_pixel_flip[256] = {
0, 64, 128, 192, 16, 80, 144, 208, 32, 96, 160, 224,
48, 112, 176, 240, 4, 68, 132, 196, 20, 84, 148, 212,
36, 100, 164, 228, 52, 116, 180, 244, 8, 72, 136, 200,
24, 88, 152, 216, 40, 104, 168, 232, 56, 120, 184, 248,
12, 76, 140, 204, 28, 92, 156, 220, 44, 108, 172, 236,
60, 124, 188, 252, 1, 65, 129, 193, 17, 81, 145, 209,
33, 97, 161, 225, 49, 113, 177, 241, 5, 69, 133, 197,
21, 85, 149, 213, 37, 101, 165, 229, 53, 117, 181, 245,
9, 73, 137, 201, 25, 89, 153, 217, 41, 105, 169, 233,
57, 121, 185, 249, 13, 77, 141, 205, 29, 93, 157, 221,
45, 109, 173, 237, 61, 125, 189, 253, 2, 66, 130, 194,
18, 82, 146, 210, 34, 98, 162, 226, 50, 114, 178, 242,
6, 70, 134, 198, 22, 86, 150, 214, 38, 102, 166, 230,
54, 118, 182, 246, 10, 74, 138, 202, 26, 90, 154, 218,
42, 106, 170, 234, 58, 122, 186, 250, 14, 78, 142, 206,
30, 94, 158, 222, 46, 110, 174, 238, 62, 126, 190, 254,
3, 67, 131, 195, 19, 83, 147, 211, 35, 99, 163, 227,
51, 115, 179, 243, 7, 71, 135, 199, 23, 87, 151, 215,
39, 103, 167, 231, 55, 119, 183, 247, 11, 75, 139, 203,
27, 91, 155, 219, 43, 107, 171, 235, 59, 123, 187, 251,
15, 79, 143, 207, 31, 95, 159, 223, 47, 111, 175, 239,
63, 127, 191, 255
};
static const uint8 PALETTE_CGA[4 * 3] = {
0x00, 0x00, 0x00, // black
0x55, 0xff, 0xff, // cyan
0xff, 0x55, 0xff, // magenta
0xff, 0xff, 0xff
};
static const uint8 PALETTE_CGA2[4 * 3] = {
0x00, 0x00, 0x00, // black
0x55, 0xff, 0x55, // green
0xff, 0x55, 0x55, // red
0xff, 0xff, 0x55 // yellow
};
/*
Switch to CGA 320x200x2bpp mode
*/
void switchToGraphicsMode(void) {
g_system->getPaletteManager()->setPalette(PALETTE_CGA, 0, 4);
}
/*
Switch to text mode
*/
void switchToTextMode(void) {
warning("STUB: switchToTextMode()");
}
void waitVBlank(void) {
pollInput();
g_system->delayMillis(10);
}
void cga_ColorSelect(byte csel) {
const byte *pal;
if (csel & 0x10)
pal = PALETTE_CGA;
else
pal = PALETTE_CGA2;
g_system->getPaletteManager()->setPalette(pal, 0, 4);
g_system->setCursorPalette(pal, 0, 4);
}
void cga_blitToScreen(int16 dx, int16 dy, int16 w, int16 h) {
dx = dy = 0;
w = g_vm->_screenW; h = g_vm->_screenH;
int16 align = dx & (0x8 / g_vm->_screenBits - 1);
dx -= align;
w += align;
if (dy + h >= g_vm->_screenH)
h = g_vm->_screenH - dy;
if (dx + w >= g_vm->_screenW)
w = g_vm->_screenW - dx;
w = (w + (0x8 / g_vm->_screenBits - 1)) / (0x8 / g_vm->_screenBits);
for (int16 y = 0; y < h; y++) {
byte *src = CGA_SCREENBUFFER + CalcXY(dx, dy + y);
byte *dst = scrbuffer + (y + dy) * g_vm->_screenW + dx;
for (int16 x = 0; x < w; x++) {
byte colors = *src++;
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA) {
for (int16 c = 0; c < 4; c++) {
byte color = (colors & 0xC0) >> 6;
colors <<= 2;
*dst++ = color;
}
} else if (g_vm->_videoMode == Common::RenderMode::kRenderHercG) {
for (int16 c = 0; c < 8; c++) {
byte color = (colors & 0x80) >> 7;
colors <<= 1;
*dst++ = color;
}
}
}
}
g_system->copyRectToScreen(scrbuffer + dy * g_vm->_screenW + dx, g_vm->_screenW, dx, dy, w * (0x8 / g_vm->_screenBits), h);
g_system->updateScreen();
}
void cga_blitToScreen(int16 ofs, int16 w, int16 h) {
uint16 byte_per_line = w / (0x8 / g_vm->_screenBits);
int16 dy = ofs / byte_per_line;
int16 dx = (ofs % byte_per_line) * (0x8 / g_vm->_screenBits);
cga_blitToScreen(dx, dy, w, h);
}
void cga_BackBufferToRealFull(void) {
memcpy(CGA_SCREENBUFFER, backbuffer, sizeof(backbuffer));
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
void cga_RealBufferToBackFull(void) {
memcpy(backbuffer, CGA_SCREENBUFFER, sizeof(backbuffer));
}
/*Copy interlaced screen data to another screen*/
/*NB! w is in bytes*/
void cga_CopyScreenBlock(byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
uint16 oh = h;
uint16 oofs = ofs;
while (h--) {
memcpy(target + ofs, source + ofs, w);
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (target == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), oh);
}
/*
Flip screen and backbuffer
*/
void cga_SwapRealBackBuffer(void) {
uint16 i;
uint16 *s, *d;
waitVBlank();
s = (uint16 *)CGA_SCREENBUFFER;
d = (uint16 *)backbuffer;
for (i = 0; i < sizeof(backbuffer) / 2; i++) {
uint16 t = *s;
*s++ = *d;
*d++ = t;
}
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
/*
Copy current screen's pixels to scratch mem, put new pixels to screen
*/
void cga_SwapScreenRect(byte *pixels, uint16 w, uint16 h, byte *screen, uint16 ofs) {
byte *old = scratch_mem2;
uint16 oh = h;
uint16 oofs = ofs;
while (h--) {
uint16 i;
for (i = 0; i < w; i++) {
*old++ = screen[ofs + i];
screen[ofs + i] = *pixels++;
}
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), oh);
}
/*
Calc screen offset from normal pixel coordinates
Out:
screen offset
*/
uint16 cga_CalcXY(uint16 x, uint16 y) {
return CalcXY_p(x / 4, y);
}
uint16 CalcXY(uint16 x, uint16 y) {
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA)
return cga_CalcXY(x, y);
else if (g_vm->_videoMode == Common::RenderMode::kRenderHercG)
return HGA_CalcXY(x, y);
else
return 0;
}
uint16 CalcXY_p(uint16 x, uint16 y) {
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA)
return cga_CalcXY_p(x, y);
else if (g_vm->_videoMode == Common::RenderMode::kRenderHercG)
return HGA_CalcXY_p(x, y);
else
return 0;
}
/*
Calc screen offset from packed pixel coordinates
Out:
screen offset
*/
uint16 cga_CalcXY_p(uint16 x, uint16 y) {
uint16 ofs = 0;
if (y & 1)
ofs += g_vm->_line_offset;
ofs += g_vm->_screenBPL * (y / 2);
ofs += x;
return ofs;
}
/*
backup screen rect to a buffer
Out:
next buffer ptr
*/
byte *cga_BackupImage(byte *screen, uint16 ofs, uint16 w, uint16 h, byte *buffer) {
*(byte *)(buffer + 0) = h;
*(byte *)(buffer + 1) = w;
*(uint16 *)(buffer + 2) = ofs;
buffer += 4;
while (h--) {
memcpy(buffer, screen + ofs, w);
buffer += w;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
return buffer;
}
byte *cga_BackupImageReal(uint16 ofs, uint16 w, uint16 h) {
return cga_BackupImage(CGA_SCREENBUFFER, ofs, w, h, scratch_mem2);
}
/*
Blit progressive image to interlaced screen buffer
NB! width and pixelswidth specify a number of bytes, not count of pixels
*/
void cga_Blit(byte *pixels, uint16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
byte *src = pixels;
uint16 oofs = ofs;
for (int16 y = 0; y < h; y++) {
memcpy(screen + ofs, src, w);
src += pw;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), h);
}
/*
Blit progressive image to interlaced screen buffer, then wait for VBlank
NB! width and pixelswidth specify a number of bytes, not count of pixels
*/
void cga_BlitAndWait(byte *pixels, uint16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
cga_Blit(pixels, pw, w, h, screen, ofs);
waitVBlank();
}
void cga_Fill(byte pixel, uint16 w, uint16 h, byte *screen, uint16 ofs) {
uint16 oofs = ofs;
for (int16 y = 0; y < h; y++) {
memset(screen + ofs, pixel, w);
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), h);
}
void cga_FillAndWait(byte pixel, uint16 w, uint16 h, byte *screen, uint16 ofs) {
cga_Fill(pixel, w, h, screen, ofs);
waitVBlank();
}
/*
Restore saved image to target screen buffer
*/
void cga_RestoreImage(byte *buffer, byte *target) {
uint16 w, h;
uint16 ofs;
if (!buffer)
return;
h = *(byte *)(buffer + 0);
w = *(byte *)(buffer + 1);
ofs = *(uint16 *)(buffer + 2);
buffer += 4; /*TODO: fix me for large int*/
cga_Blit(buffer, w, w, h, target, ofs);
}
/*
Restore saved image from scratch mem to target screen buffer
*/
void cga_RestoreBackupImage(byte *target) {
cga_RestoreImage(scratch_mem2, target);
}
/*
Copy image's real screen data to backbuffer
*/
void cga_RefreshImageData(byte *buffer) {
uint16 w, h;
uint16 ofs;
if (!buffer)
return;
h = *(byte *)(buffer + 0);
w = *(byte *)(buffer + 1);
ofs = *(uint16 *)(buffer + 2);
cga_CopyScreenBlock(CGA_SCREENBUFFER, w, h, backbuffer, ofs);
}
/*
Draw a vertical line with origin x:y and length l, using color
NB! Line must not wrap around the edge
*/
void cga_DrawVLine(uint16 x, uint16 y, uint16 l, byte color, byte *target) {
uint16 ofs;
uint16 mask = 0;
/*pixels are starting from top bits of byte*/
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA){
mask = static_cast<uint16>((~(3 << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB))) & 0xffff);
} else if (g_vm->_videoMode == Common::RenderMode::kRenderHercG) {
mask = static_cast<uint16>((~(1 << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB))) & 0xffff);
}
byte pixel = color << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB);
mask >>= (x % g_vm->_screenPPB) * g_vm->_screenPPB;
pixel >>= (x % g_vm->_screenPPB) * g_vm->_screenPPB;
ofs = CalcXY_p(x / g_vm->_screenPPB, y);
uint16 ol = l;
while (l--) {
target[ofs] = (target[ofs] & mask) | pixel;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (target == CGA_SCREENBUFFER)
cga_blitToScreen(x, y, 1, ol);
}
/*
Draw a horizontal line with origin x:y and length l, using color
NB! Line must not wrap around the edge
*/
void cga_DrawHLine(uint16 x, uint16 y, uint16 l, byte color, byte *target) {
uint16 ofs;
uint16 mask = 0;
/*pixels are starting from top bits of byte*/
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA){
mask = static_cast<uint16>((~(3 << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB))) & 0xffff);
} else if (g_vm->_videoMode == Common::RenderMode::kRenderHercG) {
mask = static_cast<uint16>((~(1 << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB))) & 0xffff);
}
byte pixel = color << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB);
mask >>= (x % g_vm->_screenPPB) * g_vm->_screenPPB;
pixel >>= (x % g_vm->_screenPPB) * g_vm->_screenPPB;
ofs = CalcXY_p(x / g_vm->_screenPPB, y);
uint16 ol = l;
while (l--) {
target[ofs] = (target[ofs] & mask) | pixel;
mask >>= g_vm->_screenPPB;
pixel >>= g_vm->_screenPPB;
if (mask == 0xFF) {
ofs++;
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA){
mask = static_cast<uint16>((~(3 << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB))) & 0xffff);
} else if (g_vm->_videoMode == Common::RenderMode::kRenderHercG) {
mask = static_cast<uint16>((~(1 << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB))) & 0xffff);
}
pixel = color << ((g_vm->_screenPPB - 1) * g_vm->_screenPPB);
}
}
if (target == CGA_SCREENBUFFER)
cga_blitToScreen(x, y, ol, 1);
}
/*
Draw horizontal line of length l with color, add surrounding pixels (bmask, bpix, left in high byte, right in low)
Return next line screen offset
NB! Length specifies byte lenght of inner segment, not amount of pixels
*/
uint16 cga_DrawHLineWithEnds(uint16 bmask, uint16 bpix, byte color, uint16 l, byte *target, uint16 ofs) {
target[ofs] = (target[ofs] & (bmask >> 8)) | (bpix >> 8);
memset(target + ofs + 1, color, l);
target[ofs + 1 + l] = (target[ofs + 1 + l] & (bmask & 255)) | (bpix & 255);
uint16 oofs = ofs;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
if (target == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, l * 4 + 2, 1);
return ofs;
}
/*
Print a character at current cursor pos, then advance
*/
void cga_PrintChar(byte c, byte *target) {
uint16 i;
byte *font = carpc_data + c * g_vm->_fontHeight;
uint16 ofs = cga_CalcXY_p(char_draw_coords_x++, char_draw_coords_y); // TODO: hga_calcxy_p doesnt work with this??
for (i = 0; i < g_vm->_fontHeight; i++) {
c = *font++;
c = char_xlat_table[c];
target[ofs] = c;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (target == CGA_SCREENBUFFER)
cga_blitToScreen((char_draw_coords_x - 1) * g_vm->_fontWidth, char_draw_coords_y, g_vm->_fontWidth, g_vm->_fontHeight);
}
/*
Blit progressive sprite (mask+pixel) from scratch buffer to interlaced screen buffer, using backbuffer pixels for transparency
NB! width specify a number of bytes, not count of pixels
TODO: generalize/merge me with BlitSprite
*/
void cga_BlitScratchBackSprite(uint16 sprofs, uint16 w, uint16 h, byte *screen, uint16 ofs) {
byte x;
byte *pixels = scratch_mem2 + 2 + sprofs;
uint16 oh = h;
uint16 oofs = ofs;
while (h--) {
for (x = 0; x < w; x++)
screen[ofs + x] = (backbuffer[ofs + x] & pixels[x * 2]) | pixels[x * 2 + 1];
pixels += w * 2;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), oh);
}
void cga_BlitFromBackBuffer(byte w, byte h, byte *screen, uint16 ofs) {
cga_CopyScreenBlock(backbuffer, w, h, screen, ofs);
}
/*
Blit progressive sprite (mask+pixel) to interlaced screen buffer
NB! width and pixelswidth specify a number of bytes, not count of pixels
*/
void cga_BlitSprite(byte *pixels, int16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
byte x;
uint16 oh = h;
uint16 oofs = ofs;
while (h--) {
for (x = 0; x < w; x++)
screen[ofs + x] = (screen[ofs + x] & pixels[x * 2]) | pixels[x * 2 + 1];
pixels += pw;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), oh);
}
/*
Blit progressive sprite (mask+pixel) to interlaced screen buffer. Flip the sprite horizontally
NB! width and pixelswidth specify a number of bytes, not count of pixels
*/
void cga_BlitSpriteFlip(byte *pixels, int16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
byte x;
uint16 oh = h;
uint16 oofs = ofs;
while (h--) {
for (x = 0; x < w; x++)
screen[ofs - x] = (screen[ofs - x] & cga_pixel_flip[pixels[x * 2]]) | cga_pixel_flip[pixels[x * 2 + 1]];
pixels += pw;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), oh);
}
/*
Blit progressive sprite (pixel+mask) to interlaced screen buffer
Backup original screen data to buffer
Apply extra mask to source pixels when drawing
Used to draw mouse cursor and backup what's under it
NB! width and pixelswidth specify a number of bytes, not count of pixels
NB! pixel+mask comes in reversed order, compared to regular BlitSprite
*/
void cga_BlitSpriteBak(byte *pixels, int16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs, byte *backup, byte mask) {
byte x;
uint16 oh = h;
uint16 oofs = ofs;
while (h--) {
for (x = 0; x < w; x++) {
*backup++ = screen[ofs + x];
screen[ofs + x] = (screen[ofs + x] & pixels[x * 2 + 1]) | (pixels[x * 2] & mask);
}
pixels += pw * 2;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER)
cga_blitToScreen(oofs, w * (0x8 / g_vm->_screenBits), oh);
}
/*
Blit progressive sprite (w+h+mask+pixel) to interlaced screen buffer
*/
void drawSprite(byte *sprite, byte *screen, uint16 ofs) {
byte w, h;
w = *sprite++;
h = *sprite++;
cga_BlitSprite(sprite, w * 2, w, h, screen, ofs);
}
/*
Blit progressive sprite (w+h+mask+pixel) to interlaced screen buffer, horizontally flipped
*/
void drawSpriteFlip(byte *sprite, byte *screen, uint16 ofs) {
byte w, h;
w = *sprite++;
h = *sprite++;
cga_BlitSpriteFlip(sprite, w * 2, w, h, screen, ofs);
}
/*
Load and uncompress 2-bit sprite
Return next ptr after the loaded sprite
*/
byte *loadSprite(byte index, byte *bank, byte *buffer, byte header_only) {
byte w, h;
uint16 rsize;
byte *sprite, *sprite_end;
byte *bitmask;
sprite = seekToEntryW(bank, index, &sprite_end);
w = *sprite++;
h = *sprite++;
rsize = w * h; /*raster part size*/
bitmask = sprite + rsize;
*buffer++ = w;
*buffer++ = h;
if (header_only) {
memset(buffer, 0, rsize * 2); /*wipe masks+pixels*/
buffer += rsize * 2;
} else {
if (*bitmask == 0) {
/*solid sprite*/
while (rsize--) {
*buffer++ = 0;
*buffer++ = *sprite++;
}
} else {
/*with transparency*/
byte bi = 1;
while (rsize--) {
byte pixels = *sprite++;
byte mask = 0;
if ((pixels & 0xC0) == 0) {
bi >>= 1;
if (bi == 0) {
bi = 0x80;
bitmask++;
}
if (*bitmask & bi)
mask |= 0xC0;
}
if ((pixels & 0x30) == 0) {
bi >>= 1;
if (bi == 0) {
bi = 0x80;
bitmask++;
}
if (*bitmask & bi)
mask |= 0x30;
}
if ((pixels & 0xC) == 0) {
bi >>= 1;
if (bi == 0) {
bi = 0x80;
bitmask++;
}
if (*bitmask & bi)
mask |= 0xC;
}
if ((pixels & 0x3) == 0) {
bi >>= 1;
if (bi == 0) {
bi = 0x80;
bitmask++;
}
if (*bitmask & bi)
mask |= 0x3;
}
*buffer++ = mask;
*buffer++ = pixels;
}
}
}
return buffer;
}
extern byte sprit_data[RES_SPRIT_MAX];
byte sprit_load_buffer[1290];
byte *loadSprit(byte index) {
loadSprite(index, sprit_data + 4, sprit_load_buffer, 0);
return sprit_load_buffer;
}
byte *loadPersSprit(byte index) {
#if 1
/*Use separate memory for pers1/pers2*/
if (index < 61)
loadSprite(index, pers1_data + 4, scratch_mem2, 0);
else
loadSprite(index - 61, pers2_data + 4, scratch_mem2, 0);
#else
/*Use single large chunk for pers1+pers2*/
loadSprite(index, pers1_data + 4, scratch_mem2, 0);
#endif
return scratch_mem2;
}
void drawSpriteN(byte index, uint16 x, uint16 y, byte *target) {
uint16 ofs;
byte *sprite;
sprite = loadSprit(index);
ofs = CalcXY_p(x, y);
drawSprite(sprite, target, ofs);
}
void drawSpriteNFlip(byte index, uint16 x, uint16 y, byte *target) {
uint16 ofs;
byte *sprite;
sprite = loadSprit(index);
ofs = CalcXY_p(x, y);
drawSpriteFlip(sprite, target, ofs);
}
void backupAndShowSprite(byte index, byte x, byte y) {
byte w, h;
uint16 ofs;
byte *sprite = loadSprit(index);
ofs = CalcXY_p(x, y);
w = sprite[0];
h = sprite[1];
cga_BackupImageReal(ofs, w, h);
drawSprite(sprite, CGA_SCREENBUFFER, ofs); /*DrawSpriteN(index, x, y, CGA_SCREENBUFFER);*/
}
/*
Blit progressive image to interlaced screen buffer, then wait for VBlank
Push image from the top to down
NB! width and pixelswidth specify a number of bytes, not count of pixels
*/
void cga_AnimLiftToDown(byte *pixels, uint16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
uint16 i;
pixels += pw * (h - 1);
for (i = 1; i <= h; i++) {
cga_BlitAndWait(pixels, pw, w, i, screen, ofs);
pixels -= pw;
}
}
/*
Blit progressive image to interlaced screen buffer, then wait for VBlank
Pull and expand image from the right to left
NB! width and pixelswidth specify a number of bytes, not count of pixels
NB! ofs specifies top-right corner of the image
*/
void cga_AnimLiftToLeft(uint16 n, byte *pixels, uint16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
uint16 i;
for (i = 0; i < n; i++) {
cga_BlitAndWait(pixels, pw, w + i, h, screen, ofs);
ofs -= 1;
}
}
/*
Blit progressive image to interlaced screen buffer, then wait for VBlank
Push image from the left to right
NB! width and pixelswidth specify a number of bytes, not count of pixels
*/
void cga_AnimLiftToRight(uint16 n, byte *pixels, uint16 pw, uint16 w, uint16 h, byte *screen, uint16 ofs) {
uint16 i;
for (i = 0; i < n; i++) {
cga_BlitAndWait(pixels, pw, w + i, h, screen, ofs);
pixels -= 1;
}
}
/*
Blit progressive image to interlaced screen buffer, then wait for VBlank
Push image from the down to up
NB! width and pixelswidth specify a number of bytes, not count of pixels
NB! x:y specifies left-bottom coords
*/
void cga_AnimLiftToUp(byte *pixels, uint16 pw, uint16 w, uint16 h, byte *screen, uint16 x, uint16 y) {
uint16 i;
for (i = 1; i <= h; i++) {
cga_BlitAndWait(pixels, pw, w, i, screen, cga_CalcXY_p(x, y));
y -= 1;
}
}
/*Copy interlaced screen data n lines down (with clipping)*/
/*Fill gap with source screen data*/
/*offs points to block's bottom most line, data will be shifted to next line*/
/*NB! w is in bytes*/
void cga_HideScreenBlockLiftToDown(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
while (n--) {
int16 i;
uint16 sofs, tofs;
sofs = ofs;
tofs = ofs;
tofs ^= g_vm->_line_offset;
if ((tofs & g_vm->_line_offset) == 0)
tofs += g_vm->_screenBPL;
/*shift whole block 1 line*/
for (i = 0; i < h; i++) {
/*copy current line to next*/
memcpy(target + tofs, screen + sofs, w);
/*next = current*/
tofs = sofs;
/*go 1 line up*/
sofs ^= g_vm->_line_offset;
if ((sofs & g_vm->_line_offset) != 0)
sofs -= g_vm->_screenBPL;
}
/*fill just freed line with new pixels*/
memcpy(target + tofs, source + tofs, w);
if (screen == CGA_SCREENBUFFER) {
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenW);
}
waitVBlank();
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
}
/*Copy interlaced screen data n lines up (with clipping)*/
/*Fill gap with source screen data*/
/*offs points to block's top most line, data will be shifted to previous line*/
/*NB! w is in bytes*/
void cga_HideScreenBlockLiftToUp(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
while (n--) {
int16 i;
uint16 sofs, tofs;
sofs = ofs;
tofs = ofs;
tofs ^= g_vm->_line_offset;
if ((tofs & g_vm->_line_offset) != 0)
tofs -= g_vm->_screenBPL;
/*shift whole block 1 line*/
for (i = 0; i < h; i++) {
/*copy current line to next*/
memcpy(target + tofs, screen + sofs, w);
/*next = current*/
tofs = sofs;
/*go 1 line down*/
sofs ^= g_vm->_line_offset;
if ((sofs & g_vm->_line_offset) == 0)
sofs += g_vm->_screenBPL;
}
/*fill just freed line with new pixels*/
memcpy(target + tofs, source + tofs, w);
if (screen == CGA_SCREENBUFFER) {
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
waitVBlank();
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) != 0)
ofs -= g_vm->_screenBPL;
}
}
/*Copy interlaced screen data n lines left (with clipping)*/
/*Fill gap with source screen data*/
/*offs points to block's left most column, data will be shifted to previous column*/
/*NB! w is in bytes*/
void cga_HideScreenBlockLiftToLeft(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
while (n--) {
int16 i;
uint16 sofs, tofs;
sofs = ofs;
/*shift whole block 1 column*/
for (i = 0; i < h; i++) {
tofs = sofs - 1;
/*use memmove since buffers may overlap*/
memmove(target + tofs, screen + sofs, w);
/*fill freed column*/
memcpy(target + tofs + w, source + tofs + w, 1);
/*go 1 line down*/
sofs ^= g_vm->_line_offset;
if ((sofs & g_vm->_line_offset) == 0)
sofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER) {
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
waitVBlank();
ofs--;
}
}
/*Copy interlaced screen data n lines right (with clipping)*/
/*Fill gap with source screen data*/
/*offs points to block's right most column, data will be shifted to next column*/
/*NB! w is in bytes*/
void cga_HideScreenBlockLiftToRight(uint16 n, byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
while (n--) {
int16 i;
uint16 sofs, tofs;
sofs = ofs;
/*shift whole block 1 column*/
for (i = 0; i < h; i++) {
tofs = sofs + 1;
/*use memmove since buffers may overlap*/
memmove(target + tofs - w, screen + sofs - w, w);
/*fill freed column*/
memcpy(target + tofs - w, source + tofs - w, 1);
/*go 1 line down*/
sofs ^= g_vm->_line_offset;
if ((sofs & g_vm->_line_offset) == 0)
sofs += g_vm->_screenBPL;
}
if (screen == CGA_SCREENBUFFER) {
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
waitVBlank();
ofs++;
}
}
typedef struct scrpiece_t {
uint16 offs;
byte delay; /*speed in bits 1..0, delay in bits 7..2*/
byte pix0;
byte pix2;
byte pix1;
byte pix3;
} scrpiece_t;
static const byte piecedelays[] = {
219, 182, 237, 187, 110, 219, 187, 120, 219, 109, 182,
219, 109, 182, 219, 104, 182, 214, 218, 219, 91, 107,
107, 104, 213, 107, 90, 214, 181, 182, 214, 216, 213,
86, 214, 173, 91, 91, 85, 88, 170, 173, 85, 170, 181,
85, 182, 168, 170, 170, 171, 85, 85, 90, 170, 168, 170,
170, 170, 170, 170, 170, 170, 168, 213, 82, 170, 85,
74, 170, 73, 88, 170, 169, 41, 82, 164, 164, 170, 168,
170, 148, 165, 41, 74, 73, 41, 40, 201, 41, 37, 36,
164, 148, 148, 152, 164, 146, 73, 36, 146, 73, 36, 152,
164, 73, 18, 68, 145, 36, 68, 136, 145, 34, 36, 68,
136, 145, 18, 40, 145, 16, 136, 136, 68, 68, 66, 24,
136, 66, 16, 132, 33, 8, 66, 24, 132, 16, 65, 8, 32,
132, 16, 72, 130, 4, 8, 8, 16, 32, 32, 72, 128, 128,
64, 32, 8, 2, 1, 8, 128, 8, 0, 32, 0, 128, 8, 8, 128,
0, 4, 0, 0, 4, 0, 8, 128, 0, 0, 0, 0, 0, 0, 8
};
/*break screen area onto 4x4 pix pieces*/
static void screenToPieces(byte width, byte height, byte *screen, uint16 offs, scrpiece_t *pieces) {
const byte *delays = piecedelays;
height = (height + 3) / 4;
while (height--) {
uint16 x;
for (x = 0; x < width; x++) {
uint16 bofs = offs + x;
pieces->offs = bofs;
pieces->delay = *delays++;
if (pieces->delay == 0) /*ensure piece is alive*/
pieces->delay = 1;
pieces->pix0 = screen[bofs];
pieces->pix2 = screen[bofs + g_vm->_screenBPL];
bofs ^= g_vm->_line_offset;
if ((bofs & g_vm->_line_offset) == 0)
bofs += g_vm->_screenBPL;
pieces->pix1 = screen[bofs];
pieces->pix3 = screen[bofs + g_vm->_screenBPL];
pieces++;
if (delays >= piecedelays + ARRAYSIZE(piecedelays))
delays = piecedelays;
}
offs += g_vm->_screenBPL * 2; /*4 lines down*/
}
pieces->offs = 0; /*end of list*/
}
static void fallPieces(scrpiece_t *pieces, byte *source, byte *target) {
byte t = 1;
byte again = 0;
do {
scrpiece_t *piece;
for (piece = pieces, again = 0; piece->offs; piece++) {
if ((piece->delay >> 2) < t) {
uint16 offs = piece->offs;
uint16 bofs = offs;
if (target[bofs] == piece->pix0)
target[bofs] = source[bofs];
if (target[bofs + g_vm->_screenBPL] == piece->pix2)
target[bofs + g_vm->_screenBPL] = source[bofs + g_vm->_screenBPL];
bofs ^= g_vm->_line_offset;
if ((bofs & g_vm->_line_offset) == 0)
bofs += g_vm->_screenBPL;
if (target[bofs] == piece->pix1)
target[bofs] = source[bofs];
if (target[bofs + g_vm->_screenBPL] == piece->pix3)
target[bofs + g_vm->_screenBPL] = source[bofs + g_vm->_screenBPL];
/*dead?*/
if (piece->delay == 0)
continue;
/*fall n lines*/
switch ((piece->delay & 3) + 1) {
case 1:
offs ^= g_vm->_line_offset;
if ((offs & g_vm->_line_offset) == 0)
offs += g_vm->_screenBPL;
break;
case 2:
offs += g_vm->_screenBPL;
break;
case 3:
offs += g_vm->_screenBPL;
offs ^= g_vm->_line_offset;
if ((offs & g_vm->_line_offset) == 0)
offs += g_vm->_screenBPL;
break;
case 4:
offs += g_vm->_screenBPL;
offs += g_vm->_screenBPL;
break;
}
/*extra 2 lines*/
offs += g_vm->_screenBPL;
piece->offs = offs;
/*past line 190?*/
if ((offs | g_vm->_line_offset) >= ((190 / 2 * g_vm->_screenBPL) | g_vm->_line_offset)) {
piece->delay = 0;
continue;
}
bofs = offs;
target[bofs] = piece->pix0;
target[bofs + g_vm->_screenBPL] = piece->pix2;
bofs ^= g_vm->_line_offset;
if ((bofs & g_vm->_line_offset) == 0)
bofs += g_vm->_screenBPL;
target[bofs] = piece->pix1;
target[bofs + g_vm->_screenBPL] = piece->pix3;
}
again = 1;
}
if (target == CGA_SCREENBUFFER) {
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
waitVBlank();
t++;
} while (again);
}
void cga_HideShatterFall(byte *screen, byte *source, uint16 w, uint16 h, byte *target, uint16 ofs) {
scrpiece_t *pieces = (scrpiece_t *)scratch_mem2;
screenToPieces(w, h, screen, ofs, pieces);
fallPieces(pieces, source, target);
}
void cga_TraceLine(uint16 sx, uint16 ex, uint16 sy, uint16 ey, byte *source, byte *target) {
byte b0 = 0;
byte b1 = 0;
byte mask;
uint16 ofs;
uint16 count;
int16 w, h, dx, dy, val;
if (ex >= sx)
w = ex - sx;
else {
w = sx - ex - 1;
b0 = 1;
}
dx = -(w * 2) - 1;
if (ey >= sy)
h = ey - sy;
else {
h = sy - ey - 1;
b1 = 1;
}
dy = h * 2;
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA) {
ofs = CalcXY_p(sx / 4, sy);
mask = 0xC0 >> ((sx % 4) * 2);
} else {
ofs = CalcXY_p(sx / 8, sy);
mask = 0x80 >> (sx % 8); // TODO: check this
}
/*
ofs = CalcXY_p(sx / 4, sy);
mask = 0xC0 >> ((sx % 4) * 2);
*/
val = dy + dx;
target[ofs] = (target[ofs] & ~mask) | (source[ofs] & mask);
count = w + h;
while (count--) {
if (val > 0) {
if (b1 == 0) {
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
} else {
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) != 0)
ofs -= g_vm->_screenBPL;
}
val += dx;
} else {
if (b0 == 0) {
mask >>= 2;
if (mask == 0) {
mask = 0xC0;
ofs += 1;
}
} else {
mask <<= 2;
if (mask == 0) {
mask = 0x03;
ofs -= 1;
}
}
val += dy;
if (val > 0)
continue;
}
target[ofs] = (target[ofs] & ~mask) | (source[ofs] & mask);
}
if (target == CGA_SCREENBUFFER)
cga_blitToScreen(0, 0, g_vm->_screenW, g_vm->_screenH);
}
/*TODO: get rid of this structure and pass everything relevant as arguments?*/
typedef struct zoom_t {
byte ybase;
byte xbase; /*IN: original x shift in pixels*/
byte xval_l;
byte xval_h;
byte xstep_l;
byte xstep_h;
byte yval_l;
byte yval_h;
byte ystep_l;
byte ystep_h;
byte ew; /*IN: original width/height in pixels*/
byte eh;
byte scale_x;
byte scale_y;
byte oh; /*IN: original width/height in bytes*/
byte ow;
byte *pixels; /*IN: image pixels*/
byte fw;
} zoom_t;
/*
Divide two normal values and return 8.8 quotient
*/
static uint16 FPDiv(uint16 a, byte b) {
byte hi = a / b;
byte lo = ((a % b) << 8) / b;
return (hi << 8) + lo;
/*is it really any better than (uint32)(a << 8) / b ?*/
}
// call commented out in void cga_ZoomImage(byte *pixels, byte w, byte h, byte nw, byte nh, byte *target, uint16 ofs) currently
// thus causes an unused function warning
#if 0
/*
Draw scaled image
NB! tw/th specify target width/height in pixels
*/
static void cga_Zoom(zoom_t *params, byte tw, byte th, byte *source, byte *target, uint16 ofs) {
byte x, y;
uint16 finofs = ofs;
byte *temp = scratch_mem2;
/*calc old/new ratio*/
params->scale_x = tw + 1;
params->xstep_l = params->ew / params->scale_x;
params->xstep_h = (params->ew % params->scale_x) * 256 / params->scale_x;
params->scale_y = th + 1;
params->ystep_l = params->eh / params->scale_y;
params->ystep_h = (params->eh % params->scale_y) * 256 / params->scale_y;
params->yval_l = 0;
params->yval_h = 0;
for (y = params->scale_y;;) {
uint16 oofs = ofs;
byte *pixels = params->pixels + params->yval_l * params->ow;
byte sc = 4 - params->xbase;
/*left partial pixel*/
byte pix = source[ofs] >> (sc * 2);
params->xval_l = 0;
params->xval_h = 0;
params->fw = 0;
for (x = params->scale_x;;) {
byte p = pixels[params->xval_l / 4] << ((params->xval_l % 4) * 2);
pix = (pix << 2) | (p >> 6);
if (--sc == 0) {
/*inner full pixel*/
*temp++ = pix;
ofs++;
params->fw++;
sc = 4;
}
params->xval_l += params->xstep_l + ((params->xval_h + params->xstep_h) >> 8);
params->xval_h += params->xstep_h;
if (x == 0)
break;
if (--x == 0)
params->xval_l = params->ew;
}
/*right partial pixel*/
*temp++ = (source[ofs] & ~(0xFF << (sc * 2))) | (pix << (sc * 2));
ofs++;
params->fw++;
/*ofs -= params->fw;*/
ofs = oofs;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
params->yval_l += params->ystep_l + ((params->yval_h + params->ystep_h) >> 8);
params->yval_h += params->ystep_h;
if (y == 0)
break;
if (--y == 0)
params->yval_l = params->eh;
}
cga_BlitAndWait(scratch_mem2, params->fw, params->fw, th + 2, target, finofs);
}
#endif
/*
Draw scaled image
NB! tw/th specify target width/height in pixels
This is slightly simplified version, but should work identical to the code above
*/
static void cga_ZoomOpt(zoom_t *params, byte tw, byte th, byte *source, byte *target, uint16 ofs) {
byte x, y;
uint16 finofs = ofs;
byte *temp = scratch_mem2;
/*calc old/new ratio*/
uint16 target_w = tw + 1;
uint16 xstep = (params->ew << 8) / target_w; /*fixed-point 8.8 value*/
uint16 target_h = th + 1;
uint16 ystep = (params->eh << 8) / target_h; /*fixed-point 8.8 value*/
uint16 yval = 0;
for (y = target_h;;) {
byte *pixels = params->pixels + (yval >> 8) * params->ow;
byte sc = 4 - params->xbase;
/*left partial pixel*/
byte pix = source[ofs] >> (sc * 2);
uint16 xval = 0;
params->fw = 0;
for (x = target_w;;) {
byte p = pixels[(xval >> 8) / 4] << (((xval >> 8) % 4) * 2);
pix = (pix << 2) | (p >> 6);
if (--sc == 0) {
/*inner full pixel*/
*temp++ = pix;
ofs++;
params->fw++;
sc = 4;
}
xval += xstep;
if (x == 0)
break;
if (--x == 0)
xval = params->ew << 8;
}
/*right partial pixel*/
*temp++ = (source[ofs] & ~(0xFF << (sc * 2))) | (pix << (sc * 2));
ofs++;
params->fw++;
ofs -= params->fw;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
yval += ystep;
if (y == 0)
break;
if (--y == 0)
yval = params->eh << 8;
}
cga_BlitAndWait(scratch_mem2, params->fw, params->fw, th + 2, target, finofs);
}
/*
Draw image zoomed from w:h to nw:nx to target at specified ofs
Use backbuffer pixels to fill sides
NB! w/nw are the number of bytes, not pixels
*/
void cga_ZoomImage(byte *pixels, byte w, byte h, byte nw, byte nh, byte *target, uint16 ofs) {
zoom_t zoom;
zoom.pixels = pixels;
zoom.ow = w;
zoom.oh = h;
zoom.ew = (w * 4) - 1;
zoom.eh = h - 1;
if (g_vm->getLanguage() == Common::EN_USA)
zoom.xbase = 0;
else
zoom.xbase = ((w * 4 / 2) - 1) % 4;
/*TODO: why this nw/nh order? maybe bug*/
#if 0
cga_Zoom(&zoom, nh - 2, nw * 4 - 2, backbuffer, target, ofs);
#else
cga_ZoomOpt(&zoom, nh - 2, nw * 4 - 2, backbuffer, target, ofs);
#endif
}
/*
Animate image zooming-in from origin ofs to final size w:h in specified number of steps
Use backbuffer pixels to fill sides
Ofs specifies zoom origin
NB! w is the number of bytes, not pixels
*/
void cga_AnimZoomOpt(zoom_t *zoom, uint16 w, uint16 h, byte steps, byte *target, uint16 ofs) {
uint16 xstep = FPDiv(w, steps); /*fixed-point 8.8 value*/
uint16 ystep = FPDiv(h, steps); /*fixed-point 8.8 value*/
uint16 xval = 0x200;
uint16 yval = 0x200;
for (steps = steps / 2 - 2; steps; steps--) {
uint16 prev;
cga_ZoomOpt(zoom, xval >> 8, yval >> 8, backbuffer, target, ofs);
prev = yval;
yval += ystep;
if (((prev ^ yval) & 0xFF00) || ((yval & 0x100) == 0)) {
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) != 0)
ofs -= g_vm->_screenBPL;
}
prev = xval;
xval += xstep;
if (((prev ^ xval) & 0xFF00) || ((xval & 0x100) == 0)) {
if (zoom->xbase-- == 0) {
ofs -= 1;
zoom->xbase = 3;
}
}
}
}
/*
Animate image zooming-in from its center to final size w:h to target at specified ofs
Use backbuffer pixels to fill sides
NB! w is the number of bytes, not pixels
NB! ofs is the final image top left corner, not the zoom origin
*/
void cga_AnimZoomIn(byte *pixels, byte w, byte h, byte *target, uint16 ofs) {
uint16 finofs = ofs;
byte x, y, maxside;
zoom_t zoom;
zoom.pixels = pixels;
zoom.ow = w;
zoom.oh = h;
zoom.ew = (w * 4) - 1;
zoom.eh = h - 1;
zoom.xbase = ((w * 4 / 2) - 1) % 4;
/*set zoom origin*/
x = (w * 4 / 2) - 1;
y = (h / 2) - 1;
ofs += x / 4;
if (y & 1) {
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
}
ofs += (y / 2) * g_vm->_screenBPL;
maxside = w * 4;
if (maxside < h)
maxside = h;
cga_AnimZoomOpt(&zoom, w * 4 * 2, h * 2, maxside, target, ofs);
cga_BlitAndWait(pixels, w, w, h, target, finofs);
}
/*
Draw scaled image
NB! tw/th specify target width/height in pixels
*/
void cga_ZoomInplace(zoom_t *params, byte tw, byte th, byte *source, byte *target, uint16 ofs) {
byte x, y;
/*calc old/new ratio*/
params->scale_x = tw + 1;
params->xstep_l = (uint16)params->ew / params->scale_x;
params->xstep_h = (uint16)(((uint16)params->ew % params->scale_x) << 8) / params->scale_x;
params->scale_y = th + 1;
params->ystep_l = (uint16)params->eh / params->scale_y;
params->ystep_h = (uint16)(((uint16)params->eh % params->scale_y) << 8) / params->scale_y;
params->yval_l = 0;
params->yval_h = 0;
for (y = params->scale_y;;) {
uint16 oofs = ofs;
byte *pixels = params->pixels + params->yval_l * params->ow;
byte sc = 4 - params->xbase;
/*left partial pixel*/
byte pix = source[ofs] >> (sc * 2);
params->xval_l = 0;
params->xval_h = 0;
params->fw = 0;
for (x = params->scale_x;;) {
byte p = pixels[params->xval_l / 4] << ((params->xval_l % 4) * 2);
pix = (pix << 2) | (p >> 6);
if (--sc == 0) {
/*inner full pixel*/
target[ofs] = pix;
ofs++;
params->fw++;
sc = 4;
}
params->xval_l += params->xstep_l + ((params->xval_h + params->xstep_h) >> 8);
params->xval_h += params->xstep_h;
if (x == 0)
break;
if (--x == 0)
params->xval_l = params->ew;
}
/*right partial pixel*/
target[ofs] = (source[ofs] & ~(0xFF << (sc * 2))) | (pix << (sc * 2));
ofs++;
params->fw++;
/*ofs -= params->fw;*/
ofs = oofs;
ofs ^= g_vm->_line_offset;
if ((ofs & g_vm->_line_offset) == 0)
ofs += g_vm->_screenBPL;
params->yval_l += params->ystep_l + ((params->yval_h + params->ystep_h) >> 8);
params->yval_h += params->ystep_h;
if (y == 0)
break;
if (--y == 0)
params->yval_l = params->eh;
}
}
void cga_ZoomInplaceXY(byte *pixels, byte w, byte h, byte nw, byte nh, uint16 x, uint16 y, byte *target) {
zoom_t zoom;
zoom.pixels = pixels;
zoom.ow = w;
zoom.oh = h;
zoom.ew = (w * 4) - 1;
zoom.eh = h - 1;
zoom.xbase = x % 4;
cga_ZoomInplace(&zoom, nw, nh, target, target, CalcXY_p(x, y));
}
} // End of namespace Chamber