Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion libvisual/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ IF(NOT HAVE_STDC_MATH)
MESSAGE(FATAL_ERROR "Libvisual requires libm to build")
ENDIF()

# Check for Zlib
FIND_PACKAGE(ZLIB 1.2 REQUIRED)

# Check for libpng
FIND_PACKAGE(PNG REQUIRED)
FIND_PACKAGE(PNG 1.4 REQUIRED)

# Internationalization
SET(GETTEXT_PACKAGE "libvisual-${LV_VERSION_SUFFIX}")
Expand Down
1 change: 1 addition & 0 deletions libvisual/libvisual/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ TARGET_LINK_LIBRARIES(libvisual
PkgConfig::ORC
PNG::PNG
Threads::Threads
ZLIB::ZLIB
)

IF(WIN32)
Expand Down
115 changes: 106 additions & 9 deletions libvisual/libvisual/lv_video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,87 @@
#include "lv_color.h"
#include "lv_common.h"
#include "lv_cpu.h"
#include "private/lv_string_hash.hpp"
#include "private/lv_video_private.hpp"
#include "private/lv_video_blit.hpp"
#include "private/lv_video_convert.hpp"
#include "private/lv_video_fill.hpp"
#include "private/lv_video_transform.hpp"
#include "private/lv_video_bmp.hpp"
#include "private/lv_video_png.hpp"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

namespace LV {

namespace fs = std::filesystem;

namespace {

using BitmapReader = std::function<VideoPtr (std::istream&)>;
using BitmapWriter = std::function<bool (Video const&, std::ostream&)>;

struct ImageFormatInfo
{
std::string name;
BitmapReader reader;
BitmapWriter writer;
std::vector<std::string> extensions;
};

using ImageFormatExtensionLookup = std::unordered_map<
std::string, ImageFormat, StringHash, std::equal_to<>>;

std::unordered_map<ImageFormat, ImageFormatInfo> const supported_image_formats
{
{ImageFormat::BMP, {"BMP", bitmap_load_bmp, nullptr, {".bmp"}}},
{ImageFormat::PNG, {"PNG", bitmap_load_png, bitmap_save_png, {".png"}}}
};

ImageFormatExtensionLookup build_image_format_extension_lookup ()
{
ImageFormatExtensionLookup map;

for (auto const& entry : supported_image_formats)
for (auto const& extension : entry.second.extensions)
map.emplace (extension, entry.first);

return map;
}

std::optional<ImageFormat> find_image_format_by_extension (std::string_view extension)
{
static auto const lookup {build_image_format_extension_lookup ()};

auto match {lookup.find (extension)};
if (match == lookup.end ())
return std::nullopt;
return match->second;
}

bool is_valid_scale_method (VisVideoScaleMethod scale_method)
{
return scale_method == VISUAL_VIDEO_SCALE_NEAREST
|| scale_method == VISUAL_VIDEO_SCALE_BILINEAR;
}

// TODO: Factor this out into a string utility library
std::string to_lower_ascii (std::string const& s)
{
auto result {s};
for (auto& c : result)
c = std::tolower (c);
return result;
}

} // anonymous namespace


Expand Down Expand Up @@ -191,17 +252,16 @@ namespace LV {

VideoPtr Video::create_from_stream (std::istream& input)
{
auto image = bitmap_load_bmp (input);
if (image) {
return image;
}

image = bitmap_load_png (input);
if (image) {
return image;
for (auto entry : supported_image_formats) {
if (entry.second.reader) {
auto image {entry.second.reader (input)};
if (image) {
return image;
}
}
}

return {};
return nullptr;
}

VideoPtr Video::create_scale_depth (VideoConstPtr const& src,
Expand Down Expand Up @@ -260,6 +320,43 @@ namespace LV {
return m_impl->buffer->is_allocated ();
}

bool Video::save_to_file (std::string const& path) const
{
auto extension {fs::path {path}.extension ()};
auto extension_lower (to_lower_ascii (extension.string ()));

auto format {find_image_format_by_extension (extension_lower)};
if (!format.has_value ()) {
visual_log (VISUAL_LOG_ERROR, "Could not deduce format from filename (%s)", path.c_str ());
return false;
}

std::ofstream output {path};
if (!output) {
visual_log (VISUAL_LOG_ERROR, "Could not create file '%s'", path.c_str ());
return false;
}

return save_to_stream (output, format.value ());
}

bool Video::save_to_stream (std::ostream& output, ImageFormat format) const
{
auto entry {supported_image_formats.find (format)};

if (entry == supported_image_formats.end ()) {
visual_log (VISUAL_LOG_ERROR, "Saving to unknown format (%d).", static_cast<int> (format));
return false;
}

if (!entry->second.writer) {
visual_log (VISUAL_LOG_ERROR, "Saving to %s is unsupported.", entry->second.name.c_str ());
return false;
}

return entry->second.writer (*this, output);
}

void Video::copy_attrs (VideoConstPtr const& src)
{
set_depth (src->m_impl->depth);
Expand Down
38 changes: 38 additions & 0 deletions libvisual/libvisual/lv_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ typedef enum {
VISUAL_VIDEO_COMPOSE_TYPE_CUSTOM /**< Custom compose function (looks up on the source VisVideo. */
} VisVideoComposeType;

/**
* The set of known and supported image formats.
*/
typedef enum {
VISUAL_IMAGE_FORMAT_BMP = 0,
VISUAL_IMAGE_FORMAT_PNG
} VisImageFormat;

typedef struct _VisVideoAttrOptions VisVideoAttrOptions;

#ifdef __cplusplus
Expand Down Expand Up @@ -125,6 +133,15 @@ struct _VisVideoAttrOptions {

namespace LV {

/**
* The set of known and supported image formats.
*/
enum class ImageFormat
{
BMP = VISUAL_IMAGE_FORMAT_BMP,
PNG = VISUAL_IMAGE_FORMAT_PNG
};

class Video;

typedef IntrusivePtr<Video> VideoPtr;
Expand Down Expand Up @@ -259,6 +276,25 @@ namespace LV {
*/
BufferPtr get_buffer () const;

/**
* Saves contents to a file.
*
* @param path Path name of file to save to. Specify the file format by using the appropriate extension.
*
* @return true if file was successfully saved, false otherwise.
*/
bool save_to_file (std::string const& path) const;

/**
* Saves contents to a stream.
*
* @param output stream to write contents to.
* @param format image file format to encode contents in.
*
* @return true if file was successfully saved, false otherwise.
*/
bool save_to_stream (std::ostream& output, ImageFormat format) const;

/**
* Sets all attributes.
*
Expand Down Expand Up @@ -513,6 +549,8 @@ LV_NODISCARD LV_API VisVideo *visual_video_new_with_buffer (int width, int heigh
LV_NODISCARD LV_API VisVideo *visual_video_new_wrap_buffer (void *buffer, int owner, int width, int height, VisVideoDepth depth, int pitch);
LV_NODISCARD LV_API VisVideo *visual_video_load_from_file (const char *path);

LV_API int visual_video_save_to_file (VisVideo *video);

LV_API void visual_video_ref (VisVideo *video);
LV_API void visual_video_unref (VisVideo *video);

Expand Down
8 changes: 8 additions & 0 deletions libvisual/libvisual/lv_video_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ VisVideo *visual_video_load_from_file (const char *path)
return self.get ();
}

int visual_video_save_to_file (VisVideo *self, const char *path)
{
visual_return_val_if_fail (self != nullptr, FALSE);
visual_return_val_if_fail (path != nullptr, FALSE);

return self->save_to_file (path);
}

int visual_video_has_allocated_buffer (VisVideo *self)
{
visual_return_val_if_fail (self != nullptr, FALSE);
Expand Down
Loading