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