blob: ac6b7a3120360810860bd8dee18901149a7b50f9 [file] [log] [blame]
/***************************************************************************
* __________ __ ___.
* 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;
}