code cleanup, more descriptive variable names, and algorithm docs for color scalers

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19381 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Andrew Mahone 2008-12-10 12:09:03 +00:00
parent eb04315b19
commit 995c89cb6f

View file

@ -58,21 +58,28 @@
#define DEBUGF(...) #define DEBUGF(...)
#endif #endif
#ifdef HAVE_LCD_COLOR /* All of these scalers use variations of Bresenham's algorithm to convert from
#define PACKRED(r, delta) ((31 * r + (r >> 3) + delta) >> 8) their input to output coordinates. The color scalers have the error value
#define PACKGREEN(g, delta) ((63 * g + (g >> 2) + delta) >> 8) shifted so that it is a useful input to the scaling algorithm.
#define PACKBLUE(b, delta) ((31 * b + (b >> 3) + delta) >> 8) */
#ifdef HAVE_LCD_COLOR
/* dither + pack on channel of RGB565, R an B share a packing macro */
#define PACKRB(v, delta) ((31 * v + (v >> 3) + delta) >> 8)
#define PACKG(g, delta) ((63 * g + (g >> 2) + delta) >> 8)
/* read new img_part unconditionally, return false on failure */
#define FILL_BUF_INIT(img_part, store_part, args) { \ #define FILL_BUF_INIT(img_part, store_part, args) { \
part = store_part(args); \ img_part = store_part(args); \
if (part == NULL) \ if (img_part == NULL) \
return false; \ return false; \
} }
/* read new img_part if current one is empty, return false on failure */
#define FILL_BUF(img_part, store_part, args) { \ #define FILL_BUF(img_part, store_part, args) { \
if (part->len == 0) \ if (img_part->len == 0) \
part = store_part(args); \ img_part = store_part(args); \
if (part == NULL) \ if (img_part == NULL) \
return false; \ return false; \
} }
@ -92,10 +99,12 @@ struct scaler_context {
void *args; void *args;
}; };
/* Set up rounding and scale factors for horizontal area scaler */
static void scale_h_area_setup(struct bitmap *bm, struct dim *src, static void scale_h_area_setup(struct bitmap *bm, struct dim *src,
struct scaler_context *ctx) struct scaler_context *ctx)
{ {
(void) bm; (void) bm;
/* sum is output value * src->width */
ctx->divmul = ((src->width - 1 + 0x80000000U) / src->width) << 1; ctx->divmul = ((src->width - 1 + 0x80000000U) / src->width) << 1;
ctx->round = (src->width + 1) >> 1; ctx->round = (src->width + 1) >> 1;
} }
@ -107,61 +116,69 @@ static bool scale_h_area(struct bitmap *bm, struct dim *src,
{ {
SDEBUGF("scale_h_area\n"); SDEBUGF("scale_h_area\n");
unsigned int ix, ox, oxe, mul; unsigned int ix, ox, oxe, mul;
struct uint32_rgb rgbval1, rgbval2; struct uint32_rgb rgbvalacc = { 0, 0, 0 },
rgbvaltmp = { 0, 0, 0 };
struct img_part *part; struct img_part *part;
FILL_BUF_INIT(part,ctx->store_part,ctx->args); FILL_BUF_INIT(part,ctx->store_part,ctx->args);
ox = 0; ox = 0;
oxe = 0; oxe = 0;
rgbval1.r = 0;
rgbval1.g = 0;
rgbval1.b = 0;
rgbval2.r = 0;
rgbval2.g = 0;
rgbval2.b = 0;
mul = 0; mul = 0;
for (ix = 0; ix < (unsigned int)src->width; ix++) for (ix = 0; ix < (unsigned int)src->width; ix++)
{ {
oxe += bm->width; oxe += bm->width;
/* end of current area has been reached */
if (oxe >= (unsigned int)src->width) if (oxe >= (unsigned int)src->width)
{ {
/* yield if we haven't since last tick */
if (ctx->last_tick != current_tick) if (ctx->last_tick != current_tick)
{ {
yield(); yield();
ctx->last_tick = current_tick; ctx->last_tick = current_tick;
} }
/* "reset" error, which now represents partial coverage of next
pixel by the next area
*/
oxe -= src->width; oxe -= src->width;
rgbval1.r = rgbval1.r * bm->width + rgbval2.r * mul; /* add saved partial pixel from start of area */
rgbval1.g = rgbval1.g * bm->width + rgbval2.g * mul; rgbvalacc.r = rgbvalacc.r * bm->width + rgbvaltmp.r * mul;
rgbval1.b = rgbval1.b * bm->width + rgbval2.b * mul; rgbvalacc.g = rgbvalacc.g * bm->width + rgbvaltmp.g * mul;
rgbvalacc.b = rgbvalacc.b * bm->width + rgbvaltmp.b * mul;
/* fill buffer if needed */
FILL_BUF(part,ctx->store_part,ctx->args); FILL_BUF(part,ctx->store_part,ctx->args);
rgbval2.r = part->buf->red; /* get new pixel , then add its partial coverage to this area */
rgbval2.g = part->buf->green; rgbvaltmp.r = part->buf->red;
rgbval2.b = part->buf->blue; rgbvaltmp.g = part->buf->green;
rgbvaltmp.b = part->buf->blue;
part->buf++; part->buf++;
part->len--; part->len--;
mul = bm->width - oxe; mul = bm->width - oxe;
rgbval1.r += rgbval2.r * mul; rgbvalacc.r += rgbvaltmp.r * mul;
rgbval1.g += rgbval2.g * mul; rgbvalacc.g += rgbvaltmp.g * mul;
rgbval1.b += rgbval2.b * mul; rgbvalacc.b += rgbvaltmp.b * mul;
/* round, divide, and either store or accumulate to output row */
out_line[ox].r = (accum ? out_line[ox].r : 0) + out_line[ox].r = (accum ? out_line[ox].r : 0) +
((rgbval1.r + ctx->round) * ((rgbvalacc.r + ctx->round) *
(uint64_t)ctx->divmul >> 32); (uint64_t)ctx->divmul >> 32);
out_line[ox].g = (accum ? out_line[ox].g : 0) + out_line[ox].g = (accum ? out_line[ox].g : 0) +
((rgbval1.g + ctx->round) * ((rgbvalacc.g + ctx->round) *
(uint64_t)ctx->divmul >> 32); (uint64_t)ctx->divmul >> 32);
out_line[ox].b = (accum ? out_line[ox].b : 0) + out_line[ox].b = (accum ? out_line[ox].b : 0) +
((rgbval1.b + ctx->round) * ((rgbvalacc.b + ctx->round) *
(uint64_t)ctx->divmul >> 32); (uint64_t)ctx->divmul >> 32);
rgbval1.r = 0; /* reset accumulator */
rgbval1.g = 0; rgbvalacc.r = 0;
rgbval1.b = 0; rgbvalacc.g = 0;
rgbvalacc.b = 0;
mul = bm->width - mul; mul = bm->width - mul;
ox += 1; ox += 1;
/* inside an area */
} else { } else {
/* fill buffer if needed */
FILL_BUF(part,ctx->store_part,ctx->args); FILL_BUF(part,ctx->store_part,ctx->args);
rgbval1.r += part->buf->red; /* add pixel value to accumulator */
rgbval1.g += part->buf->green; rgbvalacc.r += part->buf->red;
rgbval1.b += part->buf->blue; rgbvalacc.g += part->buf->green;
rgbvalacc.b += part->buf->blue;
part->buf++; part->buf++;
part->len--; part->len--;
} }
@ -180,63 +197,80 @@ static bool scale_v_area(struct bitmap *bm, bool dither, struct dim *src,
uint32_t mul, divmul, x, oy, iy, oye, round; uint32_t mul, divmul, x, oy, iy, oye, round;
int delta = 127, r, g, b; int delta = 127, r, g, b;
fb_data *row, *pix; fb_data *row, *pix;
/* Set up rounding and scale factors */
divmul = ((src->height - 1 + 0x80000000U) / src->height) << 1; divmul = ((src->height - 1 + 0x80000000U) / src->height) << 1;
round = (src->height + 1) >> 1; round = (src->height + 1) >> 1;
mul = 0; mul = 0;
oy = 0; oy = 0;
oye = 0; oye = 0;
struct uint32_rgb *crow1 = (struct uint32_rgb *)(ctx->buf), struct uint32_rgb *rowacc = (struct uint32_rgb *)(ctx->buf),
*crow2 = crow1 + bm->width; *rowtmp = rowacc + bm->width;
SDEBUGF("scale_v_area\n"); SDEBUGF("scale_v_area\n");
/* zero the accumulator and temp rows */
memset((void *)ctx->buf, 0, bm->width * 2 * sizeof(struct uint32_rgb)); memset((void *)ctx->buf, 0, bm->width * 2 * sizeof(struct uint32_rgb));
row = (fb_data *)(bm->data) + bm->width * row = (fb_data *)(bm->data) + bm->width * rset->rowstart;
(rset->rowstep == -1 ? bm->height - 1 : 0);
for (iy = 0; iy < (unsigned int)src->height; iy++) for (iy = 0; iy < (unsigned int)src->height; iy++)
{ {
oye += bm->height; oye += bm->height;
/* end of current area has been reached */
if (oye >= (unsigned int)src->height) if (oye >= (unsigned int)src->height)
{ {
/* "reset" error, which now represents partial coverage of the next
row by the next area
*/
oye -= src->height; oye -= src->height;
/* add stored partial row to accumulator */
for (x = 0; x < 3 *(unsigned int)bm->width; x++) for (x = 0; x < 3 *(unsigned int)bm->width; x++)
((uint32_t*)crow1)[x] = ((uint32_t*)crow1)[x] * ((uint32_t*)rowacc)[x] = ((uint32_t*)rowacc)[x] *
bm->height + mul * bm->height + mul *
((uint32_t*)crow2)[x]; ((uint32_t*)rowtmp)[x];
if(!h_scaler(bm, src, crow2, ctx, false)) /* store new scaled row in temp row */
goto fail; if(!h_scaler(bm, src, rowtmp, ctx, false))
return false;
/* add partial coverage by new row to this area, then round and
scale to final value
*/
mul = bm->height - oye; mul = bm->height - oye;
for (x = 0; x < 3 *(unsigned int)bm->width; x++) for (x = 0; x < 3 *(unsigned int)bm->width; x++)
{ {
((uint32_t*)crow1)[x] += mul * ((uint32_t*)crow2)[x]; ((uint32_t*)rowacc)[x] += mul * ((uint32_t*)rowtmp)[x];
((uint32_t*)crow1)[x] = (round + ((uint32_t*)rowacc)[x] = (round +
((uint32_t*)crow1)[x]) * ((uint32_t*)rowacc)[x]) *
(uint64_t)divmul >> 32; (uint64_t)divmul >> 32;
} }
/* convert to RGB565 in output bitmap */
pix = row; pix = row;
for (x = 0; x < (unsigned int)bm->width; x++) for (x = 0; x < (unsigned int)bm->width; x++)
{ {
if (dither) if (dither)
delta = dither_mat(x & 0xf, oy & 0xf); delta = dither_mat(x & 0xf, oy & 0xf);
r = PACKRED(crow1[x].r,delta); r = PACKRB(rowacc[x].r,delta);
g = PACKGREEN(crow1[x].g,delta); g = PACKG(rowacc[x].g,delta);
b = PACKBLUE(crow1[x].b,delta); b = PACKRB(rowacc[x].b,delta);
*pix++ = LCD_RGBPACK_LCD(r, g, b); *pix++ = LCD_RGBPACK_LCD(r, g, b);
} }
memset((void *)crow1, 0, bm->width * sizeof(struct uint32_rgb)); /* clear accumulator row, store partial coverage for next row */
memset((void *)rowacc, 0, bm->width * sizeof(struct uint32_rgb));
mul = oye; mul = oye;
row += bm->width * rset->rowstep; row += bm->width * rset->rowstep;
oy += 1; oy += 1;
/* inside an area */
} else { } else {
if (!h_scaler(bm, src, crow1, ctx, true)) /* accumulate new scaled row to rowacc */
goto fail; if (!h_scaler(bm, src, rowacc, ctx, true))
return false;
} }
} }
return true; return true;
fail:
return false;
} }
#ifdef HAVE_UPSCALER #ifdef HAVE_UPSCALER
/* Set up rounding and scale factors for the horizontal scaler. The divisor
is bm->width - 1, so that the first and last pixels in the row align
exactly between input and output
*/
static void scale_h_linear_setup(struct bitmap *bm, struct dim *src, static void scale_h_linear_setup(struct bitmap *bm, struct dim *src,
struct scaler_context *ctx) struct scaler_context *ctx)
{ {
@ -251,20 +285,29 @@ static bool scale_h_linear(struct bitmap *bm, struct dim *src,
struct scaler_context *ctx, bool accum) struct scaler_context *ctx, bool accum)
{ {
unsigned int ix, ox, ixe; unsigned int ix, ox, ixe;
/* type x = x is an ugly hack for hiding an unitialized data warning. The
values are conditionally initialized before use, but other values are
set such that this will occur before these are used.
*/
struct uint32_rgb rgbval=rgbval, rgbinc=rgbinc; struct uint32_rgb rgbval=rgbval, rgbinc=rgbinc;
struct img_part *part; struct img_part *part;
SDEBUGF("scale_h_linear\n"); SDEBUGF("scale_h_linear\n");
FILL_BUF_INIT(part,ctx->store_part,ctx->args); FILL_BUF_INIT(part,ctx->store_part,ctx->args);
ix = 0; ix = 0;
/* The error is set so that values are initialized on the first pass. */
ixe = bm->width - 1; ixe = bm->width - 1;
for (ox = 0; ox < (uint32_t)bm->width; ox++) { for (ox = 0; ox < (uint32_t)bm->width; ox++) {
if (ixe >= ((uint32_t)bm->width - 1)) if (ixe >= ((uint32_t)bm->width - 1))
{ {
/* yield once each tick */
if (ctx->last_tick != current_tick) if (ctx->last_tick != current_tick)
{ {
yield(); yield();
ctx->last_tick = current_tick; ctx->last_tick = current_tick;
} }
/* Store the new "current" pixel value in rgbval, and the color
step value in rgbinc.
*/
ixe -= (bm->width - 1); ixe -= (bm->width - 1);
rgbinc.r = -(part->buf->red); rgbinc.r = -(part->buf->red);
rgbinc.g = -(part->buf->green); rgbinc.g = -(part->buf->green);
@ -273,21 +316,28 @@ static bool scale_h_linear(struct bitmap *bm, struct dim *src,
rgbval.g = (part->buf->green) * (bm->width - 1); rgbval.g = (part->buf->green) * (bm->width - 1);
rgbval.b = (part->buf->blue) * (bm->width - 1); rgbval.b = (part->buf->blue) * (bm->width - 1);
ix += 1; ix += 1;
/* If this wasn't the last pixel, add the next one to rgbinc. */
if (ix < (uint32_t)src->width) { if (ix < (uint32_t)src->width) {
part->buf++; part->buf++;
part->len--; part->len--;
/* Fetch new pixels if needed */
FILL_BUF(part,ctx->store_part,ctx->args); FILL_BUF(part,ctx->store_part,ctx->args);
rgbinc.r += part->buf->red; rgbinc.r += part->buf->red;
rgbinc.g += part->buf->green; rgbinc.g += part->buf->green;
rgbinc.b += part->buf->blue; rgbinc.b += part->buf->blue;
/* Add a partial step to rgbval, in this pixel isn't precisely
aligned with the new source pixel
*/
rgbval.r += rgbinc.r * ixe; rgbval.r += rgbinc.r * ixe;
rgbval.g += rgbinc.g * ixe; rgbval.g += rgbinc.g * ixe;
rgbval.b += rgbinc.b * ixe; rgbval.b += rgbinc.b * ixe;
} }
/* Now multiple the color increment to its proper value */
rgbinc.r *= src->width - 1; rgbinc.r *= src->width - 1;
rgbinc.g *= src->width - 1; rgbinc.g *= src->width - 1;
rgbinc.b *= src->width - 1; rgbinc.b *= src->width - 1;
} }
/* round and scale values, and accumulate or store to output */
out_line[ox].r = (accum ? out_line[ox].r : 0) + out_line[ox].r = (accum ? out_line[ox].r : 0) +
((rgbval.r + ctx->round) * ((rgbval.r + ctx->round) *
(uint64_t)ctx->divmul >> 32); (uint64_t)ctx->divmul >> 32);
@ -317,24 +367,29 @@ static bool scale_v_linear(struct bitmap *bm, bool dither, struct dim *src,
int delta = 127; int delta = 127;
struct uint32_rgb p; struct uint32_rgb p;
fb_data *row, *pix; fb_data *row, *pix;
/* Set up scale and rounding factors, the divisor is bm->height - 1 */
divmul = ((bm->height - 2 + 0x80000000U) / (bm->height - 1)) << 1; divmul = ((bm->height - 2 + 0x80000000U) / (bm->height - 1)) << 1;
round = bm->height >> 1; round = bm->height >> 1;
mul = 0; mul = 0;
iy = 0; iy = 0;
iye = bm->height - 1; iye = bm->height - 1;
/* Set up our two temp buffers. The names are generic because they'll be
swapped each time a new input row is read
*/
struct uint32_rgb *crow1 = (struct uint32_rgb *)(ctx->buf), struct uint32_rgb *crow1 = (struct uint32_rgb *)(ctx->buf),
*crow2 = crow1 + bm->width, *crow2 = crow1 + bm->width,
*t; *t;
SDEBUGF("scale_v_linear\n"); SDEBUGF("scale_v_linear\n");
row = (fb_data *)(bm->data) + bm->width * row = (fb_data *)(bm->data) + bm->width * rset->rowstart;
(rset->rowstep == -1 ? bm->height - 1 : 0); /* get first scaled row in crow2 */
if(!h_scaler(bm, src, crow2, ctx, false)) if(!h_scaler(bm, src, crow2, ctx, false))
goto fail; return false;
for (oy = 0; oy < (uint32_t)bm->height; oy++) for (oy = 0; oy < (uint32_t)bm->height; oy++)
{ {
if (iye >= (uint32_t)bm->height - 1) if (iye >= (uint32_t)bm->height - 1)
{ {
/* swap temp rows, then read another row into crow2 */
t = crow2; t = crow2;
crow2 = crow1; crow2 = crow1;
crow1 = t; crow1 = t;
@ -343,35 +398,43 @@ static bool scale_v_linear(struct bitmap *bm, bool dither, struct dim *src,
if (iy < (uint32_t)src->height) if (iy < (uint32_t)src->height)
{ {
if (!h_scaler(bm, src, crow2, ctx, false)) if (!h_scaler(bm, src, crow2, ctx, false))
goto fail; return false;
} }
} }
pix = row; pix = row;
for (x = 0; x < (uint32_t)bm->width; x++) for (x = 0; x < (uint32_t)bm->width; x++)
{ {
/* iye and bm-height - 1 - iye represent the contribution of each
row to the output. Calculate their weighted sum, then round and
scale it.
*/
p.r = (crow1[x].r * (bm->height - 1 - iye) + p.r = (crow1[x].r * (bm->height - 1 - iye) +
crow2[x].r * iye + round) * (uint64_t)divmul >> 32; crow2[x].r * iye + round) * (uint64_t)divmul >> 32;
p.g = (crow1[x].g * (bm->height - 1 - iye) + p.g = (crow1[x].g * (bm->height - 1 - iye) +
crow2[x].g * iye + round) * (uint64_t)divmul >> 32; crow2[x].g * iye + round) * (uint64_t)divmul >> 32;
p.b = (crow1[x].b * (bm->height - 1 - iye) + p.b = (crow1[x].b * (bm->height - 1 - iye) +
crow2[x].b * iye + round) * (uint64_t)divmul >> 32; crow2[x].b * iye + round) * (uint64_t)divmul >> 32;
/* dither and pack pixels to output */
if (dither) if (dither)
delta = dither_mat(x & 0xf, oy & 0xf); delta = dither_mat(x & 0xf, oy & 0xf);
p.r = PACKRED(p.r,delta); p.r = PACKRB(p.r,delta);
p.g = PACKGREEN(p.g,delta); p.g = PACKG(p.g,delta);
p.b = PACKBLUE(p.b,delta); p.b = PACKRB(p.b,delta);
*pix++ = LCD_RGBPACK_LCD(p.r, p.g, p.b); *pix++ = LCD_RGBPACK_LCD(p.r, p.g, p.b);
} }
row += bm->width * rset->rowstep; row += bm->width * rset->rowstep;
iye += src->height - 1; iye += src->height - 1;
} }
return true; return true;
fail:
return false;
} }
#endif /* HAVE_UPSCALER */ #endif /* HAVE_UPSCALER */
#endif /* HAVE_LCD_COLOR */ #endif /* HAVE_LCD_COLOR */
/* docs for this are still TODO, but it's Bresenham's again, used to skip or
repeat input pixels, and with the *ls values being used for "long steps"
that skip all the way, or nearly all the way, to the next transition of
the associated value.
*/
#if LCD_DEPTH < 8 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH < 8) #if LCD_DEPTH < 8 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH < 8)
/* nearest-neighbor up/down/non-scaler */ /* nearest-neighbor up/down/non-scaler */
static inline bool scale_nearest(struct bitmap *bm, static inline bool scale_nearest(struct bitmap *bm,