[guilib] fix labelcontrols with auto width always being marked as dirty if they speci...
[vuplus_xbmc] / xbmc / guilib / JpegIO.cpp
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 #include "utils/StringUtils.h"
31 #include <setjmp.h>
32
33 #define EXIF_TAG_ORIENTATION    0x0112
34
35 struct 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
48 static void x_init_mem_source (j_decompress_ptr cinfo)
49 {
50   /* no work necessary here */
51 }
52
53 void x_term_source (j_decompress_ptr cinfo)
54 {
55   /* no work necessary here */
56 }
57
58 static 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
76 static 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
97 static 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
128 typedef 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
138 typedef x_mem_destination_mgr * x_mem_dest_ptr;
139
140 static void x_init_mem_destination (j_compress_ptr cinfo)
141 {
142   /* no work necessary here */
143 }
144
145 static 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
178 static 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
186 static 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
231 CJpegIO::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
243 CJpegIO::~CJpegIO()
244 {
245   Close();
246 }
247
248 void CJpegIO::Close()
249 {
250   free(m_inputBuff);
251   m_inputBuff = NULL;
252   m_inputBuffSize = 0;
253   ReleaseThumbnailBuffer();
254 }
255
256 bool 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
327 bool 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 (unsigned int scale = 1; scale <= 8; scale++)
378     {
379       m_cinfo.scale_num = scale;
380       jpeg_calc_output_dimensions(&m_cinfo);
381       if ((m_cinfo.output_width > maxtexsize) || (m_cinfo.output_height > maxtexsize))
382       {
383         m_cinfo.scale_num--;
384         break;
385       }
386       if (m_cinfo.output_width >= minx && m_cinfo.output_height >= miny)
387         break;
388     }
389     jpeg_calc_output_dimensions(&m_cinfo);
390     m_width  = m_cinfo.output_width;
391     m_height = m_cinfo.output_height;
392
393     if (m_cinfo.marker_list)
394       m_orientation = GetExifOrientation(m_cinfo.marker_list->data, m_cinfo.marker_list->data_length);
395     return true;
396   }
397 }
398
399 bool CJpegIO::Decode(const unsigned char *pixels, unsigned int pitch, unsigned int format)
400 {
401   unsigned char *dst = (unsigned char*)pixels;
402
403   struct my_error_mgr jerr;
404   m_cinfo.err = jpeg_std_error(&jerr.pub);
405   jerr.pub.error_exit = jpeg_error_exit;
406
407   if (setjmp(jerr.setjmp_buffer))
408   {
409     jpeg_destroy_decompress(&m_cinfo);
410     return false;
411   }
412   else
413   {
414     jpeg_start_decompress(&m_cinfo);
415
416     if (format == XB_FMT_RGB8)
417     {
418       while (m_cinfo.output_scanline < m_height)
419       {
420         jpeg_read_scanlines(&m_cinfo, &dst, 1);
421         dst += pitch;
422       }
423     }
424     else if (format == XB_FMT_A8R8G8B8)
425     {
426       unsigned char* row = new unsigned char[m_width * 3];
427       while (m_cinfo.output_scanline < m_height)
428       {
429         jpeg_read_scanlines(&m_cinfo, &row, 1);
430         unsigned char *src2 = row;
431         unsigned char *dst2 = dst;
432         for (unsigned int x = 0; x < m_width; x++, src2 += 3)
433         {
434           *dst2++ = src2[2];
435           *dst2++ = src2[1];
436           *dst2++ = src2[0];
437           *dst2++ = 0xff;
438         }
439         dst += pitch;
440       }
441       delete[] row;
442     }
443     else
444     {
445       CLog::Log(LOGWARNING, "JpegIO: Incorrect output format specified");
446       jpeg_destroy_decompress(&m_cinfo);
447       return false;
448     }
449     jpeg_finish_decompress(&m_cinfo);
450   }
451   jpeg_destroy_decompress(&m_cinfo);
452   return true;
453 }
454
455 bool CJpegIO::CreateThumbnail(const CStdString& sourceFile, const CStdString& destFile, int minx, int miny, bool rotateExif)
456 {
457   //Copy sourceFile to buffer, pass to CreateThumbnailFromMemory for decode+re-encode
458   if (!Open(sourceFile, minx, miny, false))
459     return false;
460
461   return CreateThumbnailFromMemory(m_inputBuff, m_inputBuffSize, destFile, minx, miny);
462 }
463
464 bool CJpegIO::CreateThumbnailFromMemory(unsigned char* buffer, unsigned int bufSize, const CStdString& destFile, unsigned int minx, unsigned int miny)
465 {
466   //Decode a jpeg residing in buffer, pass to CreateThumbnailFromSurface for re-encode
467   unsigned int pitch = 0;
468   unsigned char *sourceBuf = NULL;
469
470   if (!Read(buffer, bufSize, minx, miny))
471     return false;
472   pitch = Width() * 3;
473   sourceBuf = new unsigned char [Height() * pitch];
474
475   if (!Decode(sourceBuf,pitch,XB_FMT_RGB8))
476   {
477     delete [] sourceBuf;
478     return false;
479   }
480   if (!CreateThumbnailFromSurface(sourceBuf, Width(), Height() , XB_FMT_RGB8, pitch, destFile))
481   {
482     delete [] sourceBuf;
483     return false;
484   }
485   delete [] sourceBuf;
486   return true;
487 }
488
489 bool CJpegIO::CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height, unsigned int format, unsigned int pitch, const CStdString& destFile)
490 {
491   //Encode raw data from buffer, save to destFile
492   struct jpeg_compress_struct cinfo;
493   struct my_error_mgr jerr;
494   JSAMPROW row_pointer[1];
495   long unsigned int outBufSize = width * height;
496   unsigned char* result;
497   unsigned char* src = buffer;
498   unsigned char* rgbbuf;
499
500   if(buffer == NULL)
501   {
502     CLog::Log(LOGERROR, "JpegIO::CreateThumbnailFromSurface no buffer");
503     return false;
504   }
505
506   result = (unsigned char*) malloc(outBufSize); //Initial buffer. Grows as-needed.
507   if (result == NULL)
508   {
509     CLog::Log(LOGERROR, "JpegIO::CreateThumbnailFromSurface error allocating memory for image buffer");
510     return false;
511   }
512
513   if(format == XB_FMT_RGB8)
514   {
515     rgbbuf = buffer;
516   }
517   else if(format == XB_FMT_A8R8G8B8)
518   {
519     // create a copy for bgra -> rgb.
520     rgbbuf = new unsigned char [(width * height * 3)];
521     unsigned char* dst = rgbbuf;
522     for (unsigned int y = 0; y < height; y++)
523     {
524       unsigned char* dst2 = dst;
525       unsigned char* src2 = src;
526       for (unsigned int x = 0; x < width; x++, src2 += 4)
527       {
528         *dst2++ = src2[2];
529         *dst2++ = src2[1];
530         *dst2++ = src2[0];
531       }
532       dst += width * 3;
533       src += pitch;
534     }
535   }
536   else
537   {
538     CLog::Log(LOGWARNING, "JpegIO::CreateThumbnailFromSurface Unsupported format");
539     free(result);
540     return false;
541   }
542
543   cinfo.err = jpeg_std_error(&jerr.pub);
544   jerr.pub.error_exit = jpeg_error_exit;
545   jpeg_create_compress(&cinfo);
546
547   if (setjmp(jerr.setjmp_buffer))
548   {
549     jpeg_destroy_compress(&cinfo);
550     free(result);
551     if(format != XB_FMT_RGB8)
552       delete [] rgbbuf;
553     return false;
554   }
555   else
556   {
557 #if JPEG_LIB_VERSION < 80
558     x_jpeg_mem_dest(&cinfo, &result, &outBufSize);
559 #else
560     jpeg_mem_dest(&cinfo, &result, &outBufSize);
561 #endif
562     cinfo.image_width = width;
563     cinfo.image_height = height;
564     cinfo.input_components = 3;
565     cinfo.in_color_space = JCS_RGB;
566     jpeg_set_defaults(&cinfo);
567     jpeg_set_quality(&cinfo, 90, TRUE);
568     jpeg_start_compress(&cinfo, TRUE);
569
570     while (cinfo.next_scanline < cinfo.image_height)
571     {
572       row_pointer[0] = &rgbbuf[cinfo.next_scanline * width * 3];
573       jpeg_write_scanlines(&cinfo, row_pointer, 1);
574     }
575
576     jpeg_finish_compress(&cinfo);
577     jpeg_destroy_compress(&cinfo);
578   }
579   if(format != XB_FMT_RGB8)
580     delete [] rgbbuf;
581
582   XFILE::CFile file;
583   if (file.OpenForWrite(destFile, true))
584   {
585     file.Write(result, outBufSize);
586     file.Close();
587     free(result);
588     return true;
589   }
590   free(result);
591   return false;
592 }
593
594 // override libjpeg's error function to avoid an exit() call
595 void CJpegIO::jpeg_error_exit(j_common_ptr cinfo)
596 {
597   CStdString msg = StringUtils::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
604 unsigned 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
732 bool CJpegIO::LoadImageFromMemory(unsigned char* buffer, unsigned int bufSize, unsigned int width, unsigned int height)
733 {
734   return Read(buffer, bufSize, width, height);
735 }
736
737 bool 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
837 void CJpegIO::ReleaseThumbnailBuffer()
838 {
839   if(m_thumbnailbuffer != NULL)
840   {
841     free(m_thumbnailbuffer);
842     m_thumbnailbuffer = NULL;
843   }
844 }