9
|
1 /*
|
|
2 * GQview
|
|
3 * (C) 2004 John Ellis
|
|
4 *
|
|
5 * Author: John Ellis
|
|
6 *
|
|
7 * This software is released under the GNU General Public License (GNU GPL).
|
|
8 * Please read the included file COPYING for more information.
|
|
9 * This software comes with no warranty of any kind, use at your own risk!
|
|
10 */
|
|
11
|
|
12
|
|
13 #include "gqview.h"
|
|
14 #include "image-load.h"
|
|
15
|
|
16 #include "ui_fileops.h"
|
|
17
|
|
18 #include <fcntl.h>
|
|
19
|
|
20
|
|
21 /* bytes to read from file per read() */
|
|
22 #define IMAGE_LOADER_BUFFER_SIZE 512
|
|
23
|
|
24 /* the number of bytes to read per idle call (define x IMAGE_LOADER_BUFFER_SIZE) */
|
|
25 #define IMAGE_LOADER_BUFFER_DEFAULT_COUNT 1
|
|
26
|
|
27 static void image_loader_sync_pixbuf(ImageLoader *il)
|
|
28 {
|
|
29 GdkPixbuf *pb;
|
|
30
|
|
31 if (!il->loader) return;
|
|
32
|
|
33 pb = gdk_pixbuf_loader_get_pixbuf(il->loader);
|
|
34
|
|
35 if (pb == il->pixbuf) return;
|
|
36
|
|
37 if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
|
|
38 il->pixbuf = pb;
|
|
39 if (il->pixbuf) gdk_pixbuf_ref(il->pixbuf);
|
|
40 }
|
|
41
|
|
42 static void image_loader_area_cb(GdkPixbufLoader *loader,
|
|
43 guint x, guint y, guint w, guint h,
|
|
44 gpointer data)
|
|
45 {
|
|
46 ImageLoader *il = data;
|
|
47
|
|
48 if (il->func_area_ready)
|
|
49 {
|
|
50 if (!il->pixbuf)
|
|
51 {
|
|
52 image_loader_sync_pixbuf(il);
|
|
53 if (!il->pixbuf)
|
|
54 {
|
|
55 printf("critical: area_ready signal with NULL pixbuf (out of mem?)\n");
|
|
56 }
|
|
57 }
|
|
58 il->func_area_ready(il, x, y, w, h, il->data_area_ready);
|
|
59 }
|
|
60 }
|
|
61
|
|
62 static void image_loader_size_cb(GdkPixbufLoader *loader,
|
|
63 gint width, gint height, gpointer data)
|
|
64 {
|
|
65 ImageLoader *il = data;
|
|
66 GdkPixbufFormat *format;
|
|
67 gchar **mime_types;
|
|
68 gint scale = FALSE;
|
|
69 gint n;
|
|
70
|
|
71 if (il->requested_width < 1 || il->requested_height < 1) return;
|
|
72
|
|
73 format = gdk_pixbuf_loader_get_format(loader);
|
|
74 if (!format) return;
|
|
75
|
|
76 mime_types = gdk_pixbuf_format_get_mime_types(format);
|
|
77 n = 0;
|
|
78 while (mime_types[n])
|
|
79 {
|
|
80 if (strstr(mime_types[n], "jpeg")) scale = TRUE;
|
|
81 n++;
|
|
82 }
|
|
83 g_strfreev(mime_types);
|
|
84
|
|
85 if (!scale) return;
|
|
86
|
|
87 if (width > il->requested_width || height > il->requested_height)
|
|
88 {
|
|
89 gint nw, nh;
|
|
90
|
|
91 if (((gdouble)il->requested_width / width) < ((gdouble)il->requested_height / height))
|
|
92 {
|
|
93 nw = il->requested_width;
|
|
94 nh = (gdouble)nw / width * height;
|
|
95 if (nh < 1) nh = 1;
|
|
96 }
|
|
97 else
|
|
98 {
|
|
99 nh = il->requested_height;
|
|
100 nw = (gdouble)nh / height * width;
|
|
101 if (nw < 1) nw = 1;
|
|
102 }
|
|
103
|
|
104 gdk_pixbuf_loader_set_size(loader, nw, nh);
|
|
105 }
|
|
106 }
|
|
107
|
|
108 static void image_loader_stop(ImageLoader *il)
|
|
109 {
|
|
110 if (!il) return;
|
|
111
|
|
112 if (il->idle_id != -1)
|
|
113 {
|
|
114 g_source_remove(il->idle_id);
|
|
115 il->idle_id = -1;
|
|
116 }
|
|
117
|
|
118 if (il->loader)
|
|
119 {
|
|
120 /* some loaders do not have a pixbuf till close, order is important here */
|
|
121 gdk_pixbuf_loader_close(il->loader, NULL);
|
|
122 image_loader_sync_pixbuf(il);
|
|
123 g_object_unref(G_OBJECT(il->loader));
|
|
124 il->loader = NULL;
|
|
125 }
|
|
126
|
|
127 if (il->load_fd != -1)
|
|
128 {
|
|
129 close(il->load_fd);
|
|
130 il->load_fd = -1;
|
|
131 }
|
|
132
|
|
133 il->done = TRUE;
|
|
134 }
|
|
135
|
|
136 static void image_loader_done(ImageLoader *il)
|
|
137 {
|
|
138 image_loader_stop(il);
|
|
139
|
|
140 if (il->func_done) il->func_done(il, il->data_done);
|
|
141 }
|
|
142
|
|
143 static gint image_loader_done_delay_cb(gpointer data)
|
|
144 {
|
|
145 ImageLoader *il = data;
|
|
146
|
|
147 il->idle_done_id = -1;
|
|
148 image_loader_done(il);
|
|
149 return FALSE;
|
|
150 }
|
|
151
|
|
152 static void image_loader_done_delay(ImageLoader *il)
|
|
153 {
|
|
154 if (il->idle_done_id == -1) il->idle_done_id = g_idle_add_full(il->idle_priority,
|
|
155 image_loader_done_delay_cb, il, NULL);
|
|
156 }
|
|
157
|
|
158 static void image_loader_error(ImageLoader *il)
|
|
159 {
|
|
160 image_loader_stop(il);
|
|
161
|
|
162 if (debug) printf("pixbuf_loader reported load error for: %s\n", il->path);
|
|
163
|
|
164 if (il->func_error) il->func_error(il, il->data_error);
|
|
165 }
|
|
166
|
|
167 static gint image_loader_idle_cb(gpointer data)
|
|
168 {
|
|
169 ImageLoader *il = data;
|
|
170 guchar buf[IMAGE_LOADER_BUFFER_SIZE];
|
|
171 gint b;
|
|
172 gint c;
|
|
173
|
|
174 if (!il) return FALSE;
|
|
175
|
|
176 if (il->idle_id == -1) return FALSE;
|
|
177
|
|
178 c = il->buffer_size ? il->buffer_size : 1;
|
|
179 while (c > 0)
|
|
180 {
|
|
181 b = read(il->load_fd, &buf, sizeof(buf));
|
|
182
|
|
183 if (b == 0)
|
|
184 {
|
|
185 image_loader_done(il);
|
|
186 return FALSE;
|
|
187 }
|
|
188
|
|
189 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, buf, b, NULL)))
|
|
190 {
|
|
191 image_loader_error(il);
|
|
192 return FALSE;
|
|
193 }
|
|
194
|
|
195 il->bytes_read += b;
|
|
196
|
|
197 c--;
|
|
198 }
|
|
199
|
|
200 if (il->func_percent && il->bytes_total > 0)
|
|
201 {
|
|
202 il->func_percent(il, (gdouble)il->bytes_read / il->bytes_total, il->data_percent);
|
|
203 }
|
|
204
|
|
205 return TRUE;
|
|
206 }
|
|
207
|
|
208 static gint image_loader_begin(ImageLoader *il)
|
|
209 {
|
|
210 guchar buf[IMAGE_LOADER_BUFFER_SIZE];
|
|
211 int b;
|
|
212
|
|
213 if (!il->loader || il->pixbuf) return FALSE;
|
|
214
|
|
215 b = read(il->load_fd, &buf, sizeof(buf));
|
|
216
|
|
217 if (b < 1)
|
|
218 {
|
|
219 image_loader_stop(il);
|
|
220 return FALSE;
|
|
221 }
|
|
222
|
|
223 if (gdk_pixbuf_loader_write(il->loader, buf, b, NULL))
|
|
224 {
|
|
225 il->bytes_read += b;
|
|
226
|
|
227 if (b < sizeof(buf))
|
|
228 {
|
|
229 /* end of file already */
|
|
230
|
|
231 image_loader_stop(il);
|
|
232
|
|
233 if (!il->pixbuf) return FALSE;
|
|
234
|
|
235 image_loader_done_delay(il);
|
|
236 return TRUE;
|
|
237 }
|
|
238 else
|
|
239 {
|
|
240 /* larger file */
|
|
241
|
|
242 /* read until size is known */
|
|
243 while(il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
|
|
244 {
|
|
245 b = read(il->load_fd, &buf, sizeof(buf));
|
|
246 if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, buf, b, NULL)))
|
|
247 {
|
|
248 image_loader_stop(il);
|
|
249 return FALSE;
|
|
250 }
|
|
251 il->bytes_read += b;
|
|
252 }
|
|
253 if (!il->pixbuf) image_loader_sync_pixbuf(il);
|
|
254
|
|
255 if (il->bytes_read == il->bytes_total || b < sizeof(buf))
|
|
256 {
|
|
257 /* done, handle (broken) loaders that do not have pixbuf till close */
|
|
258 image_loader_stop(il);
|
|
259
|
|
260 if (!il->pixbuf) return FALSE;
|
|
261
|
|
262 image_loader_done_delay(il);
|
|
263 return TRUE;
|
|
264 }
|
|
265
|
|
266 if (!il->pixbuf)
|
|
267 {
|
|
268 image_loader_stop(il);
|
|
269 return FALSE;
|
|
270 }
|
|
271
|
|
272 /* finally, progressive loading :) */
|
|
273 il->idle_id = g_idle_add_full(il->idle_priority, image_loader_idle_cb, il, NULL);
|
|
274 return TRUE;
|
|
275 }
|
|
276 }
|
|
277 else
|
|
278 {
|
|
279 image_loader_stop(il);
|
|
280 return FALSE;
|
|
281 }
|
|
282
|
|
283 return TRUE;
|
|
284 }
|
|
285
|
|
286 static gint image_loader_setup(ImageLoader *il)
|
|
287 {
|
|
288 struct stat st;
|
|
289 gchar *pathl;
|
|
290
|
|
291 if (!il || il->load_fd != -1 || il->loader) return FALSE;
|
|
292
|
|
293 pathl = path_from_utf8(il->path);
|
|
294 il->load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
|
|
295 g_free(pathl);
|
|
296 if (il->load_fd == -1) return FALSE;
|
|
297
|
|
298 if (fstat(il->load_fd, &st) == 0)
|
|
299 {
|
|
300 il->bytes_total = st.st_size;
|
|
301 }
|
|
302
|
|
303 il->loader = gdk_pixbuf_loader_new();
|
|
304 g_signal_connect(G_OBJECT(il->loader), "area_updated",
|
|
305 G_CALLBACK(image_loader_area_cb), il);
|
|
306 g_signal_connect(G_OBJECT(il->loader), "size_prepared",
|
|
307 G_CALLBACK(image_loader_size_cb), il);
|
|
308
|
|
309 return image_loader_begin(il);
|
|
310 }
|
|
311
|
|
312 ImageLoader *image_loader_new(const gchar *path)
|
|
313 {
|
|
314 ImageLoader *il;
|
|
315
|
|
316 if (!path) return NULL;
|
|
317
|
|
318 il = g_new0(ImageLoader, 1);
|
|
319 if (path) il->path = g_strdup(path);
|
|
320 il->pixbuf = NULL;
|
|
321 il->idle_id = -1;
|
|
322 il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
|
|
323 il->done = FALSE;
|
|
324 il->loader = NULL;
|
|
325 il->load_fd = -1;
|
|
326
|
|
327 il->bytes_read = 0;
|
|
328 il->bytes_total = 0;
|
|
329
|
|
330 il->idle_done_id = -1;
|
|
331
|
|
332 il->buffer_size = IMAGE_LOADER_BUFFER_DEFAULT_COUNT;
|
|
333
|
|
334 il->requested_width = 0;
|
|
335 il->requested_height = 0;
|
|
336
|
|
337 return il;
|
|
338 }
|
|
339
|
|
340 void image_loader_free(ImageLoader *il)
|
|
341 {
|
|
342 if (!il) return;
|
|
343
|
|
344 image_loader_stop(il);
|
|
345 if (il->idle_done_id != -1) g_source_remove(il->idle_done_id);
|
|
346 if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
|
|
347 g_free(il->path);
|
|
348 g_free(il);
|
|
349 }
|
|
350
|
|
351 /* don't forget to gdk_pixbuf_ref() it if you want to use it after image_loader_free() */
|
|
352 GdkPixbuf *image_loader_get_pixbuf(ImageLoader *il)
|
|
353 {
|
|
354 if (!il) return NULL;
|
|
355
|
|
356 return il->pixbuf;
|
|
357 }
|
|
358
|
|
359 gchar *image_loader_get_format(ImageLoader *il)
|
|
360 {
|
|
361 GdkPixbufFormat *format;
|
|
362 gchar **mimev;
|
|
363 gchar *mime;
|
|
364
|
|
365 if (!il || !il->loader) return NULL;
|
|
366
|
|
367 format = gdk_pixbuf_loader_get_format(il->loader);
|
|
368 if (!format) return NULL;
|
|
369
|
|
370 mimev = gdk_pixbuf_format_get_mime_types(format);
|
|
371 if (!mimev) return NULL;
|
|
372
|
|
373 /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
|
|
374 mime = g_strdup(mimev[0]);
|
|
375 g_strfreev(mimev);
|
|
376
|
|
377 return mime;
|
|
378 }
|
|
379
|
|
380 void image_loader_set_area_ready_func(ImageLoader *il,
|
|
381 void (*func_area_ready)(ImageLoader *, guint, guint, guint, guint, gpointer),
|
|
382 gpointer data_area_ready)
|
|
383 {
|
|
384 if (!il) return;
|
|
385
|
|
386 il->func_area_ready = func_area_ready;
|
|
387 il->data_area_ready = data_area_ready;
|
|
388 }
|
|
389
|
|
390 void image_loader_set_error_func(ImageLoader *il,
|
|
391 void (*func_error)(ImageLoader *, gpointer),
|
|
392 gpointer data_error)
|
|
393 {
|
|
394 if (!il) return;
|
|
395
|
|
396 il->func_error = func_error;
|
|
397 il->data_error = data_error;
|
|
398 }
|
|
399
|
|
400 void image_loader_set_percent_func(ImageLoader *il,
|
|
401 void (*func_percent)(ImageLoader *, gdouble, gpointer),
|
|
402 gpointer data_percent)
|
|
403 {
|
|
404 if (!il) return;
|
|
405
|
|
406 il->func_percent = func_percent;
|
|
407 il->data_percent = data_percent;
|
|
408 }
|
|
409
|
|
410 void image_loader_set_requested_size(ImageLoader *il, gint width, gint height)
|
|
411 {
|
|
412 if (!il) return;
|
|
413
|
|
414 il->requested_width = width;
|
|
415 il->requested_height = height;
|
|
416 }
|
|
417
|
|
418 void image_loader_set_buffer_size(ImageLoader *il, guint size)
|
|
419 {
|
|
420 if (!il) return;
|
|
421
|
|
422 il->buffer_size = size ? size : 1;
|
|
423 }
|
|
424
|
|
425 void image_loader_set_priority(ImageLoader *il, gint priority)
|
|
426 {
|
|
427 if (!il) return;
|
|
428
|
|
429 il->idle_priority = priority;
|
|
430 }
|
|
431
|
|
432 gint image_loader_start(ImageLoader *il, void (*func_done)(ImageLoader *, gpointer), gpointer data_done)
|
|
433 {
|
|
434 if (!il) return FALSE;
|
|
435
|
|
436 if (!il->path) return FALSE;
|
|
437
|
|
438 il->func_done = func_done;
|
|
439 il->data_done = data_done;
|
|
440
|
|
441 return image_loader_setup(il);
|
|
442 }
|
|
443
|
|
444 gdouble image_loader_get_percent(ImageLoader *il)
|
|
445 {
|
|
446 if (!il || il->bytes_total == 0) return 0.0;
|
|
447
|
|
448 return (gdouble)il->bytes_read / il->bytes_total;
|
|
449 }
|
|
450
|
|
451 gint image_loader_get_is_done(ImageLoader *il)
|
|
452 {
|
|
453 if (!il) return FALSE;
|
|
454
|
|
455 return il->done;
|
|
456 }
|
|
457
|
|
458 gint image_load_dimensions(const gchar *path, gint *width, gint *height)
|
|
459 {
|
|
460 ImageLoader *il;
|
|
461 gint success;
|
|
462
|
|
463 il = image_loader_new(path);
|
|
464
|
|
465 success = image_loader_start(il, NULL, NULL);
|
|
466
|
|
467 if (success && il->pixbuf)
|
|
468 {
|
|
469 if (width) *width = gdk_pixbuf_get_width(il->pixbuf);
|
|
470 if (height) *height = gdk_pixbuf_get_height(il->pixbuf);;
|
|
471 }
|
|
472 else
|
|
473 {
|
|
474 if (width) *width = -1;
|
|
475 if (height) *height = -1;
|
|
476 }
|
|
477
|
|
478 image_loader_free(il);
|
|
479
|
|
480 return success;
|
|
481 }
|
|
482
|