1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2010 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <math.h>
24 #include <string.h>
25 #include "cairo-utils.h"
26 #include "cairo-scale.h"
27 #include "gth-image-utils.h"
28 
29 
30 G_DEFINE_BOXED_TYPE (GthCairoSurface,
31 		     gth_cairo_surface,
32 		     (GBoxedCopyFunc) cairo_surface_reference,
33 		     (GBoxedFreeFunc) cairo_surface_destroy)
34 
35 
36 const unsigned char cairo_channel[4] = { CAIRO_RED, CAIRO_GREEN, CAIRO_BLUE, CAIRO_ALPHA };
37 
38 
39 static cairo_user_data_key_t surface_metadata_key;
40 
41 
42 static void
surface_metadata_free(void * data)43 surface_metadata_free (void *data)
44 {
45 	cairo_surface_metadata_t *metadata = data;
46 	g_free (metadata);
47 }
48 
49 
50 inline int
_cairo_multiply_alpha(int color,int alpha)51 _cairo_multiply_alpha (int color,
52 		       int alpha)
53 {
54 	int temp = (alpha * color) + 0x80;
55 	return ((temp + (temp >> 8)) >> 8);
56 }
57 
58 
59 gboolean
_cairo_rectangle_contains_point(cairo_rectangle_int_t * rect,int x,int y)60 _cairo_rectangle_contains_point (cairo_rectangle_int_t *rect,
61 			 	 int                    x,
62 			 	 int                    y)
63 {
64 	return ((x >= rect->x)
65 		&& (y >= rect->y)
66 		&& (x <= rect->x + rect->width)
67 		&& (y <= rect->y + rect->height));
68 }
69 
70 
71 void
_gdk_color_to_cairo_color(GdkColor * g_color,GdkRGBA * c_color)72 _gdk_color_to_cairo_color (GdkColor *g_color,
73 			   GdkRGBA  *c_color)
74 {
75 	c_color->red = (double) g_color->red / 65535;
76 	c_color->green = (double) g_color->green / 65535;
77 	c_color->blue = (double) g_color->blue / 65535;
78 	c_color->alpha = 1.0;
79 }
80 
81 
82 void
_gdk_color_to_cairo_color_255(GdkColor * g_color,cairo_color_255_t * c_color)83 _gdk_color_to_cairo_color_255 (GdkColor          *g_color,
84 			       cairo_color_255_t *c_color)
85 {
86 	c_color->r = (guchar) 255.0 * g_color->red / 65535.0;
87 	c_color->g = (guchar) 255.0 * g_color->green / 65535.0;
88 	c_color->b = (guchar) 255.0 * g_color->blue / 65535.0;
89 	c_color->a = 0xff;
90 }
91 
92 
93 void
_gdk_rgba_to_cairo_color_255(GdkRGBA * g_color,cairo_color_255_t * c_color)94 _gdk_rgba_to_cairo_color_255 (GdkRGBA           *g_color,
95 			      cairo_color_255_t *c_color)
96 {
97 	c_color->r = (guchar) 255.0 * g_color->red;
98 	c_color->g = (guchar) 255.0 * g_color->green;
99 	c_color->b = (guchar) 255.0 * g_color->blue;
100 	c_color->a = (guchar) 255.0 * g_color->alpha;
101 }
102 
103 
104 void
_cairo_metadata_set_has_alpha(cairo_surface_metadata_t * metadata,gboolean has_alpha)105 _cairo_metadata_set_has_alpha (cairo_surface_metadata_t	*metadata,
106 			       gboolean                  has_alpha)
107 {
108 	g_return_if_fail (metadata != NULL);
109 
110 	metadata->valid_data |= _CAIRO_METADATA_FLAG_HAS_ALPHA;
111 	metadata->has_alpha = has_alpha ? TRUE : FALSE;
112 }
113 
114 
115 void
_cairo_metadata_set_original_size(cairo_surface_metadata_t * metadata,int width,int height)116 _cairo_metadata_set_original_size (cairo_surface_metadata_t *metadata,
117 				   int                       width,
118 				   int                       height)
119 {
120 	g_return_if_fail (metadata != NULL);
121 
122 	metadata->valid_data |= _CAIRO_METADATA_FLAG_ORIGINAL_SIZE;
123 	metadata->original_width = width;
124 	metadata->original_height = height;
125 }
126 
127 
128 void
_cairo_metadata_set_thumbnail_size(cairo_surface_metadata_t * metadata,int width,int height)129 _cairo_metadata_set_thumbnail_size (cairo_surface_metadata_t *metadata,
130 				    int                       width,
131 				    int                       height)
132 {
133 	g_return_if_fail (metadata != NULL);
134 
135 	metadata->valid_data |= _CAIRO_METADATA_FLAG_THUMBNAIL_SIZE;
136 	metadata->thumbnail.image_width = width;
137 	metadata->thumbnail.image_height = height;
138 }
139 
140 
141 void
_cairo_clear_surface(cairo_surface_t ** surface)142 _cairo_clear_surface (cairo_surface_t  **surface)
143 {
144 	if (surface == NULL)
145 		return;
146 	if (*surface == NULL)
147 		return;
148 
149 	cairo_surface_destroy (*surface);
150 	*surface = NULL;
151 }
152 
153 
154 unsigned char *
_cairo_image_surface_flush_and_get_data(cairo_surface_t * surface)155 _cairo_image_surface_flush_and_get_data (cairo_surface_t *surface)
156 {
157 	g_return_val_if_fail (surface != NULL, NULL);
158 
159 	cairo_surface_flush (surface);
160 	return cairo_image_surface_get_data (surface);
161 }
162 
163 
164 static void
_cairo_surface_metadata_init(cairo_surface_metadata_t * metadata)165 _cairo_surface_metadata_init (cairo_surface_metadata_t *metadata)
166 {
167 	g_return_if_fail (metadata != NULL);
168 
169 	metadata->valid_data = _CAIRO_METADATA_FLAG_NONE;
170 	metadata->has_alpha = FALSE;
171 	metadata->original_width = 0;
172 	metadata->original_height = 0;
173 	metadata->thumbnail.image_width = 0;
174 	metadata->thumbnail.image_height = 0;
175 }
176 
177 
178 cairo_surface_metadata_t *
_cairo_image_surface_get_metadata(cairo_surface_t * surface)179 _cairo_image_surface_get_metadata (cairo_surface_t *surface)
180 {
181 	cairo_surface_metadata_t *metadata;
182 
183 	g_return_val_if_fail (surface != NULL, NULL);
184 
185 	metadata = cairo_surface_get_user_data (surface, &surface_metadata_key);
186 	if (metadata == NULL) {
187 		metadata = g_new0 (cairo_surface_metadata_t, 1);
188 		_cairo_surface_metadata_init (metadata);
189 		cairo_surface_set_user_data (surface, &surface_metadata_key, metadata, surface_metadata_free);
190 	}
191 
192 	return metadata;
193 }
194 
195 
196 void
_cairo_image_surface_copy_metadata(cairo_surface_t * src,cairo_surface_t * dest)197 _cairo_image_surface_copy_metadata (cairo_surface_t *src,
198 				    cairo_surface_t *dest)
199 {
200 	cairo_surface_metadata_t *src_metadata;
201 	cairo_surface_metadata_t *dest_metadata;
202 
203 	g_return_if_fail (src != NULL);
204 	g_return_if_fail (dest != NULL);
205 
206 	src_metadata = _cairo_image_surface_get_metadata (src);
207 	dest_metadata = _cairo_image_surface_get_metadata (dest);
208 
209 	dest_metadata->valid_data = src_metadata->valid_data;
210 	dest_metadata->has_alpha = src_metadata->has_alpha;
211 	dest_metadata->original_width = src_metadata->original_width;
212 	dest_metadata->original_height = src_metadata->original_height;
213 	dest_metadata->thumbnail.image_width = src_metadata->thumbnail.image_width;
214 	dest_metadata->thumbnail.image_height = src_metadata->thumbnail.image_height;
215 
216 }
217 
218 
219 void
_cairo_image_surface_clear_metadata(cairo_surface_t * surface)220 _cairo_image_surface_clear_metadata (cairo_surface_t *surface)
221 {
222 	cairo_surface_metadata_t *metadata;
223 
224 	g_return_if_fail (surface != NULL);
225 
226 	metadata = _cairo_image_surface_get_metadata (surface);
227 	_cairo_surface_metadata_init (metadata);
228 }
229 
230 
231 gboolean
_cairo_image_surface_get_has_alpha(cairo_surface_t * surface)232 _cairo_image_surface_get_has_alpha (cairo_surface_t *surface)
233 {
234 	cairo_surface_metadata_t *metadata;
235 	gboolean                  has_alpha;
236 	int                       width;
237 	int                       height;
238 	int                       row_stride;
239 	guchar                   *row;
240 	int                       h, w;
241 
242 	if (surface == NULL)
243 		return FALSE;
244 
245 	metadata = _cairo_image_surface_get_metadata (surface);
246 	if ((metadata != NULL) && (metadata->valid_data & _CAIRO_METADATA_FLAG_HAS_ALPHA))
247 		return metadata->has_alpha;
248 
249 	has_alpha = FALSE;
250 	if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32) {
251 		/* search an alpha value lower than 255 */
252 
253 		width = cairo_image_surface_get_width (surface);
254 		height = cairo_image_surface_get_height (surface);
255 		row_stride = cairo_image_surface_get_stride (surface);
256 		row = _cairo_image_surface_flush_and_get_data (surface);
257 
258 		for (h = 0; ! has_alpha && (h < height); h++) {
259 			guchar *pixel = row;
260 			for (w = 0; w < width; w++) {
261 				if (pixel[CAIRO_ALPHA] < 255) {
262 					has_alpha = TRUE;
263 					break;
264 				}
265 				pixel += 4;
266 			}
267 			row += row_stride;
268 		}
269 	}
270 	_cairo_metadata_set_has_alpha (metadata, has_alpha);
271 
272 	return has_alpha;
273 }
274 
275 
276 gboolean
_cairo_image_surface_get_original_size(cairo_surface_t * surface,int * original_width,int * original_height)277 _cairo_image_surface_get_original_size (cairo_surface_t *surface,
278 					int             *original_width,
279 					int             *original_height)
280 {
281 	cairo_surface_metadata_t *metadata;
282 
283 	if (surface == NULL)
284 		return FALSE;
285 
286 	metadata = cairo_surface_get_user_data (surface, &surface_metadata_key);
287 	if (metadata == NULL)
288 		return FALSE;
289 
290 	if ((metadata->valid_data & _CAIRO_METADATA_FLAG_ORIGINAL_SIZE) == 0)
291 		return FALSE;
292 
293 	if (original_width)
294 		*original_width = metadata->original_width;
295 	if (original_height)
296 		*original_height = metadata->original_height;
297 
298 	return TRUE;
299 }
300 
301 
302 cairo_surface_t *
_cairo_image_surface_create(cairo_format_t format,int width,int height)303 _cairo_image_surface_create (cairo_format_t format,
304 			     int            width,
305 			     int            height)
306 {
307 	cairo_surface_t *result;
308 	cairo_status_t   status;
309 
310 	result = cairo_image_surface_create (format, width, height);
311 	status = cairo_surface_status (result);
312 	if (status != CAIRO_STATUS_SUCCESS) {
313 		g_warning ("_cairo_image_surface_create: could not create the surface: %s", cairo_status_to_string (status));
314 		cairo_surface_destroy (result);
315 		return NULL;
316 	}
317 
318 	return result;
319 }
320 
321 
322 cairo_surface_t *
_cairo_image_surface_copy(cairo_surface_t * source)323 _cairo_image_surface_copy (cairo_surface_t *source)
324 {
325 	cairo_surface_t *result;
326 	unsigned char   *p_source;
327 	unsigned char   *p_destination;
328 
329 	if (source == NULL)
330 		return NULL;
331 
332 	result = _cairo_image_surface_create (cairo_image_surface_get_format (source),
333 					      cairo_image_surface_get_width (source),
334 					      cairo_image_surface_get_height (source));
335 	if (result == NULL)
336 		return NULL;
337 
338 	p_source = _cairo_image_surface_flush_and_get_data (source);
339 	p_destination = _cairo_image_surface_flush_and_get_data (result);
340 	memcpy (p_destination, p_source, cairo_image_surface_get_stride (source) * cairo_image_surface_get_height (source));
341 	cairo_surface_mark_dirty (result);
342 
343 	return result;
344 }
345 
346 
347 cairo_surface_t *
_cairo_image_surface_copy_subsurface(cairo_surface_t * source,int src_x,int src_y,int width,int height)348 _cairo_image_surface_copy_subsurface (cairo_surface_t *source,
349 				      int              src_x,
350 				      int              src_y,
351 				      int              width,
352 				      int              height)
353 {
354 	cairo_surface_t *destination;
355 	cairo_status_t   status;
356 	int              source_stride;
357 	int              destination_stride;
358 	unsigned char   *p_source;
359 	unsigned char   *p_destination;
360 	int              row_size;
361 
362 	g_return_val_if_fail (source != NULL, NULL);
363 	g_return_val_if_fail (src_x + width <= cairo_image_surface_get_width (source), NULL);
364 	g_return_val_if_fail (src_y + height <= cairo_image_surface_get_height (source), NULL);
365 
366 	destination = cairo_image_surface_create (cairo_image_surface_get_format (source), width, height);
367 	status = cairo_surface_status (destination);
368 	if (status != CAIRO_STATUS_SUCCESS) {
369 		g_warning ("_cairo_image_surface_copy_subsurface: could not create the surface: %s", cairo_status_to_string (status));
370 		cairo_surface_destroy (destination);
371 		return NULL;
372 	}
373 
374 	source_stride = cairo_image_surface_get_stride (source);
375 	destination_stride = cairo_image_surface_get_stride (destination);
376 	p_source = _cairo_image_surface_flush_and_get_data (source) + (src_y * source_stride) + (src_x * 4);
377 	p_destination = _cairo_image_surface_flush_and_get_data (destination);
378 	row_size = width * 4;
379 	while (height-- > 0) {
380 		memcpy (p_destination, p_source, row_size);
381 
382 		p_source += source_stride;
383 		p_destination += destination_stride;
384 	}
385 
386 	cairo_surface_mark_dirty (destination);
387 
388 	return destination;
389 }
390 
391 
392 cairo_surface_t *
_cairo_image_surface_create_from_pixbuf(GdkPixbuf * pixbuf)393 _cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
394 {
395 	cairo_surface_t          *surface;
396 	cairo_surface_metadata_t *metadata;
397 	int                      width;
398 	int                      height;
399 	int                      p_stride;
400 	int                      p_n_channels;
401 	guchar                  *p_pixels;
402 	int                      s_stride;
403 	unsigned char           *s_pixels;
404 	int                      h, w;
405 	guint32                  pixel;
406 	guchar                   r, g, b, a;
407 
408 	if (pixbuf == NULL)
409 		return NULL;
410 
411 	g_object_get (G_OBJECT (pixbuf),
412 		      "width", &width,
413 		      "height", &height,
414 		      "rowstride", &p_stride,
415 		      "n-channels", &p_n_channels,
416 		      "pixels", &p_pixels,
417 		      NULL );
418 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
419 	s_stride = cairo_image_surface_get_stride (surface);
420 	s_pixels = _cairo_image_surface_flush_and_get_data (surface);
421 
422 	metadata = _cairo_image_surface_get_metadata (surface);
423 	_cairo_metadata_set_has_alpha (metadata, (p_n_channels == 4));
424 
425 	if (p_n_channels == 4) {
426 		guchar *s_iter;
427 		guchar *p_iter;
428 
429 		for (h = 0; h < height; h++) {
430 			s_iter = s_pixels;
431 			p_iter = p_pixels;
432 
433 			for (w = 0; w < width; w++) {
434 				a = p_iter[3];
435 				if (a == 0xff) {
436 					pixel = CAIRO_RGBA_TO_UINT32 (p_iter[0], p_iter[1], p_iter[2], 0xff);
437 				}
438 				else if (a == 0) {
439 					pixel = 0;
440 				}
441 				else {
442 					r = _cairo_multiply_alpha (p_iter[0], a);
443 					g = _cairo_multiply_alpha (p_iter[1], a);
444 					b = _cairo_multiply_alpha (p_iter[2], a);
445 					pixel = CAIRO_RGBA_TO_UINT32 (r, g, b, a);
446 				}
447 				memcpy (s_iter, &pixel, sizeof (guint32));
448 
449 				s_iter += 4;
450 				p_iter += p_n_channels;
451 			}
452 
453 			s_pixels += s_stride;
454 			p_pixels += p_stride;
455 		}
456 	}
457 	else {
458 		guchar *s_iter;
459 		guchar *p_iter;
460 
461 		for (h = 0; h < height; h++) {
462 			s_iter = s_pixels;
463 			p_iter = p_pixels;
464 
465 			for (w = 0; w < width; w++) {
466 				pixel = CAIRO_RGBA_TO_UINT32 (p_iter[0], p_iter[1], p_iter[2], 0xff);
467 				memcpy (s_iter, &pixel, sizeof (guint32));
468 
469 				s_iter += 4;
470 				p_iter += p_n_channels;
471 			}
472 
473 			s_pixels += s_stride;
474 			p_pixels += p_stride;
475 		}
476 	}
477 
478 	cairo_surface_mark_dirty (surface);
479 
480 	return surface;
481 }
482 
483 
484 cairo_surface_t *
_cairo_image_surface_create_compatible(cairo_surface_t * surface)485 _cairo_image_surface_create_compatible (cairo_surface_t *surface)
486 {
487 	return cairo_image_surface_create (cairo_image_surface_get_format (surface),
488 					   cairo_image_surface_get_width (surface),
489 					   cairo_image_surface_get_height (surface));
490 }
491 
492 
493 void
_cairo_image_surface_transform_get_steps(cairo_format_t format,int width,int height,GthTransform transform,int * destination_width_p,int * destination_height_p,int * line_start_p,int * line_step_p,int * pixel_step_p)494 _cairo_image_surface_transform_get_steps (cairo_format_t  format,
495 					  int             width,
496 					  int             height,
497 					  GthTransform    transform,
498 					  int            *destination_width_p,
499 					  int            *destination_height_p,
500 					  int            *line_start_p,
501 					  int            *line_step_p,
502 					  int            *pixel_step_p)
503 {
504 	int destination_stride;
505 	int destination_width = 0;
506 	int destination_height = 0;
507 	int line_start = 0;
508 	int line_step = 0;
509 	int pixel_step = 0;
510 
511 	switch (transform) {
512 	case GTH_TRANSFORM_NONE:
513 	default:
514 		destination_width = width;
515 		destination_height = height;
516 		destination_stride = cairo_format_stride_for_width (format, destination_width);
517 		line_start = 0;
518 		line_step = destination_stride;
519 		pixel_step = 4;
520 		break;
521 
522 	case GTH_TRANSFORM_FLIP_H:
523 		destination_width = width;
524 		destination_height = height;
525 		destination_stride = cairo_format_stride_for_width (format, destination_width);
526 		line_start = (destination_width - 1) * 4;
527 		line_step = destination_stride;
528 		pixel_step = -4;
529 		break;
530 
531 	case GTH_TRANSFORM_ROTATE_180:
532 		destination_width = width;
533 		destination_height = height;
534 		destination_stride = cairo_format_stride_for_width (format, destination_width);
535 		line_start = ((destination_height - 1) * destination_stride) + ((destination_width - 1) * 4);
536 		line_step = -destination_stride;
537 		pixel_step = -4;
538 		break;
539 
540 	case GTH_TRANSFORM_FLIP_V:
541 		destination_width = width;
542 		destination_height = height;
543 		destination_stride = cairo_format_stride_for_width (format, destination_width);
544 		line_start = (destination_height - 1) * destination_stride;
545 		line_step = -destination_stride;
546 		pixel_step = 4;
547 		break;
548 
549 	case GTH_TRANSFORM_TRANSPOSE:
550 		destination_width = height;
551 		destination_height = width;
552 		destination_stride = cairo_format_stride_for_width (format, destination_width);
553 		line_start = 0;
554 		line_step = 4;
555 		pixel_step = destination_stride;
556 		break;
557 
558 	case GTH_TRANSFORM_ROTATE_90:
559 		destination_width = height;
560 		destination_height = width;
561 		destination_stride = cairo_format_stride_for_width (format, destination_width);
562 		line_start = (destination_width - 1) * 4;
563 		line_step = -4;
564 		pixel_step = destination_stride;
565 		break;
566 
567 	case GTH_TRANSFORM_TRANSVERSE:
568 		destination_width = height;
569 		destination_height = width;
570 		destination_stride = cairo_format_stride_for_width (format, destination_width);
571 		line_start = ((destination_height - 1) * destination_stride) + ((destination_width - 1) * 4);
572 		line_step = -4;
573 		pixel_step = -destination_stride;
574 		break;
575 
576 	case GTH_TRANSFORM_ROTATE_270:
577 		destination_width = height;
578 		destination_height = width;
579 		destination_stride = cairo_format_stride_for_width (format, destination_width);
580 		line_start = (destination_height - 1) * destination_stride;
581 		line_step = 4;
582 		pixel_step = -destination_stride;
583 		break;
584 	}
585 
586 	if (destination_width_p != NULL)
587 		*destination_width_p = destination_width;
588 	if (destination_height_p != NULL)
589 		*destination_height_p = destination_height;
590 	if (line_start_p != NULL)
591 		*line_start_p = line_start;
592 	if (line_step_p != NULL)
593 		*line_step_p = line_step;
594 	if (pixel_step_p != NULL)
595 		*pixel_step_p = pixel_step;
596 }
597 
598 
599 cairo_surface_t *
_cairo_image_surface_transform(cairo_surface_t * source,GthTransform transform)600 _cairo_image_surface_transform (cairo_surface_t *source,
601 				GthTransform     transform)
602 {
603 	cairo_surface_t *destination = NULL;
604 	cairo_format_t   format;
605 	int              width;
606 	int              height;
607 	int              source_stride;
608 	int              destination_width;
609 	int              destination_height;
610 	int              line_start;
611 	int              line_step;
612 	int              pixel_step;
613 	unsigned char   *p_source_line;
614 	unsigned char   *p_destination_line;
615 	unsigned char   *p_source;
616 	unsigned char   *p_destination;
617 	int              x;
618 
619 	if (source == NULL)
620 		return NULL;
621 
622 	format = cairo_image_surface_get_format (source);
623 	width = cairo_image_surface_get_width (source);
624 	height = cairo_image_surface_get_height (source);
625 	source_stride = cairo_image_surface_get_stride (source);
626 
627 	_cairo_image_surface_transform_get_steps (format,
628 						  width,
629 						  height,
630 						  transform,
631 						  &destination_width,
632 						  &destination_height,
633 						  &line_start,
634 						  &line_step,
635 						  &pixel_step);
636 
637 	destination = cairo_image_surface_create (format, destination_width, destination_height);
638 	p_source_line = _cairo_image_surface_flush_and_get_data (source);
639 	p_destination_line = _cairo_image_surface_flush_and_get_data (destination) + line_start;
640 	while (height-- > 0) {
641 		p_source = p_source_line;
642 		p_destination = p_destination_line;
643 		for (x = 0; x < width; x++) {
644 			memcpy (p_destination, p_source, 4);
645 			p_source += 4;
646 			p_destination += pixel_step;
647 		}
648 		p_source_line += source_stride;
649 		p_destination_line += line_step;
650 	}
651 
652 	cairo_surface_mark_dirty (destination);
653 
654 	return destination;
655 }
656 
657 
658 cairo_surface_t *
_cairo_image_surface_color_shift(cairo_surface_t * image,int shift)659 _cairo_image_surface_color_shift (cairo_surface_t *image,
660 				  int              shift)
661 {
662 	cairo_surface_t *shifted;
663 	int              i, j;
664 	int              width, height, src_stride, dest_stride;
665 	guchar          *src_pixels, *src_row, *src_pixel;
666 	guchar          *dest_pixels, *dest_row, *dest_pixel;
667 	int              val, temp;
668 	guchar           r, g, b, a;
669 
670 	shifted = _cairo_image_surface_create_compatible (image);
671 
672 	width       = cairo_image_surface_get_width (image);
673 	height      = cairo_image_surface_get_height (image);
674 	src_stride  = cairo_image_surface_get_stride (image);
675 	src_pixels  = _cairo_image_surface_flush_and_get_data (image);
676 	dest_stride = cairo_image_surface_get_stride (shifted);
677 	dest_pixels = _cairo_image_surface_flush_and_get_data (shifted);
678 
679 	src_row = src_pixels;
680 	dest_row = dest_pixels;
681 	for (i = 0; i < height; i++) {
682 		src_pixel = src_row;
683 		dest_pixel  = dest_row;
684 
685 		for (j = 0; j < width; j++) {
686 			CAIRO_GET_RGBA (src_pixel, r, g, b, a);
687 
688 			val = r + shift;
689 			r = CLAMP (val, 0, 255);
690 
691 			val = g + shift;
692 			g = CLAMP (val, 0, 255);
693 
694 			val = b + shift;
695 			b = CLAMP (val, 0, 255);
696 
697 			CAIRO_SET_RGBA (dest_pixel, r, g, b, a);
698 
699 			src_pixel += 4;
700 			dest_pixel += 4;
701 		}
702 
703 		src_row += src_stride;
704 		dest_row += dest_stride;
705 	}
706 
707 	cairo_surface_mark_dirty (shifted);
708 
709 	return shifted;
710 }
711 
712 
713 void
_cairo_copy_line_as_rgba_big_endian(guchar * dest,guchar * src,guint width,guint alpha)714 _cairo_copy_line_as_rgba_big_endian (guchar *dest,
715 				     guchar *src,
716 				     guint   width,
717 				     guint   alpha)
718 {
719 	guint x;
720 
721 	if (alpha) {
722 		int temp;
723 
724 		for (x = 0; x < width; x++) {
725 			CAIRO_GET_RGBA (src, dest[0], dest[1], dest[2], dest[3]);
726 
727 			src += 4;
728 			dest += 4;
729 		}
730 	}
731 	else {
732 		for (x = 0; x < width; x++) {
733 			CAIRO_GET_RGB (src, dest[0], dest[1], dest[2]);
734 
735 			src += 4;
736 			dest += 3;
737 		}
738 	}
739 }
740 
741 
742 void
_cairo_copy_line_as_rgba_little_endian(guchar * dest,guchar * src,guint width,guint alpha)743 _cairo_copy_line_as_rgba_little_endian (guchar *dest,
744 					guchar *src,
745 					guint   width,
746 					guint   alpha)
747 {
748 	guint x;
749 
750 	if (alpha) {
751 		int r, g, b, a, temp;
752 
753 		for (x = 0; x < width; x++) {
754 			CAIRO_GET_RGBA (src, r, g, b, a);
755 			dest[0] = b;
756 			dest[1] = g;
757 			dest[2] = r;
758 			dest[3] = a;
759 
760 			src += 4;
761 			dest += 4;
762 		}
763 	}
764 	else {
765 		for (x = 0; x < width; x++) {
766 			dest[0] = src[CAIRO_BLUE];
767 			dest[1] = src[CAIRO_GREEN];
768 			dest[2] = src[CAIRO_RED];
769 
770 			src += 4;
771 			dest += 3;
772 		}
773 	}
774 }
775 
776 
777 void
_cairo_paint_full_gradient(cairo_surface_t * surface,GdkRGBA * h_color1,GdkRGBA * h_color2,GdkRGBA * v_color1,GdkRGBA * v_color2)778 _cairo_paint_full_gradient (cairo_surface_t *surface,
779 			    GdkRGBA         *h_color1,
780 			    GdkRGBA         *h_color2,
781 			    GdkRGBA         *v_color1,
782 			    GdkRGBA         *v_color2)
783 {
784 	cairo_color_255_t  hcolor1;
785 	cairo_color_255_t  hcolor2;
786 	cairo_color_255_t  vcolor1;
787 	cairo_color_255_t  vcolor2;
788 	int                width;
789 	int                height;
790 	int                s_stride;
791 	unsigned char     *s_pixels;
792 	int                h, w;
793 	double             x, y;
794 	double             x_y, x_1_y, y_1_x, _1_x_1_y;
795 	guchar             red, green, blue;
796 
797 	if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
798 		return;
799 
800 	_gdk_rgba_to_cairo_color_255 (h_color1, &hcolor1);
801 	_gdk_rgba_to_cairo_color_255 (h_color2, &hcolor2);
802 	_gdk_rgba_to_cairo_color_255 (v_color1, &vcolor1);
803 	_gdk_rgba_to_cairo_color_255 (v_color2, &vcolor2);
804 
805 	width = cairo_image_surface_get_width (surface);
806 	height = cairo_image_surface_get_height (surface);
807 	s_stride = cairo_image_surface_get_stride (surface);
808 	s_pixels = _cairo_image_surface_flush_and_get_data (surface);
809 
810 	for (h = 0; h < height; h++) {
811 		guchar *s_iter = s_pixels;
812 
813 	        x = (double) (height - h) / height;
814 
815 	        for (w = 0; w < width; w++) {
816 	        	y        = (double) (width - w) / width;
817 			x_y      = x * y;
818 			x_1_y    = x * (1.0 - y);
819 			y_1_x    = y * (1.0 - x);
820 			_1_x_1_y = (1.0 - x) * (1.0 - y);
821 
822 			red   = hcolor1.r * x_y + hcolor2.r * x_1_y + vcolor1.r * y_1_x + vcolor2.r * _1_x_1_y;
823 			green = hcolor1.g * x_y + hcolor2.g * x_1_y + vcolor1.g * y_1_x + vcolor2.g * _1_x_1_y;
824 			blue  = hcolor1.b * x_y + hcolor2.b * x_1_y + vcolor1.b * y_1_x + vcolor2.b * _1_x_1_y;
825 
826 			CAIRO_SET_RGB (s_iter, red, green, blue);
827 
828 			s_iter += 4;
829 		}
830 
831 		s_pixels += s_stride;
832 	}
833 
834 	cairo_surface_mark_dirty (surface);
835 }
836 
837 
838 void
_cairo_draw_rounded_box(cairo_t * cr,double x,double y,double w,double h,double r)839 _cairo_draw_rounded_box (cairo_t *cr,
840 			 double   x,
841 			 double   y,
842 			 double   w,
843 			 double   h,
844 			 double   r)
845 {
846 	if (r == 0) {
847 		cairo_rectangle (cr, x, y, w, h);
848 	}
849 	else {
850 		cairo_move_to (cr, x, y + r);
851 		cairo_arc (cr, x + r, y + r, r, 1.0 * M_PI, 1.5 * M_PI);
852 		cairo_rel_line_to (cr, w - (r * 2), 0);
853 		cairo_arc (cr, x + w - r, y + r, r, 1.5 * M_PI, 2.0 * M_PI);
854 		cairo_rel_line_to (cr, 0, h - (r * 2));
855 		cairo_arc (cr, x + w - r, y + h - r, r, 0.0 * M_PI, 0.5 * M_PI);
856 		cairo_rel_line_to (cr, - (w - (r * 2)), 0);
857 		cairo_arc (cr, x + r, y + h - r, r, 0.5 * M_PI, 1.0 * M_PI);
858 		cairo_rel_line_to (cr, 0, - (h - (r * 2)));
859 	}
860 }
861 
862 
863 void
_cairo_draw_drop_shadow(cairo_t * cr,double x,double y,double w,double h,double r)864 _cairo_draw_drop_shadow (cairo_t *cr,
865 			 double   x,
866 			 double   y,
867 			 double   w,
868 			 double   h,
869 			 double   r)
870 {
871 	int i;
872 
873 	cairo_save (cr);
874 	cairo_set_line_width (cr, 1);
875 	for (i = r; i >= 0; i--) {
876 		cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, (0.1 / r)* (r - i + 1));
877 		_cairo_draw_rounded_box (cr,
878 					 x + r - i,
879 					 y + r - i,
880 					 w + (i * 2),
881 					 h + (i * 2),
882 					 r);
883 		cairo_fill (cr);
884 	}
885 	cairo_restore (cr);
886 }
887 
888 
889 void
_cairo_draw_frame(cairo_t * cr,double x,double y,double w,double h,double r)890 _cairo_draw_frame (cairo_t *cr,
891 		   double   x,
892 		   double   y,
893 		   double   w,
894 		   double   h,
895 		   double   r)
896 {
897 	cairo_save (cr);
898 	cairo_set_line_width (cr, r);
899 	cairo_rectangle (cr, x - (r / 2), y - (r / 2), w + r, h + r);
900 	cairo_stroke (cr);
901 	cairo_restore (cr);
902 }
903 
904 
905 void
_cairo_draw_slide(cairo_t * cr,double frame_x,double frame_y,double frame_width,double frame_height,double image_width,double image_height,GdkRGBA * frame_color,gboolean draw_inner_border)906 _cairo_draw_slide (cairo_t  *cr,
907 		   double    frame_x,
908 		   double    frame_y,
909 		   double    frame_width,
910 		   double    frame_height,
911 		   double    image_width,
912 		   double    image_height,
913 		   GdkRGBA  *frame_color,
914 		   gboolean  draw_inner_border)
915 {
916 	const double dark_gray = 0.60;
917 	const double mid_gray = 0.80;
918 	const double darker_gray = 0.45;
919 	const double light_gray = 0.99;
920 	double       frame_x2;
921 	double       frame_y2;
922 
923 	frame_x += 0.5;
924 	frame_y += 0.5;
925 
926 	frame_x2 = frame_x + frame_width;
927 	frame_y2 = frame_y + frame_height;
928 
929 	cairo_save (cr);
930 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
931 	cairo_set_line_width (cr, 1.0);
932 
933 	/* background. */
934 
935 	gdk_cairo_set_source_rgba (cr, frame_color);
936 	cairo_rectangle (cr,
937 			 frame_x,
938 			 frame_y,
939 			 frame_width,
940 			 frame_height);
941 	cairo_fill (cr);
942 
943 	if ((image_width > 0) && (image_height > 0)) {
944 		double image_x, image_y;
945 
946 		image_x = frame_x + (frame_width - image_width) / 2 - 0.5;
947 		image_y = frame_y + (frame_height - image_height) / 2 - 0.5;
948 
949 		/* inner border. */
950 
951 		if (draw_inner_border) {
952 			double image_x2, image_y2;
953 
954 			cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
955 			cairo_rectangle (cr,
956 					 image_x,
957 					 image_y,
958 					 image_width,
959 					 image_height);
960 			cairo_fill (cr);
961 
962 			image_x2 = image_x + image_width + 1;
963 			image_y2 = image_y + image_height + 1;
964 
965 			cairo_set_source_rgb (cr, dark_gray, dark_gray, dark_gray);
966 			cairo_move_to (cr, image_x, image_y);
967 			cairo_line_to (cr, image_x2, image_y);
968 			cairo_move_to (cr, image_x, image_y);
969 			cairo_line_to (cr, image_x, image_y2);
970 			cairo_stroke (cr);
971 
972 			cairo_set_source_rgb (cr, mid_gray, mid_gray, mid_gray);
973 			cairo_move_to (cr, image_x2, image_y);
974 			cairo_line_to (cr, image_x2, image_y2);
975 			cairo_move_to (cr, image_x, image_y2);
976 			cairo_line_to (cr, image_x2, image_y2);
977 			cairo_stroke (cr);
978 		}
979 	}
980 
981 	/* outer border. */
982 
983 	cairo_set_source_rgb (cr, mid_gray, mid_gray, mid_gray);
984 	cairo_move_to (cr, frame_x, frame_y);
985 	cairo_line_to (cr, frame_x2, frame_y);
986 	cairo_move_to (cr, frame_x, frame_y);
987 	cairo_line_to (cr, frame_x, frame_y2);
988 	cairo_stroke (cr);
989 
990 	cairo_set_source_rgb (cr, darker_gray, darker_gray, darker_gray);
991 	cairo_move_to (cr, frame_x2, frame_y);
992 	cairo_line_to (cr, frame_x2, frame_y2);
993 	cairo_move_to (cr, frame_x, frame_y2);
994 	cairo_line_to (cr, frame_x2, frame_y2);
995 	cairo_stroke (cr);
996 
997 	cairo_set_source_rgb (cr, light_gray, light_gray, light_gray);
998 	cairo_move_to (cr, frame_x, frame_y);
999 	cairo_line_to (cr, frame_x2, frame_y);
1000 	cairo_move_to (cr, frame_x, frame_y);
1001 	cairo_line_to (cr, frame_x, frame_y2);
1002 	cairo_stroke (cr);
1003 
1004 	cairo_set_source_rgb (cr, dark_gray, dark_gray, dark_gray);
1005 	cairo_move_to (cr, frame_x2, frame_y);
1006 	cairo_line_to (cr, frame_x2, frame_y2);
1007 	cairo_move_to (cr, frame_x, frame_y2);
1008 	cairo_line_to (cr, frame_x2, frame_y2);
1009 	cairo_stroke (cr);
1010 
1011 	cairo_restore (cr);
1012 }
1013 
1014 
1015 #define GOLDEN_RATIO        1.6180339887
1016 #define GOLDER_RATIO_FACTOR (GOLDEN_RATIO / (1.0 + 2.0 * GOLDEN_RATIO))
1017 #define GRID_STEP_1         10
1018 #define GRID_STEP_2         (GRID_STEP_1 * 5)
1019 #define GRID_STEP_3         (GRID_STEP_2 * 2)
1020 #define MAX_GRID_LINES      50
1021 
1022 
1023 void
_cairo_paint_grid(cairo_t * cr,cairo_rectangle_int_t * rectangle,GthGridType grid_type)1024 _cairo_paint_grid (cairo_t               *cr,
1025 		   cairo_rectangle_int_t *rectangle,
1026 		   GthGridType            grid_type)
1027 {
1028 	double ux, uy;
1029 
1030 	cairo_save (cr);
1031 
1032 	ux = uy = 1.0;
1033 	cairo_device_to_user_distance (cr, &ux, &uy);
1034 	cairo_set_line_width (cr, MAX (ux, uy));
1035 
1036 	cairo_rectangle (cr, rectangle->x - ux + 0.5, rectangle->y - uy + 0.5, rectangle->width + (ux * 2), rectangle->height + (uy * 2));
1037 	cairo_clip (cr);
1038 
1039 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 2)
1040 	cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
1041 #endif
1042 
1043 	cairo_rectangle (cr, rectangle->x + 0.5, rectangle->y + 0.5, rectangle->width - 0.5, rectangle->height - 0.5);
1044 	cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1045 	cairo_stroke (cr);
1046 
1047 	if (grid_type == GTH_GRID_NONE) {
1048 		cairo_restore (cr);
1049 		return;
1050 	}
1051 
1052 	if (grid_type == GTH_GRID_THIRDS) {
1053 		int i;
1054 
1055 		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.60);
1056 		for (i = 1; i < 3; i++) {
1057 			cairo_move_to (cr, rectangle->x + rectangle->width * i / 3 + 0.5, rectangle->y + 1.5);
1058 			cairo_line_to (cr, rectangle->x + rectangle->width * i / 3 + 0.5, rectangle->y + rectangle->height - 0.5);
1059 
1060 			cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + rectangle->height * i / 3 + 0.5);
1061 			cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + rectangle->height * i / 3 + 0.5);
1062 		}
1063 		cairo_stroke (cr);
1064 
1065 		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.10);
1066 		for (i = 1; i < 9; i++) {
1067 
1068 			if (i % 3 == 0)
1069 				continue;
1070 
1071 			cairo_move_to (cr, rectangle->x + rectangle->width * i / 9 + 0.5, rectangle->y + 1.5);
1072 			cairo_line_to (cr, rectangle->x + rectangle->width * i / 9 + 0.5, rectangle->y + rectangle->height - 0.5);
1073 
1074 			cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + rectangle->height * i / 9 + 0.5);
1075 			cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + rectangle->height * i / 9 + 0.5);
1076 		}
1077 		cairo_stroke (cr);
1078 	}
1079 	else if (grid_type == GTH_GRID_GOLDEN) {
1080 		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.60);
1081 
1082 		int grid_x0, grid_x1, grid_x2, grid_x3;
1083 	        int grid_y0, grid_y1, grid_y2, grid_y3;
1084 		int x_delta, y_delta;
1085 
1086 		grid_x0 = rectangle->x;
1087 		grid_x3 = rectangle->x + rectangle->width;
1088 
1089                 grid_y0 = rectangle->y;
1090                 grid_y3 = rectangle->y + rectangle->height;
1091 
1092 		x_delta = rectangle->width * GOLDER_RATIO_FACTOR;
1093 		y_delta = rectangle->height * GOLDER_RATIO_FACTOR;
1094 
1095 		grid_x1 = grid_x0 + x_delta;
1096 		grid_x2 = grid_x3 - x_delta;
1097                 grid_y1 = grid_y0 + y_delta;
1098                 grid_y2 = grid_y3 - y_delta;
1099 
1100 		cairo_move_to (cr, grid_x1 + 0.5, grid_y0 + 0.5);
1101 		cairo_line_to (cr, grid_x1 + 0.5, grid_y3 + 0.5);
1102 
1103 		if (x_delta < rectangle->width / 2) {
1104 			cairo_move_to (cr, grid_x2 + 0.5, grid_y0 + 0.5);
1105 			cairo_line_to (cr, grid_x2 + 0.5, grid_y3 + 0.5);
1106 		}
1107 
1108 		cairo_move_to (cr, grid_x0 + 0.5, grid_y1 + 0.5);
1109 		cairo_line_to (cr, grid_x3 + 0.5, grid_y1 + 0.5);
1110 
1111 		if (y_delta < rectangle->height / 2) {
1112 			cairo_move_to (cr, grid_x0 + 0.5, grid_y2 + 0.5);
1113 			cairo_line_to (cr, grid_x3 + 0.5, grid_y2 + 0.5);
1114 		}
1115 
1116 		cairo_stroke (cr);
1117 	}
1118 	else if (grid_type == GTH_GRID_CENTER) {
1119 		int i;
1120 
1121 		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.60);
1122 		cairo_move_to (cr, rectangle->x + rectangle->width / 2 + 0.5, rectangle->y + 1.5);
1123 		cairo_line_to (cr, rectangle->x + rectangle->width / 2 + 0.5, rectangle->y + rectangle->height - 0.5);
1124 		cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + rectangle->height / 2 + 0.5);
1125 		cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + rectangle->height / 2 + 0.5);
1126 		cairo_stroke (cr);
1127 
1128 		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.10);
1129 		for (i = 1; i < 4; i++) {
1130 
1131 			if (i == 2)
1132 				continue;
1133 
1134 			cairo_move_to (cr, rectangle->x + rectangle->width * i / 4 + 0.5, rectangle->y + 1.5);
1135 			cairo_line_to (cr, rectangle->x + rectangle->width * i / 4 + 0.5, rectangle->y + rectangle->height - 0.5);
1136 			cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + rectangle->height * i / 4 + 0.5);
1137 			cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + rectangle->height * i / 4 + 0.5);
1138 		}
1139 		cairo_stroke (cr);
1140 	}
1141 	else if (grid_type == GTH_GRID_UNIFORM) {
1142 
1143 		int x;
1144 		int y;
1145 
1146 		if (rectangle->width / GRID_STEP_3 <= MAX_GRID_LINES) {
1147 			cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.40);
1148 			for (x = GRID_STEP_3; x < rectangle->width; x += GRID_STEP_3) {
1149 				cairo_move_to (cr, rectangle->x + x + 0.5, rectangle->y + 1.5);
1150 				cairo_line_to (cr, rectangle->x + x + 0.5, rectangle->y + rectangle->height - 0.5);
1151 			}
1152 			for (y = GRID_STEP_3; y < rectangle->height; y += GRID_STEP_3) {
1153 				cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + y + 0.5);
1154 				cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + y + 0.5);
1155 			}
1156 			cairo_stroke (cr);
1157 		}
1158 
1159 		if (rectangle->width / GRID_STEP_2 <= MAX_GRID_LINES) {
1160 			cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.20);
1161 			for (x = GRID_STEP_2; x < rectangle->width; x += GRID_STEP_2) {
1162 				if (x % GRID_STEP_3 == 0)
1163 					continue;
1164 				cairo_move_to (cr, rectangle->x + x + 0.5, rectangle->y + 1.5);
1165 				cairo_line_to (cr, rectangle->x + x + 0.5, rectangle->y + rectangle->height - 0.5);
1166 			}
1167 			for (y = GRID_STEP_2; y < rectangle->height; y += GRID_STEP_2) {
1168 				if (y % GRID_STEP_3 == 0)
1169 					continue;
1170 				cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + y + 0.5);
1171 				cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + y + 0.5);
1172 			}
1173 			cairo_stroke (cr);
1174 		}
1175 
1176 		if (rectangle->width / GRID_STEP_1 <= MAX_GRID_LINES) {
1177 			cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.10);
1178 			for (x = GRID_STEP_1; x < rectangle->width; x += GRID_STEP_1) {
1179 				if (x % GRID_STEP_2 == 0)
1180 					continue;
1181 				cairo_move_to (cr, rectangle->x + x + 0.5, rectangle->y + 1.5);
1182 				cairo_line_to (cr, rectangle->x + x + 0.5, rectangle->y + rectangle->height - 0.5);
1183 			}
1184 			for (y = GRID_STEP_1; y < rectangle->height; y += GRID_STEP_1) {
1185 				if (y % GRID_STEP_2 == 0)
1186 					continue;
1187 				cairo_move_to (cr, rectangle->x + 1.5, rectangle->y + y + 0.5);
1188 				cairo_line_to (cr, rectangle->x + rectangle->width - 0.5, rectangle->y + y + 0.5);
1189 			}
1190 		}
1191 		cairo_stroke (cr);
1192 	}
1193 
1194 	cairo_restore (cr);
1195 }
1196 
1197 
1198 cairo_pattern_t *
_cairo_create_checked_pattern(int size)1199 _cairo_create_checked_pattern (int size)
1200 {
1201 	int              h_size;
1202 	cairo_surface_t *surface;
1203 	cairo_t         *ctx;
1204 	cairo_pattern_t *pattern;
1205 
1206 	h_size = size / 2;
1207 	surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, size, size);
1208 	ctx = cairo_create (surface);
1209 
1210 	cairo_set_source_rgba (ctx, 0.4, 0.4, 0.4, 1.0);
1211 	cairo_rectangle (ctx, 0, 0, h_size, h_size);
1212 	cairo_fill (ctx);
1213 	cairo_rectangle (ctx, h_size, h_size, h_size, h_size);
1214 	cairo_fill (ctx);
1215 
1216 	cairo_set_source_rgba (ctx, 0.5, 0.5, 0.5, 1.0);
1217 	cairo_rectangle (ctx, h_size, 0, h_size, h_size);
1218 	cairo_fill (ctx);
1219 	cairo_rectangle (ctx, 0, h_size, h_size, h_size);
1220 	cairo_fill (ctx);
1221 
1222 	pattern = cairo_pattern_create_for_surface (surface);
1223 	cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
1224 
1225 	cairo_surface_destroy (surface);
1226 	cairo_destroy (ctx);
1227 
1228 	return pattern;
1229 }
1230 
1231 
1232 void
_cairo_draw_thumbnail_frame(cairo_t * cr,int x,int y,int width,int height)1233 _cairo_draw_thumbnail_frame (cairo_t *cr,
1234 			     int      x,
1235 			     int      y,
1236 			     int      width,
1237 			     int      height)
1238 {
1239 	cairo_save (cr);
1240 	cairo_translate (cr, 0.5, 0.5);
1241 	cairo_set_line_width (cr, 0.5);
1242 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
1243 
1244 	/* the drop shadow */
1245 
1246 	cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.33);
1247 	_cairo_draw_rounded_box (cr,
1248 				 x + 2,
1249 				 y + 2,
1250 				 width - 1,
1251 				 height - 1,
1252 				 0);
1253 	cairo_fill (cr);
1254 
1255 	/* the outer frame */
1256 
1257 	cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1258 	_cairo_draw_rounded_box (cr,
1259 				 x,
1260 				 y,
1261 				 width - 1,
1262 				 height - 1,
1263 				 0);
1264 	cairo_fill_preserve (cr);
1265 
1266 	cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.55);
1267 	cairo_stroke (cr);
1268 
1269 	cairo_restore (cr);
1270 }
1271 
1272 
1273 void
_cairo_draw_film_background(cairo_t * cr,int x,int y,int width,int height)1274 _cairo_draw_film_background (cairo_t *cr,
1275 			     int      x,
1276 			     int      y,
1277 			     int      width,
1278 			     int      height)
1279 {
1280 	cairo_save (cr);
1281 
1282 	/* the drop shadow */
1283 
1284 	cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.33);
1285 	cairo_rectangle (cr,
1286 			 x + 2,
1287 			 y + 2,
1288 			 width,
1289 			 height);
1290 	cairo_fill (cr);
1291 
1292 	/* dark background */
1293 
1294 	cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
1295 	cairo_rectangle (cr,
1296 			 x,
1297 			 y ,
1298 			 width,
1299 			 height);
1300 	cairo_fill (cr);
1301 
1302 	cairo_restore (cr);
1303 }
1304 
1305 
1306 static cairo_pattern_t *
_cairo_film_pattern_create(void)1307 _cairo_film_pattern_create (void)
1308 {
1309 	static cairo_pattern_t *film_pattern = NULL;
1310 	cairo_pattern_t        *pattern;
1311 	static GMutex           mutex;
1312 
1313 	g_mutex_lock (&mutex);
1314 	if (film_pattern == NULL) {
1315 		char            *filename;
1316 		cairo_surface_t *surface;
1317 
1318 		filename = g_build_filename (GTHUMB_ICON_DIR, "filmholes.png", NULL);
1319 		surface = cairo_image_surface_create_from_png (filename);
1320 		film_pattern = cairo_pattern_create_for_surface (surface);
1321 		cairo_pattern_set_filter (film_pattern, CAIRO_FILTER_GOOD);
1322 		cairo_pattern_set_extend (film_pattern, CAIRO_EXTEND_REPEAT);
1323 
1324 		cairo_surface_destroy (surface);
1325 		g_free (filename);
1326 
1327 	}
1328 	pattern = cairo_pattern_reference (film_pattern);
1329 	g_mutex_unlock (&mutex);
1330 
1331 	return pattern;
1332 }
1333 
1334 
1335 void
_cairo_draw_film_foreground(cairo_t * cr,int x,int y,int width,int height,int thumbnail_size)1336 _cairo_draw_film_foreground (cairo_t *cr,
1337 			     int      x,
1338 			     int      y,
1339 			     int      width,
1340 			     int      height,
1341 			     int      thumbnail_size)
1342 {
1343 	cairo_pattern_t *pattern;
1344 	double           film_scale;
1345 	cairo_matrix_t   matrix;
1346 	double           film_strip;
1347 
1348 	/* left film strip */
1349 
1350 	pattern = _cairo_film_pattern_create ();
1351 
1352 	if (thumbnail_size > 128)
1353 		film_scale = 256.0 / thumbnail_size;
1354 	else
1355 		film_scale = 128.0 / thumbnail_size;
1356 	film_strip = 9.0 / film_scale;
1357 
1358 	cairo_matrix_init_identity (&matrix);
1359 	cairo_matrix_scale (&matrix, film_scale, film_scale);
1360 	cairo_matrix_translate (&matrix, -x, 0);
1361 	cairo_pattern_set_matrix (pattern, &matrix);
1362 	cairo_set_source (cr, pattern);
1363 	cairo_rectangle (cr,
1364 			 x,
1365 			 y,
1366 			 film_strip,
1367 			 height);
1368 	cairo_fill (cr);
1369 
1370 	/* right film strip */
1371 
1372 	x = x + width - film_strip;
1373 	cairo_matrix_init_identity (&matrix);
1374 	cairo_matrix_scale (&matrix, film_scale, film_scale);
1375 	cairo_matrix_translate (&matrix, -x, 0);
1376 	cairo_pattern_set_matrix (pattern, &matrix);
1377 	cairo_set_source (cr, pattern);
1378 	cairo_rectangle (cr,
1379 			 x,
1380 			 y,
1381 			 film_strip,
1382 			 height);
1383 	cairo_fill (cr);
1384 
1385 	cairo_pattern_destroy (pattern);
1386 }
1387 
1388 
1389 #define DRAG_ICON_THUMBNAIL_OFFSET 4
1390 
1391 
1392 cairo_surface_t *
_cairo_create_dnd_icon(cairo_surface_t * image,int icon_size,ItemStyle style,gboolean multi_dnd)1393 _cairo_create_dnd_icon (cairo_surface_t *image,
1394 			int              icon_size,
1395 			ItemStyle        style,
1396 			gboolean         multi_dnd)
1397 {
1398 	cairo_rectangle_int_t  thumbnail_rect;
1399 	cairo_surface_t       *thumbnail;
1400 	cairo_rectangle_int_t  icon_rect;
1401 	int                    icon_padding;
1402 	cairo_rectangle_int_t  frame_rect;
1403 	cairo_surface_t       *icon;
1404 	cairo_t               *cr;
1405 
1406 	thumbnail_rect.width = cairo_image_surface_get_width (image);
1407 	thumbnail_rect.height = cairo_image_surface_get_height (image);
1408 	if (scale_keeping_ratio (&thumbnail_rect.width, &thumbnail_rect.height, icon_size, icon_size, FALSE))
1409 		thumbnail = _cairo_image_surface_scale_fast (image, thumbnail_rect.width, thumbnail_rect.height);
1410 	else
1411 		thumbnail = cairo_surface_reference (image);
1412 
1413 	switch (style) {
1414 	case ITEM_STYLE_ICON:
1415 		icon_padding = 8;
1416 		icon_rect.width = icon_size + icon_padding;
1417 		icon_rect.height = icon_size + icon_padding;
1418 		thumbnail_rect.x = round ((double) (icon_rect.width - thumbnail_rect.width) / 2.0);
1419 		thumbnail_rect.y = round ((double) (icon_rect.height - thumbnail_rect.height) / 2.0);
1420 		frame_rect.x = 0;
1421 		frame_rect.y = 0;
1422 		frame_rect.width = icon_rect.width;
1423 		frame_rect.height = icon_rect.height;
1424 		break;
1425 
1426 	case ITEM_STYLE_IMAGE:
1427 		icon_padding = 8; /* padding for the frame border */
1428 		icon_rect.width = thumbnail_rect.width + icon_padding;
1429 		icon_rect.height = thumbnail_rect.height + icon_padding;
1430 		thumbnail_rect.x = 3;
1431 		thumbnail_rect.y = 3;
1432 		frame_rect.x = 0;
1433 		frame_rect.y = 0;
1434 		frame_rect.width = thumbnail_rect.width + icon_padding - 2;
1435 		frame_rect.height = thumbnail_rect.height + icon_padding - 2;
1436 		break;
1437 
1438 	case ITEM_STYLE_VIDEO:
1439 		icon_padding = 4; /* padding for the drop shadow effect */
1440 		icon_rect.width = thumbnail_rect.width + icon_padding;
1441 		icon_rect.height = icon_size + icon_padding;
1442 		thumbnail_rect.x = 0;
1443 		thumbnail_rect.y = round ((double) (icon_size - thumbnail_rect.height) / 2.0);
1444 		frame_rect.x = thumbnail_rect.x;
1445 		frame_rect.y = 0;
1446 		frame_rect.width = thumbnail_rect.width;
1447 		frame_rect.height = icon_size;
1448 		break;
1449 	}
1450 
1451 	if (multi_dnd) {
1452 		icon_rect.width += DRAG_ICON_THUMBNAIL_OFFSET;
1453 		icon_rect.height += DRAG_ICON_THUMBNAIL_OFFSET;
1454 	}
1455 	icon = _cairo_image_surface_create (CAIRO_FORMAT_ARGB32, icon_rect.width, icon_rect.height);
1456 	cr = cairo_create (icon);
1457 
1458 	switch (style) {
1459 	case ITEM_STYLE_ICON:
1460 		cairo_save (cr);
1461 		cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.2);
1462 		_cairo_draw_rounded_box (cr, frame_rect.x, frame_rect.y, frame_rect.width, frame_rect.height, 4);
1463 		cairo_fill (cr);
1464 		cairo_restore (cr);
1465 		break;
1466 
1467 	case ITEM_STYLE_IMAGE:
1468 		if (multi_dnd)
1469 			_cairo_draw_thumbnail_frame (cr, frame_rect.x + DRAG_ICON_THUMBNAIL_OFFSET, frame_rect.y + DRAG_ICON_THUMBNAIL_OFFSET, frame_rect.width, frame_rect.height);
1470 		_cairo_draw_thumbnail_frame (cr, frame_rect.x, frame_rect.y, frame_rect.width, frame_rect.height);
1471 		break;
1472 
1473 	case ITEM_STYLE_VIDEO:
1474 		_cairo_draw_film_background (cr, frame_rect.x, frame_rect.y, frame_rect.width, frame_rect.height);
1475 		break;
1476 	}
1477 
1478 	cairo_save (cr);
1479 	cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
1480 	cairo_set_source_surface (cr, thumbnail, thumbnail_rect.x, thumbnail_rect.y);
1481 	cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
1482 	cairo_rectangle (cr, thumbnail_rect.x, thumbnail_rect.y, thumbnail_rect.width, thumbnail_rect.height);
1483 	cairo_fill (cr);
1484 	cairo_restore (cr);
1485 
1486 	if (style == ITEM_STYLE_VIDEO)
1487 		_cairo_draw_film_foreground (cr, frame_rect.x, frame_rect.y, frame_rect.width, frame_rect.height, icon_size);
1488 
1489 	cairo_surface_set_device_offset (icon, -icon_rect.width / 2, -icon_rect.height / 2);
1490 
1491 	cairo_surface_destroy (thumbnail);
1492 	cairo_destroy (cr);
1493 
1494 	return icon;
1495 }
1496