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

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