Rev Author Line No. Line
23 ovan 1 #include "texture_png.h"
2 #include "../driver/texture.h"
3 #include "../compile.h"
4 #include <stdlib.h>
5 #include <png.h>
6  
7 extern "C" {
8     void* memccpy(void *dest, const void *src, int c, size_t n);
9 }
10  
11 namespace sleek
12 {
13     namespace loader
14     {
15         #ifdef texture_loader_png_support
16         static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
17         {
18             printf("PNG fatal error: %s", msg);
19             longjmp(png_jmpbuf(png_ptr), 1);
20         }
21  
22         // PNG function for warning handling
23         static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
24         {
25             printf("PNG warning: %s", msg);
26         }
27  
28         void PNGAPI user_read_data_fcn(png_structp png_ptr, png_bytep data, png_size_t length)
29         {
30             png_size_t check;
31  
32             io::filereader *file=(io::filereader*)png_get_io_ptr(png_ptr);
33             int pos = file->pos();
34             file->read((char*)data,(u32)length);
35  
36             if((file->pos()-pos) != length)
37                 png_error(png_ptr, "Read Error");
38         }
39         #endif
40  
41         std::shared_ptr<driver::texture> textureloader_png::read(io::filesystem *fs, const std::string &file) const noexcept
42         {
43             #ifdef texture_loader_png_support
44             png_byte magic[8];
45             png_structp png_ptr;
46             png_infop info_ptr;
47             int bit_depth = 0, color_type = 0;
48             png_bytep *row_pointers = 0;
49             png_uint_32 w = 0, h = 0;
50             GLubyte *texels = 0;
51  
52             auto in = fs->read(file);
53             if(!in)
54             {
55                 printf("error: couldn't open \"%s\"!\n", file.c_str());
56                 return nullptr;
57             }
58  
59             in->read(magic, sizeof(magic));
60  
61             if(!png_check_sig(magic, sizeof (magic)))
62             {
63                 fprintf(
64                     stderr,
65                     "error: \"%s\" is not a valid PNG image!\n",
66                     file.c_str()
67                 );
68                 return nullptr;
69             }
70  
71             png_ptr = png_create_read_struct(
72                 PNG_LIBPNG_VER_STRING,
73                 NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn
74             );
75             if(!png_ptr)
76                 return nullptr;
77  
78             info_ptr = png_create_info_struct(png_ptr);
79             if(!info_ptr || setjmp(png_jmpbuf(png_ptr)))
80             {
81                 png_destroy_read_struct(&png_ptr, &info_ptr, 0);
82                 if(row_pointers)
83                     free(row_pointers);
84                 return nullptr;
85             }
86  
87             png_set_read_fn(png_ptr, in.get(), user_read_data_fcn);
88             png_set_sig_bytes(png_ptr, sizeof (magic));
89             png_read_info(png_ptr, info_ptr);
90  
91             bit_depth = png_get_bit_depth(png_ptr, info_ptr);
92             color_type = png_get_color_type(png_ptr, info_ptr);
93  
94             if(color_type == PNG_COLOR_TYPE_PALETTE)
95                 png_set_palette_to_rgb(png_ptr);
96  
97             if(bit_depth < 8)
98             {
99                 if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
100                     png_set_expand_gray_1_2_4_to_8(png_ptr);
101                 else png_set_packing(png_ptr);
102             }
103  
104             if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
105                 png_set_tRNS_to_alpha(png_ptr);
106  
107             if(bit_depth == 16)
108                 png_set_strip_16(png_ptr);
109  
110             if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
111                 png_set_gray_to_rgb(png_ptr);
112  
113             //! internal png format change to bgr/rgb
114             png_set_bgr(png_ptr);
115  
116             png_read_update_info(png_ptr, info_ptr);
117             png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, 0, 0, 0);
118  
119             int internalFormat = 0;
120             if(color_type == PNG_COLOR_TYPE_GRAY)       internalFormat = driver::TXFMT_LUMINANCE;
121             if(color_type == PNG_COLOR_TYPE_GRAY_ALPHA) internalFormat = driver::TXFMT_LUMINANCE_ALPHA;
122             if(color_type == PNG_COLOR_TYPE_RGB)        internalFormat = driver::TXFMT_RGB;
123             if(color_type == PNG_COLOR_TYPE_RGB_ALPHA)  internalFormat = driver::TXFMT_RGBA;
124  
125             texels = (GLubyte*)malloc(sizeof(GLubyte)*w*h*internalFormat);
126             row_pointers = new png_bytep[h];
127  
128             // vertival flip or not ? comment = not
129             //for(register unsigned int i = 0; i < h; ++i) row_pointers[i] = (png_bytep)(texels+(i*w*internalFormat));
130             for(register unsigned int i = 0; i < h; ++i) row_pointers[i] = (png_bytep)(texels+((h-(i+1))*w*internalFormat));
131  
132             png_read_image(png_ptr, row_pointers);
133             png_read_end(png_ptr, 0);
134             png_destroy_read_struct(&png_ptr, &info_ptr, 0);
135             delete row_pointers;
136  
137             auto tmp = std::make_shared<driver::texture>(math::vec2i(w, h), (driver::TextureFormat)internalFormat);
138             memccpy(tmp->getBuffer(), (u8*)texels, 1, tmp->getBufferSize());
139  
140             tmp->flipVertical();
141  
142             printf("Load texture: \"%s\"\n", file.c_str());
143             return tmp;
144             #else
145                 return nullptr;
146             #endif
147         }
148  
149         bool textureloader_png::write(io::filesystem *fs, driver::texture*, const std::string &a) const noexcept
150         {
151             #ifdef texture_loader_png_support
152  
153             png_structp png_ptr;
154             png_infop info_ptr;
155             png_colorp palette;
156  
157             auto out = fs->write(a);
158             if(!out)
159                 return false;
160  
161             png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
162             if(!png_ptr)
163                 return false;
164  
165             info_ptr = png_create_info_struct(png_ptr);
166             if(!info_ptr || setjmp(png_jmpbuf(png_ptr)))
167             {
168                 png_destroy_write_struct(&png_ptr, &info_ptr);
169                 return false;
170             }
171  
172             png_write_end(png_ptr, info_ptr);
173             png_destroy_write_struct(&png_ptr, &info_ptr);
174  
175             /**
176              * TODO
177              */
178  
179             return true;
180             #else
181                 return false;
182             #endif
183         }
184  
185         bool textureloader_png::match(const std::string &filename) const noexcept
186         {
187             /**
188              * TODO
189              */
190             return filename.substr(filename.find_last_of('.')+1, 3) == "png";
191         }
192     }
193 }