OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimJpegCodec.cpp
Go to the documentation of this file.
1 //---
2 //
3 // License: MIT
4 //
5 // Description: class declaration for base codec(encoder/decoder).
6 //
7 //---
8 // $Id$
9 
11 
12 // we need to get rid of the jpedlib include in this header
14 // same for here
16 
19 
21 #include <csetjmp>
22 #include <jpeglib.h>
26 {
27  struct jpeg_error_mgr pub; /* "public" fields */
28  jmp_buf setjmp_buffer; /* for return to caller */
29 };
31 
32 void ossimJpegErrorExit (jpeg_common_struct* cinfo)
33 {
34  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
35  ossimJpegErrorPtr myerr = (ossimJpegErrorPtr) cinfo->err;
36 
37  /* Always display the message. */
38  /* We could postpone this until after returning, if we chose. */
39  (*cinfo->err->output_message) (cinfo);
40 
41  /* Return control to the setjmp point */
42  longjmp(myerr->setjmp_buffer, 1);
43 }
44 
46  :m_quality(100),
47  m_ext("jpg")
48 {
49 }
50 
52 {
53 }
54 
56 {
57  return ossimString("jpeg");
58 }
59 
60 const std::string& ossimJpegCodec::getExtension() const
61 {
62  return m_ext; // "jpg"
63 }
64 
66  std::vector<ossim_uint8>& out )const
67 {
68  bool result = false;
69  if ( in.valid() && (in->getDataObjectStatus() != OSSIM_NULL) )
70  {
71  if ( in->getScalarType() == OSSIM_UINT8 )
72  {
73  // Open a memory stream up to put the jpeg image in memory:
74  std::stringstream jpegStreamBuf;
75 
76  //---
77  // Initialize JPEG compression library:
78  // NOTE: JDIMENSION is an "unsigned int"
79  //---
80  struct jpeg_compress_struct cinfo;
81  struct jpeg_error_mgr jerr;
82  cinfo.err = jpeg_std_error( &jerr );
83  jpeg_create_compress(&cinfo);
84 
85  // Define a custom stream destination manager for jpeglib to write compressed block:
86  jpeg_cpp_stream_dest(&cinfo, jpegStreamBuf);
87 
88  /* Setting the parameters of the output file here */
89  cinfo.image_width = in->getWidth();
90  cinfo.image_height = in->getHeight();
91 
92  // Bands must be one or three for this writer.
93  const ossim_uint32 INPUT_BANDS = in->getNumberOfBands();
94  if ( (INPUT_BANDS == 1) || (INPUT_BANDS == 3) )
95  {
96  cinfo.input_components = INPUT_BANDS;
97  }
98  else
99  {
100  if ( INPUT_BANDS < 3 )
101  {
102  cinfo.input_components = 1; // Use first band.
103  }
104  else
105  {
106  cinfo.input_components = 3; // Use the first 3 bands.
107  }
108  }
109 
110  // colorspace of input image
111  if ( cinfo.input_components == 3)
112  {
113  cinfo.in_color_space = JCS_RGB;
114  }
115  else
116  {
117  cinfo.in_color_space = JCS_GRAYSCALE;
118  }
119 
120  // Default compression parameters, we shouldn't be worried about these.
121  jpeg_set_defaults( &cinfo );
122 
123  jpeg_set_quality(&cinfo, m_quality, TRUE); //limit to baseline-JPEG values
124 
125  // Now do the compression...
126  jpeg_start_compress( &cinfo, TRUE );
127 
128  // Line buffer:
129  ossim_uint32 buf_size = cinfo.input_components*cinfo.image_width;
130  std::vector<ossim_uint8> buf(buf_size);
131 
132  // Compress the tile on line at a time:
133 
134  JSAMPROW row_pointer[1]; // Pointer to a single row.
135  row_pointer[0] = (JSAMPLE*)&buf.front();
136 
137  // Get pointers to the input data:
138  std::vector<const ossim_uint8*> inBuf(cinfo.input_components);
139  for ( ossim_int32 band = 0; band < cinfo.input_components; ++band )
140  {
141  inBuf[band] = in->getUcharBuf(band);
142  }
143 
144  ossim_uint32 inIdx = 0;
145  for (ossim_uint32 line=0; line< cinfo.image_height; ++line)
146  {
147  // Convert from band sequential to band interleaved by pixel.
148  ossim_uint32 outIdx = 0;
149  for ( ossim_uint32 p = 0; p < cinfo.image_width; ++p )
150  {
151  for ( ossim_int32 band = 0; band < cinfo.input_components; ++band )
152  {
153  buf[outIdx++] = inBuf[band][inIdx];
154  }
155  ++inIdx;
156  }
157 
158  // Write it...
159  jpeg_write_scanlines( &cinfo, row_pointer, 1 );
160  }
161 
162  // Similar to read file, clean up after we're done compressing.
163  jpeg_finish_compress( &cinfo );
164  jpeg_destroy_compress( &cinfo );
165 
166  // Copy the memory stream to output vector.
167  out.resize(jpegStreamBuf.str().size());
168  jpegStreamBuf.seekg(0, std::ios_base::beg);
169  jpegStreamBuf.read((char*)&out.front(), jpegStreamBuf.str().size());
170 
171  result = true;
172  }
173  else // Scalar type check...
174  {
176  << "ossimCodecFactory::encodeJpeg ERROR:"
177  << "\nPassing non-eight bit data to eight bit encoder!" << std::endl;
178  }
179 
180  } // Matches: if ( in.valid() ... )
181 
182  return result;
183 
184 }
185 
186 bool ossimJpegCodec::decode( const std::vector<ossim_uint8>& in,
187  ossimRefPtr<ossimImageData>& out ) const
188 {
189  bool result = false;
190 
191  // Check for jpeg signature:
192  if ( in.size() > 3 )
193  {
194  if ( (in[0] == 0xff) &&
195  (in[1] == 0xd8) &&
196  (in[2] == 0xff) )
197  {
198  if (in[3] == 0xe0)
199  {
200  result = decodeJpeg( in, out );
201  }
202  else if (in[3] == 0xdb)
203  {
204  result = decodeJpegToRgb( in, out );
205  }
206  }
207  }
208 
209  return result;
210 }
211 
212 bool ossimJpegCodec::decodeJpeg( const std::vector<ossim_uint8>& in,
213  ossimRefPtr<ossimImageData>& out ) const
214 {
215  bool result = false;
216 
217  /* This struct contains the JPEG decompression parameters and pointers
218  * to working space (which is allocated as needed by the JPEG library).
219  */
220  jpeg_decompress_struct cinfo;
221 
222  /* We use our private extension JPEG error handler.
223  * Note that this struct must live as long as the main JPEG parameter
224  * struct, to avoid dangling-pointer problems.
225  */
226  ossimJpegErrorMgr jerr;
227 
228  /* Step 1: allocate and initialize JPEG decompression object */
229 
230  /* We set up the normal JPEG error routines, then override error_exit. */
231  cinfo.err = jpeg_std_error(&jerr.pub);
232 
233  jerr.pub.error_exit = ossimJpegErrorExit;
234 
235  /* Establish the setjmp return context for my_error_exit to use. */
236  if (setjmp(jerr.setjmp_buffer) == 0)
237  {
238  result = true;
239 
240  /* Now we can initialize the JPEG decompression object. */
241  jpeg_CreateDecompress(&cinfo, JPEG_LIB_VERSION, sizeof(cinfo));
242 
243  //---
244  // Step 2: specify data source. In this case we will uncompress from
245  // memory so we will use "ossimJpegMemorySrc" in place of " jpeg_stdio_src".
246  //---
247  ossimJpegMemorySrc ( &cinfo, &(in.front()), (size_t)(in.size()) );
248 
249  /* Step 3: read file parameters with jpeg_read_header() */
250  jpeg_read_header(&cinfo, TRUE);
251 
252  /* Step 4: set parameters for decompression */
253 
254  /* Step 5: Start decompressor */
255  jpeg_start_decompress(&cinfo);
256 
257  const ossim_uint32 SAMPLES = cinfo.output_width;
258  const ossim_uint32 LINES = cinfo.output_height;
259  const ossim_uint32 BANDS = cinfo.output_components;
260 
261 #if 0 /* Please leave for debug. (drb) */
262  if ( traceDebug() )
263  {
265  << "jpeg cinfo.output_width: " << cinfo.output_width
266  << "\njpeg cinfo.output_height: " << cinfo.output_height
267  << "\njpeg cinfo.out_color_space: " << cinfo.out_color_space
268  << "\n";
269  }
270 #endif
271 
272  if ( out.valid() )
273  {
274  // This will resize tile if not correct.
276  ossimIrect(0,0,(ossim_int32)SAMPLES-1,(ossim_int32)LINES-1), BANDS );
277  }
278  else
279  {
280  out = new ossimU8ImageData( 0, BANDS, SAMPLES, LINES );
281  out->initialize();
282  }
283 
284  // Get pointers to the cache tile buffers.
285  std::vector<ossim_uint8*> destinationBuffer(BANDS);
286  for (ossim_uint32 band = 0; band < BANDS; ++band)
287  {
288  destinationBuffer[band] = out->getUcharBuf(band);
289  }
290 
291  std::vector<ossim_uint8> lineBuffer(SAMPLES * cinfo.output_components);
292  JSAMPROW jbuf[1];
293  jbuf[0] = (JSAMPROW) &(lineBuffer.front());
294 
295  while (cinfo.output_scanline < LINES)
296  {
297  // Read a line from the jpeg file.
298  jpeg_read_scanlines(&cinfo, jbuf, 1);
299 
300  //---
301  // Copy the line which if band interleaved by pixel the the band
302  // separate buffers.
303  //---
304  ossim_uint32 index = 0;
305  for (ossim_uint32 sample = 0; sample < SAMPLES; ++sample)
306  {
307  for (ossim_uint32 band = 0; band < BANDS; ++band)
308  {
309  destinationBuffer[band][sample] = lineBuffer[index];
310  ++index;
311  }
312  }
313 
314  for (ossim_uint32 band = 0; band < BANDS; ++band)
315  {
316  destinationBuffer[band] += SAMPLES;
317  }
318  }
319 
320  // Set the tile status:
321  out->validate();
322 
323  // clean up...
324 
325  jpeg_finish_decompress(&cinfo);
326 
327  } // Matches: if (setjmp(jerr.setjmp_buffer) == 0)
328 
329  jpeg_destroy_decompress(&cinfo);
330 
331  return result;
332 }
333 
334 bool ossimJpegCodec::decodeJpegToRgb(const std::vector<ossim_uint8>& in,
335  ossimRefPtr<ossimImageData>& out ) const
336 
337 {
338  bool result = false;
339 
340  ossim_int32 jpegColorSpace = getColorSpace( in );
341 
342  if ( jpegColorSpace == JCS_CMYK )
343  {
344  ossimRefPtr<ossimImageData> cmykTile = 0;
345 
346  result = decodeJpeg( in, cmykTile ); // Decode to CMYK tile.
347  if ( result )
348  {
349  if ( cmykTile.valid() )
350  {
351  const ossim_uint32 INPUT_BANDS = cmykTile->getNumberOfBands();
352  if ( INPUT_BANDS == 4 )
353  {
354  const ossim_uint32 OUTPUT_BANDS = 3;
355  const ossimIrect RECT = cmykTile->getImageRectangle();
356  const ossim_uint32 LINES = RECT.height();
357  const ossim_uint32 SAMPLES = RECT.width();
358  ossim_uint32 band = 0;
359 
360  // Set or create output tile:
361  if ( out.valid() )
362  {
363  // This will resize tile if not correct.
364  out->setImageRectangleAndBands( RECT, OUTPUT_BANDS );
365  }
366  else
367  {
368  out = new ossimU8ImageData( 0, OUTPUT_BANDS, SAMPLES, LINES );
369  out->initialize();
370  }
371 
372  // Assign pointers to bands.
373  std::vector<const ossim_uint8*> inBands(INPUT_BANDS);
374  for ( band = 0; band < INPUT_BANDS; ++band )
375  {
376  inBands[band] = cmykTile->getUcharBuf( band );
377  }
378  std::vector<ossim_uint8*> outBands(INPUT_BANDS);
379  for ( band = 0; band < OUTPUT_BANDS; ++band )
380  {
381  outBands[band] = out->getUcharBuf( band );
382  }
383 
384  const ossim_uint8 NP = 0; // null pixel
385  const ossim_uint8 MAXP = 255; // max pixel
386 
387  std::vector<ossim_float32> cmyk(INPUT_BANDS, 0.0);
388  std::vector<ossim_float32> rgb(OUTPUT_BANDS, 0.0);
389 
390  for ( ossim_uint32 line = 0; line < LINES; ++line )
391  {
392  for (ossim_uint32 sample = 0; sample < SAMPLES; ++sample)
393  {
394  //---
395  // NOTE:
396  // This current does NOT work, colors come out wrong, with
397  // the one dataset that I have:
398  // "2015_05_05_Whitehorse_3857.gpkg"
399  // (drb - 03 June 2015)
400  //---
401 
402  cmyk[0] = inBands[0][sample]; // C
403  cmyk[1] = inBands[1][sample]; // M
404  cmyk[2] = inBands[2][sample]; // Y
405  cmyk[3] = inBands[3][sample]; // K
406 
407  //---
408  // The red (R) color is calculated from the cyan (C) and black (K) colors.
409  // The green color (G) is calculated from the magenta (M) and black (K) colors.
410  // The blue color (B) is calculated from the yellow (Y) and black (K) colors.
411  //---
412  // rgb[0] = (255.0-cmyk[0]) * 255.0-cmyk[3];
413  // rgb[1] = (255.0-cmyk[1]) * 255.0-cmyk[3];
414  // rgb[2] = (255.0-cmyk[2]) * 255.0-cmyk[3];
415  rgb[0] = (cmyk[0]) * cmyk[3]/255.0;
416  rgb[1] = (cmyk[1]) * cmyk[3]/255.0;
417  rgb[2] = (cmyk[2]) * cmyk[3]/255.0;
418 
419  outBands[0][sample] =
420  ( (rgb[0] >= 0.0) ? ( (rgb[0] <= 255.0) ?
421  (ossim_uint8)rgb[0] : MAXP ) : NP );
422  outBands[1][sample] =
423  ( (rgb[1] >= 0.0) ? ( (rgb[1] <= 255.0) ?
424  (ossim_uint8)rgb[1] : MAXP ) : NP );
425  outBands[2][sample] =
426  ( (rgb[2] >= 0.0) ? ( (rgb[2] <= 255.0) ?
427  (ossim_uint8)rgb[2] : MAXP ) : NP );
428 
429  } // End sample loop.
430 
431  // Increment pointers.
432  for (ossim_uint32 band = 0; band < OUTPUT_BANDS; ++band)
433  {
434  inBands[band] += SAMPLES;
435  outBands[band] += SAMPLES;
436  }
437  inBands[3] += SAMPLES; // Last band of input.
438 
439  } // End of line loop.
440 
441  // Set the tile status:
442  out->validate();
443 
444  } // Matches sanity check: if ( INPUT_BANDS == 4 )
445 
446  } // Matches: if ( cmykTile.valid() )
447 
448  } // Matches: if ( decodeJpeg( in, cmykTile ) )
449 
450  } // Matches: if ( jpegColorSpace == JCS_CMYK )
451  else
452  {
454  << "ossimJpegCodec::decodeJpegRgb: WARNING: "
455  << "Unhandled jpeg output color space!" << std::endl;
456  }
457 
458  return result;
459 
460 } // End: ossimJpegCodec::decodeJpegRgb( ... )
461 
462 ossim_int32 ossimJpegCodec::getColorSpace( const std::vector<ossim_uint8>& in ) const
463 {
464  J_COLOR_SPACE result = JCS_UNKNOWN;
465 
466  if ( in.size() )
467  {
468  jpeg_decompress_struct cinfo;
469  ossimJpegErrorMgr jerr;
470  cinfo.err = jpeg_std_error(&jerr.pub);
471  jerr.pub.error_exit = ossimJpegErrorExit;
472 
473  /* Establish the setjmp return context for my_error_exit to use. */
474  if (setjmp(jerr.setjmp_buffer) == 0)
475  {
476  jpeg_CreateDecompress(&cinfo, JPEG_LIB_VERSION, sizeof(cinfo));
477  ossimJpegMemorySrc ( &cinfo, &(in.front()), (size_t)(in.size()) );
478  jpeg_read_header(&cinfo, TRUE);
479  result = cinfo.out_color_space;
480  jpeg_destroy_decompress(&cinfo);
481  }
482  }
483  return result;
484 }
485 
487 {
488  if(property->getName() == ossimKeywordNames::QUALITY_KW)
489  {
490  m_quality = property->valueToString().toUInt32();
491  }
492 }
493 
495 {
497 
499  {
502  0,
503  100);
504  }
505  else
506  {
507  result = ossimCodecBase::getProperty(name);
508  }
509  return result;
510 }
511 
512 void ossimJpegCodec::getPropertyNames(std::vector<ossimString>& propertyNames)const
513 {
514  ossimCodecBase::getPropertyNames(propertyNames);
515  propertyNames.push_back(ossimKeywordNames::QUALITY_KW);
516 }
517 
518 bool ossimJpegCodec::loadState(const ossimKeywordlist& kwl, const char* prefix)
519 {
520  const char* quality = kwl.find(prefix, ossimKeywordNames::QUALITY_KW);
521 
522  if(quality)
523  {
524  m_quality = ossimString(quality).toUInt32();
525  }
526 
527  return ossimCodecBase::loadState(kwl, prefix);
528 }
529 
530 bool ossimJpegCodec::saveState(ossimKeywordlist& kwl, const char* prefix)const
531 {
533 
534  return ossimCodecBase::saveState(kwl, prefix);
535 }
virtual ossim_uint32 getWidth() const
std::string m_ext
virtual ossim_uint32 getNumberOfBands() const
std::basic_stringstream< char > stringstream
Class for char mixed input and output memory streams.
Definition: ossimIosFwd.h:38
virtual const ossim_uint8 * getUcharBuf() const
Represents serializable keyword/value map.
ossim_uint32 m_quality
bool valid() const
Definition: ossimRefPtr.h:75
const char * find(const char *key) const
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=0) const
Definition: ossimObject.cpp:95
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
ossim_uint32 height() const
Definition: ossimIrect.h:487
static ossimString toString(bool aValue)
Numeric to string methods.
bool decodeJpegToRgb(const std::vector< ossim_uint8 > &in, ossimRefPtr< ossimImageData > &out) const
For decoding color spaces other that mono and rgb.
virtual ossimDataObjectStatus getDataObjectStatus() const
virtual ossim_uint32 getHeight() const
ossim_uint32 toUInt32() const
void ossimJpegErrorExit(jpeg_common_struct *cinfo)
Error routine that will replace jpeg&#39;s standard error_exit method.
virtual void initialize()
Initialize the data buffer.
struct jpeg_error_mgr pub
void add(const char *prefix, const ossimKeywordlist &kwl, bool overwrite=true)
ossim_int32 getColorSpace(const std::vector< ossim_uint8 > &in) const
OSSIM_DLL void ossimJpegMemorySrc(jpeg_decompress_struct *cinfo, const ossim_uint8 *buffer, std::size_t bufsize)
Method which uses memory instead of a FILE* to read from.
virtual ossimDataObjectStatus validate() const
virtual void setImageRectangleAndBands(const ossimIrect &rect, ossim_uint32 numberOfBands)
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=0) const
Save the state of the codec to the keywordlist.
virtual const std::string & getExtension() const
unsigned int ossim_uint32
virtual ~ossimJpegCodec()
bool decodeJpeg(const std::vector< ossim_uint8 > &in, ossimRefPtr< ossimImageData > &out) const
For decoding standard jpeg block.
virtual ossimIrect getImageRectangle() const
struct ossimJpegErrorMgr * ossimJpegErrorPtr
virtual bool encode(const ossimRefPtr< ossimImageData > &in, std::vector< ossim_uint8 > &out) const
Encode jpeg method.
ossim_uint32 width() const
Definition: ossimIrect.h:500
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
Allocate the state of the object thorugh a factory load/keywordlist.
virtual ossimScalarType getScalarType() const
virtual ossimRefPtr< ossimProperty > getProperty(const ossimString &name) const
Interface to get the value of a specific property.
virtual ossimRefPtr< ossimProperty > getProperty(const ossimString &name) const
virtual void getPropertyNames(std::vector< ossimString > &propertyNames) const
virtual void setProperty(ossimRefPtr< ossimProperty > property)
Ineterface to allow for specific properties to be set.
virtual void getPropertyNames(std::vector< ossimString > &propertyNames) const
Get a list of all supported property names.
virtual ossimString getCodecType() const
virtual bool decode(const std::vector< ossim_uint8 > &in, ossimRefPtr< ossimImageData > &out) const
Decode jpeg method.
8 bit unsigned integer
OSSIM_DLL void jpeg_cpp_stream_dest(jpeg_compress_struct *cinfo, std::ostream &stream)
Method which uses memory instead of a FILE* to write to.
static const char * QUALITY_KW
unsigned char ossim_uint8
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
const ossimString & getName() const
int ossim_int32