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

Last change on this file since 551 was 551, checked in by epyon, 8 years ago
  • clang compatibility
  • ecs upgrades
File size: 12.7 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
14//using 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)     nv::nvmalloc(sz)
36#define STBI_REALLOC(p,sz)  nv::nvrealloc(p,sz)
37#define STBI_FREE(p)        nv::nvfree(p)
38#define STBI_MEMMOVE(d,s,c) nv::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 nv::uchar8 byte_cast( T x )
60{
61        return nv::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 int iabs( int i )
249{
250        return i >= 0 ? i : -i;
251}
252
253static unsigned char stbiw__paeth( int a, int b, int c )
254{
255        int p = a + b - c, pa = iabs( p - a ), pb = iabs( p - b ), pc = iabs( p - c );
256        if ( pa <= pb && pa <= pc ) return byte_cast( a );
257        if ( pb <= pc ) return byte_cast( b );
258        return byte_cast( c );
259}
260
261unsigned char * stbi_zlib_compress( unsigned char *data, int data_len, int *out_len, int /*quality*/ )
262{
263        unsigned long expected = nv::miniz_bound( data_len );
264        unsigned char* result = (unsigned char *)STBI_MALLOC( expected );
265        unsigned long rlen = expected;
266        nv::miniz_compress( result, &rlen, data, data_len, 6 );
267        *out_len = rlen;
268        return result;
269}
270
271unsigned char *stbi_write_png_to_mem( const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len )
272{
273        int ctype[5] = { -1, 0, 4, 2, 6 };
274        unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
275        unsigned char *out, *o, *filt, *zlib;
276        signed char *line_buffer;
277        int i, j, k, p, zlen;
278
279        if ( stride_bytes == 0 )
280                stride_bytes = x * n;
281
282        filt = (unsigned char *)STBI_MALLOC( ( x*n + 1 ) * y ); if ( !filt ) return 0;
283        line_buffer = (signed char *)STBI_MALLOC( x * n ); if ( !line_buffer ) { STBI_FREE( filt ); return 0; }
284
285        bool flip_y = true;
286
287        for ( j = 0; j < y; ++j )
288        {
289                int line = flip_y ? y - j - 1 : j;
290                int step = flip_y ? -stride_bytes : stride_bytes;
291                static int mapping[] = { 0,1,2,3,4 };
292                static int firstmap[] = { 0,1,0,5,6 };
293                int *mymap = j ? mapping : firstmap;
294                int best = 0, bestval = 0x7fffffff;
295                for ( p = 0; p < 2; ++p )
296                {
297                        for ( k = p ? best : 0; k < 5; ++k )
298                        {
299                                int type = mymap[k], est = 0;
300                                const unsigned char *z = pixels + stride_bytes*line;
301                                for ( i = 0; i < n; ++i )
302                                        switch ( type )
303                                        {
304                                        case 0: line_buffer[i] = z[i]; break;
305                                        case 1: line_buffer[i] = z[i]; break;
306                                        case 2: line_buffer[i] = z[i] - z[i - step]; break;
307                                        case 3: line_buffer[i] = z[i] - ( z[i - step] >> 1 ); break;
308                                        case 4: line_buffer[i] = (signed char)( z[i] - stbiw__paeth( 0, z[i - step], 0 ) ); break;
309                                        case 5: line_buffer[i] = z[i]; break;
310                                        case 6: line_buffer[i] = z[i]; break;
311                                        }
312                                for ( i = n; i < x*n; ++i )
313                                {
314                                        switch ( type )
315                                        {
316                                        case 0: line_buffer[i] = z[i]; break;
317                                        case 1: line_buffer[i] = z[i] - z[i - n]; break;
318                                        case 2: line_buffer[i] = z[i] - z[i - step]; break;
319                                        case 3: line_buffer[i] = z[i] - ( ( z[i - n] + z[i - step] ) >> 1 ); break;
320                                        case 4: line_buffer[i] = z[i] - stbiw__paeth( z[i - n], z[i - step], z[i - step - n] ); break;
321                                        case 5: line_buffer[i] = z[i] - ( z[i - n] >> 1 ); break;
322                                        case 6: line_buffer[i] = z[i] - stbiw__paeth( z[i - n], 0, 0 ); break;
323                                        }
324                                }
325                                if ( p ) break;
326                                for ( i = 0; i < x*n; ++i )
327                                        est += iabs( (signed char)line_buffer[i] );
328                                if ( est < bestval ) { bestval = est; best = k; }
329                        }
330                }
331                // when we get here, best contains the filter type, and line_buffer contains the data
332                //line = j;
333                filt[j*( x*n + 1 )] = (unsigned char)best;
334                STBI_MEMMOVE( filt + j*( x*n + 1 ) + 1, line_buffer, x*n );
335        }
336        STBI_FREE( line_buffer );
337        zlib = stbi_zlib_compress( filt, y*( x*n + 1 ), &zlen, 8 ); // increase 8 to get smaller but use more memory
338        STBI_FREE( filt );
339        if ( !zlib ) return 0;
340
341        // each tag requires 12 bytes of overhead
342        out = (unsigned char *)STBI_MALLOC( 8 + 12 + 13 + 12 + zlen + 12 );
343        if ( !out ) return 0;
344        *out_len = 8 + 12 + 13 + 12 + zlen + 12;
345
346        o = out;
347        STBI_MEMMOVE( o, sig, 8 ); o += 8;
348        stbiw__wp32( o, 13 ); // header length
349        stbiw__wptag( o, "IHDR" );
350        stbiw__wp32( o, x );
351        stbiw__wp32( o, y );
352        *o++ = 8;
353        *o++ = byte_cast( ctype[n] );
354        *o++ = 0;
355        *o++ = 0;
356        *o++ = 0;
357        stbiw__wpcrc( &o, 13 );
358
359        stbiw__wp32( o, zlen );
360        stbiw__wptag( o, "IDAT" );
361        STBI_MEMMOVE( o, zlib, zlen );
362        o += zlen;
363        STBI_FREE( zlib );
364        stbiw__wpcrc( &o, zlen );
365
366        stbiw__wp32( o, 0 );
367        stbiw__wptag( o, "IEND" );
368        stbiw__wpcrc( &o, 0 );
369
370        NV_ASSERT( o == out + *out_len, "wrong output" );
371
372        return out;
373}
374
375bool nv::png_writer::save( stream& s, image_data* data )
376{
377        ivec2 sz = data->get_size();
378        int len = 0;
379        unsigned char* out = stbi_write_png_to_mem( data->get_data(), 0, sz.x, sz.y, (int)data->get_depth(), &len );
380        if ( !out ) return false;
381        s.write( out, 1, len );
382        STBI_FREE( out );
383        return true;
384}
Note: See TracBrowser for help on using the repository browser.