summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/CATEGORIES2
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/imageviewer/SUBDIRS3
-rw-r--r--apps/plugins/imageviewer/image_decoder.c6
-rw-r--r--apps/plugins/imageviewer/image_decoder.h3
-rw-r--r--apps/plugins/imageviewer/ppm/SOURCES2
-rw-r--r--apps/plugins/imageviewer/ppm/ppm.c233
-rw-r--r--apps/plugins/imageviewer/ppm/ppm.make29
-rw-r--r--apps/plugins/imageviewer/ppm/ppm_decoder.c250
-rw-r--r--apps/plugins/imageviewer/ppm/ppm_decoder.h44
-rw-r--r--apps/plugins/ppmviewer.c355
-rw-r--r--apps/plugins/viewers.config4
12 files changed, 574 insertions, 358 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index c310a2cdb8..66b6f5e4ab 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -71,7 +71,7 @@ pitch_detector,apps
71plasma,demos 71plasma,demos
72png,viewers 72png,viewers
73pong,games 73pong,games
74ppmviewer,viewers 74ppm,viewers
75properties,viewers 75properties,viewers
76random_folder_advance_config,apps 76random_folder_advance_config,apps
77remote_control,apps 77remote_control,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 3c4e9e131d..7afd97309f 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -182,7 +182,6 @@ plasma.c
182 182
183#ifdef HAVE_LCD_COLOR 183#ifdef HAVE_LCD_COLOR
184clix.c 184clix.c
185ppmviewer.c
186codebuster.c 185codebuster.c
187#endif 186#endif
188 187
diff --git a/apps/plugins/imageviewer/SUBDIRS b/apps/plugins/imageviewer/SUBDIRS
index 9d8da7d53b..0f8d953939 100644
--- a/apps/plugins/imageviewer/SUBDIRS
+++ b/apps/plugins/imageviewer/SUBDIRS
@@ -1,3 +1,6 @@
1bmp 1bmp
2jpeg 2jpeg
3png 3png
4#ifdef HAVE_LCD_COLOR
5ppm
6#endif
diff --git a/apps/plugins/imageviewer/image_decoder.c b/apps/plugins/imageviewer/image_decoder.c
index b4fa27e425..553632e559 100644
--- a/apps/plugins/imageviewer/image_decoder.c
+++ b/apps/plugins/imageviewer/image_decoder.c
@@ -27,6 +27,9 @@ static const char *decoder_names[MAX_IMAGE_TYPES] = {
27 "bmp", 27 "bmp",
28 "jpeg", 28 "jpeg",
29 "png", 29 "png",
30#ifdef HAVE_LCD_COLOR
31 "ppm"
32#endif
30}; 33};
31 34
32/* check file type by extention */ 35/* check file type by extention */
@@ -41,6 +44,9 @@ enum image_type get_image_type(const char *name)
41 { ".jpe", IMAGE_JPEG }, 44 { ".jpe", IMAGE_JPEG },
42 { ".jpeg", IMAGE_JPEG }, 45 { ".jpeg", IMAGE_JPEG },
43 { ".png", IMAGE_PNG }, 46 { ".png", IMAGE_PNG },
47#ifdef HAVE_LCD_COLOR
48 { ".ppm", IMAGE_PPM },
49#endif
44 }; 50 };
45 51
46 const char *ext = rb->strrchr(name, '.'); 52 const char *ext = rb->strrchr(name, '.');
diff --git a/apps/plugins/imageviewer/image_decoder.h b/apps/plugins/imageviewer/image_decoder.h
index 93608b1b9a..1ccf9cabf2 100644
--- a/apps/plugins/imageviewer/image_decoder.h
+++ b/apps/plugins/imageviewer/image_decoder.h
@@ -29,6 +29,9 @@ enum image_type {
29 IMAGE_BMP = 0, 29 IMAGE_BMP = 0,
30 IMAGE_JPEG, 30 IMAGE_JPEG,
31 IMAGE_PNG, 31 IMAGE_PNG,
32#ifdef HAVE_LCD_COLOR
33 IMAGE_PPM,
34#endif
32 MAX_IMAGE_TYPES 35 MAX_IMAGE_TYPES
33}; 36};
34 37
diff --git a/apps/plugins/imageviewer/ppm/SOURCES b/apps/plugins/imageviewer/ppm/SOURCES
new file mode 100644
index 0000000000..492cca3ae2
--- /dev/null
+++ b/apps/plugins/imageviewer/ppm/SOURCES
@@ -0,0 +1,2 @@
1ppm.c
2ppm_decoder.c
diff --git a/apps/plugins/imageviewer/ppm/ppm.c b/apps/plugins/imageviewer/ppm/ppm.c
new file mode 100644
index 0000000000..100a00ac84
--- /dev/null
+++ b/apps/plugins/imageviewer/ppm/ppm.c
@@ -0,0 +1,233 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Marcin Bukat
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#include "plugin.h"
23#include "lcd.h"
24#include <lib/pluginlib_bmp.h>
25#include "../imageviewer.h"
26#include "ppm_decoder.h"
27#include "bmp.h"
28
29static char print[32]; /* use a common snprintf() buffer */
30
31/* decompressed image in the possible sizes (1,2,4,8), wasting the other */
32static unsigned char *disp[9];
33static unsigned char *disp_buf;
34static struct ppm_info ppm;
35
36#if defined(HAVE_LCD_COLOR)
37#define resize_bitmap smooth_resize_bitmap
38#else
39#define resize_bitmap grey_resize_bitmap
40#endif
41
42#if defined(USEGSLIB) && (CONFIG_PLATFORM & PLATFORM_HOSTED)
43/* hack: fix error "undefined reference to `_grey_info'". */
44GREY_INFO_STRUCT
45#endif /* USEGSLIB */
46
47static void draw_image_rect(struct image_info *info,
48 int x, int y, int width, int height)
49{
50 unsigned char **pdisp = (unsigned char **)info->data;
51
52#ifdef HAVE_LCD_COLOR
53 rb->lcd_bitmap_part((fb_data *)*pdisp, info->x + x, info->y + y,
54 STRIDE(SCREEN_MAIN, info->width, info->height),
55 x + MAX(0, (LCD_WIDTH-info->width)/2),
56 y + MAX(0, (LCD_HEIGHT-info->height)/2),
57 width, height);
58#else
59 mylcd_ub_gray_bitmap_part(*pdisp,
60 info->x + x, info->y + y, info->width,
61 x + MAX(0, (LCD_WIDTH-info->width)/2),
62 y + MAX(0, (LCD_HEIGHT-info->height)/2),
63 width, height);
64#endif
65}
66
67static int img_mem(int ds)
68{
69
70#ifdef USEGSLIB
71 return (ppm.x/ds) * (ppm.y/ds);
72#else
73 return (ppm.x/ds) * (ppm.y/ds) * FB_DATA_SZ;
74#endif
75}
76
77static int load_image(char *filename, struct image_info *info,
78 unsigned char *buf, ssize_t *buf_size)
79{
80 int fd;
81 int rc = PLUGIN_OK;
82 long time = 0; /* measured ticks */
83 int w, h; /* used to center output */
84
85 unsigned char *memory, *memory_max;
86 size_t memory_size, file_size;
87
88 /* cleanup */
89 memset(&disp, 0, sizeof(disp));
90
91 /* align buffer */
92 memory = (unsigned char *)((intptr_t)(buf + 3) & ~3);
93 memory_max = (unsigned char *)((intptr_t)(memory + *buf_size) & ~3);
94 memory_size = memory_max - memory;
95
96 fd = rb->open(filename, O_RDONLY);
97 if (fd < 0)
98 {
99 rb->splashf(HZ, "err opening %s: %d", filename, fd);
100 return PLUGIN_ERROR;
101 }
102
103 file_size = rb->filesize(fd);
104 DEBUGF("reading file '%s'\n", filename);
105
106 if (!iv->running_slideshow)
107 {
108 rb->lcd_puts(0, 0, rb->strrchr(filename,'/')+1);
109 rb->lcd_update();
110 }
111
112 if (!iv->running_slideshow)
113 {
114 rb->lcd_putsf(0, 1, "loading %zu bytes", file_size);
115 rb->lcd_update();
116 }
117
118 /* init decoder struct */
119 ppm.buf = memory;
120 ppm.buf_size = memory_size;
121
122 /* the actual decoding */
123 time = *rb->current_tick;
124#ifdef HAVE_ADJUSTABLE_CPU_FREQ
125 rb->cpu_boost(true);
126 rc = read_ppm(fd, &ppm);
127 rb->cpu_boost(false);
128#else
129 rc = read_ppm(fd, &ppm);
130#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
131 time = *rb->current_tick - time;
132
133 /* close file descriptor */
134 rb->close(fd);
135
136 /* check return value from decoder */
137 if ( rc == PLUGIN_ERROR )
138 {
139 rb->splashf(HZ, "ppm decoder error");
140 return PLUGIN_ERROR;
141 }
142
143 if (!iv->running_slideshow)
144 {
145 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
146 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
147 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
148 rb->lcd_update();
149 }
150
151 info->x_size = ppm.x;
152 info->y_size = ppm.y;
153
154 ppm.native_img_size = (ppm.native_img_size + 3) & ~3;
155 disp_buf = buf + ppm.native_img_size;
156 *buf_size = memory_max - disp_buf;
157
158 return PLUGIN_OK;
159}
160
161static int get_image(struct image_info *info, int ds)
162{
163 unsigned char **p_disp = &disp[ds]; /* short cut */
164 struct ppm_info *p_ppm = &ppm;
165
166 info->width = ppm.x / ds;
167 info->height = ppm.y / ds;
168 info->data = p_disp;
169
170 if (*p_disp != NULL)
171 {
172 /* we still have it */
173 return PLUGIN_OK;
174 }
175
176 /* assign image buffer */
177 if (ds > 1)
178 {
179 if (!iv->running_slideshow)
180 {
181 rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height);
182 rb->lcd_update();
183 }
184
185 struct bitmap bmp_src, bmp_dst;
186 int size = img_mem(ds);
187
188 if (disp_buf + size >= p_ppm->buf + p_ppm->buf_size)
189 {
190 /* have to discard the current */
191 int i;
192 for (i=1; i<=8; i++)
193 disp[i] = NULL; /* invalidate all bitmaps */
194
195 /* start again from the beginning of the buffer */
196 disp_buf = p_ppm->buf + p_ppm->native_img_size;
197 }
198
199 *p_disp = disp_buf;
200 disp_buf += size;
201
202 bmp_src.width = ppm.x;
203 bmp_src.height = ppm.y;
204 bmp_src.data = ppm.buf;
205
206 bmp_dst.width = info->width;
207 bmp_dst.height = info->height;
208 bmp_dst.data = *p_disp;
209#ifdef HAVE_ADJUSTABLE_CPU_FREQ
210 rb->cpu_boost(true);
211 resize_bitmap(&bmp_src, &bmp_dst);
212 rb->cpu_boost(false);
213#else
214 resize_bitmap(&bmp_src, &bmp_dst);
215#endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
216 }
217 else
218 {
219 *p_disp = p_ppm->buf;
220 }
221
222 return PLUGIN_OK;
223}
224
225const struct image_decoder image_decoder = {
226 true,
227 img_mem,
228 load_image,
229 get_image,
230 draw_image_rect,
231};
232
233IMGDEC_HEADER
diff --git a/apps/plugins/imageviewer/ppm/ppm.make b/apps/plugins/imageviewer/ppm/ppm.make
new file mode 100644
index 0000000000..e77f282723
--- /dev/null
+++ b/apps/plugins/imageviewer/ppm/ppm.make
@@ -0,0 +1,29 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10PPMSRCDIR := $(IMGVSRCDIR)/ppm
11PPMBUILDDIR := $(IMGVBUILDDIR)/ppm
12
13PPM_SRC := $(call preprocess, $(PPMSRCDIR)/SOURCES)
14PPM_OBJ := $(call c2obj, $(PPM_SRC))
15
16OTHER_SRC += $(PPM_SRC)
17
18ROCKS += $(PPMBUILDDIR)/ppm.ovl
19
20$(PPMBUILDDIR)/ppm.refmap: $(PPM_OBJ)
21$(PPMBUILDDIR)/ppm.link: $(PPM_OBJ) $(PPMBUILDDIR)/ppm.refmap
22$(PPMBUILDDIR)/ppm.ovl: $(PPM_OBJ)
23
24PPMFLAGS = $(IMGDECFLAGS) -Os
25
26# Compile PPM plugin with extra flags (adapted from ZXBox)
27$(PPMBUILDDIR)/%.o: $(PPMSRCDIR)/%.c $(PPMSRCDIR)/ppm.make
28 $(SILENT)mkdir -p $(dir $@)
29 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PPMFLAGS) -c $< -o $@
diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.c b/apps/plugins/imageviewer/ppm/ppm_decoder.c
new file mode 100644
index 0000000000..44c4f9232e
--- /dev/null
+++ b/apps/plugins/imageviewer/ppm/ppm_decoder.c
@@ -0,0 +1,250 @@
1/*****************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
5 * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 Alexander Papst
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#include "plugin.h"
23#include "lib/pluginlib_bmp.h"
24#include "ppm_decoder.h"
25
26static int ppm_read_magic_number(int fd)
27{
28 char i1, i2;
29 if(!rb->read(fd, &i1, 1) || !rb->read(fd, &i2, 1))
30 {
31 ppm_error( "Error reading magic number from ppm image stream. "\
32 "Most often, this means your input file is empty." );
33 return PLUGIN_ERROR;
34 }
35 return i1 * 256 + i2;
36}
37
38static char ppm_getc(int fd)
39{
40 char ch;
41
42 if (!rb->read(fd, &ch, 1)) {
43 ppm_error("EOF. Read error reading a byte");
44 return PLUGIN_ERROR;
45 }
46
47 if (ch == '#') {
48 do {
49 if (!rb->read(fd, &ch, 1)) {
50 ppm_error("EOF. Read error reading a byte");
51 return PLUGIN_ERROR;
52 }
53 } while (ch != '\n' && ch != '\r');
54 }
55 return ch;
56}
57
58static int ppm_getuint(int fd)
59{
60 char ch;
61 int i;
62 int digitVal;
63
64 do {
65 ch = ppm_getc(fd);
66 } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
67
68 if (ch < '0' || ch > '9') {
69 ppm_error("Junk (%c) in file where an integer should be.", ch);
70 return PLUGIN_ERROR;
71 }
72
73 i = 0;
74
75 do {
76 digitVal = ch - '0';
77
78 if (i > INT_MAX/10 - digitVal) {
79 ppm_error("ASCII decimal integer in file is "\
80 "too large to be processed.");
81 return PLUGIN_ERROR;
82 }
83
84 i = i * 10 + digitVal;
85 ch = ppm_getc(fd);
86
87 } while (ch >= '0' && ch <= '9');
88
89 return i;
90}
91
92static int ppm_getrawbyte(int fd)
93{
94 unsigned char by;
95
96 if (!rb->read(fd, &by, 1)) {
97 ppm_error("EOF. Read error while reading a one-byte sample.");
98 return PLUGIN_ERROR;
99 }
100
101 return (int)by;
102}
103
104static int ppm_getrawsample(int fd, int const maxval)
105{
106 if (maxval < 256) {
107 /* The sample is just one byte. Read it. */
108 return(ppm_getrawbyte(fd));
109 } else {
110 /* The sample is two bytes. Read both. */
111 unsigned char byte_pair[2];
112
113 if (!rb->read(fd, byte_pair, 2)) {
114 ppm_error("EOF. Read error while reading a long sample.");
115 return PLUGIN_ERROR;
116 }
117 return((byte_pair[0]<<8) | byte_pair[1]);
118 }
119}
120
121/* Read from the file header dimensions as well as max
122 * int value used
123 */
124static int read_ppm_init_rest(int fd, struct ppm_info *ppm)
125{
126 /* Read size. */
127 ppm->x = ppm_getuint(fd);
128 ppm->y = ppm_getuint(fd);
129
130#ifdef HAVE_LCD_COLOR
131 ppm->native_img_size = ppm->x * ppm->y * FB_DATA_SZ;
132#endif
133
134 if (ppm->native_img_size > ppm->buf_size) {
135 ppm_error("Imagesize (%ld pixels) is too large. "\
136 "The maximum allowed is %ld.",
137 (long)ppm->native_img_size,
138 (long)ppm->buf_size);
139 return PLUGIN_ERROR;
140 }
141
142 /* Read maxval. */
143 ppm->maxval = ppm_getuint(fd);
144
145 if (ppm->maxval > PPM_OVERALLMAXVAL) {
146 ppm_error("maxval of input image (%u) is too large. "\
147 "The maximum allowed by the PPM is %u.",
148 ppm->maxval, PPM_OVERALLMAXVAL);
149 return PLUGIN_ERROR;
150 }
151 if (ppm->maxval == 0) {
152 ppm_error("maxval of input image is zero.");
153 return PLUGIN_ERROR;
154 }
155 return 1;
156}
157
158static void read_ppm_init(int fd, struct ppm_info *ppm)
159{
160 /* Check magic number. */
161 ppm->format = ppm_read_magic_number( fd );
162
163 if (ppm->format == PLUGIN_ERROR) return;
164 switch (ppm->format) {
165 case PPM_FORMAT:
166 case RPPM_FORMAT:
167 if(read_ppm_init_rest(fd, ppm) == PLUGIN_ERROR) {
168 ppm->format = PLUGIN_ERROR;
169 }
170 break;
171
172 default:
173 ppm_error( "Bad magic number - not a ppm or rppm file." );
174 ppm->format = PLUGIN_ERROR;
175 }
176}
177
178#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
179#define BUFADDR(x, y, width, height) ( ppm->buf + (height*(x) + (y))*FB_DATA_SZ)
180#else
181#define BUFADDR(x, y, width, height) ( ppm->buf + (width*(y) + (x))*FB_DATA_SZ)
182#endif
183
184static int read_ppm_row(int fd, struct ppm_info *ppm, int row)
185{
186
187 int col;
188 int r, g, b;
189 switch (ppm->format) {
190 case PPM_FORMAT:
191 for (col = 0; col < ppm->x; ++col) {
192 r = ppm_getuint(fd);
193 g = ppm_getuint(fd);
194 b = ppm_getuint(fd);
195
196 if (r == PLUGIN_ERROR || g == PLUGIN_ERROR ||
197 b == PLUGIN_ERROR)
198 {
199 return PLUGIN_ERROR;
200 }
201 *(fb_data *)BUFADDR(col, row, ppm->x, ppm->y) = LCD_RGBPACK(
202 (255 * r)/ppm->maxval,
203 (255 * g)/ppm->maxval,
204 (255 * b)/ppm->maxval);
205 }
206 break;
207
208 case RPPM_FORMAT:
209 for (col = 0; col < ppm->x; ++col) {
210 r = ppm_getrawsample(fd, ppm->maxval);
211 g = ppm_getrawsample(fd, ppm->maxval);
212 b = ppm_getrawsample(fd, ppm->maxval);
213
214 if (r == PLUGIN_ERROR || g == PLUGIN_ERROR ||
215 b == PLUGIN_ERROR)
216 {
217 return PLUGIN_ERROR;
218 }
219 *(fb_data *)BUFADDR(col, row, ppm->x, ppm->y) = LCD_RGBPACK(
220 (255 * r)/ppm->maxval,
221 (255 * g)/ppm->maxval,
222 (255 * b)/ppm->maxval);
223 }
224 break;
225
226 default:
227 ppm_error("What?!");
228 return PLUGIN_ERROR;
229 }
230 return 1;
231}
232
233/* public */
234int read_ppm(int fd, struct ppm_info *ppm)
235{
236 int row;
237
238 read_ppm_init(fd, ppm);
239
240 if(ppm->format == PLUGIN_ERROR) {
241 return PLUGIN_ERROR;
242 }
243
244 for (row = 0; row < ppm->y; ++row) {
245 if( read_ppm_row(fd, ppm, row) == PLUGIN_ERROR) {
246 return PLUGIN_ERROR;
247 }
248 }
249 return 1;
250}
diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.h b/apps/plugins/imageviewer/ppm/ppm_decoder.h
new file mode 100644
index 0000000000..edd1bcba55
--- /dev/null
+++ b/apps/plugins/imageviewer/ppm/ppm_decoder.h
@@ -0,0 +1,44 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Marcin Bukat
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/* Magic constants. */
23#define PPM_MAGIC1 'P'
24#define PPM_MAGIC2 '3'
25#define RPPM_MAGIC2 '6'
26#define PPM_FORMAT (PPM_MAGIC1 * 256 + PPM_MAGIC2)
27#define RPPM_FORMAT (PPM_MAGIC1 * 256 + RPPM_MAGIC2)
28
29#define PPM_OVERALLMAXVAL 65535
30
31#define ppm_error(...) rb->splashf(HZ*2, __VA_ARGS__ )
32
33struct ppm_info {
34 int x;
35 int y;
36 int maxval;
37 int format;
38 unsigned char *buf;
39 size_t buf_size;
40 unsigned int native_img_size;
41};
42
43/* public prototype */
44int read_ppm(int fd, struct ppm_info *ppm);
diff --git a/apps/plugins/ppmviewer.c b/apps/plugins/ppmviewer.c
deleted file mode 100644
index e138692aef..0000000000
--- a/apps/plugins/ppmviewer.c
+++ /dev/null
@@ -1,355 +0,0 @@
1/*****************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
5 * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 Alexander Papst
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#include "plugin.h"
23#include "lib/pluginlib_bmp.h"
24
25/* Magic constants. */
26#define PPM_MAGIC1 'P'
27#define PPM_MAGIC2 '3'
28#define RPPM_MAGIC2 '6'
29#define PPM_FORMAT (PPM_MAGIC1 * 256 + PPM_MAGIC2)
30#define RPPM_FORMAT (PPM_MAGIC1 * 256 + RPPM_MAGIC2)
31
32#define PPM_OVERALLMAXVAL 65535
33#define PPM_MAXSIZE (300*1024)/sizeof(fb_data)
34
35#define ppm_error(...) rb->splashf(HZ*2, __VA_ARGS__ )
36
37static fb_data *buffer, *lcd_buf;
38
39int ppm_read_magic_number(int fd)
40{
41 char i1, i2;
42 if(!rb->read(fd, &i1, 1) || !rb->read(fd, &i2, 1))
43 {
44 ppm_error( "Error reading magic number from ppm image stream. "\
45 "Most often, this means your input file is empty." );
46 return PLUGIN_ERROR;
47 }
48 return i1 * 256 + i2;
49}
50
51char ppm_getc(int fd)
52{
53 char ch;
54
55 if (!rb->read(fd, &ch, 1)) {
56 ppm_error("EOF. Read error reading a byte");
57 return PLUGIN_ERROR;
58 }
59
60 if (ch == '#') {
61 do {
62 if (!rb->read(fd, &ch, 1)) {
63 ppm_error("EOF. Read error reading a byte");
64 return PLUGIN_ERROR;
65 }
66 } while (ch != '\n' && ch != '\r');
67 }
68 return ch;
69}
70
71int ppm_getuint(int fd)
72{
73 char ch;
74 int i;
75 int digitVal;
76
77 do {
78 ch = ppm_getc(fd);
79 } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
80
81 if (ch < '0' || ch > '9') {
82 ppm_error("Junk (%c) in file where an integer should be.", ch);
83 return PLUGIN_ERROR;
84 }
85
86 i = 0;
87
88 do {
89 digitVal = ch - '0';
90
91 if (i > INT_MAX/10 - digitVal) {
92 ppm_error("ASCII decimal integer in file is "\
93 "too large to be processed.");
94 return PLUGIN_ERROR;
95 }
96
97 i = i * 10 + digitVal;
98 ch = ppm_getc(fd);
99
100 } while (ch >= '0' && ch <= '9');
101
102 return i;
103}
104
105int ppm_getrawbyte(int fd)
106{
107 unsigned char by;
108
109 if (!rb->read(fd, &by, 1)) {
110 ppm_error("EOF. Read error while reading a one-byte sample.");
111 return PLUGIN_ERROR;
112 }
113
114 return (int)by;
115}
116
117int ppm_getrawsample(int fd, int const maxval)
118{
119 if (maxval < 256) {
120 /* The sample is just one byte. Read it. */
121 return(ppm_getrawbyte(fd));
122 } else {
123 /* The sample is two bytes. Read both. */
124 unsigned char byte_pair[2];
125
126 if (!rb->read(fd, byte_pair, 2)) {
127 ppm_error("EOF. Read error while reading a long sample.");
128 return PLUGIN_ERROR;
129 }
130 return((byte_pair[0]<<8) | byte_pair[1]);
131 }
132}
133
134int read_ppm_init_rest(int fd,
135 int * const cols,
136 int * const rows,
137 int * const maxval)
138{
139 /* Read size. */
140 *cols = ppm_getuint(fd);
141 *rows = ppm_getuint(fd);
142
143 if ((long unsigned int)(*cols * *rows) > PPM_MAXSIZE) {
144 ppm_error("Imagesize (%ld pixels) is too large. "\
145 "The maximum allowed is %ld.",
146 (long unsigned int)(*cols * *rows),
147 (long unsigned int)PPM_MAXSIZE);
148 return PLUGIN_ERROR;
149 }
150
151 /* Read maxval. */
152 *maxval = ppm_getuint(fd);
153
154 if (*maxval > PPM_OVERALLMAXVAL) {
155 ppm_error("maxval of input image (%u) is too large. "\
156 "The maximum allowed by the PPM is %u.",
157 *maxval, PPM_OVERALLMAXVAL);
158 return PLUGIN_ERROR;
159 }
160 if (*maxval == 0) {
161 ppm_error("maxval of input image is zero.");
162 return PLUGIN_ERROR;
163 }
164 return 1;
165}
166
167void read_ppm_init(int fd,
168 int * const cols,
169 int * const rows,
170 int * const maxval,
171 int * const format)
172{
173 /* Check magic number. */
174 *format = ppm_read_magic_number( fd );
175
176 if (*format == PLUGIN_ERROR) return;
177 switch (*format) {
178 case PPM_FORMAT:
179 case RPPM_FORMAT:
180 if(read_ppm_init_rest(fd, cols, rows, maxval) == PLUGIN_ERROR) {
181 *format = PLUGIN_ERROR;
182 }
183 break;
184
185 default:
186 ppm_error( "Bad magic number - not a ppm or rppm file." );
187 *format = PLUGIN_ERROR;
188 }
189}
190
191#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
192#define BUFADDR(x, y, width, height) ( buffer + height*(x) + (y))
193#else
194#define BUFADDR(x, y, width, height) ( buffer + width*(y) + (x))
195#endif
196
197int read_ppm_row(int fd,
198 int const row,
199 int const cols,
200 int const rows,
201 int const maxval,
202 int const format)
203{
204#if !(defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE)
205 (void) rows;
206#endif
207
208 int col;
209 int r, g, b;
210 switch (format) {
211 case PPM_FORMAT:
212 for (col = 0; col < cols; ++col) {
213 r = ppm_getuint(fd);
214 g = ppm_getuint(fd);
215 b = ppm_getuint(fd);
216
217 if (r == PLUGIN_ERROR || g == PLUGIN_ERROR ||
218 b == PLUGIN_ERROR)
219 {
220 return PLUGIN_ERROR;
221 }
222 *BUFADDR(col, row, cols, rows) = LCD_RGBPACK(
223 (255 / maxval) * r,
224 (255 / maxval) * g,
225 (255 / maxval) * b);
226 }
227 break;
228
229 case RPPM_FORMAT:
230 for (col = 0; col < cols; ++col) {
231 r = ppm_getrawsample(fd, maxval);
232 g = ppm_getrawsample(fd, maxval);
233 b = ppm_getrawsample(fd, maxval);
234
235 if (r == PLUGIN_ERROR || g == PLUGIN_ERROR ||
236 b == PLUGIN_ERROR)
237 {
238 return PLUGIN_ERROR;
239 }
240 *BUFADDR(col, row, cols, rows) = LCD_RGBPACK(
241 (255 / maxval) * r,
242 (255 / maxval) * g,
243 (255 / maxval) * b);
244 }
245 break;
246
247 default:
248 ppm_error("What?!");
249 return PLUGIN_ERROR;
250 }
251 return 1;
252}
253
254int read_ppm(int fd,
255 int * const cols,
256 int * const rows,
257 int * const maxval)
258{
259 int row;
260 int format;
261
262 read_ppm_init(fd, cols, rows, maxval, &format);
263
264 if(format == PLUGIN_ERROR) {
265 return PLUGIN_ERROR;
266 }
267
268 for (row = 0; row < *rows; ++row) {
269 if( read_ppm_row(fd, row, *cols, *rows, *maxval, format) == PLUGIN_ERROR) {
270 return PLUGIN_ERROR;
271 }
272 }
273 return 1;
274}
275
276/* this is the plugin entry point */
277enum plugin_status plugin_start(const void* parameter)
278{
279 static char filename[MAX_PATH];
280 int fd;
281
282 int cols;
283 int rows;
284 int maxval;
285
286 int result;
287
288 struct bitmap small_bitmap, orig_bitmap;
289
290 if(!parameter) return PLUGIN_ERROR;
291
292 size_t buffer_size;
293 char *audiobuf = rb->plugin_get_buffer(&buffer_size);
294 if (buffer_size < PPM_MAXSIZE + LCD_WIDTH * LCD_HEIGHT + 1)
295 {
296 /* steal from audiobuffer if plugin buffer is too small */
297 audiobuf = rb->plugin_get_audio_buffer(&buffer_size);
298
299 if (buffer_size < PPM_MAXSIZE + LCD_WIDTH * LCD_HEIGHT + 1)
300 {
301 rb->splash(HZ, "Not enough memory");
302 return PLUGIN_ERROR;
303 }
304 }
305
306 /* align on 16 bits */
307 audiobuf = (char *)(((uintptr_t)audiobuf + 1) & ~1);
308 buffer = (fb_data *)audiobuf;
309 lcd_buf = (fb_data*) (audiobuf + PPM_MAXSIZE);
310
311 rb->strcpy(filename, parameter);
312
313 fd = rb->open(filename, O_RDONLY);
314 if (fd < 0)
315 {
316 ppm_error("Couldnt open file: %s, %d", filename, fd);
317 return PLUGIN_ERROR;
318 }
319
320 result = read_ppm(fd, &cols, &rows, &maxval);
321
322 rb->close(fd);
323 if(result == PLUGIN_ERROR) return PLUGIN_ERROR;
324
325 orig_bitmap.width = cols;
326 orig_bitmap.height = rows;
327 orig_bitmap.data = (char*)buffer;
328
329 if (cols > LCD_WIDTH || rows > LCD_HEIGHT)
330 {
331 if (cols > LCD_WIDTH) {
332 small_bitmap.width = LCD_WIDTH;
333 small_bitmap.height =
334 (int)(((float)LCD_WIDTH / (float)cols) * (float)rows);
335
336 } else { /* rows > LCD_HEIGHT */
337
338 small_bitmap.width =
339 (int)(((float)LCD_HEIGHT / (float)rows) * (float)cols);
340 small_bitmap.height = LCD_HEIGHT;
341 }
342 small_bitmap.data = (char*)lcd_buf;
343
344 smooth_resize_bitmap( &orig_bitmap, &small_bitmap );
345
346 rb->lcd_bitmap((fb_data*)small_bitmap.data, 0, 0,
347 small_bitmap.width, small_bitmap.height);
348 } else {
349 rb->lcd_bitmap((fb_data*)orig_bitmap.data, 0, 0, cols, rows);
350 }
351 rb->lcd_update();
352 rb->button_get(true);
353
354 return PLUGIN_OK;
355}
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 0170883817..ed2f68aaf7 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -20,6 +20,9 @@ jpeg,viewers/test_core_jpeg,-
20jpeg,viewers/test_mem_jpeg,- 20jpeg,viewers/test_mem_jpeg,-
21jpeg,viewers/bench_mem_jpeg,- 21jpeg,viewers/bench_mem_jpeg,-
22png,viewers/imageviewer,2 22png,viewers/imageviewer,2
23#ifdef HAVE_LCD_COLOR
24ppm,viewers/imageviewer,2
25#endif
23ucl,viewers/rockbox_flash,3 26ucl,viewers/rockbox_flash,3
24rvf,viewers/video,4 27rvf,viewers/video,4
25mp3,viewers/vbrfix,5 28mp3,viewers/vbrfix,5
@@ -73,7 +76,6 @@ tap,viewers/zxbox,12
73sna,viewers/zxbox,12 76sna,viewers/zxbox,12
74tzx,viewers/zxbox,12 77tzx,viewers/zxbox,12
75z80,viewers/zxbox,12 78z80,viewers/zxbox,12
76ppm,viewers/ppmviewer,2
77cfg,viewers/theme_remove,- 79cfg,viewers/theme_remove,-
78*,viewers/properties,- 80*,viewers/properties,-
79*,viewers/shortcuts_append,- 81*,viewers/shortcuts_append,-