Skip to content

Commit

Permalink
Enable true streaming with a buffer-based fallback
Browse files Browse the repository at this point in the history
When using libvips version 8.13 or higher.
  • Loading branch information
kleisauke committed Jul 25, 2024
1 parent df1ecc7 commit 269e35e
Show file tree
Hide file tree
Showing 24 changed files with 356 additions and 436 deletions.
16 changes: 8 additions & 8 deletions src/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(HEADERS
exceptions/large.h
exceptions/unreadable.h
exceptions/unsupported.h
io/blob.h
io/source.h
io/target.h
parsers/color.h
Expand Down Expand Up @@ -77,18 +78,17 @@ target_link_libraries(${PROJECT_NAME}
${VIPS_LDFLAGS}
)

# TODO(kleisauke): Enable once magickload_source is supported in libvips
#if (VIPS_VERSION VERSION_GREATER_EQUAL 8.13)
# target_compile_definitions(${PROJECT_NAME}
# PUBLIC
# WESERV_ENABLE_TRUE_STREAMING
# )
#endif()
if (VIPS_VERSION VERSION_GREATER_EQUAL 8.13)
target_compile_definitions(${PROJECT_NAME}
PUBLIC
WESERV_ENABLE_TRUE_STREAMING
)
endif()

set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 5
SOVERSION ${PROJECT_VERSION_MAJOR}
)

install(TARGETS ${PROJECT_NAME}
Expand Down
5 changes: 3 additions & 2 deletions src/api/api_manager_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ ApiManagerImpl::ApiManagerImpl(std::unique_ptr<ApiEnvInterface> env)
vips_error_clear();

env_->log_error("error: Unable to start up libvips: " + error);
} // LCOV_EXCL_STOP
// LCOV_EXCL_STOP
}
}

ApiManagerImpl::~ApiManagerImpl() {
Expand Down Expand Up @@ -142,8 +143,8 @@ Status ApiManagerImpl::exception_handler(const std::string &query) {

return {Status::Code::Unknown, error_str,
Status::ErrorCause::Application};
// LCOV_EXCL_STOP
}
// LCOV_EXCL_STOP
}

utils::Status ApiManagerImpl::process(const std::string &query,
Expand Down
81 changes: 81 additions & 0 deletions src/api/io/blob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

#include <vips/vips8>

namespace weserv::api::io {

class Blob {
public:
explicit Blob(VipsBlob *blob = nullptr) : blob_(blob) {}

// copy constructor
Blob(const Blob &a) : blob_(a.blob_) {
reference();
}

// this mustn't be virtual: we want this class to only be a pointer,
// no vtable allowed
~Blob() {
unreference();
}

// assignment ... we must delete the old ref
Blob &operator=(const Blob &a) {
// check whether we are already referencing this object -
// if so make this a null op. This will also deal with
// self-assignment.
if (blob_ != a.blob_) {
unreference();

blob_ = a.blob_;

reference();
}

return *this;
}

/**
* Get the underlying VipsBlob pointer.
* @return The underlying VipsBlob pointer.
*/
VipsBlob *get_blob() const {
return blob_;
}

/**
* Get the data from a Blob.
* @param length Return number of bytes of data.
* @return The data.
*/
const void *get_blob(size_t *length) const {
return vips_blob_get(blob_, length);
}

/**
* @return true if this Blob is a nullptr.
*/
bool is_null() const {
return blob_ == nullptr;
}

private:
/**
* The underlying VipsBlob pointer, can be nullptr.
*/
VipsBlob *blob_;

inline void unreference() {
if (blob_ != nullptr) {
vips_area_unref(reinterpret_cast<VipsArea *>(blob_));
}
}

inline void reference() {
if (blob_ != nullptr) {
vips_area_copy(reinterpret_cast<VipsArea *>(blob_));
}
}
};

} // namespace weserv::api::io
9 changes: 5 additions & 4 deletions src/api/io/source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ Source Source::new_from_pointer(std::unique_ptr<io::SourceInterface> source) {
g_object_new(WESERV_TYPE_SOURCE, "source", source.get(), nullptr));

if (vips_object_build(VIPS_OBJECT(weserv_source)) != 0) {
VIPS_UNREF(weserv_source);
VIPS_UNREF(weserv_source); // LCOV_EXCL_START
throw vips::VError();
// LCOV_EXCL_STOP
}

return Source(weserv_source);
Expand All @@ -70,18 +71,18 @@ Source Source::new_from_file(const std::string &filename) {
VipsSource *source = vips_source_new_from_file(filename.c_str());

if (source == nullptr) {
throw vips::VError();
throw vips::VError(); // LCOV_EXCL_LINE
}

return Source(source);
}

Source Source::new_from_buffer(const std::string &buffer) {
VipsSource *source =
vips_source_new_from_memory(buffer.c_str(), buffer.size());
vips_source_new_from_memory(buffer.data(), buffer.size());

if (source == nullptr) {
throw vips::VError();
throw vips::VError(); // LCOV_EXCL_LINE
}

return Source(source);
Expand Down
9 changes: 6 additions & 3 deletions src/api/io/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static gint64 weserv_target_write_wrapper(VipsTarget *target, const void *data,
return weserv_target->write(data, length);
}

// LCOV_EXCL_START
static gint64 weserv_target_read_wrapper(VipsTarget *target, void *data,
size_t length) {
auto weserv_target = WESERV_TARGET(target)->target;
Expand All @@ -30,6 +31,7 @@ static gint64 weserv_target_seek_wrapper(VipsTarget *target, gint64 offset,

return weserv_target->seek(offset, whence);
}
// LCOV_EXCL_STOP

static int weserv_target_end_wrapper(VipsTarget *target) {
auto weserv_target = WESERV_TARGET(target)->target;
Expand Down Expand Up @@ -71,8 +73,9 @@ Target Target::new_to_pointer(std::unique_ptr<io::TargetInterface> target) {
g_object_new(WESERV_TYPE_TARGET, "target", target.get(), nullptr));

if (vips_object_build(VIPS_OBJECT(weserv_target)) != 0) {
VIPS_UNREF(weserv_target);
VIPS_UNREF(weserv_target); // LCOV_EXCL_START
throw vips::VError();
// LCOV_EXCL_STOP
}

return Target(weserv_target);
Expand All @@ -82,7 +85,7 @@ Target Target::new_to_file(const std::string &filename) {
VipsTarget *target = vips_target_new_to_file(filename.c_str());

if (target == nullptr) {
throw vips::VError();
throw vips::VError(); // LCOV_EXCL_LINE
}

return Target(target);
Expand All @@ -92,7 +95,7 @@ Target Target::new_to_memory() {
VipsTarget *target = vips_target_new_to_memory();

if (target == nullptr) {
throw vips::VError();
throw vips::VError(); // LCOV_EXCL_LINE
}

return Target(target);
Expand Down
17 changes: 11 additions & 6 deletions src/api/processors/mask.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "mask.h"

#include "../io/blob.h"
#include "../utils/utility.h"

#include <algorithm>
Expand All @@ -15,6 +16,8 @@ namespace weserv::api::processors {
using enums::MaskType;
using parsers::Color;

using io::Blob;

std::string Mask::svg_path_by_type(const int width, const int height,
const MaskType &mask,
int *out_x_min, int *out_y_min,
Expand Down Expand Up @@ -323,10 +326,11 @@ VImage Mask::process(const VImage &image) const {
auto svg_mask = svg.str();

// We don't take a copy of the data or free it
auto *blob = vips_blob_new(nullptr, svg_mask.data(), svg_mask.size());
auto blob =
Blob(vips_blob_new(nullptr, svg_mask.data(), svg_mask.size()));
auto mask = VImage::svgload_buffer(
blob, VImage::option()->set("access", VIPS_ACCESS_SEQUENTIAL));
vips_area_unref(reinterpret_cast<VipsArea *>(blob));
blob.get_blob(),
VImage::option()->set("access", VIPS_ACCESS_SEQUENTIAL));

// Cutout via dest-in
output_image = output_image.composite2(mask, VIPS_BLEND_MODE_DEST_IN);
Expand All @@ -353,10 +357,11 @@ VImage Mask::process(const VImage &image) const {
auto svg_frame = svg.str();

// We don't take a copy of the data or free it
auto *blob = vips_blob_new(nullptr, svg_frame.data(), svg_frame.size());
auto blob =
Blob(vips_blob_new(nullptr, svg_frame.data(), svg_frame.size()));
auto frame = VImage::svgload_buffer(
blob, VImage::option()->set("access", VIPS_ACCESS_SEQUENTIAL));
vips_area_unref(reinterpret_cast<VipsArea *>(blob));
blob.get_blob(),
VImage::option()->set("access", VIPS_ACCESS_SEQUENTIAL));

// Ensure image to composite is premultiplied sRGB
frame = frame.premultiply();
Expand Down
Loading

0 comments on commit 269e35e

Please sign in to comment.