a1842c04f9
With LCD driver all calculation will be performed on RGB888 and the hardware/OS can display from our 24bit framebuffer. It is not yet as performance optimized as the existing drivers but should be good enough.The vast number of small changes is due to the fact that fb_data can be a struct type now, while most of the code expected a scalar type. lcd-as-memframe ASM code does not work with 24bit currently so the with 24bit it enforces the generic C code. All plugins are ported over. Except for rockpaint. It uses so much memory that it wouldnt fit into the 512k plugin buffer anymore (patches welcome). Change-Id: Ibb1964545028ce0d8ff9833ccc3ab66be3ee0754
979 lines
32 KiB
C
979 lines
32 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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;
|
|
}
|