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