source: trunk/src/image/png_writer.cc @ 534

Last change on this file since 534 was 534, checked in by epyon, 8 years ago

CONTINUED:

  • getting rid of size_t
  • datatypes now restricted to uint32 size
  • 64-bit compatibility
  • copyright updates where modified
File size: 12.6 KB
Line 
1// Copyright (C) 2015-2016 ChaosForge Ltd
2// http://chaosforge.org/
3//
4// This file is part of Nova libraries.
5// For conditions of distribution and use, see copying.txt file in root folder.
6
7#include "nv/image/png_writer.hh"
8
9#include "nv/image/miniz.hh"
10
11// TODO: remove
12#include <stdarg.h> 
13
14using namespace nv;
15
16#if NV_COMPILER == NV_CLANG
17#pragma clang diagnostic ignored "-Wunused-macros"
18#pragma clang diagnostic ignored "-Wold-style-cast"
19#pragma clang diagnostic ignored "-Wsign-conversion"
20#pragma clang diagnostic ignored "-Wreserved-id-macro"
21#pragma clang diagnostic ignored "-Wmissing-prototypes"
22#define NULL 0
23#endif
24
25enum
26{
27        STBI_default = 0, // only used for req_comp
28
29        STBI_grey = 1,
30        STBI_grey_alpha = 2,
31        STBI_rgb = 3,
32        STBI_rgb_alpha = 4
33};
34
35#define STBI_MALLOC(sz)     nvmalloc(sz)
36#define STBI_REALLOC(p,sz)  nvrealloc(p,sz)
37#define STBI_FREE(p)        nvfree(p)
38#define STBI_MEMMOVE(d,s,c) nvmemmove(d,s,c)
39
40typedef void stbi_write_func( void *context, void *data, int size );
41
42typedef struct
43{
44        stbi_write_func *func;
45        void *context;
46} stbi__write_context;
47
48// initialize a callback-based context
49// static void stbi__start_write_callbacks( stbi__write_context *s, stbi_write_func *c, void *context )
50// {
51//      s->func = c;
52//      s->context = context;
53// }
54
55typedef unsigned int stbiw_uint32;
56typedef int stb_image_write_test[sizeof( stbiw_uint32 ) == 4 ? 1 : -1];
57
58template < typename T >
59inline uchar8 byte_cast( T x )
60{
61        return uchar8( ( x ) & 255 );
62}
63
64#define stbi__err(x,y)  0
65#define stbi__errpuc(x,y)  ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
66
67/*
68static void stbiw__writefv( stbi__write_context *s, const char *fmt, va_list v )
69{
70        while ( *fmt )
71        {
72                switch ( *fmt++ )
73                {
74                case ' ': break;
75                case '1': { unsigned char x = byte_cast( va_arg( v, int ) );
76                        s->func( s->context, &x, 1 );
77                        break; }
78                case '2': { int x = va_arg( v, int );
79                        unsigned char b[2];
80                        b[0] = byte_cast( x );
81                        b[1] = byte_cast( x >> 8 );
82                        s->func( s->context, b, 2 );
83                        break; }
84                case '4': { stbiw_uint32 x = va_arg( v, int );
85                        unsigned char b[4];
86                        b[0] = byte_cast( x );
87                        b[1] = byte_cast( x >> 8 );
88                        b[2] = byte_cast( x >> 16 );
89                        b[3] = byte_cast( x >> 24 );
90                        s->func( s->context, b, 4 );
91                        break; }
92                default:
93                        byte_cast( 0 );
94                        return;
95                }
96        }
97}
98
99static void stbiw__writef( stbi__write_context *s, const char *fmt, ... )
100{
101        va_list v;
102        va_start( v, fmt );
103        stbiw__writefv( s, fmt, v );
104        va_end( v );
105}
106
107static void stbiw__write3( stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c )
108{
109        unsigned char arr[3];
110        arr[0] = a, arr[1] = b, arr[2] = c;
111        s->func( s->context, arr, 3 );
112}
113
114static void stbiw__write_pixel( stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d )
115{
116        unsigned char bg[3] = { 255, 0, 255 }, px[3];
117        int k;
118
119        if ( write_alpha < 0 )
120                s->func( s->context, &d[comp - 1], 1 );
121
122        switch ( comp )
123        {
124        case 1:
125                s->func( s->context, d, 1 );
126                break;
127        case 2:
128                if ( expand_mono )
129                        stbiw__write3( s, d[0], d[0], d[0] ); // monochrome bmp
130                else
131                        s->func( s->context, d, 1 );  // monochrome TGA
132                break;
133        case 4:
134                if ( !write_alpha )
135                {
136                        // composite against pink background
137                        for ( k = 0; k < 3; ++k )
138                                px[k] = bg[k] + ( ( d[k] - bg[k] ) * d[3] ) / 255;
139                        stbiw__write3( s, px[1 - rgb_dir], px[1], px[1 + rgb_dir] );
140                        break;
141                }
142                // FALLTHROUGH
143        case 3:
144                stbiw__write3( s, d[1 - rgb_dir], d[1], d[1 + rgb_dir] );
145                break;
146        }
147        if ( write_alpha > 0 )
148                s->func( s->context, &d[comp - 1], 1 );
149}
150
151static void stbiw__write_pixels( stbi__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 )
152{
153        stbiw_uint32 zero = 0;
154        int i, j, j_end;
155
156        if ( y <= 0 )
157                return;
158
159        if ( vdir < 0 )
160                j_end = -1, j = y - 1;
161        else
162                j_end = y, j = 0;
163
164        for ( ; j != j_end; j += vdir )
165        {
166                for ( i = 0; i < x; ++i )
167                {
168                        unsigned char *d = (unsigned char *)data + ( j*x + i )*comp;
169                        stbiw__write_pixel( s, rgb_dir, comp, write_alpha, expand_mono, d );
170                }
171                s->func( s->context, &zero, scanline_pad );
172        }
173}
174
175static int stbiw__outfile( stbi__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, ... )
176{
177        if ( y < 0 || x < 0 )
178        {
179                return 0;
180        }
181        else
182        {
183                va_list v;
184                va_start( v, fmt );
185                stbiw__writefv( s, fmt, v );
186                va_end( v );
187                stbiw__write_pixels( s, rgb_dir, vdir, x, y, comp, data, alpha, pad, expand_mono );
188                return 1;
189        }
190}
191*/
192
193static unsigned int stbiw__crc32( unsigned char *buffer, int len )
194{
195        static unsigned int crc_table[256] =
196        {
197                0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
198                0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
199                0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
200                0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
201                0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
202                0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
203                0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
204                0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
205                0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
206                0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
207                0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
208                0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
209                0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
210                0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
211                0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
212                0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
213                0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
214                0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
215                0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
216                0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
217                0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
218                0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
219                0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
220                0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
221                0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
222                0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
223                0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
224                0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
225                0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
226                0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
227                0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
228                0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
229        };
230
231        unsigned int crc = ~0u;
232        int i;
233        for ( i = 0; i < len; ++i )
234                crc = ( crc >> 8 ) ^ crc_table[buffer[i] ^ ( crc & 0xff )];
235        return ~crc;
236}
237
238#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=byte_cast(a),(o)[1]=byte_cast(b),(o)[2]=byte_cast(c),(o)[3]=byte_cast(d),(o)+=4)
239#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
240#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])
241
242static void stbiw__wpcrc( unsigned char **data, int len )
243{
244        unsigned int crc = stbiw__crc32( *data - len - 4, len + 4 );
245        stbiw__wp32( *data, crc );
246}
247
248static unsigned char stbiw__paeth( int a, int b, int c )
249{
250        int p = a + b - c, pa = abs( p - a ), pb = abs( p - b ), pc = abs( p - c );
251        if ( pa <= pb && pa <= pc ) return byte_cast( a );
252        if ( pb <= pc ) return byte_cast( b );
253        return byte_cast( c );
254}
255
256unsigned char * stbi_zlib_compress( unsigned char *data, int data_len, int *out_len, int /*quality*/ )
257{
258        unsigned long expected = nv::miniz_bound( data_len );
259        unsigned char* result = (unsigned char *)STBI_MALLOC( expected );
260        unsigned long rlen = expected;
261        nv::miniz_compress( result, &rlen, data, data_len, 6 );
262        *out_len = rlen;
263        return result;
264}
265
266unsigned char *stbi_write_png_to_mem( const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len )
267{
268        int ctype[5] = { -1, 0, 4, 2, 6 };
269        unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
270        unsigned char *out, *o, *filt, *zlib;
271        signed char *line_buffer;
272        int i, j, k, p, zlen;
273
274        if ( stride_bytes == 0 )
275                stride_bytes = x * n;
276
277        filt = (unsigned char *)STBI_MALLOC( ( x*n + 1 ) * y ); if ( !filt ) return 0;
278        line_buffer = (signed char *)STBI_MALLOC( x * n ); if ( !line_buffer ) { STBI_FREE( filt ); return 0; }
279
280        bool flip_y = true;
281
282        for ( j = 0; j < y; ++j )
283        {
284                int line = flip_y ? y - j - 1 : j;
285                int step = flip_y ? -stride_bytes : stride_bytes;
286                static int mapping[] = { 0,1,2,3,4 };
287                static int firstmap[] = { 0,1,0,5,6 };
288                int *mymap = j ? mapping : firstmap;
289                int best = 0, bestval = 0x7fffffff;
290                for ( p = 0; p < 2; ++p )
291                {
292                        for ( k = p ? best : 0; k < 5; ++k )
293                        {
294                                int type = mymap[k], est = 0;
295                                const unsigned char *z = pixels + stride_bytes*line;
296                                for ( i = 0; i < n; ++i )
297                                        switch ( type )
298                                        {
299                                        case 0: line_buffer[i] = z[i]; break;
300                                        case 1: line_buffer[i] = z[i]; break;
301                                        case 2: line_buffer[i] = z[i] - z[i - step]; break;
302                                        case 3: line_buffer[i] = z[i] - ( z[i - step] >> 1 ); break;
303                                        case 4: line_buffer[i] = (signed char)( z[i] - stbiw__paeth( 0, z[i - step], 0 ) ); break;
304                                        case 5: line_buffer[i] = z[i]; break;
305                                        case 6: line_buffer[i] = z[i]; break;
306                                        }
307                                for ( i = n; i < x*n; ++i )
308                                {
309                                        switch ( type )
310                                        {
311                                        case 0: line_buffer[i] = z[i]; break;
312                                        case 1: line_buffer[i] = z[i] - z[i - n]; break;
313                                        case 2: line_buffer[i] = z[i] - z[i - step]; break;
314                                        case 3: line_buffer[i] = z[i] - ( ( z[i - n] + z[i - step] ) >> 1 ); break;
315                                        case 4: line_buffer[i] = z[i] - stbiw__paeth( z[i - n], z[i - step], z[i - step - n] ); break;
316                                        case 5: line_buffer[i] = z[i] - ( z[i - n] >> 1 ); break;
317                                        case 6: line_buffer[i] = z[i] - stbiw__paeth( z[i - n], 0, 0 ); break;
318                                        }
319                                }
320                                if ( p ) break;
321                                for ( i = 0; i < x*n; ++i )
322                                        est += abs( (signed char)line_buffer[i] );
323                                if ( est < bestval ) { bestval = est; best = k; }
324                        }
325                }
326                // when we get here, best contains the filter type, and line_buffer contains the data
327                //line = j;
328                filt[j*( x*n + 1 )] = (unsigned char)best;
329                STBI_MEMMOVE( filt + j*( x*n + 1 ) + 1, line_buffer, x*n );
330        }
331        STBI_FREE( line_buffer );
332        zlib = stbi_zlib_compress( filt, y*( x*n + 1 ), &zlen, 8 ); // increase 8 to get smaller but use more memory
333        STBI_FREE( filt );
334        if ( !zlib ) return 0;
335
336        // each tag requires 12 bytes of overhead
337        out = (unsigned char *)STBI_MALLOC( 8 + 12 + 13 + 12 + zlen + 12 );
338        if ( !out ) return 0;
339        *out_len = 8 + 12 + 13 + 12 + zlen + 12;
340
341        o = out;
342        STBI_MEMMOVE( o, sig, 8 ); o += 8;
343        stbiw__wp32( o, 13 ); // header length
344        stbiw__wptag( o, "IHDR" );
345        stbiw__wp32( o, x );
346        stbiw__wp32( o, y );
347        *o++ = 8;
348        *o++ = byte_cast( ctype[n] );
349        *o++ = 0;
350        *o++ = 0;
351        *o++ = 0;
352        stbiw__wpcrc( &o, 13 );
353
354        stbiw__wp32( o, zlen );
355        stbiw__wptag( o, "IDAT" );
356        STBI_MEMMOVE( o, zlib, zlen );
357        o += zlen;
358        STBI_FREE( zlib );
359        stbiw__wpcrc( &o, zlen );
360
361        stbiw__wp32( o, 0 );
362        stbiw__wptag( o, "IEND" );
363        stbiw__wpcrc( &o, 0 );
364
365        NV_ASSERT( o == out + *out_len, "wrong output" );
366
367        return out;
368}
369
370bool nv::png_writer::save( stream& s, image_data* data )
371{
372        ivec2 sz = data->get_size();
373        int len = 0;
374        unsigned char* out = stbi_write_png_to_mem( data->get_data(), 0, sz.x, sz.y, (int)data->get_depth(), &len );
375        if ( !out ) return false;
376        s.write( out, 1, len );
377        STBI_FREE( out );
378        return true;
379}
Note: See TracBrowser for help on using the repository browser.