]> git.vomp.tv Git - vompclient.git/blob - wjpegcomplex.cc
Preparations for dynamic mode switching
[vompclient.git] / wjpegcomplex.cc
1 /*\r
2     Copyright 2004-2005 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
19 */\r
20 #include "boxx.h"\r
21 #include "wjpegcomplex.h"\r
22 #include <setjmp.h>\r
23 #include <sys/types.h>\r
24 #include <sys/stat.h>\r
25 \r
26 #ifndef WIN32\r
27 #include <unistd.h>\r
28 #else\r
29 \r
30 #endif\r
31 \r
32 #include "i18n.h"\r
33 #include "log.h"\r
34 #include "surface.h"\r
35 \r
36 #ifndef __ANDROID__\r
37 extern "C"\r
38 {\r
39   #include <jpeglib.h>\r
40 }\r
41 #endif\r
42 \r
43 #ifndef __ANDROID__\r
44 //#define USE_BUFFER\r
45 //#define EXTENDED_JPEGLIB\r
46 \r
47 //a struct to store user data for the jpeg decompressor\r
48 class jpegUserData{\r
49   public:\r
50     WJpegComplex::JpegControl * ctl;\r
51     JpegReader *reader;\r
52     jpegUserData() {\r
53       ctl=NULL;\r
54       reader=NULL;\r
55       }\r
56   };\r
57 \r
58 //the scale factors supported by the jpeg lib\r
59 \r
60 struct scale{\r
61   UINT num;\r
62   UINT denom;\r
63 };\r
64 \r
65 \r
66 struct scale jpegFactors[]={\r
67 #ifndef EXTENDED_JPEGLIB\r
68   {1,1},{1,2},{1,4},{1,8}\r
69 #else\r
70   {1,1},{7,8},{3,4},{5,8},{1,2},{3,8},{1,4},{1,8}\r
71 #endif\r
72 };\r
73 #endif\r
74 \r
75 #ifdef WIN32\r
76 #define LocalReader WindowsResourceJpegReader\r
77 class WindowsResourceJpegReader: public JpegReader {\r
78   public:\r
79   virtual ULONG initRead(const char *filename);\r
80   virtual ULONG readChunk(ULONG offset,ULONG len,char **buf);\r
81   virtual ULONG getSize();\r
82   virtual ~WindowsResourceJpegReader();\r
83 protected:\r
84   HGLOBAL hres;\r
85   LPVOID buffer;\r
86   DWORD size;\r
87 };\r
88 \r
89 ULONG WindowsResourceJpegReader::initRead(const char *filename)\r
90 {\r
91     HRSRC res=FindResource(NULL,filename,RT_RCDATA);\r
92     hres=LoadResource(NULL,res);\r
93     buffer=LockResource(hres);\r
94     size=SizeofResource(NULL,res);\r
95     //CloseHandle(hres);\r
96     return size;\r
97 }\r
98 \r
99  ULONG WindowsResourceJpegReader::readChunk(ULONG offset,ULONG len,char **buf)\r
100 {\r
101     if (offset>size) return 0;\r
102     ULONG toread=min(size-offset,len);\r
103     char* buffy=(char*)malloc(toread);\r
104     memcpy(buffy,((char*)buffer)+offset,toread);\r
105     *buf=buffy;\r
106     return toread;\r
107 }\r
108 \r
109  WindowsResourceJpegReader::~WindowsResourceJpegReader(){\r
110     buffer=NULL;\r
111     size=0;\r
112     FreeResource(hres);\r
113  }\r
114 \r
115  ULONG WindowsResourceJpegReader::getSize(){\r
116    return (ULONG)size;\r
117 }\r
118 #else\r
119 \r
120 #define LocalReader LocalJpegReader\r
121 class LocalJpegReader: public JpegReader {\r
122   public:\r
123   virtual ULONG initRead(const char *filename);\r
124   virtual ULONG readChunk(ULONG offset,ULONG len,char **buf);\r
125   virtual ~LocalJpegReader();\r
126   virtual ULONG getSize();\r
127   LocalJpegReader();\r
128 protected:\r
129   FILE *file;\r
130   ULONG size;\r
131 };\r
132 \r
133 LocalJpegReader::LocalJpegReader(){\r
134   file=NULL;\r
135   size=0;\r
136 }\r
137 \r
138 ULONG LocalJpegReader::initRead(const char *filename)\r
139 {\r
140     if (file) fclose(file);\r
141     size=0;\r
142     file=fopen(filename,"r");\r
143     if (file) {\r
144       struct stat st;\r
145       if (fstat(fileno(file), &st) != 0) return 0;\r
146       size= st.st_size;\r
147       return size;\r
148     }\r
149     Log::getInstance()->log("WJepg", Log::ERR, "localReader unable to open File %s", filename);\r
150     return 0;\r
151 }\r
152 \r
153 ULONG LocalJpegReader::readChunk(ULONG offset,ULONG len,char **buf)\r
154 {\r
155    *buf=NULL;\r
156    ULONG bread=0;\r
157    if (file) {\r
158      ULLONG cpos=ftell(file);\r
159      if (offset != cpos) {\r
160       fseek(file,offset-cpos,SEEK_CUR);\r
161      }\r
162      if (offset != (ULONG)ftell(file)) {\r
163        Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset);\r
164      }\r
165      else {\r
166        *buf=(char *)malloc(len);\r
167        if ( ! (*buf)) {\r
168          Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset);\r
169        }\r
170        else {\r
171          bread=fread(*buf,1,len,file);\r
172        }\r
173      }\r
174    }\r
175    return bread;\r
176 }\r
177 \r
178 LocalJpegReader::~LocalJpegReader(){\r
179   if (file) fclose(file);\r
180   file=NULL;\r
181 }\r
182 ULONG LocalJpegReader::getSize(){\r
183   return size;\r
184 }\r
185 #endif\r
186 \r
187 #ifndef __ANDROID__\r
188 /*----------------------------------------------------------------\r
189   the jpeg lib routines\r
190   ----------------------------------------------------------------\r
191  */\r
192 \r
193 extern "C" {\r
194 \r
195 \r
196 struct my_error_mgr {\r
197   struct jpeg_error_mgr pub;    /* "public" fields */\r
198 \r
199   jmp_buf setjmp_buffer;        /* for return to caller */\r
200 };\r
201 \r
202 typedef struct my_error_mgr * my_error_ptr;\r
203 \r
204 /*\r
205  * Here's the routine that will replace the standard error_exit method:\r
206  */\r
207 \r
208 METHODDEF(void)\r
209 my_error_exit (j_common_ptr cinfo)\r
210 {\r
211   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */\r
212   my_error_ptr myerr = (my_error_ptr) cinfo->err;\r
213 \r
214   /* Always display the message. */\r
215   /* We could postpone this until after returning, if we chose. */\r
216   (*cinfo->err->output_message) (cinfo);\r
217   /* Return control to the setjmp point */\r
218   longjmp(myerr->setjmp_buffer, 1);\r
219 }\r
220 \r
221 ULONG jpeg_call_reader(ULONG offset,ULONG size,char ** buf,void *cb) {\r
222   jpegUserData *user=(jpegUserData *)cb;\r
223   return user->reader->readChunk(offset,size,buf);\r
224 }\r
225 //the memory buffer reader for the jpeg lib\r
226 //taken from jdatasrc.c\r
227 \r
228 #include "jinclude.h"\r
229 #include "jpeglib.h"\r
230 #include "jerror.h"\r
231 \r
232 \r
233 typedef struct {\r
234   struct jpeg_source_mgr pub;   /* public fields */\r
235 \r
236   JOCTET * buffer;              /* start of buffer */\r
237   boolean start_of_file;        /* have we gotten any data yet? */\r
238   void * userdata;              /* used for callback */\r
239   ULONG offset;\r
240 } my_source_mgr;\r
241 \r
242 typedef my_source_mgr * my_src_ptr;\r
243 \r
244 #define INPUT_BUF_SIZE  (64*4096)       /* choose an efficiently fread'able size */\r
245 \r
246 \r
247 /*\r
248  * Initialize source --- called by jpeg_read_header\r
249  * before any data is actually read.\r
250  */\r
251 \r
252 METHODDEF(void)\r
253 linit_source (j_decompress_ptr cinfo)\r
254 {\r
255   my_src_ptr src = (my_src_ptr) cinfo->src;\r
256 \r
257   /* We reset the empty-input-file flag for each image,\r
258    * but we don't clear the input buffer.\r
259    * This is correct behavior for reading a series of images from one source.\r
260    */\r
261   src->start_of_file = TRUE;\r
262   src->offset=0;\r
263 }\r
264 \r
265 \r
266 /*\r
267  * Fill the input buffer --- called whenever buffer is emptied.\r
268  *\r
269  * In typical applications, this should read fresh data into the buffer\r
270  * (ignoring the current state of next_input_byte & bytes_in_buffer),\r
271  * reset the pointer & count to the start of the buffer, and return TRUE\r
272  * indicating that the buffer has been reloaded.  It is not necessary to\r
273  * fill the buffer entirely, only to obtain at least one more byte.\r
274  *\r
275  * There is no such thing as an EOF return.  If the end of the file has been\r
276  * reached, the routine has a choice of ERREXIT() or inserting fake data into\r
277  * the buffer.  In most cases, generating a warning message and inserting a\r
278  * fake EOI marker is the best course of action --- this will allow the\r
279  * decompressor to output however much of the image is there.  However,\r
280  * the resulting error message is misleading if the real problem is an empty\r
281  * input file, so we handle that case specially.\r
282  *\r
283  * In applications that need to be able to suspend compression due to input\r
284  * not being available yet, a FALSE return indicates that no more data can be\r
285  * obtained right now, but more may be forthcoming later.  In this situation,\r
286  * the decompressor will return to its caller (with an indication of the\r
287  * number of scanlines it has read, if any).  The application should resume\r
288  * decompression after it has loaded more data into the input buffer.  Note\r
289  * that there are substantial restrictions on the use of suspension --- see\r
290  * the documentation.\r
291  *\r
292  * When suspending, the decompressor will back up to a convenient restart point\r
293  * (typically the start of the current MCU). next_input_byte & bytes_in_buffer\r
294  * indicate where the restart point will be if the current call returns FALSE.\r
295  * Data beyond this point must be rescanned after resumption, so move it to\r
296  * the front of the buffer rather than discarding it.\r
297  */\r
298 \r
299 METHODDEF(boolean)\r
300 lfill_input_buffer (j_decompress_ptr cinfo)\r
301 {\r
302   my_src_ptr src = (my_src_ptr) cinfo->src;\r
303   size_t nbytes;\r
304   if (src->buffer) free(src->buffer);\r
305   src->buffer=NULL;\r
306   nbytes = jpeg_call_reader(src->offset, INPUT_BUF_SIZE,(char **)&(src->buffer), src->userdata);\r
307 \r
308   if (nbytes <= 0) {\r
309     WARNMS(cinfo, JWRN_JPEG_EOF);\r
310     src->buffer =  (JOCTET *)malloc(2);\r
311     src->buffer[0] = (JOCTET) 0xFF;\r
312     src->buffer[1] = (JOCTET) JPEG_EOI;\r
313     nbytes = 2;\r
314 \r
315   }\r
316   src->offset+=nbytes;\r
317 \r
318   src->pub.next_input_byte = src->buffer;\r
319   src->pub.bytes_in_buffer = nbytes;\r
320   src->start_of_file = FALSE;\r
321 \r
322   return TRUE;\r
323 }\r
324 \r
325 \r
326 /*\r
327  * Skip data --- used to skip over a potentially large amount of\r
328  * uninteresting data (such as an APPn marker).\r
329  *\r
330  * Writers of suspendable-input applications must note that skip_input_data\r
331  * is not granted the right to give a suspension return.  If the skip extends\r
332  * beyond the data currently in the buffer, the buffer can be marked empty so\r
333  * that the next read will cause a fill_input_buffer call that can suspend.\r
334  * Arranging for additional bytes to be discarded before reloading the input\r
335  * buffer is the application writer's problem.\r
336  */\r
337 \r
338 METHODDEF(void)\r
339 lskip_input_data (j_decompress_ptr cinfo, long num_bytes)\r
340 {\r
341   my_src_ptr src = (my_src_ptr) cinfo->src;\r
342 \r
343   /* Just a dumb implementation for now.  Could use fseek() except\r
344    * it doesn't work on pipes.  Not clear that being smart is worth\r
345    * any trouble anyway --- large skips are infrequent.\r
346    */\r
347   if (num_bytes > 0) {\r
348     while (num_bytes > (long) src->pub.bytes_in_buffer) {\r
349       num_bytes -= (long) src->pub.bytes_in_buffer;\r
350       (void) lfill_input_buffer(cinfo);\r
351       /* note we assume that fill_input_buffer will never return FALSE,\r
352        * so suspension need not be handled.\r
353        */\r
354     }\r
355     src->pub.next_input_byte += (size_t) num_bytes;\r
356     src->pub.bytes_in_buffer -= (size_t) num_bytes;\r
357   }\r
358 }\r
359 \r
360 \r
361 /*\r
362  * An additional method that can be provided by data source modules is the\r
363  * resync_to_restart method for error recovery in the presence of RST markers.\r
364  * For the moment, this source module just uses the default resync method\r
365  * provided by the JPEG library.  That method assumes that no backtracking\r
366  * is possible.\r
367  */\r
368 \r
369 \r
370 /*\r
371  * Terminate source --- called by jpeg_finish_decompress\r
372  * after all data has been read.  Often a no-op.\r
373  *\r
374  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding\r
375  * application must deal with any cleanup that should happen even\r
376  * for error exit.\r
377  */\r
378 \r
379 METHODDEF(void)\r
380 lterm_source (j_decompress_ptr cinfo)\r
381 {\r
382   /* no work necessary here */\r
383 }\r
384 \r
385 \r
386 /*\r
387  * Prepare for input from a stdio stream.\r
388  * The caller must have already opened the stream, and is responsible\r
389  * for closing it after finishing decompression.\r
390  */\r
391 \r
392 extern "C" void\r
393 jpeg_memio_src (j_decompress_ptr cinfo, void * userdata)\r
394 {\r
395   my_src_ptr src;\r
396 \r
397   /* The source object and input buffer are made permanent so that a series\r
398    * of JPEG images can be read from the same file by calling jpeg_stdio_src\r
399    * only before the first one.  (If we discarded the buffer at the end of\r
400    * one image, we'd likely lose the start of the next one.)\r
401    * This makes it unsafe to use this manager and a different source\r
402    * manager serially with the same JPEG object.  Caveat programmer.\r
403    */\r
404   if (cinfo->src == NULL) {     /* first time for this JPEG object? */\r
405     cinfo->src = (struct jpeg_source_mgr *)\r
406       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,\r
407                                   SIZEOF(my_source_mgr));\r
408     src = (my_src_ptr) cinfo->src;\r
409     src->buffer = NULL;\r
410   }\r
411 \r
412   src = (my_src_ptr) cinfo->src;\r
413   src->pub.init_source = linit_source;\r
414   src->pub.fill_input_buffer = lfill_input_buffer;\r
415   src->pub.skip_input_data = lskip_input_data;\r
416   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */\r
417   src->pub.term_source = lterm_source;\r
418   src->userdata=userdata;\r
419   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */\r
420   src->pub.next_input_byte = NULL; /* until buffer loaded */\r
421   src->offset=0;\r
422   src->userdata=userdata;\r
423   if (src->buffer) {\r
424      free(src->buffer);\r
425      src->buffer=NULL;\r
426      }\r
427 }\r
428 /* cleanup to be called before cleanup of cinfo*/\r
429 extern "C" void\r
430 jpeg_memio_cleanup (j_decompress_ptr cinfo) {\r
431   my_src_ptr src=(my_src_ptr)cinfo->src;\r
432   Log::getInstance()->log("BJpeg", Log::DEBUG, "cleanup src, src=%p, buf=%p",src,(src?src->buffer:0));\r
433   if (src && src->buffer) {\r
434     free(src->buffer);\r
435     src->buffer=NULL;\r
436     }\r
437 }\r
438 \r
439 //taken from mvpmc\r
440 //http://git.mvpmc.org/cgi-bin/gitweb.cgi?p=mvpmc.git;a=blob_plain;h=02d4354c0cbbed802a9aa1478918a49fcbf61d00;f=libs/libwidget/image_jpeg.c\r
441 \r
442 #define GET2BYTES(cinfo, V, swap, offset) do { \\r
443                 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \\r
444                 cinfo->src->bytes_in_buffer--; \\r
445                 V = (*cinfo->src->next_input_byte++) << (swap?0:8); \\r
446                 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \\r
447                 cinfo->src->bytes_in_buffer--; \\r
448                 V += (*cinfo->src->next_input_byte++) << (swap?8:0); \\r
449                 offset += 2; } while(0) \r
450 \r
451 #define GET4BYTES(cinfo, V, swap, offset) do { \\r
452                 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \\r
453                 cinfo->src->bytes_in_buffer--; \\r
454                 V = (*cinfo->src->next_input_byte++) << (swap?0:24); \\r
455                 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \\r
456                 cinfo->src->bytes_in_buffer--; \\r
457                 V += (*cinfo->src->next_input_byte++) << (swap?8:16); \\r
458                 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \\r
459                 cinfo->src->bytes_in_buffer--; \\r
460                 V += (*cinfo->src->next_input_byte++) << (swap?16:8); \\r
461                 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \\r
462                 cinfo->src->bytes_in_buffer--; \\r
463                 V += (*cinfo->src->next_input_byte++) << (swap?24:0); \\r
464                 offset += 4; } while(0)\r
465 \r
466 static boolean\r
467 get_exif_orient (j_decompress_ptr cinfo)\r
468 /* Get the Exif orientation info */\r
469 {\r
470         unsigned int tmp, offset, length, numtags;\r
471         int orient=-1;\r
472         jpegUserData * ud=0;\r
473         boolean swap;\r
474         orient = 1;\r
475         offset = 0;\r
476         my_src_ptr mysrc = (my_src_ptr) cinfo->src;\r
477 \r
478         /* marker length */\r
479         GET2BYTES(cinfo, length, 0, offset);\r
480         if (length<8) goto err;\r
481         /* Exif header */\r
482         GET4BYTES(cinfo, tmp, 0, offset);\r
483         if (tmp != 0x45786966) goto err;\r
484         GET2BYTES(cinfo, tmp, 0, offset);\r
485         if (tmp != 0x0000) goto err;\r
486         /* Byte-order */\r
487         GET2BYTES(cinfo, tmp, 0, offset);\r
488         if (tmp == 0x4949) swap = 1;\r
489         else if (tmp == 0x4d4d) swap = 0;\r
490         else goto err;\r
491         GET2BYTES(cinfo, tmp, swap, offset);\r
492         if (tmp != 0x002A) goto err;\r
493         /* offset to first IFD */\r
494         GET4BYTES(cinfo, tmp, swap, offset);\r
495         offset += tmp-8;\r
496         (*mysrc->pub.skip_input_data)(cinfo, tmp-8);\r
497         /* number of tags in IFD */\r
498         GET2BYTES(cinfo, numtags, swap, offset);\r
499         if (numtags == 0) goto err;\r
500         \r
501         /* Search for Orientation Tag in IFD0 */\r
502         for (;;) {\r
503                 if (offset > length-12) goto err;\r
504                 GET2BYTES(cinfo, tmp, swap, offset);\r
505                 if (tmp == 0x0112) break; /* found Orientation Tag */\r
506                 if (--numtags == 0) goto err;\r
507                 offset += 10;\r
508                 (*mysrc->pub.skip_input_data)(cinfo, 10);\r
509         }\r
510         offset += 6;\r
511         (*mysrc->pub.skip_input_data)(cinfo, 6);\r
512         GET2BYTES(cinfo, orient, swap, offset);\r
513         if( orient==0 || orient>8 ) orient = 1;\r
514         \r
515         Log::getInstance()->log("WJpeg", Log::DEBUG, "read exif orientation %u", orient);\r
516         ud=(jpegUserData *)(mysrc->userdata);\r
517         switch(orient) {\r
518           case 3:\r
519             ud->ctl->exifRotation=WJpegComplex::ROT_180;\r
520             break;\r
521           case 6:\r
522             ud->ctl->exifRotation=WJpegComplex::ROT_90;\r
523             break;\r
524           case 8:\r
525             ud->ctl->exifRotation=WJpegComplex::ROT_270;\r
526             break;\r
527         }\r
528 \r
529 err:\r
530         (*mysrc->pub.skip_input_data)(cinfo, length-offset);\r
531         return TRUE;\r
532 }\r
533 }\r
534 #endif\r
535 /*----------------------------------------------------------------\r
536   the implementation\r
537   ----------------------------------------------------------------\r
538  */\r
539 \r
540 \r
541 WJpegComplex::WJpegComplex(){\r
542   reader=NULL;\r
543   owningReader=false;\r
544   errbuf[0]=0;\r
545 }\r
546 \r
547 WJpegComplex::~WJpegComplex() {\r
548   if (owningReader && reader) delete reader;\r
549 }\r
550 \r
551 int WJpegComplex::init(const char* tfileName)\r
552 {\r
553   if (owningReader && reader) delete reader;\r
554   errbuf[0]=0; //clean error state\r
555   LocalReader *myreader=new LocalReader();\r
556   reader=myreader;\r
557   owningReader=true;\r
558   ULONG psize=myreader->initRead(tfileName);\r
559   if (psize == 0) {\r
560     delete reader;\r
561     reader=NULL;\r
562     owningReader=false;\r
563     SNPRINTF(errbuf,200,"unable to open %s",tfileName);\r
564     return 0;\r
565   }\r
566   return 1;\r
567 }\r
568 \r
569 \r
570 \r
571 \r
572 bool WJpegComplex::hasError() {\r
573   return (errbuf[0] != 0);\r
574 }\r
575 void WJpegComplex::draw()\r
576 {\r
577   bool ok=false;\r
578   JpegControl ctl;\r
579   if (reader) {\r
580     Region myRegion;\r
581     Surface *sfc=getSurface(myRegion);\r
582     myRegion.w=area.w;\r
583     myRegion.h=area.h;\r
584     ctl.area=myRegion;\r
585     ctl.enlarge=true;\r
586     if (drawJpeg(&ctl,sfc,reader,backgroundColour) ) ok=true;\r
587   }\r
588   else {\r
589     SNPRINTF(errbuf,200,"jpeg reader not initialized");\r
590   }\r
591   if (! ok) {\r
592     drawTextCentre(tr("Jpeg ERROR"), 240, 170, DrawStyle::LIGHTTEXT);\r
593     if (errbuf[0] != 0) drawTextCentre(errbuf,240,200, DrawStyle::LIGHTTEXT);\r
594     if (ctl.error[0] != 0) drawTextCentre(ctl.error,240,230, DrawStyle::LIGHTTEXT);\r
595   }\r
596 }\r
597 \r
598 \r
599 \r
600 /**\r
601   the main drawing function\r
602   this will read the pciture via the reader\r
603   and draw directly into the surface\r
604   it will compute an appropriate scale and set the infos in the\r
605   JpegControl structure\r
606 **/  \r
607 \r
608 #ifndef __ANDROID__\r
609 \r
610 bool WJpegComplex::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, DrawStyle & backgroundColour) {\r
611   Log* logger = Log::getInstance();\r
612   if (! rdr || ! sfc) {\r
613     logger->log("BJpeg", Log::ERR, "JPEG error rdr=NULL or sfc=NULL");\r
614     return false;\r
615   }\r
616   logger->log("BJpeg", Log::DEBUG, "draw Jpeg Started sfc=%p, rdr=%p",sfc,rdr);\r
617   unsigned char* buffer =NULL;\r
618   struct jpeg_decompress_struct cinfo;\r
619   struct my_error_mgr jerr;\r
620   cinfo.err = jpeg_std_error(&jerr.pub);\r
621   jerr.pub.error_exit = my_error_exit;\r
622   /* Establish the setjmp return context for my_error_exit to use. */\r
623   if (setjmp(jerr.setjmp_buffer)) {\r
624     /* If we get here, the JPEG code has signaled an error.\r
625      * We need to clean up the JPEG object, close the input file, and return.\r
626      */\r
627     if (rdr) jpeg_memio_cleanup(&cinfo);\r
628     jpeg_destroy_decompress(&cinfo);\r
629     logger->log("BJpeg", Log::ERR, "JPEG error");\r
630     if (buffer) free(buffer);\r
631     return false;\r
632   }\r
633   jpegUserData userdata;\r
634   int xpos=0;\r
635   int ypos=0;\r
636   jpeg_create_decompress(&cinfo);\r
637   userdata.reader=rdr;\r
638   userdata.ctl=ctl;\r
639   ctl->exifRotation=ROT_0;\r
640   ctl->error[0]=0;\r
641   ctl->exifDate[0]=0;\r
642   //create the input for the jpeg lib\r
643   jpeg_memio_src(&cinfo,(void *)(&userdata));\r
644   //processor for EXIF data\r
645   jpeg_set_marker_processor(&cinfo, JPEG_APP0+1, get_exif_orient);\r
646   //read in header info\r
647   jpeg_read_header(&cinfo, TRUE);\r
648   ctl->picw=cinfo.image_width;\r
649   ctl->pich=cinfo.image_height;\r
650   ctl->compressedSize=rdr->getSize();\r
651   //now we have important info available in ctl (pictuerw,h, exif orientation,size)\r
652   //compute rotation due to the defined enum values we can simply add them\r
653   ctl->finalRotation=(enum Rotation)((ctl->rotation+ctl->exifRotation)%4);\r
654   logger->log("BJpeg", Log::DEBUG, "JPEG read header w=%i h=%i, rot=%i", ctl->picw,ctl->pich,ctl->finalRotation);\r
655   //now we have to compute the scale\r
656   //input: ctl->picw,ctl->pich,ctl->finalRotation,ctl->mode,ctl->scaleAmount, ctl->scaleafter\r
657   //       list of available jpeg scale factors\r
658   //out:   scalenum,scaledenom,scaleafter\r
659 \r
660   UINT picturew=ctl->picw;\r
661   UINT pictureh=ctl->pich;\r
662   switch (ctl->finalRotation){\r
663     case ROT_90:\r
664     case ROT_270:\r
665       pictureh=ctl->picw;\r
666       picturew=ctl->pich;\r
667       break;\r
668     default:\r
669       break;\r
670   }\r
671   UINT scalenum=1;\r
672   UINT scaledenom=1;\r
673   UINT scaleafter=1;\r
674   if (! ctl->enlarge) {\r
675     //scale - compute the factors to fit 100%\r
676     int scalew=1000*picturew/ctl->area.w;\r
677     int scaleh=1000*pictureh/ctl->area.h;\r
678     int scale=scaleh;\r
679     if (scalew > scaleh) scale=scalew;\r
680 \r
681     //OK now find out which is the optimal setting\r
682     //rule: find settings being nearest to:\r
683     //   mode=LETTER - smaller or equal screen size (i.e. scale*scalefactor <= 1000)\r
684     //   mode=CROP   - bigger or equal screen size (i.e. scale *scalefactor>= 1000)\r
685     //   mode=CROPPERCENT - smaller or equal screensize*scaleamount (i.e. scale*scalefactor<= 1000*scaleamount)\r
686     //                      scaleamount is in % - so in reality we use scaleamount*10 instead scaleamount*1000\r
687     //the scalefactor computes as scalenum/(scaledenom*scaleafter)\r
688     scaledenom=8;\r
689     int minDiff=1000;\r
690     logger->log("BJpeg", Log::DEBUG, "start scaling screenw=%u, screenh=%u, pw=%u,ph=%u, scale=%d, mode=%d, %%=%u, after=%u",\r
691        ctl->area.w,ctl->area.h,picturew,pictureh,scale,(int)ctl->mode,ctl->scaleAmount, ctl->scaleafter); \r
692     for (UINT j=0;j<sizeof(jpegFactors)/sizeof(jpegFactors[0]);j++) {\r
693       for (UINT sa=1;sa<=ctl->scaleafter;sa++) {\r
694         int curf=(scale*jpegFactors[j].num)/(jpegFactors[j].denom*sa);\r
695         bool setThis=false;\r
696         logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,num=%u,denom=%u,after=%u,minDiff=%d", \r
697             curf,jpegFactors[j].num,jpegFactors[j].denom,sa,minDiff);\r
698         switch(ctl->mode) {\r
699           case CROP:\r
700             if (curf >= 1000 && curf < (minDiff +1000)) {\r
701               setThis=true;\r
702               minDiff=curf-1000;\r
703             }\r
704             break;\r
705           case LETTER:\r
706             if (curf <= 1000 && curf > (1000-minDiff)) {\r
707               setThis=true;\r
708               minDiff=1000-curf;\r
709             }\r
710             break;\r
711           case CROPPERCENT:\r
712             if (curf <= 10*(int)ctl->scaleAmount ) {\r
713               int abs=curf-1000;\r
714               if (abs < 0) abs=-abs;\r
715               if (abs < minDiff) {\r
716                    setThis=true;\r
717                    minDiff=abs;\r
718               }\r
719             }\r
720             break;\r
721         }\r
722         if (setThis) {\r
723           logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,take this",curf);\r
724           scalenum=jpegFactors[j].num;\r
725           scaledenom=jpegFactors[j].denom;\r
726           scaleafter=sa;\r
727         }\r
728       }\r
729     }\r
730     ctl->scale=100*scalenum/(scaledenom*scaleafter);\r
731 \r
732     logger->log("BJpeg", Log::DEBUG, "JPEG scaling found scale=%i num=%i denom=%i after=%i result=%i scale=%i%% ",\r
733         scale,scalenum,scaledenom,scaleafter,scale*ctl->scale/100,ctl->scale);\r
734 \r
735     cinfo.scale_denom=scaledenom;\r
736     cinfo.scale_num=scalenum;\r
737     }\r
738   else\r
739   {\r
740     //set drawing area according to picture\r
741     ctl->area.w=ctl->picw;\r
742     ctl->area.h=ctl->pich;\r
743   }\r
744 \r
745   //now we know the scaling\r
746   //compute the scaled size and position\r
747 \r
748   jpeg_start_decompress(&cinfo);\r
749   //picturew/h is now the output width from the decompressor and afterscaler\r
750   //this is unrotated \r
751   picturew=cinfo.output_width;\r
752   pictureh=cinfo.output_height;\r
753   if (scaleafter > 1) {\r
754     picturew=picturew/scaleafter;\r
755     pictureh=pictureh/scaleafter;\r
756   }\r
757   //if our image is smaller - center it\r
758   if (! ctl->enlarge) {\r
759     if (ctl->finalRotation == ROT_90 || ctl->finalRotation == ROT_270) {\r
760       int dim=pictureh;\r
761       xpos=(((int)ctl->area.w)-dim)/2;\r
762       dim=picturew;\r
763       ypos=(((int)ctl->area.h)-dim)/2;\r
764     } else {\r
765       int dim=picturew;\r
766       xpos=(((int)ctl->area.w)-dim)/2;\r
767       dim=pictureh;\r
768       ypos=(((int)ctl->area.h)-dim)/2;\r
769     }\r
770     if (xpos <0) xpos=0;\r
771     if (ypos <0) ypos=0;\r
772   }\r
773   xpos+=ctl->area.x;\r
774   ypos+=ctl->area.y;\r
775   //remember the jpeg dimensions for computing the buffer\r
776   UINT jpegwidth=cinfo.output_width;\r
777   UINT jpegheight=cinfo.output_height;\r
778   logger->log("BJpeg", Log::DEBUG, "JPEG startup done pw=%i ph=%i, xo=%i,yo=%i, jw=%i, jh=%i, rot=%d", \r
779       picturew, pictureh,xpos,ypos,jpegwidth,jpegheight,(int)ctl->finalRotation*90);\r
780 \r
781   //fill the background\r
782   sfc->fillblt(ctl->area.x,ctl->area.y,ctl->area.w,ctl->area.h,backgroundColour);\r
783 \r
784   //line len in bytes (3 bytes per Pixel) - for buffer (can be bigger then surface)\r
785   int linelen=jpegwidth*3;\r
786 #ifdef USE_BUFFER\r
787   // MAKE THE 2D ARRAY\r
788   buffer = (unsigned char*)malloc(jpegheight * linelen);\r
789   logger->log("BJpeg", Log::DEBUG, "Buffer allocated at %p, lines = %i linelen = %i", buffer, jpegheight, linelen);\r
790   if (buffer == NULL) {\r
791     if (rdr) jpeg_memio_cleanup(&cinfo);\r
792     jpeg_destroy_decompress(&cinfo);\r
793     logger->log("BJpeg", Log::ERR, "JPEG error - no room for buffer");\r
794     SNPRINTF(ctl->error,100,"no room for buffer");\r
795     return false;\r
796   }\r
797 #endif\r
798 \r
799 #ifndef USE_BUFFER\r
800   //unsigned char lbuf[linelen];\r
801   unsigned char *lbuf=new unsigned char[linelen*scaleafter];\r
802   unsigned char * ptr=lbuf;\r
803   UINT outy=0;\r
804 #else\r
805   unsigned char * ptr=buffer;\r
806 #endif\r
807 \r
808   int rowsread = 0;\r
809 \r
810   DrawStyle c;\r
811   sfc->startFastDraw();//Tell the surface, that we will draw a lot of pixel,\r
812   //so that performance, can be optimized\r
813   logger->log("BJpeg", Log::DEBUG, "start drawing ");\r
814   UINT colincr=0;\r
815   //factors to base 1024\r
816   UINT fac=1024;\r
817   if (scaleafter > 1) {\r
818      colincr=3*scaleafter-3;\r
819      fac=1024/(scaleafter*scaleafter);\r
820      }\r
821   logger->log("BJpeg", Log::DEBUG, "jpeg  draw scale %d image: %u %u, picture: %u %u", scaleafter,picturew,pictureh,jpegwidth,jpegheight);\r
822   while (cinfo.output_scanline < jpegheight)\r
823   {\r
824 //  logger->log("BJpeg", Log::DEBUG, "%i", rowsread);\r
825     rowsread += jpeg_read_scanlines(&cinfo,&ptr,1);\r
826 #ifdef USE_BUFFER\r
827     ptr+=linelen;\r
828 #else\r
829     if (scaleafter > 1) {\r
830       if (rowsread % scaleafter != scaleafter-1) {\r
831         //this simple approach wold maybe forget scaleafter -1 lines at the end...\r
832         ptr+=linelen;\r
833         continue;\r
834       }\r
835       ptr=lbuf;\r
836     }\r
837     drawLine(sfc,ctl->finalRotation,ptr,scaleafter,picturew,pictureh,xpos,ypos,outy,linelen,colincr,scaleafter,fac);\r
838     outy++;\r
839     \r
840 #endif\r
841   }\r
842   sfc->endFastDraw();\r
843 \r
844   logger->log("BJpeg", Log::DEBUG, "Done all jpeg_read");\r
845 \r
846   jpeg_finish_decompress(&cinfo);\r
847   jpeg_memio_cleanup(&cinfo);\r
848   jpeg_destroy_decompress(&cinfo);\r
849 \r
850   logger->log("BJpeg", Log::DEBUG, "jpeg shutdown done");\r
851   rdr->drawingDone();\r
852 \r
853 #ifdef USE_BUFFER\r
854   UINT y;\r
855   //Tell the surface, that we will draw a lot of pixel,\r
856   //so that performance, can be optimized\r
857   sfc->startFastDraw();\r
858   logger->log("BJpeg", Log::DEBUG, "jpeg start buffer draw" );\r
859   unsigned char* p=buffer; //start of first row\r
860   UINT rowincr=linelen*scaleafter;\r
861   //for simplicity omit last line to avoid running out of buffer\r
862   for (y = 0; y < pictureh-1 ;y++)\r
863   {\r
864     drawLine(sfc,ctl->finalRotation,p,scaleafter,picturew,pictureh,xpos,ypos,y,linelen,colincr,scaleafter,fac);\r
865     p+=rowincr;\r
866   }\r
867   sfc->endFastDraw();\r
868   logger->log("BJpeg", Log::DEBUG, "end draw");\r
869   free(buffer);\r
870 #else\r
871   delete[] lbuf;\r
872 #endif\r
873   logger->log("BJpeg", Log::DEBUG, "deleted buffer");\r
874   return true;\r
875 }\r
876 \r
877 \r
878 #else\r
879 bool WJpegComplex::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, DrawStyle & backgroundColour) {\r
880         return true;\r
881 }\r
882 #endif\r
883 \r
884 //get my own surface\r
885 Surface * WJpegComplex::getSurface(Region & r) {\r
886   r.x=getRootBoxOffsetX();\r
887   r.y=getRootBoxOffsetY();\r
888   r.w=area.w;\r
889   r.h=area.h;\r
890   return Boxx::getSurface();\r
891 }\r
892 \r