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 {
|
||||
NSVG_PAINT_UNDEF = -1,
|
||||
NSVG_PAINT_NONE = 0,
|
||||
NSVG_PAINT_COLOR = 1,
|
||||
NSVG_PAINT_LINEAR_GRADIENT = 2,
|
||||
|
@ -119,7 +120,7 @@ typedef struct NSVGgradient {
|
|||
} NSVGgradient;
|
||||
|
||||
typedef struct NSVGpaint {
|
||||
char type;
|
||||
signed char type;
|
||||
union {
|
||||
unsigned int color;
|
||||
NSVGgradient* gradient;
|
||||
|
@ -151,6 +152,9 @@ typedef struct NSVGshape
|
|||
char fillRule; // Fill rule, see NSVGfillRule.
|
||||
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
||||
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.
|
||||
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
||||
} NSVGshape;
|
||||
|
@ -181,16 +185,13 @@ void nsvgDelete(NSVGimage* image);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#endif // NANOSVG_H
|
||||
|
||||
#ifdef NANOSVG_IMPLEMENTATION
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#define NSVG_PI (3.14159265358979323846264338327f)
|
||||
#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';
|
||||
}
|
||||
|
||||
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__maxf(float a, float b) { return a > b ? a : b; }
|
||||
|
||||
|
@ -402,7 +398,7 @@ typedef struct NSVGgradientData
|
|||
{
|
||||
char id[64];
|
||||
char ref[64];
|
||||
char type;
|
||||
signed char type;
|
||||
union {
|
||||
NSVGlinearData linear;
|
||||
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;
|
||||
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)
|
||||
{
|
||||
if (p->npts > 0) {
|
||||
nsvg__addPoint(p, cpx1, cpy1);
|
||||
nsvg__addPoint(p, cpx2, cpy2);
|
||||
nsvg__addPoint(p, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
NSVGgradientData* grad = p->gradients;
|
||||
while (grad) {
|
||||
if (id == NULL || *id == '\0')
|
||||
return NULL;
|
||||
while (grad != NULL) {
|
||||
if (strcmp(grad->id, id) == 0)
|
||||
return grad;
|
||||
grad = grad->next;
|
||||
|
@ -818,28 +818,34 @@ static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
|
|||
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* ref = NULL;
|
||||
NSVGgradientStop* stops = NULL;
|
||||
NSVGgradient* grad;
|
||||
float ox, oy, sw, sh, sl;
|
||||
int nstops = 0;
|
||||
int refIter;
|
||||
|
||||
data = nsvg__findGradientData(p, id);
|
||||
if (data == NULL) return NULL;
|
||||
|
||||
// TODO: use ref to fill in all unset values too.
|
||||
ref = data;
|
||||
refIter = 0;
|
||||
while (ref != NULL) {
|
||||
NSVGgradientData* nextRef = NULL;
|
||||
if (stops == NULL && ref->stops != NULL) {
|
||||
stops = ref->stops;
|
||||
nstops = ref->nstops;
|
||||
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;
|
||||
|
||||
|
@ -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, attr->xform);
|
||||
nsvg__xformMultiply(grad->xform, xform);
|
||||
|
||||
grad->spread = data->spread;
|
||||
memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
|
||||
|
@ -952,6 +958,9 @@ static void nsvg__addShape(NSVGparser* p)
|
|||
memset(shape, 0, sizeof(NSVGshape));
|
||||
|
||||
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);
|
||||
shape->strokeWidth = attr->strokeWidth * scale;
|
||||
shape->strokeDashOffset = attr->strokeDashOffset * scale;
|
||||
|
@ -987,13 +996,7 @@ static void nsvg__addShape(NSVGparser* p)
|
|||
shape->fill.color = attr->fillColor;
|
||||
shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
|
||||
} else if (attr->hasFill == 2) {
|
||||
float inv[6], localBounds[4];
|
||||
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;
|
||||
}
|
||||
shape->fill.type = NSVG_PAINT_UNDEF;
|
||||
}
|
||||
|
||||
// Set stroke
|
||||
|
@ -1004,12 +1007,7 @@ static void nsvg__addShape(NSVGparser* p)
|
|||
shape->stroke.color = attr->strokeColor;
|
||||
shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
|
||||
} else if (attr->hasStroke == 2) {
|
||||
float inv[6], localBounds[4];
|
||||
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;
|
||||
shape->stroke.type = NSVG_PAINT_UNDEF;
|
||||
}
|
||||
|
||||
// Set flags
|
||||
|
@ -1042,6 +1040,10 @@ static void nsvg__addPath(NSVGparser* p, char closed)
|
|||
if (closed)
|
||||
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));
|
||||
if (path == NULL) goto error;
|
||||
memset(path, 0, sizeof(NSVGpath));
|
||||
|
@ -1090,7 +1092,7 @@ static double nsvg__atof(const char* s)
|
|||
char* cur = (char*)s;
|
||||
char* end = NULL;
|
||||
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;
|
||||
|
||||
// Parse optional sign
|
||||
|
@ -1104,9 +1106,13 @@ static double nsvg__atof(const char* s)
|
|||
// Parse integer part
|
||||
if (nsvg__isdigit(*cur)) {
|
||||
// Parse digit sequence
|
||||
#ifdef _MSC_VER
|
||||
intPart = (double)_strtoi64(cur, &end, 10);
|
||||
#else
|
||||
intPart = (double)strtoll(cur, &end, 10);
|
||||
#endif
|
||||
if (cur != end) {
|
||||
res = (double)intPart;
|
||||
res = intPart;
|
||||
hasIntPart = 1;
|
||||
cur = end;
|
||||
}
|
||||
|
@ -1117,9 +1123,13 @@ static double nsvg__atof(const char* s)
|
|||
cur++; // Skip '.'
|
||||
if (nsvg__isdigit(*cur)) {
|
||||
// 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) {
|
||||
res += (double)fracPart / pow(10.0, (double)(end - cur));
|
||||
res += fracPart / pow(10.0, (double)(end - cur));
|
||||
hasFracPart = 1;
|
||||
cur = end;
|
||||
}
|
||||
|
@ -1132,11 +1142,11 @@ static double nsvg__atof(const char* s)
|
|||
|
||||
// Parse optional exponent
|
||||
if (*cur == 'e' || *cur == 'E') {
|
||||
int expPart = 0;
|
||||
double expPart = 0.0;
|
||||
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) {
|
||||
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
|
||||
if (*s == 'e' || *s == 'E') {
|
||||
if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
|
||||
if (i < last) it[i++] = *s;
|
||||
s++;
|
||||
if (*s == '-' || *s == '+') {
|
||||
|
@ -1187,6 +1197,19 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
|
|||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
unsigned int c = 0, r = 0, g = 0, b = 0;
|
||||
int n = 0;
|
||||
str++; // skip #
|
||||
// Calculate number of characters.
|
||||
while(str[n] && !nsvg__isspace(str[n]))
|
||||
n++;
|
||||
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);
|
||||
unsigned int r=0, g=0, b=0;
|
||||
if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex
|
||||
return NSVG_RGB(r, g, b);
|
||||
if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa
|
||||
return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), ..
|
||||
return NSVG_RGB(128, 128, 128);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
int r = -1, g = -1, b = -1;
|
||||
char s1[32]="", s2[32]="";
|
||||
sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
|
||||
if (strchr(s1, '%')) {
|
||||
return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
|
||||
} else {
|
||||
return NSVG_RGB(r,g,b);
|
||||
int i;
|
||||
unsigned int rgbi[3];
|
||||
float rgbf[3];
|
||||
// try decimal integers first
|
||||
if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) {
|
||||
// integers failed, try percent values (float, locale independent)
|
||||
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 {
|
||||
|
@ -1460,6 +1514,15 @@ static int nsvg__parseUnits(const char* units)
|
|||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
float t[6];
|
||||
int len;
|
||||
nsvg__xformIdentity(xform);
|
||||
while (*str)
|
||||
{
|
||||
if (strncmp(str, "matrix", 6) == 0)
|
||||
str += nsvg__parseMatrix(t, str);
|
||||
len = nsvg__parseMatrix(t, str);
|
||||
else if (strncmp(str, "translate", 9) == 0)
|
||||
str += nsvg__parseTranslate(t, str);
|
||||
len = nsvg__parseTranslate(t, str);
|
||||
else if (strncmp(str, "scale", 5) == 0)
|
||||
str += nsvg__parseScale(t, str);
|
||||
len = nsvg__parseScale(t, str);
|
||||
else if (strncmp(str, "rotate", 6) == 0)
|
||||
str += nsvg__parseRotate(t, str);
|
||||
len = nsvg__parseRotate(t, str);
|
||||
else if (strncmp(str, "skewX", 5) == 0)
|
||||
str += nsvg__parseSkewX(t, str);
|
||||
len = nsvg__parseSkewX(t, str);
|
||||
else if (strncmp(str, "skewY", 5) == 0)
|
||||
str += nsvg__parseSkewY(t, str);
|
||||
len = nsvg__parseSkewY(t, str);
|
||||
else{
|
||||
++str;
|
||||
continue;
|
||||
}
|
||||
if (len != 0) {
|
||||
str += len;
|
||||
} else {
|
||||
++str;
|
||||
continue;
|
||||
}
|
||||
|
||||
nsvg__xformPremultiply(xform, t);
|
||||
}
|
||||
|
@ -1627,9 +1697,9 @@ static void nsvg__parseUrl(char* id, const char* str)
|
|||
{
|
||||
int i = 0;
|
||||
str += 4; // "url(";
|
||||
if (*str == '#')
|
||||
if (*str && *str == '#')
|
||||
str++;
|
||||
while (i < 63 && *str != ')') {
|
||||
while (i < 63 && *str && *str != ')') {
|
||||
id[i] = *str++;
|
||||
i++;
|
||||
}
|
||||
|
@ -1878,8 +1948,11 @@ static int nsvg__getArgsPerElement(char cmd)
|
|||
case 'a':
|
||||
case 'A':
|
||||
return 7;
|
||||
}
|
||||
case 'z':
|
||||
case 'Z':
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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.
|
||||
ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.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)
|
||||
kappa = -kappa;
|
||||
|
||||
|
@ -2189,6 +2267,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
float args[10];
|
||||
int nargs;
|
||||
int rargs = 0;
|
||||
char initPoint;
|
||||
float cpx, cpy, cpx2, cpy2;
|
||||
const char* tmp[4];
|
||||
char closedFlag;
|
||||
|
@ -2211,13 +2290,18 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
nsvg__resetPath(p);
|
||||
cpx = 0; cpy = 0;
|
||||
cpx2 = 0; cpy2 = 0;
|
||||
initPoint = 0;
|
||||
closedFlag = 0;
|
||||
nargs = 0;
|
||||
|
||||
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);
|
||||
if (!*item) break;
|
||||
if (nsvg__isnum(item[0])) {
|
||||
if (cmd != '\0' && nsvg__isCoordinate(item)) {
|
||||
if (nargs < 10)
|
||||
args[nargs++] = (float)nsvg__atof(item);
|
||||
if (nargs >= rargs) {
|
||||
|
@ -2230,6 +2314,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
cmd = (cmd == 'm') ? 'l' : 'L';
|
||||
rargs = nsvg__getArgsPerElement(cmd);
|
||||
cpx2 = cpx; cpy2 = cpy;
|
||||
initPoint = 1;
|
||||
break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
|
@ -2279,7 +2364,6 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
}
|
||||
} else {
|
||||
cmd = item[0];
|
||||
rargs = nsvg__getArgsPerElement(cmd);
|
||||
if (cmd == 'M' || cmd == 'm') {
|
||||
// Commit path.
|
||||
if (p->npts > 0)
|
||||
|
@ -2288,7 +2372,11 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
nsvg__resetPath(p);
|
||||
closedFlag = 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;
|
||||
// Commit path.
|
||||
if (p->npts > 0) {
|
||||
|
@ -2304,6 +2392,12 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
|||
closedFlag = 0;
|
||||
nargs = 0;
|
||||
}
|
||||
rargs = nsvg__getArgsPerElement(cmd);
|
||||
if (rargs == -1) {
|
||||
// Command not recognized
|
||||
cmd = '\0';
|
||||
rargs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
// Create gradients after all definitions have been parsed
|
||||
nsvg__createGradients(p);
|
||||
|
||||
// Scale to viewBox
|
||||
nsvg__scaleToViewbox(p, units);
|
||||
|
||||
|
@ -2899,8 +3026,6 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||
{
|
||||
FILE* fp = NULL;
|
||||
|
@ -2908,7 +3033,7 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
|||
char* data = NULL;
|
||||
NSVGimage* image = NULL;
|
||||
|
||||
fp = boost::nowide::fopen(filename, "rb");
|
||||
fp = fopen(filename, "rb");
|
||||
if (!fp) goto error;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
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;
|
||||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
|
||||
image = nsvgParse(data, units, dpi);
|
||||
free(data);
|
||||
|
||||
return image;
|
||||
|
||||
error:
|
||||
|
@ -2976,4 +3101,6 @@ void nsvgDelete(NSVGimage* 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
|
||||
#define NANOSVGRAST_H
|
||||
|
||||
#include "nanosvg.h"
|
||||
|
||||
#ifndef NANOSVGRAST_CPLUSPLUS
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -44,16 +52,19 @@ typedef struct NSVGrasterizer NSVGrasterizer;
|
|||
unsigned char* img = malloc(w*h*4);
|
||||
// Rasterize
|
||||
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.
|
||||
NSVGrasterizer* nsvgCreateRasterizer();
|
||||
NSVGrasterizer* nsvgCreateRasterizer(void);
|
||||
|
||||
// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
|
||||
// r - pointer to rasterizer context
|
||||
// image - pointer to image to rasterize
|
||||
// tx,ty - image offset (applied after scaling)
|
||||
// scale - image scale
|
||||
// scale - image scale (assumes square aspect ratio)
|
||||
// dst - pointer to destination image data, 4 bytes per pixel (RGBA)
|
||||
// w - width of the image to render
|
||||
// h - height of the image to render
|
||||
|
@ -62,6 +73,12 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
NSVGimage* image, float tx, float ty, float scale,
|
||||
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.
|
||||
void nsvgDeleteRasterizer(NSVGrasterizer*);
|
||||
|
||||
|
@ -72,11 +89,11 @@ void nsvgDeleteRasterizer(NSVGrasterizer*);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#endif // NANOSVGRAST_H
|
||||
|
||||
#ifdef NANOSVGRAST_IMPLEMENTATION
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NSVG__SUBSAMPLES 5
|
||||
#define NSVG__FIXSHIFT 10
|
||||
|
@ -112,7 +129,7 @@ typedef struct NSVGmemPage {
|
|||
} NSVGmemPage;
|
||||
|
||||
typedef struct NSVGcachedPaint {
|
||||
char type;
|
||||
signed char type;
|
||||
char spread;
|
||||
float xform[6];
|
||||
unsigned int colors[256];
|
||||
|
@ -148,7 +165,7 @@ struct NSVGrasterizer
|
|||
int width, height, stride;
|
||||
};
|
||||
|
||||
NSVGrasterizer* nsvgCreateRasterizer()
|
||||
NSVGrasterizer* nsvgCreateRasterizer(void)
|
||||
{
|
||||
NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
r->npoints = 0;
|
||||
// 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) {
|
||||
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
|
||||
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
|
||||
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);
|
||||
|
@ -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;
|
||||
NSVGpath* path;
|
||||
|
@ -740,15 +757,16 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
|||
float miterLimit = shape->miterLimit;
|
||||
int lineJoin = shape->strokeLineJoin;
|
||||
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) {
|
||||
// Flatten path
|
||||
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) {
|
||||
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)
|
||||
continue;
|
||||
|
@ -794,7 +812,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
|||
dashOffset -= shape->strokeDashArray[idash];
|
||||
idash = (idash + 1) % shape->strokeDashCount;
|
||||
}
|
||||
dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
|
||||
dashLen = (shape->strokeDashArray[idash] - dashOffset) * sw;
|
||||
|
||||
for (j = 1; j < r->npoints2; ) {
|
||||
float dx = r->points2[j].x - cur.x;
|
||||
|
@ -816,7 +834,7 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
|
|||
// Advance dash pattern
|
||||
dashState = !dashState;
|
||||
idash = (idash+1) % shape->strokeDashCount;
|
||||
dashLen = shape->strokeDashArray[idash] * scale;
|
||||
dashLen = shape->strokeDashArray[idash] * sw;
|
||||
// Restart
|
||||
cur.x = x;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -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,
|
||||
float tx, float ty, float scale, NSVGcachedPaint* cache)
|
||||
float tx, float ty, float sx, float sy, NSVGcachedPaint* cache)
|
||||
{
|
||||
|
||||
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;
|
||||
unsigned int c;
|
||||
|
||||
fx = ((float)x - tx) / scale;
|
||||
fy = ((float)y - ty) / scale;
|
||||
dx = 1.0f / scale;
|
||||
fx = ((float)x - tx) / sx;
|
||||
fy = ((float)y - ty) / sy;
|
||||
dx = 1.0f / sx;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
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;
|
||||
unsigned int c;
|
||||
|
||||
fx = ((float)x - tx) / scale;
|
||||
fy = ((float)y - ty) / scale;
|
||||
dx = 1.0f / scale;
|
||||
fx = ((float)x - tx) / sx;
|
||||
fy = ((float)y - ty) / sy;
|
||||
dx = 1.0f / sx;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
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;
|
||||
int y, s;
|
||||
|
@ -1194,7 +1212,7 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
|
|||
if (xmin < 0) xmin = 0;
|
||||
if (xmax > r->width-1) xmax = r->width-1;
|
||||
if (xmin <= xmax) {
|
||||
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
|
||||
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,
|
||||
NSVGimage* image, float tx, float ty, float scale,
|
||||
void nsvgRasterizeXY(NSVGrasterizer* r,
|
||||
NSVGimage* image, float tx, float ty,
|
||||
float sx, float sy,
|
||||
unsigned char* dst, int w, int h, int stride)
|
||||
{
|
||||
NSVGshape *shape = NULL;
|
||||
|
@ -1394,7 +1413,7 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
r->freelist = NULL;
|
||||
r->nedges = 0;
|
||||
|
||||
nsvg__flattenShape(r, shape, scale);
|
||||
nsvg__flattenShape(r, shape, sx, sy);
|
||||
|
||||
// Scale and translate edges
|
||||
for (i = 0; i < r->nedges; i++) {
|
||||
|
@ -1406,19 +1425,20 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
}
|
||||
|
||||
// Rasterize edges
|
||||
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
|
||||
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);
|
||||
r->freelist = NULL;
|
||||
r->nedges = 0;
|
||||
|
||||
nsvg__flattenShapeStroke(r, shape, scale);
|
||||
nsvg__flattenShapeStroke(r, shape, sx, sy);
|
||||
|
||||
// dumpEdges(r, "edge.svg");
|
||||
|
||||
|
@ -1432,12 +1452,13 @@ void nsvgRasterize(NSVGrasterizer* r,
|
|||
}
|
||||
|
||||
// Rasterize edges
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
#include "I18N.hpp"
|
||||
//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(::glGenTextures(1, &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()));
|
||||
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()));
|
||||
|
@ -536,7 +540,7 @@ bool GLTexture::generate_from_text(const std::string &text_str, wxFont &font, wx
|
|||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &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()));
|
||||
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()));
|
||||
|
@ -637,7 +641,7 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont&
|
|||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &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()));
|
||||
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()));
|
||||
|
@ -677,9 +681,29 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
|
|||
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 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.
|
||||
wxImage image;
|
||||
|
@ -693,6 +717,11 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
|
||||
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) {
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 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;
|
||||
|
||||
if (compression_enabled && compression_type == MultiThreaded)
|
||||
if (compression_type == MultiThreaded)
|
||||
// start asynchronous compression
|
||||
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 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);
|
||||
if (image == nullptr) {
|
||||
|
@ -836,11 +865,17 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
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_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) {
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 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);
|
||||
}
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
const int n_pixels = m_width * m_height;
|
||||
|
||||
if (n_pixels <= 0) {
|
||||
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
|
||||
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
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
|
@ -892,7 +927,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
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()));
|
||||
|
||||
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
|
||||
int lod_w = m_width;
|
||||
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_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);
|
||||
|
||||
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) {
|
||||
// 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));
|
||||
|
@ -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_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_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 "OpenGLManager.hpp"
|
||||
|
||||
|
@ -207,7 +211,7 @@ std::string OpenGLManager::GLInfo::to_string(bool for_github) const
|
|||
|
||||
OpenGLManager::GLInfo OpenGLManager::s_gl_info;
|
||||
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::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown;
|
||||
|
||||
|
@ -290,31 +294,22 @@ bool OpenGLManager::init_gl(bool popup_error)
|
|||
|
||||
#ifdef _WIN32
|
||||
// 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
|
||||
// (combined with a texture compression) and when mipmaps are generated through OpenGL glGenerateMipmap is working.
|
||||
// So, for newer drivers than 22.6.1, the last working driver version, we use mipmaps generated through OpenGL.
|
||||
if (const auto gl_info = OpenGLManager::get_gl_info(); boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.")) {
|
||||
// WHQL drivers seem to have one more version number at the end besides non-WHQL drivers.
|
||||
// WHQL: 4.6.14800 Compatibility Profile Context 22.6.1 30.0.21023.1015
|
||||
// Non-WHQL: 4.6.0 Compatibility Profile Context 22.8.1.220810
|
||||
std::regex version_rgx(R"(Compatibility\sProfile\sContext\s(\d+)\.(\d+)\.(\d+))");
|
||||
if (std::smatch matches; std::regex_search(gl_info.get_version(), matches, version_rgx) && matches.size() == 4) {
|
||||
int version_major = std::stoi(matches[1].str());
|
||||
int version_minor = std::stoi(matches[2].str());
|
||||
int version_patch = std::stoi(matches[3].str());
|
||||
BOOST_LOG_TRIVIAL(debug) << "Found AMD driver version: " << version_major << "." << version_minor << "." << version_patch;
|
||||
|
||||
if (version_major > 22 || (version_major == 22 && version_minor > 6) || (version_major == 22 && version_minor == 6 && version_patch > 1)) {
|
||||
m_use_manually_generated_mipmaps = false;
|
||||
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
|
||||
// texture of the bed (see: https://github.com/prusa3d/PrusaSlicer/issues/8417).
|
||||
// It seems that this issue only triggers when mipmaps are generated manually
|
||||
// (combined with a texture compression) with texture size not being power of two.
|
||||
// When mipmaps are generated through OpenGL function glGenerateMipmap() the driver works fine,
|
||||
// but the mipmap generation is quite slow on some machines.
|
||||
// There is no an easy way to detect the driver version without using Win32 API because the strings returned by OpenGL
|
||||
// have no standardized format, only some of them contain the driver version.
|
||||
// Until we do not know that driver will be fixed (if ever) we force the use of power of two textures on all cards
|
||||
// 1) containing the string 'Radeon' in the string returned by glGetString(GL_RENDERER)
|
||||
// 2) containing the string 'Custom' in the string returned by glGetString(GL_RENDERER)
|
||||
const auto& gl_info = OpenGLManager::get_gl_info();
|
||||
if (boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.") &&
|
||||
(boost::contains(gl_info.get_renderer(), "Radeon") ||
|
||||
boost::contains(gl_info.get_renderer(), "Custom")))
|
||||
s_force_power_of_two_textures = true;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
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_
|
||||
#define slic3r_OpenGLManager_hpp_
|
||||
|
||||
|
@ -81,10 +85,11 @@ private:
|
|||
static OSInfo s_os_info;
|
||||
#endif //__APPLE__
|
||||
static bool s_compressed_textures_supported;
|
||||
static bool s_force_power_of_two_textures;
|
||||
|
||||
static EMultisampleState s_multisample;
|
||||
static EFramebufferType s_framebuffers_type;
|
||||
|
||||
static bool m_use_manually_generated_mipmaps;
|
||||
public:
|
||||
OpenGLManager() = default;
|
||||
~OpenGLManager();
|
||||
|
@ -101,7 +106,7 @@ public:
|
|||
static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; }
|
||||
static wxGLCanvas* create_wxglcanvas(wxWindow& parent);
|
||||
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:
|
||||
static void detect_multisample(int* attribList);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue