/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ /* Required defines: ** FUNCNAME: Function name ** SIZETYPE: Type used for each pixel ** EXTRA_BYTE_OFFSET: Extra source byte offset for copying (used on big-endian machines in 24 bit mode) */ #include "sci/sci_memory.h" namespace Sci { #define EXTEND_COLOR(x) (unsigned) ((((unsigned) x) << 24) | (((unsigned) x) << 16) | (((unsigned) x) << 8) | ((unsigned) x)) #define PALETTE_MODE mode->palette void FUNCNAME(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) { SIZETYPE result_colors[GFX_PIC_COLORS]; SIZETYPE alpha_color = 0xffffffff & mode->alpha_mask; SIZETYPE alpha_ormask = 0; int xfact = (scale) ? mode->xfact : 1; int yfact = (scale) ? mode->yfact : 1; int widthc, heightc; // Width duplication counter int line_width = xfact * pxm->index_xl; int bytespp = mode->bytespp; int x, y; int i; byte byte_transparent = (mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA) ? 0 : 255; byte byte_opaque = (mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA) ? 255 : 0; byte *src = pxm->index_data; byte *dest = pxm->data; byte *alpha_dest = pxm->alpha_map; int using_alpha = pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE; int separate_alpha_map = (!mode->alpha_mask) && using_alpha; if (mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA) { alpha_ormask = alpha_color; alpha_color = 0; } assert(bytespp == COPY_BYTES); if (separate_alpha_map && !alpha_dest) alpha_dest = pxm->alpha_map = (byte *)sci_malloc(pxm->index_xl * xfact * pxm->index_yl * yfact); // Calculate all colors for (i = 0; i < pxm->colors_nr; i++) { int col; if (PALETTE_MODE) col = pxm->colors[i].global_index; else { col = mode->red_mask & ((EXTEND_COLOR(pxm->colors[i].r)) >> mode->red_shift); col |= mode->green_mask & ((EXTEND_COLOR(pxm->colors[i].g)) >> mode->green_shift); col |= mode->blue_mask & ((EXTEND_COLOR(pxm->colors[i].b)) >> mode->blue_shift); col |= alpha_ormask; } result_colors[i] = col; } if (!separate_alpha_map && pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE) result_colors[pxm->color_key] = alpha_color; src = pxm->index_data; // Workaround for gcc 4.2.3 bug on EMT64 for (y = 0; y < pxm->index_yl; y++) { byte *prev_dest = dest; byte *prev_alpha_dest = alpha_dest; for (x = 0; x < pxm->index_xl; x++) { int isalpha; SIZETYPE col = result_colors[isalpha = *src++] << (EXTRA_BYTE_OFFSET * 8); isalpha = (isalpha == pxm->color_key) && using_alpha; // O(n) loops. There is an O(ln(n)) algorithm for this, but its slower for small n (which we're optimizing for here). // And, anyway, most of the time is spent in memcpy() anyway. for (widthc = 0; widthc < xfact; widthc++) { memcpy(dest, &col, COPY_BYTES); dest += COPY_BYTES; } if (separate_alpha_map) { // Set separate alpha map memset(alpha_dest, (isalpha) ? byte_transparent : byte_opaque, xfact); alpha_dest += xfact; } } // Copies each line. O(n) iterations; again, this could be optimized to O(ln(n)) for very high resolutions, // but that wouldn't really help that much, as the same amount of data still would have to be transferred. for (heightc = 1; heightc < yfact; heightc++) { memcpy(dest, prev_dest, line_width * bytespp); dest += line_width * bytespp; if (separate_alpha_map) { memcpy(alpha_dest, prev_alpha_dest, line_width); alpha_dest += line_width; } } } } // linear filter: Macros (in reverse order) #define X_CALC_INTENSITY_NORMAL (ctexel[i] << 16) + ((linecolor[i]) * (256-column_valuator)) + ((othercolumn[i] * column_valuator)) * (256-line_valuator) #define X_CALC_INTENSITY_CENTER (ctexel[i] << 16) + ((linecolor[i]) * (256-column_valuator)) #define WRITE_XPART(X_CALC_INTENSITY, DO_X_STEP) \ for (subx = 0; subx < ((DO_X_STEP) ? (xfact >> 1) : 1); subx++) { \ unsigned int intensity; \ wrcolor = 0; \ for (i = 0; i < 3; i++) { \ intensity = X_CALC_INTENSITY; \ wrcolor |= (intensity >> shifts[i]) & masks[i]; \ } \ i = 3; \ intensity = X_CALC_INTENSITY; \ if (inverse_alpha) \ intensity = ~intensity; \ wrcolor |= (intensity >> shifts[i]) & masks[i]; \ if (separate_alpha_map) \ *alpha_wrpos++ = intensity >> 24; \ wrcolor <<= (EXTRA_BYTE_OFFSET * 8); \ memcpy(wrpos, &wrcolor, COPY_BYTES); \ wrpos += COPY_BYTES; \ if (DO_X_STEP) \ column_valuator -= column_step; \ } \ if (DO_X_STEP) \ column_step = -column_step // End of macro definition #define Y_CALC_INTENSITY_CENTER 0 #define Y_CALC_INTENSITY_NORMAL otherline[i] * line_valuator #define WRITE_YPART(DO_Y_STEP, LINE_COLOR) \ for (suby = 0; suby < ((DO_Y_STEP)? yfact >> 1 : 1); suby++) { \ int column_valuator = column_step? 128 - (column_step >> 1) : 256; \ int linecolor[4]; \ int othercolumn[4]; \ int i; \ SIZETYPE wrcolor; \ wrpos = sublinepos; \ alpha_wrpos = alpha_sublinepos; \ for (i = 0; i < 4; i++) \ linecolor[i] = LINE_COLOR; \ /*-- left half --*/ \ MAKE_PIXEL((x == 0), othercolumn, ctexel, src[-1]); \ WRITE_XPART(X_CALC_INTENSITY_NORMAL, 1); \ column_valuator -= column_step; \ /*-- center --*/ \ if (xfact & 1) { \ WRITE_XPART(X_CALC_INTENSITY_CENTER, 0); \ } \ /*-- right half --*/ \ MAKE_PIXEL((x + 1 == pxm->index_xl), othercolumn, ctexel, src[+1]); \ WRITE_XPART(X_CALC_INTENSITY_NORMAL, 1); \ if (DO_Y_STEP) \ line_valuator -= line_step; \ sublinepos += pxm->xl * bytespp; \ alpha_sublinepos += pxm->xl; \ } \ if (DO_Y_STEP) \ line_step = -line_step // End of macro definition void FUNCNAME_LINEAR(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) { int xfact = mode->xfact; int yfact = mode->yfact; int line_step = (yfact < 2) ? 0 : 256 / (yfact & ~1); int column_step = (xfact < 2) ? 0 : 256 / (xfact & ~1); int bytespp = mode->bytespp; byte *src = pxm->index_data; byte *dest = pxm->data; byte *alpha_dest = pxm->alpha_map; int using_alpha = pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE; int separate_alpha_map = (!mode->alpha_mask) && using_alpha; unsigned int masks[4], shifts[4], zero[3]; int x, y; byte inverse_alpha = mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA; zero[0] = 255; zero[1] = zero[2] = 0; if (separate_alpha_map) { masks[3] = 0; shifts[3] = 24; } assert(bytespp == COPY_BYTES); assert(!PALETTE_MODE); masks[0] = mode->red_mask; masks[1] = mode->green_mask; masks[2] = mode->blue_mask; masks[3] = mode->alpha_mask; shifts[0] = mode->red_shift; shifts[1] = mode->green_shift; shifts[2] = mode->blue_shift; shifts[3] = mode->alpha_shift; if (separate_alpha_map && !alpha_dest) alpha_dest = pxm->alpha_map = (byte *)sci_malloc(pxm->index_xl * xfact * pxm->index_yl * yfact); for (y = 0; y < pxm->index_yl; y++) { byte *linepos = dest; byte *alpha_linepos = alpha_dest; for (x = 0; x < pxm->index_xl; x++) { int otherline[4]; // the above line or the line below int ctexel[4]; // Current texel int subx, suby; int line_valuator = line_step ? 128 - (line_step >> 1) : 256; byte *wrpos, *alpha_wrpos; byte *sublinepos = linepos; byte *alpha_sublinepos = alpha_linepos; ctexel[0] = ctexel[1] = ctexel[2] = ctexel[3] = 0; #define MAKE_PIXEL(cond, rec, other, nr) \ if ((cond) || (using_alpha && nr == pxm->color_key)) { \ rec[0] = other[0] - ctexel[0]; \ rec[1] = other[1] - ctexel[1]; \ rec[2] = other[2] - ctexel[2]; \ rec[3] = 0xffff - ctexel[3]; \ } else { \ rec[0] = (EXTEND_COLOR(pxm->colors[nr].r) >> 16) - ctexel[0]; \ rec[1] = (EXTEND_COLOR(pxm->colors[nr].g) >> 16) - ctexel[1]; \ rec[2] = (EXTEND_COLOR(pxm->colors[nr].b) >> 16) - ctexel[2]; \ rec[3] = 0 - ctexel[3]; \ } MAKE_PIXEL(0, ctexel, zero, *src); //-- Upper half -- MAKE_PIXEL((y == 0), otherline, ctexel, src[-pxm->index_xl]); WRITE_YPART(1, Y_CALC_INTENSITY_NORMAL); if (yfact & 1) { WRITE_YPART(0, Y_CALC_INTENSITY_CENTER); } //-- Lower half -- line_valuator -= line_step; MAKE_PIXEL((y + 1 == pxm->index_yl), otherline, ctexel, src[pxm->index_xl]); WRITE_YPART(1, Y_CALC_INTENSITY_NORMAL); src++; linepos += xfact * bytespp; alpha_linepos += xfact; } dest += pxm->xl * yfact * bytespp; alpha_dest += pxm->xl * yfact; } } //---------------------- //** Trilinear filter ** //---------------------- #ifndef GFX_GET_PIXEL_DELTA #define GFX_GET_PIXEL_DELTA static inline void gfx_get_pixel_delta(unsigned int *color, int *delta, unsigned int *pixel0, unsigned int *pixel1) { int j; int transp0 = pixel0[3] == 0xffffff; int transp1 = pixel1[3] == 0xffffff; if (transp0 && !transp1) { // Transparent -> Opaque memset(delta, 0, sizeof(int) * 3); delta[3] = ((pixel1[3] >> 8) - (pixel0[3] >> 8)); memcpy(color, pixel1, sizeof(int) * 3); color[3] = 0xffffff; } else if (!transp0 && transp1) { // Opaque -> Transparent memset(delta, 0, sizeof(int) * 3); delta[3] = ((pixel1[3] >> 8) - (pixel0[3] >> 8)); memcpy(color, pixel0, sizeof(int) * 4); } else if (transp0 && transp1) { // Transparent delta[3] = 0; color[3] = 0xffffff; } else { // Opaque memcpy(color, pixel0, sizeof(int) * 4); for (j = 0; j < 4; j++) delta[j] = ((pixel1[j] >> 8) - (pixel0[j] >> 8)); } } static inline void gfx_apply_delta(unsigned int *color, int *delta, int factor) { int i; for (i = 0; i < 4; i++) color[i] += delta[i] * factor; } #endif #define MAKE_PIXEL_TRILINEAR(cond, rec, nr) \ if (!(cond) || (using_alpha && nr == pxm->color_key)) { \ rec[0] = 0; \ rec[1] = 0; \ rec[2] = 0; \ rec[3] = 0xffffff; \ } else { \ rec[0] = (EXTEND_COLOR(pxm->colors[nr].r) >> 8); \ rec[1] = (EXTEND_COLOR(pxm->colors[nr].g) >> 8); \ rec[2] = (EXTEND_COLOR(pxm->colors[nr].b) >> 8); \ rec[3] = 0; \ } #define REVERSE_ALPHA(foo) ((inverse_alpha) ? ~(foo) : (foo)) void FUNCNAME_TRILINEAR(gfx_mode_t *mode, gfx_pixmap_t *pxm, int scale) { int xfact = mode->xfact; int yfact = mode->yfact; int line_step = (yfact < 2) ? 0 : 256 / yfact; int column_step = (xfact < 2) ? 0 : 256 / xfact; int bytespp = mode->bytespp; byte *src = pxm->index_data; byte *dest = pxm->data; byte *alpha_dest = pxm->alpha_map; int using_alpha = pxm->color_key != GFX_PIXMAP_COLOR_KEY_NONE; int separate_alpha_map = (!mode->alpha_mask) && using_alpha; unsigned int masks[4], shifts[4]; unsigned int pixels[4][4]; // 0 1 // 2 3 int x, y; byte inverse_alpha = mode->flags & GFX_MODE_FLAG_REVERSE_ALPHA; if (separate_alpha_map) { masks[3] = 0; shifts[3] = 24; } assert(bytespp == COPY_BYTES); assert(!PALETTE_MODE); masks[0] = mode->red_mask; masks[1] = mode->green_mask; masks[2] = mode->blue_mask; masks[3] = mode->alpha_mask; shifts[0] = mode->red_shift; shifts[1] = mode->green_shift; shifts[2] = mode->blue_shift; shifts[3] = mode->alpha_shift; if (!(pxm->index_xl && pxm->index_yl)) return; if (separate_alpha_map && !alpha_dest) alpha_dest = pxm->alpha_map = (byte*)sci_malloc(pxm->index_xl * xfact * pxm->index_yl * yfact); src -= pxm->index_xl + 1; for (y = 0; y <= pxm->index_yl; y++) { byte *y_dest_backup = dest; byte *y_alpha_dest_backup = alpha_dest; int y_valuator = (y > 0) ? 0 : 128; int yc_count; if (y == 0) yc_count = yfact >> 1; else if (y == pxm->index_yl) yc_count = (yfact + 1) >> 1; else yc_count = yfact; if (yfact & 1) y_valuator += line_step >> 1; for (x = 0; x <= pxm->index_xl; x++) { byte *x_dest_backup = dest; byte *x_alpha_dest_backup = alpha_dest; int x_valuator = (x > 0) ? 0 : 128; int xc_count; unsigned int leftcolor[4], rightcolor[4]; int leftdelta[4], rightdelta[4]; int xc, yc; if (x == 0) xc_count = xfact >> 1; else if (x == pxm->index_xl) xc_count = (xfact + 1) >> 1; else xc_count = xfact; if (xfact & 1) x_valuator += column_step >> 1; MAKE_PIXEL_TRILINEAR((y && x), pixels[0], *src); MAKE_PIXEL_TRILINEAR((y && (x < pxm->index_xl)), pixels[1], src[1]); MAKE_PIXEL_TRILINEAR(((y < pxm->index_yl) && x), pixels[2], src[pxm->index_xl]); MAKE_PIXEL_TRILINEAR(((y < pxm->index_yl) && (x < pxm->index_xl)), pixels[3], src[pxm->index_xl + 1]); // Optimize Me gfx_get_pixel_delta(leftcolor, leftdelta, pixels[0], pixels[2]); gfx_get_pixel_delta(rightcolor, rightdelta, pixels[1], pixels[3]); gfx_apply_delta(leftcolor, leftdelta, y_valuator); gfx_apply_delta(rightcolor, rightdelta, y_valuator); for (yc = 0; yc < yc_count; yc++) { unsigned int color[4]; int delta[4]; byte *yc_dest_backup = dest; byte *yc_alpha_dest_backup = alpha_dest; gfx_get_pixel_delta(color, delta, leftcolor, rightcolor); gfx_apply_delta(color, delta, x_valuator); for (xc = 0; xc < xc_count; xc++) { SIZETYPE wrcolor; int i; wrcolor = 0; for (i = 0; i < 3; i++) wrcolor |= ((color[i] << 8) >> shifts[i]) & masks[i]; if (separate_alpha_map) { *alpha_dest++ = REVERSE_ALPHA(color[3] >> 16); } else wrcolor |= REVERSE_ALPHA((color[3] << 8) >> shifts[3]) & masks[3]; wrcolor <<= (EXTRA_BYTE_OFFSET * 8); memcpy(dest, &wrcolor, COPY_BYTES); dest += COPY_BYTES; gfx_apply_delta(color, delta, column_step); } gfx_apply_delta(leftcolor, leftdelta, line_step); gfx_apply_delta(rightcolor, rightdelta, line_step); dest = yc_dest_backup + pxm->index_xl * xfact * COPY_BYTES; alpha_dest = yc_alpha_dest_backup + pxm->index_xl * xfact; } dest = x_dest_backup + xc_count * COPY_BYTES; alpha_dest = x_alpha_dest_backup + xc_count; if (x < pxm->index_xl) src++; } dest = y_dest_backup + pxm->index_xl * xfact * yc_count * COPY_BYTES; alpha_dest = y_alpha_dest_backup + pxm->index_xl * xfact * yc_count; } } #undef REVERSE_ALPHA #undef WRITE_YPART #undef Y_CALC_INTENSITY_CENTER #undef Y_CALC_INTENSITY_NORMAL #undef WRITE_XPART #undef X_CALC_INTENSITY_CENTER #undef X_CALC_INTENSITY_NORMAL #undef MAKE_PIXEL_TRILINEAR #undef MAKE_PIXEL #undef FUNCNAME #undef FUNCNAME_LINEAR #undef FUNCNAME_TRILINEAR #undef SIZETYPE #undef EXTEND_COLOR } // End of namespace Sci