Commit patch #157 by Elio which adds SVG support to betawidget, along with some modifications by myself.

git-svn-id: svn+ssh://svn.gna.org/svn/warzone/trunk@6437 4a71c877-e1ca-e34f-864e-861f7616d084
master
Freddie Witherden 2008-12-06 19:43:10 +00:00
parent a60dcc9b31
commit 3e9530696d
3 changed files with 421 additions and 0 deletions

View File

@ -21,6 +21,7 @@
#include "init.h"
#include "../../widget.h"
#include "../../svgManager.h"
#include "../../window.h"
#include <SDL.h>
@ -33,6 +34,9 @@ void widgetSDLInit()
// Set the screen size
windowSetScreenSize(surface->w, surface->h);
// Initialise the SVG manager
svgManagerInit();
// Create an initial window vector to store windows
windowSetWindowVector(vectorCreate());
}
@ -44,5 +48,8 @@ void widgetSDLQuit()
// Release all active windows
vectorMapAndDestroy(windowVector, (mapCallback) widgetDestroy);
// Release any cached SVG images
svgManagerQuit();
}

View File

@ -0,0 +1,299 @@
/*
This file is part of Warzone 2100.
Copyright (C) 2008 Freddie Witherden
Copyright (C) 2008 Elio Gubser
Copyright (C) 2008 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "svgManager.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <svg-cairo.h>
static vector *svgImages = NULL;
/*
* Internal structures
*/
typedef struct _svgImage svgImage;
struct _svgImage
{
/// Path to the image
const char *filename;
/// libsvg-cairo internal parse tree
svg_cairo_t *svg;
/// Array of svgRenderedImages for the currently rendered sizes
vector *renders;
};
static void svgManagerFreeRender(void *renderedImage)
{
// Un-reference the pattern
cairo_pattern_destroy(((svgRenderedImage *) renderedImage)->pattern);
// Free the memory allocated for the structure
free(renderedImage);
}
static void svgManagerFreeImage(void *image)
{
// Free the libcairo-svg parse tree
svg_cairo_destroy(((svgImage *) image)->svg);
// Release all renders of the image
vectorMapAndDestroy(((svgImage *) image)->renders, svgManagerFreeRender);
// Finally, free the memory allocated for our structure
free(image);
}
/**
* Loads the SVG image specified by filename and adds it to the global list
* of SVG images. The newly loaded image is then returned for rendering.
*
* @param filename The path of the image to load.
* @return A pointer to the newly loaded SVG image on success, NULL otherwise.
*/
static svgImage *svgManagerLoad(const char *filename)
{
svgImage *svg = malloc(sizeof(svgImage));
svg_cairo_status_t status;
// Set the filename of the newly loaded image
svg->filename = filename;
// Initialise the svg_cairo structure
status = svg_cairo_create(&svg->svg);
// Ensure that we were able to create the structure
assert(status == SVG_CAIRO_STATUS_SUCCESS);
// Parse the SVG
status = svg_cairo_parse(svg->svg, filename);
// Ensure that the SVG was successfully parsed
assert(status == SVG_CAIRO_STATUS_SUCCESS);
// Create a new vector to store size-specific renders of the image
svg->renders = vectorCreate();
// Add the newly loaded image to the images array and return it
return vectorAdd(svgImages, svg);
}
/**
* Renders the SVG image, svg, at (width,height) and adds it to the cache. The
* rendered image is then returned.
*
* @param svg The SVG image to render.
* @param width The width to render the image at.
* @param height The height to render the image at.
* @return A pointer to the newly rendered image on success, false othwewise.
*/
static svgRenderedImage *svgManagerRender(svgImage *svg, int width, int height)
{
svgRenderedImage *render = malloc(sizeof(svgRenderedImage));
unsigned int sourceWidth, sourceHeight;
cairo_t *cr;
cairo_surface_t *surface;
cairo_pattern_t *pattern;
// Create the surface
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
// Ensure the surface was created
assert(cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS);
// Create a context to draw onto the surface with
cr = cairo_create(surface);
// Ensure the context was created
assert(cairo_status(cr) == CAIRO_STATUS_SUCCESS);
// Get the native width/height of the SVG
svg_cairo_get_size(svg->svg, &sourceWidth, &sourceHeight);
// Scale the cairo context such that the SVG is rendered at the desired size
cairo_scale(cr, (double) width / (double) sourceWidth,
(double) height / (double) sourceHeight);
// Render the SVG to the context
svg_cairo_render(svg->svg, cr);
// Create a pattern out of the surface
pattern = cairo_pattern_create_for_surface(surface);
// Release the cairo context and surface as they are no longer needed
cairo_destroy(cr);
cairo_surface_destroy(surface);
// Save the rendering information in the vector
render->pattern = pattern;
render->patternSize.x = width;
render->patternSize.y = height;
// Add the newly rendered image to the renders array and return it
return vectorAdd(svg->renders, render);
}
void svgManagerInit()
{
// Make sure that we are not being called twice in a row
assert(svgImages == NULL);
svgImages = vectorCreate();
}
void svgManagerQuit()
{
// Ensure that we have not already been called
assert(svgImages != NULL);
// Release all rendered SVG images
vectorMapAndDestroy(svgImages, svgManagerFreeImage);
// Note that we have quit
svgImages = NULL;
}
void svgManagerBlit(cairo_t *cr, const svgRenderedImage *svg)
{
// Save the state of the current cairo context
cairo_save(cr);
// Set the current painting source to be the rendered SVG image
cairo_set_source(cr, svg->pattern);
// Draw a rectangle the size of the image
cairo_rectangle(cr, 0.0, 0.0, svg->patternSize.x, svg->patternSize.y);
// Fill the rectangle with the pattern
cairo_fill(cr);
// Finally, paint the rectangle to the surface
cairo_paint(cr);
// Restore the cairo context
cairo_restore(cr);
}
svgRenderedImage *svgManagerGet(const char *filename, int width, int height)
{
svgImage *currSvg, *svg = NULL;
svgRenderedImage *currRender, *render = NULL;
// See if the image exists in the cache at *any* size
while ((currSvg = vectorNext(svgImages)))
{
// If the filenames match then we have found the image
if (strcmp(filename, currSvg->filename) == 0)
{
svg = currSvg;
break;
}
}
// If no image was found then go ahead and load/parse it
if (svg == NULL)
{
svg = svgManagerLoad(filename);
}
// Fill out any missing size information
if (width == 0 || height == 0)
{
unsigned int sourceWidth, sourceHeight;
svg_cairo_get_size(svg->svg, &sourceWidth, &sourceHeight);
// No size information provided, render at source
if (width == 0 && height == 0)
{
width = sourceWidth;
height = sourceHeight;
}
// No width given, compute from height
else if (width == 0)
{
width = ((float) height / (float) sourceHeight) * (float) sourceWidth;
}
// No height given, compute from width
else // (height == 0)
{
height = ((float) width / (float) sourceWidth) * (float) sourceHeight;
}
}
// See if the image exists at the desired size
while ((currRender = vectorNext(svg->renders)))
{
// If the sizes match then we have found the render
if (currRender->patternSize.x == width
&& currRender->patternSize.y == height)
{
render = currRender;
}
}
// If no render was found then render the SVG at the requested size
if (render == NULL)
{
render = svgManagerRender(svg, width, height);
}
// Return the final rendering
return render;
}
svgRenderedImage *svgManagerGetWithWidth(const char *filename, int width,
int *height)
{
// Render the image
svgRenderedImage *render = svgManagerGet(filename, width, 0);
// If height is non-NULL copy the height into it
if (height)
{
*height = render->patternSize.y;
}
// Return the render
return render;
}
svgRenderedImage *svgManagerGetWithHeight(const char *filename, int height,
int *width)
{
// Render the image
svgRenderedImage *render = svgManagerGet(filename, 0, height);
// If width is non-NULL copy the width into it
if (width)
{
*width = render->patternSize.x;
}
// Return the render
return render;
}

View File

@ -0,0 +1,115 @@
/*
This file is part of Warzone 2100.
Copyright (C) 2008 Freddie Witherden
Copyright (C) 2008 Elio Gubser
Copyright (C) 2008 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SVG_MANAGER_H
#define SVG_MANAGER_H
#include "internal-cairo.h"
#include "vector.h"
#include "geom.h"
/*
* Forward declarations
*/
typedef struct _svgRenderedImage svgRenderedImage;
/**
* Represents a rendered SVG image which is ready to be blitted/composited onto
* a widget.
*/
struct _svgRenderedImage
{
/// Bitmap cairo_pattern_t containing the rendered image
cairo_pattern_t *pattern;
/// Size of the rendered image
size patternSize;
};
/**
* Initialises the SVG manager. This must be called before any SVG images are
* loaded/used.
*/
void svgManagerInit(void);
/**
* Uninitialises the SVG manager. Once this method has been called the result of
* attempting to call any svgManager* methods (without a call to svgManagerInit
* first) is undefined. Undefined behaviour is also encountered if one attempts
* to call this method without having first called svgManagerInit.
*/
void svgManagerQuit(void);
/**
* Composites the rendered SVG image, svg, onto the cairo context, cr, at the
* current translation position. The state of the cairo context is unchanged by
* this method.
*
* @param cr The cairo context to composite the image onto.
* @param svg The rendered SVG image to composite.
*/
void svgManagerBlit(cairo_t *cr, const svgRenderedImage *svg);
/**
* Loads and renders the SVG image, filename, at a size of (width,height). In
* order to improve performance the SVG manager makes use of an image cache.
* Hence, repeated calls to this method with the same parameters will not
* result in any kind of performance penalty.
*
* If both width and height are 0 then the image will be rendered at its native
* size. If just width is 0 then the rendered width is the native width
* multiplied by the height scale factor. The same applies when the height is 0
* except that the width scale factor is used instead.
*
* @param filename The path to the SVG image to render.
* @param width The width to render the image at, 0 for auto-select.
* @param height The height to render the image at, 0 for auto-select.
* @return A pointer to the rendered SVG image on success or NULL on failure.
*/
svgRenderedImage *svgManagerGet(const char *filename, int width, int height);
/**
* A convenience wrapper around svgManagerGet which automatically computes the
* desired image-height based off the provided width.
*
* @param filename The path to the SVG image to render.
* @param width The width to render the image.
* @param height The resulting height of the image, may be NULL.
* @return A pointer to the rendered SVG image on success or NULL on failure.
* @see svgManagerGet
*/
svgRenderedImage *svgManagerGetWithWidth(const char *filename, int width,
int *height);
/**
* A convenience wrapper around svgManagerGet which automatically computes the
* desired image-width based off the provided height.
*
* @param filename The path to the SVG image to render.
* @param height The height to render the image.
* @param width The resulting width of the image, may be NULL.
* @return A pointer to the rendered SVG image on success or NULL on failure.
* @see svgManagerGet
*/
svgRenderedImage *svgManagerGetWithHeight(const char *filename, int height,
int *width);
#endif /*SVG_MANAGER_H*/