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* JPEG image viewer
11* (This is a real mess if it has to be coded in one single C file)
12*
13* File scrolling addition (C) 2005 Alexander Spyridakis
14* Copyright (C) 2004 Jörg Hohensohn aka [IDC]Dragon
15* Heavily borrowed from the IJG implementation (C) Thomas G. Lane
16* Small & fast downscaling IDCT (C) 2002 by Guido Vollbeding JPEGclub.org
17*
18* This program is free software; you can redistribute it and/or
19* modify it under the terms of the GNU General Public License
20* as published by the Free Software Foundation; either version 2
21* of the License, or (at your option) any later version.
22*
23* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24* KIND, either express or implied.
25*
26****************************************************************************/
27
28#include "plugin.h"
29
30#include "../imageviewer.h"
31#include "jpeg_decoder.h"
32
33#ifdef HAVE_LCD_COLOR
34#include "yuv2rgb.h"
35#endif
36
37/**************** begin Application ********************/
38
39/************************* Types ***************************/
40
41struct t_disp
42{
43#ifdef HAVE_LCD_COLOR
44 unsigned char* bitmap[3]; /* Y, Cr, Cb */
45 int csub_x, csub_y;
46#else
47 unsigned char* bitmap[1]; /* Y only */
48#endif
49 int stride;
50};
51
52/************************* Globals ***************************/
53
54/* decompressed image in the possible sizes (1,2,4,8), wasting the other */
55static struct t_disp disp[9];
56
57/* my memory pool (from the mp3 buffer) */
58static char print[32]; /* use a common snprintf() buffer */
59
60/* the root of the images, hereafter are decompresed ones */
61static unsigned char* buf_root;
62static int root_size;
63
64/* up to here currently used by image(s) */
65static unsigned char* buf_images;
66static ssize_t buf_images_size;
67
68static struct jpeg jpg; /* too large for stack */
69
70/************************* Implementation ***************************/
71
72static void draw_image_rect(struct image_info *info,
73 int x, int y, int width, int height)
74{
75 struct t_disp* pdisp = (struct t_disp*)info->data;
76#ifdef HAVE_LCD_COLOR
77 yuv_bitmap_part(
78 pdisp->bitmap, pdisp->csub_x, pdisp->csub_y,
79 info->x + x, info->y + y, pdisp->stride,
80 x + MAX(0, (LCD_WIDTH - info->width) / 2),
81 y + MAX(0, (LCD_HEIGHT - info->height) / 2),
82 width, height,
83 iv->settings->jpeg_colour_mode, iv->settings->jpeg_dither_mode);
84#else
85 mylcd_ub_gray_bitmap_part(
86 pdisp->bitmap[0], info->x + x, info->y + y, pdisp->stride,
87 x + MAX(0, (LCD_WIDTH-info->width)/2),
88 y + MAX(0, (LCD_HEIGHT-info->height)/2),
89 width, height);
90#endif
91}
92
93static int img_mem(int ds)
94{
95 int size;
96 struct jpeg *p_jpg = &jpg;
97
98 size = (p_jpg->x_phys/ds/p_jpg->subsample_x[0])
99 * (p_jpg->y_phys/ds/p_jpg->subsample_y[0]);
100#ifdef HAVE_LCD_COLOR
101 if (p_jpg->blocks > 1) /* colour, add requirements for chroma */
102 {
103 size += (p_jpg->x_phys/ds/p_jpg->subsample_x[1])
104 * (p_jpg->y_phys/ds/p_jpg->subsample_y[1]);
105 size += (p_jpg->x_phys/ds/p_jpg->subsample_x[2])
106 * (p_jpg->y_phys/ds/p_jpg->subsample_y[2]);
107 }
108#endif
109 return size;
110}
111
112static int load_image(char *filename, struct image_info *info,
113 unsigned char *buf, ssize_t *buf_size,
114 int offset, int filesize)
115{
116 int fd;
117 unsigned char* buf_jpeg; /* compressed JPEG image */
118 int status;
119 struct jpeg *p_jpg = &jpg;
120
121 rb->memset(&disp, 0, sizeof(disp));
122 rb->memset(&jpg, 0, sizeof(jpg));
123
124 fd = rb->open(filename, O_RDONLY);
125 if (fd < 0)
126 {
127 rb->splashf(HZ, "err opening %s: %d", filename, fd);
128 return PLUGIN_ERROR;
129 }
130
131 if (offset)
132 {
133 rb->lseek(fd, offset, SEEK_SET);
134 }
135 else
136 {
137 filesize = rb->filesize(fd);
138 }
139
140 /* allocate JPEG buffer */
141 buf_jpeg = buf;
142
143 /* we can start the decompressed images behind it */
144 buf_images = buf_root = buf + filesize;
145 buf_images_size = root_size = *buf_size - filesize;
146
147 if (buf_images_size <= 0)
148 {
149 rb->close(fd);
150 return PLUGIN_OUTOFMEM;
151 }
152
153 if(!iv->running_slideshow)
154 {
155 rb->lcd_puts(0, 0, rb->strrchr(filename,'/')+1);
156 rb->lcd_putsf(0, 1, "loading %d bytes", filesize);
157 rb->lcd_update();
158 }
159
160 rb->read(fd, buf_jpeg, filesize);
161 rb->close(fd);
162
163 if(!iv->running_slideshow)
164 {
165 rb->lcd_puts(0, 2, "decoding markers");
166 rb->lcd_update();
167 }
168#ifdef DISK_SPINDOWN
169 else if(iv->immediate_ata_off)
170 {
171 /* running slideshow and time is long enough: power down disk */
172 rb->storage_sleep();
173 }
174#endif
175
176 /* process markers, unstuffing */
177 status = process_markers(buf_jpeg, filesize, p_jpg);
178
179 if (status < 0 || (status & (DQT | SOF0)) != (DQT | SOF0))
180 { /* bad format or minimum components not contained */
181#ifndef HAVE_LCD_COLOR
182 rb->splashf(HZ, "unsupported %d", status);
183 return PLUGIN_ERROR;
184#else
185 return PLUGIN_JPEG_PROGRESSIVE;
186#endif
187 }
188
189 if (!(status & DHT)) /* if no Huffman table present: */
190 default_huff_tbl(p_jpg); /* use default */
191 build_lut(p_jpg); /* derive Huffman and other lookup-tables */
192
193 if(!iv->running_slideshow)
194 {
195 rb->lcd_putsf(0, 2, "image %dx%d", p_jpg->x_size, p_jpg->y_size);
196 rb->lcd_update();
197 }
198
199 info->x_size = p_jpg->x_size;
200 info->y_size = p_jpg->y_size;
201 *buf_size = buf_images_size;
202 return PLUGIN_OK;
203}
204
205static int get_image(struct image_info *info, int frame, int ds)
206{
207 (void)frame;
208 int w, h; /* used to center output */
209 int size; /* decompressed image size */
210 long time; /* measured ticks */
211 int status;
212 struct jpeg* p_jpg = &jpg;
213 struct t_disp* p_disp = &disp[ds]; /* short cut */
214
215 info->width = p_jpg->x_size / ds;
216 info->height = p_jpg->y_size / ds;
217 info->data = p_disp;
218
219 if (p_disp->bitmap[0] != NULL)
220 {
221 /* we still have it */
222 return PLUGIN_OK;
223 }
224
225 /* assign image buffer */
226
227 /* physical size needed for decoding */
228 size = img_mem(ds);
229 if (buf_images_size <= size)
230 { /* have to discard the current */
231 int i;
232 for (i=1; i<=8; i++)
233 disp[i].bitmap[0] = NULL; /* invalidate all bitmaps */
234 buf_images = buf_root; /* start again from the beginning of the buffer */
235 buf_images_size = root_size;
236 }
237
238#ifdef HAVE_LCD_COLOR
239 if (p_jpg->blocks > 1) /* colour jpeg */
240 {
241 int i;
242
243 for (i = 1; i < 3; i++)
244 {
245 size = (p_jpg->x_phys / ds / p_jpg->subsample_x[i])
246 * (p_jpg->y_phys / ds / p_jpg->subsample_y[i]);
247 p_disp->bitmap[i] = buf_images;
248 buf_images += size;
249 buf_images_size -= size;
250 }
251 p_disp->csub_x = p_jpg->subsample_x[1];
252 p_disp->csub_y = p_jpg->subsample_y[1];
253 }
254 else
255 {
256 p_disp->csub_x = p_disp->csub_y = 0;
257 p_disp->bitmap[1] = p_disp->bitmap[2] = buf_images;
258 }
259#endif
260 /* size may be less when decoded (if height is not block aligned) */
261 size = (p_jpg->x_phys/ds) * (p_jpg->y_size/ds);
262 p_disp->bitmap[0] = buf_images;
263 buf_images += size;
264 buf_images_size -= size;
265
266 if(!iv->running_slideshow)
267 {
268 rb->lcd_putsf(0, 3, "decoding %d*%d", info->width, info->height);
269 rb->lcd_update();
270 }
271
272 /* update image properties */
273 p_disp->stride = p_jpg->x_phys / ds; /* use physical size for stride */
274
275 /* the actual decoding */
276 time = *rb->current_tick;
277#ifdef HAVE_ADJUSTABLE_CPU_FREQ
278 rb->cpu_boost(true);
279 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, iv->cb_progress);
280 rb->cpu_boost(false);
281#else
282 status = jpeg_decode(p_jpg, p_disp->bitmap, ds, iv->cb_progress);
283#endif
284 if (status)
285 {
286 rb->splashf(HZ, "decode error %d", status);
287 return PLUGIN_ERROR;
288 }
289 time = *rb->current_tick - time;
290
291 if(!iv->running_slideshow)
292 {
293 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
294 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
295 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
296 rb->lcd_update();
297 }
298
299 return PLUGIN_OK;
300}
301
302const struct image_decoder image_decoder = {
303 false,
304 img_mem,
305 load_image,
306 get_image,
307 draw_image_rect,
308};
309
310IMGDEC_HEADER