xref: /plan9/sys/src/cmd/gs/src/sdctd.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1994, 1995, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: sdctd.c,v 1.4 2002/02/21 22:24:53 giles Exp $ */
18 /* DCT decoding filter stream */
19 #include "memory_.h"
20 #include "stdio_.h"
21 #include "jpeglib_.h"
22 #include "jerror_.h"
23 #include "gdebug.h"
24 #include "gsmemory.h"		/* for gsmalloc.h */
25 #include "gsmalloc.h"		/* for gs_memory_default */
26 #include "strimpl.h"
27 #include "sdct.h"
28 #include "sjpeg.h"
29 
30 /* ------ DCTDecode ------ */
31 
32 /* JPEG source manager procedures */
33 private void
dctd_init_source(j_decompress_ptr dinfo)34 dctd_init_source(j_decompress_ptr dinfo)
35 {
36 }
37 static const JOCTET fake_eoi[2] =
38 {0xFF, JPEG_EOI};
39 private boolean
dctd_fill_input_buffer(j_decompress_ptr dinfo)40 dctd_fill_input_buffer(j_decompress_ptr dinfo)
41 {
42     jpeg_decompress_data *jddp =
43     (jpeg_decompress_data *) ((char *)dinfo -
44 			      offset_of(jpeg_decompress_data, dinfo));
45 
46     if (!jddp->input_eod)
47 	return FALSE;		/* normal case: suspend processing */
48     /* Reached end of source data without finding EOI */
49     WARNMS(dinfo, JWRN_JPEG_EOF);
50     /* Insert a fake EOI marker */
51     dinfo->src->next_input_byte = fake_eoi;
52     dinfo->src->bytes_in_buffer = 2;
53     jddp->faked_eoi = true;	/* so process routine doesn't use next_input_byte */
54     return TRUE;
55 }
56 private void
dctd_skip_input_data(j_decompress_ptr dinfo,long num_bytes)57 dctd_skip_input_data(j_decompress_ptr dinfo, long num_bytes)
58 {
59     struct jpeg_source_mgr *src = dinfo->src;
60     jpeg_decompress_data *jddp =
61     (jpeg_decompress_data *) ((char *)dinfo -
62 			      offset_of(jpeg_decompress_data, dinfo));
63 
64     if (num_bytes > 0) {
65 	if (num_bytes > src->bytes_in_buffer) {
66 	    jddp->skip += num_bytes - src->bytes_in_buffer;
67 	    src->next_input_byte += src->bytes_in_buffer;
68 	    src->bytes_in_buffer = 0;
69 	    return;
70 	}
71 	src->next_input_byte += num_bytes;
72 	src->bytes_in_buffer -= num_bytes;
73     }
74 }
75 private void
dctd_term_source(j_decompress_ptr dinfo)76 dctd_term_source(j_decompress_ptr dinfo)
77 {
78 }
79 
80 /* Set the defaults for the DCTDecode filter. */
81 private void
s_DCTD_set_defaults(stream_state * st)82 s_DCTD_set_defaults(stream_state * st)
83 {
84     s_DCT_set_defaults(st);
85 }
86 
87 /* Initialize DCTDecode filter */
88 private int
s_DCTD_init(stream_state * st)89 s_DCTD_init(stream_state * st)
90 {
91     stream_DCT_state *const ss = (stream_DCT_state *) st;
92     struct jpeg_source_mgr *src = &ss->data.decompress->source;
93 
94     src->init_source = dctd_init_source;
95     src->fill_input_buffer = dctd_fill_input_buffer;
96     src->skip_input_data = dctd_skip_input_data;
97     src->term_source = dctd_term_source;
98     src->resync_to_restart = jpeg_resync_to_restart;	/* use default method */
99     ss->data.common->memory = ss->jpeg_memory;
100     ss->data.decompress->dinfo.src = src;
101     ss->data.decompress->skip = 0;
102     ss->data.decompress->input_eod = false;
103     ss->data.decompress->faked_eoi = false;
104     ss->phase = 0;
105     return 0;
106 }
107 
108 /* Process a buffer */
109 private int
s_DCTD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)110 s_DCTD_process(stream_state * st, stream_cursor_read * pr,
111 	       stream_cursor_write * pw, bool last)
112 {
113     stream_DCT_state *const ss = (stream_DCT_state *) st;
114     jpeg_decompress_data *jddp = ss->data.decompress;
115     struct jpeg_source_mgr *src = jddp->dinfo.src;
116     int code;
117 
118     if_debug3('w', "[wdd]process avail=%u, skip=%u, last=%d\n",
119 	      (uint) (pr->limit - pr->ptr), (uint) jddp->skip, last);
120     if (jddp->skip != 0) {
121 	long avail = pr->limit - pr->ptr;
122 
123 	if (avail < jddp->skip) {
124 	    jddp->skip -= avail;
125 	    pr->ptr = pr->limit;
126 	    if (!last)
127 		return 0;	/* need more data */
128 	    jddp->skip = 0;	/* don't skip past input EOD */
129 	}
130 	pr->ptr += jddp->skip;
131 	jddp->skip = 0;
132     }
133     src->next_input_byte = pr->ptr + 1;
134     src->bytes_in_buffer = pr->limit - pr->ptr;
135     jddp->input_eod = last;
136     switch (ss->phase) {
137 	case 0:		/* not initialized yet */
138 	    /*
139 	     * Adobe implementations seem to ignore leading garbage bytes,
140 	     * even though neither the standard nor Adobe's own
141 	     * documentation mention this.
142 	     */
143 	    while (pr->ptr < pr->limit && pr->ptr[1] != 0xff)
144 		pr->ptr++;
145 	    if (pr->ptr == pr->limit)
146 		return 0;
147 	    src->next_input_byte = pr->ptr + 1;
148 	    src->bytes_in_buffer = pr->limit - pr->ptr;
149 	    ss->phase = 1;
150 	    /* falls through */
151 	case 1:		/* reading header markers */
152 	    if ((code = gs_jpeg_read_header(ss, TRUE)) < 0)
153 		return ERRC;
154 	    pr->ptr =
155 		(jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
156 	    switch (code) {
157 		case JPEG_SUSPENDED:
158 		    return 0;
159 		    /*case JPEG_HEADER_OK: */
160 	    }
161 	    /* If we have a ColorTransform parameter, and it's not
162 	     * overridden by an Adobe marker in the data, set colorspace.
163 	     */
164 	    if (ss->ColorTransform >= 0 &&
165 		!jddp->dinfo.saw_Adobe_marker) {
166 		switch (jddp->dinfo.num_components) {
167 		    case 3:
168 			jddp->dinfo.jpeg_color_space =
169 			    (ss->ColorTransform ? JCS_YCbCr : JCS_RGB);
170 			/* out_color_space will default to JCS_RGB */
171 			break;
172 		    case 4:
173 			jddp->dinfo.jpeg_color_space =
174 			    (ss->ColorTransform ? JCS_YCCK : JCS_CMYK);
175 			/* out_color_space will default to JCS_CMYK */
176 			break;
177 		}
178 	    }
179 	    ss->phase = 2;
180 	    /* falls through */
181 	case 2:		/* start_decompress */
182 	    if ((code = gs_jpeg_start_decompress(ss)) < 0)
183 		return ERRC;
184 	    pr->ptr =
185 		(jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
186 	    if (code == 0)
187 		return 0;
188 	    ss->scan_line_size =
189 		jddp->dinfo.output_width * jddp->dinfo.output_components;
190 	    if_debug4('w', "[wdd]width=%u, components=%d, scan_line_size=%u, min_out_size=%u\n",
191 		      jddp->dinfo.output_width,
192 		      jddp->dinfo.output_components,
193 		      ss->scan_line_size, jddp->template.min_out_size);
194 	    if (ss->scan_line_size > (uint) jddp->template.min_out_size) {
195 		/* Create a spare buffer for oversize scanline */
196 		jddp->scanline_buffer =
197 		    gs_alloc_bytes_immovable(jddp->memory,
198 					     ss->scan_line_size,
199 					 "s_DCTD_process(scanline_buffer)");
200 		if (jddp->scanline_buffer == NULL)
201 		    return ERRC;
202 	    }
203 	    jddp->bytes_in_scanline = 0;
204 	    ss->phase = 3;
205 	    /* falls through */
206 	case 3:		/* reading data */
207 	  dumpbuffer:
208 	    if (jddp->bytes_in_scanline != 0) {
209 		uint avail = pw->limit - pw->ptr;
210 		uint tomove = min(jddp->bytes_in_scanline,
211 				  avail);
212 
213 		if_debug2('w', "[wdd]moving %u/%u\n",
214 			  tomove, avail);
215 		memcpy(pw->ptr + 1, jddp->scanline_buffer +
216 		       (ss->scan_line_size - jddp->bytes_in_scanline),
217 		       tomove);
218 		pw->ptr += tomove;
219 		jddp->bytes_in_scanline -= tomove;
220 		if (jddp->bytes_in_scanline != 0)
221 		    return 1;	/* need more room */
222 	    }
223 	    while (jddp->dinfo.output_height > jddp->dinfo.output_scanline) {
224 		int read;
225 		byte *samples;
226 
227 		if (jddp->scanline_buffer != NULL)
228 		    samples = jddp->scanline_buffer;
229 		else {
230 		    if ((uint) (pw->limit - pw->ptr) < ss->scan_line_size)
231 			return 1;	/* need more room */
232 		    samples = pw->ptr + 1;
233 		}
234 		read = gs_jpeg_read_scanlines(ss, &samples, 1);
235 		if (read < 0)
236 		    return ERRC;
237 		if_debug3('w', "[wdd]read returns %d, used=%u, faked_eoi=%d\n",
238 			  read,
239 			  (uint) (src->next_input_byte - 1 - pr->ptr),
240 			  (int)jddp->faked_eoi);
241 		pr->ptr =
242 		    (jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
243 		if (!read)
244 		    return 0;	/* need more data */
245 		if (jddp->scanline_buffer != NULL) {
246 		    jddp->bytes_in_scanline = ss->scan_line_size;
247 		    goto dumpbuffer;
248 		}
249 		pw->ptr += ss->scan_line_size;
250 	    }
251 	    ss->phase = 4;
252 	    /* falls through */
253 	case 4:		/* end of image; scan for EOI */
254 	    if ((code = gs_jpeg_finish_decompress(ss)) < 0)
255 		return ERRC;
256 	    pr->ptr =
257 		(jddp->faked_eoi ? pr->limit : src->next_input_byte - 1);
258 	    if (code == 0)
259 		return 0;
260 	    ss->phase = 5;
261 	    /* falls through */
262 	case 5:		/* we are DONE */
263 	    return EOFC;
264     }
265     /* Default case can't happen.... */
266     return ERRC;
267 }
268 
269 /* Release the stream */
270 private void
s_DCTD_release(stream_state * st)271 s_DCTD_release(stream_state * st)
272 {
273     stream_DCT_state *const ss = (stream_DCT_state *) st;
274 
275     gs_jpeg_destroy(ss);
276     if (ss->data.decompress->scanline_buffer != NULL)
277 	gs_free_object(ss->data.common->memory,
278 		       ss->data.decompress->scanline_buffer,
279 		       "s_DCTD_release(scanline_buffer)");
280     gs_free_object(ss->data.common->memory, ss->data.decompress,
281 		   "s_DCTD_release");
282     /* Switch the template pointer back in case we still need it. */
283     st->template = &s_DCTD_template;
284 }
285 
286 /* Stream template */
287 const stream_template s_DCTD_template =
288 {&st_DCT_state, s_DCTD_init, s_DCTD_process, 2000, 4000, s_DCTD_release,
289  s_DCTD_set_defaults
290 };
291