blob: 235e071f7b9264dd1c0e0e424e3a9ecbc5b406f5 [file] [log] [blame]
Andrew Mahone781421a2008-12-09 23:07:59 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Michael Sevakis441fca12008-12-10 21:10:34 +00008 * $Id$
Andrew Mahone781421a2008-12-09 23:07:59 +00009 *
10 * Copyright (C) 2008 by Akio Idehara, Andrew Mahone
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/*
23 * Implementation of area average and linear row and vertical scalers, and
24 * nearest-neighbor grey scaler (C) 2008 Andrew Mahone
25 *
26 * All files in this archive are subject to the GNU General Public License.
27 * See the file COPYING in the source tree root for full license agreement.
28 *
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
30 * KIND, either express or implied.
31 *
32 ****************************************************************************/
33
Andrew Mahone2fbf0972009-01-13 13:48:26 +000034#include <system.h>
Andrew Mahone781421a2008-12-09 23:07:59 +000035#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
Andrew Mahone524c5402008-12-14 17:58:04 +000038#include <general.h>
Andrew Mahone781421a2008-12-09 23:07:59 +000039#include "inttypes.h"
Andrew Mahone4eedc932009-01-04 21:22:05 +000040#ifndef PLUGIN
Andrew Mahone781421a2008-12-09 23:07:59 +000041#include "debug.h"
Andrew Mahone4eedc932009-01-04 21:22:05 +000042#endif
Andrew Mahone781421a2008-12-09 23:07:59 +000043#include "lcd.h"
44#include "file.h"
45#ifdef HAVE_REMOTE_LCD
46#include "lcd-remote.h"
47#endif
48#ifdef ROCKBOX_DEBUG_SCALERS
49#define SDEBUGF DEBUGF
50#else
51#define SDEBUGF(...)
52#endif
53#ifndef __PCTOOL__
54#include "config.h"
55#include "system.h"
Andrew Mahone4eedc932009-01-04 21:22:05 +000056#include <bmp.h>
Andrew Mahone781421a2008-12-09 23:07:59 +000057#include "resize.h"
Andrew Mahone781421a2008-12-09 23:07:59 +000058#else
59#undef DEBUGF
60#define DEBUGF(...)
61#endif
62
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000063/* calculate the maximum dimensions which will preserve the aspect ration of
64 src while fitting in the constraints passed in dst, and store result in dst,
65 returning 0 if rounding and 1 if not rounding.
66*/
67int recalc_dimension(struct dim *dst, struct dim *src)
68{
Andrew Mahone738a5642009-01-13 14:41:29 +000069 /* This only looks backwards. The input image size is being pre-scaled by
70 * the inverse of the pixel aspect ratio, so that once the size it scaled
71 * to meet the output constraints, the scaled image will have appropriate
72 * proportions.
73 */
74 int sw = src->width * LCD_PIXEL_ASPECT_HEIGHT;
75 int sh = src->height * LCD_PIXEL_ASPECT_WIDTH;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000076 int tmp;
77 if (dst->width <= 0)
78 dst->width = LCD_WIDTH;
79 if (dst->height <= 0)
80 dst->height = LCD_HEIGHT;
81#ifndef HAVE_UPSCALER
Andrew Mahone738a5642009-01-13 14:41:29 +000082 if (dst->width > sw || dst->height > sh)
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000083 {
Andrew Mahone738a5642009-01-13 14:41:29 +000084 dst->width = sw;
85 dst->height = sh;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000086 }
Andrew Mahone738a5642009-01-13 14:41:29 +000087 if (sw == dst->width && sh == dst->height)
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000088 return 1;
89#endif
Andrew Mahone738a5642009-01-13 14:41:29 +000090 tmp = (sw * dst->height + (sh >> 1)) / sh;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000091 if (tmp > dst->width)
Andrew Mahone738a5642009-01-13 14:41:29 +000092 dst->height = (sh * dst->width + (sw >> 1)) / sw;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000093 else
94 dst->width = tmp;
95 return src->width == dst->width && src->height == dst->height;
96}
97
Andrew Mahone995c89c2008-12-10 12:09:03 +000098/* All of these scalers use variations of Bresenham's algorithm to convert from
Andrew Mahonef7fa7e52008-12-26 07:03:22 +000099 their input to output coordinates. The error value is shifted from the
100 "classic" version such that it is a useful input to the scaling calculation.
Andrew Mahone995c89c2008-12-10 12:09:03 +0000101*/
Andrew Mahone781421a2008-12-09 23:07:59 +0000102
Andrew Mahone995c89c2008-12-10 12:09:03 +0000103#ifdef HAVE_LCD_COLOR
104/* dither + pack on channel of RGB565, R an B share a packing macro */
105#define PACKRB(v, delta) ((31 * v + (v >> 3) + delta) >> 8)
106#define PACKG(g, delta) ((63 * g + (g >> 2) + delta) >> 8)
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000107#endif
Andrew Mahone995c89c2008-12-10 12:09:03 +0000108
109/* read new img_part unconditionally, return false on failure */
Andrew Mahone781421a2008-12-09 23:07:59 +0000110#define FILL_BUF_INIT(img_part, store_part, args) { \
Andrew Mahone995c89c2008-12-10 12:09:03 +0000111 img_part = store_part(args); \
112 if (img_part == NULL) \
Andrew Mahone781421a2008-12-09 23:07:59 +0000113 return false; \
114}
115
Andrew Mahone995c89c2008-12-10 12:09:03 +0000116/* read new img_part if current one is empty, return false on failure */
Andrew Mahone781421a2008-12-09 23:07:59 +0000117#define FILL_BUF(img_part, store_part, args) { \
Andrew Mahone995c89c2008-12-10 12:09:03 +0000118 if (img_part->len == 0) \
119 img_part = store_part(args); \
120 if (img_part == NULL) \
Andrew Mahone781421a2008-12-09 23:07:59 +0000121 return false; \
122}
123
Andrew Mahone995c89c2008-12-10 12:09:03 +0000124/* Set up rounding and scale factors for horizontal area scaler */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000125static inline void scale_h_area_setup(struct scaler_context *ctx)
Andrew Mahone781421a2008-12-09 23:07:59 +0000126{
Andrew Mahone995c89c2008-12-10 12:09:03 +0000127/* sum is output value * src->width */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000128 SDEBUGF("scale_h_area_setup\n");
129 ctx->divisor = ctx->src->width;
Andrew Mahone781421a2008-12-09 23:07:59 +0000130}
131
132/* horizontal area average scaler */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000133static bool scale_h_area(void *out_line_ptr,
Andrew Mahone781421a2008-12-09 23:07:59 +0000134 struct scaler_context *ctx, bool accum)
135{
136 SDEBUGF("scale_h_area\n");
137 unsigned int ix, ox, oxe, mul;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000138#ifdef HAVE_LCD_COLOR
139 struct uint32_rgb rgbvalacc = { 0, 0, 0 },
140 rgbvaltmp = { 0, 0, 0 },
141 *out_line = (struct uint32_rgb *)out_line_ptr;
142#else
143 uint32_t acc = 0, tmp = 0, *out_line = (uint32_t*)out_line_ptr;
144#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000145 struct img_part *part;
146 FILL_BUF_INIT(part,ctx->store_part,ctx->args);
147 ox = 0;
148 oxe = 0;
Andrew Mahone781421a2008-12-09 23:07:59 +0000149 mul = 0;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000150 /* give other tasks a chance to run */
Andrew Mahone07e982d2009-01-08 02:49:23 +0000151 yield();
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000152 for (ix = 0; ix < (unsigned int)ctx->src->width; ix++)
Andrew Mahone781421a2008-12-09 23:07:59 +0000153 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000154 oxe += ctx->bm->width;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000155 /* end of current area has been reached */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000156 /* fill buffer if needed */
157 FILL_BUF(part,ctx->store_part,ctx->args);
158#ifdef HAVE_LCD_COLOR
159 if (oxe >= (unsigned int)ctx->src->width)
Andrew Mahone781421a2008-12-09 23:07:59 +0000160 {
Andrew Mahone995c89c2008-12-10 12:09:03 +0000161 /* "reset" error, which now represents partial coverage of next
162 pixel by the next area
163 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000164 oxe -= ctx->src->width;
165
Andrew Mahone995c89c2008-12-10 12:09:03 +0000166 /* add saved partial pixel from start of area */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000167 rgbvalacc.r = rgbvalacc.r * ctx->bm->width + rgbvaltmp.r * mul;
168 rgbvalacc.g = rgbvalacc.g * ctx->bm->width + rgbvaltmp.g * mul;
169 rgbvalacc.b = rgbvalacc.b * ctx->bm->width + rgbvaltmp.b * mul;
170
Andrew Mahone995c89c2008-12-10 12:09:03 +0000171 /* get new pixel , then add its partial coverage to this area */
172 rgbvaltmp.r = part->buf->red;
173 rgbvaltmp.g = part->buf->green;
174 rgbvaltmp.b = part->buf->blue;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000175 mul = ctx->bm->width - oxe;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000176 rgbvalacc.r += rgbvaltmp.r * mul;
177 rgbvalacc.g += rgbvaltmp.g * mul;
178 rgbvalacc.b += rgbvaltmp.b * mul;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000179 /* store or accumulate to output row */
180 if (accum)
181 {
182 rgbvalacc.r += out_line[ox].r;
183 rgbvalacc.g += out_line[ox].g;
184 rgbvalacc.b += out_line[ox].b;
185 }
186 out_line[ox].r = rgbvalacc.r;
187 out_line[ox].g = rgbvalacc.g;
188 out_line[ox].b = rgbvalacc.b;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000189 /* reset accumulator */
190 rgbvalacc.r = 0;
191 rgbvalacc.g = 0;
192 rgbvalacc.b = 0;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000193 mul = ctx->bm->width - mul;
Andrew Mahone781421a2008-12-09 23:07:59 +0000194 ox += 1;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000195 /* inside an area */
Andrew Mahone781421a2008-12-09 23:07:59 +0000196 } else {
Andrew Mahone995c89c2008-12-10 12:09:03 +0000197 /* add pixel value to accumulator */
198 rgbvalacc.r += part->buf->red;
199 rgbvalacc.g += part->buf->green;
200 rgbvalacc.b += part->buf->blue;
Andrew Mahone781421a2008-12-09 23:07:59 +0000201 }
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000202#else
203 if (oxe >= (unsigned int)ctx->src->width)
204 {
205 /* "reset" error, which now represents partial coverage of next
206 pixel by the next area
207 */
208 oxe -= ctx->src->width;
209
210 /* add saved partial pixel from start of area */
211 acc = acc * ctx->bm->width + tmp * mul;
212
213 /* get new pixel , then add its partial coverage to this area */
214 tmp = *(part->buf);
215 mul = ctx->bm->width - oxe;
216 acc += tmp * mul;
217 /* round, divide, and either store or accumulate to output row */
218 if (accum)
219 {
220 acc += out_line[ox];
221 }
222 out_line[ox] = acc;
223 /* reset accumulator */
224 acc = 0;
225 mul = ctx->bm->width - mul;
226 ox += 1;
227 /* inside an area */
228 } else {
229 /* add pixel value to accumulator */
230 acc += *(part->buf);
231 }
232#endif
233 part->buf++;
234 part->len--;
Andrew Mahone781421a2008-12-09 23:07:59 +0000235 }
236 return true;
237}
238
239/* vertical area average scaler */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000240static inline bool scale_v_area(struct rowset *rset, struct scaler_context *ctx)
Andrew Mahone781421a2008-12-09 23:07:59 +0000241{
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000242 uint32_t mul, x, oy, iy, oye;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000243
244 /* Set up rounding and scale factors */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000245 ctx->divisor *= ctx->src->height;
246 ctx->round = ctx->divisor >> 1;
Andrew Mahone1b132992009-01-20 17:24:49 +0000247 ctx->divisor = (((ctx->divisor >> 1) + SC_NUM) / ctx->divisor) << SC_FIX;
Andrew Mahone781421a2008-12-09 23:07:59 +0000248 mul = 0;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000249 oy = rset->rowstart;
Andrew Mahone781421a2008-12-09 23:07:59 +0000250 oye = 0;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000251#ifdef HAVE_LCD_COLOR
252 uint32_t *rowacc = (uint32_t *) ctx->buf,
253 *rowtmp = rowacc + 3 * ctx->bm->width;
Andrew Mahone07e982d2009-01-08 02:49:23 +0000254 memset((void *)ctx->buf, 0, ctx->bm->width * 2 * sizeof(struct uint32_rgb));
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000255#else
256 uint32_t *rowacc = (uint32_t *) ctx->buf,
257 *rowtmp = rowacc + ctx->bm->width;
Andrew Mahone07e982d2009-01-08 02:49:23 +0000258 memset((void *)ctx->buf, 0, ctx->bm->width * 2 * sizeof(uint32_t));
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000259#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000260 SDEBUGF("scale_v_area\n");
Andrew Mahone995c89c2008-12-10 12:09:03 +0000261 /* zero the accumulator and temp rows */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000262 for (iy = 0; iy < (unsigned int)ctx->src->height; iy++)
Andrew Mahone781421a2008-12-09 23:07:59 +0000263 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000264 oye += ctx->bm->height;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000265 /* end of current area has been reached */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000266 if (oye >= (unsigned int)ctx->src->height)
Andrew Mahone781421a2008-12-09 23:07:59 +0000267 {
Andrew Mahone995c89c2008-12-10 12:09:03 +0000268 /* "reset" error, which now represents partial coverage of the next
269 row by the next area
270 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000271 oye -= ctx->src->height;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000272 /* add stored partial row to accumulator */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000273#ifdef HAVE_LCD_COLOR
274 for (x = 0; x < 3 * (unsigned int)ctx->bm->width; x++)
275#else
276 for (x = 0; x < (unsigned int)ctx->bm->width; x++)
277#endif
278 rowacc[x] = rowacc[x] * ctx->bm->height + mul * rowtmp[x];
Andrew Mahone995c89c2008-12-10 12:09:03 +0000279 /* store new scaled row in temp row */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000280 if(!ctx->h_scaler(rowtmp, ctx, false))
Andrew Mahone995c89c2008-12-10 12:09:03 +0000281 return false;
282 /* add partial coverage by new row to this area, then round and
283 scale to final value
284 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000285 mul = ctx->bm->height - oye;
286#ifdef HAVE_LCD_COLOR
287 for (x = 0; x < 3 * (unsigned int)ctx->bm->width; x++)
288#else
289 for (x = 0; x < (unsigned int)ctx->bm->width; x++)
290#endif
291 rowacc[x] += mul * rowtmp[x];
292 ctx->output_row(oy, (void*)rowacc, ctx);
Andrew Mahone995c89c2008-12-10 12:09:03 +0000293 /* clear accumulator row, store partial coverage for next row */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000294#ifdef HAVE_LCD_COLOR
Andrew Mahone07e982d2009-01-08 02:49:23 +0000295 memset((void *)rowacc, 0, ctx->bm->width * sizeof(uint32_t) * 3);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000296#else
Andrew Mahone07e982d2009-01-08 02:49:23 +0000297 memset((void *)rowacc, 0, ctx->bm->width * sizeof(uint32_t));
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000298#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000299 mul = oye;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000300 oy += rset->rowstep;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000301 /* inside an area */
Andrew Mahone781421a2008-12-09 23:07:59 +0000302 } else {
Andrew Mahone995c89c2008-12-10 12:09:03 +0000303 /* accumulate new scaled row to rowacc */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000304 if (!ctx->h_scaler(rowacc, ctx, true))
Andrew Mahone995c89c2008-12-10 12:09:03 +0000305 return false;
Andrew Mahone781421a2008-12-09 23:07:59 +0000306 }
307 }
308 return true;
Andrew Mahone781421a2008-12-09 23:07:59 +0000309}
310
311#ifdef HAVE_UPSCALER
Andrew Mahone995c89c2008-12-10 12:09:03 +0000312/* Set up rounding and scale factors for the horizontal scaler. The divisor
313 is bm->width - 1, so that the first and last pixels in the row align
314 exactly between input and output
315*/
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000316static inline void scale_h_linear_setup(struct scaler_context *ctx)
Andrew Mahone781421a2008-12-09 23:07:59 +0000317{
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000318 ctx->divisor = ctx->bm->width - 1;
Andrew Mahone781421a2008-12-09 23:07:59 +0000319}
320
321/* horizontal linear scaler */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000322static bool scale_h_linear(void *out_line_ptr, struct scaler_context *ctx,
323 bool accum)
Andrew Mahone781421a2008-12-09 23:07:59 +0000324{
325 unsigned int ix, ox, ixe;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000326 /* type x = x is an ugly hack for hiding an unitialized data warning. The
327 values are conditionally initialized before use, but other values are
328 set such that this will occur before these are used.
329 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000330#ifdef HAVE_LCD_COLOR
331 struct uint32_rgb rgbval=rgbval, rgbinc=rgbinc,
332 *out_line = (struct uint32_rgb*)out_line_ptr;
333#else
334 uint32_t val=val, inc=inc, *out_line = (uint32_t*)out_line_ptr;
335#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000336 struct img_part *part;
337 SDEBUGF("scale_h_linear\n");
338 FILL_BUF_INIT(part,ctx->store_part,ctx->args);
339 ix = 0;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000340 /* The error is set so that values are initialized on the first pass. */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000341 ixe = ctx->bm->width - 1;
342 /* give other tasks a chance to run */
Andrew Mahone07e982d2009-01-08 02:49:23 +0000343 yield();
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000344 for (ox = 0; ox < (uint32_t)ctx->bm->width; ox++)
345 {
346#ifdef HAVE_LCD_COLOR
347 if (ixe >= ((uint32_t)ctx->bm->width - 1))
Andrew Mahone781421a2008-12-09 23:07:59 +0000348 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000349 /* Store the new "current" pixel value in rgbval, and the color
Andrew Mahone995c89c2008-12-10 12:09:03 +0000350 step value in rgbinc.
351 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000352 ixe -= (ctx->bm->width - 1);
Andrew Mahone781421a2008-12-09 23:07:59 +0000353 rgbinc.r = -(part->buf->red);
354 rgbinc.g = -(part->buf->green);
355 rgbinc.b = -(part->buf->blue);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000356 rgbval.r = (part->buf->red) * (ctx->bm->width - 1);
357 rgbval.g = (part->buf->green) * (ctx->bm->width - 1);
358 rgbval.b = (part->buf->blue) * (ctx->bm->width - 1);
Andrew Mahone781421a2008-12-09 23:07:59 +0000359 ix += 1;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000360 /* If this wasn't the last pixel, add the next one to rgbinc. */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000361 if (ix < (uint32_t)ctx->src->width) {
Andrew Mahone781421a2008-12-09 23:07:59 +0000362 part->buf++;
363 part->len--;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000364 /* Fetch new pixels if needed */
Andrew Mahone781421a2008-12-09 23:07:59 +0000365 FILL_BUF(part,ctx->store_part,ctx->args);
366 rgbinc.r += part->buf->red;
367 rgbinc.g += part->buf->green;
368 rgbinc.b += part->buf->blue;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000369 /* Add a partial step to rgbval, in this pixel isn't precisely
370 aligned with the new source pixel
371 */
Andrew Mahone781421a2008-12-09 23:07:59 +0000372 rgbval.r += rgbinc.r * ixe;
373 rgbval.g += rgbinc.g * ixe;
374 rgbval.b += rgbinc.b * ixe;
375 }
Andrew Mahone995c89c2008-12-10 12:09:03 +0000376 /* Now multiple the color increment to its proper value */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000377 rgbinc.r *= ctx->src->width - 1;
378 rgbinc.g *= ctx->src->width - 1;
379 rgbinc.b *= ctx->src->width - 1;
380 } else {
381 rgbval.r += rgbinc.r;
382 rgbval.g += rgbinc.g;
383 rgbval.b += rgbinc.b;
Andrew Mahone781421a2008-12-09 23:07:59 +0000384 }
Andrew Mahone995c89c2008-12-10 12:09:03 +0000385 /* round and scale values, and accumulate or store to output */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000386 if (accum)
387 {
388 out_line[ox].r += rgbval.r;
389 out_line[ox].g += rgbval.g;
390 out_line[ox].b += rgbval.b;
391 } else {
392 out_line[ox].r = rgbval.r;
393 out_line[ox].g = rgbval.g;
394 out_line[ox].b = rgbval.b;
395 }
396#else
397 if (ixe >= ((uint32_t)ctx->bm->width - 1))
398 {
399 /* Store the new "current" pixel value in rgbval, and the color
400 step value in rgbinc.
401 */
402 ixe -= (ctx->bm->width - 1);
403 val = *(part->buf);
404 inc = -val;
405 val *= (ctx->bm->width - 1);
406 ix += 1;
407 /* If this wasn't the last pixel, add the next one to rgbinc. */
408 if (ix < (uint32_t)ctx->src->width) {
409 part->buf++;
410 part->len--;
411 /* Fetch new pixels if needed */
412 FILL_BUF(part,ctx->store_part,ctx->args);
413 inc += *(part->buf);
414 /* Add a partial step to rgbval, in this pixel isn't precisely
415 aligned with the new source pixel
416 */
417 val += inc * ixe;
418 }
419 /* Now multiply the color increment to its proper value */
420 inc *= ctx->src->width - 1;
421 } else
422 val += inc;
423 /* round and scale values, and accumulate or store to output */
424 if (accum)
425 {
426 out_line[ox] += val;
427 } else {
428 out_line[ox] = val;
429 }
430#endif
431 ixe += ctx->src->width - 1;
Andrew Mahone781421a2008-12-09 23:07:59 +0000432 }
433 return true;
434}
435
436/* vertical linear scaler */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000437static inline bool scale_v_linear(struct rowset *rset,
438 struct scaler_context *ctx)
Andrew Mahone781421a2008-12-09 23:07:59 +0000439{
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000440 uint32_t mul, x, iy, iye;
441 int32_t oy;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000442 /* Set up scale and rounding factors, the divisor is bm->height - 1 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000443 ctx->divisor *= (ctx->bm->height - 1);
444 ctx->round = ctx->divisor >> 1;
Andrew Mahone1b132992009-01-20 17:24:49 +0000445 ctx->divisor = (((ctx->divisor >> 1) + SC_NUM) / ctx->divisor) << SC_FIX;
Andrew Mahone995c89c2008-12-10 12:09:03 +0000446 /* Set up our two temp buffers. The names are generic because they'll be
447 swapped each time a new input row is read
448 */
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000449#ifdef HAVE_LCD_COLOR
450 uint32_t *rowinc = (uint32_t *)(ctx->buf),
451 *rowval = rowinc + 3 * ctx->bm->width,
452 *rowtmp = rowval + 3 * ctx->bm->width;
453#else
454 uint32_t *rowinc = (uint32_t *)(ctx->buf),
455 *rowval = rowinc + ctx->bm->width,
456 *rowtmp = rowval + ctx->bm->width;
457#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000458
459 SDEBUGF("scale_v_linear\n");
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000460 mul = 0;
461 iy = 0;
462 iye = ctx->bm->height - 1;
463 /* get first scaled row in rowtmp */
464 if(!ctx->h_scaler((void*)rowtmp, ctx, false))
Andrew Mahone995c89c2008-12-10 12:09:03 +0000465 return false;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000466 for (oy = rset->rowstart; oy != rset->rowstop; oy += rset->rowstep)
Andrew Mahone781421a2008-12-09 23:07:59 +0000467 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000468 if (iye >= (uint32_t)ctx->bm->height - 1)
Andrew Mahone781421a2008-12-09 23:07:59 +0000469 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000470 iye -= ctx->bm->height - 1;
Andrew Mahone781421a2008-12-09 23:07:59 +0000471 iy += 1;
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000472#ifdef HAVE_LCD_COLOR
473 for (x = 0; x < 3 * (uint32_t)ctx->bm->width; x++)
474#else
475 for (x = 0; x < (uint32_t)ctx->bm->width; x++)
476#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000477 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000478 rowinc[x] = -rowtmp[x];
479 rowval[x] = rowtmp[x] * (ctx->bm->height - 1);
Andrew Mahone781421a2008-12-09 23:07:59 +0000480 }
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000481 if (iy < (uint32_t)ctx->src->height)
482 {
483 if (!ctx->h_scaler((void*)rowtmp, ctx, false))
484 return false;
485#ifdef HAVE_LCD_COLOR
486 for (x = 0; x < 3 * (uint32_t)ctx->bm->width; x++)
487#else
488 for (x = 0; x < (uint32_t)ctx->bm->width; x++)
489#endif
490 {
491 rowinc[x] += rowtmp[x];
492 rowval[x] += rowinc[x] * iye;
493 rowinc[x] *= ctx->src->height - 1;
494 }
495 }
496 } else
497#ifdef HAVE_LCD_COLOR
498 for (x = 0; x < 3 * (uint32_t)ctx->bm->width; x++)
499#else
500 for (x = 0; x < (uint32_t)ctx->bm->width; x++)
501#endif
502 rowval[x] += rowinc[x];
503 ctx->output_row(oy, (void*)rowval, ctx);
504 iye += ctx->src->height - 1;
Andrew Mahone781421a2008-12-09 23:07:59 +0000505 }
506 return true;
Andrew Mahone781421a2008-12-09 23:07:59 +0000507}
508#endif /* HAVE_UPSCALER */
Andrew Mahone781421a2008-12-09 23:07:59 +0000509
Andrew Mahone4eedc932009-01-04 21:22:05 +0000510#ifndef PLUGIN
511void output_row_native(uint32_t row, void * row_in, struct scaler_context *ctx)
Andrew Mahone781421a2008-12-09 23:07:59 +0000512{
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000513 int col;
514 int fb_width = BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0);
515 uint8_t dy = DITHERY(row);
516#ifdef HAVE_LCD_COLOR
517 struct uint32_rgb *qp = (struct uint32_rgb*)row_in;
518#else
519 uint32_t *qp = (uint32_t*)row_in;
520#endif
Andrew Mahone4eedc932009-01-04 21:22:05 +0000521 SDEBUGF("output_row: y: %lu in: %p\n",row, row_in);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000522#if LCD_DEPTH == 2
523#if LCD_PIXELFORMAT == HORIZONTAL_PACKING
524 /* greyscale iPods */
525 fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row;
526 int shift = 6;
527 int delta = 127;
528 unsigned bright;
529 unsigned data = 0;
530
531 for (col = 0; col < ctx->bm->width; col++) {
532 if (ctx->dither)
533 delta = DITHERXDY(col,dy);
Andrew Mahone1b132992009-01-20 17:24:49 +0000534 bright = SC_MUL((*qp++) + ctx->round,ctx->divisor);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000535 bright = (3 * bright + (bright >> 6) + delta) >> 8;
536 data |= (~bright & 3) << shift;
537 shift -= 2;
538 if (shift < 0) {
539 *dest++ = data;
540 data = 0;
541 shift = 6;
542 }
543 }
544 if (shift < 6)
545 *dest++ = data;
546#elif LCD_PIXELFORMAT == VERTICAL_PACKING
547 /* iriver H1x0 */
548 fb_data *dest = (fb_data *)ctx->bm->data + fb_width *
549 (row >> 2);
550 int shift = 2 * (row & 3);
551 int delta = 127;
552 unsigned bright;
553
554 for (col = 0; col < ctx->bm->width; col++) {
555 if (ctx->dither)
556 delta = DITHERXDY(col,dy);
Andrew Mahone1b132992009-01-20 17:24:49 +0000557 bright = SC_MUL((*qp++) + ctx->round, ctx->divisor);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000558 bright = (3 * bright + (bright >> 6) + delta) >> 8;
559 *dest++ |= (~bright & 3) << shift;
560 }
561#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
562 /* iAudio M3 */
563 fb_data *dest = (fb_data *)ctx->bm->data + fb_width *
564 (row >> 3);
565 int shift = row & 7;
566 int delta = 127;
567 unsigned bright;
568
569 for (col = 0; col < ctx->bm->width; col++) {
570 if (ctx->dither)
571 delta = DITHERXDY(col,dy);
Andrew Mahone1b132992009-01-20 17:24:49 +0000572 bright = SC_MUL((*qp++) + ctx->round, ctx->divisor);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000573 bright = (3 * bright + (bright >> 6) + delta) >> 8;
574 *dest++ |= vi_pattern[bright] << shift;
575 }
576#endif /* LCD_PIXELFORMAT */
577#elif LCD_DEPTH == 16
578 /* iriver h300, colour iPods, X5 */
579 fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row;
580 int delta = 127;
581 unsigned r, g, b;
582 struct uint32_rgb q0;
583
584 for (col = 0; col < ctx->bm->width; col++) {
585 if (ctx->dither)
586 delta = DITHERXDY(col,dy);
587 q0 = *qp++;
Andrew Mahone1b132992009-01-20 17:24:49 +0000588 r = SC_MUL(q0.r + ctx->round, ctx->divisor);
589 g = SC_MUL(q0.g + ctx->round, ctx->divisor);
590 b = SC_MUL(q0.b + ctx->round, ctx->divisor);
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000591 r = (31 * r + (r >> 3) + delta) >> 8;
592 g = (63 * g + (g >> 2) + delta) >> 8;
593 b = (31 * b + (b >> 3) + delta) >> 8;
594 *dest++ = LCD_RGBPACK_LCD(r, g, b);
595 }
596#endif /* LCD_DEPTH */
597}
Andrew Mahone4eedc932009-01-04 21:22:05 +0000598#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000599
600int resize_on_load(struct bitmap *bm, bool dither, struct dim *src,
601 struct rowset *rset, unsigned char *buf, unsigned int len,
Andrew Mahone90586202008-12-26 07:05:13 +0000602 const struct custom_format *format,
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000603 struct img_part* (*store_part)(void *args),
604 void *args)
605{
606
607#ifdef HAVE_UPSCALER
Andrew Mahone781421a2008-12-09 23:07:59 +0000608 const int sw = src->width;
609 const int sh = src->height;
610 const int dw = bm->width;
611 const int dh = bm->height;
Andrew Mahone781421a2008-12-09 23:07:59 +0000612#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000613 int ret;
614#ifdef HAVE_LCD_COLOR
615 unsigned int needed = sizeof(struct uint32_rgb) * 3 * bm->width;
Andrew Mahone781421a2008-12-09 23:07:59 +0000616#else
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000617 unsigned int needed = sizeof(uint32_t) * 3 * bm->width;
Andrew Mahone781421a2008-12-09 23:07:59 +0000618#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000619#if MAX_SC_STACK_ALLOC
620 uint8_t sc_buf[(needed <= len || needed > MAX_SC_STACK_ALLOC) ?
621 0 : needed];
Andrew Mahone781421a2008-12-09 23:07:59 +0000622#endif
Andrew Mahone2fbf0972009-01-13 13:48:26 +0000623 ALIGN_BUFFER(buf, len, sizeof(uint32_t));
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000624 if (needed > len)
625 {
626#if MAX_SC_STACK_ALLOC
627 if (needed > MAX_SC_STACK_ALLOC)
Andrew Mahone781421a2008-12-09 23:07:59 +0000628 {
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000629 DEBUGF("unable to allocate required buffer: %d needed, "
630 "%d available, %d permitted from stack\n",
631 needed, len, MAX_SC_STACK_ALLOC);
632 return 0;
Andrew Mahone781421a2008-12-09 23:07:59 +0000633 }
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000634 if (sizeof(sc_buf) < needed)
635 {
636 DEBUGF("failed to allocate large enough buffer on stack: "
637 "%d needed, only got %d",
638 needed, MAX_SC_STACK_ALLOC);
639 return 0;
640 }
641#else
642 DEBUGF("unable to allocate required buffer: %d needed, "
643 "%d available\n", needed, len);
644 return 0;
645#endif
Andrew Mahone781421a2008-12-09 23:07:59 +0000646 }
Andrew Mahone781421a2008-12-09 23:07:59 +0000647
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000648 struct scaler_context ctx;
Andrew Mahone4eedc932009-01-04 21:22:05 +0000649#ifdef HAVE_ADJUSTABLE_CPU_FREQ
Andrew Mahone07e982d2009-01-08 02:49:23 +0000650 cpu_boost(true);
Andrew Mahone4eedc932009-01-04 21:22:05 +0000651#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000652 ctx.store_part = store_part;
653 ctx.args = args;
Andrew Mahone781421a2008-12-09 23:07:59 +0000654#if MAX_SC_STACK_ALLOC
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000655 ctx.buf = needed > len ? sc_buf : buf;
Andrew Mahone781421a2008-12-09 23:07:59 +0000656#else
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000657 ctx.buf = buf;
Andrew Mahone781421a2008-12-09 23:07:59 +0000658#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000659 ctx.len = len;
660 ctx.bm = bm;
661 ctx.src = src;
662 ctx.dither = dither;
Andrew Mahone4eedc932009-01-04 21:22:05 +0000663#ifndef PLUGIN
664 ctx.output_row = output_row_native;
Andrew Mahone90586202008-12-26 07:05:13 +0000665 if (format)
Andrew Mahone4eedc932009-01-04 21:22:05 +0000666#endif
Andrew Mahone90586202008-12-26 07:05:13 +0000667 ctx.output_row = format->output_row;
Andrew Mahone781421a2008-12-09 23:07:59 +0000668#ifdef HAVE_UPSCALER
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000669 if (sw > dw)
670 {
Andrew Mahone781421a2008-12-09 23:07:59 +0000671#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000672 ctx.h_scaler = scale_h_area;
673 scale_h_area_setup(&ctx);
Andrew Mahone781421a2008-12-09 23:07:59 +0000674#ifdef HAVE_UPSCALER
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000675 } else {
676 ctx.h_scaler = scale_h_linear;
677 scale_h_linear_setup(&ctx);
678 }
Andrew Mahone781421a2008-12-09 23:07:59 +0000679#endif
Andrew Mahone1b132992009-01-20 17:24:49 +0000680 SC_MUL_INIT;
Andrew Mahone781421a2008-12-09 23:07:59 +0000681#ifdef HAVE_UPSCALER
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000682 if (sh > dh)
Andrew Mahone781421a2008-12-09 23:07:59 +0000683#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000684 ret = scale_v_area(rset, &ctx);
Andrew Mahone781421a2008-12-09 23:07:59 +0000685#ifdef HAVE_UPSCALER
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000686 else
687 ret = scale_v_linear(rset, &ctx);
Andrew Mahone781421a2008-12-09 23:07:59 +0000688#endif
Andrew Mahone1b132992009-01-20 17:24:49 +0000689 SC_MUL_END;
Andrew Mahone4eedc932009-01-04 21:22:05 +0000690#ifdef HAVE_ADJUSTABLE_CPU_FREQ
Andrew Mahone07e982d2009-01-08 02:49:23 +0000691 cpu_boost(false);
Andrew Mahone4eedc932009-01-04 21:22:05 +0000692#endif
Andrew Mahonef7fa7e52008-12-26 07:03:22 +0000693 if (!ret)
694 return 0;
Andrew Mahone90586202008-12-26 07:05:13 +0000695 return 1;
Andrew Mahone781421a2008-12-09 23:07:59 +0000696}