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