1 /*******************************************************
2 
3    CoolReader Engine
4 
5    lvimg.cpp:  Image formats support
6 
7    (c) Vadim Lopatin, 2000-2006
8    This source code is distributed under the terms of
9    GNU General Public License
10 
11    See LICENSE file for details
12 
13 *******************************************************/
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 
19 #define XMD_H
20 
21 #include "../include/lvimg.h"
22 #include "../include/lvtinydom.h"
23 #include "../include/crlog.h"
24 
25 #if (USE_LIBPNG==1)
26 #include <png.h>
27 #endif
28 
29 #if (USE_LIBJPEG==1)
30 
31 //#include "../../wxWidgets/src/jpeg/jinclude.h"
32 extern "C" {
33 #include <jpeglib.h>
34 }
35 
36 #include <jerror.h>
37 
38 #if (USE_NANOSVG==1)
39 // Support for SVG
40 #include <math.h>
41 #define NANOSVG_ALL_COLOR_KEYWORDS
42 #define NANOSVG_IMPLEMENTATION
43 #define NANOSVGRAST_IMPLEMENTATION
44 #define STB_IMAGE_WRITE_IMPLEMENTATION
45 #include <nanosvg.h>
46 #include <nanosvgrast.h>
47 #include <stb_image_write.h> // for svg to png conversion
48 #endif
49 
50 #if !defined(HAVE_WXJPEG_BOOLEAN)
51 typedef boolean wxjpeg_boolean;
52 #endif
53 
54 #endif
55 
56 static lUInt32 NEXT_CACHEABLE_OBJECT_ID = 1;
CacheableObject()57 CacheableObject::CacheableObject() : _callback(NULL), _cache(NULL)
58 {
59 	_objectId = ++NEXT_CACHEABLE_OBJECT_ID;
60 }
61 
applyPadding(lvRect & dstPadding) const62 void CR9PatchInfo::applyPadding(lvRect & dstPadding) const
63 {
64 	if (dstPadding.left < padding.left)
65 		dstPadding.left = padding.left;
66 	if (dstPadding.right < padding.right)
67 		dstPadding.right = padding.right;
68 	if (dstPadding.top < padding.top)
69 		dstPadding.top = padding.top;
70 	if (dstPadding.bottom < padding.bottom)
71 		dstPadding.bottom = padding.bottom;
72 }
73 
fixNegative(int n[4])74 static void fixNegative(int n[4]) {
75 	int d1 = n[1] - n[0];
76 	int d2 = n[3] - n[2];
77 	if (d1 + d2 > 0) {
78 		n[1] = n[2] = n[0] + (n[3] - n[0]) * d1 / (d1 + d2);
79 	} else {
80 		n[1] = n[2] = (n[0] + n [3]) / 2;
81 	}
82 }
83 
84 /// caclulate dst and src rectangles (src does not include layout frame)
calcRectangles(const lvRect & dst,const lvRect & src,lvRect dstitems[9],lvRect srcitems[9]) const85 void CR9PatchInfo::calcRectangles(const lvRect & dst, const lvRect & src, lvRect dstitems[9], lvRect srcitems[9]) const {
86 	for (int i=0; i<9; i++) {
87 		srcitems[i].clear();
88 		dstitems[i].clear();
89 	}
90 	if (dst.isEmpty() || src.isEmpty())
91 		return;
92 
93 	int sx[4], sy[4], dx[4], dy[4];
94 	sx[0] = src.left;
95 	sx[1] = src.left + frame.left;
96 	sx[2] = src.right - frame.right;
97 	sx[3] = src.right;
98 	sy[0] = src.top;
99 	sy[1] = src.top + frame.top;
100 	sy[2] = src.bottom - frame.bottom;
101 	sy[3] = src.bottom;
102 	dx[0] = dst.left;
103 	dx[1] = dst.left + frame.left;
104 	dx[2] = dst.right - frame.right;
105 	dx[3] = dst.right;
106 	dy[0] = dst.top;
107 	dy[1] = dst.top + frame.top;
108 	dy[2] = dst.bottom - frame.bottom;
109 	dy[3] = dst.bottom;
110 	if (dx[1] > dx[2]) {
111 		// shrink horizontal
112 		fixNegative(dx);
113 	}
114 	if (dy[1] > dy[2]) {
115 		// shrink vertical
116 		fixNegative(dy);
117 	}
118 	// fill calculated rectangles
119 	for (int y = 0; y<3; y++) {
120 		for (int x = 0; x < 3; x++) {
121 			int i = y * 3 + x;
122 			srcitems[i].left = sx[x];
123 			srcitems[i].right = sx[x + 1];
124 			srcitems[i].top = sy[y];
125 			srcitems[i].bottom = sy[y + 1];
126 			dstitems[i].left = dx[x];
127 			dstitems[i].right = dx[x + 1];
128 			dstitems[i].top = dy[y];
129 			dstitems[i].bottom = dy[y + 1];
130 		}
131 	}
132 }
133 
134 
135 class CRNinePatchDecoder : public LVImageDecoderCallback
136 {
137 	int _dx;
138 	int _dy;
139 	CR9PatchInfo * _info;
140 public:
CRNinePatchDecoder(int dx,int dy,CR9PatchInfo * info)141 	CRNinePatchDecoder(int dx, int dy, CR9PatchInfo * info) : _dx(dx), _dy(dy), _info(info) {
142 	}
~CRNinePatchDecoder()143     virtual ~CRNinePatchDecoder() { }
OnStartDecode(LVImageSource * obj)144     virtual void OnStartDecode( LVImageSource * obj ) {
145         CR_UNUSED(obj);
146     }
isUsedPixel(lUInt32 pixel)147     bool isUsedPixel(lUInt32 pixel) {
148     	return (pixel == 0x000000); // black
149     }
decodeHLine(lUInt32 * line,int & x0,int & x1)150     void decodeHLine(lUInt32 * line, int & x0, int & x1) {
151     	bool foundUsed = false;
152     	for (int x = 0; x < _dx; x++) {
153     		if (isUsedPixel(line[x])) {
154     			if (!foundUsed) {
155     				x0 = x;
156         			foundUsed = true;
157     			}
158     			x1 = x + 1;
159     		}
160     	}
161     }
decodeVLine(lUInt32 pixel,int y,int & y0,int & y1)162     void decodeVLine(lUInt32 pixel, int y, int & y0, int & y1) {
163     	if (isUsedPixel(pixel)) {
164     		if (y0 == 0)
165     			y0 = y;
166     		y1 = y + 1;
167     	}
168     }
OnLineDecoded(LVImageSource * obj,int y,lUInt32 * data)169     virtual bool OnLineDecoded( LVImageSource * obj, int y, lUInt32 * data ) {
170         CR_UNUSED(obj);
171         if (y == 0) {
172     		decodeHLine(data, _info->frame.left, _info->frame.right);
173     	} else if (y == _dy - 1) {
174     		decodeHLine(data, _info->padding.left, _info->padding.right);
175     	} else {
176     		decodeVLine(data[0], y, _info->frame.top, _info->frame.bottom);
177     		decodeVLine(data[_dx - 1], y, _info->padding.top, _info->padding.bottom);
178     	}
179     	return true;
180     }
OnEndDecode(LVImageSource * obj,bool errors)181     virtual void OnEndDecode( LVImageSource * obj, bool errors ) {
182         CR_UNUSED2(obj, errors);
183     }
184 };
185 
186 
fixNegative(int & n)187 static void fixNegative(int & n) {
188 	if (n < 0)
189 		n = 0;
190 }
DetectNinePatch()191 CR9PatchInfo * LVImageSource::DetectNinePatch()
192 {
193 	if (_ninePatch)
194 		return _ninePatch;
195 	_ninePatch = new CR9PatchInfo();
196 	CRNinePatchDecoder decoder(GetWidth(), GetHeight(), _ninePatch);
197 	Decode(&decoder);
198 	if (_ninePatch->frame.left > 0 && _ninePatch->frame.top > 0
199 			&& _ninePatch->frame.left < _ninePatch->frame.right
200 			&& _ninePatch->frame.top < _ninePatch->frame.bottom) {
201 		// remove 1 pixel frame
202 		_ninePatch->padding.left--;
203 		_ninePatch->padding.top--;
204 		_ninePatch->padding.right = GetWidth() - _ninePatch->padding.right - 1;
205 		_ninePatch->padding.bottom = GetHeight() - _ninePatch->padding.bottom - 1;
206 		fixNegative(_ninePatch->padding.left);
207 		fixNegative(_ninePatch->padding.top);
208 		fixNegative(_ninePatch->padding.right);
209 		fixNegative(_ninePatch->padding.bottom);
210 		_ninePatch->frame.left--;
211 		_ninePatch->frame.top--;
212 		_ninePatch->frame.right = GetWidth() - _ninePatch->frame.right - 1;
213 		_ninePatch->frame.bottom = GetHeight() - _ninePatch->frame.bottom - 1;
214 		fixNegative(_ninePatch->frame.left);
215 		fixNegative(_ninePatch->frame.top);
216 		fixNegative(_ninePatch->frame.right);
217 		fixNegative(_ninePatch->frame.bottom);
218 	} else {
219 		delete _ninePatch;
220 		_ninePatch = NULL;
221 	}
222 	return _ninePatch;
223 }
224 
~LVImageSource()225 LVImageSource::~LVImageSource() {
226 	if (_ninePatch)
227 		delete _ninePatch;
228 }
229 
230 
231 class LVNodeImageSource : public LVImageSource
232 {
233 protected:
234     ldomNode *  _node;
235     LVStreamRef _stream;
236     int _width;
237     int _height;
238 public:
LVNodeImageSource(ldomNode * node,LVStreamRef stream)239     LVNodeImageSource( ldomNode * node, LVStreamRef stream )
240         : _node(node), _stream(stream), _width(0), _height(0)
241     {
242 
243     }
244 
GetSourceNode()245     ldomNode * GetSourceNode() { return _node; }
GetSourceStream()246     virtual LVStream * GetSourceStream() { return _stream.get(); }
Compact()247     virtual void   Compact() { }
GetWidth()248     virtual int    GetWidth() { return _width; }
GetHeight()249     virtual int    GetHeight() { return _height; }
250     virtual bool   Decode( LVImageDecoderCallback * callback ) = 0;
~LVNodeImageSource()251     virtual ~LVNodeImageSource() {}
252 };
253 
254 #if (USE_LIBJPEG==1)
255 
256 METHODDEF(void)
257 cr_jpeg_error (j_common_ptr cinfo);
258 
259 
260 typedef struct {
261     struct jpeg_source_mgr pub;   /* public fields */
262     LVStream * stream;        /* source stream */
263     JOCTET * buffer;      /* start of buffer */
264     bool start_of_file;    /* have we gotten any data yet? */
265 } cr_jpeg_source_mgr;
266 
267 #define INPUT_BUF_SIZE  4096    /* choose an efficiently fread'able size */
268 
269 /*
270  * Initialize source --- called by jpeg_read_header
271  * before any data is actually read.
272  */
273 
274 METHODDEF(void)
cr_init_source(j_decompress_ptr cinfo)275 cr_init_source (j_decompress_ptr cinfo)
276 {
277     cr_jpeg_source_mgr * src = (cr_jpeg_source_mgr*) cinfo->src;
278 
279     /* We reset the empty-input-file flag for each image,
280      * but we don't clear the input buffer.
281      * This is correct behavior for reading a series of images from one source.
282      */
283     src->start_of_file = true;
284 }
285 
286 /*
287  * Fill the input buffer --- called whenever buffer is emptied.
288  *
289  * In typical applications, this should read fresh data into the buffer
290  * (ignoring the current state of next_input_byte & bytes_in_buffer),
291  * reset the pointer & count to the start of the buffer, and return TRUE
292  * indicating that the buffer has been reloaded.  It is not necessary to
293  * fill the buffer entirely, only to obtain at least one more byte.
294  *
295  * There is no such thing as an EOF return.  If the end of the file has been
296  * reached, the routine has a choice of ERREXIT() or inserting fake data into
297  * the buffer.  In most cases, generating a warning message and inserting a
298  * fake EOI marker is the best course of action --- this will allow the
299  * decompressor to output however much of the image is there.  However,
300  * the resulting error message is misleading if the real problem is an empty
301  * input file, so we handle that case specially.
302  *
303  * In applications that need to be able to suspend compression due to input
304  * not being available yet, a FALSE return indicates that no more data can be
305  * obtained right now, but more may be forthcoming later.  In this situation,
306  * the decompressor will return to its caller (with an indication of the
307  * number of scanlines it has read, if any).  The application should resume
308  * decompression after it has loaded more data into the input buffer.  Note
309  * that there are substantial restrictions on the use of suspension --- see
310  * the documentation.
311  *
312  * When suspending, the decompressor will back up to a convenient restart point
313  * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
314  * indicate where the restart point will be if the current call returns FALSE.
315  * Data beyond this point must be rescanned after resumption, so move it to
316  * the front of the buffer rather than discarding it.
317  */
318 
319 METHODDEF(wxjpeg_boolean)
cr_fill_input_buffer(j_decompress_ptr cinfo)320 cr_fill_input_buffer (j_decompress_ptr cinfo)
321 {
322     cr_jpeg_source_mgr * src = (cr_jpeg_source_mgr *) cinfo->src;
323     lvsize_t bytesRead = 0;
324     if ( src->stream->Read( src->buffer, INPUT_BUF_SIZE, &bytesRead ) != LVERR_OK )
325         cr_jpeg_error((jpeg_common_struct*)cinfo);
326 
327     if (bytesRead <= 0) {
328         if (src->start_of_file) /* Treat empty input file as fatal error */
329             ERREXIT(cinfo, JERR_INPUT_EMPTY);
330         WARNMS(cinfo, JWRN_JPEG_EOF);
331         /* Insert a fake EOI marker */
332         src->buffer[0] = (JOCTET) 0xFF;
333         src->buffer[1] = (JOCTET) JPEG_EOI;
334         bytesRead = 2;
335     }
336 
337     src->pub.next_input_byte = src->buffer;
338     src->pub.bytes_in_buffer = (lUInt32)bytesRead;
339     src->start_of_file = false;
340 
341     return TRUE;
342 }
343 
344 /*
345  * Skip data --- used to skip over a potentially large amount of
346  * uninteresting data (such as an APPn marker).
347  *
348  * Writers of suspendable-input applications must note that skip_input_data
349  * is not granted the right to give a suspension return.  If the skip extends
350  * beyond the data currently in the buffer, the buffer can be marked empty so
351  * that the next read will cause a fill_input_buffer call that can suspend.
352  * Arranging for additional bytes to be discarded before reloading the input
353  * buffer is the application writer's problem.
354  */
355 
356 METHODDEF(void)
cr_skip_input_data(j_decompress_ptr cinfo,long num_bytes)357 cr_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
358 {
359     cr_jpeg_source_mgr * src = (cr_jpeg_source_mgr *) cinfo->src;
360 
361     /* Just a dumb implementation for now.  Could use fseek() except
362      * it doesn't work on pipes.  Not clear that being smart is worth
363      * any trouble anyway --- large skips are infrequent.
364      */
365     if (num_bytes > 0) {
366         while (num_bytes > (long) src->pub.bytes_in_buffer) {
367           num_bytes -= (long) src->pub.bytes_in_buffer;
368           (void) cr_fill_input_buffer(cinfo);
369           /* note we assume that fill_input_buffer will never return FALSE,
370            * so suspension need not be handled.
371            */
372         }
373         src->pub.next_input_byte += (size_t) num_bytes;
374         src->pub.bytes_in_buffer -= (size_t) num_bytes;
375     }
376 }
377 
378 /*
379  * An additional method that can be provided by data source modules is the
380  * resync_to_restart method for error recovery in the presence of RST markers.
381  * For the moment, this source module just uses the default resync method
382  * provided by the JPEG library.  That method assumes that no backtracking
383  * is possible.
384  */
385 GLOBAL(wxjpeg_boolean)
cr_resync_to_restart(j_decompress_ptr,int)386 cr_resync_to_restart (j_decompress_ptr, int)
387 {
388     return FALSE;
389 }
390 
391 
392 /*
393  * Terminate source --- called by jpeg_finish_decompress
394  * after all data has been read.  Often a no-op.
395  *
396  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
397  * application must deal with any cleanup that should happen even
398  * for error exit.
399  */
400 
401 METHODDEF(void)
cr_term_source(j_decompress_ptr)402 cr_term_source (j_decompress_ptr)
403 {
404   /* no work necessary here */
405 }
406 
407 GLOBAL(void)
cr_jpeg_src(j_decompress_ptr cinfo,LVStream * stream)408 cr_jpeg_src (j_decompress_ptr cinfo, LVStream * stream)
409 {
410     cr_jpeg_source_mgr * src;
411 
412     /* The source object and input buffer are made permanent so that a series
413      * of JPEG images can be read from the same file by calling jpeg_stdio_src
414      * only before the first one.  (If we discarded the buffer at the end of
415      * one image, we'd likely lose the start of the next one.)
416      * This makes it unsafe to use this manager and a different source
417      * manager serially with the same JPEG object.  Caveat programmer.
418      */
419     if (cinfo->src == NULL) { /* first time for this JPEG object? */
420         src = new cr_jpeg_source_mgr();
421         cinfo->src = (struct jpeg_source_mgr *) src;
422         src->buffer = new JOCTET[INPUT_BUF_SIZE];
423     }
424 
425     src = (cr_jpeg_source_mgr *) cinfo->src;
426     src->pub.init_source = cr_init_source;
427     src->pub.fill_input_buffer = cr_fill_input_buffer;
428     src->pub.skip_input_data = cr_skip_input_data;
429     src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
430     src->pub.term_source = cr_term_source;
431     src->stream = stream;
432     src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
433     src->pub.next_input_byte = NULL; /* until buffer loaded */
434 }
435 
436 GLOBAL(void)
cr_jpeg_src_free(j_decompress_ptr cinfo)437 cr_jpeg_src_free (j_decompress_ptr cinfo)
438 {
439     cr_jpeg_source_mgr * src = (cr_jpeg_source_mgr *) cinfo->src;
440     if ( src && src->buffer )
441     {
442         delete[] src->buffer;
443         src->buffer = NULL;
444     }
445     delete src;
446 }
447 
448 
449 /*
450  * ERROR HANDLING:
451  *
452  * The JPEG library's standard error handler (jerror.c) is divided into
453  * several "methods" which you can override individually.  This lets you
454  * adjust the behavior without duplicating a lot of code, which you might
455  * have to update with each future release.
456  *
457  * Our example here shows how to override the "error_exit" method so that
458  * control is returned to the library's caller when a fatal error occurs,
459  * rather than calling exit() as the standard error_exit method does.
460  *
461  * We use C's setjmp/longjmp facility to return control.  This means that the
462  * routine which calls the JPEG library must first execute a setjmp() call to
463  * establish the return point.  We want the replacement error_exit to do a
464  * longjmp().  But we need to make the setjmp buffer accessible to the
465  * error_exit routine.  To do this, we make a private extension of the
466  * standard JPEG error handler object.  (If we were using C++, we'd say we
467  * were making a subclass of the regular error handler.)
468  *
469  * Here's the extended error handler struct:
470  */
471 
472 struct my_error_mgr {
473   struct jpeg_error_mgr pub;	/* "public" fields */
474 
475   jmp_buf setjmp_buffer;	/* for return to caller */
476 };
477 
478 typedef struct my_error_mgr * my_error_ptr;
479 
480 /*
481  * Here's the routine that will replace the standard error_exit method:
482  */
483 
484 METHODDEF(void)
cr_jpeg_error(j_common_ptr cinfo)485 cr_jpeg_error (j_common_ptr cinfo)
486 {
487     //fprintf(stderr, "cr_jpeg_error() : fatal error while decoding JPEG image\n");
488 
489     char buffer[JMSG_LENGTH_MAX];
490 
491     /* Create the message */
492     (*cinfo->err->format_message) (cinfo, buffer);
493     CRLog::error("cr_jpeg_error: %s", buffer);
494 
495     /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
496     my_error_ptr myerr = (my_error_ptr) cinfo->err;
497 
498     /* Always display the message. */
499     /* We could postpone this until after returning, if we chose. */
500     //(*cinfo->err->output_message) (cinfo);
501 
502     //CRLog::error("cr_jpeg_error : returning control to setjmp point %08x", &myerr->setjmp_buffer);
503     /* Return control to the setjmp point */
504     longjmp(myerr->setjmp_buffer, -1);
505 }
506 
507 #endif
508 
509 #if (USE_LIBPNG==1)
510 
511 class LVPngImageSource : public LVNodeImageSource
512 {
513 protected:
514 public:
515     LVPngImageSource( ldomNode * node, LVStreamRef stream );
516     virtual ~LVPngImageSource();
517     virtual void   Compact();
518     virtual bool   Decode( LVImageDecoderCallback * callback );
519     static bool CheckPattern( const lUInt8 * buf, int len );
520 };
521 
522 
lvpng_error_func(png_structp png,png_const_charp msg)523 static void lvpng_error_func (png_structp png, png_const_charp msg)
524 {
525     CRLog::error("libpng: %s", msg);
526     longjmp(png_jmpbuf(png), 1);
527 }
528 
lvpng_warning_func(png_structp png,png_const_charp msg)529 static void lvpng_warning_func (png_structp png, png_const_charp msg)
530 {
531     CR_UNUSED(png);
532     CRLog::warn("libpng: %s", msg);
533 }
534 
lvpng_read_func(png_structp png,png_bytep buf,png_size_t len)535 static void lvpng_read_func(png_structp png, png_bytep buf, png_size_t len)
536 {
537     LVNodeImageSource * obj = (LVNodeImageSource *) png_get_io_ptr(png);
538     LVStream * stream = obj->GetSourceStream();
539     lvsize_t bytesRead = 0;
540     if ( stream->Read( buf, (int)len, &bytesRead )!=LVERR_OK || bytesRead!=len )
541         longjmp(png_jmpbuf(png), 1);
542 }
543 
544 #endif
545 
546 /// dummy image source to show invalid image
547 class LVDummyImageSource : public LVImageSource
548 {
549 protected:
550     ldomNode *  _node;
551     int _width;
552     int _height;
553 public:
LVDummyImageSource(ldomNode * node,int width,int height)554     LVDummyImageSource( ldomNode * node, int width, int height )
555         : _node(node), _width(width), _height(height)
556     {
557 
558     }
559 
GetSourceNode()560     ldomNode * GetSourceNode() { return _node; }
GetSourceStream()561     virtual LVStream * GetSourceStream() { return NULL; }
Compact()562     virtual void   Compact() { }
GetWidth()563     virtual int    GetWidth() { return _width; }
GetHeight()564     virtual int    GetHeight() { return _height; }
Decode(LVImageDecoderCallback * callback)565     virtual bool   Decode( LVImageDecoderCallback * callback )
566     {
567         if ( callback )
568         {
569             callback->OnStartDecode(this);
570             lUInt32 * row = new lUInt32[ _width ];
571             for (int i=0; i<_height; i++)
572             {
573                 if ( i==0 || i==_height-1 )
574                 {
575                     for ( int x=0; x<_width; x++ )
576                         row[ x ] = 0x000000;
577                 }
578                 else
579                 {
580                     for ( int x=1; x<_width-1; x++ )
581                         row[ x ] = 0xFFFFFF;
582                     row[ 0 ] = 0x000000;
583                     row[ _width-1 ] = 0x000000;
584                 }
585                 callback->OnLineDecoded(this, i, row);
586             }
587             delete[] row;
588             callback->OnEndDecode(this, false);
589         }
590         return true;
591     }
~LVDummyImageSource()592     virtual ~LVDummyImageSource() {}
593 };
594 
595 /// dummy image source to show invalid image
596 class LVXPMImageSource : public LVImageSource
597 {
598 protected:
599     char ** _rows;
600     lUInt32 * _palette;
601     lUInt8 _pchars[128];
602     int _width;
603     int _height;
604     int _ncolors;
605 public:
LVXPMImageSource(const char ** data)606     LVXPMImageSource( const char ** data )
607         : _rows(NULL), _palette(NULL), _width(0), _height(0), _ncolors(0)
608     {
609         bool err = false;
610         int charsperpixel;
611         if ( sscanf( data[0], "%d %d %d %d", &_width, &_height, &_ncolors, &charsperpixel )!=4 ) {
612             err = true;
613         } else if ( _width>0 && _width<255 && _height>0 && _height<255 && _ncolors>=2 && _ncolors<255 && charsperpixel == 1 ) {
614             _rows = new char * [_height];
615             for ( int i=0; i<_height; i++ ) {
616                 _rows[i] = new char[_width];
617                 memcpy( _rows[i], data[i+1+_ncolors], _width );
618             }
619 
620             _palette = new lUInt32[_ncolors];
621             memset( _pchars, 0, 128 );
622             for ( int cl=0; cl<_ncolors; cl++ ) {
623                 const char * src = data[1+cl];
624                 _pchars[((unsigned)(*src++)) & 127] = cl;
625                 if ( (*src++)!=' ' || (*src++)!='c' || (*src++)!=' ' ) {
626                     err = true;
627                     break;
628                 }
629                 if ( *src == '#' ) {
630                     src++;
631                     unsigned c;
632                     if ( sscanf(src, "%x", &c) != 1 ) {
633                         err = true;
634                         break;
635                     }
636                     _palette[cl] = (lUInt32)c;
637                 } else if ( !strcmp( src, "None" ) )
638                     _palette[cl] = 0xFF000000;
639                 else if ( !strcmp( src, "Black" ) )
640                     _palette[cl] = 0x000000;
641                 else if ( !strcmp( src, "White" ) )
642                     _palette[cl] = 0xFFFFFF;
643                 else
644                     _palette[cl] = 0x000000;
645             }
646         } else {
647             err = true;
648         }
649         if ( err ) {
650             _width = _height = 0;
651         }
652     }
~LVXPMImageSource()653     virtual ~LVXPMImageSource()
654     {
655         if ( _rows ) {
656             for ( int i=0; i<_height; i++ ) {
657                 delete[]( _rows[i] );
658             }
659             delete[] _rows;
660         }
661         if ( _palette )
662             delete[] _palette;
663     }
664 
GetSourceNode()665     ldomNode * GetSourceNode() { return NULL; }
GetSourceStream()666     virtual LVStream * GetSourceStream() { return NULL; }
Compact()667     virtual void   Compact() { }
GetWidth()668     virtual int    GetWidth() { return _width; }
GetHeight()669     virtual int    GetHeight() { return _height; }
Decode(LVImageDecoderCallback * callback)670     virtual bool   Decode( LVImageDecoderCallback * callback )
671     {
672         if ( callback )
673         {
674             callback->OnStartDecode(this);
675             lUInt32 * row = new lUInt32[ _width ];
676             for (int i=0; i<_height; i++)
677             {
678                 const char * src = _rows[i];
679                 for ( int x=0; x<_width; x++ ) {
680                     row[x] = _palette[_pchars[(unsigned)src[x]]];
681                 }
682                 callback->OnLineDecoded(this, i, row);
683             }
684             delete[] row;
685             callback->OnEndDecode(this, false);
686         }
687         return true;
688     }
689 };
690 
LVCreateXPMImageSource(const char * data[])691 LVImageSourceRef LVCreateXPMImageSource( const char * data[] )
692 {
693     LVImageSourceRef ref( new LVXPMImageSource( data ) );
694     if ( ref->GetWidth()<1 )
695         return LVImageSourceRef();
696     return ref;
697 }
698 
699 
700 #if (USE_LIBJPEG==1)
701 
702 class LVJpegImageSource : public LVNodeImageSource
703 {
704     my_error_mgr jerr;
705     jpeg_decompress_struct cinfo;
706 protected:
707 public:
LVJpegImageSource(ldomNode * node,LVStreamRef stream)708     LVJpegImageSource( ldomNode * node, LVStreamRef stream )
709         : LVNodeImageSource(node, stream)
710     {
711     	//CRLog::trace("creating LVJpegImageSource");
712 
713         // testing setjmp
714 
715 //        jmp_buf buf;
716 //        if (setjmp(buf)) {
717 //            CRLog::trace("longjmp is working ok");
718 //            return;
719 //        }
720 //        longjmp(buf, -1);
721     }
~LVJpegImageSource()722     virtual ~LVJpegImageSource() {}
Compact()723     virtual void   Compact() { }
Decode(LVImageDecoderCallback * callback)724     virtual bool   Decode( LVImageDecoderCallback * callback )
725     {
726     	//CRLog::trace("LVJpegImageSource::decode called");
727         memset(&cinfo, 0, sizeof(jpeg_decompress_struct));
728         /* Step 1: allocate and initialize JPEG decompression object */
729 
730 		/* We use our private extension JPEG error handler.
731 		 * Note that this struct must live as long as the main JPEG parameter
732 		 * struct, to avoid dangling-pointer problems.
733 		 */
734 
735         /* We set up the normal JPEG error routines, then override error_exit. */
736         cinfo.err = jpeg_std_error(&jerr.pub);
737         jerr.pub.error_exit = cr_jpeg_error;
738 
739         /* Now we can initialize the JPEG decompression object. */
740         jpeg_create_decompress(&cinfo);
741 
742         lUInt8 * buffer = NULL;
743         lUInt32 * row = NULL;
744 
745         if (setjmp(jerr.setjmp_buffer)) {
746         	CRLog::error("JPEG setjmp error handling");
747 	    /* If we get here, the JPEG code has signaled an error.
748 	     * We need to clean up the JPEG object, close the input file, and return.
749 	     */
750             if ( buffer )
751                 delete[] buffer;
752             if ( row )
753                 delete[] row;
754         	CRLog::debug("JPEG decoder cleanup");
755             cr_jpeg_src_free (&cinfo);
756             jpeg_destroy_decompress(&cinfo);
757             return false;
758 	     }
759 
760             _stream->SetPos( 0 );
761             /* Step 2: specify data source (eg, a file) */
762             cr_jpeg_src( &cinfo, _stream.get() );
763             /* Step 3: read file parameters with jpeg_read_header() */
764 
765             //fprintf(stderr, "    try to call jpeg_read_header...\n");
766             (void) jpeg_read_header(&cinfo, TRUE);
767             /* We can ignore the return value from jpeg_read_header since
768              *   (a) suspension is not possible with the stdio data source, and
769              *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
770              * See libjpeg.doc for more info.
771              */
772             _width = cinfo.image_width;
773             _height = cinfo.image_height;
774             //fprintf(stderr, "    jpeg_read_header() finished succesfully: image size = %d x %d\n", _width, _height);
775 
776             if ( callback )
777             {
778                 callback->OnStartDecode(this);
779                 /* Step 4: set parameters for decompression */
780 
781                 /* In this example, we don't need to change any of the defaults set by
782                  * jpeg_read_header(), so we do nothing here.
783                  */
784                 cinfo.out_color_space = JCS_RGB;
785 
786                 /* Step 5: Start decompressor */
787 
788                 (void) jpeg_start_decompress(&cinfo);
789                 /* We can ignore the return value since suspension is not possible
790                  * with the stdio data source.
791                  */
792                 buffer = new lUInt8 [ cinfo.output_width * cinfo.output_components ];
793                 row = new lUInt32 [ cinfo.output_width ];
794                 /* Step 6: while (scan lines remain to be read) */
795                 /*           jpeg_read_scanlines(...); */
796 
797                 /* Here we use the library's state variable cinfo.output_scanline as the
798                  * loop counter, so that we don't have to keep track ourselves.
799                  */
800                 while (cinfo.output_scanline < cinfo.output_height) {
801                     int y = cinfo.output_scanline;
802                     /* jpeg_read_scanlines expects an array of pointers to scanlines.
803                      * Here the array is only one element long, but you could ask for
804                      * more than one scanline at a time if that's more convenient.
805                      */
806                     (void) jpeg_read_scanlines(&cinfo, &buffer, 1);
807                     /* Assume put_scanline_someplace wants a pointer and sample count. */
808                     lUInt8 * p = buffer;
809                     for (int x=0; x<(int)cinfo.output_width; x++)
810                     {
811                         row[x] = (((lUInt32)p[0])<<16) | (((lUInt32)p[1])<<8) | (((lUInt32)p[2])<<0);
812                         p += 3;
813                     }
814                     callback->OnLineDecoded( this, y, row );
815                 }
816                 callback->OnEndDecode(this, false);
817             }
818 
819         if ( buffer )
820             delete[] buffer;
821         if ( row )
822             delete[] row;
823         cr_jpeg_src_free (&cinfo);
824         jpeg_destroy_decompress(&cinfo);
825         return true;
826     }
CheckPattern(const lUInt8 * buf,int)827     static bool CheckPattern( const lUInt8 * buf, int )
828     {
829         //check for SOI marker at beginning of file
830         return (buf[0]==0xFF && buf[1]==0xD8);
831     }
832 };
833 
834 #endif
835 
836 
837 #if (USE_LIBPNG==1)
838 
LVPngImageSource(ldomNode * node,LVStreamRef stream)839 LVPngImageSource::LVPngImageSource( ldomNode * node, LVStreamRef stream )
840         : LVNodeImageSource(node, stream)
841 {
842 }
~LVPngImageSource()843 LVPngImageSource::~LVPngImageSource() {}
Compact()844 void LVPngImageSource::Compact() { }
Decode(LVImageDecoderCallback * callback)845 bool LVPngImageSource::Decode( LVImageDecoderCallback * callback )
846 {
847     png_structp png_ptr = NULL;
848     png_infop info_ptr = NULL;
849     _stream->SetPos( 0 );
850     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
851         (png_voidp)this, lvpng_error_func, lvpng_warning_func);
852     if ( !png_ptr )
853         return false;
854 
855     if (setjmp(png_jmpbuf(png_ptr))) {
856         _width = 0;
857         _height = 0;
858         if (png_ptr)
859         {
860             png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
861         }
862         if (callback)
863             callback->OnEndDecode(this, true); // error!
864         return false;
865     }
866 
867     //
868     info_ptr = png_create_info_struct(png_ptr);
869     if (!info_ptr)
870         lvpng_error_func(png_ptr, "cannot create png info struct");
871     png_set_read_fn(png_ptr,
872         (void*)this, lvpng_read_func);
873     png_read_info( png_ptr, info_ptr );
874 
875 
876     png_uint_32 width, height;
877     int bit_depth, color_type, interlace_type;
878     png_get_IHDR(png_ptr, info_ptr, &width, &height,
879         &bit_depth, &color_type, &interlace_type,
880         NULL, NULL);
881     _width = width;
882     _height = height;
883 
884 
885     if ( callback )
886     {
887         callback->OnStartDecode(this);
888 
889         //int png_transforms = PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_INVERT_ALPHA;
890             //PNG_TRANSFORM_PACKING|
891             //PNG_TRANSFORM_STRIP_16|
892             //PNG_TRANSFORM_INVERT_ALPHA;
893 
894         // SET TRANSFORMS
895         if (color_type & PNG_COLOR_MASK_PALETTE)
896             png_set_palette_to_rgb(png_ptr);
897 
898         if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
899 //#if PNG_LIBPNG_VER_RELEASE==7
900 #if (PNG_LIBPNG_VER_MAJOR == 1) && (PNG_LIBPNG_VER_MINOR < 4)
901             png_set_gray_1_2_4_to_8(png_ptr);
902 #else
903             png_set_expand_gray_1_2_4_to_8(png_ptr);
904 #endif
905 
906         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
907             png_set_tRNS_to_alpha(png_ptr);
908 
909         if (bit_depth == 16)
910             png_set_strip_16(png_ptr);
911 
912         png_set_invert_alpha(png_ptr);
913 
914         if (bit_depth < 8)
915             png_set_packing(png_ptr);
916 
917         //if (color_type == PNG_COLOR_TYPE_RGB)
918             png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
919 
920         //if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
921         //    png_set_swap_alpha(png_ptr);
922 
923         if (color_type == PNG_COLOR_TYPE_GRAY ||
924             color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
925                 png_set_gray_to_rgb(png_ptr);
926 
927         //if (color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
928         //    color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
929 
930         //if (color_type == PNG_COLOR_TYPE_RGB ||
931         //    color_type == PNG_COLOR_TYPE_RGB_ALPHA)
932         png_set_bgr(png_ptr);
933 
934         png_set_interlace_handling(png_ptr);
935         png_read_update_info(png_ptr,info_ptr);//update after set
936         png_bytep *image=NULL;
937         image =  new png_bytep[height];
938         for (lUInt32 i=0; i<height; i++)
939             image[i] =  new png_byte[png_get_rowbytes(png_ptr,info_ptr)];
940         png_read_image(png_ptr,image);
941         for (lUInt32 y = 0; y < height; y++)
942         {
943             callback->OnLineDecoded( this, y,  (lUInt32*) image[y] );
944         }
945         png_read_end(png_ptr, info_ptr);
946 
947         callback->OnEndDecode(this, false);
948         for (lUInt32 i=0; i<height; i++)
949             delete [] image[i];
950         delete [] image;
951     }
952     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
953 
954     return true;
955 }
956 
CheckPattern(const lUInt8 * buf,int)957 bool LVPngImageSource::CheckPattern( const lUInt8 * buf, int )
958 {
959     return( !png_sig_cmp((unsigned char *)buf, (png_size_t)0, 4) );
960 }
961 
962 #endif
963 
964 // GIF support
965 #if (USE_GIF==1)
966 
967 class LVGifImageSource;
968 class LVGifFrame;
969 
970 class LVGifImageSource : public LVNodeImageSource
971 {
972     friend class LVGifFrame;
973 protected:
974     LVGifFrame ** m_frames;
975     int m_frame_count;
976     unsigned char m_version;
977     unsigned char m_bpp;     //
978     unsigned char m_flg_gtc; // GTC (gobal table of colors) flag
979     unsigned char m_transparent_color; // index
980     unsigned char m_background_color;
981     lUInt32 * m_global_color_table;
982     bool defined_transparent_color;
983 public:
LVGifImageSource(ldomNode * node,LVStreamRef stream)984     LVGifImageSource( ldomNode * node, LVStreamRef stream )
985         : LVNodeImageSource(node, stream)
986     {
987         m_global_color_table = NULL;
988         m_frames = NULL;
989         m_frame_count = 0;
990         Clear();
991     }
992 public:
CheckPattern(const lUInt8 * buf,int)993     static bool CheckPattern( const lUInt8 * buf, int )
994     {
995         if (buf[0]!='G' || buf[1]!='I' || buf[2]!='F')
996             return false;
997         // version: '87a' or '89a'
998         if (buf[3]!='8' || buf[5]!='a')
999             return false;
1000         if (buf[4]!='7' && buf[4]!='9')
1001             return false; // bad version
1002         return true;
1003     }
Compact()1004     virtual void   Compact()
1005     {
1006         // TODO: implement compacting
1007     }
1008     virtual bool Decode( LVImageDecoderCallback * callback );
1009 
1010     int DecodeFromBuffer(unsigned char *buf, int buf_size, LVImageDecoderCallback * callback);
1011     //int LoadFromFile( const char * fname );
1012     LVGifImageSource();
1013     virtual ~LVGifImageSource();
1014     void Clear();
GetColorTable()1015     lUInt32 * GetColorTable() {
1016         if (m_flg_gtc)
1017             return m_global_color_table;
1018         else
1019             return NULL;
1020     };
1021 };
1022 
1023 class LVGifFrame
1024 {
1025 protected:
1026     int        m_cx;
1027     int        m_cy;
1028     int m_left;
1029     int m_top;
1030     unsigned char m_bpp;     // bits per pixel
1031     unsigned char m_flg_ltc; // GTC (gobal table of colors) flag
1032     unsigned char m_flg_interlaced; // interlace flag
1033 
1034     LVGifImageSource * m_pImage;
1035     lUInt32 *    m_local_color_table;
1036 
1037     unsigned char * m_buffer;
1038 public:
1039     int DecodeFromBuffer( unsigned char * buf, int buf_size, int &bytes_read );
1040     LVGifFrame(LVGifImageSource * pImage);
1041     ~LVGifFrame();
1042     void Clear();
GetColorTable()1043     lUInt32 * GetColorTable() {
1044         if (m_flg_ltc)
1045             return m_local_color_table;
1046         else
1047             return m_pImage->GetColorTable();
1048     };
Draw(LVImageDecoderCallback * callback)1049     void Draw( LVImageDecoderCallback * callback )
1050     {
1051         int w = m_pImage->GetWidth();
1052         int h = m_pImage->GetHeight();
1053         if ( w<=0 || w>4096 || h<=0 || h>4096 )
1054             return; // wrong image width
1055         callback->OnStartDecode( m_pImage );
1056         lUInt32 * line = new lUInt32[w];
1057         int background_color = m_pImage->m_background_color;
1058         int transparent_color = m_pImage->m_transparent_color;
1059         bool defined_transparent = m_pImage->defined_transparent_color;
1060         lUInt32 * pColorTable = GetColorTable();
1061         int interlacePos = 0;
1062         int interlaceTable[] = {8, 0, 8, 4, 4, 2, 2, 1, 1, 1}; // pairs: step, offset
1063         int dy = interlaceTable[interlacePos];
1064         int y = 0;
1065         for ( int i=0; i<h; i++ ) {
1066             for ( int j=0; j<w; j++ ) {
1067                 line[j] = pColorTable[background_color];
1068             }
1069             if ( i >= m_top  && i < m_top+m_cy ) {
1070                 unsigned char * p_line = m_buffer + (i-m_top)*m_cx;
1071                 for ( int x=0; x<m_cx; x++ ) {
1072                     unsigned char b = p_line[x];
1073                     if (b!=background_color) {
1074                         if (defined_transparent && b==transparent_color)
1075                             line[x + m_left] = 0xFF000000;
1076                         else line[x + m_left] = pColorTable[b];
1077                     }
1078                     else if (defined_transparent && b==transparent_color)  {
1079                         line[x + m_left] = 0xFF000000;
1080                     }
1081                 }
1082             }
1083             callback->OnLineDecoded( m_pImage, y, line );
1084             if ( m_flg_interlaced ) {
1085                 y += dy;
1086                 if ( y>=m_cy ) {
1087                     interlacePos += 2;
1088                     dy = interlaceTable[interlacePos];
1089                     y = interlaceTable[interlacePos+1];
1090                 }
1091             } else {
1092                 y++;
1093             }
1094         }
1095         delete[] line;
1096         callback->OnEndDecode( m_pImage, false );
1097     }
1098 };
1099 
~LVGifImageSource()1100 LVGifImageSource::~LVGifImageSource()
1101 {
1102     Clear();
1103 }
1104 
lRGB(lUInt32 r,lUInt32 g,lUInt32 b)1105 inline lUInt32 lRGB(lUInt32 r, lUInt32 g, lUInt32 b )
1106 {
1107     return (r<<16)|(g<<8)|b;
1108 }
1109 
skipGifExtension(unsigned char * & buf,int buf_size)1110 static bool skipGifExtension(unsigned char *&buf, int buf_size) {
1111     unsigned char * endp = buf + buf_size;
1112     if (*buf != '!')
1113         return false;
1114     buf += 2;
1115     for (;;) {
1116         if (buf >= endp)
1117             return false;
1118         unsigned blockSize = *buf;
1119         buf++;
1120         if (blockSize == 0)
1121             return true;
1122         buf += blockSize;
1123     }
1124 }
1125 
DecodeFromBuffer(unsigned char * buf,int buf_size,LVImageDecoderCallback * callback)1126 int LVGifImageSource::DecodeFromBuffer(unsigned char *buf, int buf_size, LVImageDecoderCallback * callback)
1127 {
1128     // check GIF header (6 bytes)
1129     // 'GIF'
1130     if ( !CheckPattern( buf, buf_size ) )
1131         return 0;
1132     if (buf[0]!='G' || buf[1]!='I' || buf[2]!='F')
1133         return 0;
1134     // version: '87a' or '89a'
1135     if (buf[3]!='8' || buf[5]!='a')
1136         return 0;
1137     if (buf[4]=='7')
1138         m_version = 7;
1139     else if (buf[4]=='9')
1140         m_version = 9;
1141     else
1142         return 0; // bad version
1143 
1144     // read screen descriptor
1145     unsigned char * p = buf+6;
1146 
1147     _width = p[0] + (p[1]<<8);
1148     _height = p[2] + (p[3]<<8);
1149     m_bpp = (p[4]&7)+1;
1150     m_flg_gtc = (p[4]&0x80)?1:0;
1151     m_background_color = p[5];
1152     defined_transparent_color = false;
1153     if ( !(_width>=1 && _height>=1 && _width<4096 && _height<4096 ) )
1154         return false;
1155     if ( !callback )
1156         return true;
1157     // next
1158     p+=7;
1159 
1160 
1161     // read global color table
1162     if (m_flg_gtc) {
1163         int m_color_count = 1<<m_bpp;
1164 
1165         if (m_color_count*3 + (p-buf) >= buf_size)
1166             return 0; // error
1167 
1168         m_global_color_table = new lUInt32[m_color_count];
1169         for (int i=0; i<m_color_count; i++) {
1170             m_global_color_table[i] = lRGB(p[i*3],p[i*3+1],p[i*3+2]);
1171             //m_global_color_table[i] = lRGB(p[i*3+2],p[i*3+1],p[i*3+0]);
1172         }
1173 
1174         // next
1175         p+=(m_color_count * 3);
1176     }
1177 
1178     bool found = false;
1179     bool res = true;
1180     while (res && p - buf < buf_size) {
1181         // search for delimiter char ','
1182         int recordType = *p;
1183 
1184         //            while (*p != ',' && p-buf<buf_size)
1185         //                p++;
1186         switch (recordType) {
1187         case ',': // image descriptor, ','
1188             // found image descriptor!
1189             {
1190                 LVGifFrame * pFrame = new LVGifFrame(this);
1191                 int cbRead = 0;
1192                 if (pFrame->DecodeFromBuffer(p, (int)(buf_size - (p - buf)), cbRead) ) {
1193                     found = true;
1194                     pFrame->Draw( callback );
1195                 }
1196                 delete pFrame;
1197                 res = false; // first frame found, stop!
1198             }
1199             break;
1200         case '!': // extension record
1201             {
1202                 if ( p[1]==0xf9 && ( (p[3]&1)!=0 ) )
1203                 {
1204                     m_transparent_color = p[6];
1205                     defined_transparent_color = true;
1206                 }
1207                 res = skipGifExtension(p, (int)buf_size - (p - buf));
1208             }
1209             break;
1210         case ';': // terminate record
1211             res = false;
1212             break;
1213         default:
1214             res = false;
1215             break;
1216         }
1217     }
1218 
1219     return found;
1220 }
1221 
Clear()1222 void LVGifImageSource::Clear()
1223 {
1224     _width = 0;
1225     _height = 0;
1226     m_version = 0;
1227     m_bpp = 0;
1228     if (m_global_color_table) {
1229         delete[] m_global_color_table;
1230         m_global_color_table = NULL;
1231     }
1232     if (m_frame_count) {
1233         for (int i=0; i<m_frame_count; i++) {
1234             delete m_frames[i];
1235         }
1236         delete m_frames;//Looks like the delete[] operator should be used
1237         m_frames = NULL;
1238         m_frame_count = 0;
1239     }
1240 }
1241 
1242 #define LSWDECODER_MAX_TABLE_SIZE 4096
1243 #define LSWDECODER_MAX_BITS 12
1244 class CLZWDecoder
1245 {
1246 protected:
1247 
1248     // in_stream
1249     const unsigned char * p_in_stream;
1250     int          in_stream_size;
1251     int          in_bit_pos;
1252 
1253     // out_stream
1254     unsigned char * p_out_stream;
1255     int          out_stream_size;
1256 
1257     int  clearcode;
1258     int  eoicode;
1259     int  bits;
1260     int  lastadd;
1261     /* // old implementation
1262     unsigned char * * str_table;
1263     int             * str_size;
1264     */
1265     unsigned char str_table[LSWDECODER_MAX_TABLE_SIZE];
1266     unsigned char last_table[LSWDECODER_MAX_TABLE_SIZE];
1267     unsigned char rev_buf[LSWDECODER_MAX_TABLE_SIZE/2];
1268     short         str_nextchar[LSWDECODER_MAX_TABLE_SIZE];
1269     //int           str_size;
1270 public:
1271 
SetInputStream(const unsigned char * p,int sz)1272     void SetInputStream (const unsigned char * p, int sz ) {
1273         p_in_stream = p;
1274         in_stream_size = sz;
1275         in_bit_pos = 0;
1276     };
1277 
SetOutputStream(unsigned char * p,int sz)1278     void SetOutputStream (unsigned char * p, int sz ) {
1279         p_out_stream = p;
1280         out_stream_size = sz;
1281     };
1282 
WriteOutChar(unsigned char b)1283     int WriteOutChar( unsigned char b ) {
1284         if (--out_stream_size>=0) {
1285             *p_out_stream++ = b;
1286             return 1;
1287         } else {
1288             return 0;
1289         }
1290 
1291     };
1292 
WriteOutString(int code)1293     int WriteOutString( int code ) {
1294         int pos = 0;
1295         do {
1296             rev_buf[pos++] = str_table[code];
1297             code = str_nextchar[code];
1298         } while (code>=0 && pos < LSWDECODER_MAX_TABLE_SIZE/2);
1299         while (--pos>=0) {
1300             if (!WriteOutChar(rev_buf[pos]))
1301                 return 0;
1302         }
1303         return 1;
1304     };
1305 
FillRestOfOutStream(unsigned char b)1306     void FillRestOfOutStream( unsigned char b ) {
1307         for (; out_stream_size>0; out_stream_size--) {
1308             *p_out_stream++ = b;
1309         }
1310     }
1311 
ReadInCode()1312     int ReadInCode() {
1313         int code = (p_in_stream[0])+
1314             (p_in_stream[1]<<8)+
1315             (p_in_stream[2]<<16);
1316         code >>= in_bit_pos;
1317         code &= (1<<bits)-1;
1318         in_bit_pos += bits;
1319         if (in_bit_pos >= 8) {
1320             p_in_stream++;
1321             in_stream_size--;
1322             in_bit_pos -= 8;
1323             if (in_bit_pos>=8) {
1324                 p_in_stream++;
1325                 in_stream_size--;
1326                 in_bit_pos -= 8;
1327             }
1328         }
1329         if (in_stream_size<0)
1330             return -1;
1331         else
1332             return code;
1333     };
1334 
AddString(int OldCode,unsigned char NewChar)1335     int AddString( int OldCode, unsigned char NewChar ) {
1336         if (lastadd == LSWDECODER_MAX_TABLE_SIZE)
1337             return -1;
1338         if (lastadd == (1<<bits)-1) {
1339             // increase table size, except case when ClearCode is expected
1340             if (bits < LSWDECODER_MAX_BITS)
1341                 bits++;
1342         }
1343 
1344         str_table[lastadd] = NewChar;
1345         str_nextchar[lastadd] = OldCode;
1346         last_table[lastadd] = last_table[OldCode];
1347 
1348 
1349         lastadd++;
1350         return lastadd-1;
1351     };
1352 
CLZWDecoder()1353     CLZWDecoder() {
1354         /* // ld implementation
1355         str_table = NULL;
1356         str_size = NULL;
1357         */
1358         lastadd=0;
1359     };
1360 
Clear()1361     void Clear() {
1362         /* // old implementation
1363         for (int i=0; i<lastadd; i++) {
1364             if (str_table[i])
1365                 delete str_table[i];
1366         }
1367         */
1368         lastadd=0;
1369     };
1370 
1371 
~CLZWDecoder()1372     ~CLZWDecoder() {
1373         Clear();
1374     };
1375 
Init(int sizecode)1376     void Init(int sizecode) {
1377         bits = sizecode + 1;
1378         // init table
1379         Clear();
1380         //ResizeTable(1<<bits);
1381         for (int i=(1<<sizecode) + 1; i>=0; i--) {
1382             str_table[i] = i;
1383             last_table[i] = i;
1384             str_nextchar[i] = -1;
1385         }
1386         // init codes
1387         clearcode = (1 << sizecode);
1388         eoicode = clearcode + 1;
1389 
1390         str_table[clearcode] = 0;
1391         str_nextchar[clearcode] = -1;
1392         str_table[eoicode] = 0;
1393         str_nextchar[eoicode] = -1;
1394         //str_table[eoicode] = NULL;
1395         lastadd = eoicode + 1;
1396     };
CodeExists(int code)1397     int  CodeExists(int code) {
1398         return (code<lastadd);
1399     };
1400 
Decode(int init_code_size)1401     int  Decode( int init_code_size ) {
1402 
1403         int code, oldcode;
1404 
1405         Init( init_code_size );
1406 
1407         code = ReadInCode(); // == 256, ignore
1408         if (code<0 || code>lastadd)
1409             return 0;
1410 
1411         while (1) { // 3
1412 
1413             code = ReadInCode();
1414 
1415             if (code<0 || code>lastadd)
1416                 return 1; // allow partial image
1417 
1418             if (!WriteOutString(code))
1419                 return 0;
1420 
1421             while (1) { // 5
1422 
1423                 oldcode = code;
1424 
1425                 code = ReadInCode();
1426 
1427                 if (code<0 || code>lastadd)
1428                     return 0;
1429 
1430                 if (CodeExists(code)) {
1431                     if (code == eoicode)
1432                         return 1;
1433                     else if (code == clearcode) {
1434                         break; // clear & goto 3
1435                     }
1436 
1437                     // write  code
1438                     if (!WriteOutString(code))
1439                         return 0;
1440 
1441                     // add  old + code[0]
1442                     if (AddString(oldcode, last_table[code])<0)
1443                         // return 0; // table overflow
1444                         {}
1445                         // Ignore table overflow, which seems ok, and done by Pillow:
1446                         //   https://github.com/python-pillow/Pillow/blob/ae43af61/src/libImaging/GifDecode.c#L234-L251
1447                         // which is fine handling this image:
1448                         //   https://cms-assets.tutsplus.com/uploads/users/30/posts/19890/image/hanging-punctuation-example.gif
1449                         // (Aborting on table overflow, we would fail while in the middle
1450                         // of the last line of text in this image.)
1451                         // (giflib/lib/dgif_lib.c is fine with this image too, but its algo is too different
1452                         // to have an idea how it handles this situation.)
1453 
1454 
1455                 } else {
1456                     // write  old + old[0]
1457                     if (!WriteOutString(oldcode))
1458                         return 0;
1459                     if (!WriteOutChar(last_table[oldcode]))
1460                         return 0;
1461 
1462                     // add  old + old[0]
1463                     if (AddString(oldcode, last_table[oldcode])<0)
1464                         // return 0; // table overflow
1465                         {}
1466                         // Ignore table overflow, see above (might be less needed here than there?)
1467                 }
1468             }
1469 
1470             Init( init_code_size );
1471         }
1472     };
1473 };
1474 
Decode(LVImageDecoderCallback * callback)1475 bool LVGifImageSource::Decode( LVImageDecoderCallback * callback )
1476 {
1477     if ( _stream.isNull() )
1478         return false;
1479     lvsize_t sz = _stream->GetSize();
1480     if ( sz<32 )
1481         return false; // wrong size
1482     lUInt8 * buf = new lUInt8[ sz ];
1483     lvsize_t bytesRead = 0;
1484     bool res = true;
1485     _stream->SetPos(0);
1486     if ( _stream->Read( buf, sz, &bytesRead )!=LVERR_OK || bytesRead!=sz )
1487         res = false;
1488 
1489 //    // for DEBUG
1490 //    {
1491 //        LVStreamRef out = LVOpenFileStream("/tmp/test.gif", LVOM_WRITE);
1492 //        out->Write(buf, sz, NULL);
1493 //    }
1494 
1495     res = res && DecodeFromBuffer( buf, sz, callback );
1496     delete[] buf;
1497     return res;
1498 }
1499 
DecodeFromBuffer(unsigned char * buf,int buf_size,int & bytes_read)1500 int LVGifFrame::DecodeFromBuffer( unsigned char * buf, int buf_size, int &bytes_read )
1501 {
1502     bytes_read = 0;
1503     unsigned char * p = buf;
1504     if (*p!=',' || buf_size<=10)
1505         return 0; // error: no delimiter
1506     p++;
1507 
1508     // read info
1509     m_left = p[0] + (((unsigned int)p[1])<<8);
1510     m_top = p[2] + (((unsigned int)p[3])<<8);
1511     m_cx = p[4] + (((unsigned int)p[5])<<8);
1512     m_cy = p[6] + (((unsigned int)p[7])<<8);
1513 
1514     if (m_cx<1 || m_cx>4096 ||
1515         m_cy<1 || m_cy>4096 ||
1516         m_left+m_cx>m_pImage->GetWidth() ||
1517         m_top+m_cy>m_pImage->GetHeight())
1518         return 0; // error: wrong size
1519 
1520     m_flg_ltc = (p[8]&0x80)?1:0;
1521     m_flg_interlaced = (p[8]&0x40)?1:0;
1522     m_bpp = (p[8]&0x7) + 1;
1523 
1524     if (m_bpp==1)
1525         m_bpp = m_pImage->m_bpp;
1526     else if (m_bpp!=m_pImage->m_bpp && !m_flg_ltc)
1527         return 0; // wrong color table
1528 
1529     // next
1530     p+=9;
1531 
1532     if (m_flg_ltc) {
1533         // read color table
1534         int m_color_count = 1<<m_bpp;
1535 
1536         if (m_color_count*3 + (p-buf) >= buf_size)
1537             return 0; // error
1538 
1539         m_local_color_table = new lUInt32[m_color_count];
1540         for (int i=0; i<m_color_count; i++) {
1541             m_local_color_table[i] = lRGB(p[i*3],p[i*3+1],p[i*3+2]);
1542             //m_local_color_table[i] = lRGB(p[i*3+2],p[i*3+1],p[i*3+0]);
1543         }
1544         // next
1545         p+=(m_color_count * 3);
1546     }
1547 
1548     // unpack image
1549     unsigned char * stream_buffer = NULL;
1550     int stream_buffer_size = 0;
1551 
1552     int size_code = *p++;
1553 
1554     // test raster stream size
1555     int i;
1556     int rest_buf_size = (int)(buf_size - (p-buf));
1557     for (i=0; i<rest_buf_size && p[i]; ) {
1558         // next block
1559         int block_size = p[i];
1560         stream_buffer_size += block_size;
1561         i+=block_size+1;
1562     }
1563 
1564     if (!stream_buffer_size || i>rest_buf_size)
1565         return 0; // error
1566 
1567     // set read bytes count
1568     bytes_read = (int)((p-buf) + i);
1569 
1570     // create stream buffer
1571     stream_buffer = new unsigned char[stream_buffer_size+3];
1572     // copy data to stream buffer
1573     int sb_index = 0;
1574     for (i=0; p[i]; ) {
1575         // next block
1576         int block_size = p[i];
1577         for (int j=1; j<=block_size; j++) {
1578             stream_buffer[sb_index++] = p[i+j];
1579         }
1580         i+=block_size+1;
1581     }
1582 
1583 
1584     // create image buffer
1585     m_buffer = new unsigned char [m_cx*m_cy];
1586 
1587     // decode image to buffer
1588     CLZWDecoder decoder;
1589     decoder.SetInputStream( stream_buffer, stream_buffer_size );
1590     decoder.SetOutputStream( m_buffer, m_cx*m_cy );
1591 
1592     int res=0;
1593 
1594     if (decoder.Decode(size_code)) {
1595         // decoded Ok
1596         // fill rest with transparent color
1597         decoder.FillRestOfOutStream( m_pImage->m_transparent_color );
1598         res = 1;
1599     } else {
1600         // error
1601         delete[] m_buffer;
1602         m_buffer = NULL;
1603     }
1604 
1605     // cleanup
1606     delete[] stream_buffer;
1607 
1608     return res; // OK
1609 }
1610 
LVGifFrame(LVGifImageSource * pImage)1611 LVGifFrame::LVGifFrame(LVGifImageSource * pImage)
1612 {
1613     m_pImage = pImage;
1614     m_left = 0;
1615     m_top = 0;
1616     m_cx = 0;
1617     m_cy = 0;
1618     m_flg_ltc = 0; // GTC (gobal table of colors) flag
1619     m_local_color_table = NULL;
1620     m_buffer = NULL;
1621 }
1622 
~LVGifFrame()1623 LVGifFrame::~LVGifFrame()
1624 {
1625     Clear();
1626 }
1627 
Clear()1628 void LVGifFrame::Clear()
1629 {
1630     if (m_buffer) {
1631         delete[] m_buffer;
1632         m_buffer = NULL;
1633     }
1634     if (m_local_color_table) {
1635         delete[] m_local_color_table;
1636         m_local_color_table = NULL;
1637     }
1638 }
1639 
1640 #endif
1641 // ======= end of GIF support
1642 
1643 
1644 #if (USE_NANOSVG==1)
1645 // SVG support
1646 
1647 class LVSvgImageSource : public LVNodeImageSource
1648 {
1649 protected:
1650 public:
1651     LVSvgImageSource( ldomNode * node, LVStreamRef stream );
1652     virtual ~LVSvgImageSource();
1653     virtual void   Compact();
1654     virtual bool   Decode( LVImageDecoderCallback * callback );
1655     int DecodeFromBuffer(unsigned char *buf, int buf_size, LVImageDecoderCallback * callback);
1656     static bool CheckPattern( const lUInt8 * buf, int len );
1657 };
1658 
LVSvgImageSource(ldomNode * node,LVStreamRef stream)1659 LVSvgImageSource::LVSvgImageSource( ldomNode * node, LVStreamRef stream )
1660         : LVNodeImageSource(node, stream)
1661 {
1662 }
~LVSvgImageSource()1663 LVSvgImageSource::~LVSvgImageSource() {}
1664 
Compact()1665 void LVSvgImageSource::Compact() { }
1666 
CheckPattern(const lUInt8 * buf,int len)1667 bool LVSvgImageSource::CheckPattern( const lUInt8 * buf, int len)
1668 {
1669     // check for <?xml or <svg
1670     if (len > 5 && buf[0]=='<' && buf[1]=='?' &&
1671             (buf[2]=='x' || buf[2] == 'X') &&
1672             (buf[3]=='m' || buf[3] == 'M') &&
1673             (buf[4]=='l' || buf[4] == 'L'))
1674         return true;
1675     if (len > 4 && buf[0]=='<' &&
1676             (buf[1]=='s' || buf[1] == 'S') &&
1677             (buf[2]=='v' || buf[2] == 'V') &&
1678             (buf[3]=='g' || buf[3] == 'G'))
1679         return true;
1680     return false;
1681 }
1682 
Decode(LVImageDecoderCallback * callback)1683 bool LVSvgImageSource::Decode( LVImageDecoderCallback * callback )
1684 {
1685     if ( _stream.isNull() )
1686         return false;
1687     lvsize_t sz = _stream->GetSize();
1688     // if ( sz<32 || sz>0x80000 ) return false; // do not impose (yet) a max size for svg
1689     lUInt8 * buf = new lUInt8[ sz+1 ];
1690     lvsize_t bytesRead = 0;
1691     bool res = true;
1692     _stream->SetPos(0);
1693     if ( _stream->Read( buf, sz, &bytesRead )!=LVERR_OK || bytesRead!=sz ) {
1694         res = false;
1695     }
1696     else {
1697         buf[sz] = 0;
1698         res = DecodeFromBuffer( buf, sz, callback );
1699     }
1700     delete[] buf;
1701     return res;
1702 }
1703 
DecodeFromBuffer(unsigned char * buf,int buf_size,LVImageDecoderCallback * callback)1704 int LVSvgImageSource::DecodeFromBuffer(unsigned char *buf, int buf_size, LVImageDecoderCallback * callback)
1705 {
1706     NSVGimage *image = NULL;
1707     NSVGrasterizer *rast = NULL;
1708     unsigned char* img = NULL;
1709     int w, h;
1710     bool res = false;
1711 
1712     // printf("SVG: parsing...\n");
1713     image = nsvgParse((char*)buf, "px", 96.0f);
1714     if (image == NULL) {
1715         printf("SVG: could not parse SVG stream.\n");
1716         nsvgDelete(image);
1717         return res;
1718     }
1719 
1720     w = (int)image->width;
1721     h = (int)image->height;
1722     // The rasterizer (while antialiasing?) has a tendency to eat the last
1723     // right and bottom pixel. We can avoid that by adding 1 pixel around
1724     // each side, by increasing width and height with 2 here, and using
1725     // offsets of 1 in nsvgRasterize
1726     w += 2;
1727     h += 2;
1728     _width = w;
1729     _height = h;
1730 
1731     // int nbshapes = 0;
1732     // for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) nbshapes++;
1733     // printf("SVG: nb of shapes: %d\n", nbshapes);
1734     if (! image->shapes) {
1735         // If no supported shapes, it will be a blank empty image.
1736         // Better to let user know that with an unsupported image display (empty
1737         // square with borders).
1738         // But commented to not flood koreader's log for books with many such
1739         // svg images (crengine would log this at each page change)
1740         // printf("SVG: got image with zero supported shape.\n");
1741         nsvgDelete(image);
1742         return res;
1743     }
1744 
1745     if ( ! callback ) { // If no callback provided, only size is wanted.
1746         res = true;
1747     }
1748     else {
1749         rast = nsvgCreateRasterizer();
1750         if (rast == NULL) {
1751             printf("SVG: could not init rasterizer.\n");
1752         }
1753         else {
1754             img = (unsigned char*) malloc(w*h*4);
1755             if (img == NULL) {
1756                 printf("SVG: could not alloc image buffer.\n");
1757             }
1758             else {
1759                 // printf("SVG: rasterizing image %d x %d\n", w, h);
1760                 nsvgRasterize(rast, image, 1, 1, 1, img, w, h, w*4); // offsets of 1 pixel, scale = 1
1761                 // stbi_write_png("/tmp/svg.png", w, h, 4, img, w*4); // for debug
1762                 callback->OnStartDecode(this);
1763                 lUInt32 * row = new lUInt32 [ _width ];
1764                 lUInt8 * p = img;
1765                 lUInt8 r, g, b, a, ia, blend, iblend;
1766                 lUInt32 ro, go, bo;
1767                 for (int y=0; y<_height; y++) {
1768                     for (int x=0; x<_width; x++) {
1769                         // We mostly get full white or full black when using alpha channel like this:
1770                         //   row[x] = (((lUInt32)p[3])<<24) | (((lUInt32)p[0])<<16) | (((lUInt32)p[1])<<8) | (((lUInt32)p[2])<<0);
1771                         // We can ignore the alpha channel but we get a black background for transparent pixels with:
1772                         //   row[x] = (((lUInt32)p[0])<<16) | (((lUInt32)p[1])<<8) | (((lUInt32)p[2])<<0);
1773                         // It's better to use alpha channel here to blend pixels over a white background and set opacity to full
1774                         // """ To perform a source-over blend between two colors that use straight alpha format:
1775                         //           result = (source.RGB * source.A) + (dest.RGB * (1 - source.A))        """
1776                         r = (lUInt8)p[0];
1777                         g = (lUInt8)p[1];
1778                         b = (lUInt8)p[2];
1779                         a = (lUInt8)p[3];
1780                         ia = a ^ 0xFF;
1781                         ro = (lUInt32)( r*a + 0xff*ia );
1782                         go = (lUInt32)( g*a + 0xff*ia );
1783                         bo = (lUInt32)( b*a + 0xff*ia );
1784                         // More accurate divide by 256 than just >> 8 (255 becomes 254 with just >> 8)
1785                         ro = (ro+1 + (ro >> 8)) >> 8;
1786                         go = (go+1 + (go >> 8)) >> 8;
1787                         bo = (bo+1 + (bo >> 8)) >> 8;
1788                         row[x] = ro<<16|go<<8|bo;
1789                         // if (y == 80) // output bytes for a single row
1790                         // printf("SVG: byte colors %d %d %d %d > %d %d %d\n", (int)a, (int)r, (int)g, (int)b, (int)ro, (int)go, (int)bo);
1791                         p += 4;
1792                     }
1793                     callback->OnLineDecoded( this, y, row );
1794                 }
1795                 delete[] row;
1796                 callback->OnEndDecode(this, false);
1797                 res = true;
1798                 free(img);
1799             }
1800         }
1801     }
1802     nsvgDeleteRasterizer(rast);
1803     nsvgDelete(image);
1804     return res;
1805 }
1806 
1807 // Convenience function to convert SVG image data to PNG
convertSVGtoPNG(unsigned char * svg_data,int svg_data_size,float zoom_factor,int * png_data_len)1808 unsigned char * convertSVGtoPNG(unsigned char *svg_data, int svg_data_size, float zoom_factor, int *png_data_len)
1809 {
1810     NSVGimage *image = NULL;
1811     NSVGrasterizer *rast = NULL;
1812     unsigned char* img = NULL;
1813     int w, h, pw, ph;
1814     unsigned char *png = NULL;
1815 
1816     // printf("SVG: converting to PNG...\n");
1817     image = nsvgParse((char*)svg_data, "px", 96.0f);
1818     if (image == NULL) {
1819         printf("SVG: could not parse SVG stream.\n");
1820         nsvgDelete(image);
1821         return png;
1822     }
1823 
1824     if (! image->shapes) {
1825         printf("SVG: got image with zero supported shape.\n");
1826         nsvgDelete(image);
1827         return png;
1828     }
1829 
1830     w = (int)image->width;
1831     h = (int)image->height;
1832     // The rasterizer (while antialiasing?) has a tendency to eat some of the
1833     // right and bottom pixels. We can avoid that by adding N pixels around
1834     // each side, by increasing width and height with 2*N here, and using
1835     // offsets of N in nsvgRasterize. Using zoom_factor as N gives nice results.
1836     int offset = zoom_factor;
1837     pw = w*zoom_factor + 2*offset;
1838     ph = h*zoom_factor + 2*offset;
1839     rast = nsvgCreateRasterizer();
1840     if (rast == NULL) {
1841         printf("SVG: could not init rasterizer.\n");
1842     }
1843     else {
1844         img = (unsigned char*) malloc(pw*ph*4);
1845         if (img == NULL) {
1846             printf("SVG: could not alloc image buffer.\n");
1847         }
1848         else {
1849             // printf("SVG: rasterizing to png image %d x %d\n", pw, ph);
1850             nsvgRasterize(rast, image, offset, offset, zoom_factor, img, pw, ph, pw*4);
1851             png = stbi_write_png_to_mem(img, pw*4, pw, ph, 4, png_data_len);
1852             free(img);
1853         }
1854     }
1855     nsvgDeleteRasterizer(rast);
1856     nsvgDelete(image);
1857     return png;
1858 }
1859 // ======= end of SVG support
1860 #endif
1861 
~LVImageDecoderCallback()1862 LVImageDecoderCallback::~LVImageDecoderCallback()
1863 {
1864 }
1865 
1866 
1867 /// dummy image object, to show invalid image
LVCreateDummyImageSource(ldomNode * node,int width,int height)1868 LVImageSourceRef LVCreateDummyImageSource( ldomNode * node, int width, int height )
1869 {
1870     return LVImageSourceRef( new LVDummyImageSource( node, width, height ) );
1871 }
1872 
LVCreateStreamImageSource(ldomNode * node,LVStreamRef stream)1873 LVImageSourceRef LVCreateStreamImageSource( ldomNode * node, LVStreamRef stream )
1874 {
1875     LVImageSourceRef ref;
1876     if ( stream.isNull() )
1877         return ref;
1878     lUInt8 hdr[256];
1879     lvsize_t bytesRead = 0;
1880     if ( stream->Read( hdr, 256, &bytesRead )!=LVERR_OK )
1881         return ref;
1882     stream->SetPos( 0 );
1883 
1884 
1885     LVImageSource * img = NULL;
1886 #if (USE_LIBPNG==1)
1887     if ( LVPngImageSource::CheckPattern( hdr, (lUInt32)bytesRead ) )
1888         img = new LVPngImageSource( node, stream );
1889     else
1890 #endif
1891 #if (USE_LIBJPEG==1)
1892     if ( LVJpegImageSource::CheckPattern( hdr, (lUInt32)bytesRead ) )
1893         img = new LVJpegImageSource( node, stream );
1894     else
1895 #endif
1896 #if (USE_GIF==1)
1897     if ( LVGifImageSource::CheckPattern( hdr, (lUInt32)bytesRead ) )
1898         img = new LVGifImageSource( node, stream );
1899     else
1900 #endif
1901 #if (USE_NANOSVG==1)
1902     if ( LVSvgImageSource::CheckPattern( hdr, (lUInt32)bytesRead ) )
1903         img = new LVSvgImageSource( node, stream );
1904     else
1905 #endif
1906         img = new LVDummyImageSource( node, 50, 50 );
1907 
1908     if ( !img )
1909         return ref;
1910     ref = LVImageSourceRef( img );
1911     if ( !img->Decode( NULL ) )
1912     {
1913         return LVImageSourceRef();
1914     }
1915     return ref;
1916 }
1917 
LVCreateStreamImageSource(LVStreamRef stream)1918 LVImageSourceRef LVCreateStreamImageSource( LVStreamRef stream )
1919 {
1920     return LVCreateStreamImageSource( NULL, stream );
1921 }
1922 
1923 /// create image from node source
LVCreateNodeImageSource(ldomNode * node)1924 LVImageSourceRef LVCreateNodeImageSource( ldomNode * node )
1925 {
1926     LVImageSourceRef ref;
1927     if (!node->isElement())
1928         return ref;
1929     LVStreamRef stream = node->createBase64Stream();
1930     if (stream.isNull())
1931         return ref;
1932 //    if ( CRLog::isDebugEnabled() ) {
1933 //        lUInt16 attr_id = node->getDocument()->getAttrNameIndex(U"id");
1934 //        lString32 id = node->getAttributeValue(attr_id);
1935 //        CRLog::debug("Opening node image id=%s", LCSTR(id));
1936 //    }
1937     return LVCreateStreamImageSource( stream );
1938 }
1939 
1940 /// creates image source as memory copy of file contents
LVCreateFileCopyImageSource(lString32 fname)1941 LVImageSourceRef LVCreateFileCopyImageSource( lString32 fname )
1942 {
1943     return LVCreateStreamImageSource( LVCreateMemoryStream(fname) );
1944 }
1945 
1946 /// creates image source as memory copy of stream contents
LVCreateStreamCopyImageSource(LVStreamRef stream)1947 LVImageSourceRef LVCreateStreamCopyImageSource( LVStreamRef stream )
1948 {
1949     if ( stream.isNull() )
1950         return LVImageSourceRef();
1951     return LVCreateStreamImageSource( LVCreateMemoryStream(stream) );
1952 }
1953 
1954 class LVStretchImgSource : public LVImageSource, public LVImageDecoderCallback
1955 {
1956 protected:
1957 	LVImageSourceRef _src;
1958 	int _src_dx;
1959 	int _src_dy;
1960 	int _dst_dx;
1961 	int _dst_dy;
1962     ImageTransform _hTransform;
1963     ImageTransform _vTransform;
1964 	int _split_x;
1965 	int _split_y;
1966 	LVArray<lUInt32> _line;
1967 	LVImageDecoderCallback * _callback;
1968 public:
LVStretchImgSource(LVImageSourceRef src,int newWidth,int newHeight,ImageTransform hTransform,ImageTransform vTransform,int splitX,int splitY)1969     LVStretchImgSource( LVImageSourceRef src, int newWidth, int newHeight, ImageTransform hTransform, ImageTransform vTransform, int splitX, int splitY )
1970 		: _src( src )
1971 		, _src_dx( src->GetWidth() )
1972 		, _src_dy( src->GetHeight() )
1973 		, _dst_dx( newWidth )
1974 		, _dst_dy( newHeight )
1975         , _hTransform(hTransform)
1976         , _vTransform(vTransform)
1977 		, _split_x( splitX )
1978 		, _split_y( splitY )
1979 	{
1980         if ( _hTransform == IMG_TRANSFORM_TILE )
1981             if ( _split_x>=_src_dx )
1982                 _split_x %=_src_dx;
1983         if ( _vTransform == IMG_TRANSFORM_TILE )
1984             if ( _split_y>=_src_dy )
1985                 _split_y %=_src_dy;
1986         if ( _split_x<0 || _split_x>=_src_dx )
1987 			_split_x = _src_dx / 2;
1988 		if ( _split_y<0 || _split_y>=_src_dy )
1989 			_split_y = _src_dy / 2;
1990 	}
OnStartDecode(LVImageSource *)1991     virtual void OnStartDecode( LVImageSource * )
1992 	{
1993 		_line.reserve( _dst_dx );
1994         _callback->OnStartDecode(this);
1995 	}
1996     virtual bool OnLineDecoded( LVImageSource * obj, int y, lUInt32 * data );
OnEndDecode(LVImageSource *,bool res)1997     virtual void OnEndDecode( LVImageSource *, bool res)
1998 	{
1999 		_line.clear();
2000         _callback->OnEndDecode(this, res);
2001     }
GetSourceNode()2002 	virtual ldomNode * GetSourceNode() { return NULL; }
GetSourceStream()2003 	virtual LVStream * GetSourceStream() { return NULL; }
Compact()2004 	virtual void   Compact() { }
GetWidth()2005 	virtual int    GetWidth() { return _dst_dx; }
GetHeight()2006 	virtual int    GetHeight() { return _dst_dy; }
Decode(LVImageDecoderCallback * callback)2007     virtual bool   Decode( LVImageDecoderCallback * callback )
2008 	{
2009 		_callback = callback;
2010 		return _src->Decode( this );
2011 	}
~LVStretchImgSource()2012     virtual ~LVStretchImgSource()
2013 	{
2014 	}
2015 };
2016 
OnLineDecoded(LVImageSource * obj,int y,lUInt32 * data)2017 bool LVStretchImgSource::OnLineDecoded( LVImageSource * obj, int y, lUInt32 * data )
2018 {
2019     bool res = false;
2020 
2021     switch ( _hTransform ) {
2022     case IMG_TRANSFORM_SPLIT:
2023         {
2024             int right_pixels = (_src_dx-_split_x-1);
2025             int first_right_pixel = _dst_dx - right_pixels;
2026             int right_offset = _src_dx - _dst_dx;
2027             //int bottom_pixels = (_src_dy-_split_y-1);
2028             //int first_bottom_pixel = _dst_dy - bottom_pixels;
2029             for ( int x=0; x<_dst_dx; x++ ) {
2030                 if ( x<_split_x )
2031                     _line[x] = data[x];
2032                 else if ( x < first_right_pixel )
2033                     _line[x] = data[_split_x];
2034                 else
2035                     _line[x] = data[x + right_offset];
2036             }
2037         }
2038         break;
2039     case IMG_TRANSFORM_STRETCH:
2040         {
2041             for ( int x=0; x<_dst_dx; x++ )
2042                 _line[x] = data[x * _src_dx / _dst_dx];
2043         }
2044         break;
2045     case IMG_TRANSFORM_NONE:
2046         {
2047             for ( int x=0; x<_dst_dx && x<_src_dx; x++ )
2048                 _line[x] = data[x];
2049         }
2050         break;
2051     case IMG_TRANSFORM_TILE:
2052         {
2053             int offset = _src_dx - _split_x;
2054             for ( int x=0; x<_dst_dx; x++ )
2055                 _line[x] = data[ (x + offset) % _src_dx];
2056         }
2057         break;
2058     }
2059 
2060     switch ( _vTransform ) {
2061     case IMG_TRANSFORM_SPLIT:
2062         {
2063             int middle_pixels = _dst_dy - _src_dy + 1;
2064             if ( y < _split_y ) {
2065                 res = _callback->OnLineDecoded( obj, y, _line.get() );
2066             } else if ( y==_split_y ) {
2067                 for ( int i=0; i < middle_pixels; i++ ) {
2068                     res = _callback->OnLineDecoded( obj, y+i, _line.get() );
2069                 }
2070             } else {
2071                 res = _callback->OnLineDecoded( obj, y + (_dst_dy - _src_dy), _line.get() );
2072             }
2073         }
2074         break;
2075     case IMG_TRANSFORM_STRETCH:
2076         {
2077             int y0 = y * _dst_dy / _src_dy;
2078             int y1 = (y+1) * _dst_dy / _src_dy;
2079             for ( int yy=y0; yy<y1; yy++ ) {
2080                 res = _callback->OnLineDecoded( obj, yy, _line.get() );
2081             }
2082         }
2083         break;
2084     case IMG_TRANSFORM_NONE:
2085         {
2086             if ( y<_dst_dy )
2087                 res = _callback->OnLineDecoded( obj, y, _line.get() );
2088         }
2089         break;
2090     case IMG_TRANSFORM_TILE:
2091         {
2092             int offset = _src_dy - _split_y;
2093             int y0 = (y + offset) % _src_dy;
2094             for ( int yy=y0; yy<_dst_dy; yy+=_src_dy ) {
2095                 res = _callback->OnLineDecoded( obj, yy, _line.get() );
2096             }
2097         }
2098         break;
2099     }
2100 
2101     return res;
2102 }
2103 
2104 /// creates image which stretches source image by filling center with pixels at splitX, splitY
LVCreateStretchFilledTransform(LVImageSourceRef src,int newWidth,int newHeight,ImageTransform hTransform,ImageTransform vTransform,int splitX,int splitY)2105 LVImageSourceRef LVCreateStretchFilledTransform( LVImageSourceRef src, int newWidth, int newHeight, ImageTransform hTransform, ImageTransform vTransform, int splitX, int splitY )
2106 {
2107 	if ( src.isNull() )
2108 		return LVImageSourceRef();
2109     return LVImageSourceRef( new LVStretchImgSource( src, newWidth, newHeight, hTransform, vTransform, splitX, splitY ) );
2110 }
2111 
2112 /// creates image which fills area with tiled copy
LVCreateTileTransform(LVImageSourceRef src,int newWidth,int newHeight,int offsetX,int offsetY)2113 LVImageSourceRef LVCreateTileTransform( LVImageSourceRef src, int newWidth, int newHeight, int offsetX, int offsetY )
2114 {
2115     if ( src.isNull() )
2116         return LVImageSourceRef();
2117     return LVImageSourceRef( new LVStretchImgSource( src, newWidth, newHeight, IMG_TRANSFORM_TILE, IMG_TRANSFORM_TILE,
2118                                                      offsetX, offsetY ) );
2119 }
2120 
limit256(int n)2121 static inline lUInt32 limit256(int n) {
2122     if (n < 0)
2123         return 0;
2124     else if (n > 0xFF)
2125         return 0xFF;
2126     else
2127         return (lUInt32)n;
2128 }
2129 
2130 class LVColorTransformImgSource : public LVImageSource, public LVImageDecoderCallback
2131 {
2132 protected:
2133     LVImageSourceRef _src;
2134     lUInt32 _add;
2135     lUInt32 _multiply;
2136     LVImageDecoderCallback * _callback;
2137     LVColorDrawBuf * _drawbuf;
2138     int _sumR;
2139     int _sumG;
2140     int _sumB;
2141     int _countPixels;
2142 public:
LVColorTransformImgSource(LVImageSourceRef src,lUInt32 addRGB,lUInt32 multiplyRGB)2143     LVColorTransformImgSource(LVImageSourceRef src, lUInt32 addRGB, lUInt32 multiplyRGB)
2144         : _src( src )
2145         , _add(addRGB)
2146         , _multiply(multiplyRGB)
2147         , _drawbuf(NULL)
2148     {
2149     }
~LVColorTransformImgSource()2150     virtual ~LVColorTransformImgSource() {
2151         if (_drawbuf)
2152             delete _drawbuf;
2153     }
2154 
OnStartDecode(LVImageSource *)2155     virtual void OnStartDecode( LVImageSource * )
2156     {
2157         _callback->OnStartDecode(this);
2158         _sumR = _sumG = _sumB = _countPixels = 0;
2159         if (_drawbuf)
2160             delete _drawbuf;
2161         _drawbuf = new LVColorDrawBuf(_src->GetWidth(), _src->GetHeight(), 32);
2162     }
OnLineDecoded(LVImageSource * obj,int y,lUInt32 * data)2163     virtual bool OnLineDecoded( LVImageSource * obj, int y, lUInt32 * data ) {
2164         CR_UNUSED(obj);
2165         int dx = _src->GetWidth();
2166 
2167         lUInt32 * row = (lUInt32*)_drawbuf->GetScanLine(y);
2168         for (int x = 0; x < dx; x++) {
2169             lUInt32 cl = data[x];
2170             row[x] = cl;
2171             if (((cl >> 24) & 0xFF) < 0xC0) { // count non-transparent pixels only
2172                 _sumR += (cl >> 16) & 0xFF;
2173                 _sumG += (cl >> 8) & 0xFF;
2174                 _sumB += (cl >> 0) & 0xFF;
2175                 _countPixels++;
2176             }
2177         }
2178         return true;
2179 
2180     }
OnEndDecode(LVImageSource * obj,bool res)2181     virtual void OnEndDecode( LVImageSource * obj, bool res)
2182     {
2183         int dx = _src->GetWidth();
2184         int dy = _src->GetHeight();
2185         // simple add
2186         int ar = (((_add >> 16) & 0xFF) - 0x80) * 2;
2187         int ag = (((_add >> 8) & 0xFF) - 0x80) * 2;
2188         int ab = (((_add >> 0) & 0xFF) - 0x80) * 2;
2189         // fixed point * 256
2190         int mr = ((_multiply >> 16) & 0xFF) << 3;
2191         int mg = ((_multiply >> 8) & 0xFF) << 3;
2192         int mb = ((_multiply >> 0) & 0xFF) << 3;
2193 
2194         int avgR = _countPixels > 0 ? _sumR / _countPixels : 128;
2195         int avgG = _countPixels > 0 ? _sumG / _countPixels : 128;
2196         int avgB = _countPixels > 0 ? _sumB / _countPixels : 128;
2197 
2198         for (int y = 0; y < dy; y++) {
2199             lUInt32 * row = (lUInt32*)_drawbuf->GetScanLine(y);
2200             for ( int x=0; x<dx; x++ ) {
2201                 lUInt32 cl = row[x];
2202                 lUInt32 a = cl & 0xFF000000;
2203                 if (a != 0xFF000000) {
2204                     int r = (cl >> 16) & 0xFF;
2205                     int g = (cl >> 8) & 0xFF;
2206                     int b = (cl >> 0) & 0xFF;
2207                     r = (((r - avgR) * mr) >> 8) + avgR + ar;
2208                     g = (((g - avgG) * mg) >> 8) + avgG + ag;
2209                     b = (((b - avgB) * mb) >> 8) + avgB + ab;
2210                     row[x] = a | (limit256(r) << 16) | (limit256(g) << 8) | (limit256(b) << 0);
2211                 }
2212             }
2213             _callback->OnLineDecoded(obj, y, row);
2214         }
2215         if (_drawbuf)
2216             delete _drawbuf;
2217         _drawbuf = NULL;
2218         _callback->OnEndDecode(this, res);
2219     }
GetSourceNode()2220     virtual ldomNode * GetSourceNode() { return NULL; }
GetSourceStream()2221     virtual LVStream * GetSourceStream() { return NULL; }
Compact()2222     virtual void   Compact() { }
GetWidth()2223     virtual int    GetWidth() { return _src->GetWidth(); }
GetHeight()2224     virtual int    GetHeight() { return _src->GetHeight(); }
Decode(LVImageDecoderCallback * callback)2225     virtual bool   Decode( LVImageDecoderCallback * callback )
2226     {
2227         _callback = callback;
2228         return _src->Decode( this );
2229     }
2230 };
2231 
2232 /// creates image source which transforms colors of another image source (add RGB components (c - 0x80) * 2 from addedRGB first, then multiplyed by multiplyRGB fixed point components (0x20 is 1.0f)
LVCreateColorTransformImageSource(LVImageSourceRef srcImage,lUInt32 addRGB,lUInt32 multiplyRGB)2233 LVImageSourceRef LVCreateColorTransformImageSource(LVImageSourceRef srcImage, lUInt32 addRGB, lUInt32 multiplyRGB) {
2234     return LVImageSourceRef(new LVColorTransformImgSource(srcImage, addRGB, multiplyRGB));
2235 }
2236 
2237 class LVAlphaTransformImgSource : public LVImageSource, public LVImageDecoderCallback
2238 {
2239 protected:
2240     LVImageSourceRef _src;
2241     LVImageDecoderCallback * _callback;
2242     int _alpha;
2243 public:
LVAlphaTransformImgSource(LVImageSourceRef src,int alpha)2244     LVAlphaTransformImgSource(LVImageSourceRef src, int alpha)
2245         : _src( src )
2246         , _alpha(alpha ^ 0xFF)
2247     {
2248     }
~LVAlphaTransformImgSource()2249     virtual ~LVAlphaTransformImgSource() {
2250     }
2251 
OnStartDecode(LVImageSource *)2252     virtual void OnStartDecode( LVImageSource * )
2253     {
2254         _callback->OnStartDecode(this);
2255     }
OnLineDecoded(LVImageSource * obj,int y,lUInt32 * data)2256     virtual bool OnLineDecoded( LVImageSource * obj, int y, lUInt32 * data ) {
2257         CR_UNUSED(obj);
2258         int dx = _src->GetWidth();
2259 
2260         for (int x = 0; x < dx; x++) {
2261             lUInt32 cl = data[x];
2262             int srcalpha = (cl >> 24) ^ 0xFF;
2263             if (srcalpha > 0) {
2264                 srcalpha = _alpha * srcalpha;
2265                 cl = (cl & 0xFFFFFF) | (((_alpha * srcalpha) ^ 0xFF)<<24);
2266             }
2267             data[x] = cl;
2268         }
2269         return _callback->OnLineDecoded(obj, y, data);
2270     }
OnEndDecode(LVImageSource * obj,bool res)2271     virtual void OnEndDecode( LVImageSource * obj, bool res)
2272     {
2273         CR_UNUSED(obj);
2274         _callback->OnEndDecode(this, res);
2275     }
GetSourceNode()2276     virtual ldomNode * GetSourceNode() { return NULL; }
GetSourceStream()2277     virtual LVStream * GetSourceStream() { return NULL; }
Compact()2278     virtual void   Compact() { }
GetWidth()2279     virtual int    GetWidth() { return _src->GetWidth(); }
GetHeight()2280     virtual int    GetHeight() { return _src->GetHeight(); }
Decode(LVImageDecoderCallback * callback)2281     virtual bool   Decode( LVImageDecoderCallback * callback )
2282     {
2283         _callback = callback;
2284         return _src->Decode( this );
2285     }
2286 };
2287 
2288 /// creates image source which applies alpha to another image source (0 is no change, 255 is totally transparent)
LVCreateAlphaTransformImageSource(LVImageSourceRef srcImage,int alpha)2289 LVImageSourceRef LVCreateAlphaTransformImageSource(LVImageSourceRef srcImage, int alpha) {
2290     if (alpha <= 0)
2291         return srcImage;
2292     return LVImageSourceRef(new LVAlphaTransformImgSource(srcImage, alpha));
2293 }
2294 
2295 class LVUnpackedImgSource : public LVImageSource, public LVImageDecoderCallback
2296 {
2297 protected:
2298     bool _isGray;
2299     int _bpp;
2300     lUInt8 * _grayImage;
2301     lUInt32 * _colorImage;
2302     lUInt16 * _colorImage16;
2303     int _dx;
2304     int _dy;
2305 public:
LVUnpackedImgSource(LVImageSourceRef src,int bpp)2306     LVUnpackedImgSource( LVImageSourceRef src, int bpp )
2307         : _isGray(bpp<=8)
2308         , _bpp(bpp)
2309         , _grayImage(NULL)
2310         , _colorImage(NULL)
2311         , _colorImage16(NULL)
2312         , _dx( src->GetWidth() )
2313         , _dy( src->GetHeight() )
2314     {
2315         if ( bpp<=8  ) {
2316             _grayImage = (lUInt8*)malloc( _dx * _dy * sizeof(lUInt8) );
2317         } else if ( bpp==16 ) {
2318             _colorImage16 = (lUInt16*)malloc( _dx * _dy * sizeof(lUInt16) );
2319         } else {
2320             _colorImage = (lUInt32*)malloc( _dx * _dy * sizeof(lUInt32) );
2321         }
2322         src->Decode( this );
2323     }
OnStartDecode(LVImageSource *)2324     virtual void OnStartDecode( LVImageSource * )
2325     {
2326         //CRLog::trace( "LVUnpackedImgSource::OnStartDecode" );
2327     }
2328     // aaaaaaaarrrrrrrrggggggggbbbbbbbb -> yyyyyyaa
grayPack(lUInt32 pixel)2329     inline lUInt8 grayPack( lUInt32 pixel )
2330     {
2331         lUInt8 gray = (lUInt8)(( (pixel & 0xFF) + ((pixel>>16) & 0xFF) + ((pixel>>7)&510) ) >> 2);
2332         lUInt8 alpha = (lUInt8)((pixel>>24) & 0xFF);
2333         return (gray & 0xFC) | ((alpha >> 6) & 3);
2334     }
2335     // yyyyyyaa -> aaaaaaaarrrrrrrrggggggggbbbbbbbb
grayUnpack(lUInt8 pixel)2336     inline lUInt32 grayUnpack( lUInt8 pixel )
2337     {
2338         lUInt32 gray = pixel & 0xFC;
2339         lUInt32 alpha = (pixel & 3) << 6;
2340         if ( alpha==0xC0 )
2341             alpha = 0xFF;
2342         return gray | (gray<<8) | (gray<<16) | (alpha<<24);
2343     }
OnLineDecoded(LVImageSource *,int y,lUInt32 * data)2344     virtual bool OnLineDecoded( LVImageSource *, int y, lUInt32 * data )
2345     {
2346         if ( y<0 || y>=_dy )
2347             return false;
2348         if ( _isGray ) {
2349             lUInt8 * dst = _grayImage + _dx * y;
2350             for ( int x=0; x<_dx; x++ ) {
2351                 dst[x] = grayPack( data[x] );
2352             }
2353         } else if ( _bpp==16 ) {
2354             lUInt16 * dst = _colorImage16 + _dx * y;
2355             for ( int x=0; x<_dx; x++ ) {
2356                 dst[x] = rgb888to565( data[x] );
2357             }
2358         } else {
2359             lUInt32 * dst = _colorImage + _dx * y;
2360             memcpy( dst, data, sizeof(lUInt32) * _dx );
2361         }
2362         return true;
2363     }
OnEndDecode(LVImageSource *,bool)2364     virtual void OnEndDecode( LVImageSource *, bool )
2365     {
2366         //CRLog::trace( "LVUnpackedImgSource::OnEndDecode" );
2367     }
GetSourceNode()2368     virtual ldomNode * GetSourceNode() { return NULL; }
GetSourceStream()2369     virtual LVStream * GetSourceStream() { return NULL; }
Compact()2370     virtual void   Compact() { }
GetWidth()2371     virtual int    GetWidth() { return _dx; }
GetHeight()2372     virtual int    GetHeight() { return _dy; }
Decode(LVImageDecoderCallback * callback)2373     virtual bool   Decode( LVImageDecoderCallback * callback )
2374     {
2375         callback->OnStartDecode( this );
2376         //bool res = false;
2377         if ( _isGray ) {
2378             // gray
2379             LVArray<lUInt32> line;
2380             line.reserve( _dx );
2381             for ( int y=0; y<_dy; y++ ) {
2382                 lUInt8 * src = _grayImage + _dx * y;
2383                 lUInt32 * dst = line.ptr();
2384                 for ( int x=0; x<_dx; x++ )
2385                     dst[x] = grayUnpack( src[x] );
2386                 callback->OnLineDecoded( this, y, dst );
2387             }
2388             line.clear();
2389         } else if ( _bpp==16 ) {
2390             // 16bit
2391             LVArray<lUInt32> line;
2392             line.reserve( _dx );
2393             for ( int y=0; y<_dy; y++ ) {
2394                 lUInt16 * src = _colorImage16 + _dx * y;
2395                 lUInt32 * dst = line.ptr();
2396                 for ( int x=0; x<_dx; x++ )
2397                     dst[x] = rgb565to888( src[x] );
2398                 callback->OnLineDecoded( this, y, dst );
2399             }
2400             line.clear();
2401         } else {
2402             // color
2403             for ( int y=0; y<_dy; y++ ) {
2404                 callback->OnLineDecoded( this, y, _colorImage + _dx * y );
2405             }
2406         }
2407         callback->OnEndDecode( this, false );
2408         return true;
2409     }
~LVUnpackedImgSource()2410     virtual ~LVUnpackedImgSource()
2411     {
2412         if ( _grayImage )
2413             free( _grayImage );
2414         if ( _colorImage )
2415             free( _colorImage );
2416         if ( _colorImage )
2417             free( _colorImage16 );
2418     }
2419 };
2420 
2421 class LVDrawBufImgSource : public LVImageSource
2422 {
2423 protected:
2424     LVColorDrawBuf * _buf;
2425     bool _own;
2426     int _dx;
2427     int _dy;
2428 public:
LVDrawBufImgSource(LVColorDrawBuf * buf,bool own)2429     LVDrawBufImgSource( LVColorDrawBuf * buf, bool own )
2430         : _buf(buf)
2431         , _own(own)
2432         , _dx( buf->GetWidth() )
2433         , _dy( buf->GetHeight() )
2434     {
2435     }
GetSourceNode()2436     virtual ldomNode * GetSourceNode() { return NULL; }
GetSourceStream()2437     virtual LVStream * GetSourceStream() { return NULL; }
Compact()2438     virtual void   Compact() { }
GetWidth()2439     virtual int    GetWidth() { return _dx; }
GetHeight()2440     virtual int    GetHeight() { return _dy; }
Decode(LVImageDecoderCallback * callback)2441     virtual bool   Decode( LVImageDecoderCallback * callback )
2442     {
2443         callback->OnStartDecode( this );
2444         //bool res = false;
2445         if ( _buf->GetBitsPerPixel()==32 ) {
2446             // 32 bpp
2447             for ( int y=0; y<_dy; y++ ) {
2448                 callback->OnLineDecoded( this, y, (lUInt32 *)_buf->GetScanLine(y) );
2449             }
2450         } else {
2451             // 16 bpp
2452             lUInt32 * row = new lUInt32[_dx];
2453             for ( int y=0; y<_dy; y++ ) {
2454                 lUInt16 * src = (lUInt16 *)_buf->GetScanLine(y);
2455                 for ( int x=0; x<_dx; x++ )
2456                     row[x] = rgb565to888(src[x]);
2457                 callback->OnLineDecoded( this, y, row );
2458             }
2459             delete[] row;
2460         }
2461         callback->OnEndDecode( this, false );
2462         return true;
2463     }
~LVDrawBufImgSource()2464     virtual ~LVDrawBufImgSource()
2465     {
2466         if ( _own )
2467             delete _buf;
2468     }
2469 };
2470 
2471 /// creates decoded memory copy of image, if it's unpacked size is less than maxSize
LVCreateUnpackedImageSource(LVImageSourceRef srcImage,int maxSize,bool gray)2472 LVImageSourceRef LVCreateUnpackedImageSource( LVImageSourceRef srcImage, int maxSize, bool gray )
2473 {
2474     if ( srcImage.isNull() )
2475         return srcImage;
2476     int dx = srcImage->GetWidth();
2477     int dy = srcImage->GetHeight();
2478     int sz = dx*dy * (gray?1:4);
2479     if ( sz>maxSize )
2480         return srcImage;
2481     CRLog::trace("Unpacking image %dx%d (%d)", dx, dy, sz);
2482     LVUnpackedImgSource * img = new LVUnpackedImgSource( srcImage, gray ? 8 : 32 );
2483     CRLog::trace("Unpacking done");
2484     return LVImageSourceRef( img );
2485 }
2486 
2487 /// creates decoded memory copy of image, if it's unpacked size is less than maxSize
LVCreateUnpackedImageSource(LVImageSourceRef srcImage,int maxSize,int bpp)2488 LVImageSourceRef LVCreateUnpackedImageSource( LVImageSourceRef srcImage, int maxSize, int bpp )
2489 {
2490     if ( srcImage.isNull() )
2491         return srcImage;
2492     int dx = srcImage->GetWidth();
2493     int dy = srcImage->GetHeight();
2494     int sz = dx*dy * (bpp>>3);
2495     if ( sz>maxSize )
2496         return srcImage;
2497     CRLog::trace("Unpacking image %dx%d (%d)", dx, dy, sz);
2498     LVUnpackedImgSource * img = new LVUnpackedImgSource( srcImage, bpp );
2499     CRLog::trace("Unpacking done");
2500     return LVImageSourceRef( img );
2501 }
2502 
LVCreateDrawBufImageSource(LVColorDrawBuf * buf,bool own)2503 LVImageSourceRef LVCreateDrawBufImageSource( LVColorDrawBuf * buf, bool own )
2504 {
2505     return LVImageSourceRef( new LVDrawBufImgSource( buf, own ) );
2506 }
2507 
2508 
2509 /// draws battery icon in specified rectangle of draw buffer; if font is specified, draws charge %
2510 // first icon is for charging, the rest - indicate progress icon[1] is lowest level, icon[n-1] is full power
2511 // if no icons provided, battery will be drawn
LVDrawBatteryIcon(LVDrawBuf * drawbuf,const lvRect & batteryRc,int percent,bool charging,LVRefVec<LVImageSource> icons,LVFont * font)2512 void LVDrawBatteryIcon( LVDrawBuf * drawbuf, const lvRect & batteryRc, int percent, bool charging, LVRefVec<LVImageSource> icons, LVFont * font )
2513 {
2514     lvRect rc( batteryRc );
2515     bool drawText = (font != NULL);
2516     if ( icons.length()>1 ) {
2517         int iconIndex = 0;
2518         if ( !charging ) {
2519             if ( icons.length()>2 ) {
2520                 int numTicks = icons.length() - 1;
2521                 int perTick = 10000/(numTicks -1);
2522                 //iconIndex = ((numTicks - 1) * percent + (100/numTicks/2) )/ 100 + 1;
2523                 iconIndex = (percent * 100 + perTick/2)/perTick + 1;
2524                 if ( iconIndex<1 )
2525                     iconIndex = 1;
2526                 if ( iconIndex>icons.length()-1 )
2527                     iconIndex = icons.length()-1;
2528             } else {
2529                 // empty battery icon, for % display
2530                 iconIndex = 1;
2531             }
2532         }
2533 
2534         lvPoint sz( icons[0]->GetWidth(), icons[0]->GetHeight() );
2535         rc.left += (rc.width() - sz.x)/2;
2536         rc.top += (rc.height() - sz.y)/2;
2537         rc.right = rc.left + sz.x;
2538         rc.bottom = rc.top + sz.y;
2539         LVImageSourceRef icon = icons[iconIndex];
2540         drawbuf->Draw( icon, rc.left,
2541             rc.top,
2542             sz.x,
2543             sz.y, false );
2544         if ( charging )
2545             drawText = false;
2546         rc.left += 3;
2547     } else {
2548         // todo: draw w/o icons
2549     }
2550 
2551     if ( drawText ) {
2552         // rc is rectangle to draw text to
2553         lString32 txt;
2554         if ( charging )
2555             txt = "+++";
2556         else
2557             txt = lString32::itoa(percent); // + U"%";
2558         int w = font->getTextWidth(txt.c_str(), txt.length());
2559         int h = font->getHeight();
2560         int x = (rc.left + rc.right - w)/2;
2561         int y = (rc.top + rc.bottom - h)/2+1;
2562         lUInt32 bgcolor = drawbuf->GetBackgroundColor();
2563         lUInt32 textcolor = drawbuf->GetTextColor();
2564 
2565         drawbuf->SetBackgroundColor( textcolor );
2566         drawbuf->SetTextColor( bgcolor );
2567         font->DrawTextString(drawbuf, x-1, y, txt.c_str(), txt.length(), '?', NULL);
2568         font->DrawTextString(drawbuf, x+1, y, txt.c_str(), txt.length(), '?', NULL);
2569 //        font->DrawTextString(drawbuf, x-1, y+1, txt.c_str(), txt.length(), '?', NULL);
2570 //        font->DrawTextString(drawbuf, x+1, y-1, txt.c_str(), txt.length(), '?', NULL);
2571         font->DrawTextString(drawbuf, x, y-1, txt.c_str(), txt.length(), '?', NULL);
2572         font->DrawTextString(drawbuf, x, y+1, txt.c_str(), txt.length(), '?', NULL);
2573 //        font->DrawTextString(drawbuf, x+1, y+1, txt.c_str(), txt.length(), '?', NULL);
2574 //        font->DrawTextString(drawbuf, x-1, y+1, txt.c_str(), txt.length(), '?', NULL);
2575         //drawbuf->SetBackgroundColor( textcolor );
2576         //drawbuf->SetTextColor( bgcolor );
2577         drawbuf->SetBackgroundColor( bgcolor );
2578         drawbuf->SetTextColor( textcolor );
2579         font->DrawTextString(drawbuf, x, y, txt.c_str(), txt.length(), '?', NULL);
2580     }
2581 }
2582