Reputation: 629
I'm playing around with some code in a project; specifically a function that takes SVG images and makes pngs out of them.
I have this:
typedef std::vector<uint8_t> BinaryBuffer;
BinaryBuffer readFile(fs::path const& path) {
BinaryBuffer ret;
fs::ifstream f(path, std::ios::binary);
f.seekg(0, std::ios::end);
ret.resize(f.tellg());
f.seekg(0);
f.read(reinterpret_cast<char*>(ret.data()), ret.size());
if (!f) throw std::runtime_error("File cannot be read: " + path.string());
return ret;
}
void loadSVG(Bitmap& bitmap, fs::path const& filename) {
double factor = config["graphic/svg_lod"].f();
// Try to load a cached PNG instead
if (cache::loadSVG(bitmap, filename, factor)) return;
std::clog << "image/debug: Loading SVG: " + filename.string() << std::endl;
// Open the SVG file in librsvg
#if !GLIB_CHECK_VERSION(2, 36, 0) // Avoid deprecation warnings
g_type_init();
#endif
GError* pError = nullptr;
std::shared_ptr<RsvgHandle> svgHandle(rsvg_handle_new_with_flags(RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA), g_object_unref);
rsvg_handle_set_base_uri(svgHandle.get(),filename.string().c_str());
BinaryBuffer data = readFile(filename);
std::clog << "svg/debug: svg data size is: " << data.size() << std::endl;
gboolean result = rsvg_handle_write(svgHandle.get(), data.data(), data.size(), &pError);
// rsvg_handle_new_from_file(filename.string().c_str(), &pError)
if (result != TRUE) {
g_error_free(pError);
throw std::runtime_error("Unable to load " + filename.string());
}
else {
std::clog << "svg/debug: SVG loaded succesfully." << std::endl;
}
// Get SVG dimensions
RsvgDimensionData svgDimension;
rsvg_handle_get_dimensions(svgHandle.get(), &svgDimension);
// Prepare the pixel buffer
std::clog << "svg/debug: svg width is: " << svgDimension.width << ", and height: " << svgDimension.height << std::endl;
bitmap.resize(svgDimension.width*factor, svgDimension.height*factor);
bitmap.fmt = pix::INT_ARGB;
bitmap.linearPremul = true;
// Raster with Cairo
std::shared_ptr<cairo_surface_t> surface(
cairo_image_surface_create_for_data(&bitmap.buf[0], CAIRO_FORMAT_ARGB32, bitmap.width, bitmap.height, bitmap.width * 4),
cairo_surface_destroy);
std::shared_ptr<cairo_t> dc(cairo_create(surface.get()), cairo_destroy);
cairo_scale(dc.get(), factor, factor);
gboolean renderRes = TRUE;
renderRes = rsvg_handle_render_cairo(svgHandle.get(), dc.get());
if (renderRes != TRUE) {
throw std::runtime_error("Unable to render " + filename.string());
}
// Change byte order from BGRA to RGBA
for (uint32_t *ptr = reinterpret_cast<uint32_t*>(&*bitmap.buf.begin()), *end = ptr + bitmap.buf.size() / 4; ptr < end; ++ptr) {
uint8_t* pixel = reinterpret_cast<uint8_t*>(ptr);
uint8_t r = pixel[2], g = pixel[1], b = pixel[0], a = pixel[3];
pixel[0] = r; pixel[1] = g; pixel[2] = b; pixel[3] = a;
}
bitmap.fmt = pix::CHAR_RGBA;
// Write to cache so that it can be loaded faster the next time
fs::path cache_filename = cache::constructSVGCacheFileName(filename, factor);
fs::create_directories(cache_filename.parent_path());
writePNG(cache_filename, bitmap);
}
But it fails in rsvg_handle_render_cairo
... I don't know why. A previous version of the function using rsvg_handle_new_from_file (which doesn't use the BinaryBuffer struct) worked fine. Note, however that essentially the same struct and readFile function is used elsewhere without any issues. And from the debug messages I've dropped in there I can see that the file is indeed getting read. I am also getting correct dimensions from my svg file and there don't seem to be any errors prior to the call to the render function (so I'd assume it's parsed ok) but maybe not?
Upvotes: 2
Views: 691
Reputation: 629
The answer was ridiculously simple. I was missing rsvg_handle_close(svgHandle.get(), &pError);
Upvotes: 2