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