Skip to content

Commit df9f976

Browse files
committed
Core (LV::Video): Implement saving to PNG files.
1 parent 4aa49d6 commit df9f976

File tree

5 files changed

+183
-6
lines changed

5 files changed

+183
-6
lines changed

libvisual/libvisual/lv_video.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@
3737
#include "private/lv_video_bmp.hpp"
3838
#include "private/lv_video_png.hpp"
3939
#include <cstring>
40+
#include <iostream>
4041
#include <fstream>
42+
#include <filesystem>
4143

4244
namespace LV {
4345

46+
namespace fs = std::filesystem;
47+
4448
namespace {
4549

4650
bool is_valid_scale_method (VisVideoScaleMethod scale_method)
@@ -260,6 +264,21 @@ namespace LV {
260264
return m_impl->buffer->is_allocated ();
261265
}
262266

267+
bool Video::save_to_file (std::string const& path) const
268+
{
269+
auto extension = fs::path {path}.extension ();
270+
271+
std::ofstream file {path};
272+
273+
if (extension == ".png") {
274+
return bitmap_save_png (*this, file);
275+
}
276+
277+
visual_log (VISUAL_LOG_ERROR, "Unsupported format with extension '%s'.", extension.c_str ());
278+
279+
return false;
280+
}
281+
263282
void Video::copy_attrs (VideoConstPtr const& src)
264283
{
265284
set_depth (src->m_impl->depth);

libvisual/libvisual/lv_video.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,15 @@ namespace LV {
259259
*/
260260
BufferPtr get_buffer () const;
261261

262+
/**
263+
* Saves contents to a file.
264+
*
265+
* @param path Path name of file to save to. Specify the file format by using the appropriate extension.
266+
*
267+
* @return true if file was successfully saved, false otherwise.
268+
*/
269+
bool save_to_file (std::string const& path) const;
270+
262271
/**
263272
* Sets all attributes.
264273
*
@@ -513,6 +522,8 @@ LV_NODISCARD LV_API VisVideo *visual_video_new_with_buffer (int width, int heigh
513522
LV_NODISCARD LV_API VisVideo *visual_video_new_wrap_buffer (void *buffer, int owner, int width, int height, VisVideoDepth depth, int pitch);
514523
LV_NODISCARD LV_API VisVideo *visual_video_load_from_file (const char *path);
515524

525+
LV_API int visual_video_save_to_file (VisVideo *video);
526+
516527
LV_API void visual_video_ref (VisVideo *video);
517528
LV_API void visual_video_unref (VisVideo *video);
518529

libvisual/libvisual/lv_video_c.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ VisVideo *visual_video_load_from_file (const char *path)
6868
return self.get ();
6969
}
7070

71+
int visual_video_save_to_file (VisVideo *self, const char *path)
72+
{
73+
visual_return_val_if_fail (self != nullptr, FALSE);
74+
75+
return self->save_to_file (path);
76+
}
77+
7178
int visual_video_has_allocated_buffer (VisVideo *self)
7279
{
7380
visual_return_val_if_fail (self != nullptr, FALSE);

libvisual/libvisual/private/lv_video_png.cpp

Lines changed: 141 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
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

2931
namespace 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

libvisual/libvisual/private/lv_video_png.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
#include <iosfwd>
2727

2828
namespace LV {
29+
2930
VideoPtr bitmap_load_png (std::istream& input);
30-
}
31+
32+
bool bitmap_save_png (Video const& bitmap, std::ostream& output);
33+
34+
} // LV namespace
3135

3236
#endif // _LV_VIDEO_BMP_HPP

0 commit comments

Comments
 (0)