mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-15 02:37:51 -06:00
Fixed generation of texture mipmap using glGenerateMipMap (added also for png)
Fix mipmap of compressed textures on AMD Radeon graphics cards by forcing the use of squared power of two textures (cherry picked from commit prusa3d/PrusaSlicer@971f2a08e2) (cherry picked from commit prusa3d/PrusaSlicer@eee4453993)
This commit is contained in:
parent
c0386d786c
commit
8c0cf34a6c
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;
|
||||||
|
@ -151,6 +152,9 @@ typedef struct NSVGshape
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
if (p->npts > 0) {
|
||||||
nsvg__addPoint(p, cpx1, cpy1);
|
nsvg__addPoint(p, cpx1, cpy1);
|
||||||
nsvg__addPoint(p, cpx2, cpy2);
|
nsvg__addPoint(p, cpx2, cpy2);
|
||||||
nsvg__addPoint(p, x, y);
|
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) {
|
||||||
|
item[0] = '\0';
|
||||||
|
if ((cmd == 'A' || cmd == 'a') && (nargs == 3 || nargs == 4))
|
||||||
|
s = nsvg__getNextPathItemWhenArcFlag(s, item);
|
||||||
|
if (!*item)
|
||||||
s = nsvg__getNextPathItem(s, 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,7 +3033,7 @@ 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);
|
||||||
|
@ -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
|
||||||
|
if (r->nedges != 0)
|
||||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
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
|
||||||
|
if (r->nedges != 0)
|
||||||
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
|
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
|
||||||
|
@ -418,7 +422,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()));
|
||||||
|
@ -536,7 +540,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()));
|
||||||
|
@ -637,7 +641,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()));
|
||||||
|
@ -677,9 +681,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;
|
||||||
|
@ -693,6 +717,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;
|
||||||
|
@ -819,7 +848,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();
|
||||||
|
|
||||||
|
@ -828,7 +857,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) {
|
||||||
|
@ -836,11 +865,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;
|
||||||
|
@ -853,7 +888,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();
|
||||||
|
@ -870,7 +905,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));
|
||||||
|
@ -892,7 +927,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;
|
||||||
|
@ -902,11 +937,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));
|
||||||
|
@ -921,9 +957,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"
|
||||||
|
|
||||||
|
@ -207,7 +211,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;
|
||||||
|
|
||||||
|
@ -290,31 +294,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_
|
||||||
|
|
||||||
|
@ -81,10 +85,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();
|
||||||
|
@ -101,7 +106,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