/src/gdk-pixbuf/gdk-pixbuf/gdk-pixbuf-util.c
Line | Count | Source |
1 | | /* GdkPixbuf library - Utilities and miscellaneous convenience functions |
2 | | * |
3 | | * Copyright (C) 1999 The Free Software Foundation |
4 | | * |
5 | | * Authors: Federico Mena-Quintero <federico@gimp.org> |
6 | | * Cody Russell <bratsche@gnome.org> |
7 | | * |
8 | | * This library is free software; you can redistribute it and/or |
9 | | * modify it under the terms of the GNU Lesser General Public |
10 | | * License as published by the Free Software Foundation; either |
11 | | * version 2 of the License, or (at your option) any later version. |
12 | | * |
13 | | * This library 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 GNU |
16 | | * Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public |
19 | | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
20 | | */ |
21 | | |
22 | | #include "config.h" |
23 | | #include <string.h> |
24 | | #include <libintl.h> |
25 | | |
26 | | #include "gdk-pixbuf-transform.h" |
27 | | #include "gdk-pixbuf-private.h" |
28 | | |
29 | | /** |
30 | | * gdk_pixbuf_add_alpha: |
31 | | * @pixbuf: A #GdkPixbuf. |
32 | | * @substitute_color: Whether to set a color to zero opacity. |
33 | | * @r: Red value to substitute. |
34 | | * @g: Green value to substitute. |
35 | | * @b: Blue value to substitute. |
36 | | * |
37 | | * Takes an existing pixbuf and adds an alpha channel to it. |
38 | | * |
39 | | * If the existing pixbuf already had an alpha channel, the channel |
40 | | * values are copied from the original; otherwise, the alpha channel |
41 | | * is initialized to 255 (full opacity). |
42 | | * |
43 | | * If `substitute_color` is `TRUE`, then the color specified by the |
44 | | * (`r`, `g`, `b`) arguments will be assigned zero opacity. That is, |
45 | | * if you pass `(255, 255, 255)` for the substitute color, all white |
46 | | * pixels will become fully transparent. |
47 | | * |
48 | | * If `substitute_color` is `FALSE`, then the (`r`, `g`, `b`) arguments |
49 | | * will be ignored. |
50 | | * |
51 | | * Returns: (transfer full) (nullable): A newly-created pixbuf |
52 | | **/ |
53 | | GdkPixbuf * |
54 | | gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf, |
55 | | gboolean substitute_color, |
56 | | guchar r, |
57 | | guchar g, |
58 | | guchar b) |
59 | 0 | { |
60 | 0 | GdkPixbuf *new_pixbuf; |
61 | 0 | int x, y; |
62 | 0 | const guint8 *src_pixels; |
63 | 0 | guint8 *ret_pixels; |
64 | 0 | const guchar *src; |
65 | 0 | guchar *dest; |
66 | |
|
67 | 0 | g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); |
68 | 0 | g_return_val_if_fail (pixbuf->colorspace == GDK_COLORSPACE_RGB, NULL); |
69 | 0 | g_return_val_if_fail (pixbuf->n_channels == 3 || pixbuf->n_channels == 4, NULL); |
70 | 0 | g_return_val_if_fail (pixbuf->bits_per_sample == 8, NULL); |
71 | | |
72 | 0 | src_pixels = gdk_pixbuf_read_pixels (pixbuf); |
73 | |
|
74 | 0 | if (pixbuf->has_alpha) { |
75 | 0 | new_pixbuf = gdk_pixbuf_copy (pixbuf); |
76 | 0 | if (!new_pixbuf) |
77 | 0 | return NULL; |
78 | | |
79 | 0 | if (!substitute_color) |
80 | 0 | return new_pixbuf; |
81 | 0 | } else { |
82 | 0 | new_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, pixbuf->width, pixbuf->height); |
83 | 0 | } |
84 | | |
85 | 0 | if (!new_pixbuf) |
86 | 0 | return NULL; |
87 | | |
88 | 0 | ret_pixels = gdk_pixbuf_get_pixels (new_pixbuf); |
89 | |
|
90 | 0 | for (y = 0; y < pixbuf->height; y++, src_pixels += pixbuf->rowstride, ret_pixels += new_pixbuf->rowstride) { |
91 | 0 | guchar tr, tg, tb; |
92 | |
|
93 | 0 | src = src_pixels; |
94 | 0 | dest = ret_pixels; |
95 | |
|
96 | 0 | if (pixbuf->has_alpha) { |
97 | | /* Just subst color, we already copied everything else */ |
98 | 0 | for (x = 0; x < pixbuf->width; x++) { |
99 | 0 | if (src[0] == r && src[1] == g && src[2] == b) |
100 | 0 | dest[3] = 0; |
101 | 0 | src += 4; |
102 | 0 | dest += 4; |
103 | 0 | } |
104 | 0 | } else { |
105 | 0 | for (x = 0; x < pixbuf->width; x++) { |
106 | 0 | tr = *dest++ = *src++; |
107 | 0 | tg = *dest++ = *src++; |
108 | 0 | tb = *dest++ = *src++; |
109 | |
|
110 | 0 | if (substitute_color && tr == r && tg == g && tb == b) |
111 | 0 | *dest++ = 0; |
112 | 0 | else |
113 | 0 | *dest++ = 255; |
114 | 0 | } |
115 | 0 | } |
116 | 0 | } |
117 | |
|
118 | 0 | return new_pixbuf; |
119 | 0 | } |
120 | | |
121 | | /** |
122 | | * gdk_pixbuf_copy_area: |
123 | | * @src_pixbuf: Source pixbuf. |
124 | | * @src_x: Source X coordinate within @src_pixbuf. |
125 | | * @src_y: Source Y coordinate within @src_pixbuf. |
126 | | * @width: Width of the area to copy. |
127 | | * @height: Height of the area to copy. |
128 | | * @dest_pixbuf: Destination pixbuf. |
129 | | * @dest_x: X coordinate within @dest_pixbuf. |
130 | | * @dest_y: Y coordinate within @dest_pixbuf. |
131 | | * |
132 | | * Copies a rectangular area from `src_pixbuf` to `dest_pixbuf`. |
133 | | * |
134 | | * Conversion of pixbuf formats is done automatically. |
135 | | * |
136 | | * If the source rectangle overlaps the destination rectangle on the |
137 | | * same pixbuf, it will be overwritten during the copy operation. |
138 | | * Therefore, you can not use this function to scroll a pixbuf. |
139 | | **/ |
140 | | void |
141 | | gdk_pixbuf_copy_area (const GdkPixbuf *src_pixbuf, |
142 | | int src_x, int src_y, |
143 | | int width, int height, |
144 | | GdkPixbuf *dest_pixbuf, |
145 | | int dest_x, int dest_y) |
146 | 10.6k | { |
147 | 10.6k | g_return_if_fail (src_pixbuf != NULL); |
148 | 10.6k | g_return_if_fail (dest_pixbuf != NULL); |
149 | | |
150 | 10.6k | g_return_if_fail (src_x >= 0 && src_x + width <= src_pixbuf->width); |
151 | 10.6k | g_return_if_fail (src_y >= 0 && src_y + height <= src_pixbuf->height); |
152 | | |
153 | 9.02k | g_return_if_fail (dest_x >= 0 && dest_x + width <= dest_pixbuf->width); |
154 | 2.39k | g_return_if_fail (dest_y >= 0 && dest_y + height <= dest_pixbuf->height); |
155 | | |
156 | 2.39k | g_return_if_fail (!(gdk_pixbuf_get_has_alpha (src_pixbuf) && !gdk_pixbuf_get_has_alpha (dest_pixbuf))); |
157 | | |
158 | | /* This will perform format conversions automatically */ |
159 | | |
160 | 2.39k | gdk_pixbuf_scale (src_pixbuf, |
161 | 2.39k | dest_pixbuf, |
162 | 2.39k | dest_x, dest_y, |
163 | 2.39k | width, height, |
164 | 2.39k | (double) (dest_x - src_x), |
165 | 2.39k | (double) (dest_y - src_y), |
166 | 2.39k | 1.0, 1.0, |
167 | 2.39k | GDK_INTERP_NEAREST); |
168 | 2.39k | } |
169 | | |
170 | | |
171 | | |
172 | | /** |
173 | | * gdk_pixbuf_saturate_and_pixelate: |
174 | | * @src: source image |
175 | | * @dest: place to write modified version of @src |
176 | | * @saturation: saturation factor |
177 | | * @pixelate: whether to pixelate |
178 | | * |
179 | | * Modifies saturation and optionally pixelates `src`, placing the result in |
180 | | * `dest`. |
181 | | * |
182 | | * The `src` and `dest` pixbufs must have the same image format, size, and |
183 | | * rowstride. |
184 | | * |
185 | | * The `src` and `dest` arguments may be the same pixbuf with no ill effects. |
186 | | * |
187 | | * If `saturation` is 1.0 then saturation is not changed. If it's less than 1.0, |
188 | | * saturation is reduced (the image turns toward grayscale); if greater than |
189 | | * 1.0, saturation is increased (the image gets more vivid colors). |
190 | | * |
191 | | * If `pixelate` is `TRUE`, then pixels are faded in a checkerboard pattern to |
192 | | * create a pixelated image. |
193 | | * |
194 | | **/ |
195 | | void |
196 | | gdk_pixbuf_saturate_and_pixelate (const GdkPixbuf *src, |
197 | | GdkPixbuf *dest, |
198 | | gfloat saturation, |
199 | | gboolean pixelate) |
200 | 0 | { |
201 | | /* NOTE that src and dest MAY be the same pixbuf! */ |
202 | | |
203 | 0 | g_return_if_fail (GDK_IS_PIXBUF (src)); |
204 | 0 | g_return_if_fail (GDK_IS_PIXBUF (dest)); |
205 | 0 | g_return_if_fail (gdk_pixbuf_get_height (src) == gdk_pixbuf_get_height (dest)); |
206 | 0 | g_return_if_fail (gdk_pixbuf_get_width (src) == gdk_pixbuf_get_width (dest)); |
207 | 0 | g_return_if_fail (gdk_pixbuf_get_has_alpha (src) == gdk_pixbuf_get_has_alpha (dest)); |
208 | 0 | g_return_if_fail (gdk_pixbuf_get_colorspace (src) == gdk_pixbuf_get_colorspace (dest)); |
209 | | |
210 | 0 | if (saturation == 1.0 && !pixelate) { |
211 | 0 | if (dest != src) |
212 | 0 | gdk_pixbuf_copy_area (src, 0, 0, |
213 | 0 | gdk_pixbuf_get_width (src), |
214 | 0 | gdk_pixbuf_get_height (src), |
215 | 0 | dest, 0, 0); |
216 | 0 | } else { |
217 | 0 | int i, j, t; |
218 | 0 | int width, height, has_alpha, src_rowstride, dest_rowstride, bytes_per_pixel; |
219 | 0 | const guchar *src_line; |
220 | 0 | guchar *dest_line; |
221 | 0 | const guchar *src_pixel; |
222 | 0 | guchar *dest_pixel; |
223 | 0 | guchar intensity; |
224 | |
|
225 | 0 | has_alpha = gdk_pixbuf_get_has_alpha (src); |
226 | 0 | bytes_per_pixel = has_alpha ? 4 : 3; |
227 | 0 | width = gdk_pixbuf_get_width (src); |
228 | 0 | height = gdk_pixbuf_get_height (src); |
229 | 0 | src_rowstride = gdk_pixbuf_get_rowstride (src); |
230 | 0 | dest_rowstride = gdk_pixbuf_get_rowstride (dest); |
231 | | |
232 | 0 | dest_line = gdk_pixbuf_get_pixels (dest); |
233 | 0 | src_line = gdk_pixbuf_read_pixels (src); |
234 | | |
235 | 0 | #define DARK_FACTOR 0.7 |
236 | 0 | #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11) |
237 | 0 | #define CLAMP_UCHAR(v) (t = (v), CLAMP (t, 0, 255)) |
238 | 0 | #define SATURATE(v) ((1.0 - saturation) * intensity + saturation * (v)) |
239 | |
|
240 | 0 | for (i = 0 ; i < height ; i++) { |
241 | 0 | src_pixel = src_line; |
242 | 0 | src_line += src_rowstride; |
243 | 0 | dest_pixel = dest_line; |
244 | 0 | dest_line += dest_rowstride; |
245 | |
|
246 | 0 | for (j = 0 ; j < width ; j++) { |
247 | 0 | intensity = INTENSITY (src_pixel[0], src_pixel[1], src_pixel[2]); |
248 | 0 | if (pixelate && (i + j) % 2 == 0) { |
249 | 0 | dest_pixel[0] = intensity / 2 + 127; |
250 | 0 | dest_pixel[1] = intensity / 2 + 127; |
251 | 0 | dest_pixel[2] = intensity / 2 + 127; |
252 | 0 | } else if (pixelate) { |
253 | 0 | dest_pixel[0] = CLAMP_UCHAR ((SATURATE (src_pixel[0])) * DARK_FACTOR); |
254 | 0 | dest_pixel[1] = CLAMP_UCHAR ((SATURATE (src_pixel[1])) * DARK_FACTOR); |
255 | 0 | dest_pixel[2] = CLAMP_UCHAR ((SATURATE (src_pixel[2])) * DARK_FACTOR); |
256 | 0 | } else { |
257 | 0 | dest_pixel[0] = CLAMP_UCHAR (SATURATE (src_pixel[0])); |
258 | 0 | dest_pixel[1] = CLAMP_UCHAR (SATURATE (src_pixel[1])); |
259 | 0 | dest_pixel[2] = CLAMP_UCHAR (SATURATE (src_pixel[2])); |
260 | 0 | } |
261 | | |
262 | 0 | if (has_alpha) |
263 | 0 | dest_pixel[3] = src_pixel[3]; |
264 | |
|
265 | 0 | src_pixel += bytes_per_pixel; |
266 | 0 | dest_pixel += bytes_per_pixel; |
267 | 0 | } |
268 | 0 | } |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | |
273 | | /** |
274 | | * gdk_pixbuf_apply_embedded_orientation: |
275 | | * @src: a pixbuf with an orientation option |
276 | | * |
277 | | * Takes an existing pixbuf and checks for the presence of an |
278 | | * associated "orientation" option. |
279 | | * |
280 | | * The orientation option may be provided by the JPEG loader (which |
281 | | * reads the exif orientation tag) or the TIFF loader (which reads |
282 | | * the TIFF orientation tag, and compensates it for the partial |
283 | | * transforms performed by libtiff). |
284 | | * |
285 | | * If an orientation option/tag is present, the appropriate transform |
286 | | * will be performed so that the pixbuf is oriented correctly. |
287 | | * |
288 | | * Return: (transfer full) (nullable): A newly-created pixbuf |
289 | | * |
290 | | * Since: 2.12 |
291 | | **/ |
292 | | GdkPixbuf * |
293 | | gdk_pixbuf_apply_embedded_orientation (GdkPixbuf *src) |
294 | 0 | { |
295 | 0 | const gchar *orientation_string; |
296 | 0 | int transform = 0; |
297 | 0 | GdkPixbuf *temp; |
298 | 0 | GdkPixbuf *dest; |
299 | |
|
300 | 0 | g_return_val_if_fail (GDK_IS_PIXBUF (src), NULL); |
301 | | |
302 | | /* Read the orientation option associated with the pixbuf */ |
303 | 0 | orientation_string = gdk_pixbuf_get_option (src, "orientation"); |
304 | |
|
305 | 0 | if (orientation_string) { |
306 | | /* If an orientation option was found, convert the |
307 | | orientation string into an integer. */ |
308 | 0 | transform = (int) g_ascii_strtoll (orientation_string, NULL, 10); |
309 | 0 | } |
310 | | |
311 | | /* Apply the actual transforms, which involve rotations and flips. |
312 | | The meaning of orientation values 1-8 and the required transforms |
313 | | are defined by the TIFF and EXIF (for JPEGs) standards. */ |
314 | 0 | switch (transform) { |
315 | 0 | case 1: |
316 | 0 | dest = src; |
317 | 0 | g_object_ref (dest); |
318 | 0 | break; |
319 | 0 | case 2: |
320 | 0 | dest = gdk_pixbuf_flip (src, TRUE); |
321 | 0 | break; |
322 | 0 | case 3: |
323 | 0 | dest = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_UPSIDEDOWN); |
324 | 0 | break; |
325 | 0 | case 4: |
326 | 0 | dest = gdk_pixbuf_flip (src, FALSE); |
327 | 0 | break; |
328 | 0 | case 5: |
329 | 0 | temp = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_CLOCKWISE); |
330 | 0 | dest = gdk_pixbuf_flip (temp, TRUE); |
331 | 0 | g_object_unref (temp); |
332 | 0 | break; |
333 | 0 | case 6: |
334 | 0 | dest = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_CLOCKWISE); |
335 | 0 | break; |
336 | 0 | case 7: |
337 | 0 | temp = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_CLOCKWISE); |
338 | 0 | dest = gdk_pixbuf_flip (temp, FALSE); |
339 | 0 | g_object_unref (temp); |
340 | 0 | break; |
341 | 0 | case 8: |
342 | 0 | dest = gdk_pixbuf_rotate_simple (src, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); |
343 | 0 | break; |
344 | 0 | default: |
345 | | /* if no orientation tag was present */ |
346 | 0 | dest = src; |
347 | 0 | g_object_ref (dest); |
348 | 0 | break; |
349 | 0 | } |
350 | | |
351 | 0 | return dest; |
352 | 0 | } |
353 | | |
354 | | #ifdef GDK_PIXBUF_RELOCATABLE |
355 | | |
356 | | static const gchar * |
357 | | get_localedir (void) |
358 | | { |
359 | | gchar *temp; |
360 | | |
361 | | temp = g_build_filename (gdk_pixbuf_get_toplevel (), "share/locale", NULL); |
362 | | |
363 | | #ifdef G_OS_WIN32 |
364 | | { |
365 | | gchar *retval; |
366 | | /* The localedir is passed to bindtextdomain() which isn't |
367 | | * UTF-8-aware. |
368 | | */ |
369 | | retval = g_win32_locale_filename_from_utf8 (temp); |
370 | | g_free (temp); |
371 | | return retval; |
372 | | } |
373 | | #else |
374 | | return temp; |
375 | | #endif |
376 | | } |
377 | | |
378 | | #undef GDK_PIXBUF_LOCALEDIR |
379 | | #define GDK_PIXBUF_LOCALEDIR get_localedir () |
380 | | |
381 | | #endif |
382 | | |
383 | | void |
384 | | _gdk_pixbuf_init_gettext (void) |
385 | 5 | { |
386 | 5 | static gsize gettext_initialized = FALSE; |
387 | | |
388 | 5 | if (G_UNLIKELY (g_once_init_enter (&gettext_initialized))) { |
389 | 5 | bindtextdomain (GETTEXT_PACKAGE, GDK_PIXBUF_LOCALEDIR); |
390 | 5 | #ifdef HAVE_BIND_TEXTDOMAIN_CODESET |
391 | 5 | bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); |
392 | 5 | #endif |
393 | 5 | g_once_init_leave (&gettext_initialized, TRUE); |
394 | 5 | } |
395 | 5 | } |
396 | | |
397 | | const gchar * |
398 | | gdk_pixbuf_gettext (const gchar *msgid) |
399 | 0 | { |
400 | 0 | return g_dgettext (GETTEXT_PACKAGE, msgid); |
401 | 0 | } |