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 * Return value: (transfer full): A newly-created pixbuf
52 **/
53GdkPixbuf *
54gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf,
55 gboolean substitute_color,
56 guchar r,
57 guchar g,
58 guchar b)
59{
60 GdkPixbuf *new_pixbuf;
61 int x, y;
62 const guint8 *src_pixels;
63 guint8 *ret_pixels;
64 const guchar *src;
65 guchar *dest;
66
67 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
68 g_return_val_if_fail (pixbuf->colorspace == GDK_COLORSPACE_RGB, NULL);
69 g_return_val_if_fail (pixbuf->n_channels == 3 || pixbuf->n_channels == 4, NULL);
70 g_return_val_if_fail (pixbuf->bits_per_sample == 8, NULL);
71
72 src_pixels = gdk_pixbuf_read_pixels (pixbuf);
73
74 if (pixbuf->has_alpha) {
75 new_pixbuf = gdk_pixbuf_copy (pixbuf);
76 if (!new_pixbuf)
77 return NULL;
78
79 if (!substitute_color)
80 return new_pixbuf;
81 } else {
82 new_pixbuf = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, TRUE, bits_per_sample: 8, width: pixbuf->width, height: pixbuf->height);
83 }
84
85 if (!new_pixbuf)
86 return NULL;
87
88 ret_pixels = gdk_pixbuf_get_pixels (pixbuf: new_pixbuf);
89
90 for (y = 0; y < pixbuf->height; y++, src_pixels += pixbuf->rowstride, ret_pixels += new_pixbuf->rowstride) {
91 guchar tr, tg, tb;
92
93 src = src_pixels;
94 dest = ret_pixels;
95
96 if (pixbuf->has_alpha) {
97 /* Just subst color, we already copied everything else */
98 for (x = 0; x < pixbuf->width; x++) {
99 if (src[0] == r && src[1] == g && src[2] == b)
100 dest[3] = 0;
101 src += 4;
102 dest += 4;
103 }
104 } else {
105 for (x = 0; x < pixbuf->width; x++) {
106 tr = *dest++ = *src++;
107 tg = *dest++ = *src++;
108 tb = *dest++ = *src++;
109
110 if (substitute_color && tr == r && tg == g && tb == b)
111 *dest++ = 0;
112 else
113 *dest++ = 255;
114 }
115 }
116 }
117
118 return new_pixbuf;
119}
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 **/
140void
141gdk_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{
147 g_return_if_fail (src_pixbuf != NULL);
148 g_return_if_fail (dest_pixbuf != NULL);
149
150 g_return_if_fail (src_x >= 0 && src_x + width <= src_pixbuf->width);
151 g_return_if_fail (src_y >= 0 && src_y + height <= src_pixbuf->height);
152
153 g_return_if_fail (dest_x >= 0 && dest_x + width <= dest_pixbuf->width);
154 g_return_if_fail (dest_y >= 0 && dest_y + height <= dest_pixbuf->height);
155
156 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 gdk_pixbuf_scale (src: src_pixbuf,
161 dest: dest_pixbuf,
162 dest_x, dest_y,
163 dest_width: width, dest_height: height,
164 offset_x: (double) (dest_x - src_x),
165 offset_y: (double) (dest_y - src_y),
166 scale_x: 1.0, scale_y: 1.0,
167 interp_type: GDK_INTERP_NEAREST);
168}
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 **/
195void
196gdk_pixbuf_saturate_and_pixelate (const GdkPixbuf *src,
197 GdkPixbuf *dest,
198 gfloat saturation,
199 gboolean pixelate)
200{
201 /* NOTE that src and dest MAY be the same pixbuf! */
202
203 g_return_if_fail (GDK_IS_PIXBUF (src));
204 g_return_if_fail (GDK_IS_PIXBUF (dest));
205 g_return_if_fail (gdk_pixbuf_get_height (src) == gdk_pixbuf_get_height (dest));
206 g_return_if_fail (gdk_pixbuf_get_width (src) == gdk_pixbuf_get_width (dest));
207 g_return_if_fail (gdk_pixbuf_get_has_alpha (src) == gdk_pixbuf_get_has_alpha (dest));
208 g_return_if_fail (gdk_pixbuf_get_colorspace (src) == gdk_pixbuf_get_colorspace (dest));
209
210 if (saturation == 1.0 && !pixelate) {
211 if (dest != src)
212 gdk_pixbuf_copy_area (src_pixbuf: src, src_x: 0, src_y: 0,
213 width: gdk_pixbuf_get_width (pixbuf: src),
214 height: gdk_pixbuf_get_height (pixbuf: src),
215 dest_pixbuf: dest, dest_x: 0, dest_y: 0);
216 } else {
217 int i, j, t;
218 int width, height, has_alpha, src_rowstride, dest_rowstride, bytes_per_pixel;
219 const guchar *src_line;
220 guchar *dest_line;
221 const guchar *src_pixel;
222 guchar *dest_pixel;
223 guchar intensity;
224
225 has_alpha = gdk_pixbuf_get_has_alpha (pixbuf: src);
226 bytes_per_pixel = has_alpha ? 4 : 3;
227 width = gdk_pixbuf_get_width (pixbuf: src);
228 height = gdk_pixbuf_get_height (pixbuf: src);
229 src_rowstride = gdk_pixbuf_get_rowstride (pixbuf: src);
230 dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf: dest);
231
232 dest_line = gdk_pixbuf_get_pixels (pixbuf: dest);
233 src_line = gdk_pixbuf_read_pixels (pixbuf: src);
234
235#define DARK_FACTOR 0.7
236#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
237#define CLAMP_UCHAR(v) (t = (v), CLAMP (t, 0, 255))
238#define SATURATE(v) ((1.0 - saturation) * intensity + saturation * (v))
239
240 for (i = 0 ; i < height ; i++) {
241 src_pixel = src_line;
242 src_line += src_rowstride;
243 dest_pixel = dest_line;
244 dest_line += dest_rowstride;
245
246 for (j = 0 ; j < width ; j++) {
247 intensity = INTENSITY (src_pixel[0], src_pixel[1], src_pixel[2]);
248 if (pixelate && (i + j) % 2 == 0) {
249 dest_pixel[0] = intensity / 2 + 127;
250 dest_pixel[1] = intensity / 2 + 127;
251 dest_pixel[2] = intensity / 2 + 127;
252 } else if (pixelate) {
253 dest_pixel[0] = CLAMP_UCHAR ((SATURATE (src_pixel[0])) * DARK_FACTOR);
254 dest_pixel[1] = CLAMP_UCHAR ((SATURATE (src_pixel[1])) * DARK_FACTOR);
255 dest_pixel[2] = CLAMP_UCHAR ((SATURATE (src_pixel[2])) * DARK_FACTOR);
256 } else {
257 dest_pixel[0] = CLAMP_UCHAR (SATURATE (src_pixel[0]));
258 dest_pixel[1] = CLAMP_UCHAR (SATURATE (src_pixel[1]));
259 dest_pixel[2] = CLAMP_UCHAR (SATURATE (src_pixel[2]));
260 }
261
262 if (has_alpha)
263 dest_pixel[3] = src_pixel[3];
264
265 src_pixel += bytes_per_pixel;
266 dest_pixel += bytes_per_pixel;
267 }
268 }
269 }
270}
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 value: (transfer full) (nullable): A newly-created pixbuf
289 *
290 * Since: 2.12
291 **/
292GdkPixbuf *
293gdk_pixbuf_apply_embedded_orientation (GdkPixbuf *src)
294{
295 const gchar *orientation_string;
296 int transform = 0;
297 GdkPixbuf *temp;
298 GdkPixbuf *dest;
299
300 g_return_val_if_fail (GDK_IS_PIXBUF (src), NULL);
301
302 /* Read the orientation option associated with the pixbuf */
303 orientation_string = gdk_pixbuf_get_option (pixbuf: src, key: "orientation");
304
305 if (orientation_string) {
306 /* If an orientation option was found, convert the
307 orientation string into an integer. */
308 transform = (int) g_ascii_strtoll (nptr: orientation_string, NULL, base: 10);
309 }
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 switch (transform) {
315 case 1:
316 dest = src;
317 g_object_ref (dest);
318 break;
319 case 2:
320 dest = gdk_pixbuf_flip (src, TRUE);
321 break;
322 case 3:
323 dest = gdk_pixbuf_rotate_simple (src, angle: GDK_PIXBUF_ROTATE_UPSIDEDOWN);
324 break;
325 case 4:
326 dest = gdk_pixbuf_flip (src, FALSE);
327 break;
328 case 5:
329 temp = gdk_pixbuf_rotate_simple (src, angle: GDK_PIXBUF_ROTATE_CLOCKWISE);
330 dest = gdk_pixbuf_flip (src: temp, TRUE);
331 g_object_unref (object: temp);
332 break;
333 case 6:
334 dest = gdk_pixbuf_rotate_simple (src, angle: GDK_PIXBUF_ROTATE_CLOCKWISE);
335 break;
336 case 7:
337 temp = gdk_pixbuf_rotate_simple (src, angle: GDK_PIXBUF_ROTATE_CLOCKWISE);
338 dest = gdk_pixbuf_flip (src: temp, FALSE);
339 g_object_unref (object: temp);
340 break;
341 case 8:
342 dest = gdk_pixbuf_rotate_simple (src, angle: GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
343 break;
344 default:
345 /* if no orientation tag was present */
346 dest = src;
347 g_object_ref (dest);
348 break;
349 }
350
351 return dest;
352}
353
354#ifdef GDK_PIXBUF_RELOCATABLE
355
356static const gchar *
357get_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
383void
384_gdk_pixbuf_init_gettext (void)
385{
386 static gsize gettext_initialized = FALSE;
387
388 if (G_UNLIKELY (g_once_init_enter (&gettext_initialized))) {
389 bindtextdomain (GETTEXT_PACKAGE, GDK_PIXBUF_LOCALEDIR);
390#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
391 bind_textdomain_codeset (GETTEXT_PACKAGE, codeset: "UTF-8");
392#endif
393 g_once_init_leave (&gettext_initialized, TRUE);
394 }
395}
396
397const gchar *
398gdk_pixbuf_gettext (const gchar *msgid)
399{
400 return g_dgettext (GETTEXT_PACKAGE, msgid);
401}
402

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/gdk-pixbuf-util.c