mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-13 09:47:58 -06:00
Merge pull request #2780 from Noisyfox:bugfix/amd-png
Fix PNG build plate texture not rendering on AMD GPUs
This commit is contained in:
commit
93f62a47f5
6 changed files with 360 additions and 168 deletions
|
@ -1 +1 @@
|
||||||
Upstream source: https://github.com/memononen/nanosvg/tree/c1f6e209c16b18b46aa9f45d7e619acf42c29726
|
Upstream source: https://github.com/fltk/nanosvg/archive/abcd277ea45e9098bed752cf9c6875b533c0892f.zip
|
|
@ -72,6 +72,7 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum NSVGpaintType {
|
enum NSVGpaintType {
|
||||||
|
NSVG_PAINT_UNDEF = -1,
|
||||||
NSVG_PAINT_NONE = 0,
|
NSVG_PAINT_NONE = 0,
|
||||||
NSVG_PAINT_COLOR = 1,
|
NSVG_PAINT_COLOR = 1,
|
||||||
NSVG_PAINT_LINEAR_GRADIENT = 2,
|
NSVG_PAINT_LINEAR_GRADIENT = 2,
|
||||||
|
@ -119,7 +120,7 @@ typedef struct NSVGgradient {
|
||||||
} NSVGgradient;
|
} NSVGgradient;
|
||||||
|
|
||||||
typedef struct NSVGpaint {
|
typedef struct NSVGpaint {
|
||||||
char type;
|
signed char type;
|
||||||
union {
|
union {
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
NSVGgradient* gradient;
|
NSVGgradient* gradient;
|
||||||
|
@ -143,14 +144,17 @@ typedef struct NSVGshape
|
||||||
float opacity; // Opacity of the shape.
|
float opacity; // Opacity of the shape.
|
||||||
float strokeWidth; // Stroke width (scaled).
|
float strokeWidth; // Stroke width (scaled).
|
||||||
float strokeDashOffset; // Stroke dash offset (scaled).
|
float strokeDashOffset; // Stroke dash offset (scaled).
|
||||||
float strokeDashArray[8]; // Stroke dash array (scaled).
|
float strokeDashArray[8]; // Stroke dash array (scaled).
|
||||||
char strokeDashCount; // Number of dash values in dash array.
|
char strokeDashCount; // Number of dash values in dash array.
|
||||||
char strokeLineJoin; // Stroke join type.
|
char strokeLineJoin; // Stroke join type.
|
||||||
char strokeLineCap; // Stroke cap type.
|
char strokeLineCap; // Stroke cap type.
|
||||||
float miterLimit; // Miter limit
|
float miterLimit; // Miter limit
|
||||||
char fillRule; // Fill rule, see NSVGfillRule.
|
char fillRule; // Fill rule, see NSVGfillRule.
|
||||||
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
||||||
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
||||||
|
char fillGradient[64]; // Optional 'id' of fill gradient
|
||||||
|
char strokeGradient[64]; // Optional 'id' of stroke gradient
|
||||||
|
float xform[6]; // Root transformation for fill/stroke gradient
|
||||||
NSVGpath* paths; // Linked list of paths in the image.
|
NSVGpath* paths; // Linked list of paths in the image.
|
||||||
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
||||||
} NSVGshape;
|
} NSVGshape;
|
||||||
|
@ -181,16 +185,13 @@ void nsvgDelete(NSVGimage* image);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // NANOSVG_H
|
|
||||||
|
|
||||||
#ifdef NANOSVG_IMPLEMENTATION
|
#ifdef NANOSVG_IMPLEMENTATION
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
|
||||||
|
|
||||||
#define NSVG_PI (3.14159265358979323846264338327f)
|
#define NSVG_PI (3.14159265358979323846264338327f)
|
||||||
#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
|
#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
|
||||||
|
|
||||||
|
@ -227,11 +228,6 @@ static int nsvg__isdigit(char c)
|
||||||
return c >= '0' && c <= '9';
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nsvg__isnum(char c)
|
|
||||||
{
|
|
||||||
return strchr("0123456789+-.eE", c) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
|
static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
|
||||||
static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
|
static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
|
||||||
|
|
||||||
|
@ -402,7 +398,7 @@ typedef struct NSVGgradientData
|
||||||
{
|
{
|
||||||
char id[64];
|
char id[64];
|
||||||
char ref[64];
|
char ref[64];
|
||||||
char type;
|
signed char type;
|
||||||
union {
|
union {
|
||||||
NSVGlinearData linear;
|
NSVGlinearData linear;
|
||||||
NSVGradialData radial;
|
NSVGradialData radial;
|
||||||
|
@ -618,7 +614,7 @@ static void nsvg__curveBounds(float* bounds, float* curve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSVGparser* nsvg__createParser()
|
static NSVGparser* nsvg__createParser(void)
|
||||||
{
|
{
|
||||||
NSVGparser* p;
|
NSVGparser* p;
|
||||||
p = (NSVGparser*)malloc(sizeof(NSVGparser));
|
p = (NSVGparser*)malloc(sizeof(NSVGparser));
|
||||||
|
@ -738,9 +734,11 @@ static void nsvg__lineTo(NSVGparser* p, float x, float y)
|
||||||
|
|
||||||
static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
|
static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
|
||||||
{
|
{
|
||||||
nsvg__addPoint(p, cpx1, cpy1);
|
if (p->npts > 0) {
|
||||||
nsvg__addPoint(p, cpx2, cpy2);
|
nsvg__addPoint(p, cpx1, cpy1);
|
||||||
nsvg__addPoint(p, x, y);
|
nsvg__addPoint(p, cpx2, cpy2);
|
||||||
|
nsvg__addPoint(p, x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSVGattrib* nsvg__getAttr(NSVGparser* p)
|
static NSVGattrib* nsvg__getAttr(NSVGparser* p)
|
||||||
|
@ -810,7 +808,9 @@ static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig,
|
||||||
static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
|
static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
|
||||||
{
|
{
|
||||||
NSVGgradientData* grad = p->gradients;
|
NSVGgradientData* grad = p->gradients;
|
||||||
while (grad) {
|
if (id == NULL || *id == '\0')
|
||||||
|
return NULL;
|
||||||
|
while (grad != NULL) {
|
||||||
if (strcmp(grad->id, id) == 0)
|
if (strcmp(grad->id, id) == 0)
|
||||||
return grad;
|
return grad;
|
||||||
grad = grad->next;
|
grad = grad->next;
|
||||||
|
@ -818,28 +818,34 @@ static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
|
static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, float *xform, signed char* paintType)
|
||||||
{
|
{
|
||||||
NSVGattrib* attr = nsvg__getAttr(p);
|
|
||||||
NSVGgradientData* data = NULL;
|
NSVGgradientData* data = NULL;
|
||||||
NSVGgradientData* ref = NULL;
|
NSVGgradientData* ref = NULL;
|
||||||
NSVGgradientStop* stops = NULL;
|
NSVGgradientStop* stops = NULL;
|
||||||
NSVGgradient* grad;
|
NSVGgradient* grad;
|
||||||
float ox, oy, sw, sh, sl;
|
float ox, oy, sw, sh, sl;
|
||||||
int nstops = 0;
|
int nstops = 0;
|
||||||
|
int refIter;
|
||||||
|
|
||||||
data = nsvg__findGradientData(p, id);
|
data = nsvg__findGradientData(p, id);
|
||||||
if (data == NULL) return NULL;
|
if (data == NULL) return NULL;
|
||||||
|
|
||||||
// TODO: use ref to fill in all unset values too.
|
// TODO: use ref to fill in all unset values too.
|
||||||
ref = data;
|
ref = data;
|
||||||
|
refIter = 0;
|
||||||
while (ref != NULL) {
|
while (ref != NULL) {
|
||||||
|
NSVGgradientData* nextRef = NULL;
|
||||||
if (stops == NULL && ref->stops != NULL) {
|
if (stops == NULL && ref->stops != NULL) {
|
||||||
stops = ref->stops;
|
stops = ref->stops;
|
||||||
nstops = ref->nstops;
|
nstops = ref->nstops;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ref = nsvg__findGradientData(p, ref->ref);
|
nextRef = nsvg__findGradientData(p, ref->ref);
|
||||||
|
if (nextRef == ref) break; // prevent infite loops on malformed data
|
||||||
|
ref = nextRef;
|
||||||
|
refIter++;
|
||||||
|
if (refIter > 32) break; // prevent infite loops on malformed data
|
||||||
}
|
}
|
||||||
if (stops == NULL) return NULL;
|
if (stops == NULL) return NULL;
|
||||||
|
|
||||||
|
@ -888,7 +894,7 @@ static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const f
|
||||||
}
|
}
|
||||||
|
|
||||||
nsvg__xformMultiply(grad->xform, data->xform);
|
nsvg__xformMultiply(grad->xform, data->xform);
|
||||||
nsvg__xformMultiply(grad->xform, attr->xform);
|
nsvg__xformMultiply(grad->xform, xform);
|
||||||
|
|
||||||
grad->spread = data->spread;
|
grad->spread = data->spread;
|
||||||
memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
|
memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
|
||||||
|
@ -952,6 +958,9 @@ static void nsvg__addShape(NSVGparser* p)
|
||||||
memset(shape, 0, sizeof(NSVGshape));
|
memset(shape, 0, sizeof(NSVGshape));
|
||||||
|
|
||||||
memcpy(shape->id, attr->id, sizeof shape->id);
|
memcpy(shape->id, attr->id, sizeof shape->id);
|
||||||
|
memcpy(shape->fillGradient, attr->fillGradient, sizeof shape->fillGradient);
|
||||||
|
memcpy(shape->strokeGradient, attr->strokeGradient, sizeof shape->strokeGradient);
|
||||||
|
memcpy(shape->xform, attr->xform, sizeof shape->xform);
|
||||||
scale = nsvg__getAverageScale(attr->xform);
|
scale = nsvg__getAverageScale(attr->xform);
|
||||||
shape->strokeWidth = attr->strokeWidth * scale;
|
shape->strokeWidth = attr->strokeWidth * scale;
|
||||||
shape->strokeDashOffset = attr->strokeDashOffset * scale;
|
shape->strokeDashOffset = attr->strokeDashOffset * scale;
|
||||||
|
@ -987,13 +996,7 @@ static void nsvg__addShape(NSVGparser* p)
|
||||||
shape->fill.color = attr->fillColor;
|
shape->fill.color = attr->fillColor;
|
||||||
shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
|
shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
|
||||||
} else if (attr->hasFill == 2) {
|
} else if (attr->hasFill == 2) {
|
||||||
float inv[6], localBounds[4];
|
shape->fill.type = NSVG_PAINT_UNDEF;
|
||||||
nsvg__xformInverse(inv, attr->xform);
|
|
||||||
nsvg__getLocalBounds(localBounds, shape, inv);
|
|
||||||
shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
|
|
||||||
if (shape->fill.gradient == NULL) {
|
|
||||||
shape->fill.type = NSVG_PAINT_NONE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stroke
|
// Set stroke
|
||||||
|
@ -1004,12 +1007,7 @@ static void nsvg__addShape(NSVGparser* p)
|
||||||
shape->stroke.color = attr->strokeColor;
|
shape->stroke.color = attr->strokeColor;
|
||||||
shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
|
shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
|
||||||
} else if (attr->hasStroke == 2) {
|
} else if (attr->hasStroke == 2) {
|
||||||
float inv[6], localBounds[4];
|
shape->stroke.type = NSVG_PAINT_UNDEF;
|
||||||
nsvg__xformInverse(inv, attr->xform);
|
|
||||||
nsvg__getLocalBounds(localBounds, shape, inv);
|
|
||||||
shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
|
|
||||||
if (shape->stroke.gradient == NULL)
|
|
||||||
shape->stroke.type = NSVG_PAINT_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set flags
|
// Set flags
|
||||||
|
@ -1042,6 +1040,10 @@ static void nsvg__addPath(NSVGparser* p, char closed)
|
||||||
if (closed)
|
if (closed)
|
||||||
nsvg__lineTo(p, p->pts[0], p->pts[1]);
|
nsvg__lineTo(p, p->pts[0], p->pts[1]);
|
||||||
|
|
||||||
|
// Expect 1 + N*3 points (N = number of cubic bezier segments).
|
||||||
|
if ((p->npts % 3) != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
path = (NSVGpath*)malloc(sizeof(NSVGpath));
|
path = (NSVGpath*)malloc(sizeof(NSVGpath));
|
||||||
if (path == NULL) goto error;
|
if (path == NULL) goto error;
|
||||||
memset(path, 0, sizeof(NSVGpath));
|
memset(path, 0, sizeof(NSVGpath));
|
||||||
|
@ -1090,7 +1092,7 @@ static double nsvg__atof(const char* s)
|
||||||
char* cur = (char*)s;
|
char* cur = (char*)s;
|
||||||
char* end = NULL;
|
char* end = NULL;
|
||||||
double res = 0.0, sign = 1.0;
|
double res = 0.0, sign = 1.0;
|
||||||
long long intPart = 0, fracPart = 0;
|
double intPart = 0.0, fracPart = 0.0;
|
||||||
char hasIntPart = 0, hasFracPart = 0;
|
char hasIntPart = 0, hasFracPart = 0;
|
||||||
|
|
||||||
// Parse optional sign
|
// Parse optional sign
|
||||||
|
@ -1104,9 +1106,13 @@ static double nsvg__atof(const char* s)
|
||||||
// Parse integer part
|
// Parse integer part
|
||||||
if (nsvg__isdigit(*cur)) {
|
if (nsvg__isdigit(*cur)) {
|
||||||
// Parse digit sequence
|
// Parse digit sequence
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
intPart = (double)_strtoi64(cur, &end, 10);
|
||||||
|
#else
|
||||||
intPart = (double)strtoll(cur, &end, 10);
|
intPart = (double)strtoll(cur, &end, 10);
|
||||||
|
#endif
|
||||||
if (cur != end) {
|
if (cur != end) {
|
||||||
res = (double)intPart;
|
res = intPart;
|
||||||
hasIntPart = 1;
|
hasIntPart = 1;
|
||||||
cur = end;
|
cur = end;
|
||||||
}
|
}
|
||||||
|
@ -1117,9 +1123,13 @@ static double nsvg__atof(const char* s)
|
||||||
cur++; // Skip '.'
|
cur++; // Skip '.'
|
||||||
if (nsvg__isdigit(*cur)) {
|
if (nsvg__isdigit(*cur)) {
|
||||||
// Parse digit sequence
|
// Parse digit sequence
|
||||||
fracPart = strtoll(cur, &end, 10);
|
#ifdef _MSC_VER
|
||||||
|
fracPart = (double)_strtoi64(cur, &end, 10);
|
||||||
|
#else
|
||||||
|
fracPart = (double)strtoll(cur, &end, 10);
|
||||||
|
#endif
|
||||||
if (cur != end) {
|
if (cur != end) {
|
||||||
res += (double)fracPart / pow(10.0, (double)(end - cur));
|
res += fracPart / pow(10.0, (double)(end - cur));
|
||||||
hasFracPart = 1;
|
hasFracPart = 1;
|
||||||
cur = end;
|
cur = end;
|
||||||
}
|
}
|
||||||
|
@ -1132,11 +1142,11 @@ static double nsvg__atof(const char* s)
|
||||||
|
|
||||||
// Parse optional exponent
|
// Parse optional exponent
|
||||||
if (*cur == 'e' || *cur == 'E') {
|
if (*cur == 'e' || *cur == 'E') {
|
||||||
int expPart = 0;
|
double expPart = 0.0;
|
||||||
cur++; // skip 'E'
|
cur++; // skip 'E'
|
||||||
expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
|
expPart = (double)strtol(cur, &end, 10); // Parse digit sequence with sign
|
||||||
if (cur != end) {
|
if (cur != end) {
|
||||||
res *= pow(10.0, (double)expPart);
|
res *= pow(10.0, expPart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,7 +1180,7 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// exponent
|
// exponent
|
||||||
if (*s == 'e' || *s == 'E') {
|
if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
|
||||||
if (i < last) it[i++] = *s;
|
if (i < last) it[i++] = *s;
|
||||||
s++;
|
s++;
|
||||||
if (*s == '-' || *s == '+') {
|
if (*s == '-' || *s == '+') {
|
||||||
|
@ -1187,6 +1197,19 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* nsvg__getNextPathItemWhenArcFlag(const char* s, char* it)
|
||||||
|
{
|
||||||
|
it[0] = '\0';
|
||||||
|
while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
|
||||||
|
if (!*s) return s;
|
||||||
|
if (*s == '0' || *s == '1') {
|
||||||
|
it[0] = *s++;
|
||||||
|
it[1] = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* nsvg__getNextPathItem(const char* s, char* it)
|
static const char* nsvg__getNextPathItem(const char* s, char* it)
|
||||||
{
|
{
|
||||||
it[0] = '\0';
|
it[0] = '\0';
|
||||||
|
@ -1207,35 +1230,66 @@ static const char* nsvg__getNextPathItem(const char* s, char* it)
|
||||||
|
|
||||||
static unsigned int nsvg__parseColorHex(const char* str)
|
static unsigned int nsvg__parseColorHex(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int c = 0, r = 0, g = 0, b = 0;
|
unsigned int r=0, g=0, b=0;
|
||||||
int n = 0;
|
if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex
|
||||||
str++; // skip #
|
return NSVG_RGB(r, g, b);
|
||||||
// Calculate number of characters.
|
if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa
|
||||||
while(str[n] && !nsvg__isspace(str[n]))
|
return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), ..
|
||||||
n++;
|
return NSVG_RGB(128, 128, 128);
|
||||||
if (n == 6) {
|
|
||||||
sscanf(str, "%x", &c);
|
|
||||||
} else if (n == 3) {
|
|
||||||
sscanf(str, "%x", &c);
|
|
||||||
c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
|
|
||||||
c |= c<<4;
|
|
||||||
}
|
|
||||||
r = (c >> 16) & 0xff;
|
|
||||||
g = (c >> 8) & 0xff;
|
|
||||||
b = c & 0xff;
|
|
||||||
return NSVG_RGB(r,g,b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters).
|
||||||
|
// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors
|
||||||
|
// for backwards compatibility. Note: other image viewers return black instead.
|
||||||
|
|
||||||
static unsigned int nsvg__parseColorRGB(const char* str)
|
static unsigned int nsvg__parseColorRGB(const char* str)
|
||||||
{
|
{
|
||||||
int r = -1, g = -1, b = -1;
|
int i;
|
||||||
char s1[32]="", s2[32]="";
|
unsigned int rgbi[3];
|
||||||
sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
|
float rgbf[3];
|
||||||
if (strchr(s1, '%')) {
|
// try decimal integers first
|
||||||
return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
|
if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) {
|
||||||
} else {
|
// integers failed, try percent values (float, locale independent)
|
||||||
return NSVG_RGB(r,g,b);
|
const char delimiter[3] = {',', ',', ')'};
|
||||||
|
str += 4; // skip "rgb("
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces
|
||||||
|
if (*str == '+') str++; // skip '+' (don't allow '-')
|
||||||
|
if (!*str) break;
|
||||||
|
rgbf[i] = nsvg__atof(str);
|
||||||
|
|
||||||
|
// Note 1: it would be great if nsvg__atof() returned how many
|
||||||
|
// bytes it consumed but it doesn't. We need to skip the number,
|
||||||
|
// the '%' character, spaces, and the delimiter ',' or ')'.
|
||||||
|
|
||||||
|
// Note 2: The following code does not allow values like "33.%",
|
||||||
|
// i.e. a decimal point w/o fractional part, but this is consistent
|
||||||
|
// with other image viewers, e.g. firefox, chrome, eog, gimp.
|
||||||
|
|
||||||
|
while (*str && nsvg__isdigit(*str)) str++; // skip integer part
|
||||||
|
if (*str == '.') {
|
||||||
|
str++;
|
||||||
|
if (!nsvg__isdigit(*str)) break; // error: no digit after '.'
|
||||||
|
while (*str && nsvg__isdigit(*str)) str++; // skip fractional part
|
||||||
|
}
|
||||||
|
if (*str == '%') str++; else break;
|
||||||
|
while (nsvg__isspace(*str)) str++;
|
||||||
|
if (*str == delimiter[i]) str++;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
if (i == 3) {
|
||||||
|
rgbi[0] = roundf(rgbf[0] * 2.55f);
|
||||||
|
rgbi[1] = roundf(rgbf[1] * 2.55f);
|
||||||
|
rgbi[2] = roundf(rgbf[2] * 2.55f);
|
||||||
|
} else {
|
||||||
|
rgbi[0] = rgbi[1] = rgbi[2] = 128;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// clip values as the CSS spec requires
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (rgbi[i] > 255) rgbi[i] = 255;
|
||||||
|
}
|
||||||
|
return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct NSVGNamedColor {
|
typedef struct NSVGNamedColor {
|
||||||
|
@ -1460,6 +1514,15 @@ static int nsvg__parseUnits(const char* units)
|
||||||
return NSVG_UNITS_USER;
|
return NSVG_UNITS_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nsvg__isCoordinate(const char* s)
|
||||||
|
{
|
||||||
|
// optional sign
|
||||||
|
if (*s == '-' || *s == '+')
|
||||||
|
s++;
|
||||||
|
// must have at least one digit, or start by a dot
|
||||||
|
return (nsvg__isdigit(*s) || *s == '.');
|
||||||
|
}
|
||||||
|
|
||||||
static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
|
static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
|
||||||
{
|
{
|
||||||
NSVGcoordinate coord = {0, NSVG_UNITS_USER};
|
NSVGcoordinate coord = {0, NSVG_UNITS_USER};
|
||||||
|
@ -1599,25 +1662,32 @@ static int nsvg__parseRotate(float* xform, const char* str)
|
||||||
static void nsvg__parseTransform(float* xform, const char* str)
|
static void nsvg__parseTransform(float* xform, const char* str)
|
||||||
{
|
{
|
||||||
float t[6];
|
float t[6];
|
||||||
|
int len;
|
||||||
nsvg__xformIdentity(xform);
|
nsvg__xformIdentity(xform);
|
||||||
while (*str)
|
while (*str)
|
||||||
{
|
{
|
||||||
if (strncmp(str, "matrix", 6) == 0)
|
if (strncmp(str, "matrix", 6) == 0)
|
||||||
str += nsvg__parseMatrix(t, str);
|
len = nsvg__parseMatrix(t, str);
|
||||||
else if (strncmp(str, "translate", 9) == 0)
|
else if (strncmp(str, "translate", 9) == 0)
|
||||||
str += nsvg__parseTranslate(t, str);
|
len = nsvg__parseTranslate(t, str);
|
||||||
else if (strncmp(str, "scale", 5) == 0)
|
else if (strncmp(str, "scale", 5) == 0)
|
||||||
str += nsvg__parseScale(t, str);
|
len = nsvg__parseScale(t, str);
|
||||||
else if (strncmp(str, "rotate", 6) == 0)
|
else if (strncmp(str, "rotate", 6) == 0)
|
||||||
str += nsvg__parseRotate(t, str);
|
len = nsvg__parseRotate(t, str);
|
||||||
else if (strncmp(str, "skewX", 5) == 0)
|
else if (strncmp(str, "skewX", 5) == 0)
|
||||||
str += nsvg__parseSkewX(t, str);
|
len = nsvg__parseSkewX(t, str);
|
||||||
else if (strncmp(str, "skewY", 5) == 0)
|
else if (strncmp(str, "skewY", 5) == 0)
|
||||||
str += nsvg__parseSkewY(t, str);
|
len = nsvg__parseSkewY(t, str);
|
||||||
else{
|
else{
|
||||||
++str;
|
++str;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (len != 0) {
|
||||||
|
str += len;
|
||||||
|
} else {
|
||||||
|
++str;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
nsvg__xformPremultiply(xform, t);
|
nsvg__xformPremultiply(xform, t);
|
||||||
}
|
}
|
||||||
|
@ -1627,9 +1697,9 @@ static void nsvg__parseUrl(char* id, const char* str)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
str += 4; // "url(";
|
str += 4; // "url(";
|
||||||
if (*str == '#')
|
if (*str && *str == '#')
|
||||||
str++;
|
str++;
|
||||||
while (i < 63 && *str != ')') {
|
while (i < 63 && *str && *str != ')') {
|
||||||
id[i] = *str++;
|
id[i] = *str++;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -1878,8 +1948,11 @@ static int nsvg__getArgsPerElement(char cmd)
|
||||||
case 'a':
|
case 'a':
|
||||||
case 'A':
|
case 'A':
|
||||||
return 7;
|
return 7;
|
||||||
|
case 'z':
|
||||||
|
case 'Z':
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
|
static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
|
||||||
|
@ -2160,7 +2233,12 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args,
|
||||||
// The loop assumes an iteration per end point (including start and end), this +1.
|
// The loop assumes an iteration per end point (including start and end), this +1.
|
||||||
ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
|
ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
|
||||||
hda = (da / (float)ndivs) / 2.0f;
|
hda = (da / (float)ndivs) / 2.0f;
|
||||||
kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
|
// Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite)
|
||||||
|
if ((hda < 1e-3f) && (hda > -1e-3f))
|
||||||
|
hda *= 0.5f;
|
||||||
|
else
|
||||||
|
hda = (1.0f - cosf(hda)) / sinf(hda);
|
||||||
|
kappa = fabsf(4.0f / 3.0f * hda);
|
||||||
if (da < 0.0f)
|
if (da < 0.0f)
|
||||||
kappa = -kappa;
|
kappa = -kappa;
|
||||||
|
|
||||||
|
@ -2189,6 +2267,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
float args[10];
|
float args[10];
|
||||||
int nargs;
|
int nargs;
|
||||||
int rargs = 0;
|
int rargs = 0;
|
||||||
|
char initPoint;
|
||||||
float cpx, cpy, cpx2, cpy2;
|
float cpx, cpy, cpx2, cpy2;
|
||||||
const char* tmp[4];
|
const char* tmp[4];
|
||||||
char closedFlag;
|
char closedFlag;
|
||||||
|
@ -2211,13 +2290,18 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
nsvg__resetPath(p);
|
nsvg__resetPath(p);
|
||||||
cpx = 0; cpy = 0;
|
cpx = 0; cpy = 0;
|
||||||
cpx2 = 0; cpy2 = 0;
|
cpx2 = 0; cpy2 = 0;
|
||||||
|
initPoint = 0;
|
||||||
closedFlag = 0;
|
closedFlag = 0;
|
||||||
nargs = 0;
|
nargs = 0;
|
||||||
|
|
||||||
while (*s) {
|
while (*s) {
|
||||||
s = nsvg__getNextPathItem(s, item);
|
item[0] = '\0';
|
||||||
|
if ((cmd == 'A' || cmd == 'a') && (nargs == 3 || nargs == 4))
|
||||||
|
s = nsvg__getNextPathItemWhenArcFlag(s, item);
|
||||||
|
if (!*item)
|
||||||
|
s = nsvg__getNextPathItem(s, item);
|
||||||
if (!*item) break;
|
if (!*item) break;
|
||||||
if (nsvg__isnum(item[0])) {
|
if (cmd != '\0' && nsvg__isCoordinate(item)) {
|
||||||
if (nargs < 10)
|
if (nargs < 10)
|
||||||
args[nargs++] = (float)nsvg__atof(item);
|
args[nargs++] = (float)nsvg__atof(item);
|
||||||
if (nargs >= rargs) {
|
if (nargs >= rargs) {
|
||||||
|
@ -2230,6 +2314,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
cmd = (cmd == 'm') ? 'l' : 'L';
|
cmd = (cmd == 'm') ? 'l' : 'L';
|
||||||
rargs = nsvg__getArgsPerElement(cmd);
|
rargs = nsvg__getArgsPerElement(cmd);
|
||||||
cpx2 = cpx; cpy2 = cpy;
|
cpx2 = cpx; cpy2 = cpy;
|
||||||
|
initPoint = 1;
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
case 'L':
|
case 'L':
|
||||||
|
@ -2279,7 +2364,6 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd = item[0];
|
cmd = item[0];
|
||||||
rargs = nsvg__getArgsPerElement(cmd);
|
|
||||||
if (cmd == 'M' || cmd == 'm') {
|
if (cmd == 'M' || cmd == 'm') {
|
||||||
// Commit path.
|
// Commit path.
|
||||||
if (p->npts > 0)
|
if (p->npts > 0)
|
||||||
|
@ -2288,7 +2372,11 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
nsvg__resetPath(p);
|
nsvg__resetPath(p);
|
||||||
closedFlag = 0;
|
closedFlag = 0;
|
||||||
nargs = 0;
|
nargs = 0;
|
||||||
} else if (cmd == 'Z' || cmd == 'z') {
|
} else if (initPoint == 0) {
|
||||||
|
// Do not allow other commands until initial point has been set (moveTo called once).
|
||||||
|
cmd = '\0';
|
||||||
|
}
|
||||||
|
if (cmd == 'Z' || cmd == 'z') {
|
||||||
closedFlag = 1;
|
closedFlag = 1;
|
||||||
// Commit path.
|
// Commit path.
|
||||||
if (p->npts > 0) {
|
if (p->npts > 0) {
|
||||||
|
@ -2304,6 +2392,12 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
closedFlag = 0;
|
closedFlag = 0;
|
||||||
nargs = 0;
|
nargs = 0;
|
||||||
}
|
}
|
||||||
|
rargs = nsvg__getArgsPerElement(cmd);
|
||||||
|
if (rargs == -1) {
|
||||||
|
// Command not recognized
|
||||||
|
cmd = '\0';
|
||||||
|
rargs = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Commit path.
|
// Commit path.
|
||||||
|
@ -2550,7 +2644,7 @@ static void nsvg__parseSVG(NSVGparser* p, const char** attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
|
static void nsvg__parseGradient(NSVGparser* p, const char** attr, signed char type)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
|
NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
|
||||||
|
@ -2875,6 +2969,36 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nsvg__createGradients(NSVGparser* p)
|
||||||
|
{
|
||||||
|
NSVGshape* shape;
|
||||||
|
|
||||||
|
for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
|
||||||
|
if (shape->fill.type == NSVG_PAINT_UNDEF) {
|
||||||
|
if (shape->fillGradient[0] != '\0') {
|
||||||
|
float inv[6], localBounds[4];
|
||||||
|
nsvg__xformInverse(inv, shape->xform);
|
||||||
|
nsvg__getLocalBounds(localBounds, shape, inv);
|
||||||
|
shape->fill.gradient = nsvg__createGradient(p, shape->fillGradient, localBounds, shape->xform, &shape->fill.type);
|
||||||
|
}
|
||||||
|
if (shape->fill.type == NSVG_PAINT_UNDEF) {
|
||||||
|
shape->fill.type = NSVG_PAINT_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shape->stroke.type == NSVG_PAINT_UNDEF) {
|
||||||
|
if (shape->strokeGradient[0] != '\0') {
|
||||||
|
float inv[6], localBounds[4];
|
||||||
|
nsvg__xformInverse(inv, shape->xform);
|
||||||
|
nsvg__getLocalBounds(localBounds, shape, inv);
|
||||||
|
shape->stroke.gradient = nsvg__createGradient(p, shape->strokeGradient, localBounds, shape->xform, &shape->stroke.type);
|
||||||
|
}
|
||||||
|
if (shape->stroke.type == NSVG_PAINT_UNDEF) {
|
||||||
|
shape->stroke.type = NSVG_PAINT_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
||||||
{
|
{
|
||||||
NSVGparser* p;
|
NSVGparser* p;
|
||||||
|
@ -2888,6 +3012,9 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
||||||
|
|
||||||
nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
|
nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
|
||||||
|
|
||||||
|
// Create gradients after all definitions have been parsed
|
||||||
|
nsvg__createGradients(p);
|
||||||
|
|
||||||
// Scale to viewBox
|
// Scale to viewBox
|
||||||
nsvg__scaleToViewbox(p, units);
|
nsvg__scaleToViewbox(p, units);
|
||||||
|
|
||||||
|
@ -2899,8 +3026,6 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/nowide/cstdio.hpp>
|
|
||||||
|
|
||||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||||
{
|
{
|
||||||
FILE* fp = NULL;
|
FILE* fp = NULL;
|
||||||
|
@ -2908,8 +3033,8 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||||
char* data = NULL;
|
char* data = NULL;
|
||||||
NSVGimage* image = NULL;
|
NSVGimage* image = NULL;
|
||||||
|
|
||||||
fp = boost::nowide::fopen(filename, "rb");
|
fp = fopen(filename, "rb");
|
||||||
if (!fp) goto error;
|
if (!fp) goto error;
|
||||||
fseek(fp, 0, SEEK_END);
|
fseek(fp, 0, SEEK_END);
|
||||||
size = ftell(fp);
|
size = ftell(fp);
|
||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
@ -2918,9 +3043,9 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||||
if (fread(data, 1, size, fp) != size) goto error;
|
if (fread(data, 1, size, fp) != size) goto error;
|
||||||
data[size] = '\0'; // Must be null terminated.
|
data[size] = '\0'; // Must be null terminated.
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
image = nsvgParse(data, units, dpi);
|
image = nsvgParse(data, units, dpi);
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -2976,4 +3101,6 @@ void nsvgDelete(NSVGimage* image)
|
||||||
free(image);
|
free(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // NANOSVG_IMPLEMENTATION
|
||||||
|
|
||||||
|
#endif // NANOSVG_H
|
||||||
|
|
|
@ -22,9 +22,17 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Modified by FLTK to support non-square X,Y axes scaling.
|
||||||
|
*
|
||||||
|
* Added: nsvgRasterizeXY()
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef NANOSVGRAST_H
|
#ifndef NANOSVGRAST_H
|
||||||
#define NANOSVGRAST_H
|
#define NANOSVGRAST_H
|
||||||
|
|
||||||
|
#include "nanosvg.h"
|
||||||
|
|
||||||
#ifndef NANOSVGRAST_CPLUSPLUS
|
#ifndef NANOSVGRAST_CPLUSPLUS
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -44,16 +52,19 @@ typedef struct NSVGrasterizer NSVGrasterizer;
|
||||||
unsigned char* img = malloc(w*h*4);
|
unsigned char* img = malloc(w*h*4);
|
||||||
// Rasterize
|
// Rasterize
|
||||||
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
|
nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
|
||||||
|
|
||||||
|
// For non-square X,Y scaling, use
|
||||||
|
nsvgRasterizeXY(rast, image, 0,0,1,1, img, w, h, w*4);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Allocated rasterizer context.
|
// Allocated rasterizer context.
|
||||||
NSVGrasterizer* nsvgCreateRasterizer();
|
NSVGrasterizer* nsvgCreateRasterizer(void);
|
||||||
|
|
||||||
// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
|
// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
|
||||||
// r - pointer to rasterizer context
|
// r - pointer to rasterizer context
|
||||||
// image - pointer to image to rasterize
|
// image - pointer to image to rasterize
|
||||||
// tx,ty - image offset (applied after scaling)
|
// tx,ty - image offset (applied after scaling)
|
||||||
// scale - image scale
|
// scale - image scale (assumes square aspect ratio)
|
||||||
// dst - pointer to destination image data, 4 bytes per pixel (RGBA)
|
// dst - pointer to destination image data, 4 bytes per pixel (RGBA)
|
||||||
// w - width of the image to render
|
// w - width of the image to render
|
||||||
// h - height of the image to render
|
// h - height of the image to render
|
||||||
|
@ -62,6 +73,12 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||||
NSVGimage* image, float tx, float ty, float scale,
|
NSVGimage* image, float tx, float ty, float scale,
|
||||||
unsigned char* dst, int w, int h, int stride);
|
unsigned char* dst, int w, int h, int stride);
|
||||||
|
|
||||||
|
// As above, but allow X and Y axes to scale independently for non-square aspects
|
||||||
|
void nsvgRasterizeXY(NSVGrasterizer* r,
|
||||||
|
NSVGimage* image, float tx, float ty,
|
||||||
|
float sx, float sy,
|
||||||
|
unsigned char* dst, int w, int h, int stride);
|
||||||
|
|
||||||
// Deletes rasterizer context.
|
// Deletes rasterizer context.
|
||||||
void nsvgDeleteRasterizer(NSVGrasterizer*);
|
void nsvgDeleteRasterizer(NSVGrasterizer*);
|
||||||
|
|
||||||
|
@ -72,11 +89,11 @@ void nsvgDeleteRasterizer(NSVGrasterizer*);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // NANOSVGRAST_H
|
|
||||||
|
|
||||||
#ifdef NANOSVGRAST_IMPLEMENTATION
|
#ifdef NANOSVGRAST_IMPLEMENTATION
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define NSVG__SUBSAMPLES 5
|
#define NSVG__SUBSAMPLES 5
|
||||||
#define NSVG__FIXSHIFT 10
|
#define NSVG__FIXSHIFT 10
|
||||||
|
@ -112,7 +129,7 @@ typedef struct NSVGmemPage {
|
||||||
} NSVGmemPage;
|
} NSVGmemPage;
|
||||||
|
|
||||||
typedef struct NSVGcachedPaint {
|
typedef struct NSVGcachedPaint {
|
||||||
char type;
|
signed char type;
|
||||||
char spread;
|
char spread;
|
||||||
float xform[6];
|
float xform[6];
|
||||||
unsigned int colors[256];
|
unsigned int colors[256];
|
||||||
|
@ -148,7 +165,7 @@ struct NSVGrasterizer
|
||||||
int width, height, stride;
|
int width, height, stride;
|
||||||
};
|
};
|
||||||
|
|
||||||
NSVGrasterizer* nsvgCreateRasterizer()
|
NSVGrasterizer* nsvgCreateRasterizer(void)
|
||||||
{
|
{
|
||||||
NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
|
NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
|
||||||
if (r == NULL) goto error;
|
if (r == NULL) goto error;
|
||||||
|
@ -368,7 +385,7 @@ static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
||||||
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
|
nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
NSVGpath* path;
|
NSVGpath* path;
|
||||||
|
@ -376,13 +393,13 @@ static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
||||||
for (path = shape->paths; path != NULL; path = path->next) {
|
for (path = shape->paths; path != NULL; path = path->next) {
|
||||||
r->npoints = 0;
|
r->npoints = 0;
|
||||||
// Flatten path
|
// Flatten path
|
||||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
|
nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0);
|
||||||
for (i = 0; i < path->npts-1; i += 3) {
|
for (i = 0; i < path->npts-1; i += 3) {
|
||||||
float* p = &path->pts[i*2];
|
float* p = &path->pts[i*2];
|
||||||
nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
|
nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, 0);
|
||||||
}
|
}
|
||||||
// Close path
|
// Close path
|
||||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
|
nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0);
|
||||||
// Build edges
|
// Build edges
|
||||||
for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
|
for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
|
||||||
nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
|
nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
|
||||||
|
@ -732,7 +749,7 @@ static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
|
static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy)
|
||||||
{
|
{
|
||||||
int i, j, closed;
|
int i, j, closed;
|
||||||
NSVGpath* path;
|
NSVGpath* path;
|
||||||
|
@ -740,15 +757,16 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
||||||
float miterLimit = shape->miterLimit;
|
float miterLimit = shape->miterLimit;
|
||||||
int lineJoin = shape->strokeLineJoin;
|
int lineJoin = shape->strokeLineJoin;
|
||||||
int lineCap = shape->strokeLineCap;
|
int lineCap = shape->strokeLineCap;
|
||||||
float lineWidth = shape->strokeWidth * scale;
|
const float sw = (sx + sy) / 2; // average scaling factor
|
||||||
|
const float lineWidth = shape->strokeWidth * sw; // FIXME (?)
|
||||||
|
|
||||||
for (path = shape->paths; path != NULL; path = path->next) {
|
for (path = shape->paths; path != NULL; path = path->next) {
|
||||||
// Flatten path
|
// Flatten path
|
||||||
r->npoints = 0;
|
r->npoints = 0;
|
||||||
nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
|
nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, NSVG_PT_CORNER);
|
||||||
for (i = 0; i < path->npts-1; i += 3) {
|
for (i = 0; i < path->npts-1; i += 3) {
|
||||||
float* p = &path->pts[i*2];
|
float* p = &path->pts[i*2];
|
||||||
nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
|
nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, NSVG_PT_CORNER);
|
||||||
}
|
}
|
||||||
if (r->npoints < 2)
|
if (r->npoints < 2)
|
||||||
continue;
|
continue;
|
||||||
|
@ -794,7 +812,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
||||||
dashOffset -= shape->strokeDashArray[idash];
|
dashOffset -= shape->strokeDashArray[idash];
|
||||||
idash = (idash + 1) % shape->strokeDashCount;
|
idash = (idash + 1) % shape->strokeDashCount;
|
||||||
}
|
}
|
||||||
dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
|
dashLen = (shape->strokeDashArray[idash] - dashOffset) * sw;
|
||||||
|
|
||||||
for (j = 1; j < r->npoints2; ) {
|
for (j = 1; j < r->npoints2; ) {
|
||||||
float dx = r->points2[j].x - cur.x;
|
float dx = r->points2[j].x - cur.x;
|
||||||
|
@ -816,7 +834,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
||||||
// Advance dash pattern
|
// Advance dash pattern
|
||||||
dashState = !dashState;
|
dashState = !dashState;
|
||||||
idash = (idash+1) % shape->strokeDashCount;
|
idash = (idash+1) % shape->strokeDashCount;
|
||||||
dashLen = shape->strokeDashArray[idash] * scale;
|
dashLen = shape->strokeDashArray[idash] * sw;
|
||||||
// Restart
|
// Restart
|
||||||
cur.x = x;
|
cur.x = x;
|
||||||
cur.y = y;
|
cur.y = y;
|
||||||
|
@ -956,7 +974,7 @@ static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a
|
||||||
|
|
||||||
static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char 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);
|
return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
|
static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
|
||||||
|
@ -985,7 +1003,7 @@ static inline int nsvg__div255(int x)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
|
static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
|
||||||
float tx, float ty, float scale, NSVGcachedPaint* cache)
|
float tx, float ty, float sx, float sy, NSVGcachedPaint* cache)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (cache->type == NSVG_PAINT_COLOR) {
|
if (cache->type == NSVG_PAINT_COLOR) {
|
||||||
|
@ -1026,9 +1044,9 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
|
||||||
int i, cr, cg, cb, ca;
|
int i, cr, cg, cb, ca;
|
||||||
unsigned int c;
|
unsigned int c;
|
||||||
|
|
||||||
fx = ((float)x - tx) / scale;
|
fx = ((float)x - tx) / sx;
|
||||||
fy = ((float)y - ty) / scale;
|
fy = ((float)y - ty) / sy;
|
||||||
dx = 1.0f / scale;
|
dx = 1.0f / sx;
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
int r,g,b,a,ia;
|
int r,g,b,a,ia;
|
||||||
|
@ -1071,9 +1089,9 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
|
||||||
int i, cr, cg, cb, ca;
|
int i, cr, cg, cb, ca;
|
||||||
unsigned int c;
|
unsigned int c;
|
||||||
|
|
||||||
fx = ((float)x - tx) / scale;
|
fx = ((float)x - tx) / sx;
|
||||||
fy = ((float)y - ty) / scale;
|
fy = ((float)y - ty) / sy;
|
||||||
dx = 1.0f / scale;
|
dx = 1.0f / sx;
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
int r,g,b,a,ia;
|
int r,g,b,a,ia;
|
||||||
|
@ -1112,7 +1130,7 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
|
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float sx, float sy, NSVGcachedPaint* cache, char fillRule)
|
||||||
{
|
{
|
||||||
NSVGactiveEdge *active = NULL;
|
NSVGactiveEdge *active = NULL;
|
||||||
int y, s;
|
int y, s;
|
||||||
|
@ -1194,7 +1212,7 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
|
||||||
if (xmin < 0) xmin = 0;
|
if (xmin < 0) xmin = 0;
|
||||||
if (xmax > r->width-1) xmax = r->width-1;
|
if (xmax > r->width-1) xmax = r->width-1;
|
||||||
if (xmin <= xmax) {
|
if (xmin <= xmax) {
|
||||||
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
|
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, sx, sy, cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1362,8 +1380,9 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void nsvgRasterize(NSVGrasterizer* r,
|
void nsvgRasterizeXY(NSVGrasterizer* r,
|
||||||
NSVGimage* image, float tx, float ty, float scale,
|
NSVGimage* image, float tx, float ty,
|
||||||
|
float sx, float sy,
|
||||||
unsigned char* dst, int w, int h, int stride)
|
unsigned char* dst, int w, int h, int stride)
|
||||||
{
|
{
|
||||||
NSVGshape *shape = NULL;
|
NSVGshape *shape = NULL;
|
||||||
|
@ -1394,7 +1413,7 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||||
r->freelist = NULL;
|
r->freelist = NULL;
|
||||||
r->nedges = 0;
|
r->nedges = 0;
|
||||||
|
|
||||||
nsvg__flattenShape(r, shape, scale);
|
nsvg__flattenShape(r, shape, sx, sy);
|
||||||
|
|
||||||
// Scale and translate edges
|
// Scale and translate edges
|
||||||
for (i = 0; i < r->nedges; i++) {
|
for (i = 0; i < r->nedges; i++) {
|
||||||
|
@ -1406,19 +1425,20 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rasterize edges
|
// Rasterize edges
|
||||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
if (r->nedges != 0)
|
||||||
|
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
||||||
|
|
||||||
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
||||||
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
|
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
|
||||||
|
|
||||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
|
nsvg__rasterizeSortedEdges(r, tx,ty, sx, sy, &cache, shape->fillRule);
|
||||||
}
|
}
|
||||||
if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
|
if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * sx) > 0.01f) {
|
||||||
nsvg__resetPool(r);
|
nsvg__resetPool(r);
|
||||||
r->freelist = NULL;
|
r->freelist = NULL;
|
||||||
r->nedges = 0;
|
r->nedges = 0;
|
||||||
|
|
||||||
nsvg__flattenShapeStroke(r, shape, scale);
|
nsvg__flattenShapeStroke(r, shape, sx, sy);
|
||||||
|
|
||||||
// dumpEdges(r, "edge.svg");
|
// dumpEdges(r, "edge.svg");
|
||||||
|
|
||||||
|
@ -1432,12 +1452,13 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rasterize edges
|
// Rasterize edges
|
||||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
if (r->nedges != 0)
|
||||||
|
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
||||||
|
|
||||||
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
|
||||||
nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
|
nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
|
||||||
|
|
||||||
nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
|
nsvg__rasterizeSortedEdges(r, tx,ty,sx, sy, &cache, NSVG_FILLRULE_NONZERO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1449,4 +1470,13 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||||
r->stride = 0;
|
r->stride = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
void nsvgRasterize(NSVGrasterizer* r,
|
||||||
|
NSVGimage* image, float tx, float ty, float scale,
|
||||||
|
unsigned char* dst, int w, int h, int stride)
|
||||||
|
{
|
||||||
|
nsvgRasterizeXY(r,image, tx, ty, scale, scale, dst, w, h, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NANOSVGRAST_IMPLEMENTATION
|
||||||
|
|
||||||
|
#endif // NANOSVGRAST_H
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
//BBS:add i18n
|
//BBS:add i18n
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
//BBS: add fstream for debug output
|
//BBS: add fstream for debug output
|
||||||
|
@ -416,7 +420,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
||||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||||
glsafe(::glGenTextures(1, &m_id));
|
glsafe(::glGenTextures(1, &m_id));
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
if (compress && OpenGLManager::are_compressed_textures_supported())
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
else
|
else
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
|
@ -534,7 +538,7 @@ bool GLTexture::generate_from_text(const std::string &text_str, wxFont &font, wx
|
||||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||||
glsafe(::glGenTextures(1, &m_id));
|
glsafe(::glGenTextures(1, &m_id));
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
||||||
if (GLEW_EXT_texture_compression_s3tc)
|
if (OpenGLManager::are_compressed_textures_supported())
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
else
|
else
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
|
@ -635,7 +639,7 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont&
|
||||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||||
glsafe(::glGenTextures(1, &m_id));
|
glsafe(::glGenTextures(1, &m_id));
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
||||||
if (GLEW_EXT_texture_compression_s3tc)
|
if (OpenGLManager::are_compressed_textures_supported())
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
else
|
else
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
|
@ -695,9 +699,29 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
|
||||||
glsafe(::glDisable(GL_BLEND));
|
glsafe(::glDisable(GL_BLEND));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h)
|
||||||
|
{
|
||||||
|
auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; };
|
||||||
|
auto upper_power_of_two = [](int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; };
|
||||||
|
|
||||||
|
int new_w = std::max(w, h);
|
||||||
|
if (!is_power_of_two(new_w))
|
||||||
|
new_w = upper_power_of_two(new_w);
|
||||||
|
|
||||||
|
while (new_w > max_size_px) {
|
||||||
|
new_w /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int new_h = new_w;
|
||||||
|
const bool ret = (new_w != w || new_h != h);
|
||||||
|
w = new_w;
|
||||||
|
h = new_h;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||||
{
|
{
|
||||||
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
|
const bool compression_enabled = (compression_type != None) && OpenGLManager::are_compressed_textures_supported();
|
||||||
|
|
||||||
// Load a PNG with an alpha channel.
|
// Load a PNG with an alpha channel.
|
||||||
wxImage image;
|
wxImage image;
|
||||||
|
@ -711,6 +735,11 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
||||||
|
|
||||||
bool requires_rescale = false;
|
bool requires_rescale = false;
|
||||||
|
|
||||||
|
if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures()) {
|
||||||
|
if (to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), OpenGLManager::get_gl_info().get_max_tex_size(), m_width, m_height))
|
||||||
|
requires_rescale = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (compression_enabled && compression_type == MultiThreaded) {
|
if (compression_enabled && compression_type == MultiThreaded) {
|
||||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||||
int width_rem = m_width % 4;
|
int width_rem = m_width % 4;
|
||||||
|
@ -837,7 +866,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
||||||
|
|
||||||
m_source = filename;
|
m_source = filename;
|
||||||
|
|
||||||
if (compression_enabled && compression_type == MultiThreaded)
|
if (compression_type == MultiThreaded)
|
||||||
// start asynchronous compression
|
// start asynchronous compression
|
||||||
m_compressor.start_compressing();
|
m_compressor.start_compressing();
|
||||||
|
|
||||||
|
@ -846,7 +875,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
||||||
|
|
||||||
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
||||||
{
|
{
|
||||||
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
|
const bool compression_enabled = compress && OpenGLManager::are_compressed_textures_supported();
|
||||||
|
|
||||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||||
if (image == nullptr) {
|
if (image == nullptr) {
|
||||||
|
@ -854,11 +883,17 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float scale = (float)max_size_px / std::max(image->width, image->height);
|
const float scale = (float)max_size_px / std::max(image->width, image->height);
|
||||||
|
|
||||||
m_width = (int)(scale * image->width);
|
m_width = (int)(scale * image->width);
|
||||||
m_height = (int)(scale * image->height);
|
m_height = (int)(scale * image->height);
|
||||||
|
|
||||||
|
if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures())
|
||||||
|
to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), max_size_px, m_width, m_height);
|
||||||
|
|
||||||
|
float scale_w = (float)m_width / image->width;
|
||||||
|
float scale_h = (float)m_height / image->height;
|
||||||
|
|
||||||
if (compression_enabled) {
|
if (compression_enabled) {
|
||||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||||
int width_rem = m_width % 4;
|
int width_rem = m_width % 4;
|
||||||
|
@ -871,7 +906,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||||
m_height += (4 - height_rem);
|
m_height += (4 - height_rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
int n_pixels = m_width * m_height;
|
const int n_pixels = m_width * m_height;
|
||||||
|
|
||||||
if (n_pixels <= 0) {
|
if (n_pixels <= 0) {
|
||||||
reset();
|
reset();
|
||||||
|
@ -888,7 +923,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||||
|
|
||||||
// creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps
|
// creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps
|
||||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4);
|
nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), m_width, m_height, m_width * 4);
|
||||||
|
|
||||||
// sends data to gpu
|
// sends data to gpu
|
||||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||||
|
@ -910,7 +945,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||||
else
|
else
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||||
|
|
||||||
if (use_mipmaps && OpenGLManager::use_manually_generated_mipmaps()) {
|
if (use_mipmaps) {
|
||||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||||
int lod_w = m_width;
|
int lod_w = m_width;
|
||||||
int lod_h = m_height;
|
int lod_h = m_height;
|
||||||
|
@ -920,11 +955,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||||
|
|
||||||
lod_w = std::max(lod_w / 2, 1);
|
lod_w = std::max(lod_w / 2, 1);
|
||||||
lod_h = std::max(lod_h / 2, 1);
|
lod_h = std::max(lod_h / 2, 1);
|
||||||
scale /= 2.0f;
|
scale_w /= 2.0f;
|
||||||
|
scale_h /= 2.0f;
|
||||||
|
|
||||||
data.resize(lod_w * lod_h * 4);
|
data.resize(lod_w * lod_h * 4);
|
||||||
|
|
||||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
|
nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), lod_w, lod_h, lod_w * 4);
|
||||||
if (compression_enabled) {
|
if (compression_enabled) {
|
||||||
// initializes the texture on GPU
|
// initializes the texture on GPU
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||||
|
@ -939,9 +975,8 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||||
}
|
}
|
||||||
} else if (use_mipmaps && !OpenGLManager::use_manually_generated_mipmaps()) {
|
}
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
else {
|
||||||
} else {
|
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "OpenGLManager.hpp"
|
#include "OpenGLManager.hpp"
|
||||||
|
|
||||||
|
@ -212,7 +216,7 @@ std::string OpenGLManager::GLInfo::to_string(bool for_github) const
|
||||||
|
|
||||||
OpenGLManager::GLInfo OpenGLManager::s_gl_info;
|
OpenGLManager::GLInfo OpenGLManager::s_gl_info;
|
||||||
bool OpenGLManager::s_compressed_textures_supported = false;
|
bool OpenGLManager::s_compressed_textures_supported = false;
|
||||||
bool OpenGLManager::m_use_manually_generated_mipmaps = true;
|
bool OpenGLManager::s_force_power_of_two_textures = false;
|
||||||
OpenGLManager::EMultisampleState OpenGLManager::s_multisample = OpenGLManager::EMultisampleState::Unknown;
|
OpenGLManager::EMultisampleState OpenGLManager::s_multisample = OpenGLManager::EMultisampleState::Unknown;
|
||||||
OpenGLManager::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown;
|
OpenGLManager::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown;
|
||||||
|
|
||||||
|
@ -295,31 +299,22 @@ bool OpenGLManager::init_gl(bool popup_error)
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Since AMD driver version 22.7.1, there is probably some bug in the driver that causes the issue with the missing
|
// Since AMD driver version 22.7.1, there is probably some bug in the driver that causes the issue with the missing
|
||||||
// texture of the bed. It seems that this issue only triggers when mipmaps are generated manually
|
// texture of the bed (see: https://github.com/prusa3d/PrusaSlicer/issues/8417).
|
||||||
// (combined with a texture compression) and when mipmaps are generated through OpenGL glGenerateMipmap is working.
|
// It seems that this issue only triggers when mipmaps are generated manually
|
||||||
// So, for newer drivers than 22.6.1, the last working driver version, we use mipmaps generated through OpenGL.
|
// (combined with a texture compression) with texture size not being power of two.
|
||||||
if (const auto gl_info = OpenGLManager::get_gl_info(); boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.")) {
|
// When mipmaps are generated through OpenGL function glGenerateMipmap() the driver works fine,
|
||||||
// WHQL drivers seem to have one more version number at the end besides non-WHQL drivers.
|
// but the mipmap generation is quite slow on some machines.
|
||||||
// WHQL: 4.6.14800 Compatibility Profile Context 22.6.1 30.0.21023.1015
|
// There is no an easy way to detect the driver version without using Win32 API because the strings returned by OpenGL
|
||||||
// Non-WHQL: 4.6.0 Compatibility Profile Context 22.8.1.220810
|
// have no standardized format, only some of them contain the driver version.
|
||||||
std::regex version_rgx(R"(Compatibility\sProfile\sContext\s(\d+)\.(\d+)\.(\d+))");
|
// Until we do not know that driver will be fixed (if ever) we force the use of power of two textures on all cards
|
||||||
if (std::smatch matches; std::regex_search(gl_info.get_version(), matches, version_rgx) && matches.size() == 4) {
|
// 1) containing the string 'Radeon' in the string returned by glGetString(GL_RENDERER)
|
||||||
int version_major = std::stoi(matches[1].str());
|
// 2) containing the string 'Custom' in the string returned by glGetString(GL_RENDERER)
|
||||||
int version_minor = std::stoi(matches[2].str());
|
const auto& gl_info = OpenGLManager::get_gl_info();
|
||||||
int version_patch = std::stoi(matches[3].str());
|
if (boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.") &&
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Found AMD driver version: " << version_major << "." << version_minor << "." << version_patch;
|
(boost::contains(gl_info.get_renderer(), "Radeon") ||
|
||||||
|
boost::contains(gl_info.get_renderer(), "Custom")))
|
||||||
if (version_major > 22 || (version_major == 22 && version_minor > 6) || (version_major == 22 && version_minor == 6 && version_patch > 1)) {
|
s_force_power_of_two_textures = true;
|
||||||
m_use_manually_generated_mipmaps = false;
|
#endif // _WIN32
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Mipmapping through OpenGL was enabled.";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "Not recognized format of version.";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "not AMD driver.";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
#ifndef slic3r_OpenGLManager_hpp_
|
#ifndef slic3r_OpenGLManager_hpp_
|
||||||
#define slic3r_OpenGLManager_hpp_
|
#define slic3r_OpenGLManager_hpp_
|
||||||
|
|
||||||
|
@ -83,10 +87,11 @@ private:
|
||||||
static OSInfo s_os_info;
|
static OSInfo s_os_info;
|
||||||
#endif //__APPLE__
|
#endif //__APPLE__
|
||||||
static bool s_compressed_textures_supported;
|
static bool s_compressed_textures_supported;
|
||||||
|
static bool s_force_power_of_two_textures;
|
||||||
|
|
||||||
static EMultisampleState s_multisample;
|
static EMultisampleState s_multisample;
|
||||||
static EFramebufferType s_framebuffers_type;
|
static EFramebufferType s_framebuffers_type;
|
||||||
|
|
||||||
static bool m_use_manually_generated_mipmaps;
|
|
||||||
public:
|
public:
|
||||||
OpenGLManager() = default;
|
OpenGLManager() = default;
|
||||||
~OpenGLManager();
|
~OpenGLManager();
|
||||||
|
@ -103,7 +108,7 @@ public:
|
||||||
static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; }
|
static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; }
|
||||||
static wxGLCanvas* create_wxglcanvas(wxWindow& parent);
|
static wxGLCanvas* create_wxglcanvas(wxWindow& parent);
|
||||||
static const GLInfo& get_gl_info() { return s_gl_info; }
|
static const GLInfo& get_gl_info() { return s_gl_info; }
|
||||||
static bool use_manually_generated_mipmaps() { return m_use_manually_generated_mipmaps; }
|
static bool force_power_of_two_textures() { return s_force_power_of_two_textures; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void detect_multisample(int* attribList);
|
static void detect_multisample(int* attribList);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue