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