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