File Coverage

src/pdfmake_colorspace.c
Criterion Covered Total %
statement 0 326 0.0
branch 0 230 0.0
condition n/a
subroutine n/a
pod n/a
total 0 556 0.0


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_colorspace.c - Color space conversion for image rendering
3             *
4             * Implements color space conversions needed for PDF image rendering:
5             * - CMYK to RGB (subtractive to additive)
6             * - Lab to RGB (via XYZ)
7             * - Indexed to RGB (palette expansion)
8             * - Decode array application
9             *
10             * Reference: PDF 32000-1:2008 ยง8.6 Colour Spaces
11             */
12              
13             #include "pdfmake_image_render.h"
14             #include "pdfmake_arena.h"
15             #include
16             #include
17             #include
18              
19             /*============================================================================
20             * CMYK to RGB conversion
21             *
22             * PDF CMYK is device-dependent. We use simple conversion:
23             * R = 255 * (1 - C) * (1 - K)
24             * G = 255 * (1 - M) * (1 - K)
25             * B = 255 * (1 - Y) * (1 - K)
26             *==========================================================================*/
27              
28 0           void pdfmake_cmyk_to_rgb8(
29             uint8_t c, uint8_t m, uint8_t y, uint8_t k,
30             uint8_t *r, uint8_t *g, uint8_t *b)
31             {
32             /* Convert from 0-255 to 0-1 */
33 0           double cf = c / 255.0;
34 0           double mf = m / 255.0;
35 0           double yf = y / 255.0;
36 0           double kf = k / 255.0;
37            
38             /* CMYK to RGB */
39 0           double rf = (1.0 - cf) * (1.0 - kf);
40 0           double gf = (1.0 - mf) * (1.0 - kf);
41 0           double bf = (1.0 - yf) * (1.0 - kf);
42            
43             /* Back to 0-255 */
44 0           *r = (uint8_t)(rf * 255.0 + 0.5);
45 0           *g = (uint8_t)(gf * 255.0 + 0.5);
46 0           *b = (uint8_t)(bf * 255.0 + 0.5);
47 0           }
48              
49 0           void pdfmake_cmyk_to_rgb_f(
50             double c, double m, double y, double k,
51             double *r, double *g, double *b)
52             {
53             /* Clamp inputs */
54 0 0         if (c < 0.0) c = 0.0; if (c > 1.0) c = 1.0;
    0          
55 0 0         if (m < 0.0) m = 0.0; if (m > 1.0) m = 1.0;
    0          
56 0 0         if (y < 0.0) y = 0.0; if (y > 1.0) y = 1.0;
    0          
57 0 0         if (k < 0.0) k = 0.0; if (k > 1.0) k = 1.0;
    0          
58            
59 0           *r = (1.0 - c) * (1.0 - k);
60 0           *g = (1.0 - m) * (1.0 - k);
61 0           *b = (1.0 - y) * (1.0 - k);
62 0           }
63              
64             /*============================================================================
65             * Lab to RGB conversion
66             *
67             * Lab -> XYZ -> sRGB
68             * Using D65 illuminant.
69             *==========================================================================*/
70              
71             /* D65 reference white */
72             #define D65_X 0.95047
73             #define D65_Y 1.00000
74             #define D65_Z 1.08883
75              
76             /* Lab parameters */
77             #define LAB_DELTA (6.0 / 29.0)
78             #define LAB_DELTA2 (LAB_DELTA * LAB_DELTA)
79             #define LAB_DELTA3 (LAB_DELTA * LAB_DELTA * LAB_DELTA)
80              
81 0           static double lab_f_inv(double t) {
82 0 0         if (t > LAB_DELTA) {
83 0           return t * t * t;
84             }
85 0           return 3.0 * LAB_DELTA2 * (t - 4.0 / 29.0);
86             }
87              
88 0           void pdfmake_lab_to_xyz(
89             double L, double a, double b,
90             double *x, double *y, double *z)
91             {
92             /* L is 0-100, a and b are typically -128 to 127 */
93 0           double fy = (L + 16.0) / 116.0;
94 0           double fx = a / 500.0 + fy;
95 0           double fz = fy - b / 200.0;
96            
97 0           *x = D65_X * lab_f_inv(fx);
98 0           *y = D65_Y * lab_f_inv(fy);
99 0           *z = D65_Z * lab_f_inv(fz);
100 0           }
101              
102 0           static double srgb_gamma(double linear) {
103 0 0         if (linear <= 0.0031308) {
104 0           return 12.92 * linear;
105             }
106 0           return 1.055 * pow(linear, 1.0 / 2.4) - 0.055;
107             }
108              
109 0           void pdfmake_xyz_to_srgb(
110             double x, double y, double z,
111             double *r, double *g, double *b)
112             {
113             /* sRGB transformation matrix (D65) */
114 0           double rlin = 3.2404542 * x - 1.5371385 * y - 0.4985314 * z;
115 0           double glin = -0.9692660 * x + 1.8760108 * y + 0.0415560 * z;
116 0           double blin = 0.0556434 * x - 0.2040259 * y + 1.0572252 * z;
117            
118             /* Apply gamma */
119 0           *r = srgb_gamma(rlin);
120 0           *g = srgb_gamma(glin);
121 0           *b = srgb_gamma(blin);
122            
123             /* Clamp */
124 0 0         if (*r < 0.0) *r = 0.0; if (*r > 1.0) *r = 1.0;
    0          
125 0 0         if (*g < 0.0) *g = 0.0; if (*g > 1.0) *g = 1.0;
    0          
126 0 0         if (*b < 0.0) *b = 0.0; if (*b > 1.0) *b = 1.0;
    0          
127 0           }
128              
129 0           void pdfmake_lab_to_rgb8(
130             double L, double a, double b,
131             uint8_t *r, uint8_t *g, uint8_t *bl)
132             {
133             double x, y, z;
134             double rf, gf, bf;
135            
136 0           pdfmake_lab_to_xyz(L, a, b, &x, &y, &z);
137 0           pdfmake_xyz_to_srgb(x, y, z, &rf, &gf, &bf);
138            
139 0           *r = (uint8_t)(rf * 255.0 + 0.5);
140 0           *g = (uint8_t)(gf * 255.0 + 0.5);
141 0           *bl = (uint8_t)(bf * 255.0 + 0.5);
142 0           }
143              
144             /*============================================================================
145             * Decoded image operations
146             *==========================================================================*/
147              
148 0           pdfmake_decoded_image_t *pdfmake_decoded_image_create(pdfmake_arena_t *arena)
149             {
150             pdfmake_decoded_image_t *img;
151            
152 0 0         if (arena) {
153 0           img = pdfmake_arena_alloc(arena, sizeof(pdfmake_decoded_image_t));
154             } else {
155 0           img = malloc(sizeof(pdfmake_decoded_image_t));
156             }
157            
158 0 0         if (!img) return NULL;
159 0           memset(img, 0, sizeof(*img));
160            
161 0           img->bits_per_component = 8;
162 0           img->arena = arena;
163 0           img->owns_data = !arena;
164            
165 0           return img;
166             }
167              
168 0           pdfmake_decoded_image_t *pdfmake_decoded_image_create_sized(
169             int width, int height,
170             pdfmake_render_cs_t colorspace,
171             pdfmake_arena_t *arena)
172             {
173 0           pdfmake_decoded_image_t *img = pdfmake_decoded_image_create(arena);
174 0 0         if (!img) return NULL;
175            
176 0           img->width = width;
177 0           img->height = height;
178 0           img->colorspace = colorspace;
179            
180             /* Determine components */
181 0           switch (colorspace) {
182 0           case PDFMAKE_RCS_GRAY: img->components = 1; break;
183 0           case PDFMAKE_RCS_RGB: img->components = 3; break;
184 0           case PDFMAKE_RCS_CMYK: img->components = 4; break;
185 0           case PDFMAKE_RCS_INDEXED: img->components = 1; break;
186 0           case PDFMAKE_RCS_LAB: img->components = 3; break;
187 0           default: img->components = 3; break;
188             }
189            
190 0           img->row_stride = (size_t)width * img->components;
191 0           img->pixels_len = img->row_stride * height;
192            
193 0 0         if (arena) {
194 0           img->pixels = pdfmake_arena_alloc(arena, img->pixels_len);
195             } else {
196 0           img->pixels = malloc(img->pixels_len);
197             }
198            
199 0 0         if (!img->pixels) {
200 0 0         if (!arena) free(img);
201 0           return NULL;
202             }
203            
204 0           memset(img->pixels, 0, img->pixels_len);
205 0           return img;
206             }
207              
208 0           void pdfmake_decoded_image_free(pdfmake_decoded_image_t *img)
209             {
210 0 0         if (!img) return;
211            
212             /* Arena-managed images don't need individual frees */
213 0 0         if (img->arena) return;
214            
215 0 0         if (img->owns_data) {
216 0           free(img->pixels);
217 0           free(img->alpha);
218 0           free(img->rgba);
219 0           free(img->palette);
220 0           free(img->decode);
221 0           free(img->matte);
222             }
223            
224 0           free(img);
225             }
226              
227 0           pdfmake_decoded_image_t *pdfmake_decoded_image_clone(
228             pdfmake_decoded_image_t *src,
229             pdfmake_arena_t *arena)
230             {
231             pdfmake_decoded_image_t *dst;
232 0 0         if (!src) return NULL;
233            
234 0           dst = pdfmake_decoded_image_create(arena);
235 0 0         if (!dst) return NULL;
236            
237             /* Copy metadata */
238 0           dst->width = src->width;
239 0           dst->height = src->height;
240 0           dst->bits_per_component = src->bits_per_component;
241 0           dst->components = src->components;
242 0           dst->colorspace = src->colorspace;
243 0           dst->row_stride = src->row_stride;
244 0           dst->interpolate = src->interpolate;
245 0           dst->has_alpha = src->has_alpha;
246 0           dst->premultiplied = src->premultiplied;
247            
248             /* Allocate and copy pixel data */
249 0 0         if (src->pixels && src->pixels_len > 0) {
    0          
250 0           dst->pixels_len = src->pixels_len;
251 0 0         if (arena) {
252 0           dst->pixels = pdfmake_arena_alloc(arena, dst->pixels_len);
253             } else {
254 0           dst->pixels = malloc(dst->pixels_len);
255             }
256 0 0         if (dst->pixels) {
257 0           memcpy(dst->pixels, src->pixels, dst->pixels_len);
258             }
259             }
260            
261             /* Copy alpha */
262 0 0         if (src->alpha && src->alpha_len > 0) {
    0          
263 0           dst->alpha_len = src->alpha_len;
264 0 0         if (arena) {
265 0           dst->alpha = pdfmake_arena_alloc(arena, dst->alpha_len);
266             } else {
267 0           dst->alpha = malloc(dst->alpha_len);
268             }
269 0 0         if (dst->alpha) {
270 0           memcpy(dst->alpha, src->alpha, dst->alpha_len);
271             }
272             }
273            
274             /* Copy RGBA */
275 0 0         if (src->rgba && src->rgba_len > 0) {
    0          
276 0           dst->rgba_len = src->rgba_len;
277 0 0         if (arena) {
278 0           dst->rgba = pdfmake_arena_alloc(arena, dst->rgba_len * sizeof(uint32_t));
279             } else {
280 0           dst->rgba = malloc(dst->rgba_len * sizeof(uint32_t));
281             }
282 0 0         if (dst->rgba) {
283 0           memcpy(dst->rgba, src->rgba, dst->rgba_len * sizeof(uint32_t));
284             }
285             }
286            
287             /* Copy palette */
288 0 0         if (src->palette && src->palette_entries > 0) {
    0          
289             size_t pal_bytes;
290 0           dst->palette_entries = src->palette_entries;
291 0           pal_bytes = src->palette_entries * 3;
292 0 0         if (arena) {
293 0           dst->palette = pdfmake_arena_alloc(arena, pal_bytes);
294             } else {
295 0           dst->palette = malloc(pal_bytes);
296             }
297 0 0         if (dst->palette) {
298 0           memcpy(dst->palette, src->palette, pal_bytes);
299             }
300             }
301            
302             /* Copy decode array */
303 0 0         if (src->decode && src->decode_len > 0) {
    0          
304 0           dst->decode_len = src->decode_len;
305 0 0         if (arena) {
306 0           dst->decode = pdfmake_arena_alloc(arena, dst->decode_len * sizeof(double));
307             } else {
308 0           dst->decode = malloc(dst->decode_len * sizeof(double));
309             }
310 0 0         if (dst->decode) {
311 0           memcpy(dst->decode, src->decode, dst->decode_len * sizeof(double));
312             }
313             }
314            
315             /* Copy matte */
316 0 0         if (src->matte) {
317 0           size_t matte_bytes = src->components * sizeof(double);
318 0 0         if (arena) {
319 0           dst->matte = pdfmake_arena_alloc(arena, matte_bytes);
320             } else {
321 0           dst->matte = malloc(matte_bytes);
322             }
323 0 0         if (dst->matte) {
324 0           memcpy(dst->matte, src->matte, matte_bytes);
325             }
326             }
327            
328 0           return dst;
329             }
330              
331             /*============================================================================
332             * Pixel access
333             *==========================================================================*/
334              
335 0           void pdfmake_decoded_image_get_pixel(
336             pdfmake_decoded_image_t *img,
337             int x, int y,
338             uint8_t *out)
339             {
340             uint8_t *row;
341             uint8_t *px;
342             int i;
343 0 0         if (!img || !img->pixels || !out) return;
    0          
    0          
344 0 0         if (x < 0 || x >= img->width || y < 0 || y >= img->height) return;
    0          
    0          
    0          
345            
346 0           row = img->pixels + y * img->row_stride;
347 0           px = row + x * img->components;
348            
349 0 0         for (i = 0; i < img->components; i++) {
350 0           out[i] = px[i];
351             }
352             }
353              
354 0           uint8_t pdfmake_decoded_image_get_alpha(
355             pdfmake_decoded_image_t *img,
356             int x, int y)
357             {
358 0 0         if (!img) return 255;
359 0 0         if (!img->has_alpha || !img->alpha) return 255;
    0          
360 0 0         if (x < 0 || x >= img->width || y < 0 || y >= img->height) return 0;
    0          
    0          
    0          
361            
362 0           return img->alpha[y * img->width + x];
363             }
364              
365 0           uint32_t pdfmake_decoded_image_get_rgba(
366             pdfmake_decoded_image_t *img,
367             int x, int y)
368             {
369             uint8_t comp[4];
370             uint8_t alpha;
371             uint8_t r, g, b;
372 0 0         if (!img) return 0;
373 0 0         if (x < 0 || x >= img->width || y < 0 || y >= img->height) return 0;
    0          
    0          
    0          
374            
375             /* Fast path: pre-converted RGBA */
376 0 0         if (img->rgba) {
377 0           return img->rgba[y * img->width + x];
378             }
379            
380 0           pdfmake_decoded_image_get_pixel(img, x, y, comp);
381 0           alpha = pdfmake_decoded_image_get_alpha(img, x, y);
382            
383 0           switch (img->colorspace) {
384 0           case PDFMAKE_RCS_GRAY:
385 0           r = g = b = comp[0];
386 0           break;
387            
388 0           case PDFMAKE_RCS_RGB:
389 0           r = comp[0];
390 0           g = comp[1];
391 0           b = comp[2];
392 0           break;
393            
394 0           case PDFMAKE_RCS_CMYK:
395 0           pdfmake_cmyk_to_rgb8(comp[0], comp[1], comp[2], comp[3], &r, &g, &b);
396 0           break;
397            
398 0           case PDFMAKE_RCS_LAB: {
399 0           double L = comp[0] * 100.0 / 255.0;
400 0           double a = comp[1] - 128.0;
401 0           double bb = comp[2] - 128.0;
402 0           pdfmake_lab_to_rgb8(L, a, bb, &r, &g, &b);
403 0           break;
404             }
405            
406 0           case PDFMAKE_RCS_INDEXED:
407 0 0         if (img->palette && comp[0] < img->palette_entries) {
    0          
408 0           uint8_t *pal = img->palette + comp[0] * 3;
409 0           r = pal[0];
410 0           g = pal[1];
411 0           b = pal[2];
412             } else {
413 0           r = g = b = comp[0];
414             }
415 0           break;
416            
417 0           default:
418 0           r = g = b = 128;
419 0           break;
420             }
421            
422 0           return PDFMAKE_RGBA(r, g, b, alpha);
423             }
424              
425             /*============================================================================
426             * Color space conversion
427             *==========================================================================*/
428              
429 0           pdfmake_imgr_err_t pdfmake_decoded_image_expand_indexed(
430             pdfmake_decoded_image_t *img,
431             pdfmake_arena_t *arena)
432             {
433             size_t new_stride;
434             size_t new_len;
435             uint8_t *new_pixels;
436             int y;
437 0 0         if (!img) return PDFMAKE_IMGR_ERR_NULL;
438 0 0         if (img->colorspace != PDFMAKE_RCS_INDEXED) return PDFMAKE_IMGR_OK;
439 0 0         if (!img->palette) return PDFMAKE_IMGR_ERR_INVALID;
440            
441 0           new_stride = (size_t)img->width * 3;
442 0           new_len = new_stride * img->height;
443            
444 0 0         if (arena) {
445 0           new_pixels = pdfmake_arena_alloc(arena, new_len);
446             } else {
447 0           new_pixels = malloc(new_len);
448             }
449            
450 0 0         if (!new_pixels) return PDFMAKE_IMGR_ERR_MEMORY;
451            
452             /* Expand each index to RGB */
453 0 0         for (y = 0; y < img->height; y++) {
454 0           uint8_t *src_row = img->pixels + y * img->row_stride;
455 0           uint8_t *dst_row = new_pixels + y * new_stride;
456             int x;
457            
458 0 0         for (x = 0; x < img->width; x++) {
459 0           uint8_t idx = src_row[x];
460            
461 0 0         if (idx < img->palette_entries) {
462 0           uint8_t *pal = img->palette + idx * 3;
463 0           dst_row[x * 3 + 0] = pal[0];
464 0           dst_row[x * 3 + 1] = pal[1];
465 0           dst_row[x * 3 + 2] = pal[2];
466             } else {
467             /* Invalid index - use black */
468 0           dst_row[x * 3 + 0] = 0;
469 0           dst_row[x * 3 + 1] = 0;
470 0           dst_row[x * 3 + 2] = 0;
471             }
472             }
473             }
474            
475             /* Update image */
476 0 0         if (!arena && img->owns_data) {
    0          
477 0           free(img->pixels);
478             }
479            
480 0           img->pixels = new_pixels;
481 0           img->pixels_len = new_len;
482 0           img->row_stride = new_stride;
483 0           img->components = 3;
484 0           img->colorspace = PDFMAKE_RCS_RGB;
485            
486 0           return PDFMAKE_IMGR_OK;
487             }
488              
489 0           pdfmake_imgr_err_t pdfmake_decoded_image_to_rgba(
490             pdfmake_decoded_image_t *img,
491             pdfmake_arena_t *arena)
492             {
493 0 0         if (!img) return PDFMAKE_IMGR_ERR_NULL;
494 0 0         if (!img->pixels) return PDFMAKE_IMGR_ERR_INVALID;
495            
496             /* Already converted */
497 0 0         if (img->rgba) return PDFMAKE_IMGR_OK;
498            
499             /* Expand indexed first */
500 0 0         if (img->colorspace == PDFMAKE_RCS_INDEXED) {
501 0           pdfmake_imgr_err_t err = pdfmake_decoded_image_expand_indexed(img, arena);
502 0 0         if (err != PDFMAKE_IMGR_OK) return err;
503             }
504            
505 0           img->rgba_len = (size_t)img->width * img->height;
506            
507 0 0         if (arena) {
508 0           img->rgba = pdfmake_arena_alloc(arena, img->rgba_len * sizeof(uint32_t));
509             } else {
510 0           img->rgba = malloc(img->rgba_len * sizeof(uint32_t));
511             }
512            
513 0 0         if (!img->rgba) return PDFMAKE_IMGR_ERR_MEMORY;
514            
515             /* Convert each pixel */
516             {
517             int y;
518 0 0         for (y = 0; y < img->height; y++) {
519 0           uint8_t *row = img->pixels + y * img->row_stride;
520 0           uint32_t *rgba_row = img->rgba + y * img->width;
521             int x;
522            
523 0 0         for (x = 0; x < img->width; x++) {
524 0           uint8_t *px = row + x * img->components;
525 0           uint8_t alpha = pdfmake_decoded_image_get_alpha(img, x, y);
526             uint8_t r, g, b;
527            
528 0           switch (img->colorspace) {
529 0           case PDFMAKE_RCS_GRAY:
530 0           r = g = b = px[0];
531 0           break;
532            
533 0           case PDFMAKE_RCS_RGB:
534 0           r = px[0];
535 0           g = px[1];
536 0           b = px[2];
537 0           break;
538            
539 0           case PDFMAKE_RCS_CMYK:
540 0           pdfmake_cmyk_to_rgb8(px[0], px[1], px[2], px[3], &r, &g, &b);
541 0           break;
542            
543 0           case PDFMAKE_RCS_LAB: {
544 0           double L = px[0] * 100.0 / 255.0;
545 0           double a = px[1] - 128.0;
546 0           double bb = px[2] - 128.0;
547 0           pdfmake_lab_to_rgb8(L, a, bb, &r, &g, &b);
548 0           break;
549             }
550            
551 0           default:
552 0           r = g = b = 128;
553 0           break;
554             }
555            
556 0           rgba_row[x] = PDFMAKE_RGBA(r, g, b, alpha);
557             }
558             }
559             }
560            
561 0           return PDFMAKE_IMGR_OK;
562             }
563              
564             /*============================================================================
565             * Decode array application
566             *
567             * The Decode array maps raw sample values to color component values.
568             * Formula: output = Dmin + (sample / max_sample) * (Dmax - Dmin)
569             *==========================================================================*/
570              
571 0           void pdfmake_decoded_image_apply_decode(pdfmake_decoded_image_t *img)
572             {
573 0 0         if (!img || !img->pixels || !img->decode) return;
    0          
    0          
574 0 0         if (img->decode_len < 2 * (size_t)img->components) return;
575            
576             {
577 0           double max_sample = (1 << img->bits_per_component) - 1;
578             int y;
579 0 0         if (max_sample <= 0) max_sample = 255;
580            
581 0 0         for (y = 0; y < img->height; y++) {
582 0           uint8_t *row = img->pixels + y * img->row_stride;
583             int x;
584            
585 0 0         for (x = 0; x < img->width; x++) {
586 0           uint8_t *px = row + x * img->components;
587             int c;
588            
589 0 0         for (c = 0; c < img->components; c++) {
590 0           double dmin = img->decode[c * 2];
591 0           double dmax = img->decode[c * 2 + 1];
592            
593 0           double sample = px[c] / max_sample;
594 0           double value = dmin + sample * (dmax - dmin);
595            
596             /* Convert back to 0-255 */
597 0           int ival = (int)(value * 255.0 + 0.5);
598 0 0         if (ival < 0) ival = 0;
599 0 0         if (ival > 255) ival = 255;
600 0           px[c] = (uint8_t)ival;
601             }
602             }
603             }
604             }
605             }
606              
607             /*============================================================================
608             * Grayscale utilities
609             *==========================================================================*/
610              
611             /*
612             * Convert RGB image to grayscale.
613             */
614 0           pdfmake_imgr_err_t pdfmake_decoded_image_to_gray(
615             pdfmake_decoded_image_t *img,
616             pdfmake_arena_t *arena)
617             {
618             size_t new_stride;
619             size_t new_len;
620             uint8_t *new_pixels;
621             int y;
622 0 0         if (!img || !img->pixels) return PDFMAKE_IMGR_ERR_NULL;
    0          
623 0 0         if (img->colorspace == PDFMAKE_RCS_GRAY) return PDFMAKE_IMGR_OK;
624 0 0         if (img->colorspace != PDFMAKE_RCS_RGB) return PDFMAKE_IMGR_ERR_COLORSPACE;
625            
626 0           new_stride = (size_t)img->width;
627 0           new_len = new_stride * img->height;
628            
629 0 0         if (arena) {
630 0           new_pixels = pdfmake_arena_alloc(arena, new_len);
631             } else {
632 0           new_pixels = malloc(new_len);
633             }
634            
635 0 0         if (!new_pixels) return PDFMAKE_IMGR_ERR_MEMORY;
636            
637             /* Convert using luminance formula */
638 0 0         for (y = 0; y < img->height; y++) {
639 0           uint8_t *src_row = img->pixels + y * img->row_stride;
640 0           uint8_t *dst_row = new_pixels + y * new_stride;
641             int x;
642            
643 0 0         for (x = 0; x < img->width; x++) {
644 0           uint8_t r = src_row[x * 3 + 0];
645 0           uint8_t g = src_row[x * 3 + 1];
646 0           uint8_t b = src_row[x * 3 + 2];
647            
648             /* ITU-R BT.601 luma */
649 0           int gray = (299 * r + 587 * g + 114 * b) / 1000;
650 0           dst_row[x] = (uint8_t)gray;
651             }
652             }
653            
654 0 0         if (!arena && img->owns_data) {
    0          
655 0           free(img->pixels);
656             }
657            
658 0           img->pixels = new_pixels;
659 0           img->pixels_len = new_len;
660 0           img->row_stride = new_stride;
661 0           img->components = 1;
662 0           img->colorspace = PDFMAKE_RCS_GRAY;
663            
664 0           return PDFMAKE_IMGR_OK;
665             }
666              
667             /*
668             * Invert image (for masks, etc).
669             */
670 0           void pdfmake_decoded_image_invert(pdfmake_decoded_image_t *img)
671             {
672             size_t i;
673 0 0         if (!img || !img->pixels) return;
    0          
674            
675 0 0         for (i = 0; i < img->pixels_len; i++) {
676 0           img->pixels[i] = 255 - img->pixels[i];
677             }
678             }