/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 | } |