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