A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
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
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <stdint.h>
38
39#include "general.h"
40#include "kernel.h"
41#include "system.h"
42#ifndef PLUGIN
43#include "debug.h"
44#endif
45#include "lcd.h"
46#include "file.h"
47#ifdef HAVE_REMOTE_LCD
48#include "lcd-remote.h"
49#endif
50#ifdef ROCKBOX_DEBUG_SCALERS
51#define SDEBUGF DEBUGF
52#else
53#define SDEBUGF(...)
54#endif
55#ifndef __PCTOOL__
56#include "config.h"
57#include "system.h"
58#include <bmp.h>
59#include "resize.h"
60#else
61#undef DEBUGF
62#define DEBUGF(...)
63#endif
64#include <jpeg_load.h>
65
66#define MULUQ(a, b) ((a) * (b))
67#define MULQ(a, b) ((a) * (b))
68
69#ifdef HAVE_LCD_COLOR
70#define CHANNEL_BYTES (sizeof(struct uint32_argb)/sizeof(uint32_t))
71#else
72#define CHANNEL_BYTES (sizeof(uint32_t)/sizeof(uint32_t)) /* packed */
73#endif
74
75/* calculate the maximum dimensions which will preserve the aspect ration of
76 src while fitting in the constraints passed in dst, and store result in dst,
77 returning 0 if rounding and 1 if not rounding.
78*/
79int recalc_dimension(struct dim *dst, struct dim *src)
80{
81 /* This only looks backwards. The input image size is being pre-scaled by
82 * the inverse of the pixel aspect ratio, so that once the size it scaled
83 * to meet the output constraints, the scaled image will have appropriate
84 * proportions.
85 */
86 int sw = src->width * LCD_PIXEL_ASPECT_HEIGHT;
87 int sh = src->height * LCD_PIXEL_ASPECT_WIDTH;
88 int tmp;
89 if (dst->width <= 0)
90 dst->width = LCD_WIDTH;
91 if (dst->height <= 0)
92 dst->height = LCD_HEIGHT;
93#ifndef HAVE_UPSCALER
94 if (dst->width > sw || dst->height > sh)
95 {
96 dst->width = sw;
97 dst->height = sh;
98 }
99 if (sw == dst->width && sh == dst->height)
100 return 1;
101#endif
102 tmp = (sw * dst->height + (sh >> 1)) / sh;
103 if (tmp > dst->width)
104 dst->height = (sh * dst->width + (sw >> 1)) / sw;
105 else
106 dst->width = tmp;
107 return src->width == dst->width && src->height == dst->height;
108}
109
110/* All of these scalers use variations of Bresenham's algorithm to convert from
111 their input to output coordinates. The error value is shifted from the
112 "classic" version such that it is a useful input to the scaling calculation.
113*/
114
115#ifdef HAVE_LCD_COLOR
116/* dither + pack on channel of RGB565, R an B share a packing macro */
117#define PACKRB(v, delta) ((31 * v + (v >> 3) + delta) >> 8)
118#define PACKG(g, delta) ((63 * g + (g >> 2) + delta) >> 8)
119#endif
120
121/* read new img_part unconditionally, return false on failure */
122#define FILL_BUF_INIT(img_part, store_part, args) { \
123 img_part = store_part(args); \
124 if (img_part == NULL) \
125 return false; \
126}
127
128/* read new img_part if current one is empty, return false on failure */
129#define FILL_BUF(img_part, store_part, args) { \
130 if (img_part->len == 0) \
131 img_part = store_part(args); \
132 if (img_part == NULL) \
133 return false; \
134}
135
136#if defined(CPU_COLDFIRE)
137#define MAC(op1, op2, num) \
138 asm volatile( \
139 "mac.l %0, %1, %%acc" #num \
140 : \
141 : "%d" (op1), "d" (op2)\
142 )
143#define MAC_OUT(dest, num) \
144 asm volatile( \
145 "movclr.l %%acc" #num ", %0" \
146 : "=d" (dest) \
147 )
148#endif
149
150/* horizontal area average scaler */
151static bool scale_h_area(void *out_line_ptr,
152 struct scaler_context *ctx, bool accum)
153{
154 SDEBUGF("scale_h_area\n");
155 unsigned int ix, ox, oxe, mul;
156 const uint32_t h_i_val = ctx->h_i_val,
157 h_o_val = ctx->h_o_val;
158#ifdef HAVE_LCD_COLOR
159 struct uint32_argb rgbvalacc = { 0, 0, 0, 0 },
160 rgbvaltmp = { 0, 0, 0, 0 },
161 *out_line = (struct uint32_argb *)out_line_ptr;
162#else
163 uint32_t acc = 0, tmp = 0, *out_line = (uint32_t*)out_line_ptr;
164#endif
165 struct img_part *part;
166 FILL_BUF_INIT(part,ctx->store_part,ctx->args);
167 ox = 0;
168 oxe = 0;
169 mul = 0;
170 /* give other tasks a chance to run */
171 yield();
172 for (ix = 0; ix < (unsigned int)ctx->src->width; ix++)
173 {
174 oxe += h_o_val;
175 /* end of current area has been reached */
176 /* fill buffer if needed */
177 FILL_BUF(part,ctx->store_part,ctx->args);
178#ifdef HAVE_LCD_COLOR
179 if (oxe >= h_i_val)
180 {
181 /* "reset" error, which now represents partial coverage of next
182 pixel by the next area
183 */
184 oxe -= h_i_val;
185
186#if defined(CPU_COLDFIRE)
187/* Coldfire EMAC math */
188 /* add saved partial pixel from start of area */
189 MAC(rgbvalacc.r, h_o_val, 0);
190 MAC(rgbvalacc.g, h_o_val, 1);
191 MAC(rgbvalacc.b, h_o_val, 2);
192 MAC(rgbvalacc.a, h_o_val, 3);
193 MAC(rgbvaltmp.r, mul, 0);
194 MAC(rgbvaltmp.g, mul, 1);
195 MAC(rgbvaltmp.b, mul, 2);
196 MAC(rgbvaltmp.a, mul, 3);
197 /* get new pixel , then add its partial coverage to this area */
198 mul = h_o_val - oxe;
199 rgbvaltmp.r = part->buf->red;
200 rgbvaltmp.g = part->buf->green;
201 rgbvaltmp.b = part->buf->blue;
202 rgbvaltmp.a = part->buf->alpha;
203 MAC(rgbvaltmp.r, mul, 0);
204 MAC(rgbvaltmp.g, mul, 1);
205 MAC(rgbvaltmp.b, mul, 2);
206 MAC(rgbvaltmp.a, mul, 3);
207 MAC_OUT(rgbvalacc.r, 0);
208 MAC_OUT(rgbvalacc.g, 1);
209 MAC_OUT(rgbvalacc.b, 2);
210 MAC_OUT(rgbvalacc.a, 3);
211#else
212/* generic C math */
213 /* add saved partial pixel from start of area */
214 rgbvalacc.r = rgbvalacc.r * h_o_val + rgbvaltmp.r * mul;
215 rgbvalacc.g = rgbvalacc.g * h_o_val + rgbvaltmp.g * mul;
216 rgbvalacc.b = rgbvalacc.b * h_o_val + rgbvaltmp.b * mul;
217 rgbvalacc.a = rgbvalacc.a * h_o_val + rgbvaltmp.a * mul;
218
219 /* get new pixel , then add its partial coverage to this area */
220 rgbvaltmp.r = part->buf->red;
221 rgbvaltmp.g = part->buf->green;
222 rgbvaltmp.b = part->buf->blue;
223 rgbvaltmp.a = part->buf->alpha;
224 mul = h_o_val - oxe;
225 rgbvalacc.r += rgbvaltmp.r * mul;
226 rgbvalacc.g += rgbvaltmp.g * mul;
227 rgbvalacc.b += rgbvaltmp.b * mul;
228 rgbvalacc.a += rgbvaltmp.a * mul;
229#endif /* CPU */
230 rgbvalacc.r = (rgbvalacc.r + (1 << 21)) >> 22;
231 rgbvalacc.g = (rgbvalacc.g + (1 << 21)) >> 22;
232 rgbvalacc.b = (rgbvalacc.b + (1 << 21)) >> 22;
233 rgbvalacc.a = (rgbvalacc.a + (1 << 21)) >> 22;
234 /* store or accumulate to output row */
235 if (accum)
236 {
237 rgbvalacc.r += out_line[ox].r;
238 rgbvalacc.g += out_line[ox].g;
239 rgbvalacc.b += out_line[ox].b;
240 rgbvalacc.a += out_line[ox].a;
241 }
242 out_line[ox].r = rgbvalacc.r;
243 out_line[ox].g = rgbvalacc.g;
244 out_line[ox].b = rgbvalacc.b;
245 out_line[ox].a = rgbvalacc.a;
246 /* reset accumulator */
247 rgbvalacc.r = 0;
248 rgbvalacc.g = 0;
249 rgbvalacc.b = 0;
250 rgbvalacc.a = 0;
251 mul = oxe;
252 ox += 1;
253 /* inside an area */
254 } else {
255 /* add pixel value to accumulator */
256 rgbvalacc.r += part->buf->red;
257 rgbvalacc.g += part->buf->green;
258 rgbvalacc.b += part->buf->blue;
259 rgbvalacc.a += part->buf->alpha;
260 }
261#else
262 if (oxe >= h_i_val)
263 {
264 /* "reset" error, which now represents partial coverage of next
265 pixel by the next area
266 */
267 oxe -= h_i_val;
268#if defined(CPU_COLDFIRE)
269/* Coldfire EMAC math */
270 /* add saved partial pixel from start of area */
271 MAC(acc, h_o_val, 0);
272 MAC(tmp, mul, 0);
273 /* get new pixel , then add its partial coverage to this area */
274 tmp = *(part->buf);
275 mul = h_o_val - oxe;
276 MAC(tmp, mul, 0);
277 MAC_OUT(acc, 0);
278#else
279/* generic C math */
280 /* add saved partial pixel from start of area */
281 acc = (acc * h_o_val) + (tmp * mul);
282
283 /* get new pixel , then add its partial coverage to this area */
284 tmp = *(part->buf);
285 mul = h_o_val - oxe;
286 acc += tmp * mul;
287#endif /* CPU */
288 /* round, divide, and either store or accumulate to output row */
289 acc = (acc + (1 << 21)) >> 22;
290 if (accum)
291 {
292 acc += out_line[ox];
293 }
294 out_line[ox] = acc;
295 /* reset accumulator */
296 acc = 0;
297 mul = oxe;
298 ox += 1;
299 /* inside an area */
300 } else {
301 /* add pixel value to accumulator */
302 acc += *(part->buf);
303 }
304#endif
305 part->buf++;
306 part->len--;
307 }
308 return true;
309}
310
311/* vertical area average scaler */
312static inline bool scale_v_area(struct rowset *rset, struct scaler_context *ctx)
313{
314 uint32_t mul, oy, iy, oye;
315 const uint32_t v_i_val = ctx->v_i_val,
316 v_o_val = ctx->v_o_val;
317
318 /* Set up rounding and scale factors */
319 mul = 0;
320 oy = rset->rowstart;
321 oye = 0;
322 uint32_t *rowacc = (uint32_t *) ctx->buf,
323 *rowtmp = rowacc + ctx->bm->width * CHANNEL_BYTES,
324 *rowacc_px, *rowtmp_px;
325 memset((void *)ctx->buf, 0, ctx->bm->width * 2 * sizeof(uint32_t)*CHANNEL_BYTES);
326 SDEBUGF("scale_v_area\n");
327 /* zero the accumulator and temp rows */
328 for (iy = 0; iy < (unsigned int)ctx->src->height; iy++)
329 {
330 oye += v_o_val;
331 /* end of current area has been reached */
332 if (oye >= v_i_val)
333 {
334 /* "reset" error, which now represents partial coverage of the next
335 row by the next area
336 */
337 oye -= v_i_val;
338 /* add stored partial row to accumulator */
339 for(rowacc_px = rowacc, rowtmp_px = rowtmp; rowacc_px != rowtmp;
340 rowacc_px++, rowtmp_px++)
341 *rowacc_px = *rowacc_px * v_o_val + *rowtmp_px * mul;
342 /* store new scaled row in temp row */
343 if(!ctx->h_scaler(rowtmp, ctx, false))
344 return false;
345 /* add partial coverage by new row to this area, then round and
346 scale to final value
347 */
348 mul = v_o_val - oye;
349 for(rowacc_px = rowacc, rowtmp_px = rowtmp; rowacc_px != rowtmp;
350 rowacc_px++, rowtmp_px++)
351 *rowacc_px += mul * *rowtmp_px;
352 ctx->output_row(oy, (void*)rowacc, ctx);
353 /* clear accumulator row, store partial coverage for next row */
354 memset((void *)rowacc, 0, ctx->bm->width * sizeof(uint32_t) * CHANNEL_BYTES);
355 mul = oye;
356 oy += rset->rowstep;
357 /* inside an area */
358 } else {
359 /* accumulate new scaled row to rowacc */
360 if (!ctx->h_scaler(rowacc, ctx, true))
361 return false;
362 }
363 }
364 return true;
365}
366
367#ifdef HAVE_UPSCALER
368/* horizontal linear scaler */
369static bool scale_h_linear(void *out_line_ptr, struct scaler_context *ctx,
370 bool accum)
371{
372 unsigned int ix, ox, ixe;
373 const uint32_t h_i_val = ctx->h_i_val,
374 h_o_val = ctx->h_o_val;
375 /* type x = x is an ugly hack for hiding an unitialized data warning. The
376 values are conditionally initialized before use, but other values are
377 set such that this will occur before these are used.
378 */
379#ifdef HAVE_LCD_COLOR
380 struct uint32_argb rgbval=rgbval, rgbinc=rgbinc,
381 *out_line = (struct uint32_argb*)out_line_ptr;
382#else
383 uint32_t val=val, inc=inc, *out_line = (uint32_t*)out_line_ptr;
384#endif
385 struct img_part *part;
386 SDEBUGF("scale_h_linear\n");
387 FILL_BUF_INIT(part,ctx->store_part,ctx->args);
388 ix = 0;
389 /* The error is set so that values are initialized on the first pass. */
390 ixe = h_o_val;
391 /* give other tasks a chance to run */
392 yield();
393 for (ox = 0; ox < (uint32_t)ctx->bm->width; ox++)
394 {
395#ifdef HAVE_LCD_COLOR
396 if (ixe >= h_o_val)
397 {
398 /* Store the new "current" pixel value in rgbval, and the color
399 step value in rgbinc.
400 */
401 ixe -= h_o_val;
402 rgbinc.r = -(part->buf->red);
403 rgbinc.g = -(part->buf->green);
404 rgbinc.b = -(part->buf->blue);
405 rgbinc.a = -(part->buf->alpha);
406#if defined(CPU_COLDFIRE)
407/* Coldfire EMAC math */
408 MAC(part->buf->red, h_o_val, 0);
409 MAC(part->buf->green, h_o_val, 1);
410 MAC(part->buf->blue, h_o_val, 2);
411 MAC(part->buf->alpha, h_o_val, 3);
412#else
413/* generic C math */
414 rgbval.r = (part->buf->red) * h_o_val;
415 rgbval.g = (part->buf->green) * h_o_val;
416 rgbval.b = (part->buf->blue) * h_o_val;
417 rgbval.a = (part->buf->alpha) * h_o_val;
418#endif /* CPU */
419 ix += 1;
420 /* If this wasn't the last pixel, add the next one to rgbinc. */
421 if (LIKELY(ix < (uint32_t)ctx->src->width)) {
422 part->buf++;
423 part->len--;
424 /* Fetch new pixels if needed */
425 FILL_BUF(part,ctx->store_part,ctx->args);
426 rgbinc.r += part->buf->red;
427 rgbinc.g += part->buf->green;
428 rgbinc.b += part->buf->blue;
429 rgbinc.a += part->buf->alpha;
430 /* Add a partial step to rgbval, in this pixel isn't precisely
431 aligned with the new source pixel
432 */
433#if defined(CPU_COLDFIRE)
434/* Coldfire EMAC math */
435 MAC(rgbinc.r, ixe, 0);
436 MAC(rgbinc.g, ixe, 1);
437 MAC(rgbinc.b, ixe, 2);
438 MAC(rgbinc.a, ixe, 3);
439#else
440/* generic C math */
441 rgbval.r += rgbinc.r * ixe;
442 rgbval.g += rgbinc.g * ixe;
443 rgbval.b += rgbinc.b * ixe;
444 rgbval.a += rgbinc.a * ixe;
445#endif
446 }
447#if defined(CPU_COLDFIRE)
448/* get final EMAC result out of ACC registers */
449 MAC_OUT(rgbval.r, 0);
450 MAC_OUT(rgbval.g, 1);
451 MAC_OUT(rgbval.b, 2);
452 MAC_OUT(rgbval.a, 3);
453#endif
454 /* Now multiply the color increment to its proper value */
455 rgbinc.r *= h_i_val;
456 rgbinc.g *= h_i_val;
457 rgbinc.b *= h_i_val;
458 rgbinc.a *= h_i_val;
459 } else {
460 rgbval.r += rgbinc.r;
461 rgbval.g += rgbinc.g;
462 rgbval.b += rgbinc.b;
463 rgbval.a += rgbinc.a;
464 }
465 /* round and scale values, and accumulate or store to output */
466 if (accum)
467 {
468 out_line[ox].r += (rgbval.r + (1 << 21)) >> 22;
469 out_line[ox].g += (rgbval.g + (1 << 21)) >> 22;
470 out_line[ox].b += (rgbval.b + (1 << 21)) >> 22;
471 out_line[ox].a += (rgbval.a + (1 << 21)) >> 22;
472 } else {
473 out_line[ox].r = (rgbval.r + (1 << 21)) >> 22;
474 out_line[ox].g = (rgbval.g + (1 << 21)) >> 22;
475 out_line[ox].b = (rgbval.b + (1 << 21)) >> 22;
476 out_line[ox].a = (rgbval.a + (1 << 21)) >> 22;
477 }
478#else
479 if (ixe >= h_o_val)
480 {
481 /* Store the new "current" pixel value in rgbval, and the color
482 step value in rgbinc.
483 */
484 ixe -= h_o_val;
485 val = *(part->buf);
486 inc = -val;
487#if defined(CPU_COLDFIRE)
488/* Coldfire EMAC math */
489 MAC(val, h_o_val, 0);
490#else
491/* generic C math */
492 val = val * h_o_val;
493#endif
494 ix += 1;
495 /* If this wasn't the last pixel, add the next one to rgbinc. */
496 if (LIKELY(ix < (uint32_t)ctx->src->width)) {
497 part->buf++;
498 part->len--;
499 /* Fetch new pixels if needed */
500 FILL_BUF(part,ctx->store_part,ctx->args);
501 inc += *(part->buf);
502 /* Add a partial step to rgbval, in this pixel isn't precisely
503 aligned with the new source pixel
504 */
505#if defined(CPU_COLDFIRE)
506/* Coldfire EMAC math */
507 MAC(inc, ixe, 0);
508#else
509/* generic C math */
510 val += inc * ixe;
511#endif
512 }
513#if defined(CPU_COLDFIRE)
514/* get final EMAC result out of ACC register */
515 MAC_OUT(val, 0);
516#endif
517 /* Now multiply the color increment to its proper value */
518/* generic C math */
519 inc *= h_i_val;
520 } else
521 val += inc;
522 /* round and scale values, and accumulate or store to output */
523 if (accum)
524 {
525 out_line[ox] += (val + (1 << 21)) >> 22;
526 } else {
527 out_line[ox] = (val + (1 << 21)) >> 22;
528 }
529#endif
530 ixe += h_i_val;
531 }
532 return true;
533}
534
535/* vertical linear scaler */
536static inline bool scale_v_linear(struct rowset *rset,
537 struct scaler_context *ctx)
538{
539 uint32_t iy, iye;
540 int32_t oy;
541 const uint32_t v_i_val = ctx->v_i_val,
542 v_o_val = ctx->v_o_val;
543 /* Set up our buffers, to store the increment and current value for each
544 column, and one temp buffer used to read in new rows.
545 */
546 uint32_t *rowinc = (uint32_t *)(ctx->buf),
547 *rowval = rowinc + ctx->bm->width * CHANNEL_BYTES,
548 *rowtmp = rowval + ctx->bm->width * CHANNEL_BYTES,
549 *rowinc_px, *rowval_px, *rowtmp_px;
550
551 SDEBUGF("scale_v_linear\n");
552 iy = 0;
553 iye = v_o_val;
554 /* get first scaled row in rowtmp */
555 if(!ctx->h_scaler((void*)rowtmp, ctx, false))
556 return false;
557 for (oy = rset->rowstart; oy != rset->rowstop; oy += rset->rowstep)
558 {
559 if (iye >= v_o_val)
560 {
561 iye -= v_o_val;
562 iy += 1;
563 for(rowinc_px = rowinc, rowtmp_px = rowtmp, rowval_px = rowval;
564 rowinc_px < rowval; rowinc_px++, rowtmp_px++, rowval_px++)
565 {
566 *rowinc_px = -*rowtmp_px;
567 *rowval_px = *rowtmp_px * v_o_val;
568 }
569 if (iy < (uint32_t)ctx->src->height)
570 {
571 if (!ctx->h_scaler((void*)rowtmp, ctx, false))
572 return false;
573 for(rowinc_px = rowinc, rowtmp_px = rowtmp, rowval_px = rowval;
574 rowinc_px < rowval; rowinc_px++, rowtmp_px++, rowval_px++)
575 {
576 *rowinc_px += *rowtmp_px;
577 *rowval_px += *rowinc_px * iye;
578 *rowinc_px *= v_i_val;
579 }
580 }
581 } else
582 for(rowinc_px = rowinc, rowval_px = rowval; rowinc_px < rowval;
583 rowinc_px++, rowval_px++)
584 *rowval_px += *rowinc_px;
585 ctx->output_row(oy, (void*)rowval, ctx);
586 iye += v_i_val;
587 }
588 return true;
589}
590#endif /* HAVE_UPSCALER */
591
592#if defined(HAVE_LCD_COLOR) && (defined(HAVE_JPEG) || defined(PLUGIN))
593static void output_row_32_native_fromyuv(uint32_t row, void * row_in,
594 struct scaler_context *ctx)
595{
596#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
597#define DEST_STEP (ctx->bm->height)
598#define Y_STEP (1)
599#else
600#define DEST_STEP (1)
601#define Y_STEP (BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0))
602#endif
603
604 int col;
605 uint8_t dy = DITHERY(row);
606 struct uint32_argb *qp = (struct uint32_argb *)row_in;
607 SDEBUGF("output_row: y: %lu in: %p\n",row, row_in);
608 fb_data *dest = (fb_data *)ctx->bm->data + Y_STEP * row;
609 int delta = 127;
610 unsigned r, g, b, y, u, v;
611
612 for (col = 0; col < ctx->bm->width; col++) {
613 (void) delta;
614 if (ctx->dither)
615 delta = DITHERXDY(col,dy);
616 y = SC_OUT(qp->b, ctx);
617 u = SC_OUT(qp->g, ctx);
618 v = SC_OUT(qp->r, ctx);
619 qp++;
620 yuv_to_rgb(y, u, v, &r, &g, &b);
621#if LCD_DEPTH < 24
622 r = (31 * r + (r >> 3) + delta) >> 8;
623 g = (63 * g + (g >> 2) + delta) >> 8;
624 b = (31 * b + (b >> 3) + delta) >> 8;
625#endif
626 *dest = FB_RGBPACK_LCD(r, g, b);
627 dest += DEST_STEP;
628 }
629}
630#endif
631
632#if !defined(PLUGIN) || LCD_DEPTH > 1
633static void output_row_32_native(uint32_t row, void * row_in,
634 struct scaler_context *ctx)
635{
636 int col;
637 int fb_width = BM_WIDTH(ctx->bm->width,FORMAT_NATIVE,0);
638 uint8_t dy = DITHERY(row);
639#ifdef HAVE_LCD_COLOR
640 struct uint32_argb *qp = (struct uint32_argb*)row_in;
641#else
642 uint32_t *qp = (uint32_t*)row_in;
643#endif
644 SDEBUGF("output_row: y: %lu in: %p\n",row, row_in);
645#if LCD_DEPTH == 2
646#if LCD_PIXELFORMAT == HORIZONTAL_PACKING
647 /* greyscale iPods */
648 fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row;
649 int shift = 6;
650 int delta = 127;
651 unsigned bright;
652 unsigned data = 0;
653
654 for (col = 0; col < ctx->bm->width; col++) {
655 if (ctx->dither)
656 delta = DITHERXDY(col,dy);
657 bright = SC_OUT(*qp++, ctx);
658 bright = (3 * bright + (bright >> 6) + delta) >> 8;
659 data |= (~bright & 3) << shift;
660 shift -= 2;
661 if (shift < 0) {
662 *dest++ = data;
663 data = 0;
664 shift = 6;
665 }
666 }
667 if (shift < 6)
668 *dest++ = data;
669#elif LCD_PIXELFORMAT == VERTICAL_PACKING
670 /* iriver H1x0 */
671 fb_data *dest = (fb_data *)ctx->bm->data + fb_width *
672 (row >> 2);
673 int shift = 2 * (row & 3);
674 int delta = 127;
675 unsigned bright;
676
677 for (col = 0; col < ctx->bm->width; col++) {
678 if (ctx->dither)
679 delta = DITHERXDY(col,dy);
680 bright = SC_OUT(*qp++, ctx);
681 bright = (3 * bright + (bright >> 6) + delta) >> 8;
682 *dest++ |= (~bright & 3) << shift;
683 }
684#elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
685 /* iAudio M3 */
686 fb_data *dest = (fb_data *)ctx->bm->data + fb_width *
687 (row >> 3);
688 int shift = row & 7;
689 int delta = 127;
690 unsigned bright;
691
692 for (col = 0; col < ctx->bm->width; col++) {
693 if (ctx->dither)
694 delta = DITHERXDY(col,dy);
695 bright = SC_OUT(*qp++, ctx);
696 bright = (3 * bright + (bright >> 6) + delta) >> 8;
697 *dest++ |= vi_pattern[bright] << shift;
698 }
699#endif /* LCD_PIXELFORMAT */
700#elif LCD_DEPTH >= 16
701 /* iriver h300, colour iPods, X5 */
702 (void)fb_width;
703 fb_data *dest = STRIDE_MAIN((fb_data *)ctx->bm->data + fb_width * row,
704 (fb_data *)ctx->bm->data + row);
705 int delta = 127;
706 unsigned r, g, b;
707 struct uint32_argb q0;
708 /* setup alpha channel buffer */
709 unsigned char *bm_alpha = NULL;
710 if (ctx->bm->alpha_offset > 0)
711 bm_alpha = ctx->bm->data + ctx->bm->alpha_offset;
712 if (bm_alpha)
713 bm_alpha += ALIGN_UP(ctx->bm->width, 2)*row/2;
714
715 for (col = 0; col < ctx->bm->width; col++) {
716 (void) delta;
717 if (ctx->dither)
718 delta = DITHERXDY(col,dy);
719 q0 = *qp++;
720 r = SC_OUT(q0.r, ctx);
721 g = SC_OUT(q0.g, ctx);
722 b = SC_OUT(q0.b, ctx);
723#if LCD_DEPTH < 24
724 r = (31 * r + (r >> 3) + delta) >> 8;
725 g = (63 * g + (g >> 2) + delta) >> 8;
726 b = (31 * b + (b >> 3) + delta) >> 8;
727#endif
728 *dest = FB_RGBPACK_LCD(r, g, b);
729 dest += STRIDE_MAIN(1, ctx->bm->height);
730 if (bm_alpha) {
731 /* pack alpha channel for 2 pixels into 1 byte */
732 unsigned alpha = SC_OUT(q0.a, ctx);
733 if (col%2)
734 *bm_alpha++ |= alpha&0xf0;
735 else
736 *bm_alpha = alpha>>4;
737 }
738 }
739#endif /* LCD_DEPTH */
740}
741#endif
742
743#if defined(PLUGIN) && LCD_DEPTH > 1
744unsigned int get_size_native(struct bitmap *bm)
745{
746 return BM_SIZE(bm->width,bm->height,FORMAT_NATIVE,0);
747}
748
749const struct custom_format format_native = {
750 .output_row_8 = output_row_8_native,
751#if defined(HAVE_LCD_COLOR) && (defined(HAVE_JPEG) || defined(PLUGIN))
752 .output_row_32 = {
753 output_row_32_native,
754 output_row_32_native_fromyuv
755 },
756#else
757 .output_row_32 = output_row_32_native,
758#endif
759 .get_size = get_size_native
760};
761#endif
762
763int resize_on_load(struct bitmap *bm, bool dither, struct dim *src,
764 struct rowset *rset, unsigned char *buf, unsigned int len,
765 const struct custom_format *format,
766 IF_PIX_FMT(int format_index,)
767 struct img_part* (*store_part)(void *args),
768 void *args)
769{
770 const int sw = src->width;
771 const int sh = src->height;
772 const int dw = bm->width;
773 const int dh = bm->height;
774 int ret;
775 /* buffer for 1 line + 2 spare lines */
776#ifdef HAVE_LCD_COLOR
777 unsigned int needed = sizeof(struct uint32_argb) * 3 * bm->width;
778#else
779 unsigned int needed = sizeof(uint32_t) * 3 * bm->width;
780#endif
781#if MAX_SC_STACK_ALLOC
782 uint8_t sc_buf[(needed <= len || needed > MAX_SC_STACK_ALLOC) ?
783 0 : needed];
784#endif
785 ALIGN_BUFFER(buf, len, sizeof(uint32_t));
786 if (needed > len)
787 {
788#if MAX_SC_STACK_ALLOC
789 if (needed > MAX_SC_STACK_ALLOC)
790 {
791 DEBUGF("unable to allocate required buffer: %d needed, "
792 "%d available, %d permitted from stack\n",
793 needed, len, MAX_SC_STACK_ALLOC);
794 return 0;
795 }
796 if (sizeof(sc_buf) < needed)
797 {
798 DEBUGF("failed to allocate large enough buffer on stack: "
799 "%d needed, only got %d",
800 needed, MAX_SC_STACK_ALLOC);
801 return 0;
802 }
803#else
804 DEBUGF("unable to allocate required buffer: %d needed, "
805 "%d available\n", needed, len);
806 return 0;
807#endif
808 }
809
810 struct scaler_context ctx;
811#ifdef HAVE_ADJUSTABLE_CPU_FREQ
812 cpu_boost(true);
813#endif
814 ctx.store_part = store_part;
815 ctx.args = args;
816#if MAX_SC_STACK_ALLOC
817 ctx.buf = needed > len ? sc_buf : buf;
818#else
819 ctx.buf = buf;
820#endif
821 ctx.len = len;
822 ctx.bm = bm;
823 ctx.src = src;
824 ctx.dither = dither;
825#if !defined(PLUGIN)
826#if defined(HAVE_LCD_COLOR) && defined(HAVE_JPEG)
827 ctx.output_row = format_index ? output_row_32_native_fromyuv
828 : output_row_32_native;
829#else
830 ctx.output_row = output_row_32_native;
831#endif
832 if (format)
833#endif
834#ifdef HAVE_LCD_COLOR
835 ctx.output_row = format->output_row_32[format_index];
836#else
837 ctx.output_row = format->output_row_32;
838#endif
839#ifdef HAVE_UPSCALER
840 if (sw > dw)
841 {
842#endif
843 ctx.h_scaler = scale_h_area;
844 uint32_t h_div = (1U << 24) / sw;
845 ctx.h_i_val = sw * h_div;
846 ctx.h_o_val = dw * h_div;
847#ifdef HAVE_UPSCALER
848 } else {
849 ctx.h_scaler = scale_h_linear;
850 uint32_t h_div = (1U << 24) / (dw - 1);
851 ctx.h_i_val = (sw - 1) * h_div;
852 ctx.h_o_val = (dw - 1) * h_div;
853 }
854#endif
855#ifdef CPU_COLDFIRE
856 unsigned old_macsr = coldfire_get_macsr();
857 coldfire_set_macsr(EMAC_UNSIGNED);
858#endif
859#ifdef HAVE_UPSCALER
860 if (sh > dh)
861#endif
862 {
863 uint32_t v_div = (1U << 22) / sh;
864 ctx.v_i_val = sh * v_div;
865 ctx.v_o_val = dh * v_div;
866 ret = scale_v_area(rset, &ctx);
867 }
868#ifdef HAVE_UPSCALER
869 else
870 {
871 uint32_t v_div = (1U << 22) / dh;
872 ctx.v_i_val = (sh - 1) * v_div;
873 ctx.v_o_val = (dh - 1) * v_div;
874 ret = scale_v_linear(rset, &ctx);
875 }
876#endif
877#ifdef CPU_COLDFIRE
878 /* Restore emac status; other modules like tone control filter
879 * calculation may rely on it. */
880 coldfire_set_macsr(old_macsr);
881#endif
882#ifdef HAVE_ADJUSTABLE_CPU_FREQ
883 cpu_boost(false);
884#endif
885 if (!ret)
886 return 0;
887 return 1;
888}