2222#include " config.h"
2323#include " lv_video_png.hpp"
2424#include " lv_common.h"
25- #include < png.h >
26- #include < istream >
25+ #include < iostream >
26+ #include < vector >
2727#include < csetjmp>
28+ #include < png.h>
29+ #include < zlib.h>
2830
2931namespace LV {
3032
@@ -40,20 +42,52 @@ namespace LV {
4042 }
4143 }
4244
43- void handle_png_warning (png_structp png_ptr, char const * message)
45+ void handle_png_write (png_structp png_ptr, png_bytep data, png_size_t length)
46+ {
47+ auto io_ptr = png_get_io_ptr (png_ptr);
48+ auto & output = *static_cast <std::ostream*> (io_ptr);
49+
50+ if (!output.write (reinterpret_cast <char *>(data), length)) {
51+ std::longjmp (png_jmpbuf (png_ptr), -1 );
52+ }
53+ }
54+
55+ void handle_png_flush (png_structp png_ptr)
56+ {
57+ auto io_ptr = png_get_io_ptr (png_ptr);
58+ auto & output = *static_cast <std::ostream*> (io_ptr);
59+
60+ output.flush ();
61+ }
62+
63+ void handle_png_load_warning (png_structp png_ptr, char const * message)
4464 {
4565 (void )png_ptr;
4666
4767 visual_log (VISUAL_LOG_WARNING, " PNG load error: %s" , message);
4868 }
4969
50- void handle_png_error (png_structp png_ptr, char const * message)
70+ void handle_png_load_error (png_structp png_ptr, char const * message)
5171 {
5272 (void )png_ptr;
5373
5474 visual_log (VISUAL_LOG_ERROR, " PNG load error: %s" , message);
5575 }
5676
77+ void handle_png_save_warning (png_structp png_ptr, char const * message)
78+ {
79+ (void )png_ptr;
80+
81+ visual_log (VISUAL_LOG_WARNING, " PNG save error: %s" , message);
82+ }
83+
84+ void handle_png_save_error (png_structp png_ptr, char const * message)
85+ {
86+ (void )png_ptr;
87+
88+ visual_log (VISUAL_LOG_ERROR, " PNG save error: %s" , message);
89+ }
90+
5791 } // anonymous
5892
5993 VideoPtr bitmap_load_png (std::istream& input)
@@ -69,7 +103,10 @@ namespace LV {
69103 return nullptr ;
70104 }
71105
72- auto png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, nullptr , handle_png_error, handle_png_warning);
106+ auto png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
107+ nullptr ,
108+ handle_png_load_error,
109+ handle_png_load_warning);
73110 if (!png_ptr) {
74111 return nullptr ;
75112 }
@@ -176,4 +213,103 @@ namespace LV {
176213 return Video::wrap (pixels, true , width, height, depth);
177214 }
178215
216+ bool bitmap_save_png (Video const & bitmap, std::ostream &output)
217+ {
218+ auto saved_stream_pos = output.tellp ();
219+
220+ auto png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
221+ nullptr ,
222+ handle_png_save_error,
223+ handle_png_save_warning);
224+ if (!png_ptr) {
225+ visual_log (VISUAL_LOG_ERROR, " Failed to create PNG write struct." );
226+ return false ;
227+ }
228+
229+ auto info_ptr = png_create_info_struct (png_ptr);
230+ if (!info_ptr) {
231+ visual_log (VISUAL_LOG_ERROR, " Failed to create PNG info struct." );
232+ png_destroy_write_struct (&png_ptr, nullptr );
233+ return false ;
234+ }
235+
236+ auto bitmap_width = bitmap.get_width ();
237+ auto bitmap_height = bitmap.get_height ();
238+
239+ std::vector<png_byte*> pixel_row_ptrs;
240+ pixel_row_ptrs.reserve (bitmap_height);
241+
242+ for (auto y = 0 ; y < bitmap_height; y++) {
243+ pixel_row_ptrs.push_back (static_cast <png_byte *> (bitmap.get_pixel_ptr (0 , y)));
244+ }
245+
246+ if (setjmp (png_jmpbuf (png_ptr))) {
247+ visual_log (VISUAL_LOG_ERROR, " Some error occurred." );
248+ output.seekp (saved_stream_pos);
249+ png_destroy_write_struct (&png_ptr, &info_ptr);
250+ }
251+
252+ int bit_depth = 0 ;
253+ int color_type = 0 ;
254+
255+ switch (bitmap.get_depth ()) {
256+ case VISUAL_VIDEO_DEPTH_8BIT: {
257+ bit_depth = 8 ;
258+ color_type = PNG_COLOR_TYPE_PALETTE;
259+ break ;
260+ }
261+ case VISUAL_VIDEO_DEPTH_24BIT: {
262+ bit_depth = 8 ;
263+ color_type = PNG_COLOR_TYPE_RGB;
264+ break ;
265+ }
266+ case VISUAL_VIDEO_DEPTH_32BIT: {
267+ bit_depth = 8 ;
268+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
269+ break ;
270+ }
271+ default : {
272+ visual_log (VISUAL_LOG_ERROR, " Invalid depth or depth not supported." );
273+ return false ;
274+ }
275+ }
276+
277+ #if VISUAL_LITTLE_ENDIAN
278+ png_set_bgr (png_ptr);
279+ #endif
280+
281+ png_set_filter (png_ptr, 0 , PNG_FILTER_NONE);
282+
283+ png_set_compression_level (png_ptr, Z_BEST_COMPRESSION);
284+
285+ png_set_IHDR (png_ptr, info_ptr, bitmap_width, bitmap_height,
286+ bit_depth, color_type, PNG_INTERLACE_NONE,
287+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
288+
289+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
290+ auto const & colors = bitmap.get_palette ().colors ;
291+
292+ std::vector<png_color> out_palette;
293+ out_palette.reserve (colors.size ());
294+
295+ for (auto const & color : colors) {
296+ out_palette.push_back ({color.r , color.g , color.b });
297+ }
298+
299+ png_set_PLTE (png_ptr, info_ptr, out_palette.data (), out_palette.size ());
300+ }
301+
302+ png_set_sRGB (png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
303+
304+ png_set_write_fn (png_ptr, &output, handle_png_write, handle_png_flush);
305+
306+ png_write_info (png_ptr, info_ptr);
307+ png_write_rows (png_ptr, pixel_row_ptrs.data (), bitmap_height);
308+ png_write_end (png_ptr, info_ptr);
309+
310+ png_destroy_write_struct (&png_ptr, &info_ptr);
311+
312+ return true ;
313+ }
314+
179315} // LV namespace
0 commit comments