NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
bitmap.h
Go to the documentation of this file.
1#pragma once
2
3// Bitmap: a premultiplied-ARGB32 pixel buffer -- novasvg's drawable
4// surface, texture source, and render target all in one. Self-contained:
5// needs only Color and the low-level render::surface_t it wraps, nothing
6// from the SVG/Canvas/Font layers, so it can be used entirely on its own
7// (e.g. to build up an image and write it out) without pulling in the
8// rest of novasvg.
9
10#include "color.h"
11#include "render/blend.h" // memfill32, used by surface_clear()
12#include "render/paint.h" // color_to_argb32 / color_init_rgba32, used by surface_clear()
13#include "render/surface.h"
14
15#include <cstdint>
16#include <cstdio>
17#include <cstdlib>
18#include <cstring>
19#include <cctype>
20#include <string>
21#include <utility>
22
23namespace novasvg {
24using namespace render;
25
27public:
31 Bitmap() = default;
32
39 Bitmap(int width, int height);
40
49 Bitmap(uint8_t* data, int width, int height, int stride);
50
55 Bitmap(const Bitmap& bitmap);
56
61 Bitmap(Bitmap&& bitmap);
62
66 Bitmap(surface_t* surface) : m_surface(surface) {}
67
71 ~Bitmap();
72
78 Bitmap& operator=(const Bitmap& bitmap);
79
85 Bitmap& operator=(Bitmap&& bitmap);
86
91 void swap(Bitmap& bitmap);
92
97 uint8_t* data() const;
98
103 int width() const;
104
109 int height() const;
110
115 int stride() const;
116
121 void clear(const Color& value);
122
126 void convertToRGBA();
127
132 bool isNull() const { return m_surface == nullptr; }
133
139 bool valid() const { return !isNull(); }
140
146 bool writeToPng(const std::string& filename) const;
147
154 bool writeToPng(novasvg_write_func_t callback, void* closure) const;
155
159 bool writeToBmp(const std::string& filename) const;
160
164 bool writeToBmp(novasvg_write_func_t callback, void* closure) const;
165
169 bool writeToTga(const std::string& filename) const;
170
174 bool writeToTga(novasvg_write_func_t callback, void* closure) const;
175
180 bool writeToJpg(const std::string& filename, int quality = 80) const;
181
186 bool writeToJpg(novasvg_write_func_t callback, void* closure, int quality = 80) const;
187
197 bool write(const std::string& filename, int jpgQuality = 80) const;
198
202 surface_t* surface() const { return m_surface; }
203
204private:
205 surface_t* release();
206 surface_t* m_surface{nullptr};
207};
208
209
210// Bitmap's own image encoder (PNG/BMP/TGA/JPEG), used only by the
211// writeTo*()/write() methods above -- a class (BitmapCodec), not a pile
212// of free functions, precisely so nothing about writing an image needs a
213// symbol outside novasvg::Bitmap's own world. Ported from stb_image_write
214// v1.16 (Sean Barrett, public domain / MIT): every identifier renamed to
215// this project's naming and every function turned into a static method.
216// Upstream: https://github.com/nothings/stb
218public:
219
220#ifdef _WIN32
221 #ifndef _CRT_SECURE_NO_WARNINGS
222 #define _CRT_SECURE_NO_WARNINGS
223 #endif
224 #ifndef _CRT_NONSTDC_NO_DEPRECATE
225 #define _CRT_NONSTDC_NO_DEPRECATE
226 #endif
227#endif
228
229#ifndef NOVASVG_IW_NO_STDIO
230#endif // NOVASVG_IW_NO_STDIO
231
232
233#if defined(NOVASVG_IW_MALLOC) && defined(NOVASVG_IW_FREE) && (defined(NOVASVG_IW_REALLOC) || defined(NOVASVG_IW_REALLOC_SIZED))
234// ok
235#elif !defined(NOVASVG_IW_MALLOC) && !defined(NOVASVG_IW_FREE) && !defined(NOVASVG_IW_REALLOC) && !defined(NOVASVG_IW_REALLOC_SIZED)
236// ok
237#else
238#error "Must define all or none of NOVASVG_IW_MALLOC, NOVASVG_IW_FREE, and NOVASVG_IW_REALLOC (or NOVASVG_IW_REALLOC_SIZED)."
239#endif
240
241#ifndef NOVASVG_IW_MALLOC
242#define NOVASVG_IW_MALLOC(sz) malloc(sz)
243#define NOVASVG_IW_REALLOC(p,newsz) realloc(p,newsz)
244#define NOVASVG_IW_FREE(p) free(p)
245#endif
246
247#ifndef NOVASVG_IW_REALLOC_SIZED
248#define NOVASVG_IW_REALLOC_SIZED(p,oldsz,newsz) NOVASVG_IW_REALLOC(p,newsz)
249#endif
250
251
252#ifndef NOVASVG_IW_MEMMOVE
253#define NOVASVG_IW_MEMMOVE(a,b,sz) memmove(a,b,sz)
254#endif
255
256
257#ifndef NOVASVG_IW_ASSERT
258#define NOVASVG_IW_ASSERT(x) assert(x)
259#endif
260
261#define NOVASVG_IW_UCHAR(x) (unsigned char) ((x) & 0xff)
262
263inline static int g_png_compression_level = 8;
264inline static int g_tga_with_rle = 1;
265inline static int g_png_force_filter = -1;
266
267inline static int g_flip_vertically_on_write = 0;
268
269static void set_flip_vertically_on_write(int flag)
270{
272}
273
274typedef struct
275{
277 void *context;
278 unsigned char buffer[64];
281
282// initialize a callback-based context
283static void write_start_callbacks(write_context *s, write_func_t c, void *context)
284{
285 s->func = c;
286 s->context = context;
287}
288
289#ifndef NOVASVG_IW_NO_STDIO
290
291static void write_stdio_callback(void *context, void *data, int size)
292{
293 fwrite(data,1,size,(FILE*) context);
294}
295
296#if defined(_WIN32) && defined(NOVASVG_IW_WINDOWS_UTF8)
297#ifdef __cplusplus
298#define NOVASVG_IW_EXTERN extern "C"
299#else
300#define NOVASVG_IW_EXTERN extern
301#endif
302NOVASVG_IW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
303NOVASVG_IW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
304
305static int convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
306{
307 return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
308}
309#endif
310
311static FILE *open_file(char const *filename, char const *mode)
312{
313 FILE *f;
314#if defined(_WIN32) && defined(NOVASVG_IW_WINDOWS_UTF8)
315 wchar_t wMode[64];
316 wchar_t wFilename[1024];
317 if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
318 return 0;
319
320 if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
321 return 0;
322
323#if defined(_MSC_VER) && _MSC_VER >= 1400
324 if (0 != _wfopen_s(&f, wFilename, wMode))
325 f = 0;
326#else
327 f = _wfopen(wFilename, wMode);
328#endif
329
330#elif defined(_MSC_VER) && _MSC_VER >= 1400
331 if (0 != fopen_s(&f, filename, mode))
332 f=0;
333#else
334 f = fopen(filename, mode);
335#endif
336 return f;
337}
338
339static int write_start_file(write_context *s, const char *filename)
340{
341 FILE *f = open_file(filename, "wb");
343 return f != NULL;
344}
345
347{
348 fclose((FILE *)s->context);
349}
350
351#endif // !NOVASVG_IW_NO_STDIO
352
353typedef unsigned int iw_uint32;
354typedef int iw_uint32_size_check[sizeof(iw_uint32) == 4 ? 1 : -1];
355
356static void write_formatted_v(write_context *s, const char *fmt, va_list v)
357{
358 while (*fmt) {
359 switch (*fmt++) {
360 case ' ': break;
361 case '1': { unsigned char x = NOVASVG_IW_UCHAR(va_arg(v, int));
362 s->func(s->context,&x,1);
363 break; }
364 case '2': { int x = va_arg(v,int);
365 unsigned char b[2];
366 b[0] = NOVASVG_IW_UCHAR(x);
367 b[1] = NOVASVG_IW_UCHAR(x>>8);
368 s->func(s->context,b,2);
369 break; }
370 case '4': { iw_uint32 x = va_arg(v,int);
371 unsigned char b[4];
372 b[0]=NOVASVG_IW_UCHAR(x);
373 b[1]=NOVASVG_IW_UCHAR(x>>8);
374 b[2]=NOVASVG_IW_UCHAR(x>>16);
375 b[3]=NOVASVG_IW_UCHAR(x>>24);
376 s->func(s->context,b,4);
377 break; }
378 default:
380 return;
381 }
382 }
383}
384
385static void write_formatted(write_context *s, const char *fmt, ...)
386{
387 va_list v;
388 va_start(v, fmt);
389 write_formatted_v(s, fmt, v);
390 va_end(v);
391}
392
394{
395 if (s->buf_used) {
396 s->func(s->context, &s->buffer, s->buf_used);
397 s->buf_used = 0;
398 }
399}
400
401static void write_putc(write_context *s, unsigned char c)
402{
403 s->func(s->context, &c, 1);
404}
405
406static void write_u8(write_context *s, unsigned char a)
407{
408 if ((size_t)s->buf_used + 1 > sizeof(s->buffer))
409 write_flush(s);
410 s->buffer[s->buf_used++] = a;
411}
412
413static void write_u8x3(write_context *s, unsigned char a, unsigned char b, unsigned char c)
414{
415 int n;
416 if ((size_t)s->buf_used + 3 > sizeof(s->buffer))
417 write_flush(s);
418 n = s->buf_used;
419 s->buf_used = n+3;
420 s->buffer[n+0] = a;
421 s->buffer[n+1] = b;
422 s->buffer[n+2] = c;
423}
424
425static void write_pixel(write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
426{
427 unsigned char bg[3] = { 255, 0, 255}, px[3];
428 int k;
429
430 if (write_alpha < 0)
431 write_u8(s, d[comp - 1]);
432
433 switch (comp) {
434 case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
435 case 1:
436 if (expand_mono)
437 write_u8x3(s, d[0], d[0], d[0]); // monochrome bmp
438 else
439 write_u8(s, d[0]); // monochrome TGA
440 break;
441 case 4:
442 if (!write_alpha) {
443 // composite against pink background
444 for (k = 0; k < 3; ++k)
445 px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
446 write_u8x3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
447 break;
448 }
449 /* FALLTHROUGH */
450 case 3:
451 write_u8x3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
452 break;
453 }
454 if (write_alpha > 0)
455 write_u8(s, d[comp - 1]);
456}
457
458static void write_pixels(write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
459{
460 iw_uint32 zero = 0;
461 int i,j, j_end;
462
463 if (y <= 0)
464 return;
465
467 vdir *= -1;
468
469 if (vdir < 0) {
470 j_end = -1; j = y-1;
471 } else {
472 j_end = y; j = 0;
473 }
474
475 for (; j != j_end; j += vdir) {
476 for (i=0; i < x; ++i) {
477 unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
478 write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
479 }
480 write_flush(s);
481 s->func(s->context, &zero, scanline_pad);
482 }
483}
484
485static int outfile(write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
486{
487 if (y < 0 || x < 0) {
488 return 0;
489 } else {
490 va_list v;
491 va_start(v, fmt);
492 write_formatted_v(s, fmt, v);
493 va_end(v);
494 write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
495 return 1;
496 }
497}
498
499static int write_bmp_core(write_context *s, int x, int y, int comp, const void *data)
500{
501 if (comp != 4) {
502 // write RGB bitmap
503 int pad = (-x*3) & 3;
504 return outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
505 "11 4 22 4" "4 44 22 444444",
506 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
507 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
508 } else {
509 // RGBA bitmaps need a v4 header
510 // use BI_BITFIELDS mode with 32bpp and alpha mask
511 // (straight BI_RGB with alpha mask doesn't work in most readers)
512 return outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0,
513 "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444",
514 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header
515 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header
516 }
517}
518
519static int write_bmp_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data)
520{
521 write_context s = { 0 };
522 write_start_callbacks(&s, func, context);
523 return write_bmp_core(&s, x, y, comp, data);
524}
525
526#ifndef NOVASVG_IW_NO_STDIO
527static int write_bmp(char const *filename, int x, int y, int comp, const void *data)
528{
529 write_context s = { 0 };
530 if (write_start_file(&s,filename)) {
531 int r = write_bmp_core(&s, x, y, comp, data);
532 write_end_file(&s);
533 return r;
534 } else
535 return 0;
536}
537#endif
538
539static int write_tga_core(write_context *s, int x, int y, int comp, void *data)
540{
541 int has_alpha = (comp == 2 || comp == 4);
542 int colorbytes = has_alpha ? comp-1 : comp;
543 int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
544
545 if (y < 0 || x < 0)
546 return 0;
547
548 if (!g_tga_with_rle) {
549 return outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
550 "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
551 } else {
552 int i,j,k;
553 int jend, jdir;
554
555 write_formatted(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
556
558 j = 0;
559 jend = y;
560 jdir = 1;
561 } else {
562 j = y-1;
563 jend = -1;
564 jdir = -1;
565 }
566 for (; j != jend; j += jdir) {
567 unsigned char *row = (unsigned char *) data + j * x * comp;
568 int len;
569
570 for (i = 0; i < x; i += len) {
571 unsigned char *begin = row + i * comp;
572 int diff = 1;
573 len = 1;
574
575 if (i < x - 1) {
576 ++len;
577 diff = memcmp(begin, row + (i + 1) * comp, comp);
578 if (diff) {
579 const unsigned char *prev = begin;
580 for (k = i + 2; k < x && len < 128; ++k) {
581 if (memcmp(prev, row + k * comp, comp)) {
582 prev += comp;
583 ++len;
584 } else {
585 --len;
586 break;
587 }
588 }
589 } else {
590 for (k = i + 2; k < x && len < 128; ++k) {
591 if (!memcmp(begin, row + k * comp, comp)) {
592 ++len;
593 } else {
594 break;
595 }
596 }
597 }
598 }
599
600 if (diff) {
601 unsigned char header = NOVASVG_IW_UCHAR(len - 1);
602 write_u8(s, header);
603 for (k = 0; k < len; ++k) {
604 write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
605 }
606 } else {
607 unsigned char header = NOVASVG_IW_UCHAR(len - 129);
608 write_u8(s, header);
609 write_pixel(s, -1, comp, has_alpha, 0, begin);
610 }
611 }
612 }
613 write_flush(s);
614 }
615 return 1;
616}
617
618static int write_tga_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data)
619{
620 write_context s = { 0 };
621 write_start_callbacks(&s, func, context);
622 return write_tga_core(&s, x, y, comp, (void *) data);
623}
624
625#ifndef NOVASVG_IW_NO_STDIO
626static int write_tga(char const *filename, int x, int y, int comp, const void *data)
627{
628 write_context s = { 0 };
629 if (write_start_file(&s,filename)) {
630 int r = write_tga_core(&s, x, y, comp, (void *) data);
631 write_end_file(&s);
632 return r;
633 } else
634 return 0;
635}
636#endif
637
638// *************************************************************************************************
639// Radiance RGBE HDR writer
640// by Baldur Karlsson
641
642#define iw_max(a, b) ((a) > (b) ? (a) : (b))
643
644#ifndef NOVASVG_IW_NO_STDIO
645
646static void hdr_linear_to_rgbe(unsigned char *rgbe, float *linear)
647{
648 int exponent;
649 float maxcomp = iw_max(linear[0], iw_max(linear[1], linear[2]));
650
651 if (maxcomp < 1e-32f) {
652 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
653 } else {
654 float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
655
656 rgbe[0] = (unsigned char)(linear[0] * normalize);
657 rgbe[1] = (unsigned char)(linear[1] * normalize);
658 rgbe[2] = (unsigned char)(linear[2] * normalize);
659 rgbe[3] = (unsigned char)(exponent + 128);
660 }
661}
662
663static void hdr_write_run_data(write_context *s, int length, unsigned char databyte)
664{
665 unsigned char lengthbyte = NOVASVG_IW_UCHAR(length+128);
666 NOVASVG_IW_ASSERT(length+128 <= 255);
667 s->func(s->context, &lengthbyte, 1);
668 s->func(s->context, &databyte, 1);
669}
670
671static void hdr_write_dump_data(write_context *s, int length, unsigned char *data)
672{
673 unsigned char lengthbyte = NOVASVG_IW_UCHAR(length);
674 NOVASVG_IW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
675 s->func(s->context, &lengthbyte, 1);
676 s->func(s->context, data, length);
677}
678
679static void hdr_write_scanline(write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
680{
681 unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
682 unsigned char rgbe[4];
683 float linear[3];
684 int x;
685
686 scanlineheader[2] = (width&0xff00)>>8;
687 scanlineheader[3] = (width&0x00ff);
688
689 /* skip RLE for images too small or large */
690 if (width < 8 || width >= 32768) {
691 for (x=0; x < width; x++) {
692 switch (ncomp) {
693 case 4: /* fallthrough */
694 case 3: linear[2] = scanline[x*ncomp + 2];
695 linear[1] = scanline[x*ncomp + 1];
696 linear[0] = scanline[x*ncomp + 0];
697 break;
698 default:
699 linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
700 break;
701 }
702 hdr_linear_to_rgbe(rgbe, linear);
703 s->func(s->context, rgbe, 4);
704 }
705 } else {
706 int c,r;
707 /* encode into scratch buffer */
708 for (x=0; x < width; x++) {
709 switch(ncomp) {
710 case 4: /* fallthrough */
711 case 3: linear[2] = scanline[x*ncomp + 2];
712 linear[1] = scanline[x*ncomp + 1];
713 linear[0] = scanline[x*ncomp + 0];
714 break;
715 default:
716 linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
717 break;
718 }
719 hdr_linear_to_rgbe(rgbe, linear);
720 scratch[x + width*0] = rgbe[0];
721 scratch[x + width*1] = rgbe[1];
722 scratch[x + width*2] = rgbe[2];
723 scratch[x + width*3] = rgbe[3];
724 }
725
726 s->func(s->context, scanlineheader, 4);
727
728 /* RLE each component separately */
729 for (c=0; c < 4; c++) {
730 unsigned char *comp = &scratch[width*c];
731
732 x = 0;
733 while (x < width) {
734 // find first run
735 r = x;
736 while (r+2 < width) {
737 if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
738 break;
739 ++r;
740 }
741 if (r+2 >= width)
742 r = width;
743 // dump up to first run
744 while (x < r) {
745 int len = r-x;
746 if (len > 128) len = 128;
747 hdr_write_dump_data(s, len, &comp[x]);
748 x += len;
749 }
750 // if there's a run, output it
751 if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
752 // find next byte after run
753 while (r < width && comp[r] == comp[x])
754 ++r;
755 // output run up to r
756 while (x < r) {
757 int len = r-x;
758 if (len > 127) len = 127;
759 hdr_write_run_data(s, len, comp[x]);
760 x += len;
761 }
762 }
763 }
764 }
765 }
766}
767
768static int write_hdr_core(write_context *s, int x, int y, int comp, float *data)
769{
770 if (y <= 0 || x <= 0 || data == NULL)
771 return 0;
772 else {
773 // Each component is stored separately. Allocate scratch space for full output scanline.
774 unsigned char *scratch = (unsigned char *) NOVASVG_IW_MALLOC(x*4);
775 int i, len;
776 char buffer[128];
777 char header[] = "#?RADIANCE\n# Written by novasvg\nFORMAT=32-bit_rle_rgbe\n";
778 s->func(s->context, header, sizeof(header)-1);
779
780#ifdef __STDC_LIB_EXT1__
781 len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
782#else
783 len = snprintf(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
784#endif
785 s->func(s->context, buffer, len);
786
787 for(i=0; i < y; i++)
788 hdr_write_scanline(s, x, comp, scratch, data + comp*x*(g_flip_vertically_on_write ? y-1-i : i));
789 NOVASVG_IW_FREE(scratch);
790 return 1;
791 }
792}
793
794static int write_hdr_to_func(write_func_t func, void *context, int x, int y, int comp, const float *data)
795{
796 write_context s = { 0 };
797 write_start_callbacks(&s, func, context);
798 return write_hdr_core(&s, x, y, comp, (float *) data);
799}
800
801static int write_hdr(char const *filename, int x, int y, int comp, const float *data)
802{
803 write_context s = { 0 };
804 if (write_start_file(&s,filename)) {
805 int r = write_hdr_core(&s, x, y, comp, (float *) data);
806 write_end_file(&s);
807 return r;
808 } else
809 return 0;
810}
811#endif // NOVASVG_IW_NO_STDIO
812
813
815//
816// PNG writer
817//
818
819#ifndef NOVASVG_IW_ZLIB_COMPRESS
820// stretchy buffer; sb_push() == vector<>::push_back() -- sb_count() == vector<>::size()
821#define sb_raw(a) ((int *) (void *) (a) - 2)
822#define sb_m(a) sb_raw(a)[0]
823#define sb_n(a) sb_raw(a)[1]
824
825#define sb_needgrow(a,n) ((a)==0 || sb_n(a)+n >= sb_m(a))
826#define sb_maybegrow(a,n) (sb_needgrow(a,(n)) ? sb_grow(a,n) : 0)
827#define sb_grow(a,n) sb_growf((void **) &(a), (n), sizeof(*(a)))
828
829#define sb_push(a, v) (sb_maybegrow(a,1), (a)[sb_n(a)++] = (v))
830#define sb_count(a) ((a) ? sb_n(a) : 0)
831#define sb_free(a) ((a) ? NOVASVG_IW_FREE(sb_raw(a)),0 : 0)
832
833static void *sb_growf(void **arr, int increment, int itemsize)
834{
835 int m = *arr ? 2*sb_m(*arr)+increment : increment+1;
836 void *p = NOVASVG_IW_REALLOC_SIZED(*arr ? sb_raw(*arr) : 0, *arr ? (sb_m(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
838 if (p) {
839 if (!*arr) ((int *) p)[1] = 0;
840 *arr = (void *) ((int *) p + 2);
841 sb_m(*arr) = m;
842 }
843 return *arr;
844}
845
846static unsigned char *zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
847{
848 while (*bitcount >= 8) {
849 sb_push(data, NOVASVG_IW_UCHAR(*bitbuffer));
850 *bitbuffer >>= 8;
851 *bitcount -= 8;
852 }
853 return data;
854}
855
856static int zlib_bitrev(int code, int codebits)
857{
858 int res=0;
859 while (codebits--) {
860 res = (res << 1) | (code & 1);
861 code >>= 1;
862 }
863 return res;
864}
865
866static unsigned int zlib_countm(unsigned char *a, unsigned char *b, int limit)
867{
868 int i;
869 for (i=0; i < limit && i < 258; ++i)
870 if (a[i] != b[i]) break;
871 return i;
872}
873
874static unsigned int g_zlib_hash_table(unsigned char *data)
875{
876 iw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
877 hash ^= hash << 3;
878 hash += hash >> 5;
879 hash ^= hash << 4;
880 hash += hash >> 17;
881 hash ^= hash << 25;
882 hash += hash >> 6;
883 return hash;
884}
885
886#define zlib_flush() (out = zlib_flushf(out, &bitbuf, &bitcount))
887#define zlib_add(code,codebits) \
888 (bitbuf |= (code) << bitcount, bitcount += (codebits), zlib_flush())
889#define zlib_huffa(b,c) zlib_add(zlib_bitrev(b,c),c)
890// default huffman tables
891#define zlib_huff1(n) zlib_huffa(0x30 + (n), 8)
892#define zlib_huff2(n) zlib_huffa(0x190 + (n)-144, 9)
893#define zlib_huff3(n) zlib_huffa(0 + (n)-256,7)
894#define zlib_huff4(n) zlib_huffa(0xc0 + (n)-280,8)
895#define zlib_huff(n) ((n) <= 143 ? zlib_huff1(n) : (n) <= 255 ? zlib_huff2(n) : (n) <= 279 ? zlib_huff3(n) : zlib_huff4(n))
896#define zlib_huffb(n) ((n) <= 143 ? zlib_huff1(n) : zlib_huff2(n))
897
898#define ZLIB_HASH 16384
899
900#endif // NOVASVG_IW_ZLIB_COMPRESS
901
902static unsigned char * zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
903{
904#ifdef NOVASVG_IW_ZLIB_COMPRESS
905 // user provided a zlib compress implementation, use that
906 return NOVASVG_IW_ZLIB_COMPRESS(data, data_len, out_len, quality);
907#else // use builtin
908 static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
909 static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
910 static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
911 static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
912 unsigned int bitbuf=0;
913 int i,j, bitcount=0;
914 unsigned char *out = NULL;
915 unsigned char ***hash_table = (unsigned char***) NOVASVG_IW_MALLOC(ZLIB_HASH * sizeof(unsigned char**));
916 if (hash_table == NULL)
917 return NULL;
918 if (quality < 5) quality = 5;
919
920 sb_push(out, 0x78); // DEFLATE 32K window
921 sb_push(out, 0x5e); // FLEVEL = 1
922 zlib_add(1,1); // BFINAL = 1
923 zlib_add(1,2); // BTYPE = 1 -- fixed huffman
924
925 for (i=0; i < ZLIB_HASH; ++i)
926 hash_table[i] = NULL;
927
928 i=0;
929 while (i < data_len-3) {
930 // hash next 3 bytes of data to be compressed
931 int h = g_zlib_hash_table(data+i)&(ZLIB_HASH-1), best=3;
932 unsigned char *bestloc = 0;
933 unsigned char **hlist = hash_table[h];
934 int n = sb_count(hlist);
935 for (j=0; j < n; ++j) {
936 if (hlist[j]-data > i-32768) { // if entry lies within window
937 int d = zlib_countm(hlist[j], data+i, data_len-i);
938 if (d >= best) { best=d; bestloc=hlist[j]; }
939 }
940 }
941 // when hash table entry is too long, delete half the entries
942 if (hash_table[h] && sb_n(hash_table[h]) == 2*quality) {
943 NOVASVG_IW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
944 sb_n(hash_table[h]) = quality;
945 }
946 sb_push(hash_table[h],data+i);
947
948 if (bestloc) {
949 // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
950 h = g_zlib_hash_table(data+i+1)&(ZLIB_HASH-1);
951 hlist = hash_table[h];
952 n = sb_count(hlist);
953 for (j=0; j < n; ++j) {
954 if (hlist[j]-data > i-32767) {
955 int e = zlib_countm(hlist[j], data+i+1, data_len-i-1);
956 if (e > best) { // if next match is better, bail on current match
957 bestloc = NULL;
958 break;
959 }
960 }
961 }
962 }
963
964 if (bestloc) {
965 int d = (int) (data+i - bestloc); // distance back
966 NOVASVG_IW_ASSERT(d <= 32767 && best <= 258);
967 for (j=0; best > lengthc[j+1]-1; ++j);
968 zlib_huff(j+257);
969 if (lengtheb[j]) zlib_add(best - lengthc[j], lengtheb[j]);
970 for (j=0; d > distc[j+1]-1; ++j);
971 zlib_add(zlib_bitrev(j,5),5);
972 if (disteb[j]) zlib_add(d - distc[j], disteb[j]);
973 i += best;
974 } else {
975 zlib_huffb(data[i]);
976 ++i;
977 }
978 }
979 // write out final bytes
980 for (;i < data_len; ++i)
981 zlib_huffb(data[i]);
982 zlib_huff(256); // end of block
983 // pad with 0 bits to byte boundary
984 while (bitcount)
985 zlib_add(0,1);
986
987 for (i=0; i < ZLIB_HASH; ++i)
988 (void) sb_free(hash_table[i]);
989 NOVASVG_IW_FREE(hash_table);
990
991 // store uncompressed instead if compression was worse
992 if (sb_n(out) > data_len + 2 + ((data_len+32766)/32767)*5) {
993 sb_n(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1
994 for (j = 0; j < data_len;) {
995 int blocklen = data_len - j;
996 if (blocklen > 32767) blocklen = 32767;
997 sb_push(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression
998 sb_push(out, NOVASVG_IW_UCHAR(blocklen)); // LEN
999 sb_push(out, NOVASVG_IW_UCHAR(blocklen >> 8));
1000 sb_push(out, NOVASVG_IW_UCHAR(~blocklen)); // NLEN
1001 sb_push(out, NOVASVG_IW_UCHAR(~blocklen >> 8));
1002 memcpy(out+sb_n(out), data+j, blocklen);
1003 sb_n(out) += blocklen;
1004 j += blocklen;
1005 }
1006 }
1007
1008 {
1009 // compute adler32 on input
1010 unsigned int s1=1, s2=0;
1011 int blocklen = (int) (data_len % 5552);
1012 j=0;
1013 while (j < data_len) {
1014 for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }
1015 s1 %= 65521; s2 %= 65521;
1016 j += blocklen;
1017 blocklen = 5552;
1018 }
1019 sb_push(out, NOVASVG_IW_UCHAR(s2 >> 8));
1020 sb_push(out, NOVASVG_IW_UCHAR(s2));
1021 sb_push(out, NOVASVG_IW_UCHAR(s1 >> 8));
1022 sb_push(out, NOVASVG_IW_UCHAR(s1));
1023 }
1024 *out_len = sb_n(out);
1025 // make returned pointer freeable
1026 NOVASVG_IW_MEMMOVE(sb_raw(out), out, *out_len);
1027 return (unsigned char *) sb_raw(out);
1028#endif // NOVASVG_IW_ZLIB_COMPRESS
1029}
1030
1031static unsigned int png_crc32(unsigned char *buffer, int len)
1032{
1033#ifdef NOVASVG_IW_CRC32
1034 return NOVASVG_IW_CRC32(buffer, len);
1035#else
1036 static unsigned int crc_table[256] =
1037 {
1038 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
1039 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
1040 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
1041 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
1042 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
1043 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
1044 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
1045 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
1046 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
1047 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
1048 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
1049 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
1050 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
1051 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
1052 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
1053 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
1054 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
1055 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
1056 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
1057 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
1058 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
1059 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
1060 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
1061 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
1062 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
1063 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
1064 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
1065 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
1066 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
1067 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
1068 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
1069 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
1070 };
1071
1072 unsigned int crc = ~0u;
1073 int i;
1074 for (i=0; i < len; ++i)
1075 crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
1076 return ~crc;
1077#endif
1078}
1079
1080#define png_write_u32x4(o,a,b,c,d) ((o)[0]=NOVASVG_IW_UCHAR(a),(o)[1]=NOVASVG_IW_UCHAR(b),(o)[2]=NOVASVG_IW_UCHAR(c),(o)[3]=NOVASVG_IW_UCHAR(d),(o)+=4)
1081#define zlib_write_u32(data,v) png_write_u32x4(data, (v)>>24,(v)>>16,(v)>>8,(v));
1082#define png_write_tag(data,s) png_write_u32x4(data, s[0],s[1],s[2],s[3])
1083
1084static void png_write_crc(unsigned char **data, int len)
1085{
1086 unsigned int crc = png_crc32(*data - len - 4, len+4);
1087 zlib_write_u32(*data, crc);
1088}
1089
1090static unsigned char png_paeth_predictor(int a, int b, int c)
1091{
1092 int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
1093 if (pa <= pb && pa <= pc) return NOVASVG_IW_UCHAR(a);
1094 if (pb <= pc) return NOVASVG_IW_UCHAR(b);
1095 return NOVASVG_IW_UCHAR(c);
1096}
1097
1098// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
1099static void png_encode_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)
1100{
1101 static int mapping[] = { 0,1,2,3,4 };
1102 static int firstmap[] = { 0,1,0,5,6 };
1103 int *mymap = (y != 0) ? mapping : firstmap;
1104 int i;
1105 int type = mymap[filter_type];
1106 unsigned char *z = pixels + stride_bytes * (g_flip_vertically_on_write ? height-1-y : y);
1107 int signed_stride = g_flip_vertically_on_write ? -stride_bytes : stride_bytes;
1108
1109 if (type==0) {
1110 memcpy(line_buffer, z, width*n);
1111 return;
1112 }
1113
1114 // first loop isn't optimized since it's just one pixel
1115 for (i = 0; i < n; ++i) {
1116 switch (type) {
1117 case 1: line_buffer[i] = z[i]; break;
1118 case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;
1119 case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;
1120 case 4: line_buffer[i] = (signed char) (z[i] - png_paeth_predictor(0,z[i-signed_stride],0)); break;
1121 case 5: line_buffer[i] = z[i]; break;
1122 case 6: line_buffer[i] = z[i]; break;
1123 }
1124 }
1125 switch (type) {
1126 case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;
1127 case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;
1128 case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;
1129 case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - png_paeth_predictor(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;
1130 case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;
1131 case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - png_paeth_predictor(z[i-n], 0,0); break;
1132 }
1133}
1134
1135static unsigned char *write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
1136{
1137 int force_filter = g_png_force_filter;
1138 int ctype[5] = { -1, 0, 4, 2, 6 };
1139 unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
1140 unsigned char *out,*o, *filt, *zlib;
1141 signed char *line_buffer;
1142 int j,zlen;
1143
1144 if (stride_bytes == 0)
1145 stride_bytes = x * n;
1146
1147 if (force_filter >= 5) {
1148 force_filter = -1;
1149 }
1150
1151 filt = (unsigned char *) NOVASVG_IW_MALLOC((x*n+1) * y); if (!filt) return 0;
1152 line_buffer = (signed char *) NOVASVG_IW_MALLOC(x * n); if (!line_buffer) { NOVASVG_IW_FREE(filt); return 0; }
1153 for (j=0; j < y; ++j) {
1154 int filter_type;
1155 if (force_filter > -1) {
1156 filter_type = force_filter;
1157 png_encode_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);
1158 } else { // Estimate the best filter by running through all of them:
1159 int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
1160 for (filter_type = 0; filter_type < 5; filter_type++) {
1161 png_encode_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);
1162
1163 // Estimate the entropy of the line using this filter; the less, the better.
1164 est = 0;
1165 for (i = 0; i < x*n; ++i) {
1166 est += abs((signed char) line_buffer[i]);
1167 }
1168 if (est < best_filter_val) {
1169 best_filter_val = est;
1170 best_filter = filter_type;
1171 }
1172 }
1173 if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
1174 png_encode_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
1175 filter_type = best_filter;
1176 }
1177 }
1178 // when we get here, filter_type contains the filter type, and line_buffer contains the data
1179 filt[j*(x*n+1)] = (unsigned char) filter_type;
1180 NOVASVG_IW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
1181 }
1182 NOVASVG_IW_FREE(line_buffer);
1183 zlib = zlib_compress(filt, y*( x*n+1), &zlen, g_png_compression_level);
1184 NOVASVG_IW_FREE(filt);
1185 if (!zlib) return 0;
1186
1187 // each tag requires 12 bytes of overhead
1188 out = (unsigned char *) NOVASVG_IW_MALLOC(8 + 12+13 + 12+zlen + 12);
1189 if (!out) return 0;
1190 *out_len = 8 + 12+13 + 12+zlen + 12;
1191
1192 o=out;
1193 NOVASVG_IW_MEMMOVE(o,sig,8); o+= 8;
1194 zlib_write_u32(o, 13); // header length
1195 png_write_tag(o, "IHDR");
1196 zlib_write_u32(o, x);
1197 zlib_write_u32(o, y);
1198 *o++ = 8;
1199 *o++ = NOVASVG_IW_UCHAR(ctype[n]);
1200 *o++ = 0;
1201 *o++ = 0;
1202 *o++ = 0;
1203 png_write_crc(&o,13);
1204
1205 zlib_write_u32(o, zlen);
1206 png_write_tag(o, "IDAT");
1207 NOVASVG_IW_MEMMOVE(o, zlib, zlen);
1208 o += zlen;
1209 NOVASVG_IW_FREE(zlib);
1210 png_write_crc(&o, zlen);
1211
1212 zlib_write_u32(o,0);
1213 png_write_tag(o, "IEND");
1214 png_write_crc(&o,0);
1215
1216 NOVASVG_IW_ASSERT(o == out + *out_len);
1217
1218 return out;
1219}
1220
1221#ifndef NOVASVG_IW_NO_STDIO
1222static int write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
1223{
1224 FILE *f;
1225 int len;
1226 unsigned char *png = write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1227 if (png == NULL) return 0;
1228
1229 f = open_file(filename, "wb");
1230 if (!f) { NOVASVG_IW_FREE(png); return 0; }
1231 fwrite(png, 1, len, f);
1232 fclose(f);
1233 NOVASVG_IW_FREE(png);
1234 return 1;
1235}
1236#endif
1237
1238static int write_png_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
1239{
1240 int len;
1241 unsigned char *png = write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1242 if (png == NULL) return 0;
1243 func(context, png, len);
1244 NOVASVG_IW_FREE(png);
1245 return 1;
1246}
1247
1248
1249/* ***************************************************************************
1250 *
1251 * JPEG writer
1252 *
1253 * This is based on Jon Olick's jo_jpeg.cpp:
1254 * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html
1255 */
1256
1257inline static const unsigned char jpg_zig_zag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,
1258 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
1259
1260static void jpg_write_bits(write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {
1261 int bitBuf = *bitBufP, bitCnt = *bitCntP;
1262 bitCnt += bs[1];
1263 bitBuf |= bs[0] << (24 - bitCnt);
1264 while(bitCnt >= 8) {
1265 unsigned char c = (bitBuf >> 16) & 255;
1266 write_putc(s, c);
1267 if(c == 255) {
1268 write_putc(s, 0);
1269 }
1270 bitBuf <<= 8;
1271 bitCnt -= 8;
1272 }
1273 *bitBufP = bitBuf;
1274 *bitCntP = bitCnt;
1275}
1276
1277static void jpg_dct(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {
1278 float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;
1279 float z1, z2, z3, z4, z5, z11, z13;
1280
1281 float tmp0 = d0 + d7;
1282 float tmp7 = d0 - d7;
1283 float tmp1 = d1 + d6;
1284 float tmp6 = d1 - d6;
1285 float tmp2 = d2 + d5;
1286 float tmp5 = d2 - d5;
1287 float tmp3 = d3 + d4;
1288 float tmp4 = d3 - d4;
1289
1290 // Even part
1291 float tmp10 = tmp0 + tmp3; // phase 2
1292 float tmp13 = tmp0 - tmp3;
1293 float tmp11 = tmp1 + tmp2;
1294 float tmp12 = tmp1 - tmp2;
1295
1296 d0 = tmp10 + tmp11; // phase 3
1297 d4 = tmp10 - tmp11;
1298
1299 z1 = (tmp12 + tmp13) * 0.707106781f; // c4
1300 d2 = tmp13 + z1; // phase 5
1301 d6 = tmp13 - z1;
1302
1303 // Odd part
1304 tmp10 = tmp4 + tmp5; // phase 2
1305 tmp11 = tmp5 + tmp6;
1306 tmp12 = tmp6 + tmp7;
1307
1308 // The rotator is modified from fig 4-8 to avoid extra negations.
1309 z5 = (tmp10 - tmp12) * 0.382683433f; // c6
1310 z2 = tmp10 * 0.541196100f + z5; // c2-c6
1311 z4 = tmp12 * 1.306562965f + z5; // c2+c6
1312 z3 = tmp11 * 0.707106781f; // c4
1313
1314 z11 = tmp7 + z3; // phase 5
1315 z13 = tmp7 - z3;
1316
1317 *d5p = z13 + z2; // phase 6
1318 *d3p = z13 - z2;
1319 *d1p = z11 + z4;
1320 *d7p = z11 - z4;
1321
1322 *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6;
1323}
1324
1325static void jpg_calc_bits(int val, unsigned short bits[2]) {
1326 int tmp1 = val < 0 ? -val : val;
1327 val = val < 0 ? val-1 : val;
1328 bits[1] = 1;
1329 while(tmp1 >>= 1) {
1330 ++bits[1];
1331 }
1332 bits[0] = val & ((1<<bits[1])-1);
1333}
1334
1335static int jpg_process_du(write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {
1336 const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };
1337 const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };
1338 int dataOff, i, j, n, diff, end0pos, x, y;
1339 int DU[64];
1340
1341 // DCT rows
1342 for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) {
1343 jpg_dct(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);
1344 }
1345 // DCT columns
1346 for(dataOff=0; dataOff<8; ++dataOff) {
1347 jpg_dct(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4],
1348 &CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]);
1349 }
1350 // Quantize/descale/zigzag the coefficients
1351 for(y = 0, j=0; y < 8; ++y) {
1352 for(x = 0; x < 8; ++x,++j) {
1353 float v;
1354 i = y*du_stride+x;
1355 v = CDU[i]*fdtbl[j];
1356 // DU[jpg_zig_zag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
1357 // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
1358 DU[jpg_zig_zag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
1359 }
1360 }
1361
1362 // Encode DC
1363 diff = DU[0] - DC;
1364 if (diff == 0) {
1365 jpg_write_bits(s, bitBuf, bitCnt, HTDC[0]);
1366 } else {
1367 unsigned short bits[2];
1368 jpg_calc_bits(diff, bits);
1369 jpg_write_bits(s, bitBuf, bitCnt, HTDC[bits[1]]);
1370 jpg_write_bits(s, bitBuf, bitCnt, bits);
1371 }
1372 // Encode ACs
1373 end0pos = 63;
1374 for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) {
1375 }
1376 // end0pos = first element in reverse order !=0
1377 if(end0pos == 0) {
1378 jpg_write_bits(s, bitBuf, bitCnt, EOB);
1379 return DU[0];
1380 }
1381 for(i = 1; i <= end0pos; ++i) {
1382 int startpos = i;
1383 int nrzeroes;
1384 unsigned short bits[2];
1385 for (; DU[i]==0 && i<=end0pos; ++i) {
1386 }
1387 nrzeroes = i-startpos;
1388 if ( nrzeroes >= 16 ) {
1389 int lng = nrzeroes>>4;
1390 int nrmarker;
1391 for (nrmarker=1; nrmarker <= lng; ++nrmarker)
1392 jpg_write_bits(s, bitBuf, bitCnt, M16zeroes);
1393 nrzeroes &= 15;
1394 }
1395 jpg_calc_bits(DU[i], bits);
1396 jpg_write_bits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);
1397 jpg_write_bits(s, bitBuf, bitCnt, bits);
1398 }
1399 if(end0pos != 63) {
1400 jpg_write_bits(s, bitBuf, bitCnt, EOB);
1401 }
1402 return DU[0];
1403}
1404
1405static int write_jpg_core(write_context *s, int width, int height, int comp, const void* data, int quality) {
1406 // Constants that don't pollute global namespace
1407 static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};
1408 static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
1409 static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};
1410 static const unsigned char std_ac_luminance_values[] = {
1411 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
1412 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
1413 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
1414 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
1415 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
1416 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
1417 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
1418 };
1419 static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
1420 static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
1421 static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};
1422 static const unsigned char std_ac_chrominance_values[] = {
1423 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
1424 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
1425 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
1426 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
1427 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
1428 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
1429 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
1430 };
1431 // Huffman tables
1432 static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};
1433 static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};
1434 static const unsigned short YAC_HT[256][2] = {
1435 {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1436 {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1437 {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1438 {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1439 {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1440 {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1441 {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1442 {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1443 {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1444 {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1445 {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1446 {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1447 {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1448 {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1449 {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},
1450 {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
1451 };
1452 static const unsigned short UVAC_HT[256][2] = {
1453 {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1454 {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1455 {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1456 {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1457 {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1458 {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1459 {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1460 {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1461 {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1462 {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1463 {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1464 {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1465 {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1466 {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
1467 {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},
1468 {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
1469 };
1470 static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,
1471 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};
1472 static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,
1473 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};
1474 static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
1475 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
1476
1477 int row, col, i, k, subsample;
1478 float fdtbl_Y[64], fdtbl_UV[64];
1479 unsigned char YTable[64], UVTable[64];
1480
1481 if(!data || !width || !height || comp > 4 || comp < 1) {
1482 return 0;
1483 }
1484
1485 quality = quality ? quality : 90;
1486 subsample = quality <= 90 ? 1 : 0;
1487 quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
1488 quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
1489
1490 for(i = 0; i < 64; ++i) {
1491 int uvti, yti = (YQT[i]*quality+50)/100;
1492 YTable[jpg_zig_zag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
1493 uvti = (UVQT[i]*quality+50)/100;
1494 UVTable[jpg_zig_zag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
1495 }
1496
1497 for(row = 0, k = 0; row < 8; ++row) {
1498 for(col = 0; col < 8; ++col, ++k) {
1499 fdtbl_Y[k] = 1 / (YTable [jpg_zig_zag[k]] * aasf[row] * aasf[col]);
1500 fdtbl_UV[k] = 1 / (UVTable[jpg_zig_zag[k]] * aasf[row] * aasf[col]);
1501 }
1502 }
1503
1504 // Write Headers
1505 {
1506 static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };
1507 static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
1508 const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),NOVASVG_IW_UCHAR(height),(unsigned char)(width>>8),NOVASVG_IW_UCHAR(width),
1509 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
1510 s->func(s->context, (void*)head0, sizeof(head0));
1511 s->func(s->context, (void*)YTable, sizeof(YTable));
1512 write_putc(s, 1);
1513 s->func(s->context, UVTable, sizeof(UVTable));
1514 s->func(s->context, (void*)head1, sizeof(head1));
1515 s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);
1516 s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
1517 write_putc(s, 0x10); // HTYACinfo
1518 s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);
1519 s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
1520 write_putc(s, 1); // HTUDCinfo
1521 s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);
1522 s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
1523 write_putc(s, 0x11); // HTUACinfo
1524 s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);
1525 s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
1526 s->func(s->context, (void*)head2, sizeof(head2));
1527 }
1528
1529 // Encode 8x8 macroblocks
1530 {
1531 static const unsigned short fillBits[] = {0x7F, 7};
1532 int DCY=0, DCU=0, DCV=0;
1533 int bitBuf=0, bitCnt=0;
1534 // comp == 2 is grey+alpha (alpha is ignored)
1535 int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
1536 const unsigned char *dataR = (const unsigned char *)data;
1537 const unsigned char *dataG = dataR + ofsG;
1538 const unsigned char *dataB = dataR + ofsB;
1539 int x, y, pos;
1540 if(subsample) {
1541 for(y = 0; y < height; y += 16) {
1542 for(x = 0; x < width; x += 16) {
1543 float Y[256], U[256], V[256];
1544 for(row = y, pos = 0; row < y+16; ++row) {
1545 // row >= height => use last input row
1546 int clamped_row = (row < height) ? row : height - 1;
1547 int base_p = (g_flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
1548 for(col = x; col < x+16; ++col, ++pos) {
1549 // if col >= width => use pixel from last input column
1550 int p = base_p + ((col < width) ? col : (width-1))*comp;
1551 float r = dataR[p], g = dataG[p], b = dataB[p];
1552 Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
1553 U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
1554 V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
1555 }
1556 }
1557 DCY = jpg_process_du(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1558 DCY = jpg_process_du(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1559 DCY = jpg_process_du(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1560 DCY = jpg_process_du(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1561
1562 // subsample U,V
1563 {
1564 float subU[64], subV[64];
1565 int yy, xx;
1566 for(yy = 0, pos = 0; yy < 8; ++yy) {
1567 for(xx = 0; xx < 8; ++xx, ++pos) {
1568 int j = yy*32+xx*2;
1569 subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
1570 subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
1571 }
1572 }
1573 DCU = jpg_process_du(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
1574 DCV = jpg_process_du(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
1575 }
1576 }
1577 }
1578 } else {
1579 for(y = 0; y < height; y += 8) {
1580 for(x = 0; x < width; x += 8) {
1581 float Y[64], U[64], V[64];
1582 for(row = y, pos = 0; row < y+8; ++row) {
1583 // row >= height => use last input row
1584 int clamped_row = (row < height) ? row : height - 1;
1585 int base_p = (g_flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
1586 for(col = x; col < x+8; ++col, ++pos) {
1587 // if col >= width => use pixel from last input column
1588 int p = base_p + ((col < width) ? col : (width-1))*comp;
1589 float r = dataR[p], g = dataG[p], b = dataB[p];
1590 Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
1591 U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
1592 V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
1593 }
1594 }
1595
1596 DCY = jpg_process_du(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
1597 DCU = jpg_process_du(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
1598 DCV = jpg_process_du(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
1599 }
1600 }
1601 }
1602
1603 // Do the bit alignment of the EOI marker
1604 jpg_write_bits(s, &bitBuf, &bitCnt, fillBits);
1605 }
1606
1607 // EOI
1608 write_putc(s, 0xFF);
1609 write_putc(s, 0xD9);
1610
1611 return 1;
1612}
1613
1614static int write_jpg_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data, int quality)
1615{
1616 write_context s = { 0 };
1617 write_start_callbacks(&s, func, context);
1618 return write_jpg_core(&s, x, y, comp, (void *) data, quality);
1619}
1620
1621
1622#ifndef NOVASVG_IW_NO_STDIO
1623static int write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
1624{
1625 write_context s = { 0 };
1626 if (write_start_file(&s,filename)) {
1627 int r = write_jpg_core(&s, x, y, comp, data, quality);
1628 write_end_file(&s);
1629 return r;
1630 } else
1631 return 0;
1632}
1633#endif
1634
1635};
1636
1637
1638NOVASVG_INLINE Bitmap::Bitmap(int width, int height)
1639 : m_surface(surface_create(width, height))
1640{
1641}
1642
1643NOVASVG_INLINE Bitmap::Bitmap(uint8_t* data, int width, int height, int stride)
1644 : m_surface(surface_create_for_data(data, width, height, stride))
1645{
1646}
1647
1649 : m_surface(surface_reference(bitmap.surface()))
1650{
1651}
1652
1654 : m_surface(bitmap.release())
1655{
1656}
1657
1659{
1660 surface_destroy(m_surface);
1661}
1662
1664{
1665 Bitmap(bitmap).swap(*this);
1666 return *this;
1667}
1668
1670{
1671 std::swap(m_surface, bitmap.m_surface);
1672}
1673
1675{
1676 if(m_surface)
1677 return surface_get_data(m_surface);
1678 return nullptr;
1679}
1680
1682{
1683 if(m_surface)
1684 return surface_get_width(m_surface);
1685 return 0;
1686}
1687
1689{
1690 if(m_surface)
1691 return surface_get_height(m_surface);
1692 return 0;
1693}
1694
1696{
1697 if(m_surface)
1698 return surface_get_stride(m_surface);
1699 return 0;
1700}
1701
1703{
1704 if(m_surface == nullptr)
1705 return;
1706 color_t color;
1707 color_init_rgba32(&color, value.value());
1708 surface_clear(m_surface, &color);
1709}
1710
1712{
1713 if(m_surface == nullptr)
1714 return;
1715 auto data = surface_get_data(m_surface);
1716 auto width = surface_get_width(m_surface);
1717 auto height = surface_get_height(m_surface);
1718 auto stride = surface_get_stride(m_surface);
1720}
1721
1723{
1724 Bitmap(std::move(bitmap)).swap(*this);
1725 return *this;
1726}
1727
1728namespace {
1729
1730// One-pass, unpremultiplied RGBA copy of a Bitmap's pixels -- lives only
1731// long enough to hand to BitmapCodec. This is an implementation detail of
1732// Bitmap's writeTo*() methods below, not a type of its own: no
1733// declaration in the class, nothing exported.
1734//
1735// Bitmap itself stays ARGB32-Premultiplied always -- it doubles as a
1736// drawable surface (Canvas can render onto an existing Bitmap) and as a
1737// texture source (canvas_set_texture reads bitmap.surface() directly for
1738// <image>/pattern fills), both of which need premultiplied alpha for
1739// `over`-compositing to be correct. Only encoding needs straight alpha,
1740// so only encoding pays for the one unavoidable unpremultiply pass, via
1741// this scratch buffer -- never a permanent, library-wide format change.
1742class BitmapRgbaScratch {
1743public:
1744 explicit BitmapRgbaScratch(const Bitmap& bitmap)
1745 : m_width(bitmap.width())
1746 , m_height(bitmap.height())
1747 , m_stride(m_width * 4)
1748 , m_data(bitmap.isNull() ? nullptr : static_cast<uint8_t*>(malloc(size_t(m_stride) * size_t(m_height))))
1749 {
1750 if(m_data != nullptr)
1751 convert_argb_to_rgba(m_data, bitmap.data(), m_width, m_height, bitmap.stride());
1752 }
1753
1754 ~BitmapRgbaScratch() { free(m_data); }
1755
1756 BitmapRgbaScratch(const BitmapRgbaScratch&) = delete;
1757 BitmapRgbaScratch& operator=(const BitmapRgbaScratch&) = delete;
1758
1759 explicit operator bool() const { return m_data != nullptr; }
1760 uint8_t* data() const { return m_data; }
1761 int width() const { return m_width; }
1762 int height() const { return m_height; }
1763 int stride() const { return m_stride; }
1764
1765private:
1766 int m_width;
1767 int m_height;
1768 int m_stride;
1769 uint8_t* m_data;
1770};
1771
1772} // namespace
1773
1774NOVASVG_INLINE bool Bitmap::writeToPng(const std::string& filename) const
1775{
1776 BitmapRgbaScratch rgba(*this);
1777 if(!rgba)
1778 return false;
1779 return BitmapCodec::write_png(filename.data(), rgba.width(), rgba.height(), 4, rgba.data(), rgba.stride()) != 0;
1780}
1781
1782NOVASVG_INLINE bool Bitmap::writeToPng(novasvg_write_func_t callback, void* closure) const
1783{
1784 BitmapRgbaScratch rgba(*this);
1785 if(!rgba)
1786 return false;
1787 return BitmapCodec::write_png_to_func(callback, closure, rgba.width(), rgba.height(), 4, rgba.data(), rgba.stride()) != 0;
1788}
1789
1790NOVASVG_INLINE bool Bitmap::writeToBmp(const std::string& filename) const
1791{
1792 BitmapRgbaScratch rgba(*this);
1793 if(!rgba)
1794 return false;
1795 return BitmapCodec::write_bmp(filename.data(), rgba.width(), rgba.height(), 4, rgba.data()) != 0;
1796}
1797
1798NOVASVG_INLINE bool Bitmap::writeToBmp(novasvg_write_func_t callback, void* closure) const
1799{
1800 BitmapRgbaScratch rgba(*this);
1801 if(!rgba)
1802 return false;
1803 return BitmapCodec::write_bmp_to_func(callback, closure, rgba.width(), rgba.height(), 4, rgba.data()) != 0;
1804}
1805
1806NOVASVG_INLINE bool Bitmap::writeToTga(const std::string& filename) const
1807{
1808 BitmapRgbaScratch rgba(*this);
1809 if(!rgba)
1810 return false;
1811 return BitmapCodec::write_tga(filename.data(), rgba.width(), rgba.height(), 4, rgba.data()) != 0;
1812}
1813
1814NOVASVG_INLINE bool Bitmap::writeToTga(novasvg_write_func_t callback, void* closure) const
1815{
1816 BitmapRgbaScratch rgba(*this);
1817 if(!rgba)
1818 return false;
1819 return BitmapCodec::write_tga_to_func(callback, closure, rgba.width(), rgba.height(), 4, rgba.data()) != 0;
1820}
1821
1822NOVASVG_INLINE bool Bitmap::writeToJpg(const std::string& filename, int quality) const
1823{
1824 BitmapRgbaScratch rgba(*this);
1825 if(!rgba)
1826 return false;
1827 return BitmapCodec::write_jpg(filename.data(), rgba.width(), rgba.height(), 4, rgba.data(), quality) != 0;
1828}
1829
1830NOVASVG_INLINE bool Bitmap::writeToJpg(novasvg_write_func_t callback, void* closure, int quality) const
1831{
1832 BitmapRgbaScratch rgba(*this);
1833 if(!rgba)
1834 return false;
1835 return BitmapCodec::write_jpg_to_func(callback, closure, rgba.width(), rgba.height(), 4, rgba.data(), quality) != 0;
1836}
1837
1838NOVASVG_INLINE bool Bitmap::write(const std::string& filename, int jpgQuality) const
1839{
1840 // write() *is* the auto-detecting entry point -- it just delegates to
1841 // whichever writeTo*() the extension picks, rather than duplicating
1842 // their bodies.
1843 auto dot = filename.find_last_of('.');
1844 std::string ext = dot == std::string::npos ? std::string() : filename.substr(dot + 1);
1845 for(auto& c : ext)
1846 c = char(std::tolower(static_cast<unsigned char>(c)));
1847
1848 if(ext == "bmp")
1849 return writeToBmp(filename);
1850 if(ext == "tga")
1851 return writeToTga(filename);
1852 if(ext == "jpg" || ext == "jpeg")
1853 return writeToJpg(filename, jpgQuality);
1854 return writeToPng(filename);
1855}
1856
1857NOVASVG_INLINE surface_t* Bitmap::release()
1858{
1859 return std::exchange(m_surface, nullptr);
1860}
1861
1862
1863} // namespace novasvg
#define NOVASVG_IW_MEMMOVE(a, b, sz)
Definition bitmap.h:253
#define NOVASVG_IW_UCHAR(x)
Definition bitmap.h:261
#define NOVASVG_IW_FREE(p)
Definition bitmap.h:244
#define sb_raw(a)
Definition bitmap.h:821
#define zlib_add(code, codebits)
Definition bitmap.h:887
#define NOVASVG_IW_REALLOC_SIZED(p, oldsz, newsz)
Definition bitmap.h:248
#define NOVASVG_IW_ASSERT(x)
Definition bitmap.h:258
#define sb_count(a)
Definition bitmap.h:830
#define NOVASVG_IW_MALLOC(sz)
Definition bitmap.h:242
#define sb_push(a, v)
Definition bitmap.h:829
#define sb_n(a)
Definition bitmap.h:823
#define zlib_write_u32(data, v)
Definition bitmap.h:1081
#define sb_m(a)
Definition bitmap.h:822
#define png_write_tag(data, s)
Definition bitmap.h:1082
#define ZLIB_HASH
Definition bitmap.h:898
#define zlib_huffb(n)
Definition bitmap.h:896
#define iw_max(a, b)
Definition bitmap.h:642
#define sb_free(a)
Definition bitmap.h:831
#define zlib_huff(n)
Definition bitmap.h:895
static void write_end_file(write_context *s)
Definition bitmap.h:346
static void write_stdio_callback(void *context, void *data, int size)
Definition bitmap.h:291
static void write_pixels(write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
Definition bitmap.h:458
static int write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
Definition bitmap.h:1222
static unsigned int png_crc32(unsigned char *buffer, int len)
Definition bitmap.h:1031
static void write_formatted_v(write_context *s, const char *fmt, va_list v)
Definition bitmap.h:356
static int write_bmp_core(write_context *s, int x, int y, int comp, const void *data)
Definition bitmap.h:499
static int write_hdr_to_func(write_func_t func, void *context, int x, int y, int comp, const float *data)
Definition bitmap.h:794
static void * sb_growf(void **arr, int increment, int itemsize)
Definition bitmap.h:833
static void hdr_write_dump_data(write_context *s, int length, unsigned char *data)
Definition bitmap.h:671
static int write_start_file(write_context *s, const char *filename)
Definition bitmap.h:339
static void write_u8x3(write_context *s, unsigned char a, unsigned char b, unsigned char c)
Definition bitmap.h:413
int iw_uint32_size_check[sizeof(iw_uint32)==4 ? 1 :-1]
Definition bitmap.h:354
static void hdr_write_run_data(write_context *s, int length, unsigned char databyte)
Definition bitmap.h:663
static void hdr_write_scanline(write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
Definition bitmap.h:679
static void write_start_callbacks(write_context *s, write_func_t c, void *context)
Definition bitmap.h:283
static unsigned int zlib_countm(unsigned char *a, unsigned char *b, int limit)
Definition bitmap.h:866
static int g_png_force_filter
Definition bitmap.h:265
static void set_flip_vertically_on_write(int flag)
Definition bitmap.h:269
static int write_png_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
Definition bitmap.h:1238
static int write_jpg_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data, int quality)
Definition bitmap.h:1614
static void write_flush(write_context *s)
Definition bitmap.h:393
static FILE * open_file(char const *filename, char const *mode)
Definition bitmap.h:311
static const unsigned char jpg_zig_zag[]
Definition bitmap.h:1257
static int write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
Definition bitmap.h:1623
static unsigned char * zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
Definition bitmap.h:846
static void hdr_linear_to_rgbe(unsigned char *rgbe, float *linear)
Definition bitmap.h:646
static void write_pixel(write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
Definition bitmap.h:425
static unsigned char * write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
Definition bitmap.h:1135
static int write_hdr_core(write_context *s, int x, int y, int comp, float *data)
Definition bitmap.h:768
static int write_hdr(char const *filename, int x, int y, int comp, const float *data)
Definition bitmap.h:801
static void write_u8(write_context *s, unsigned char a)
Definition bitmap.h:406
static int zlib_bitrev(int code, int codebits)
Definition bitmap.h:856
static void png_write_crc(unsigned char **data, int len)
Definition bitmap.h:1084
static void jpg_write_bits(write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs)
Definition bitmap.h:1260
static void jpg_dct(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p)
Definition bitmap.h:1277
static void png_encode_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)
Definition bitmap.h:1099
static int write_jpg_core(write_context *s, int width, int height, int comp, const void *data, int quality)
Definition bitmap.h:1405
static int outfile(write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt,...)
Definition bitmap.h:485
static void jpg_calc_bits(int val, unsigned short bits[2])
Definition bitmap.h:1325
static int write_tga_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data)
Definition bitmap.h:618
static int g_flip_vertically_on_write
Definition bitmap.h:267
static int g_tga_with_rle
Definition bitmap.h:264
static int jpg_process_du(write_context *s, int *bitBuf, int *bitCnt, float *CDU, int du_stride, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2])
Definition bitmap.h:1335
static int write_tga_core(write_context *s, int x, int y, int comp, void *data)
NOVASVG_IW_NO_STDIO.
Definition bitmap.h:539
unsigned int iw_uint32
Definition bitmap.h:353
static int write_bmp(char const *filename, int x, int y, int comp, const void *data)
Definition bitmap.h:527
static void write_formatted(write_context *s, const char *fmt,...)
Definition bitmap.h:385
static unsigned char * zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
Definition bitmap.h:902
static void write_putc(write_context *s, unsigned char c)
Definition bitmap.h:401
static int write_bmp_to_func(write_func_t func, void *context, int x, int y, int comp, const void *data)
Definition bitmap.h:519
static int write_tga(char const *filename, int x, int y, int comp, const void *data)
Definition bitmap.h:626
static int g_png_compression_level
Definition bitmap.h:263
static unsigned char png_paeth_predictor(int a, int b, int c)
Definition bitmap.h:1090
static unsigned int g_zlib_hash_table(unsigned char *data)
Definition bitmap.h:874
bool isNull() const
Checks if the bitmap is null.
Definition bitmap.h:132
void swap(Bitmap &bitmap)
Swaps the content of this bitmap with another.
Definition bitmap.h:1669
bool writeToJpg(const std::string &filename, int quality=80) const
Writes the bitmap to a JPEG file.
Definition bitmap.h:1822
int height() const
Gets the height of the bitmap.
Definition bitmap.h:1688
~Bitmap()
Cleans up any resources associated with the bitmap.
Definition bitmap.h:1658
Bitmap & operator=(const Bitmap &bitmap)
Copy assignment operator.
Definition bitmap.h:1663
bool writeToTga(const std::string &filename) const
Writes the bitmap to a TGA file.
Definition bitmap.h:1806
bool writeToBmp(const std::string &filename) const
Writes the bitmap to a BMP file.
Definition bitmap.h:1790
bool write(const std::string &filename, int jpgQuality=80) const
Writes the bitmap to a file, picking PNG/BMP/TGA/JPEG from filename's extension (....
Definition bitmap.h:1838
int stride() const
Gets the stride of the bitmap.
Definition bitmap.h:1695
bool writeToPng(const std::string &filename) const
Writes the bitmap to a PNG file.
Definition bitmap.h:1774
int width() const
Gets the width of the bitmap.
Definition bitmap.h:1681
uint8_t * data() const
Gets the pointer to the raw pixel data.
Definition bitmap.h:1674
Bitmap()=default
Constructs a null bitmap.
void clear(const Color &value)
Clears the bitmap with the specified color.
Definition bitmap.h:1702
bool valid() const
Checks if the bitmap is valid.
Definition bitmap.h:139
void convertToRGBA()
Converts the bitmap pixel data from ARGB32 Premultiplied to RGBA Plain format in place.
Definition bitmap.h:1711
Bitmap(surface_t *surface)
Definition bitmap.h:66
surface_t * surface() const
Definition bitmap.h:202
Represents a color in RGBA format (0xRRGGBBAA).
Definition color.h:85
uint32_t value() const
Convert the color to a 32-bit unsigned integer.
Definition color.h:308
NOVASVG_INLINE void color_init_rgba32(color_t *color, unsigned int value)
Initializes a color from a 32-bit unsigned RGBA value.
Definition paint.h:34
surface_t * surface_create_for_data(unsigned char *data, int width, int height, int stride)
Creates an image surface using existing pixel data.
Definition surface.h:7761
void(* write_func_t)(void *closure, void *data, int size)
A function pointer type for a write callback.
Definition render.h:100
int surface_get_width(const surface_t *surface)
Gets the width of the surface.
Definition surface.h:7943
int surface_get_height(const surface_t *surface)
Gets the height of the surface.
Definition surface.h:7948
int surface_get_stride(const surface_t *surface)
Gets the stride of the surface.
Definition surface.h:7953
surface_t * surface_create(int width, int height)
Creates a new image surface with the specified dimensions.
Definition surface.h:7753
void surface_destroy(surface_t *surface)
Decrements the reference count and destroys the surface if the count reaches zero.
Definition surface.h:7926
void convert_argb_to_rgba(unsigned char *dst, const unsigned char *src, int width, int height, int stride)
Converts pixel data from premultiplied ARGB to RGBA format.
Definition surface.h:7967
surface_t * surface_reference(surface_t *surface)
Increments the reference count for a surface.
Definition surface.h:7920
void surface_clear(surface_t *surface, const color_t *color)
Clears the entire surface with the specified color.
Definition surface.h:7958
unsigned char * surface_get_data(const surface_t *surface)
Gets the pixel data of the surface.
Definition surface.h:7938
Main namespace for the NovaSVG library.
Definition bitmap.h:23
void(* novasvg_write_func_t)(void *closure, void *data, int size)
A function pointer type for a write callback.
Definition novasvg.h:129
#define NOVASVG_INLINE
Definition novasvg.h:42
#define NOVASVG_API
Definition render.h:62
Represents a color with red, green, blue, and alpha components.
Definition render.h:1019