|
From: rainbyte <rai...@us...> - 2012-07-17 10:51:07
|
Author: Lasse Karkkainen <tro...@tr...>
Date: Fri Jul 6 05:03:06 2012 +0300
Reimplement SVG loading
- Use Cairo surfaces instead of GDKPixbuf (avoids rsvg depracation warnings)
- Do not reopen the same SVG multiple times (the old code was hackish)
- PNG caching simplified, no longer throws anything
- Cairo used for writing PNGs (Cairo internally converts into non-premultiplied alpha)
- Cairo surfaces use premultiplied alpha (affects SVG loading)
- BUG: our rendering system assumes non-premultiplied alpha (SVGs display incorrectly)
---
game/cache.hh | 36 +++++++------------------------
game/image.hh | 65 +++++++++++++++++++-------------------------------------
2 files changed, 30 insertions(+), 71 deletions(-)
diff --git a/game/cache.hh b/game/cache.hh
index 9121a54..2427432 100644
--- a/game/cache.hh
+++ b/game/cache.hh
@@ -8,38 +8,18 @@
namespace cache {
- /** Cache is for some reason invalid. (i.e. too old or not non existent) **/
- class invalid_cache_error : public std::runtime_error {
- /* This exception should always be caught so that the user never
- * ever sees it. We shouldn't bother them with caching issues.
- */
- public:
- invalid_cache_error() : std::runtime_error("Invalid Cache. This error should never be seen.") {}
- };
-
-
/** Builds the full path and file name for the SVG cache resource **/
fs::path constructSVGCacheFileName(fs::path const& svgfilename, double factor);
- /** Given a path to an SVG the caching policy is returned **/
- inline bool cachableSVGResource(fs::path const& /*svgfilename*/ ) {
- // FIXME: Currently all is cached, so should this be removed?
- return true;
- }
-
/** Load an SVG from the cache, if loading fails invalid_cache_error is thrown **/
- template <typename T> void loadSVG(T& target, fs::path const& source_filename, double factor) {
- if(!cachableSVGResource(source_filename)) throw invalid_cache_error();
-
+ template <typename T> bool loadSVG(T& target, fs::path const& source_filename, double factor) {
fs::path const cache_filename = cache::constructSVGCacheFileName(source_filename, factor);
- if(!fs::exists(cache_filename)) throw invalid_cache_error();
-
- // SVG file is newer, so the cache is now invalid
- if(fs::last_write_time(source_filename) > fs::last_write_time(cache_filename))
- throw invalid_cache_error();
-
- try {
- loadPNG(target, cache_filename.string());
- } catch( ... ) { throw invalid_cache_error(); }
+ // Verify that a cached file exists and that it is more recent than the original SVG
+ if (!fs::exists(cache_filename)) return false;
+ if (fs::last_write_time(source_filename) > fs::last_write_time(cache_filename)) return false;
+ // Try to load the cached file
+ try { loadPNG(target, cache_filename.string()); } catch( ... ) { return false; }
+ return true;
}
}
+
diff --git a/game/image.hh b/game/image.hh
index f36d231..61dbb14 100644
--- a/game/image.hh
+++ b/game/image.hh
@@ -53,6 +53,7 @@ namespace {
bpp = 4;
break;
default:
+ // Note: INT_ARGB uses premultiplied alpha and cannot be supported by libpng
throw std::logic_error("Unsupported pixel format in writePNG_internal");
}
png_write_info(pngPtr, infoPtr);
@@ -94,55 +95,33 @@ namespace {
static inline void loadSVG(Bitmap& bitmap, std::string const& filename) {
double factor = config["graphic/svg_lod"].f();
-
- /* always */ try {
- cache::loadSVG(bitmap, filename, factor);
- return;
- } catch ( ... ) { /* no-op. Failing to read the cache only means more work here */ }
-
- struct RSVGInit {
- RSVGInit() { rsvg_init(); }
- ~RSVGInit() { rsvg_term(); }
- } rsvgInit;
+ // Try to load a cached PNG instead
+ if (cache::loadSVG(bitmap, filename, factor)) return;
+ // Open the SVG file in librsvg
+ g_type_init();
GError* pError = NULL;
- // Find SVG dimensions (in pixels)
- RsvgHandle* svgHandle = rsvg_handle_new_from_file(filename.c_str(), &pError);
+ boost::shared_ptr<RsvgHandle> svgHandle(rsvg_handle_new_from_file(filename.c_str(), &pError), g_object_unref);
if (pError) {
g_error_free(pError);
throw std::runtime_error("Unable to load " + filename);
}
+ // Get SVG dimensions
RsvgDimensionData svgDimension;
- rsvg_handle_get_dimensions (svgHandle, &svgDimension);
- rsvg_handle_free(svgHandle);
- unsigned int w = nextPow2(svgDimension.width*factor);
- unsigned int h = nextPow2(svgDimension.height*factor);
- // Load and raster the SVG
- GdkPixbuf* pb = rsvg_pixbuf_from_file_at_size(filename.c_str(), w, h, &pError);
- if (pError) {
- g_error_free(pError);
- throw std::runtime_error("Unable to load " + filename);
- }
- bitmap.resize(w, h);
- std::memcpy(&bitmap.buf[0], gdk_pixbuf_get_pixels(pb), bitmap.buf.size());
- bitmap.ar = float(svgDimension.width)/svgDimension.height;
- gdk_pixbuf_unref(pb);
-
- // write the cache iff the resource is cachable
- if(cache::cachableSVGResource(filename)) {
- fs::path const cache_filename = cache::constructSVGCacheFileName(filename, factor);
-
- // need to reload the svg to have the correct aspect ratio
- w = svgDimension.width * factor;
- h = svgDimension.height * factor;
- pb = rsvg_pixbuf_from_file_at_size(filename.c_str(), w, h, &pError);
- if (pError) {
- g_error_free(pError);
- throw std::runtime_error("Unable to load " + filename);
- }
- fs::create_directories(cache_filename.parent_path());
- writePNG(cache_filename.string(), w, h, pix::CHAR_RGBA, false, gdk_pixbuf_get_pixels(pb));
- gdk_pixbuf_unref(pb);
- }
+ rsvg_handle_get_dimensions(svgHandle.get(), &svgDimension);
+ // Prepare the pixel buffer
+ bitmap.resize(svgDimension.width*factor, svgDimension.height*factor);
+ bitmap.fmt = pix::INT_ARGB;
+ // Raster with Cairo
+ boost::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);
+ boost::shared_ptr<cairo_t> dc(cairo_create(surface.get()), cairo_destroy);
+ cairo_scale(dc.get(), factor, factor);
+ rsvg_handle_render_cairo(svgHandle.get(), dc.get());
+ // Write to cache so that it can be loaded faster the next time
+ fs::path const cache_filename = cache::constructSVGCacheFileName(filename, factor);
+ fs::create_directories(cache_filename.parent_path());
+ cairo_surface_write_to_png(surface.get(), cache_filename.string().c_str());
}
static inline void loadPNG(Bitmap& bitmap, std::string const& filename) {
|