1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "main.h"
23 #include "pixbuf_util.h"
24 #include "exif.h"
25 #include "ui_fileops.h"
26 
27 #include "icons/icons_inline.h"
28 
29 #include <math.h>
30 
31 
32 /*
33  *-----------------------------------------------------------------------------
34  * png save
35  *-----------------------------------------------------------------------------
36  */
37 
pixbuf_to_file_as_png(GdkPixbuf * pixbuf,const gchar * filename)38 gboolean pixbuf_to_file_as_png(GdkPixbuf *pixbuf, const gchar *filename)
39 {
40 	GError *error = NULL;
41 	gboolean ret;
42 
43 	if (!pixbuf || !filename) return FALSE;
44 
45 	ret = gdk_pixbuf_save(pixbuf, filename, "png", &error,
46 			      "tEXt::Software", GQ_APPNAME " " VERSION, NULL);
47 
48 	if (error)
49 		{
50 		log_printf("Error saving png file: %s\n", error->message);
51 		g_error_free(error);
52 		}
53 
54 	return ret;
55 }
56 
57 /*
58  *-----------------------------------------------------------------------------
59  * jpeg save
60  *-----------------------------------------------------------------------------
61  */
62 
pixbuf_to_file_as_jpg(GdkPixbuf * pixbuf,const gchar * filename,gint quality)63 gboolean pixbuf_to_file_as_jpg(GdkPixbuf *pixbuf, const gchar *filename, gint quality)
64 {
65 	GError *error = NULL;
66 	gchar *qbuf;
67 	gboolean ret;
68 
69 	if (!pixbuf || !filename) return FALSE;
70 
71 	if (quality == -1) quality = 75;
72 	if (quality < 1 || quality > 100)
73 		{
74 		log_printf("Jpeg not saved, invalid quality %d\n", quality);
75 		return FALSE;
76 		}
77 
78 	qbuf = g_strdup_printf("%d", quality);
79 	ret = gdk_pixbuf_save(pixbuf, filename, "jpeg", &error, "quality", qbuf, NULL);
80 	g_free(qbuf);
81 
82 	if (error)
83 		{
84 		log_printf("Error saving jpeg to %s\n%s\n", filename, error->message);
85 		g_error_free(error);
86 		}
87 
88 	return ret;
89 }
90 
91 /*
92  *-----------------------------------------------------------------------------
93  * pixbuf from inline
94  *-----------------------------------------------------------------------------
95  */
96 
97 typedef struct _PixbufInline PixbufInline;
98 struct _PixbufInline
99 {
100 	const gchar *key;
101 	const guint8 *data;
102 };
103 
104 static PixbufInline inline_pixbuf_data[] = {
105 	{ PIXBUF_INLINE_FOLDER_CLOSED,	folder_closed },
106 	{ PIXBUF_INLINE_FOLDER_LOCKED,	folder_locked },
107 	{ PIXBUF_INLINE_FOLDER_OPEN,	folder_open },
108 	{ PIXBUF_INLINE_FOLDER_UP,	folder_up },
109 	{ PIXBUF_INLINE_SCROLLER,	icon_scroller },
110 	{ PIXBUF_INLINE_BROKEN,		icon_broken },
111 	{ PIXBUF_INLINE_METADATA,	icon_metadata },
112 	{ PIXBUF_INLINE_UNKNOWN,	icon_unknown },
113 	{ PIXBUF_INLINE_VIDEO,		icon_video },
114 	{ PIXBUF_INLINE_COLLECTION,	icon_collection },
115 	{ PIXBUF_INLINE_ICON,		gqview_icon },
116 	{ PIXBUF_INLINE_LOGO,		geeqie_logo },
117 	{ PIXBUF_INLINE_ICON_FLOAT,	icon_float },
118 	{ PIXBUF_INLINE_ICON_THUMB,	icon_thumb },
119 	{ PIXBUF_INLINE_ICON_BOOK,	icon_book },
120 	{ PIXBUF_INLINE_ICON_CONFIG,	icon_config },
121 	{ PIXBUF_INLINE_ICON_TOOLS,	icon_tools },
122 	{ PIXBUF_INLINE_ICON_VIEW,	icon_view },
123 	{ PIXBUF_INLINE_ICON_GUIDELINES,	icon_guidelines },
124 	{ PIXBUF_INLINE_ICON_PANORAMA,	icon_panorama },
125 	{ PIXBUF_INLINE_ICON_MAINTENANCE,	icon_maintenance },
126 	{ PIXBUF_INLINE_ICON_ZOOMFILLHOR,	icon_zoomfillhor },
127 	{ PIXBUF_INLINE_ICON_ZOOMFILLVERT,	icon_zoomfillvert },
128 	{ PIXBUF_INLINE_ICON_HIDETOOLS,	icon_hidetools },
129 	{ PIXBUF_INLINE_ICON_EXIF,	icon_exif },
130 	{ PIXBUF_INLINE_ICON_MARKS,	icon_marks },
131 	{ PIXBUF_INLINE_ICON_INFO,	icon_info },
132 	{ PIXBUF_INLINE_ICON_SORT,	icon_sort },
133 	{ PIXBUF_INLINE_ICON_PDF,	icon_pdf },
134 	{ PIXBUF_INLINE_ICON_DRAW_RECTANGLE,	icon_draw_rectangle },
135 	{ PIXBUF_INLINE_ICON_MOVE,	icon_move },
136 	{ PIXBUF_INLINE_ICON_RENAME,	icon_rename },
137 	{ PIXBUF_INLINE_ICON_SELECT_ALL,	icon_select_all },
138 	{ PIXBUF_INLINE_ICON_SELECT_NONE,	icon_select_none },
139 	{ PIXBUF_INLINE_ICON_SELECT_INVERT,	icon_select_invert },
140 	{ PIXBUF_INLINE_ICON_SELECT_RECTANGLE,	icon_select_rectangle },
141 	{ PIXBUF_INLINE_ICON_FILE_FILTER,	icon_file_filter },
142 	{ PIXBUF_INLINE_ICON_CW,	icon_rotate_clockwise },
143 	{ PIXBUF_INLINE_ICON_CCW,	icon_rotate_counter_clockwise },
144 	{ PIXBUF_INLINE_ICON_180,	icon_rotate_180 },
145 	{ PIXBUF_INLINE_ICON_MIRROR,	icon_mirror },
146 	{ PIXBUF_INLINE_ICON_FLIP,	icon_flip },
147 	{ PIXBUF_INLINE_ICON_ORIGINAL,	icon_original },
148 	{ PIXBUF_INLINE_ICON_TRASH,	icon_trash },
149 	{ PIXBUF_INLINE_ICON_HEIF,	icon_heic },
150 	{ PIXBUF_INLINE_ICON_GRAYSCALE,	icon_grayscale },
151 	{ PIXBUF_INLINE_ICON_EXPOSURE,	icon_exposure },
152 	{ PIXBUF_INLINE_SPLIT_PANE_SYNC, icon_split_pane_sync },
153 	{ NULL, NULL }
154 };
155 
pixbuf_inline(const gchar * key)156 GdkPixbuf *pixbuf_inline(const gchar *key)
157 {
158 	gint i;
159 
160 	if (!key) return NULL;
161 
162 	i = 0;
163 	while (inline_pixbuf_data[i].key)
164 		{
165 		if (strcmp(inline_pixbuf_data[i].key, key) == 0)
166 			{
167 			return gdk_pixbuf_new_from_inline(-1, inline_pixbuf_data[i].data, FALSE, NULL);
168 			}
169 		i++;
170 		}
171 
172 	log_printf("warning: inline pixbuf key \"%s\" not found.\n", key);
173 
174 	return NULL;
175 }
176 
register_stock_icon(const gchar * key,GdkPixbuf * pixbuf)177 static void register_stock_icon(const gchar *key, GdkPixbuf *pixbuf)
178 {
179 	static GtkIconFactory *icon_factory = NULL;
180 	GtkIconSet *icon_set;
181 
182 	if (!icon_factory)
183 		{
184 		icon_factory = gtk_icon_factory_new();
185 		gtk_icon_factory_add_default(icon_factory);
186 		}
187 
188 	icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
189 	gtk_icon_factory_add(icon_factory, key, icon_set);
190 }
191 
192 
pixbuf_inline_register_stock_icons(void)193 void pixbuf_inline_register_stock_icons(void)
194 {
195 	gint i;
196 
197 	i = 0;
198 	while (inline_pixbuf_data[i].key)
199 		{
200 		register_stock_icon(inline_pixbuf_data[i].key, pixbuf_inline(inline_pixbuf_data[i].key));
201 		i++;
202 		}
203 }
204 
register_theme_icon_as_stock(const gchar * key,const gchar * icon)205 gboolean register_theme_icon_as_stock(const gchar *key, const gchar *icon)
206 {
207 	GtkIconTheme *icon_theme;
208 	GdkPixbuf *pixbuf;
209 	GError *error = NULL;
210 
211 	icon_theme = gtk_icon_theme_get_default();
212 
213 	if (gtk_icon_theme_has_icon(icon_theme, key)) return FALSE;
214 
215 	pixbuf = gtk_icon_theme_load_icon(icon_theme,
216                            icon, /* icon name */
217                            64, /* size */
218                            0,  /* flags */
219                            &error);
220 	if (!pixbuf)
221 		{
222 		if (error)
223 			{
224 			DEBUG_1("Couldn't load icon %s: %s", icon, error->message);
225 			g_error_free(error);
226 			error = NULL;
227 			}
228 
229 		if (strchr(icon, '.'))
230 			{
231 			/* try again without extension */
232 			gchar *icon2 = remove_extension_from_path(icon);
233 			pixbuf = gtk_icon_theme_load_icon(icon_theme,
234 		                           icon2, /* icon name */
235 		                           64, /* size */
236 		                           0,  /* flags */
237 		                           &error);
238 			if (error)
239 				{
240 				DEBUG_1("Couldn't load icon %s: %s", icon2, error->message);
241 				g_error_free(error);
242 				error = NULL;
243 
244 				/* try as an absolute path */
245 				pixbuf = gdk_pixbuf_new_from_file(icon, &error);
246 				if (error)
247 					{
248 					DEBUG_1("Couldn't load icon as absolute path %s: %s", icon, error->message);
249 					g_error_free(error);
250 					}
251 				}
252 			g_free(icon2);
253 			}
254 		}
255 
256 	if (!pixbuf) return FALSE;
257 
258 	register_stock_icon(key, pixbuf);
259 	return TRUE;
260 }
261 
pixbuf_scale_aspect(gint req_w,gint req_h,gint old_w,gint old_h,gint * new_w,gint * new_h)262 gboolean pixbuf_scale_aspect(gint req_w, gint req_h,
263 			     gint old_w, gint old_h,
264 			     gint *new_w, gint *new_h)
265 {
266 	if (((gdouble)req_w / old_w) < ((gdouble)req_h / old_h))
267 		{
268 		*new_w = req_w;
269 		*new_h = (gdouble)*new_w / old_w * old_h;
270 		if (*new_h < 1) *new_h = 1;
271 		}
272 	else
273 		{
274 		*new_h = req_h;
275 		*new_w = (gdouble)*new_h / old_h * old_w;
276 		if (*new_w < 1) *new_w = 1;
277 		}
278 
279 	return (*new_w != old_w || *new_h != old_h);
280 }
281 
pixbuf_fallback(FileData * fd,gint requested_width,gint requested_height)282 GdkPixbuf *pixbuf_fallback(FileData *fd, gint requested_width, gint requested_height)
283 {
284 	GdkPixbuf *pixbuf;
285 
286 	switch (fd->format_class)
287 		{
288 		case FORMAT_CLASS_UNKNOWN:
289 			pixbuf = pixbuf_inline(PIXBUF_INLINE_UNKNOWN);
290 			break;
291 		case FORMAT_CLASS_META:
292 			pixbuf = pixbuf_inline(PIXBUF_INLINE_METADATA);
293 			break;
294 		case FORMAT_CLASS_VIDEO:
295 			pixbuf = pixbuf_inline(PIXBUF_INLINE_VIDEO);
296 			break;
297 		case FORMAT_CLASS_COLLECTION:
298 			pixbuf = pixbuf_inline(PIXBUF_INLINE_COLLECTION);
299 			break;
300 		case FORMAT_CLASS_DOCUMENT:
301 			pixbuf = pixbuf_inline(PIXBUF_INLINE_ICON_PDF);
302 			break;
303 		default:
304 			pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
305 		}
306 
307 	if (requested_width && requested_height)
308 		{
309 		gint w = gdk_pixbuf_get_width(pixbuf);
310 		gint h = gdk_pixbuf_get_height(pixbuf);
311 
312 		if (w > requested_width || h > requested_height)
313 			{
314 			gint nw, nh;
315 
316 			if (pixbuf_scale_aspect(requested_width, requested_height,
317 							  w, h, &nw, &nh))
318 				{
319 				GdkPixbuf *tmp;
320 
321 				tmp = pixbuf;
322 				pixbuf = gdk_pixbuf_scale_simple(tmp, nw, nh, GDK_INTERP_TILES);
323 				g_object_unref(G_OBJECT(tmp));
324 				}
325 			}
326 		}
327 	return pixbuf;
328 }
329 
330 
331 /*
332  *-----------------------------------------------------------------------------
333  * misc utils
334  *-----------------------------------------------------------------------------
335  */
336 
util_clip_region(gint x,gint y,gint w,gint h,gint clip_x,gint clip_y,gint clip_w,gint clip_h,gint * rx,gint * ry,gint * rw,gint * rh)337 gboolean util_clip_region(gint x, gint y, gint w, gint h,
338 			  gint clip_x, gint clip_y, gint clip_w, gint clip_h,
339 			  gint *rx, gint *ry, gint *rw, gint *rh)
340 {
341 	if (clip_x + clip_w <= x ||
342 	    clip_x >= x + w ||
343 	    clip_y + clip_h <= y ||
344 	    clip_y >= y + h)
345 		{
346 		return FALSE;
347 		}
348 
349 	*rx = MAX(x, clip_x);
350 	*rw = MIN((x + w), (clip_x + clip_w)) - *rx;
351 
352 	*ry = MAX(y, clip_y);
353 	*rh = MIN((y + h), (clip_y + clip_h)) - *ry;
354 
355 	return TRUE;
356 }
357 
358 /*
359  *-----------------------------------------------------------------------------
360  * pixbuf rotation
361  *-----------------------------------------------------------------------------
362  */
363 
pixbuf_copy_block_rotate(guchar * src,gint src_row_stride,gint x,gint y,guchar * dest,gint dest_row_stride,gint w,gint h,gint bytes_per_pixel,gboolean counter_clockwise)364 static void pixbuf_copy_block_rotate(guchar *src, gint src_row_stride, gint x, gint y,
365 				     guchar *dest, gint dest_row_stride, gint w, gint h,
366 				     gint bytes_per_pixel, gboolean counter_clockwise)
367 {
368 	gint i, j;
369 	guchar *sp;
370 	guchar *dp;
371 
372 	for (i = 0; i < h; i++)
373 		{
374 		sp = src + ((i + y) * src_row_stride) + (x * bytes_per_pixel);
375 		for (j = 0; j < w; j++)
376 			{
377 			if (counter_clockwise)
378 				{
379 				dp = dest + ((w - j - 1) * dest_row_stride) + (i * bytes_per_pixel);
380 				}
381 			else
382 				{
383 				dp = dest + (j * dest_row_stride) + ((h - i - 1) * bytes_per_pixel);
384 				}
385 			*(dp++) = *(sp++);	/* r */
386 			*(dp++) = *(sp++);	/* g */
387 			*(dp++) = *(sp++);	/* b */
388 			if (bytes_per_pixel == 4) *(dp) = *(sp++);	/* a */
389 			}
390 		}
391 
392 }
393 
pixbuf_copy_block(guchar * src,gint src_row_stride,gint w,gint h,guchar * dest,gint dest_row_stride,gint x,gint y,gint bytes_per_pixel)394 static void pixbuf_copy_block(guchar *src, gint src_row_stride, gint w, gint h,
395 			      guchar *dest, gint dest_row_stride, gint x, gint y, gint bytes_per_pixel)
396 {
397 	gint i;
398 	guchar *sp;
399 	guchar *dp;
400 
401 	for (i = 0; i < h; i++)
402 		{
403 		sp = src + (i * src_row_stride);
404 		dp = dest + ((y + i) * dest_row_stride) + (x * bytes_per_pixel);
405 		memcpy(dp, sp, w * bytes_per_pixel);
406 		}
407 }
408 
409 #define ROTATE_BUFFER_WIDTH 48
410 #define ROTATE_BUFFER_HEIGHT 48
411 
412 /*
413  * Returns a copy of pixbuf src rotated 90 degrees clockwise or 90 counterclockwise
414  *
415  */
pixbuf_copy_rotate_90(GdkPixbuf * src,gboolean counter_clockwise)416 GdkPixbuf *pixbuf_copy_rotate_90(GdkPixbuf *src, gboolean counter_clockwise)
417 {
418 	GdkPixbuf *dest;
419 	gboolean has_alpha;
420 	gint sw, sh, srs;
421 	gint dw, dh, drs;
422 	guchar *s_pix;
423 	guchar *d_pix;
424 	gint i, j;
425 	gint a;
426 	GdkPixbuf *buffer;
427 	guchar *b_pix;
428 	gint brs;
429 	gint w, h;
430 
431 	if (!src) return NULL;
432 
433 	sw = gdk_pixbuf_get_width(src);
434 	sh = gdk_pixbuf_get_height(src);
435 	has_alpha = gdk_pixbuf_get_has_alpha(src);
436 	srs = gdk_pixbuf_get_rowstride(src);
437 	s_pix = gdk_pixbuf_get_pixels(src);
438 
439 	dw = sh;
440 	dh = sw;
441 	dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, dw, dh);
442 	drs = gdk_pixbuf_get_rowstride(dest);
443 	d_pix = gdk_pixbuf_get_pixels(dest);
444 
445 	a = (has_alpha ? 4 : 3);
446 
447 	buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8,
448 				ROTATE_BUFFER_WIDTH, ROTATE_BUFFER_HEIGHT);
449 	b_pix = gdk_pixbuf_get_pixels(buffer);
450 	brs = gdk_pixbuf_get_rowstride(buffer);
451 
452 	for (i = 0; i < sh; i+= ROTATE_BUFFER_WIDTH)
453 		{
454 		w = MIN(ROTATE_BUFFER_WIDTH, (sh - i));
455 		for (j = 0; j < sw; j += ROTATE_BUFFER_HEIGHT)
456 			{
457 			gint x, y;
458 
459 			h = MIN(ROTATE_BUFFER_HEIGHT, (sw - j));
460 			pixbuf_copy_block_rotate(s_pix, srs, j, i,
461 						 b_pix, brs, h, w,
462 						 a, counter_clockwise);
463 
464 			if (counter_clockwise)
465 				{
466 				x = i;
467 				y = sw - h - j;
468 				}
469 			else
470 				{
471 				x = sh - w - i;
472 				y = j;
473 				}
474 			pixbuf_copy_block(b_pix, brs, w, h,
475 					  d_pix, drs, x, y, a);
476 			}
477 		}
478 
479 	g_object_unref(buffer);
480 
481 #if 0
482 	/* this is the simple version of rotation (roughly 2-4x slower) */
483 
484 	for (i = 0; i < sh; i++)
485 		{
486 		sp = s_pix + (i * srs);
487 		for (j = 0; j < sw; j++)
488 			{
489 			if (counter_clockwise)
490 				{
491 				dp = d_pix + ((dh - j - 1) * drs) + (i * a);
492 				}
493 			else
494 				{
495 				dp = d_pix + (j * drs) + ((dw - i - 1) * a);
496 				}
497 
498 			*(dp++) = *(sp++);	/* r */
499 			*(dp++) = *(sp++);	/* g */
500 			*(dp++) = *(sp++);	/* b */
501 			if (has_alpha) *(dp) = *(sp++);	/* a */
502 			}
503 		}
504 #endif
505 
506 	return dest;
507 }
508 
509 /*
510  * Returns a copy of pixbuf mirrored and or flipped.
511  * TO do a 180 degree rotations set both mirror and flipped TRUE
512  * if mirror and flip are FALSE, result is a simple copy.
513  */
pixbuf_copy_mirror(GdkPixbuf * src,gboolean mirror,gboolean flip)514 GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gboolean mirror, gboolean flip)
515 {
516 	GdkPixbuf *dest;
517 	gboolean has_alpha;
518 	gint w, h, srs;
519 	gint drs;
520 	guchar *s_pix;
521 	guchar *d_pix;
522 	guchar *sp;
523 	guchar *dp;
524 	gint i, j;
525 	gint a;
526 
527 	if (!src) return NULL;
528 
529 	w = gdk_pixbuf_get_width(src);
530 	h = gdk_pixbuf_get_height(src);
531 	has_alpha = gdk_pixbuf_get_has_alpha(src);
532 	srs = gdk_pixbuf_get_rowstride(src);
533 	s_pix = gdk_pixbuf_get_pixels(src);
534 
535 	dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, w, h);
536 	drs = gdk_pixbuf_get_rowstride(dest);
537 	d_pix = gdk_pixbuf_get_pixels(dest);
538 
539 	a = has_alpha ? 4 : 3;
540 
541 	for (i = 0; i < h; i++)
542 		{
543 		sp = s_pix + (i * srs);
544 		if (flip)
545 			{
546 			dp = d_pix + ((h - i - 1) * drs);
547 			}
548 		else
549 			{
550 			dp = d_pix + (i * drs);
551 			}
552 		if (mirror)
553 			{
554 			dp += (w - 1) * a;
555 			for (j = 0; j < w; j++)
556 				{
557 				*(dp++) = *(sp++);	/* r */
558 				*(dp++) = *(sp++);	/* g */
559 				*(dp++) = *(sp++);	/* b */
560 				if (has_alpha) *(dp) = *(sp++);	/* a */
561 				dp -= (a + 3);
562 				}
563 			}
564 		else
565 			{
566 			for (j = 0; j < w; j++)
567 				{
568 				*(dp++) = *(sp++);	/* r */
569 				*(dp++) = *(sp++);	/* g */
570 				*(dp++) = *(sp++);	/* b */
571 				if (has_alpha) *(dp++) = *(sp++);	/* a */
572 				}
573 			}
574 		}
575 
576 	return dest;
577 }
578 
pixbuf_apply_orientation(GdkPixbuf * pixbuf,gint orientation)579 GdkPixbuf *pixbuf_apply_orientation(GdkPixbuf *pixbuf, gint orientation)
580 {
581 	GdkPixbuf *dest;
582 	GdkPixbuf *tmp = NULL;
583 
584 	switch (orientation)
585 		{
586 		case EXIF_ORIENTATION_TOP_LEFT:
587 			dest = gdk_pixbuf_copy(pixbuf);
588 			break;
589 		case EXIF_ORIENTATION_TOP_RIGHT:
590 			/* mirrored */
591 			dest = pixbuf_copy_mirror(pixbuf, TRUE, FALSE);
592 			break;
593 		case EXIF_ORIENTATION_BOTTOM_RIGHT:
594 			/* upside down */
595 			dest = pixbuf_copy_mirror(pixbuf, TRUE, TRUE);
596 			break;
597 		case EXIF_ORIENTATION_BOTTOM_LEFT:
598 			/* flipped */
599 			dest = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
600 			break;
601 		case EXIF_ORIENTATION_LEFT_TOP:
602 			tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
603 			dest = pixbuf_copy_rotate_90(tmp, FALSE);
604 			break;
605 		case EXIF_ORIENTATION_RIGHT_TOP:
606 			/* rotated -90 (270) */
607 			dest = pixbuf_copy_rotate_90(pixbuf, FALSE);
608 			break;
609 		case EXIF_ORIENTATION_RIGHT_BOTTOM:
610 			tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
611 			dest = pixbuf_copy_rotate_90(tmp, TRUE);
612 			break;
613 		case EXIF_ORIENTATION_LEFT_BOTTOM:
614 			/* rotated 90 */
615 			dest = pixbuf_copy_rotate_90(pixbuf, TRUE);
616 			break;
617 		default:
618 			dest = gdk_pixbuf_copy(pixbuf);
619 			break;
620 		}
621 	if (tmp) g_object_unref(tmp);
622 	return dest;
623 
624 }
625 
626 
627 /*
628  *-----------------------------------------------------------------------------
629  * pixbuf drawing (rectangles)
630  *-----------------------------------------------------------------------------
631  */
632 
633 /*
634  * Fills region of pixbuf at x,y over w,h
635  * with colors red (r), green (g), blue (b)
636  * applying alpha (a), use a=255 for solid.
637  */
pixbuf_draw_rect_fill(GdkPixbuf * pb,gint x,gint y,gint w,gint h,gint r,gint g,gint b,gint a)638 void pixbuf_draw_rect_fill(GdkPixbuf *pb,
639 			   gint x, gint y, gint w, gint h,
640 			   gint r, gint g, gint b, gint a)
641 {
642 	gboolean has_alpha;
643 	gint pw, ph, prs;
644 	guchar *p_pix;
645 	guchar *pp;
646 	gint i, j;
647 
648 	if (!pb) return;
649 
650 	pw = gdk_pixbuf_get_width(pb);
651 	ph = gdk_pixbuf_get_height(pb);
652 
653 	if (x < 0 || x + w > pw) return;
654 	if (y < 0 || y + h > ph) return;
655 
656 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
657 	prs = gdk_pixbuf_get_rowstride(pb);
658 	p_pix = gdk_pixbuf_get_pixels(pb);
659 
660 	for (i = 0; i < h; i++)
661 		{
662 		pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
663 		for (j = 0; j < w; j++)
664 			{
665 			*pp = (r * a + *pp * (256-a)) >> 8;
666 			pp++;
667 			*pp = (g * a + *pp * (256-a)) >> 8;
668 			pp++;
669 			*pp = (b * a + *pp * (256-a)) >> 8;
670 			pp++;
671 			if (has_alpha) pp++;
672 			}
673 		}
674 }
675 
pixbuf_draw_rect(GdkPixbuf * pb,gint x,gint y,gint w,gint h,gint r,gint g,gint b,gint a,gint left,gint right,gint top,gint bottom)676 void pixbuf_draw_rect(GdkPixbuf *pb,
677 		      gint x, gint y, gint w, gint h,
678 		      gint r, gint g, gint b, gint a,
679 		      gint left, gint right, gint top, gint bottom)
680 {
681 	pixbuf_draw_rect_fill(pb, x + left, y, w - left - right, top,
682 			      r, g, b ,a);
683 	pixbuf_draw_rect_fill(pb, x + w - right, y, right, h,
684 			      r, g, b ,a);
685 	pixbuf_draw_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
686 			      r, g, b ,a);
687 	pixbuf_draw_rect_fill(pb, x, y, left, h,
688 			      r, g, b ,a);
689 }
690 
pixbuf_set_rect_fill(GdkPixbuf * pb,gint x,gint y,gint w,gint h,gint r,gint g,gint b,gint a)691 void pixbuf_set_rect_fill(GdkPixbuf *pb,
692 			  gint x, gint y, gint w, gint h,
693 			  gint r, gint g, gint b, gint a)
694 {
695 	gboolean has_alpha;
696 	gint pw, ph, prs;
697 	guchar *p_pix;
698 	guchar *pp;
699 	gint i, j;
700 
701 	if (!pb) return;
702 
703 	pw = gdk_pixbuf_get_width(pb);
704 	ph = gdk_pixbuf_get_height(pb);
705 
706 	if (x < 0 || x + w > pw) return;
707 	if (y < 0 || y + h > ph) return;
708 
709 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
710 	prs = gdk_pixbuf_get_rowstride(pb);
711 	p_pix = gdk_pixbuf_get_pixels(pb);
712 
713 	for (i = 0; i < h; i++)
714 		{
715 		pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
716 		for (j = 0; j < w; j++)
717 			{
718 			*pp = r; pp++;
719 			*pp = g; pp++;
720 			*pp = b; pp++;
721 			if (has_alpha) { *pp = a; pp++; }
722 			}
723 		}
724 }
725 
pixbuf_set_rect(GdkPixbuf * pb,gint x,gint y,gint w,gint h,gint r,gint g,gint b,gint a,gint left,gint right,gint top,gint bottom)726 void pixbuf_set_rect(GdkPixbuf *pb,
727 		     gint x, gint y, gint w, gint h,
728 		     gint r, gint g, gint b, gint a,
729 		     gint left, gint right, gint top, gint bottom)
730 {
731 	pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
732 			     r, g, b ,a);
733 	pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
734 			     r, g, b ,a);
735 	pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
736 			     r, g, b ,a);
737 	pixbuf_set_rect_fill(pb, x, y, left, h,
738 			     r, g, b ,a);
739 }
740 
pixbuf_pixel_set(GdkPixbuf * pb,gint x,gint y,gint r,gint g,gint b,gint a)741 void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a)
742 {
743 	guchar *buf;
744 	gboolean has_alpha;
745 	gint rowstride;
746 	guchar *p;
747 
748 	if (x < 0 || x >= gdk_pixbuf_get_width(pb) ||
749 	    y < 0 || y >= gdk_pixbuf_get_height(pb)) return;
750 
751 	buf = gdk_pixbuf_get_pixels(pb);
752 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
753 	rowstride = gdk_pixbuf_get_rowstride(pb);
754 
755 	p = buf + (y * rowstride) + (x * (has_alpha ? 4 : 3));
756 	*p = r; p++;
757 	*p = g; p++;
758 	*p = b; p++;
759 	if (has_alpha) *p = a;
760 }
761 
762 
763 /*
764  *-----------------------------------------------------------------------------
765  * pixbuf text rendering
766  *-----------------------------------------------------------------------------
767  */
768 
pixbuf_copy_font(GdkPixbuf * src,gint sx,gint sy,GdkPixbuf * dest,gint dx,gint dy,gint w,gint h,guint8 r,guint8 g,guint8 b,guint8 a)769 static void pixbuf_copy_font(GdkPixbuf *src, gint sx, gint sy,
770 			     GdkPixbuf *dest, gint dx, gint dy,
771 			     gint w, gint h,
772 			     guint8 r, guint8 g, guint8 b, guint8 a)
773 {
774 	gint sw, sh, srs;
775 	gboolean s_alpha;
776 	gint s_step;
777 	guchar *s_pix;
778 	gint dw, dh, drs;
779 	gboolean d_alpha;
780 	gint d_step;
781 	guchar *d_pix;
782 
783 	guchar *sp;
784 	guchar *dp;
785 	gint i, j;
786 
787 	if (!src || !dest) return;
788 
789 	sw = gdk_pixbuf_get_width(src);
790 	sh = gdk_pixbuf_get_height(src);
791 
792 	if (sx < 0 || sx + w > sw) return;
793 	if (sy < 0 || sy + h > sh) return;
794 
795 	dw = gdk_pixbuf_get_width(dest);
796 	dh = gdk_pixbuf_get_height(dest);
797 
798 	if (dx < 0 || dx + w > dw) return;
799 	if (dy < 0 || dy + h > dh) return;
800 
801 	s_alpha = gdk_pixbuf_get_has_alpha(src);
802 	d_alpha = gdk_pixbuf_get_has_alpha(dest);
803 	srs = gdk_pixbuf_get_rowstride(src);
804 	drs = gdk_pixbuf_get_rowstride(dest);
805 	s_pix = gdk_pixbuf_get_pixels(src);
806 	d_pix = gdk_pixbuf_get_pixels(dest);
807 
808 	s_step = (s_alpha) ? 4 : 3;
809 	d_step = (d_alpha) ? 4 : 3;
810 
811 	for (i = 0; i < h; i++)
812 		{
813 		sp = s_pix + (sy + i) * srs + sx * s_step;
814 		dp = d_pix + (dy + i) * drs + dx * d_step;
815 		for (j = 0; j < w; j++)
816 			{
817 			if (*sp)
818 				{
819 				guint8 asub;
820 
821 				asub = a * sp[0] / 255;
822 				*dp = (r * asub + *dp * (256-asub)) >> 8;
823 				dp++;
824 				asub = a * sp[1] / 255;
825 				*dp = (g * asub + *dp * (256-asub)) >> 8;
826 				dp++;
827 				asub = a * sp[2] / 255;
828 				*dp = (b * asub + *dp * (256-asub)) >> 8;
829 				dp++;
830 
831 				if (d_alpha)
832 					{
833 					*dp = MAX(*dp, a * ((sp[0] + sp[1] + sp[2]) / 3) / 255);
834 					dp++;
835 					}
836 				}
837 			else
838 				{
839 				dp += d_step;
840 				}
841 
842 			sp += s_step;
843 			}
844 		}
845 }
846 
pixbuf_draw_layout(GdkPixbuf * pixbuf,PangoLayout * layout,GtkWidget * widget,gint x,gint y,guint8 r,guint8 g,guint8 b,guint8 a)847 void pixbuf_draw_layout(GdkPixbuf *pixbuf, PangoLayout *layout, GtkWidget *widget,
848 			gint x, gint y,
849 			guint8 r, guint8 g, guint8 b, guint8 a)
850 {
851 	GdkPixbuf *buffer;
852 	gint w, h;
853 	gint sx, sy;
854 	gint dw, dh;
855 	cairo_surface_t *source;
856 	cairo_t *cr;
857 
858 	pango_layout_get_pixel_size(layout, &w, &h);
859 	if (w < 1 || h < 1) return;
860 
861 	source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
862 
863 	cr = cairo_create (source);
864 	cairo_set_source_rgb(cr, 0, 0, 0);
865 	cairo_rectangle (cr, 0, 0, w, h);
866 	cairo_fill (cr);
867 	cairo_set_source_rgb(cr, 1, 1, 1);
868 	pango_cairo_show_layout (cr, layout);
869 	cairo_destroy (cr);
870 
871 	buffer = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (source),
872 	                                   GDK_COLORSPACE_RGB,
873 	                                   cairo_image_surface_get_format (source) == CAIRO_FORMAT_ARGB32,
874 	                                   8,
875 	                                   cairo_image_surface_get_width (source),
876 	                                   cairo_image_surface_get_height (source),
877 	                                   cairo_image_surface_get_stride (source),
878 	                                   NULL,
879 	                                   NULL);
880 
881 	sx = 0;
882 	sy = 0;
883 	dw = gdk_pixbuf_get_width(pixbuf);
884 	dh = gdk_pixbuf_get_height(pixbuf);
885 
886 	if (x < 0)
887 		{
888 		w += x;
889 		sx = -x;
890 		x = 0;
891 		}
892 
893 	if (y < 0)
894 		{
895 		h += y;
896 		sy = -y;
897 		y = 0;
898 		}
899 
900 	if (x + w > dw)	w = dw - x;
901 	if (y + h > dh) h = dh - y;
902 
903 	pixbuf_copy_font(buffer, sx, sy,
904 			 pixbuf, x, y, w, h,
905 			 r, g, b, a);
906 
907 	g_object_unref(buffer);
908 	cairo_surface_destroy(source);
909 }
910 
911 /*
912  *-----------------------------------------------------------------------------
913  * pixbuf drawing (triangle)
914  *-----------------------------------------------------------------------------
915  */
916 
util_clip_triangle(gint x1,gint y1,gint x2,gint y2,gint x3,gint y3,gint * rx,gint * ry,gint * rw,gint * rh)917 void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
918 			gint *rx, gint *ry, gint *rw, gint *rh)
919 {
920 	gint tx, ty, tw, th;
921 
922 	tx = MIN(x1, x2);
923 	tx = MIN(tx, x3);
924 	ty = MIN(y1, y2);
925 	ty = MIN(ty, y3);
926 	tw = MAX(abs(x1 - x2), abs(x2 - x3));
927 	tw = MAX(tw, abs(x3 - x1));
928 	th = MAX(abs(y1 - y2), abs(y2 - y3));
929 	th = MAX(th, abs(y3 - y1));
930 
931 	*rx = tx;
932 	*ry = ty;
933 	*rw = tw;
934 	*rh = th;
935 }
936 
pixbuf_draw_triangle(GdkPixbuf * pb,gint clip_x,gint clip_y,gint clip_w,gint clip_h,gint x1,gint y1,gint x2,gint y2,gint x3,gint y3,guint8 r,guint8 g,guint8 b,guint8 a)937 void pixbuf_draw_triangle(GdkPixbuf *pb,
938 			  gint clip_x, gint clip_y, gint clip_w, gint clip_h,
939 			  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
940 			  guint8 r, guint8 g, guint8 b, guint8 a)
941 {
942 	gboolean has_alpha;
943 	gint pw, ph, prs;
944 	gint rx, ry, rw, rh;
945 	gint tx, ty, tw, th;
946 	gint fx1, fy1;
947 	gint fx2, fy2;
948 	gint fw, fh;
949 	guchar *p_pix;
950 	guchar *pp;
951 	gint p_step;
952 	gdouble slope1, slope2;
953 	gint slope1_x, slope1_y;
954 	gint y;
955 	gint t;
956 	gboolean middle = FALSE;
957 
958 	if (!pb) return;
959 
960 	pw = gdk_pixbuf_get_width(pb);
961 	ph = gdk_pixbuf_get_height(pb);
962 
963 	if (!util_clip_region(0, 0, pw, ph,
964 			      clip_x, clip_y, clip_w, clip_h,
965 			      &rx, &ry, &rw, &rh)) return;
966 
967 	util_clip_triangle(x1, y1, x2, y2, x3, y3,
968 			   &tx, &ty, &tw, &th);
969 
970 	if (!util_clip_region(rx, ry, rw, rh,
971 			      tx, ty, tw, th,
972 			      &fx1, &fy1, &fw, &fh)) return;
973 	fx2 = fx1 + fw;
974 	fy2 = fy1 + fh;
975 
976 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
977 	prs = gdk_pixbuf_get_rowstride(pb);
978 	p_pix = gdk_pixbuf_get_pixels(pb);
979 
980 	p_step = (has_alpha) ? 4 : 3;
981 
982 	if (y1 > y2)
983 		{
984 		t = x1; x1 = x2; x2 = t;
985 		t = y1; y1 = y2; y2 = t;
986 		}
987 	if (y2 > y3)
988 		{
989 		t = x2; x2 = x3; x3 = t;
990 		t = y2; y2 = y3; y3 = t;
991 		}
992 	if (y1 > y2)
993 		{
994 		t = x1; x1 = x2; x2 = t;
995 		t = y1; y1 = y2; y2 = t;
996 		}
997 
998 	slope1 = (gdouble)(y2 - y1);
999 	if (slope1) slope1 = (gdouble)(x2 - x1) / slope1;
1000 	slope1_x = x1;
1001 	slope1_y = y1;
1002 	slope2 = (gdouble)(y3 - y1);
1003 	if (slope2) slope2 = (gdouble)(x3 - x1) / slope2;
1004 
1005 	for (y = fy1; y < fy2; y++)
1006 		{
1007 		gint xa, xb;
1008 
1009 		if (!middle && y > y2)
1010 			{
1011 			slope1 = (gdouble)(y3 - y2);
1012 			if (slope1) slope1 = (gdouble)(x3 - x2) / slope1;
1013 			slope1_x = x2;
1014 			slope1_y = y2;
1015 
1016 			middle = TRUE;
1017 			}
1018 
1019 		xa = slope1_x + ((gdouble)slope1 * (y - slope1_y) + 0.5);
1020 		xb = x1 + ((gdouble)slope2 * (y - y1) + 0.5);
1021 
1022 		if (xa > xb)
1023 			{
1024 			t = xa; xa = xb; xb = t;
1025 			}
1026 
1027 		xa = CLAMP(xa, fx1, fx2);
1028 		xb = CLAMP(xb, fx1, fx2);
1029 
1030 		pp = p_pix + y * prs + xa * p_step;
1031 
1032 		while (xa < xb)
1033 			{
1034 			*pp = (r * a + *pp * (256-a)) >> 8;
1035 			pp++;
1036 			*pp = (g * a + *pp * (256-a)) >> 8;
1037 			pp++;
1038 			*pp = (b * a + *pp * (256-a)) >> 8;
1039 			pp++;
1040 			if (has_alpha) pp++;
1041 
1042 			xa++;
1043 			}
1044 		}
1045 }
1046 
1047 /*
1048  *-----------------------------------------------------------------------------
1049  * pixbuf drawing (line)
1050  *-----------------------------------------------------------------------------
1051  */
1052 
util_clip_line(gdouble clip_x,gdouble clip_y,gdouble clip_w,gdouble clip_h,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * rx1,gdouble * ry1,gdouble * rx2,gdouble * ry2)1053 static gboolean util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
1054 			       gdouble x1, gdouble y1, gdouble x2, gdouble y2,
1055 			       gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
1056 {
1057 	gboolean flip = FALSE;
1058 	gdouble d;
1059 
1060 	if (x1 > x2)
1061 		{
1062 		gdouble t;
1063 
1064 		t = x1; x1 = x2; x2 = t;
1065 		t = y1; y1 = y2; y2 = t;
1066 		flip = TRUE;
1067 		}
1068 
1069 	if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
1070 
1071 	if (y1 < y2)
1072 		{
1073 		if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1074 		}
1075 	else
1076 		{
1077 		if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1078 		}
1079 
1080 	d = x2 - x1;
1081 	if (d > 0.0)
1082 		{
1083 		gdouble slope;
1084 
1085 		slope = (y2 - y1) / d;
1086 		if (x1 < clip_x)
1087 			{
1088 			y1 = y1 + slope * (clip_x - x1);
1089 			x1 = clip_x;
1090 			}
1091 		if (x2 > clip_x + clip_w)
1092 			{
1093 			y2 = y2 + slope * (clip_x + clip_w - x2);
1094 			x2 = clip_x + clip_w;
1095 			}
1096 		}
1097 
1098 	if (y1 < y2)
1099 		{
1100 		if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1101 		}
1102 	else
1103 		{
1104 		gdouble t;
1105 
1106 		if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1107 
1108 		t = x1; x1 = x2; x2 = t;
1109 		t = y1; y1 = y2; y2 = t;
1110 		flip = !flip;
1111 		}
1112 
1113 	d = y2 - y1;
1114 	if (d > 0.0)
1115 		{
1116 		gdouble slope;
1117 
1118 		slope = (x2 - x1) / d;
1119 		if (y1 < clip_y)
1120 			{
1121 			x1 = x1 + slope * (clip_y - y1);
1122 			y1 = clip_y;
1123 			}
1124 		if (y2 > clip_y + clip_h)
1125 			{
1126 			x2 = x2 + slope * (clip_y + clip_h - y2);
1127 			y2 = clip_y + clip_h;
1128 			}
1129 		}
1130 
1131 	if (flip)
1132 		{
1133 		*rx1 = x2;
1134 		*ry1 = y2;
1135 		*rx2 = x1;
1136 		*ry2 = y1;
1137 		}
1138 	else
1139 		{
1140 		*rx1 = x1;
1141 		*ry1 = y1;
1142 		*rx2 = x2;
1143 		*ry2 = y2;
1144 		}
1145 
1146 	return TRUE;
1147 }
1148 
pixbuf_draw_line(GdkPixbuf * pb,gint clip_x,gint clip_y,gint clip_w,gint clip_h,gint x1,gint y1,gint x2,gint y2,guint8 r,guint8 g,guint8 b,guint8 a)1149 void pixbuf_draw_line(GdkPixbuf *pb,
1150 		      gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1151 		      gint x1, gint y1, gint x2, gint y2,
1152 		      guint8 r, guint8 g, guint8 b, guint8 a)
1153 {
1154 	gboolean has_alpha;
1155 	gint pw, ph, prs;
1156 	gint rx, ry, rw, rh;
1157 	gdouble rx1, ry1, rx2, ry2;
1158 	guchar *p_pix;
1159 	guchar *pp;
1160 	gint p_step;
1161 	gdouble slope;
1162 	gdouble x, y;
1163 	gint px, py;
1164 	gint cx1, cy1, cx2, cy2;
1165 
1166 	if (!pb) return;
1167 
1168 	pw = gdk_pixbuf_get_width(pb);
1169 	ph = gdk_pixbuf_get_height(pb);
1170 
1171 	if (!util_clip_region(0, 0, pw, ph,
1172 			      clip_x, clip_y, clip_w, clip_h,
1173 			      &rx, &ry, &rw, &rh)) return;
1174 	if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
1175 			    (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
1176 			    &rx1, &ry1, &rx2, &ry2)) return;
1177 
1178 	cx1 = rx;
1179 	cy1 = ry;
1180 	cx2 = rx + rw;
1181 	cy2 = ry + rh;
1182 
1183 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
1184 	prs = gdk_pixbuf_get_rowstride(pb);
1185 	p_pix = gdk_pixbuf_get_pixels(pb);
1186 
1187 	p_step = (has_alpha) ? 4 : 3;
1188 
1189 	if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
1190 		{
1191 		if (rx1 > rx2)
1192 			{
1193 			gdouble t;
1194 			t = rx1; rx1 = rx2; rx2 = t;
1195 			t = ry1; ry1 = ry2; ry2 = t;
1196 			}
1197 
1198 		slope = rx2 - rx1;
1199 		if (slope != 0.0) slope = (ry2 - ry1) / slope;
1200 		for (x = rx1; x < rx2; x += 1.0)
1201 			{
1202 			px = (gint)(x + 0.5);
1203 			py = (gint)(ry1 + (x - rx1) * slope + 0.5);
1204 
1205 			if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1206 				{
1207 				pp = p_pix + py * prs + px * p_step;
1208 				*pp = (r * a + *pp * (256-a)) >> 8;
1209 				pp++;
1210 				*pp = (g * a + *pp * (256-a)) >> 8;
1211 				pp++;
1212 				*pp = (b * a + *pp * (256-a)) >> 8;
1213 				}
1214 			}
1215 		}
1216 	else
1217 		{
1218 		if (ry1 > ry2)
1219 			{
1220 			gdouble t;
1221 			t = rx1; rx1 = rx2; rx2 = t;
1222 			t = ry1; ry1 = ry2; ry2 = t;
1223 			}
1224 
1225 		slope = ry2 - ry1;
1226 		if (slope != 0.0) slope = (rx2 - rx1) / slope;
1227 		for (y = ry1; y < ry2; y += 1.0)
1228 			{
1229 			px = (gint)(rx1 + (y - ry1) * slope + 0.5);
1230 			py = (gint)(y + 0.5);
1231 
1232 			if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1233 				{
1234 				pp = p_pix + py * prs + px * p_step;
1235 				*pp = (r * a + *pp * (256-a)) >> 8;
1236 				pp++;
1237 				*pp = (g * a + *pp * (256-a)) >> 8;
1238 				pp++;
1239 				*pp = (b * a + *pp * (256-a)) >> 8;
1240 				}
1241 			}
1242 		}
1243 }
1244 
1245 /*
1246  *-----------------------------------------------------------------------------
1247  * pixbuf drawing (fades and shadows)
1248  *-----------------------------------------------------------------------------
1249  */
1250 
pixbuf_draw_fade_linear(guchar * p_pix,gint prs,gboolean has_alpha,gint s,gboolean vertical,gint border,gint x1,gint y1,gint x2,gint y2,guint8 r,guint8 g,guint8 b,guint8 a)1251 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gboolean has_alpha,
1252 				    gint s, gboolean vertical, gint border,
1253 				    gint x1, gint y1, gint x2, gint y2,
1254 				    guint8 r, guint8 g, guint8 b, guint8 a)
1255 {
1256 	guchar *pp;
1257 	gint p_step;
1258 	guint8 n = a;
1259 	gint i, j;
1260 
1261 	p_step = (has_alpha) ? 4 : 3;
1262 	for (j = y1; j < y2; j++)
1263 		{
1264 		pp = p_pix + j * prs + x1 * p_step;
1265 		if (!vertical) n = a - a * abs(j - s) / border;
1266 		for (i = x1; i < x2; i++)
1267 			{
1268 			if (vertical) n = a - a * abs(i - s) / border;
1269 			*pp = (r * n + *pp * (256-n)) >> 8;
1270 			pp++;
1271 			*pp = (g * n + *pp * (256-n)) >> 8;
1272 			pp++;
1273 			*pp = (b * n + *pp * (256-n)) >> 8;
1274 			pp++;
1275 			if (has_alpha) pp++;
1276 			}
1277 		}
1278 }
1279 
pixbuf_draw_fade_radius(guchar * p_pix,gint prs,gboolean has_alpha,gint sx,gint sy,gint border,gint x1,gint y1,gint x2,gint y2,guint8 r,guint8 g,guint8 b,guint8 a)1280 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gboolean has_alpha,
1281 				    gint sx, gint sy, gint border,
1282 				    gint x1, gint y1, gint x2, gint y2,
1283 				    guint8 r, guint8 g, guint8 b, guint8 a)
1284 {
1285 	guchar *pp;
1286 	gint p_step;
1287 	gint i, j;
1288 
1289 	p_step = (has_alpha) ? 4 : 3;
1290 	for (j = y1; j < y2; j++)
1291 		{
1292 		pp = p_pix + j * prs + x1 * p_step;
1293 		for (i = x1; i < x2; i++)
1294 			{
1295 			guint8 n;
1296 			gint r;
1297 
1298 			r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
1299 			n = a - a * r / border;
1300 			*pp = (r * n + *pp * (256-n)) >> 8;
1301 			pp++;
1302 			*pp = (g * n + *pp * (256-n)) >> 8;
1303 			pp++;
1304 			*pp = (b * n + *pp * (256-n)) >> 8;
1305 			pp++;
1306 			if (has_alpha) pp++;
1307 			}
1308 		}
1309 }
1310 
pixbuf_draw_shadow(GdkPixbuf * pb,gint clip_x,gint clip_y,gint clip_w,gint clip_h,gint x,gint y,gint w,gint h,gint border,guint8 r,guint8 g,guint8 b,guint8 a)1311 void pixbuf_draw_shadow(GdkPixbuf *pb,
1312 			gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1313 			gint x, gint y, gint w, gint h, gint border,
1314 			guint8 r, guint8 g, guint8 b, guint8 a)
1315 {
1316 	gint has_alpha;
1317 	gint pw, ph, prs;
1318 	gint rx, ry, rw, rh;
1319 	gint fx, fy, fw, fh;
1320 	guchar *p_pix;
1321 
1322 	if (!pb) return;
1323 
1324 	pw = gdk_pixbuf_get_width(pb);
1325 	ph = gdk_pixbuf_get_height(pb);
1326 
1327 	if (!util_clip_region(0, 0, pw, ph,
1328 			      clip_x, clip_y, clip_w, clip_h,
1329 			      &rx, &ry, &rw, &rh)) return;
1330 
1331 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
1332 	prs = gdk_pixbuf_get_rowstride(pb);
1333 	p_pix = gdk_pixbuf_get_pixels(pb);
1334 
1335 	if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
1336 			     rx, ry, rw, rh,
1337 			     &fx, &fy, &fw, &fh))
1338 		{
1339 		pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
1340 		}
1341 
1342 	if (border < 1) return;
1343 
1344 	if (util_clip_region(x, y + border, border, h - border * 2,
1345 			     rx, ry, rw, rh,
1346 			     &fx, &fy, &fw, &fh))
1347 		{
1348 		pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1349 					x + border, TRUE, border,
1350 					fx, fy, fx + fw, fy + fh,
1351 					r, g, b, a);
1352 		}
1353 	if (util_clip_region(x + w - border, y + border, border, h - border * 2,
1354 			     rx, ry, rw, rh,
1355 			     &fx, &fy, &fw, &fh))
1356 		{
1357 		pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1358 					x + w - border, TRUE, border,
1359 					fx, fy, fx + fw, fy + fh,
1360 					r, g, b, a);
1361 		}
1362 	if (util_clip_region(x + border, y, w - border * 2, border,
1363 			     rx, ry, rw, rh,
1364 			     &fx, &fy, &fw, &fh))
1365 		{
1366 		pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1367 					y + border, FALSE, border,
1368 					fx, fy, fx + fw, fy + fh,
1369 					r, g, b, a);
1370 		}
1371 	if (util_clip_region(x + border, y + h - border, w - border * 2, border,
1372 			     rx, ry, rw, rh,
1373 			     &fx, &fy, &fw, &fh))
1374 		{
1375 		pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1376 					y + h - border, FALSE, border,
1377 					fx, fy, fx + fw, fy + fh,
1378 					r, g, b, a);
1379 		}
1380 	if (util_clip_region(x, y, border, border,
1381 			     rx, ry, rw, rh,
1382 			     &fx, &fy, &fw, &fh))
1383 		{
1384 		pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1385 					x + border, y + border, border,
1386 					fx, fy, fx + fw, fy + fh,
1387 					r, g, b, a);
1388 		}
1389 	if (util_clip_region(x + w - border, y, border, border,
1390 			     rx, ry, rw, rh,
1391 			     &fx, &fy, &fw, &fh))
1392 		{
1393 		pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1394 					x + w - border, y + border, border,
1395 					fx, fy, fx + fw, fy + fh,
1396 					r, g, b, a);
1397 		}
1398 	if (util_clip_region(x, y + h - border, border, border,
1399 			     rx, ry, rw, rh,
1400 			     &fx, &fy, &fw, &fh))
1401 		{
1402 		pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1403 					x + border, y + h - border, border,
1404 					fx, fy, fx + fw, fy + fh,
1405 					r, g, b, a);
1406 		}
1407 	if (util_clip_region(x + w - border, y + h - border, border, border,
1408 			     rx, ry, rw, rh,
1409 			     &fx, &fy, &fw, &fh))
1410 		{
1411 		pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1412 					x + w - border, y + h - border, border,
1413 					fx, fy, fx + fw, fy + fh,
1414 					r, g, b, a);
1415 		}
1416 }
1417 
1418 
1419 /*
1420  *-----------------------------------------------------------------------------
1421  * pixbuf color alterations
1422  *-----------------------------------------------------------------------------
1423  */
1424 
pixbuf_desaturate_rect(GdkPixbuf * pb,gint x,gint y,gint w,gint h)1425 void pixbuf_desaturate_rect(GdkPixbuf *pb,
1426 			    gint x, gint y, gint w, gint h)
1427 {
1428 	gboolean has_alpha;
1429 	gint pw, ph, prs;
1430 	guchar *p_pix;
1431 	guchar *pp;
1432 	gint i, j;
1433 
1434 	if (!pb) return;
1435 
1436 	pw = gdk_pixbuf_get_width(pb);
1437 	ph = gdk_pixbuf_get_height(pb);
1438 
1439 	if (x < 0 || x + w > pw) return;
1440 	if (y < 0 || y + h > ph) return;
1441 
1442 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
1443 	prs = gdk_pixbuf_get_rowstride(pb);
1444 	p_pix = gdk_pixbuf_get_pixels(pb);
1445 
1446 	for (i = 0; i < h; i++)
1447 		{
1448 		pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1449 		for (j = 0; j < w; j++)
1450 			{
1451 			guint8 grey;
1452 
1453 			grey = (pp[0] + pp[1] + pp[2]) / 3;
1454 			*pp = grey;
1455 			pp++;
1456 			*pp = grey;
1457 			pp++;
1458 			*pp = grey;
1459 			pp++;
1460 			if (has_alpha) pp++;
1461 			}
1462 		}
1463 }
1464 
1465 /*
1466  *-----------------------------------------------------------------------------
1467  * pixbuf highlight under/over exposure *-----------------------------------------------------------------------------
1468  */
pixbuf_highlight_overunderexposed(GdkPixbuf * pb,gint x,gint y,gint w,gint h)1469 void pixbuf_highlight_overunderexposed(GdkPixbuf *pb, gint x, gint y, gint w, gint h)
1470 {
1471 	gboolean has_alpha;
1472 	gint pw, ph, prs;
1473 	guchar *p_pix;
1474 	guchar *pp;
1475 	gint i, j;
1476 
1477 	if (!pb) return;
1478 
1479 	pw = gdk_pixbuf_get_width(pb);
1480 	ph = gdk_pixbuf_get_height(pb);
1481 
1482 	if (x < 0 || x + w > pw) return;
1483 	if (y < 0 || y + h > ph) return;
1484 
1485 	has_alpha = gdk_pixbuf_get_has_alpha(pb);
1486 	prs = gdk_pixbuf_get_rowstride(pb);
1487 	p_pix = gdk_pixbuf_get_pixels(pb);
1488 
1489 	for (i = 0; i < h; i++)
1490 		{
1491 		pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1492 		for (j = 0; j < w; j++)
1493 			{
1494 			if (pp[0] == 255 || pp[1] == 255 || pp[2] == 255 || pp[0] == 0 || pp[1] == 0 || pp[2] == 0)
1495 				{
1496 				*pp = 255;
1497 				pp++;
1498 				*pp = 0;
1499 				pp++;
1500 				*pp = 0;
1501 				pp++;
1502 				if (has_alpha) pp++;
1503 				}
1504 			else
1505 				{
1506 				pp = pp + 3;
1507 				if (has_alpha) pp++;
1508 				}
1509 			}
1510 		}
1511 }
1512 
1513 /*
1514  *-----------------------------------------------------------------------------
1515  * pixbuf ignore alpha
1516  *-----------------------------------------------------------------------------
1517 */
pixbuf_ignore_alpha_rect(GdkPixbuf * pb,gint x,gint y,gint w,gint h)1518 void pixbuf_ignore_alpha_rect(GdkPixbuf *pb,
1519                  gint x, gint y, gint w, gint h)
1520 {
1521    gboolean has_alpha;
1522    gint pw, ph, prs;
1523    guchar *p_pix;
1524    guchar *pp;
1525    gint i, j;
1526 
1527    if (!pb) return;
1528 
1529    pw = gdk_pixbuf_get_width(pb);
1530    ph = gdk_pixbuf_get_height(pb);
1531 
1532    if (x < 0 || x + w > pw) return;
1533    if (y < 0 || y + h > ph) return;
1534 
1535    has_alpha = gdk_pixbuf_get_has_alpha(pb);
1536    if (!has_alpha) return;
1537 
1538    prs = gdk_pixbuf_get_rowstride(pb);
1539    p_pix = gdk_pixbuf_get_pixels(pb);
1540 
1541    for (i = 0; i < h; i++)
1542        {
1543        pp = p_pix + (y + i) * prs + (x * 4 );
1544        for (j = 0; j < w; j++)
1545            {
1546            pp[3] = 0xff;
1547            pp+=4;
1548            }
1549        }
1550 }
1551 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
1552