1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2001-2009 The 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 <stdio.h>
26 #include <stdlib.h>
27 #define GDK_PIXBUF_ENABLE_BACKEND 1
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include "cairo-utils.h"
30 #include "pixbuf-utils.h"
31 #include "gimp-op.h"
32 
33 
34 GdkPixbuf *
_gdk_pixbuf_new_from_cairo_context(cairo_t * cr)35 _gdk_pixbuf_new_from_cairo_context (cairo_t *cr)
36 {
37 	return _gdk_pixbuf_new_from_cairo_surface (cairo_get_target (cr));
38 }
39 
40 
41 /* Started from from http://www.gtkforums.com/about5204.html
42  * Author: tadeboro */
43 GdkPixbuf *
_gdk_pixbuf_new_from_cairo_surface(cairo_surface_t * surface)44 _gdk_pixbuf_new_from_cairo_surface (cairo_surface_t *surface)
45 {
46 	int            width;
47 	int            height;
48 	int            s_stride;
49 	unsigned char *s_pixels;
50 	GdkPixbuf     *pixbuf;
51 	int            p_stride;
52 	guchar        *p_pixels;
53 	int            p_n_channels;
54 
55 	if (surface == NULL)
56 		return NULL;
57 
58 	if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
59 		return NULL;
60 
61 	width = cairo_image_surface_get_width (surface);
62 	height = cairo_image_surface_get_height (surface);
63 	s_stride = cairo_image_surface_get_stride (surface);
64 	s_pixels = _cairo_image_surface_flush_and_get_data (surface);
65 
66 	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, _cairo_image_surface_get_has_alpha (surface), 8, width, height);
67 	p_stride = gdk_pixbuf_get_rowstride (pixbuf);
68 	p_pixels = gdk_pixbuf_get_pixels (pixbuf);
69 	p_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
70 
71 	while (height--) {
72 		guchar *s_iter = s_pixels;
73 	        guchar *p_iter = p_pixels;
74 	        int     i, temp;
75 
76 	        for (i = 0; i < width; i++) {
77 	        	gdouble alpha_factor = (gdouble) 0xff / s_iter[CAIRO_ALPHA];
78 
79 	        	p_iter[0] = CLAMP_PIXEL (alpha_factor * s_iter[CAIRO_RED]);
80 	        	p_iter[1] = CLAMP_PIXEL (alpha_factor * s_iter[CAIRO_GREEN]);
81 	        	p_iter[2] = CLAMP_PIXEL (alpha_factor * s_iter[CAIRO_BLUE]);
82 	        	if (p_n_channels == 4)
83 	        		p_iter[3] = s_iter[CAIRO_ALPHA];
84 
85 	        	s_iter += 4;
86 	        	p_iter += p_n_channels;
87 		}
88 
89 		s_pixels += s_stride;
90 		p_pixels += p_stride;
91 	}
92 
93 	/* copy the known metadata from the surface to the pixbuf */
94 
95 	{
96 		cairo_surface_metadata_t *metadata;
97 
98 		metadata = _cairo_image_surface_get_metadata (surface);
99 		if ((metadata->thumbnail.image_width > 0) && (metadata->thumbnail.image_height > 0)) {
100 			char *value;
101 
102 			value = g_strdup_printf ("%d", metadata->thumbnail.image_width);
103 			gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", value);
104 			g_free (value);
105 
106 			value = g_strdup_printf ("%d", metadata->thumbnail.image_height);
107 			gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", value);
108 			g_free (value);
109 		}
110 	}
111 
112 	return pixbuf;
113 }
114 
115 
116 /* The gdk_pixbuf scaling routines do not handle large-ratio downscaling
117    very well. Memory usage explodes and the application may freeze or crash.
118    For scale-down ratios in excess of 100, do the scale in two steps.
119    It is faster and safer that way. See bug 80925 for background info. */
120 GdkPixbuf*
_gdk_pixbuf_scale_simple_safe(const GdkPixbuf * src,int dest_width,int dest_height,GdkInterpType interp_type)121 _gdk_pixbuf_scale_simple_safe (const GdkPixbuf *src,
122 			       int              dest_width,
123 			       int              dest_height,
124 			       GdkInterpType    interp_type)
125 {
126 	GdkPixbuf* temp_pixbuf1;
127 	GdkPixbuf* temp_pixbuf2;
128 	int        x_ratio, y_ratio;
129 	int        temp_width = dest_width, temp_height = dest_height;
130 
131 	g_assert (dest_width >= 1);
132 	g_assert (dest_height >= 1);
133 
134 	x_ratio = gdk_pixbuf_get_width (src) / dest_width;
135 	y_ratio = gdk_pixbuf_get_height (src) / dest_height;
136 
137 	if (x_ratio > 100)
138 		/* Scale down to 10x the requested size first. */
139 		temp_width = 10 * dest_width;
140 
141 	if (y_ratio > 100)
142 		/* Scale down to 10x the requested size first. */
143 		temp_height = 10 * dest_height;
144 
145 	if ( (temp_width != dest_width) || (temp_height != dest_height)) {
146 		temp_pixbuf1 = gdk_pixbuf_scale_simple (src, temp_width, temp_height, interp_type);
147 		temp_pixbuf2 = gdk_pixbuf_scale_simple (temp_pixbuf1, dest_width, dest_height, interp_type);
148 		g_object_unref (temp_pixbuf1);
149 	} else
150 		temp_pixbuf2 = gdk_pixbuf_scale_simple (src, dest_width, dest_height, interp_type);
151 
152 	return temp_pixbuf2;
153 }
154 
155 
156 /*
157  * Returns a transformed image.
158  */
159 GdkPixbuf *
_gdk_pixbuf_transform(GdkPixbuf * src,GthTransform transform)160 _gdk_pixbuf_transform (GdkPixbuf    *src,
161 		       GthTransform  transform)
162 {
163 	GdkPixbuf *temp = NULL, *dest = NULL;
164 
165 	if (src == NULL)
166 		return NULL;
167 
168 	switch (transform) {
169 	case GTH_TRANSFORM_NONE:
170 		dest = src;
171 		g_object_ref (dest);
172 		break;
173 	case GTH_TRANSFORM_FLIP_H:
174 		dest = gdk_pixbuf_flip (src, TRUE);
175 		break;
176 	case GTH_TRANSFORM_ROTATE_180:
177 		dest = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
178 		break;
179 	case GTH_TRANSFORM_FLIP_V:
180 		dest = gdk_pixbuf_flip (src, FALSE);
181 		break;
182 	case GTH_TRANSFORM_TRANSPOSE:
183 		temp = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_CLOCKWISE);
184 		dest = gdk_pixbuf_flip (temp, TRUE);
185 		g_object_unref (temp);
186 		break;
187 	case GTH_TRANSFORM_ROTATE_90:
188 		dest = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_CLOCKWISE);
189 		break;
190 	case GTH_TRANSFORM_TRANSVERSE:
191 		temp = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_CLOCKWISE);
192 		dest = gdk_pixbuf_flip (temp, FALSE);
193 		g_object_unref (temp);
194 		break;
195 	case GTH_TRANSFORM_ROTATE_270:
196 		dest = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
197 		break;
198 	default:
199 		dest = src;
200 		g_object_ref (dest);
201 		break;
202 	}
203 
204 	return dest;
205 }
206 
207 
208 char *
_gdk_pixbuf_get_type_from_mime_type(const char * mime_type)209 _gdk_pixbuf_get_type_from_mime_type (const char *mime_type)
210 {
211 	if (mime_type == NULL)
212 		return NULL;
213 
214 	if (g_str_has_prefix (mime_type, "image/x-"))
215 		return g_strdup (mime_type + strlen ("image/x-"));
216 	else if (g_str_has_prefix (mime_type, "image/"))
217 		return g_strdup (mime_type + strlen ("image/"));
218 	else
219 		return g_strdup (mime_type);
220 }
221 
222 
223 gboolean
_gdk_pixbuf_mime_type_is_readable(const char * mime_type)224 _gdk_pixbuf_mime_type_is_readable (const char *mime_type)
225 {
226 	GSList   *formats;
227 	GSList   *scan;
228 	gboolean  result;
229 
230 	if (mime_type == NULL)
231 		return FALSE;
232 
233 	result = FALSE;
234 	formats = gdk_pixbuf_get_formats ();
235 	for (scan = formats; ! result && scan; scan = scan->next) {
236 		GdkPixbufFormat  *format = scan->data;
237 		char            **mime_types;
238 		int               i;
239 
240 		if (gdk_pixbuf_format_is_disabled (format))
241 			continue;
242 
243 		mime_types = gdk_pixbuf_format_get_mime_types (format);
244 		for (i = 0; mime_types[i] != NULL; i++) {
245 			if (strcmp (mime_type, mime_types[i]) == 0) {
246 				result = TRUE;
247 				break;
248 			}
249 		}
250 	}
251 
252 	g_slist_free (formats);
253 
254 	return result;
255 }
256