rockbox/apps/plugins/lib/bmp_smooth_scale.c

447 lines
18 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* 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 <willem@stack.nl>. 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 <willem@stack.nl>. All other modifications are
* (C) Daniel M. Duley.
*/
#include "pluginlib_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++;
}
}
}
}