Mercurial > hg > forks > geeqie
comparison src/image_load_jpeg.c @ 1901:d21859e612de
added custom jpeg loader
author | Vladimir Nadvornik <nadvornik@suse.cz> |
---|---|
date | Sat, 26 Mar 2011 22:03:30 +0100 |
parents | |
children | 5ae882c45657 |
comparison
equal
deleted
inserted
replaced
1900:6e146fa6ec29 | 1901:d21859e612de |
---|---|
1 | |
2 #include "main.h" | |
3 #include "image-load.h" | |
4 #include "image_load_jpeg.h" | |
5 | |
6 #include <setjmp.h> | |
7 #include <jpeglib.h> | |
8 #include <jerror.h> | |
9 | |
10 typedef struct _ImageLoaderJpeg ImageLoaderJpeg; | |
11 struct _ImageLoaderJpeg { | |
12 ImageLoaderBackendCbAreaUpdated area_updated_cb; | |
13 ImageLoaderBackendCbSize size_cb; | |
14 ImageLoaderBackendCbAreaPrepared area_prepared_cb; | |
15 | |
16 gpointer data; | |
17 | |
18 GdkPixbuf *pixbuf; | |
19 guint requested_width; | |
20 guint requested_height; | |
21 | |
22 gboolean abort; | |
23 | |
24 }; | |
25 | |
26 /* error handler data */ | |
27 struct error_handler_data { | |
28 struct jpeg_error_mgr pub; | |
29 sigjmp_buf setjmp_buffer; | |
30 GError **error; | |
31 }; | |
32 | |
33 /* explode gray image data from jpeg library into rgb components in pixbuf */ | |
34 static void | |
35 explode_gray_into_buf (struct jpeg_decompress_struct *cinfo, | |
36 guchar **lines) | |
37 { | |
38 gint i, j; | |
39 guint w; | |
40 | |
41 g_return_if_fail (cinfo != NULL); | |
42 g_return_if_fail (cinfo->output_components == 1); | |
43 g_return_if_fail (cinfo->out_color_space == JCS_GRAYSCALE); | |
44 | |
45 /* Expand grey->colour. Expand from the end of the | |
46 * memory down, so we can use the same buffer. | |
47 */ | |
48 w = cinfo->output_width; | |
49 for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) { | |
50 guchar *from, *to; | |
51 | |
52 from = lines[i] + w - 1; | |
53 to = lines[i] + (w - 1) * 3; | |
54 for (j = w - 1; j >= 0; j--) { | |
55 to[0] = from[0]; | |
56 to[1] = from[0]; | |
57 to[2] = from[0]; | |
58 to -= 3; | |
59 from--; | |
60 } | |
61 } | |
62 } | |
63 | |
64 | |
65 static void | |
66 convert_cmyk_to_rgb (struct jpeg_decompress_struct *cinfo, | |
67 guchar **lines) | |
68 { | |
69 gint i, j; | |
70 | |
71 g_return_if_fail (cinfo != NULL); | |
72 g_return_if_fail (cinfo->output_components == 4); | |
73 g_return_if_fail (cinfo->out_color_space == JCS_CMYK); | |
74 | |
75 for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) { | |
76 guchar *p; | |
77 | |
78 p = lines[i]; | |
79 for (j = 0; j < cinfo->output_width; j++) { | |
80 int c, m, y, k; | |
81 c = p[0]; | |
82 m = p[1]; | |
83 y = p[2]; | |
84 k = p[3]; | |
85 if (cinfo->saw_Adobe_marker) { | |
86 p[0] = k*c / 255; | |
87 p[1] = k*m / 255; | |
88 p[2] = k*y / 255; | |
89 } | |
90 else { | |
91 p[0] = (255 - k)*(255 - c) / 255; | |
92 p[1] = (255 - k)*(255 - m) / 255; | |
93 p[2] = (255 - k)*(255 - y) / 255; | |
94 } | |
95 p[3] = 255; | |
96 p += 4; | |
97 } | |
98 } | |
99 } | |
100 | |
101 | |
102 static gpointer image_loader_jpeg_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data) | |
103 { | |
104 ImageLoaderJpeg *loader = g_new0(ImageLoaderJpeg, 1); | |
105 | |
106 loader->area_updated_cb = area_updated_cb; | |
107 loader->size_cb = size_cb; | |
108 loader->area_prepared_cb = area_prepared_cb; | |
109 loader->data = data; | |
110 return (gpointer) loader; | |
111 } | |
112 | |
113 static void | |
114 fatal_error_handler (j_common_ptr cinfo) | |
115 { | |
116 struct error_handler_data *errmgr; | |
117 char buffer[JMSG_LENGTH_MAX]; | |
118 | |
119 errmgr = (struct error_handler_data *) cinfo->err; | |
120 | |
121 /* Create the message */ | |
122 (* cinfo->err->format_message) (cinfo, buffer); | |
123 | |
124 /* broken check for *error == NULL for robustness against | |
125 * crappy JPEG library | |
126 */ | |
127 if (errmgr->error && *errmgr->error == NULL) { | |
128 g_set_error (errmgr->error, | |
129 GDK_PIXBUF_ERROR, | |
130 cinfo->err->msg_code == JERR_OUT_OF_MEMORY | |
131 ? GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY | |
132 : GDK_PIXBUF_ERROR_CORRUPT_IMAGE, | |
133 _("Error interpreting JPEG image file (%s)"), | |
134 buffer); | |
135 } | |
136 | |
137 siglongjmp (errmgr->setjmp_buffer, 1); | |
138 | |
139 g_assert_not_reached (); | |
140 } | |
141 | |
142 static void | |
143 output_message_handler (j_common_ptr cinfo) | |
144 { | |
145 /* This method keeps libjpeg from dumping crap to stderr */ | |
146 | |
147 /* do nothing */ | |
148 } | |
149 | |
150 static gboolean image_loader_jpeg_load (gpointer loader, const guchar *buf, gsize count, GError **error) | |
151 { | |
152 ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; | |
153 struct jpeg_decompress_struct cinfo; | |
154 guchar *dptr; | |
155 guint rowstride; | |
156 | |
157 struct error_handler_data jerr; | |
158 // stdio_src_ptr src; | |
159 | |
160 /* setup error handler */ | |
161 cinfo.err = jpeg_std_error (&jerr.pub); | |
162 jerr.pub.error_exit = fatal_error_handler; | |
163 jerr.pub.output_message = output_message_handler; | |
164 | |
165 jerr.error = error; | |
166 | |
167 | |
168 if (setjmp(jerr.setjmp_buffer)) | |
169 { | |
170 /* If we get here, the JPEG code has signaled an error. | |
171 * We need to clean up the JPEG object, close the input file, and return. | |
172 */ | |
173 jpeg_destroy_decompress(&cinfo); | |
174 return FALSE; | |
175 } | |
176 | |
177 jpeg_create_decompress(&cinfo); | |
178 | |
179 jpeg_mem_src(&cinfo, (unsigned char *)buf, count); | |
180 | |
181 | |
182 jpeg_read_header(&cinfo, TRUE); | |
183 | |
184 lj->requested_width = cinfo.image_width; | |
185 lj->requested_height = cinfo.image_height; | |
186 lj->size_cb(loader, cinfo.image_width, cinfo.image_height, lj->data); | |
187 | |
188 cinfo.scale_num = 1; | |
189 for (cinfo.scale_denom = 2; cinfo.scale_denom <= 8; cinfo.scale_denom *= 2) { | |
190 jpeg_calc_output_dimensions(&cinfo); | |
191 if (cinfo.output_width < lj->requested_width || cinfo.output_height < lj->requested_height) { | |
192 cinfo.scale_denom /= 2; | |
193 break; | |
194 } | |
195 } | |
196 jpeg_calc_output_dimensions(&cinfo); | |
197 | |
198 jpeg_start_decompress(&cinfo); | |
199 | |
200 lj->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, | |
201 cinfo.out_color_components == 4 ? TRUE : FALSE, | |
202 8, cinfo.output_width, cinfo.output_height); | |
203 | |
204 lj->area_prepared_cb(loader, lj->data); | |
205 rowstride = gdk_pixbuf_get_rowstride(lj->pixbuf); | |
206 dptr = gdk_pixbuf_get_pixels(lj->pixbuf); | |
207 | |
208 if (!lj->pixbuf) | |
209 { | |
210 jpeg_destroy_decompress (&cinfo); | |
211 return 0; | |
212 } | |
213 | |
214 while (cinfo.output_scanline < cinfo.output_height && !lj->abort) | |
215 { | |
216 guchar *lines[4]; | |
217 guchar **lptr; | |
218 gint i; | |
219 guint scanline = cinfo.output_scanline; | |
220 | |
221 lptr = lines; | |
222 for (i = 0; i < cinfo.rec_outbuf_height; i++) | |
223 { | |
224 *lptr++ = dptr; | |
225 dptr += rowstride; | |
226 } | |
227 | |
228 jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height); | |
229 | |
230 switch (cinfo.out_color_space) | |
231 { | |
232 case JCS_GRAYSCALE: | |
233 explode_gray_into_buf (&cinfo, lines); | |
234 break; | |
235 case JCS_RGB: | |
236 /* do nothing */ | |
237 break; | |
238 case JCS_CMYK: | |
239 convert_cmyk_to_rgb (&cinfo, lines); | |
240 break; | |
241 default: | |
242 break; | |
243 } | |
244 lj->area_updated_cb(loader, 0, scanline, cinfo.output_width, cinfo.rec_outbuf_height, lj->data); | |
245 } | |
246 | |
247 jpeg_finish_decompress(&cinfo); | |
248 jpeg_destroy_decompress(&cinfo); | |
249 return TRUE; | |
250 } | |
251 | |
252 static void image_loader_jpeg_set_size(gpointer loader, int width, int height) | |
253 { | |
254 ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; | |
255 lj->requested_width = width; | |
256 lj->requested_height = height; | |
257 } | |
258 | |
259 static GdkPixbuf* image_loader_jpeg_get_pixbuf(gpointer loader) | |
260 { | |
261 ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; | |
262 return lj->pixbuf; | |
263 } | |
264 | |
265 static gchar* image_loader_jpeg_get_format_name(gpointer loader) | |
266 { | |
267 return g_strdup("jpeg"); | |
268 } | |
269 static gchar** image_loader_jpeg_get_format_mime_types(gpointer loader) | |
270 { | |
271 static gchar *mime[] = {"image/jpeg", NULL}; | |
272 return g_strdupv(mime); | |
273 } | |
274 | |
275 static gboolean image_loader_jpeg_close(gpointer loader, GError **error) | |
276 { | |
277 return TRUE; | |
278 } | |
279 | |
280 static void image_loader_jpeg_abort(gpointer loader) | |
281 { | |
282 ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; | |
283 lj->abort = TRUE; | |
284 } | |
285 | |
286 static void image_loader_jpeg_free(gpointer loader) | |
287 { | |
288 ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; | |
289 if (lj->pixbuf) g_object_unref(lj->pixbuf); | |
290 g_free(lj); | |
291 } | |
292 | |
293 | |
294 void image_loader_backend_set_jpeg(ImageLoaderBackend *funcs) | |
295 { | |
296 funcs->loader_new = image_loader_jpeg_new; | |
297 funcs->set_size = image_loader_jpeg_set_size; | |
298 funcs->load = image_loader_jpeg_load; | |
299 funcs->write = NULL; | |
300 funcs->get_pixbuf = image_loader_jpeg_get_pixbuf; | |
301 funcs->close = image_loader_jpeg_close; | |
302 funcs->abort = image_loader_jpeg_abort; | |
303 funcs->free = image_loader_jpeg_free; | |
304 | |
305 funcs->get_format_name = image_loader_jpeg_get_format_name; | |
306 funcs->get_format_mime_types = image_loader_jpeg_get_format_mime_types; | |
307 } | |
308 | |
309 | |
310 |