2008-12-09 23:07:59 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
2008-12-10 21:10:34 +00:00
|
|
|
* $Id$
|
2008-12-09 23:07:59 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2009-01-13 13:48:26 +00:00
|
|
|
#include <system.h>
|
2008-12-09 23:07:59 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2008-12-14 17:58:04 +00:00
|
|
|
#include <general.h>
|
2008-12-09 23:07:59 +00:00
|
|
|
#include "inttypes.h"
|
2009-01-04 21:22:05 +00:00
|
|
|
#ifndef PLUGIN
|
2008-12-09 23:07:59 +00:00
|
|
|
#include "debug.h"
|
2009-01-04 21:22:05 +00:00
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
#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"
|
2009-01-04 21:22:05 +00:00
|
|
|
#include <bmp.h>
|
2008-12-09 23:07:59 +00:00
|
|
|
#include "resize.h"
|
|
|
|
#else
|
|
|
|
#undef DEBUGF
|
|
|
|
#define DEBUGF(...)
|
|
|
|
#endif
|
|
|
|
|
2008-12-26 07:03:22 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
2009-01-13 14:41:29 +00:00
|
|
|
/* 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;
|
2008-12-26 07:03:22 +00:00
|
|
|
int tmp;
|
|
|
|
if (dst->width <= 0)
|
|
|
|
dst->width = LCD_WIDTH;
|
|
|
|
if (dst->height <= 0)
|
|
|
|
dst->height = LCD_HEIGHT;
|
|
|
|
#ifndef HAVE_UPSCALER
|
2009-01-13 14:41:29 +00:00
|
|
|
if (dst->width > sw || dst->height > sh)
|
2008-12-26 07:03:22 +00:00
|
|
|
{
|
2009-01-13 14:41:29 +00:00
|
|
|
dst->width = sw;
|
|
|
|
dst->height = sh;
|
2008-12-26 07:03:22 +00:00
|
|
|
}
|
2009-01-13 14:41:29 +00:00
|
|
|
if (sw == dst->width && sh == dst->height)
|
2008-12-26 07:03:22 +00:00
|
|
|
return 1;
|
|
|
|
#endif
|
2009-01-13 14:41:29 +00:00
|
|
|
tmp = (sw * dst->height + (sh >> 1)) / sh;
|
2008-12-26 07:03:22 +00:00
|
|
|
if (tmp > dst->width)
|
2009-01-13 14:41:29 +00:00
|
|
|
dst->height = (sh * dst->width + (sw >> 1)) / sw;
|
2008-12-26 07:03:22 +00:00
|
|
|
else
|
|
|
|
dst->width = tmp;
|
|
|
|
return src->width == dst->width && src->height == dst->height;
|
|
|
|
}
|
|
|
|
|
2008-12-10 12:09:03 +00:00
|
|
|
/* All of these scalers use variations of Bresenham's algorithm to convert from
|
2008-12-26 07:03:22 +00:00
|
|
|
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.
|
2008-12-10 12:09:03 +00:00
|
|
|
*/
|
|
|
|
|
2008-12-09 23:07:59 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
2008-12-10 12:09:03 +00:00
|
|
|
/* 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)
|
2008-12-26 07:03:22 +00:00
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
|
2008-12-10 12:09:03 +00:00
|
|
|
/* read new img_part unconditionally, return false on failure */
|
2008-12-09 23:07:59 +00:00
|
|
|
#define FILL_BUF_INIT(img_part, store_part, args) { \
|
2008-12-10 12:09:03 +00:00
|
|
|
img_part = store_part(args); \
|
|
|
|
if (img_part == NULL) \
|
2008-12-09 23:07:59 +00:00
|
|
|
return false; \
|
|
|
|
}
|
|
|
|
|
2008-12-10 12:09:03 +00:00
|
|
|
/* read new img_part if current one is empty, return false on failure */
|
2008-12-09 23:07:59 +00:00
|
|
|
#define FILL_BUF(img_part, store_part, args) { \
|
2008-12-10 12:09:03 +00:00
|
|
|
if (img_part->len == 0) \
|
|
|
|
img_part = store_part(args); \
|
|
|
|
if (img_part == NULL) \
|
2008-12-09 23:07:59 +00:00
|
|
|
return false; \
|
|
|
|
}
|
|
|
|
|
2008-12-10 12:09:03 +00:00
|
|
|
/* Set up rounding and scale factors for horizontal area scaler */
|
2008-12-26 07:03:22 +00:00
|
|
|
static inline void scale_h_area_setup(struct scaler_context *ctx)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-10 12:09:03 +00:00
|
|
|
/* sum is output value * src->width */
|
2008-12-26 07:03:22 +00:00
|
|
|
SDEBUGF("scale_h_area_setup\n");
|
|
|
|
ctx->divisor = ctx->src->width;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* horizontal area average scaler */
|
2008-12-26 07:03:22 +00:00
|
|
|
static bool scale_h_area(void *out_line_ptr,
|
2008-12-09 23:07:59 +00:00
|
|
|
struct scaler_context *ctx, bool accum)
|
|
|
|
{
|
|
|
|
SDEBUGF("scale_h_area\n");
|
|
|
|
unsigned int ix, ox, oxe, mul;
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
struct uint32_rgb rgbvalacc = { 0, 0, 0 },
|
|
|
|
rgbvaltmp = { 0, 0, 0 },
|
|
|
|
*out_line = (struct uint32_rgb *)out_line_ptr;
|
|
|
|
#else
|
|
|
|
uint32_t acc = 0, tmp = 0, *out_line = (uint32_t*)out_line_ptr;
|
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
struct img_part *part;
|
|
|
|
FILL_BUF_INIT(part,ctx->store_part,ctx->args);
|
|
|
|
ox = 0;
|
|
|
|
oxe = 0;
|
|
|
|
mul = 0;
|
2008-12-26 07:03:22 +00:00
|
|
|
/* give other tasks a chance to run */
|
2009-01-08 02:49:23 +00:00
|
|
|
yield();
|
2008-12-26 07:03:22 +00:00
|
|
|
for (ix = 0; ix < (unsigned int)ctx->src->width; ix++)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
oxe += ctx->bm->width;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* end of current area has been reached */
|
2008-12-26 07:03:22 +00:00
|
|
|
/* fill buffer if needed */
|
|
|
|
FILL_BUF(part,ctx->store_part,ctx->args);
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
if (oxe >= (unsigned int)ctx->src->width)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-10 12:09:03 +00:00
|
|
|
/* "reset" error, which now represents partial coverage of next
|
|
|
|
pixel by the next area
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
oxe -= ctx->src->width;
|
|
|
|
|
2008-12-10 12:09:03 +00:00
|
|
|
/* add saved partial pixel from start of area */
|
2008-12-26 07:03:22 +00:00
|
|
|
rgbvalacc.r = rgbvalacc.r * ctx->bm->width + rgbvaltmp.r * mul;
|
|
|
|
rgbvalacc.g = rgbvalacc.g * ctx->bm->width + rgbvaltmp.g * mul;
|
|
|
|
rgbvalacc.b = rgbvalacc.b * ctx->bm->width + rgbvaltmp.b * mul;
|
|
|
|
|
2008-12-10 12:09:03 +00:00
|
|
|
/* 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;
|
2008-12-26 07:03:22 +00:00
|
|
|
mul = ctx->bm->width - oxe;
|
2008-12-10 12:09:03 +00:00
|
|
|
rgbvalacc.r += rgbvaltmp.r * mul;
|
|
|
|
rgbvalacc.g += rgbvaltmp.g * mul;
|
|
|
|
rgbvalacc.b += rgbvaltmp.b * mul;
|
2008-12-26 07:03:22 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
out_line[ox].r = rgbvalacc.r;
|
|
|
|
out_line[ox].g = rgbvalacc.g;
|
|
|
|
out_line[ox].b = rgbvalacc.b;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* reset accumulator */
|
|
|
|
rgbvalacc.r = 0;
|
|
|
|
rgbvalacc.g = 0;
|
|
|
|
rgbvalacc.b = 0;
|
2008-12-26 07:03:22 +00:00
|
|
|
mul = ctx->bm->width - mul;
|
2008-12-09 23:07:59 +00:00
|
|
|
ox += 1;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* inside an area */
|
2008-12-09 23:07:59 +00:00
|
|
|
} else {
|
2008-12-10 12:09:03 +00:00
|
|
|
/* add pixel value to accumulator */
|
|
|
|
rgbvalacc.r += part->buf->red;
|
|
|
|
rgbvalacc.g += part->buf->green;
|
|
|
|
rgbvalacc.b += part->buf->blue;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
2008-12-26 07:03:22 +00:00
|
|
|
#else
|
|
|
|
if (oxe >= (unsigned int)ctx->src->width)
|
|
|
|
{
|
|
|
|
/* "reset" error, which now represents partial coverage of next
|
|
|
|
pixel by the next area
|
|
|
|
*/
|
|
|
|
oxe -= ctx->src->width;
|
|
|
|
|
|
|
|
/* add saved partial pixel from start of area */
|
|
|
|
acc = acc * ctx->bm->width + tmp * mul;
|
|
|
|
|
|
|
|
/* get new pixel , then add its partial coverage to this area */
|
|
|
|
tmp = *(part->buf);
|
|
|
|
mul = ctx->bm->width - oxe;
|
|
|
|
acc += tmp * mul;
|
|
|
|
/* round, divide, and either store or accumulate to output row */
|
|
|
|
if (accum)
|
|
|
|
{
|
|
|
|
acc += out_line[ox];
|
|
|
|
}
|
|
|
|
out_line[ox] = acc;
|
|
|
|
/* reset accumulator */
|
|
|
|
acc = 0;
|
|
|
|
mul = ctx->bm->width - mul;
|
|
|
|
ox += 1;
|
|
|
|
/* inside an area */
|
|
|
|
} else {
|
|
|
|
/* add pixel value to accumulator */
|
|
|
|
acc += *(part->buf);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
part->buf++;
|
|
|
|
part->len--;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vertical area average scaler */
|
2008-12-26 07:03:22 +00:00
|
|
|
static inline bool scale_v_area(struct rowset *rset, struct scaler_context *ctx)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
uint32_t mul, x, oy, iy, oye;
|
2008-12-10 12:09:03 +00:00
|
|
|
|
|
|
|
/* Set up rounding and scale factors */
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx->divisor *= ctx->src->height;
|
|
|
|
ctx->round = ctx->divisor >> 1;
|
2009-01-20 17:24:49 +00:00
|
|
|
ctx->divisor = (((ctx->divisor >> 1) + SC_NUM) / ctx->divisor) << SC_FIX;
|
2008-12-09 23:07:59 +00:00
|
|
|
mul = 0;
|
2008-12-26 07:03:22 +00:00
|
|
|
oy = rset->rowstart;
|
2008-12-09 23:07:59 +00:00
|
|
|
oye = 0;
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
uint32_t *rowacc = (uint32_t *) ctx->buf,
|
|
|
|
*rowtmp = rowacc + 3 * ctx->bm->width;
|
2009-01-08 02:49:23 +00:00
|
|
|
memset((void *)ctx->buf, 0, ctx->bm->width * 2 * sizeof(struct uint32_rgb));
|
2008-12-26 07:03:22 +00:00
|
|
|
#else
|
|
|
|
uint32_t *rowacc = (uint32_t *) ctx->buf,
|
|
|
|
*rowtmp = rowacc + ctx->bm->width;
|
2009-01-08 02:49:23 +00:00
|
|
|
memset((void *)ctx->buf, 0, ctx->bm->width * 2 * sizeof(uint32_t));
|
2008-12-26 07:03:22 +00:00
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
SDEBUGF("scale_v_area\n");
|
2008-12-10 12:09:03 +00:00
|
|
|
/* zero the accumulator and temp rows */
|
2008-12-26 07:03:22 +00:00
|
|
|
for (iy = 0; iy < (unsigned int)ctx->src->height; iy++)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
oye += ctx->bm->height;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* end of current area has been reached */
|
2008-12-26 07:03:22 +00:00
|
|
|
if (oye >= (unsigned int)ctx->src->height)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-10 12:09:03 +00:00
|
|
|
/* "reset" error, which now represents partial coverage of the next
|
|
|
|
row by the next area
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
oye -= ctx->src->height;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* add stored partial row to accumulator */
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
for (x = 0; x < 3 * (unsigned int)ctx->bm->width; x++)
|
|
|
|
#else
|
|
|
|
for (x = 0; x < (unsigned int)ctx->bm->width; x++)
|
|
|
|
#endif
|
|
|
|
rowacc[x] = rowacc[x] * ctx->bm->height + mul * rowtmp[x];
|
2008-12-10 12:09:03 +00:00
|
|
|
/* store new scaled row in temp row */
|
2008-12-26 07:03:22 +00:00
|
|
|
if(!ctx->h_scaler(rowtmp, ctx, false))
|
2008-12-10 12:09:03 +00:00
|
|
|
return false;
|
|
|
|
/* add partial coverage by new row to this area, then round and
|
|
|
|
scale to final value
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
mul = ctx->bm->height - oye;
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
for (x = 0; x < 3 * (unsigned int)ctx->bm->width; x++)
|
|
|
|
#else
|
|
|
|
for (x = 0; x < (unsigned int)ctx->bm->width; x++)
|
|
|
|
#endif
|
|
|
|
rowacc[x] += mul * rowtmp[x];
|
|
|
|
ctx->output_row(oy, (void*)rowacc, ctx);
|
2008-12-10 12:09:03 +00:00
|
|
|
/* clear accumulator row, store partial coverage for next row */
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
2009-01-08 02:49:23 +00:00
|
|
|
memset((void *)rowacc, 0, ctx->bm->width * sizeof(uint32_t) * 3);
|
2008-12-26 07:03:22 +00:00
|
|
|
#else
|
2009-01-08 02:49:23 +00:00
|
|
|
memset((void *)rowacc, 0, ctx->bm->width * sizeof(uint32_t));
|
2008-12-26 07:03:22 +00:00
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
mul = oye;
|
2008-12-26 07:03:22 +00:00
|
|
|
oy += rset->rowstep;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* inside an area */
|
2008-12-09 23:07:59 +00:00
|
|
|
} else {
|
2008-12-10 12:09:03 +00:00
|
|
|
/* accumulate new scaled row to rowacc */
|
2008-12-26 07:03:22 +00:00
|
|
|
if (!ctx->h_scaler(rowacc, ctx, true))
|
2008-12-10 12:09:03 +00:00
|
|
|
return false;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_UPSCALER
|
2008-12-10 12:09:03 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
static inline void scale_h_linear_setup(struct scaler_context *ctx)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx->divisor = ctx->bm->width - 1;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* horizontal linear scaler */
|
2008-12-26 07:03:22 +00:00
|
|
|
static bool scale_h_linear(void *out_line_ptr, struct scaler_context *ctx,
|
|
|
|
bool accum)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
|
|
|
unsigned int ix, ox, ixe;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
struct uint32_rgb rgbval=rgbval, rgbinc=rgbinc,
|
|
|
|
*out_line = (struct uint32_rgb*)out_line_ptr;
|
|
|
|
#else
|
|
|
|
uint32_t val=val, inc=inc, *out_line = (uint32_t*)out_line_ptr;
|
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
struct img_part *part;
|
|
|
|
SDEBUGF("scale_h_linear\n");
|
|
|
|
FILL_BUF_INIT(part,ctx->store_part,ctx->args);
|
|
|
|
ix = 0;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* The error is set so that values are initialized on the first pass. */
|
2008-12-26 07:03:22 +00:00
|
|
|
ixe = ctx->bm->width - 1;
|
|
|
|
/* give other tasks a chance to run */
|
2009-01-08 02:49:23 +00:00
|
|
|
yield();
|
2008-12-26 07:03:22 +00:00
|
|
|
for (ox = 0; ox < (uint32_t)ctx->bm->width; ox++)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
if (ixe >= ((uint32_t)ctx->bm->width - 1))
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
/* Store the new "current" pixel value in rgbval, and the color
|
2008-12-10 12:09:03 +00:00
|
|
|
step value in rgbinc.
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
ixe -= (ctx->bm->width - 1);
|
2008-12-09 23:07:59 +00:00
|
|
|
rgbinc.r = -(part->buf->red);
|
|
|
|
rgbinc.g = -(part->buf->green);
|
|
|
|
rgbinc.b = -(part->buf->blue);
|
2008-12-26 07:03:22 +00:00
|
|
|
rgbval.r = (part->buf->red) * (ctx->bm->width - 1);
|
|
|
|
rgbval.g = (part->buf->green) * (ctx->bm->width - 1);
|
|
|
|
rgbval.b = (part->buf->blue) * (ctx->bm->width - 1);
|
2008-12-09 23:07:59 +00:00
|
|
|
ix += 1;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* If this wasn't the last pixel, add the next one to rgbinc. */
|
2008-12-26 07:03:22 +00:00
|
|
|
if (ix < (uint32_t)ctx->src->width) {
|
2008-12-09 23:07:59 +00:00
|
|
|
part->buf++;
|
|
|
|
part->len--;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* Fetch new pixels if needed */
|
2008-12-09 23:07:59 +00:00
|
|
|
FILL_BUF(part,ctx->store_part,ctx->args);
|
|
|
|
rgbinc.r += part->buf->red;
|
|
|
|
rgbinc.g += part->buf->green;
|
|
|
|
rgbinc.b += part->buf->blue;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* Add a partial step to rgbval, in this pixel isn't precisely
|
|
|
|
aligned with the new source pixel
|
|
|
|
*/
|
2008-12-09 23:07:59 +00:00
|
|
|
rgbval.r += rgbinc.r * ixe;
|
|
|
|
rgbval.g += rgbinc.g * ixe;
|
|
|
|
rgbval.b += rgbinc.b * ixe;
|
|
|
|
}
|
2008-12-10 12:09:03 +00:00
|
|
|
/* Now multiple the color increment to its proper value */
|
2008-12-26 07:03:22 +00:00
|
|
|
rgbinc.r *= ctx->src->width - 1;
|
|
|
|
rgbinc.g *= ctx->src->width - 1;
|
|
|
|
rgbinc.b *= ctx->src->width - 1;
|
|
|
|
} else {
|
|
|
|
rgbval.r += rgbinc.r;
|
|
|
|
rgbval.g += rgbinc.g;
|
|
|
|
rgbval.b += rgbinc.b;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
2008-12-10 12:09:03 +00:00
|
|
|
/* round and scale values, and accumulate or store to output */
|
2008-12-26 07:03:22 +00:00
|
|
|
if (accum)
|
|
|
|
{
|
|
|
|
out_line[ox].r += rgbval.r;
|
|
|
|
out_line[ox].g += rgbval.g;
|
|
|
|
out_line[ox].b += rgbval.b;
|
|
|
|
} else {
|
|
|
|
out_line[ox].r = rgbval.r;
|
|
|
|
out_line[ox].g = rgbval.g;
|
|
|
|
out_line[ox].b = rgbval.b;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (ixe >= ((uint32_t)ctx->bm->width - 1))
|
|
|
|
{
|
|
|
|
/* Store the new "current" pixel value in rgbval, and the color
|
|
|
|
step value in rgbinc.
|
|
|
|
*/
|
|
|
|
ixe -= (ctx->bm->width - 1);
|
|
|
|
val = *(part->buf);
|
|
|
|
inc = -val;
|
|
|
|
val *= (ctx->bm->width - 1);
|
|
|
|
ix += 1;
|
|
|
|
/* If this wasn't the last pixel, add the next one to rgbinc. */
|
|
|
|
if (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
|
|
|
|
*/
|
|
|
|
val += inc * ixe;
|
|
|
|
}
|
|
|
|
/* Now multiply the color increment to its proper value */
|
|
|
|
inc *= ctx->src->width - 1;
|
|
|
|
} else
|
|
|
|
val += inc;
|
|
|
|
/* round and scale values, and accumulate or store to output */
|
|
|
|
if (accum)
|
|
|
|
{
|
|
|
|
out_line[ox] += val;
|
|
|
|
} else {
|
|
|
|
out_line[ox] = val;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
ixe += ctx->src->width - 1;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vertical linear scaler */
|
2008-12-26 07:03:22 +00:00
|
|
|
static inline bool scale_v_linear(struct rowset *rset,
|
|
|
|
struct scaler_context *ctx)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
uint32_t mul, x, iy, iye;
|
|
|
|
int32_t oy;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* Set up scale and rounding factors, the divisor is bm->height - 1 */
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx->divisor *= (ctx->bm->height - 1);
|
|
|
|
ctx->round = ctx->divisor >> 1;
|
2009-01-20 17:24:49 +00:00
|
|
|
ctx->divisor = (((ctx->divisor >> 1) + SC_NUM) / ctx->divisor) << SC_FIX;
|
2008-12-10 12:09:03 +00:00
|
|
|
/* Set up our two temp buffers. The names are generic because they'll be
|
|
|
|
swapped each time a new input row is read
|
|
|
|
*/
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
uint32_t *rowinc = (uint32_t *)(ctx->buf),
|
|
|
|
*rowval = rowinc + 3 * ctx->bm->width,
|
|
|
|
*rowtmp = rowval + 3 * ctx->bm->width;
|
|
|
|
#else
|
|
|
|
uint32_t *rowinc = (uint32_t *)(ctx->buf),
|
|
|
|
*rowval = rowinc + ctx->bm->width,
|
|
|
|
*rowtmp = rowval + ctx->bm->width;
|
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
|
|
|
|
SDEBUGF("scale_v_linear\n");
|
2008-12-26 07:03:22 +00:00
|
|
|
mul = 0;
|
|
|
|
iy = 0;
|
|
|
|
iye = ctx->bm->height - 1;
|
|
|
|
/* get first scaled row in rowtmp */
|
|
|
|
if(!ctx->h_scaler((void*)rowtmp, ctx, false))
|
2008-12-10 12:09:03 +00:00
|
|
|
return false;
|
2008-12-26 07:03:22 +00:00
|
|
|
for (oy = rset->rowstart; oy != rset->rowstop; oy += rset->rowstep)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
if (iye >= (uint32_t)ctx->bm->height - 1)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
iye -= ctx->bm->height - 1;
|
2008-12-09 23:07:59 +00:00
|
|
|
iy += 1;
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
for (x = 0; x < 3 * (uint32_t)ctx->bm->width; x++)
|
|
|
|
#else
|
|
|
|
for (x = 0; x < (uint32_t)ctx->bm->width; x++)
|
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
rowinc[x] = -rowtmp[x];
|
|
|
|
rowval[x] = rowtmp[x] * (ctx->bm->height - 1);
|
|
|
|
}
|
|
|
|
if (iy < (uint32_t)ctx->src->height)
|
|
|
|
{
|
|
|
|
if (!ctx->h_scaler((void*)rowtmp, ctx, false))
|
2008-12-10 12:09:03 +00:00
|
|
|
return false;
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
for (x = 0; x < 3 * (uint32_t)ctx->bm->width; x++)
|
|
|
|
#else
|
|
|
|
for (x = 0; x < (uint32_t)ctx->bm->width; x++)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
rowinc[x] += rowtmp[x];
|
|
|
|
rowval[x] += rowinc[x] * iye;
|
|
|
|
rowinc[x] *= ctx->src->height - 1;
|
|
|
|
}
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
2008-12-26 07:03:22 +00:00
|
|
|
} else
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
for (x = 0; x < 3 * (uint32_t)ctx->bm->width; x++)
|
|
|
|
#else
|
|
|
|
for (x = 0; x < (uint32_t)ctx->bm->width; x++)
|
|
|
|
#endif
|
|
|
|
rowval[x] += rowinc[x];
|
|
|
|
ctx->output_row(oy, (void*)rowval, ctx);
|
|
|
|
iye += ctx->src->height - 1;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_UPSCALER */
|
|
|
|
|
2009-01-04 21:22:05 +00:00
|
|
|
#ifndef PLUGIN
|
2009-01-31 23:51:11 +00:00
|
|
|
static void output_row_native(uint32_t row, void * row_in, struct scaler_context *ctx)
|
2008-12-09 23:07:59 +00:00
|
|
|
{
|
2008-12-26 07:03:22 +00:00
|
|
|
int col;
|
|
|
|
int fb_width = BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0);
|
|
|
|
uint8_t dy = DITHERY(row);
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
|
|
struct uint32_rgb *qp = (struct uint32_rgb*)row_in;
|
2008-12-09 23:07:59 +00:00
|
|
|
#else
|
2008-12-26 07:03:22 +00:00
|
|
|
uint32_t *qp = (uint32_t*)row_in;
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2009-01-04 21:22:05 +00:00
|
|
|
SDEBUGF("output_row: y: %lu in: %p\n",row, row_in);
|
2008-12-26 07:03:22 +00:00
|
|
|
#if LCD_DEPTH == 2
|
2008-12-09 23:07:59 +00:00
|
|
|
#if LCD_PIXELFORMAT == HORIZONTAL_PACKING
|
|
|
|
/* greyscale iPods */
|
2008-12-26 07:03:22 +00:00
|
|
|
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);
|
2009-01-20 17:24:49 +00:00
|
|
|
bright = SC_MUL((*qp++) + ctx->round,ctx->divisor);
|
2008-12-26 07:03:22 +00:00
|
|
|
bright = (3 * bright + (bright >> 6) + delta) >> 8;
|
|
|
|
data |= (~bright & 3) << shift;
|
|
|
|
shift -= 2;
|
|
|
|
if (shift < 0) {
|
|
|
|
*dest++ = data;
|
2008-12-09 23:07:59 +00:00
|
|
|
data = 0;
|
2008-12-26 07:03:22 +00:00
|
|
|
shift = 6;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-26 07:03:22 +00:00
|
|
|
if (shift < 6)
|
|
|
|
*dest++ = data;
|
2008-12-09 23:07:59 +00:00
|
|
|
#elif LCD_PIXELFORMAT == VERTICAL_PACKING
|
|
|
|
/* iriver H1x0 */
|
2008-12-26 07:03:22 +00:00
|
|
|
fb_data *dest = (fb_data *)ctx->bm->data + fb_width *
|
|
|
|
(row >> 2);
|
|
|
|
int shift = 2 * (row & 3);
|
|
|
|
int delta = 127;
|
|
|
|
unsigned bright;
|
2008-12-09 23:07:59 +00:00
|
|
|
|
2008-12-26 07:03:22 +00:00
|
|
|
for (col = 0; col < ctx->bm->width; col++) {
|
|
|
|
if (ctx->dither)
|
|
|
|
delta = DITHERXDY(col,dy);
|
2009-01-20 17:24:49 +00:00
|
|
|
bright = SC_MUL((*qp++) + ctx->round, ctx->divisor);
|
2008-12-26 07:03:22 +00:00
|
|
|
bright = (3 * bright + (bright >> 6) + delta) >> 8;
|
|
|
|
*dest++ |= (~bright & 3) << shift;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
|
2008-12-26 07:03:22 +00:00
|
|
|
/* iAudio M3 */
|
|
|
|
fb_data *dest = (fb_data *)ctx->bm->data + fb_width *
|
|
|
|
(row >> 3);
|
|
|
|
int shift = row & 7;
|
|
|
|
int delta = 127;
|
|
|
|
unsigned bright;
|
2008-12-09 23:07:59 +00:00
|
|
|
|
2008-12-26 07:03:22 +00:00
|
|
|
for (col = 0; col < ctx->bm->width; col++) {
|
|
|
|
if (ctx->dither)
|
|
|
|
delta = DITHERXDY(col,dy);
|
2009-01-20 17:24:49 +00:00
|
|
|
bright = SC_MUL((*qp++) + ctx->round, ctx->divisor);
|
2008-12-26 07:03:22 +00:00
|
|
|
bright = (3 * bright + (bright >> 6) + delta) >> 8;
|
|
|
|
*dest++ |= vi_pattern[bright] << shift;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
|
|
|
#endif /* LCD_PIXELFORMAT */
|
2008-12-26 07:03:22 +00:00
|
|
|
#elif LCD_DEPTH == 16
|
|
|
|
/* iriver h300, colour iPods, X5 */
|
|
|
|
fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row;
|
|
|
|
int delta = 127;
|
|
|
|
unsigned r, g, b;
|
|
|
|
struct uint32_rgb q0;
|
2008-12-09 23:07:59 +00:00
|
|
|
|
2008-12-26 07:03:22 +00:00
|
|
|
for (col = 0; col < ctx->bm->width; col++) {
|
|
|
|
if (ctx->dither)
|
|
|
|
delta = DITHERXDY(col,dy);
|
|
|
|
q0 = *qp++;
|
2009-01-20 17:24:49 +00:00
|
|
|
r = SC_MUL(q0.r + ctx->round, ctx->divisor);
|
|
|
|
g = SC_MUL(q0.g + ctx->round, ctx->divisor);
|
|
|
|
b = SC_MUL(q0.b + ctx->round, ctx->divisor);
|
2008-12-26 07:03:22 +00:00
|
|
|
r = (31 * r + (r >> 3) + delta) >> 8;
|
|
|
|
g = (63 * g + (g >> 2) + delta) >> 8;
|
|
|
|
b = (31 * b + (b >> 3) + delta) >> 8;
|
|
|
|
*dest++ = LCD_RGBPACK_LCD(r, g, b);
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
2008-12-26 07:03:22 +00:00
|
|
|
#endif /* LCD_DEPTH */
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|
2009-01-04 21:22:05 +00:00
|
|
|
#endif
|
2008-12-09 23:07:59 +00:00
|
|
|
|
|
|
|
int resize_on_load(struct bitmap *bm, bool dither, struct dim *src,
|
2008-12-26 07:03:22 +00:00
|
|
|
struct rowset *rset, unsigned char *buf, unsigned int len,
|
2008-12-26 07:05:13 +00:00
|
|
|
const struct custom_format *format,
|
2008-12-09 23:07:59 +00:00
|
|
|
struct img_part* (*store_part)(void *args),
|
|
|
|
void *args)
|
|
|
|
{
|
|
|
|
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_UPSCALER
|
|
|
|
const int sw = src->width;
|
|
|
|
const int sh = src->height;
|
|
|
|
const int dw = bm->width;
|
|
|
|
const int dh = bm->height;
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
int ret;
|
2008-12-09 23:07:59 +00:00
|
|
|
#ifdef HAVE_LCD_COLOR
|
2008-12-26 07:03:22 +00:00
|
|
|
unsigned int needed = sizeof(struct uint32_rgb) * 3 * bm->width;
|
|
|
|
#else
|
|
|
|
unsigned int needed = sizeof(uint32_t) * 3 * bm->width;
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
|
|
|
#if MAX_SC_STACK_ALLOC
|
2008-12-26 07:03:22 +00:00
|
|
|
uint8_t sc_buf[(needed <= len || needed > MAX_SC_STACK_ALLOC) ?
|
|
|
|
0 : needed];
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2009-01-13 13:48:26 +00:00
|
|
|
ALIGN_BUFFER(buf, len, sizeof(uint32_t));
|
2008-12-26 07:03:22 +00:00
|
|
|
if (needed > len)
|
|
|
|
{
|
2008-12-09 23:07:59 +00:00
|
|
|
#if MAX_SC_STACK_ALLOC
|
2008-12-26 07:03:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-12-09 23:07:59 +00:00
|
|
|
#else
|
2008-12-26 07:03:22 +00:00
|
|
|
DEBUGF("unable to allocate required buffer: %d needed, "
|
|
|
|
"%d available\n", needed, len);
|
|
|
|
return 0;
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
}
|
2008-12-09 23:07:59 +00:00
|
|
|
|
2008-12-26 07:03:22 +00:00
|
|
|
struct scaler_context ctx;
|
2009-01-04 21:22:05 +00:00
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
2009-01-08 02:49:23 +00:00
|
|
|
cpu_boost(true);
|
2009-01-04 21:22:05 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx.store_part = store_part;
|
|
|
|
ctx.args = args;
|
2008-12-09 23:07:59 +00:00
|
|
|
#if MAX_SC_STACK_ALLOC
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx.buf = needed > len ? sc_buf : buf;
|
2008-12-09 23:07:59 +00:00
|
|
|
#else
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx.buf = buf;
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx.len = len;
|
|
|
|
ctx.bm = bm;
|
|
|
|
ctx.src = src;
|
|
|
|
ctx.dither = dither;
|
2009-01-04 21:22:05 +00:00
|
|
|
#ifndef PLUGIN
|
|
|
|
ctx.output_row = output_row_native;
|
2008-12-26 07:05:13 +00:00
|
|
|
if (format)
|
2009-01-04 21:22:05 +00:00
|
|
|
#endif
|
2008-12-26 07:05:13 +00:00
|
|
|
ctx.output_row = format->output_row;
|
2008-12-09 23:07:59 +00:00
|
|
|
#ifdef HAVE_UPSCALER
|
2008-12-26 07:03:22 +00:00
|
|
|
if (sw > dw)
|
|
|
|
{
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
ctx.h_scaler = scale_h_area;
|
|
|
|
scale_h_area_setup(&ctx);
|
2008-12-09 23:07:59 +00:00
|
|
|
#ifdef HAVE_UPSCALER
|
2008-12-26 07:03:22 +00:00
|
|
|
} else {
|
|
|
|
ctx.h_scaler = scale_h_linear;
|
|
|
|
scale_h_linear_setup(&ctx);
|
|
|
|
}
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2009-01-20 17:24:49 +00:00
|
|
|
SC_MUL_INIT;
|
2008-12-26 07:03:22 +00:00
|
|
|
#ifdef HAVE_UPSCALER
|
|
|
|
if (sh > dh)
|
2008-12-09 23:07:59 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
ret = scale_v_area(rset, &ctx);
|
|
|
|
#ifdef HAVE_UPSCALER
|
|
|
|
else
|
|
|
|
ret = scale_v_linear(rset, &ctx);
|
|
|
|
#endif
|
2009-01-20 17:24:49 +00:00
|
|
|
SC_MUL_END;
|
2009-01-04 21:22:05 +00:00
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
2009-01-08 02:49:23 +00:00
|
|
|
cpu_boost(false);
|
2009-01-04 21:22:05 +00:00
|
|
|
#endif
|
2008-12-26 07:03:22 +00:00
|
|
|
if (!ret)
|
|
|
|
return 0;
|
2008-12-26 07:05:13 +00:00
|
|
|
return 1;
|
2008-12-09 23:07:59 +00:00
|
|
|
}
|