2 Copyright 2004-2005 Chris Tallon
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
21 #include "wjpegcomplex.h"
\r
23 #include <sys/types.h>
\r
24 #include <sys/stat.h>
\r
34 #include "surface.h"
\r
39 #include <jpeglib.h>
\r
44 //#define USE_BUFFER
\r
45 //#define EXTENDED_JPEGLIB
\r
47 //a struct to store user data for the jpeg decompressor
\r
50 WJpegComplex::JpegControl * ctl;
\r
58 //the scale factors supported by the jpeg lib
\r
66 struct scale jpegFactors[]={
\r
67 #ifndef EXTENDED_JPEGLIB
\r
68 {1,1},{1,2},{1,4},{1,8}
\r
70 {1,1},{7,8},{3,4},{5,8},{1,2},{3,8},{1,4},{1,8}
\r
76 #define LocalReader WindowsResourceJpegReader
\r
77 class WindowsResourceJpegReader: public JpegReader {
\r
79 virtual ULONG initRead(const char *filename);
\r
80 virtual ULONG readChunk(ULONG offset,ULONG len,char **buf);
\r
81 virtual ULONG getSize();
\r
82 virtual ~WindowsResourceJpegReader();
\r
89 ULONG WindowsResourceJpegReader::initRead(const char *filename)
\r
91 HRSRC res=FindResource(NULL,filename,RT_RCDATA);
\r
92 hres=LoadResource(NULL,res);
\r
93 buffer=LockResource(hres);
\r
94 size=SizeofResource(NULL,res);
\r
95 //CloseHandle(hres);
\r
99 ULONG WindowsResourceJpegReader::readChunk(ULONG offset,ULONG len,char **buf)
\r
101 if (offset>size) return 0;
\r
102 ULONG toread=min(size-offset,len);
\r
103 char* buffy=(char*)malloc(toread);
\r
104 memcpy(buffy,((char*)buffer)+offset,toread);
\r
109 WindowsResourceJpegReader::~WindowsResourceJpegReader(){
\r
112 FreeResource(hres);
\r
115 ULONG WindowsResourceJpegReader::getSize(){
\r
116 return (ULONG)size;
\r
120 #define LocalReader LocalJpegReader
\r
121 class LocalJpegReader: public JpegReader {
\r
123 virtual ULONG initRead(const char *filename);
\r
124 virtual ULONG readChunk(ULONG offset,ULONG len,char **buf);
\r
125 virtual ~LocalJpegReader();
\r
126 virtual ULONG getSize();
\r
133 LocalJpegReader::LocalJpegReader(){
\r
138 ULONG LocalJpegReader::initRead(const char *filename)
\r
140 if (file) fclose(file);
\r
142 file=fopen(filename,"r");
\r
145 if (fstat(fileno(file), &st) != 0) return 0;
\r
149 Log::getInstance()->log("WJepg", Log::ERR, "localReader unable to open File %s", filename);
\r
153 ULONG LocalJpegReader::readChunk(ULONG offset,ULONG len,char **buf)
\r
158 ULLONG cpos=ftell(file);
\r
159 if (offset != cpos) {
\r
160 fseek(file,offset-cpos,SEEK_CUR);
\r
162 if (offset != (ULONG)ftell(file)) {
\r
163 Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset);
\r
166 *buf=(char *)malloc(len);
\r
168 Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset);
\r
171 bread=fread(*buf,1,len,file);
\r
178 LocalJpegReader::~LocalJpegReader(){
\r
179 if (file) fclose(file);
\r
182 ULONG LocalJpegReader::getSize(){
\r
187 #ifndef __ANDROID__
\r
188 /*----------------------------------------------------------------
\r
189 the jpeg lib routines
\r
190 ----------------------------------------------------------------
\r
196 struct my_error_mgr {
\r
197 struct jpeg_error_mgr pub; /* "public" fields */
\r
199 jmp_buf setjmp_buffer; /* for return to caller */
\r
202 typedef struct my_error_mgr * my_error_ptr;
\r
205 * Here's the routine that will replace the standard error_exit method:
\r
209 my_error_exit (j_common_ptr cinfo)
\r
211 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
\r
212 my_error_ptr myerr = (my_error_ptr) cinfo->err;
\r
214 /* Always display the message. */
\r
215 /* We could postpone this until after returning, if we chose. */
\r
216 (*cinfo->err->output_message) (cinfo);
\r
217 /* Return control to the setjmp point */
\r
218 longjmp(myerr->setjmp_buffer, 1);
\r
221 ULONG jpeg_call_reader(ULONG offset,ULONG size,char ** buf,void *cb) {
\r
222 jpegUserData *user=(jpegUserData *)cb;
\r
223 return user->reader->readChunk(offset,size,buf);
\r
225 //the memory buffer reader for the jpeg lib
\r
226 //taken from jdatasrc.c
\r
228 #include "jinclude.h"
\r
229 #include "jpeglib.h"
\r
230 #include "jerror.h"
\r
234 struct jpeg_source_mgr pub; /* public fields */
\r
236 JOCTET * buffer; /* start of buffer */
\r
237 boolean start_of_file; /* have we gotten any data yet? */
\r
238 void * userdata; /* used for callback */
\r
242 typedef my_source_mgr * my_src_ptr;
\r
244 #define INPUT_BUF_SIZE (64*4096) /* choose an efficiently fread'able size */
\r
248 * Initialize source --- called by jpeg_read_header
\r
249 * before any data is actually read.
\r
253 linit_source (j_decompress_ptr cinfo)
\r
255 my_src_ptr src = (my_src_ptr) cinfo->src;
\r
257 /* We reset the empty-input-file flag for each image,
\r
258 * but we don't clear the input buffer.
\r
259 * This is correct behavior for reading a series of images from one source.
\r
261 src->start_of_file = TRUE;
\r
267 * Fill the input buffer --- called whenever buffer is emptied.
\r
269 * In typical applications, this should read fresh data into the buffer
\r
270 * (ignoring the current state of next_input_byte & bytes_in_buffer),
\r
271 * reset the pointer & count to the start of the buffer, and return TRUE
\r
272 * indicating that the buffer has been reloaded. It is not necessary to
\r
273 * fill the buffer entirely, only to obtain at least one more byte.
\r
275 * There is no such thing as an EOF return. If the end of the file has been
\r
276 * reached, the routine has a choice of ERREXIT() or inserting fake data into
\r
277 * the buffer. In most cases, generating a warning message and inserting a
\r
278 * fake EOI marker is the best course of action --- this will allow the
\r
279 * decompressor to output however much of the image is there. However,
\r
280 * the resulting error message is misleading if the real problem is an empty
\r
281 * input file, so we handle that case specially.
\r
283 * In applications that need to be able to suspend compression due to input
\r
284 * not being available yet, a FALSE return indicates that no more data can be
\r
285 * obtained right now, but more may be forthcoming later. In this situation,
\r
286 * the decompressor will return to its caller (with an indication of the
\r
287 * number of scanlines it has read, if any). The application should resume
\r
288 * decompression after it has loaded more data into the input buffer. Note
\r
289 * that there are substantial restrictions on the use of suspension --- see
\r
290 * the documentation.
\r
292 * When suspending, the decompressor will back up to a convenient restart point
\r
293 * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
\r
294 * indicate where the restart point will be if the current call returns FALSE.
\r
295 * Data beyond this point must be rescanned after resumption, so move it to
\r
296 * the front of the buffer rather than discarding it.
\r
300 lfill_input_buffer (j_decompress_ptr cinfo)
\r
302 my_src_ptr src = (my_src_ptr) cinfo->src;
\r
304 if (src->buffer) free(src->buffer);
\r
306 nbytes = jpeg_call_reader(src->offset, INPUT_BUF_SIZE,(char **)&(src->buffer), src->userdata);
\r
309 WARNMS(cinfo, JWRN_JPEG_EOF);
\r
310 src->buffer = (JOCTET *)malloc(2);
\r
311 src->buffer[0] = (JOCTET) 0xFF;
\r
312 src->buffer[1] = (JOCTET) JPEG_EOI;
\r
316 src->offset+=nbytes;
\r
318 src->pub.next_input_byte = src->buffer;
\r
319 src->pub.bytes_in_buffer = nbytes;
\r
320 src->start_of_file = FALSE;
\r
327 * Skip data --- used to skip over a potentially large amount of
\r
328 * uninteresting data (such as an APPn marker).
\r
330 * Writers of suspendable-input applications must note that skip_input_data
\r
331 * is not granted the right to give a suspension return. If the skip extends
\r
332 * beyond the data currently in the buffer, the buffer can be marked empty so
\r
333 * that the next read will cause a fill_input_buffer call that can suspend.
\r
334 * Arranging for additional bytes to be discarded before reloading the input
\r
335 * buffer is the application writer's problem.
\r
339 lskip_input_data (j_decompress_ptr cinfo, long num_bytes)
\r
341 my_src_ptr src = (my_src_ptr) cinfo->src;
\r
343 /* Just a dumb implementation for now. Could use fseek() except
\r
344 * it doesn't work on pipes. Not clear that being smart is worth
\r
345 * any trouble anyway --- large skips are infrequent.
\r
347 if (num_bytes > 0) {
\r
348 while (num_bytes > (long) src->pub.bytes_in_buffer) {
\r
349 num_bytes -= (long) src->pub.bytes_in_buffer;
\r
350 (void) lfill_input_buffer(cinfo);
\r
351 /* note we assume that fill_input_buffer will never return FALSE,
\r
352 * so suspension need not be handled.
\r
355 src->pub.next_input_byte += (size_t) num_bytes;
\r
356 src->pub.bytes_in_buffer -= (size_t) num_bytes;
\r
362 * An additional method that can be provided by data source modules is the
\r
363 * resync_to_restart method for error recovery in the presence of RST markers.
\r
364 * For the moment, this source module just uses the default resync method
\r
365 * provided by the JPEG library. That method assumes that no backtracking
\r
371 * Terminate source --- called by jpeg_finish_decompress
\r
372 * after all data has been read. Often a no-op.
\r
374 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
\r
375 * application must deal with any cleanup that should happen even
\r
380 lterm_source (j_decompress_ptr cinfo)
\r
382 /* no work necessary here */
\r
387 * Prepare for input from a stdio stream.
\r
388 * The caller must have already opened the stream, and is responsible
\r
389 * for closing it after finishing decompression.
\r
393 jpeg_memio_src (j_decompress_ptr cinfo, void * userdata)
\r
397 /* The source object and input buffer are made permanent so that a series
\r
398 * of JPEG images can be read from the same file by calling jpeg_stdio_src
\r
399 * only before the first one. (If we discarded the buffer at the end of
\r
400 * one image, we'd likely lose the start of the next one.)
\r
401 * This makes it unsafe to use this manager and a different source
\r
402 * manager serially with the same JPEG object. Caveat programmer.
\r
404 if (cinfo->src == NULL) { /* first time for this JPEG object? */
\r
405 cinfo->src = (struct jpeg_source_mgr *)
\r
406 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
\r
407 SIZEOF(my_source_mgr));
\r
408 src = (my_src_ptr) cinfo->src;
\r
409 src->buffer = NULL;
\r
412 src = (my_src_ptr) cinfo->src;
\r
413 src->pub.init_source = linit_source;
\r
414 src->pub.fill_input_buffer = lfill_input_buffer;
\r
415 src->pub.skip_input_data = lskip_input_data;
\r
416 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
\r
417 src->pub.term_source = lterm_source;
\r
418 src->userdata=userdata;
\r
419 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
\r
420 src->pub.next_input_byte = NULL; /* until buffer loaded */
\r
422 src->userdata=userdata;
\r
428 /* cleanup to be called before cleanup of cinfo*/
\r
430 jpeg_memio_cleanup (j_decompress_ptr cinfo) {
\r
431 my_src_ptr src=(my_src_ptr)cinfo->src;
\r
432 Log::getInstance()->log("BJpeg", Log::DEBUG, "cleanup src, src=%p, buf=%p",src,(src?src->buffer:0));
\r
433 if (src && src->buffer) {
\r
440 //http://git.mvpmc.org/cgi-bin/gitweb.cgi?p=mvpmc.git;a=blob_plain;h=02d4354c0cbbed802a9aa1478918a49fcbf61d00;f=libs/libwidget/image_jpeg.c
\r
442 #define GET2BYTES(cinfo, V, swap, offset) do { \
\r
443 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \
\r
444 cinfo->src->bytes_in_buffer--; \
\r
445 V = (*cinfo->src->next_input_byte++) << (swap?0:8); \
\r
446 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \
\r
447 cinfo->src->bytes_in_buffer--; \
\r
448 V += (*cinfo->src->next_input_byte++) << (swap?8:0); \
\r
449 offset += 2; } while(0)
\r
451 #define GET4BYTES(cinfo, V, swap, offset) do { \
\r
452 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \
\r
453 cinfo->src->bytes_in_buffer--; \
\r
454 V = (*cinfo->src->next_input_byte++) << (swap?0:24); \
\r
455 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \
\r
456 cinfo->src->bytes_in_buffer--; \
\r
457 V += (*cinfo->src->next_input_byte++) << (swap?8:16); \
\r
458 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \
\r
459 cinfo->src->bytes_in_buffer--; \
\r
460 V += (*cinfo->src->next_input_byte++) << (swap?16:8); \
\r
461 if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \
\r
462 cinfo->src->bytes_in_buffer--; \
\r
463 V += (*cinfo->src->next_input_byte++) << (swap?24:0); \
\r
464 offset += 4; } while(0)
\r
467 get_exif_orient (j_decompress_ptr cinfo)
\r
468 /* Get the Exif orientation info */
\r
470 unsigned int tmp, offset, length, numtags;
\r
472 jpegUserData * ud=0;
\r
476 my_src_ptr mysrc = (my_src_ptr) cinfo->src;
\r
478 /* marker length */
\r
479 GET2BYTES(cinfo, length, 0, offset);
\r
480 if (length<8) goto err;
\r
482 GET4BYTES(cinfo, tmp, 0, offset);
\r
483 if (tmp != 0x45786966) goto err;
\r
484 GET2BYTES(cinfo, tmp, 0, offset);
\r
485 if (tmp != 0x0000) goto err;
\r
487 GET2BYTES(cinfo, tmp, 0, offset);
\r
488 if (tmp == 0x4949) swap = 1;
\r
489 else if (tmp == 0x4d4d) swap = 0;
\r
491 GET2BYTES(cinfo, tmp, swap, offset);
\r
492 if (tmp != 0x002A) goto err;
\r
493 /* offset to first IFD */
\r
494 GET4BYTES(cinfo, tmp, swap, offset);
\r
496 (*mysrc->pub.skip_input_data)(cinfo, tmp-8);
\r
497 /* number of tags in IFD */
\r
498 GET2BYTES(cinfo, numtags, swap, offset);
\r
499 if (numtags == 0) goto err;
\r
501 /* Search for Orientation Tag in IFD0 */
\r
503 if (offset > length-12) goto err;
\r
504 GET2BYTES(cinfo, tmp, swap, offset);
\r
505 if (tmp == 0x0112) break; /* found Orientation Tag */
\r
506 if (--numtags == 0) goto err;
\r
508 (*mysrc->pub.skip_input_data)(cinfo, 10);
\r
511 (*mysrc->pub.skip_input_data)(cinfo, 6);
\r
512 GET2BYTES(cinfo, orient, swap, offset);
\r
513 if( orient==0 || orient>8 ) orient = 1;
\r
515 Log::getInstance()->log("WJpeg", Log::DEBUG, "read exif orientation %u", orient);
\r
516 ud=(jpegUserData *)(mysrc->userdata);
\r
519 ud->ctl->exifRotation=WJpegComplex::ROT_180;
\r
522 ud->ctl->exifRotation=WJpegComplex::ROT_90;
\r
525 ud->ctl->exifRotation=WJpegComplex::ROT_270;
\r
530 (*mysrc->pub.skip_input_data)(cinfo, length-offset);
\r
535 /*----------------------------------------------------------------
\r
537 ----------------------------------------------------------------
\r
541 WJpegComplex::WJpegComplex(){
\r
543 owningReader=false;
\r
547 WJpegComplex::~WJpegComplex() {
\r
548 if (owningReader && reader) delete reader;
\r
551 int WJpegComplex::init(const char* tfileName)
\r
553 if (owningReader && reader) delete reader;
\r
554 errbuf[0]=0; //clean error state
\r
555 LocalReader *myreader=new LocalReader();
\r
558 ULONG psize=myreader->initRead(tfileName);
\r
562 owningReader=false;
\r
563 SNPRINTF(errbuf,200,"unable to open %s",tfileName);
\r
572 bool WJpegComplex::hasError() {
\r
573 return (errbuf[0] != 0);
\r
575 void WJpegComplex::draw()
\r
581 Surface *sfc=getSurface(myRegion);
\r
586 if (drawJpeg(&ctl,sfc,reader,backgroundColour) ) ok=true;
\r
589 SNPRINTF(errbuf,200,"jpeg reader not initialized");
\r
592 drawTextCentre(tr("Jpeg ERROR"), 240, 170, DrawStyle::LIGHTTEXT);
\r
593 if (errbuf[0] != 0) drawTextCentre(errbuf,240,200, DrawStyle::LIGHTTEXT);
\r
594 if (ctl.error[0] != 0) drawTextCentre(ctl.error,240,230, DrawStyle::LIGHTTEXT);
\r
601 the main drawing function
\r
602 this will read the pciture via the reader
\r
603 and draw directly into the surface
\r
604 it will compute an appropriate scale and set the infos in the
\r
605 JpegControl structure
\r
608 #ifndef __ANDROID__
\r
610 bool WJpegComplex::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, DrawStyle & backgroundColour) {
\r
611 Log* logger = Log::getInstance();
\r
612 if (! rdr || ! sfc) {
\r
613 logger->log("BJpeg", Log::ERR, "JPEG error rdr=NULL or sfc=NULL");
\r
616 logger->log("BJpeg", Log::DEBUG, "draw Jpeg Started sfc=%p, rdr=%p",sfc,rdr);
\r
617 unsigned char* buffer =NULL;
\r
618 struct jpeg_decompress_struct cinfo;
\r
619 struct my_error_mgr jerr;
\r
620 cinfo.err = jpeg_std_error(&jerr.pub);
\r
621 jerr.pub.error_exit = my_error_exit;
\r
622 /* Establish the setjmp return context for my_error_exit to use. */
\r
623 if (setjmp(jerr.setjmp_buffer)) {
\r
624 /* If we get here, the JPEG code has signaled an error.
\r
625 * We need to clean up the JPEG object, close the input file, and return.
\r
627 if (rdr) jpeg_memio_cleanup(&cinfo);
\r
628 jpeg_destroy_decompress(&cinfo);
\r
629 logger->log("BJpeg", Log::ERR, "JPEG error");
\r
630 if (buffer) free(buffer);
\r
633 jpegUserData userdata;
\r
636 jpeg_create_decompress(&cinfo);
\r
637 userdata.reader=rdr;
\r
639 ctl->exifRotation=ROT_0;
\r
641 ctl->exifDate[0]=0;
\r
642 //create the input for the jpeg lib
\r
643 jpeg_memio_src(&cinfo,(void *)(&userdata));
\r
644 //processor for EXIF data
\r
645 jpeg_set_marker_processor(&cinfo, JPEG_APP0+1, get_exif_orient);
\r
646 //read in header info
\r
647 jpeg_read_header(&cinfo, TRUE);
\r
648 ctl->picw=cinfo.image_width;
\r
649 ctl->pich=cinfo.image_height;
\r
650 ctl->compressedSize=rdr->getSize();
\r
651 //now we have important info available in ctl (pictuerw,h, exif orientation,size)
\r
652 //compute rotation due to the defined enum values we can simply add them
\r
653 ctl->finalRotation=(enum Rotation)((ctl->rotation+ctl->exifRotation)%4);
\r
654 logger->log("BJpeg", Log::DEBUG, "JPEG read header w=%i h=%i, rot=%i", ctl->picw,ctl->pich,ctl->finalRotation);
\r
655 //now we have to compute the scale
\r
656 //input: ctl->picw,ctl->pich,ctl->finalRotation,ctl->mode,ctl->scaleAmount, ctl->scaleafter
\r
657 // list of available jpeg scale factors
\r
658 //out: scalenum,scaledenom,scaleafter
\r
660 UINT picturew=ctl->picw;
\r
661 UINT pictureh=ctl->pich;
\r
662 switch (ctl->finalRotation){
\r
665 pictureh=ctl->picw;
\r
666 picturew=ctl->pich;
\r
674 if (! ctl->enlarge) {
\r
675 //scale - compute the factors to fit 100%
\r
676 int scalew=1000*picturew/ctl->area.w;
\r
677 int scaleh=1000*pictureh/ctl->area.h;
\r
679 if (scalew > scaleh) scale=scalew;
\r
681 //OK now find out which is the optimal setting
\r
682 //rule: find settings being nearest to:
\r
683 // mode=LETTER - smaller or equal screen size (i.e. scale*scalefactor <= 1000)
\r
684 // mode=CROP - bigger or equal screen size (i.e. scale *scalefactor>= 1000)
\r
685 // mode=CROPPERCENT - smaller or equal screensize*scaleamount (i.e. scale*scalefactor<= 1000*scaleamount)
\r
686 // scaleamount is in % - so in reality we use scaleamount*10 instead scaleamount*1000
\r
687 //the scalefactor computes as scalenum/(scaledenom*scaleafter)
\r
690 logger->log("BJpeg", Log::DEBUG, "start scaling screenw=%u, screenh=%u, pw=%u,ph=%u, scale=%d, mode=%d, %%=%u, after=%u",
\r
691 ctl->area.w,ctl->area.h,picturew,pictureh,scale,(int)ctl->mode,ctl->scaleAmount, ctl->scaleafter);
\r
692 for (UINT j=0;j<sizeof(jpegFactors)/sizeof(jpegFactors[0]);j++) {
\r
693 for (UINT sa=1;sa<=ctl->scaleafter;sa++) {
\r
694 int curf=(scale*jpegFactors[j].num)/(jpegFactors[j].denom*sa);
\r
695 bool setThis=false;
\r
696 logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,num=%u,denom=%u,after=%u,minDiff=%d",
\r
697 curf,jpegFactors[j].num,jpegFactors[j].denom,sa,minDiff);
\r
698 switch(ctl->mode) {
\r
700 if (curf >= 1000 && curf < (minDiff +1000)) {
\r
706 if (curf <= 1000 && curf > (1000-minDiff)) {
\r
712 if (curf <= 10*(int)ctl->scaleAmount ) {
\r
714 if (abs < 0) abs=-abs;
\r
715 if (abs < minDiff) {
\r
723 logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,take this",curf);
\r
724 scalenum=jpegFactors[j].num;
\r
725 scaledenom=jpegFactors[j].denom;
\r
730 ctl->scale=100*scalenum/(scaledenom*scaleafter);
\r
732 logger->log("BJpeg", Log::DEBUG, "JPEG scaling found scale=%i num=%i denom=%i after=%i result=%i scale=%i%% ",
\r
733 scale,scalenum,scaledenom,scaleafter,scale*ctl->scale/100,ctl->scale);
\r
735 cinfo.scale_denom=scaledenom;
\r
736 cinfo.scale_num=scalenum;
\r
740 //set drawing area according to picture
\r
741 ctl->area.w=ctl->picw;
\r
742 ctl->area.h=ctl->pich;
\r
745 //now we know the scaling
\r
746 //compute the scaled size and position
\r
748 jpeg_start_decompress(&cinfo);
\r
749 //picturew/h is now the output width from the decompressor and afterscaler
\r
750 //this is unrotated
\r
751 picturew=cinfo.output_width;
\r
752 pictureh=cinfo.output_height;
\r
753 if (scaleafter > 1) {
\r
754 picturew=picturew/scaleafter;
\r
755 pictureh=pictureh/scaleafter;
\r
757 //if our image is smaller - center it
\r
758 if (! ctl->enlarge) {
\r
759 if (ctl->finalRotation == ROT_90 || ctl->finalRotation == ROT_270) {
\r
761 xpos=(((int)ctl->area.w)-dim)/2;
\r
763 ypos=(((int)ctl->area.h)-dim)/2;
\r
766 xpos=(((int)ctl->area.w)-dim)/2;
\r
768 ypos=(((int)ctl->area.h)-dim)/2;
\r
770 if (xpos <0) xpos=0;
\r
771 if (ypos <0) ypos=0;
\r
775 //remember the jpeg dimensions for computing the buffer
\r
776 UINT jpegwidth=cinfo.output_width;
\r
777 UINT jpegheight=cinfo.output_height;
\r
778 logger->log("BJpeg", Log::DEBUG, "JPEG startup done pw=%i ph=%i, xo=%i,yo=%i, jw=%i, jh=%i, rot=%d",
\r
779 picturew, pictureh,xpos,ypos,jpegwidth,jpegheight,(int)ctl->finalRotation*90);
\r
781 //fill the background
\r
782 sfc->fillblt(ctl->area.x,ctl->area.y,ctl->area.w,ctl->area.h,backgroundColour);
\r
784 //line len in bytes (3 bytes per Pixel) - for buffer (can be bigger then surface)
\r
785 int linelen=jpegwidth*3;
\r
787 // MAKE THE 2D ARRAY
\r
788 buffer = (unsigned char*)malloc(jpegheight * linelen);
\r
789 logger->log("BJpeg", Log::DEBUG, "Buffer allocated at %p, lines = %i linelen = %i", buffer, jpegheight, linelen);
\r
790 if (buffer == NULL) {
\r
791 if (rdr) jpeg_memio_cleanup(&cinfo);
\r
792 jpeg_destroy_decompress(&cinfo);
\r
793 logger->log("BJpeg", Log::ERR, "JPEG error - no room for buffer");
\r
794 SNPRINTF(ctl->error,100,"no room for buffer");
\r
800 //unsigned char lbuf[linelen];
\r
801 unsigned char *lbuf=new unsigned char[linelen*scaleafter];
\r
802 unsigned char * ptr=lbuf;
\r
805 unsigned char * ptr=buffer;
\r
811 sfc->startFastDraw();//Tell the surface, that we will draw a lot of pixel,
\r
812 //so that performance, can be optimized
\r
813 logger->log("BJpeg", Log::DEBUG, "start drawing ");
\r
815 //factors to base 1024
\r
817 if (scaleafter > 1) {
\r
818 colincr=3*scaleafter-3;
\r
819 fac=1024/(scaleafter*scaleafter);
\r
821 logger->log("BJpeg", Log::DEBUG, "jpeg draw scale %d image: %u %u, picture: %u %u", scaleafter,picturew,pictureh,jpegwidth,jpegheight);
\r
822 while (cinfo.output_scanline < jpegheight)
\r
824 // logger->log("BJpeg", Log::DEBUG, "%i", rowsread);
\r
825 rowsread += jpeg_read_scanlines(&cinfo,&ptr,1);
\r
829 if (scaleafter > 1) {
\r
830 if (rowsread % scaleafter != scaleafter-1) {
\r
831 //this simple approach wold maybe forget scaleafter -1 lines at the end...
\r
837 drawLine(sfc,ctl->finalRotation,ptr,scaleafter,picturew,pictureh,xpos,ypos,outy,linelen,colincr,scaleafter,fac);
\r
842 sfc->endFastDraw();
\r
844 logger->log("BJpeg", Log::DEBUG, "Done all jpeg_read");
\r
846 jpeg_finish_decompress(&cinfo);
\r
847 jpeg_memio_cleanup(&cinfo);
\r
848 jpeg_destroy_decompress(&cinfo);
\r
850 logger->log("BJpeg", Log::DEBUG, "jpeg shutdown done");
\r
851 rdr->drawingDone();
\r
855 //Tell the surface, that we will draw a lot of pixel,
\r
856 //so that performance, can be optimized
\r
857 sfc->startFastDraw();
\r
858 logger->log("BJpeg", Log::DEBUG, "jpeg start buffer draw" );
\r
859 unsigned char* p=buffer; //start of first row
\r
860 UINT rowincr=linelen*scaleafter;
\r
861 //for simplicity omit last line to avoid running out of buffer
\r
862 for (y = 0; y < pictureh-1 ;y++)
\r
864 drawLine(sfc,ctl->finalRotation,p,scaleafter,picturew,pictureh,xpos,ypos,y,linelen,colincr,scaleafter,fac);
\r
867 sfc->endFastDraw();
\r
868 logger->log("BJpeg", Log::DEBUG, "end draw");
\r
873 logger->log("BJpeg", Log::DEBUG, "deleted buffer");
\r
879 bool WJpegComplex::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, DrawStyle & backgroundColour) {
\r
884 //get my own surface
\r
885 Surface * WJpegComplex::getSurface(Region & r) {
\r
886 r.x=getRootBoxOffsetX();
\r
887 r.y=getRootBoxOffsetY();
\r
890 return Boxx::getSurface();
\r