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;
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();
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.