Fix #100718: NLA Hold Forward Inconsistency #109182

Merged
Nate Rupsis merged 14 commits from 100718-NLA-hold-forward-inconsistency into main 2023-09-11 18:40:38 +02:00
17 changed files with 3150 additions and 2759 deletions
Showing only changes of commit ed707bade2 - Show all commits

View File

@ -1,7 +1,9 @@
Project: NanoSVG
URL: https://github.com/memononen/nanosvg
License: zlib
Upstream version: 3cdd4a9d7886
Local modifications: Added some functionality to manage grease pencil layers
Upstream SHA: 9da543e8329fdd81b64eb48742d8ccb09377aed1
GPG key ID: 4AEE18F83AFDEB23
Last Commit: "Merge pull request #236 from sezero/signed-char"
Last Commit Date: December 4, 2022
Added a fix to SVG import arc and float errors (https://developer.blender.org/rB11dc674c78b49fc4e0b7c134c375b6c8b8eacbcc)
Local modifications: Added some functionality to manage grease pencil layers

5072
extern/nanosvg/nanosvg.h vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,86 +1,647 @@
diff --git a/c:/tmp/nanosvg_original.h b/c:/tmp/nanosvg_modif.h
index 24a01a86d3d..eca0d07e79d 100644
--- a/c:/tmp/nanosvg_original.h
+++ b/c:/tmp/nanosvg_modif.h
@@ -24,7 +24,8 @@
diff --git a/extern/nanosvg/nanosvg.h b/extern/nanosvg/nanosvg.h
index 60a323820cb..1bfb891c397 100644
--- a/extern/nanosvg/nanosvg.h
+++ b/extern/nanosvg/nanosvg.h
@@ -1,56 +1,58 @@
/*
* Copyright (c) 2013-14 Mikko Mononen memon@inside.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
* Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
*
* Arc calculation code based on canvg (https://code.google.com/p/canvg/)
*
* Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
*
- */
+ * This is a modified version for Blender used by importers.
+ **/
+ *
*/
#ifndef NANOSVG_H
#define NANOSVG_H
@@ -148,6 +149,8 @@ extern "C" {
typedef struct NSVGshape
{
char id[64]; // Optional 'id' attr of the shape or its group
+ /* Blender: Parent ID used for layer creation. */
+ char id_parent[64];
NSVGpaint fill; // Fill paint
NSVGpaint stroke; // Stroke paint
float opacity; // Opacity of the shape.
@@ -370,6 +373,7 @@ int nsvg__parseXML(char* input,
#ifndef NANOSVG_CPLUSPLUS
#ifdef __cplusplus
extern "C" {
#endif
#endif
// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
//
// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
//
// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
//
// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
// That is, you should get the same looking data as your designed in your favorite app.
//
// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
//
// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
// DPI (dots-per-inch) controls how the unit conversion is done.
//
// If you don't know or care about the units stuff, "px" and 96 should get you going.
/* Example Usage:
@@ -112,60 +114,61 @@ typedef struct NSVGgradientStop {
} NSVGgradientStop;
typedef struct NSVGgradient {
float xform[6];
char spread;
float fx, fy;
int nstops;
NSVGgradientStop stops[1];
} NSVGgradient;
typedef struct NSVGpaint {
signed char type;
union {
unsigned int color;
NSVGgradient* gradient;
};
} NSVGpaint;
typedef struct NSVGpath
{
float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
int npts; // Total number of bezier points.
char closed; // Flag indicating if shapes should be treated as closed.
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
struct NSVGpath* next; // Pointer to next path, or NULL if last element.
} NSVGpath;
typedef struct NSVGshape
{
char id[64]; // Optional 'id' attr of the shape or its group
+ char id_parent[64]; // Blender: Parent ID used for layer creation.
NSVGpaint fill; // Fill paint
NSVGpaint stroke; // Stroke paint
float opacity; // Opacity of the shape.
float strokeWidth; // Stroke width (scaled).
float strokeDashOffset; // Stroke dash offset (scaled).
float strokeDashArray[8]; // Stroke dash array (scaled).
char strokeDashCount; // Number of dash values in dash array.
char strokeLineJoin; // Stroke join type.
char strokeLineCap; // Stroke cap type.
float miterLimit; // Miter limit
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;
typedef struct NSVGimage
{
float width; // Width of the image.
float height; // Height of the image.
NSVGshape* shapes; // Linked list of shapes in the image.
} NSVGimage;
// Parses SVG file from a file, returns SVG image as paths.
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
@@ -333,60 +336,61 @@ int nsvg__parseXML(char* input,
void* ud)
{
char* s = input;
char* mark = s;
int state = NSVG_XML_CONTENT;
while (*s) {
if (*s == '<' && state == NSVG_XML_CONTENT) {
// Start of a tag
*s++ = '\0';
nsvg__parseContent(mark, contentCb, ud);
mark = s;
state = NSVG_XML_TAG;
} else if (*s == '>' && state == NSVG_XML_TAG) {
// Start of a content or new tag.
*s++ = '\0';
nsvg__parseElement(mark, startelCb, endelCb, ud);
mark = s;
state = NSVG_XML_CONTENT;
} else {
s++;
}
}
return 1;
}
/* Simple SVG parser. */
#define NSVG_MAX_ATTR 128
+#define NSVG_MAX_BREADCRUMB 5
enum NSVGgradientUnits
enum NSVGgradientUnits {
NSVG_USER_SPACE = 0,
NSVG_OBJECT_SPACE = 1
};
#define NSVG_MAX_DASHES 8
enum NSVGunits {
NSVG_UNITS_USER,
NSVG_UNITS_PX,
NSVG_UNITS_PT,
NSVG_UNITS_PC,
NSVG_UNITS_MM,
NSVG_UNITS_CM,
NSVG_UNITS_IN,
NSVG_UNITS_PERCENT,
NSVG_UNITS_EM,
NSVG_UNITS_EX
};
typedef struct NSVGcoordinate {
float value;
int units;
} NSVGcoordinate;
typedef struct NSVGlinearData {
NSVGcoordinate x1, y1, x2, y2;
} NSVGlinearData;
@@ -428,60 +432,64 @@ typedef struct NSVGattrib
int strokeDashCount;
char strokeLineJoin;
char strokeLineCap;
float miterLimit;
char fillRule;
float fontSize;
unsigned int stopColor;
float stopOpacity;
float stopOffset;
char hasFill;
char hasStroke;
char visible;
} NSVGattrib;
typedef struct NSVGparser
{
@@ -471,6 +475,10 @@ typedef struct NSVGparser
NSVGattrib attr[NSVG_MAX_ATTR];
int attrHead;
float* pts;
int npts;
int cpts;
NSVGpath* plist;
NSVGimage* image;
NSVGgradientData* gradients;
NSVGshape* shapesTail;
float viewMinx, viewMiny, viewWidth, viewHeight;
int alignX, alignY, alignType;
float dpi;
char pathFlag;
char defsFlag;
+ /** Blender breadcrumb for layers. */
+ char breadcrumb[NSVG_MAX_BREADCRUMB][64];
+ /** Blender number of elements in breadcrumb. */
+ int breadcrumb_len;
+ /** Blender breadcrumb for layers. */
+ char breadcrumb[NSVG_MAX_BREADCRUMB][64];
+ /** Blender number of elements in breadcrumb. */
+ int breadcrumb_len;
} NSVGparser;
static void nsvg__xformIdentity(float* t)
@@ -980,6 +988,14 @@ static void nsvg__addShape(NSVGparser* p)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = 0.0f; t[5] = 0.0f;
}
static void nsvg__xformSetTranslation(float* t, float tx, float ty)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = 0.0f; t[3] = 1.0f;
t[4] = tx; t[5] = ty;
}
static void nsvg__xformSetScale(float* t, float sx, float sy)
{
t[0] = sx; t[1] = 0.0f;
t[2] = 0.0f; t[3] = sy;
t[4] = 0.0f; t[5] = 0.0f;
}
static void nsvg__xformSetSkewX(float* t, float a)
{
t[0] = 1.0f; t[1] = 0.0f;
t[2] = tanf(a); t[3] = 1.0f;
t[4] = 0.0f; t[5] = 0.0f;
}
@@ -930,60 +938,68 @@ static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
bounds[2] = curveBounds[2];
bounds[3] = curveBounds[3];
first = 0;
} else {
bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
}
curve[0] = curve[6];
curve[1] = curve[7];
}
}
}
static void nsvg__addShape(NSVGparser* p)
{
NSVGattrib* attr = nsvg__getAttr(p);
float scale = 1.0f;
NSVGshape* shape;
NSVGpath* path;
int i;
if (p->plist == NULL)
return;
shape = (NSVGshape*)malloc(sizeof(NSVGshape));
if (shape == NULL) goto error;
memset(shape, 0, sizeof(NSVGshape));
memcpy(shape->id, attr->id, sizeof shape->id);
+ /* Copy parent id from breadcrumb. */
+ if (p->breadcrumb_len > 0) {
+ memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent);
+ }
+ else {
+ memcpy(shape->id_parent, attr->id, sizeof shape->id_parent);
+ }
+ /* Copy parent id from breadcrumb. */
+ if (p->breadcrumb_len > 0) {
+ memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent);
+ }
+ else {
+ memcpy(shape->id_parent, attr->id, sizeof shape->id_parent);
+ }
+
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;
@@ -2814,6 +2830,14 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr)
shape->strokeDashCount = (char)attr->strokeDashCount;
for (i = 0; i < attr->strokeDashCount; i++)
shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
shape->strokeLineJoin = attr->strokeLineJoin;
shape->strokeLineCap = attr->strokeLineCap;
shape->miterLimit = attr->miterLimit;
shape->fillRule = attr->fillRule;
shape->opacity = attr->opacity;
shape->paths = p->plist;
p->plist = NULL;
// Calculate shape bounds
shape->bounds[0] = shape->paths->bounds[0];
shape->bounds[1] = shape->paths->bounds[1];
shape->bounds[2] = shape->paths->bounds[2];
shape->bounds[3] = shape->paths->bounds[3];
for (path = shape->paths->next; path != NULL; path = path->next) {
shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
}
@@ -1175,66 +1191,75 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size)
if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
if (i < last) it[i++] = *s;
s++;
if (*s == '-' || *s == '+') {
if (i < last) it[i++] = *s;
s++;
}
while (*s && nsvg__isdigit(*s)) {
if (i < last) it[i++] = *s;
s++;
}
}
it[i] = '\0';
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, char cmd, int nargs)
{
it[0] = '\0';
// Skip white spaces and commas
while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
if (!*s) return s;
+
+ /* Blender: Special case for arc command's 4th and 5th arguments. */
+ if ((cmd == 'a' || cmd == 'A') && (nargs == 3 || nargs == 4)) {
+ it[0] = s[0];
+ it[1] = '\0';
+ s++;
+ return s;
+ }
+
if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
s = nsvg__parseNumber(s, it, 64);
} else {
// Parse command
it[0] = *s++;
it[1] = '\0';
return s;
}
return s;
}
static unsigned int nsvg__parseColorHex(const char* str)
{
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 i;
unsigned int rgbi[3];
@@ -2264,61 +2289,61 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
const char* tmp[4];
char closedFlag;
int i;
char item[64];
for (i = 0; attr[i]; i += 2) {
if (strcmp(attr[i], "d") == 0) {
s = attr[i + 1];
} else {
tmp[0] = attr[i];
tmp[1] = attr[i + 1];
tmp[2] = 0;
tmp[3] = 0;
nsvg__parseAttribs(p, tmp);
}
}
if (s) {
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);
+ s = nsvg__getNextPathItem(s, item, cmd, nargs);
if (!*item) break;
if (cmd != '\0' && nsvg__isCoordinate(item)) {
if (nargs < 10)
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= rargs) {
switch (cmd) {
case 'm':
case 'M':
nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
// Moveto can be followed by multiple coordinate pairs,
// which should be treated as linetos.
cmd = (cmd == 'm') ? 'l' : 'L';
rargs = nsvg__getArgsPerElement(cmd);
cpx2 = cpx; cpy2 = cpy;
initPoint = 1;
break;
case 'l':
case 'L':
nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
break;
case 'H':
case 'h':
nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
break;
case 'V':
case 'v':
nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
cpx2 = cpx; cpy2 = cpy;
@@ -2534,61 +2559,61 @@ static void nsvg__parseLine(NSVGparser* p, const char** attr)
if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
}
}
nsvg__resetPath(p);
nsvg__moveTo(p, x1, y1);
nsvg__lineTo(p, x2, y2);
nsvg__addPath(p, 0);
nsvg__addShape(p);
}
static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
{
int i;
const char* s;
float args[2];
int nargs, npts = 0;
char item[64];
nsvg__resetPath(p);
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "points") == 0) {
s = attr[i + 1];
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, '\0', nargs);
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= 2) {
if (npts == 0)
nsvg__moveTo(p, args[0], args[1]);
else
nsvg__lineTo(p, args[0], args[1]);
nargs = 0;
npts++;
}
}
}
}
}
nsvg__addPath(p, (char)closeFlag);
nsvg__addShape(p);
}
static void nsvg__parseSVG(NSVGparser* p, const char** attr)
{
int i;
for (i = 0; attr[i]; i += 2) {
if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
if (strcmp(attr[i], "width") == 0) {
p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
} else if (strcmp(attr[i], "height") == 0) {
p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
} else if (strcmp(attr[i], "viewBox") == 0) {
const char *s = attr[i + 1];
@@ -2740,108 +2765,122 @@ static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
if (idx != grad->nstops-1) {
for (i = grad->nstops-1; i > idx; i--)
grad->stops[i] = grad->stops[i-1];
}
stop = &grad->stops[idx];
stop->color = curAttr->stopColor;
stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
stop->offset = curAttr->stopOffset;
}
static void nsvg__startElement(void* ud, const char* el, const char** attr)
{
NSVGparser* p = (NSVGparser*)ud;
if (p->defsFlag) {
// Skip everything but gradients in defs
if (strcmp(el, "linearGradient") == 0) {
nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
} else if (strcmp(el, "radialGradient") == 0) {
nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
} else if (strcmp(el, "stop") == 0) {
nsvg__parseGradientStop(p, attr);
}
return;
}
if (strcmp(el, "g") == 0) {
nsvg__pushAttr(p);
nsvg__parseAttribs(p, attr);
+
+ /* Save the breadcrumb of groups. */
+ if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) {
+ NSVGattrib *attr_id = nsvg__getAttr(p);
+ memcpy(
+ p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len]));
+ p->breadcrumb_len++;
+ }
}
else if (strcmp(el, "path") == 0) {
+ /* Save the breadcrumb of groups. */
+ if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) {
+ NSVGattrib *attr_id = nsvg__getAttr(p);
+ memcpy(
+ p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len]));
+ p->breadcrumb_len++;
+ }
} else if (strcmp(el, "path") == 0) {
if (p->pathFlag) // Do not allow nested paths.
@@ -2874,7 +2898,12 @@ static void nsvg__endElement(void* ud, const char* el)
return;
nsvg__pushAttr(p);
nsvg__parsePath(p, attr);
nsvg__popAttr(p);
} else if (strcmp(el, "rect") == 0) {
nsvg__pushAttr(p);
nsvg__parseRect(p, attr);
nsvg__popAttr(p);
} else if (strcmp(el, "circle") == 0) {
nsvg__pushAttr(p);
nsvg__parseCircle(p, attr);
nsvg__popAttr(p);
} else if (strcmp(el, "ellipse") == 0) {
nsvg__pushAttr(p);
nsvg__parseEllipse(p, attr);
nsvg__popAttr(p);
} else if (strcmp(el, "line") == 0) {
nsvg__pushAttr(p);
nsvg__parseLine(p, attr);
nsvg__popAttr(p);
} else if (strcmp(el, "polyline") == 0) {
nsvg__pushAttr(p);
nsvg__parsePoly(p, attr, 0);
nsvg__popAttr(p);
} else if (strcmp(el, "polygon") == 0) {
nsvg__pushAttr(p);
nsvg__parsePoly(p, attr, 1);
nsvg__popAttr(p);
} else if (strcmp(el, "linearGradient") == 0) {
nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
} else if (strcmp(el, "radialGradient") == 0) {
nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
} else if (strcmp(el, "stop") == 0) {
nsvg__parseGradientStop(p, attr);
} else if (strcmp(el, "defs") == 0) {
p->defsFlag = 1;
} else if (strcmp(el, "svg") == 0) {
nsvg__parseSVG(p, attr);
}
}
static void nsvg__endElement(void* ud, const char* el)
{
NSVGparser* p = (NSVGparser*)ud;
if (strcmp(el, "g") == 0) {
- nsvg__popAttr(p);
+ /* Remove the breadcrumb level. */
+ if (p->breadcrumb_len > 0) {
+ p->breadcrumb[p->breadcrumb_len - 1][0] = '\0';
+ p->breadcrumb_len--;
+ }
+ nsvg__popAttr(p);
}
else if (strcmp(el, "path") == 0) {
+ /* Remove the breadcrumb level. */
+ if (p->breadcrumb_len > 0) {
+ p->breadcrumb[p->breadcrumb_len - 1][0] = '\0';
+ p->breadcrumb_len--;
+ }
+
nsvg__popAttr(p);
} else if (strcmp(el, "path") == 0) {
p->pathFlag = 0;
} else if (strcmp(el, "defs") == 0) {
p->defsFlag = 0;
}
}
static void nsvg__content(void* ud, const char* s)
{
NSVG_NOTUSED(ud);
NSVG_NOTUSED(s);
// empty
}
static void nsvg__imageBounds(NSVGparser* p, float* bounds)
{
NSVGshape* shape;
shape = p->image->shapes;
if (shape == NULL) {
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
return;
}
bounds[0] = shape->bounds[0];
bounds[1] = shape->bounds[1];
bounds[2] = shape->bounds[2];
bounds[3] = shape->bounds[3];
for (shape = shape->next; shape != NULL; shape = shape->next) {
bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);

View File

@ -1253,7 +1253,9 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
klights[light_index].distant.eval_fac = (light->normalize && angle > 0) ?
M_1_PI_F / sqr(sinf(angle)) :
1.0f;
klights[light_index].distant.half_inv_sin_half_angle = 0.5f / sinf(0.5f * angle);
klights[light_index].distant.half_inv_sin_half_angle = (angle == 0.0f) ?
0.0f :
0.5f / sinf(0.5f * angle);
}
else if (light->light_type == LIGHT_BACKGROUND) {
uint visibility = scene->background->get_visibility();

View File

@ -77,7 +77,7 @@ static GHOST_TButton convertButton(int button)
* \param recvChar: the character ignoring modifiers (except for shift)
* \return Ghost key code
*/
static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
static GHOST_TKey convertKey(int rawCode, unichar recvChar)
{
// printf("\nrecvchar %c 0x%x",recvChar,recvChar);
switch (rawCode) {
@ -238,7 +238,10 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
return GHOST_kKeyUpPage;
case kVK_PageDown:
return GHOST_kKeyDownPage;
#if 0 /* TODO: why are these commented? */
#if 0
/* These constants with "ANSI" in the name are labeled according to the key position on an
* ANSI-standard US keyboard. Therefore they may not match the physical key label on other
* keyboard layouts. */
case kVK_ANSI_Minus: return GHOST_kKeyMinus;
case kVK_ANSI_Equal: return GHOST_kKeyEqual;
case kVK_ANSI_Comma: return GHOST_kKeyComma;
@ -284,10 +287,10 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
UCKeyTranslate((UCKeyboardLayout *)CFDataGetBytePtr(uchrHandle),
rawCode,
keyAction,
kUCKeyActionDown,
0,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
kUCKeyTranslateNoDeadKeysMask,
&deadKeyState,
1,
&actualStrLength,
@ -1830,18 +1833,13 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
case NSEventTypeKeyDown:
case NSEventTypeKeyUp:
/* Returns an empty string for dead keys. */
charsIgnoringModifiers = [event charactersIgnoringModifiers];
if ([charsIgnoringModifiers length] > 0) {
keyCode = convertKey([event keyCode],
[charsIgnoringModifiers characterAtIndex:0],
[event type] == NSEventTypeKeyDown ? kUCKeyActionDown :
kUCKeyActionUp);
keyCode = convertKey([event keyCode], [charsIgnoringModifiers characterAtIndex:0]);
}
else {
keyCode = convertKey([event keyCode],
0,
[event type] == NSEventTypeKeyDown ? kUCKeyActionDown :
kUCKeyActionUp);
keyCode = convertKey([event keyCode], 0);
}
characters = [event characters];

View File

@ -464,24 +464,27 @@ GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y)
GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
{
bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
/* `GetAsyncKeyState` returns the current interrupt-level state of the hardware, which is needed
* when passing key states to a newly-activated window - #40059. Alterative `GetKeyState` only
* returns the state as processed by the thread's message queue. */
bool down = HIBYTE(::GetAsyncKeyState(VK_LSHIFT)) != 0;
keys.set(GHOST_kModifierKeyLeftShift, down);
down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_RSHIFT)) != 0;
keys.set(GHOST_kModifierKeyRightShift, down);
down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_LMENU)) != 0;
keys.set(GHOST_kModifierKeyLeftAlt, down);
down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_RMENU)) != 0;
keys.set(GHOST_kModifierKeyRightAlt, down);
down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_LCONTROL)) != 0;
keys.set(GHOST_kModifierKeyLeftControl, down);
down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_RCONTROL)) != 0;
keys.set(GHOST_kModifierKeyRightControl, down);
down = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_LWIN)) != 0;
keys.set(GHOST_kModifierKeyLeftOS, down);
down = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
down = HIBYTE(::GetAsyncKeyState(VK_RWIN)) != 0;
keys.set(GHOST_kModifierKeyRightOS, down);
return GHOST_kSuccess;
@ -1207,16 +1210,11 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA
bool is_repeat = false;
bool is_repeated_modifier = false;
if (key_down) {
if (system->m_keycode_last_repeat_key == vk) {
if (HIBYTE(::GetKeyState(vk)) != 0) {
/* This thread's message queue shows this key as already down. */
is_repeat = true;
is_repeated_modifier = GHOST_KEY_MODIFIER_CHECK(key);
}
system->m_keycode_last_repeat_key = vk;
}
else {
if (system->m_keycode_last_repeat_key == vk) {
system->m_keycode_last_repeat_key = 0;
}
}
/* We used to check `if (key != GHOST_kKeyUnknown)`, but since the message
@ -1992,7 +1990,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam,
* so the window is activated immediately. */
system->m_wheelDeltaAccum = 0;
system->m_keycode_last_repeat_key = 0;
event = processWindowEvent(
LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
/* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL

View File

@ -455,8 +455,6 @@ class GHOST_SystemWin32 : public GHOST_System {
*/
bool setConsoleWindowState(GHOST_TConsoleWindowState action);
/** The virtual-key code (VKey) of the last press event. Used to detect repeat events. */
unsigned short m_keycode_last_repeat_key;
/** State variable set at initialization. */
bool m_hasPerformanceCounter;
/** High frequency timer variable. */

View File

@ -49,6 +49,7 @@
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_memarena.h"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@ -1617,8 +1618,14 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
return best_root_id_candidate;
}
static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID *id_from)
static void lib_override_root_hierarchy_set(
Main *bmain, ID *id_root, ID *id, ID *id_from, blender::Set<ID *> &processed_ids)
{
if (processed_ids.contains(id)) {
/* This ID has already been checked as having a valid hierarchy root, do not attempt to replace
* it with another one just because it is also used by another liboverride hierarchy. */
return;
}
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
if (id->override_library->hierarchy_root == id_root) {
/* Already set, nothing else to do here, sub-hierarchy is also assumed to be properly set
@ -1688,6 +1695,15 @@ static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID
}
}
CLOG_INFO(&LOG,
3,
"Modifying library override hierarchy of ID '%s'.\n"
"\tFrom old root '%s' to new root '%s'.",
id->name,
id->override_library->hierarchy_root ? id->override_library->hierarchy_root->name :
"<NONE>",
id_root->name);
id->override_library->hierarchy_root = id_root;
}
@ -1712,7 +1728,7 @@ static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID
}
/* Recursively process the sub-hierarchy. */
lib_override_root_hierarchy_set(bmain, id_root, to_id, id);
lib_override_root_hierarchy_set(bmain, id_root, to_id, id, processed_ids);
}
}
@ -1721,9 +1737,11 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain)
ID *id;
BKE_main_relations_create(bmain, 0);
blender::Set<ID *> processed_ids;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
processed_ids.add(id);
continue;
}
if (id->override_library->hierarchy_root != nullptr) {
@ -1741,6 +1759,8 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain)
id->override_library->hierarchy_root = nullptr;
}
else {
/* This ID is considered as having a valid hierarchy root. */
processed_ids.add(id);
continue;
}
}
@ -1751,16 +1771,20 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain)
ID *id_root = lib_override_root_find(bmain, id, best_level, &best_level);
if (!ELEM(id_root->override_library->hierarchy_root, id_root, nullptr)) {
/* FIXME This is probably never actually reached with current code? Check above for non-null
* hierarchy root pointer either skip the rest of the loop, or reset it to nullptr. */
CLOG_WARN(&LOG,
"Potential inconsistency in library override hierarchy of ID '%s', detected as "
"part of the hierarchy of '%s', which has a different root '%s'",
"Potential inconsistency in library override hierarchy of ID '%s' (current root "
"%s), detected as part of the hierarchy of '%s' (current root '%s')",
id->name,
id->override_library->hierarchy_root->name,
id_root->name,
id_root->override_library->hierarchy_root->name);
processed_ids.add(id);
continue;
}
lib_override_root_hierarchy_set(bmain, id_root, id, nullptr);
lib_override_root_hierarchy_set(bmain, id_root, id, nullptr, processed_ids);
BLI_assert(id->override_library->hierarchy_root != nullptr);
}

View File

@ -398,6 +398,8 @@ void DeferredLayer::begin_sync()
gbuffer_single_sided_ps_ = &gbuffer_ps_.sub("SingleSided");
gbuffer_single_sided_ps_->state_set(state | DRW_STATE_CULL_BACK);
}
closure_bits_ = CLOSURE_NONE;
}
void DeferredLayer::end_sync()

View File

@ -162,7 +162,7 @@ class DeferredLayer {
PassSimple eval_light_ps_ = {"EvalLights"};
/* Closures bits from the materials in this pass. */
eClosureBits closure_bits_;
eClosureBits closure_bits_ = CLOSURE_NONE;
/**
* Accumulation textures for all stages of lighting evaluation (Light, SSR, SSSS, SSGI ...).

View File

@ -81,7 +81,7 @@ void Sampling::end_sync()
sample_ = viewport_sample_;
}
else if (interactive_mode_) {
int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
int interactive_sample_count = interactive_sample_max_;
if (viewport_sample_ < interactive_sample_count) {
/* Loop over the same starting samples. */
@ -98,12 +98,13 @@ void Sampling::end_sync()
void Sampling::step()
{
{
uint64_t sample_filter = sample_ % interactive_sample_aa_;
/* TODO(fclem) we could use some persistent states to speedup the computation. */
double2 r, offset = {0, 0};
/* Using 2,3 primes as per UE4 Temporal AA presentation.
* http://advances.realtimerendering.com/s2014/epic/TemporalAA.pptx (slide 14) */
uint2 primes = {2, 3};
BLI_halton_2d(primes, offset, sample_ + 1, r);
BLI_halton_2d(primes, offset, sample_filter + 1, r);
/* WORKAROUND: We offset the distribution to make the first sample (0,0). This way, we are
* assured that at least one of the samples inside the TAA rotation will match the one from the
* draw manager. This makes sure overlays are correctly composited in static scene. */

View File

@ -31,7 +31,8 @@ class Sampling {
/* High number of sample for viewport infinite rendering. */
static constexpr uint64_t infinite_sample_count_ = 0xFFFFFFu;
/* During interactive rendering, loop over the first few samples. */
static constexpr uint64_t interactive_sample_max_ = 8;
static constexpr uint64_t interactive_sample_aa_ = 8;
static constexpr uint64_t interactive_sample_max_ = interactive_sample_aa_;
/** 0 based current sample. Might not increase sequentially in viewport. */
uint64_t sample_ = 0;

View File

@ -966,6 +966,7 @@ BLI_STATIC_ASSERT_ALIGN(HiZData, 16)
* \{ */
enum eClosureBits : uint32_t {
CLOSURE_NONE = 0u,
/** NOTE: These are used as stencil bits. So we are limited to 8bits. */
CLOSURE_DIFFUSE = (1u << 0u),
CLOSURE_SSS = (1u << 1u),

View File

@ -766,7 +766,15 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op)
case SIMEDGE_CREASE: {
has_custom_data_layer = CustomData_has_layer_named(
&bm->edata, CD_PROP_FLOAT, "crease_edge");
ATTR_FALLTHROUGH;
if (!has_custom_data_layer) {
/* Proceed only if we have to select all the edges that have custom data value of 0.0f.
* In this case we will just select all the edges.
* Otherwise continue the for loop. */
if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
continue;
}
}
break;
}
case SIMEDGE_BEVEL: {
has_custom_data_layer = CustomData_has_layer_named(
@ -779,6 +787,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op)
continue;
}
}
break;
}
}

View File

@ -162,7 +162,7 @@ typedef struct RenderEngine {
void *update_render_passes_data;
/* GPU context. */
void *wm_blender_gpu_context; /* WindowManager GPU context -> GHOSTContext. */
void *system_gpu_context; /* WindowManager GPU context -> GHOSTContext. */
ThreadMutex blender_gpu_context_mutex;
bool use_drw_render_context;
struct GPUContext *blender_gpu_context;

View File

@ -1287,17 +1287,17 @@ bool RE_engine_gpu_context_create(RenderEngine *engine)
BLI_assert(BLI_thread_is_main());
const bool drw_state = DRW_gpu_context_release();
engine->wm_blender_gpu_context = WM_system_gpu_context_create();
engine->system_gpu_context = WM_system_gpu_context_create();
if (engine->wm_blender_gpu_context) {
if (engine->system_gpu_context) {
/* Activate new GPU Context for GPUContext creation. */
WM_system_gpu_context_activate(engine->wm_blender_gpu_context);
WM_system_gpu_context_activate(engine->system_gpu_context);
/* Requires GPUContext for usage of GPU Module for displaying results. */
engine->blender_gpu_context = GPU_context_create(nullptr, engine->wm_blender_gpu_context);
engine->blender_gpu_context = GPU_context_create(nullptr, engine->system_gpu_context);
GPU_context_active_set(nullptr);
/* Deactivate newly created GPU Context, as it is not needed until
* `RE_engine_gpu_context_enable` is called. */
WM_system_gpu_context_release(engine->wm_blender_gpu_context);
WM_system_gpu_context_release(engine->system_gpu_context);
}
else {
engine->blender_gpu_context = nullptr;
@ -1305,18 +1305,18 @@ bool RE_engine_gpu_context_create(RenderEngine *engine)
DRW_gpu_context_activate(drw_state);
return engine->wm_blender_gpu_context != nullptr;
return engine->system_gpu_context != nullptr;
}
void RE_engine_gpu_context_destroy(RenderEngine *engine)
{
if (!engine->wm_blender_gpu_context) {
if (!engine->system_gpu_context) {
return;
}
const bool drw_state = DRW_gpu_context_release();
WM_system_gpu_context_activate(engine->wm_blender_gpu_context);
WM_system_gpu_context_activate(engine->system_gpu_context);
if (engine->blender_gpu_context) {
GPUContext *restore_context = GPU_context_active_get();
GPU_context_active_set(engine->blender_gpu_context);
@ -1326,7 +1326,8 @@ void RE_engine_gpu_context_destroy(RenderEngine *engine)
}
engine->blender_gpu_context = nullptr;
}
WM_system_gpu_context_dispose(engine->wm_blender_gpu_context);
WM_system_gpu_context_dispose(engine->system_gpu_context);
engine->system_gpu_context = nullptr;
DRW_gpu_context_activate(drw_state);
}
@ -1338,14 +1339,14 @@ bool RE_engine_gpu_context_enable(RenderEngine *engine)
DRW_render_context_enable(engine->re);
return true;
}
if (engine->wm_blender_gpu_context) {
if (engine->system_gpu_context) {
BLI_mutex_lock(&engine->blender_gpu_context_mutex);
/* If a previous GPU/GPUContext was active (DST.blender_gpu_context), we should later
* restore this when disabling the RenderEngine context. */
engine->gpu_restore_context = DRW_gpu_context_release();
/* Activate RenderEngine System and Blender GPU Context. */
WM_system_gpu_context_activate(engine->wm_blender_gpu_context);
WM_system_gpu_context_activate(engine->system_gpu_context);
if (engine->blender_gpu_context) {
GPU_context_active_set(engine->blender_gpu_context);
GPU_render_begin();
@ -1361,12 +1362,12 @@ void RE_engine_gpu_context_disable(RenderEngine *engine)
DRW_render_context_disable(engine->re);
}
else {
if (engine->wm_blender_gpu_context) {
if (engine->system_gpu_context) {
if (engine->blender_gpu_context) {
GPU_render_end();
GPU_context_active_set(nullptr);
}
WM_system_gpu_context_release(engine->wm_blender_gpu_context);
WM_system_gpu_context_release(engine->system_gpu_context);
/* Restore DRW state context if previously active. */
DRW_gpu_context_activate(engine->gpu_restore_context);
BLI_mutex_unlock(&engine->blender_gpu_context_mutex);
@ -1380,7 +1381,7 @@ void RE_engine_gpu_context_lock(RenderEngine *engine)
/* Locking already handled by the draw manager. */
}
else {
if (engine->wm_blender_gpu_context) {
if (engine->system_gpu_context) {
BLI_mutex_lock(&engine->blender_gpu_context_mutex);
}
}
@ -1392,7 +1393,7 @@ void RE_engine_gpu_context_unlock(RenderEngine *engine)
/* Locking already handled by the draw manager. */
}
else {
if (engine->wm_blender_gpu_context) {
if (engine->system_gpu_context) {
BLI_mutex_unlock(&engine->blender_gpu_context_mutex);
}
}

View File

@ -1267,24 +1267,8 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
break;
}
case GHOST_kEventWindowActivate: {
#ifdef WIN32
/* NOTE(@ideasman42): Alt-Tab on Windows-10 (22H2) can deactivate the window,
* then (in rare cases - approx 1 in 20) immediately call `WM_ACTIVATE` on the window
* (which isn't active) and doesn't receive modifier release events.
* This looks like a bug in MS-Windows, searching online other apps
* have run into similar issues although it's not clear exactly which.
*
* - Therefor activation must always clear modifiers
* or Alt-Tab can occasionally get stuck, see: #105381.
* - Unfortunately modifiers that are held before
* the window is active are ignored, see: #40059.
*/
wm_window_update_eventstate_modifiers_clear(wm, win);
#else
/* Ensure the event state matches modifiers (window was inactive). */
wm_window_update_eventstate_modifiers(wm, win);
#endif
/* Entering window, update mouse position (without sending an event). */
wm_window_update_eventstate(win);