1
|
1 /*
|
9
|
2 * GQview
|
|
3 * (C) 2004 John Ellis
|
1
|
4 *
|
|
5 * Author: John Ellis
|
|
6 *
|
9
|
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!
|
1
|
10 */
|
|
11
|
9
|
12
|
1
|
13 #include "gqview.h"
|
|
14 #include "image.h"
|
9
|
15
|
|
16
|
|
17 #include "image-load.h"
|
|
18 #include "collect.h"
|
|
19 #include "exif.h"
|
|
20 #include "pixbuf_util.h"
|
|
21 #include "ui_fileops.h"
|
|
22
|
|
23 #include <math.h>
|
|
24
|
|
25
|
|
26 #define IMAGE_TILE_SIZE 128
|
|
27 #define IMAGE_ZOOM_MIN -32.0
|
|
28 #define IMAGE_ZOOM_MAX 32.0
|
|
29
|
|
30 /* size of the image loader buffer (512 bytes x defined number) */
|
|
31 #define IMAGE_LOAD_BUFFER_COUNT 8
|
|
32
|
|
33 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
|
|
34 #define IMAGE_THROTTLE_LARGER_IMAGES 1
|
|
35
|
|
36 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
|
|
37 #define IMAGE_THROTTLE_FACTOR 4
|
|
38
|
|
39 /* the file size at which throttling take place */
|
|
40 #define IMAGE_THROTTLE_THRESHOLD 1048576
|
|
41
|
|
42 /* distance to drag mouse to disable image flip */
|
|
43 #define IMAGE_DRAG_SCROLL_THRESHHOLD 4
|
|
44
|
|
45 /* alpha channel checkerboard background (same as gimp) */
|
|
46 #define IMAGE_ALPHA_CHECK1 0x00999999
|
|
47 #define IMAGE_ALPHA_CHECK2 0x00666666
|
|
48 #define IMAGE_ALPHA_CHECK_SIZE 16
|
|
49
|
|
50 #define IMAGE_AUTO_REFRESH_TIME 3000
|
|
51
|
|
52 /* when scaling image to below this size, use nearest pixel for scaling
|
|
53 * (below about 4, the other scale types become slow generating their conversion tables)
|
|
54 */
|
|
55 #define IMAGE_MIN_SCALE_SIZE 8
|
|
56
|
|
57
|
|
58 typedef enum {
|
|
59 TILE_RENDER_NONE = 0, /* do nothing */
|
|
60 TILE_RENDER_AREA, /* render an area of the tile */
|
|
61 TILE_RENDER_ALL /* render the whole tile */
|
|
62 } TileRenderType;
|
|
63
|
|
64 typedef struct _ImageTile ImageTile;
|
|
65 struct _ImageTile
|
|
66 {
|
|
67 GdkPixmap *pixmap; /* off screen buffer */
|
|
68 GdkPixbuf *pixbuf; /* pixbuf area for zooming */
|
|
69 gint x; /* x offset into image */
|
|
70 gint y; /* y offset into image */
|
|
71 gint w; /* width that is visible (may be less if at edge of image) */
|
|
72 gint h; /* height '' */
|
|
73
|
|
74 gint blank;
|
|
75
|
|
76 /* render_todo: (explanation)
|
|
77 NONE do nothing
|
|
78 AREA render area of tile, usually only used when loading an image
|
|
79 note: will jump to an ALL if render_done is not ALL.
|
|
80 ALL render entire tile, if never done before w/ ALL, for expose events *only*
|
|
81 */
|
|
82
|
|
83 TileRenderType render_todo; /* what to do (see above) */
|
|
84 TileRenderType render_done; /* highest that has been done before on tile */
|
|
85 };
|
|
86
|
|
87 typedef struct _QueueData QueueData;
|
|
88 struct _QueueData
|
|
89 {
|
|
90 ImageTile *it;
|
|
91 gint x;
|
|
92 gint y;
|
|
93 gint w;
|
|
94 gint h;
|
|
95 gint new_data;
|
|
96 };
|
|
97
|
|
98 typedef struct _CacheData CacheData;
|
|
99 struct _CacheData
|
|
100 {
|
|
101 GdkPixmap *pixmap;
|
|
102 GdkPixbuf *pixbuf;
|
|
103 ImageTile *it;
|
|
104 guint size;
|
|
105 };
|
|
106
|
|
107 typedef struct _OverlayData OverlayData;
|
|
108 struct _OverlayData
|
|
109 {
|
|
110 gint id;
|
|
111
|
|
112 GdkPixbuf *pixbuf;
|
|
113
|
|
114 gint x;
|
|
115 gint y;
|
|
116 gint relative; /* x,y coordinates are relative, negative values start bottom right */
|
|
117
|
|
118 gint visible;
|
|
119 gint always; /* hide temporarily when scrolling */
|
|
120 };
|
|
121
|
|
122
|
|
123 static void image_queue_clear(ImageWindow *imd);
|
|
124
|
|
125 static void image_update_title(ImageWindow *imd);
|
|
126 static void image_update_util(ImageWindow *imd);
|
|
127 static void image_complete_util(ImageWindow *imd, gint preload);
|
|
128
|
|
129 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent);
|
|
130
|
|
131 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h);
|
|
132 static void image_overlay_queue_all(ImageWindow *imd);
|
|
133
|
|
134 static void image_scroller_timer_set(ImageWindow *imd, gint start);
|
|
135
|
|
136
|
|
137 static gint util_clip_region(gint x, gint y, gint w, gint h,
|
|
138 gint clip_x, gint clip_y, gint clip_w, gint clip_h,
|
|
139 gint *rx, gint *ry, gint *rw, gint *rh)
|
|
140 {
|
|
141 if (clip_x + clip_w <= x ||
|
|
142 clip_x >= x + w ||
|
|
143 clip_y + clip_h <= y ||
|
|
144 clip_y >= y + h)
|
|
145 {
|
|
146 return FALSE;
|
|
147 }
|
|
148
|
|
149 *rx = MAX(x, clip_x);
|
|
150 *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
|
|
151
|
|
152 *ry = MAX(y, clip_y);
|
|
153 *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
|
|
154
|
|
155 return TRUE;
|
|
156 }
|
|
157
|
1
|
158
|
|
159 /*
|
9
|
160 *-------------------------------------------------------------------
|
|
161 * tile cache
|
|
162 *-------------------------------------------------------------------
|
1
|
163 */
|
|
164
|
9
|
165 static gint pixmap_calc_size(GdkPixmap *pixmap)
|
|
166 {
|
|
167 gint w, h, d;
|
|
168
|
|
169 d = gdk_drawable_get_depth(pixmap);
|
|
170 gdk_drawable_get_size(pixmap, &w, &h);
|
|
171 return w * h * (d / 8);
|
|
172 }
|
|
173
|
|
174 static void image_tile_cache_remove(ImageWindow *imd, ImageTile *it)
|
|
175 {
|
|
176 GList *work;
|
|
177
|
|
178 work = imd->tile_cache;
|
|
179 while(work)
|
|
180 {
|
|
181 CacheData *cd = work->data;
|
|
182 work = work->next;
|
|
183
|
|
184 if (cd->it == it)
|
|
185 {
|
|
186 imd->tile_cache = g_list_remove(imd->tile_cache, cd);
|
|
187 imd->tile_cache_size -= cd->size;
|
|
188 g_free(cd);
|
|
189 }
|
|
190 }
|
|
191 }
|
|
192
|
|
193 static void image_tile_cache_free(ImageWindow *imd, CacheData *cd)
|
1
|
194 {
|
9
|
195 imd->tile_cache = g_list_remove(imd->tile_cache, cd);
|
|
196 if (cd->pixmap)
|
|
197 {
|
|
198 g_object_unref(cd->it->pixmap);
|
|
199 cd->it->pixmap = NULL;
|
|
200 cd->it->render_done = TILE_RENDER_NONE;
|
|
201 }
|
|
202 if (cd->pixbuf)
|
|
203 {
|
|
204 gdk_pixbuf_unref(cd->it->pixbuf);
|
|
205 cd->it->pixbuf = NULL;
|
|
206 }
|
|
207 imd->tile_cache_size -= cd->size;
|
|
208 g_free(cd);
|
|
209 }
|
|
210
|
|
211 static void image_tile_cache_free_space(ImageWindow *imd, gint space, ImageTile *it)
|
|
212 {
|
|
213 GList *work = g_list_last(imd->tile_cache);
|
|
214
|
|
215 while (work && imd->tile_cache_size > 0 && imd->tile_cache_size + space > tile_cache_max * 1048576)
|
1
|
216 {
|
9
|
217 CacheData *cd = work->data;
|
|
218 work = work->prev;
|
|
219 if (cd->it != it) image_tile_cache_free(imd, cd);
|
1
|
220 }
|
|
221 }
|
|
222
|
9
|
223 static void image_tile_cache_add(ImageWindow *imd, ImageTile *it,
|
|
224 GdkPixmap *pixmap, GdkPixbuf *pixbuf, guint size)
|
|
225 {
|
|
226 CacheData *cd;
|
|
227
|
|
228 cd = g_new(CacheData, 1);
|
|
229 cd->pixmap = pixmap;
|
|
230 cd->pixbuf = pixbuf;
|
|
231 cd->it = it;
|
|
232 cd->size = size;
|
|
233
|
|
234 imd->tile_cache = g_list_prepend(imd->tile_cache, cd);
|
|
235
|
|
236 imd->tile_cache_size += cd->size;
|
|
237 }
|
|
238
|
|
239 static void image_tile_prepare(ImageWindow *imd, ImageTile *it)
|
1
|
240 {
|
9
|
241 if (!it->pixmap)
|
|
242 {
|
|
243 GdkPixmap *pixmap;
|
|
244 guint size;
|
|
245
|
|
246 pixmap = gdk_pixmap_new(imd->image->window, imd->tile_width, imd->tile_height, -1);
|
|
247
|
|
248 size = pixmap_calc_size(pixmap);
|
|
249 image_tile_cache_free_space(imd, size, it);
|
|
250
|
|
251 it->pixmap = pixmap;
|
|
252 image_tile_cache_add(imd, it, pixmap, NULL, size);
|
|
253 }
|
|
254
|
|
255 if ((imd->zoom != 1.0 || gdk_pixbuf_get_has_alpha(imd->pixbuf)) &&
|
|
256 !it->pixbuf)
|
|
257 {
|
|
258 GdkPixbuf *pixbuf;
|
|
259 guint size;
|
|
260
|
|
261 pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(imd->pixbuf),
|
|
262 gdk_pixbuf_get_has_alpha(imd->pixbuf),
|
|
263 gdk_pixbuf_get_bits_per_sample(imd->pixbuf),
|
|
264 imd->tile_width, imd->tile_height);
|
|
265
|
|
266 size = gdk_pixbuf_get_rowstride(pixbuf) * imd->tile_height;
|
|
267 image_tile_cache_free_space(imd, size, it);
|
|
268
|
|
269 it->pixbuf = pixbuf;
|
|
270 image_tile_cache_add(imd, it, NULL, pixbuf, size);
|
|
271 }
|
1
|
272 }
|
|
273
|
9
|
274 /*
|
|
275 *-------------------------------------------------------------------
|
|
276 * tiles
|
|
277 *-------------------------------------------------------------------
|
|
278 */
|
|
279
|
|
280 static ImageTile *image_tile_new(gint w, gint h)
|
1
|
281 {
|
9
|
282 ImageTile *it;
|
|
283
|
|
284 it = g_new0(ImageTile, 1);
|
|
285 it->w = w;
|
|
286 it->h = h;
|
|
287 it->pixmap = NULL;
|
|
288 it->pixbuf = NULL;
|
|
289 it->blank = TRUE;
|
|
290 it->render_todo = TILE_RENDER_NONE;
|
|
291 it->render_done = TILE_RENDER_NONE;
|
|
292
|
|
293 return it;
|
1
|
294 }
|
|
295
|
9
|
296 static void image_tile_free(ImageTile *it)
|
|
297 {
|
|
298 if (!it) return;
|
|
299
|
|
300 if (it->pixbuf) gdk_pixbuf_unref(it->pixbuf);
|
|
301 if (it->pixmap) g_object_unref(it->pixmap);
|
|
302
|
|
303 g_free(it);
|
|
304 }
|
|
305
|
|
306 static void image_tile_sync_count(ImageWindow *imd, gint n)
|
1
|
307 {
|
9
|
308 gint l;
|
|
309
|
|
310 l = g_list_length(imd->tiles);
|
|
311
|
|
312 if (l == n) return;
|
|
313
|
|
314 if (l < n)
|
1
|
315 {
|
9
|
316 while (l < n)
|
|
317 {
|
|
318 imd->tiles = g_list_prepend(imd->tiles, image_tile_new(imd->tile_width, imd->tile_height));
|
|
319 l++;
|
|
320 }
|
1
|
321 }
|
|
322 else
|
|
323 {
|
9
|
324 /* This should remove from the tail of the GList, but with large images there are many tiles,
|
|
325 * making this significantly faster for those cases.
|
|
326 */
|
|
327 while (l > n && imd->tiles)
|
|
328 {
|
|
329 ImageTile *it = imd->tiles->data;
|
|
330 imd->tiles = g_list_remove(imd->tiles, it);
|
|
331 image_tile_cache_remove(imd, it);
|
|
332 image_tile_free(it);
|
|
333 l--;
|
|
334 }
|
|
335 }
|
|
336 }
|
|
337
|
|
338 static void image_tile_sync(ImageWindow *imd, gint width, gint height, gint blank)
|
|
339 {
|
|
340 gint rows;
|
|
341 gint x, y;
|
|
342 GList *work;
|
|
343
|
|
344 imd->tile_cols = (width + imd->tile_width - 1) / imd->tile_width;
|
|
345
|
|
346 rows = (height + imd->tile_height - 1) / imd->tile_height;
|
|
347
|
|
348 image_tile_sync_count(imd, imd->tile_cols * rows);
|
|
349
|
|
350 x = y = 0;
|
|
351 work = imd->tiles;
|
|
352 while(work)
|
|
353 {
|
|
354 ImageTile *it = work->data;
|
|
355 work = work->next;
|
|
356
|
|
357 it->x = x;
|
|
358 it->y = y;
|
|
359 if (x + imd->tile_width > width)
|
|
360 {
|
|
361 it->w = width - x;
|
|
362 }
|
|
363 else
|
|
364 {
|
|
365 it->w = imd->tile_width;
|
|
366 }
|
|
367 if (y + imd->tile_height > height)
|
|
368 {
|
|
369 it->h = height - y;
|
|
370 }
|
|
371 else
|
|
372 {
|
|
373 it->h = imd->tile_height;
|
|
374 }
|
|
375
|
|
376 it->blank = blank;
|
|
377 it->render_todo = TILE_RENDER_NONE;
|
|
378 it->render_done = TILE_RENDER_NONE;
|
|
379
|
|
380 x += imd->tile_width;
|
|
381 if (x >= width)
|
|
382 {
|
|
383 x = 0;
|
|
384 y += imd->tile_height;
|
|
385 }
|
|
386 }
|
|
387
|
|
388 /* all it's are now useless in queue */
|
|
389 image_queue_clear(imd);
|
|
390 }
|
|
391
|
|
392 static void image_tile_render(ImageWindow *imd, ImageTile *it,
|
|
393 gint x, gint y, gint w, gint h,
|
|
394 gint new_data, gint fast)
|
|
395 {
|
|
396 gint has_alpha;
|
|
397 gint draw = FALSE;
|
|
398
|
|
399 if (it->render_todo == TILE_RENDER_NONE && it->pixmap) return;
|
|
400
|
|
401 if (it->render_done != TILE_RENDER_ALL)
|
|
402 {
|
|
403 x = 0;
|
|
404 y = 0;
|
|
405 w = it->w;
|
|
406 h = it->h;
|
|
407 if (!fast) it->render_done = TILE_RENDER_ALL;
|
|
408 }
|
|
409 else if (it->render_todo != TILE_RENDER_AREA)
|
|
410 {
|
|
411 if (!fast) it->render_todo = TILE_RENDER_NONE;
|
|
412 return;
|
1
|
413 }
|
|
414
|
9
|
415 if (!fast) it->render_todo = TILE_RENDER_NONE;
|
|
416
|
|
417 if (new_data) it->blank = FALSE;
|
|
418
|
|
419 image_tile_prepare(imd, it);
|
|
420 has_alpha = gdk_pixbuf_get_has_alpha(imd->pixbuf);
|
|
421
|
|
422 /* FIXME checker colors for alpha should be configurable,
|
|
423 * also should be drawn for blank = TRUE
|
|
424 */
|
|
425
|
|
426 if (it->blank)
|
|
427 {
|
|
428 /* no data, do fast rect fill */
|
|
429 gdk_draw_rectangle(it->pixmap, imd->image->style->black_gc, TRUE,
|
|
430 0, 0, it->w, it->h);
|
|
431 }
|
|
432 else if (imd->zoom == 1.0 || imd->scale == 1.0)
|
|
433 {
|
|
434 if (has_alpha)
|
|
435 {
|
|
436 gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
|
|
437 (double) 0.0 - it->x,
|
|
438 (double) 0.0 - it->y,
|
|
439 1.0, 1.0, GDK_INTERP_NEAREST,
|
|
440 255, it->x + x, it->y + y,
|
|
441 IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
|
|
442 draw = TRUE;
|
|
443 }
|
|
444 else
|
|
445 {
|
|
446 /* faster, simple */
|
|
447 gdk_draw_pixbuf(it->pixmap,
|
|
448 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
|
|
449 imd->pixbuf,
|
|
450 it->x + x, it->y + y,
|
|
451 x, y,
|
|
452 w, h,
|
|
453 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
|
|
454 }
|
|
455 }
|
|
456 else
|
|
457 {
|
|
458 double scale_x, scale_y;
|
|
459
|
|
460 if (imd->image_width == 0 || imd->image_height == 0) return;
|
|
461 scale_x = (double)imd->width / imd->image_width;
|
|
462 scale_y = (double)imd->height / imd->image_height;
|
|
463
|
|
464 /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely
|
|
465 * small sizes for anything but GDK_INTERP_NEAREST
|
|
466 */
|
|
467 if (imd->width < IMAGE_MIN_SCALE_SIZE || imd->height < IMAGE_MIN_SCALE_SIZE) fast = TRUE;
|
|
468
|
|
469 if (!has_alpha)
|
|
470 {
|
|
471 gdk_pixbuf_scale(imd->pixbuf, it->pixbuf, x, y, w, h,
|
|
472 (double) 0.0 - it->x,
|
|
473 (double) 0.0 - it->y,
|
|
474 scale_x, scale_y,
|
|
475 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality);
|
|
476 }
|
|
477 else
|
|
478 {
|
|
479 gdk_pixbuf_composite_color(imd->pixbuf, it->pixbuf, x, y, w, h,
|
|
480 (double) 0.0 - it->x,
|
|
481 (double) 0.0 - it->y,
|
|
482 scale_x, scale_y,
|
|
483 (fast) ? GDK_INTERP_NEAREST : (GdkInterpType)zoom_quality,
|
|
484 255, it->x + x, it->y + y,
|
|
485 IMAGE_ALPHA_CHECK_SIZE, IMAGE_ALPHA_CHECK1, IMAGE_ALPHA_CHECK2);
|
|
486 }
|
|
487 draw = TRUE;
|
|
488 }
|
|
489
|
|
490 if (draw && it->pixbuf && !it->blank)
|
|
491 {
|
|
492 gdk_draw_pixbuf(it->pixmap,
|
|
493 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
|
|
494 it->pixbuf,
|
|
495 x, y,
|
|
496 x, y,
|
|
497 w, h,
|
|
498 (GdkRgbDither)dither_quality, it->x + x, it->y + y);
|
|
499 }
|
|
500 }
|
|
501
|
|
502 static void image_tile_expose(ImageWindow *imd, ImageTile *it,
|
|
503 gint x, gint y, gint w, gint h,
|
|
504 gint new_data, gint fast)
|
|
505 {
|
|
506 image_tile_render(imd, it, x, y, w, h, new_data, fast);
|
|
507
|
|
508 gdk_draw_drawable(imd->image->window, imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
|
|
509 it->pixmap, x, y,
|
|
510 imd->x_offset + (it->x - imd->x_scroll) + x, imd->y_offset + (it->y - imd->y_scroll) + y, w, h);
|
|
511
|
|
512 if (imd->overlay_list)
|
|
513 {
|
|
514 image_overlay_draw(imd, imd->x_offset + (it->x - imd->x_scroll) + x,
|
|
515 imd->y_offset + (it->y - imd->y_scroll) + y,
|
|
516 w, h);
|
|
517 }
|
|
518 }
|
|
519
|
|
520 static gint image_tile_is_visible(ImageWindow *imd, ImageTile *it)
|
|
521 {
|
|
522 return (it->x + it->w >= imd->x_scroll && it->x <= imd->x_scroll + imd->window_width - imd->x_offset * 2 &&
|
|
523 it->y + it->h >= imd->y_scroll && it->y <= imd->y_scroll + imd->window_height - imd->y_offset * 2);
|
1
|
524 }
|
|
525
|
|
526 /*
|
9
|
527 *-------------------------------------------------------------------
|
|
528 * render queue
|
|
529 *-------------------------------------------------------------------
|
|
530 */
|
|
531
|
|
532
|
|
533 static gint image_queue_draw_idle_cb(gpointer data)
|
|
534 {
|
|
535 ImageWindow *imd = data;
|
|
536 QueueData *qd;
|
|
537 gint fast;
|
|
538
|
|
539 if (!imd->pixbuf || (!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1)
|
|
540 {
|
|
541 if (!imd->completed) image_complete_util(imd, FALSE);
|
|
542
|
|
543 imd->draw_idle_id = -1;
|
|
544 return FALSE;
|
|
545 }
|
|
546
|
|
547 if (imd->draw_queue)
|
|
548 {
|
|
549 qd = imd->draw_queue->data;
|
|
550 fast = (two_pass_zoom && (GdkInterpType)zoom_quality != GDK_INTERP_NEAREST && imd->scale != 1.0);
|
|
551 }
|
|
552 else
|
|
553 {
|
|
554 if (imd->il)
|
|
555 {
|
|
556 /* still loading, wait till done (also drops the higher priority) */
|
|
557
|
|
558 imd->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
|
|
559 image_queue_draw_idle_cb, imd, NULL);
|
|
560 imd->draw_idle_high = FALSE;
|
|
561 return FALSE;
|
|
562 }
|
|
563 qd = imd->draw_queue_2pass->data;
|
|
564 fast = FALSE;
|
|
565 }
|
|
566
|
|
567 if (GTK_WIDGET_REALIZED(imd->image))
|
|
568 {
|
|
569 if (image_tile_is_visible(imd, qd->it))
|
|
570 {
|
|
571 image_tile_expose(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
|
|
572 }
|
|
573 else if (qd->new_data)
|
|
574 {
|
|
575 /* if new pixel data, and we already have a pixmap, update the tile */
|
|
576 qd->it->blank = FALSE;
|
|
577 if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL)
|
|
578 {
|
|
579 image_tile_render(imd, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast);
|
|
580 }
|
|
581 }
|
|
582 }
|
|
583
|
|
584 if (imd->draw_queue)
|
|
585 {
|
|
586 imd->draw_queue = g_list_remove(imd->draw_queue, qd);
|
|
587 if (fast)
|
|
588 {
|
|
589 imd->draw_queue_2pass = g_list_append(imd->draw_queue_2pass, qd);
|
|
590 }
|
|
591 else
|
|
592 {
|
|
593 g_free(qd);
|
|
594 }
|
|
595 }
|
|
596 else
|
|
597 {
|
|
598 imd->draw_queue_2pass = g_list_remove(imd->draw_queue_2pass, qd);
|
|
599 g_free(qd);
|
|
600 }
|
|
601
|
|
602 if (!imd->draw_queue && !imd->draw_queue_2pass)
|
|
603 {
|
|
604 if (!imd->completed) image_complete_util(imd, FALSE);
|
|
605
|
|
606 imd->draw_idle_id = -1;
|
|
607 return FALSE;
|
|
608 }
|
|
609
|
|
610 return TRUE;
|
|
611 }
|
|
612
|
|
613 static QueueData *image_queue_combine(ImageWindow *imd, QueueData *qd)
|
|
614 {
|
|
615 QueueData *found = NULL;
|
|
616 GList *work;
|
|
617
|
|
618 work = imd->draw_queue;
|
|
619 while (work && !found)
|
|
620 {
|
|
621 found = work->data;
|
|
622 work = work->next;
|
|
623
|
|
624 if (found->it != qd->it) found = NULL;
|
|
625 }
|
|
626
|
|
627 if (found)
|
|
628 {
|
|
629 if (found->x + found->w < qd->x + qd->w) found->w += (qd->x + qd->w) - (found->x + found->w);
|
|
630 if (found->x > qd->x)
|
|
631 {
|
|
632 found->w += found->x - qd->x;
|
|
633 found->x = qd->x;
|
|
634 }
|
|
635
|
|
636 if (found->y + found->h < qd->y + qd->h) found->h += (qd->y + qd->h) - (found->y + found->h);
|
|
637 if (found->y > qd->y)
|
|
638 {
|
|
639 found->h += found->y - qd->y;
|
|
640 found->y = qd->y;
|
|
641 }
|
|
642 found->new_data |= qd->new_data;
|
|
643 }
|
|
644
|
|
645 return found;
|
|
646 }
|
|
647
|
|
648 static gint image_clamp_to_visible(ImageWindow *imd, gint *x, gint *y, gint *w, gint *h)
|
|
649 {
|
|
650 gint nx, ny;
|
|
651 gint nw, nh;
|
|
652 gint vx, vy;
|
|
653 gint vw, vh;
|
|
654
|
|
655 vw = imd->vis_width;
|
|
656 vh = imd->vis_height;
|
|
657
|
|
658 vx = imd->x_scroll;
|
|
659 vy = imd->y_scroll;
|
|
660
|
|
661 if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE;
|
|
662
|
|
663 /* now clamp it */
|
|
664 nx = CLAMP(*x, vx, vx + vw);
|
|
665 nw = CLAMP(*w - (nx - *x), 1, vw);
|
|
666
|
|
667 ny = CLAMP(*y, vy, vy + vh);
|
|
668 nh = CLAMP(*h - (ny - *y), 1, vh);
|
|
669
|
|
670 *x = nx;
|
|
671 *y = ny;
|
|
672 *w = nw;
|
|
673 *h = nh;
|
|
674
|
|
675 return TRUE;
|
|
676 }
|
|
677
|
|
678 static gint image_queue_to_tiles(ImageWindow *imd, gint x, gint y, gint w, gint h,
|
|
679 gint clamp, TileRenderType render, gint new_data)
|
|
680 {
|
|
681 gint i, j;
|
|
682 gint x1, x2;
|
|
683 gint y1, y2;
|
|
684 GList *work;
|
|
685
|
|
686 if (clamp && !image_clamp_to_visible(imd, &x, &y, &w, &h)) return FALSE;
|
|
687
|
|
688 x1 = (gint)floor(x / imd->tile_width) * imd->tile_width;
|
|
689 x2 = (gint)ceil((x + w) / imd->tile_width) * imd->tile_width;
|
|
690
|
|
691 y1 = (gint)floor(y / imd->tile_height) * imd->tile_height;
|
|
692 y2 = (gint)ceil((y + h) / imd->tile_height) * imd->tile_height;
|
|
693
|
|
694 work = g_list_nth(imd->tiles, y1 / imd->tile_height * imd->tile_cols + (x1 / imd->tile_width));
|
|
695 for (j = y1; j <= y2; j += imd->tile_height)
|
|
696 {
|
|
697 GList *tmp;
|
|
698 tmp = work;
|
|
699 for (i = x1; i <= x2; i += imd->tile_width)
|
|
700 {
|
|
701 if (tmp)
|
|
702 {
|
|
703 ImageTile *it = tmp->data;
|
|
704 QueueData *qd;
|
|
705
|
|
706 if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) ||
|
|
707 (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL))
|
|
708 {
|
|
709 it->render_todo = render;
|
|
710 }
|
|
711
|
|
712 qd = g_new(QueueData, 1);
|
|
713 qd->it = it;
|
|
714 qd->new_data = new_data;
|
|
715
|
|
716 if (i < x)
|
|
717 {
|
|
718 qd->x = x - i;
|
|
719 }
|
|
720 else
|
|
721 {
|
|
722 qd->x = 0;
|
|
723 }
|
|
724 qd->w = x + w - i - qd->x;
|
|
725 if (qd->x + qd->w > imd->tile_width) qd->w = imd->tile_width - qd->x;
|
|
726
|
|
727
|
|
728 if (j < y)
|
|
729 {
|
|
730 qd->y = y - j;
|
|
731 }
|
|
732 else
|
|
733 {
|
|
734 qd->y = 0;
|
|
735 }
|
|
736 qd->h = y + h - j - qd->y;
|
|
737 if (qd->y + qd->h > imd->tile_height) qd->h = imd->tile_height - qd->y;
|
|
738
|
|
739 if (qd->w < 1 || qd->h < 1 || /* <--- sanity checks, rare cases cause this */
|
|
740 image_queue_combine(imd, qd))
|
|
741 {
|
|
742 g_free(qd);
|
|
743 }
|
|
744 else
|
|
745 {
|
|
746 imd->draw_queue = g_list_append(imd->draw_queue, qd);
|
|
747 }
|
|
748
|
|
749 tmp = tmp->next;
|
|
750 }
|
|
751 }
|
|
752 work = g_list_nth(work, imd->tile_cols); /* step 1 row */
|
|
753 }
|
|
754
|
|
755 return TRUE;
|
|
756 }
|
|
757
|
|
758 static void image_queue(ImageWindow *imd, gint x, gint y, gint w, gint h,
|
|
759 gint clamp, TileRenderType render, gint new_data)
|
|
760 {
|
|
761 gint nx, ny;
|
|
762
|
|
763 nx = CLAMP(x, 0, imd->width - 1);
|
|
764 ny = CLAMP(y, 0, imd->height - 1);
|
|
765 w -= (nx - x);
|
|
766 h -= (ny - y);
|
|
767 w = CLAMP(w, 0, imd->width - nx);
|
|
768 h = CLAMP(h, 0, imd->height - ny);
|
|
769 if (w < 1 || h < 1) return;
|
|
770
|
|
771 if (image_queue_to_tiles(imd, nx, ny, w, h, clamp, render, new_data) &&
|
|
772 ((!imd->draw_queue && !imd->draw_queue_2pass) || imd->draw_idle_id == -1 || !imd->draw_idle_high))
|
|
773 {
|
|
774 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
|
|
775 imd->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW,
|
|
776 image_queue_draw_idle_cb, imd, NULL);
|
|
777 imd->draw_idle_high = TRUE;
|
|
778 }
|
|
779 }
|
|
780
|
|
781 static void image_queue_list_free(GList *list)
|
|
782 {
|
|
783 GList *work;
|
|
784
|
|
785 work = list;
|
|
786 while (work)
|
|
787 {
|
|
788 QueueData *qd;
|
|
789
|
|
790 qd = work->data;
|
|
791 work = work->next;
|
|
792 g_free(qd);
|
|
793 }
|
|
794
|
|
795 g_list_free(list);
|
|
796 }
|
|
797
|
|
798 static void image_queue_clear(ImageWindow *imd)
|
|
799 {
|
|
800 image_queue_list_free(imd->draw_queue);
|
|
801 imd->draw_queue = NULL;
|
|
802
|
|
803 image_queue_list_free(imd->draw_queue_2pass);
|
|
804 imd->draw_queue_2pass = NULL;
|
|
805
|
|
806 if (imd->draw_idle_id != -1) g_source_remove(imd->draw_idle_id);
|
|
807 imd->draw_idle_id = -1;
|
|
808 }
|
|
809
|
|
810 /*
|
|
811 *-------------------------------------------------------------------
|
|
812 * core calculations
|
|
813 *-------------------------------------------------------------------
|
|
814 */
|
|
815
|
|
816 static gint image_top_window_sizable(ImageWindow *imd)
|
1
|
817 {
|
|
818 if (!imd->top_window) return FALSE;
|
|
819 if (!fit_window) return FALSE;
|
9
|
820 if (!imd->top_window_sync) return FALSE;
|
|
821 if (!imd->image->window) return FALSE;
|
|
822 if (window_maximized(imd->top_window)) return FALSE;
|
|
823
|
|
824 return TRUE;
|
|
825 }
|
|
826
|
|
827 static gint image_size_top_window(ImageWindow *imd, gint w, gint h)
|
|
828 {
|
|
829 gint ww, wh;
|
|
830
|
|
831 if (!image_top_window_sizable(imd)) return FALSE;
|
1
|
832
|
|
833 if (limit_window_size)
|
|
834 {
|
|
835 gint sw = gdk_screen_width() * max_window_size / 100;
|
|
836 gint sh = gdk_screen_height() * max_window_size / 100;
|
|
837
|
|
838 if (w > sw) w = sw;
|
|
839 if (h > sh) h = sh;
|
|
840 }
|
|
841
|
9
|
842 w += (imd->top_window->allocation.width - imd->image->allocation.width);
|
|
843 h += (imd->top_window->allocation.height - imd->image->allocation.height);
|
|
844
|
|
845 gdk_drawable_get_size(imd->top_window->window, &ww, &wh);
|
|
846 if (w == ww && h == wh) return FALSE;
|
1
|
847
|
|
848 gdk_window_resize(imd->top_window->window, w, h);
|
|
849
|
|
850 return TRUE;
|
|
851 }
|
|
852
|
9
|
853 static void image_redraw(ImageWindow *imd, gint new_data)
|
|
854 {
|
|
855 image_queue_clear(imd);
|
|
856 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, new_data);
|
|
857 }
|
|
858
|
|
859 static void image_border_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
|
1
|
860 {
|
9
|
861 gint rx, ry, rw, rh;
|
|
862
|
|
863 if (!imd->image->window) return;
|
|
864
|
|
865 if (!imd->pixbuf)
|
|
866 {
|
|
867 if (util_clip_region(x, y, w, h,
|
|
868 0, 0,
|
|
869 imd->window_width, imd->window_height,
|
|
870 &rx, &ry, &rw, &rh))
|
|
871 {
|
|
872 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
|
|
873 image_overlay_draw(imd, rx, ry, rw, rh);
|
|
874 }
|
|
875 return;
|
|
876 }
|
|
877
|
|
878 if (imd->vis_width < imd->window_width)
|
1
|
879 {
|
9
|
880 if (imd->x_offset > 0 &&
|
|
881 util_clip_region(x, y, w, h,
|
|
882 0, 0,
|
|
883 imd->x_offset, imd->window_height,
|
|
884 &rx, &ry, &rw, &rh))
|
|
885 {
|
|
886 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
|
|
887 image_overlay_draw(imd, rx, ry, rw, rh);
|
|
888 }
|
|
889 if (imd->window_width - imd->vis_width - imd->x_offset > 0 &&
|
|
890 util_clip_region(x, y, w, h,
|
|
891 imd->x_offset + imd->vis_width, 0,
|
|
892 imd->window_width - imd->vis_width - imd->x_offset, imd->window_height,
|
|
893 &rx, &ry, &rw, &rh))
|
|
894 {
|
|
895 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
|
|
896 image_overlay_draw(imd, rx, ry, rw, rh);
|
|
897 }
|
1
|
898 }
|
9
|
899 if (imd->vis_height < imd->window_height)
|
1
|
900 {
|
9
|
901 if (imd->y_offset > 0 &&
|
|
902 util_clip_region(x, y, w, h,
|
|
903 imd->x_offset, 0,
|
|
904 imd->vis_width, imd->y_offset,
|
|
905 &rx, &ry, &rw, &rh))
|
|
906 {
|
|
907 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
|
|
908 image_overlay_draw(imd, rx, ry, rw, rh);
|
|
909 }
|
|
910 if (imd->window_height - imd->vis_height - imd->y_offset > 0 &&
|
|
911 util_clip_region(x, y, w, h,
|
|
912 imd->x_offset, imd->y_offset + imd->vis_height,
|
|
913 imd->vis_width, imd->window_height - imd->vis_height - imd->y_offset,
|
|
914 &rx, &ry, &rw, &rh))
|
|
915 {
|
|
916 gdk_window_clear_area(imd->image->window, rx, ry, rw, rh);
|
|
917 image_overlay_draw(imd, rx, ry, rw, rh);
|
|
918 }
|
1
|
919 }
|
|
920 }
|
|
921
|
9
|
922 static void image_border_clear(ImageWindow *imd)
|
1
|
923 {
|
9
|
924 image_border_draw(imd, 0, 0, imd->window_width, imd->window_height);
|
1
|
925 }
|
|
926
|
9
|
927 static gint image_scroll_clamp(ImageWindow *imd)
|
1
|
928 {
|
9
|
929 gint old_xs;
|
|
930 gint old_ys;
|
|
931
|
|
932 if (imd->zoom == 0.0)
|
1
|
933 {
|
9
|
934 imd->x_scroll = 0;
|
|
935 imd->y_scroll = 0;
|
|
936 return FALSE;
|
|
937 }
|
|
938
|
|
939 old_xs = imd->x_scroll;
|
|
940 old_ys = imd->y_scroll;
|
|
941
|
|
942 if (imd->x_offset > 0)
|
|
943 {
|
|
944 imd->x_scroll = 0;
|
1
|
945 }
|
|
946 else
|
|
947 {
|
9
|
948 imd->x_scroll = CLAMP(imd->x_scroll, 0, imd->width - imd->vis_width);
|
1
|
949 }
|
9
|
950
|
|
951 if (imd->y_offset > 0)
|
1
|
952 {
|
9
|
953 imd->y_scroll = 0;
|
1
|
954 }
|
|
955 else
|
|
956 {
|
9
|
957 imd->y_scroll = CLAMP(imd->y_scroll, 0, imd->height - imd->vis_height);
|
1
|
958 }
|
|
959
|
9
|
960 return (old_xs != imd->x_scroll || old_ys != imd->y_scroll);
|
|
961 }
|
|
962
|
|
963 static gint image_zoom_clamp(ImageWindow *imd, gdouble zoom, gint force, gint new)
|
|
964 {
|
|
965 gint w, h;
|
|
966 gdouble scale;
|
|
967
|
|
968 zoom = CLAMP(zoom, IMAGE_ZOOM_MIN, IMAGE_ZOOM_MAX);
|
|
969
|
|
970 if (imd->zoom == zoom && !force) return FALSE;
|
|
971
|
|
972 w = imd->image_width;
|
|
973 h = imd->image_height;
|
|
974
|
|
975 if (zoom == 0.0 && !imd->pixbuf)
|
1
|
976 {
|
9
|
977 scale = 1.0;
|
1
|
978 }
|
9
|
979 else if (zoom == 0.0)
|
1
|
980 {
|
9
|
981 gint max_w;
|
|
982 gint max_h;
|
|
983 gint sizeable;
|
|
984
|
|
985 sizeable = (new && image_top_window_sizable(imd));
|
|
986
|
|
987 if (sizeable)
|
1
|
988 {
|
9
|
989 max_w = gdk_screen_width();
|
|
990 max_h = gdk_screen_height();
|
|
991
|
1
|
992 if (limit_window_size)
|
|
993 {
|
9
|
994 max_w = max_w * max_window_size / 100;
|
|
995 max_h = max_h * max_window_size / 100;
|
|
996 }
|
|
997 }
|
|
998 else
|
|
999 {
|
|
1000 max_w = imd->window_width;
|
|
1001 max_h = imd->window_height;
|
|
1002 }
|
|
1003
|
|
1004 if ((zoom_to_fit_expands && !sizeable) || w > max_w || h > max_h)
|
|
1005 {
|
|
1006 if ((gdouble)max_w / w > (gdouble)max_h / h)
|
|
1007 {
|
|
1008 scale = (gdouble)max_h / h;
|
|
1009 h = max_h;
|
|
1010 w = w * scale + 0.5;
|
|
1011 if (w > max_w) w = max_w;
|
1
|
1012 }
|
|
1013 else
|
|
1014 {
|
9
|
1015 scale = (gdouble)max_w / w;
|
|
1016 w = max_w;
|
|
1017 h = h * scale + 0.5;
|
|
1018 if (h > max_h) h = max_h;
|
1
|
1019 }
|
|
1020 if (w < 1) w = 1;
|
|
1021 if (h < 1) h = 1;
|
|
1022 }
|
9
|
1023 else
|
|
1024 {
|
|
1025 scale = 1.0;
|
|
1026 }
|
1
|
1027 }
|
9
|
1028 else if (zoom > 0.0) /* zoom orig, in */
|
1
|
1029 {
|
9
|
1030 scale = zoom;
|
|
1031 w = w * scale;
|
|
1032 h = h * scale;
|
1
|
1033 }
|
9
|
1034 else /* zoom out */
|
|
1035 {
|
|
1036 scale = 1.0 / (0.0 - zoom);
|
|
1037 w = w * scale;
|
|
1038 h = h * scale;
|
|
1039 }
|
|
1040
|
|
1041 imd->zoom = zoom;
|
1
|
1042 imd->width = w;
|
|
1043 imd->height = h;
|
9
|
1044 imd->scale = scale;
|
|
1045
|
|
1046 return TRUE;
|
1
|
1047 }
|
|
1048
|
9
|
1049 static gint image_size_clamp(ImageWindow *imd)
|
1
|
1050 {
|
9
|
1051 gint old_vw, old_vh;
|
|
1052
|
|
1053 old_vw = imd->vis_width;
|
|
1054 old_vh = imd->vis_height;
|
|
1055
|
|
1056 if (imd->width < imd->window_width)
|
1
|
1057 {
|
9
|
1058 imd->vis_width = imd->width;
|
|
1059 imd->x_offset = (imd->window_width - imd->width) / 2;
|
1
|
1060 }
|
|
1061 else
|
|
1062 {
|
9
|
1063 imd->vis_width = imd->window_width;
|
|
1064 imd->x_offset = 0;
|
1
|
1065 }
|
|
1066
|
9
|
1067 if (imd->height < imd->window_height)
|
1
|
1068 {
|
9
|
1069 imd->vis_height = imd->height;
|
|
1070 imd->y_offset = (imd->window_height - imd->height) / 2;
|
1
|
1071 }
|
|
1072 else
|
|
1073 {
|
9
|
1074 imd->vis_height = imd->window_height;
|
|
1075 imd->y_offset = 0;
|
1
|
1076 }
|
|
1077
|
9
|
1078 return (old_vw != imd->vis_width || old_vh != imd->vis_height);
|
|
1079 }
|
|
1080
|
|
1081 static void image_size_sync(ImageWindow *imd, gint new_width, gint new_height)
|
|
1082 {
|
|
1083 if (imd->window_width == new_width && imd->window_height == new_height) return;
|
|
1084
|
|
1085 imd->window_width = new_width;
|
|
1086 imd->window_height = new_height;
|
|
1087
|
|
1088 if (imd->zoom == 0.0) image_zoom_clamp(imd, 0.0, TRUE, FALSE);
|
|
1089
|
|
1090 image_size_clamp(imd);
|
|
1091 image_scroll_clamp(imd);
|
|
1092
|
|
1093 gtk_widget_set_size_request(imd->image, imd->window_width, imd->window_height);
|
|
1094
|
|
1095 /* ensure scroller remains visible */
|
|
1096 if (imd->scroller_overlay != -1)
|
1
|
1097 {
|
9
|
1098 gint update = FALSE;
|
|
1099
|
|
1100 if (imd->scroller_x > new_width)
|
|
1101 {
|
|
1102 imd->scroller_x = new_width;
|
|
1103 imd->scroller_xpos = new_width;
|
|
1104 update = TRUE;
|
|
1105 }
|
|
1106 if (imd->scroller_y > new_height)
|
|
1107 {
|
|
1108 imd->scroller_y = new_height;
|
|
1109 imd->scroller_ypos = new_height;
|
|
1110 update = TRUE;
|
|
1111 }
|
|
1112
|
|
1113 if (update)
|
|
1114 {
|
|
1115 GdkPixbuf *pixbuf;
|
|
1116
|
|
1117 if (image_overlay_get(imd, imd->scroller_overlay, &pixbuf, NULL, NULL))
|
|
1118 {
|
|
1119 gint w, h;
|
|
1120
|
|
1121 w = gdk_pixbuf_get_width(pixbuf);
|
|
1122 h = gdk_pixbuf_get_height(pixbuf);
|
|
1123 image_overlay_set(imd, imd->scroller_overlay, pixbuf,
|
|
1124 imd->scroller_x - w / 2, imd->scroller_y - h / 2);
|
|
1125 }
|
|
1126 }
|
1
|
1127 }
|
9
|
1128
|
|
1129 /* clear any borders */
|
|
1130 image_border_clear(imd);
|
|
1131
|
|
1132 image_tile_sync(imd, imd->width, imd->height, FALSE);
|
|
1133 #if 0
|
|
1134 /* no longer needed? (expose event should be doing this for us) */
|
|
1135 image_redraw(imd, FALSE);
|
|
1136 #endif
|
|
1137
|
|
1138 if (imd->title_show_zoom) image_update_title(imd);
|
|
1139 image_update_util(imd);
|
1
|
1140 }
|
|
1141
|
|
1142 /*
|
9
|
1143 *-------------------------------------------------------------------
|
|
1144 * misc
|
|
1145 *-------------------------------------------------------------------
|
|
1146 */
|
|
1147
|
|
1148 static void image_update_title(ImageWindow *imd)
|
1
|
1149 {
|
9
|
1150 gchar *title = NULL;
|
|
1151 gchar *zoom = NULL;
|
|
1152 gchar *collection = NULL;
|
|
1153
|
|
1154 if (!imd->top_window) return;
|
|
1155
|
|
1156 if (imd->collection && collection_to_number(imd->collection) >= 0)
|
|
1157 {
|
|
1158 const gchar *name;
|
|
1159 name = imd->collection->name;
|
|
1160 if (!name) name = _("Untitled");
|
|
1161 collection = g_strdup_printf(" (Collection %s)", name);
|
|
1162 }
|
|
1163
|
|
1164 if (imd->title_show_zoom)
|
1
|
1165 {
|
9
|
1166 gchar *buf = image_zoom_get_as_text(imd);
|
|
1167 zoom = g_strconcat(" [", buf, "]", NULL);
|
|
1168 g_free(buf);
|
1
|
1169 }
|
|
1170
|
9
|
1171 title = g_strdup_printf("%s%s%s%s%s%s",
|
|
1172 imd->title ? imd->title : "",
|
|
1173 imd->image_name ? imd->image_name : "",
|
|
1174 zoom ? zoom : "",
|
|
1175 collection ? collection : "",
|
|
1176 imd->image_name ? " - " : "",
|
|
1177 imd->title_right ? imd->title_right : "");
|
|
1178
|
|
1179 gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
|
|
1180
|
|
1181 g_free(title);
|
|
1182 g_free(zoom);
|
|
1183 g_free(collection);
|
|
1184 }
|
|
1185
|
|
1186 static void image_update_util(ImageWindow *imd)
|
|
1187 {
|
|
1188 if (imd->func_update) imd->func_update(imd, imd->data_update);
|
1
|
1189 }
|
|
1190
|
9
|
1191 static void image_complete_util(ImageWindow *imd, gint preload)
|
|
1192 {
|
|
1193 if (imd->il && imd->pixbuf != image_loader_get_pixbuf(imd->il)) return;
|
|
1194
|
|
1195 if (debug) printf("image load completed \"%s\" (%s)\n",
|
|
1196 (preload) ? imd->read_ahead_path : imd->image_path,
|
|
1197 (preload) ? "preload" : "current");
|
|
1198
|
|
1199 if (!preload) imd->completed = TRUE;
|
|
1200 if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
|
|
1201 }
|
|
1202
|
|
1203 static void image_new_util(ImageWindow *imd)
|
|
1204 {
|
|
1205 if (imd->func_new) imd->func_new(imd, imd->data_new);
|
|
1206 }
|
|
1207
|
|
1208 static void image_scroll_real(ImageWindow *imd, gint x, gint y)
|
1
|
1209 {
|
9
|
1210 gint old_x, old_y;
|
|
1211 gint x_off, y_off;
|
|
1212 gint w, h;
|
|
1213
|
|
1214 if (!imd->pixbuf) return;
|
|
1215
|
|
1216 old_x = imd->x_scroll;
|
|
1217 old_y = imd->y_scroll;
|
|
1218
|
|
1219 imd->x_scroll += x;
|
|
1220 imd->y_scroll += y;
|
|
1221
|
|
1222 image_scroll_clamp(imd);
|
|
1223 if (imd->x_scroll == old_x && imd->y_scroll == old_y) return;
|
|
1224
|
|
1225 if (imd->overlay_list)
|
1
|
1226 {
|
9
|
1227 gint new_x, new_y;
|
|
1228
|
|
1229 new_x = imd->x_scroll;
|
|
1230 new_y = imd->y_scroll;
|
|
1231 imd->x_scroll = old_x;
|
|
1232 imd->y_scroll = old_y;
|
|
1233 image_overlay_queue_all(imd);
|
|
1234 imd->x_scroll = new_x;
|
|
1235 imd->y_scroll = new_y;
|
1
|
1236 }
|
|
1237
|
9
|
1238 x_off = imd->x_scroll - old_x;
|
|
1239 y_off = imd->y_scroll - old_y;
|
|
1240
|
|
1241 w = imd->vis_width - abs(x_off);
|
|
1242 h = imd->vis_height - abs(y_off);
|
|
1243
|
|
1244 if (w < 1 || h < 1)
|
1
|
1245 {
|
9
|
1246 /* scrolled completely to new material */
|
|
1247 image_queue(imd, 0, 0, imd->width, imd->height, TRUE, TILE_RENDER_ALL, FALSE);
|
|
1248 return;
|
1
|
1249 }
|
|
1250 else
|
|
1251 {
|
9
|
1252 gint x1, y1;
|
|
1253 gint x2, y2;
|
|
1254 GdkGC *gc;
|
|
1255
|
|
1256 if (x_off < 0)
|
|
1257 {
|
|
1258 x1 = abs(x_off);
|
|
1259 x2 = 0;
|
|
1260 }
|
|
1261 else
|
|
1262 {
|
|
1263 x1 = 0;
|
|
1264 x2 = abs(x_off);
|
|
1265 }
|
|
1266
|
|
1267 if (y_off < 0)
|
|
1268 {
|
|
1269 y1 = abs(y_off);
|
|
1270 y2 = 0;
|
|
1271 }
|
|
1272 else
|
|
1273 {
|
|
1274 y1 = 0;
|
|
1275 y2 = abs(y_off);
|
|
1276 }
|
|
1277
|
|
1278 gc = gdk_gc_new(imd->image->window);
|
|
1279 gdk_gc_set_exposures(gc, TRUE);
|
|
1280 gdk_draw_drawable(imd->image->window, gc,
|
|
1281 imd->image->window,
|
|
1282 x2 + imd->x_offset, y2 + imd->y_offset,
|
|
1283 x1 + imd->x_offset, y1 + imd->y_offset, w, h);
|
|
1284 g_object_unref(gc);
|
|
1285
|
|
1286 if (imd->overlay_list)
|
|
1287 {
|
|
1288 image_overlay_queue_all(imd);
|
|
1289 }
|
|
1290
|
|
1291 w = imd->vis_width - w;
|
|
1292 h = imd->vis_height - h;
|
|
1293
|
|
1294 if (w > 0)
|
|
1295 {
|
|
1296 image_queue(imd,
|
|
1297 x_off > 0 ? imd->x_scroll + (imd->vis_width - w) : imd->x_scroll, imd->y_scroll,
|
|
1298 w, imd->vis_height, TRUE, TILE_RENDER_ALL, FALSE);
|
|
1299 }
|
|
1300 if (h > 0)
|
|
1301 {
|
|
1302 /* FIXME, to optimize this, remove overlap */
|
|
1303 image_queue(imd,
|
|
1304 imd->x_scroll, y_off > 0 ? imd->y_scroll + (imd->vis_height - h) : imd->y_scroll,
|
|
1305 imd->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE);
|
|
1306 }
|
1
|
1307 }
|
|
1308 }
|
|
1309
|
9
|
1310 static void widget_set_cursor(GtkWidget *widget, gint icon)
|
1
|
1311 {
|
|
1312 GdkCursor *cursor;
|
|
1313
|
9
|
1314 if (!widget->window) return;
|
|
1315
|
1
|
1316 if (icon == -1)
|
|
1317 {
|
|
1318 cursor = NULL;
|
|
1319 }
|
|
1320 else
|
|
1321 {
|
|
1322 cursor = gdk_cursor_new (icon);
|
|
1323 }
|
|
1324
|
9
|
1325 gdk_window_set_cursor(widget->window, cursor);
|
|
1326
|
|
1327 if (cursor) gdk_cursor_unref(cursor);
|
|
1328 }
|
|
1329
|
|
1330 /*
|
|
1331 *-------------------------------------------------------------------
|
|
1332 * image pixbuf handling
|
|
1333 *-------------------------------------------------------------------
|
|
1334 */
|
|
1335
|
|
1336 static void image_zoom_sync(ImageWindow *imd, gdouble zoom,
|
|
1337 gint force, gint blank, gint new,
|
|
1338 gint center_point, gint px, gint py)
|
|
1339 {
|
|
1340 gdouble old_scale;
|
|
1341 gint old_cx, old_cy;
|
|
1342 gint clamped;
|
|
1343 gint sized;
|
|
1344
|
|
1345 old_scale = imd->scale;
|
|
1346 if (center_point)
|
|
1347 {
|
|
1348 px = CLAMP(px, 0, imd->width);
|
|
1349 py = CLAMP(py, 0, imd->height);
|
|
1350 old_cx = imd->x_scroll + (px - imd->x_offset);
|
|
1351 old_cy = imd->y_scroll + (py - imd->y_offset);
|
|
1352 }
|
|
1353 else
|
|
1354 {
|
|
1355 px = py = 0;
|
|
1356 old_cx = imd->x_scroll + imd->vis_width / 2;
|
|
1357 old_cy = imd->y_scroll + imd->vis_height / 2;
|
|
1358 }
|
|
1359
|
|
1360 if (!image_zoom_clamp(imd, zoom, force, new)) return;
|
|
1361
|
|
1362 clamped = image_size_clamp(imd);
|
|
1363 sized = image_size_top_window(imd, imd->width, imd->height);
|
|
1364
|
|
1365 if (force)
|
|
1366 {
|
|
1367 /* force means new image, so update scroll offset per options */
|
|
1368 switch (scroll_reset_method)
|
|
1369 {
|
|
1370 case SCROLL_RESET_NOCHANGE:
|
|
1371 /* maintain old scroll position, do nothing */
|
|
1372 break;
|
|
1373 case SCROLL_RESET_CENTER:
|
|
1374 /* center new image */
|
|
1375 imd->x_scroll = ((double)imd->image_width / 2.0 * imd->scale) - imd->vis_width / 2;
|
|
1376 imd->y_scroll = ((double)imd->image_height / 2.0 * imd->scale) - imd->vis_height / 2;
|
|
1377 break;
|
|
1378 case SCROLL_RESET_TOPLEFT:
|
|
1379 default:
|
|
1380 /* reset to upper left */
|
|
1381 imd->x_scroll = 0;
|
|
1382 imd->y_scroll = 0;
|
|
1383 break;
|
|
1384 }
|
|
1385 }
|
|
1386 else
|
|
1387 {
|
|
1388 /* user zoom does not force, so keep visible center point */
|
|
1389 if (center_point)
|
|
1390 {
|
|
1391 imd->x_scroll = old_cx / old_scale * imd->scale - (px - imd->x_offset);
|
|
1392 imd->y_scroll = old_cy / old_scale * imd->scale - (py - imd->y_offset);
|
|
1393 }
|
|
1394 else
|
|
1395 {
|
|
1396 imd->x_scroll = old_cx / old_scale * imd->scale - (imd->vis_width / 2);
|
|
1397 imd->y_scroll = old_cy / old_scale * imd->scale - (imd->vis_height / 2);
|
|
1398 }
|
|
1399 }
|
|
1400 image_scroll_clamp(imd);
|
|
1401
|
|
1402 image_tile_sync(imd, imd->width, imd->height, blank);
|
|
1403
|
|
1404 /* If the window was not sized, redraw the image - we know there will be no size/expose signal.
|
|
1405 * But even if a size is claimed, there is no guarantee that the window manager will allow it,
|
|
1406 * so redraw the window anyway :/
|
|
1407 */
|
|
1408 if (sized || clamped) image_border_clear(imd);
|
|
1409 image_redraw(imd, FALSE);
|
|
1410
|
|
1411 if (imd->title_show_zoom) image_update_title(imd);
|
|
1412 image_update_util(imd);
|
|
1413 }
|
|
1414
|
|
1415 static void image_pixbuf_sync(ImageWindow *imd, gdouble zoom, gint blank, gint new)
|
|
1416 {
|
|
1417 if (!imd->pixbuf)
|
|
1418 {
|
|
1419 /* no pixbuf so just clear the window */
|
|
1420 imd->image_width = 0;
|
|
1421 imd->image_height = 0;
|
|
1422 imd->scale = 1.0;
|
|
1423
|
|
1424 if (imd->image->window)
|
|
1425 {
|
|
1426 gdk_window_clear(imd->image->window);
|
|
1427 image_overlay_draw(imd, 0, 0, imd->window_width, imd->window_height);
|
|
1428 }
|
|
1429
|
|
1430 image_update_util(imd);
|
|
1431
|
|
1432 return;
|
|
1433 }
|
|
1434
|
|
1435 imd->image_width = gdk_pixbuf_get_width(imd->pixbuf);
|
|
1436 imd->image_height = gdk_pixbuf_get_height(imd->pixbuf);
|
|
1437
|
|
1438 #if 0
|
|
1439 /* reset scrolling */
|
|
1440 imd->x_scroll = 0;
|
|
1441 imd->y_scroll = 0;
|
|
1442 #endif
|
|
1443
|
|
1444 image_zoom_sync(imd, zoom, TRUE, blank, new, FALSE, 0, 0);
|
|
1445 }
|
|
1446
|
|
1447 static void image_set_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gint new)
|
|
1448 {
|
|
1449 if (pixbuf) g_object_ref(pixbuf);
|
|
1450 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1451 imd->pixbuf = pixbuf;
|
|
1452
|
|
1453 image_pixbuf_sync(imd, zoom, FALSE, new);
|
|
1454 }
|
|
1455
|
|
1456 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
|
|
1457 {
|
|
1458 GdkPixbuf *new = NULL;
|
|
1459 gint x, y;
|
|
1460 gint t;
|
|
1461
|
|
1462 imd->delay_alter_type = ALTER_NONE;
|
|
1463
|
|
1464 if (!imd->pixbuf) return;
|
|
1465
|
|
1466 x = imd->x_scroll + (imd->vis_width / 2);
|
|
1467 y = imd->y_scroll + (imd->vis_height / 2);
|
|
1468
|
|
1469 switch (type)
|
|
1470 {
|
|
1471 case ALTER_ROTATE_90:
|
|
1472 new = pixbuf_copy_rotate_90(imd->pixbuf, FALSE);
|
|
1473 t = x;
|
|
1474 x = imd->height - y;
|
|
1475 y = t;
|
|
1476 break;
|
|
1477 case ALTER_ROTATE_90_CC:
|
|
1478 new = pixbuf_copy_rotate_90(imd->pixbuf, TRUE);
|
|
1479 t = x;
|
|
1480 x = y;
|
|
1481 y = imd->width - t;
|
|
1482 break;
|
|
1483 case ALTER_ROTATE_180:
|
|
1484 new = pixbuf_copy_mirror(imd->pixbuf, TRUE, TRUE);
|
|
1485 x = imd->width - x;
|
|
1486 y = imd->height - y;
|
|
1487 break;
|
|
1488 case ALTER_MIRROR:
|
|
1489 new = pixbuf_copy_mirror(imd->pixbuf, TRUE, FALSE);
|
|
1490 x = imd->width - x;
|
|
1491 break;
|
|
1492 case ALTER_FLIP:
|
|
1493 new = pixbuf_copy_mirror(imd->pixbuf, FALSE, TRUE);
|
|
1494 y = imd->height - y;
|
|
1495 break;
|
|
1496 case ALTER_NONE:
|
|
1497 default:
|
|
1498 return;
|
|
1499 break;
|
|
1500 }
|
|
1501
|
|
1502 if (!new) return;
|
|
1503
|
|
1504 if (clamp)
|
|
1505 {
|
|
1506 image_set_pixbuf(imd, new, imd->zoom, TRUE);
|
|
1507 g_object_unref(new);
|
|
1508
|
|
1509 if (imd->zoom != 0.0)
|
|
1510 {
|
|
1511 image_scroll(imd, x - (imd->vis_width / 2), y - (imd->vis_height / 2));
|
|
1512 }
|
|
1513 }
|
|
1514 else
|
|
1515 {
|
|
1516 g_object_unref(imd->pixbuf);
|
|
1517 imd->pixbuf = new;
|
|
1518 }
|
1
|
1519 }
|
|
1520
|
9
|
1521 static void image_post_process(ImageWindow *imd, gint clamp)
|
|
1522 {
|
|
1523 if (exif_rotate_enable && imd->pixbuf)
|
|
1524 {
|
|
1525 ExifData *ed;
|
|
1526 gint orientation;
|
|
1527
|
|
1528 ed = exif_read(imd->image_path);
|
|
1529 if (ed && exif_get_integer(ed, "Orientation", &orientation))
|
|
1530 {
|
|
1531 /* see http://jpegclub.org/exif_orientation.html
|
|
1532 1 2 3 4 5 6 7 8
|
|
1533
|
|
1534 888888 888888 88 88 8888888888 88 88 8888888888
|
|
1535 88 88 88 88 88 88 88 88 88 88 88 88
|
|
1536 8888 8888 8888 8888 88 8888888888 8888888888 88
|
|
1537 88 88 88 88
|
|
1538 88 88 888888 888888
|
|
1539 */
|
|
1540
|
|
1541 switch (orientation)
|
|
1542 {
|
|
1543 case EXIF_ORIENTATION_TOP_LEFT:
|
|
1544 /* normal -- nothing to do */
|
|
1545 break;
|
|
1546 case EXIF_ORIENTATION_TOP_RIGHT:
|
|
1547 /* mirrored */
|
|
1548 imd->delay_alter_type = ALTER_MIRROR;
|
|
1549 break;
|
|
1550 case EXIF_ORIENTATION_BOTTOM_RIGHT:
|
|
1551 /* upside down */
|
|
1552 imd->delay_alter_type = ALTER_ROTATE_180;
|
|
1553 break;
|
|
1554 case EXIF_ORIENTATION_BOTTOM_LEFT:
|
|
1555 /* flipped */
|
|
1556 imd->delay_alter_type = ALTER_FLIP;
|
|
1557 break;
|
|
1558 case EXIF_ORIENTATION_LEFT_TOP:
|
|
1559 /* not implemented -- too wacky to fix in one step */
|
|
1560 break;
|
|
1561 case EXIF_ORIENTATION_RIGHT_TOP:
|
|
1562 /* rotated -90 (270) */
|
|
1563 imd->delay_alter_type = ALTER_ROTATE_90;
|
|
1564 break;
|
|
1565 case EXIF_ORIENTATION_RIGHT_BOTTOM:
|
|
1566 /* not implemented -- too wacky to fix in one step */
|
|
1567 break;
|
|
1568 case EXIF_ORIENTATION_LEFT_BOTTOM:
|
|
1569 /* rotated 90 */
|
|
1570 imd->delay_alter_type = ALTER_ROTATE_90_CC;
|
|
1571 break;
|
|
1572 default:
|
|
1573 /* The other values are out of range */
|
|
1574 break;
|
|
1575 }
|
|
1576 }
|
|
1577 exif_free(ed);
|
|
1578 }
|
|
1579
|
|
1580 if (imd->delay_alter_type != ALTER_NONE)
|
|
1581 {
|
|
1582 image_alter_real(imd, imd->delay_alter_type, clamp);
|
|
1583 }
|
|
1584 }
|
|
1585
|
|
1586 /*
|
|
1587 *-------------------------------------------------------------------
|
|
1588 * read ahead (prebuffer)
|
|
1589 *-------------------------------------------------------------------
|
|
1590 */
|
|
1591
|
|
1592 static void image_read_ahead_cancel(ImageWindow *imd)
|
|
1593 {
|
|
1594 if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
|
|
1595
|
|
1596 image_loader_free(imd->read_ahead_il);
|
|
1597 imd->read_ahead_il = NULL;
|
|
1598
|
|
1599 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
|
|
1600 imd->read_ahead_pixbuf = NULL;
|
|
1601
|
|
1602 g_free(imd->read_ahead_path);
|
|
1603 imd->read_ahead_path = NULL;
|
|
1604 }
|
|
1605
|
|
1606 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
|
|
1607 {
|
|
1608 ImageWindow *imd = data;
|
|
1609
|
|
1610 if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
|
|
1611
|
|
1612 imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
|
|
1613 if (imd->read_ahead_pixbuf)
|
|
1614 {
|
|
1615 g_object_ref(imd->read_ahead_pixbuf);
|
|
1616 }
|
|
1617 else
|
|
1618 {
|
|
1619 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
|
|
1620 }
|
|
1621 image_loader_free(imd->read_ahead_il);
|
|
1622 imd->read_ahead_il = NULL;
|
|
1623
|
|
1624 image_complete_util(imd, TRUE);
|
|
1625 }
|
|
1626
|
|
1627 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
|
|
1628 {
|
|
1629 /* we even treat errors as success, maybe at least some of the file was ok */
|
|
1630 image_read_ahead_done_cb(il, data);
|
|
1631 }
|
|
1632
|
|
1633 static void image_read_ahead_start(ImageWindow *imd)
|
|
1634 {
|
|
1635 /* already started ? */
|
|
1636 if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
|
|
1637
|
|
1638 /* still loading ?, do later */
|
|
1639 if (imd->il) return;
|
|
1640
|
|
1641 if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
|
|
1642
|
|
1643 imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
|
|
1644
|
|
1645 image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
|
|
1646 if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
|
|
1647 {
|
|
1648 image_read_ahead_cancel(imd);
|
|
1649 image_complete_util(imd, TRUE);
|
|
1650 }
|
|
1651 }
|
|
1652
|
|
1653 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
|
|
1654 {
|
|
1655 if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
|
|
1656
|
|
1657 image_read_ahead_cancel(imd);
|
|
1658
|
|
1659 imd->read_ahead_path = g_strdup(path);
|
|
1660
|
|
1661 if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
|
|
1662
|
|
1663 image_read_ahead_start(imd);
|
|
1664 }
|
|
1665
|
|
1666 /*
|
|
1667 *-------------------------------------------------------------------
|
|
1668 * post buffering
|
|
1669 *-------------------------------------------------------------------
|
|
1670 */
|
|
1671
|
|
1672 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
|
|
1673 {
|
|
1674 g_free(imd->prev_path);
|
|
1675 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
|
|
1676
|
|
1677 if (path && pixbuf)
|
|
1678 {
|
|
1679 imd->prev_path = g_strdup(path);
|
|
1680
|
|
1681 g_object_ref(pixbuf);
|
|
1682 imd->prev_pixbuf = pixbuf;
|
|
1683 }
|
|
1684 else
|
|
1685 {
|
|
1686 imd->prev_path = NULL;
|
|
1687 imd->prev_pixbuf = NULL;
|
|
1688 }
|
|
1689
|
|
1690 if (debug) printf("post buffer set: %s\n", path);
|
|
1691 }
|
|
1692
|
|
1693 static gint image_post_buffer_get(ImageWindow *imd)
|
|
1694 {
|
|
1695 gint success;
|
|
1696
|
|
1697 if (imd->prev_pixbuf &&
|
|
1698 imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
|
|
1699 {
|
|
1700 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1701 imd->pixbuf = imd->prev_pixbuf;
|
|
1702 success = TRUE;
|
|
1703 }
|
|
1704 else
|
|
1705 {
|
|
1706 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
|
|
1707 success = FALSE;
|
|
1708 }
|
|
1709
|
|
1710 imd->prev_pixbuf = NULL;
|
|
1711
|
|
1712 g_free(imd->prev_path);
|
|
1713 imd->prev_path = NULL;
|
|
1714
|
|
1715 return success;
|
|
1716 }
|
|
1717
|
|
1718 /*
|
|
1719 *-------------------------------------------------------------------
|
|
1720 * loading
|
|
1721 *-------------------------------------------------------------------
|
|
1722 */
|
|
1723
|
|
1724 static void image_load_pixbuf_ready(ImageWindow *imd)
|
|
1725 {
|
|
1726 if (imd->pixbuf || !imd->il) return;
|
|
1727
|
|
1728 imd->pixbuf = image_loader_get_pixbuf(imd->il);
|
|
1729
|
|
1730 if (imd->pixbuf) g_object_ref(imd->pixbuf);
|
|
1731
|
|
1732 image_pixbuf_sync(imd, imd->zoom, TRUE, TRUE);
|
|
1733 }
|
|
1734
|
|
1735 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
|
|
1736 {
|
|
1737 ImageWindow *imd = data;
|
|
1738
|
|
1739 if (imd->delay_flip &&
|
|
1740 imd->pixbuf != image_loader_get_pixbuf(il))
|
|
1741 {
|
|
1742 return;
|
|
1743 }
|
|
1744
|
|
1745 if (!imd->pixbuf) image_load_pixbuf_ready(imd);
|
|
1746
|
|
1747 if (imd->scale != 1.0)
|
|
1748 {
|
|
1749 x = (guint) floor((double)x * imd->scale);
|
|
1750 y = (guint) floor((double)y * imd->scale);
|
|
1751 w = (guint) ceil((double)w * imd->scale);
|
|
1752 h = (guint) ceil((double)h * imd->scale);
|
|
1753
|
|
1754 if (w == 0) w = 1;
|
|
1755 if (h == 0) h = 1;
|
|
1756
|
|
1757 if ((GdkInterpType)zoom_quality != GDK_INTERP_NEAREST)
|
|
1758 {
|
|
1759 /* some scaling types use surrounding pixels to smooth the image,
|
|
1760 * this will expand the new area to cover up for faint black
|
|
1761 * lines caused by previous renders with non-complete image
|
|
1762 */
|
|
1763 y -= 1;
|
|
1764 h += 2;
|
|
1765 }
|
|
1766
|
|
1767 }
|
|
1768
|
|
1769 image_queue(imd, (gint) x, (gint) y, (gint) w, (gint) h, FALSE, TILE_RENDER_AREA, TRUE);
|
|
1770 }
|
|
1771
|
|
1772 static void image_load_done_cb(ImageLoader *il, gpointer data)
|
1
|
1773 {
|
|
1774 ImageWindow *imd = data;
|
9
|
1775
|
|
1776 if (debug) printf ("image done\n");
|
|
1777
|
|
1778 if (imd->delay_flip &&
|
|
1779 imd->pixbuf != image_loader_get_pixbuf(imd->il))
|
|
1780 {
|
|
1781 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1782 imd->pixbuf = image_loader_get_pixbuf(imd->il);
|
|
1783 if (imd->pixbuf) g_object_ref(imd->pixbuf);
|
|
1784 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
|
|
1785 }
|
|
1786
|
|
1787 image_loader_free(imd->il);
|
|
1788 imd->il = NULL;
|
|
1789
|
|
1790 image_post_process(imd, TRUE);
|
|
1791
|
|
1792 image_read_ahead_start(imd);
|
|
1793 }
|
|
1794
|
|
1795 static void image_load_error_cb(ImageLoader *il, gpointer data)
|
|
1796 {
|
|
1797 if (debug) printf ("image error\n");
|
|
1798
|
|
1799 /* even on error handle it like it was done,
|
|
1800 * since we have a pixbuf with _something_ */
|
|
1801
|
|
1802 image_load_done_cb(il, data);
|
|
1803 }
|
|
1804
|
|
1805 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
|
|
1806 static void image_load_buffer_throttle(ImageLoader *il)
|
|
1807 {
|
|
1808 if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
|
|
1809
|
|
1810 /* Larger image files usually have larger chunks of data per pixel...
|
|
1811 * So increase the buffer read size so that the rendering chunks called
|
|
1812 * are also larger.
|
|
1813 */
|
|
1814
|
|
1815 image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
|
|
1816 }
|
|
1817 #endif
|
|
1818
|
|
1819 /* this read ahead is located here merely for the callbacks, above */
|
|
1820
|
|
1821 static gint image_read_ahead_check(ImageWindow *imd)
|
|
1822 {
|
|
1823 if (!imd->read_ahead_path) return FALSE;
|
|
1824 if (imd->il) return FALSE;
|
|
1825
|
|
1826 if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
|
|
1827 {
|
|
1828 image_read_ahead_cancel(imd);
|
|
1829 return FALSE;
|
|
1830 }
|
|
1831
|
|
1832 if (imd->read_ahead_il)
|
|
1833 {
|
|
1834 imd->il = imd->read_ahead_il;
|
|
1835 imd->read_ahead_il = NULL;
|
|
1836
|
|
1837 /* override the old signals */
|
|
1838 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
|
|
1839 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
|
|
1840 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
|
|
1841
|
|
1842 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
|
|
1843 image_load_buffer_throttle(imd->il);
|
|
1844 #endif
|
|
1845
|
|
1846 /* do this one directly (probably should add a set func) */
|
|
1847 imd->il->func_done = image_load_done_cb;
|
|
1848
|
|
1849 if (!imd->delay_flip)
|
|
1850 {
|
|
1851 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1852 imd->pixbuf = image_loader_get_pixbuf(imd->il);
|
|
1853 if (imd->pixbuf) g_object_ref(imd->pixbuf);
|
|
1854 }
|
|
1855
|
|
1856 image_read_ahead_cancel(imd);
|
|
1857 return TRUE;
|
|
1858 }
|
|
1859 else if (imd->read_ahead_pixbuf)
|
|
1860 {
|
|
1861 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1862 imd->pixbuf = imd->read_ahead_pixbuf;
|
|
1863 imd->read_ahead_pixbuf = NULL;
|
|
1864
|
|
1865 image_read_ahead_cancel(imd);
|
|
1866
|
|
1867 image_post_process(imd, FALSE);
|
|
1868 return TRUE;
|
|
1869 }
|
|
1870
|
|
1871 image_read_ahead_cancel(imd);
|
|
1872 return FALSE;
|
|
1873 }
|
|
1874
|
|
1875 static gint image_load_begin(ImageWindow *imd, const gchar *path)
|
|
1876 {
|
|
1877 if (debug) printf ("image begin \n");
|
|
1878
|
|
1879 if (imd->il) return FALSE;
|
|
1880
|
|
1881 imd->completed = FALSE;
|
|
1882
|
|
1883 if (image_post_buffer_get(imd))
|
|
1884 {
|
|
1885 if (debug) printf("from post buffer: %s\n", imd->image_path);
|
|
1886
|
|
1887 image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
|
|
1888 return TRUE;
|
|
1889 }
|
|
1890
|
|
1891 if (image_read_ahead_check(imd))
|
|
1892 {
|
|
1893 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
|
|
1894
|
|
1895 if (!imd->delay_flip || !imd->il) image_pixbuf_sync(imd, imd->zoom, FALSE, TRUE);
|
|
1896 return TRUE;
|
|
1897 }
|
|
1898
|
|
1899 if (!imd->delay_flip && imd->pixbuf)
|
|
1900 {
|
|
1901 g_object_unref(imd->pixbuf);
|
|
1902 imd->pixbuf = NULL;
|
|
1903 }
|
|
1904
|
|
1905 imd->il = image_loader_new(path);
|
|
1906
|
|
1907 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
|
|
1908 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
|
|
1909 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
|
|
1910
|
|
1911 if (!image_loader_start(imd->il, image_load_done_cb, imd))
|
|
1912 {
|
|
1913 if (debug) printf("image start error\n");
|
|
1914
|
|
1915 image_loader_free(imd->il);
|
|
1916 imd->il = NULL;
|
|
1917
|
|
1918 image_complete_util(imd, FALSE);
|
|
1919
|
|
1920 return FALSE;
|
|
1921 }
|
|
1922
|
|
1923 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
|
|
1924 image_load_buffer_throttle(imd->il);
|
|
1925 #endif
|
|
1926
|
|
1927 if (!imd->delay_flip && !imd->pixbuf) image_load_pixbuf_ready(imd);
|
|
1928
|
|
1929 return TRUE;
|
|
1930 }
|
|
1931
|
|
1932 static void image_reset(ImageWindow *imd)
|
|
1933 {
|
|
1934 /* stops anything currently being done */
|
|
1935
|
|
1936 if (debug) printf("image reset\n");
|
|
1937
|
|
1938 image_loader_free(imd->il);
|
|
1939 imd->il = NULL;
|
|
1940
|
|
1941 image_queue_clear(imd);
|
|
1942 imd->delay_alter_type = ALTER_NONE;
|
|
1943 }
|
|
1944
|
|
1945 /*
|
|
1946 *-------------------------------------------------------------------
|
|
1947 * image changer
|
|
1948 *-------------------------------------------------------------------
|
|
1949 */
|
|
1950
|
|
1951 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
|
|
1952 {
|
|
1953 gint sync = TRUE;
|
|
1954
|
|
1955 imd->zoom = zoom; /* store the zoom, needed by the loader */
|
|
1956
|
|
1957 image_reset(imd);
|
|
1958
|
|
1959 if (imd->image_path && isfile(imd->image_path))
|
|
1960 {
|
|
1961 if (image_load_begin(imd, imd->image_path))
|
|
1962 {
|
|
1963 imd->unknown = FALSE;
|
|
1964 sync = FALSE;
|
|
1965 }
|
|
1966 else
|
|
1967 {
|
|
1968 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1969 imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
|
|
1970 imd->unknown = TRUE;
|
|
1971 }
|
|
1972 imd->size = filesize(imd->image_path);
|
|
1973 imd->mtime = filetime(imd->image_path);
|
|
1974 }
|
|
1975 else
|
|
1976 {
|
|
1977 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
1978 imd->pixbuf = NULL;
|
|
1979
|
|
1980 if (imd->image_path)
|
|
1981 {
|
|
1982 imd->pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
|
|
1983 imd->mtime = filetime(imd->image_path);
|
|
1984 }
|
|
1985 else
|
|
1986 {
|
|
1987 imd->pixbuf = NULL;
|
|
1988 imd->mtime = 0;
|
|
1989 }
|
|
1990 imd->unknown = TRUE;
|
|
1991 imd->size = 0;
|
|
1992 }
|
|
1993
|
|
1994 if (sync)
|
|
1995 {
|
|
1996 image_pixbuf_sync(imd, zoom, FALSE, new);
|
|
1997 }
|
|
1998 else
|
|
1999 {
|
|
2000 image_update_util(imd);
|
|
2001 }
|
|
2002 }
|
|
2003
|
|
2004 static void image_change_real(ImageWindow *imd, const gchar *path,
|
|
2005 CollectionData *cd, CollectInfo *info, gdouble zoom)
|
|
2006 {
|
|
2007 GdkPixbuf *prev_pixbuf = NULL;
|
|
2008 gchar *prev_path = NULL;
|
|
2009 gint prev_clear = FALSE;
|
|
2010
|
|
2011 imd->collection = cd;
|
|
2012 imd->collection_info = info;
|
|
2013
|
|
2014 if (enable_read_ahead && imd->image_path && imd->pixbuf)
|
|
2015 {
|
|
2016 if (imd->il)
|
|
2017 {
|
|
2018 /* current image is not finished */
|
|
2019 prev_clear = TRUE;
|
|
2020 }
|
|
2021 else
|
|
2022 {
|
|
2023 prev_path = g_strdup(imd->image_path);
|
|
2024 prev_pixbuf = imd->pixbuf;
|
|
2025 g_object_ref(prev_pixbuf);
|
|
2026 }
|
|
2027 }
|
|
2028
|
|
2029 g_free(imd->image_path);
|
|
2030 imd->image_path = g_strdup(path);
|
|
2031 imd->image_name = filename_from_path(imd->image_path);
|
|
2032
|
|
2033 image_change_complete(imd, zoom, TRUE);
|
|
2034
|
|
2035 if (prev_pixbuf)
|
|
2036 {
|
|
2037 image_post_buffer_set(imd, prev_path, prev_pixbuf);
|
|
2038 g_free(prev_path);
|
|
2039 g_object_unref(prev_pixbuf);
|
|
2040 }
|
|
2041 else if (prev_clear)
|
|
2042 {
|
|
2043 image_post_buffer_set(imd, NULL, NULL);
|
|
2044 }
|
|
2045
|
|
2046 image_update_title(imd);
|
|
2047 image_new_util(imd);
|
|
2048 }
|
|
2049
|
|
2050 /*
|
|
2051 *-------------------------------------------------------------------
|
|
2052 * callbacks
|
|
2053 *-------------------------------------------------------------------
|
|
2054 */
|
|
2055
|
|
2056 static gint image_expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
|
|
2057 {
|
|
2058 gint x, y;
|
|
2059
|
|
2060 ImageWindow *imd = data;
|
|
2061
|
|
2062 image_border_draw(imd, event->area.x, event->area.y,
|
|
2063 event->area.width, event->area.height);
|
|
2064
|
|
2065 /* image */
|
|
2066 x = MAX(0, (gint)event->area.x - imd->x_offset + imd->x_scroll);
|
|
2067 y = MAX(0, (gint)event->area.y - imd->y_offset + imd->y_scroll);
|
|
2068
|
|
2069 image_queue(imd, x, y,
|
|
2070 MIN((gint)event->area.width, imd->width - x),
|
|
2071 MIN((gint)event->area.height, imd->height - y),
|
|
2072 FALSE, TILE_RENDER_ALL, FALSE);
|
|
2073
|
|
2074 return TRUE;
|
|
2075 }
|
|
2076
|
|
2077 static void image_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
|
|
2078 {
|
|
2079 ImageWindow *imd = data;
|
|
2080
|
|
2081 image_size_sync(imd, allocation->width, allocation->height);
|
|
2082 }
|
|
2083
|
|
2084 /*
|
|
2085 *-------------------------------------------------------------------
|
|
2086 * focus stuff
|
|
2087 *-------------------------------------------------------------------
|
|
2088 */
|
|
2089
|
|
2090 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
|
|
2091 {
|
|
2092 GtkWidget *widget;
|
|
2093
|
|
2094 widget = imd->widget;
|
|
2095 if (!widget->window) return;
|
|
2096
|
|
2097 if (has_focus)
|
|
2098 {
|
|
2099 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
|
|
2100 area, widget, "image_window",
|
|
2101 widget->allocation.x, widget->allocation.y,
|
|
2102 widget->allocation.width - 1, widget->allocation.height - 1);
|
|
2103 }
|
|
2104 else
|
|
2105 {
|
|
2106 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
|
|
2107 area, widget, "image_window",
|
|
2108 widget->allocation.x, widget->allocation.y,
|
|
2109 widget->allocation.width - 1, widget->allocation.height - 1);
|
|
2110 }
|
|
2111 }
|
|
2112
|
|
2113 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
|
|
2114 {
|
|
2115 ImageWindow *imd = data;
|
|
2116
|
|
2117 image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
|
|
2118 return TRUE;
|
|
2119 }
|
|
2120
|
|
2121 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
|
|
2122 {
|
|
2123 ImageWindow *imd = data;
|
|
2124
|
|
2125 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
|
|
2126 image_focus_paint(imd, TRUE, NULL);
|
|
2127
|
|
2128 return TRUE;
|
|
2129 }
|
|
2130
|
|
2131 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
|
|
2132 {
|
|
2133 ImageWindow *imd = data;
|
|
2134
|
|
2135 GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
|
|
2136 image_focus_paint(imd, FALSE, NULL);
|
|
2137
|
|
2138 return TRUE;
|
|
2139 }
|
|
2140
|
|
2141
|
|
2142 /*
|
|
2143 *-------------------------------------------------------------------
|
|
2144 * overlays
|
|
2145 *-------------------------------------------------------------------
|
|
2146 */
|
|
2147
|
|
2148 static void image_overlay_draw(ImageWindow *imd, gint x, gint y, gint w, gint h)
|
|
2149 {
|
|
2150 GList *work;
|
|
2151
|
|
2152 work = imd->overlay_list;
|
|
2153 while (work)
|
|
2154 {
|
|
2155 OverlayData *od;
|
|
2156 gint px, py, pw, ph;
|
|
2157 gint rx, ry, rw, rh;
|
|
2158
|
|
2159 od = work->data;
|
|
2160 work = work->next;
|
|
2161
|
|
2162 if (!od->visible) continue;
|
|
2163
|
|
2164 pw = gdk_pixbuf_get_width(od->pixbuf);
|
|
2165 ph = gdk_pixbuf_get_height(od->pixbuf);
|
|
2166 px = od->x;
|
|
2167 py = od->y;
|
|
2168
|
|
2169 if (od->relative)
|
|
2170 {
|
|
2171 if (px < 0) px = imd->window_width - pw + px;
|
|
2172 if (py < 0) py = imd->window_height - ph + py;
|
|
2173 }
|
|
2174
|
|
2175 if (util_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh))
|
|
2176 {
|
|
2177 gdk_draw_pixbuf(imd->image->window,
|
|
2178 imd->image->style->fg_gc[GTK_WIDGET_STATE(imd->image)],
|
|
2179 od->pixbuf,
|
|
2180 rx - px, ry - py,
|
|
2181 rx, ry, rw, rh,
|
|
2182 (GdkRgbDither)dither_quality, rx, ry);
|
|
2183 }
|
|
2184 }
|
|
2185 }
|
|
2186
|
|
2187 static void image_overlay_queue_draw(ImageWindow *imd, OverlayData *od, gint hidden)
|
|
2188 {
|
|
2189 gint x, y, w, h;
|
|
2190 gint old_vis;
|
|
2191
|
|
2192 w = gdk_pixbuf_get_width(od->pixbuf);
|
|
2193 h = gdk_pixbuf_get_height(od->pixbuf);
|
|
2194 x = od->x;
|
|
2195 y = od->y;
|
|
2196
|
|
2197 if (od->relative)
|
|
2198 {
|
|
2199 if (x < 0) x = imd->window_width - w + x;
|
|
2200 if (y < 0) y = imd->window_height - h + y;
|
|
2201 }
|
|
2202
|
|
2203 image_queue(imd, imd->x_scroll - imd->x_offset + x,
|
|
2204 imd->y_scroll - imd->y_offset + y,
|
|
2205 w, h,
|
|
2206 FALSE, TILE_RENDER_ALL, FALSE);
|
|
2207
|
|
2208 old_vis = od->visible;
|
|
2209 if (hidden) od->visible = FALSE;
|
|
2210 image_border_draw(imd, x, y, w, h);
|
|
2211 od->visible = old_vis;
|
|
2212 }
|
|
2213
|
|
2214 static void image_overlay_queue_all(ImageWindow *imd)
|
|
2215 {
|
|
2216 GList *work;
|
|
2217
|
|
2218 work = imd->overlay_list;
|
|
2219 while (work)
|
|
2220 {
|
|
2221 OverlayData *od = work->data;
|
|
2222 work = work->next;
|
|
2223
|
|
2224 image_overlay_queue_draw(imd, od, FALSE);
|
|
2225 }
|
|
2226 }
|
|
2227
|
|
2228 static OverlayData *image_overlay_find(ImageWindow *imd, gint id)
|
|
2229 {
|
|
2230 GList *work;
|
|
2231
|
|
2232 work = imd->overlay_list;
|
|
2233 while (work)
|
|
2234 {
|
|
2235 OverlayData *od = work->data;
|
|
2236 work = work->next;
|
|
2237
|
|
2238 if (od->id == id) return od;
|
|
2239 }
|
|
2240
|
|
2241 return NULL;
|
|
2242 }
|
|
2243
|
|
2244 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
|
|
2245 gint relative, gint always)
|
|
2246 {
|
|
2247 OverlayData *od;
|
|
2248 gint id;
|
|
2249
|
|
2250 if (!imd || !pixbuf) return -1;
|
|
2251
|
|
2252 id = 1;
|
|
2253 while (image_overlay_find(imd, id)) id++;
|
|
2254
|
|
2255 od = g_new0(OverlayData, 1);
|
|
2256 od->id = id;
|
|
2257 od->pixbuf = pixbuf;
|
|
2258 g_object_ref(G_OBJECT(od->pixbuf));
|
|
2259 od->x = x;
|
|
2260 od->y = y;
|
|
2261 od->relative = relative;
|
|
2262 od->visible = TRUE;
|
|
2263 od->always = always;
|
|
2264
|
|
2265 imd->overlay_list = g_list_append(imd->overlay_list, od);
|
|
2266
|
|
2267 image_overlay_queue_draw(imd, od, FALSE);
|
|
2268
|
|
2269 return od->id;
|
|
2270 }
|
|
2271
|
|
2272 static void image_overlay_free(ImageWindow *imd, OverlayData *od)
|
|
2273 {
|
|
2274 imd->overlay_list = g_list_remove(imd->overlay_list, od);
|
|
2275
|
|
2276 if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf));
|
|
2277 g_free(od);
|
|
2278 }
|
|
2279
|
|
2280 static void image_overlay_list_clear(ImageWindow *imd)
|
|
2281 {
|
|
2282 while (imd->overlay_list)
|
|
2283 {
|
|
2284 OverlayData *od;
|
|
2285
|
|
2286 od = imd->overlay_list->data;
|
|
2287 image_overlay_free(imd, od);
|
|
2288 }
|
|
2289 }
|
|
2290
|
|
2291 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
|
|
2292 {
|
|
2293 OverlayData *od;
|
|
2294
|
|
2295 if (!imd) return;
|
|
2296
|
|
2297 od = image_overlay_find(imd, id);
|
|
2298 if (!od) return;
|
|
2299
|
|
2300 if (pixbuf)
|
|
2301 {
|
|
2302 image_overlay_queue_draw(imd, od, TRUE);
|
|
2303
|
|
2304 g_object_ref(G_OBJECT(pixbuf));
|
|
2305 g_object_unref(G_OBJECT(od->pixbuf));
|
|
2306 od->pixbuf = pixbuf;
|
|
2307
|
|
2308 od->x = x;
|
|
2309 od->y = y;
|
|
2310
|
|
2311 image_overlay_queue_draw(imd, od, FALSE);
|
|
2312 }
|
|
2313 else
|
|
2314 {
|
|
2315 image_overlay_queue_draw(imd, od, TRUE);
|
|
2316 image_overlay_free(imd, od);
|
|
2317 }
|
|
2318 }
|
|
2319
|
|
2320 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
|
|
2321 {
|
|
2322 OverlayData *od;
|
|
2323
|
|
2324 if (!imd) return FALSE;
|
|
2325
|
|
2326 od = image_overlay_find(imd, id);
|
|
2327 if (!od) return FALSE;
|
|
2328
|
|
2329 if (pixbuf) *pixbuf = od->pixbuf;
|
|
2330 if (x) *x = od->x;
|
|
2331 if (y) *y = od->y;
|
|
2332
|
|
2333 return TRUE;
|
|
2334 }
|
|
2335
|
|
2336 void image_overlay_remove(ImageWindow *imd, gint id)
|
|
2337 {
|
|
2338 image_overlay_set(imd, id, NULL, 0, 0);
|
|
2339 }
|
|
2340
|
|
2341 /*
|
|
2342 *-------------------------------------------------------------------
|
|
2343 * scroller
|
|
2344 *-------------------------------------------------------------------
|
|
2345 */
|
|
2346
|
|
2347 #define SCROLLER_UPDATES_PER_SEC 30
|
|
2348 #define SCROLLER_DEAD_ZONE 6
|
|
2349
|
|
2350
|
|
2351 static gboolean image_scroller_update_cb(gpointer data)
|
|
2352 {
|
|
2353 ImageWindow *imd = data;
|
|
2354 gint x, y;
|
|
2355 gint xinc, yinc;
|
|
2356
|
|
2357 /* this was a simple scroll by difference between scroller and mouse position,
|
|
2358 * but all this math results in a smoother result and accounts for a dead zone.
|
|
2359 */
|
|
2360
|
|
2361 if (abs(imd->scroller_xpos - imd->scroller_x) < SCROLLER_DEAD_ZONE)
|
|
2362 {
|
|
2363 x = 0;
|
|
2364 }
|
|
2365 else
|
|
2366 {
|
|
2367 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
|
|
2368 x = (imd->scroller_xpos - imd->scroller_x) / 2 * SCROLLER_UPDATES_PER_SEC;
|
|
2369 x += (x > 0) ? -shift : shift;
|
|
2370 }
|
|
2371
|
|
2372 if (abs(imd->scroller_ypos - imd->scroller_y) < SCROLLER_DEAD_ZONE)
|
|
2373 {
|
|
2374 y = 0;
|
|
2375 }
|
|
2376 else
|
|
2377 {
|
|
2378 gint shift = SCROLLER_DEAD_ZONE / 2 * SCROLLER_UPDATES_PER_SEC;
|
|
2379 y = (imd->scroller_ypos - imd->scroller_y) / 2 * SCROLLER_UPDATES_PER_SEC;
|
|
2380 y += (y > 0) ? -shift : shift;
|
|
2381 }
|
|
2382
|
|
2383 if (abs(x) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
|
|
2384 {
|
|
2385 xinc = x;
|
|
2386 }
|
|
2387 else
|
|
2388 {
|
|
2389 xinc = imd->scroller_xinc;
|
|
2390
|
|
2391 if (x >= 0)
|
|
2392 {
|
|
2393 if (xinc < 0) xinc = 0;
|
|
2394 if (x < xinc) xinc = x;
|
|
2395 if (x > xinc) xinc = MIN(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
|
|
2396 }
|
|
2397 else
|
|
2398 {
|
|
2399 if (xinc > 0) xinc = 0;
|
|
2400 if (x > xinc) xinc = x;
|
|
2401 if (x < xinc) xinc = MAX(xinc + x / SCROLLER_UPDATES_PER_SEC, x);
|
|
2402 }
|
|
2403 }
|
|
2404
|
|
2405 if (abs(y) < SCROLLER_DEAD_ZONE * SCROLLER_UPDATES_PER_SEC)
|
|
2406 {
|
|
2407 yinc = y;
|
|
2408 }
|
|
2409 else
|
|
2410 {
|
|
2411 yinc = imd->scroller_yinc;
|
|
2412
|
|
2413 if (y >= 0)
|
|
2414 {
|
|
2415 if (yinc < 0) yinc = 0;
|
|
2416 if (y < yinc) yinc = y;
|
|
2417 if (y > yinc) yinc = MIN(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
|
|
2418 }
|
|
2419 else
|
|
2420 {
|
|
2421 if (yinc > 0) yinc = 0;
|
|
2422 if (y > yinc) yinc = y;
|
|
2423 if (y < yinc) yinc = MAX(yinc + y / SCROLLER_UPDATES_PER_SEC, y);
|
|
2424 }
|
|
2425 }
|
|
2426
|
|
2427 imd->scroller_xinc = xinc;
|
|
2428 imd->scroller_yinc = yinc;
|
|
2429
|
|
2430 xinc = xinc / SCROLLER_UPDATES_PER_SEC;
|
|
2431 yinc = yinc / SCROLLER_UPDATES_PER_SEC;
|
|
2432
|
|
2433 image_scroll(imd, xinc, yinc);
|
|
2434
|
|
2435 return TRUE;
|
|
2436 }
|
|
2437
|
|
2438 static void image_scroller_timer_set(ImageWindow *imd, gint start)
|
|
2439 {
|
|
2440 if (imd->scroller_id != -1)
|
|
2441 {
|
|
2442 g_source_remove(imd->scroller_id);
|
|
2443 imd->scroller_id = -1;
|
|
2444 }
|
|
2445
|
|
2446 if (start)
|
|
2447 {
|
|
2448 imd->scroller_id = g_timeout_add(1000 / SCROLLER_UPDATES_PER_SEC,
|
|
2449 image_scroller_update_cb, imd);
|
|
2450 }
|
|
2451 }
|
|
2452
|
|
2453 static void image_scroller_start(ImageWindow *imd, gint x, gint y)
|
|
2454 {
|
|
2455 if (imd->scroller_overlay == -1)
|
|
2456 {
|
|
2457 GdkPixbuf *pixbuf;
|
|
2458 gint w, h;
|
|
2459
|
|
2460 pixbuf = pixbuf_inline(PIXBUF_INLINE_SCROLLER);
|
|
2461 w = gdk_pixbuf_get_width(pixbuf);
|
|
2462 h = gdk_pixbuf_get_height(pixbuf);
|
|
2463
|
|
2464 imd->scroller_overlay = image_overlay_add(imd, pixbuf, x - w / 2, y - h / 2, FALSE, TRUE);
|
|
2465 }
|
|
2466
|
|
2467 imd->scroller_x = x;
|
|
2468 imd->scroller_y = y;
|
|
2469 imd->scroller_xpos = x;
|
|
2470 imd->scroller_ypos = y;
|
|
2471
|
|
2472 image_scroller_timer_set(imd, TRUE);
|
|
2473 }
|
|
2474
|
|
2475 static void image_scroller_stop(ImageWindow *imd)
|
|
2476 {
|
|
2477 if (imd->scroller_id == -1) return;
|
|
2478
|
|
2479 image_overlay_remove(imd, imd->scroller_overlay);
|
|
2480 imd->scroller_overlay = -1;
|
|
2481
|
|
2482 image_scroller_timer_set(imd, FALSE);
|
|
2483 }
|
|
2484
|
|
2485 /*
|
|
2486 *-------------------------------------------------------------------
|
|
2487 * mouse stuff
|
|
2488 *-------------------------------------------------------------------
|
|
2489 */
|
|
2490
|
|
2491 static gint image_mouse_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
|
|
2492 {
|
|
2493 ImageWindow *imd = data;
|
|
2494
|
|
2495 if (imd->scroller_id != -1)
|
|
2496 {
|
|
2497 imd->scroller_xpos = bevent->x;
|
|
2498 imd->scroller_ypos = bevent->y;
|
|
2499 }
|
|
2500
|
|
2501 if (!imd->in_drag || !gdk_pointer_is_grabbed()) return FALSE;
|
|
2502
|
|
2503 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD)
|
1
|
2504 {
|
|
2505 imd->drag_moved++;
|
|
2506 }
|
|
2507 else
|
|
2508 {
|
9
|
2509 widget_set_cursor (imd->image, GDK_FLEUR);
|
1
|
2510 }
|
|
2511
|
9
|
2512 /* do the scroll */
|
|
2513 image_scroll_real(imd, imd->drag_last_x - bevent->x, imd->drag_last_y - bevent->y);
|
1
|
2514
|
|
2515 imd->drag_last_x = bevent->x;
|
|
2516 imd->drag_last_y = bevent->y;
|
9
|
2517
|
|
2518 return FALSE;
|
1
|
2519 }
|
|
2520
|
9
|
2521 static gint image_mouse_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
|
1
|
2522 {
|
|
2523 ImageWindow *imd = data;
|
9
|
2524
|
|
2525 if (imd->scroller_id != -1) return TRUE;
|
|
2526
|
1
|
2527 switch (bevent->button)
|
|
2528 {
|
|
2529 case 1:
|
|
2530 imd->in_drag = TRUE;
|
|
2531 imd->drag_last_x = bevent->x;
|
|
2532 imd->drag_last_y = bevent->y;
|
|
2533 imd->drag_moved = 0;
|
9
|
2534 gdk_pointer_grab(imd->image->window, FALSE,
|
|
2535 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
|
1
|
2536 NULL, NULL, bevent->time);
|
9
|
2537 gtk_grab_add(imd->image);
|
1
|
2538 break;
|
|
2539 case 2:
|
|
2540 imd->drag_moved = 0;
|
|
2541 break;
|
|
2542 case 3:
|
9
|
2543 image_button_do(imd, bevent);
|
4
|
2544 break;
|
1
|
2545 default:
|
|
2546 break;
|
|
2547 }
|
9
|
2548
|
|
2549 gtk_widget_grab_focus(imd->widget);
|
|
2550
|
|
2551 return FALSE;
|
1
|
2552 }
|
|
2553
|
9
|
2554 static gint image_mouse_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
|
1
|
2555 {
|
|
2556 ImageWindow *imd = data;
|
9
|
2557
|
|
2558 if (imd->scroller_id != -1)
|
1
|
2559 {
|
9
|
2560 image_scroller_stop(imd);
|
|
2561 return TRUE;
|
1
|
2562 }
|
|
2563
|
9
|
2564 if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(imd->image))
|
1
|
2565 {
|
9
|
2566 gtk_grab_remove(imd->image);
|
|
2567 gdk_pointer_ungrab(bevent->time);
|
|
2568 widget_set_cursor(imd->image, -1);
|
1
|
2569 }
|
|
2570
|
9
|
2571 if (bevent->button == 1 && (bevent->state & GDK_SHIFT_MASK))
|
1
|
2572 {
|
9
|
2573 image_scroller_start(imd, bevent->x, bevent->y);
|
|
2574 }
|
|
2575 else if (bevent->button == 1 || bevent->button == 2)
|
|
2576 {
|
|
2577 if (imd->drag_moved < IMAGE_DRAG_SCROLL_THRESHHOLD) image_button_do(imd, bevent);
|
1
|
2578 }
|
|
2579
|
|
2580 imd->in_drag = FALSE;
|
9
|
2581
|
|
2582 return FALSE;
|
1
|
2583 }
|
|
2584
|
9
|
2585 static gint image_mouse_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
|
1
|
2586 {
|
|
2587 ImageWindow *imd = data;
|
9
|
2588
|
|
2589 if (imd->scroller_id != -1)
|
|
2590 {
|
|
2591 imd->scroller_xpos = imd->scroller_x;
|
|
2592 imd->scroller_ypos = imd->scroller_y;
|
|
2593 imd->scroller_xinc = 0;
|
|
2594 imd->scroller_yinc = 0;
|
|
2595 }
|
|
2596
|
|
2597 return FALSE;
|
|
2598 }
|
|
2599
|
|
2600 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
|
|
2601 {
|
|
2602 ImageWindow *imd = data;
|
|
2603
|
|
2604 if (imd->func_scroll &&
|
|
2605 event && event->type == GDK_SCROLL)
|
|
2606 {
|
|
2607 imd->func_scroll(imd, event->direction, event->time,
|
|
2608 event->x, event->y, event->state, imd->data_scroll);
|
|
2609 return TRUE;
|
|
2610 }
|
|
2611
|
|
2612 return FALSE;
|
|
2613 }
|
|
2614
|
|
2615 static void image_mouse_drag_cb(GtkWidget *widget, GdkDragContext *context, gpointer data)
|
|
2616 {
|
|
2617 ImageWindow *imd = data;
|
|
2618
|
|
2619 imd->drag_moved = IMAGE_DRAG_SCROLL_THRESHHOLD;
|
1
|
2620 }
|
|
2621
|
|
2622 /*
|
9
|
2623 *-------------------------------------------------------------------
|
|
2624 * drag and drop
|
|
2625 *-------------------------------------------------------------------
|
|
2626 */
|
|
2627
|
|
2628 /*
|
|
2629 *-------------------------------------------------------------------
|
|
2630 * public interface
|
|
2631 *-------------------------------------------------------------------
|
|
2632 */
|
|
2633
|
|
2634 void image_attach_window(ImageWindow *imd, GtkWidget *window,
|
|
2635 const gchar *title, const gchar *title_right, gint show_zoom)
|
1
|
2636 {
|
|
2637 imd->top_window = window;
|
|
2638 g_free(imd->title);
|
9
|
2639 imd->title = g_strdup(title);
|
|
2640 g_free(imd->title_right);
|
|
2641 imd->title_right = g_strdup(title_right);
|
|
2642 imd->title_show_zoom = show_zoom;
|
|
2643
|
|
2644 image_update_title(imd);
|
|
2645 }
|
|
2646
|
|
2647 void image_set_update_func(ImageWindow *imd,
|
|
2648 void (*func)(ImageWindow *imd, gpointer data),
|
|
2649 gpointer data)
|
|
2650 {
|
|
2651 imd->func_update = func;
|
|
2652 imd->data_update = data;
|
1
|
2653 }
|
|
2654
|
9
|
2655 void image_set_complete_func(ImageWindow *imd,
|
|
2656 void (*func)(ImageWindow *, gint preload, gpointer),
|
|
2657 gpointer data)
|
1
|
2658 {
|
9
|
2659 imd->func_complete = func;
|
|
2660 imd->data_complete = data;
|
1
|
2661 }
|
|
2662
|
9
|
2663 void image_set_new_func(ImageWindow *imd,
|
|
2664 void (*func)(ImageWindow *, gpointer),
|
|
2665 gpointer data)
|
1
|
2666 {
|
9
|
2667 imd->func_new = func;
|
|
2668 imd->data_new = data;
|
|
2669 }
|
|
2670
|
|
2671
|
|
2672 static void image_button_do(ImageWindow *imd, GdkEventButton *bevent)
|
|
2673 {
|
|
2674 if (imd->func_button &&
|
|
2675 bevent &&
|
|
2676 (bevent->type == GDK_BUTTON_PRESS || bevent->type == GDK_BUTTON_RELEASE))
|
1
|
2677 {
|
9
|
2678 imd->func_button(imd, bevent->button, bevent->time,
|
|
2679 bevent->x, bevent->y, bevent->state, imd->data_button);
|
1
|
2680 }
|
|
2681 }
|
|
2682
|
9
|
2683 void image_set_button_func(ImageWindow *imd,
|
|
2684 void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
|
|
2685 gpointer data)
|
|
2686 {
|
|
2687 imd->func_button = func;
|
|
2688 imd->data_button = data;
|
|
2689 }
|
|
2690
|
|
2691 void image_set_scroll_func(ImageWindow *imd,
|
|
2692 void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
|
|
2693 gpointer data)
|
1
|
2694 {
|
9
|
2695 imd->func_scroll = func;
|
|
2696 imd->data_scroll = data;
|
1
|
2697 }
|
|
2698
|
9
|
2699 /* path, name */
|
|
2700
|
|
2701 const gchar *image_get_path(ImageWindow *imd)
|
|
2702 {
|
|
2703 return imd->image_path;
|
|
2704 }
|
|
2705
|
|
2706 const gchar *image_get_name(ImageWindow *imd)
|
|
2707 {
|
|
2708 return imd->image_name;
|
|
2709 }
|
|
2710
|
|
2711 /* merely changes path string, does not change the image! */
|
|
2712 void image_set_path(ImageWindow *imd, const gchar *newpath)
|
1
|
2713 {
|
|
2714 g_free(imd->image_path);
|
9
|
2715 imd->image_path = g_strdup(newpath);
|
|
2716 imd->image_name = filename_from_path(imd->image_path);
|
|
2717
|
|
2718 image_update_title(imd);
|
|
2719 image_new_util(imd);
|
|
2720 }
|
|
2721
|
|
2722 /* load a new image */
|
|
2723
|
|
2724 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
|
|
2725 {
|
|
2726 if (imd->image_path == path ||
|
|
2727 (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
|
|
2728
|
|
2729 image_change_real(imd, path, NULL, NULL, zoom);
|
|
2730 }
|
|
2731
|
|
2732 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
|
|
2733 {
|
|
2734 image_set_pixbuf(imd, pixbuf, zoom, TRUE);
|
|
2735 image_new_util(imd);
|
|
2736 }
|
|
2737
|
|
2738 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
|
|
2739 {
|
|
2740 if (!cd || !info || !g_list_find(cd->list, info)) return;
|
|
2741
|
|
2742 image_change_real(imd, info->path, cd, info, zoom);
|
|
2743 }
|
|
2744
|
|
2745 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
|
|
2746 {
|
|
2747 if (collection_to_number(imd->collection) >= 0)
|
|
2748 {
|
|
2749 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
|
|
2750 {
|
|
2751 if (info) *info = imd->collection_info;
|
|
2752 }
|
|
2753 else
|
|
2754 {
|
|
2755 if (info) *info = NULL;
|
|
2756 }
|
|
2757 return imd->collection;
|
|
2758 }
|
|
2759
|
|
2760 if (info) *info = NULL;
|
|
2761 return NULL;
|
|
2762 }
|
|
2763
|
|
2764 static void image_loader_sync_data(ImageLoader *il, gpointer data)
|
|
2765 {
|
|
2766 /* change data for the callbacks directly */
|
|
2767
|
|
2768 il->data_area_ready = data;
|
|
2769 il->data_error = data;
|
|
2770 il->data_done = data;
|
|
2771 il->data_percent = data;
|
1
|
2772 }
|
|
2773
|
9
|
2774 /* this is more like a move function
|
|
2775 * it moves most data from source to imd, source does keep a ref on the pixbuf
|
|
2776 */
|
|
2777 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
|
|
2778 {
|
|
2779 if (imd == source) return;
|
|
2780
|
|
2781 imd->unknown = source->unknown;
|
|
2782
|
|
2783 image_set_pixbuf(imd, source->pixbuf, image_zoom_get(source), TRUE);
|
|
2784
|
|
2785 imd->collection = source->collection;
|
|
2786 imd->collection_info = source->collection_info;
|
|
2787 imd->size = source->size;
|
|
2788 imd->mtime = source->mtime;
|
|
2789
|
|
2790 image_set_path(imd, image_get_path(source));
|
|
2791
|
|
2792 image_loader_free(imd->il);
|
|
2793 imd->il = NULL;
|
|
2794
|
|
2795 if (imd->pixbuf && source->il)
|
|
2796 {
|
|
2797 imd->il = source->il;
|
|
2798 source->il = NULL;
|
|
2799
|
|
2800 image_loader_sync_data(imd->il, imd);
|
|
2801
|
|
2802 imd->delay_alter_type = source->delay_alter_type;
|
|
2803 source->delay_alter_type = ALTER_NONE;
|
|
2804 }
|
|
2805
|
|
2806 image_loader_free(imd->read_ahead_il);
|
|
2807 imd->read_ahead_il = source->read_ahead_il;
|
|
2808 source->read_ahead_il = NULL;
|
|
2809 if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
|
|
2810
|
|
2811 if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
|
|
2812 imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
|
|
2813 source->read_ahead_pixbuf = NULL;
|
|
2814
|
|
2815 g_free(imd->read_ahead_path);
|
|
2816 imd->read_ahead_path = source->read_ahead_path;
|
|
2817 source->read_ahead_path = NULL;
|
|
2818
|
|
2819 if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
|
|
2820 imd->prev_pixbuf = source->prev_pixbuf;
|
|
2821 source->prev_pixbuf = NULL;
|
|
2822
|
|
2823 g_free(imd->prev_path);
|
|
2824 imd->prev_path = source->prev_path;
|
|
2825 source->prev_path = NULL;
|
|
2826
|
|
2827 imd->completed = source->completed;
|
|
2828
|
|
2829 imd->x_scroll = source->x_scroll;
|
|
2830 imd->y_scroll = source->y_scroll;
|
|
2831
|
|
2832 image_scroll_clamp(imd);
|
|
2833 }
|
|
2834
|
|
2835 /* manipulation */
|
|
2836
|
|
2837 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
|
|
2838 {
|
|
2839 gint sx, sy, sw, sh;
|
|
2840
|
|
2841 sx = (gint)floor((double)x * imd->scale);
|
|
2842 sy = (gint)floor((double)y * imd->scale);
|
|
2843 sw = (gint)ceil((double)width * imd->scale);
|
|
2844 sh = (gint)ceil((double)height * imd->scale);
|
|
2845
|
|
2846 image_queue(imd, sx, sy, sw, sh, FALSE, TILE_RENDER_AREA, TRUE);
|
|
2847 }
|
|
2848
|
|
2849 void image_reload(ImageWindow *imd)
|
|
2850 {
|
|
2851 image_change_complete(imd, imd->zoom, FALSE);
|
|
2852 }
|
|
2853
|
|
2854 void image_scroll(ImageWindow *imd, gint x, gint y)
|
|
2855 {
|
|
2856 image_scroll_real(imd, x, y);
|
|
2857 }
|
|
2858
|
|
2859 void image_alter(ImageWindow *imd, AlterType type)
|
1
|
2860 {
|
9
|
2861 if (imd->il)
|
|
2862 {
|
|
2863 /* still loading, wait till done */
|
|
2864 imd->delay_alter_type = type;
|
|
2865 return;
|
|
2866 }
|
|
2867
|
|
2868 image_alter_real(imd, type, TRUE);
|
|
2869 }
|
|
2870
|
|
2871 /* zoom */
|
|
2872
|
|
2873 static void image_zoom_adjust_real(ImageWindow *imd, gdouble increment,
|
|
2874 gint center_point, gint x, gint y)
|
|
2875 {
|
|
2876 gdouble zoom = imd->zoom;
|
|
2877
|
|
2878 if (increment == 0.0) return; /* avoid possible div by zero, a no-op anyway... */
|
|
2879
|
|
2880 if (zoom == 0.0)
|
|
2881 {
|
|
2882 if (imd->scale < 1.0)
|
|
2883 {
|
|
2884 zoom = 0.0 - 1.0 / imd->scale;
|
|
2885 }
|
|
2886 else
|
|
2887 {
|
|
2888 zoom = imd->scale;
|
|
2889 }
|
|
2890 }
|
|
2891
|
|
2892 if (increment < 0.0)
|
|
2893 {
|
|
2894 if (zoom >= 1.0 && zoom + increment < 1.0)
|
|
2895 {
|
|
2896 zoom = zoom + increment - 2.0;
|
|
2897 }
|
|
2898 else
|
|
2899 {
|
|
2900 zoom = zoom + increment;
|
|
2901 }
|
|
2902 }
|
|
2903 else
|
1
|
2904 {
|
9
|
2905 if (zoom <= -1.0 && zoom + increment > -1.0)
|
|
2906 {
|
|
2907 zoom = zoom + increment + 2.0;
|
|
2908 }
|
|
2909 else
|
|
2910 {
|
|
2911 zoom = zoom + increment;
|
|
2912 }
|
|
2913 }
|
|
2914
|
|
2915 image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, center_point, x, y);
|
|
2916 }
|
|
2917
|
|
2918 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
|
|
2919 {
|
|
2920 image_zoom_adjust_real(imd, increment, FALSE, 0, 0);
|
|
2921 }
|
|
2922
|
|
2923 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
|
|
2924 {
|
|
2925 image_zoom_adjust_real(imd, increment, TRUE, x, y);
|
|
2926 }
|
|
2927
|
|
2928 void image_zoom_set(ImageWindow *imd, gdouble zoom)
|
|
2929 {
|
|
2930 image_zoom_sync(imd, zoom, FALSE, FALSE, FALSE, FALSE, 0, 0);
|
|
2931 }
|
|
2932
|
|
2933 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
|
|
2934 {
|
|
2935 gdouble zoom;
|
|
2936
|
|
2937 if (!imd->pixbuf || imd->image_width < 1 || imd->image_height < 1) return;
|
|
2938
|
|
2939 if (vertical)
|
|
2940 {
|
|
2941 zoom = (gdouble)imd->window_height / imd->image_height;
|
|
2942 }
|
|
2943 else
|
|
2944 {
|
|
2945 zoom = (gdouble)imd->window_width / imd->image_width;
|
|
2946 }
|
|
2947
|
|
2948 if (zoom < 1.0)
|
|
2949 {
|
|
2950 zoom = 0.0 - 1.0 / zoom;
|
1
|
2951 }
|
9
|
2952
|
|
2953 image_zoom_set(imd, zoom);
|
|
2954 }
|
|
2955
|
|
2956 gdouble image_zoom_get(ImageWindow *imd)
|
|
2957 {
|
|
2958 return imd->zoom;
|
|
2959 }
|
|
2960
|
|
2961 gdouble image_zoom_get_real(ImageWindow *imd)
|
|
2962 {
|
|
2963 return imd->scale;
|
|
2964 }
|
|
2965
|
|
2966 gchar *image_zoom_get_as_text(ImageWindow *imd)
|
|
2967 {
|
|
2968 gdouble l = 1.0;
|
|
2969 gdouble r = 1.0;
|
|
2970 gint pl = 0;
|
|
2971 gint pr = 0;
|
|
2972 gchar *approx = " ";
|
|
2973
|
|
2974 if (imd->zoom > 0.0)
|
|
2975 {
|
|
2976 l = imd->zoom;
|
|
2977 }
|
|
2978 else if (imd->zoom < 0.0)
|
1
|
2979 {
|
9
|
2980 r = 0.0 - imd->zoom;
|
|
2981 }
|
|
2982 else if (imd->zoom == 0.0 && imd->scale != 0.0)
|
|
2983 {
|
|
2984 if (imd->scale >= 1.0)
|
|
2985 {
|
|
2986 l = imd->scale;
|
|
2987 }
|
|
2988 else
|
|
2989 {
|
|
2990 r = 1.0 / imd->scale;
|
|
2991 }
|
|
2992 approx = " ~";
|
|
2993 }
|
|
2994
|
|
2995 if (rint(l) != l) pl = 1;
|
|
2996 if (rint(r) != r) pr = 1;
|
|
2997
|
|
2998 return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
|
|
2999 }
|
|
3000
|
|
3001 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
|
|
3002 {
|
|
3003 gdouble zoom;
|
|
3004
|
|
3005 if (mode == ZOOM_RESET_ORIGINAL)
|
|
3006 {
|
|
3007 zoom = 1.0;
|
|
3008 }
|
|
3009 else if (mode == ZOOM_RESET_FIT_WINDOW)
|
|
3010 {
|
|
3011 zoom = 0.0;
|
1
|
3012 }
|
|
3013 else
|
|
3014 {
|
|
3015 if (imd)
|
|
3016 {
|
9
|
3017 zoom = image_zoom_get(imd);
|
1
|
3018 }
|
|
3019 else
|
|
3020 {
|
9
|
3021 zoom = 1.0;
|
1
|
3022 }
|
|
3023 }
|
|
3024
|
|
3025 return zoom;
|
|
3026 }
|
|
3027
|
9
|
3028 /* read ahead */
|
|
3029
|
|
3030 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
|
|
3031 {
|
|
3032 if (path)
|
|
3033 {
|
|
3034 image_read_ahead_set(imd, path);
|
|
3035 }
|
|
3036 else
|
|
3037 {
|
|
3038 image_read_ahead_cancel(imd);
|
|
3039 }
|
|
3040 }
|
|
3041
|
|
3042 static gint image_auto_refresh_cb(gpointer data)
|
|
3043 {
|
|
3044 ImageWindow *imd = data;
|
|
3045 time_t newtime;
|
|
3046
|
|
3047 if (!imd || !imd->pixbuf ||
|
|
3048 imd->il || !imd->image_path ||
|
|
3049 !update_on_time_change) return TRUE;
|
|
3050
|
|
3051 newtime = filetime(imd->image_path);
|
|
3052 if (newtime > 0 && newtime != imd->mtime)
|
|
3053 {
|
|
3054 imd->mtime = newtime;
|
|
3055 image_reload(imd);
|
|
3056 }
|
|
3057
|
|
3058 return TRUE;
|
|
3059 }
|
|
3060
|
|
3061 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
|
|
3062
|
|
3063 void image_auto_refresh(ImageWindow *imd, gint interval)
|
|
3064 {
|
|
3065 if (!imd) return;
|
|
3066
|
|
3067 if (imd->auto_refresh_id > -1)
|
|
3068 {
|
|
3069 g_source_remove(imd->auto_refresh_id);
|
|
3070 imd->auto_refresh_id = -1;
|
|
3071 imd->auto_refresh_interval = -1;
|
|
3072 }
|
|
3073
|
|
3074 if (interval < 0) return;
|
|
3075
|
|
3076 if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
|
|
3077
|
|
3078 imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
|
|
3079 imd->auto_refresh_interval = interval;
|
|
3080 }
|
|
3081
|
|
3082 /* allow top window to be resized ? */
|
|
3083
|
|
3084 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
|
|
3085 {
|
|
3086 imd->top_window_sync = allow_sync;
|
|
3087 }
|
|
3088
|
|
3089 /* background colors */
|
|
3090
|
|
3091 void image_background_set_black(ImageWindow *imd, gint black)
|
|
3092 {
|
|
3093 GtkStyle *style;
|
|
3094
|
|
3095 if (!imd) return;
|
|
3096
|
|
3097 style = gtk_style_copy(gtk_widget_get_style(imd->widget));
|
|
3098 g_object_ref(G_OBJECT(style));
|
|
3099
|
|
3100 if (black)
|
|
3101 {
|
|
3102 style->bg[GTK_STATE_NORMAL] = style->black;
|
|
3103 }
|
|
3104
|
|
3105 gtk_widget_set_style(imd->image, style);
|
|
3106 g_object_unref(G_OBJECT(style));
|
|
3107
|
|
3108 if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
|
|
3109 }
|
|
3110
|
|
3111 void image_background_set_color(ImageWindow *imd, GdkColor *color)
|
|
3112 {
|
|
3113 GtkStyle *style;
|
|
3114
|
|
3115 if (!imd) return;
|
|
3116
|
|
3117 style = gtk_style_copy(gtk_widget_get_style(imd->widget));
|
|
3118 g_object_ref(G_OBJECT(style));
|
|
3119
|
|
3120 if (color)
|
|
3121 {
|
|
3122 GdkColor *slot;
|
|
3123
|
|
3124 slot = &style->bg[GTK_STATE_NORMAL];
|
|
3125
|
|
3126 slot->red = color->red;
|
|
3127 slot->green = color->green;
|
|
3128 slot->blue = color->blue;
|
|
3129 }
|
|
3130
|
|
3131 gtk_widget_set_style(imd->image, style);
|
|
3132 g_object_unref(G_OBJECT(style));
|
|
3133
|
|
3134 if (GTK_WIDGET_VISIBLE(imd->widget)) image_border_clear(imd);
|
|
3135 }
|
|
3136
|
|
3137 void image_set_delay_flip(ImageWindow *imd, gint delay)
|
|
3138 {
|
|
3139 if (!imd ||
|
|
3140 imd->delay_flip == delay) return;
|
|
3141
|
|
3142 imd->delay_flip = delay;
|
|
3143 if (!imd->delay_flip && imd->il)
|
|
3144 {
|
|
3145 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
3146 imd->pixbuf = NULL;
|
|
3147 image_load_pixbuf_ready(imd);
|
|
3148
|
|
3149 image_queue_clear(imd);
|
|
3150 image_queue(imd, 0, 0, imd->width, imd->height, FALSE, TILE_RENDER_AREA, TRUE);
|
|
3151 }
|
|
3152 }
|
|
3153
|
|
3154 /* wallpaper util */
|
|
3155
|
|
3156 void image_to_root_window(ImageWindow *imd, gint scaled)
|
|
3157 {
|
|
3158 GdkScreen *screen;
|
3
|
3159 GdkWindow *rootwindow;
|
|
3160 GdkPixmap *pixmap;
|
9
|
3161 GdkPixbuf *pb;
|
|
3162
|
|
3163 if (!imd || !imd->pixbuf) return;
|
|
3164
|
|
3165
|
|
3166 screen = gtk_widget_get_screen(imd->image);
|
|
3167 rootwindow = gdk_screen_get_root_window(screen);
|
|
3168 if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
|
3
|
3169
|
|
3170 if (scaled)
|
|
3171 {
|
9
|
3172 pb = gdk_pixbuf_scale_simple(imd->pixbuf, gdk_screen_width(), gdk_screen_height(), (GdkInterpType)zoom_quality);
|
3
|
3173 }
|
|
3174 else
|
|
3175 {
|
9
|
3176 pb = gdk_pixbuf_scale_simple(imd->pixbuf, imd->width, imd->height, (GdkInterpType)zoom_quality);
|
3
|
3177 }
|
|
3178
|
9
|
3179 gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
|
3
|
3180 gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
|
|
3181 gdk_window_clear(rootwindow);
|
9
|
3182 g_object_unref(pb);
|
|
3183 g_object_unref(pixmap);
|
3
|
3184
|
|
3185 gdk_flush();
|
|
3186 }
|
|
3187
|
|
3188
|
9
|
3189 /*
|
|
3190 *-------------------------------------------------------------------
|
|
3191 * init / destroy
|
|
3192 *-------------------------------------------------------------------
|
|
3193 */
|
|
3194
|
|
3195 static void image_free(ImageWindow *imd)
|
|
3196 {
|
|
3197 image_read_ahead_cancel(imd);
|
|
3198 image_post_buffer_set(imd, NULL, NULL);
|
|
3199 image_auto_refresh(imd, -1);
|
|
3200
|
|
3201 g_free(imd->image_path);
|
|
3202 g_free(imd->title);
|
|
3203 g_free(imd->title_right);
|
|
3204
|
|
3205 image_reset(imd);
|
|
3206 image_tile_sync_count(imd, 0);
|
|
3207 if (imd->pixbuf) g_object_unref(imd->pixbuf);
|
|
3208
|
|
3209 image_scroller_timer_set(imd, FALSE);
|
|
3210
|
|
3211 image_overlay_list_clear(imd);
|
|
3212
|
|
3213 g_free(imd);
|
|
3214 }
|
|
3215
|
|
3216 static void image_destroy_cb(GtkObject *widget, gpointer data)
|
|
3217 {
|
|
3218 ImageWindow *imd = data;
|
|
3219 image_free(imd);
|
|
3220 }
|
|
3221
|
|
3222 ImageWindow *image_new(gint frame)
|
|
3223 {
|
|
3224 ImageWindow *imd;
|
|
3225
|
|
3226 imd = g_new0(ImageWindow, 1);
|
|
3227 imd->zoom = 1.0;
|
|
3228 imd->scale = 1.0;
|
|
3229
|
|
3230 imd->draw_idle_id = -1;
|
|
3231
|
|
3232 imd->tile_width = IMAGE_TILE_SIZE;
|
|
3233 imd->tile_height = IMAGE_TILE_SIZE;
|
|
3234
|
|
3235 imd->top_window = NULL;
|
|
3236 imd->title = NULL;
|
|
3237 imd->title_right = NULL;
|
|
3238 imd->title_show_zoom = FALSE;
|
|
3239
|
|
3240 imd->unknown = TRUE;
|
|
3241
|
|
3242 imd->pixbuf = NULL;
|
|
3243
|
|
3244 imd->has_frame = frame;
|
|
3245 imd->top_window_sync = FALSE;
|
|
3246
|
|
3247 imd->tile_cache = NULL;
|
|
3248 imd->tile_cache_size = 0;
|
|
3249
|
|
3250 imd->delay_alter_type = ALTER_NONE;
|
|
3251
|
|
3252 imd->read_ahead_il = NULL;
|
|
3253 imd->read_ahead_pixbuf = NULL;
|
|
3254 imd->read_ahead_path = NULL;
|
|
3255
|
|
3256 imd->completed = FALSE;
|
|
3257
|
|
3258 imd->auto_refresh_id = -1;
|
|
3259 imd->auto_refresh_interval = -1;
|
|
3260
|
|
3261 imd->delay_flip = FALSE;
|
|
3262
|
|
3263 imd->func_update = NULL;
|
|
3264 imd->func_complete = NULL;
|
|
3265
|
|
3266 imd->func_button = NULL;
|
|
3267 imd->func_scroll = NULL;
|
|
3268
|
|
3269 imd->scroller_id = -1;
|
|
3270 imd->scroller_overlay = -1;
|
|
3271
|
|
3272 imd->image = gtk_drawing_area_new();
|
|
3273 gtk_widget_set_double_buffered(imd->image, FALSE);
|
|
3274
|
|
3275 if (imd->has_frame)
|
|
3276 {
|
|
3277 imd->widget = gtk_frame_new(NULL);
|
|
3278 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
|
|
3279 gtk_container_add(GTK_CONTAINER(imd->widget), imd->image);
|
|
3280 gtk_widget_show(imd->image);
|
|
3281
|
|
3282 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
|
|
3283 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
|
|
3284 G_CALLBACK(image_focus_in_cb), imd);
|
|
3285 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
|
|
3286 G_CALLBACK(image_focus_out_cb), imd);
|
|
3287
|
|
3288 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
|
|
3289 G_CALLBACK(image_focus_expose), imd);
|
|
3290 }
|
|
3291 else
|
|
3292 {
|
|
3293 imd->widget = imd->image;
|
|
3294 }
|
|
3295
|
|
3296 g_signal_connect(G_OBJECT(imd->image), "motion_notify_event",
|
|
3297 G_CALLBACK(image_mouse_motion_cb), imd);
|
|
3298 g_signal_connect(G_OBJECT(imd->image), "button_press_event",
|
|
3299 G_CALLBACK(image_mouse_press_cb), imd);
|
|
3300 g_signal_connect(G_OBJECT(imd->image), "button_release_event",
|
|
3301 G_CALLBACK(image_mouse_release_cb), imd);
|
|
3302 g_signal_connect(G_OBJECT(imd->image), "leave_notify_event",
|
|
3303 G_CALLBACK(image_mouse_leave_cb), imd);
|
|
3304 gtk_widget_set_events(imd->image, GDK_POINTER_MOTION_MASK |
|
|
3305 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK |
|
|
3306 GDK_LEAVE_NOTIFY_MASK);
|
|
3307
|
|
3308 g_signal_connect(G_OBJECT(imd->image), "expose_event",
|
|
3309 G_CALLBACK(image_expose_cb), imd);
|
|
3310 g_signal_connect_after(G_OBJECT(imd->image), "size_allocate",
|
|
3311 G_CALLBACK(image_size_cb), imd);
|
|
3312
|
|
3313 g_signal_connect(G_OBJECT(imd->image), "drag_begin",
|
|
3314 G_CALLBACK(image_mouse_drag_cb), imd);
|
|
3315 g_signal_connect(G_OBJECT(imd->image), "scroll_event",
|
|
3316 G_CALLBACK(image_scroll_cb), imd);
|
|
3317
|
|
3318 g_signal_connect(G_OBJECT(imd->widget), "destroy",
|
|
3319 G_CALLBACK(image_destroy_cb), imd);
|
|
3320
|
|
3321 return imd;
|
|
3322 }
|
|
3323
|