Added .svg vector graphics file support.

Should "just work" for any TextureResource::get("*.svg"). dynamic_cast it to an SVGResource and call rasterizeAt(w, h) if you need to re-rasterize it.
Only supports scaling that maintains aspect ratio.
This commit is contained in:
Aloshi 2014-03-19 20:13:59 -05:00
parent 07edad611f
commit a82ef25886
10 changed files with 3504 additions and 40 deletions

View file

@ -200,6 +200,9 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/nanosvg/nanosvg.h
${CMAKE_CURRENT_SOURCE_DIR}/src/nanosvg/nanosvgrast.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/DetailedGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/DetailedGameListView.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/IGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/IGameListView.h
@ -216,6 +219,7 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.h
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/SVGResource.h
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h
${CMAKE_CURRENT_SOURCE_DIR}/data/Resources.h ${CMAKE_CURRENT_SOURCE_DIR}/data/Resources.h
) )
@ -279,8 +283,11 @@ set(ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/nanosvg/nanosvg_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Font.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/SVGResource.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.cpp

View file

@ -6,6 +6,7 @@
#include "../Renderer.h" #include "../Renderer.h"
#include "../ThemeData.h" #include "../ThemeData.h"
#include "../Util.h" #include "../Util.h"
#include "../resources/SVGResource.h"
Eigen::Vector2i ImageComponent::getTextureSize() const Eigen::Vector2i ImageComponent::getTextureSize() const
{ {
@ -73,6 +74,12 @@ void ImageComponent::resize()
} }
} }
} }
SVGResource* svg = dynamic_cast<SVGResource*>(mTexture.get());
if(svg)
{
svg->rasterizeAt((int)mSize.x(), (int)mSize.y());
}
} }
void ImageComponent::setImage(std::string path, bool tile) void ImageComponent::setImage(std::string path, bool tile)

2525
src/nanosvg/nanosvg.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
#define NANOSVG_IMPLEMENTATION
#define NANOSVGRAST_IMPLEMENTATION
#include <stdio.h>
#include "nanosvg.h"
#include "nanosvgrast.h"

View file

@ -0,0 +1,18 @@
Copyright (c) 2013-14 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

794
src/nanosvg/nanosvgrast.h Normal file
View file

@ -0,0 +1,794 @@
/*
* Copyright (c) 2013-14 Mikko Mononen memon@inside.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* The polygon rasterization is heavily based on stb_truetype rasterizer
* by Sean Barrett - http://nothings.org/
*
*/
#ifndef NANOSVGRAST_H
#define NANOSVGRAST_H
#ifdef __cplusplus
extern "C" {
#endif
/* Example Usage:
// Load SVG
struct SNVGImage* image = nsvgParseFromFile("test.svg.");
// Create rasterizer (can be used to render multiple images).
struct NSVGrasterizer* rast = nsvgCreateRasterizer();
// Allocate memory for image
unsigned char* img = malloc(w*h*4);
// Rasterize
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
*/
// Allocated rasterizer context.
struct NSVGrasterizer* nsvgCreateRasterizer();
// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
// r - pointer to rasterizer context
// image - pointer to image to rasterize
// tx,ty - image offset (applied after scaling)
// scale - image scale
// dst - pointer to destination image data, 4 bytes per pixel (RGBA)
// w - width of the image to render
// h - height of the image to render
// stride - number of bytes per scaleline in the destination buffer
void nsvgRasterize(struct NSVGrasterizer* r,
struct NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride);
// Deletes rasterizer context.
void nsvgDeleteRasterizer(struct NSVGrasterizer*);
#ifdef __cplusplus
};
#endif
#endif // NANOSVGRAST_H
#ifdef NANOSVGRAST_IMPLEMENTATION
#include <math.h>
#define NSVG__SUBSAMPLES 5
#define NSVG__FIXSHIFT 10
#define NSVG__FIX (1 << NSVG__FIXSHIFT)
#define NSVG__FIXMASK (NSVG__FIX-1)
#define NSVG__MEMPAGE_SIZE 1024
struct NSVGedge {
float x0,y0, x1,y1;
int dir;
struct NSVGedge* next;
};
struct NSVGactiveEdge {
int x,dx;
float ey;
int dir;
struct NSVGactiveEdge *next;
};
struct NSVGmemPage {
unsigned char mem[NSVG__MEMPAGE_SIZE];
int size;
struct NSVGmemPage* next;
};
struct NSVGcachedPaint {
char type;
char spread;
float xform[6];
unsigned int colors[256];
};
struct NSVGrasterizer
{
float px, py;
struct NSVGedge* edges;
int nedges;
int cedges;
struct NSVGactiveEdge* freelist;
struct NSVGmemPage* pages;
struct NSVGmemPage* curpage;
unsigned char* scanline;
int cscanline;
unsigned char* bitmap;
int width, height, stride;
};
struct NSVGrasterizer* nsvgCreateRasterizer()
{
struct NSVGrasterizer* r = (struct NSVGrasterizer*)malloc(sizeof(struct NSVGrasterizer));
if (r == NULL) goto error;
memset(r, 0, sizeof(struct NSVGrasterizer));
return r;
error:
nsvgDeleteRasterizer(r);
return NULL;
}
void nsvgDeleteRasterizer(struct NSVGrasterizer* r)
{
struct NSVGmemPage* p;
if (r == NULL) return;
p = r->pages;
while (p != NULL) {
struct NSVGmemPage* next = p->next;
free(p);
p = next;
}
if (r->edges) free(r->edges);
if (r->scanline) free(r->scanline);
free(r);
}
static struct NSVGmemPage* nsvg__nextPage(struct NSVGrasterizer* r, struct NSVGmemPage* cur)
{
struct NSVGmemPage *newp;
// If using existing chain, return the next page in chain
if (cur != NULL && cur->next != NULL) {
return cur->next;
}
// Alloc new page
newp = (struct NSVGmemPage*)malloc(sizeof(struct NSVGmemPage));
if (newp == NULL) return NULL;
memset(newp, 0, sizeof(struct NSVGmemPage));
// Add to linked list
if (cur != NULL)
cur->next = newp;
else
r->pages = newp;
return newp;
}
static void nsvg__resetPool(struct NSVGrasterizer* r)
{
struct NSVGmemPage* p = r->pages;
while (p != NULL) {
p->size = 0;
p = p->next;
}
r->curpage = r->pages;
}
static unsigned char* nsvg__alloc(struct NSVGrasterizer* r, int size)
{
unsigned char* buf;
if (size > NSVG__MEMPAGE_SIZE) return NULL;
if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) {
r->curpage = nsvg__nextPage(r, r->curpage);
}
buf = &r->curpage->mem[r->curpage->size];
r->curpage->size += size;
return buf;
}
static void nsvg__addEdge(struct NSVGrasterizer* r, float x0, float y0, float x1, float y1)
{
struct NSVGedge* e;
// Skip horizontal edges
if (y0 == y1)
return;
if (r->nedges+1 > r->cedges) {
r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
r->edges = (struct NSVGedge*)realloc(r->edges, sizeof(struct NSVGedge) * r->cedges);
if (r->edges == NULL) return;
}
e = &r->edges[r->nedges];
r->nedges++;
if (y0 < y1) {
e->x0 = x0;
e->y0 = y0;
e->x1 = x1;
e->y1 = y1;
e->dir = 1;
} else {
e->x0 = x1;
e->y0 = y1;
e->x1 = x0;
e->y1 = y0;
e->dir = -1;
}
}
static float nsvg__absf(float x) { return x < 0 ? -x : x; }
static void nsvg__flattenCubicBez(struct NSVGrasterizer* r,
float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4,
float tol, int level)
{
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
if (level > 10) return;
if (nsvg__absf(x1+x3-x2-x2) + nsvg__absf(y1+y3-y2-y2) + nsvg__absf(x2+x4-x3-x3) + nsvg__absf(y2+y4-y3-y3) < tol) {
nsvg__addEdge(r, r->px, r->py, x4, y4);
r->px = x4;
r->py = y4;
return;
}
x12 = (x1+x2)*0.5f;
y12 = (y1+y2)*0.5f;
x23 = (x2+x3)*0.5f;
y23 = (y2+y3)*0.5f;
x34 = (x3+x4)*0.5f;
y34 = (y3+y4)*0.5f;
x123 = (x12+x23)*0.5f;
y123 = (y12+y23)*0.5f;
x234 = (x23+x34)*0.5f;
y234 = (y23+y34)*0.5f;
x1234 = (x123+x234)*0.5f;
y1234 = (y123+y234)*0.5f;
nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, tol, level+1);
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1);
}
static void nsvg__flattenShape(struct NSVGrasterizer* r, struct NSVGshape* shape, float scale)
{
struct NSVGpath* path;
float tol = 0.25f * 4.0f / scale;
int i;
for (path = shape->paths; path != NULL; path = path->next) {
// Flatten path
r->px = path->pts[0];
r->py = path->pts[1];
for (i = 0; i < path->npts-1; i += 3) {
float* p = &path->pts[i*2];
nsvg__flattenCubicBez(r, p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7], tol, 0);
}
// Close path
nsvg__addEdge(r, r->px,r->py, path->pts[0],path->pts[1]);
}
}
static int nsvg__cmpEdge(const void *p, const void *q)
{
struct NSVGedge* a = (struct NSVGedge*)p;
struct NSVGedge* b = (struct NSVGedge*)q;
if (a->y0 < b->y0) return -1;
if (a->y0 > b->y0) return 1;
return 0;
}
static struct NSVGactiveEdge* nsvg__addActive(struct NSVGrasterizer* r, struct NSVGedge* e, float startPoint)
{
struct NSVGactiveEdge* z;
if (r->freelist != NULL) {
// Restore from freelist.
z = r->freelist;
r->freelist = z->next;
} else {
// Alloc new edge.
z = (struct NSVGactiveEdge*)nsvg__alloc(r, sizeof(struct NSVGactiveEdge));
if (z == NULL) return NULL;
}
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
// STBTT_assert(e->y0 <= start_point);
// round dx down to avoid going too far
if (dxdy < 0)
z->dx = -floorf(NSVG__FIX * -dxdy);
else
z->dx = floorf(NSVG__FIX * dxdy);
z->x = floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0)));
// z->x -= off_x * FIX;
z->ey = e->y1;
z->next = 0;
z->dir = e->dir;
return z;
}
static void nsvg__freeActive(struct NSVGrasterizer* r, struct NSVGactiveEdge* z)
{
z->next = r->freelist;
r->freelist = z;
}
// note: this routine clips fills that extend off the edges... ideally this
// wouldn't happen, but it could happen if the truetype glyph bounding boxes
// are wrong, or if the user supplies a too-small bitmap
static void nsvg__fillActiveEdges(unsigned char* scanline, int len, struct NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax)
{
// non-zero winding fill
int x0 = 0, w = 0;
while (e != NULL) {
if (w == 0) {
// if we're currently at zero, we need to record the edge start point
x0 = e->x; w += e->dir;
} else {
int x1 = e->x; w += e->dir;
// if we went to zero, we need to draw
if (w == 0) {
int i = x0 >> NSVG__FIXSHIFT;
int j = x1 >> NSVG__FIXSHIFT;
if (i < *xmin) *xmin = i;
if (j > *xmax) *xmax = j;
if (i < len && j >= 0) {
if (i == j) {
// x0,x1 are the same pixel, so compute combined coverage
scanline[i] += (unsigned char)((x1 - x0) * maxWeight >> NSVG__FIXSHIFT);
} else {
if (i >= 0) // add antialiasing for x0
scanline[i] += (unsigned char)(((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT);
else
i = -1; // clip
if (j < len) // add antialiasing for x1
scanline[j] += (unsigned char)(((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT);
else
j = len; // clip
for (++i; i < j; ++i) // fill pixels between x0 and x1
scanline[i] += (unsigned char)maxWeight;
}
}
}
}
e = e->next;
}
}
static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return (r) | (g << 8) | (b << 16) | (a << 24);
}
static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
{
int iu = (float)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8;
int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8;
int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8;
int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8;
return nsvg__RGBA(r,g,b,a);
}
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
float tx, float ty, float scale, struct NSVGcachedPaint* cache)
{
if (cache->type == NSVG_PAINT_COLOR) {
int i, cr, cg, cb, ca;
cr = cache->colors[0] & 0xff;
cg = (cache->colors[0] >> 8) & 0xff;
cb = (cache->colors[0] >> 16) & 0xff;
ca = (cache->colors[0] >> 24) & 0xff;
for (i = 0; i < count; i++) {
int r,g,b;
int a = ((int)cover[0] * ca) >> 8;
int ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
// Blend over
r += ((ia * (int)dst[0]) >> 8);
g += ((ia * (int)dst[1]) >> 8);
b += ((ia * (int)dst[2]) >> 8);
a += ((ia * (int)dst[3]) >> 8);
dst[0] = (unsigned char)r;
dst[1] = (unsigned char)g;
dst[2] = (unsigned char)b;
dst[3] = (unsigned char)a;
cover++;
dst += 4;
}
} else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) {
// TODO: spread modes.
// TODO: plenty of opportunities to optimize.
float fx, fy, dx, gy;
float* t = cache->xform;
int i, cr, cg, cb, ca;
unsigned int c;
fx = (x - tx) / scale;
fy = (y - ty) / scale;
dx = 1.0f / scale;
for (i = 0; i < count; i++) {
int r,g,b,a,ia;
gy = fx*t[1] + fy*t[3] + t[5];
c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)];
cr = (c) & 0xff;
cg = (c >> 8) & 0xff;
cb = (c >> 16) & 0xff;
ca = (c >> 24) & 0xff;
a = ((int)cover[0] * ca) >> 8;
ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
// Blend over
r += ((ia * (int)dst[0]) >> 8);
g += ((ia * (int)dst[1]) >> 8);
b += ((ia * (int)dst[2]) >> 8);
a += ((ia * (int)dst[3]) >> 8);
dst[0] = (unsigned char)r;
dst[1] = (unsigned char)g;
dst[2] = (unsigned char)b;
dst[3] = (unsigned char)a;
cover++;
dst += 4;
fx += dx;
}
} else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) {
// TODO: spread modes.
// TODO: plenty of opportunities to optimize.
// TODO: focus (fx,fy)
float fx, fy, dx, gx, gy, gd;
float* t = cache->xform;
int i, cr, cg, cb, ca;
unsigned int c;
fx = (x - tx) / scale;
fy = (y - ty) / scale;
dx = 1.0f / scale;
for (i = 0; i < count; i++) {
int r,g,b,a,ia;
gx = fx*t[0] + fy*t[2] + t[4];
gy = fx*t[1] + fy*t[3] + t[5];
gd = sqrtf(gx*gx + gy*gy);
c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)];
cr = (c) & 0xff;
cg = (c >> 8) & 0xff;
cb = (c >> 16) & 0xff;
ca = (c >> 24) & 0xff;
a = ((int)cover[0] * ca) >> 8;
ia = 255 - a;
// Premultiply
r = (cr * a) >> 8;
g = (cg * a) >> 8;
b = (cb * a) >> 8;
// Blend over
r += ((ia * (int)dst[0]) >> 8);
g += ((ia * (int)dst[1]) >> 8);
b += ((ia * (int)dst[2]) >> 8);
a += ((ia * (int)dst[3]) >> 8);
dst[0] = (unsigned char)r;
dst[1] = (unsigned char)g;
dst[2] = (unsigned char)b;
dst[3] = (unsigned char)a;
cover++;
dst += 4;
fx += dx;
}
}
}
static void nsvg__rasterizeSortedEdges(struct NSVGrasterizer *r, float tx, float ty, float scale, struct NSVGcachedPaint* cache)
{
struct NSVGactiveEdge *active = NULL;
int y, s;
int e = 0;
int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline
int xmin, xmax;
for (y = 0; y < r->height; y++) {
memset(r->scanline, 0, r->width);
xmin = r->width;
xmax = 0;
for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
// find center of pixel for this scanline
float scany = y*NSVG__SUBSAMPLES + s + 0.5f;
struct NSVGactiveEdge **step = &active;
// update all active edges;
// remove all active edges that terminate before the center of this scanline
while (*step) {
struct NSVGactiveEdge *z = *step;
if (z->ey <= scany) {
*step = z->next; // delete from list
// NSVG__assert(z->valid);
nsvg__freeActive(r, z);
} else {
z->x += z->dx; // advance to position for current scanline
step = &((*step)->next); // advance through list
}
}
// resort the list if needed
for (;;) {
int changed = 0;
step = &active;
while (*step && (*step)->next) {
if ((*step)->x > (*step)->next->x) {
struct NSVGactiveEdge* t = *step;
struct NSVGactiveEdge* q = t->next;
t->next = q->next;
q->next = t;
*step = q;
changed = 1;
}
step = &(*step)->next;
}
if (!changed) break;
}
// insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
while (e < r->nedges && r->edges[e].y0 <= scany) {
if (r->edges[e].y1 > scany) {
struct NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany);
if (z == NULL) break;
// find insertion point
if (active == NULL) {
active = z;
} else if (z->x < active->x) {
// insert at front
z->next = active;
active = z;
} else {
// find thing to insert AFTER
struct NSVGactiveEdge* p = active;
while (p->next && p->next->x < z->x)
p = p->next;
// at this point, p->next->x is NOT < z->x
z->next = p->next;
p->next = z;
}
}
e++;
}
// now process all active edges in non-zero fashion
if (active != NULL)
nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax);
}
// Blit
if (xmin < 0) xmin = 0;
if (xmax > r->width-1) xmax = r->width-1;
if (xmin <= xmax) {
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty,scale,cache);
}
}
}
static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)
{
int x,y;
// Unpremultiply
for (y = 0; y < h; y++) {
unsigned char *row = &image[y*stride];
for (x = 0; x < w; x++) {
int r = row[0], g = row[1], b = row[2], a = row[3];
if (a != 0) {
row[0] = (int)(r*255/a);
row[1] = (int)(g*255/a);
row[2] = (int)(b*255/a);
}
row += 4;
}
}
// Defringe
for (y = 0; y < h; y++) {
unsigned char *row = &image[y*stride];
for (x = 0; x < w; x++) {
int r = 0, g = 0, b = 0, a = row[3], n = 0;
if (a == 0) {
if (x-1 > 0 && row[-1] != 0) {
r += row[-4];
g += row[-3];
b += row[-2];
n++;
}
if (x+1 < w && row[7] != 0) {
r += row[4];
g += row[5];
b += row[6];
n++;
}
if (y-1 > 0 && row[-stride+3] != 0) {
r += row[-stride];
g += row[-stride+1];
b += row[-stride+2];
n++;
}
if (y+1 < h && row[stride+3] != 0) {
r += row[stride];
g += row[stride+1];
b += row[stride+2];
n++;
}
if (n > 0) {
row[0] = r/n;
row[1] = g/n;
row[2] = b/n;
}
}
row += 4;
}
}
}
static void nsvg__initPaint(struct NSVGcachedPaint* cache, struct NSVGpaint* paint)
{
int i, j;
struct NSVGgradient* grad;
cache->type = paint->type;
if (paint->type == NSVG_PAINT_COLOR) {
cache->colors[0] = paint->color;
return;
}
grad = paint->gradient;
cache->spread = grad->spread;
memcpy(cache->xform, grad->xform, sizeof(float)*6);
if (grad->nstops == 0) {
for (i = 0; i < 256; i++)
cache->colors[i] = 0;
} if (grad->nstops == 1) {
for (i = 0; i < 256; i++)
cache->colors[i] = grad->stops[i].color;
} else {
unsigned int ca, cb;
float ua, ub, du, u;
int ia, ib, count;
ca = grad->stops[0].color;
cb = grad->stops[grad->nstops-1].color;
ua = nsvg__clampf(grad->stops[0].offset, 0, 1);
ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1);
ia = ua * 255.0f;
ib = ub * 255.0f;
for (i = 0; i < ia; i++) {
cache->colors[i] = ca;
}
for (i = 0; i < grad->nstops-1; i++) {
ca = grad->stops[i].color;
cb = grad->stops[i+1].color;
ua = nsvg__clampf(grad->stops[i].offset, 0, 1);
ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1);
ia = ua * 255.0f;
ib = ub * 255.0f;
count = ib - ia;
if (count <= 0) continue;
u = 0;
du = 1.0f / (float)count;
for (j = 0; j < count; j++) {
cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u);
u += du;
}
}
for (i = ib; i < 256; i++)
cache->colors[i] = cb;
}
}
void nsvgRasterize(struct NSVGrasterizer* r,
struct NSVGimage* image, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride)
{
struct NSVGshape *shape = NULL;
struct NSVGedge *e = NULL;
struct NSVGcachedPaint cache;
int i;
r->bitmap = dst;
r->width = w;
r->height = h;
r->stride = stride;
if (w > r->cscanline) {
r->cscanline = w;
r->scanline = (unsigned char*)realloc(r->scanline, w);
if (r->scanline == NULL) return;
}
for (i = 0; i < h; i++)
memset(&dst[i*stride], 0, w*4);
for (shape = image->shapes; shape != NULL; shape = shape->next) {
if (shape->fill.type == NSVG_PAINT_NONE)
continue;
nsvg__resetPool(r);
r->freelist = NULL;
r->nedges = 0;
nsvg__flattenShape(r, shape, scale);
// Scale and translate edges
for (i = 0; i < r->nedges; i++) {
e = &r->edges[i];
e->x0 = tx + e->x0 * scale;
e->y0 = (ty + e->y0 * scale) * NSVG__SUBSAMPLES;
e->x1 = tx + e->x1 * scale;
e->y1 = (ty + e->y1 * scale) * NSVG__SUBSAMPLES;
}
// Rasterize edges
qsort(r->edges, r->nedges, sizeof(struct NSVGedge), nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
nsvg__initPaint(&cache, &shape->fill);
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache);
}
nsvg__unpremultiplyAlpha(dst, w, h, stride);
r->bitmap = NULL;
r->width = 0;
r->height = 0;
r->stride = 0;
}
#endif

View file

@ -0,0 +1,88 @@
#include "SVGResource.h"
#include "../nanosvg/nanosvg.h"
#include "../nanosvg/nanosvgrast.h"
#include "../Log.h"
#define DPI 96
SVGResource::SVGResource(const std::string& path, bool tile) : TextureResource(path, tile), mSVGImage(NULL)
{
assert(tile == false);
}
SVGResource::~SVGResource()
{
deinitSVG();
}
void SVGResource::unload(std::shared_ptr<ResourceManager>& rm)
{
deinitSVG();
TextureResource::unload(rm);
}
void SVGResource::initFromMemory(const char* file, size_t length)
{
deinit();
deinitSVG();
// nsvgParse excepts a modifiable, null-terminated string
char* copy = (char*)malloc(length + 1);
memcpy(copy, file, length);
copy[length] = '\0';
mSVGImage = nsvgParse(copy, "px", DPI);
free(copy);
if(!mSVGImage)
{
LOG(LogError) << "Error parsing SVG image.";
return;
}
rasterizeAt((int)mSVGImage->width, (int)mSVGImage->height);
}
void SVGResource::rasterizeAt(size_t width, size_t height)
{
if(!mSVGImage)
return;
unsigned char* imagePx = (unsigned char*)malloc(width * height * 4);
NSVGrasterizer* rast = nsvgCreateRasterizer();
nsvgRasterize(rast, mSVGImage, 0, 0, width / mSVGImage->width, imagePx, width, height, width * 4);
nsvgDeleteRasterizer(rast);
// flip the pixels
unsigned int temp;
unsigned int* arr = (unsigned int*)imagePx;
for(size_t y = 0; y < height / 2; y++)
{
for(size_t x = 0; x < width; x++)
{
temp = arr[x + (y * width)];
arr[x + (y * width)] = arr[x + (height * width) - ((y + 1) * width)];
arr[x + (height * width) - ((y + 1) * width)] = temp;
}
}
initFromPixels(imagePx, width, height);
free(imagePx);
}
Eigen::Vector2i SVGResource::getImageSize() const
{
if(mSVGImage)
return Eigen::Vector2i((int)mSVGImage->width, (int)mSVGImage->height);
return Eigen::Vector2i::Zero();
}
void SVGResource::deinitSVG()
{
if(mSVGImage)
nsvgDelete(mSVGImage);
mSVGImage = NULL;
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "TextureResource.h"
struct NSVGimage;
class SVGResource : public TextureResource
{
public:
virtual ~SVGResource();
virtual void unload(std::shared_ptr<ResourceManager>& rm) override;
virtual void initFromMemory(const char* image, size_t length) override;
void rasterizeAt(size_t width, size_t height);
Eigen::Vector2i getImageSize() const;
protected:
friend TextureResource;
SVGResource(const std::string& path, bool tile);
void deinitSVG();
NSVGimage* mSVGImage;
};

View file

@ -5,12 +5,13 @@
#include "../ImageIO.h" #include "../ImageIO.h"
#include "../Renderer.h" #include "../Renderer.h"
#include "SVGResource.h"
std::map< TextureResource::TextureKeyType, std::weak_ptr<TextureResource> > TextureResource::sTextureMap; std::map< TextureResource::TextureKeyType, std::weak_ptr<TextureResource> > TextureResource::sTextureMap;
TextureResource::TextureResource(const std::string& path, bool tile) : TextureResource::TextureResource(const std::string& path, bool tile) :
mTextureID(0), mPath(path), mTextureSize(Eigen::Vector2i::Zero()), mTile(tile) mTextureID(0), mPath(path), mTextureSize(Eigen::Vector2i::Zero()), mTile(tile)
{ {
reload(ResourceManager::getInstance());
} }
TextureResource::~TextureResource() TextureResource::~TextureResource()
@ -26,28 +27,23 @@ void TextureResource::unload(std::shared_ptr<ResourceManager>& rm)
void TextureResource::reload(std::shared_ptr<ResourceManager>& rm) void TextureResource::reload(std::shared_ptr<ResourceManager>& rm)
{ {
if(!mPath.empty()) if(!mPath.empty())
initFromResource(rm->getFileData(mPath)); {
const ResourceData& data = rm->getFileData(mPath);
initFromMemory((const char*)data.ptr.get(), data.length);
}
} }
void TextureResource::initFromResource(const ResourceData data) void TextureResource::initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height)
{ {
//make sure we aren't going to leak an old texture
deinit(); deinit();
size_t width, height; assert(width > 0 && height > 0);
std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32(const_cast<unsigned char*>(data.ptr.get()), data.length, width, height);
if(imageRGBA.size() == 0)
{
LOG(LogError) << "Could not initialize texture (invalid resource data)!";
return;
}
//now for the openGL texture stuff //now for the openGL texture stuff
glGenTextures(1, &mTextureID); glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID); glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageRGBA.data()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dataRGBA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@ -61,8 +57,6 @@ void TextureResource::initFromResource(const ResourceData data)
void TextureResource::initFromMemory(const char* data, size_t length) void TextureResource::initFromMemory(const char* data, size_t length)
{ {
deinit();
size_t width, height; size_t width, height;
std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(data), length, width, height); std::vector<unsigned char> imageRGBA = ImageIO::loadFromMemoryRGBA32((const unsigned char*)(data), length, width, height);
@ -72,20 +66,7 @@ void TextureResource::initFromMemory(const char* data, size_t length)
return; return;
} }
//now for the openGL texture stuff initFromPixels(imageRGBA.data(), width, height);
glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageRGBA.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
const GLint wrapMode = mTile ? GL_REPEAT : GL_CLAMP_TO_EDGE;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
mTextureSize << width, height;
} }
void TextureResource::deinit() void TextureResource::deinit()
@ -97,7 +78,7 @@ void TextureResource::deinit()
} }
} }
Eigen::Vector2i TextureResource::getSize() const const Eigen::Vector2i& TextureResource::getSize() const
{ {
return mTextureSize; return mTextureSize;
} }
@ -135,8 +116,20 @@ std::shared_ptr<TextureResource> TextureResource::get(const std::string& path, b
return foundTexture->second.lock(); return foundTexture->second.lock();
} }
std::shared_ptr<TextureResource> tex = std::shared_ptr<TextureResource>(new TextureResource(path, tile)); // need to create it
std::shared_ptr<TextureResource> tex;
// is it an SVG?
if(path.substr(path.size() - 4, std::string::npos) == ".svg")
{
// probably
tex = std::shared_ptr<SVGResource>(new SVGResource(path, tile));
}else{
tex = std::shared_ptr<TextureResource>(new TextureResource(path, tile));
}
sTextureMap[key] = std::weak_ptr<TextureResource>(tex); sTextureMap[key] = std::weak_ptr<TextureResource>(tex);
rm->addReloadable(tex); rm->addReloadable(tex);
tex->reload(ResourceManager::getInstance());
return tex; return tex;
} }

View file

@ -16,28 +16,29 @@ public:
virtual ~TextureResource(); virtual ~TextureResource();
void unload(std::shared_ptr<ResourceManager>& rm) override; virtual void unload(std::shared_ptr<ResourceManager>& rm) override;
void reload(std::shared_ptr<ResourceManager>& rm) override; virtual void reload(std::shared_ptr<ResourceManager>& rm) override;
bool isTiled() const; bool isTiled() const;
Eigen::Vector2i getSize() const; const Eigen::Vector2i& getSize() const;
void bind() const; void bind() const;
// Warning: will NOT correctly reinitialize when this texture is reloaded (e.g. ES starts/stops playing a game). // Warning: will NOT correctly reinitialize when this texture is reloaded (e.g. ES starts/stops playing a game).
void initFromMemory(const char* image, size_t length); virtual void initFromMemory(const char* file, size_t length);
private: // Warning: will NOT correctly reinitialize when this texture is reloaded (e.g. ES starts/stops playing a game).
void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height);
protected:
TextureResource(const std::string& path, bool tile); TextureResource(const std::string& path, bool tile);
void initFromPath();
void initFromResource(const ResourceData data);
void deinit(); void deinit();
Eigen::Vector2i mTextureSize; Eigen::Vector2i mTextureSize;
GLuint mTextureID;
const std::string mPath; const std::string mPath;
const bool mTile; const bool mTile;
private:
GLuint mTextureID;
typedef std::pair<std::string, bool> TextureKeyType; typedef std::pair<std::string, bool> TextureKeyType;
static std::map< TextureKeyType, std::weak_ptr<TextureResource> > sTextureMap; static std::map< TextureKeyType, std::weak_ptr<TextureResource> > sTextureMap;
}; };