| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2008 by Akio Idehara, Andrew Mahone |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /* |
| * Implementation of area average and linear row and vertical scalers, and |
| * nearest-neighbor grey scaler (C) 2008 Andrew Mahone |
| * |
| * All files in this archive are subject to the GNU General Public License. |
| * See the file COPYING in the source tree root for full license agreement. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| |
| #include "general.h" |
| #include "kernel.h" |
| #include "system.h" |
| #ifndef PLUGIN |
| #include "debug.h" |
| #endif |
| #include "lcd.h" |
| #include "file.h" |
| #ifdef HAVE_REMOTE_LCD |
| #include "lcd-remote.h" |
| #endif |
| #ifdef ROCKBOX_DEBUG_SCALERS |
| #define SDEBUGF DEBUGF |
| #else |
| #define SDEBUGF(...) |
| #endif |
| #ifndef __PCTOOL__ |
| #include "config.h" |
| #include "system.h" |
| #include <bmp.h> |
| #include "resize.h" |
| #else |
| #undef DEBUGF |
| #define DEBUGF(...) |
| #endif |
| #include <jpeg_load.h> |
| |
| #if CONFIG_CPU == SH7034 |
| /* 16*16->32 bit multiplication is a single instrcution on the SH1 */ |
| #define MULUQ(a, b) ((uint32_t) (((uint16_t) (a)) * ((uint16_t) (b)))) |
| #define MULQ(a, b) ((int32_t) (((int16_t) (a)) * ((int16_t) (b)))) |
| #else |
| #define MULUQ(a, b) ((a) * (b)) |
| #define MULQ(a, b) ((a) * (b)) |
| #endif |
| |
| #ifdef HAVE_LCD_COLOR |
| #define CHANNEL_BYTES (sizeof(struct uint32_argb)/sizeof(uint32_t)) |
| #else |
| #define CHANNEL_BYTES (sizeof(uint32_t)/sizeof(uint32_t)) /* packed */ |
| #endif |
| |
| /* calculate the maximum dimensions which will preserve the aspect ration of |
| src while fitting in the constraints passed in dst, and store result in dst, |
| returning 0 if rounding and 1 if not rounding. |
| */ |
| int recalc_dimension(struct dim *dst, struct dim *src) |
| { |
| /* This only looks backwards. The input image size is being pre-scaled by |
| * the inverse of the pixel aspect ratio, so that once the size it scaled |
| * to meet the output constraints, the scaled image will have appropriate |
| * proportions. |
| */ |
| int sw = src->width * LCD_PIXEL_ASPECT_HEIGHT; |
| int sh = src->height * LCD_PIXEL_ASPECT_WIDTH; |
| int tmp; |
| if (dst->width <= 0) |
| dst->width = LCD_WIDTH; |
| if (dst->height <= 0) |
| dst->height = LCD_HEIGHT; |
| #ifndef HAVE_UPSCALER |
| if (dst->width > sw || dst->height > sh) |
| { |
| dst->width = sw; |
| dst->height = sh; |
| } |
| if (sw == dst->width && sh == dst->height) |
| return 1; |
| #endif |
| tmp = (sw * dst->height + (sh >> 1)) / sh; |
| if (tmp > dst->width) |
| dst->height = (sh * dst->width + (sw >> 1)) / sw; |
| else |
| dst->width = tmp; |
| return src->width == dst->width && src->height == dst->height; |
| } |
| |
| /* All of these scalers use variations of Bresenham's algorithm to convert from |
| their input to output coordinates. The error value is shifted from the |
| "classic" version such that it is a useful input to the scaling calculation. |
| */ |
| |
| #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) |
| #endif |
| |
| /* read new img_part unconditionally, return false on failure */ |
| #define FILL_BUF_INIT(img_part, store_part, args) { \ |
| img_part = store_part(args); \ |
| if (img_part == NULL) \ |
| return false; \ |
| } |
| |
| /* read new img_part if current one is empty, return false on failure */ |
| #define FILL_BUF(img_part, store_part, args) { \ |
| if (img_part->len == 0) \ |
| img_part = store_part(args); \ |
| if (img_part == NULL) \ |
| return false; \ |
| } |
| |
| #if defined(CPU_COLDFIRE) |
| #define MAC(op1, op2, num) \ |
| asm volatile( \ |
| "mac.l %0, %1, %%acc" #num \ |
| : \ |
| : "%d" (op1), "d" (op2)\ |
| ) |
| #define MAC_OUT(dest, num) \ |
| asm volatile( \ |
| "movclr.l %%acc" #num ", %0" \ |
| : "=d" (dest) \ |
| ) |
| #elif defined(CPU_SH) |
| /* calculate the 32-bit product of unsigned 16-bit op1 and op2 */ |
| static inline int32_t mul_s16_s16(int16_t op1, int16_t op2) |
| { |
| return (int32_t)(op1 * op2); |
| } |
| |
| /* calculate the 32-bit product of signed 16-bit op1 and op2 */ |
| static inline uint32_t mul_u16_u16(uint16_t op1, uint16_t op2) |
| { |
| return (uint32_t)(op1 * op2); |
| } |
| #endif |
| |
| /* horizontal area average scaler */ |
| static bool scale_h_area(void *out_line_ptr, |
| struct scaler_context *ctx, bool accum) |
| { |
| SDEBUGF("scale_h_area\n"); |
| unsigned int ix, ox, oxe, mul; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| const uint32_t h_i_val = ctx->src->width, |
| h_o_val = ctx->bm->width; |
| #else |
| const uint32_t h_i_val = ctx->h_i_val, |
| h_o_val = ctx->h_o_val; |
| #endif |
| #ifdef HAVE_LCD_COLOR |
| struct uint32_argb rgbvalacc = { 0, 0, 0, 0 }, |
| rgbvaltmp = { 0, 0, 0, 0 }, |
| *out_line = (struct uint32_argb *)out_line_ptr; |
| #else |
| uint32_t acc = 0, tmp = 0, *out_line = (uint32_t*)out_line_ptr; |
| #endif |
| struct img_part *part; |
| FILL_BUF_INIT(part,ctx->store_part,ctx->args); |
| ox = 0; |
| oxe = 0; |
| mul = 0; |
| /* give other tasks a chance to run */ |
| yield(); |
| for (ix = 0; ix < (unsigned int)ctx->src->width; ix++) |
| { |
| oxe += h_o_val; |
| /* end of current area has been reached */ |
| /* fill buffer if needed */ |
| FILL_BUF(part,ctx->store_part,ctx->args); |
| #ifdef HAVE_LCD_COLOR |
| if (oxe >= h_i_val) |
| { |
| /* "reset" error, which now represents partial coverage of next |
| pixel by the next area |
| */ |
| oxe -= h_i_val; |
| |
| #if defined(CPU_COLDFIRE) |
| /* Coldfire EMAC math */ |
| /* add saved partial pixel from start of area */ |
| MAC(rgbvalacc.r, h_o_val, 0); |
| MAC(rgbvalacc.g, h_o_val, 1); |
| MAC(rgbvalacc.b, h_o_val, 2); |
| MAC(rgbvalacc.a, h_o_val, 3); |
| MAC(rgbvaltmp.r, mul, 0); |
| MAC(rgbvaltmp.g, mul, 1); |
| MAC(rgbvaltmp.b, mul, 2); |
| MAC(rgbvaltmp.a, mul, 3); |
| /* get new pixel , then add its partial coverage to this area */ |
| mul = h_o_val - oxe; |
| rgbvaltmp.r = part->buf->red; |
| rgbvaltmp.g = part->buf->green; |
| rgbvaltmp.b = part->buf->blue; |
| rgbvaltmp.a = part->buf->alpha; |
| MAC(rgbvaltmp.r, mul, 0); |
| MAC(rgbvaltmp.g, mul, 1); |
| MAC(rgbvaltmp.b, mul, 2); |
| MAC(rgbvaltmp.a, mul, 3); |
| MAC_OUT(rgbvalacc.r, 0); |
| MAC_OUT(rgbvalacc.g, 1); |
| MAC_OUT(rgbvalacc.b, 2); |
| MAC_OUT(rgbvalacc.a, 3); |
| #else |
| /* generic C math */ |
| /* add saved partial pixel from start of area */ |
| rgbvalacc.r = rgbvalacc.r * h_o_val + rgbvaltmp.r * mul; |
| rgbvalacc.g = rgbvalacc.g * h_o_val + rgbvaltmp.g * mul; |
| rgbvalacc.b = rgbvalacc.b * h_o_val + rgbvaltmp.b * mul; |
| rgbvalacc.a = rgbvalacc.a * h_o_val + rgbvaltmp.a * mul; |
| |
| /* get new pixel , then add its partial coverage to this area */ |
| rgbvaltmp.r = part->buf->red; |
| rgbvaltmp.g = part->buf->green; |
| rgbvaltmp.b = part->buf->blue; |
| rgbvaltmp.a = part->buf->alpha; |
| mul = h_o_val - oxe; |
| rgbvalacc.r += rgbvaltmp.r * mul; |
| rgbvalacc.g += rgbvaltmp.g * mul; |
| rgbvalacc.b += rgbvaltmp.b * mul; |
| rgbvalacc.a += rgbvaltmp.a * mul; |
| #endif /* CPU */ |
| rgbvalacc.r = (rgbvalacc.r + (1 << 21)) >> 22; |
| rgbvalacc.g = (rgbvalacc.g + (1 << 21)) >> 22; |
| rgbvalacc.b = (rgbvalacc.b + (1 << 21)) >> 22; |
| rgbvalacc.a = (rgbvalacc.a + (1 << 21)) >> 22; |
| /* store or accumulate to output row */ |
| if (accum) |
| { |
| rgbvalacc.r += out_line[ox].r; |
| rgbvalacc.g += out_line[ox].g; |
| rgbvalacc.b += out_line[ox].b; |
| rgbvalacc.a += out_line[ox].a; |
| } |
| out_line[ox].r = rgbvalacc.r; |
| out_line[ox].g = rgbvalacc.g; |
| out_line[ox].b = rgbvalacc.b; |
| out_line[ox].a = rgbvalacc.a; |
| /* reset accumulator */ |
| rgbvalacc.r = 0; |
| rgbvalacc.g = 0; |
| rgbvalacc.b = 0; |
| rgbvalacc.a = 0; |
| mul = oxe; |
| ox += 1; |
| /* inside an area */ |
| } else { |
| /* add pixel value to accumulator */ |
| rgbvalacc.r += part->buf->red; |
| rgbvalacc.g += part->buf->green; |
| rgbvalacc.b += part->buf->blue; |
| rgbvalacc.a += part->buf->alpha; |
| } |
| #else |
| if (oxe >= h_i_val) |
| { |
| /* "reset" error, which now represents partial coverage of next |
| pixel by the next area |
| */ |
| oxe -= h_i_val; |
| #if defined(CPU_COLDFIRE) |
| /* Coldfire EMAC math */ |
| /* add saved partial pixel from start of area */ |
| MAC(acc, h_o_val, 0); |
| MAC(tmp, mul, 0); |
| /* get new pixel , then add its partial coverage to this area */ |
| tmp = *(part->buf); |
| mul = h_o_val - oxe; |
| MAC(tmp, mul, 0); |
| MAC_OUT(acc, 0); |
| #elif defined(CPU_SH) |
| /* SH-1 16x16->32 math */ |
| /* add saved partial pixel from start of area */ |
| acc = mul_u16_u16(acc, h_o_val) + mul_u16_u16(tmp, mul); |
| |
| /* get new pixel , then add its partial coverage to this area */ |
| tmp = *(part->buf); |
| mul = h_o_val - oxe; |
| acc += mul_u16_u16(tmp, mul); |
| #else |
| /* generic C math */ |
| /* add saved partial pixel from start of area */ |
| acc = (acc * h_o_val) + (tmp * mul); |
| |
| /* get new pixel , then add its partial coverage to this area */ |
| tmp = *(part->buf); |
| mul = h_o_val - oxe; |
| acc += tmp * mul; |
| #endif /* CPU */ |
| #if !(defined(CPU_SH) || defined(TEST_SH_MATH)) |
| /* round, divide, and either store or accumulate to output row */ |
| acc = (acc + (1 << 21)) >> 22; |
| #endif |
| if (accum) |
| { |
| acc += out_line[ox]; |
| } |
| out_line[ox] = acc; |
| /* reset accumulator */ |
| acc = 0; |
| mul = oxe; |
| ox += 1; |
| /* inside an area */ |
| } else { |
| /* add pixel value to accumulator */ |
| acc += *(part->buf); |
| } |
| #endif |
| part->buf++; |
| part->len--; |
| } |
| return true; |
| } |
| |
| /* vertical area average scaler */ |
| static inline bool scale_v_area(struct rowset *rset, struct scaler_context *ctx) |
| { |
| uint32_t mul, oy, iy, oye; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| const uint32_t v_i_val = ctx->src->height, |
| v_o_val = ctx->bm->height; |
| #else |
| const uint32_t v_i_val = ctx->v_i_val, |
| v_o_val = ctx->v_o_val; |
| #endif |
| |
| /* Set up rounding and scale factors */ |
| mul = 0; |
| oy = rset->rowstart; |
| oye = 0; |
| uint32_t *rowacc = (uint32_t *) ctx->buf, |
| *rowtmp = rowacc + ctx->bm->width * CHANNEL_BYTES, |
| *rowacc_px, *rowtmp_px; |
| memset((void *)ctx->buf, 0, ctx->bm->width * 2 * sizeof(uint32_t)*CHANNEL_BYTES); |
| SDEBUGF("scale_v_area\n"); |
| /* zero the accumulator and temp rows */ |
| for (iy = 0; iy < (unsigned int)ctx->src->height; iy++) |
| { |
| oye += v_o_val; |
| /* end of current area has been reached */ |
| if (oye >= v_i_val) |
| { |
| /* "reset" error, which now represents partial coverage of the next |
| row by the next area |
| */ |
| oye -= v_i_val; |
| /* add stored partial row to accumulator */ |
| for(rowacc_px = rowacc, rowtmp_px = rowtmp; rowacc_px != rowtmp; |
| rowacc_px++, rowtmp_px++) |
| *rowacc_px = *rowacc_px * v_o_val + *rowtmp_px * mul; |
| /* store new scaled row in temp row */ |
| if(!ctx->h_scaler(rowtmp, ctx, false)) |
| return false; |
| /* add partial coverage by new row to this area, then round and |
| scale to final value |
| */ |
| mul = v_o_val - oye; |
| for(rowacc_px = rowacc, rowtmp_px = rowtmp; rowacc_px != rowtmp; |
| rowacc_px++, rowtmp_px++) |
| *rowacc_px += mul * *rowtmp_px; |
| ctx->output_row(oy, (void*)rowacc, ctx); |
| /* clear accumulator row, store partial coverage for next row */ |
| memset((void *)rowacc, 0, ctx->bm->width * sizeof(uint32_t) * CHANNEL_BYTES); |
| mul = oye; |
| oy += rset->rowstep; |
| /* inside an area */ |
| } else { |
| /* accumulate new scaled row to rowacc */ |
| if (!ctx->h_scaler(rowacc, ctx, true)) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| #ifdef HAVE_UPSCALER |
| /* horizontal linear scaler */ |
| static bool scale_h_linear(void *out_line_ptr, struct scaler_context *ctx, |
| bool accum) |
| { |
| unsigned int ix, ox, ixe; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| const uint32_t h_i_val = ctx->src->width - 1, |
| h_o_val = ctx->bm->width - 1; |
| #else |
| const uint32_t h_i_val = ctx->h_i_val, |
| h_o_val = ctx->h_o_val; |
| #endif |
| /* 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. |
| */ |
| #ifdef HAVE_LCD_COLOR |
| struct uint32_argb rgbval=rgbval, rgbinc=rgbinc, |
| *out_line = (struct uint32_argb*)out_line_ptr; |
| #else |
| uint32_t val=val, inc=inc, *out_line = (uint32_t*)out_line_ptr; |
| #endif |
| struct img_part *part; |
| SDEBUGF("scale_h_linear\n"); |
| FILL_BUF_INIT(part,ctx->store_part,ctx->args); |
| ix = 0; |
| /* The error is set so that values are initialized on the first pass. */ |
| ixe = h_o_val; |
| /* give other tasks a chance to run */ |
| yield(); |
| for (ox = 0; ox < (uint32_t)ctx->bm->width; ox++) |
| { |
| #ifdef HAVE_LCD_COLOR |
| if (ixe >= h_o_val) |
| { |
| /* Store the new "current" pixel value in rgbval, and the color |
| step value in rgbinc. |
| */ |
| ixe -= h_o_val; |
| rgbinc.r = -(part->buf->red); |
| rgbinc.g = -(part->buf->green); |
| rgbinc.b = -(part->buf->blue); |
| rgbinc.a = -(part->buf->alpha); |
| #if defined(CPU_COLDFIRE) |
| /* Coldfire EMAC math */ |
| MAC(part->buf->red, h_o_val, 0); |
| MAC(part->buf->green, h_o_val, 1); |
| MAC(part->buf->blue, h_o_val, 2); |
| MAC(part->buf->alpha, h_o_val, 3); |
| #else |
| /* generic C math */ |
| rgbval.r = (part->buf->red) * h_o_val; |
| rgbval.g = (part->buf->green) * h_o_val; |
| rgbval.b = (part->buf->blue) * h_o_val; |
| rgbval.a = (part->buf->alpha) * h_o_val; |
| #endif /* CPU */ |
| ix += 1; |
| /* If this wasn't the last pixel, add the next one to rgbinc. */ |
| if (LIKELY(ix < (uint32_t)ctx->src->width)) { |
| part->buf++; |
| part->len--; |
| /* Fetch new pixels if needed */ |
| FILL_BUF(part,ctx->store_part,ctx->args); |
| rgbinc.r += part->buf->red; |
| rgbinc.g += part->buf->green; |
| rgbinc.b += part->buf->blue; |
| rgbinc.a += part->buf->alpha; |
| /* Add a partial step to rgbval, in this pixel isn't precisely |
| aligned with the new source pixel |
| */ |
| #if defined(CPU_COLDFIRE) |
| /* Coldfire EMAC math */ |
| MAC(rgbinc.r, ixe, 0); |
| MAC(rgbinc.g, ixe, 1); |
| MAC(rgbinc.b, ixe, 2); |
| MAC(rgbinc.a, ixe, 3); |
| #else |
| /* generic C math */ |
| rgbval.r += rgbinc.r * ixe; |
| rgbval.g += rgbinc.g * ixe; |
| rgbval.b += rgbinc.b * ixe; |
| rgbval.a += rgbinc.a * ixe; |
| #endif |
| } |
| #if defined(CPU_COLDFIRE) |
| /* get final EMAC result out of ACC registers */ |
| MAC_OUT(rgbval.r, 0); |
| MAC_OUT(rgbval.g, 1); |
| MAC_OUT(rgbval.b, 2); |
| MAC_OUT(rgbval.a, 3); |
| #endif |
| /* Now multiply the color increment to its proper value */ |
| rgbinc.r *= h_i_val; |
| rgbinc.g *= h_i_val; |
| rgbinc.b *= h_i_val; |
| rgbinc.a *= h_i_val; |
| } else { |
| rgbval.r += rgbinc.r; |
| rgbval.g += rgbinc.g; |
| rgbval.b += rgbinc.b; |
| rgbval.a += rgbinc.a; |
| } |
| /* round and scale values, and accumulate or store to output */ |
| if (accum) |
| { |
| out_line[ox].r += (rgbval.r + (1 << 21)) >> 22; |
| out_line[ox].g += (rgbval.g + (1 << 21)) >> 22; |
| out_line[ox].b += (rgbval.b + (1 << 21)) >> 22; |
| out_line[ox].a += (rgbval.a + (1 << 21)) >> 22; |
| } else { |
| out_line[ox].r = (rgbval.r + (1 << 21)) >> 22; |
| out_line[ox].g = (rgbval.g + (1 << 21)) >> 22; |
| out_line[ox].b = (rgbval.b + (1 << 21)) >> 22; |
| out_line[ox].a = (rgbval.a + (1 << 21)) >> 22; |
| } |
| #else |
| if (ixe >= h_o_val) |
| { |
| /* Store the new "current" pixel value in rgbval, and the color |
| step value in rgbinc. |
| */ |
| ixe -= h_o_val; |
| val = *(part->buf); |
| inc = -val; |
| #if defined(CPU_COLDFIRE) |
| /* Coldfire EMAC math */ |
| MAC(val, h_o_val, 0); |
| #elif defined(CPU_SH) |
| /* SH-1 16x16->32 math */ |
| val = mul_u16_u16(val, h_o_val); |
| #else |
| /* generic C math */ |
| val = val * h_o_val; |
| #endif |
| ix += 1; |
| /* If this wasn't the last pixel, add the next one to rgbinc. */ |
| if (LIKELY(ix < (uint32_t)ctx->src->width)) { |
| part->buf++; |
| part->len--; |
| /* Fetch new pixels if needed */ |
| FILL_BUF(part,ctx->store_part,ctx->args); |
| inc += *(part->buf); |
| /* Add a partial step to rgbval, in this pixel isn't precisely |
| aligned with the new source pixel |
| */ |
| #if defined(CPU_COLDFIRE) |
| /* Coldfire EMAC math */ |
| MAC(inc, ixe, 0); |
| #elif defined(CPU_SH) |
| /* SH-1 16x16->32 math */ |
| val += mul_s16_s16(inc, ixe); |
| #else |
| /* generic C math */ |
| val += inc * ixe; |
| #endif |
| } |
| #if defined(CPU_COLDFIRE) |
| /* get final EMAC result out of ACC register */ |
| MAC_OUT(val, 0); |
| #endif |
| /* Now multiply the color increment to its proper value */ |
| #if defined(CPU_SH) |
| /* SH-1 16x16->32 math */ |
| inc = mul_s16_s16(inc, h_i_val); |
| #else |
| /* generic C math */ |
| inc *= h_i_val; |
| #endif |
| } else |
| val += inc; |
| #if !(defined(CPU_SH) || defined(TEST_SH_MATH)) |
| /* round and scale values, and accumulate or store to output */ |
| if (accum) |
| { |
| out_line[ox] += (val + (1 << 21)) >> 22; |
| } else { |
| out_line[ox] = (val + (1 << 21)) >> 22; |
| } |
| #else |
| /* round and scale values, and accumulate or store to output */ |
| if (accum) |
| { |
| out_line[ox] += val; |
| } else { |
| out_line[ox] = val; |
| } |
| #endif |
| #endif |
| ixe += h_i_val; |
| } |
| return true; |
| } |
| |
| /* vertical linear scaler */ |
| static inline bool scale_v_linear(struct rowset *rset, |
| struct scaler_context *ctx) |
| { |
| uint32_t iy, iye; |
| int32_t oy; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| const uint32_t v_i_val = ctx->src->height - 1, |
| v_o_val = ctx->bm->height - 1; |
| #else |
| const uint32_t v_i_val = ctx->v_i_val, |
| v_o_val = ctx->v_o_val; |
| #endif |
| /* Set up our buffers, to store the increment and current value for each |
| column, and one temp buffer used to read in new rows. |
| */ |
| uint32_t *rowinc = (uint32_t *)(ctx->buf), |
| *rowval = rowinc + ctx->bm->width * CHANNEL_BYTES, |
| *rowtmp = rowval + ctx->bm->width * CHANNEL_BYTES, |
| *rowinc_px, *rowval_px, *rowtmp_px; |
| |
| SDEBUGF("scale_v_linear\n"); |
| iy = 0; |
| iye = v_o_val; |
| /* get first scaled row in rowtmp */ |
| if(!ctx->h_scaler((void*)rowtmp, ctx, false)) |
| return false; |
| for (oy = rset->rowstart; oy != rset->rowstop; oy += rset->rowstep) |
| { |
| if (iye >= v_o_val) |
| { |
| iye -= v_o_val; |
| iy += 1; |
| for(rowinc_px = rowinc, rowtmp_px = rowtmp, rowval_px = rowval; |
| rowinc_px < rowval; rowinc_px++, rowtmp_px++, rowval_px++) |
| { |
| *rowinc_px = -*rowtmp_px; |
| *rowval_px = *rowtmp_px * v_o_val; |
| } |
| if (iy < (uint32_t)ctx->src->height) |
| { |
| if (!ctx->h_scaler((void*)rowtmp, ctx, false)) |
| return false; |
| for(rowinc_px = rowinc, rowtmp_px = rowtmp, rowval_px = rowval; |
| rowinc_px < rowval; rowinc_px++, rowtmp_px++, rowval_px++) |
| { |
| *rowinc_px += *rowtmp_px; |
| *rowval_px += *rowinc_px * iye; |
| *rowinc_px *= v_i_val; |
| } |
| } |
| } else |
| for(rowinc_px = rowinc, rowval_px = rowval; rowinc_px < rowval; |
| rowinc_px++, rowval_px++) |
| *rowval_px += *rowinc_px; |
| ctx->output_row(oy, (void*)rowval, ctx); |
| iye += v_i_val; |
| } |
| return true; |
| } |
| #endif /* HAVE_UPSCALER */ |
| |
| #if defined(HAVE_LCD_COLOR) && (defined(HAVE_JPEG) || defined(PLUGIN)) |
| static void output_row_32_native_fromyuv(uint32_t row, void * row_in, |
| struct scaler_context *ctx) |
| { |
| #if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE |
| #define DEST_STEP (ctx->bm->height) |
| #define Y_STEP (1) |
| #else |
| #define DEST_STEP (1) |
| #define Y_STEP (BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0)) |
| #endif |
| |
| int col; |
| uint8_t dy = DITHERY(row); |
| struct uint32_argb *qp = (struct uint32_argb *)row_in; |
| SDEBUGF("output_row: y: %lu in: %p\n",row, row_in); |
| fb_data *dest = (fb_data *)ctx->bm->data + Y_STEP * row; |
| int delta = 127; |
| unsigned r, g, b, y, u, v; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| (void) delta; |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| y = SC_OUT(qp->b, ctx); |
| u = SC_OUT(qp->g, ctx); |
| v = SC_OUT(qp->r, ctx); |
| qp++; |
| yuv_to_rgb(y, u, v, &r, &g, &b); |
| #if LCD_DEPTH < 24 |
| r = (31 * r + (r >> 3) + delta) >> 8; |
| g = (63 * g + (g >> 2) + delta) >> 8; |
| b = (31 * b + (b >> 3) + delta) >> 8; |
| #endif |
| *dest = FB_RGBPACK_LCD(r, g, b); |
| dest += DEST_STEP; |
| } |
| } |
| #endif |
| |
| #if !defined(PLUGIN) || LCD_DEPTH > 1 |
| static void output_row_32_native(uint32_t row, void * row_in, |
| struct scaler_context *ctx) |
| { |
| int col; |
| int fb_width = BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0); |
| uint8_t dy = DITHERY(row); |
| #ifdef HAVE_LCD_COLOR |
| struct uint32_argb *qp = (struct uint32_argb*)row_in; |
| #else |
| uint32_t *qp = (uint32_t*)row_in; |
| #endif |
| SDEBUGF("output_row: y: %lu in: %p\n",row, row_in); |
| #if LCD_DEPTH == 2 |
| #if LCD_PIXELFORMAT == HORIZONTAL_PACKING |
| /* greyscale iPods */ |
| fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row; |
| int shift = 6; |
| int delta = 127; |
| unsigned bright; |
| unsigned data = 0; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| bright = SC_OUT(*qp++, ctx); |
| bright = (3 * bright + (bright >> 6) + delta) >> 8; |
| data |= (~bright & 3) << shift; |
| shift -= 2; |
| if (shift < 0) { |
| *dest++ = data; |
| data = 0; |
| shift = 6; |
| } |
| } |
| if (shift < 6) |
| *dest++ = data; |
| #elif LCD_PIXELFORMAT == VERTICAL_PACKING |
| /* iriver H1x0 */ |
| fb_data *dest = (fb_data *)ctx->bm->data + fb_width * |
| (row >> 2); |
| int shift = 2 * (row & 3); |
| int delta = 127; |
| unsigned bright; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| bright = SC_OUT(*qp++, ctx); |
| bright = (3 * bright + (bright >> 6) + delta) >> 8; |
| *dest++ |= (~bright & 3) << shift; |
| } |
| #elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED |
| /* iAudio M3 */ |
| fb_data *dest = (fb_data *)ctx->bm->data + fb_width * |
| (row >> 3); |
| int shift = row & 7; |
| int delta = 127; |
| unsigned bright; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| bright = SC_OUT(*qp++, ctx); |
| bright = (3 * bright + (bright >> 6) + delta) >> 8; |
| *dest++ |= vi_pattern[bright] << shift; |
| } |
| #endif /* LCD_PIXELFORMAT */ |
| #elif LCD_DEPTH >= 16 |
| /* iriver h300, colour iPods, X5 */ |
| (void)fb_width; |
| fb_data *dest = STRIDE_MAIN((fb_data *)ctx->bm->data + fb_width * row, |
| (fb_data *)ctx->bm->data + row); |
| int delta = 127; |
| unsigned r, g, b; |
| struct uint32_argb q0; |
| /* setup alpha channel buffer */ |
| unsigned char *bm_alpha = NULL; |
| if (ctx->bm->alpha_offset > 0) |
| bm_alpha = ctx->bm->data + ctx->bm->alpha_offset; |
| if (bm_alpha) |
| bm_alpha += ALIGN_UP(ctx->bm->width, 2)*row/2; |
| |
| for (col = 0; col < ctx->bm->width; col++) { |
| (void) delta; |
| if (ctx->dither) |
| delta = DITHERXDY(col,dy); |
| q0 = *qp++; |
| r = SC_OUT(q0.r, ctx); |
| g = SC_OUT(q0.g, ctx); |
| b = SC_OUT(q0.b, ctx); |
| #if LCD_DEPTH < 24 |
| r = (31 * r + (r >> 3) + delta) >> 8; |
| g = (63 * g + (g >> 2) + delta) >> 8; |
| b = (31 * b + (b >> 3) + delta) >> 8; |
| #endif |
| *dest = FB_RGBPACK_LCD(r, g, b); |
| dest += STRIDE_MAIN(1, ctx->bm->height); |
| if (bm_alpha) { |
| /* pack alpha channel for 2 pixels into 1 byte */ |
| unsigned alpha = SC_OUT(q0.a, ctx); |
| if (col%2) |
| *bm_alpha++ |= alpha&0xf0; |
| else |
| *bm_alpha = alpha>>4; |
| } |
| } |
| #endif /* LCD_DEPTH */ |
| } |
| #endif |
| |
| #if defined(PLUGIN) && LCD_DEPTH > 1 |
| unsigned int get_size_native(struct bitmap *bm) |
| { |
| return BM_SIZE(bm->width,bm->height,FORMAT_NATIVE,0); |
| } |
| |
| const struct custom_format format_native = { |
| .output_row_8 = output_row_8_native, |
| #if defined(HAVE_LCD_COLOR) && (defined(HAVE_JPEG) || defined(PLUGIN)) |
| .output_row_32 = { |
| output_row_32_native, |
| output_row_32_native_fromyuv |
| }, |
| #else |
| .output_row_32 = output_row_32_native, |
| #endif |
| .get_size = get_size_native |
| }; |
| #endif |
| |
| int resize_on_load(struct bitmap *bm, bool dither, struct dim *src, |
| struct rowset *rset, unsigned char *buf, unsigned int len, |
| const struct custom_format *format, |
| IF_PIX_FMT(int format_index,) |
| struct img_part* (*store_part)(void *args), |
| void *args) |
| { |
| const int sw = src->width; |
| const int sh = src->height; |
| const int dw = bm->width; |
| const int dh = bm->height; |
| int ret; |
| /* buffer for 1 line + 2 spare lines */ |
| #ifdef HAVE_LCD_COLOR |
| unsigned int needed = sizeof(struct uint32_argb) * 3 * bm->width; |
| #else |
| unsigned int needed = sizeof(uint32_t) * 3 * bm->width; |
| #endif |
| #if MAX_SC_STACK_ALLOC |
| uint8_t sc_buf[(needed <= len || needed > MAX_SC_STACK_ALLOC) ? |
| 0 : needed]; |
| #endif |
| ALIGN_BUFFER(buf, len, sizeof(uint32_t)); |
| if (needed > len) |
| { |
| #if MAX_SC_STACK_ALLOC |
| if (needed > MAX_SC_STACK_ALLOC) |
| { |
| DEBUGF("unable to allocate required buffer: %d needed, " |
| "%d available, %d permitted from stack\n", |
| needed, len, MAX_SC_STACK_ALLOC); |
| return 0; |
| } |
| if (sizeof(sc_buf) < needed) |
| { |
| DEBUGF("failed to allocate large enough buffer on stack: " |
| "%d needed, only got %d", |
| needed, MAX_SC_STACK_ALLOC); |
| return 0; |
| } |
| #else |
| DEBUGF("unable to allocate required buffer: %d needed, " |
| "%d available\n", needed, len); |
| return 0; |
| #endif |
| } |
| |
| struct scaler_context ctx; |
| #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
| cpu_boost(true); |
| #endif |
| ctx.store_part = store_part; |
| ctx.args = args; |
| #if MAX_SC_STACK_ALLOC |
| ctx.buf = needed > len ? sc_buf : buf; |
| #else |
| ctx.buf = buf; |
| #endif |
| ctx.len = len; |
| ctx.bm = bm; |
| ctx.src = src; |
| ctx.dither = dither; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| uint32_t div; |
| #endif |
| #if !defined(PLUGIN) |
| #if defined(HAVE_LCD_COLOR) && defined(HAVE_JPEG) |
| ctx.output_row = format_index ? output_row_32_native_fromyuv |
| : output_row_32_native; |
| #else |
| ctx.output_row = output_row_32_native; |
| #endif |
| if (format) |
| #endif |
| #ifdef HAVE_LCD_COLOR |
| ctx.output_row = format->output_row_32[format_index]; |
| #else |
| ctx.output_row = format->output_row_32; |
| #endif |
| #ifdef HAVE_UPSCALER |
| if (sw > dw) |
| { |
| #endif |
| ctx.h_scaler = scale_h_area; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| div = sw; |
| #else |
| uint32_t h_div = (1U << 24) / sw; |
| ctx.h_i_val = sw * h_div; |
| ctx.h_o_val = dw * h_div; |
| #endif |
| #ifdef HAVE_UPSCALER |
| } else { |
| ctx.h_scaler = scale_h_linear; |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| div = dw - 1; |
| #else |
| uint32_t h_div = (1U << 24) / (dw - 1); |
| ctx.h_i_val = (sw - 1) * h_div; |
| ctx.h_o_val = (dw - 1) * h_div; |
| #endif |
| } |
| #endif |
| #ifdef CPU_COLDFIRE |
| unsigned old_macsr = coldfire_get_macsr(); |
| coldfire_set_macsr(EMAC_UNSIGNED); |
| #endif |
| #ifdef HAVE_UPSCALER |
| if (sh > dh) |
| #endif |
| { |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| div *= sh; |
| ctx.recip = ((uint32_t)(-div)) / div + 1; |
| #else |
| uint32_t v_div = (1U << 22) / sh; |
| ctx.v_i_val = sh * v_div; |
| ctx.v_o_val = dh * v_div; |
| #endif |
| ret = scale_v_area(rset, &ctx); |
| } |
| #ifdef HAVE_UPSCALER |
| else |
| { |
| #if defined(CPU_SH) || defined (TEST_SH_MATH) |
| div *= dh - 1; |
| ctx.recip = ((uint32_t)(-div)) / div + 1; |
| #else |
| uint32_t v_div = (1U << 22) / dh; |
| ctx.v_i_val = (sh - 1) * v_div; |
| ctx.v_o_val = (dh - 1) * v_div; |
| #endif |
| ret = scale_v_linear(rset, &ctx); |
| } |
| #endif |
| #ifdef CPU_COLDFIRE |
| /* Restore emac status; other modules like tone control filter |
| * calculation may rely on it. */ |
| coldfire_set_macsr(old_macsr); |
| #endif |
| #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
| cpu_boost(false); |
| #endif |
| if (!ret) |
| return 0; |
| return 1; |
| } |