00001 // Filename: pnmFileTypeJPGWriter.cxx 00002 // Created by: mike (19Jun00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pnmFileTypeJPG.h" 00016 00017 #ifdef HAVE_JPEG 00018 00019 #include "config_pnmimagetypes.h" 00020 00021 #include "pnmImage.h" 00022 #include "pnmWriter.h" 00023 #include "thread.h" 00024 00025 00026 // 00027 // The following bit of code, for setting up jpeg_ostream_src(), was 00028 // lifted from jpeglib, and modified to work with ostream instead of 00029 // stdio. 00030 // 00031 00032 /* 00033 * jdatadst.c 00034 * 00035 * Copyright (C) 1994-1996, Thomas G. Lane. 00036 * This file is part of the Independent JPEG Group's software. 00037 * For conditions of distribution and use, see the accompanying README file. 00038 * 00039 * This file contains compression data destination routines for the case of 00040 * emitting JPEG data to a file (or any stdio stream). While these routines 00041 * are sufficient for most applications, some will want to use a different 00042 * destination manager. 00043 * IMPORTANT: we assume that fwrite() will correctly transcribe an array of 00044 * JOCTETs into 8-bit-wide elements on external storage. If char is wider 00045 * than 8 bits on your machine, you may need to do some tweaking. 00046 */ 00047 00048 /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ 00049 extern "C" { 00050 #include <jpeglib.h> 00051 #include <jerror.h> 00052 } 00053 00054 00055 /* Expanded data destination object for stdio output */ 00056 00057 typedef struct { 00058 struct jpeg_destination_mgr pub; /* public fields */ 00059 00060 ostream * outfile; /* target stream */ 00061 JOCTET * buffer; /* start of buffer */ 00062 } my_destination_mgr; 00063 00064 typedef my_destination_mgr * my_dest_ptr; 00065 00066 #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ 00067 00068 00069 /* 00070 * Initialize destination --- called by jpeg_start_compress 00071 * before any data is actually written. 00072 */ 00073 00074 METHODDEF(void) 00075 init_destination (j_compress_ptr cinfo) 00076 { 00077 my_dest_ptr dest = (my_dest_ptr) cinfo->dest; 00078 00079 /* Allocate the output buffer --- it will be released when done with image */ 00080 dest->buffer = (JOCTET *) 00081 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 00082 OUTPUT_BUF_SIZE * sizeof(JOCTET)); 00083 00084 dest->pub.next_output_byte = dest->buffer; 00085 dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; 00086 } 00087 00088 00089 /* 00090 * Empty the output buffer --- called whenever buffer fills up. 00091 * 00092 * In typical applications, this should write the entire output buffer 00093 * (ignoring the current state of next_output_byte & free_in_buffer), 00094 * reset the pointer & count to the start of the buffer, and return TRUE 00095 * indicating that the buffer has been dumped. 00096 * 00097 * In applications that need to be able to suspend compression due to output 00098 * overrun, a FALSE return indicates that the buffer cannot be emptied now. 00099 * In this situation, the compressor will return to its caller (possibly with 00100 * an indication that it has not accepted all the supplied scanlines). The 00101 * application should resume compression after it has made more room in the 00102 * output buffer. Note that there are substantial restrictions on the use of 00103 * suspension --- see the documentation. 00104 * 00105 * When suspending, the compressor will back up to a convenient restart point 00106 * (typically the start of the current MCU). next_output_byte & free_in_buffer 00107 * indicate where the restart point will be if the current call returns FALSE. 00108 * Data beyond this point will be regenerated after resumption, so do not 00109 * write it out when emptying the buffer externally. 00110 */ 00111 00112 METHODDEF(boolean) 00113 empty_output_buffer (j_compress_ptr cinfo) 00114 { 00115 my_dest_ptr dest = (my_dest_ptr) cinfo->dest; 00116 00117 if (!dest->outfile->write((const char *)dest->buffer, OUTPUT_BUF_SIZE)) 00118 ERREXIT(cinfo, JERR_FILE_WRITE); 00119 00120 dest->pub.next_output_byte = dest->buffer; 00121 dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; 00122 Thread::consider_yield(); 00123 00124 return TRUE; 00125 } 00126 00127 00128 /* 00129 * Terminate destination --- called by jpeg_finish_compress 00130 * after all data has been written. Usually needs to flush buffer. 00131 * 00132 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding 00133 * application must deal with any cleanup that should happen even 00134 * for error exit. 00135 */ 00136 00137 METHODDEF(void) 00138 term_destination (j_compress_ptr cinfo) 00139 { 00140 my_dest_ptr dest = (my_dest_ptr) cinfo->dest; 00141 size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; 00142 00143 /* Write any data remaining in the buffer */ 00144 if (datacount > 0) { 00145 if (!dest->outfile->write((const char *)dest->buffer, datacount)) 00146 ERREXIT(cinfo, JERR_FILE_WRITE); 00147 } 00148 dest->outfile->flush(); 00149 Thread::consider_yield(); 00150 /* Make sure we wrote the output file OK */ 00151 if (dest->outfile->fail()) 00152 ERREXIT(cinfo, JERR_FILE_WRITE); 00153 } 00154 00155 00156 /* 00157 * Prepare for output to a stdio stream. 00158 * The caller must have already opened the stream, and is responsible 00159 * for closing it after finishing compression. 00160 */ 00161 00162 GLOBAL(void) 00163 jpeg_ostream_dest (j_compress_ptr cinfo, ostream * outfile) 00164 { 00165 my_dest_ptr dest; 00166 00167 /* The destination object is made permanent so that multiple JPEG images 00168 * can be written to the same file without re-executing jpeg_stdio_dest. 00169 * This makes it dangerous to use this manager and a different destination 00170 * manager serially with the same JPEG object, because their private object 00171 * sizes may be different. Caveat programmer. 00172 */ 00173 if (cinfo->dest == NULL) { /* first time for this JPEG object? */ 00174 cinfo->dest = (struct jpeg_destination_mgr *) 00175 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, 00176 sizeof(my_destination_mgr)); 00177 } 00178 00179 dest = (my_dest_ptr) cinfo->dest; 00180 dest->pub.init_destination = init_destination; 00181 dest->pub.empty_output_buffer = empty_output_buffer; 00182 dest->pub.term_destination = term_destination; 00183 dest->outfile = outfile; 00184 } 00185 00186 00187 00188 // 00189 // The rest of the code in this file is new to Panda. 00190 // 00191 00192 //////////////////////////////////////////////////////////////////// 00193 // Function: PNMFileTypeJPG::Writer::Constructor 00194 // Access: Public 00195 // Description: 00196 //////////////////////////////////////////////////////////////////// 00197 PNMFileTypeJPG::Writer:: 00198 Writer(PNMFileType *type, ostream *file, bool owns_file) : 00199 PNMWriter(type, file, owns_file) 00200 { 00201 } 00202 00203 //////////////////////////////////////////////////////////////////// 00204 // Function: PNMFileTypeJPG::Writer::write_data 00205 // Access: Public, Virtual 00206 // Description: Writes out an entire image all at once, including the 00207 // header, based on the image data stored in the given 00208 // _x_size * _y_size array and alpha pointers. (If the 00209 // image type has no alpha channel, alpha is ignored.) 00210 // Returns the number of rows correctly written. 00211 // 00212 // It is the user's responsibility to fill in the header 00213 // data via calls to set_x_size(), set_num_channels(), 00214 // etc., or copy_header_from(), before calling 00215 // write_data(). 00216 // 00217 // It is important to delete the PNMWriter class after 00218 // successfully writing the data. Failing to do this 00219 // may result in some data not getting flushed! 00220 // 00221 // Derived classes need not override this if they 00222 // instead provide supports_streaming() and write_row(), 00223 // below. 00224 //////////////////////////////////////////////////////////////////// 00225 int PNMFileTypeJPG::Writer:: 00226 write_data(xel *array, xelval *) { 00227 if (_y_size<=0 || _x_size<=0) { 00228 return 0; 00229 } 00230 00231 /* This struct contains the JPEG compression parameters and pointers to 00232 * working space (which is allocated as needed by the JPEG library). 00233 * It is possible to have several such structures, representing multiple 00234 * compression/decompression processes, in existence at once. We refer 00235 * to any one struct (and its associated working data) as a "JPEG object". 00236 */ 00237 struct jpeg_compress_struct cinfo; 00238 /* This struct represents a JPEG error handler. It is declared separately 00239 * because applications often want to supply a specialized error handler 00240 * (see the second half of this file for an example). But here we just 00241 * take the easy way out and use the standard error handler, which will 00242 * print a message on stderr and call exit() if compression fails. 00243 * Note that this struct must live as long as the main JPEG parameter 00244 * struct, to avoid dangling-pointer problems. 00245 */ 00246 struct jpeg_error_mgr jerr; 00247 /* More stuff */ 00248 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ 00249 int row_stride; /* physical row width in image buffer */ 00250 00251 /* Step 1: allocate and initialize JPEG compression object */ 00252 00253 /* We have to set up the error handler first, in case the initialization 00254 * step fails. (Unlikely, but it could happen if you are out of memory.) 00255 * This routine fills in the contents of struct jerr, and returns jerr's 00256 * address which we place into the link field in cinfo. 00257 */ 00258 cinfo.err = jpeg_std_error(&jerr); 00259 00260 /* Now we can initialize the JPEG compression object. */ 00261 jpeg_create_compress(&cinfo); 00262 00263 /* Step 2: specify data destination (eg, a file) */ 00264 /* Note: steps 2 and 3 can be done in either order. */ 00265 jpeg_ostream_dest(&cinfo, _file); 00266 00267 /* Step 3: set parameters for compression */ 00268 00269 /* First we supply a description of the input image. 00270 * Four fields of the cinfo struct must be filled in: 00271 */ 00272 cinfo.image_width = _x_size; /* image width and height, in pixels */ 00273 cinfo.image_height = _y_size; 00274 if (is_grayscale()) { 00275 cinfo.input_components = 1; 00276 cinfo.in_color_space = JCS_GRAYSCALE; 00277 } else { 00278 cinfo.input_components = 3; 00279 cinfo.in_color_space = JCS_RGB; 00280 } 00281 /* Now use the library's routine to set default compression parameters. 00282 * (You must set at least cinfo.in_color_space before calling this, 00283 * since the defaults depend on the source color space.) 00284 */ 00285 jpeg_set_defaults(&cinfo); 00286 /* Now you can set any non-default parameters you wish to. 00287 * Here we just illustrate the use of quality (quantization table) scaling: 00288 */ 00289 jpeg_set_quality(&cinfo, jpeg_quality, TRUE /* limit to baseline-JPEG values */); 00290 00291 /* Step 4: Start compressor */ 00292 00293 /* TRUE ensures that we will write a complete interchange-JPEG file. 00294 * Pass TRUE unless you are very sure of what you're doing. 00295 */ 00296 jpeg_start_compress(&cinfo, TRUE); 00297 00298 /* Write the user comment, if any */ 00299 if (_comment.size()) { 00300 jpeg_write_marker( 00301 &cinfo, JPEG_COM, (JOCTET *)_comment.c_str(), strlen(_comment.c_str())); 00302 } 00303 00304 /* Step 5: while (scan lines remain to be written) */ 00305 /* jpeg_write_scanlines(...); */ 00306 00307 /* Here we use the library's state variable cinfo.next_scanline as the 00308 * loop counter, so that we don't have to keep track ourselves. 00309 * To keep things simple, we pass one scanline per call; you can pass 00310 * more if you wish, though. 00311 */ 00312 row_stride = _x_size * cinfo.input_components; /* JSAMPLEs per row in image_buffer */ 00313 00314 int x = 0; 00315 JSAMPROW row = new JSAMPLE[row_stride]; 00316 while (cinfo.next_scanline < cinfo.image_height) { 00317 /* jpeg_write_scanlines expects an array of pointers to scanlines. 00318 * Here the array is only one element long, but you could pass 00319 * more than one scanline at a time if that's more convenient. 00320 */ 00321 for (int i = 0; i < row_stride; i += cinfo.input_components) { 00322 if (cinfo.input_components == 1) { 00323 row[i] = (JSAMPLE)(MAXJSAMPLE * PPM_GETB(array[x])/_maxval); 00324 } else { 00325 row[i] = (JSAMPLE)(MAXJSAMPLE * PPM_GETR(array[x])/_maxval); 00326 row[i+1] = (JSAMPLE)(MAXJSAMPLE * PPM_GETG(array[x])/_maxval); 00327 row[i+2] = (JSAMPLE)(MAXJSAMPLE * PPM_GETB(array[x])/_maxval); 00328 } 00329 x++; 00330 } 00331 //row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride]; 00332 //(void) jpeg_write_scanlines(&cinfo, row_pointer, 1); 00333 row_pointer[0] = row; 00334 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); 00335 } 00336 delete row; 00337 00338 /* Step 6: Finish compression */ 00339 00340 jpeg_finish_compress(&cinfo); 00341 00342 /* Step 7: release JPEG compression object */ 00343 00344 /* This is an important step since it will release a good deal of memory. */ 00345 jpeg_destroy_compress(&cinfo); 00346 00347 return _y_size; 00348 } 00349 00350 #endif // HAVE_JPEG