/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Code for the scaling algorithm: * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code * is by Willem Monsuwe . Additional modifications are by * (C) Daniel M. Duley. * * Port to Rockbox * Copyright (C) 2007 Jonas Hurrelmann (j@outpo.st) * * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /* * Copyright (C) 2004, 2005 Daniel M. Duley * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* OTHER CREDITS: * * This is the normal smoothscale method, based on Imlib2's smoothscale. * * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's * C algorithm and it ran at about the same speed as my MMX optimized one... * Finally I ported Imlib's MMX version and it ran in less than half the * time as my MMX algorithm, (taking only a quarter of the time Qt does). * After further optimization it seems to run at around 1/6th. * * Changes include formatting, namespaces and other C++'ings, removal of old * #ifdef'ed code, and removal of unneeded border calculation code. * * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code * is by Willem Monsuwe . All other modifications are * (C) Daniel M. Duley. */ #include "bmp.h" #include "lcd.h" void smooth_resize_bitmap(struct bitmap *src_bmp, struct bitmap *dest_bmp) { fb_data *sptr, *dptr; int x, y, end; int val_y = 0, val_x; const int sw = src_bmp->width; const int sh = src_bmp->height; const int dw = dest_bmp->width; const int dh = dest_bmp->height; const int inc_x = (sw << 16) / dw; const int inc_y = (sh << 16) / dh; const int Cp_x = ((dw << 14) / sw) + 1; const int Cp_y = ((dh << 14) / sh) + 1; const int xup_yup = (dw >= sw) + ((dh >= sh) << 1); const int dow = dw; const int sow = sw; fb_data *src = (fb_data*)src_bmp->data; fb_data *dest = (fb_data*)dest_bmp->data; int XAP, YAP, INV_YAP, INV_XAP; int xpoint; fb_data *ypoint; end = dw; /* scaling up both ways */ if (xup_yup == 3) { /* go through every scanline in the output buffer */ for (y = 0; y < dh; y++) { /* calculate the source line we'll scan from */ ypoint = src + ((val_y >> 16) * sw); YAP = ((val_y >> 16) >= (sh - 1)) ? 0 : (val_y >> 8) - ((val_y >> 8) & 0xffffff00); INV_YAP = 256 - YAP; val_y += inc_y; val_x = 0; dptr = dest + (y * dow); sptr = ypoint; if (YAP > 0) { for (x = 0; x < end; x++) { int r = 0, g = 0, b = 0; int rr = 0, gg = 0, bb = 0; fb_data *pix; xpoint = (val_x >> 16); XAP = ((val_x >> 16) >= (sw - 1)) ? 0 : (val_x >> 8) - ((val_x >> 8) & 0xffffff00); INV_XAP = 256 - XAP; val_x += inc_x; if (XAP > 0) { pix = ypoint + xpoint; r = RGB_UNPACK_RED(*pix) * INV_XAP; g = RGB_UNPACK_GREEN(*pix) * INV_XAP; b = RGB_UNPACK_BLUE(*pix) * INV_XAP; pix++; r += RGB_UNPACK_RED(*pix) * XAP; g += RGB_UNPACK_GREEN(*pix) * XAP; b += RGB_UNPACK_BLUE(*pix) * XAP; pix += sow; rr = RGB_UNPACK_RED(*pix) * XAP; gg = RGB_UNPACK_GREEN(*pix) * XAP; bb = RGB_UNPACK_BLUE(*pix) * XAP; pix--; rr += RGB_UNPACK_RED(*pix) * INV_XAP; gg += RGB_UNPACK_GREEN(*pix) * INV_XAP; bb += RGB_UNPACK_BLUE(*pix) * INV_XAP; r = ((rr * YAP) + (r * INV_YAP)) >> 16; g = ((gg * YAP) + (g * INV_YAP)) >> 16; b = ((bb * YAP) + (b * INV_YAP)) >> 16; *dptr++ = LCD_RGBPACK(r, g, b); } else { pix = ypoint + xpoint; r = RGB_UNPACK_RED(*pix) * INV_YAP; g = RGB_UNPACK_GREEN(*pix) * INV_YAP; b = RGB_UNPACK_BLUE(*pix) * INV_YAP; pix += sow; r += RGB_UNPACK_RED(*pix) * YAP; g += RGB_UNPACK_GREEN(*pix) * YAP; b += RGB_UNPACK_BLUE(*pix) * YAP; r >>= 8; g >>= 8; b >>= 8; *dptr++ = LCD_RGBPACK(r, g, b); } } } else { for (x = 0; x < end; x++) { int r = 0, g = 0, b = 0; fb_data *pix; xpoint = (val_x >> 16); XAP = ((val_x >> 16) >= (sw - 1)) ? 0 : (val_x >> 8) - ((val_x >> 8) & 0xffffff00); INV_XAP = 256 - XAP; val_x += inc_x; if (XAP > 0) { pix = ypoint + xpoint; r = RGB_UNPACK_RED(*pix) * INV_XAP; g = RGB_UNPACK_GREEN(*pix) * INV_XAP; b = RGB_UNPACK_BLUE(*pix) * INV_XAP; pix++; r += RGB_UNPACK_RED(*pix) * XAP; g += RGB_UNPACK_GREEN(*pix) * XAP; b += RGB_UNPACK_BLUE(*pix) * XAP; r >>= 8; g >>= 8; b >>= 8; *dptr++ = LCD_RGBPACK(r, g, b); } else *dptr++ = sptr[xpoint]; } } } } /* if we're scaling down vertically */ else if (xup_yup == 1) { /*\ 'Correct' version, with math units prepared for MMXification \ */ int Cy, j; fb_data *pix; int r, g, b, rr, gg, bb; int yap; /* go through every scanline in the output buffer */ for (y = 0; y < dh; y++) { ypoint = src + ((val_y >> 16) * sw); YAP = (((0x100 - ((val_y >> 8) & 0xff)) * Cp_y) >> 8) | (Cp_y << 16); INV_YAP = 256 - YAP; val_y += inc_y; val_x = 0; Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y * dow); for (x = 0; x < end; x++) { xpoint = (val_x >> 16); XAP = ((val_x >> 16) >= (sw - 1)) ? 0 : (val_x >> 8) - ((val_x >> 8) & 0xffffff00); INV_XAP = 256 - XAP; val_x += inc_x; pix = ypoint + xpoint; r = (RGB_UNPACK_RED(*pix) * yap) >> 10; g = (RGB_UNPACK_GREEN(*pix) * yap) >> 10; b = (RGB_UNPACK_BLUE(*pix) * yap) >> 10; pix += sow; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { r += (RGB_UNPACK_RED(*pix) * Cy) >> 10; g += (RGB_UNPACK_GREEN(*pix) * Cy) >> 10; b += (RGB_UNPACK_BLUE(*pix) * Cy) >> 10; pix += sow; } if (j > 0) { r += (RGB_UNPACK_RED(*pix) * j) >> 10; g += (RGB_UNPACK_GREEN(*pix) * j) >> 10; b += (RGB_UNPACK_BLUE(*pix) * j) >> 10; } if (XAP > 0) { pix = ypoint + xpoint + 1; rr = (RGB_UNPACK_RED(*pix) * yap) >> 10; gg = (RGB_UNPACK_GREEN(*pix) * yap) >> 10; bb = (RGB_UNPACK_BLUE(*pix) * yap) >> 10; pix += sow; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { rr += (RGB_UNPACK_RED(*pix) * Cy) >> 10; gg += (RGB_UNPACK_GREEN(*pix) * Cy) >> 10; bb += (RGB_UNPACK_BLUE(*pix) * Cy) >> 10; pix += sow; } if (j > 0) { rr += (RGB_UNPACK_RED(*pix) * j) >> 10; gg += (RGB_UNPACK_GREEN(*pix) * j) >> 10; bb += (RGB_UNPACK_BLUE(*pix) * j) >> 10; } r = r * INV_XAP; g = g * INV_XAP; b = b * INV_XAP; r = (r + ((rr * XAP))) >> 12; g = (g + ((gg * XAP))) >> 12; b = (b + ((bb * XAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; } *dptr = LCD_RGBPACK(r, g, b); dptr++; } } } /* if we're scaling down horizontally */ else if (xup_yup == 2) { /*\ 'Correct' version, with math units prepared for MMXification \ */ int Cx, j; fb_data *pix; int r, g, b, rr, gg, bb; int xap; /* go through every scanline in the output buffer */ for (y = 0; y < dh; y++) { ypoint = src + ((val_y >> 16) * sw); YAP = ((val_y >> 16) >= (sh - 1)) ? 0 : (val_y >> 8) - ((val_y >> 8) & 0xffffff00); INV_YAP = 256 - YAP; val_y += inc_y; val_x = 0; dptr = dest + (y * dow); for (x = 0; x < end; x++) { xpoint = (val_x >> 16); XAP = (((0x100 - ((val_x >> 8) & 0xff)) * Cp_x) >> 8) | (Cp_x << 16); INV_XAP = 256 - XAP; val_x += inc_x; Cx = XAP >> 16; xap = XAP & 0xffff; pix = ypoint + xpoint; r = (RGB_UNPACK_RED(*pix) * xap) >> 10; g = (RGB_UNPACK_GREEN(*pix) * xap) >> 10; b = (RGB_UNPACK_BLUE(*pix) * xap) >> 10; pix++; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { r += (RGB_UNPACK_RED(*pix) * Cx) >> 10; g += (RGB_UNPACK_GREEN(*pix) * Cx) >> 10; b += (RGB_UNPACK_BLUE(*pix) * Cx) >> 10; pix++; } if (j > 0) { r += (RGB_UNPACK_RED(*pix) * j) >> 10; g += (RGB_UNPACK_GREEN(*pix) * j) >> 10; b += (RGB_UNPACK_BLUE(*pix) * j) >> 10; } if (YAP > 0) { pix = ypoint + xpoint + sow; rr = (RGB_UNPACK_RED(*pix) * xap) >> 10; gg = (RGB_UNPACK_GREEN(*pix) * xap) >> 10; bb = (RGB_UNPACK_BLUE(*pix) * xap) >> 10; pix++; for (j = (1 << 14) - xap; j > Cx; j -= Cx) { rr += (RGB_UNPACK_RED(*pix) * Cx) >> 10; gg += (RGB_UNPACK_GREEN(*pix) * Cx) >> 10; bb += (RGB_UNPACK_BLUE(*pix) * Cx) >> 10; pix++; } if (j > 0) { rr += (RGB_UNPACK_RED(*pix) * j) >> 10; gg += (RGB_UNPACK_GREEN(*pix) * j) >> 10; bb += (RGB_UNPACK_BLUE(*pix) * j) >> 10; } r = r * INV_YAP; g = g * INV_YAP; b = b * INV_YAP; r = (r + ((rr * YAP))) >> 12; g = (g + ((gg * YAP))) >> 12; b = (b + ((bb * YAP))) >> 12; } else { r >>= 4; g >>= 4; b >>= 4; } *dptr = LCD_RGBPACK(r, g, b); dptr++; } } } /* fully optimized (i think) - only change of algorithm can help */ /* if we're scaling down horizontally & vertically */ else { /*\ 'Correct' version, with math units prepared for MMXification \ */ int Cx, Cy, i, j; fb_data *pix; int r, g, b, rx, gx, bx; int xap, yap; for (y = 0; y < dh; y++) { ypoint = src + ((val_y >> 16) * sw); YAP = (((0x100 - ((val_y >> 8) & 0xff)) * Cp_y) >> 8) | (Cp_y << 16); INV_YAP = 256 - YAP; val_y += inc_y; val_x = 0; Cy = YAP >> 16; yap = YAP & 0xffff; dptr = dest + (y * dow); for (x = 0; x < end; x++) { xpoint = (val_x >> 16); XAP = (((0x100 - ((val_x >> 8) & 0xff)) * Cp_x) >> 8) | (Cp_x << 16); INV_XAP = 256 - XAP; val_x += inc_x; Cx = XAP >> 16; xap = XAP & 0xffff; sptr = ypoint + xpoint; pix = sptr; sptr += sow; rx = (RGB_UNPACK_RED(*pix) * xap) >> 9; gx = (RGB_UNPACK_GREEN(*pix) * xap) >> 9; bx = (RGB_UNPACK_BLUE(*pix) * xap) >> 9; pix++; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (RGB_UNPACK_RED(*pix) * Cx) >> 9; gx += (RGB_UNPACK_GREEN(*pix) * Cx) >> 9; bx += (RGB_UNPACK_BLUE(*pix) * Cx) >> 9; pix++; } if (i > 0) { rx += (RGB_UNPACK_RED(*pix) * i) >> 9; gx += (RGB_UNPACK_GREEN(*pix) * i) >> 9; bx += (RGB_UNPACK_BLUE(*pix) * i) >> 9; } r = (rx * yap) >> 14; g = (gx * yap) >> 14; b = (bx * yap) >> 14; for (j = (1 << 14) - yap; j > Cy; j -= Cy) { pix = sptr; sptr += sow; rx = (RGB_UNPACK_RED(*pix) * xap) >> 9; gx = (RGB_UNPACK_GREEN(*pix) * xap) >> 9; bx = (RGB_UNPACK_BLUE(*pix) * xap) >> 9; pix++; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (RGB_UNPACK_RED(*pix) * Cx) >> 9; gx += (RGB_UNPACK_GREEN(*pix) * Cx) >> 9; bx += (RGB_UNPACK_BLUE(*pix) * Cx) >> 9; pix++; } if (i > 0) { rx += (RGB_UNPACK_RED(*pix) * i) >> 9; gx += (RGB_UNPACK_GREEN(*pix) * i) >> 9; bx += (RGB_UNPACK_BLUE(*pix) * i) >> 9; } r += (rx * Cy) >> 14; g += (gx * Cy) >> 14; b += (bx * Cy) >> 14; } if (j > 0) { pix = sptr; sptr += sow; rx = (RGB_UNPACK_RED(*pix) * xap) >> 9; gx = (RGB_UNPACK_GREEN(*pix) * xap) >> 9; bx = (RGB_UNPACK_BLUE(*pix) * xap) >> 9; pix++; for (i = (1 << 14) - xap; i > Cx; i -= Cx) { rx += (RGB_UNPACK_RED(*pix) * Cx) >> 9; gx += (RGB_UNPACK_GREEN(*pix) * Cx) >> 9; bx += (RGB_UNPACK_BLUE(*pix) * Cx) >> 9; pix++; } if (i > 0) { rx += (RGB_UNPACK_RED(*pix) * i) >> 9; gx += (RGB_UNPACK_GREEN(*pix) * i) >> 9; bx += (RGB_UNPACK_BLUE(*pix) * i) >> 9; } r += (rx * j) >> 14; g += (gx * j) >> 14; b += (bx * j) >> 14; } *dptr = LCD_RGBPACK(r >> 5, g >> 5, b >> 5); dptr++; } } } }