]> git.vomp.tv Git - vompclient.git/blob - wjpeg.cc
Switch trunk code to new boxes
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "wjpeg.h"
22 #include <setjmp.h>
23
24 #include "i18n.h"
25 #include "log.h"
26 #include "surface.h"
27
28 extern "C" void
29 jpeg_memio_src (j_decompress_ptr cinfo, void * userdata);
30 extern "C" void
31 jpeg_memio_cleanup (j_decompress_ptr cinfo);
32
33 WJpeg::WJpeg(){
34   fileName=NULL;
35   reader=NULL;
36   useImageDimensions=true;
37   jheight=0;
38   jwidth=0;
39   jsize=0;
40   jerror=true;
41   rotate=0;
42   jscale=1;
43 }
44
45 WJpeg::~WJpeg() {
46   if (fileName) delete fileName;
47 }
48
49 int WJpeg::init(char* tfileName,bool useImage, JpegReader *rdr)
50 {
51   rotate=0;
52   if (fileName) delete fileName;
53   fileName=NULL;
54   if (tfileName) {
55     fileName = new char[strlen(tfileName)+1];
56     strcpy(fileName,tfileName);
57   }
58   reader=rdr;
59   useImageDimensions=useImage;
60   return 1;
61 }
62
63
64 ULONG WJpeg::getJpegInfo(ULONG tag){
65   switch(tag) {
66   case JPEG_HEIGHT:
67      return jheight;
68      break;
69   case JPEG_WIDTH:
70      return jwidth;
71      break;
72   case JPEG_SIZE:
73      return jsize;
74      break;
75   case JPEG_ROTATE:
76      return rotate;
77      break;
78   case JPEG_SCALE:
79      return jscale;
80      break;
81   }
82   return 0;
83 }
84
85 int WJpeg::getJpegInfo(ULONG tag, char * buffer) {
86   switch (tag) {
87   case JPEG_FILENAME:
88     strncpy(buffer,fileName,INFO_BUFFER-1);
89     buffer[INFO_BUFFER-1]=0;
90     return 0;
91     break;
92   }
93   return -1;
94 }
95
96 void WJpeg::setRotate(int amount) {
97   rotate=amount;
98 }
99
100 extern "C" {
101
102
103 struct my_error_mgr {
104   struct jpeg_error_mgr pub;    /* "public" fields */
105   FILE *infile;                 /* to be used in error handler */
106
107   jmp_buf setjmp_buffer;        /* for return to caller */
108 };
109
110 typedef struct my_error_mgr * my_error_ptr;
111
112 /*
113  * Here's the routine that will replace the standard error_exit method:
114  */
115
116 METHODDEF(void)
117 my_error_exit (j_common_ptr cinfo)
118 {
119   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
120   my_error_ptr myerr = (my_error_ptr) cinfo->err;
121
122   /* Always display the message. */
123   /* We could postpone this until after returning, if we chose. */
124   (*cinfo->err->output_message) (cinfo);
125   if (myerr->infile) fclose(myerr->infile);
126   /* Return control to the setjmp point */
127   longjmp(myerr->setjmp_buffer, 1);
128 }
129
130 }
131
132 bool WJpeg::hasError() {
133   return jerror;
134 }
135 void WJpeg::draw()
136 {
137   jerror=false;
138   if (drawJpeg() != 0) {
139     jerror=true;
140     drawTextCentre(tr("Jpeg ERROR"), 240, 180, Colour::LIGHTTEXT);
141   }
142 }
143
144 /* handle picture rotation
145    90: xr=h-y
146        yr=x
147    180:xr=w-x
148        yr=h-y
149    270:xr=y
150        yr=w-x
151 */
152 void WJpeg::drawPixel(int x, int y,int w,int h,int xpos, int ypos,Colour c){
153   int xb=0;
154   int yb=0;
155   switch(rotate) {
156      case ROT_0:
157         xb=x;
158         yb=y;
159         break;
160      case ROT_90:
161         xb=h-y;
162         yb=x;
163         break;
164      case ROT_180:
165         xb=w-x;
166         yb=h-y;
167         break;
168      case ROT_270:
169         xb=y;
170         yb=w-x;
171         break;
172      }
173    xb+=xpos;
174    yb+=ypos;
175    if (xb < 0 || yb < 0 ) {
176      Log::getInstance()->log("WJpeg:drawPixel",Log::ERR,"pixpos < 0 x=%d, y=%d",xb,yb);
177      return;
178    }
179    Boxx::drawPixel((UINT)xb,(UINT)yb,c);
180 }
181
182 int WJpeg::drawJpeg() {
183 #ifndef WIN32
184   Log* logger = Log::getInstance();
185   unsigned char* buffer =NULL;
186   struct jpeg_decompress_struct cinfo;
187   struct my_error_mgr jerr;
188   jerr.infile=NULL;
189   cinfo.err = jpeg_std_error(&jerr.pub);
190   jerr.pub.error_exit = my_error_exit;
191   /* Establish the setjmp return context for my_error_exit to use. */
192   if (setjmp(jerr.setjmp_buffer)) {
193     /* If we get here, the JPEG code has signaled an error.
194      * We need to clean up the JPEG object, close the input file, and return.
195      */
196     if (reader) jpeg_memio_cleanup(&cinfo);
197     jpeg_destroy_decompress(&cinfo);
198     logger->log("BJpeg", Log::ERR, "JPEG error");
199     if (buffer) free(buffer);
200     return 1;
201   }
202   int xpos=0;
203   int ypos=0;
204   jpeg_create_decompress(&cinfo);
205   if (fileName && ! reader) {
206     jsize=0; //TODO: compute size for local files
207     jerr.infile=fopen(fileName, "r");
208     if (jerr.infile == NULL)
209     {
210       logger->log("BJpeg", Log::ERR, "Can't open JPEG");
211       jpeg_destroy_decompress(&cinfo);
212       return 1;
213     }
214     logger->log("BJpeg", Log::DEBUG, "File opened");
215     jpeg_stdio_src(&cinfo, jerr.infile);
216   }
217   else if (reader) {
218      jsize=reader->initRead(fileName);
219      if (jsize <= 0) {
220         logger->log("BJpeg", Log::ERR, "Can't init JPEG transfer");
221         jpeg_destroy_decompress(&cinfo);
222         return 1;
223      }
224      jpeg_memio_src(&cinfo,(void *)reader);
225   }
226   else {
227      logger->log("BJpeg", Log::ERR, "neither  filename nor reader set");
228      jpeg_destroy_decompress(&cinfo);
229      return 1;
230   }
231   jpeg_read_header(&cinfo, TRUE);
232   logger->log("BJpeg", Log::DEBUG, "JPEG read header w=%i h=%i, rot=%i", cinfo.image_width, cinfo.image_height, rotate);
233   int picturew=cinfo.image_width;
234   int pictureh=cinfo.image_height;
235   switch (rotate){
236     case ROT_90:
237     case ROT_270:
238       pictureh=cinfo.image_width;
239       picturew=cinfo.image_height;
240       break;
241   }
242   if (! useImageDimensions) {
243     //scale - we can have factors 1,2,4,8
244     int scalew=getWidth()*1000/picturew;
245     int scaleh=getHeight()*1000/pictureh;
246     int scale=scaleh;
247     if (scalew < scaleh) scale=scalew;
248     int fac=8;
249     //we allow for 10% bigger...
250     if (scale >= 900) fac=1;
251     else if (scale >= 450 ) fac=2;
252     else if (scale >= 225 ) fac=4;
253     cinfo.scale_denom=fac;
254     logger->log("BJpeg", Log::DEBUG, "JPEG scaling (1/1000) pw=%i ph=%i w=%i h=%i r=%i f=%i",
255        getWidth(),
256        getHeight(),
257        scalew,scaleh,scale,fac);
258     jscale=fac;
259     }
260
261   //remember picture parameters
262   jheight=pictureh;
263   jwidth=picturew;
264
265   jpeg_start_decompress(&cinfo);
266   //recompute width based on rotation (consider scale)
267   picturew=cinfo.output_width;
268   pictureh=cinfo.output_height;
269   switch (rotate){
270     case ROT_90:
271     case ROT_270:
272       pictureh=cinfo.output_width;
273       picturew=cinfo.output_height;
274       break;
275   }  
276   //if our image is smaller - center it
277   if (! useImageDimensions) {
278      xpos=(getWidth()-picturew)/2;
279      if (xpos <0) xpos=0;
280      ypos=(getHeight()-pictureh)/2;
281      if (ypos <0) ypos=0;
282   }
283   //center point for rotation
284   int w=cinfo.output_width;
285   int h=cinfo.output_height;
286   logger->log("BJpeg", Log::DEBUG, "JPEG startup done pw=%i ph=%i, xo=%i,yo=%i, iw=%i, ih=%i", picturew, pictureh,xpos,ypos,w,h);
287
288   // Init the surface
289
290 // Temp disabled for boxx. what does this do anyway .. - cjt
291 /*
292   if (useImageDimensions) setDimensions(picturew, pictureh);
293   fillColour(backgroundColour);
294 */
295
296
297   //line len in bytes (3 bytes per Pixel) - for buffer (can be bigger then surface)
298   int linelen=cinfo.output_width*3;
299 #ifdef USE_BUFFER
300   // MAKE THE 2D ARRAY
301   buffer = (unsigned char*)malloc(config.output_height * linelen);
302   logger->log("BJpeg", Log::DEBUG, "Buffer allocated at %p, width = %i height = %i", buffer, cinfo.output_height, linelen);
303   if (buffer == NULL) {
304     if (reader) jpeg_memio_cleanup(&cinfo);
305     jpeg_destroy_decompress(&cinfo);
306     if (jerr.infile) fclose(jerr.infile);
307     logger->log("BJpeg", Log::ERR, "JPEG error - no room for buffer");
308     return 1;
309   }
310 #endif
311
312   //unsigned char* bufferPointers[area.h];
313   //for(UINT ps = 0; ps < area.h; ps++) bufferPointers[ps] = buffer + (ps * area.w * 3);
314   
315   logger->log("BJpeg", Log::DEBUG, "header w=%d,h=%d",cinfo.output_width,cinfo.output_height);
316 #ifndef USE_BUFFER
317   unsigned char lbuf[linelen];
318   unsigned char * ptr=lbuf;
319 #else
320   unsigned char * ptr=buffer;
321 #endif
322
323   int rowsread = 0;
324
325   Colour c;
326   while (cinfo.output_scanline < cinfo.output_height)
327   {
328 //  logger->log("BJpeg", Log::DEBUG, "%i", rowsread);
329     rowsread += jpeg_read_scanlines(&cinfo,&ptr,1);
330 #ifdef USE_BUFFER
331     ptr+=linelen;
332 #else
333      int x=0;
334      for (unsigned char * lp=ptr;lp < (ptr+linelen);lp+=3,x++) {
335        c.red=*lp ;
336        c.green=*(lp+1);
337        c.blue=*(lp+2);
338        drawPixel(x, rowsread-1,w,h,xpos,ypos, c);
339        }
340     
341 #endif
342   }
343
344   logger->log("BJpeg", Log::DEBUG, "Done all jpeg_read");
345
346   jpeg_finish_decompress(&cinfo);
347   if (reader) jpeg_memio_cleanup(&cinfo);
348   jpeg_destroy_decompress(&cinfo);
349
350   if (jerr.infile) fclose(jerr.infile);
351
352   logger->log("BJpeg", Log::DEBUG, "jpeg shutdown done, x, y %u %u", area.w, area.h);
353 #ifdef USE_BUFFER
354   Colour c;
355   UINT x, y, xoff;
356   unsigned char* p;
357
358   for (y = 0; y < numlines; y++)
359   {
360     p = bufferPointers[y];
361
362     for (x = 0; x < sfclen; x++)
363     {
364       xoff = x * 3;
365
366 //      c = (  (0xFF000000        )
367 //           | (p[xoff    ]  << 16)
368 //           | (p[xoff + 1]  <<  8)
369 //           | (p[xoff + 2]       ) );
370
371       c.red = p[xoff];
372       c.green = p[xoff + 1];
373       c.blue = p[xoff + 2];
374
375       drawPixel(x, y, w,h,xpos,ypos,c);
376     }
377   }
378
379   free(buffer);
380 #endif
381   logger->log("BJpeg", Log::DEBUG, "deleted buffer");
382 #else
383   DWORD width,height;
384   width=height=1;
385   ((SurfaceWin*)surface)->drawJpeg(fileName+1,offsetX,offsetY,&width,&height);//This should went into the abstract base classes?
386   //Windows has a problem with the leading / fixme
387
388   setDimensions(width, height);
389 #endif
390   return 0;
391 }
392
393
394 extern "C" {
395 ULONG jpeg_call_reader(ULONG offset,ULONG size,char ** buf,void *cb) {
396   JpegReader *rd=(JpegReader *)cb;
397   return rd->readChunk(offset,size,buf);
398 }
399 }
400 //the memory buffer reader for the jpeg lib
401 //taken from jdatasrc.c
402
403 extern "C" {
404 /* Expanded data source object for stdio input */
405 #include "jinclude.h"
406 #include "jpeglib.h"
407 #include "jerror.h"/* Expanded data source object for stdio input */
408
409 typedef struct {
410   struct jpeg_source_mgr pub;   /* public fields */
411
412   JOCTET * buffer;              /* start of buffer */
413   boolean start_of_file;        /* have we gotten any data yet? */
414   void * userdata;              /* used for callback */
415   ULONG offset;
416 } my_source_mgr;
417
418 typedef my_source_mgr * my_src_ptr;
419
420 #define INPUT_BUF_SIZE  (64*4096)       /* choose an efficiently fread'able size */
421
422
423 /*
424  * Initialize source --- called by jpeg_read_header
425  * before any data is actually read.
426  */
427
428 METHODDEF(void)
429 linit_source (j_decompress_ptr cinfo)
430 {
431   my_src_ptr src = (my_src_ptr) cinfo->src;
432
433   /* We reset the empty-input-file flag for each image,
434    * but we don't clear the input buffer.
435    * This is correct behavior for reading a series of images from one source.
436    */
437   src->start_of_file = TRUE;
438   src->offset=0;
439 }
440
441
442 /*
443  * Fill the input buffer --- called whenever buffer is emptied.
444  *
445  * In typical applications, this should read fresh data into the buffer
446  * (ignoring the current state of next_input_byte & bytes_in_buffer),
447  * reset the pointer & count to the start of the buffer, and return TRUE
448  * indicating that the buffer has been reloaded.  It is not necessary to
449  * fill the buffer entirely, only to obtain at least one more byte.
450  *
451  * There is no such thing as an EOF return.  If the end of the file has been
452  * reached, the routine has a choice of ERREXIT() or inserting fake data into
453  * the buffer.  In most cases, generating a warning message and inserting a
454  * fake EOI marker is the best course of action --- this will allow the
455  * decompressor to output however much of the image is there.  However,
456  * the resulting error message is misleading if the real problem is an empty
457  * input file, so we handle that case specially.
458  *
459  * In applications that need to be able to suspend compression due to input
460  * not being available yet, a FALSE return indicates that no more data can be
461  * obtained right now, but more may be forthcoming later.  In this situation,
462  * the decompressor will return to its caller (with an indication of the
463  * number of scanlines it has read, if any).  The application should resume
464  * decompression after it has loaded more data into the input buffer.  Note
465  * that there are substantial restrictions on the use of suspension --- see
466  * the documentation.
467  *
468  * When suspending, the decompressor will back up to a convenient restart point
469  * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
470  * indicate where the restart point will be if the current call returns FALSE.
471  * Data beyond this point must be rescanned after resumption, so move it to
472  * the front of the buffer rather than discarding it.
473  */
474
475 METHODDEF(boolean)
476 lfill_input_buffer (j_decompress_ptr cinfo)
477 {
478   my_src_ptr src = (my_src_ptr) cinfo->src;
479   size_t nbytes;
480   if (src->buffer) free(src->buffer);
481   src->buffer=NULL;
482   nbytes = jpeg_call_reader(src->offset, INPUT_BUF_SIZE,(char **)&(src->buffer), src->userdata);
483
484   if (nbytes <= 0) {
485     WARNMS(cinfo, JWRN_JPEG_EOF);
486     src->buffer =  (JOCTET *)malloc(2);
487     src->buffer[0] = (JOCTET) 0xFF;
488     src->buffer[1] = (JOCTET) JPEG_EOI;
489     nbytes = 2;
490
491   }
492   src->offset+=nbytes;
493
494   src->pub.next_input_byte = src->buffer;
495   src->pub.bytes_in_buffer = nbytes;
496   src->start_of_file = FALSE;
497
498   return TRUE;
499 }
500
501
502 /*
503  * Skip data --- used to skip over a potentially large amount of
504  * uninteresting data (such as an APPn marker).
505  *
506  * Writers of suspendable-input applications must note that skip_input_data
507  * is not granted the right to give a suspension return.  If the skip extends
508  * beyond the data currently in the buffer, the buffer can be marked empty so
509  * that the next read will cause a fill_input_buffer call that can suspend.
510  * Arranging for additional bytes to be discarded before reloading the input
511  * buffer is the application writer's problem.
512  */
513
514 METHODDEF(void)
515 lskip_input_data (j_decompress_ptr cinfo, long num_bytes)
516 {
517   my_src_ptr src = (my_src_ptr) cinfo->src;
518
519   /* Just a dumb implementation for now.  Could use fseek() except
520    * it doesn't work on pipes.  Not clear that being smart is worth
521    * any trouble anyway --- large skips are infrequent.
522    */
523   if (num_bytes > 0) {
524     while (num_bytes > (long) src->pub.bytes_in_buffer) {
525       num_bytes -= (long) src->pub.bytes_in_buffer;
526       (void) lfill_input_buffer(cinfo);
527       /* note we assume that fill_input_buffer will never return FALSE,
528        * so suspension need not be handled.
529        */
530     }
531     src->pub.next_input_byte += (size_t) num_bytes;
532     src->pub.bytes_in_buffer -= (size_t) num_bytes;
533   }
534 }
535
536
537 /*
538  * An additional method that can be provided by data source modules is the
539  * resync_to_restart method for error recovery in the presence of RST markers.
540  * For the moment, this source module just uses the default resync method
541  * provided by the JPEG library.  That method assumes that no backtracking
542  * is possible.
543  */
544
545
546 /*
547  * Terminate source --- called by jpeg_finish_decompress
548  * after all data has been read.  Often a no-op.
549  *
550  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
551  * application must deal with any cleanup that should happen even
552  * for error exit.
553  */
554
555 METHODDEF(void)
556 lterm_source (j_decompress_ptr cinfo)
557 {
558   /* no work necessary here */
559 }
560
561
562 /*
563  * Prepare for input from a stdio stream.
564  * The caller must have already opened the stream, and is responsible
565  * for closing it after finishing decompression.
566  */
567
568 extern "C" void
569 jpeg_memio_src (j_decompress_ptr cinfo, void * userdata)
570 {
571   my_src_ptr src;
572
573   /* The source object and input buffer are made permanent so that a series
574    * of JPEG images can be read from the same file by calling jpeg_stdio_src
575    * only before the first one.  (If we discarded the buffer at the end of
576    * one image, we'd likely lose the start of the next one.)
577    * This makes it unsafe to use this manager and a different source
578    * manager serially with the same JPEG object.  Caveat programmer.
579    */
580   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
581     cinfo->src = (struct jpeg_source_mgr *)
582       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
583                                   SIZEOF(my_source_mgr));
584     src = (my_src_ptr) cinfo->src;
585     src->buffer = NULL;
586   }
587
588   src = (my_src_ptr) cinfo->src;
589   src->pub.init_source = linit_source;
590   src->pub.fill_input_buffer = lfill_input_buffer;
591   src->pub.skip_input_data = lskip_input_data;
592   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
593   src->pub.term_source = lterm_source;
594   src->userdata=userdata;
595   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
596   src->pub.next_input_byte = NULL; /* until buffer loaded */
597   src->offset=0;
598   src->userdata=userdata;
599   if (src->buffer) {
600      free(src->buffer);
601      src->buffer=NULL;
602      }
603 }
604 /* cleanup to be called before cleanup of cinfo*/
605 extern "C" void
606 jpeg_memio_cleanup (j_decompress_ptr cinfo) {
607   my_src_ptr src=(my_src_ptr)cinfo->src;
608   Log::getInstance()->log("BJpeg", Log::DEBUG, "cleanup src, src=%p, buf=%p",src,(src?src->buffer:0));
609   if (src && src->buffer) {
610     free(src->buffer);
611     src->buffer=NULL;
612     }
613 }
614 }
615