• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  * Copyright (C) 2016 Mopria Alliance, Inc.
4  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include <math.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "wprint_image.h"
23 #include "lib_wprint.h"
24 
25 #define TAG "wprint_image"
26 #define MIN_DECODE_MEM (1 * 1024 * 1024)
27 #define MAX_DECODE_MEM (4 * 1024 * 1024)
28 
wprint_image_setup(wprint_image_info_t * image_info,const char * mime_type,const ifc_wprint_t * wprint_ifc,unsigned int output_resolution,int pdf_render_resolution)29 void wprint_image_setup(wprint_image_info_t *image_info, const char *mime_type,
30         const ifc_wprint_t *wprint_ifc, unsigned int output_resolution,
31         int pdf_render_resolution) {
32     if (image_info != NULL) {
33         LOGD("image_setup");
34         memset(image_info, 0, sizeof(wprint_image_info_t));
35         image_info->wprint_ifc = wprint_ifc;
36         image_info->mime_type = mime_type;
37         image_info->print_resolution = output_resolution;
38         image_info->pdf_render_resolution = pdf_render_resolution;
39     }
40 }
41 
wprint_image_get_info(FILE * imgfile,wprint_image_info_t * image_info)42 status_t wprint_image_get_info(FILE *imgfile, wprint_image_info_t *image_info) {
43     if (image_info == NULL) return ERROR;
44 
45     image_info->imgfile = imgfile;
46     image_info->rotation = ROT_0;
47     image_info->swath_start = -1;
48     image_info->rows_cached = 0;
49     image_info->output_cache = NULL;
50     image_info->output_swath_start = -1;
51     image_info->scaled_sample_size = 1;
52 
53     image_info->stripe_height = 0;
54     image_info->unscaled_rows = NULL;
55     image_info->unscaled_rows_needed = 0;
56     image_info->mixed_memory = NULL;
57     image_info->mixed_memory_needed = 0;
58     image_info->scaled_width = -1;
59     image_info->scaled_height = -1;
60     image_info->unscaled_start_row = -1;
61     image_info->unscaled_end_row = -1;
62     image_info->scaling_needed = FALSE;
63 
64     image_info->output_padding_top = 0;
65     image_info->output_padding_left = 0;
66     image_info->output_padding_right = 0;
67     image_info->output_padding_bottom = 0;
68 
69     const image_decode_ifc_t *decode_ifc = image_info->decode_ifc;
70 
71     if ((decode_ifc != NULL) && (decode_ifc->get_hdr != NULL)) {
72         if (OK == decode_ifc->get_hdr(image_info)) {
73             LOGI("wprint_image_get_info(): %s dim = %dx%d", image_info->mime_type,
74                     image_info->width, image_info->height);
75             return OK;
76         } else {
77             LOGE("ERROR: get_hdr for %s", image_info->mime_type);
78             return ERROR;
79         }
80     }
81     LOGE("Unsupported image type: %s", image_info->mime_type);
82     return ERROR;
83 }
84 
wprint_image_set_output_properties(wprint_image_info_t * image_info,wprint_rotation_t rotation,unsigned int printable_width,unsigned int printable_height,unsigned int top_margin,unsigned int left_margin,unsigned int right_margin,unsigned int bottom_margin,unsigned int render_flags,unsigned int max_decode_stripe,unsigned int concurrent_stripes,unsigned int padding_options,pcl_t pclenum)85 status_t wprint_image_set_output_properties(wprint_image_info_t *image_info,
86         wprint_rotation_t rotation, unsigned int printable_width, unsigned int printable_height,
87         unsigned int top_margin, unsigned int left_margin, unsigned int right_margin,
88         unsigned int bottom_margin, unsigned int render_flags, unsigned int max_decode_stripe,
89         unsigned int concurrent_stripes, unsigned int padding_options, pcl_t pclenum) {
90     // validate rotation
91     switch (rotation) {
92         default:
93             rotation = ROT_0;
94         case ROT_0:
95         case ROT_90:
96         case ROT_180:
97         case ROT_270:
98             break;
99     }
100 
101     // rotate margins
102     switch (rotation) {
103         case ROT_90:
104         case ROT_270: {
105             unsigned int temp;
106             temp = top_margin;
107             top_margin = left_margin;
108             left_margin = bottom_margin;
109             bottom_margin = right_margin;
110             right_margin = temp;
111             break;
112         }
113         default:
114             break;
115     }
116 
117     unsigned int input_render_flags = render_flags;
118 
119     // store padding options
120     image_info->padding_options = (padding_options & PAD_ALL);
121 
122     // store margin adjusted printable area
123     if (pclenum == PCLPWG) {
124         // no need to adjust the margins again for PWG raster
125         image_info->printable_width = printable_width;
126         image_info->printable_height = printable_height;
127     } else {
128         image_info->printable_width = printable_width - (left_margin + right_margin);
129         image_info->printable_height = printable_height - (top_margin + bottom_margin);
130     }
131 
132     // store rendering parameters
133     image_info->render_flags = render_flags;
134     image_info->output_rows = max_decode_stripe;
135     image_info->stripe_height = max_decode_stripe;
136     image_info->concurrent_stripes = concurrent_stripes;
137 
138     // free data just in case
139     if (image_info->unscaled_rows != NULL) {
140         free(image_info->unscaled_rows);
141     }
142 
143     // free data just in case
144     if (image_info->mixed_memory != NULL) {
145         free(image_info->mixed_memory);
146     }
147 
148     image_info->row_offset = 0;
149     image_info->col_offset = 0;
150     image_info->scaled_sample_size = 1;
151     image_info->scaled_width = -1;
152     image_info->scaled_height = -1;
153     image_info->unscaled_start_row = -1;
154     image_info->unscaled_end_row = -1;
155     image_info->unscaled_rows = NULL;
156     image_info->unscaled_rows_needed = 0;
157     image_info->mixed_memory = NULL;
158     image_info->mixed_memory_needed = 0;
159     image_info->rotation = rotation;
160 
161     unsigned int image_output_width;
162     unsigned int image_output_height;
163 
164     // save margins
165     switch (image_info->rotation) {
166         case ROT_180:
167         case ROT_270:
168             image_info->output_padding_top = bottom_margin;
169             image_info->output_padding_left = right_margin;
170             image_info->output_padding_right = left_margin;
171             image_info->output_padding_bottom = top_margin;
172             break;
173         case ROT_0:
174         case ROT_90:
175         default:
176             image_info->output_padding_top = top_margin;
177             image_info->output_padding_left = left_margin;
178             image_info->output_padding_right = right_margin;
179             image_info->output_padding_bottom = bottom_margin;
180             break;
181     }
182 
183     // swap dimensions
184     switch (image_info->rotation) {
185         case ROT_90:
186         case ROT_270:
187             image_output_width = image_info->height;
188             image_output_height = image_info->width;
189             break;
190         case ROT_0:
191         case ROT_180:
192         default:
193             image_output_width = image_info->width;
194             image_output_height = image_info->height;
195             break;
196     }
197 
198     int native_units = 0;
199 
200     const image_decode_ifc_t *decode_ifc = image_info->decode_ifc;
201     if ((image_info->render_flags & RENDER_FLAG_AUTO_FIT) && (decode_ifc != NULL) &&
202             (decode_ifc->native_units != NULL)) {
203         native_units = decode_ifc->native_units(image_info);
204     }
205 
206     if (native_units <= 0) {
207         native_units = image_info->print_resolution;
208     }
209 
210     float native_scaling = 1.0f;
211     unsigned int native_image_output_width = image_output_width;
212     unsigned int native_image_output_height = image_output_height;
213 
214     if ((native_units != image_info->print_resolution)
215             && !((image_info->render_flags & RENDER_FLAG_AUTO_SCALE)
216                     || ((image_info->render_flags & RENDER_FLAG_AUTO_FIT)
217                             && !(image_info->render_flags & RENDER_FLAG_DOCUMENT_SCALING)))) {
218         native_scaling = (image_info->print_resolution * 1.0f) / (native_units * 1.0f);
219         native_image_output_width = (int) floorf(image_output_width * native_scaling);
220         native_image_output_height = (int) floorf(image_output_height * native_scaling);
221         LOGD("need to do native scaling by %f factor to size %dx%d", native_scaling,
222                 native_image_output_width, native_image_output_height);
223     }
224 
225     // if we have to scale determine if we can use subsampling to scale down
226     if ((image_info->render_flags & (RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT)) &&
227             (native_scaling == 1.0f)) {
228         LOGD("calculating subsampling");
229 
230         /*
231          * Find a subsampling scale factor that produces an image that is still bigger
232          * than the printable area and then finish scaling later using the fine-scaler.
233          * This produces better quality than subsampling to a smaller size and scaling up.
234          */
235         image_info->scaled_sample_size = 1;
236         if ((decode_ifc != NULL) && (decode_ifc->supports_subsampling(image_info) == OK)) {
237             // subsampling supported
238             int next_width, next_height;
239             next_width = image_output_width >> 1;
240             next_height = image_output_height >> 1;
241             while (((image_info->render_flags & RENDER_FLAG_AUTO_SCALE) &&
242                     (next_width > image_info->printable_width) &&
243                     (next_height > image_info->printable_height)) ||
244                     ((image_info->render_flags & RENDER_FLAG_AUTO_FIT) &&
245                             ((next_width > image_info->printable_width) ||
246                                     (next_height > image_info->printable_height)))) {
247                 image_info->scaled_sample_size <<= 1;
248                 next_width >>= 1;
249                 next_height >>= 1;
250             }
251         }
252 
253         LOGD("calculated sample size: %d", image_info->scaled_sample_size);
254 
255         // are we dong any subsampling?
256         if (image_info->scaled_sample_size > 1) {
257             // force the decoder to close and reopen with the new sample size setting
258             decode_ifc->cleanup(image_info);
259             decode_ifc->get_hdr(image_info);
260 
261             // update the output size
262             image_output_width /= image_info->scaled_sample_size;
263             image_output_height /= image_info->scaled_sample_size;
264         }
265 
266         /*
267          * have we reached our target size with subsampling?
268          * if so disable further scaling
269          */
270         // check if width matches and height meets criteria
271         if ((image_output_width == image_info->printable_width) &&
272                 (((image_info->render_flags & RENDER_FLAG_AUTO_SCALE) &&
273                         (image_output_height >= image_info->printable_height)) ||
274                         ((image_info->render_flags & RENDER_FLAG_AUTO_FIT) &&
275                                 (image_output_height < image_info->printable_height)))) {
276             LOGD("disabling fine scaling since width matches and height meets criteria");
277             image_info->render_flags &= ~(RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT);
278         } else if ((image_output_height == image_info->printable_height) &&
279                 (((image_info->render_flags & RENDER_FLAG_AUTO_SCALE) &&
280                         (image_output_width >= image_info->printable_width)) ||
281                         ((image_info->render_flags & RENDER_FLAG_AUTO_FIT) &&
282                                 (image_output_width < image_info->printable_width)))) {
283             // height matches and width meets criteria
284             LOGD("disabling fine scaling since height matches and width meets criteria");
285             image_info->render_flags &= ~(RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT);
286         }
287 
288         if ((image_info->render_flags & RENDER_FLAG_DOCUMENT_SCALING)
289                 && (image_output_height <= image_info->printable_height)
290                 && (image_output_width <= image_info->printable_width)) {
291             image_info->render_flags &= ~(RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT);
292         }
293     } else if ((native_scaling != 1.0f) &&
294             (image_info->render_flags & RENDER_FLAG_DOCUMENT_SCALING)) {
295         LOGD("checking native document scaling factor");
296         if ((native_image_output_height <= image_info->printable_height)
297                 && (native_image_output_width <= image_info->printable_width)) {
298             LOGD("fit in printable area, just scale to native units");
299             image_info->render_flags &= ~(RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT);
300         } else {
301             LOGD("we don't fit in printable area, continue with fit-to-page");
302             native_scaling = 1.0f;
303         }
304     }
305 
306     // store the subsampled dimensions
307     image_info->sampled_width = (image_info->width / image_info->scaled_sample_size);
308     image_info->sampled_height = (image_info->height / image_info->scaled_sample_size);
309 
310     // do we have any additional scaling to do?
311     if ((image_info->render_flags & (RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT))
312             || (native_scaling != 1.0f)) {
313         LOGD("calculating fine-scaling");
314         int i;
315         float targetHeight, targetWidth;
316         float sourceHeight, sourceWidth;
317         float rw;
318         int useHeight;
319 
320         sourceWidth = image_output_width * 1.0f;
321         sourceHeight = image_output_height * 1.0f;
322 
323         if (image_info->render_flags & (RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT)) {
324             targetHeight = image_info->printable_height * 1.0f;
325             targetWidth = image_info->printable_width * 1.0f;
326 
327             // determine what our bounding edge is
328             rw = (targetHeight * sourceWidth) / sourceHeight;
329             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
330                 useHeight = (rw >= targetWidth);
331             } else {
332                 useHeight = (rw < targetWidth);
333             }
334 
335             // determine the scaling factor
336             if (useHeight) {
337                 image_info->scaled_width = (int) floorf(rw);
338                 image_info->scaled_height = (int) floorf(targetHeight);
339             } else {
340                 image_info->scaled_height = (int) floorf(targetWidth * sourceHeight / sourceWidth);
341                 image_info->scaled_width = (int) floorf(targetWidth);
342             }
343         } else {
344             image_info->scaled_height = native_image_output_height;
345             image_info->scaled_width = native_image_output_width;
346         }
347         image_info->scaling_needed = TRUE;
348 
349         /*
350          * setup the fine-scaler
351          * we use rotated image_output_width rather than the pre-rotated sampled_width
352          */
353         scaler_make_image_scaler_tables(image_output_width, BYTES_PER_PIXEL(image_output_width),
354                 image_info->scaled_width, BYTES_PER_PIXEL(image_info->scaled_width),
355                 image_output_height, image_info->scaled_height, &image_info->scaler_config);
356 
357         image_info->unscaled_rows_needed = 0;
358         image_info->mixed_memory_needed = 0;
359 
360         // calculate memory requirements
361         for (i = 0; i < image_info->printable_height; i += max_decode_stripe) {
362             uint16 row;
363             uint16 row_start, row_end, gen_rows, row_offset;
364             uint32 mixed;
365             row = i;
366             if (row >= image_info->scaled_height) {
367                 break;
368             }
369             scaler_calculate_scaling_rows(row,
370                     MIN((row + (max_decode_stripe - 1)),
371                             (image_info->scaled_height - 1)),
372                     (void *) &image_info->scaler_config,
373                     &row_start, &row_end, &gen_rows,
374                     &row_offset, &mixed);
375 
376             image_info->output_rows = MAX(image_info->output_rows, gen_rows);
377             image_info->unscaled_rows_needed = MAX(image_info->unscaled_rows_needed,
378                     ((row_end - row_start) + 3));
379             image_info->mixed_memory_needed = MAX(image_info->mixed_memory_needed, mixed);
380         }
381         int unscaled_size = BYTES_PER_PIXEL(
382                 (MAX(image_output_width, image_output_height) * image_info->unscaled_rows_needed));
383 
384         // allocate memory required for scaling
385         image_info->unscaled_rows = malloc(unscaled_size);
386 
387         if (image_info->unscaled_rows != NULL) {
388             memset(image_info->unscaled_rows, 0xff, unscaled_size);
389         }
390         image_info->mixed_memory = (image_info->mixed_memory_needed != 0) ? malloc(
391                 image_info->mixed_memory_needed) : NULL;
392     } else {
393         image_info->scaled_height = image_output_height;
394         image_info->scaled_width = image_output_width;
395     }
396 
397     // final calculations
398     if ((image_info->render_flags & (RENDER_FLAG_AUTO_SCALE | RENDER_FLAG_AUTO_FIT)) ||
399             image_info->scaling_needed) {
400         /* use the full image size since both of the dimensions could be greater than
401          * the printable area */
402         image_info->output_width = image_output_width;
403         image_info->output_height = image_output_height;
404     } else {
405         // clip the image within the printable area
406         image_info->output_width = MIN(image_info->printable_width, image_output_width);
407         image_info->output_height = MIN(image_info->printable_height, image_output_height);
408     }
409 
410     int delta;
411     switch (image_info->rotation) {
412         case ROT_90:
413             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
414             } else if (image_info->render_flags & RENDER_FLAG_CENTER_HORIZONTAL) {
415                 if (image_info->scaled_width > image_info->printable_width) {
416                     image_info->col_offset = (
417                             (image_info->scaled_width - image_info->printable_width) / 2);
418                 } else {
419                     int paddingDelta = (image_info->printable_width - image_info->scaled_width);
420                     delta = paddingDelta / 2;
421                     image_info->output_padding_left += delta;
422                     image_info->output_padding_right += delta + (paddingDelta & 0x1);
423                 }
424             } else if (image_info->scaled_width > image_info->printable_width) {
425                 image_info->col_offset = (image_info->scaled_width - image_info->printable_width);
426             } else if (image_info->scaled_width < image_info->printable_width) {
427                 image_info->output_padding_right += (image_info->printable_width -
428                         image_info->scaled_width);
429             }
430 
431             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
432             } else if (image_info->render_flags & RENDER_FLAG_CENTER_VERTICAL) {
433                 if (image_info->scaled_height > image_info->printable_height) {
434                     image_info->row_offset = (
435                             (image_info->scaled_height - image_info->printable_height) / 2);
436                 } else {
437                     int paddingDelta = (image_info->printable_height - image_info->scaled_height);
438                     delta = paddingDelta / 2;
439                     image_info->output_padding_top += delta;
440                     image_info->output_padding_bottom += delta + (paddingDelta & 0x1);
441                 }
442             } else if (image_info->scaled_height < image_info->printable_height) {
443                 image_info->output_padding_bottom += (image_info->printable_height -
444                         image_info->scaled_height);
445             }
446             break;
447         case ROT_180:
448             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
449             } else if (image_info->render_flags & RENDER_FLAG_CENTER_HORIZONTAL) {
450                 if (image_info->scaled_width > image_info->printable_width) {
451                     image_info->col_offset = (
452                             (image_info->scaled_width - image_info->printable_width) / 2);
453                 } else {
454                     int paddingDelta = (image_info->printable_width - image_info->scaled_width);
455                     delta = paddingDelta / 2;
456                     image_info->output_padding_left += delta;
457                     image_info->output_padding_right += delta + (paddingDelta & 0x1);
458                 }
459             } else if (image_info->scaled_width > image_info->printable_width) {
460                 image_info->col_offset = (image_info->scaled_width - image_info->printable_width);
461             } else if (image_info->scaled_width < image_info->printable_width) {
462                 image_info->output_padding_left += (image_info->printable_width -
463                         image_info->scaled_width);
464             }
465 
466             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
467             } else if (image_info->render_flags & RENDER_FLAG_CENTER_VERTICAL) {
468                 if (image_info->scaled_height > image_info->printable_height) {
469                     image_info->row_offset = (
470                             (image_info->scaled_height - image_info->printable_height) / 2);
471                 } else {
472                     int paddingDelta = (image_info->printable_height - image_info->scaled_height);
473                     delta = paddingDelta / 2;
474                     image_info->output_padding_top += delta;
475                     image_info->output_padding_bottom += delta + (paddingDelta & 0x1);
476                 }
477             } else if (image_info->scaled_height > image_info->printable_height) {
478                 image_info->row_offset = (image_info->scaled_height - image_info->printable_height);
479             } else if (image_info->scaled_height < image_info->printable_height) {
480                 image_info->output_padding_top += (image_info->printable_height -
481                         image_info->scaled_height);
482             }
483             break;
484         case ROT_270:
485             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
486             } else if (image_info->render_flags & RENDER_FLAG_CENTER_HORIZONTAL) {
487                 if (image_info->scaled_width > image_info->printable_width) {
488                     image_info->col_offset = (
489                             (image_info->scaled_width - image_info->printable_width) / 2);
490                 } else {
491                     int paddingDelta = (image_info->printable_width - image_info->scaled_width);
492                     delta = paddingDelta / 2;
493                     image_info->output_padding_left += delta;
494                     image_info->output_padding_right += delta + (paddingDelta & 0x1);
495                 }
496             } else if (image_info->scaled_width > image_info->printable_width) {
497                 image_info->col_offset = (image_info->scaled_width - image_info->printable_width);
498             } else if (image_info->scaled_width < image_info->printable_width) {
499                 image_info->output_padding_left += (image_info->printable_width -
500                         image_info->scaled_width);
501             }
502 
503             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
504             } else if (image_info->render_flags & RENDER_FLAG_CENTER_VERTICAL) {
505                 if (image_info->scaled_height > image_info->printable_height) {
506                     image_info->row_offset = (
507                             (image_info->scaled_height - image_info->printable_height) / 2);
508                 } else {
509                     int paddingDelta = (image_info->printable_height - image_info->scaled_height);
510                     delta = paddingDelta / 2;
511                     image_info->output_padding_top += delta;
512                     image_info->output_padding_bottom += delta + (paddingDelta & 0x1);
513                 }
514             } else if (image_info->scaled_height < image_info->printable_height) {
515                 image_info->output_padding_top += (image_info->printable_height -
516                         image_info->scaled_height);
517             } else if (image_info->scaled_height > image_info->printable_height) {
518                 image_info->row_offset = (image_info->scaled_height - image_info->printable_height);
519             }
520             break;
521         case ROT_0:
522         default:
523             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
524             } else if (image_info->render_flags & RENDER_FLAG_CENTER_HORIZONTAL) {
525                 if (image_info->scaled_width > image_info->printable_width) {
526                     image_info->col_offset = (
527                             (image_info->scaled_width - image_info->printable_width) / 2);
528                 } else {
529                     int paddingDelta = (image_info->printable_width - image_info->scaled_width);
530                     delta = paddingDelta / 2;
531                     image_info->output_padding_left += delta;
532                     image_info->output_padding_right += delta + (paddingDelta & 0x1);
533                 }
534             } else if (image_info->scaled_width < image_info->printable_width) {
535                 image_info->output_padding_right += (image_info->printable_width -
536                         image_info->scaled_width);
537             }
538 
539             if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
540             } else if (image_info->render_flags & RENDER_FLAG_CENTER_VERTICAL) {
541                 if (image_info->scaled_height > image_info->printable_height) {
542                     image_info->row_offset = (
543                             (image_info->scaled_height - image_info->printable_height) / 2);
544                 } else {
545                     int paddingDelta = (image_info->printable_height - image_info->scaled_height);
546                     delta = paddingDelta / 2;
547                     image_info->output_padding_top += delta;
548                     image_info->output_padding_bottom += delta + (paddingDelta & 0x1);
549                 }
550             } else if (image_info->scaled_height < image_info->printable_height) {
551                 image_info->output_padding_bottom += (image_info->printable_height -
552                         image_info->scaled_height);
553             }
554             break;
555     }
556 
557     LOGD("wprint_image_set_output_properties(): input render flags - %d (0x%8.8x)",
558          input_render_flags, input_render_flags);
559     LOGD("wprint_image_set_output_properties(): printable area - %dx%d",
560          printable_width, printable_height);
561     LOGD("wprint_image_set_output_properties(): input margins: Top:%d Left:%d Right:%d Bottom:%d",
562          top_margin, left_margin, right_margin, bottom_margin);
563     LOGD("wprint_image_set_output_properties(): padding options: %d (0x%2.2x)",
564          image_info->padding_options, image_info->padding_options);
565     LOGD("wprint_image_set_output_properties(): concurrent stripes - %d",
566          image_info->concurrent_stripes);
567     LOGD("wprint_image_set_output_properties(): stripe height - %d", image_info->stripe_height);
568     LOGD("wprint_image_set_output_properties(): image dimensions: %dx%d",
569          image_info->width, image_info->height);
570     LOGD("wprint_image_set_output_properties(): image rotation: %d", image_info->rotation);
571     LOGD("wprint_image_set_output_properties(): final render flags - %d (0x%8.8x)",
572          image_info->render_flags, image_info->render_flags);
573     LOGD("wprint_image_set_output_properties(): printable area after margins - %dx%d",
574          image_info->printable_width, image_info->printable_height);
575     LOGD("wprint_image_set_output_properties(): output_padding: Top:%d Left:%d Right:%d Bottom:%d",
576          image_info->output_padding_top, image_info->output_padding_left,
577          image_info->output_padding_right, image_info->output_padding_bottom);
578     LOGD("wprint_image_set_output_properties(): output dimensions: %dx%d", image_info->output_width,
579          image_info->output_height);
580     LOGD("wprint_image_set_output_properties(): subsampled image dimensions - %dx%d",
581          image_info->sampled_width, image_info->sampled_height);
582     LOGD("wprint_image_set_output_properties(): scaled image dimensions - %dx%d",
583          image_info->scaled_width, image_info->scaled_height);
584     LOGD("wprint_image_set_output_properties(): image offsets - row: %d, col: %d",
585          image_info->row_offset, image_info->col_offset);
586     LOGD("wprint_image_set_output_properties(): margins - top: %d, left: %d, right: %d, bottom: %d",
587          image_info->output_padding_top, image_info->output_padding_left,
588          image_info->output_padding_right, image_info->output_padding_bottom);
589 
590     return OK;
591 }
592 
_get_width(wprint_image_info_t * image_info,unsigned int padding_options)593 static int _get_width(wprint_image_info_t *image_info, unsigned int padding_options) {
594     int width;
595     if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
596         width = image_info->printable_width;
597     } else if ((image_info->render_flags & RENDER_FLAG_AUTO_FIT) || image_info->scaling_needed) {
598         width = image_info->scaled_width;
599     } else {
600         width = image_info->output_width;
601     }
602     if (padding_options & PAD_LEFT) {
603         width += image_info->output_padding_left;
604     }
605     if (padding_options & PAD_RIGHT) {
606         width += image_info->output_padding_right;
607     }
608     return width;
609 }
610 
wprint_image_get_width(wprint_image_info_t * image_info)611 int wprint_image_get_width(wprint_image_info_t *image_info) {
612     int width = _get_width(image_info, image_info->padding_options);
613     LOGD("wprint_image_get_width(): %d", width);
614     return width;
615 }
616 
wprint_image_get_output_buff_size(wprint_image_info_t * image_info)617 int wprint_image_get_output_buff_size(wprint_image_info_t *image_info) {
618     int width = MAX(MAX(image_info->scaled_width, image_info->scaled_height),
619             _get_width(image_info, image_info->padding_options));
620     LOGD("wprint_image_get_output_buff_size(): %dx%d", width, image_info->output_rows);
621     return (BYTES_PER_PIXEL(width * image_info->output_rows));
622 }
623 
_get_height(wprint_image_info_t * image_info,unsigned int padding_options)624 static int _get_height(wprint_image_info_t *image_info, unsigned int padding_options) {
625     int height;
626     if (image_info->render_flags & RENDER_FLAG_AUTO_SCALE) {
627         height = image_info->printable_height;
628     } else {
629         height = MIN(image_info->scaled_height, image_info->printable_height);
630     }
631     if (padding_options & PAD_TOP) {
632         height += image_info->output_padding_top;
633     }
634     if (padding_options & PAD_BOTTOM) {
635         height += image_info->output_padding_bottom;
636     }
637     return height;
638 }
639 
wprint_image_get_height(wprint_image_info_t * image_info)640 int wprint_image_get_height(wprint_image_info_t *image_info) {
641     int height = _get_height(image_info, image_info->padding_options);
642     LOGD("wprint_image_get_height(): %d", height);
643     return height;
644 }
645 
wprint_image_is_landscape(wprint_image_info_t * image_info)646 bool wprint_image_is_landscape(wprint_image_info_t *image_info) {
647     return (image_info->width > image_info->height);
648 }
649 
_decode_stripe(wprint_image_info_t * image_info,int start_row,int num_rows,unsigned int padding_options,unsigned char * rgb_pixels)650 int _decode_stripe(wprint_image_info_t *image_info, int start_row, int num_rows,
651         unsigned int padding_options, unsigned char *rgb_pixels) {
652     int image_y, image_x;
653     unsigned char *image_data;
654     int nbytes = -1;
655     int rbytes;
656     int col_offset;
657     int old_num_rows;
658     const image_decode_ifc_t *decode_ifc = image_info->decode_ifc;
659     if ((decode_ifc == NULL) || (decode_ifc->decode_row == NULL)) {
660         return nbytes;
661     }
662 
663     nbytes = 0;
664     start_row += image_info->row_offset;
665     rbytes = BYTES_PER_PIXEL(image_info->output_width);
666 
667     // get padding values
668     int padding_left = ((padding_options & PAD_LEFT) ? BYTES_PER_PIXEL(
669             image_info->output_padding_left) : 0);
670     int padding_right = ((padding_options & PAD_RIGHT) ? BYTES_PER_PIXEL(
671             image_info->output_padding_right) : 0);
672 
673     old_num_rows = ~num_rows;
674     switch (image_info->rotation) {
675         case ROT_90:
676             col_offset = BYTES_PER_PIXEL(image_info->col_offset);
677             while (num_rows > 0) {
678                 if (start_row > image_info->sampled_width) {
679                     return nbytes;
680                 }
681                 if (old_num_rows == num_rows) {
682                     LOGE("Bad ROT_90 calculations. Exiting to prevent infinite loop");
683                     return ERROR;
684                 }
685                 old_num_rows = num_rows;
686                 if ((image_info->output_swath_start == -1) ||
687                         (start_row < image_info->output_swath_start) ||
688                         (start_row >= (image_info->output_swath_start + image_info->rows_cached))) {
689                     if (image_info->output_swath_start == -1) {
690                         if (decode_ifc->decode_row(image_info, 0) == NULL) {
691                             return ERROR;
692                         }
693                     }
694                     image_info->output_swath_start = ((start_row / image_info->rows_cached) *
695                             image_info->rows_cached);
696                     for (image_y = 0; image_y < image_info->sampled_height; image_y++) {
697                         image_data = decode_ifc->decode_row(image_info, image_y);
698                         if (image_data == NULL) {
699                             return ERROR;
700                         }
701                         for (image_x = 0; (image_x < image_info->rows_cached &&
702                                 ((image_x + image_info->output_swath_start) <
703                                         image_info->sampled_width));
704                                 image_x++) {
705                             memcpy(image_info->output_cache[image_x] + BYTES_PER_PIXEL(
706                                             (image_info->sampled_height - image_y - 1)),
707                                     image_data + BYTES_PER_PIXEL(
708                                             (image_info->output_swath_start + image_x)),
709                                     BYTES_PER_PIXEL(1));
710                         }
711                     }
712                 }
713 
714                 for (image_y = start_row; ((num_rows != 0) &&
715                         (image_y < image_info->sampled_width) &&
716                         (image_y < (image_info->output_swath_start + image_info->rows_cached)));
717                         image_y++, num_rows--, start_row++) {
718                     memcpy(rgb_pixels + padding_left,
719                             image_info->output_cache[image_y - image_info->output_swath_start] +
720                                     col_offset, rbytes);
721                     nbytes += rbytes + padding_left + padding_right;
722                     rgb_pixels += rbytes + padding_left + padding_right;
723                 }
724             }
725             break;
726         case ROT_180:
727             col_offset = image_info->col_offset;
728             for (image_y = start_row;
729                     ((image_y < image_info->sampled_height) && (num_rows != 0));
730                     image_y++, num_rows--) {
731                 image_data = decode_ifc->decode_row(image_info,
732                         (image_info->sampled_height - image_y - 1));
733                 if (image_data == NULL) {
734                     return ERROR;
735                 }
736                 for (image_x = 0; image_x < image_info->output_width; image_x++) {
737                     memcpy(rgb_pixels + padding_left + BYTES_PER_PIXEL(image_x),
738                             image_data + BYTES_PER_PIXEL(image_info->sampled_width -
739                                     image_x - col_offset - 1),
740                             BYTES_PER_PIXEL(1));
741                 }
742                 nbytes += rbytes + padding_left + padding_right;
743                 rgb_pixels += rbytes + padding_left + padding_right;
744             }
745             break;
746         case ROT_270:
747             col_offset = BYTES_PER_PIXEL(image_info->col_offset);
748             while (num_rows > 0) {
749                 if (start_row > image_info->sampled_width) {
750                     return nbytes;
751                 }
752                 if (old_num_rows == num_rows) {
753                     LOGE("Bad ROT_270 calculations. Erroring out to prevent infinite loop.");
754                     return ERROR;
755                 }
756                 old_num_rows = num_rows;
757                 if ((image_info->output_swath_start == -1) ||
758                         (start_row < image_info->output_swath_start) ||
759                         (start_row >= (image_info->output_swath_start + image_info->rows_cached))) {
760                     if (image_info->output_swath_start == -1) {
761                         if (decode_ifc->decode_row(image_info, 0) == NULL) {
762                             return ERROR;
763                         }
764                     }
765                     image_info->output_swath_start = ((start_row / image_info->rows_cached) *
766                             image_info->rows_cached);
767                     for (image_y = 0; image_y < image_info->sampled_height; image_y++) {
768                         image_data = decode_ifc->decode_row(image_info, image_y);
769                         if (image_data == NULL) {
770                             return ERROR;
771                         }
772                         for (image_x = 0; ((image_x < image_info->rows_cached) &&
773                                 ((image_x + image_info->output_swath_start) <
774                                         image_info->sampled_width));
775                                 image_x++) {
776                             memcpy(image_info->output_cache[image_x] + BYTES_PER_PIXEL(image_y),
777                                     image_data + BYTES_PER_PIXEL(image_info->sampled_width -
778                                             (image_info->output_swath_start +
779                                                     image_x) - 1),
780                                     BYTES_PER_PIXEL(1));
781                         }
782                     }
783                 }
784                 for (image_y = start_row;
785                         ((num_rows != 0) &&
786                                 (image_y < image_info->sampled_width) &&
787                                 (image_y < (image_info->output_swath_start
788                                         + image_info->rows_cached)));
789                         image_y++, num_rows--, start_row++) {
790                     memcpy(rgb_pixels + padding_left,
791                             image_info->output_cache[image_y - image_info->output_swath_start] +
792                                     col_offset, rbytes);
793                     nbytes += rbytes + padding_left + padding_right;
794                     rgb_pixels += rbytes + padding_left + padding_right;
795                 }
796             }
797             break;
798         case ROT_0:
799         default:
800             col_offset = BYTES_PER_PIXEL(image_info->col_offset);
801             for (image_y = start_row;
802                     ((image_y < image_info->sampled_height) && (num_rows != 0));
803                     image_y++, num_rows--) {
804                 image_data = decode_ifc->decode_row(image_info, image_y);
805                 if (image_data == NULL) {
806                     LOGE("ERROR: received no data for row: %d", image_y);
807                     return ERROR;
808                 }
809                 memcpy(rgb_pixels + padding_left, image_data + col_offset, rbytes);
810                 nbytes += rbytes + padding_left + padding_right;
811                 rgb_pixels += rbytes + padding_left + padding_right;
812             }
813             break;
814     }
815     return nbytes;
816 }
817 
wprint_image_decode_stripe(wprint_image_info_t * image_info,int start_row,int * height,unsigned char * rgb_pixels)818 int wprint_image_decode_stripe(wprint_image_info_t *image_info, int start_row, int *height,
819         unsigned char *rgb_pixels) {
820     int nbytes = 0;
821     int bytes_per_row = BYTES_PER_PIXEL(_get_width(image_info, image_info->padding_options));
822 
823     if (height == NULL) {
824         return -1;
825     }
826 
827     int num_rows = *height;
828 
829     *height = 0;
830 
831     // get padding values
832     int padding_left = ((image_info->padding_options & PAD_LEFT) ? BYTES_PER_PIXEL(
833             image_info->output_padding_left) : 0);
834     int padding_right = ((image_info->padding_options & PAD_RIGHT) ? BYTES_PER_PIXEL(
835             image_info->output_padding_right) : 0);
836     int padding_top = ((image_info->padding_options & PAD_TOP) ?
837             image_info->output_padding_top : 0);
838     // handle invalid requests
839     if ((start_row < 0) || (start_row >= _get_height(image_info, image_info->padding_options))) {
840         *height = 0;
841         return ERROR;
842     } else if ((image_info->padding_options & PAD_TOP) &&
843             (start_row < padding_top)) {
844         int blank_rows = MIN(num_rows, (padding_top - start_row));
845         int bytesToBlank = (blank_rows * bytes_per_row);
846         nbytes += bytesToBlank;
847         num_rows -= blank_rows;
848         *height += blank_rows;
849         memset(rgb_pixels, 0xff, bytesToBlank);
850         rgb_pixels += bytesToBlank;
851         start_row += blank_rows;
852     } else if ((image_info->padding_options & PAD_BOTTOM) &&
853             (start_row >= _get_height(image_info, image_info->padding_options & PAD_TOP))) {
854         // handle image padding on bottom
855         int blank_rows = MIN(num_rows,
856                 _get_height(image_info, image_info->padding_options) - start_row);
857         int bytesToBlank = (blank_rows * bytes_per_row);
858         nbytes += bytesToBlank;
859         num_rows -= blank_rows;
860         *height += blank_rows;
861         memset(rgb_pixels, 0xff, bytesToBlank);
862         rgb_pixels += bytesToBlank;
863         start_row += blank_rows;
864     }
865 
866     if (num_rows <= 0) {
867         return nbytes;
868     }
869 
870     unsigned char *pad_rgb_pixels = rgb_pixels;
871     int unpadded_start_row = start_row;
872     // adjust start row to fit within image bounds
873     if (image_info->padding_options & PAD_TOP) {
874         unpadded_start_row -= padding_top;
875     }
876 
877     // check if we need to scaling
878     if (image_info->scaling_needed) {
879         // scaling required
880         uint32 scaled_start_row = unpadded_start_row;
881         if (image_info->scaled_height > image_info->printable_height) {
882             scaled_start_row += ((image_info->scaled_height - image_info->printable_height) / 2);
883         }
884         uint32 stripe_height, mixed;
885         uint16 unscaled_row_start, unscaled_row_end;
886         uint16 generated_rows, row_offset;
887         uint32 predecoded_rows;
888 
889         int scaled_num_rows = (((scaled_start_row + num_rows) > image_info->scaled_height) ?
890                 (image_info->scaled_height - scaled_start_row) : num_rows);
891         while (scaled_num_rows > 0) {
892             stripe_height = MIN(scaled_num_rows, image_info->stripe_height);
893             scaler_calculate_scaling_rows(scaled_start_row,
894                     MIN((scaled_start_row + stripe_height - 1),
895                             (image_info->scaled_height - 1)), (void *) &image_info->scaler_config,
896                     &unscaled_row_start, &unscaled_row_end, &generated_rows, &row_offset, &mixed);
897 
898             if (mixed > image_info->mixed_memory_needed) {
899                 LOGE("need more memory");
900                 return -1;
901             }
902 
903             predecoded_rows = 0;
904             if (unscaled_row_start <= image_info->unscaled_end_row) {
905                 // shift over any rows we need that were decoded in the previous pass
906                 predecoded_rows = (image_info->unscaled_end_row - unscaled_row_start) + 1;
907 
908                 memmove(image_info->unscaled_rows, image_info->unscaled_rows +
909                         BYTES_PER_PIXEL(((unscaled_row_start - image_info->unscaled_start_row) *
910                                 image_info->output_width)),
911                         BYTES_PER_PIXEL((predecoded_rows * image_info->output_width)));
912             }
913 
914             image_info->unscaled_start_row = unscaled_row_start;
915             image_info->unscaled_end_row = unscaled_row_end;
916 
917             /*
918              * decode the remaining rows we need
919              * don't pad the output since we need to move the data after scaling anyways
920              */
921             int rowsLeftToDecode = ((image_info->unscaled_end_row -
922                     (image_info->unscaled_start_row + predecoded_rows)) + 1);
923             if (rowsLeftToDecode > 0) {
924                 int dbytes = _decode_stripe(image_info,
925                         image_info->unscaled_start_row + predecoded_rows, rowsLeftToDecode,
926                         PAD_NONE, (image_info->unscaled_rows + BYTES_PER_PIXEL(predecoded_rows *
927                                 image_info->output_width)));
928                 if (dbytes <= 0) {
929                     if (dbytes < 0) {
930                         LOGE("couldn't decode rows");
931                     }
932                     return dbytes;
933                 }
934             } else if (predecoded_rows <= 0) {
935                 return 0;
936             }
937 
938             // scale the data to it's final size
939             scaler_scale_image_data(image_info->unscaled_rows, (void *) &image_info->scaler_config,
940                     rgb_pixels, image_info->mixed_memory);
941             // do we have to move the data around??
942             if ((row_offset != 0) ||
943                     (image_info->scaled_width > image_info->printable_width) ||
944                     (padding_left > 0) ||
945                     (padding_right > 0)) {
946                 int delta = 0;
947                 int pixelsToMove = BYTES_PER_PIXEL(MIN(image_info->scaled_width,
948                         image_info->printable_width));
949 
950                 int memMoveRow = ((bytes_per_row < image_info->scaler_config.iOutBufWidth) ? 0 : (
951                         stripe_height - 1));
952                 int memMoveIncrement = ((bytes_per_row < image_info->scaler_config.iOutBufWidth)
953                         ? 1 : -1);
954 
955                 // if scaled width is greater than the printable area drop pixels on either size
956                 if (image_info->scaled_width > image_info->printable_width) {
957                     delta = BYTES_PER_PIXEL(
958                             ((image_info->scaled_width - image_info->printable_width) / 2));
959                 }
960 
961                 // move the data into the correct location in the output buffer
962                 for (generated_rows = 0; generated_rows < stripe_height; generated_rows++,
963                         memMoveRow += memMoveIncrement) {
964                     memmove(rgb_pixels + (memMoveRow * bytes_per_row) + padding_left,
965                             rgb_pixels + ((memMoveRow + row_offset) *
966                                     image_info->scaler_config.iOutBufWidth) + delta, pixelsToMove);
967                 }
968             }
969 
970             num_rows -= stripe_height;
971             scaled_num_rows -= stripe_height;
972             scaled_start_row += stripe_height;
973             nbytes += (bytes_per_row * stripe_height);
974             rgb_pixels += (bytes_per_row * stripe_height);
975             *height += stripe_height;
976             start_row += stripe_height;
977         }
978     } else {
979         // no scaling needed
980 
981         // decode the request
982         int dbytes = _decode_stripe(image_info, unpadded_start_row,
983                 (((unpadded_start_row + num_rows) >
984                         _get_height(image_info, PAD_NONE)) ?
985                         (_get_height(image_info, PAD_NONE) - unpadded_start_row)
986                         : num_rows),
987                 image_info->padding_options, rgb_pixels);
988         if (dbytes <= 0) {
989             if (dbytes < 0) {
990                 LOGE("couldn't decode rows");
991             }
992             return dbytes;
993         }
994 
995         int rows = (dbytes / bytes_per_row);
996         *height += rows;
997         num_rows -= rows;
998         start_row += rows;
999         unpadded_start_row += rows;
1000         rgb_pixels += dbytes;
1001         nbytes += dbytes;
1002     }
1003 
1004     // white pad the left and right edges
1005     if ((pad_rgb_pixels != rgb_pixels) &&
1006             (image_info->padding_options & (PAD_LEFT | PAD_RIGHT)) &&
1007             ((padding_left != 0) || (padding_right != 0))) {
1008         while (pad_rgb_pixels != rgb_pixels) {
1009             if (padding_left != 0) {
1010                 memset(pad_rgb_pixels, 0xff, padding_left);
1011             }
1012             if (padding_right != 0) {
1013                 memset(pad_rgb_pixels + (bytes_per_row - padding_right), 0xff, padding_right);
1014             }
1015             pad_rgb_pixels += bytes_per_row;
1016         }
1017     }
1018 
1019     if ((image_info->padding_options & PAD_BOTTOM) && (num_rows > 0) &&
1020             (start_row >= _get_height(image_info, image_info->padding_options & PAD_TOP))) {
1021         int blank_rows = MIN(num_rows,
1022                 _get_height(image_info, image_info->padding_options) - start_row);
1023         int bytesToBlank = (blank_rows * bytes_per_row);
1024         nbytes += bytesToBlank;
1025         num_rows -= blank_rows;
1026         *height += blank_rows;
1027         memset(rgb_pixels, 0xff, bytesToBlank);
1028         rgb_pixels += bytesToBlank;
1029     }
1030 
1031     return nbytes;
1032 }
1033 
wprint_image_compute_rows_to_cache(wprint_image_info_t * image_info)1034 int wprint_image_compute_rows_to_cache(wprint_image_info_t *image_info) {
1035     int i;
1036     int row_width, max_rows;
1037     unsigned char output_mem;
1038     int available_mem = MAX_DECODE_MEM;
1039     int width, height;
1040 
1041     width = image_info->sampled_width;
1042     height = image_info->sampled_height;
1043 
1044     switch (image_info->rotation) {
1045         case ROT_90:
1046         case ROT_270:
1047             output_mem = 1;
1048             row_width = height;
1049             break;
1050         case ROT_0:
1051         case ROT_180:
1052         default:
1053             output_mem = 0;
1054             row_width = width;
1055             break;
1056     }
1057 
1058     available_mem -= (wprint_image_get_output_buff_size(image_info) *
1059             image_info->concurrent_stripes);
1060     if (image_info->unscaled_rows != NULL) {
1061         // remove any memory allocated for scaling from our pool
1062         available_mem -= BYTES_PER_PIXEL(
1063                 image_info->unscaled_rows_needed * image_info->output_width);
1064         available_mem -= image_info->mixed_memory_needed;
1065     }
1066 
1067     // make sure we have a valid amount of memory to work with
1068     available_mem = MAX(available_mem, MIN_DECODE_MEM);
1069 
1070     LOGD("wprint_image_compute_rows_to_cache(): %d bytes available for row caching", available_mem);
1071 
1072     row_width = BYTES_PER_PIXEL(row_width);
1073     max_rows = (available_mem / row_width);
1074 
1075     if (max_rows > 0xf) {
1076         max_rows &= ~0xf;
1077     }
1078 
1079     LOGD("wprint_image_compute_rows_to_cache(): based on row width %d (%d), %d rows can be cached",
1080             row_width, output_mem, max_rows);
1081 
1082     if (output_mem) {
1083         if (max_rows > (MAX(width, height))) {
1084             max_rows = MAX(width, height);
1085         }
1086 
1087         image_info->output_cache = (unsigned char **) malloc(sizeof(unsigned char *) * max_rows);
1088         for (i = 0; i < max_rows; i++) {
1089             image_info->output_cache[i] = (unsigned char *) malloc(row_width);
1090         }
1091     } else {
1092         max_rows = MIN(max_rows, height);
1093     }
1094 
1095     image_info->rows_cached = max_rows;
1096     LOGD("wprint_image_compute_rows_to_cache(): %d rows being cached", max_rows);
1097 
1098     return wprint_image_input_rows_cached(image_info);
1099 }
1100 
wprint_image_input_rows_cached(wprint_image_info_t * image_info)1101 int wprint_image_input_rows_cached(wprint_image_info_t *image_info) {
1102     return ((image_info->output_cache != NULL) ? 1 : image_info->rows_cached);
1103 }
1104 
wprint_image_cleanup(wprint_image_info_t * image_info)1105 void wprint_image_cleanup(wprint_image_info_t *image_info) {
1106     int i;
1107     const image_decode_ifc_t *decode_ifc = image_info->decode_ifc;
1108 
1109     if ((decode_ifc != NULL) && (decode_ifc->cleanup != NULL)) {
1110         decode_ifc->cleanup(image_info);
1111     }
1112 
1113     // free memory allocated for saving unscaled rows
1114     if (image_info->unscaled_rows != NULL) {
1115         free(image_info->unscaled_rows);
1116         image_info->unscaled_rows = NULL;
1117     }
1118 
1119     // free memory allocated needed for mixed scaling
1120     if (image_info->mixed_memory != NULL) {
1121         free(image_info->mixed_memory);
1122         image_info->mixed_memory = NULL;
1123     }
1124 
1125     if (image_info->output_cache != NULL) {
1126         for (i = 0; i < image_info->rows_cached; i++) {
1127             free(image_info->output_cache[i]);
1128         }
1129         free(image_info->output_cache);
1130         image_info->output_cache = NULL;
1131     }
1132 }
1133