ecere: Initial Emscripten support
[sdk] / ecere / src / gfx / bitmaps / PNGFormat.ec
1 namespace gfx::bitmaps;
2
3 #define _Noreturn
4
5 import "Display"
6
7 #define uint _uint
8 #include "png.h"
9 #undef uint
10
11 #ifdef __BIG_ENDIAN__
12 static void swap16pair(uint16 n[2]) {
13    char *p = (char*)n;
14    char tmp;
15    tmp = p[0];
16    p[0] = p[1];
17    p[1] = tmp;
18    p += 2;
19    tmp = p[0];
20    p[0] = p[1];
21    p[1] = tmp;
22 }
23 //this function only swaps on big endian
24 static void swap32s(uint32 n[], uint count) {
25    unsigned char *i = (unsigned char*)n;
26    uint *o = n;
27    uint32 tmp;
28    while (count--) {
29       unsigned char *p = (i+=4);
30       tmp = 0;
31       tmp |= (*--p) & 0xFF;
32       tmp <<= 8;
33       tmp |= (*--p) & 0xFF;
34       tmp <<= 8;
35       tmp |= (*--p) & 0xFF;
36       tmp <<= 8;
37       tmp |= (*--p) & 0xFF;
38       *o++ = tmp;
39    }
40 }
41 #endif
42
43 static void ReadData(png_structp png, png_bytep bytes, png_size_t size)
44 {
45    File f = png_get_io_ptr(png);
46    f.Read(bytes, 1, (uint)size);
47 }
48
49 static void WriteData(png_structp png, png_bytep bytes, png_size_t size)
50 {
51    File f = png_get_io_ptr(png);
52    f.Write(bytes, 1, (uint)size);
53 }
54
55 static const char * extensions[] = { "png", null };
56
57 class PNGFormat : BitmapFormat
58 {
59    class_property(extensions) = extensions;
60
61    bool Load(Bitmap bitmap, File f)
62    {
63       bool result = false;
64
65       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, null, null, null);
66
67       if(png_ptr)
68       {
69          png_infop info_ptr = png_create_info_struct(png_ptr);
70          if (info_ptr)
71          {
72             if(!setjmp(png_jmpbuf(png_ptr)))
73             {
74                png_uint_32 width, height;
75                int bit_depth, color_type, interlace_type;
76                unsigned int sig_read = 0;
77                int numPasses;
78                int channels;
79
80                png_set_read_fn(png_ptr, f, ReadData);
81
82                png_set_sig_bytes(png_ptr, sig_read);
83
84                png_read_info(png_ptr, info_ptr);
85                channels = png_get_channels(png_ptr, info_ptr);
86                if(channels == 3 || channels == 4 || channels == 1 || channels == 2)
87                {
88                   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
89                       &interlace_type, null, null);
90                   numPasses = png_set_interlace_handling(png_ptr);
91                   if(color_type == PNG_COLOR_TYPE_PALETTE)
92                   {
93                      if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
94                      {
95                         channels = 4;
96                         png_set_tRNS_to_alpha(png_ptr);
97                      }
98                      else
99                         channels = 3;
100                      png_set_palette_to_rgb(png_ptr);
101                   }
102                   else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
103                       png_set_tRNS_to_alpha(png_ptr);
104
105                   if((result = bitmap.Allocate(null, (uint)width, (uint)height, 0, pixelFormatRGBA, false)))
106                   {
107                      int pass;
108
109                      result = false;
110
111                      if(channels == 1)
112                      {
113                         byte * rowPtr = new byte[width * ((bit_depth == 16) ? 2 : 1)];
114                         for (pass = 0; pass < numPasses; pass++)
115                         {
116                            uint y;
117                            for (y = 0; y < height; y++)
118                            {
119                               uint x;
120                               ColorRGBA * destPtr = ((ColorRGBA *)bitmap.picture) + y * bitmap.stride;
121                               png_read_rows(png_ptr, &rowPtr, null, 1);
122                               if(bit_depth == 16)
123                               {
124                                  for(x = 0; x<width; x++)
125                                     destPtr[x] = ColorRGBA { rowPtr[x*2+0], rowPtr[x*2+0], rowPtr[x*2+0], 255 };
126                               }
127                               else if(bit_depth == 8)
128                               {
129                                  for(x = 0; x<width; x++)
130                                     destPtr[x] = ColorRGBA { rowPtr[x], rowPtr[x], rowPtr[x], 255 };
131                               }
132                               else if(bit_depth == 1)
133                               {
134                                  for(x = 0; x<width; x++)
135                                  {
136                                     int offset = x >> 3;
137                                     uint mask = 1 << (7 - (x & 0x07));
138                                     byte b = (rowPtr[offset] & mask) ? 255 : 0;
139                                     destPtr[x] = ColorRGBA { b, b, b, 255 };
140                                  }
141                               }
142                            }
143                         }
144                         delete rowPtr;
145                         result = true;
146                      }
147                      else if(channels == 2)
148                      {
149                         byte * rowPtr = new byte[width * 2 * ((bit_depth == 16) ? 2 : 1)];
150                         for (pass = 0; pass < numPasses; pass++)
151                         {
152                            uint y;
153                            for (y = 0; y < height; y++)
154                            {
155                               uint x;
156                               ColorRGBA * destPtr = ((ColorRGBA *)bitmap.picture) + y * bitmap.stride;
157                               png_read_rows(png_ptr, &rowPtr, null, 1);
158                               if(bit_depth == 16)
159                               {
160                                  for(x = 0; x<width; x++)
161                                     destPtr[x] = ColorRGBA { rowPtr[x*4], rowPtr[x*4], rowPtr[x*4], rowPtr[x*4+2] };
162                               }
163                               else if(bit_depth == 8)
164                               {
165                                  for(x = 0; x<width; x++)
166                                     destPtr[x] = ColorRGBA { rowPtr[x*2], rowPtr[x*2], rowPtr[x*2], rowPtr[x*2+1] };
167                               }
168                            }
169                         }
170                         delete rowPtr;
171                         result = true;
172                      }
173                      else if(channels == 3)
174                      {
175                         byte * rowPtr = new byte[width * 4 /*3*/ * ((bit_depth == 16) ? 2 : 1)];
176                         for (pass = 0; pass < numPasses; pass++)
177                         {
178                            uint y;
179                            for (y = 0; y < height; y++)
180                            {
181                               uint x;
182                               ColorRGBA * destPtr = ((ColorRGBA *)bitmap.picture) + y * bitmap.stride;
183                               png_read_rows(png_ptr, &rowPtr, null, 1);
184                               if(bit_depth == 16)
185                               {
186                                  for(x = 0; x<width; x++)
187                                     destPtr[x] = ColorRGBA { rowPtr[x*6+0], rowPtr[x*6+2], rowPtr[x*6+4], 255 };
188                               }
189                               else
190                               {
191                                  for(x = 0; x<width; x++)
192                                     destPtr[x] = ColorRGBA { rowPtr[x*3+0], rowPtr[x*3+1], rowPtr[x*3+2], 255 };
193                               }
194                            }
195                         }
196                         delete rowPtr;
197                         result = true;
198                      }
199                      else if(channels == 4)
200                      {
201                         if(bit_depth == 16)
202                         {
203                            // 16 bit per pixel not supported... Convert to 8 bit
204                            byte * rowPtr = new byte[width * 4 * 2];
205                            for (pass = 0; pass < numPasses; pass++)
206                            {
207                               uint y;
208                               for (y = 0; y < height; y++)
209                               {
210                                  uint x;
211                                  ColorRGBA * destPtr = ((ColorRGBA *)bitmap.picture) + y * bitmap.stride;
212                                  png_read_rows(png_ptr, &rowPtr, null, 1);
213                                  for(x = 0; x<width; x++)
214                                     destPtr[x] = ColorRGBA { rowPtr[x*8+0], rowPtr[x*8+2], rowPtr[x*8+4], rowPtr[x*8+6] };
215                               }
216                            }
217                            delete rowPtr;
218                            result = true;
219                         }
220                         else
221                         {
222                            for (pass = 0; pass < numPasses; pass++)
223                            {
224                               uint y;
225                               for (y = 0; y < height; y++)
226                               {
227                                  byte * rowPtr = (byte *)(((ColorRGBA *)bitmap.picture) + y * bitmap.stride);
228                                  png_read_rows(png_ptr, &rowPtr, null, 1);
229                                  #ifdef __BIG_ENDIAN__
230                                  swap32s((uint32*)rowPtr, width);
231                                  #endif
232                               }
233                            }
234                            result = true;
235                         }
236                      }
237                   }
238                }
239
240                png_read_end(png_ptr, info_ptr);
241             }
242          }
243          png_destroy_read_struct(&png_ptr, &info_ptr, null);
244       }
245       if(!result)
246          bitmap.Free();
247       return result;
248    }
249
250    bool Save(Bitmap bitmap, const char *filename, void * options)
251    {
252       bool result = false;
253       Bitmap tempBitmap = null;
254       if(bitmap && bitmap.pixelFormat != pixelFormatRGBA)
255       {
256          tempBitmap = Bitmap { };
257          if(tempBitmap.Copy(bitmap) && tempBitmap.Convert(null, pixelFormatRGBA, null))
258             bitmap = tempBitmap;
259          else
260             bitmap = null;
261       }
262
263       if(bitmap)
264       {
265          File f = FileOpen(filename, write);
266          if(f)
267          {
268             png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, null, null, null);
269             if(png_ptr)
270             {
271                png_infop info_ptr = png_create_info_struct(png_ptr);
272                if(info_ptr)
273                {
274                   if(!setjmp(png_jmpbuf(png_ptr)))
275                   {
276                      uint y;
277
278                      png_set_write_fn(png_ptr, f, WriteData, null);
279
280                      png_set_IHDR(png_ptr, info_ptr, bitmap.width, bitmap.height, 8, PNG_COLOR_TYPE_RGBA,
281                         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
282
283                      png_write_info(png_ptr, info_ptr);
284
285                      for (y = 0; y < bitmap.height; y++)
286                      {
287                         byte * rowPtr = (byte *)(((uint *)bitmap.picture) + y * bitmap.stride);
288                         png_write_rows(png_ptr, &rowPtr, 1);
289                      }
290
291                      png_write_end(png_ptr, info_ptr);
292
293                      result = true;
294                   }
295                }
296                png_destroy_write_struct(&png_ptr, &info_ptr);
297             }
298             delete f;
299          }
300       }
301       if(tempBitmap)
302          delete tempBitmap;
303       return result;
304    }
305
306    ColorAlpha * LoadPalette(const char * fileName, const char * type)
307    {
308       ColorAlpha * result = null;
309       return result;
310    }
311 }