diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2011-01-09 13:22:54 +0000 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2011-01-09 13:22:54 +0000 |
commit | 6e3da841be4f0c7eda5d0daec19fc4919bd56d9f (patch) | |
tree | 05c2b3628720bcf2fd6f181b25771a874628cb38 | |
parent | 6d05e27d68ff6a65580e771bccb20e6139ef5d68 (diff) | |
download | rockbox-6e3da841be4f0c7eda5d0daec19fc4919bd56d9f.tar.gz rockbox-6e3da841be4f0c7eda5d0daec19fc4919bd56d9f.zip |
Rework ppmviewer to fit our shiny new imageviewer framework. FS#11818 by me.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29012 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/plugins/CATEGORIES | 2 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/imageviewer/SUBDIRS | 3 | ||||
-rw-r--r-- | apps/plugins/imageviewer/image_decoder.c | 6 | ||||
-rw-r--r-- | apps/plugins/imageviewer/image_decoder.h | 3 | ||||
-rw-r--r-- | apps/plugins/imageviewer/ppm/SOURCES | 2 | ||||
-rw-r--r-- | apps/plugins/imageviewer/ppm/ppm.c | 233 | ||||
-rw-r--r-- | apps/plugins/imageviewer/ppm/ppm.make | 29 | ||||
-rw-r--r-- | apps/plugins/imageviewer/ppm/ppm_decoder.c | 250 | ||||
-rw-r--r-- | apps/plugins/imageviewer/ppm/ppm_decoder.h | 44 | ||||
-rw-r--r-- | apps/plugins/ppmviewer.c | 355 | ||||
-rw-r--r-- | apps/plugins/viewers.config | 4 |
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 | |||
71 | plasma,demos | 71 | plasma,demos |
72 | png,viewers | 72 | png,viewers |
73 | pong,games | 73 | pong,games |
74 | ppmviewer,viewers | 74 | ppm,viewers |
75 | properties,viewers | 75 | properties,viewers |
76 | random_folder_advance_config,apps | 76 | random_folder_advance_config,apps |
77 | remote_control,apps | 77 | remote_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 |
184 | clix.c | 184 | clix.c |
185 | ppmviewer.c | ||
186 | codebuster.c | 185 | codebuster.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 @@ | |||
1 | bmp | 1 | bmp |
2 | jpeg | 2 | jpeg |
3 | png | 3 | png |
4 | #ifdef HAVE_LCD_COLOR | ||
5 | ppm | ||
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 @@ | |||
1 | ppm.c | ||
2 | ppm_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 | |||
29 | static char print[32]; /* use a common snprintf() buffer */ | ||
30 | |||
31 | /* decompressed image in the possible sizes (1,2,4,8), wasting the other */ | ||
32 | static unsigned char *disp[9]; | ||
33 | static unsigned char *disp_buf; | ||
34 | static 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'". */ | ||
44 | GREY_INFO_STRUCT | ||
45 | #endif /* USEGSLIB */ | ||
46 | |||
47 | static 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 | |||
67 | static 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 | |||
77 | static 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 | |||
161 | static 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 | |||
225 | const struct image_decoder image_decoder = { | ||
226 | true, | ||
227 | img_mem, | ||
228 | load_image, | ||
229 | get_image, | ||
230 | draw_image_rect, | ||
231 | }; | ||
232 | |||
233 | IMGDEC_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 | |||
10 | PPMSRCDIR := $(IMGVSRCDIR)/ppm | ||
11 | PPMBUILDDIR := $(IMGVBUILDDIR)/ppm | ||
12 | |||
13 | PPM_SRC := $(call preprocess, $(PPMSRCDIR)/SOURCES) | ||
14 | PPM_OBJ := $(call c2obj, $(PPM_SRC)) | ||
15 | |||
16 | OTHER_SRC += $(PPM_SRC) | ||
17 | |||
18 | ROCKS += $(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 | |||
24 | PPMFLAGS = $(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 | |||
26 | static 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 | |||
38 | static 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 | |||
58 | static 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 | |||
92 | static 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 | |||
104 | static 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 | */ | ||
124 | static 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 | |||
158 | static 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 | |||
184 | static 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 */ | ||
234 | int 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 | |||
33 | struct 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 */ | ||
44 | int 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 | |||
37 | static fb_data *buffer, *lcd_buf; | ||
38 | |||
39 | int 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 | |||
51 | char 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 | |||
71 | int 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 | |||
105 | int 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 | |||
117 | int 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 | |||
134 | int 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 | |||
167 | void 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 | |||
197 | int 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 | |||
254 | int 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 */ | ||
277 | enum 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,- | |||
20 | jpeg,viewers/test_mem_jpeg,- | 20 | jpeg,viewers/test_mem_jpeg,- |
21 | jpeg,viewers/bench_mem_jpeg,- | 21 | jpeg,viewers/bench_mem_jpeg,- |
22 | png,viewers/imageviewer,2 | 22 | png,viewers/imageviewer,2 |
23 | #ifdef HAVE_LCD_COLOR | ||
24 | ppm,viewers/imageviewer,2 | ||
25 | #endif | ||
23 | ucl,viewers/rockbox_flash,3 | 26 | ucl,viewers/rockbox_flash,3 |
24 | rvf,viewers/video,4 | 27 | rvf,viewers/video,4 |
25 | mp3,viewers/vbrfix,5 | 28 | mp3,viewers/vbrfix,5 |
@@ -73,7 +76,6 @@ tap,viewers/zxbox,12 | |||
73 | sna,viewers/zxbox,12 | 76 | sna,viewers/zxbox,12 |
74 | tzx,viewers/zxbox,12 | 77 | tzx,viewers/zxbox,12 |
75 | z80,viewers/zxbox,12 | 78 | z80,viewers/zxbox,12 |
76 | ppm,viewers/ppmviewer,2 | ||
77 | cfg,viewers/theme_remove,- | 79 | cfg,viewers/theme_remove,- |
78 | *,viewers/properties,- | 80 | *,viewers/properties,- |
79 | *,viewers/shortcuts_append,- | 81 | *,viewers/shortcuts_append,- |