PageRenderTime 83ms CodeModel.GetById 3ms app.highlight 74ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/guilib/JpegIO.cpp

http://github.com/xbmc/xbmc
C++ | 844 lines | 650 code | 104 blank | 90 comment | 147 complexity | ac909f4ff357a0a61ee4c60aa1e0ebfb MD5 | raw file
  1/*
  2*      Copyright (C) 2005-2013 Team XBMC
  3 *      http://xbmc.org
  4 *
  5 *  This Program is free software; you can redistribute it and/or modify
  6 *  it under the terms of the GNU General Public License as published by
  7 *  the Free Software Foundation; either version 2, or (at your option)
  8 *  any later version.
  9 *
 10 *  This Program is distributed in the hope that it will be useful,
 11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13 *  GNU General Public License for more details.
 14 *
 15 *  You should have received a copy of the GNU General Public License
 16 *  along with XBMC; see the file COPYING.  If not, see
 17 *  <http://www.gnu.org/licenses/>.
 18 *
 19 *  Parts of this code taken from Guido Vollbeding <http://sylvana.net/jpegcrop/exif_orientation.html>
 20 *
 21*/
 22
 23#include "lib/libexif/libexif.h"
 24#include "windowing/WindowingFactory.h"
 25#include "settings/AdvancedSettings.h"
 26#include "filesystem/File.h"
 27#include "utils/log.h"
 28#include "XBTF.h"
 29#include "JpegIO.h"
 30
 31#include <setjmp.h>
 32
 33#define EXIF_TAG_ORIENTATION    0x0112
 34
 35struct my_error_mgr
 36{
 37  struct jpeg_error_mgr pub;    // "public" fields
 38  jmp_buf setjmp_buffer;        // for return to caller
 39};
 40
 41#if JPEG_LIB_VERSION < 80
 42
 43/*Versions of libjpeg prior to 8.0 did not have a pre-made mechanism for
 44  decoding directly from memory. Here we backport the functions from v8.
 45  When using v8 or higher, the built-in functions are used instead.
 46  Original formatting left intact for the most part for easy comparison. */
 47
 48static void x_init_mem_source (j_decompress_ptr cinfo)
 49{
 50  /* no work necessary here */
 51}
 52
 53void x_term_source (j_decompress_ptr cinfo)
 54{
 55  /* no work necessary here */
 56}
 57
 58static boolean x_fill_mem_input_buffer (j_decompress_ptr cinfo)
 59{
 60  static JOCTET mybuffer[4];
 61
 62  /* The whole JPEG data is expected to reside in the supplied memory
 63   * buffer, so any request for more data beyond the given buffer size
 64   * is treated as an error.
 65   */
 66  /* Insert a fake EOI marker */
 67  mybuffer[0] = (JOCTET) 0xFF;
 68  mybuffer[1] = (JOCTET) JPEG_EOI;
 69
 70  cinfo->src->next_input_byte = mybuffer;
 71  cinfo->src->bytes_in_buffer = 2;
 72
 73  return true;
 74}
 75
 76static void x_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
 77{
 78  struct jpeg_source_mgr * src = cinfo->src;
 79
 80  /* Just a dumb implementation for now.  Could use fseek() except
 81   * it doesn't work on pipes.  Not clear that being smart is worth
 82   * any trouble anyway --- large skips are infrequent.
 83   */
 84  if (num_bytes > 0) {
 85    while (num_bytes > (long) src->bytes_in_buffer) {
 86      num_bytes -= (long) src->bytes_in_buffer;
 87      (void) (*src->fill_input_buffer) (cinfo);
 88      /* note we assume that fill_input_buffer will never return FALSE,
 89       * so suspension need not be handled.
 90       */
 91    }
 92    src->next_input_byte += (size_t) num_bytes;
 93    src->bytes_in_buffer -= (size_t) num_bytes;
 94  }
 95}
 96
 97static void x_mem_src (j_decompress_ptr cinfo, unsigned char * inbuffer, unsigned long insize)
 98{
 99  struct jpeg_source_mgr * src;
100
101  if (inbuffer == NULL || insize == 0)	/* Treat empty input as fatal error */
102  {
103    (cinfo)->err->msg_code = 0;
104    (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo));
105  }
106
107  /* The source object is made permanent so that a series of JPEG images
108   * can be read from the same buffer by calling jpeg_mem_src only before
109   * the first one.
110   */
111  if (cinfo->src == NULL) {	/* first time for this JPEG object? */
112    cinfo->src = (struct jpeg_source_mgr *)
113      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
114				  sizeof(struct jpeg_source_mgr));
115  }
116
117  src = cinfo->src;
118  src->init_source = x_init_mem_source;
119  src->fill_input_buffer = x_fill_mem_input_buffer;
120  src->skip_input_data = x_skip_input_data;
121  src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
122  src->term_source = x_term_source;
123  src->bytes_in_buffer = (size_t) insize;
124  src->next_input_byte = (JOCTET *) inbuffer;
125}
126
127#define OUTPUT_BUF_SIZE  4096
128typedef struct {
129  struct jpeg_destination_mgr pub; /* public fields */
130
131  unsigned char ** outbuffer;   /* target buffer */
132  unsigned long * outsize;
133  unsigned char * newbuffer;    /* newly allocated buffer */
134  JOCTET * buffer;              /* start of buffer */
135  size_t bufsize;
136} x_mem_destination_mgr;
137
138typedef x_mem_destination_mgr * x_mem_dest_ptr;
139
140static void x_init_mem_destination (j_compress_ptr cinfo)
141{
142  /* no work necessary here */
143}
144
145static boolean x_empty_mem_output_buffer (j_compress_ptr cinfo)
146{
147  size_t nextsize;
148  JOCTET * nextbuffer;
149  x_mem_dest_ptr dest = (x_mem_dest_ptr) cinfo->dest;
150
151  /* Try to allocate new buffer with double size */
152  nextsize = dest->bufsize * 2;
153  nextbuffer = (JOCTET*) malloc(nextsize);
154
155  if (nextbuffer == NULL)
156  {
157    (cinfo)->err->msg_code = 0;
158    (cinfo)->err->msg_parm.i[0] = 10;
159    (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo));
160  }
161
162  memcpy(nextbuffer, dest->buffer, dest->bufsize);
163
164  if (dest->newbuffer != NULL)
165    free(dest->newbuffer);
166
167  dest->newbuffer = nextbuffer;
168
169  dest->pub.next_output_byte = nextbuffer + dest->bufsize;
170  dest->pub.free_in_buffer = dest->bufsize;
171
172  dest->buffer = nextbuffer;
173  dest->bufsize = nextsize;
174
175  return TRUE;
176}
177
178static void x_term_mem_destination (j_compress_ptr cinfo)
179{
180  x_mem_dest_ptr dest = (x_mem_dest_ptr) cinfo->dest;
181
182  *dest->outbuffer = dest->buffer;
183  *dest->outsize = dest->bufsize - dest->pub.free_in_buffer;
184}
185
186static void x_jpeg_mem_dest (j_compress_ptr cinfo,
187               unsigned char ** outbuffer, unsigned long * outsize)
188{
189  x_mem_dest_ptr dest;
190
191  if (outbuffer == NULL || outsize == NULL)     /* sanity check */
192  {
193    (cinfo)->err->msg_code = 0;
194    (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo));
195  }
196
197  /* The destination object is made permanent so that multiple JPEG images
198   * can be written to the same buffer without re-executing jpeg_mem_dest.
199   */
200  if (cinfo->dest == NULL) {    /* first time for this JPEG object? */
201    cinfo->dest = (struct jpeg_destination_mgr *)
202      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
203                                  sizeof(x_mem_destination_mgr));
204  }
205
206  dest = (x_mem_dest_ptr) cinfo->dest;
207  dest->pub.init_destination = x_init_mem_destination;
208  dest->pub.empty_output_buffer = x_empty_mem_output_buffer;
209  dest->pub.term_destination = x_term_mem_destination;
210  dest->outbuffer = outbuffer;
211  dest->outsize = outsize;
212  dest->newbuffer = NULL;
213
214  if (*outbuffer == NULL || *outsize == 0) {
215    /* Allocate initial buffer */
216    dest->newbuffer = *outbuffer = (unsigned char*)malloc(OUTPUT_BUF_SIZE);
217    if (dest->newbuffer == NULL)
218    {
219      (cinfo)->err->msg_code = 0;
220      (cinfo)->err->msg_parm.i[0] = 10;
221      (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo));
222    }
223    *outsize = OUTPUT_BUF_SIZE;
224  }
225
226  dest->pub.next_output_byte = dest->buffer = *outbuffer;
227  dest->pub.free_in_buffer = dest->bufsize = *outsize;
228}
229#endif
230
231CJpegIO::CJpegIO()
232{
233  m_width  = 0;
234  m_height = 0;
235  m_orientation = 0;
236  m_inputBuffSize = 0;
237  m_inputBuff = NULL;
238  m_texturePath = "";
239  memset(&m_cinfo, 0, sizeof(m_cinfo));
240  m_thumbnailbuffer = NULL;
241}
242
243CJpegIO::~CJpegIO()
244{
245  Close();
246}
247
248void CJpegIO::Close()
249{
250  free(m_inputBuff);
251  m_inputBuff = NULL;
252  m_inputBuffSize = 0;
253  ReleaseThumbnailBuffer();
254}
255
256bool CJpegIO::Open(const CStdString &texturePath, unsigned int minx, unsigned int miny, bool read)
257{
258  Close();
259
260  m_texturePath = texturePath;
261
262  XFILE::CFile file;
263  if (file.Open(m_texturePath.c_str(), READ_TRUNCATED))
264  {
265    /*
266     GetLength() will typically return values that fall into three cases:
267       1. The real filesize. This is the typical case.
268       2. Zero. This is the case for some http:// streams for example.
269       3. Some value smaller than the real filesize. This is the case for an expanding file.
270
271     In order to handle all three cases, we read the file in chunks, relying on Read()
272     returning 0 at EOF.  To minimize (re)allocation of the buffer, the chunksize in
273     cases 1 and 3 is set to one byte larger** than the value returned by GetLength().
274     The chunksize in case 2 is set to the larger of 64k and GetChunkSize().
275
276     We fill the buffer entirely before reallocation.  Thus, reallocation never occurs in case 1
277     as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
278
279     To minimize reallocation, we double the chunksize each time up to a maxchunksize of 2MB.
280     */
281    unsigned int filesize = (unsigned int)file.GetLength();
282    unsigned int chunksize = filesize ? (filesize + 1) : std::max(65536U, (unsigned int)file.GetChunkSize());
283    unsigned int maxchunksize = 2048*1024U; /* max 2MB chunksize */
284
285    unsigned int total_read = 0, free_space = 0;
286    while (true)
287    {
288      if (!free_space)
289      { // (re)alloc
290        m_inputBuffSize += chunksize;
291        unsigned char* new_buf = (unsigned char *)realloc(m_inputBuff, m_inputBuffSize);
292        if (!new_buf)
293        {
294          CLog::Log(LOGERROR, "%s unable to allocate buffer of size %u", __FUNCTION__, m_inputBuffSize);
295          free(m_inputBuff);
296          return false;
297        }
298        else
299          m_inputBuff = new_buf;
300
301        free_space = chunksize;
302        chunksize = std::min(chunksize*2, maxchunksize);
303      }
304      unsigned int read = file.Read(m_inputBuff + total_read, free_space);
305      free_space -= read;
306      total_read += read;
307      if (!read)
308        break;
309    }
310    m_inputBuffSize = total_read;
311    file.Close();
312
313    if (m_inputBuffSize == 0)
314      return false;
315  }
316  else
317    return false;
318
319  if (!read)
320    return true;
321
322  if (Read(m_inputBuff, m_inputBuffSize, minx, miny))
323    return true;
324  return false;
325}
326
327bool CJpegIO::Read(unsigned char* buffer, unsigned int bufSize, unsigned int minx, unsigned int miny)
328{
329  struct my_error_mgr jerr;
330  m_cinfo.err = jpeg_std_error(&jerr.pub);
331  jerr.pub.error_exit = jpeg_error_exit;
332
333  if (buffer == NULL || !bufSize )
334    return false;
335
336  jpeg_create_decompress(&m_cinfo);
337#if JPEG_LIB_VERSION < 80
338  x_mem_src(&m_cinfo, buffer, bufSize);
339#else
340  jpeg_mem_src(&m_cinfo, buffer, bufSize);
341#endif
342
343  if (setjmp(jerr.setjmp_buffer))
344  {
345    jpeg_destroy_decompress(&m_cinfo);
346    return false;
347  }
348  else
349  {
350    jpeg_save_markers (&m_cinfo, JPEG_APP0 + 1, 0xFFFF);
351    jpeg_read_header(&m_cinfo, true);
352
353    /*  libjpeg can scale the image for us if it is too big. It must be in the format
354    num/denom, where (for our purposes) that is [1-8]/8 where 8/8 is the unscaled image.
355    The only way to know how big a resulting image will be is to try a ratio and
356    test its resulting size.
357    If the res is greater than the one desired, use that one since there's no need
358    to decode a bigger one just to squish it back down. If the res is greater than
359    the gpu can hold, use the previous one.*/
360    if (minx == 0 || miny == 0)
361    {
362      miny = g_advancedSettings.m_imageRes;
363      if (g_advancedSettings.m_fanartRes > g_advancedSettings.m_imageRes)
364      { // a separate fanart resolution is specified - check if the image is exactly equal to this res
365        if (m_cinfo.image_width == (unsigned int)g_advancedSettings.m_fanartRes * 16/9 &&
366            m_cinfo.image_height == (unsigned int)g_advancedSettings.m_fanartRes)
367        { // special case for fanart res
368          miny = g_advancedSettings.m_fanartRes;
369        }
370      }
371      minx = miny * 16/9;
372    }
373
374    m_cinfo.scale_denom = 8;
375    m_cinfo.out_color_space = JCS_RGB;
376    unsigned int maxtexsize = g_Windowing.GetMaxTextureSize();
377    for (m_cinfo.scale_num = 1; m_cinfo.scale_num <= 8; m_cinfo.scale_num++)
378    {
379      jpeg_calc_output_dimensions(&m_cinfo);
380      if ((m_cinfo.output_width > maxtexsize) || (m_cinfo.output_height > maxtexsize))
381      {
382        m_cinfo.scale_num--;
383        break;
384      }
385      if (m_cinfo.output_width >= minx && m_cinfo.output_height >= miny)
386        break;
387    }
388    jpeg_calc_output_dimensions(&m_cinfo);
389    m_width  = m_cinfo.output_width;
390    m_height = m_cinfo.output_height;
391
392    if (m_cinfo.marker_list)
393      m_orientation = GetExifOrientation(m_cinfo.marker_list->data, m_cinfo.marker_list->data_length);
394    return true;
395  }
396}
397
398bool CJpegIO::Decode(const unsigned char *pixels, unsigned int pitch, unsigned int format)
399{
400  unsigned char *dst = (unsigned char*)pixels;
401
402  struct my_error_mgr jerr;
403  m_cinfo.err = jpeg_std_error(&jerr.pub);
404  jerr.pub.error_exit = jpeg_error_exit;
405
406  if (setjmp(jerr.setjmp_buffer))
407  {
408    jpeg_destroy_decompress(&m_cinfo);
409    return false;
410  }
411  else
412  {
413    jpeg_start_decompress(&m_cinfo);
414
415    if (format == XB_FMT_RGB8)
416    {
417      while (m_cinfo.output_scanline < m_height)
418      {
419        jpeg_read_scanlines(&m_cinfo, &dst, 1);
420        dst += pitch;
421      }
422    }
423    else if (format == XB_FMT_A8R8G8B8)
424    {
425      unsigned char* row = new unsigned char[m_width * 3];
426      while (m_cinfo.output_scanline < m_height)
427      {
428        jpeg_read_scanlines(&m_cinfo, &row, 1);
429        unsigned char *src2 = row;
430        unsigned char *dst2 = dst;
431        for (unsigned int x = 0; x < m_width; x++, src2 += 3)
432        {
433          *dst2++ = src2[2];
434          *dst2++ = src2[1];
435          *dst2++ = src2[0];
436          *dst2++ = 0xff;
437        }
438        dst += pitch;
439      }
440      delete[] row;
441    }
442    else
443    {
444      CLog::Log(LOGWARNING, "JpegIO: Incorrect output format specified");
445      jpeg_destroy_decompress(&m_cinfo);
446      return false;
447    }
448    jpeg_finish_decompress(&m_cinfo);
449  }
450  jpeg_destroy_decompress(&m_cinfo);
451  return true;
452}
453
454bool CJpegIO::CreateThumbnail(const CStdString& sourceFile, const CStdString& destFile, int minx, int miny, bool rotateExif)
455{
456  //Copy sourceFile to buffer, pass to CreateThumbnailFromMemory for decode+re-encode
457  if (!Open(sourceFile, minx, miny, false))
458    return false;
459
460  return CreateThumbnailFromMemory(m_inputBuff, m_inputBuffSize, destFile, minx, miny);
461}
462
463bool CJpegIO::CreateThumbnailFromMemory(unsigned char* buffer, unsigned int bufSize, const CStdString& destFile, unsigned int minx, unsigned int miny)
464{
465  //Decode a jpeg residing in buffer, pass to CreateThumbnailFromSurface for re-encode
466  unsigned int pitch = 0;
467  unsigned char *sourceBuf = NULL;
468
469  if (!Read(buffer, bufSize, minx, miny))
470    return false;
471  pitch = Width() * 3;
472  sourceBuf = new unsigned char [Height() * pitch];
473
474  if (!Decode(sourceBuf,pitch,XB_FMT_RGB8))
475  {
476    delete [] sourceBuf;
477    return false;
478  }
479  if (!CreateThumbnailFromSurface(sourceBuf, Width(), Height() , XB_FMT_RGB8, pitch, destFile))
480  {
481    delete [] sourceBuf;
482    return false;
483  }
484  delete [] sourceBuf;
485  return true;
486}
487
488bool CJpegIO::CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height, unsigned int format, unsigned int pitch, const CStdString& destFile)
489{
490  //Encode raw data from buffer, save to destFile
491  struct jpeg_compress_struct cinfo;
492  struct my_error_mgr jerr;
493  JSAMPROW row_pointer[1];
494  long unsigned int outBufSize = width * height;
495  unsigned char* result;
496  unsigned char* src = buffer;
497  unsigned char* rgbbuf;
498
499  if(buffer == NULL)
500  {
501    CLog::Log(LOGERROR, "JpegIO::CreateThumbnailFromSurface no buffer");
502    return false;
503  }
504
505  result = (unsigned char*) malloc(outBufSize); //Initial buffer. Grows as-needed.
506  if (result == NULL)
507  {
508    CLog::Log(LOGERROR, "JpegIO::CreateThumbnailFromSurface error allocating memory for image buffer");
509    return false;
510  }
511
512  if(format == XB_FMT_RGB8)
513  {
514    rgbbuf = buffer;
515  }
516  else if(format == XB_FMT_A8R8G8B8)
517  {
518    // create a copy for bgra -> rgb.
519    rgbbuf = new unsigned char [(width * height * 3)];
520    unsigned char* dst = rgbbuf;
521    for (unsigned int y = 0; y < height; y++)
522    {
523      unsigned char* dst2 = dst;
524      unsigned char* src2 = src;
525      for (unsigned int x = 0; x < width; x++, src2 += 4)
526      {
527        *dst2++ = src2[2];
528        *dst2++ = src2[1];
529        *dst2++ = src2[0];
530      }
531      dst += width * 3;
532      src += pitch;
533    }
534  }
535  else
536  {
537    CLog::Log(LOGWARNING, "JpegIO::CreateThumbnailFromSurface Unsupported format");
538    free(result);
539    return false;
540  }
541
542  cinfo.err = jpeg_std_error(&jerr.pub);
543  jerr.pub.error_exit = jpeg_error_exit;
544  jpeg_create_compress(&cinfo);
545
546  if (setjmp(jerr.setjmp_buffer))
547  {
548    jpeg_destroy_compress(&cinfo);
549    free(result);
550    if(format != XB_FMT_RGB8)
551      delete [] rgbbuf;
552    return false;
553  }
554  else
555  {
556#if JPEG_LIB_VERSION < 80
557    x_jpeg_mem_dest(&cinfo, &result, &outBufSize);
558#else
559    jpeg_mem_dest(&cinfo, &result, &outBufSize);
560#endif
561    cinfo.image_width = width;
562    cinfo.image_height = height;
563    cinfo.input_components = 3;
564    cinfo.in_color_space = JCS_RGB;
565    jpeg_set_defaults(&cinfo);
566    jpeg_set_quality(&cinfo, 90, TRUE);
567    jpeg_start_compress(&cinfo, TRUE);
568
569    while (cinfo.next_scanline < cinfo.image_height)
570    {
571      row_pointer[0] = &rgbbuf[cinfo.next_scanline * width * 3];
572      jpeg_write_scanlines(&cinfo, row_pointer, 1);
573    }
574
575    jpeg_finish_compress(&cinfo);
576    jpeg_destroy_compress(&cinfo);
577  }
578  if(format != XB_FMT_RGB8)
579    delete [] rgbbuf;
580
581  XFILE::CFile file;
582  if (file.OpenForWrite(destFile, true))
583  {
584    file.Write(result, outBufSize);
585    file.Close();
586    free(result);
587    return true;
588  }
589  free(result);
590  return false;
591}
592
593// override libjpeg's error function to avoid an exit() call
594void CJpegIO::jpeg_error_exit(j_common_ptr cinfo)
595{
596  CStdString msg;
597  msg.Format("Error %i: %s",cinfo->err->msg_code, cinfo->err->jpeg_message_table[cinfo->err->msg_code]);
598  CLog::Log(LOGWARNING, "JpegIO: %s", msg.c_str());
599
600  my_error_mgr *myerr = (my_error_mgr*)cinfo->err;
601  longjmp(myerr->setjmp_buffer, 1);
602}
603
604unsigned int CJpegIO::GetExifOrientation(unsigned char* exif_data, unsigned int exif_data_size)
605{
606  unsigned int offset = 0;
607  unsigned int numberOfTags = 0;
608  unsigned int tagNumber = 0;
609  bool isMotorola = false;
610  unsigned const char ExifHeader[] = "Exif\0\0";
611  unsigned int orientation = 0;
612
613  // read exif head, check for "Exif"
614  //   next we want to read to current offset + length
615  //   check if buffer is big enough
616  if (exif_data_size && memcmp(exif_data, ExifHeader, 6) == 0)
617  {
618    //read exif body
619    exif_data += 6;
620  }
621  else
622  {
623    return 0;
624  }
625
626  // Discover byte order
627  if (exif_data[0] == 'I' && exif_data[1] == 'I')
628    isMotorola = false;
629  else if (exif_data[0] == 'M' && exif_data[1] == 'M')
630    isMotorola = true;
631  else
632    return 0;
633
634  // Check Tag Mark
635  if (isMotorola)
636  {
637    if (exif_data[2] != 0 || exif_data[3] != 0x2A)
638      return 0;
639  }
640  else
641  {
642    if (exif_data[3] != 0 || exif_data[2] != 0x2A)
643      return 0;
644  }
645
646  // Get first IFD offset (offset to IFD0)
647  if (isMotorola)
648  {
649    if (exif_data[4] != 0 || exif_data[5] != 0)
650      return 0;
651    offset = exif_data[6];
652    offset <<= 8;
653    offset += exif_data[7];
654  }
655  else
656  {
657    if (exif_data[7] != 0 || exif_data[6] != 0)
658      return 0;
659    offset = exif_data[5];
660    offset <<= 8;
661    offset += exif_data[4];
662  }
663
664  if (offset > exif_data_size - 2)
665    return 0; // check end of data segment
666
667  // Get the number of directory entries contained in this IFD
668  if (isMotorola)
669  {
670    numberOfTags = exif_data[offset];
671    numberOfTags <<= 8;
672    numberOfTags += exif_data[offset+1];
673  }
674  else
675  {
676    numberOfTags = exif_data[offset+1];
677    numberOfTags <<= 8;
678    numberOfTags += exif_data[offset];
679  }
680
681  if (numberOfTags == 0)
682    return 0;
683  offset += 2;
684
685  // Search for Orientation Tag in IFD0 - hey almost there! :D
686  while(1)//hopefully this jpeg has correct exif data...
687  {
688    if (offset > exif_data_size - 12)
689      return 0; // check end of data segment
690
691    // Get Tag number
692    if (isMotorola)
693    {
694      tagNumber = exif_data[offset];
695      tagNumber <<= 8;
696      tagNumber += exif_data[offset+1];
697    }
698    else
699    {
700      tagNumber = exif_data[offset+1];
701      tagNumber <<= 8;
702      tagNumber += exif_data[offset];
703    }
704
705    if (tagNumber == EXIF_TAG_ORIENTATION)
706      break; //found orientation tag
707
708    if ( --numberOfTags == 0)
709      return 0;//no orientation found
710    offset += 12;//jump to next tag
711  }
712
713  // Get the Orientation value
714  if (isMotorola)
715  {
716    if (exif_data[offset+8] != 0)
717      return 0;
718    orientation = exif_data[offset+9];
719  }
720  else
721  {
722    if (exif_data[offset+9] != 0)
723      return 0;
724    orientation = exif_data[offset+8];
725  }
726  if (orientation > 8)
727    orientation = 0;
728
729  return orientation;//done
730}
731
732bool CJpegIO::LoadImageFromMemory(unsigned char* buffer, unsigned int bufSize, unsigned int width, unsigned int height)
733{
734  return Read(buffer, bufSize, width, height);
735}
736
737bool CJpegIO::CreateThumbnailFromSurface(unsigned char* bufferin, unsigned int width, unsigned int height, unsigned int format, unsigned int pitch, const CStdString& destFile, 
738                                         unsigned char* &bufferout, unsigned int &bufferoutSize)
739{
740  //Encode raw data from buffer, save to destbuffer
741  struct jpeg_compress_struct cinfo;
742  struct my_error_mgr jerr;
743  JSAMPROW row_pointer[1];
744  long unsigned int outBufSize = width * height;
745  unsigned char* src = bufferin;
746  unsigned char* rgbbuf;
747
748  if(bufferin == NULL)
749  {
750    CLog::Log(LOGERROR, "JpegIO::CreateThumbnailFromSurface no buffer");
751    return false;
752  }
753
754  m_thumbnailbuffer = (unsigned char*) malloc(outBufSize); //Initial buffer. Grows as-needed.
755  if (m_thumbnailbuffer == NULL)
756  {
757    CLog::Log(LOGERROR, "JpegIO::CreateThumbnailFromSurface error allocating memory for image buffer");
758    return false;
759  }
760
761  if(format == XB_FMT_RGB8)
762  {
763    rgbbuf = bufferin;
764  }
765  else if(format == XB_FMT_A8R8G8B8)
766  {
767    // create a copy for bgra -> rgb.
768    rgbbuf = new unsigned char [(width * height * 3)];
769    unsigned char* dst = rgbbuf;
770    for (unsigned int y = 0; y < height; y++)
771    {
772
773      unsigned char* dst2 = dst;
774      unsigned char* src2 = src;
775      for (unsigned int x = 0; x < width; x++, src2 += 4)
776      {
777        *dst2++ = src2[2];
778        *dst2++ = src2[1];
779        *dst2++ = src2[0];
780      }
781      dst += width * 3;
782      src += pitch;
783    }
784  }
785  else
786  {
787    CLog::Log(LOGWARNING, "JpegIO::CreateThumbnailFromSurface Unsupported format");
788    free(m_thumbnailbuffer);
789    return false;
790  }
791
792  cinfo.err = jpeg_std_error(&jerr.pub);
793  jerr.pub.error_exit = jpeg_error_exit;
794  jpeg_create_compress(&cinfo);
795
796  if (setjmp(jerr.setjmp_buffer))
797  {
798    jpeg_destroy_compress(&cinfo);
799    free(m_thumbnailbuffer);
800    if(format != XB_FMT_RGB8)
801      delete [] rgbbuf;
802    return false;
803  }
804  else
805  {
806#if JPEG_LIB_VERSION < 80
807    x_jpeg_mem_dest(&cinfo, &m_thumbnailbuffer, &outBufSize);
808#else
809    jpeg_mem_dest(&cinfo, &m_thumbnailbuffer, &outBufSize);
810#endif
811    cinfo.image_width = width;
812    cinfo.image_height = height;
813    cinfo.input_components = 3;
814    cinfo.in_color_space = JCS_RGB;
815    jpeg_set_defaults(&cinfo);
816    jpeg_set_quality(&cinfo, 90, TRUE);
817    jpeg_start_compress(&cinfo, TRUE);
818
819    while (cinfo.next_scanline < cinfo.image_height)
820    {
821      row_pointer[0] = &rgbbuf[cinfo.next_scanline * width * 3];
822      jpeg_write_scanlines(&cinfo, row_pointer, 1);
823    }
824
825    jpeg_finish_compress(&cinfo);
826    jpeg_destroy_compress(&cinfo);
827  }
828  if(format != XB_FMT_RGB8)
829    delete [] rgbbuf;
830
831  bufferout = m_thumbnailbuffer;
832  bufferoutSize = outBufSize;
833
834  return true;
835}
836
837void CJpegIO::ReleaseThumbnailBuffer()
838{
839  if(m_thumbnailbuffer != NULL)
840  {
841    free(m_thumbnailbuffer);
842    m_thumbnailbuffer = NULL;
843  }
844}