mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +00:00
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:
parent
07edad611f
commit
a82ef25886
|
@ -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
|
||||||
|
|
|
@ -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
2525
src/nanosvg/nanosvg.h
Normal file
File diff suppressed because it is too large
Load diff
6
src/nanosvg/nanosvg_impl.cpp
Normal file
6
src/nanosvg/nanosvg_impl.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#define NANOSVG_IMPLEMENTATION
|
||||||
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "nanosvg.h"
|
||||||
|
#include "nanosvgrast.h"
|
18
src/nanosvg/nanosvg_license.txt
Normal file
18
src/nanosvg/nanosvg_license.txt
Normal 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
794
src/nanosvg/nanosvgrast.h
Normal 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
|
88
src/resources/SVGResource.cpp
Normal file
88
src/resources/SVGResource.cpp
Normal 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;
|
||||||
|
}
|
25
src/resources/SVGResource.h
Normal file
25
src/resources/SVGResource.h
Normal 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;
|
||||||
|
};
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue