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