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