1 /*
2 Module : image.c
3 Purpose : Routines dealing with image display
4 More : see qiv README
5 Policy : GNU GPL
6 Homepage : http://qiv.spiegl.de/
7 Original : http://www.klografx.net/qiv/
8 */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <gdk/gdkx.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include "qiv.h"
18 #include "xmalloc.h"
19 #include "qiv_icon.h"
20
21 static void setup_win(qiv_image *);
22 //static void setup_magnify(qiv_image *, qiv_mgl *); // [lc]
23 static int used_masks_before=0;
24 static struct timeval load_before, load_after;
25 static double load_elapsed;
26 static GdkCursor *cursor, *visible_cursor, *invisible_cursor;
27
im_from_pixbuf_loader(char * image_name,int * has_alpha)28 Imlib_Image im_from_pixbuf_loader(char * image_name, int * has_alpha)
29 {
30 GError *error=NULL;
31 GdkPixbuf *pixbuf_ori;
32 GdkPixbuf *pixbuf;
33 char * argbdata;
34 guchar * pixels;
35 guchar * pixels_ori;
36 int i,j,k,rs;
37 int pb_w, pb_h;
38 Imlib_Image * im=NULL;
39 const gchar *gdk_orientation = NULL;
40 #ifdef SUPPORT_LCMS
41 char *icc_profile;
42 cmsHPROFILE h_emb_profile;
43 cmsHTRANSFORM h_emb_transform;
44 #endif
45
46
47 pixbuf_ori = gdk_pixbuf_new_from_file(image_name, &error);
48 if (error != NULL)
49 {
50 /* Report error to user, and free error */
51 fprintf(stderr, "Unable to read file: %s\n", error->message);
52 g_error_free (error);
53 }
54 else
55 {
56 #if GDK_PIXBUF_MINOR >= 12
57 if(autorotate)
58 {
59 gdk_orientation = gdk_pixbuf_get_option(pixbuf_ori, "orientation");
60 if(gdk_orientation)
61 {
62 #ifdef DEBUG
63 printf("orientation %s\n", gdk_orientation);
64 #endif
65 pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf_ori);
66 g_object_unref(pixbuf_ori);
67 pixbuf_ori = pixbuf;
68 }
69 }
70 #else
71 #warning autoration needs at least gdk version 2.12
72 #endif
73
74 *has_alpha = ( gdk_pixbuf_get_n_channels(pixbuf_ori) == 4) ? 1 : 0;
75
76 pixels_ori = gdk_pixbuf_get_pixels(pixbuf_ori);
77 /* create checkboard if image has transparency */
78 if(*has_alpha)
79 {
80 pixbuf = gdk_pixbuf_composite_color_simple(pixbuf_ori, gdk_pixbuf_get_width(pixbuf_ori),
81 gdk_pixbuf_get_height(pixbuf_ori), GDK_INTERP_NEAREST, 255, 0x08, 0x00666666, 0x00aaaaaa);
82
83 pixels = gdk_pixbuf_get_pixels(pixbuf);
84 }
85 else
86 {
87 pixbuf = pixbuf_ori;
88 pixels = pixels_ori;
89 }
90
91 #ifdef DEBUG
92 printf("channels %i\n", gdk_pixbuf_get_n_channels(pixbuf));
93 printf("rowstride %i\n", gdk_pixbuf_get_rowstride(pixbuf));
94 #endif
95
96 pb_w = gdk_pixbuf_get_width(pixbuf);
97 pb_h = gdk_pixbuf_get_height(pixbuf);
98
99 argbdata=malloc(4 * pb_w * pb_h);
100
101 /* create imlib2 compatible data */
102 if(*has_alpha)
103 {
104 for(i=0; i< pb_w*pb_h*4; i=i+4)
105 {
106 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
107 argbdata[i+0]=pixels[i+2]; // B
108 argbdata[i+1]=pixels[i+1]; // G
109 argbdata[i+2]=pixels[i]; // R
110 // keep old alpha values
111 argbdata[i+3]=pixels_ori[i+3]; // Alpha
112 #else
113 // BIG_ENDIAN
114 argbdata[i+3]=pixels[i+2]; // B
115 argbdata[i+2]=pixels[i+1]; // G
116 argbdata[i+1]=pixels[i]; // R
117 // keep old alpha values
118 argbdata[i+0]=pixels_ori[i+3]; // Alpha
119 #endif
120 }
121 }
122 else
123 {
124 rs = gdk_pixbuf_get_rowstride (pixbuf);
125 k=0;
126 for(i=0; i < pb_h; i++)
127 {
128 for(j=0; j< pb_w*3; j+=3)
129 {
130 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
131 argbdata[k++]=pixels[i*rs+j+2]; // B
132 argbdata[k++]=pixels[i*rs+j+1]; // G
133 argbdata[k++]=pixels[i*rs+j]; // R
134 argbdata[k++]=0;
135 #else
136 argbdata[k++]=0;
137 argbdata[k++]=pixels[i*rs+j]; // R
138 argbdata[k++]=pixels[i*rs+j+1]; // G
139 argbdata[k++]=pixels[i*rs+j+2]; // B
140 #endif
141 }
142 }
143 }
144
145 #ifdef SUPPORT_LCMS
146 if((icc_profile=get_icc_profile(image_name)))
147 {
148 h_emb_profile = cmsOpenProfileFromMem(icc_profile+sizeof(cmsUInt32Number), *(cmsUInt32Number *)icc_profile);
149 if(h_display_profile==NULL)
150 {
151 h_display_profile = cmsCreate_sRGBProfile();
152 }
153
154 h_emb_transform = cmsCreateTransform(h_emb_profile,
155 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
156 TYPE_BGRA_8,
157 h_display_profile,
158 TYPE_BGRA_8,
159 #else
160 TYPE_ARGB_8,
161 h_display_profile,
162 TYPE_ARGB_8,
163 #endif
164 INTENT_PERCEPTUAL, 0);
165 if(h_emb_transform)
166 {
167 cmsDoTransform(h_emb_transform, argbdata, argbdata, pb_w*pb_h);
168 cmsCloseProfile(h_emb_profile);
169 cmsDeleteTransform(h_emb_transform);
170 }
171 else
172 {
173 printf("qiv warning: %s contains corrupt color profile\n",image_name);
174 }
175 free(icc_profile);
176 }
177
178 /* do the color transform */
179 else if (cms_transform && h_cms_transform)
180 {
181 cmsDoTransform(h_cms_transform, argbdata, argbdata, pb_w*pb_h);
182 }
183 #endif
184 im = imlib_create_image_using_copied_data(pb_w, pb_h, (DATA32*)argbdata);
185 free(argbdata);
186 if(*has_alpha)
187 {
188 g_object_unref(pixbuf);
189 }
190 g_object_unref(pixbuf_ori);
191 }
192 return im;
193 }
194
195 /*
196 * Load & display image
197 */
198
qiv_load_image(qiv_image * q)199 void qiv_load_image(qiv_image *q)
200 {
201 struct stat statbuf;
202 const char * image_name = image_names[ image_idx];
203 Imlib_Image * im=NULL;
204 int has_alpha=0, rot;
205
206 q->exposed=0;
207 gettimeofday(&load_before, 0);
208
209 if (imlib_context_get_image())
210 imlib_free_image();
211
212 stat(image_name, &statbuf);
213 current_mtime = statbuf.st_mtime;
214 file_size = statbuf.st_size;
215
216 #ifdef DEBUG
217 g_print("loading %s\n",image_name);
218 #endif
219
220 /* use gdk_pixbuf for loading
221 im = imlib_load_image( (char*)image_name );
222 */
223
224 im = im_from_pixbuf_loader( (char*)image_name, &has_alpha);
225
226 if (!im) { /* error */
227 q->error = 1;
228 q->orig_w = 400;
229 q->orig_h = 300;
230 } else { /* Retrieve image properties */
231 imlib_context_set_image(im);
232 if(has_alpha) {
233 imlib_image_set_has_alpha(has_alpha);
234 }
235 q->error = 0;
236 q->orig_w = imlib_image_get_width();
237 q->orig_h = imlib_image_get_height();
238
239 if (rotation > 10) {
240 /* conditional rotation -- apply rotation only if image fits better */
241 int screen_is_wide = monitor[q->mon_id].width > monitor[q->mon_id].height;
242 int image_is_wide = q->orig_w > q->orig_h;
243 int does_not_fit = q->orig_w > monitor[q->mon_id].width || q->orig_h > monitor[q->mon_id].height;
244 if (screen_is_wide != image_is_wide && does_not_fit)
245 rot = rotation - 10; /* we want the rotation (will be 11 -> 1 or 13 -> 3) */
246 else
247 rot = 0; /* don't rotate */
248 } else
249 rot = rotation;
250
251 if (rot) {
252 imlib_image_orientate(rot);
253 if (rot != 2) {
254 swap(&q->orig_w, &q->orig_h);
255 swap(&q->win_w, &q->win_h);
256 }
257 }
258
259 if (rot && rot != 2)
260 correct_image_position(q);
261
262 }
263
264 if (first) {
265 setup_win(q);
266 }
267
268 check_size(q, TRUE);
269
270 /* desktop-background -> exit */
271 if (to_root || to_root_t || to_root_s) {
272 if (!im) {
273 fprintf(stderr, "qiv: cannot load background_image\n");
274 qiv_exit(1);
275 }
276 set_desktop_image(q);
277 qiv_exit(0);
278 }
279
280 gdk_window_set_background(q->win, im ? &image_bg : &error_bg);
281
282 gettimeofday(&load_after, 0);
283 load_elapsed = ((load_after.tv_sec + load_after.tv_usec / 1.0e6) -
284 (load_before.tv_sec + load_before.tv_usec / 1.0e6));
285
286 update_image(q, FULL_REDRAW);
287 // if (magnify && !fullscreen) { // [lc]
288 // setup_magnify(q, &magnify_img);
289 // update_magnify(q, &magnify_img, FULL_REDRAW, 0, 0);
290 // }
291 }
292
293 static gchar blank_cursor[1];
294
setup_imlib_for_drawable(GdkDrawable * d)295 static void setup_imlib_for_drawable(GdkDrawable * d)
296 {
297 imlib_context_set_dither(1); /* dither for depths < 24bpp */
298 imlib_context_set_display(
299 gdk_x11_drawable_get_xdisplay(d));
300 imlib_context_set_visual(
301 gdk_x11_visual_get_xvisual(gdk_drawable_get_visual(d)));
302 imlib_context_set_colormap(
303 gdk_x11_colormap_get_xcolormap(gdk_drawable_get_colormap(d)));
304 imlib_context_set_drawable(
305 gdk_x11_drawable_get_xid(d));
306 }
307
setup_imlib_color_modifier(qiv_color_modifier q)308 static void setup_imlib_color_modifier(qiv_color_modifier q)
309 {
310 if (q.gamma == DEFAULT_GAMMA &&
311 q.brightness == DEFAULT_BRIGHTNESS &&
312 q.contrast == DEFAULT_CONTRAST) {
313 if (imlib_context_get_color_modifier())
314 imlib_free_color_modifier();
315 return;
316 }
317
318 if (imlib_context_get_color_modifier())
319 imlib_reset_color_modifier();
320 else
321 imlib_context_set_color_modifier(imlib_create_color_modifier());
322
323 imlib_modify_color_modifier_gamma(q.gamma / 256.0);
324 imlib_modify_color_modifier_brightness((q.brightness - 256) / 256.0);
325 imlib_modify_color_modifier_contrast(q.contrast / 256.0);
326 }
327
setup_win(qiv_image * q)328 static void setup_win(qiv_image *q)
329 {
330 GdkWindowAttr attr;
331 GdkPixmap *cursor_pixmap;
332 static GList icon_list = {NULL, NULL, NULL};
333
334 destroy_image(q);
335
336 if (!fullscreen) {
337 attr.window_type=GDK_WINDOW_TOPLEVEL;
338 attr.wclass=GDK_INPUT_OUTPUT;
339 attr.event_mask=GDK_ALL_EVENTS_MASK;
340 attr.x = center ? q->win_x : 0;
341 attr.y = center ? q->win_y : 0;
342 attr.width = q->win_w;
343 attr.height = q->win_h;
344 q->win = gdk_window_new(NULL, &attr, GDK_WA_X|GDK_WA_Y);
345
346 if (center) {
347 GdkGeometry geometry = {
348 .min_width = q->win_w,
349 .min_height = q->win_h,
350 .max_width = q->win_w,
351 .max_height = q->win_h,
352 .win_gravity = GDK_GRAVITY_STATIC
353 };
354 gdk_window_set_geometry_hints(q->win, &geometry,
355 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_WIN_GRAVITY);
356 gdk_window_move_resize(q->win, q->win_x, q->win_y, q->win_w, q->win_h);
357 } else {
358 GdkGeometry geometry = {
359 .min_width = q->win_w,
360 .min_height = q->win_h,
361 .max_width = q->win_w,
362 .max_height = q->win_h,
363 };
364 gdk_window_set_geometry_hints(q->win, &geometry,
365 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
366 gdk_window_resize(q->win, q->win_w, q->win_h);
367 }
368 if (!(to_root || to_root_t || to_root_s))
369 gdk_window_lower(q->win);
370
371 } else { /* fullscreen */
372
373 attr.window_type=GDK_WINDOW_TOPLEVEL;
374 attr.wclass=GDK_INPUT_OUTPUT;
375 attr.event_mask=GDK_ALL_EVENTS_MASK;
376 // attr.x = 0;
377 // attr.y = 0;
378 attr.x = monitor[q->mon_id].x;
379 attr.y = monitor[q->mon_id].y;
380 // printf("mon_id %d \n",q->mon_id);
381 attr.width=monitor[q->mon_id].width;
382 attr.height=monitor[q->mon_id].height;
383
384 // q->win = gdk_window_new(NULL, &attr, GDK_WA_X|GDK_WA_Y|GDK_WA_TYPE_HINT);
385 q->win = gdk_window_new(NULL, &attr, GDK_WA_X|GDK_WA_Y);
386 gdk_window_set_cursor(q->win, cursor);
387 if (!(to_root || to_root_t || to_root_s))
388 {
389 gdk_window_fullscreen(q->win);
390 gdk_window_show(q->win);
391 }
392 }
393
394 /* this is all done only once */
395 if(icon_list.data==NULL)
396 {
397 icon_list.data = gdk_pixbuf_new_from_inline (-1, qiv_icon, FALSE, NULL);
398 cursor_pixmap = gdk_bitmap_create_from_data(q->win, blank_cursor, 1, 1);
399 invisible_cursor = gdk_cursor_new_from_pixmap(cursor_pixmap, cursor_pixmap,
400 &text_bg, &text_bg, 0, 0);
401 cursor = visible_cursor = gdk_cursor_new(CURSOR);
402 }
403
404 gdk_window_set_icon_list(q->win, &icon_list);
405 gdk_window_set_cursor(q->win, cursor);
406
407 q->bg_gc = gdk_gc_new(q->win);
408 q->text_gc = gdk_gc_new(q->win); /* black is default */
409 q->status_gc = gdk_gc_new(q->win);
410 q->comment_gc = gdk_gc_new(q->win);
411 gdk_gc_set_foreground(q->bg_gc, &image_bg);
412 gdk_gc_set_foreground(q->status_gc, &text_bg);
413 gdk_gc_set_foreground(q->comment_gc, &comment_bg);
414
415 setup_imlib_for_drawable(GDK_DRAWABLE(q->win));
416 }
417
hide_cursor(qiv_image * q)418 void hide_cursor(qiv_image *q)
419 {
420 if (cursor != invisible_cursor)
421 gdk_window_set_cursor(q->win, cursor = invisible_cursor);
422 }
423
show_cursor(qiv_image * q)424 void show_cursor(qiv_image *q)
425 {
426 if (cursor != visible_cursor)
427 gdk_window_set_cursor(q->win, cursor = visible_cursor);
428 }
429
430 /* set image as background */
431
set_desktop_image(qiv_image * q)432 void set_desktop_image(qiv_image *q)
433 {
434 GdkWindow *root_win = gdk_get_default_root_window();
435 GdkVisual *gvis = gdk_drawable_get_visual(root_win);
436 GdkPixmap *temp;
437 gchar *buffer;
438
439 gint root_w = screen_x, root_h = screen_y;
440 gint root_x = 0, root_y = 0;
441
442 Pixmap x_pixmap, x_mask;
443
444 if (to_root || to_root_t) {
445 root_w = q->win_w;
446 root_h = q->win_h;
447 }
448
449 if (to_root) {
450 root_x = (screen_x - root_w) / 2;
451 root_y = (screen_y - root_h) / 2;
452 }
453
454 setup_imlib_for_drawable(GDK_DRAWABLE(root_win));
455
456 imlib_render_pixmaps_for_whole_image_at_size(&x_pixmap, &x_mask, root_w, root_h);
457 #ifdef DEBUG
458 if (x_mask) g_print("*** image has transparency\n");
459 #endif
460
461 if(x_pixmap) {
462 GdkPixmap * p = gdk_pixmap_foreign_new(x_pixmap);
463 gdk_drawable_set_colormap(GDK_DRAWABLE(p),
464 gdk_drawable_get_colormap(GDK_DRAWABLE(root_win)));
465 if (to_root_t) {
466 gdk_window_set_back_pixmap(root_win, p, FALSE);
467 } else {
468 GdkGC *rootGC;
469 buffer = xcalloc(1, screen_x * screen_y * gdk_visual_get_depth(gvis) / 8);
470 rootGC = gdk_gc_new(root_win);
471 temp = gdk_pixmap_create_from_data(root_win, buffer, screen_x,
472 screen_y, gdk_visual_get_depth(gvis), &image_bg, &image_bg);
473 gdk_drawable_set_colormap(GDK_DRAWABLE(temp),
474 gdk_drawable_get_colormap(GDK_DRAWABLE(root_win)));
475 gdk_draw_drawable(temp, rootGC, p, 0, 0, root_x, root_y, root_w, root_h);
476 gdk_window_set_back_pixmap(root_win, temp, FALSE);
477 g_object_unref(temp);
478 g_object_unref(rootGC);
479 free(buffer);
480 }
481 g_object_unref(p);
482 imlib_free_pixmap_and_mask(x_pixmap);
483 }
484 gdk_window_clear(root_win);
485 gdk_flush();
486
487 setup_imlib_for_drawable(q->win);
488 }
489
zoom_in(qiv_image * q)490 void zoom_in(qiv_image *q)
491 {
492 int zoom_percentage;
493 int w_old, h_old;
494
495 /* first compute current zoom_factor */
496 if (maxpect || scale_down || fixed_window_size) {
497 zoom_percentage=myround((1.0-(q->orig_w - q->win_w)/(double)q->orig_w)*100);
498 zoom_factor=(zoom_percentage - 100) / 10;
499 }
500
501 maxpect = scale_down = 0;
502
503 zoom_factor++;
504 w_old = q->win_w;
505 h_old = q->win_h;
506 q->win_w = (gint)(q->orig_w * (1 + zoom_factor * 0.1));
507 q->win_h = (gint)(q->orig_h * (1 + zoom_factor * 0.1));
508
509 /* adapt image position */
510 q->win_x -= (q->win_w - w_old) / 2;
511 q->win_y -= (q->win_h - h_old) / 2;
512
513 if (fullscreen) {
514 if (center)
515 center_image(q);
516 else
517 correct_image_position(q);
518 } else {
519 correct_image_position(q);
520 }
521 }
522
zoom_out(qiv_image * q)523 void zoom_out(qiv_image *q)
524 {
525 int zoom_percentage;
526 int w_old, h_old;
527
528 /* first compute current zoom_factor */
529 if (maxpect || scale_down || fixed_window_size) {
530 zoom_percentage=myround((1.0-(q->orig_w - q->win_w)/(double)q->orig_w)*100);
531 zoom_factor=(zoom_percentage - 100) / 10;
532 }
533
534 maxpect = scale_down = 0;
535
536 w_old = q->win_w;
537 h_old = q->win_h;
538
539 if(zoom_factor > -9 && q->win_w > MIN(64, q->orig_w) && q->win_h > MIN(64, q->orig_h)) {
540 zoom_factor--;
541 q->win_w = (gint)(q->orig_w * (1 + zoom_factor * 0.1));
542 q->win_h = (gint)(q->orig_h * (1 + zoom_factor * 0.1));
543
544 /* adapt image position */
545 q->win_x -= (q->win_w - w_old) / 2;
546 q->win_y -= (q->win_h - h_old) / 2;
547
548 if (fullscreen) {
549 if (center)
550 center_image(q);
551 else
552 correct_image_position(q);
553 } else {
554 correct_image_position(q);
555 }
556 } else {
557 snprintf(infotext, sizeof infotext, "(Cannot zoom out anymore)");
558 fprintf(stderr, "qiv: cannot zoom out anymore\n");
559 }
560 }
561
zoom_maxpect(qiv_image * q)562 void zoom_maxpect(qiv_image *q)
563 {
564 double zx = (double)monitor[q->mon_id].width / (double)q->orig_w;
565 double zy = (double)monitor[q->mon_id].height / (double)q->orig_h;
566
567 /* titlebar and frames ignored on purpose to use full height/width of screen */
568 q->win_w = (gint)(q->orig_w * MIN(zx, zy));
569 q->win_h = (gint)(q->orig_h * MIN(zx, zy));
570 center_image(q);
571 }
572
573 /*
574 Set display settings to startup values
575 which are used whenever a new image is loaded.
576 */
577
reload_image(qiv_image * q)578 void reload_image(qiv_image *q)
579 {
580 Imlib_Image *im;
581 int has_alpha = 0;
582
583 imlib_image_set_changes_on_disk();
584
585 im = im_from_pixbuf_loader(image_names[image_idx], &has_alpha);
586
587 if (!im && watch_file)
588 return;
589
590 struct stat statbuf;
591 stat(image_names[image_idx], &statbuf);
592 current_mtime = statbuf.st_mtime;
593
594 if (imlib_context_get_image())
595 imlib_free_image();
596
597 if (!im)
598 {
599 q->error = 1;
600 q->orig_w = 400;
601 q->orig_h = 300;
602 } else { /* Retrieve image properties */
603 q->error = 0;
604 imlib_context_set_image(im);
605 if(has_alpha) {
606 imlib_image_set_has_alpha(has_alpha);
607 }
608 q->orig_w = imlib_image_get_width();
609 q->orig_h = imlib_image_get_height();
610 }
611
612 q->win_w = (gint)(q->orig_w * (1 + zoom_factor * 0.1));
613 q->win_h = (gint)(q->orig_h * (1 + zoom_factor * 0.1));
614 reset_mod(q);
615 if (center) center_image(q);
616 }
617
check_size(qiv_image * q,gint reset)618 void check_size(qiv_image *q, gint reset)
619 {
620 if (maxpect || (scale_down && (q->orig_w>monitor[q->mon_id].width || q->orig_h>monitor[q->mon_id].height))) {
621 zoom_maxpect(q);
622 } else if (reset || (scale_down && (q->win_w<q->orig_w || q->win_h<q->orig_h))) {
623 reset_coords(q);
624 }
625 if (center){
626 center_image(q);
627 }
628 else if (fullscreen) {
629 if (q->win_x > monitor[q->mon_id].x)
630 q->win_x -= monitor[q->mon_id].x;
631 if (q->win_y > monitor[q->mon_id].y)
632 q->win_y -= monitor[q->mon_id].y;
633 }
634 else {
635 if (q->win_x < monitor[q->mon_id].x)
636 q->win_x += monitor[q->mon_id].x;
637 if (q->win_y < monitor[q->mon_id].y)
638 q->win_y += monitor[q->mon_id].y;
639 }
640
641 }
642
reset_coords(qiv_image * q)643 void reset_coords(qiv_image *q)
644 {
645 if (fixed_window_size) {
646 double w_o_ratio = (double)(fixed_window_size) / q->orig_w;
647 q->win_w = fixed_window_size;
648 q->win_h = q->orig_h * w_o_ratio;
649 } else {
650 q->win_w = (gint)(q->orig_w * (1 + zoom_factor * 0.1));
651 q->win_h = (gint)(q->orig_h * (1 + zoom_factor * 0.1));
652 }
653 }
654
655 /* Something changed the image. Redraw it. */
656
update_image(qiv_image * q,int mode)657 void update_image(qiv_image *q, int mode)
658 {
659 static GdkPixmap * m = NULL;
660 Pixmap x_pixmap, x_mask;
661 double elapsed=0;
662 struct timeval before, after;
663 int i;
664
665 if (q->error) {
666 g_snprintf(q->win_title, sizeof q->win_title,
667 "qiv: ERROR! cannot load image: %s", image_names[image_idx]);
668 gdk_beep();
669
670 /* take this image out of the file list */
671 --images;
672 for(i=image_idx;i<images;++i) {
673 image_names[i] = image_names[i+1];
674 }
675
676 /* If deleting the last file out of x */
677 if(images == image_idx)
678 image_idx = 0;
679
680 /* If deleting the only file left */
681 if(!images) {
682 #ifdef DEBUG
683 g_print("*** deleted last file in list. Exiting.\n");
684 #endif
685 exit(0);
686 }
687 /* else load the next image */
688 qiv_load_image(q);
689 return;
690
691 } else {
692 if (mode == REDRAW || mode == FULL_REDRAW)
693 setup_imlib_color_modifier(q->mod);
694
695 if (mode == MOVED) {
696 if (transparency && used_masks_before) {
697 /* there should be a faster way to update the mask, but how? */
698 if (q->p)
699 {
700 imlib_free_pixmap_and_mask(GDK_PIXMAP_XID(q->p));
701 g_object_unref(q->p);
702 }
703 if (m) g_object_unref(m);
704 imlib_render_pixmaps_for_whole_image_at_size(&x_pixmap, &x_mask, q->win_w, q->win_h);
705 q->p = gdk_pixmap_foreign_new(x_pixmap);
706 gdk_drawable_set_colormap(GDK_DRAWABLE(q->p),
707 gdk_drawable_get_colormap(GDK_DRAWABLE(q->win)));
708 m = gdk_pixmap_foreign_new(x_mask);
709 }
710
711 g_snprintf(q->win_title, sizeof q->win_title,
712 "qiv: %s (%dx%d) %d%% [%d/%d] b%d/c%d/g%d %s",
713 image_names[image_idx], q->orig_w, q->orig_h,
714 myround((1.0-(q->orig_w - q->win_w)/(double)q->orig_w)*100), image_idx+1, images,
715 q->mod.brightness/8-32, q->mod.contrast/8-32, q->mod.gamma/8-32, infotext);
716 snprintf(infotext, sizeof infotext, "(-)");
717
718 } // mode == MOVED
719 else
720 {
721 if(mode != MIN_REDRAW)
722 {
723 GdkPixmap * pix_ptr = NULL;
724 if (q->p) {
725 imlib_free_pixmap_and_mask(GDK_PIXMAP_XID(q->p));
726 g_object_unref(q->p);
727 }
728 if (m) g_object_unref(m);
729
730 /* calculate elapsed time while we render image */
731 gettimeofday(&before, 0);
732 imlib_render_pixmaps_for_whole_image_at_size(&x_pixmap, &x_mask, q->win_w, q->win_h);
733 gettimeofday(&after, 0);
734 elapsed = ((after.tv_sec + after.tv_usec / 1.0e6) -
735 (before.tv_sec + before.tv_usec / 1.0e6));
736
737 pix_ptr = gdk_pixmap_lookup (x_pixmap);
738 if(pix_ptr == NULL)
739 {
740 q->p = gdk_pixmap_foreign_new_for_screen(screen, x_pixmap, q->win_w, q->win_h, 24);
741 }
742 else
743 {
744 q->p = pix_ptr;
745 g_object_ref(q->p);
746 }
747 gdk_drawable_set_colormap(GDK_DRAWABLE(q->p),
748 gdk_drawable_get_colormap(GDK_DRAWABLE(q->win)));
749 m = x_mask == None ? NULL : gdk_pixmap_foreign_new_for_screen(screen, x_mask, q->win_w, q->win_h, 1);
750 }
751
752 #ifdef DEBUG
753 if (m) g_print("*** image has transparency\n");
754 #endif
755
756 g_snprintf(q->win_title, sizeof q->win_title,
757 "qiv: %s (%dx%d) %1.01fs %d%% [%d/%d] b%d/c%d/g%d %s",
758 image_names[image_idx], q->orig_w, q->orig_h, load_elapsed+elapsed,
759 myround((1.0-(q->orig_w - q->win_w)/(double)q->orig_w)*100), image_idx+1, images,
760 q->mod.brightness/8-32, q->mod.contrast/8-32, q->mod.gamma/8-32, infotext);
761 snprintf(infotext, sizeof infotext, "(-)");
762 }
763 }
764
765 gdk_window_set_title(q->win, q->win_title);
766
767 q->text_len = strlen(q->win_title);
768 pango_layout_set_text(layout, q->win_title, -1);
769 pango_layout_get_pixel_size (layout, &(q->text_w), &(q->text_h));
770
771 if(comment && comment_window) {
772 pango_layout_set_text(layoutComment, comment, -1);
773 pango_layout_get_pixel_size (layoutComment, &(q->comment_w), &(q->comment_h));
774 }
775
776 if (!fullscreen) {
777 GdkGeometry geometry = {
778 .min_width = q->win_w,
779 .min_height = q->win_h,
780 .max_width = q->win_w,
781 .max_height = q->win_h,
782 .win_gravity = GDK_GRAVITY_STATIC
783 };
784 gdk_window_set_geometry_hints(q->win, &geometry,
785 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_WIN_GRAVITY);
786
787 if(first) {
788 gdk_window_show(q->win);
789 first = 0;
790 q->exposed=0;
791 }
792
793 if(mode != MIN_REDRAW)
794 gdk_window_move_resize(q->win, q->win_x, q->win_y, q->win_w, q->win_h);
795
796 if (!q->error) {
797 gdk_window_set_back_pixmap(q->win, q->p, FALSE);
798 /* remove or set transparency mask */
799 if (used_masks_before) {
800 if (transparency)
801 gdk_window_shape_combine_mask(q->win, m, 0, 0);
802 else
803 gdk_window_shape_combine_mask(q->win, 0, 0, 0);
804 }
805 else
806 {
807 if (transparency && m) {
808 gdk_window_shape_combine_mask(q->win, m, 0, 0);
809 used_masks_before=1;
810 }
811 }
812 }
813 gdk_window_clear(q->win);
814
815 if(statusbar_window)
816 {
817 #ifdef DEBUG
818 g_print("*** print statusbar at (%d, %d)\n", MAX(2,q->win_w-q->text_w-10), MAX(2,q->win_h-q->text_h-10));
819 #endif
820 gdk_draw_rectangle(q->win, q->bg_gc, 0,
821 MAX(2,q->win_w-q->text_w-10), MAX(2,q->win_h-q->text_h-10),
822 q->text_w+5, q->text_h+5);
823 gdk_draw_rectangle(q->win, q->status_gc, 1,
824 MAX(3,q->win_w-q->text_w-9), MAX(3,q->win_h-q->text_h-9),
825 q->text_w+4, q->text_h+4);
826
827 gdk_draw_layout (q->win, q->text_gc, MAX(5,q->win_w-q->text_w-7), MAX(5,q->win_h-7-q->text_h), layout);
828 }
829
830 if(comment && comment_window) {
831 /* draw comment */
832 gdk_draw_rectangle(q->win, q->bg_gc, 0,
833 25, MAX(5,q->win_h-q->comment_h-30),
834 q->comment_w+5, q->comment_h+5);
835
836 gdk_draw_rectangle(q->win, q->comment_gc, 1,
837 26, MAX(6,q->win_h-q->comment_h-29),
838 q->comment_w+4, q->comment_h+4);
839
840 gdk_draw_layout (q->win, q->text_gc, 27, MAX(5,q->win_h - 27 - q->comment_h), layoutComment);
841 }
842
843 } // if (!fullscreen)
844 else
845 {
846 # define statusbar_x monitor[q->mon_id].width
847 # define statusbar_y monitor[q->mon_id].height
848 if (mode == FULL_REDRAW) {
849 gdk_window_clear(q->win);
850 } else {
851 if (q->win_x > q->win_ox)
852 gdk_draw_rectangle(q->win, q->bg_gc, 1,
853 q->win_ox, q->win_oy, q->win_x - q->win_ox, q->win_oh);
854 if (q->win_y > q->win_oy)
855 gdk_draw_rectangle(q->win, q->bg_gc, 1,
856 q->win_ox, q->win_oy, q->win_ow, q->win_y - q->win_oy);
857 if (q->win_x + q->win_w < q->win_ox + q->win_ow)
858 gdk_draw_rectangle(q->win, q->bg_gc, 1,
859 q->win_x + q->win_w, q->win_oy, q->win_ox + q->win_ow, q->win_oh);
860 if (q->win_y + q->win_h < q->win_oy + q->win_oh)
861 gdk_draw_rectangle(q->win, q->bg_gc, 1,
862 q->win_ox, q->win_y + q->win_h, q->win_ow, q->win_oy + q->win_oh);
863
864 if (q->statusbar_was_on && (!statusbar_fullscreen ||
865 q->text_ow > q->text_w || q->text_oh > q->text_h))
866 gdk_draw_rectangle(q->win, q->bg_gc, 1,
867 statusbar_x-q->text_ow-9, statusbar_y-q->text_oh-9,
868 q->text_ow+4, q->text_oh+4);
869 }
870
871 /* remove or set transparency mask */
872 if (used_masks_before) {
873 if (transparency)
874 gdk_window_shape_combine_mask(q->win, m, q->win_x, q->win_y);
875 else
876 gdk_window_shape_combine_mask(q->win, 0, q->win_x, q->win_y);
877 }
878 else
879 {
880 if (transparency && m) {
881 gdk_window_shape_combine_mask(q->win, m, q->win_x, q->win_y);
882 used_masks_before=1;
883 }
884 }
885
886 if (!q->error)
887 gdk_draw_drawable(q->win, q->bg_gc, q->p, 0, 0,
888 q->win_x, q->win_y, q->win_w, q->win_h);
889
890 if (statusbar_fullscreen) {
891 gdk_draw_rectangle(q->win, q->bg_gc, 0,
892 statusbar_x-q->text_w-10, statusbar_y-q->text_h-10, q->text_w+5, q->text_h+5);
893
894 gdk_draw_rectangle(q->win, q->status_gc, 1,
895 statusbar_x-q->text_w-9, statusbar_y-q->text_h-9, q->text_w+4, q->text_h+4);
896
897 gdk_draw_layout (q->win, q->text_gc, statusbar_x-q->text_w-7, statusbar_y-7-q->text_h, layout);
898 }
899
900 if(comment && comment_window) {
901 /* draw comment */
902 gdk_draw_rectangle(q->win, q->bg_gc, 0,
903 25, statusbar_y - q->comment_h - 30,
904 q->comment_w + 5, q->comment_h + 5);
905
906 gdk_draw_rectangle(q->win, q->comment_gc, 1,
907 26, statusbar_y - q->comment_h - 29,
908 q->comment_w + 4, q->comment_h + 4);
909
910 gdk_draw_layout (q->win, q->text_gc, 27, statusbar_y - 27 - q->comment_h, layoutComment);
911 }
912
913 q->win_ox = q->win_x;
914 q->win_oy = q->win_y;
915 q->win_ow = q->win_w;
916 q->win_oh = q->win_h;
917 q->text_ow = q->text_w;
918 q->text_oh = q->text_h;
919 q->statusbar_was_on = statusbar_fullscreen;
920
921 if(first) {
922 gdk_window_show(q->win);
923 first = 0;
924 }
925
926 gdk_window_move_resize(q->win, monitor[q->mon_id].x, monitor[q->mon_id].y,
927 monitor[q->mon_id].width, monitor[q->mon_id].height);
928 }
929 gdk_flush();
930 }
931
932
reset_mod(qiv_image * q)933 void reset_mod(qiv_image *q)
934 {
935 q->mod.brightness = default_brightness;
936 q->mod.contrast = default_contrast;
937 q->mod.gamma = default_gamma;
938 }
939
destroy_image(qiv_image * q)940 void destroy_image(qiv_image *q)
941 {
942 if (q->p) {
943 imlib_free_pixmap_and_mask(GDK_PIXMAP_XID(q->p));
944 g_object_unref(q->p);
945 }
946 if (q->win) g_object_unref(q->win);
947 if (q->bg_gc) g_object_unref(q->bg_gc);
948 if (q->text_gc) g_object_unref(q->text_gc);
949 if (q->status_gc) g_object_unref(q->status_gc);
950 if (q->comment_gc) g_object_unref(q->comment_gc);
951
952 q->p=NULL;
953 q->win=NULL;
954 q->bg_gc=NULL;
955 q->text_gc=NULL;
956 q->status_gc=NULL;
957 q->comment_gc=NULL;
958 }
959
setup_magnify(qiv_image * q,qiv_mgl * m)960 void setup_magnify(qiv_image *q, qiv_mgl *m)
961 {
962 GdkWindowAttr mgl_attr;
963 GdkGeometry mgl_hints;
964
965 m->win_w=300; m->win_h=200;
966 m->zoom=2.0;
967
968 // gdk_flush();
969 gdk_window_get_root_origin(q->win, &m->frame_x, &m->frame_y);
970 // printf("frame %d %d\n", m->frame_x, m->frame_y);
971
972 mgl_attr.window_type=GDK_WINDOW_TOPLEVEL; // Set up attributes for GDK to create a Window
973 mgl_attr.wclass=GDK_INPUT_OUTPUT;
974 mgl_attr.event_mask=GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK;
975 mgl_attr.width=m->win_w;
976 mgl_attr.height=m->win_h;
977 mgl_attr.override_redirect=TRUE;
978 // m->win=gdk_window_new(NULL,&mgl_attr,GDK_WA_X|GDK_WA_Y|GDK_WA_WMCLASS);
979 m->win=gdk_window_new(NULL,&mgl_attr,GDK_WA_X|GDK_WA_Y);
980 mgl_hints.min_width=m->win_w;
981 mgl_hints.max_width=m->win_w;
982 mgl_hints.min_height=m->win_h;
983 mgl_hints.max_height=m->win_h;
984 gdk_window_set_geometry_hints(m->win, &mgl_hints,
985 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
986 gdk_window_set_decorations(m->win, GDK_DECOR_BORDER);
987 gdk_flush();
988 }
989
update_magnify(qiv_image * q,qiv_mgl * m,int mode,gint xcur,gint ycur)990 void update_magnify(qiv_image *q, qiv_mgl *m, int mode, gint xcur, gint ycur)
991 {
992 // GdkWindowAttr mgl_attr;
993 // GdkGeometry mgl_hints;
994 register gint xx, yy; // win_pos_x, win_pos_y;
995
996 /***********
997 if (mode == FULL_REDRAW) {
998 // printf("> update_magnify: FULL_REDRAW \n");
999 m->im=gdk_imlib_crop_and_clone_image(
1000 q->im, 0, 0, m->win_w, m->win_h);
1001 gdk_imlib_apply_image(m->im,m->win);
1002
1003 mgl_hints.min_width=m->win_w;
1004 mgl_hints.max_width=m->win_w;
1005 mgl_hints.min_height=m->win_h;
1006 mgl_hints.max_height=m->win_h;
1007
1008 // gdk_window_set_hints(m->win, mgl_attr.x, mgl_attr.y,mglw,
1009 // mglw,mglh, mglh, GDK_HINT_POS | GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
1010 gdk_window_set_hints(m->win, xcur+50, ycur-50-m->win_h,m->win_w,
1011 m->win_w,m->win_h, m->win_h, GDK_HINT_POS | GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
1012 // gdk_window_move_resize(m->win,mgl_attr.x, mgl_attr.y, mglw, mglh);
1013
1014 // gdk_window_set_geometry_hints(magnify_img.win, &mgl_hints, GDK_HINT_POS );
1015 }
1016 ************/
1017 if (mode == REDRAW ) {
1018
1019 /* scale position to original size */
1020 xx=xcur * ((double)q->orig_w/(double)q->win_w);
1021 yy=ycur * ((double)q->orig_h/(double)q->win_h);
1022
1023 /* center mouse cursor position */
1024 xx -= 150 / m->zoom;
1025 yy -= 100 / m->zoom;
1026
1027 /* keep magnify part allways inside image */
1028 if(xx + m->win_w / m->zoom > q->orig_w) {
1029 xx=q->orig_w - m->win_w / m->zoom;
1030 }
1031 if(yy + m->win_h / m->zoom > q->orig_h) {
1032 yy=q->orig_h - m->win_h / m->zoom;
1033 }
1034
1035
1036 setup_imlib_for_drawable(m->win);
1037 imlib_render_image_part_on_drawable_at_size(xx, yy, m->win_w / m->zoom , m->win_h / m->zoom,
1038 0, 0, m->win_w, m->win_h);
1039 setup_imlib_for_drawable(q->win);
1040 gdk_window_show(m->win);
1041
1042 xx= m->frame_x + xcur - 50 - m->win_w;
1043 yy= m->frame_y + ycur - 50 - m->win_h;
1044 if (xx < 0) {
1045 if (xcur < m->win_w - magnify_img.frame_x)
1046 xx=m->frame_x + xcur + 50;
1047 else
1048 xx=0;
1049 }
1050 if (yy < 0) {
1051 if (ycur < m->win_h - magnify_img.frame_y)
1052 yy=m->frame_y + ycur + 50;
1053 else
1054 yy=0;
1055 }
1056 // printf("MGL: m->frame_x: %d, m->frame_y: %d, xx: %d, yy: %d\n", m->frame_x, m->frame_y, xx, yy);
1057 gdk_window_move(m->win, xx, yy);
1058 }
1059 gdk_flush();
1060 }
1061
center_image(qiv_image * q)1062 void center_image(qiv_image *q)
1063 {
1064
1065 q->win_x = (monitor[q->mon_id].width - q->win_w) / 2;
1066 q->win_y = (monitor[q->mon_id].height - q->win_h) / 2;
1067 if(!fullscreen)
1068 {
1069 q->win_x += monitor[q->mon_id].x;
1070 q->win_y += monitor[q->mon_id].y;
1071 }
1072 }
1073
correct_image_position(qiv_image * q)1074 void correct_image_position(qiv_image *q)
1075 {
1076 // g_print("before: q->win_x = %d, q->win_y = %d, q->win_w = %d\n", q->win_x, q->win_y, q->win_w);
1077
1078 /* try to keep inside the screen */
1079 if (q->win_w < screen_x) {
1080 if (q->win_x < 0)
1081 q->win_x = 0;
1082 if (q->win_x + q->win_w > screen_x)
1083 q->win_x = screen_x - q->win_w;
1084 } else {
1085 if (q->win_x > 0)
1086 q->win_x = 0;
1087 if (q->win_x + q->win_w < screen_x)
1088 q->win_x = screen_x - q->win_w;
1089 }
1090
1091 /* don't leave ugly borders */
1092 if (q->win_h < screen_y) {
1093 if (q->win_y < 0)
1094 q->win_y = 0;
1095 if (q->win_y + q->win_h > screen_y)
1096 q->win_y = screen_y - q->win_h;
1097 } else {
1098 if (q->win_y > 0)
1099 q->win_y = 0;
1100 if (q->win_y + q->win_h < screen_y)
1101 q->win_y = screen_y - q->win_h;
1102 }
1103 // g_print("after: q->win_x = %d, q->win_y = %d, q->win_w = %d\n", q->win_x, q->win_y, q->win_w);
1104 }
1105