Panda3D

pnmFileTypeJPGWriter.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file pnmFileTypeJPGWriter.cxx
10 * @author mike
11 * @date 2000-06-19
12 */
13
14 #include "pnmFileTypeJPG.h"
15
16 #ifdef HAVE_JPEG
17
18 #include "config_pnmimagetypes.h"
19
20 #include "pnmImage.h"
21 #include "pnmWriter.h"
22 #include "thread.h"
23
24
25 // The following bit of code, for setting up jpeg_ostream_src(), was lifted
26 // from jpeglib, and modified to work with ostream instead of stdio.
27
28 /*
29 * jdatadst.c
30 *
31 * Copyright (C) 1994-1996, Thomas G. Lane.
32 * This file is part of the Independent JPEG Group's software.
33 * For conditions of distribution and use, see the accompanying README file.
34 *
35 * This file contains compression data destination routines for the case of
36 * emitting JPEG data to a file (or any stdio stream). While these routines
37 * are sufficient for most applications, some will want to use a different
38 * destination manager.
39 * IMPORTANT: we assume that fwrite() will correctly transcribe an array of
40 * JOCTETs into 8-bit-wide elements on external storage. If char is wider
41 * than 8 bits on your machine, you may need to do some tweaking.
42 */
43
44 /* this is not a core library module, so it doesn't define JPEG_INTERNALS */
45 extern "C" {
46 #include <jpeglib.h>
47 #include <jerror.h>
48 }
49
50
51 /* Expanded data destination object for stdio output */
52
53 typedef struct {
54 struct jpeg_destination_mgr pub; /* public fields */
55
56 std::ostream * outfile; /* target stream */
57 JOCTET * buffer; /* start of buffer */
58 } my_destination_mgr;
59
60 typedef my_destination_mgr * my_dest_ptr;
61
62 #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
63
64
65 /*
66 * Initialize destination --- called by jpeg_start_compress
67 * before any data is actually written.
68 */
69
70 METHODDEF(void)
71 init_destination (j_compress_ptr cinfo)
72 {
73 my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
74
75 /* Allocate the output buffer --- it will be released when done with image */
76 dest->buffer = (JOCTET *)
77 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
78 OUTPUT_BUF_SIZE * sizeof(JOCTET));
79
80 dest->pub.next_output_byte = dest->buffer;
81 dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
82 }
83
84
85 /*
86 * Empty the output buffer --- called whenever buffer fills up.
87 *
88 * In typical applications, this should write the entire output buffer
89 * (ignoring the current state of next_output_byte & free_in_buffer),
90 * reset the pointer & count to the start of the buffer, and return TRUE
91 * indicating that the buffer has been dumped.
92 *
93 * In applications that need to be able to suspend compression due to output
94 * overrun, a FALSE return indicates that the buffer cannot be emptied now.
95 * In this situation, the compressor will return to its caller (possibly with
96 * an indication that it has not accepted all the supplied scanlines). The
97 * application should resume compression after it has made more room in the
98 * output buffer. Note that there are substantial restrictions on the use of
99 * suspension --- see the documentation.
100 *
101 * When suspending, the compressor will back up to a convenient restart point
102 * (typically the start of the current MCU). next_output_byte & free_in_buffer
103 * indicate where the restart point will be if the current call returns FALSE.
104 * Data beyond this point will be regenerated after resumption, so do not
105 * write it out when emptying the buffer externally.
106 */
107
108 METHODDEF(boolean)
109 empty_output_buffer (j_compress_ptr cinfo)
110 {
111 my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
112
113 if (!dest->outfile->write((const char *)dest->buffer, OUTPUT_BUF_SIZE))
114 ERREXIT(cinfo, JERR_FILE_WRITE);
115
116 dest->pub.next_output_byte = dest->buffer;
117 dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
118 Thread::consider_yield();
119
120 return TRUE;
121 }
122
123
124 /*
125 * Terminate destination --- called by jpeg_finish_compress
126 * after all data has been written. Usually needs to flush buffer.
127 *
128 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
129 * application must deal with any cleanup that should happen even
130 * for error exit.
131 */
132
133 METHODDEF(void)
134 term_destination (j_compress_ptr cinfo)
135 {
136 my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
137 size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
138
139 /* Write any data remaining in the buffer */
140 if (datacount > 0) {
141 if (!dest->outfile->write((const char *)dest->buffer, datacount))
142 ERREXIT(cinfo, JERR_FILE_WRITE);
143 }
144 dest->outfile->flush();
145 Thread::consider_yield();
146 /* Make sure we wrote the output file OK */
147 if (dest->outfile->fail())
148 ERREXIT(cinfo, JERR_FILE_WRITE);
149 }
150
151
152 /*
153 * Prepare for output to a stdio stream.
154 * The caller must have already opened the stream, and is responsible
155 * for closing it after finishing compression.
156 */
157
158 GLOBAL(void)
159 jpeg_ostream_dest (j_compress_ptr cinfo, std::ostream * outfile)
160 {
161 my_dest_ptr dest;
162
163 /* The destination object is made permanent so that multiple JPEG images
164 * can be written to the same file without re-executing jpeg_stdio_dest.
165 * This makes it dangerous to use this manager and a different destination
166 * manager serially with the same JPEG object, because their private object
167 * sizes may be different. Caveat programmer.
168 */
169 if (cinfo->dest == nullptr) { /* first time for this JPEG object? */
170 cinfo->dest = (struct jpeg_destination_mgr *)
171 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
172 sizeof(my_destination_mgr));
173 }
174
175 dest = (my_dest_ptr) cinfo->dest;
176 dest->pub.init_destination = init_destination;
177 dest->pub.empty_output_buffer = empty_output_buffer;
178 dest->pub.term_destination = term_destination;
179 dest->outfile = outfile;
180 }
181
182
183
184 // The rest of the code in this file is new to Panda.
185
186 /**
187 *
188 */
189 PNMFileTypeJPG::Writer::
190 Writer(PNMFileType *type, std::ostream *file, bool owns_file) :
191 PNMWriter(type, file, owns_file)
192 {
193 }
194
195 /**
196 * Writes out an entire image all at once, including the header, based on the
197 * image data stored in the given _x_size * _y_size array and alpha pointers.
198 * (If the image type has no alpha channel, alpha is ignored.) Returns the
199 * number of rows correctly written.
200 *
201 * It is the user's responsibility to fill in the header data via calls to
202 * set_x_size(), set_num_channels(), etc., or copy_header_from(), before
203 * calling write_data().
204 *
205 * It is important to delete the PNMWriter class after successfully writing
206 * the data. Failing to do this may result in some data not getting flushed!
207 *
208 * Derived classes need not override this if they instead provide
209 * supports_streaming() and write_row(), below.
210 */
211 int PNMFileTypeJPG::Writer::
212 write_data(xel *array, xelval *) {
213 if (_y_size<=0 || _x_size<=0) {
214 return 0;
215 }
216
217 /* This struct contains the JPEG compression parameters and pointers to
218 * working space (which is allocated as needed by the JPEG library).
219 * It is possible to have several such structures, representing multiple
220 * compression/decompression processes, in existence at once. We refer
221 * to any one struct (and its associated working data) as a "JPEG object".
222 */
223 struct jpeg_compress_struct cinfo;
224 /* This struct represents a JPEG error handler. It is declared separately
225 * because applications often want to supply a specialized error handler
226 * (see the second half of this file for an example). But here we just
227 * take the easy way out and use the standard error handler, which will
228 * print a message on stderr and call exit() if compression fails.
229 * Note that this struct must live as long as the main JPEG parameter
230 * struct, to avoid dangling-pointer problems.
231 */
232 struct jpeg_error_mgr jerr;
233 /* More stuff */
234 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
235 int row_stride; /* physical row width in image buffer */
236
237 /* Step 1: allocate and initialize JPEG compression object */
238
239 /* We have to set up the error handler first, in case the initialization
240 * step fails. (Unlikely, but it could happen if you are out of memory.)
241 * This routine fills in the contents of struct jerr, and returns jerr's
242 * address which we place into the link field in cinfo.
243 */
244 cinfo.err = jpeg_std_error(&jerr);
245
246 /* Now we can initialize the JPEG compression object. */
247 jpeg_create_compress(&cinfo);
248
249 /* Step 2: specify data destination (eg, a file) */
250 /* Note: steps 2 and 3 can be done in either order. */
251 jpeg_ostream_dest(&cinfo, _file);
252
253 /* Step 3: set parameters for compression */
254
255 /* First we supply a description of the input image.
256 * Four fields of the cinfo struct must be filled in:
257 */
258 cinfo.image_width = _x_size; /* image width and height, in pixels */
259 cinfo.image_height = _y_size;
260 if (is_grayscale()) {
261 cinfo.input_components = 1;
262 cinfo.in_color_space = JCS_GRAYSCALE;
263 } else {
264 cinfo.input_components = 3;
265 cinfo.in_color_space = JCS_RGB;
266 }
267 /* Now use the library's routine to set default compression parameters.
268 * (You must set at least cinfo.in_color_space before calling this,
269 * since the defaults depend on the source color space.)
270 */
271 jpeg_set_defaults(&cinfo);
272 /* Now you can set any non-default parameters you wish to.
273 * Here we just illustrate the use of quality (quantization table) scaling:
274 */
275 jpeg_set_quality(&cinfo, jpeg_quality, TRUE /* limit to baseline-JPEG values */);
276
277 /* Step 4: Start compressor */
278
279 /* TRUE ensures that we will write a complete interchange-JPEG file.
280 * Pass TRUE unless you are very sure of what you're doing.
281 */
282 jpeg_start_compress(&cinfo, TRUE);
283
284 /* Write the user comment, if any */
285 if (_comment.size()) {
286 jpeg_write_marker(
287 &cinfo, JPEG_COM, (JOCTET *)_comment.c_str(), strlen(_comment.c_str()));
288 }
289
290 /* Step 5: while (scan lines remain to be written) */
291 /* jpeg_write_scanlines(...); */
292
293 /* Here we use the library's state variable cinfo.next_scanline as the
294 * loop counter, so that we don't have to keep track ourselves.
295 * To keep things simple, we pass one scanline per call; you can pass
296 * more if you wish, though.
297 */
298 row_stride = _x_size * cinfo.input_components; /* JSAMPLEs per row in image_buffer */
299
300 int x = 0;
301 JSAMPROW row = new JSAMPLE[row_stride];
302 while (cinfo.next_scanline < cinfo.image_height) {
303 /* jpeg_write_scanlines expects an array of pointers to scanlines.
304 * Here the array is only one element long, but you could pass
305 * more than one scanline at a time if that's more convenient.
306 */
307 for (int i = 0; i < row_stride; i += cinfo.input_components) {
308 if (cinfo.input_components == 1) {
309 row[i] = (JSAMPLE)(MAXJSAMPLE * PPM_GETB(array[x])/_maxval);
310 } else {
311 row[i] = (JSAMPLE)(MAXJSAMPLE * PPM_GETR(array[x])/_maxval);
312 row[i+1] = (JSAMPLE)(MAXJSAMPLE * PPM_GETG(array[x])/_maxval);
313 row[i+2] = (JSAMPLE)(MAXJSAMPLE * PPM_GETB(array[x])/_maxval);
314 }
315 x++;
316 }
317 // row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
318 // (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
319 row_pointer[0] = row;
320 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
321 }
322 delete[] row;
323
324 /* Step 6: Finish compression */
325
326 jpeg_finish_compress(&cinfo);
327
328 /* Step 7: release JPEG compression object */
329
330 /* This is an important step since it will release a good deal of memory. */
331 jpeg_destroy_compress(&cinfo);
332
333 return _y_size;
334 }
335
336 #endif // HAVE_JPEG
PNMFileType
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
PNMWriter
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
Thread::consider_yield
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:212
config_pnmimagetypes.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmFileTypeJPG.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pixel
Definition: pnmimage_base.h:41
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.