Coverage Report

Created: 2026-06-13 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-raster-image.cc
Line
Count
Source
1
/*
2
 * Copyright © 2026  Behdad Esfahbod
3
 *
4
 *  This is part of HarfBuzz, a text shaping library.
5
 *
6
 * Permission is hereby granted, without written agreement and without
7
 * license or royalty fees, to use, copy, modify, and distribute this
8
 * software and its documentation for any purpose, provided that the
9
 * above copyright notice and the following two paragraphs appear in
10
 * all copies of this software.
11
 *
12
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16
 * DAMAGE.
17
 *
18
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23
 *
24
 * Author(s): Behdad Esfahbod
25
 */
26
27
#include "hb.hh"
28
29
#include "hb-raster-image.hh"
30
31
#include <math.h>
32
33
/**
34
 * SECTION:hb-raster
35
 * @title: hb-raster
36
 * @short_description: Glyph rasterization
37
 * @include: hb-raster.h
38
 *
39
 * Functions for rasterizing glyph outlines into pixel buffers.
40
 *
41
 * #hb_raster_draw_t rasterizes outline geometry and always outputs
42
 * @HB_RASTER_FORMAT_A8. Typical flow:
43
 *
44
 * |[<!-- language="plain" -->
45
 * hb_raster_draw_t *draw = hb_raster_draw_create_or_fail ();
46
 * hb_raster_draw_set_scale_factor (draw, 64.f, 64.f);
47
 * hb_raster_draw_set_transform (draw, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f);
48
 * hb_raster_draw_set_glyph_extents (draw, &glyph_extents);
49
 * hb_raster_draw_glyph (draw, font, gid, pen_x, pen_y);
50
 * hb_raster_image_t *mask = hb_raster_draw_render (draw);
51
 * ]|
52
 *
53
 * #hb_raster_paint_t renders color paint graphs and always outputs
54
 * @HB_RASTER_FORMAT_BGRA32. Typical flow:
55
 *
56
 * |[<!-- language="plain" -->
57
 * hb_raster_paint_t *paint = hb_raster_paint_create_or_fail ();
58
 * hb_raster_paint_set_scale_factor (paint, 64.f, 64.f);
59
 * hb_raster_paint_set_transform (paint, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f);
60
 * hb_raster_paint_set_foreground (paint, foreground);
61
 * hb_glyph_extents_t glyph_extents;
62
 * hb_font_get_glyph_extents (font, gid, &glyph_extents);
63
 * hb_raster_paint_set_glyph_extents (paint, &glyph_extents);
64
 * hb_raster_paint_glyph (paint, font, gid, pen_x, pen_y);
65
 * hb_raster_image_t *img = hb_raster_paint_render (paint);
66
 * ]|
67
 *
68
 * In both modes, set extents explicitly (or via glyph extents) before
69
 * rendering to avoid implicit allocations and to get deterministic bounds.
70
 **/
71
72
#ifdef HAVE_PNG
73
#include <png.h>
74
#endif
75
76
#ifdef HAVE_PNG
77
struct hb_raster_png_read_blob_t
78
{
79
  const uint8_t *data = nullptr;
80
  size_t size = 0;
81
  size_t offset = 0;
82
  uint8_t *rgba = nullptr;
83
  png_bytep *rows = nullptr;
84
};
85
86
static void
87
hb_raster_png_error (png_structp png,
88
         png_const_charp msg HB_UNUSED)
89
{
90
#ifdef PNG_SETJMP_SUPPORTED
91
  longjmp (png_jmpbuf (png), 1);
92
#endif
93
}
94
95
static void
96
hb_raster_png_warning (png_structp png HB_UNUSED,
97
           png_const_charp msg HB_UNUSED)
98
{}
99
100
static void
101
hb_raster_png_read_blob (png_structp png, png_bytep out, png_size_t length)
102
{
103
  hb_raster_png_read_blob_t *r = (hb_raster_png_read_blob_t *) png_get_io_ptr (png);
104
105
  if (!r || !r->data || length > r->size - r->offset)
106
    png_error (png, "read error");
107
108
  hb_memcpy (out, r->data + r->offset, length);
109
  r->offset += length;
110
}
111
112
static void
113
hb_raster_png_read_blob_fini (hb_raster_png_read_blob_t *r)
114
{
115
  if (!r)
116
    return;
117
118
  hb_free (r->rgba);
119
  hb_free (r->rows);
120
  hb_free (r);
121
}
122
123
struct hb_raster_png_write_blob_t
124
{
125
  char *data = nullptr;
126
  size_t length = 0;
127
  size_t allocated = 0;
128
  uint8_t *rgba = nullptr;
129
  png_bytep *rows = nullptr;
130
};
131
132
static void
133
hb_raster_png_write_blob (png_structp png, png_bytep in, png_size_t length)
134
{
135
  hb_raster_png_write_blob_t *w = (hb_raster_png_write_blob_t *) png_get_io_ptr (png);
136
  if (!w)
137
    png_error (png, "write error");
138
139
  size_t old_length = w->length;
140
  if ((size_t) length > (size_t) -1 - old_length)
141
    png_error (png, "write error");
142
143
  size_t new_length = old_length + length;
144
  if (new_length > w->allocated)
145
  {
146
    size_t new_allocated = w->allocated ? w->allocated : 4096;
147
    while (new_allocated < new_length)
148
    {
149
      size_t next = new_allocated * 2;
150
      if (next <= new_allocated)
151
      {
152
  new_allocated = new_length;
153
  break;
154
      }
155
      new_allocated = next;
156
    }
157
158
    char *data = (char *) hb_realloc (w->data, new_allocated);
159
    if (!data)
160
      png_error (png, "write error");
161
    w->data = data;
162
    w->allocated = new_allocated;
163
  }
164
165
  hb_memcpy (w->data + old_length, in, length);
166
  w->length = new_length;
167
}
168
169
static void
170
hb_raster_png_flush_blob (png_structp png HB_UNUSED)
171
{}
172
173
static void
174
hb_raster_png_write_blob_fini (hb_raster_png_write_blob_t *w)
175
{
176
  if (!w)
177
    return;
178
179
  hb_free (w->data);
180
  hb_free (w->rgba);
181
  hb_free (w->rows);
182
  hb_free (w);
183
}
184
#endif
185
186
187
/*
188
 * Image compositing
189
 */
190
191
/* Unpack premultiplied pixel to float RGBA [0,1]. */
192
static inline void
193
unpack_to_float (uint32_t px, float &r, float &g, float &b, float &a)
194
283M
{
195
283M
  b = (px & 0xFF) / 255.f;
196
283M
  g = ((px >> 8) & 0xFF) / 255.f;
197
283M
  r = ((px >> 16) & 0xFF) / 255.f;
198
283M
  a = (px >> 24) / 255.f;
199
283M
}
200
201
/* Pack float RGBA [0,1] premultiplied back to uint32_t. */
202
static inline uint32_t
203
pack_from_float (float r, float g, float b, float a)
204
141M
{
205
141M
  return hb_raster_pack_pixel ((uint8_t) (hb_clamp (b, 0.f, 1.f) * 255.f + 0.5f),
206
141M
             (uint8_t) (hb_clamp (g, 0.f, 1.f) * 255.f + 0.5f),
207
141M
             (uint8_t) (hb_clamp (r, 0.f, 1.f) * 255.f + 0.5f),
208
141M
             (uint8_t) (hb_clamp (a, 0.f, 1.f) * 255.f + 0.5f));
209
141M
}
210
211
/* Separable blend mode functions: operate on unpremultiplied [0,1] channels. */
212
static inline float
213
32.8M
blend_multiply (float sc, float dc) { return sc * dc; }
214
static inline float
215
65.7k
blend_screen (float sc, float dc) { return sc + dc - sc * dc; }
216
static inline float
217
blend_overlay (float sc, float dc)
218
62.7k
{ return dc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); }
219
static inline float
220
36.1k
blend_darken (float sc, float dc) { return hb_min (sc, dc); }
221
static inline float
222
32.6M
blend_lighten (float sc, float dc) { return hb_max (sc, dc); }
223
static inline float
224
blend_color_dodge (float sc, float dc)
225
32.6M
{
226
32.6M
  if (dc <= 0.f) return 0.f;
227
17.9k
  if (sc >= 1.f) return 1.f;
228
17.2k
  return hb_min (1.f, dc / (1.f - sc));
229
17.9k
}
230
static inline float
231
blend_color_burn (float sc, float dc)
232
32.6M
{
233
32.6M
  if (dc >= 1.f) return 1.f;
234
32.6M
  if (sc <= 0.f) return 0.f;
235
11.0k
  return 1.f - hb_min (1.f, (1.f - dc) / sc);
236
32.6M
}
237
static inline float
238
blend_hard_light (float sc, float dc)
239
32.8M
{ return sc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); }
240
static inline float
241
blend_soft_light (float sc, float dc)
242
32.6M
{
243
32.6M
  if (sc <= 0.5f)
244
32.6M
    return dc - (1.f - 2.f * sc) * dc * (1.f - dc);
245
8.67k
  float d = (dc <= 0.25f) ? ((16.f * dc - 12.f) * dc + 4.f) * dc
246
8.67k
         : sqrtf (dc);
247
8.67k
  return dc + (2.f * sc - 1.f) * (d - dc);
248
32.6M
}
249
static inline float
250
32.6M
blend_difference (float sc, float dc) { return fabsf (sc - dc); }
251
static inline float
252
32.6M
blend_exclusion (float sc, float dc) { return sc + dc - 2.f * sc * dc; }
253
254
/* Apply a separable blend mode per-pixel.
255
 * Both src and dst are premultiplied BGRA32. */
256
static inline uint32_t
257
apply_separable_blend (uint32_t src, uint32_t dst,
258
           float (*blend_fn)(float, float))
259
87.2M
{
260
87.2M
  float sr, sg, sb, sa;
261
87.2M
  float dr, dg, db, da;
262
87.2M
  unpack_to_float (src, sr, sg, sb, sa);
263
87.2M
  unpack_to_float (dst, dr, dg, db, da);
264
265
87.2M
  float usr = sa > 0.f ? sr / sa : 0.f;
266
87.2M
  float usg = sa > 0.f ? sg / sa : 0.f;
267
87.2M
  float usb = sa > 0.f ? sb / sa : 0.f;
268
87.2M
  float udr = da > 0.f ? dr / da : 0.f;
269
87.2M
  float udg = da > 0.f ? dg / da : 0.f;
270
87.2M
  float udb = da > 0.f ? db / da : 0.f;
271
272
87.2M
  float br = blend_fn (usr, udr);
273
87.2M
  float bg = blend_fn (usg, udg);
274
87.2M
  float bb = blend_fn (usb, udb);
275
276
87.2M
  float ra = sa + da - sa * da;
277
87.2M
  float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr;
278
87.2M
  float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg;
279
87.2M
  float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb;
280
281
87.2M
  return pack_from_float (rr, rg, rb, ra);
282
87.2M
}
283
284
/* HSL helpers */
285
static inline float
286
hsl_luminosity (float r, float g, float b)
287
163M
{ return 0.299f * r + 0.587f * g + 0.114f * b; }
288
289
static inline float
290
hsl_saturation (float r, float g, float b)
291
32.7M
{ return hb_max (hb_max (r, g), b) - hb_min (hb_min (r, g), b); }
292
293
static inline void
294
hsl_clip_color (float &r, float &g, float &b)
295
54.6M
{
296
54.6M
  float l = hsl_luminosity (r, g, b);
297
54.6M
  float mn = hb_min (hb_min (r, g), b);
298
54.6M
  float mx = hb_max (hb_max (r, g), b);
299
54.6M
  if (mn < 0.f)
300
11.5k
  {
301
11.5k
    float d = l - mn;
302
11.5k
    if (d > 0.f) { r = l + (r - l) * l / d; g = l + (g - l) * l / d; b = l + (b - l) * l / d; }
303
11.5k
  }
304
54.6M
  if (mx > 1.f)
305
6.04k
  {
306
6.04k
    float d = mx - l;
307
6.04k
    if (d > 0.f) { r = l + (r - l) * (1.f - l) / d; g = l + (g - l) * (1.f - l) / d; b = l + (b - l) * (1.f - l) / d; }
308
6.04k
  }
309
54.6M
}
310
311
static inline void
312
hsl_set_luminosity (float &r, float &g, float &b, float l)
313
54.6M
{
314
54.6M
  float d = l - hsl_luminosity (r, g, b);
315
54.6M
  r += d; g += d; b += d;
316
54.6M
  hsl_clip_color (r, g, b);
317
54.6M
}
318
319
static inline void
320
hsl_set_saturation_inner (float &mn, float &mid, float &mx, float s)
321
32.7M
{
322
32.7M
  if (mx > mn)
323
23.4k
  {
324
23.4k
    mid = (mid - mn) * s / (mx - mn);
325
23.4k
    mx = s;
326
23.4k
  }
327
32.7M
  else
328
32.7M
    mid = mx = 0.f;
329
32.7M
  mn = 0.f;
330
32.7M
}
331
332
static inline void
333
hsl_set_saturation (float &r, float &g, float &b, float s)
334
32.7M
{
335
32.7M
  if (r <= g)
336
32.7M
  {
337
32.7M
    if (g <= b)      hsl_set_saturation_inner (r, g, b, s);
338
1.09k
    else if (r <= b) hsl_set_saturation_inner (r, b, g, s);
339
889
    else             hsl_set_saturation_inner (b, r, g, s);
340
32.7M
  }
341
15.2k
  else
342
15.2k
  {
343
15.2k
    if (r <= b)      hsl_set_saturation_inner (g, r, b, s);
344
9.33k
    else if (g <= b) hsl_set_saturation_inner (g, b, r, s);
345
8.92k
    else             hsl_set_saturation_inner (b, g, r, s);
346
15.2k
  }
347
32.7M
}
348
349
static inline uint32_t
350
apply_hsl_blend (uint32_t src, uint32_t dst,
351
     hb_paint_composite_mode_t mode)
352
54.6M
{
353
54.6M
  float sr, sg, sb, sa;
354
54.6M
  float dr, dg, db, da;
355
54.6M
  unpack_to_float (src, sr, sg, sb, sa);
356
54.6M
  unpack_to_float (dst, dr, dg, db, da);
357
358
54.6M
  float usr = sa > 0.f ? sr / sa : 0.f;
359
54.6M
  float usg = sa > 0.f ? sg / sa : 0.f;
360
54.6M
  float usb = sa > 0.f ? sb / sa : 0.f;
361
54.6M
  float udr = da > 0.f ? dr / da : 0.f;
362
54.6M
  float udg = da > 0.f ? dg / da : 0.f;
363
54.6M
  float udb = da > 0.f ? db / da : 0.f;
364
365
54.6M
  float br = udr, bg = udg, bb = udb;
366
367
54.6M
  if (mode == HB_PAINT_COMPOSITE_MODE_HSL_HUE)
368
21.8M
  {
369
21.8M
    br = usr; bg = usg; bb = usb;
370
21.8M
    hsl_set_saturation (br, bg, bb, hsl_saturation (udr, udg, udb));
371
21.8M
    hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
372
21.8M
  }
373
32.8M
  else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_SATURATION)
374
10.9M
  {
375
10.9M
    br = udr; bg = udg; bb = udb;
376
10.9M
    hsl_set_saturation (br, bg, bb, hsl_saturation (usr, usg, usb));
377
10.9M
    hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
378
10.9M
  }
379
21.8M
  else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_COLOR)
380
10.9M
  {
381
10.9M
    br = usr; bg = usg; bb = usb;
382
10.9M
    hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
383
10.9M
  }
384
10.9M
  else /* HSL_LUMINOSITY */
385
10.9M
  {
386
10.9M
    br = udr; bg = udg; bb = udb;
387
10.9M
    hsl_set_luminosity (br, bg, bb, hsl_luminosity (usr, usg, usb));
388
10.9M
  }
389
390
54.6M
  float ra = sa + da - sa * da;
391
54.6M
  float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr;
392
54.6M
  float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg;
393
54.6M
  float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb;
394
395
54.6M
  return pack_from_float (rr, rg, rb, ra);
396
54.6M
}
397
398
/* Composite per-pixel with full blend mode support. */
399
static inline uint32_t
400
composite_pixel (uint32_t src, uint32_t dst,
401
     hb_paint_composite_mode_t mode)
402
284M
{
403
284M
  uint8_t sa = (uint8_t) (src >> 24);
404
284M
  uint8_t da = (uint8_t) (dst >> 24);
405
406
284M
  switch (mode)
407
284M
  {
408
15.6k
  case HB_PAINT_COMPOSITE_MODE_CLEAR:
409
15.6k
    return 0;
410
10.0k
  case HB_PAINT_COMPOSITE_MODE_SRC:
411
10.0k
    return src;
412
10.0k
  case HB_PAINT_COMPOSITE_MODE_DEST:
413
10.0k
    return dst;
414
142M
  case HB_PAINT_COMPOSITE_MODE_SRC_OVER:
415
142M
    return hb_raster_src_over (src, dst);
416
17.4k
  case HB_PAINT_COMPOSITE_MODE_DEST_OVER:
417
17.4k
    return hb_raster_src_over (dst, src);
418
10.5k
  case HB_PAINT_COMPOSITE_MODE_SRC_IN:
419
10.5k
    return hb_raster_alpha_mul (src, da);
420
24.1k
  case HB_PAINT_COMPOSITE_MODE_DEST_IN:
421
24.1k
    return hb_raster_alpha_mul (dst, sa);
422
24.1k
  case HB_PAINT_COMPOSITE_MODE_SRC_OUT:
423
24.1k
    return hb_raster_alpha_mul (src, 255 - da);
424
21.4k
  case HB_PAINT_COMPOSITE_MODE_DEST_OUT:
425
21.4k
    return hb_raster_alpha_mul (dst, 255 - sa);
426
18.6k
  case HB_PAINT_COMPOSITE_MODE_SRC_ATOP:
427
18.6k
  {
428
    /* Fa=Da, Fb=1-Sa */
429
18.6k
    uint32_t a = hb_raster_alpha_mul (src, da);
430
18.6k
    uint32_t b = hb_raster_alpha_mul (dst, 255 - sa);
431
18.6k
    uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
432
18.6k
    uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
433
18.6k
    uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
434
18.6k
    uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
435
18.6k
    return hb_raster_pack_pixel (rb, rg, rr, ra);
436
0
  }
437
50.0k
  case HB_PAINT_COMPOSITE_MODE_DEST_ATOP:
438
50.0k
  {
439
50.0k
    uint32_t a = hb_raster_alpha_mul (dst, sa);
440
50.0k
    uint32_t b = hb_raster_alpha_mul (src, 255 - da);
441
50.0k
    uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
442
50.0k
    uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
443
50.0k
    uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
444
50.0k
    uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
445
50.0k
    return hb_raster_pack_pixel (rb, rg, rr, ra);
446
0
  }
447
18.8k
  case HB_PAINT_COMPOSITE_MODE_XOR:
448
18.8k
  {
449
18.8k
    uint32_t a = hb_raster_alpha_mul (src, 255 - da);
450
18.8k
    uint32_t b = hb_raster_alpha_mul (dst, 255 - sa);
451
18.8k
    uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
452
18.8k
    uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
453
18.8k
    uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
454
18.8k
    uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
455
18.8k
    return hb_raster_pack_pixel (rb, rg, rr, ra);
456
0
  }
457
21.9k
  case HB_PAINT_COMPOSITE_MODE_PLUS:
458
21.9k
  {
459
21.9k
    uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (src & 0xFF) + (dst & 0xFF));
460
21.9k
    uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF));
461
21.9k
    uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF));
462
21.9k
    uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (src >> 24) + (dst >> 24));
463
21.9k
    return hb_raster_pack_pixel (rb, rg, rr, ra);
464
0
  }
465
466
10.9M
  case HB_PAINT_COMPOSITE_MODE_MULTIPLY:  return apply_separable_blend (src, dst, blend_multiply);
467
21.9k
  case HB_PAINT_COMPOSITE_MODE_SCREEN:    return apply_separable_blend (src, dst, blend_screen);
468
20.9k
  case HB_PAINT_COMPOSITE_MODE_OVERLAY:   return apply_separable_blend (src, dst, blend_overlay);
469
12.0k
  case HB_PAINT_COMPOSITE_MODE_DARKEN:    return apply_separable_blend (src, dst, blend_darken);
470
10.8M
  case HB_PAINT_COMPOSITE_MODE_LIGHTEN:   return apply_separable_blend (src, dst, blend_lighten);
471
10.8M
  case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return apply_separable_blend (src, dst, blend_color_dodge);
472
10.8M
  case HB_PAINT_COMPOSITE_MODE_COLOR_BURN:  return apply_separable_blend (src, dst, blend_color_burn);
473
10.9M
  case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT:  return apply_separable_blend (src, dst, blend_hard_light);
474
10.8M
  case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT:  return apply_separable_blend (src, dst, blend_soft_light);
475
10.8M
  case HB_PAINT_COMPOSITE_MODE_DIFFERENCE:  return apply_separable_blend (src, dst, blend_difference);
476
10.8M
  case HB_PAINT_COMPOSITE_MODE_EXCLUSION:   return apply_separable_blend (src, dst, blend_exclusion);
477
478
21.8M
  case HB_PAINT_COMPOSITE_MODE_HSL_HUE:
479
32.7M
  case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION:
480
43.7M
  case HB_PAINT_COMPOSITE_MODE_HSL_COLOR:
481
54.6M
  case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY:
482
54.6M
    return apply_hsl_blend (src, dst, mode);
483
484
0
  default:
485
0
    return hb_raster_src_over (src, dst);
486
284M
  }
487
284M
}
488
489
/* hb_raster_image_t */
490
491
unsigned
492
hb_raster_image_t::bytes_per_pixel (hb_raster_format_t format)
493
683k
{
494
683k
  return format == HB_RASTER_FORMAT_BGRA32 ? 4u : 1u;
495
683k
}
496
497
bool
498
hb_raster_image_t::configure (hb_raster_format_t format,
499
            hb_raster_extents_t extents)
500
683k
{
501
683k
  if (format != HB_RASTER_FORMAT_A8 &&
502
604k
      format != HB_RASTER_FORMAT_BGRA32)
503
0
    format = HB_RASTER_FORMAT_A8;
504
505
683k
  unsigned bpp = bytes_per_pixel (format);
506
683k
  if (extents.width > UINT_MAX / bpp)
507
636
    return false;
508
509
682k
  unsigned min_stride = extents.width * bpp;
510
682k
  if (extents.stride == 0 || extents.stride < min_stride)
511
67.3k
    extents.stride = min_stride;
512
513
682k
  if (extents.height && extents.stride > (size_t) -1 / extents.height)
514
0
    return false;
515
516
682k
  size_t buf_size = (size_t) extents.stride * extents.height;
517
682k
  if (buf_size > HB_RASTER_MAX_BUFFER_SIZE)
518
596k
    return false;
519
86.3k
  if (unlikely (!buffer.resize_dirty (buf_size)))
520
21
    return false;
521
522
86.3k
  this->format = format;
523
86.3k
  this->extents = extents;
524
86.3k
  return true;
525
86.3k
}
526
527
bool
528
hb_raster_image_t::deserialize_from_png (hb_blob_t *blob)
529
0
{
530
0
#ifndef HAVE_PNG
531
0
  return false;
532
#else
533
  if (!blob)
534
    return false;
535
536
  unsigned blob_len = 0;
537
  const uint8_t *blob_data = (const uint8_t *) hb_blob_get_data (blob, &blob_len);
538
  if (!blob_data || !blob_len)
539
    return false;
540
541
  png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING, nullptr,
542
              hb_raster_png_error,
543
              hb_raster_png_warning);
544
  if (!png)
545
    return false;
546
547
  png_infop info = png_create_info_struct (png);
548
  if (!info)
549
  {
550
    png_destroy_read_struct (&png, nullptr, nullptr);
551
    return false;
552
  }
553
554
  hb_raster_png_read_blob_t *reader = (hb_raster_png_read_blob_t *) hb_calloc (1, sizeof (*reader));
555
  if (!reader)
556
  {
557
    png_destroy_read_struct (&png, &info, nullptr);
558
    return false;
559
  }
560
561
  reader->data = blob_data;
562
  reader->size = (size_t) blob_len;
563
  png_set_read_fn (png, reader, hb_raster_png_read_blob);
564
  if (setjmp (png_jmpbuf (png)))
565
  {
566
    png_destroy_read_struct (&png, &info, nullptr);
567
    hb_raster_png_read_blob_fini (reader);
568
    return false;
569
  }
570
571
  png_read_info (png, info);
572
573
  png_uint_32 w = 0, h = 0;
574
  int bit_depth = 0, color_type = 0;
575
  int interlace_type = 0, compression_type = 0, filter_method = 0;
576
  png_get_IHDR (png, info, &w, &h, &bit_depth, &color_type,
577
    &interlace_type, &compression_type, &filter_method);
578
579
  if (!w || !h || w > (png_uint_32) INT_MAX || h > (png_uint_32) INT_MAX)
580
  {
581
    png_destroy_read_struct (&png, &info, nullptr);
582
    hb_raster_png_read_blob_fini (reader);
583
    return false;
584
  }
585
586
  bool has_trns = png_get_valid (png, info, PNG_INFO_tRNS);
587
588
  if (bit_depth == 16)
589
    png_set_strip_16 (png);
590
  if (color_type == PNG_COLOR_TYPE_PALETTE)
591
    png_set_palette_to_rgb (png);
592
  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
593
    png_set_expand_gray_1_2_4_to_8 (png);
594
  if (has_trns)
595
    png_set_tRNS_to_alpha (png);
596
  if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
597
    png_set_gray_to_rgb (png);
598
  if (!(color_type & PNG_COLOR_MASK_ALPHA) && !has_trns)
599
    png_set_add_alpha (png, 0xff, PNG_FILLER_AFTER);
600
  if (interlace_type != PNG_INTERLACE_NONE)
601
    png_set_interlace_handling (png);
602
603
  png_read_update_info (png, info);
604
605
  if (png_get_bit_depth (png, info) != 8 || png_get_channels (png, info) != 4)
606
  {
607
    png_destroy_read_struct (&png, &info, nullptr);
608
    hb_raster_png_read_blob_fini (reader);
609
    return false;
610
  }
611
612
  png_size_t rowbytes = png_get_rowbytes (png, info);
613
  if (rowbytes < (png_size_t) w * 4u)
614
  {
615
    png_destroy_read_struct (&png, &info, nullptr);
616
    hb_raster_png_read_blob_fini (reader);
617
    return false;
618
  }
619
620
  size_t rgba_size = (size_t) rowbytes * (size_t) h;
621
  if (h && rgba_size / (size_t) h != (size_t) rowbytes)
622
  {
623
    png_destroy_read_struct (&png, &info, nullptr);
624
    hb_raster_png_read_blob_fini (reader);
625
    return false;
626
  }
627
628
  reader->rgba = (uint8_t *) hb_malloc (rgba_size);
629
  if (!reader->rgba)
630
  {
631
    png_destroy_read_struct (&png, &info, nullptr);
632
    hb_raster_png_read_blob_fini (reader);
633
    return false;
634
  }
635
636
  size_t rows_size = (size_t) h * sizeof (png_bytep);
637
  if (h && rows_size / (size_t) h != sizeof (png_bytep))
638
  {
639
    png_destroy_read_struct (&png, &info, nullptr);
640
    hb_raster_png_read_blob_fini (reader);
641
    return false;
642
  }
643
644
  reader->rows = (png_bytep *) hb_malloc (rows_size);
645
  if (!reader->rows)
646
  {
647
    png_destroy_read_struct (&png, &info, nullptr);
648
    hb_raster_png_read_blob_fini (reader);
649
    return false;
650
  }
651
652
  for (unsigned y = 0; y < (unsigned) h; y++)
653
    reader->rows[y] = (png_bytep) (reader->rgba + (size_t) y * (size_t) rowbytes);
654
655
  png_read_image (png, reader->rows);
656
  png_read_end (png, nullptr);
657
  png_destroy_read_struct (&png, &info, nullptr);
658
659
  hb_raster_image_t decoded;
660
  hb_raster_extents_t decoded_extents = {0, 0, (unsigned) w, (unsigned) h, 0};
661
  if (!decoded.configure (HB_RASTER_FORMAT_BGRA32, decoded_extents))
662
  {
663
    hb_raster_png_read_blob_fini (reader);
664
    return false;
665
  }
666
667
  for (unsigned y = 0; y < (unsigned) h; y++)
668
  {
669
    hb_packed_t<uint32_t> *dst = (hb_packed_t<uint32_t> *) (decoded.buffer.arrayZ + (size_t) ((unsigned) h - 1 - y) * decoded.extents.stride);
670
    const uint8_t *src = reader->rgba + (size_t) y * (size_t) rowbytes;
671
    for (unsigned x = 0; x < (unsigned) w; x++)
672
    {
673
      uint8_t r = src[4 * x + 0];
674
      uint8_t g = src[4 * x + 1];
675
      uint8_t b = src[4 * x + 2];
676
      uint8_t a = src[4 * x + 3];
677
      dst[x] = hb_packed_t<uint32_t> ((uint32_t) hb_raster_div255 (b * a)
678
            | ((uint32_t) hb_raster_div255 (g * a) << 8)
679
            | ((uint32_t) hb_raster_div255 (r * a) << 16)
680
            | ((uint32_t) a << 24));
681
    }
682
  }
683
684
  hb_swap (buffer, decoded.buffer);
685
  hb_swap (this->extents, decoded.extents);
686
  hb_swap (format, decoded.format);
687
  hb_raster_png_read_blob_fini (reader);
688
  return true;
689
#endif
690
0
}
691
692
hb_blob_t *
693
hb_raster_image_t::serialize_to_png_or_fail () const
694
0
{
695
0
#ifndef HAVE_PNG
696
0
  return nullptr;
697
#else
698
  if (format != HB_RASTER_FORMAT_BGRA32 || !extents.width || !extents.height)
699
    return nullptr;
700
701
  png_structp png = png_create_write_struct (PNG_LIBPNG_VER_STRING, nullptr,
702
               hb_raster_png_error,
703
               hb_raster_png_warning);
704
  if (!png)
705
    return nullptr;
706
707
  png_infop info = png_create_info_struct (png);
708
  if (!info)
709
  {
710
    png_destroy_write_struct (&png, nullptr);
711
    return nullptr;
712
  }
713
714
  hb_raster_png_write_blob_t *writer = (hb_raster_png_write_blob_t *) hb_calloc (1, sizeof (*writer));
715
  if (!writer)
716
  {
717
    png_destroy_write_struct (&png, &info);
718
    return nullptr;
719
  }
720
721
  png_set_write_fn (png, writer, hb_raster_png_write_blob, hb_raster_png_flush_blob);
722
  if (setjmp (png_jmpbuf (png)))
723
  {
724
    png_destroy_write_struct (&png, &info);
725
    hb_raster_png_write_blob_fini (writer);
726
    return nullptr;
727
  }
728
729
  png_set_IHDR (png, info,
730
    extents.width, extents.height,
731
    8, PNG_COLOR_TYPE_RGBA,
732
    PNG_INTERLACE_NONE,
733
    PNG_COMPRESSION_TYPE_DEFAULT,
734
    PNG_FILTER_TYPE_DEFAULT);
735
  png_write_info (png, info);
736
737
  size_t rowbytes = (size_t) extents.width * 4u;
738
  size_t rgba_size = rowbytes * (size_t) extents.height;
739
  if (extents.height && rgba_size / (size_t) extents.height != rowbytes)
740
  {
741
    png_destroy_write_struct (&png, &info);
742
    hb_raster_png_write_blob_fini (writer);
743
    return nullptr;
744
  }
745
746
  writer->rgba = (uint8_t *) hb_malloc (rgba_size);
747
  if (!writer->rgba)
748
  {
749
    png_destroy_write_struct (&png, &info);
750
    hb_raster_png_write_blob_fini (writer);
751
    return nullptr;
752
  }
753
754
  size_t rows_size = (size_t) extents.height * sizeof (png_bytep);
755
  if (extents.height && rows_size / (size_t) extents.height != sizeof (png_bytep))
756
  {
757
    png_destroy_write_struct (&png, &info);
758
    hb_raster_png_write_blob_fini (writer);
759
    return nullptr;
760
  }
761
762
  writer->rows = (png_bytep *) hb_malloc (rows_size);
763
  if (!writer->rows)
764
  {
765
    png_destroy_write_struct (&png, &info);
766
    hb_raster_png_write_blob_fini (writer);
767
    return nullptr;
768
  }
769
770
  for (unsigned y = 0; y < extents.height; y++)
771
  {
772
    uint8_t *dst = writer->rgba + (size_t) y * rowbytes;
773
    const uint8_t *src = buffer.arrayZ + (size_t) (extents.height - 1 - y) * extents.stride;
774
775
    for (unsigned x = 0; x < extents.width; x++)
776
    {
777
      uint32_t px;
778
      hb_memcpy (&px, src + x * 4, 4);
779
      uint8_t b = (uint8_t) (px & 0xFF);
780
      uint8_t g = (uint8_t) ((px >> 8) & 0xFF);
781
      uint8_t r = (uint8_t) ((px >> 16) & 0xFF);
782
      uint8_t a = (uint8_t) (px >> 24);
783
784
      dst[4 * x + 3] = a;
785
      if (a)
786
      {
787
  dst[4 * x + 0] = (uint8_t) hb_min (255u, ((unsigned) r * 255u + a / 2u) / a);
788
  dst[4 * x + 1] = (uint8_t) hb_min (255u, ((unsigned) g * 255u + a / 2u) / a);
789
  dst[4 * x + 2] = (uint8_t) hb_min (255u, ((unsigned) b * 255u + a / 2u) / a);
790
      }
791
      else
792
  dst[4 * x + 0] = dst[4 * x + 1] = dst[4 * x + 2] = 0;
793
    }
794
795
    writer->rows[y] = (png_bytep) dst;
796
  }
797
798
  png_write_image (png, writer->rows);
799
  png_write_end (png, info);
800
  png_destroy_write_struct (&png, &info);
801
802
  if (writer->length > (size_t) (unsigned) -1)
803
  {
804
    hb_raster_png_write_blob_fini (writer);
805
    return nullptr;
806
  }
807
808
  unsigned length = (unsigned) writer->length;
809
  char *data = writer->data;
810
  writer->data = nullptr;
811
  hb_raster_png_write_blob_fini (writer);
812
  if (!data && length)
813
    return nullptr;
814
815
  hb_blob_t *blob = hb_blob_create_or_fail (data, length,
816
              HB_MEMORY_MODE_WRITABLE,
817
              data, hb_free);
818
  if (!blob)
819
    hb_free (data);
820
  return blob;
821
#endif
822
0
}
823
824
void
825
hb_raster_image_t::clear ()
826
86.3k
{
827
86.3k
  size_t buf_size = (size_t) extents.stride * extents.height;
828
86.3k
  hb_memset (buffer.arrayZ, 0, buf_size);
829
86.3k
}
830
831
const uint8_t *
832
hb_raster_image_t::get_buffer () const
833
79.2k
{
834
79.2k
  return buffer.arrayZ;
835
79.2k
}
836
837
void
838
hb_raster_image_t::composite_from (const hb_raster_image_t *src,
839
           hb_paint_composite_mode_t mode)
840
4.53k
{
841
4.53k
  unsigned w = extents.width;
842
4.53k
  unsigned h = extents.height;
843
4.53k
  unsigned stride = extents.stride;
844
845
290k
  for (unsigned y = 0; y < h; y++)
846
285k
  {
847
285k
    hb_packed_t<uint32_t> *dp = (hb_packed_t<uint32_t> *) (buffer.arrayZ + y * stride);
848
285k
    const hb_packed_t<uint32_t> *sp = (const hb_packed_t<uint32_t> *) (src->buffer.arrayZ + y * stride);
849
284M
    for (unsigned x = 0; x < w; x++)
850
284M
      dp[x] = hb_packed_t<uint32_t> (composite_pixel ((uint32_t) sp[x], (uint32_t) dp[x], mode));
851
285k
  }
852
4.53k
}
853
854
/* Composite src image onto dst image.
855
 * Both images must have the same extents and BGRA32 format. */
856
void
857
hb_raster_image_composite (hb_raster_image_t *dst,
858
         const hb_raster_image_t *src,
859
         hb_paint_composite_mode_t mode)
860
4.53k
{
861
4.53k
  dst->composite_from (src, mode);
862
4.53k
}
863
864
/**
865
 * hb_raster_image_create_or_fail:
866
 *
867
 * Creates a new raster image object.
868
 *
869
 * Return value: (transfer full):
870
 * A newly allocated #hb_raster_image_t with a reference count of 1,
871
 * or `NULL` on allocation failure.
872
 *
873
 * The returned image can be released with hb_raster_image_destroy(), or
874
 * transferred for reuse with hb_raster_draw_recycle_image() or
875
 * hb_raster_paint_recycle_image().
876
 *
877
 * Since: 13.0.0
878
 **/
879
hb_raster_image_t *
880
hb_raster_image_create_or_fail (void)
881
600k
{
882
600k
  return hb_object_create<hb_raster_image_t> ();
883
600k
}
884
885
/**
886
 * hb_raster_image_reference: (skip)
887
 * @image: a raster image
888
 *
889
 * Increases the reference count on @image by one.
890
 *
891
 * This prevents @image from being destroyed until a matching
892
 * call to hb_raster_image_destroy() is made.
893
 *
894
 * Return value: (transfer full):
895
 * The referenced #hb_raster_image_t.
896
 *
897
 * Since: 13.0.0
898
 **/
899
hb_raster_image_t *
900
hb_raster_image_reference (hb_raster_image_t *image)
901
0
{
902
0
  return hb_object_reference (image);
903
0
}
904
905
/**
906
 * hb_raster_image_destroy: (skip)
907
 * @image: a raster image
908
 *
909
 * Decreases the reference count on @image by one. When the
910
 * reference count reaches zero, the image and its pixel buffer
911
 * are freed.
912
 *
913
 * Since: 13.0.0
914
 **/
915
void
916
hb_raster_image_destroy (hb_raster_image_t *image)
917
916k
{
918
916k
  if (!hb_object_destroy (image)) return;
919
600k
  hb_free (image);
920
600k
}
921
922
/**
923
 * hb_raster_image_set_user_data: (skip)
924
 * @image: a raster image
925
 * @key: the user-data key
926
 * @data: a pointer to the user data
927
 * @destroy: (nullable): a callback to call when @data is not needed anymore
928
 * @replace: whether to replace an existing data with the same key
929
 *
930
 * Attaches a user-data key/data pair to the specified raster image.
931
 *
932
 * Return value: `true` if success, `false` otherwise
933
 *
934
 * Since: 13.0.0
935
 **/
936
hb_bool_t
937
hb_raster_image_set_user_data (hb_raster_image_t  *image,
938
             hb_user_data_key_t *key,
939
             void               *data,
940
             hb_destroy_func_t   destroy,
941
             hb_bool_t           replace)
942
0
{
943
0
  return hb_object_set_user_data (image, key, data, destroy, replace);
944
0
}
945
946
/**
947
 * hb_raster_image_get_user_data: (skip)
948
 * @image: a raster image
949
 * @key: the user-data key
950
 *
951
 * Fetches the user-data associated with the specified key,
952
 * attached to the specified raster image.
953
 *
954
 * Return value: (transfer none):
955
 * A pointer to the user data
956
 *
957
 * Since: 13.0.0
958
 **/
959
void *
960
hb_raster_image_get_user_data (const hb_raster_image_t  *image,
961
             hb_user_data_key_t *key)
962
0
{
963
0
  return hb_object_get_user_data (image, key);
964
0
}
965
966
/**
967
 * hb_raster_image_configure:
968
 * @image: a raster image
969
 * @format: the pixel format
970
 * @extents: (nullable): desired image extents
971
 *
972
 * Configures @image format and extents together, resizing backing storage
973
 * at most once. This function does not clear pixel contents.
974
 *
975
 * Passing `NULL` for @extents clears extents and releases the backing
976
 * allocation.
977
 *
978
 * Return value: `true` if configuration succeeds, `false` on allocation
979
 * failure
980
 *
981
 * Since: 13.0.0
982
 **/
983
hb_bool_t
984
hb_raster_image_configure (hb_raster_image_t         *image,
985
         hb_raster_format_t        format,
986
         const hb_raster_extents_t *extents)
987
0
{
988
0
  if (unlikely (!extents))
989
0
  {
990
0
    image->extents = {};
991
0
    image->buffer.resize_exact (0);
992
0
    return true;
993
0
  }
994
0
  return image->configure (format, *extents);
995
0
}
996
997
/**
998
 * hb_raster_image_clear:
999
 * @image: a raster image
1000
 *
1001
 * Clears @image pixels to zero while keeping current extents and format.
1002
 *
1003
 * Since: 13.0.0
1004
 **/
1005
void
1006
hb_raster_image_clear (hb_raster_image_t *image)
1007
0
{
1008
0
  image->clear ();
1009
0
}
1010
1011
/**
1012
 * hb_raster_image_get_buffer:
1013
 * @image: a raster image
1014
 *
1015
 * Fetches the raw pixel buffer of @image.  The buffer layout is
1016
 * described by the extents obtained from hb_raster_image_get_extents()
1017
 * and the format from hb_raster_image_get_format(). Rows are stored
1018
 * bottom-to-top.
1019
 *
1020
 * Return value: (transfer none) (array):
1021
 * The pixel buffer, or `NULL`
1022
 *
1023
 * Since: 13.0.0
1024
 **/
1025
const uint8_t *
1026
hb_raster_image_get_buffer (const hb_raster_image_t *image)
1027
79.2k
{
1028
79.2k
  return image->get_buffer ();
1029
79.2k
}
1030
1031
/**
1032
 * hb_raster_image_get_extents:
1033
 * @image: a raster image
1034
 * @extents: (out) (nullable): the image extents
1035
 *
1036
 * Fetches the pixel-buffer extents of @image.
1037
 *
1038
 * Since: 13.0.0
1039
 **/
1040
void
1041
hb_raster_image_get_extents (const hb_raster_image_t   *image,
1042
           hb_raster_extents_t *extents)
1043
79.2k
{
1044
79.2k
  if (extents)
1045
79.2k
    *extents = image->extents;
1046
79.2k
}
1047
1048
/**
1049
 * hb_raster_image_get_format:
1050
 * @image: a raster image
1051
 *
1052
 * Fetches the pixel format of @image.
1053
 *
1054
 * Return value:
1055
 * The #hb_raster_format_t of the image
1056
 *
1057
 * Since: 13.0.0
1058
 **/
1059
hb_raster_format_t
1060
hb_raster_image_get_format (const hb_raster_image_t *image)
1061
0
{
1062
0
  return image->format;
1063
0
}
1064
1065
/**
1066
 * hb_raster_image_deserialize_from_png_or_fail:
1067
 * @image: a raster image
1068
 * @png: PNG data
1069
 *
1070
 * Replaces @image contents by deserializing a PNG blob into a
1071
 * #HB_RASTER_FORMAT_BGRA32 raster image.
1072
 *
1073
 * On success, @image extents are reset to pixel extents with origin
1074
 * `(0, 0)`. Rows in the resulting image buffer are stored bottom-to-top.
1075
 * On failure, @image is left unchanged.
1076
 *
1077
 * Return value: `true` if deserialization succeeded, `false` otherwise
1078
 *
1079
 * Since: 13.1.0
1080
 **/
1081
hb_bool_t
1082
hb_raster_image_deserialize_from_png_or_fail (hb_raster_image_t *image,
1083
                hb_blob_t         *png)
1084
0
{
1085
0
  return image->deserialize_from_png (png);
1086
0
}
1087
1088
/**
1089
 * hb_raster_image_serialize_to_png_or_fail:
1090
 * @image: a raster image
1091
 *
1092
 * Serializes @image to a PNG blob.
1093
 *
1094
 * Currently only #HB_RASTER_FORMAT_BGRA32 images are supported.
1095
 *
1096
 * Return value: (transfer full):
1097
 * A newly allocated PNG #hb_blob_t, or `NULL` on failure
1098
 *
1099
 * Since: 13.1.0
1100
 **/
1101
hb_blob_t *
1102
hb_raster_image_serialize_to_png_or_fail (const hb_raster_image_t *image)
1103
0
{
1104
0
  return image->serialize_to_png_or_fail ();
1105
0
}