1 /*
2  * jdpostct.c
3  *
4  * This file was part of the Independent JPEG Group's software:
5  * Copyright (C) 1994-1996, Thomas G. Lane.
6  * It was modified by The libjpeg-turbo Project to include only code relevant
7  * to libjpeg-turbo.
8  * For conditions of distribution and use, see the accompanying README file.
9  *
10  * This file contains the decompression postprocessing controller.
11  * This controller manages the upsampling, color conversion, and color
12  * quantization/reduction steps; specifically, it controls the buffering
13  * between upsample/color conversion and color quantization/reduction.
14  *
15  * If no color quantization/reduction is required, then this module has no
16  * work to do, and it just hands off to the upsample/color conversion code.
17  * An integrated upsample/convert/quantize process would replace this module
18  * entirely.
19  */
20 
21 #define JPEG_INTERNALS
22 #include "jinclude.h"
23 #include "jpeglib.h"
24 
25 
26 /* Private buffer controller object */
27 
28 typedef struct {
29   struct jpeg_d_post_controller pub; /* public fields */
30 
31   /* Color quantization source buffer: this holds output data from
32    * the upsample/color conversion step to be passed to the quantizer.
33    * For two-pass color quantization, we need a full-image buffer;
34    * for one-pass operation, a strip buffer is sufficient.
35    */
36   jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */
37   JSAMPARRAY buffer;            /* strip buffer, or current strip of virtual */
38   JDIMENSION strip_height;      /* buffer size in rows */
39   /* for two-pass mode only: */
40   JDIMENSION starting_row;      /* row # of first row in current strip */
41   JDIMENSION next_row;          /* index of next row to fill/empty in strip */
42 } my_post_controller;
43 
44 typedef my_post_controller * my_post_ptr;
45 
46 
47 /* Forward declarations */
48 METHODDEF(void) post_process_1pass
49         (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
50          JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail,
51          JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
52          JDIMENSION out_rows_avail);
53 #ifdef QUANT_2PASS_SUPPORTED
54 METHODDEF(void) post_process_prepass
55         (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
56          JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail,
57          JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
58          JDIMENSION out_rows_avail);
59 METHODDEF(void) post_process_2pass
60         (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
61          JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail,
62          JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
63          JDIMENSION out_rows_avail);
64 #endif
65 
66 
67 /*
68  * Initialize for a processing pass.
69  */
70 
71 METHODDEF(void)
start_pass_dpost(j_decompress_ptr cinfo,J_BUF_MODE pass_mode)72 start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)
73 {
74   my_post_ptr post = (my_post_ptr) cinfo->post;
75 
76   switch (pass_mode) {
77   case JBUF_PASS_THRU:
78     if (cinfo->quantize_colors) {
79       /* Single-pass processing with color quantization. */
80       post->pub.post_process_data = post_process_1pass;
81       /* We could be doing buffered-image output before starting a 2-pass
82        * color quantization; in that case, jinit_d_post_controller did not
83        * allocate a strip buffer.  Use the virtual-array buffer as workspace.
84        */
85       if (post->buffer == NULL) {
86         post->buffer = (*cinfo->mem->access_virt_sarray)
87           ((j_common_ptr) cinfo, post->whole_image,
88            (JDIMENSION) 0, post->strip_height, TRUE);
89       }
90     } else {
91       /* For single-pass processing without color quantization,
92        * I have no work to do; just call the upsampler directly.
93        */
94       post->pub.post_process_data = cinfo->upsample->upsample;
95     }
96     break;
97 #ifdef QUANT_2PASS_SUPPORTED
98   case JBUF_SAVE_AND_PASS:
99     /* First pass of 2-pass quantization */
100     if (post->whole_image == NULL)
101       ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
102     post->pub.post_process_data = post_process_prepass;
103     break;
104   case JBUF_CRANK_DEST:
105     /* Second pass of 2-pass quantization */
106     if (post->whole_image == NULL)
107       ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
108     post->pub.post_process_data = post_process_2pass;
109     break;
110 #endif /* QUANT_2PASS_SUPPORTED */
111   default:
112     ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
113     break;
114   }
115   post->starting_row = post->next_row = 0;
116 }
117 
118 
119 /*
120  * Process some data in the one-pass (strip buffer) case.
121  * This is used for color precision reduction as well as one-pass quantization.
122  */
123 
124 METHODDEF(void)
post_process_1pass(j_decompress_ptr cinfo,JSAMPIMAGE input_buf,JDIMENSION * in_row_group_ctr,JDIMENSION in_row_groups_avail,JSAMPARRAY output_buf,JDIMENSION * out_row_ctr,JDIMENSION out_rows_avail)125 post_process_1pass (j_decompress_ptr cinfo,
126                     JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
127                     JDIMENSION in_row_groups_avail,
128                     JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
129                     JDIMENSION out_rows_avail)
130 {
131   my_post_ptr post = (my_post_ptr) cinfo->post;
132   JDIMENSION num_rows, max_rows;
133 
134   /* Fill the buffer, but not more than what we can dump out in one go. */
135   /* Note we rely on the upsampler to detect bottom of image. */
136   max_rows = out_rows_avail - *out_row_ctr;
137   if (max_rows > post->strip_height)
138     max_rows = post->strip_height;
139   num_rows = 0;
140   (*cinfo->upsample->upsample) (cinfo,
141                 input_buf, in_row_group_ctr, in_row_groups_avail,
142                 post->buffer, &num_rows, max_rows);
143   /* Quantize and emit data. */
144   (*cinfo->cquantize->color_quantize) (cinfo,
145                 post->buffer, output_buf + *out_row_ctr, (int) num_rows);
146   *out_row_ctr += num_rows;
147 }
148 
149 
150 #ifdef QUANT_2PASS_SUPPORTED
151 
152 /*
153  * Process some data in the first pass of 2-pass quantization.
154  */
155 
156 METHODDEF(void)
post_process_prepass(j_decompress_ptr cinfo,JSAMPIMAGE input_buf,JDIMENSION * in_row_group_ctr,JDIMENSION in_row_groups_avail,JSAMPARRAY output_buf,JDIMENSION * out_row_ctr,JDIMENSION out_rows_avail)157 post_process_prepass (j_decompress_ptr cinfo,
158                       JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
159                       JDIMENSION in_row_groups_avail,
160                       JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
161                       JDIMENSION out_rows_avail)
162 {
163   my_post_ptr post = (my_post_ptr) cinfo->post;
164   JDIMENSION old_next_row, num_rows;
165 
166   /* Reposition virtual buffer if at start of strip. */
167   if (post->next_row == 0) {
168     post->buffer = (*cinfo->mem->access_virt_sarray)
169         ((j_common_ptr) cinfo, post->whole_image,
170          post->starting_row, post->strip_height, TRUE);
171   }
172 
173   /* Upsample some data (up to a strip height's worth). */
174   old_next_row = post->next_row;
175   (*cinfo->upsample->upsample) (cinfo,
176                 input_buf, in_row_group_ctr, in_row_groups_avail,
177                 post->buffer, &post->next_row, post->strip_height);
178 
179   /* Allow quantizer to scan new data.  No data is emitted, */
180   /* but we advance out_row_ctr so outer loop can tell when we're done. */
181   if (post->next_row > old_next_row) {
182     num_rows = post->next_row - old_next_row;
183     (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row,
184                                          (JSAMPARRAY) NULL, (int) num_rows);
185     *out_row_ctr += num_rows;
186   }
187 
188   /* Advance if we filled the strip. */
189   if (post->next_row >= post->strip_height) {
190     post->starting_row += post->strip_height;
191     post->next_row = 0;
192   }
193 }
194 
195 
196 /*
197  * Process some data in the second pass of 2-pass quantization.
198  */
199 
200 METHODDEF(void)
post_process_2pass(j_decompress_ptr cinfo,JSAMPIMAGE input_buf,JDIMENSION * in_row_group_ctr,JDIMENSION in_row_groups_avail,JSAMPARRAY output_buf,JDIMENSION * out_row_ctr,JDIMENSION out_rows_avail)201 post_process_2pass (j_decompress_ptr cinfo,
202                     JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr,
203                     JDIMENSION in_row_groups_avail,
204                     JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
205                     JDIMENSION out_rows_avail)
206 {
207   my_post_ptr post = (my_post_ptr) cinfo->post;
208   JDIMENSION num_rows, max_rows;
209 
210   /* Reposition virtual buffer if at start of strip. */
211   if (post->next_row == 0) {
212     post->buffer = (*cinfo->mem->access_virt_sarray)
213         ((j_common_ptr) cinfo, post->whole_image,
214          post->starting_row, post->strip_height, FALSE);
215   }
216 
217   /* Determine number of rows to emit. */
218   num_rows = post->strip_height - post->next_row; /* available in strip */
219   max_rows = out_rows_avail - *out_row_ctr; /* available in output area */
220   if (num_rows > max_rows)
221     num_rows = max_rows;
222   /* We have to check bottom of image here, can't depend on upsampler. */
223   max_rows = cinfo->output_height - post->starting_row;
224   if (num_rows > max_rows)
225     num_rows = max_rows;
226 
227   /* Quantize and emit data. */
228   (*cinfo->cquantize->color_quantize) (cinfo,
229                 post->buffer + post->next_row, output_buf + *out_row_ctr,
230                 (int) num_rows);
231   *out_row_ctr += num_rows;
232 
233   /* Advance if we filled the strip. */
234   post->next_row += num_rows;
235   if (post->next_row >= post->strip_height) {
236     post->starting_row += post->strip_height;
237     post->next_row = 0;
238   }
239 }
240 
241 #endif /* QUANT_2PASS_SUPPORTED */
242 
243 
244 /*
245  * Initialize postprocessing controller.
246  */
247 
248 GLOBAL(void)
jinit_d_post_controller(j_decompress_ptr cinfo,boolean need_full_buffer)249 jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer)
250 {
251   my_post_ptr post;
252 
253   post = (my_post_ptr)
254     (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
255                                 sizeof(my_post_controller));
256   cinfo->post = (struct jpeg_d_post_controller *) post;
257   post->pub.start_pass = start_pass_dpost;
258   post->whole_image = NULL;     /* flag for no virtual arrays */
259   post->buffer = NULL;          /* flag for no strip buffer */
260 
261   /* Create the quantization buffer, if needed */
262   if (cinfo->quantize_colors) {
263     /* The buffer strip height is max_v_samp_factor, which is typically
264      * an efficient number of rows for upsampling to return.
265      * (In the presence of output rescaling, we might want to be smarter?)
266      */
267     post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor;
268     if (need_full_buffer) {
269       /* Two-pass color quantization: need full-image storage. */
270       /* We round up the number of rows to a multiple of the strip height. */
271 #ifdef QUANT_2PASS_SUPPORTED
272       post->whole_image = (*cinfo->mem->request_virt_sarray)
273         ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
274          cinfo->output_width * cinfo->out_color_components,
275          (JDIMENSION) jround_up((long) cinfo->output_height,
276                                 (long) post->strip_height),
277          post->strip_height);
278 #else
279       ERREXIT(cinfo, JERR_BAD_BUFFER_MODE);
280 #endif /* QUANT_2PASS_SUPPORTED */
281     } else {
282       /* One-pass color quantization: just make a strip buffer. */
283       post->buffer = (*cinfo->mem->alloc_sarray)
284         ((j_common_ptr) cinfo, JPOOL_IMAGE,
285          cinfo->output_width * cinfo->out_color_components,
286          post->strip_height);
287     }
288   }
289 }
290