Coverage Report

Created: 2026-05-16 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}