File Coverage

src/pdfmake_image_scale.c
Criterion Covered Total %
statement 0 391 0.0
branch 0 208 0.0
condition n/a
subroutine n/a
pod n/a
total 0 599 0.0


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_image_scale.c - Image scaling algorithms
3             *
4             * Implements interpolation methods for scaling images:
5             * - Nearest neighbor: Fast, no interpolation
6             * - Bilinear: Smooth, good for scaling up
7             * - Bicubic: High quality, good for scaling down
8             *
9             * Reference: PDF 32000-1:2008 ยง8.9.5.3 Image Interpolation
10             */
11              
12             #include "pdfmake_image_render.h"
13             #include "pdfmake_arena.h"
14             #include
15             #include
16             #include
17              
18             /*============================================================================
19             * Nearest neighbor scaling
20             *==========================================================================*/
21              
22 0           static void scale_nearest_gray(
23             const uint8_t *src, int src_w, int src_h,
24             uint8_t *dst, int dst_w, int dst_h)
25             {
26 0           double x_ratio = (double)src_w / dst_w;
27 0           double y_ratio = (double)src_h / dst_h;
28             int x, y, src_x, src_y;
29             const uint8_t *src_row;
30             uint8_t *dst_row;
31            
32 0 0         for (y = 0; y < dst_h; y++) {
33 0           src_y = (int)(y * y_ratio);
34 0 0         if (src_y >= src_h) src_y = src_h - 1;
35            
36 0           src_row = src + src_y * src_w;
37 0           dst_row = dst + y * dst_w;
38            
39 0 0         for (x = 0; x < dst_w; x++) {
40 0           src_x = (int)(x * x_ratio);
41 0 0         if (src_x >= src_w) src_x = src_w - 1;
42            
43 0           dst_row[x] = src_row[src_x];
44             }
45             }
46 0           }
47              
48 0           static void scale_nearest_rgb(
49             const uint8_t *src, int src_w, int src_h,
50             uint8_t *dst, int dst_w, int dst_h)
51             {
52 0           double x_ratio = (double)src_w / dst_w;
53 0           double y_ratio = (double)src_h / dst_h;
54 0           int src_stride = src_w * 3;
55 0           int dst_stride = dst_w * 3;
56             int x, y, src_x, src_y;
57             const uint8_t *src_row;
58             uint8_t *dst_row;
59            
60 0 0         for (y = 0; y < dst_h; y++) {
61 0           src_y = (int)(y * y_ratio);
62 0 0         if (src_y >= src_h) src_y = src_h - 1;
63            
64 0           src_row = src + src_y * src_stride;
65 0           dst_row = dst + y * dst_stride;
66            
67 0 0         for (x = 0; x < dst_w; x++) {
68 0           src_x = (int)(x * x_ratio);
69 0 0         if (src_x >= src_w) src_x = src_w - 1;
70            
71 0           dst_row[x * 3 + 0] = src_row[src_x * 3 + 0];
72 0           dst_row[x * 3 + 1] = src_row[src_x * 3 + 1];
73 0           dst_row[x * 3 + 2] = src_row[src_x * 3 + 2];
74             }
75             }
76 0           }
77              
78 0           static void scale_nearest_rgba32(
79             const uint32_t *src, int src_w, int src_h,
80             uint32_t *dst, int dst_w, int dst_h)
81             {
82 0           double x_ratio = (double)src_w / dst_w;
83 0           double y_ratio = (double)src_h / dst_h;
84             int x, y, src_x, src_y;
85             const uint32_t *src_row;
86             uint32_t *dst_row;
87            
88 0 0         for (y = 0; y < dst_h; y++) {
89 0           src_y = (int)(y * y_ratio);
90 0 0         if (src_y >= src_h) src_y = src_h - 1;
91            
92 0           src_row = src + src_y * src_w;
93 0           dst_row = dst + y * dst_w;
94            
95 0 0         for (x = 0; x < dst_w; x++) {
96 0           src_x = (int)(x * x_ratio);
97 0 0         if (src_x >= src_w) src_x = src_w - 1;
98            
99 0           dst_row[x] = src_row[src_x];
100             }
101             }
102 0           }
103              
104             /*============================================================================
105             * Bilinear interpolation
106             *==========================================================================*/
107              
108 0           static PDFMAKE_INLINE uint8_t bilinear_sample_gray(
109             const uint8_t *src, int src_w, int src_h,
110             double fx, double fy)
111             {
112 0           int x0 = (int)fx;
113 0           int y0 = (int)fy;
114 0           int x1 = x0 + 1;
115 0           int y1 = y0 + 1;
116             double tx, ty;
117             double p00, p10, p01, p11;
118             double top, bot, val;
119            
120 0 0         if (x0 < 0) x0 = 0;
121 0 0         if (y0 < 0) y0 = 0;
122 0 0         if (x1 >= src_w) x1 = src_w - 1;
123 0 0         if (y1 >= src_h) y1 = src_h - 1;
124            
125 0           tx = fx - x0;
126 0           ty = fy - y0;
127            
128 0           p00 = src[y0 * src_w + x0];
129 0           p10 = src[y0 * src_w + x1];
130 0           p01 = src[y1 * src_w + x0];
131 0           p11 = src[y1 * src_w + x1];
132            
133 0           top = p00 + tx * (p10 - p00);
134 0           bot = p01 + tx * (p11 - p01);
135 0           val = top + ty * (bot - top);
136            
137 0           return (uint8_t)(val + 0.5);
138             }
139              
140 0           static void scale_bilinear_gray(
141             const uint8_t *src, int src_w, int src_h,
142             uint8_t *dst, int dst_w, int dst_h)
143             {
144 0           double x_ratio = (double)(src_w - 1) / (dst_w - 1);
145 0           double y_ratio = (double)(src_h - 1) / (dst_h - 1);
146             int x, y;
147             double fx, fy;
148             uint8_t *dst_row;
149            
150 0 0         if (dst_w == 1) x_ratio = 0;
151 0 0         if (dst_h == 1) y_ratio = 0;
152            
153 0 0         for (y = 0; y < dst_h; y++) {
154 0           fy = y * y_ratio;
155 0           dst_row = dst + y * dst_w;
156            
157 0 0         for (x = 0; x < dst_w; x++) {
158 0           fx = x * x_ratio;
159 0           dst_row[x] = bilinear_sample_gray(src, src_w, src_h, fx, fy);
160             }
161             }
162 0           }
163              
164 0           static void scale_bilinear_rgb(
165             const uint8_t *src, int src_w, int src_h,
166             uint8_t *dst, int dst_w, int dst_h)
167             {
168 0           double x_ratio = (double)(src_w - 1) / (dst_w - 1);
169 0           double y_ratio = (double)(src_h - 1) / (dst_h - 1);
170 0           int src_stride = src_w * 3;
171 0           int dst_stride = dst_w * 3;
172             int x, y, c;
173             int x0, x1, y0, y1;
174             double fx, fy, tx, ty;
175             double p00, p10, p01, p11;
176             double top, bot, val;
177             uint8_t *dst_row;
178            
179 0 0         if (dst_w == 1) x_ratio = 0;
180 0 0         if (dst_h == 1) y_ratio = 0;
181            
182 0 0         for (y = 0; y < dst_h; y++) {
183 0           fy = y * y_ratio;
184 0           y0 = (int)fy;
185 0           y1 = y0 + 1;
186 0 0         if (y0 < 0) y0 = 0;
187 0 0         if (y1 >= src_h) y1 = src_h - 1;
188 0           ty = fy - (int)fy;
189            
190 0           dst_row = dst + y * dst_stride;
191            
192 0 0         for (x = 0; x < dst_w; x++) {
193 0           fx = x * x_ratio;
194 0           x0 = (int)fx;
195 0           x1 = x0 + 1;
196 0 0         if (x0 < 0) x0 = 0;
197 0 0         if (x1 >= src_w) x1 = src_w - 1;
198 0           tx = fx - (int)fx;
199            
200 0 0         for (c = 0; c < 3; c++) {
201 0           p00 = src[y0 * src_stride + x0 * 3 + c];
202 0           p10 = src[y0 * src_stride + x1 * 3 + c];
203 0           p01 = src[y1 * src_stride + x0 * 3 + c];
204 0           p11 = src[y1 * src_stride + x1 * 3 + c];
205            
206 0           top = p00 + tx * (p10 - p00);
207 0           bot = p01 + tx * (p11 - p01);
208 0           val = top + ty * (bot - top);
209            
210 0           dst_row[x * 3 + c] = (uint8_t)(val + 0.5);
211             }
212             }
213             }
214 0           }
215              
216 0           static void scale_bilinear_rgba32(
217             const uint32_t *src, int src_w, int src_h,
218             uint32_t *dst, int dst_w, int dst_h)
219             {
220 0           double x_ratio = (double)(src_w - 1) / (dst_w - 1);
221 0           double y_ratio = (double)(src_h - 1) / (dst_h - 1);
222             int x, y;
223             int x0, x1, y0, y1;
224             double fx, fy, tx, ty;
225             uint32_t p00, p10, p01, p11;
226             uint8_t a, r, g, b;
227             double a00, a10, a01, a11, atop, abot;
228             double r00, r10, r01, r11, rtop, rbot;
229             double g00, g10, g01, g11, gtop, gbot;
230             double b00, b10, b01, b11, btop, bbot;
231             uint32_t *dst_row;
232            
233 0 0         if (dst_w == 1) x_ratio = 0;
234 0 0         if (dst_h == 1) y_ratio = 0;
235            
236 0 0         for (y = 0; y < dst_h; y++) {
237 0           fy = y * y_ratio;
238 0           y0 = (int)fy;
239 0           y1 = y0 + 1;
240 0 0         if (y0 < 0) y0 = 0;
241 0 0         if (y1 >= src_h) y1 = src_h - 1;
242 0           ty = fy - (int)fy;
243            
244 0           dst_row = dst + y * dst_w;
245            
246 0 0         for (x = 0; x < dst_w; x++) {
247 0           fx = x * x_ratio;
248 0           x0 = (int)fx;
249 0           x1 = x0 + 1;
250 0 0         if (x0 < 0) x0 = 0;
251 0 0         if (x1 >= src_w) x1 = src_w - 1;
252 0           tx = fx - (int)fx;
253            
254 0           p00 = src[y0 * src_w + x0];
255 0           p10 = src[y0 * src_w + x1];
256 0           p01 = src[y1 * src_w + x0];
257 0           p11 = src[y1 * src_w + x1];
258            
259             /* Interpolate each channel */
260            
261             /* Alpha */
262 0           a00 = PDFMAKE_RGBA_A(p00);
263 0           a10 = PDFMAKE_RGBA_A(p10);
264 0           a01 = PDFMAKE_RGBA_A(p01);
265 0           a11 = PDFMAKE_RGBA_A(p11);
266 0           atop = a00 + tx * (a10 - a00);
267 0           abot = a01 + tx * (a11 - a01);
268 0           a = (uint8_t)(atop + ty * (abot - atop) + 0.5);
269            
270             /* Red */
271 0           r00 = PDFMAKE_RGBA_R(p00);
272 0           r10 = PDFMAKE_RGBA_R(p10);
273 0           r01 = PDFMAKE_RGBA_R(p01);
274 0           r11 = PDFMAKE_RGBA_R(p11);
275 0           rtop = r00 + tx * (r10 - r00);
276 0           rbot = r01 + tx * (r11 - r01);
277 0           r = (uint8_t)(rtop + ty * (rbot - rtop) + 0.5);
278            
279             /* Green */
280 0           g00 = PDFMAKE_RGBA_G(p00);
281 0           g10 = PDFMAKE_RGBA_G(p10);
282 0           g01 = PDFMAKE_RGBA_G(p01);
283 0           g11 = PDFMAKE_RGBA_G(p11);
284 0           gtop = g00 + tx * (g10 - g00);
285 0           gbot = g01 + tx * (g11 - g01);
286 0           g = (uint8_t)(gtop + ty * (gbot - gtop) + 0.5);
287            
288             /* Blue */
289 0           b00 = PDFMAKE_RGBA_B(p00);
290 0           b10 = PDFMAKE_RGBA_B(p10);
291 0           b01 = PDFMAKE_RGBA_B(p01);
292 0           b11 = PDFMAKE_RGBA_B(p11);
293 0           btop = b00 + tx * (b10 - b00);
294 0           bbot = b01 + tx * (b11 - b01);
295 0           b = (uint8_t)(btop + ty * (bbot - btop) + 0.5);
296            
297 0           dst_row[x] = PDFMAKE_RGBA(r, g, b, a);
298             }
299             }
300 0           }
301              
302             /*============================================================================
303             * Bicubic interpolation (Mitchell-Netravali)
304             *==========================================================================*/
305              
306             /* Mitchell-Netravali coefficients (B=1/3, C=1/3) */
307             #define MN_B (1.0/3.0)
308             #define MN_C (1.0/3.0)
309              
310 0           static double mitchell_kernel(double x) {
311 0           x = fabs(x);
312            
313 0 0         if (x < 1.0) {
314 0           return ((12.0 - 9.0*MN_B - 6.0*MN_C) * x*x*x +
315 0           (-18.0 + 12.0*MN_B + 6.0*MN_C) * x*x +
316 0           (6.0 - 2.0*MN_B)) / 6.0;
317 0 0         } else if (x < 2.0) {
318 0           return ((-MN_B - 6.0*MN_C) * x*x*x +
319 0           (6.0*MN_B + 30.0*MN_C) * x*x +
320 0           (-12.0*MN_B - 48.0*MN_C) * x +
321 0           (8.0*MN_B + 24.0*MN_C)) / 6.0;
322             }
323 0           return 0.0;
324             }
325              
326 0           static void scale_bicubic_gray(
327             const uint8_t *src, int src_w, int src_h,
328             uint8_t *dst, int dst_w, int dst_h)
329             {
330 0           double x_ratio = (double)src_w / dst_w;
331 0           double y_ratio = (double)src_h / dst_h;
332             int dx, dy, i, j;
333             int sx, sy, ix, iy;
334             int val;
335             double fx, fy, tx, ty;
336             double sum, weight_sum, wx, wy, w;
337             uint8_t *dst_row;
338            
339 0 0         for (dy = 0; dy < dst_h; dy++) {
340 0           fy = (dy + 0.5) * y_ratio - 0.5;
341 0           iy = (int)floor(fy);
342 0           ty = fy - iy;
343            
344 0           dst_row = dst + dy * dst_w;
345            
346 0 0         for (dx = 0; dx < dst_w; dx++) {
347 0           fx = (dx + 0.5) * x_ratio - 0.5;
348 0           ix = (int)floor(fx);
349 0           tx = fx - ix;
350            
351 0           sum = 0;
352 0           weight_sum = 0;
353            
354             /* Sample 4x4 neighborhood */
355 0 0         for (j = -1; j <= 2; j++) {
356 0           sy = iy + j;
357 0 0         if (sy < 0) sy = 0;
358 0 0         if (sy >= src_h) sy = src_h - 1;
359            
360 0           wy = mitchell_kernel(ty - j);
361            
362 0 0         for (i = -1; i <= 2; i++) {
363 0           sx = ix + i;
364 0 0         if (sx < 0) sx = 0;
365 0 0         if (sx >= src_w) sx = src_w - 1;
366            
367 0           wx = mitchell_kernel(tx - i);
368 0           w = wx * wy;
369            
370 0           sum += src[sy * src_w + sx] * w;
371 0           weight_sum += w;
372             }
373             }
374            
375 0 0         if (weight_sum > 0) {
376 0           val = (int)(sum / weight_sum + 0.5);
377 0 0         if (val < 0) val = 0;
378 0 0         if (val > 255) val = 255;
379 0           dst_row[dx] = (uint8_t)val;
380             } else {
381 0           dst_row[dx] = 0;
382             }
383             }
384             }
385 0           }
386              
387 0           static void scale_bicubic_rgb(
388             const uint8_t *src, int src_w, int src_h,
389             uint8_t *dst, int dst_w, int dst_h)
390             {
391 0           double x_ratio = (double)src_w / dst_w;
392 0           double y_ratio = (double)src_h / dst_h;
393 0           int src_stride = src_w * 3;
394 0           int dst_stride = dst_w * 3;
395             int dx, dy, i, j, c;
396             int sx, sy, ix, iy;
397             int val;
398             double fx, fy, tx, ty;
399             double sum[3];
400             double weight_sum, wx, wy, w;
401             const uint8_t *px;
402             uint8_t *dst_row;
403            
404 0 0         for (dy = 0; dy < dst_h; dy++) {
405 0           fy = (dy + 0.5) * y_ratio - 0.5;
406 0           iy = (int)floor(fy);
407 0           ty = fy - iy;
408            
409 0           dst_row = dst + dy * dst_stride;
410            
411 0 0         for (dx = 0; dx < dst_w; dx++) {
412 0           fx = (dx + 0.5) * x_ratio - 0.5;
413 0           ix = (int)floor(fx);
414 0           tx = fx - ix;
415            
416 0           sum[0] = 0;
417 0           sum[1] = 0;
418 0           sum[2] = 0;
419 0           weight_sum = 0;
420            
421 0 0         for (j = -1; j <= 2; j++) {
422 0           sy = iy + j;
423 0 0         if (sy < 0) sy = 0;
424 0 0         if (sy >= src_h) sy = src_h - 1;
425            
426 0           wy = mitchell_kernel(ty - j);
427            
428 0 0         for (i = -1; i <= 2; i++) {
429 0           sx = ix + i;
430 0 0         if (sx < 0) sx = 0;
431 0 0         if (sx >= src_w) sx = src_w - 1;
432            
433 0           wx = mitchell_kernel(tx - i);
434 0           w = wx * wy;
435            
436 0           px = src + sy * src_stride + sx * 3;
437 0           sum[0] += px[0] * w;
438 0           sum[1] += px[1] * w;
439 0           sum[2] += px[2] * w;
440 0           weight_sum += w;
441             }
442             }
443            
444 0 0         for (c = 0; c < 3; c++) {
445 0           val = 0;
446 0 0         if (weight_sum > 0) {
447 0           val = (int)(sum[c] / weight_sum + 0.5);
448             }
449 0 0         if (val < 0) val = 0;
450 0 0         if (val > 255) val = 255;
451 0           dst_row[dx * 3 + c] = (uint8_t)val;
452             }
453             }
454             }
455 0           }
456              
457 0           static void scale_bicubic_rgba32(
458             const uint32_t *src, int src_w, int src_h,
459             uint32_t *dst, int dst_w, int dst_h)
460             {
461 0           double x_ratio = (double)src_w / dst_w;
462 0           double y_ratio = (double)src_h / dst_h;
463             int dx, dy, i, j;
464             int sx, sy, ix, iy;
465             int va, vr, vg, vb;
466             double fx, fy, tx, ty;
467             double sum_a, sum_r, sum_g, sum_b;
468             double weight_sum, wx, wy, w;
469             uint32_t px;
470             uint8_t a, r, g, b;
471             uint32_t *dst_row;
472            
473 0 0         for (dy = 0; dy < dst_h; dy++) {
474 0           fy = (dy + 0.5) * y_ratio - 0.5;
475 0           iy = (int)floor(fy);
476 0           ty = fy - iy;
477            
478 0           dst_row = dst + dy * dst_w;
479            
480 0 0         for (dx = 0; dx < dst_w; dx++) {
481 0           fx = (dx + 0.5) * x_ratio - 0.5;
482 0           ix = (int)floor(fx);
483 0           tx = fx - ix;
484            
485 0           sum_a = 0;
486 0           sum_r = 0;
487 0           sum_g = 0;
488 0           sum_b = 0;
489 0           weight_sum = 0;
490            
491 0 0         for (j = -1; j <= 2; j++) {
492 0           sy = iy + j;
493 0 0         if (sy < 0) sy = 0;
494 0 0         if (sy >= src_h) sy = src_h - 1;
495            
496 0           wy = mitchell_kernel(ty - j);
497            
498 0 0         for (i = -1; i <= 2; i++) {
499 0           sx = ix + i;
500 0 0         if (sx < 0) sx = 0;
501 0 0         if (sx >= src_w) sx = src_w - 1;
502            
503 0           wx = mitchell_kernel(tx - i);
504 0           w = wx * wy;
505            
506 0           px = src[sy * src_w + sx];
507 0           sum_a += PDFMAKE_RGBA_A(px) * w;
508 0           sum_r += PDFMAKE_RGBA_R(px) * w;
509 0           sum_g += PDFMAKE_RGBA_G(px) * w;
510 0           sum_b += PDFMAKE_RGBA_B(px) * w;
511 0           weight_sum += w;
512             }
513             }
514            
515 0           a = 0;
516 0           r = 0;
517 0           g = 0;
518 0           b = 0;
519 0 0         if (weight_sum > 0) {
520 0           va = (int)(sum_a / weight_sum + 0.5);
521 0           vr = (int)(sum_r / weight_sum + 0.5);
522 0           vg = (int)(sum_g / weight_sum + 0.5);
523 0           vb = (int)(sum_b / weight_sum + 0.5);
524            
525 0 0         a = (uint8_t)PDFMAKE_CLAMP8(va);
    0          
526 0 0         r = (uint8_t)PDFMAKE_CLAMP8(vr);
    0          
527 0 0         g = (uint8_t)PDFMAKE_CLAMP8(vg);
    0          
528 0 0         b = (uint8_t)PDFMAKE_CLAMP8(vb);
    0          
529             }
530            
531 0           dst_row[dx] = PDFMAKE_RGBA(r, g, b, a);
532             }
533             }
534 0           }
535              
536             /*============================================================================
537             * Public API
538             *==========================================================================*/
539              
540 0           pdfmake_imgr_err_t pdfmake_decoded_image_scale(
541             pdfmake_decoded_image_t *src,
542             int dst_width, int dst_height,
543             pdfmake_interp_mode_t mode,
544             pdfmake_decoded_image_t **out,
545             pdfmake_arena_t *arena)
546             {
547             pdfmake_decoded_image_t *dst;
548            
549 0 0         if (!src || !out) return PDFMAKE_IMGR_ERR_NULL;
    0          
550 0 0         if (!src->pixels) return PDFMAKE_IMGR_ERR_INVALID;
551 0 0         if (dst_width <= 0 || dst_height <= 0) return PDFMAKE_IMGR_ERR_INVALID;
    0          
552            
553             /* Create destination image */
554 0           dst = pdfmake_decoded_image_create_sized(
555             dst_width, dst_height, src->colorspace, arena);
556            
557 0 0         if (!dst) return PDFMAKE_IMGR_ERR_MEMORY;
558            
559             /* Copy metadata */
560 0           dst->bits_per_component = src->bits_per_component;
561 0           dst->interpolate = src->interpolate;
562 0           dst->has_alpha = src->has_alpha;
563 0           dst->premultiplied = src->premultiplied;
564            
565             /* Scale based on colorspace and mode */
566 0 0         if (src->components == 1) {
567             /* Grayscale / indexed */
568 0           switch (mode) {
569 0           case PDFMAKE_INTERP_NEAREST:
570 0           scale_nearest_gray(src->pixels, src->width, src->height,
571             dst->pixels, dst_width, dst_height);
572 0           break;
573 0           case PDFMAKE_INTERP_BILINEAR:
574 0           scale_bilinear_gray(src->pixels, src->width, src->height,
575             dst->pixels, dst_width, dst_height);
576 0           break;
577 0           case PDFMAKE_INTERP_BICUBIC:
578 0           scale_bicubic_gray(src->pixels, src->width, src->height,
579             dst->pixels, dst_width, dst_height);
580 0           break;
581             }
582 0 0         } else if (src->components == 3) {
583             /* RGB */
584 0           switch (mode) {
585 0           case PDFMAKE_INTERP_NEAREST:
586 0           scale_nearest_rgb(src->pixels, src->width, src->height,
587             dst->pixels, dst_width, dst_height);
588 0           break;
589 0           case PDFMAKE_INTERP_BILINEAR:
590 0           scale_bilinear_rgb(src->pixels, src->width, src->height,
591             dst->pixels, dst_width, dst_height);
592 0           break;
593 0           case PDFMAKE_INTERP_BICUBIC:
594 0           scale_bicubic_rgb(src->pixels, src->width, src->height,
595             dst->pixels, dst_width, dst_height);
596 0           break;
597             }
598 0 0         } else if (src->components == 4) {
599             /* CMYK - convert to RGB first then scale? Or scale as 4-channel */
600             /* For now, scale each channel independently like RGB */
601 0 0         switch (mode) {
602 0           case PDFMAKE_INTERP_NEAREST:
603 0           scale_nearest_rgb(src->pixels, src->width, src->height,
604             dst->pixels, dst_width, dst_height);
605             /* Handle 4th channel */
606 0           break;
607 0           default:
608             /* Just do nearest for now */
609 0           scale_nearest_rgb(src->pixels, src->width, src->height,
610             dst->pixels, dst_width, dst_height);
611 0           break;
612             }
613             }
614            
615             /* Scale alpha channel if present */
616 0 0         if (src->has_alpha && src->alpha) {
    0          
617 0           dst->alpha_len = (size_t)dst_width * dst_height;
618 0 0         if (arena) {
619 0           dst->alpha = pdfmake_arena_alloc(arena, dst->alpha_len);
620             } else {
621 0           dst->alpha = malloc(dst->alpha_len);
622             }
623            
624 0 0         if (dst->alpha) {
625 0           switch (mode) {
626 0           case PDFMAKE_INTERP_NEAREST:
627 0           scale_nearest_gray(src->alpha, src->width, src->height,
628             dst->alpha, dst_width, dst_height);
629 0           break;
630 0           case PDFMAKE_INTERP_BILINEAR:
631 0           scale_bilinear_gray(src->alpha, src->width, src->height,
632             dst->alpha, dst_width, dst_height);
633 0           break;
634 0           case PDFMAKE_INTERP_BICUBIC:
635 0           scale_bicubic_gray(src->alpha, src->width, src->height,
636             dst->alpha, dst_width, dst_height);
637 0           break;
638             }
639             }
640             }
641            
642 0           *out = dst;
643 0           return PDFMAKE_IMGR_OK;
644             }
645              
646 0           pdfmake_imgr_err_t pdfmake_rgba_scale(
647             const uint32_t *src, int src_w, int src_h,
648             uint32_t *dst, int dst_w, int dst_h,
649             pdfmake_interp_mode_t mode)
650             {
651 0 0         if (!src || !dst) return PDFMAKE_IMGR_ERR_NULL;
    0          
652 0 0         if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
    0          
    0          
    0          
653 0           return PDFMAKE_IMGR_ERR_INVALID;
654             }
655            
656 0           switch (mode) {
657 0           case PDFMAKE_INTERP_NEAREST:
658 0           scale_nearest_rgba32(src, src_w, src_h, dst, dst_w, dst_h);
659 0           break;
660 0           case PDFMAKE_INTERP_BILINEAR:
661 0           scale_bilinear_rgba32(src, src_w, src_h, dst, dst_w, dst_h);
662 0           break;
663 0           case PDFMAKE_INTERP_BICUBIC:
664 0           scale_bicubic_rgba32(src, src_w, src_h, dst, dst_w, dst_h);
665 0           break;
666 0           default:
667 0           return PDFMAKE_IMGR_ERR_INVALID;
668             }
669            
670 0           return PDFMAKE_IMGR_OK;
671             }
672              
673             /*============================================================================
674             * Convenience: scale in place (allocates new buffer)
675             *==========================================================================*/
676              
677 0           pdfmake_imgr_err_t pdfmake_decoded_image_resize(
678             pdfmake_decoded_image_t *img,
679             int new_width, int new_height,
680             pdfmake_interp_mode_t mode,
681             pdfmake_arena_t *arena)
682             {
683             pdfmake_decoded_image_t *scaled;
684             pdfmake_imgr_err_t err;
685            
686 0 0         if (!img) return PDFMAKE_IMGR_ERR_NULL;
687            
688 0           err = pdfmake_decoded_image_scale(
689             img, new_width, new_height, mode, &scaled, arena);
690            
691 0 0         if (err != PDFMAKE_IMGR_OK) return err;
692            
693             /* Swap data */
694 0 0         if (!arena && img->owns_data) {
    0          
695 0           free(img->pixels);
696 0           free(img->alpha);
697 0           free(img->rgba);
698             }
699            
700 0           img->width = scaled->width;
701 0           img->height = scaled->height;
702 0           img->pixels = scaled->pixels;
703 0           img->pixels_len = scaled->pixels_len;
704 0           img->row_stride = scaled->row_stride;
705 0           img->alpha = scaled->alpha;
706 0           img->alpha_len = scaled->alpha_len;
707 0           img->rgba = NULL; /* Invalidate RGBA cache */
708 0           img->rgba_len = 0;
709            
710             /* Free the wrapper struct only */
711 0 0         if (!arena) free(scaled);
712            
713 0           return PDFMAKE_IMGR_OK;
714             }