diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index d129ff1c2a..eb0c420faf 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -188,6 +188,7 @@ endif () include(JPEG/JPEG.cmake) include(TIFF/TIFF.cmake) +include(NanoSVG/NanoSVG.cmake) include(wxWidgets/wxWidgets.cmake) set(_dep_list diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake index ae2832577a..76ffc0a8d6 100644 --- a/deps/GLEW/GLEW.cmake +++ b/deps/GLEW/GLEW.cmake @@ -4,8 +4,8 @@ find_package(OpenGL QUIET REQUIRED) prusaslicer_add_cmake_project( GLEW - URL https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip - URL_HASH SHA256=2700383d4de2455f06114fbaf872684f15529d4bdc5cdea69b5fb0e9aa7763f1 + URL https://sourceforge.net/projects/glew/files/glew/2.2.0/glew-2.2.0.zip + URL_HASH SHA256=a9046a913774395a095edcc0b0ac2d81c3aacca61787b39839b941e9be14e0d4 SOURCE_SUBDIR build/cmake CMAKE_ARGS -DBUILD_UTILS=OFF diff --git a/deps/NanoSVG/NanoSVG.cmake b/deps/NanoSVG/NanoSVG.cmake new file mode 100644 index 0000000000..9623d32260 --- /dev/null +++ b/deps/NanoSVG/NanoSVG.cmake @@ -0,0 +1,4 @@ +prusaslicer_add_cmake_project(NanoSVG + URL https://github.com/memononen/nanosvg/archive/4c8f0139b62c6e7faa3b67ce1fbe6e63590ed148.zip + URL_HASH SHA256=584e084af1a75bf633f79753ce2f6f6ec8686002ca27f35f1037c25675fecfb6 +) \ No newline at end of file diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index bf5fd62894..29374974bd 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -1,5 +1,3 @@ -set(_wx_git_tag v3.1.4-patched) - set(_wx_toolkit "") if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(_gtk_ver 2) @@ -15,11 +13,9 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for endif() prusaslicer_add_cmake_project(wxWidgets - # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - # GIT_TAG tm_cross_compile #${_wx_git_tag} - URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip - URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b - DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG + URL https://github.com/prusa3d/wxWidgets/archive/2a0b365df947138c513a888d707d46248d78a341.zip + URL_HASH SHA256=9ab05cd5179196fad4ae702c78eaae9418e73a402cfd390f7438e469b13eb735 + DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG CMAKE_ARGS -DwxBUILD_PRECOMP=ON ${_wx_toolkit} @@ -32,13 +28,16 @@ prusaslicer_add_cmake_project(wxWidgets -DwxUSE_OPENGL=ON -DwxUSE_LIBPNG=sys -DwxUSE_ZLIB=sys - -DwxUSE_REGEX=builtin + -DwxUSE_NANOSVG=sys + -DwxUSE_NANOSVG_EXTERNAL=ON + -DwxUSE_REGEX=OFF -DwxUSE_LIBXPM=builtin -DwxUSE_LIBJPEG=sys -DwxUSE_LIBTIFF=sys -DwxUSE_EXPAT=sys -DwxUSE_LIBSDL=OFF -DwxUSE_XTEST=OFF + -DwxUSE_GLCANVAS_EGL=OFF ) if (MSVC) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 801760b8cf..a0becdab96 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,18 +58,25 @@ if (SLIC3R_GUI) "Hint: On Linux you can set -DSLIC3R_WX_STABLE=1 to use wxWidgets 3.0\n") endif () endif () + + include(${wxWidgets_USE_FILE}) else () - find_package(wxWidgets 3.1 REQUIRED COMPONENTS html adv gl core base) + find_package(wxWidgets 3.1 COMPONENTS html adv gl core base) + if (NOT wxWidgets_FOUND) + message(STATUS "Trying to find wxWidgets in CONFIG mode...") + find_package(wxWidgets 3.2 CONFIG REQUIRED COMPONENTS html adv gl core base) + else () + include(${wxWidgets_USE_FILE}) + endif () endif () if(UNIX) message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}") endif() - include(${wxWidgets_USE_FILE}) - find_package(JPEG QUIET) find_package(TIFF QUIET) + find_package(NanoSVG REQUIRED) string(REGEX MATCH "wxpng" WX_PNG_BUILTIN ${wxWidgets_LIBRARIES}) if (PNG_FOUND AND NOT WX_PNG_BUILTIN) @@ -105,7 +112,10 @@ if (SLIC3R_GUI) # wrong libs for opengl in the link line and it does not link to it by himself. # libslic3r_gui will link to opengl anyway, so lets override wx list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL) - + + if (UNIX AND NOT APPLE) + list(APPEND wxWidgets_LIBRARIES X11 wayland-client wayland-egl EGL) + endif () # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") diff --git a/src/nanosvg/README-prusa.txt b/src/nanosvg/README-prusa.txt deleted file mode 100644 index 8388aa8ef3..0000000000 --- a/src/nanosvg/README-prusa.txt +++ /dev/null @@ -1 +0,0 @@ -Upstream source: https://github.com/memononen/nanosvg/tree/c1f6e209c16b18b46aa9f45d7e619acf42c29726 \ No newline at end of file diff --git a/src/nanosvg/nanosvg.h b/src/nanosvg/nanosvg.h deleted file mode 100644 index 57bcb7c2c1..0000000000 --- a/src/nanosvg/nanosvg.h +++ /dev/null @@ -1,2979 +0,0 @@ -/* - * 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 - * - */ - -#ifndef NANOSVG_H -#define NANOSVG_H - -#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: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - printf("size: %f x %f\n", image->width, image->height); - // Use... - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - for (int i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); - } - } - } - // Delete - nsvgDelete(image); -*/ - -enum NSVGpaintType { - NSVG_PAINT_NONE = 0, - NSVG_PAINT_COLOR = 1, - NSVG_PAINT_LINEAR_GRADIENT = 2, - NSVG_PAINT_RADIAL_GRADIENT = 3 -}; - -enum NSVGspreadType { - NSVG_SPREAD_PAD = 0, - NSVG_SPREAD_REFLECT = 1, - NSVG_SPREAD_REPEAT = 2 -}; - -enum NSVGlineJoin { - NSVG_JOIN_MITER = 0, - NSVG_JOIN_ROUND = 1, - NSVG_JOIN_BEVEL = 2 -}; - -enum NSVGlineCap { - NSVG_CAP_BUTT = 0, - NSVG_CAP_ROUND = 1, - NSVG_CAP_SQUARE = 2 -}; - -enum NSVGfillRule { - NSVG_FILLRULE_NONZERO = 0, - NSVG_FILLRULE_EVENODD = 1 -}; - -enum NSVGflags { - NSVG_FLAGS_VISIBLE = 0x01 -}; - -typedef struct NSVGgradientStop { - unsigned int color; - float offset; -} NSVGgradientStop; - -typedef struct NSVGgradient { - float xform[6]; - char spread; - float fx, fy; - int nstops; - NSVGgradientStop stops[1]; -} NSVGgradient; - -typedef struct NSVGpaint { - 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 - 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]. - 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); - -// Parses SVG file from a null terminated string, returns SVG image as paths. -// Important note: changes the string. -NSVGimage* nsvgParse(char* input, const char* units, float dpi); - -// Duplicates a path. -NSVGpath* nsvgDuplicatePath(NSVGpath* p); - -// Deletes an image. -void nsvgDelete(NSVGimage* image); - -#ifndef NANOSVG_CPLUSPLUS -#ifdef __cplusplus -} -#endif -#endif - -#endif // NANOSVG_H - -#ifdef NANOSVG_IMPLEMENTATION - -#include -#include -#include - -#include - -#define NSVG_PI (3.14159265358979323846264338327f) -#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NSVG_ALIGN_MIN 0 -#define NSVG_ALIGN_MID 1 -#define NSVG_ALIGN_MAX 2 -#define NSVG_ALIGN_NONE 0 -#define NSVG_ALIGN_MEET 1 -#define NSVG_ALIGN_SLICE 2 - -#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) -#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) - -#ifdef _MSC_VER - #pragma warning (disable: 4996) // Switch off security warnings - #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings - #ifdef __cplusplus - #define NSVG_INLINE inline - #else - #define NSVG_INLINE - #endif -#else - #define NSVG_INLINE inline -#endif - - -static int nsvg__isspace(char c) -{ - return strchr(" \t\n\v\f\r", c) != 0; -} - -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; } - - -// Simple XML parser - -#define NSVG_XML_TAG 1 -#define NSVG_XML_CONTENT 2 -#define NSVG_XML_MAX_ATTRIBS 256 - -static void nsvg__parseContent(char* s, - void (*contentCb)(void* ud, const char* s), - void* ud) -{ - // Trim start white spaces - while (*s && nsvg__isspace(*s)) s++; - if (!*s) return; - - if (contentCb) - (*contentCb)(ud, s); -} - -static void nsvg__parseElement(char* s, - void (*startelCb)(void* ud, const char* el, const char** attr), - void (*endelCb)(void* ud, const char* el), - void* ud) -{ - const char* attr[NSVG_XML_MAX_ATTRIBS]; - int nattr = 0; - char* name; - int start = 0; - int end = 0; - char quote; - - // Skip white space after the '<' - while (*s && nsvg__isspace(*s)) s++; - - // Check if the tag is end tag - if (*s == '/') { - s++; - end = 1; - } else { - start = 1; - } - - // Skip comments, data and preprocessor stuff. - if (!*s || *s == '?' || *s == '!') - return; - - // Get tag name - name = s; - while (*s && !nsvg__isspace(*s)) s++; - if (*s) { *s++ = '\0'; } - - // Get attribs - while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { - char* name = NULL; - char* value = NULL; - - // Skip white space before the attrib name - while (*s && nsvg__isspace(*s)) s++; - if (!*s) break; - if (*s == '/') { - end = 1; - break; - } - name = s; - // Find end of the attrib name. - while (*s && !nsvg__isspace(*s) && *s != '=') s++; - if (*s) { *s++ = '\0'; } - // Skip until the beginning of the value. - while (*s && *s != '\"' && *s != '\'') s++; - if (!*s) break; - quote = *s; - s++; - // Store value and find the end of it. - value = s; - while (*s && *s != quote) s++; - if (*s) { *s++ = '\0'; } - - // Store only well formed attributes - if (name && value) { - attr[nattr++] = name; - attr[nattr++] = value; - } - } - - // List terminator - attr[nattr++] = 0; - attr[nattr++] = 0; - - // Call callbacks. - if (start && startelCb) - (*startelCb)(ud, name, attr); - if (end && endelCb) - (*endelCb)(ud, name); -} - -int nsvg__parseXML(char* input, - void (*startelCb)(void* ud, const char* el, const char** attr), - void (*endelCb)(void* ud, const char* el), - void (*contentCb)(void* ud, const char* s), - 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 - -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; - -typedef struct NSVGradialData { - NSVGcoordinate cx, cy, r, fx, fy; -} NSVGradialData; - -typedef struct NSVGgradientData -{ - char id[64]; - char ref[64]; - char type; - union { - NSVGlinearData linear; - NSVGradialData radial; - }; - char spread; - char units; - float xform[6]; - int nstops; - NSVGgradientStop* stops; - struct NSVGgradientData* next; -} NSVGgradientData; - -typedef struct NSVGattrib -{ - char id[64]; - float xform[6]; - unsigned int fillColor; - unsigned int strokeColor; - float opacity; - float fillOpacity; - float strokeOpacity; - char fillGradient[64]; - char strokeGradient[64]; - float strokeWidth; - float strokeDashOffset; - float strokeDashArray[NSVG_MAX_DASHES]; - 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 -{ - 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; -} NSVGparser; - -static void nsvg__xformIdentity(float* t) -{ - 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; -} - -static void nsvg__xformSetSkewY(float* t, float a) -{ - t[0] = 1.0f; t[1] = tanf(a); - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetRotation(float* t, float a) -{ - float cs = cosf(a), sn = sinf(a); - t[0] = cs; t[1] = sn; - t[2] = -sn; t[3] = cs; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformMultiply(float* t, float* s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -static void nsvg__xformInverse(float* inv, float* t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nsvg__xformIdentity(t); - return; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); -} - -static void nsvg__xformPremultiply(float* t, float* s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float)*6); - nsvg__xformMultiply(s2, t); - memcpy(t, s2, sizeof(float)*6); -} - -static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) -{ - *dx = x*t[0] + y*t[2] + t[4]; - *dy = x*t[1] + y*t[3] + t[5]; -} - -static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) -{ - *dx = x*t[0] + y*t[2]; - *dy = x*t[1] + y*t[3]; -} - -#define NSVG_EPSILON (1e-12) - -static int nsvg__ptInBounds(float* pt, float* bounds) -{ - return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; -} - - -static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) -{ - double it = 1.0-t; - return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; -} - -static void nsvg__curveBounds(float* bounds, float* curve) -{ - int i, j, count; - double roots[2], a, b, c, b2ac, t, v; - float* v0 = &curve[0]; - float* v1 = &curve[2]; - float* v2 = &curve[4]; - float* v3 = &curve[6]; - - // Start the bounding box by end points - bounds[0] = nsvg__minf(v0[0], v3[0]); - bounds[1] = nsvg__minf(v0[1], v3[1]); - bounds[2] = nsvg__maxf(v0[0], v3[0]); - bounds[3] = nsvg__maxf(v0[1], v3[1]); - - // Bezier curve fits inside the convex hull of it's control points. - // If control points are inside the bounds, we're done. - if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) - return; - - // Add bezier curve inflection points in X and Y. - for (i = 0; i < 2; i++) { - a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; - b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; - c = 3.0 * v1[i] - 3.0 * v0[i]; - count = 0; - if (fabs(a) < NSVG_EPSILON) { - if (fabs(b) > NSVG_EPSILON) { - t = -c / b; - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - } - } else { - b2ac = b*b - 4.0*c*a; - if (b2ac > NSVG_EPSILON) { - t = (-b + sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - t = (-b - sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - } - } - for (j = 0; j < count; j++) { - v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); - bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); - bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); - } - } -} - -static NSVGparser* nsvg__createParser() -{ - NSVGparser* p; - p = (NSVGparser*)malloc(sizeof(NSVGparser)); - if (p == NULL) goto error; - memset(p, 0, sizeof(NSVGparser)); - - p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); - if (p->image == NULL) goto error; - memset(p->image, 0, sizeof(NSVGimage)); - - // Init style - nsvg__xformIdentity(p->attr[0].xform); - memset(p->attr[0].id, 0, sizeof p->attr[0].id); - p->attr[0].fillColor = NSVG_RGB(0,0,0); - p->attr[0].strokeColor = NSVG_RGB(0,0,0); - p->attr[0].opacity = 1; - p->attr[0].fillOpacity = 1; - p->attr[0].strokeOpacity = 1; - p->attr[0].stopOpacity = 1; - p->attr[0].strokeWidth = 1; - p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; - p->attr[0].strokeLineCap = NSVG_CAP_BUTT; - p->attr[0].miterLimit = 4; - p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; - p->attr[0].hasFill = 1; - p->attr[0].visible = 1; - - return p; - -error: - if (p) { - if (p->image) free(p->image); - free(p); - } - return NULL; -} - -static void nsvg__deletePaths(NSVGpath* path) -{ - while (path) { - NSVGpath *next = path->next; - if (path->pts != NULL) - free(path->pts); - free(path); - path = next; - } -} - -static void nsvg__deletePaint(NSVGpaint* paint) -{ - if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) - free(paint->gradient); -} - -static void nsvg__deleteGradientData(NSVGgradientData* grad) -{ - NSVGgradientData* next; - while (grad != NULL) { - next = grad->next; - free(grad->stops); - free(grad); - grad = next; - } -} - -static void nsvg__deleteParser(NSVGparser* p) -{ - if (p != NULL) { - nsvg__deletePaths(p->plist); - nsvg__deleteGradientData(p->gradients); - nsvgDelete(p->image); - free(p->pts); - free(p); - } -} - -static void nsvg__resetPath(NSVGparser* p) -{ - p->npts = 0; -} - -static void nsvg__addPoint(NSVGparser* p, float x, float y) -{ - if (p->npts+1 > p->cpts) { - p->cpts = p->cpts ? p->cpts*2 : 8; - p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); - if (!p->pts) return; - } - p->pts[p->npts*2+0] = x; - p->pts[p->npts*2+1] = y; - p->npts++; -} - -static void nsvg__moveTo(NSVGparser* p, float x, float y) -{ - if (p->npts > 0) { - p->pts[(p->npts-1)*2+0] = x; - p->pts[(p->npts-1)*2+1] = y; - } else { - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__lineTo(NSVGparser* p, float x, float y) -{ - float px,py, dx,dy; - if (p->npts > 0) { - px = p->pts[(p->npts-1)*2+0]; - py = p->pts[(p->npts-1)*2+1]; - dx = x - px; - dy = y - py; - nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); - nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) -{ - nsvg__addPoint(p, cpx1, cpy1); - nsvg__addPoint(p, cpx2, cpy2); - nsvg__addPoint(p, x, y); -} - -static NSVGattrib* nsvg__getAttr(NSVGparser* p) -{ - return &p->attr[p->attrHead]; -} - -static void nsvg__pushAttr(NSVGparser* p) -{ - if (p->attrHead < NSVG_MAX_ATTR-1) { - p->attrHead++; - memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); - } -} - -static void nsvg__popAttr(NSVGparser* p) -{ - if (p->attrHead > 0) - p->attrHead--; -} - -static float nsvg__actualOrigX(NSVGparser* p) -{ - return p->viewMinx; -} - -static float nsvg__actualOrigY(NSVGparser* p) -{ - return p->viewMiny; -} - -static float nsvg__actualWidth(NSVGparser* p) -{ - return p->viewWidth; -} - -static float nsvg__actualHeight(NSVGparser* p) -{ - return p->viewHeight; -} - -static float nsvg__actualLength(NSVGparser* p) -{ - float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); - return sqrtf(w*w + h*h) / sqrtf(2.0f); -} - -static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) -{ - NSVGattrib* attr = nsvg__getAttr(p); - switch (c.units) { - case NSVG_UNITS_USER: return c.value; - case NSVG_UNITS_PX: return c.value; - case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; - case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; - case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; - case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; - case NSVG_UNITS_IN: return c.value * p->dpi; - case NSVG_UNITS_EM: return c.value * attr->fontSize; - case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. - case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; - default: return c.value; - } - return c.value; -} - -static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) -{ - NSVGgradientData* grad = p->gradients; - while (grad) { - if (strcmp(grad->id, id) == 0) - return grad; - grad = grad->next; - } - return NULL; -} - -static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, 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; - - data = nsvg__findGradientData(p, id); - if (data == NULL) return NULL; - - // TODO: use ref to fill in all unset values too. - ref = data; - while (ref != NULL) { - if (stops == NULL && ref->stops != NULL) { - stops = ref->stops; - nstops = ref->nstops; - break; - } - ref = nsvg__findGradientData(p, ref->ref); - } - if (stops == NULL) return NULL; - - grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); - if (grad == NULL) return NULL; - - // The shape width and height. - if (data->units == NSVG_OBJECT_SPACE) { - ox = localBounds[0]; - oy = localBounds[1]; - sw = localBounds[2] - localBounds[0]; - sh = localBounds[3] - localBounds[1]; - } else { - ox = nsvg__actualOrigX(p); - oy = nsvg__actualOrigY(p); - sw = nsvg__actualWidth(p); - sh = nsvg__actualHeight(p); - } - sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); - - if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { - float x1, y1, x2, y2, dx, dy; - x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); - y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); - x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); - y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); - // Calculate transform aligned to the line - dx = x2 - x1; - dy = y2 - y1; - grad->xform[0] = dy; grad->xform[1] = -dx; - grad->xform[2] = dx; grad->xform[3] = dy; - grad->xform[4] = x1; grad->xform[5] = y1; - } else { - float cx, cy, fx, fy, r; - cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); - cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); - fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); - fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); - r = nsvg__convertToPixels(p, data->radial.r, 0, sl); - // Calculate transform aligned to the circle - grad->xform[0] = r; grad->xform[1] = 0; - grad->xform[2] = 0; grad->xform[3] = r; - grad->xform[4] = cx; grad->xform[5] = cy; - grad->fx = fx / r; - grad->fy = fy / r; - } - - nsvg__xformMultiply(grad->xform, data->xform); - nsvg__xformMultiply(grad->xform, attr->xform); - - grad->spread = data->spread; - memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); - grad->nstops = nstops; - - *paintType = data->type; - - return grad; -} - -static float nsvg__getAverageScale(float* t) -{ - float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); - float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); - return (sx + sy) * 0.5f; -} - -static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) -{ - NSVGpath* path; - float curve[4*2], curveBounds[4]; - int i, first = 1; - for (path = shape->paths; path != NULL; path = path->next) { - nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); - for (i = 0; i < path->npts-1; i += 3) { - nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); - nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); - nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); - nsvg__curveBounds(curveBounds, curve); - if (first) { - bounds[0] = curveBounds[0]; - bounds[1] = curveBounds[1]; - 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); - scale = nsvg__getAverageScale(attr->xform); - shape->strokeWidth = attr->strokeWidth * scale; - shape->strokeDashOffset = attr->strokeDashOffset * scale; - 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]); - } - - // Set fill - if (attr->hasFill == 0) { - shape->fill.type = NSVG_PAINT_NONE; - } else if (attr->hasFill == 1) { - shape->fill.type = NSVG_PAINT_COLOR; - 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; - } - } - - // Set stroke - if (attr->hasStroke == 0) { - shape->stroke.type = NSVG_PAINT_NONE; - } else if (attr->hasStroke == 1) { - shape->stroke.type = NSVG_PAINT_COLOR; - 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; - } - - // Set flags - shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); - - // Add to tail - if (p->image->shapes == NULL) - p->image->shapes = shape; - else - p->shapesTail->next = shape; - p->shapesTail = shape; - - return; - -error: - if (shape) free(shape); -} - -static void nsvg__addPath(NSVGparser* p, char closed) -{ - NSVGattrib* attr = nsvg__getAttr(p); - NSVGpath* path = NULL; - float bounds[4]; - float* curve; - int i; - - if (p->npts < 4) - return; - - if (closed) - nsvg__lineTo(p, p->pts[0], p->pts[1]); - - path = (NSVGpath*)malloc(sizeof(NSVGpath)); - if (path == NULL) goto error; - memset(path, 0, sizeof(NSVGpath)); - - path->pts = (float*)malloc(p->npts*2*sizeof(float)); - if (path->pts == NULL) goto error; - path->closed = closed; - path->npts = p->npts; - - // Transform path. - for (i = 0; i < p->npts; ++i) - nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); - - // Find bounds - for (i = 0; i < path->npts-1; i += 3) { - curve = &path->pts[i*2]; - nsvg__curveBounds(bounds, curve); - if (i == 0) { - path->bounds[0] = bounds[0]; - path->bounds[1] = bounds[1]; - path->bounds[2] = bounds[2]; - path->bounds[3] = bounds[3]; - } else { - path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); - path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); - path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); - path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); - } - } - - path->next = p->plist; - p->plist = path; - - return; - -error: - if (path != NULL) { - if (path->pts != NULL) free(path->pts); - free(path); - } -} - -// We roll our own string to float because the std library one uses locale and messes things up. -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; - char hasIntPart = 0, hasFracPart = 0; - - // Parse optional sign - if (*cur == '+') { - cur++; - } else if (*cur == '-') { - sign = -1; - cur++; - } - - // Parse integer part - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - intPart = (double)strtoll(cur, &end, 10); - if (cur != end) { - res = (double)intPart; - hasIntPart = 1; - cur = end; - } - } - - // Parse fractional part. - if (*cur == '.') { - cur++; // Skip '.' - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - fracPart = strtoll(cur, &end, 10); - if (cur != end) { - res += (double)fracPart / pow(10.0, (double)(end - cur)); - hasFracPart = 1; - cur = end; - } - } - } - - // A valid number should have integer or fractional part. - if (!hasIntPart && !hasFracPart) - return 0.0; - - // Parse optional exponent - if (*cur == 'e' || *cur == 'E') { - int expPart = 0; - cur++; // skip 'E' - expPart = strtol(cur, &end, 10); // Parse digit sequence with sign - if (cur != end) { - res *= pow(10.0, (double)expPart); - } - } - - return res * sign; -} - - -static const char* nsvg__parseNumber(const char* s, char* it, const int size) -{ - const int last = size-1; - int i = 0; - - // sign - if (*s == '-' || *s == '+') { - if (i < last) it[i++] = *s; - s++; - } - // integer part - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - if (*s == '.') { - // decimal point - if (i < last) it[i++] = *s; - s++; - // fraction part - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - } - // exponent - if (*s == 'e' || *s == 'E') { - 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__getNextPathItem(const char* s, char* it) -{ - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - if (!*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 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); -} - -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); - } -} - -typedef struct NSVGNamedColor { - const char* name; - unsigned int color; -} NSVGNamedColor; - -NSVGNamedColor nsvg__colors[] = { - - { "red", NSVG_RGB(255, 0, 0) }, - { "green", NSVG_RGB( 0, 128, 0) }, - { "blue", NSVG_RGB( 0, 0, 255) }, - { "yellow", NSVG_RGB(255, 255, 0) }, - { "cyan", NSVG_RGB( 0, 255, 255) }, - { "magenta", NSVG_RGB(255, 0, 255) }, - { "black", NSVG_RGB( 0, 0, 0) }, - { "grey", NSVG_RGB(128, 128, 128) }, - { "gray", NSVG_RGB(128, 128, 128) }, - { "white", NSVG_RGB(255, 255, 255) }, - -#ifdef NANOSVG_ALL_COLOR_KEYWORDS - { "aliceblue", NSVG_RGB(240, 248, 255) }, - { "antiquewhite", NSVG_RGB(250, 235, 215) }, - { "aqua", NSVG_RGB( 0, 255, 255) }, - { "aquamarine", NSVG_RGB(127, 255, 212) }, - { "azure", NSVG_RGB(240, 255, 255) }, - { "beige", NSVG_RGB(245, 245, 220) }, - { "bisque", NSVG_RGB(255, 228, 196) }, - { "blanchedalmond", NSVG_RGB(255, 235, 205) }, - { "blueviolet", NSVG_RGB(138, 43, 226) }, - { "brown", NSVG_RGB(165, 42, 42) }, - { "burlywood", NSVG_RGB(222, 184, 135) }, - { "cadetblue", NSVG_RGB( 95, 158, 160) }, - { "chartreuse", NSVG_RGB(127, 255, 0) }, - { "chocolate", NSVG_RGB(210, 105, 30) }, - { "coral", NSVG_RGB(255, 127, 80) }, - { "cornflowerblue", NSVG_RGB(100, 149, 237) }, - { "cornsilk", NSVG_RGB(255, 248, 220) }, - { "crimson", NSVG_RGB(220, 20, 60) }, - { "darkblue", NSVG_RGB( 0, 0, 139) }, - { "darkcyan", NSVG_RGB( 0, 139, 139) }, - { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, - { "darkgray", NSVG_RGB(169, 169, 169) }, - { "darkgreen", NSVG_RGB( 0, 100, 0) }, - { "darkgrey", NSVG_RGB(169, 169, 169) }, - { "darkkhaki", NSVG_RGB(189, 183, 107) }, - { "darkmagenta", NSVG_RGB(139, 0, 139) }, - { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, - { "darkorange", NSVG_RGB(255, 140, 0) }, - { "darkorchid", NSVG_RGB(153, 50, 204) }, - { "darkred", NSVG_RGB(139, 0, 0) }, - { "darksalmon", NSVG_RGB(233, 150, 122) }, - { "darkseagreen", NSVG_RGB(143, 188, 143) }, - { "darkslateblue", NSVG_RGB( 72, 61, 139) }, - { "darkslategray", NSVG_RGB( 47, 79, 79) }, - { "darkslategrey", NSVG_RGB( 47, 79, 79) }, - { "darkturquoise", NSVG_RGB( 0, 206, 209) }, - { "darkviolet", NSVG_RGB(148, 0, 211) }, - { "deeppink", NSVG_RGB(255, 20, 147) }, - { "deepskyblue", NSVG_RGB( 0, 191, 255) }, - { "dimgray", NSVG_RGB(105, 105, 105) }, - { "dimgrey", NSVG_RGB(105, 105, 105) }, - { "dodgerblue", NSVG_RGB( 30, 144, 255) }, - { "firebrick", NSVG_RGB(178, 34, 34) }, - { "floralwhite", NSVG_RGB(255, 250, 240) }, - { "forestgreen", NSVG_RGB( 34, 139, 34) }, - { "fuchsia", NSVG_RGB(255, 0, 255) }, - { "gainsboro", NSVG_RGB(220, 220, 220) }, - { "ghostwhite", NSVG_RGB(248, 248, 255) }, - { "gold", NSVG_RGB(255, 215, 0) }, - { "goldenrod", NSVG_RGB(218, 165, 32) }, - { "greenyellow", NSVG_RGB(173, 255, 47) }, - { "honeydew", NSVG_RGB(240, 255, 240) }, - { "hotpink", NSVG_RGB(255, 105, 180) }, - { "indianred", NSVG_RGB(205, 92, 92) }, - { "indigo", NSVG_RGB( 75, 0, 130) }, - { "ivory", NSVG_RGB(255, 255, 240) }, - { "khaki", NSVG_RGB(240, 230, 140) }, - { "lavender", NSVG_RGB(230, 230, 250) }, - { "lavenderblush", NSVG_RGB(255, 240, 245) }, - { "lawngreen", NSVG_RGB(124, 252, 0) }, - { "lemonchiffon", NSVG_RGB(255, 250, 205) }, - { "lightblue", NSVG_RGB(173, 216, 230) }, - { "lightcoral", NSVG_RGB(240, 128, 128) }, - { "lightcyan", NSVG_RGB(224, 255, 255) }, - { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, - { "lightgray", NSVG_RGB(211, 211, 211) }, - { "lightgreen", NSVG_RGB(144, 238, 144) }, - { "lightgrey", NSVG_RGB(211, 211, 211) }, - { "lightpink", NSVG_RGB(255, 182, 193) }, - { "lightsalmon", NSVG_RGB(255, 160, 122) }, - { "lightseagreen", NSVG_RGB( 32, 178, 170) }, - { "lightskyblue", NSVG_RGB(135, 206, 250) }, - { "lightslategray", NSVG_RGB(119, 136, 153) }, - { "lightslategrey", NSVG_RGB(119, 136, 153) }, - { "lightsteelblue", NSVG_RGB(176, 196, 222) }, - { "lightyellow", NSVG_RGB(255, 255, 224) }, - { "lime", NSVG_RGB( 0, 255, 0) }, - { "limegreen", NSVG_RGB( 50, 205, 50) }, - { "linen", NSVG_RGB(250, 240, 230) }, - { "maroon", NSVG_RGB(128, 0, 0) }, - { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, - { "mediumblue", NSVG_RGB( 0, 0, 205) }, - { "mediumorchid", NSVG_RGB(186, 85, 211) }, - { "mediumpurple", NSVG_RGB(147, 112, 219) }, - { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, - { "mediumslateblue", NSVG_RGB(123, 104, 238) }, - { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, - { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, - { "mediumvioletred", NSVG_RGB(199, 21, 133) }, - { "midnightblue", NSVG_RGB( 25, 25, 112) }, - { "mintcream", NSVG_RGB(245, 255, 250) }, - { "mistyrose", NSVG_RGB(255, 228, 225) }, - { "moccasin", NSVG_RGB(255, 228, 181) }, - { "navajowhite", NSVG_RGB(255, 222, 173) }, - { "navy", NSVG_RGB( 0, 0, 128) }, - { "oldlace", NSVG_RGB(253, 245, 230) }, - { "olive", NSVG_RGB(128, 128, 0) }, - { "olivedrab", NSVG_RGB(107, 142, 35) }, - { "orange", NSVG_RGB(255, 165, 0) }, - { "orangered", NSVG_RGB(255, 69, 0) }, - { "orchid", NSVG_RGB(218, 112, 214) }, - { "palegoldenrod", NSVG_RGB(238, 232, 170) }, - { "palegreen", NSVG_RGB(152, 251, 152) }, - { "paleturquoise", NSVG_RGB(175, 238, 238) }, - { "palevioletred", NSVG_RGB(219, 112, 147) }, - { "papayawhip", NSVG_RGB(255, 239, 213) }, - { "peachpuff", NSVG_RGB(255, 218, 185) }, - { "peru", NSVG_RGB(205, 133, 63) }, - { "pink", NSVG_RGB(255, 192, 203) }, - { "plum", NSVG_RGB(221, 160, 221) }, - { "powderblue", NSVG_RGB(176, 224, 230) }, - { "purple", NSVG_RGB(128, 0, 128) }, - { "rosybrown", NSVG_RGB(188, 143, 143) }, - { "royalblue", NSVG_RGB( 65, 105, 225) }, - { "saddlebrown", NSVG_RGB(139, 69, 19) }, - { "salmon", NSVG_RGB(250, 128, 114) }, - { "sandybrown", NSVG_RGB(244, 164, 96) }, - { "seagreen", NSVG_RGB( 46, 139, 87) }, - { "seashell", NSVG_RGB(255, 245, 238) }, - { "sienna", NSVG_RGB(160, 82, 45) }, - { "silver", NSVG_RGB(192, 192, 192) }, - { "skyblue", NSVG_RGB(135, 206, 235) }, - { "slateblue", NSVG_RGB(106, 90, 205) }, - { "slategray", NSVG_RGB(112, 128, 144) }, - { "slategrey", NSVG_RGB(112, 128, 144) }, - { "snow", NSVG_RGB(255, 250, 250) }, - { "springgreen", NSVG_RGB( 0, 255, 127) }, - { "steelblue", NSVG_RGB( 70, 130, 180) }, - { "tan", NSVG_RGB(210, 180, 140) }, - { "teal", NSVG_RGB( 0, 128, 128) }, - { "thistle", NSVG_RGB(216, 191, 216) }, - { "tomato", NSVG_RGB(255, 99, 71) }, - { "turquoise", NSVG_RGB( 64, 224, 208) }, - { "violet", NSVG_RGB(238, 130, 238) }, - { "wheat", NSVG_RGB(245, 222, 179) }, - { "whitesmoke", NSVG_RGB(245, 245, 245) }, - { "yellowgreen", NSVG_RGB(154, 205, 50) }, -#endif -}; - -static unsigned int nsvg__parseColorName(const char* str) -{ - int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); - - for (i = 0; i < ncolors; i++) { - if (strcmp(nsvg__colors[i].name, str) == 0) { - return nsvg__colors[i].color; - } - } - - return NSVG_RGB(128, 128, 128); -} - -static unsigned int nsvg__parseColor(const char* str) -{ - size_t len = 0; - while(*str == ' ') ++str; - len = strlen(str); - if (len >= 1 && *str == '#') - return nsvg__parseColorHex(str); - else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') - return nsvg__parseColorRGB(str); - return nsvg__parseColorName(str); -} - -static float nsvg__parseOpacity(const char* str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) val = 0.0f; - if (val > 1.0f) val = 1.0f; - return val; -} - -static float nsvg__parseMiterLimit(const char* str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) val = 0.0f; - return val; -} - -static int nsvg__parseUnits(const char* units) -{ - if (units[0] == 'p' && units[1] == 'x') - return NSVG_UNITS_PX; - else if (units[0] == 'p' && units[1] == 't') - return NSVG_UNITS_PT; - else if (units[0] == 'p' && units[1] == 'c') - return NSVG_UNITS_PC; - else if (units[0] == 'm' && units[1] == 'm') - return NSVG_UNITS_MM; - else if (units[0] == 'c' && units[1] == 'm') - return NSVG_UNITS_CM; - else if (units[0] == 'i' && units[1] == 'n') - return NSVG_UNITS_IN; - else if (units[0] == '%') - return NSVG_UNITS_PERCENT; - else if (units[0] == 'e' && units[1] == 'm') - return NSVG_UNITS_EM; - else if (units[0] == 'e' && units[1] == 'x') - return NSVG_UNITS_EX; - return NSVG_UNITS_USER; -} - -static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) -{ - NSVGcoordinate coord = {0, NSVG_UNITS_USER}; - char buf[64]; - coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); - coord.value = nsvg__atof(buf); - return coord; -} - -static NSVGcoordinate nsvg__coord(float v, int units) -{ - NSVGcoordinate coord = {v, units}; - return coord; -} - -static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) -{ - NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); - return nsvg__convertToPixels(p, coord, orig, length); -} - -static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) -{ - const char* end; - const char* ptr; - char it[64]; - - *na = 0; - ptr = str; - while (*ptr && *ptr != '(') ++ptr; - if (*ptr == 0) - return 1; - end = ptr; - while (*end && *end != ')') ++end; - if (*end == 0) - return 1; - - while (ptr < end) { - if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { - if (*na >= maxNa) return 0; - ptr = nsvg__parseNumber(ptr, it, 64); - args[(*na)++] = (float)nsvg__atof(it); - } else { - ++ptr; - } - } - return (int)(end - str); -} - - -static int nsvg__parseMatrix(float* xform, const char* str) -{ - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, t, 6, &na); - if (na != 6) return len; - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseTranslate(float* xform, const char* str) -{ - float args[2]; - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) args[1] = 0.0; - - nsvg__xformSetTranslation(t, args[0], args[1]); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseScale(float* xform, const char* str) -{ - float args[2]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) args[1] = args[0]; - nsvg__xformSetScale(t, args[0], args[1]); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseSkewX(float* xform, const char* str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseSkewY(float* xform, const char* str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseRotate(float* xform, const char* str) -{ - float args[3]; - int na = 0; - float m[6]; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 3, &na); - if (na == 1) - args[1] = args[2] = 0.0f; - nsvg__xformIdentity(m); - - if (na > 1) { - nsvg__xformSetTranslation(t, -args[1], -args[2]); - nsvg__xformMultiply(m, t); - } - - nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); - nsvg__xformMultiply(m, t); - - if (na > 1) { - nsvg__xformSetTranslation(t, args[1], args[2]); - nsvg__xformMultiply(m, t); - } - - memcpy(xform, m, sizeof(float)*6); - - return len; -} - -static void nsvg__parseTransform(float* xform, const char* str) -{ - float t[6]; - nsvg__xformIdentity(xform); - while (*str) - { - if (strncmp(str, "matrix", 6) == 0) - str += nsvg__parseMatrix(t, str); - else if (strncmp(str, "translate", 9) == 0) - str += nsvg__parseTranslate(t, str); - else if (strncmp(str, "scale", 5) == 0) - str += nsvg__parseScale(t, str); - else if (strncmp(str, "rotate", 6) == 0) - str += nsvg__parseRotate(t, str); - else if (strncmp(str, "skewX", 5) == 0) - str += nsvg__parseSkewX(t, str); - else if (strncmp(str, "skewY", 5) == 0) - str += nsvg__parseSkewY(t, str); - else{ - ++str; - continue; - } - - nsvg__xformPremultiply(xform, t); - } -} - -static void nsvg__parseUrl(char* id, const char* str) -{ - int i = 0; - str += 4; // "url("; - if (*str == '#') - str++; - while (i < 63 && *str != ')') { - id[i] = *str++; - i++; - } - id[i] = '\0'; -} - -static char nsvg__parseLineCap(const char* str) -{ - if (strcmp(str, "butt") == 0) - return NSVG_CAP_BUTT; - else if (strcmp(str, "round") == 0) - return NSVG_CAP_ROUND; - else if (strcmp(str, "square") == 0) - return NSVG_CAP_SQUARE; - // TODO: handle inherit. - return NSVG_CAP_BUTT; -} - -static char nsvg__parseLineJoin(const char* str) -{ - if (strcmp(str, "miter") == 0) - return NSVG_JOIN_MITER; - else if (strcmp(str, "round") == 0) - return NSVG_JOIN_ROUND; - else if (strcmp(str, "bevel") == 0) - return NSVG_JOIN_BEVEL; - // TODO: handle inherit. - return NSVG_JOIN_MITER; -} - -static char nsvg__parseFillRule(const char* str) -{ - if (strcmp(str, "nonzero") == 0) - return NSVG_FILLRULE_NONZERO; - else if (strcmp(str, "evenodd") == 0) - return NSVG_FILLRULE_EVENODD; - // TODO: handle inherit. - return NSVG_FILLRULE_NONZERO; -} - -static const char* nsvg__getNextDashItem(const char* s, char* it) -{ - int n = 0; - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - // Advance until whitespace, comma or end. - while (*s && (!nsvg__isspace(*s) && *s != ',')) { - if (n < 63) - it[n++] = *s; - s++; - } - it[n++] = '\0'; - return s; -} - -static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) -{ - char item[64]; - int count = 0, i; - float sum = 0.0f; - - // Handle "none" - if (str[0] == 'n') - return 0; - - // Parse dashes - while (*str) { - str = nsvg__getNextDashItem(str, item); - if (!*item) break; - if (count < NSVG_MAX_DASHES) - strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); - } - - for (i = 0; i < count; i++) - sum += strokeDashArray[i]; - if (sum <= 1e-6f) - count = 0; - - return count; -} - -static void nsvg__parseStyle(NSVGparser* p, const char* str); - -static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) -{ - float xform[6]; - NSVGattrib* attr = nsvg__getAttr(p); - if (!attr) return 0; - - if (strcmp(name, "style") == 0) { - nsvg__parseStyle(p, value); - } else if (strcmp(name, "display") == 0) { - if (strcmp(value, "none") == 0) - attr->visible = 0; - // Don't reset ->visible on display:inline, one display:none hides the whole subtree - - } else if (strcmp(name, "fill") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasFill = 0; - } else if (strncmp(value, "url(", 4) == 0) { - attr->hasFill = 2; - nsvg__parseUrl(attr->fillGradient, value); - } else { - attr->hasFill = 1; - attr->fillColor = nsvg__parseColor(value); - } - } else if (strcmp(name, "opacity") == 0) { - attr->opacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "fill-opacity") == 0) { - attr->fillOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "stroke") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasStroke = 0; - } else if (strncmp(value, "url(", 4) == 0) { - attr->hasStroke = 2; - nsvg__parseUrl(attr->strokeGradient, value); - } else { - attr->hasStroke = 1; - attr->strokeColor = nsvg__parseColor(value); - } - } else if (strcmp(name, "stroke-width") == 0) { - attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "stroke-dasharray") == 0) { - attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); - } else if (strcmp(name, "stroke-dashoffset") == 0) { - attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "stroke-opacity") == 0) { - attr->strokeOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "stroke-linecap") == 0) { - attr->strokeLineCap = nsvg__parseLineCap(value); - } else if (strcmp(name, "stroke-linejoin") == 0) { - attr->strokeLineJoin = nsvg__parseLineJoin(value); - } else if (strcmp(name, "stroke-miterlimit") == 0) { - attr->miterLimit = nsvg__parseMiterLimit(value); - } else if (strcmp(name, "fill-rule") == 0) { - attr->fillRule = nsvg__parseFillRule(value); - } else if (strcmp(name, "font-size") == 0) { - attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "transform") == 0) { - nsvg__parseTransform(xform, value); - nsvg__xformPremultiply(attr->xform, xform); - } else if (strcmp(name, "stop-color") == 0) { - attr->stopColor = nsvg__parseColor(value); - } else if (strcmp(name, "stop-opacity") == 0) { - attr->stopOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "offset") == 0) { - attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); - } else if (strcmp(name, "id") == 0) { - strncpy(attr->id, value, 63); - attr->id[63] = '\0'; - } else { - return 0; - } - return 1; -} - -static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) -{ - const char* str; - const char* val; - char name[512]; - char value[512]; - int n; - - str = start; - while (str < end && *str != ':') ++str; - - val = str; - - // Right Trim - while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; - ++str; - - n = (int)(str - start); - if (n > 511) n = 511; - if (n) memcpy(name, start, n); - name[n] = 0; - - while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; - - n = (int)(end - val); - if (n > 511) n = 511; - if (n) memcpy(value, val, n); - value[n] = 0; - - return nsvg__parseAttr(p, name, value); -} - -static void nsvg__parseStyle(NSVGparser* p, const char* str) -{ - const char* start; - const char* end; - - while (*str) { - // Left Trim - while(*str && nsvg__isspace(*str)) ++str; - start = str; - while(*str && *str != ';') ++str; - end = str; - - // Right Trim - while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; - ++end; - - nsvg__parseNameValue(p, start, end); - if (*str) ++str; - } -} - -static void nsvg__parseAttribs(NSVGparser* p, const char** attr) -{ - int i; - for (i = 0; attr[i]; i += 2) - { - if (strcmp(attr[i], "style") == 0) - nsvg__parseStyle(p, attr[i + 1]); - else - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } -} - -static int nsvg__getArgsPerElement(char cmd) -{ - switch (cmd) { - case 'v': - case 'V': - case 'h': - case 'H': - return 1; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - return 2; - case 'q': - case 'Q': - case 's': - case 'S': - return 4; - case 'c': - case 'C': - return 6; - case 'a': - case 'A': - return 7; - } - return 0; -} - -static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__moveTo(p, *cpx, *cpy); -} - -static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) - *cpx += args[0]; - else - *cpx = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) - *cpy += args[0]; - else - *cpy = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x2, y2, cx1, cy1, cx2, cy2; - - if (rel) { - cx1 = *cpx + args[0]; - cy1 = *cpy + args[1]; - cx2 = *cpx + args[2]; - cy2 = *cpy + args[3]; - x2 = *cpx + args[4]; - y2 = *cpy + args[5]; - } else { - cx1 = args[0]; - cy1 = args[1]; - cx2 = args[2]; - cy2 = args[3]; - x2 = args[4]; - y2 = args[5]; - } - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx2 = *cpx + args[0]; - cy2 = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } else { - cx2 = args[0]; - cy2 = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - cx1 = 2*x1 - *cpx2; - cy1 = 2*y1 - *cpy2; - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx = *cpx + args[0]; - cy = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } else { - cx = args[0]; - cy = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - // Convert to cubic bezier - cx1 = x1 + 2.0f/3.0f*(cx - x1); - cy1 = y1 + 2.0f/3.0f*(cy - y1); - cx2 = x2 + 2.0f/3.0f*(cx - x2); - cy2 = y2 + 2.0f/3.0f*(cy - y2); - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - x2 = *cpx + args[0]; - y2 = *cpy + args[1]; - } else { - x2 = args[0]; - y2 = args[1]; - } - - cx = 2*x1 - *cpx2; - cy = 2*y1 - *cpy2; - - // Convert to cubix bezier - cx1 = x1 + 2.0f/3.0f*(cx - x1); - cy1 = y1 + 2.0f/3.0f*(cy - y1); - cx2 = x2 + 2.0f/3.0f*(cx - x2); - cy2 = y2 + 2.0f/3.0f*(cy - y2); - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static float nsvg__sqr(float x) { return x*x; } -static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } - -static float nsvg__vecrat(float ux, float uy, float vx, float vy) -{ - return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); -} - -static float nsvg__vecang(float ux, float uy, float vx, float vy) -{ - float r = nsvg__vecrat(ux,uy, vx,vy); - if (r < -1.0f) r = -1.0f; - if (r > 1.0f) r = 1.0f; - return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); -} - -static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - // Ported from canvg (https://code.google.com/p/canvg/) - float rx, ry, rotx; - float x1, y1, x2, y2, cx, cy, dx, dy, d; - float x1p, y1p, cxp, cyp, s, sa, sb; - float ux, uy, vx, vy, a1, da; - float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; - float sinrx, cosrx; - int fa, fs; - int i, ndivs; - float hda, kappa; - - rx = fabsf(args[0]); // y radius - ry = fabsf(args[1]); // x radius - rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle - fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc - fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction - x1 = *cpx; // start point - y1 = *cpy; - if (rel) { // end point - x2 = *cpx + args[5]; - y2 = *cpy + args[6]; - } else { - x2 = args[5]; - y2 = args[6]; - } - - dx = x1 - x2; - dy = y1 - y2; - d = sqrtf(dx*dx + dy*dy); - if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { - // The arc degenerates to a line - nsvg__lineTo(p, x2, y2); - *cpx = x2; - *cpy = y2; - return; - } - - sinrx = sinf(rotx); - cosrx = cosf(rotx); - - // Convert to center point parameterization. - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - // 1) Compute x1', y1' - x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; - y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; - d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); - if (d > 1) { - d = sqrtf(d); - rx *= d; - ry *= d; - } - // 2) Compute cx', cy' - s = 0.0f; - sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); - sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); - if (sa < 0.0f) sa = 0.0f; - if (sb > 0.0f) - s = sqrtf(sa / sb); - if (fa == fs) - s = -s; - cxp = s * rx * y1p / ry; - cyp = s * -ry * x1p / rx; - - // 3) Compute cx,cy from cx',cy' - cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; - cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; - - // 4) Calculate theta1, and delta theta. - ux = (x1p - cxp) / rx; - uy = (y1p - cyp) / ry; - vx = (-x1p - cxp) / rx; - vy = (-y1p - cyp) / ry; - a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle - da = nsvg__vecang(ux,uy, vx,vy); // Delta angle - -// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; -// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; - - if (fs == 0 && da > 0) - da -= 2 * NSVG_PI; - else if (fs == 1 && da < 0) - da += 2 * NSVG_PI; - - // Approximate the arc using cubic spline segments. - t[0] = cosrx; t[1] = sinrx; - t[2] = -sinrx; t[3] = cosrx; - t[4] = cx; t[5] = cy; - - // Split arc into max 90 degree segments. - // 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)); - if (da < 0.0f) - kappa = -kappa; - - for (i = 0; i <= ndivs; i++) { - a = a1 + da * ((float)i/(float)ndivs); - dx = cosf(a); - dy = sinf(a); - nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position - nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent - if (i > 0) - nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - *cpx = x2; - *cpy = y2; -} - -static void nsvg__parsePath(NSVGparser* p, const char** attr) -{ - const char* s = NULL; - char cmd = '\0'; - float args[10]; - int nargs; - int rargs = 0; - float cpx, cpy, cpx2, cpy2; - 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; - closedFlag = 0; - nargs = 0; - - while (*s) { - s = nsvg__getNextPathItem(s, item); - if (!*item) break; - if (nsvg__isnum(item[0])) { - 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; - 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; - break; - case 'C': - case 'c': - nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); - break; - case 'S': - case 's': - nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); - break; - case 'Q': - case 'q': - nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); - break; - case 'T': - case 't': - nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); - break; - case 'A': - case 'a': - nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - default: - if (nargs >= 2) { - cpx = args[nargs-2]; - cpy = args[nargs-1]; - cpx2 = cpx; cpy2 = cpy; - } - break; - } - nargs = 0; - } - } else { - cmd = item[0]; - rargs = nsvg__getArgsPerElement(cmd); - if (cmd == 'M' || cmd == 'm') { - // Commit path. - if (p->npts > 0) - nsvg__addPath(p, closedFlag); - // Start new subpath. - nsvg__resetPath(p); - closedFlag = 0; - nargs = 0; - } else if (cmd == 'Z' || cmd == 'z') { - closedFlag = 1; - // Commit path. - if (p->npts > 0) { - // Move current point to first point - cpx = p->pts[0]; - cpy = p->pts[1]; - cpx2 = cpx; cpy2 = cpy; - nsvg__addPath(p, closedFlag); - } - // Start new subpath. - nsvg__resetPath(p); - nsvg__moveTo(p, cpx, cpy); - closedFlag = 0; - nargs = 0; - } - } - } - // Commit path. - if (p->npts) - nsvg__addPath(p, closedFlag); - } - - nsvg__addShape(p); -} - -static void nsvg__parseRect(NSVGparser* p, const char** attr) -{ - float x = 0.0f; - float y = 0.0f; - float w = 0.0f; - float h = 0.0f; - float rx = -1.0f; // marks not set - float ry = -1.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); - if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx < 0.0f && ry > 0.0f) rx = ry; - if (ry < 0.0f && rx > 0.0f) ry = rx; - if (rx < 0.0f) rx = 0.0f; - if (ry < 0.0f) ry = 0.0f; - if (rx > w/2.0f) rx = w/2.0f; - if (ry > h/2.0f) ry = h/2.0f; - - if (w != 0.0f && h != 0.0f) { - nsvg__resetPath(p); - - if (rx < 0.00001f || ry < 0.0001f) { - nsvg__moveTo(p, x, y); - nsvg__lineTo(p, x+w, y); - nsvg__lineTo(p, x+w, y+h); - nsvg__lineTo(p, x, y+h); - } else { - // Rounded rectangle - nsvg__moveTo(p, x+rx, y); - nsvg__lineTo(p, x+w-rx, y); - nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); - nsvg__lineTo(p, x+w, y+h-ry); - nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); - nsvg__lineTo(p, x+rx, y+h); - nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); - nsvg__lineTo(p, x, y+ry); - nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); - } - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseCircle(NSVGparser* p, const char** attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float r = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); - } - } - - if (r > 0.0f) { - nsvg__resetPath(p); - - nsvg__moveTo(p, cx+r, cy); - nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); - nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); - nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); - nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseEllipse(NSVGparser* p, const char** attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float rx = 0.0f; - float ry = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx > 0.0f && ry > 0.0f) { - - nsvg__resetPath(p); - - nsvg__moveTo(p, cx+rx, cy); - nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); - nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); - nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); - nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseLine(NSVGparser* p, const char** attr) -{ - float x1 = 0.0; - float y1 = 0.0; - float x2 = 0.0; - float y2 = 0.0; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - 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); - 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]; - char buf[64]; - s = nsvg__parseNumber(s, buf, 64); - p->viewMinx = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewMiny = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewWidth = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewHeight = nsvg__atof(buf); - } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { - if (strstr(attr[i + 1], "none") != 0) { - // No uniform scaling - p->alignType = NSVG_ALIGN_NONE; - } else { - // Parse X align - if (strstr(attr[i + 1], "xMin") != 0) - p->alignX = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "xMid") != 0) - p->alignX = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "xMax") != 0) - p->alignX = NSVG_ALIGN_MAX; - // Parse X align - if (strstr(attr[i + 1], "yMin") != 0) - p->alignY = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "yMid") != 0) - p->alignY = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "yMax") != 0) - p->alignY = NSVG_ALIGN_MAX; - // Parse meet/slice - p->alignType = NSVG_ALIGN_MEET; - if (strstr(attr[i + 1], "slice") != 0) - p->alignType = NSVG_ALIGN_SLICE; - } - } - } - } -} - -static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) -{ - int i; - NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); - if (grad == NULL) return; - memset(grad, 0, sizeof(NSVGgradientData)); - grad->units = NSVG_OBJECT_SPACE; - grad->type = type; - if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { - grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); - grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { - grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - } - - nsvg__xformIdentity(grad->xform); - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "id") == 0) { - strncpy(grad->id, attr[i+1], 63); - grad->id[63] = '\0'; - } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "gradientUnits") == 0) { - if (strcmp(attr[i+1], "objectBoundingBox") == 0) - grad->units = NSVG_OBJECT_SPACE; - else - grad->units = NSVG_USER_SPACE; - } else if (strcmp(attr[i], "gradientTransform") == 0) { - nsvg__parseTransform(grad->xform, attr[i + 1]); - } else if (strcmp(attr[i], "cx") == 0) { - grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "cy") == 0) { - grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "r") == 0) { - grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "fx") == 0) { - grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "fy") == 0) { - grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "x1") == 0) { - grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "y1") == 0) { - grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "x2") == 0) { - grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "y2") == 0) { - grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "spreadMethod") == 0) { - if (strcmp(attr[i+1], "pad") == 0) - grad->spread = NSVG_SPREAD_PAD; - else if (strcmp(attr[i+1], "reflect") == 0) - grad->spread = NSVG_SPREAD_REFLECT; - else if (strcmp(attr[i+1], "repeat") == 0) - grad->spread = NSVG_SPREAD_REPEAT; - } else if (strcmp(attr[i], "xlink:href") == 0) { - const char *href = attr[i+1]; - strncpy(grad->ref, href+1, 62); - grad->ref[62] = '\0'; - } - } - } - - grad->next = p->gradients; - p->gradients = grad; -} - -static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) -{ - NSVGattrib* curAttr = nsvg__getAttr(p); - NSVGgradientData* grad; - NSVGgradientStop* stop; - int i, idx; - - curAttr->stopOffset = 0; - curAttr->stopColor = 0; - curAttr->stopOpacity = 1.0f; - - for (i = 0; attr[i]; i += 2) { - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } - - // Add stop to the last gradient. - grad = p->gradients; - if (grad == NULL) return; - - grad->nstops++; - grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); - if (grad->stops == NULL) return; - - // Insert - idx = grad->nstops-1; - for (i = 0; i < grad->nstops-1; i++) { - if (curAttr->stopOffset < grad->stops[i].offset) { - idx = i; - break; - } - } - 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); - } else if (strcmp(el, "path") == 0) { - if (p->pathFlag) // Do not allow nested paths. - 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); - } 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]); - bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); - bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); - } -} - -static float nsvg__viewAlign(float content, float container, int type) -{ - if (type == NSVG_ALIGN_MIN) - return 0; - else if (type == NSVG_ALIGN_MAX) - return container - content; - // mid - return (container - content) * 0.5f; -} - -static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) -{ - float t[6]; - nsvg__xformSetTranslation(t, tx, ty); - nsvg__xformMultiply (grad->xform, t); - - nsvg__xformSetScale(t, sx, sy); - nsvg__xformMultiply (grad->xform, t); -} - -static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) -{ - NSVGshape* shape; - NSVGpath* path; - float tx, ty, sx, sy, us, bounds[4], t[6], avgs; - int i; - float* pt; - - // Guess image size if not set completely. - nsvg__imageBounds(p, bounds); - - if (p->viewWidth == 0) { - if (p->image->width > 0) { - p->viewWidth = p->image->width; - } else { - p->viewMinx = bounds[0]; - p->viewWidth = bounds[2] - bounds[0]; - } - } - if (p->viewHeight == 0) { - if (p->image->height > 0) { - p->viewHeight = p->image->height; - } else { - p->viewMiny = bounds[1]; - p->viewHeight = bounds[3] - bounds[1]; - } - } - if (p->image->width == 0) - p->image->width = p->viewWidth; - if (p->image->height == 0) - p->image->height = p->viewHeight; - - tx = -p->viewMinx; - ty = -p->viewMiny; - sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; - sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; - // Unit scaling - us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); - - // Fix aspect ratio - if (p->alignType == NSVG_ALIGN_MEET) { - // fit whole image into viewbox - sx = sy = nsvg__minf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; - } else if (p->alignType == NSVG_ALIGN_SLICE) { - // fill whole viewbox with image - sx = sy = nsvg__maxf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; - } - - // Transform - sx *= us; - sy *= us; - avgs = (sx+sy) / 2.0f; - for (shape = p->image->shapes; shape != NULL; shape = shape->next) { - shape->bounds[0] = (shape->bounds[0] + tx) * sx; - shape->bounds[1] = (shape->bounds[1] + ty) * sy; - shape->bounds[2] = (shape->bounds[2] + tx) * sx; - shape->bounds[3] = (shape->bounds[3] + ty) * sy; - for (path = shape->paths; path != NULL; path = path->next) { - path->bounds[0] = (path->bounds[0] + tx) * sx; - path->bounds[1] = (path->bounds[1] + ty) * sy; - path->bounds[2] = (path->bounds[2] + tx) * sx; - path->bounds[3] = (path->bounds[3] + ty) * sy; - for (i =0; i < path->npts; i++) { - pt = &path->pts[i*2]; - pt[0] = (pt[0] + tx) * sx; - pt[1] = (pt[1] + ty) * sy; - } - } - - if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); - memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); - nsvg__xformInverse(shape->fill.gradient->xform, t); - } - if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); - memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); - nsvg__xformInverse(shape->stroke.gradient->xform, t); - } - - shape->strokeWidth *= avgs; - shape->strokeDashOffset *= avgs; - for (i = 0; i < shape->strokeDashCount; i++) - shape->strokeDashArray[i] *= avgs; - } -} - -NSVGimage* nsvgParse(char* input, const char* units, float dpi) -{ - NSVGparser* p; - NSVGimage* ret = 0; - - p = nsvg__createParser(); - if (p == NULL) { - return NULL; - } - p->dpi = dpi; - - nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); - - // Scale to viewBox - nsvg__scaleToViewbox(p, units); - - ret = p->image; - p->image = NULL; - - nsvg__deleteParser(p); - - return ret; -} - -#include - -NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) -{ - FILE* fp = NULL; - size_t size; - char* data = NULL; - NSVGimage* image = NULL; - - fp = boost::nowide::fopen(filename, "rb"); - if (!fp) goto error; - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - data = (char*)malloc(size+1); - if (data == NULL) goto error; - 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: - if (fp) fclose(fp); - if (data) free(data); - if (image) nsvgDelete(image); - return NULL; -} - -NSVGpath* nsvgDuplicatePath(NSVGpath* p) -{ - NSVGpath* res = NULL; - - if (p == NULL) - return NULL; - - res = (NSVGpath*)malloc(sizeof(NSVGpath)); - if (res == NULL) goto error; - memset(res, 0, sizeof(NSVGpath)); - - res->pts = (float*)malloc(p->npts*2*sizeof(float)); - if (res->pts == NULL) goto error; - memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); - res->npts = p->npts; - - memcpy(res->bounds, p->bounds, sizeof(p->bounds)); - - res->closed = p->closed; - - return res; - -error: - if (res != NULL) { - free(res->pts); - free(res); - } - return NULL; -} - -void nsvgDelete(NSVGimage* image) -{ - NSVGshape *snext, *shape; - if (image == NULL) return; - shape = image->shapes; - while (shape != NULL) { - snext = shape->next; - nsvg__deletePaths(shape->paths); - nsvg__deletePaint(&shape->fill); - nsvg__deletePaint(&shape->stroke); - free(shape); - shape = snext; - } - free(image); -} - -#endif diff --git a/src/nanosvg/nanosvgrast.h b/src/nanosvg/nanosvgrast.h deleted file mode 100644 index b740c316ca..0000000000 --- a/src/nanosvg/nanosvgrast.h +++ /dev/null @@ -1,1452 +0,0 @@ -/* - * Copyright (c) 2013-14 Mikko Mononen memon@inside.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The polygon rasterization is heavily based on stb_truetype rasterizer - * by Sean Barrett - http://nothings.org/ - * - */ - -#ifndef NANOSVGRAST_H -#define NANOSVGRAST_H - -#ifndef NANOSVGRAST_CPLUSPLUS -#ifdef __cplusplus -extern "C" { -#endif -#endif - -typedef struct NSVGrasterizer NSVGrasterizer; - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - - // Create rasterizer (can be used to render multiple images). - struct NSVGrasterizer* rast = nsvgCreateRasterizer(); - // Allocate memory for image - unsigned char* img = malloc(w*h*4); - // Rasterize - nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); -*/ - -// Allocated rasterizer context. -NSVGrasterizer* nsvgCreateRasterizer(); - -// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) -// r - pointer to rasterizer context -// image - pointer to image to rasterize -// tx,ty - image offset (applied after scaling) -// scale - image scale -// dst - pointer to destination image data, 4 bytes per pixel (RGBA) -// w - width of the image to render -// h - height of the image to render -// stride - number of bytes per scaleline in the destination buffer -void nsvgRasterize(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride); - -// Deletes rasterizer context. -void nsvgDeleteRasterizer(NSVGrasterizer*); - - -#ifndef NANOSVGRAST_CPLUSPLUS -#ifdef __cplusplus -} -#endif -#endif - -#endif // NANOSVGRAST_H - -#ifdef NANOSVGRAST_IMPLEMENTATION - -#include - -#define NSVG__SUBSAMPLES 5 -#define NSVG__FIXSHIFT 10 -#define NSVG__FIX (1 << NSVG__FIXSHIFT) -#define NSVG__FIXMASK (NSVG__FIX-1) -#define NSVG__MEMPAGE_SIZE 1024 - -typedef struct NSVGedge { - float x0,y0, x1,y1; - int dir; - struct NSVGedge* next; -} NSVGedge; - -typedef struct NSVGpoint { - float x, y; - float dx, dy; - float len; - float dmx, dmy; - unsigned char flags; -} NSVGpoint; - -typedef struct NSVGactiveEdge { - int x,dx; - float ey; - int dir; - struct NSVGactiveEdge *next; -} NSVGactiveEdge; - -typedef struct NSVGmemPage { - unsigned char mem[NSVG__MEMPAGE_SIZE]; - int size; - struct NSVGmemPage* next; -} NSVGmemPage; - -typedef struct NSVGcachedPaint { - char type; - char spread; - float xform[6]; - unsigned int colors[256]; -} NSVGcachedPaint; - -struct NSVGrasterizer -{ - float px, py; - - float tessTol; - float distTol; - - NSVGedge* edges; - int nedges; - int cedges; - - NSVGpoint* points; - int npoints; - int cpoints; - - NSVGpoint* points2; - int npoints2; - int cpoints2; - - NSVGactiveEdge* freelist; - NSVGmemPage* pages; - NSVGmemPage* curpage; - - unsigned char* scanline; - int cscanline; - - unsigned char* bitmap; - int width, height, stride; -}; - -NSVGrasterizer* nsvgCreateRasterizer() -{ - NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); - if (r == NULL) goto error; - memset(r, 0, sizeof(NSVGrasterizer)); - - r->tessTol = 0.25f; - r->distTol = 0.01f; - - return r; - -error: - nsvgDeleteRasterizer(r); - return NULL; -} - -void nsvgDeleteRasterizer(NSVGrasterizer* r) -{ - NSVGmemPage* p; - - if (r == NULL) return; - - p = r->pages; - while (p != NULL) { - NSVGmemPage* next = p->next; - free(p); - p = next; - } - - if (r->edges) free(r->edges); - if (r->points) free(r->points); - if (r->points2) free(r->points2); - if (r->scanline) free(r->scanline); - - free(r); -} - -static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) -{ - NSVGmemPage *newp; - - // If using existing chain, return the next page in chain - if (cur != NULL && cur->next != NULL) { - return cur->next; - } - - // Alloc new page - newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); - if (newp == NULL) return NULL; - memset(newp, 0, sizeof(NSVGmemPage)); - - // Add to linked list - if (cur != NULL) - cur->next = newp; - else - r->pages = newp; - - return newp; -} - -static void nsvg__resetPool(NSVGrasterizer* r) -{ - NSVGmemPage* p = r->pages; - while (p != NULL) { - p->size = 0; - p = p->next; - } - r->curpage = r->pages; -} - -static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) -{ - unsigned char* buf; - if (size > NSVG__MEMPAGE_SIZE) return NULL; - if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { - r->curpage = nsvg__nextPage(r, r->curpage); - } - buf = &r->curpage->mem[r->curpage->size]; - r->curpage->size += size; - return buf; -} - -static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) -{ - float dx = x2 - x1; - float dy = y2 - y1; - return dx*dx + dy*dy < tol*tol; -} - -static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) -{ - NSVGpoint* pt; - - if (r->npoints > 0) { - pt = &r->points[r->npoints-1]; - if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { - pt->flags = (unsigned char)(pt->flags | flags); - return; - } - } - - if (r->npoints+1 > r->cpoints) { - r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; - r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); - if (r->points == NULL) return; - } - - pt = &r->points[r->npoints]; - pt->x = x; - pt->y = y; - pt->flags = (unsigned char)flags; - r->npoints++; -} - -static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) -{ - if (r->npoints+1 > r->cpoints) { - r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; - r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); - if (r->points == NULL) return; - } - r->points[r->npoints] = pt; - r->npoints++; -} - -static void nsvg__duplicatePoints(NSVGrasterizer* r) -{ - if (r->npoints > r->cpoints2) { - r->cpoints2 = r->npoints; - r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); - if (r->points2 == NULL) return; - } - - memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); - r->npoints2 = r->npoints; -} - -static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) -{ - NSVGedge* e; - - // Skip horizontal edges - if (y0 == y1) - return; - - if (r->nedges+1 > r->cedges) { - r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; - r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); - if (r->edges == NULL) return; - } - - e = &r->edges[r->nedges]; - r->nedges++; - - if (y0 < y1) { - e->x0 = x0; - e->y0 = y0; - e->x1 = x1; - e->y1 = y1; - e->dir = 1; - } else { - e->x0 = x1; - e->y0 = y1; - e->x1 = x0; - e->y1 = y0; - e->dir = -1; - } -} - -static float nsvg__normalize(float *x, float* y) -{ - float d = sqrtf((*x)*(*x) + (*y)*(*y)); - if (d > 1e-6f) { - float id = 1.0f / d; - *x *= id; - *y *= id; - } - return d; -} - -static float nsvg__absf(float x) { return x < 0 ? -x : x; } - -static void nsvg__flattenCubicBez(NSVGrasterizer* r, - float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4, - int level, int type) -{ - float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; - float dx,dy,d2,d3; - - if (level > 10) return; - - x12 = (x1+x2)*0.5f; - y12 = (y1+y2)*0.5f; - x23 = (x2+x3)*0.5f; - y23 = (y2+y3)*0.5f; - x34 = (x3+x4)*0.5f; - y34 = (y3+y4)*0.5f; - x123 = (x12+x23)*0.5f; - y123 = (y12+y23)*0.5f; - - dx = x4 - x1; - dy = y4 - y1; - d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); - d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); - - if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { - nsvg__addPathPoint(r, x4, y4, type); - return; - } - - x234 = (x23+x34)*0.5f; - y234 = (y23+y34)*0.5f; - x1234 = (x123+x234)*0.5f; - y1234 = (y123+y234)*0.5f; - - nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); - nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); -} - -static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) -{ - int i, j; - NSVGpath* path; - - 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); - 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); - } - // Close path - nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 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); - } -} - -enum NSVGpointFlags -{ - NSVG_PT_CORNER = 0x01, - NSVG_PT_BEVEL = 0x02, - NSVG_PT_LEFT = 0x04 -}; - -static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - float len = nsvg__normalize(&dx, &dy); - float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) -{ - float w = lineWidth * 0.5f; - float px = p->x, py = p->y; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - - nsvg__addEdge(r, lx, ly, rx, ry); - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) -{ - float w = lineWidth * 0.5f; - float px = p->x - dx*w, py = p->y - dy*w; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - - nsvg__addEdge(r, lx, ly, rx, ry); - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -#ifndef NSVG_PI -#define NSVG_PI (3.14159265358979323846264338327f) -#endif - -static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) -{ - int i; - float w = lineWidth * 0.5f; - float px = p->x, py = p->y; - float dlx = dy, dly = -dx; - float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; - - for (i = 0; i < ncap; i++) { - float a = (float)i/(float)(ncap-1)*NSVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - float x = px - dlx*ax - dx*ay; - float y = py - dly*ax - dy*ay; - - if (i > 0) - nsvg__addEdge(r, prevx, prevy, x, y); - - prevx = x; - prevy = y; - - if (i == 0) { - lx = x; ly = y; - } else if (i == ncap-1) { - rx = x; ry = y; - } - } - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); - float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); - float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); - float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); - - nsvg__addEdge(r, lx0, ly0, left->x, left->y); - nsvg__addEdge(r, lx1, ly1, lx0, ly0); - - nsvg__addEdge(r, right->x, right->y, rx0, ry0); - nsvg__addEdge(r, rx0, ry0, rx1, ry1); - - left->x = lx1; left->y = ly1; - right->x = rx1; right->y = ry1; -} - -static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float lx0, rx0, lx1, rx1; - float ly0, ry0, ly1, ry1; - - if (p1->flags & NSVG_PT_LEFT) { - lx0 = lx1 = p1->x - p1->dmx * w; - ly0 = ly1 = p1->y - p1->dmy * w; - nsvg__addEdge(r, lx1, ly1, left->x, left->y); - - rx0 = p1->x + (dlx0 * w); - ry0 = p1->y + (dly0 * w); - rx1 = p1->x + (dlx1 * w); - ry1 = p1->y + (dly1 * w); - nsvg__addEdge(r, right->x, right->y, rx0, ry0); - nsvg__addEdge(r, rx0, ry0, rx1, ry1); - } else { - lx0 = p1->x - (dlx0 * w); - ly0 = p1->y - (dly0 * w); - lx1 = p1->x - (dlx1 * w); - ly1 = p1->y - (dly1 * w); - nsvg__addEdge(r, lx0, ly0, left->x, left->y); - nsvg__addEdge(r, lx1, ly1, lx0, ly0); - - rx0 = rx1 = p1->x + p1->dmx * w; - ry0 = ry1 = p1->y + p1->dmy * w; - nsvg__addEdge(r, right->x, right->y, rx1, ry1); - } - - left->x = lx1; left->y = ly1; - right->x = rx1; right->y = ry1; -} - -static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) -{ - int i, n; - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float a0 = atan2f(dly0, dlx0); - float a1 = atan2f(dly1, dlx1); - float da = a1 - a0; - float lx, ly, rx, ry; - - if (da < NSVG_PI) da += NSVG_PI*2; - if (da > NSVG_PI) da -= NSVG_PI*2; - - n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); - if (n < 2) n = 2; - if (n > ncap) n = ncap; - - lx = left->x; - ly = left->y; - rx = right->x; - ry = right->y; - - for (i = 0; i < n; i++) { - float u = (float)i/(float)(n-1); - float a = a0 + u*da; - float ax = cosf(a) * w, ay = sinf(a) * w; - float lx1 = p1->x - ax, ly1 = p1->y - ay; - float rx1 = p1->x + ax, ry1 = p1->y + ay; - - nsvg__addEdge(r, lx1, ly1, lx, ly); - nsvg__addEdge(r, rx, ry, rx1, ry1); - - lx = lx1; ly = ly1; - rx = rx1; ry = ry1; - } - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); - float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); - - nsvg__addEdge(r, lx, ly, left->x, left->y); - nsvg__addEdge(r, right->x, right->y, rx, ry); - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static int nsvg__curveDivs(float r, float arc, float tol) -{ - float da = acosf(r / (r + tol)) * 2.0f; - int divs = (int)ceilf(arc / da); - if (divs < 2) divs = 2; - return divs; -} - -static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) -{ - int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. - NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; - NSVGpoint* p0, *p1; - int j, s, e; - - // Build stroke edges - if (closed) { - // Looping - p0 = &points[npoints-1]; - p1 = &points[0]; - s = 0; - e = npoints; - } else { - // Add cap - p0 = &points[0]; - p1 = &points[1]; - s = 1; - e = npoints-1; - } - - if (closed) { - nsvg__initClosed(&left, &right, p0, p1, lineWidth); - firstLeft = left; - firstRight = right; - } else { - // Add cap - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - nsvg__normalize(&dx, &dy); - if (lineCap == NSVG_CAP_BUTT) - nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); - else if (lineCap == NSVG_CAP_SQUARE) - nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); - else if (lineCap == NSVG_CAP_ROUND) - nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); - } - - for (j = s; j < e; ++j) { - if (p1->flags & NSVG_PT_CORNER) { - if (lineJoin == NSVG_JOIN_ROUND) - nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); - else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) - nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); - else - nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); - } else { - nsvg__straightJoin(r, &left, &right, p1, lineWidth); - } - p0 = p1++; - } - - if (closed) { - // Loop it - nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); - nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); - } else { - // Add cap - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - nsvg__normalize(&dx, &dy); - if (lineCap == NSVG_CAP_BUTT) - nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); - else if (lineCap == NSVG_CAP_SQUARE) - nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); - else if (lineCap == NSVG_CAP_ROUND) - nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); - } -} - -static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) -{ - int i, j; - NSVGpoint* p0, *p1; - - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - for (i = 0; i < r->npoints; i++) { - // Calculate segment direction and length - p0->dx = p1->x - p0->x; - p0->dy = p1->y - p0->y; - p0->len = nsvg__normalize(&p0->dx, &p0->dy); - // Advance - p0 = p1++; - } - - // calculate joins - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - for (j = 0; j < r->npoints; j++) { - float dlx0, dly0, dlx1, dly1, dmr2, cross; - dlx0 = p0->dy; - dly0 = -p0->dx; - dlx1 = p1->dy; - dly1 = -p1->dx; - // Calculate extrusions - p1->dmx = (dlx0 + dlx1) * 0.5f; - p1->dmy = (dly0 + dly1) * 0.5f; - dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; - if (dmr2 > 0.000001f) { - float s2 = 1.0f / dmr2; - if (s2 > 600.0f) { - s2 = 600.0f; - } - p1->dmx *= s2; - p1->dmy *= s2; - } - - // Clear flags, but keep the corner. - p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; - - // Keep track of left turns. - cross = p1->dx * p0->dy - p0->dx * p1->dy; - if (cross > 0.0f) - p1->flags |= NSVG_PT_LEFT; - - // Check to see if the corner needs to be beveled. - if (p1->flags & NSVG_PT_CORNER) { - if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { - p1->flags |= NSVG_PT_BEVEL; - } - } - - p0 = p1++; - } -} - -static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) -{ - int i, j, closed; - NSVGpath* path; - NSVGpoint* p0, *p1; - float miterLimit = shape->miterLimit; - int lineJoin = shape->strokeLineJoin; - int lineCap = shape->strokeLineCap; - float lineWidth = shape->strokeWidth * scale; - - 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); - 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); - } - if (r->npoints < 2) - continue; - - closed = path->closed; - - // If the first and last points are the same, remove the last, mark as closed path. - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { - r->npoints--; - p0 = &r->points[r->npoints-1]; - closed = 1; - } - - if (shape->strokeDashCount > 0) { - int idash = 0, dashState = 1; - float totalDist = 0, dashLen, allDashLen, dashOffset; - NSVGpoint cur; - - if (closed) - nsvg__appendPathPoint(r, r->points[0]); - - // Duplicate points -> points2. - nsvg__duplicatePoints(r); - - r->npoints = 0; - cur = r->points2[0]; - nsvg__appendPathPoint(r, cur); - - // Figure out dash offset. - allDashLen = 0; - for (j = 0; j < shape->strokeDashCount; j++) - allDashLen += shape->strokeDashArray[j]; - if (shape->strokeDashCount & 1) - allDashLen *= 2.0f; - // Find location inside pattern - dashOffset = fmodf(shape->strokeDashOffset, allDashLen); - if (dashOffset < 0.0f) - dashOffset += allDashLen; - - while (dashOffset > shape->strokeDashArray[idash]) { - dashOffset -= shape->strokeDashArray[idash]; - idash = (idash + 1) % shape->strokeDashCount; - } - dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; - - for (j = 1; j < r->npoints2; ) { - float dx = r->points2[j].x - cur.x; - float dy = r->points2[j].y - cur.y; - float dist = sqrtf(dx*dx + dy*dy); - - if ((totalDist + dist) > dashLen) { - // Calculate intermediate point - float d = (dashLen - totalDist) / dist; - float x = cur.x + dx * d; - float y = cur.y + dy * d; - nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); - - // Stroke - if (r->npoints > 1 && dashState) { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); - } - // Advance dash pattern - dashState = !dashState; - idash = (idash+1) % shape->strokeDashCount; - dashLen = shape->strokeDashArray[idash] * scale; - // Restart - cur.x = x; - cur.y = y; - cur.flags = NSVG_PT_CORNER; - totalDist = 0.0f; - r->npoints = 0; - nsvg__appendPathPoint(r, cur); - } else { - totalDist += dist; - cur = r->points2[j]; - nsvg__appendPathPoint(r, cur); - j++; - } - } - // Stroke any leftover path - if (r->npoints > 1 && dashState) - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); - } else { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); - } - } -} - -static int nsvg__cmpEdge(const void *p, const void *q) -{ - const NSVGedge* a = (const NSVGedge*)p; - const NSVGedge* b = (const NSVGedge*)q; - - if (a->y0 < b->y0) return -1; - if (a->y0 > b->y0) return 1; - return 0; -} - - -static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) -{ - NSVGactiveEdge* z; - - if (r->freelist != NULL) { - // Restore from freelist. - z = r->freelist; - r->freelist = z->next; - } else { - // Alloc new edge. - z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); - if (z == NULL) return NULL; - } - - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); -// STBTT_assert(e->y0 <= start_point); - // round dx down to avoid going too far - if (dxdy < 0) - z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); - else - z->dx = (int)floorf(NSVG__FIX * dxdy); - z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); -// z->x -= off_x * FIX; - z->ey = e->y1; - z->next = 0; - z->dir = e->dir; - - return z; -} - -static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) -{ - z->next = r->freelist; - r->freelist = z; -} - -static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) -{ - int i = x0 >> NSVG__FIXSHIFT; - int j = x1 >> NSVG__FIXSHIFT; - if (i < *xmin) *xmin = i; - if (j > *xmax) *xmax = j; - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = (unsigned char)(scanline[i] + maxWeight); - } - } -} - -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) -{ - // non-zero winding fill - int x0 = 0, w = 0; - - if (fillRule == NSVG_FILLRULE_NONZERO) { - // Non-zero - while (e != NULL) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->dir; - } else { - int x1 = e->x; w += e->dir; - // if we went to zero, we need to draw - if (w == 0) - nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); - } - e = e->next; - } - } else if (fillRule == NSVG_FILLRULE_EVENODD) { - // Even-odd - while (e != NULL) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w = 1; - } else { - int x1 = e->x; w = 0; - nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); - } - e = e->next; - } - } -} - -static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } - -static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - return (r) | (g << 8) | (b << 16) | (a << 24); -} - -static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) -{ - int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); - int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; - int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; - int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; - int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; - return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); -} - -static unsigned int nsvg__applyOpacity(unsigned int c, float u) -{ - int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); - int r = (c) & 0xff; - int g = (c>>8) & 0xff; - int b = (c>>16) & 0xff; - int a = (((c>>24) & 0xff)*iu) >> 8; - return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); -} - -static inline int nsvg__div255(int x) -{ - return ((x+1) * 257) >> 16; -} - -static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, - float tx, float ty, float scale, NSVGcachedPaint* cache) -{ - - if (cache->type == NSVG_PAINT_COLOR) { - int i, cr, cg, cb, ca; - cr = cache->colors[0] & 0xff; - cg = (cache->colors[0] >> 8) & 0xff; - cb = (cache->colors[0] >> 16) & 0xff; - ca = (cache->colors[0] >> 24) & 0xff; - - for (i = 0; i < count; i++) { - int r,g,b; - int a = nsvg__div255((int)cover[0] * ca); - int ia = 255 - a; - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - } - } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { - // TODO: spread modes. - // TODO: plenty of opportunities to optimize. - float fx, fy, dx, gy; - float* t = cache->xform; - int i, cr, cg, cb, ca; - unsigned int c; - - fx = ((float)x - tx) / scale; - fy = ((float)y - ty) / scale; - dx = 1.0f / scale; - - for (i = 0; i < count; i++) { - int r,g,b,a,ia; - gy = fx*t[1] + fy*t[3] + t[5]; - c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; - cr = (c) & 0xff; - cg = (c >> 8) & 0xff; - cb = (c >> 16) & 0xff; - ca = (c >> 24) & 0xff; - - a = nsvg__div255((int)cover[0] * ca); - ia = 255 - a; - - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - fx += dx; - } - } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { - // TODO: spread modes. - // TODO: plenty of opportunities to optimize. - // TODO: focus (fx,fy) - float fx, fy, dx, gx, gy, gd; - float* t = cache->xform; - int i, cr, cg, cb, ca; - unsigned int c; - - fx = ((float)x - tx) / scale; - fy = ((float)y - ty) / scale; - dx = 1.0f / scale; - - for (i = 0; i < count; i++) { - int r,g,b,a,ia; - gx = fx*t[0] + fy*t[2] + t[4]; - gy = fx*t[1] + fy*t[3] + t[5]; - gd = sqrtf(gx*gx + gy*gy); - c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; - cr = (c) & 0xff; - cg = (c >> 8) & 0xff; - cb = (c >> 16) & 0xff; - ca = (c >> 24) & 0xff; - - a = nsvg__div255((int)cover[0] * ca); - ia = 255 - a; - - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - fx += dx; - } - } -} - -static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) -{ - NSVGactiveEdge *active = NULL; - int y, s; - int e = 0; - int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline - int xmin, xmax; - - for (y = 0; y < r->height; y++) { - memset(r->scanline, 0, r->width); - xmin = r->width; - xmax = 0; - for (s = 0; s < NSVG__SUBSAMPLES; ++s) { - // find center of pixel for this scanline - float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; - NSVGactiveEdge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - NSVGactiveEdge *z = *step; - if (z->ey <= scany) { - *step = z->next; // delete from list -// NSVG__assert(z->valid); - nsvg__freeActive(r, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for (;;) { - int changed = 0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - NSVGactiveEdge* t = *step; - NSVGactiveEdge* q = t->next; - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e < r->nedges && r->edges[e].y0 <= scany) { - if (r->edges[e].y1 > scany) { - NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); - if (z == NULL) break; - // find insertion point - if (active == NULL) { - active = z; - } else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - NSVGactiveEdge* p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - e++; - } - - // now process all active edges in non-zero fashion - if (active != NULL) - nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); - } - // Blit - if (xmin < 0) xmin = 0; - if (xmax > r->width-1) xmax = r->width-1; - if (xmin <= xmax) { - nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); - } - } - -} - -static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) -{ - int x,y; - - // Unpremultiply - for (y = 0; y < h; y++) { - unsigned char *row = &image[y*stride]; - for (x = 0; x < w; x++) { - int r = row[0], g = row[1], b = row[2], a = row[3]; - if (a != 0) { - row[0] = (unsigned char)(r*255/a); - row[1] = (unsigned char)(g*255/a); - row[2] = (unsigned char)(b*255/a); - } - row += 4; - } - } - - // Defringe - for (y = 0; y < h; y++) { - unsigned char *row = &image[y*stride]; - for (x = 0; x < w; x++) { - int r = 0, g = 0, b = 0, a = row[3], n = 0; - if (a == 0) { - if (x-1 > 0 && row[-1] != 0) { - r += row[-4]; - g += row[-3]; - b += row[-2]; - n++; - } - if (x+1 < w && row[7] != 0) { - r += row[4]; - g += row[5]; - b += row[6]; - n++; - } - if (y-1 > 0 && row[-stride+3] != 0) { - r += row[-stride]; - g += row[-stride+1]; - b += row[-stride+2]; - n++; - } - if (y+1 < h && row[stride+3] != 0) { - r += row[stride]; - g += row[stride+1]; - b += row[stride+2]; - n++; - } - if (n > 0) { - row[0] = (unsigned char)(r/n); - row[1] = (unsigned char)(g/n); - row[2] = (unsigned char)(b/n); - } - } - row += 4; - } - } -} - - -static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) -{ - int i, j; - NSVGgradient* grad; - - cache->type = paint->type; - - if (paint->type == NSVG_PAINT_COLOR) { - cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); - return; - } - - grad = paint->gradient; - - cache->spread = grad->spread; - memcpy(cache->xform, grad->xform, sizeof(float)*6); - - if (grad->nstops == 0) { - for (i = 0; i < 256; i++) - cache->colors[i] = 0; - } if (grad->nstops == 1) { - for (i = 0; i < 256; i++) - cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); - } else { - unsigned int ca, cb = 0; - float ua, ub, du, u; - int ia, ib, count; - - ca = nsvg__applyOpacity(grad->stops[0].color, opacity); - ua = nsvg__clampf(grad->stops[0].offset, 0, 1); - ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - for (i = 0; i < ia; i++) { - cache->colors[i] = ca; - } - - for (i = 0; i < grad->nstops-1; i++) { - ca = nsvg__applyOpacity(grad->stops[i].color, opacity); - cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); - ua = nsvg__clampf(grad->stops[i].offset, 0, 1); - ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - count = ib - ia; - if (count <= 0) continue; - u = 0; - du = 1.0f / (float)count; - for (j = 0; j < count; j++) { - cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); - u += du; - } - } - - for (i = ib; i < 256; i++) - cache->colors[i] = cb; - } - -} - -/* -static void dumpEdges(NSVGrasterizer* r, const char* name) -{ - float xmin = 0, xmax = 0, ymin = 0, ymax = 0; - NSVGedge *e = NULL; - int i; - if (r->nedges == 0) return; - FILE* fp = fopen(name, "w"); - if (fp == NULL) return; - - xmin = xmax = r->edges[0].x0; - ymin = ymax = r->edges[0].y0; - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - xmin = nsvg__minf(xmin, e->x0); - xmin = nsvg__minf(xmin, e->x1); - xmax = nsvg__maxf(xmax, e->x0); - xmax = nsvg__maxf(xmax, e->x1); - ymin = nsvg__minf(ymin, e->y0); - ymin = nsvg__minf(ymin, e->y1); - ymax = nsvg__maxf(ymax, e->y0); - ymax = nsvg__maxf(ymax, e->y1); - } - - fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); - - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); - } - - for (i = 0; i < r->npoints; i++) { - if (i+1 < r->npoints) - fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); - fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); - } - - fprintf(fp, ""); - fclose(fp); -} -*/ - -void nsvgRasterize(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride) -{ - NSVGshape *shape = NULL; - NSVGedge *e = NULL; - NSVGcachedPaint cache; - int i; - - r->bitmap = dst; - r->width = w; - r->height = h; - r->stride = stride; - - if (w > r->cscanline) { - r->cscanline = w; - r->scanline = (unsigned char*)realloc(r->scanline, w); - if (r->scanline == NULL) return; - } - - for (i = 0; i < h; i++) - memset(&dst[i*stride], 0, w*4); - - for (shape = image->shapes; shape != NULL; shape = shape->next) { - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) - continue; - - if (shape->fill.type != NSVG_PAINT_NONE) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShape(r, shape, scale); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - 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); - } - if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShapeStroke(r, shape, scale); - -// dumpEdges(r, "edge.svg"); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - 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__unpremultiplyAlpha(dst, w, h, stride); - - r->bitmap = NULL; - r->width = 0; - r->height = 0; - r->stride = 0; -} - -#endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 09066bebd5..71a4941a4d 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -281,7 +281,7 @@ endforeach() encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES} NanoSVG::nanosvg NanoSVG::nanosvgrast) if (MSVC) target_link_libraries(libslic3r_gui Setupapi.lib) diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 103f321fd8..f00259559d 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -223,8 +223,7 @@ AboutDialog::AboutDialog() main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20); // logo - m_logo_bitmap = ScalableBitmap(this, wxGetApp().logo_name(), 192); - m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp()); + m_logo = new wxStaticBitmap(this, wxID_ANY, *get_bmp_bundle(wxGetApp().logo_name(), 192)); hsizer->Add(m_logo, 1, wxALIGN_CENTER_VERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); @@ -324,8 +323,8 @@ AboutDialog::AboutDialog() void AboutDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_logo_bitmap.msw_rescale(); - m_logo->SetBitmap(m_logo_bitmap.bmp()); +// m_logo_bitmap.msw_rescale(); +// m_logo->SetBitmap(m_logo_bitmap.bmp()); const wxFont& font = GetFont(); const int fs = font.GetPointSize() - 1; diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index b585798d32..1476152779 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -15,10 +15,8 @@ #include #endif /* __WXGTK2__ */ -//#define NANOSVG_IMPLEMENTATION -#include "nanosvg/nanosvg.h" -#define NANOSVGRAST_IMPLEMENTATION -#include "nanosvg/nanosvgrast.h" +#include +#include namespace Slic3r { namespace GUI { @@ -62,7 +60,168 @@ static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1. #endif } -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height) +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vector& bmps) +{ + wxVector bitmaps; + + std::set scales = {1.0}; +#ifndef __linux__ + +#ifdef __APPLE__ + scales.emplace(m_scale); +#else + size_t disp_cnt = wxDisplay::GetCount(); + for (size_t disp = 0; disp < disp_cnt; ++disp) + scales.emplace(wxDisplay(disp).GetScaleFactor()); +#endif + +#endif // !__linux__ + + for (double scale : scales) { + size_t width = 0; + size_t height = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { +#ifdef __APPLE__ + wxSize size = bmp_bndl->GetDefaultSize(); +#else + wxSize size = bmp_bndl->GetPreferredBitmapSizeAtScale(scale); +#endif + width += size.GetWidth(); + height = std::max(height, size.GetHeight()); + } + + std::string bitmap_key = name + "," +float_to_string_decimal_point(scale); + +#ifdef __WXGTK2__ + // Broken alpha workaround + wxImage image(width, height); + image.InitAlpha(); + // Fill in with a white color. + memset(image.GetData(), 0x0ff, width * height * 3); + // Fill in with full transparency. + memset(image.GetAlpha(), 0, width * height); + size_t x = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize()); + if (bmp.GetWidth() > 0) { + if (bmp.GetDepth() == 32) { + wxAlphaPixelData data(bmp); + //FIXME The following method is missing from wxWidgets 3.1.1. + // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). + //data.UseAlpha(); + if (data) { + for (int r = 0; r < bmp.GetHeight(); ++r) { + wxAlphaPixelData::Iterator src(data); + src.Offset(data, 0, r); + unsigned char* dst_pixels = image.GetData() + (x + r * width) * 3; + unsigned char* dst_alpha = image.GetAlpha() + x + r * width; + for (int c = 0; c < bmp.GetWidth(); ++c, ++src) { + *dst_pixels++ = src.Red(); + *dst_pixels++ = src.Green(); + *dst_pixels++ = src.Blue(); + *dst_alpha++ = src.Alpha(); + } + } + } + } + else if (bmp.GetDepth() == 24) { + wxNativePixelData data(bmp); + if (data) { + for (int r = 0; r < bmp.GetHeight(); ++r) { + wxNativePixelData::Iterator src(data); + src.Offset(data, 0, r); + unsigned char* dst_pixels = image.GetData() + (x + r * width) * 3; + unsigned char* dst_alpha = image.GetAlpha() + x + r * width; + for (int c = 0; c < bmp.GetWidth(); ++c, ++src) { + *dst_pixels++ = src.Red(); + *dst_pixels++ = src.Green(); + *dst_pixels++ = src.Blue(); + *dst_alpha++ = wxALPHA_OPAQUE; + } + } + } + } + } + x += bmp.GetScaledWidth(); + } + + bitmaps.push_back(* this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)))); + +#else + + wxBitmap* bitmap = this->insert(bitmap_key, width, height, scale); + wxMemoryDC memDC; + memDC.SelectObject(*bitmap); + memDC.SetBackground(*wxTRANSPARENT_BRUSH); + memDC.Clear(); + size_t x = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetPreferredBitmapSizeAtScale(scale)); + + if (bmp.GetWidth() > 0) + memDC.DrawBitmap(bmp, x, 0, true); + // we should "move" with step equal to non-scaled width +#ifdef __APPLE__ + x += bmp.GetScaledWidth(); +#else + x += bmp.GetWidth(); +#endif + } + memDC.SelectObject(wxNullBitmap); + bitmaps.push_back(*bitmap); + +#endif + } + + return insert_bndl(name, bitmaps); +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string &bitmap_key, const char* data, size_t width, size_t height) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(wxBitmapBundle::FromSVG(data, wxSize(width, height))); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle::FromSVG(data, wxSize(width, height)); + } + return bndl; +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& bitmap_key, const wxBitmapBundle& bmp) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(bmp); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle(bmp); + } + return bndl; +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& bitmap_key, const wxVector& bmps) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(wxBitmapBundle::FromBitmaps(bmps)); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle::FromBitmaps(bmps); + } + return bndl; +} + +wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height, double scale/* = -1.0*/) { wxBitmap *bitmap = nullptr; auto it = m_map.find(bitmap_key); @@ -78,7 +237,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_ // So, We need to let the Mac OS wxBitmap implementation // know that the image may already be scaled appropriately for Retina, // and thereby that it's not supposed to upscale it. - bitmap->CreateScaled(width, height, -1, m_scale); + bitmap->CreateScaled(width, height, -1, scale < 0.0 ? m_scale : scale); #endif m_map[bitmap_key] = bitmap; } else { @@ -107,110 +266,6 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp return bitmap; } -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[2] = { bmp, bmp2 }; - return this->insert(bitmap_key, bmps, bmps + 2); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[3] = { bmp, bmp2, bmp3 }; - return this->insert(bitmap_key, bmps, bmps + 3); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end) -{ - size_t width = 0; - size_t height = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { -#ifdef __APPLE__ - width += bmp->GetScaledWidth(); - height = std::max(height, bmp->GetScaledHeight()); -#else - width += bmp->GetWidth(); - height = std::max(height, bmp->GetHeight()); -#endif - } - -#ifdef __WXGTK2__ - // Broken alpha workaround - wxImage image(width, height); - image.InitAlpha(); - // Fill in with a white color. - memset(image.GetData(), 0x0ff, width * height * 3); - // Fill in with full transparency. - memset(image.GetAlpha(), 0, width * height); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) { - if (bmp->GetDepth() == 32) { - wxAlphaPixelData data(*const_cast(bmp)); - //FIXME The following method is missing from wxWidgets 3.1.1. - // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). - //data.UseAlpha(); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxAlphaPixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = src.Alpha(); - } - } - } - } else if (bmp->GetDepth() == 24) { - wxNativePixelData data(*const_cast(bmp)); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxNativePixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = wxALPHA_OPAQUE; - } - } - } - } - } - x += bmp->GetWidth(); - } - return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); - -#else - - wxBitmap *bitmap = this->insert(bitmap_key, width, height); - wxMemoryDC memDC; - memDC.SelectObject(*bitmap); - memDC.SetBackground(*wxTRANSPARENT_BRUSH); - memDC.Clear(); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) - memDC.DrawBitmap(*bmp, x, 0, true); -#ifdef __APPLE__ - // we should "move" with step equal to non-scaled width - x += bmp->GetScaledWidth(); -#else - x += bmp->GetWidth(); -#endif - } - memDC.SelectObject(wxNullBitmap); - return bitmap; - -#endif -} - wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale/* = false*/) { wxImage image(width, height); @@ -299,6 +354,92 @@ error: return NULL; } +void BitmapCache::nsvgGetDataFromFileWithReplace(const char* filename, std::string& data_str, const std::map& replaces) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + + fp = boost::nowide::fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size + 1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + + data_str.assign(data); + for (auto val : replaces) + boost::replace_all(data_str, val.first, val.second); + + free(data); + return; + +error: + if (fp) fclose(fp); + if (data) free(data); + return; +} + +wxBitmapBundle* BitmapCache::from_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, + const bool dark_mode, const std::string& new_color /*= ""*/) +{ + if (target_width == 0) + target_width = target_height; + std::string bitmap_key = bitmap_name + (target_height != 0 ? + "-h" + std::to_string(target_height) : + "-w" + std::to_string(target_width)) + + (dark_mode ? "-dm" : "") + + new_color; + + auto it = m_bndl_map.find(bitmap_key); + if (it != m_bndl_map.end()) + return it->second; + + // map of color replaces + std::map replaces; + if (dark_mode) + replaces["\"#808080\""] = "\"#FFFFFF\""; + if (!new_color.empty()) + replaces["\"#ED6B21\""] = "\"" + new_color + "\""; + + std::string str; + nsvgGetDataFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), str, replaces); + if (str.empty()) + return nullptr; + + return insert_bndl(bitmap_key, str.data(), target_width, target_height); +} + +wxBitmapBundle* BitmapCache::from_png(const std::string& bitmap_name, unsigned width, unsigned height) +{ + std::string bitmap_key = bitmap_name + (height != 0 ? + "-h" + std::to_string(height) : + "-w" + std::to_string(width)); + + auto it = m_bndl_map.find(bitmap_key); + if (it != m_bndl_map.end()) + return it->second; + + wxImage image; + if (!image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) || + image.GetWidth() == 0 || image.GetHeight() == 0) + return nullptr; + + if (height != 0 && unsigned(image.GetHeight()) != height) + width = unsigned(0.5f + float(image.GetWidth()) * height / image.GetHeight()); + else if (width != 0 && unsigned(image.GetWidth()) != width) + height = unsigned(0.5f + float(image.GetHeight()) * width / image.GetWidth()); + + if (height != 0 && width != 0) + image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); + + return this->insert_bndl(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); +} + wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/) { @@ -352,9 +493,9 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale); } - +/* //we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap -wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false*/, size_t border_width /*= 0*/, bool dark_mode/* = false*/) +wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false* /, size_t border_width /*= 0* /, bool dark_mode/* = false* /) { double scale = suppress_scaling ? 1.0f : m_scale; width *= scale; @@ -396,6 +537,89 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi return wxImage_to_wxBitmap_with_alpha(std::move(image), scale); } +*/ +//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap +wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/) +{ + wxVector bitmaps; + + std::set scales = { 1.0 }; +#ifndef __linux__ + +#ifdef __APPLE__ + scales.emplace(m_scale); +#else + size_t disp_cnt = wxDisplay::GetCount(); + for (size_t disp = 0; disp < disp_cnt; ++disp) + scales.emplace(wxDisplay(disp).GetScaleFactor()); +#endif + +#endif // !__linux__ + + for (double scale : scales) { + size_t width = width_in * scale; + size_t height = height_in * scale; + + wxImage image(width, height); + image.InitAlpha(); + unsigned char* imgdata = image.GetData(); + unsigned char* imgalpha = image.GetAlpha(); + for (size_t i = 0; i < width * height; ++i) { + *imgdata++ = r; + *imgdata++ = g; + *imgdata++ = b; + *imgalpha++ = transparency; + } + + // Add border, make white/light spools easier to see + if (border_width > 0) { + + // Restrict to width of image + if (border_width > height) border_width = height - 1; + if (border_width > width) border_width = width - 1; + + auto px_data = (uint8_t*)image.GetData(); + auto a_data = (uint8_t*)image.GetAlpha(); + + for (size_t x = 0; x < width; ++x) { + for (size_t y = 0; y < height; ++y) { + if (x < border_width || y < border_width || + x >= (width - border_width) || y >= (height - border_width)) { + const size_t idx = (x + y * width); + const size_t idx_rgb = (x + y * width) * 3; + px_data[idx_rgb] = px_data[idx_rgb + 1] = px_data[idx_rgb + 2] = dark_mode ? 245u : 110u; + a_data[idx] = 255u; + } + } + } + } + + bitmaps.push_back(wxImage_to_wxBitmap_with_alpha(std::move(image), scale)); + } + return wxBitmapBundle::FromBitmaps(bitmaps); +} + +wxBitmapBundle* BitmapCache::mksolid_bndl(size_t width, size_t height, const std::string& color, size_t border_width, bool dark_mode) +{ + std::string bitmap_key = (color.empty() ? "empty" : color) + "-h" + std::to_string(height) + "-w" + std::to_string(width) + (dark_mode ? "-dm" : ""); + + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + if (color.empty()) + bndl = new wxBitmapBundle(mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, size_t(0))); + else { + ColorRGB rgb;// [3] ; + decode_color(color, rgb); + bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode)); + } + m_bndl_map[bitmap_key] = bndl; + } + else + return it->second; + + return bndl; +} } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 5af90c5f7b..6ba9ae15b2 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -24,15 +24,19 @@ public: void clear(); double scale() { return m_scale; } + wxBitmapBundle* find_bndl(const std::string &name) { auto it = m_bndl_map.find(name); return (it == m_bndl_map.end()) ? nullptr : it->second; } + const wxBitmapBundle* find_bndl(const std::string &name) const { return const_cast(this)->find_bndl(name); } wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; } const wxBitmap* find(const std::string &name) const { return const_cast(this)->find(name); } - wxBitmap* insert(const std::string &name, size_t width, size_t height); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const char* data, size_t width, size_t height); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const wxBitmapBundle &bmp); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const wxVector& bmps); + wxBitmapBundle* insert_bndl(const std::string& name, const std::vector& bmps); + wxBitmapBundle* insert_raw_rgba_bndl(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); + + wxBitmap* insert(const std::string &name, size_t width, size_t height, double scale = -1.0); wxBitmap* insert(const std::string &name, const wxBitmap &bmp); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); - wxBitmap* insert(const std::string &name, const std::vector &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } - wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. @@ -42,15 +46,21 @@ public: // And makes replases befor parsing // replace_map containes old_value->new_value static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replaces); + // Gets a data from SVG file and makes replases + // replace_map containes old_value->new_value + static void nsvgGetDataFromFileWithReplace(const char* filename, std::string& data_str, const std::map& replaces); + wxBitmapBundle* from_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, const bool dark_mode, const std::string& new_color = ""); + wxBitmapBundle* from_png(const std::string& bitmap_name, unsigned width, unsigned height); // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = ""); - wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false); - wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } - wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } + wxBitmapBundle mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width = 0, bool dark_mode = false); + wxBitmapBundle* mksolid_bndl(size_t width, size_t height, const std::string& color = std::string(), size_t border_width = 0, bool dark_mode = false); + wxBitmapBundle* mkclear_bndl(size_t width, size_t height) { return mksolid_bndl(width, height); } private: std::map m_map; + std::map m_bndl_map; double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs) double m_scale = 1.0; // value, used for correct scaling of SVG icons on Retina display }; diff --git a/src/slic3r/GUI/BitmapComboBox.cpp b/src/slic3r/GUI/BitmapComboBox.cpp index 3396c627be..70c985cf90 100644 --- a/src/slic3r/GUI/BitmapComboBox.cpp +++ b/src/slic3r/GUI/BitmapComboBox.cpp @@ -54,17 +54,6 @@ using Slic3r::GUI::format_wxstr; namespace Slic3r { namespace GUI { -/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - BitmapComboBox::BitmapComboBox(wxWindow* parent, wxWindowID id/* = wxID_ANY*/, const wxString& value/* = wxEmptyString*/, @@ -90,72 +79,6 @@ BitmapComboBox::~BitmapComboBox() { } -#ifdef __APPLE__ -bool BitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) -{ - if (bitmap.IsOk()) - { - // we should use scaled! size values of bitmap - int width = (int)bitmap.GetScaledWidth(); - int height = (int)bitmap.GetScaledHeight(); - - if (m_usedImgSize.x < 0) - { - // If size not yet determined, get it from this image. - m_usedImgSize.x = width; - m_usedImgSize.y = height; - - // Adjust control size to vertically fit the bitmap - wxWindow* ctrl = GetControl(); - ctrl->InvalidateBestSize(); - wxSize newSz = ctrl->GetBestSize(); - wxSize sz = ctrl->GetSize(); - if (newSz.y > sz.y) - ctrl->SetSize(sz.x, newSz.y); - else - DetermineIndent(); - } - - wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, - false, - "you can only add images of same size"); - - return true; - } - - return false; -} - -void BitmapComboBox::OnDrawItem(wxDC& dc, - const wxRect& rect, - int item, - int flags) const -{ - const wxBitmap& bmp = *(static_cast(m_bitmaps[item])); - if (bmp.IsOk()) - { - // we should use scaled! size values of bitmap - wxCoord w = bmp.GetScaledWidth(); - wxCoord h = bmp.GetScaledHeight(); - - const int imgSpacingLeft = 4; - - // Draw the image centered - dc.DrawBitmap(bmp, - rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, - rect.y + (rect.height - h) / 2, - true); - } - - wxString text = GetString(item); - if (!text.empty()) - dc.DrawText(text, - rect.x + m_imgAreaWidth + 1, - rect.y + (rect.height - dc.GetCharHeight()) / 2); -} -#endif - - #ifdef _WIN32 int BitmapComboBox::Append(const wxString& item) @@ -166,18 +89,11 @@ int BitmapComboBox::Append(const wxString& item) //2. But then set width to 0 value for no using of bitmap left and right spacing //3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct - wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1)); - { - // bitmap.SetWidth(0); is depricated now - // so, use next code - bitmap.UnShare();// AllocExclusive(); - bitmap.GetGDIImageData()->m_width = 0; - } - + wxBitmapBundle bitmap = *get_empty_bmp_bundle(1, 16); OnAddBitmap(bitmap); + const int n = wxComboBox::Append(item); - if (n != wxNOT_FOUND) - DoSetItemBitmap(n, bitmap); + return n; } diff --git a/src/slic3r/GUI/BitmapComboBox.hpp b/src/slic3r/GUI/BitmapComboBox.hpp index a77bf401d6..545213fc3c 100644 --- a/src/slic3r/GUI/BitmapComboBox.hpp +++ b/src/slic3r/GUI/BitmapComboBox.hpp @@ -29,28 +29,13 @@ BitmapComboBox(wxWindow* parent, #ifdef _WIN32 int Append(const wxString& item); #endif - int Append(const wxString& item, const wxBitmap& bitmap) + int Append(const wxString& item, const wxBitmapBundle& bitmap) { return wxBitmapComboBox::Append(item, bitmap); } protected: -#ifdef __APPLE__ -/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ -bool OnAddBitmap(const wxBitmap& bitmap) override; -void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; -#endif - #ifdef _WIN32 bool MSWOnDraw(WXDRAWITEMSTRUCT* item) override; void DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(item), int flags) const; diff --git a/src/slic3r/GUI/ButtonsDescription.cpp b/src/slic3r/GUI/ButtonsDescription.cpp index 2c5262d47c..37daffd9d2 100644 --- a/src/slic3r/GUI/ButtonsDescription.cpp +++ b/src/slic3r/GUI/ButtonsDescription.cpp @@ -17,9 +17,6 @@ void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWi wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 5, 5); sizer->Add(grid_sizer, 0, wxEXPAND); - ScalableBitmap bmp_delete = ScalableBitmap(parent, "cross"); - ScalableBitmap bmp_delete_focus = ScalableBitmap(parent, "cross_focus"); - auto add_color = [grid_sizer, parent](wxColourPickerCtrl** color_picker, const wxColour& color, const wxColour& def_color, wxString label_text) { // wrap the label_text to the max 80 characters if (label_text.Len() > 80) { diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 803a6bfed5..d95848300e 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1609,7 +1609,7 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) #ifndef __WXOSX__ SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX #endif //__WXOSX__ - SetMinSize(bg.bmp().GetSize()); + SetMinSize(bg.GetSize()); const wxSize size = GetTextExtent("m"); em_w = size.x; @@ -1734,8 +1734,8 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) wxPaintDC dc(this); - const auto bullet_w = bullet_black.bmp().GetSize().GetWidth(); - const auto bullet_h = bullet_black.bmp().GetSize().GetHeight(); + const auto bullet_w = bullet_black.GetWidth(); + const auto bullet_h = bullet_black.GetHeight(); const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0; const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0; const int yinc = item_height(); @@ -1748,10 +1748,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) unsigned x = em_w/2 + item.indent * em_w; if (i == item_active || (item_hover >= 0 && i == (size_t)item_hover)) { - dc.DrawBitmap(bullet_blue.bmp(), x, y + yoff_icon, false); + dc.DrawBitmap(bullet_blue.get_bitmap(), x, y + yoff_icon, false); } - else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); } - else if (i > item_active) { dc.DrawBitmap(bullet_white.bmp(), x, y + yoff_icon, false); } + else if (i < item_active) { dc.DrawBitmap(bullet_black.get_bitmap(), x, y + yoff_icon, false); } + else if (i > item_active) { dc.DrawBitmap(bullet_white.get_bitmap(), x, y + yoff_icon, false); } x += + bullet_w + em_w/2; const auto text_size = dc.GetTextExtent(item.label); @@ -1763,9 +1763,9 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) } //draw logo - if (int y = size.y - bg.GetBmpHeight(); y>=0) { - dc.DrawBitmap(bg.bmp(), 0, y, false); - index_width = std::max(index_width, bg.GetBmpWidth() + em_w / 2); + if (int y = size.y - bg.GetHeight(); y>=0) { + dc.DrawBitmap(bg.get_bitmap(), 0, y, false); + index_width = std::max(index_width, bg.GetWidth() + em_w / 2); } if (GetMinSize().x < index_width) { @@ -1797,12 +1797,8 @@ void ConfigWizardIndex::msw_rescale() em_w = size.x; em_h = size.y; - bg.msw_rescale(); - SetMinSize(bg.bmp().GetSize()); + SetMinSize(bg.GetSize()); - bullet_black.msw_rescale(); - bullet_blue.msw_rescale(); - bullet_white.msw_rescale(); Refresh(); } diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 4de8381ffe..aa074f9253 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -519,7 +519,7 @@ private: ssize_t item_hover; size_t last_page; - int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; } + int item_height() const { return std::max(bullet_black.GetHeight(), em_w) + em_w; } void on_paint(wxPaintEvent &evt); void on_mouse_move(wxMouseEvent &evt); diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index dda50ec053..717af39ba8 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -86,24 +86,24 @@ Control::Control( wxWindow *parent, m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_right") : ScalableBitmap(this, "thumb_up")); m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_left") : ScalableBitmap(this, "thumb_down")); - m_thumb_size = m_bmp_thumb_lower.GetBmpSize(); + m_thumb_size = m_bmp_thumb_lower.GetSize(); m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); - m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth(); + m_tick_icon_dim = m_bmp_add_tick_on.GetWidth(); m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.GetWidth(); m_bmp_revert = ScalableBitmap(this, "undo"); - m_revert_icon_dim = m_bmp_revert.GetBmpWidth(); + m_revert_icon_dim = m_bmp_revert.GetWidth(); m_bmp_cog = ScalableBitmap(this, "cog"); - m_cog_icon_dim = m_bmp_cog.GetBmpWidth(); + m_cog_icon_dim = m_bmp_cog.GetWidth(); m_selection = ssUndef; m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume printing"))); @@ -155,26 +155,11 @@ void Control::msw_rescale() { m_font = GUI::wxGetApp().normal_font(); - m_bmp_thumb_higher.msw_rescale(); - m_bmp_thumb_lower .msw_rescale(); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); - - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; - - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; - - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; + m_thumb_size = m_bmp_thumb_lower.GetSize(); + m_tick_icon_dim = m_bmp_add_tick_on.GetWidth(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.GetWidth(); + m_revert_icon_dim = m_bmp_revert.GetWidth(); + m_cog_icon_dim = m_bmp_cog.GetWidth(); SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); @@ -189,22 +174,18 @@ void Control::sys_color_changed() { GUI::wxGetApp().UpdateDarkUI(GetParent()); - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth(); + m_bmp_add_tick_on .sys_color_changed(); + m_bmp_add_tick_off.sys_color_changed(); + m_bmp_del_tick_on .sys_color_changed(); + m_bmp_del_tick_off.sys_color_changed(); - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth(); + m_bmp_one_layer_lock_on .sys_color_changed(); + m_bmp_one_layer_lock_off .sys_color_changed(); + m_bmp_one_layer_unlock_on .sys_color_changed(); + m_bmp_one_layer_unlock_off.sys_color_changed(); - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.GetBmpWidth(); - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.GetBmpWidth(); + m_bmp_revert.sys_color_changed(); + m_bmp_cog .sys_color_changed(); } int Control::GetActiveValue() const @@ -604,9 +585,12 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ return; } - wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + //wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + //if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) + // icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + ScalableBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off : &m_bmp_add_tick_on; if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) - icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off : &m_bmp_del_tick_on; wxCoord x_draw, y_draw; is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; @@ -615,10 +599,12 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ else is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - if (m_draw_mode == dmSequentialFffPrint) - dc.DrawBitmap(create_scaled_bitmap("colorchange_add", nullptr, 16, true), x_draw, y_draw); + if (m_draw_mode == dmSequentialFffPrint) { + wxBitmap disabled_add = get_bmp_bundle("colorchange_add")->GetBitmapFor(this).ConvertToDisabled(); + dc.DrawBitmap(disabled_add, x_draw, y_draw); + } else - dc.DrawBitmap(*icon, x_draw, y_draw); + dc.DrawBitmap((*icon).get_bitmap(), x_draw, y_draw); //update rect of the tick action icon m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); @@ -851,7 +837,7 @@ void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider { wxCoord x_draw = pos.x - int(0.5 * m_thumb_size.x); wxCoord y_draw = pos.y - int(0.5 * m_thumb_size.y); - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); + dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.get_bitmap() : m_bmp_thumb_higher.get_bitmap(), x_draw, y_draw); // Update thumb rect update_thumb_rect(x_draw, y_draw, selection); @@ -945,12 +931,12 @@ void Control::draw_ticks(wxDC& dc) // Draw icon for "Pause print", "Custom Gcode" or conflict tick if (!icon_name.empty()) { - wxBitmap icon = create_scaled_bitmap(icon_name); + wxBitmapBundle* icon = get_bmp_bundle(icon_name); wxCoord x_draw, y_draw; is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; - dc.DrawBitmap(icon, x_draw, y_draw); + dc.DrawBitmap(icon->GetBitmapFor(this), x_draw, y_draw); } } } @@ -1262,9 +1248,12 @@ void Control::draw_one_layer_icon(wxDC& dc) if (m_draw_mode == dmSequentialGCodeView) return; - const wxBitmap& icon = m_is_one_layer ? - m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : - m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + //const wxBitmap& icon = m_is_one_layer ? + // m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : + // m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + const ScalableBitmap& icon = m_is_one_layer ? + m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off : m_bmp_one_layer_lock_on : + m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off : m_bmp_one_layer_unlock_on; int width, height; get_size(&width, &height); @@ -1273,7 +1262,7 @@ void Control::draw_one_layer_icon(wxDC& dc) is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - dc.DrawBitmap(icon, x_draw, y_draw); + dc.DrawBitmap(icon.bmp().GetBitmapFor(this), x_draw, y_draw); //update rect of the lock/unlock icon m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); @@ -1291,7 +1280,7 @@ void Control::draw_revert_icon(wxDC& dc) is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; - dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); + dc.DrawBitmap(m_bmp_revert.get_bitmap(), x_draw, y_draw); //update rect of the lock/unlock icon m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); @@ -1315,7 +1304,7 @@ void Control::draw_cog_icon(wxDC& dc) is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height - 2; } - dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); + dc.DrawBitmap(m_bmp_cog.get_bitmap(), x_draw, y_draw); //update rect of the lock/unlock icon m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); @@ -1673,7 +1662,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current if (extruders_cnt > 1) { std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); - std::vector icons = get_extruder_color_icons(true); + std::vector icons = get_extruder_color_icons(true); wxMenu* change_extruder_menu = new wxMenu(); @@ -1684,7 +1673,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current if (m_mode == MultiAsSingle) append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(ToolChange, i); }, *icons[i-1], menu, + [this, i](wxCommandEvent&) { add_code_as_tick(ToolChange, i); }, icons[i-1], menu, [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); } @@ -1722,7 +1711,7 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren format_wxstr(_L("Switch code to Color change (%1%) for:"), gcode(ColorChange)) : format_wxstr(_L("Add color change (%1%) for:"), gcode(ColorChange)); wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); - add_color_change_menu_item->SetBitmap(create_menu_bitmap("colorchange_add_m")); + add_color_change_menu_item->SetBitmap(*get_bmp_bundle("colorchange_add_m")); } } diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index d72e1dd328..0368a2fe28 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -33,6 +33,15 @@ wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) +static wxSize get_size(const wxBitmap& icon) +{ +#ifdef __WIN32__ + return icon.GetSize(); +#else + return icon.GetScaledSize(); +#endif +} + // --------------------------------------------------------- // BitmapTextRenderer // --------------------------------------------------------- @@ -124,11 +133,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) const wxBitmap& icon = m_value.GetBitmap(); if (icon.IsOk()) { -#ifdef __APPLE__ - wxSize icon_sz = icon.GetScaledSize(); -#else - wxSize icon_sz = icon.GetSize(); -#endif + wxSize icon_sz = get_size(icon); dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); xoffset = icon_sz.x + 4; } @@ -264,11 +269,13 @@ bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) const wxBitmap& icon = m_value.GetBitmap(); if (icon.IsOk()) { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; + wxSize icon_sz = get_size(icon); + + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.GetHeight()) / 2); + xoffset = icon_sz.GetWidth() + 4; if (rect.height==0) - rect.height= icon.GetHeight(); + rect.height= icon_sz.GetHeight(); } #ifdef _WIN32 @@ -297,7 +304,7 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; - std::vector icons = get_extruder_color_icons(); + std::vector icons = get_extruder_color_icons(); if (icons.empty()) return nullptr; diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.cpp b/src/slic3r/GUI/ExtruderSequenceDialog.cpp index 42313636ec..e1c6a7ce02 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.cpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.cpp @@ -264,9 +264,6 @@ void ExtruderSequenceDialog::on_dpi_changed(const wxRect& suggested_rect) { SetFont(wxGetApp().normal_font()); - m_bmp_add.msw_rescale(); - m_bmp_del.msw_rescale(); - const int em = em_unit(); m_intervals_grid_sizer->SetHGap(em); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 97db4ea7f0..60a06427d7 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -106,14 +106,14 @@ public: bool set_undo_to_sys_tooltip(const wxString* tip) { return m_undo_ui.set_undo_to_sys_tooltip(tip); } // ui items used for revert line value - bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; } - const wxBitmap& undo_bitmap() const { return m_undo_ui.undo_bitmap->bmp(); } - const wxString* undo_tooltip() const { return m_undo_ui.undo_tooltip; } - const wxBitmap& undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap->bmp(); } - const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; } - const wxColour* label_color() const { return m_undo_ui.label_color; } - const bool blink() const { return m_undo_ui.blink; } - bool* get_blink_ptr() { return &m_undo_ui.blink; } + bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; } + const wxBitmapBundle& undo_bitmap() const { return m_undo_ui.undo_bitmap->bmp(); } + const wxString* undo_tooltip() const { return m_undo_ui.undo_tooltip; } + const wxBitmapBundle& undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap->bmp(); } + const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; } + const wxColour* label_color() const { return m_undo_ui.label_color; } + const bool blink() const { return m_undo_ui.blink; } + bool* get_blink_ptr() { return &m_undo_ui.blink; } }; diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 17dfe9356d..53566a9dc8 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -23,8 +23,8 @@ #define STB_DXT_IMPLEMENTATION #include "stb_dxt/stb_dxt.h" -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" +#include +#include #include "libslic3r/Utils.hpp" diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 533e46900b..95ef05c450 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -125,13 +125,16 @@ public: int init_dpi = get_dpi_for_window(this); this->SetPosition(pos); + // The size of the SplashScreen can be hanged after its moving to another display + // So, update it from a bitmap size + this->SetClientSize(bitmap.GetWidth(), bitmap.GetHeight()); this->CenterOnScreen(); int new_dpi = get_dpi_for_window(this); - m_scale = (float)(new_dpi) / (float)(init_dpi); +// m_scale = (float)(new_dpi) / (float)(init_dpi); m_main_bitmap = bitmap; - scale_bitmap(m_main_bitmap, m_scale); +// scale_bitmap(m_main_bitmap, m_scale); // init constant texts and scale fonts init_constant_text(); @@ -331,7 +334,7 @@ private: // See https://github.com/wxWidgets/wxWidgets/blob/master/src/msw/font.cpp // void wxNativeFontInfo::SetFractionalPointSize(float pointSizeNew) wxNativeFontInfo nfi= *font.GetNativeFontInfo(); - float pointSizeNew = scale * font.GetPointSize(); + float pointSizeNew = wxDisplay(this).GetScaleFactor() * scale * font.GetPointSize(); nfi.lf.lfHeight = nfi.GetLogFontHeightAtPPI(pointSizeNew, get_dpi_for_window(this)); nfi.pointSize = pointSizeNew; font = wxFont(nfi); @@ -1188,7 +1191,7 @@ bool GUI_App::on_init_inner() } // create splash screen with updated bmp - scrn = new SplashScreen(bmp.IsOk() ? bmp : create_scaled_bitmap("PrusaSlicer", nullptr, 400), + scrn = new SplashScreen(bmp.IsOk() ? bmp : get_bmp_bundle("PrusaSlicer", 400)->GetPreferredBitmapSizeAtScale(1.0), wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, splashscreen_pos); if (!default_splashscreen_pos) @@ -2099,6 +2102,15 @@ bool GUI_App::load_language(wxString language, bool initial) { // Allocating a temporary locale will switch the default wxTranslations to its internal wxTranslations instance. wxLocale temp_locale; +#ifdef __WXOSX__ + // ysFIXME - temporary workaround till it isn't fixed in wxWidgets: + // Use English as an initial language, because of under OSX it try to load "inappropriate" language for wxLANGUAGE_DEFAULT. + // For example in our case it's trying to load "en_CZ" and as a result PrusaSlicer catch warning message. + // But wxWidgets guys work on it. + temp_locale.Init(wxLANGUAGE_ENGLISH); +#else + temp_locale.Init(); +#endif // __WXOSX__ // Set the current translation's language to default, otherwise GetBestTranslation() may not work (see the wxWidgets source code). wxTranslations::Get()->SetLanguage(wxLANGUAGE_DEFAULT); // Let the wxFileTranslationsLoader enumerate all translation dictionaries for PrusaSlicer @@ -2239,7 +2251,7 @@ void GUI_App::update_mode() { sidebar().update_mode(); -#ifdef _MSW_DARK_MODE +#ifdef _WIN32 //_MSW_DARK_MODE if (!wxGetApp().tabs_as_menu()) dynamic_cast(mainframe->m_tabpanel)->UpdateMode(); #endif diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 7b3476d711..6a3dad5f4d 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -142,14 +142,13 @@ std::map SettingsFactory::CATEGORY_ICON = { L("Hollowing") , "hollowing" } }; -wxBitmap SettingsFactory::get_category_bitmap(const std::string& category_name, bool menu_bmp /*= true*/) +wxBitmapBundle* SettingsFactory::get_category_bitmap(const std::string& category_name) { if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) - return wxNullBitmap; - return menu_bmp ? create_menu_bitmap(CATEGORY_ICON.at(category_name)) : create_scaled_bitmap(CATEGORY_ICON.at(category_name)); + return get_bmp_bundle("empty"); + return get_bmp_bundle(CATEGORY_ICON.at(category_name)); } - //------------------------------------- // MenuFactory //------------------------------------- @@ -430,12 +429,12 @@ static void create_freq_settings_popupmenu(wxMenu* menu, const bool is_object_se #endif } -std::vector MenuFactory::get_volume_bitmaps() +std::vector MenuFactory::get_volume_bitmaps() { - std::vector volume_bmps; + std::vector volume_bmps; volume_bmps.reserve(ADD_VOLUME_MENU_ITEMS.size()); for (auto item : ADD_VOLUME_MENU_ITEMS) - volume_bmps.push_back(create_menu_bitmap(item.second)); + volume_bmps.push_back(get_bmp_bundle(item.second)); return volume_bmps; } @@ -615,7 +614,7 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) // Add full settings list auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); - menu_item->SetBitmap(create_menu_bitmap("cog")); + menu_item->SetBitmap(*get_bmp_bundle("cog")); menu_item->SetSubMenu(create_settings_popupmenu(menu, is_object_settings, item)); return menu->Append(menu_item); @@ -760,7 +759,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) return; } - std::vector icons = get_extruder_color_icons(true); + std::vector icons = get_extruder_color_icons(true); wxMenu* extruder_selection_menu = new wxMenu(); const wxString& name = sels.Count() == 1 ? names[0] : names[1]; @@ -779,7 +778,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) (is_active_extruder ? " (" + _L("active") + ")" : ""); append_menu_item(extruder_selection_menu, wxID_ANY, item_name, "", - [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, *icons[icon_idx], menu, + [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, icons[icon_idx], menu, [is_active_extruder]() { return !is_active_extruder; }, m_parent); } @@ -1139,12 +1138,6 @@ void MenuFactory::update_default_menu() create_default_menu(); } -void MenuFactory::msw_rescale() -{ - for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) - msw_rescale_menu(dynamic_cast(menu)); -} - #ifdef _WIN32 // For this class is used code from stackoverflow: // https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence @@ -1174,7 +1167,7 @@ static void update_menu_item_def_colors(T* item) void MenuFactory::sys_color_changed() { for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) { - msw_rescale_menu(dynamic_cast(menu));// msw_rescale_menu updates just icons, so use it + sys_color_changed_menu(dynamic_cast(menu));// msw_rescale_menu updates just icons, so use it #ifdef _WIN32 // but under MSW we have to update item's bachground color for (wxMenuItem* item : menu->GetMenuItems()) @@ -1187,14 +1180,17 @@ void MenuFactory::sys_color_changed(wxMenuBar* menubar) { for (size_t id = 0; id < menubar->GetMenuCount(); id++) { wxMenu* menu = menubar->GetMenu(id); - msw_rescale_menu(menu); + sys_color_changed_menu(menu); +#ifndef __linux__ + menu->SetupBitmaps(); #ifdef _WIN32 // but under MSW we have to update item's bachground color for (wxMenuItem* item : menu->GetMenuItems()) update_menu_item_def_colors(item); +#endif #endif } - menubar->Refresh(); +// menubar->Refresh(); } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 0c478a97b2..bbbc00d42b 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -25,7 +25,7 @@ struct SettingsFactory typedef std::map> Bundle; static std::map CATEGORY_ICON; - static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true); + static wxBitmapBundle* get_category_bitmap(const std::string& category_name); static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings); static std::vector get_options(bool is_part); }; @@ -34,7 +34,7 @@ class MenuFactory { public: static const std::vector> ADD_VOLUME_MENU_ITEMS; - static std::vector get_volume_bitmaps(); + static std::vector get_volume_bitmaps(); MenuFactory(); ~MenuFactory() = default; @@ -43,7 +43,6 @@ public: void update(); void update_object_menu(); void update_default_menu(); - void msw_rescale(); void sys_color_changed(); static void sys_color_changed(wxMenuBar* menu_bar); diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 0587f353fc..6f6ec52380 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -234,47 +234,47 @@ void ObjectLayers::UpdateAndShow(const bool show) void ObjectLayers::msw_rescale() { - m_bmp_delete.msw_rescale(); - m_bmp_add.msw_rescale(); + //m_bmp_delete.msw_rescale(); + //m_bmp_add.msw_rescale(); - m_grid_sizer->SetHGap(wxGetApp().em_unit()); + //m_grid_sizer->SetHGap(wxGetApp().em_unit()); - // rescale edit-boxes - const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; ++i) { - const wxSizerItem* item = m_grid_sizer->GetItem(i); - if (item->IsWindow()) { - LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); - if (editor != nullptr) - editor->msw_rescale(); - } - else if (item->IsSizer()) // case when we have editor with buttons - { - wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor - if (e_item->IsWindow()) { - LayerRangeEditor* editor = dynamic_cast(e_item->GetWindow()); - if (editor != nullptr) - editor->msw_rescale(); - } + //// rescale edit-boxes + //const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); + //for (int i = 0; i < cells_cnt; ++i) { + // const wxSizerItem* item = m_grid_sizer->GetItem(i); + // if (item->IsWindow()) { + // LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); + // if (editor != nullptr) + // editor->msw_rescale(); + // } + // else if (item->IsSizer()) // case when we have editor with buttons + // { + // wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor + // if (e_item->IsWindow()) { + // LayerRangeEditor* editor = dynamic_cast(e_item->GetWindow()); + // if (editor != nullptr) + // editor->msw_rescale(); + // } - if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons - for (size_t btn : {2, 3}) { // del_btn, add_btn - wxSizerItem* b_item = item->GetSizer()->GetItem(btn); - if (b_item->IsWindow()) { - auto button = dynamic_cast(b_item->GetWindow()); - if (button != nullptr) - button->msw_rescale(); - } - } - } - } + // if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons + // for (size_t btn : {2, 3}) { // del_btn, add_btn + // wxSizerItem* b_item = item->GetSizer()->GetItem(btn); + // if (b_item->IsWindow()) { + // auto button = dynamic_cast(b_item->GetWindow()); + // if (button != nullptr) + // button->msw_rescale(); + // } + // } + // } + //} m_grid_sizer->Layout(); } void ObjectLayers::sys_color_changed() { - m_bmp_delete.msw_rescale(); - m_bmp_add.msw_rescale(); + m_bmp_delete.sys_color_changed(); + m_bmp_add.sys_color_changed(); // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); @@ -286,7 +286,7 @@ void ObjectLayers::sys_color_changed() if (b_item->IsWindow()) { auto button = dynamic_cast(b_item->GetWindow()); if (button != nullptr) - button->msw_rescale(); + button->sys_color_changed(); } } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c1fdf1c9f4..90fbd9d5e6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -971,12 +971,11 @@ void ObjectList::extruder_editing() if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) return; - const int column_width = GetColumn(colExtruder)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5; - - wxPoint pos = this->get_mouse_position_in_control(); - wxSize size = wxSize(column_width, -1); - pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5; - pos.y -= GetTextExtent("m").y; + wxRect rect = this->GetItemRect(item, GetColumn(colExtruder)); + wxPoint pos = rect.GetPosition(); + pos.y -= 4; + wxSize size = rect.GetSize(); + size.SetWidth(size.GetWidth() + 8); apply_extruder_selector(&m_extruder_editor, this, L("default"), pos, size); @@ -2433,6 +2432,21 @@ bool ObjectList::can_merge_to_single_object() const return (*m_objects)[obj_idx]->volumes.size() > 1; } +wxPoint ObjectList::get_mouse_position_in_control() const +{ + wxPoint pt = wxGetMousePosition() - this->GetScreenPosition(); + +#ifdef __APPLE__ + // Workaround for OSX. From wxWidgets 3.1.6 Hittest doesn't respect to the header of wxDataViewCtrl + if (wxDataViewItem top_item = this->GetTopItem(); top_item.IsOk()) { + auto rect = this->GetItemRect(top_item, this->GetColumn(0)); + pt.y -= rect.y; + } +#endif // __APPLE__ + + return pt; +} + // NO_PARAMETERS function call means that changed object index will be determine from Selection() void ObjectList::changed_object(const int obj_idx/* = -1*/) const { @@ -4239,9 +4253,6 @@ void ObjectList::msw_rescale() GetColumn(colExtruder)->SetWidth( 8 * em); GetColumn(colEditing )->SetWidth( 3 * em); - // rescale/update existing items with bitmaps - m_objects_model->Rescale(); - Layout(); } @@ -4249,7 +4260,10 @@ void ObjectList::sys_color_changed() { wxGetApp().UpdateDVCDarkUI(this, true); - msw_rescale(); + // update existing items with bitmaps + m_objects_model->UpdateBitmaps(); + + Layout(); } void ObjectList::ItemValueChanged(wxDataViewEvent &event) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b9b816b7be..c838203ae3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -280,7 +280,7 @@ public: bool can_merge_to_multipart_object() const; bool can_merge_to_single_object() const; - wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); } + wxPoint get_mouse_position_in_control() const; wxBoxSizer* get_sizer() {return m_sizer;} int get_selected_obj_idx() const; ModelConfig& get_item_config(const wxDataViewItem& item) const; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 24ae01389b..a538f2b335 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -126,7 +126,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Load bitmaps to be used for the mirroring buttons: m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); - m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); + m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent"); const int border = wxOSX ? 0 : 4; const int em = wxGetApp().em_unit(); @@ -1009,7 +1009,7 @@ void ObjectManipulation::update_warning_icon_state(const MeshErrorsInfo& warning m_manifold_warning_bmp = ScalableBitmap(m_parent, warning_icon_name); const wxString& tooltip = warning.tooltip; m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); - m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize()); + m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.GetSize()); m_fix_throught_netfab_bitmap->SetToolTip(tooltip); } @@ -1336,25 +1336,10 @@ void ObjectManipulation::msw_rescale() m_item_name->SetMinSize(wxSize(20*em, wxDefaultCoord)); msw_rescale_word_local_combo(m_word_local_combo); m_word_local_combo_sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); - m_manifold_warning_bmp.msw_rescale(); const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText(); m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); - m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.bmp().GetSize()); - - m_mirror_bitmap_on.msw_rescale(); - m_mirror_bitmap_off.msw_rescale(); - m_mirror_bitmap_hidden.msw_rescale(); - m_reset_scale_button->msw_rescale(); - m_reset_rotation_button->msw_rescale(); -#if ENABLE_WORLD_COORDINATE - m_reset_skew_button->msw_rescale(); -#endif /// ENABLE_WORLD_COORDINATE - m_drop_to_bed_button->msw_rescale(); - m_lock_bnt->msw_rescale(); - - for (int id = 0; id < 3; ++id) - m_mirror_buttons[id].first->msw_rescale(); + m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.GetSize()); // rescale label-heights // Text trick to grid sizer layout: @@ -1383,20 +1368,16 @@ void ObjectManipulation::sys_color_changed() for (ManipulationEditor* editor : m_editors) editor->sys_color_changed(this); - // btn...->msw_rescale() updates icon on button, so use it - m_mirror_bitmap_on.msw_rescale(); - m_mirror_bitmap_off.msw_rescale(); - m_mirror_bitmap_hidden.msw_rescale(); - m_reset_scale_button->msw_rescale(); - m_reset_rotation_button->msw_rescale(); -#if ENABLE_WORLD_COORDINATE - m_reset_skew_button->msw_rescale(); -#endif // ENABLE_WORLD_COORDINATE - m_drop_to_bed_button->msw_rescale(); - m_lock_bnt->msw_rescale(); + m_mirror_bitmap_on.sys_color_changed(); + m_mirror_bitmap_off.sys_color_changed(); + m_mirror_bitmap_hidden.sys_color_changed(); + m_reset_scale_button->sys_color_changed(); + m_reset_rotation_button->sys_color_changed(); + m_drop_to_bed_button->sys_color_changed(); + m_lock_bnt->sys_color_changed(); for (int id = 0; id < 3; ++id) - m_mirror_buttons[id].first->msw_rescale(); + m_mirror_buttons[id].first->sys_color_changed(); } #if ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 291013fe9d..97eb5f10d9 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -99,7 +99,7 @@ bool ObjectSettings::update_settings_list() btn->SetToolTip(_(L("Remove parameter"))); btn->SetBitmapFocus(m_bmp_delete_focus.bmp()); - btn->SetBitmapHover(m_bmp_delete_focus.bmp()); + btn->SetBitmapCurrent(m_bmp_delete_focus.bmp()); btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Delete Option %s"))) % opt_key).str())); @@ -133,7 +133,7 @@ bool ObjectSettings::update_settings_list() return; ctrl->SetBitmap_(m_bmp_delete); ctrl->SetBitmapFocus(m_bmp_delete_focus.bmp()); - ctrl->SetBitmapHover(m_bmp_delete_focus.bmp()); + ctrl->SetBitmapCurrent(m_bmp_delete_focus.bmp()); }; const bool is_extruders_cat = cat.first == "Extruders"; @@ -268,15 +268,6 @@ void ObjectSettings::UpdateAndShow(const bool show) OG_Settings::UpdateAndShow(show ? update_settings_list() : false); } -void ObjectSettings::msw_rescale() -{ - m_bmp_delete.msw_rescale(); - m_bmp_delete_focus.msw_rescale(); - - for (auto group : m_og_settings) - group->msw_rescale(); -} - void ObjectSettings::sys_color_changed() { m_og->sys_color_changed(); diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index e5a6937f11..5d0be13082 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -56,7 +56,6 @@ public: bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from); void update_config_values(ModelConfig *config); void UpdateAndShow(const bool show) override; - void msw_rescale(); void sys_color_changed(); }; diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index 975b807dcb..6be6a94fed 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -157,8 +157,9 @@ bool GalleryDialog::can_change_thumbnail() void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect) { - const int& em = em_unit(); + update(); + const int& em = em_unit(); msw_buttons_rescale(this, em, { ID_BTN_ADD_CUSTOM_SHAPE, ID_BTN_DEL_CUSTOM_SHAPE, ID_BTN_REPLACE_CUSTOM_PNG, wxID_OK, wxID_CLOSE }); wxSize size = wxSize(50 * em, 35 * em); @@ -169,13 +170,14 @@ void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect) Refresh(); } -static void add_lock(wxImage& image) +static void add_lock(wxImage& image, wxWindow* parent_win) { - int lock_sz = 22; + wxBitmapBundle* bmp_bndl = get_bmp_bundle("lock", 22); #ifdef __APPLE__ - lock_sz /= mac_max_scaling_factor(); + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize() * mac_max_scaling_factor()); +#else + wxBitmap bmp = bmp_bndl->GetBitmapFor(parent_win); #endif - wxBitmap bmp = create_scaled_bitmap("lock", nullptr, lock_sz); wxImage lock_image = bmp.ConvertToImage(); if (!lock_image.IsOk() || lock_image.GetWidth() == 0 || lock_image.GetHeight() == 0) @@ -213,21 +215,28 @@ static void add_lock(wxImage& image) } } -static void add_default_image(wxImageList* img_list, bool is_system) +static void add_default_image(wxImageList* img_list, bool is_system, wxWindow* parent_win) { - int sz = IMG_PX_CNT; + wxBitmapBundle* bmp_bndl = get_bmp_bundle("cog", IMG_PX_CNT); #ifdef __APPLE__ - sz /= mac_max_scaling_factor(); + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize() * mac_max_scaling_factor()); +#else + wxBitmap bmp = bmp_bndl->GetBitmapFor(parent_win); #endif - wxBitmap bmp = create_scaled_bitmap("cog", nullptr, sz, true); + bmp = bmp.ConvertToDisabled(); if (is_system) { wxImage image = bmp.ConvertToImage(); if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) { - add_lock(image); + add_lock(image, parent_win); +#ifdef __APPLE__ + bmp = wxBitmap(std::move(image), -1, mac_max_scaling_factor()); +#else bmp = wxBitmap(std::move(image)); +#endif } } + img_list->Add(bmp); }; @@ -344,8 +353,13 @@ void GalleryDialog::load_label_icon_list() // Make an image list containing large icons +#ifdef __APPLE__ + m_image_list = new wxImageList(IMG_PX_CNT, IMG_PX_CNT); + int px_cnt = IMG_PX_CNT * mac_max_scaling_factor(); +#else int px_cnt = (int)(em_unit() * IMG_PX_CNT * 0.1f + 0.5f); m_image_list = new wxImageList(px_cnt, px_cnt); +#endif std::string ext = ".png"; @@ -364,7 +378,7 @@ void GalleryDialog::load_label_icon_list() if (can_generate_thumbnail) generate_thumbnail_from_model(model_name); else { - add_default_image(m_image_list, item.is_system); + add_default_image(m_image_list, item.is_system, this); continue; } } @@ -373,14 +387,18 @@ void GalleryDialog::load_label_icon_list() if (!image.CanRead(from_u8(img_name)) || !image.LoadFile(from_u8(img_name), wxBITMAP_TYPE_PNG) || image.GetWidth() == 0 || image.GetHeight() == 0) { - add_default_image(m_image_list, item.is_system); + add_default_image(m_image_list, item.is_system, this); continue; } image.Rescale(px_cnt, px_cnt, wxIMAGE_QUALITY_BILINEAR); if (item.is_system) - add_lock(image); + add_lock(image, this); +#ifdef __APPLE__ + wxBitmap bmp = wxBitmap(std::move(image), -1, mac_max_scaling_factor()); +#else wxBitmap bmp = wxBitmap(std::move(image)); +#endif m_image_list->Add(bmp); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 813c754196..eb1ec72e47 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -35,9 +35,8 @@ #include "GUI_App.hpp" #include "../Utils/MacDarkMode.hpp" - -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" +#include +#include namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index d19925f313..01b6cd626b 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -57,8 +57,8 @@ KBShortcutsDialog::KBShortcutsDialog() void KBShortcutsDialog::on_dpi_changed(const wxRect& suggested_rect) { - m_logo_bmp.msw_rescale(); - m_header_bitmap->SetBitmap(m_logo_bmp.bmp()); + //m_logo_bmp.msw_rescale(); + //m_header_bitmap->SetBitmap(m_logo_bmp.bmp()); msw_buttons_rescale(this, em_unit(), { wxID_OK }); Layout(); @@ -266,8 +266,8 @@ wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_f sizer->AddStretchSpacer(); // logo - m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 32); - m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp()); + m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, *get_bmp_bundle(wxGetApp().logo_name(), 32)); + sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); // text diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0ec2b99643..fe2f18769b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1014,9 +1014,6 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) for (auto tab : wxGetApp().tabs_list) tab->msw_rescale(); - for (size_t id = 0; id < m_menubar->GetMenuCount(); id++) - msw_rescale_menu(m_menubar->GetMenu(id)); - // Workarounds for correct Window rendering after rescale /* Even if Window is maximized during moving, @@ -1051,7 +1048,7 @@ void MainFrame::on_sys_color_changed() #ifdef _MSW_DARK_MODE // update common mode sizer if (!wxGetApp().tabs_as_menu()) - dynamic_cast(m_tabpanel)->Rescale(); + dynamic_cast(m_tabpanel)->OnColorsChanged(); #endif #endif @@ -1612,9 +1609,9 @@ void MainFrame::update_menubar() m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _L("S&end G-code") : _L("S&end to print")) + dots + "\tCtrl+Shift+G"); m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _L("&Filament Settings Tab") : _L("Mate&rial Settings Tab")) + "\tCtrl+3"); - m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_menu_bitmap(is_fff ? "spool" : "resin")); + m_changeable_menu_items[miMaterialTab] ->SetBitmap(*get_bmp_bundle(is_fff ? "spool" : "resin")); - m_changeable_menu_items[miPrinterTab] ->SetBitmap(create_menu_bitmap(is_fff ? "printer" : "sla_printer")); + m_changeable_menu_items[miPrinterTab] ->SetBitmap(*get_bmp_bundle(is_fff ? "printer" : "sla_printer")); } #if 0 diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 94e9ca5f3b..43e13841c5 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -99,9 +99,10 @@ void MsgDialog::apply_style(long style) if (style & wxNO) add_button(wxID_NO, (style & wxNO_DEFAULT)); if (style & wxCANCEL) add_button(wxID_CANCEL, (style & wxCANCEL_DEFAULT)); - logo->SetBitmap( create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" : - style & wxICON_INFORMATION ? "info" : - style & wxICON_QUESTION ? "question" : "PrusaSlicer", this, 64, style & wxICON_ERROR)); + std::string icon_name = style & wxICON_WARNING ? "exclamation" : + style & wxICON_INFORMATION ? "info" : + style & wxICON_QUESTION ? "question" : "PrusaSlicer"; + logo->SetBitmap(*get_bmp_bundle(icon_name, 64)); } void MsgDialog::finalize() @@ -230,7 +231,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_ add_msg_content(this, content_sizer, msg, monospaced_font); // Use a small bitmap with monospaced font, as the error text will not be wrapped. - logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/84)); + logo->SetBitmap(*get_bmp_bundle("PrusaSlicer_192px_grayscale.png", monospaced_font ? 48 : /*1*/84)); SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit())); diff --git a/src/slic3r/GUI/Notebook.cpp b/src/slic3r/GUI/Notebook.cpp index 9c5ccb834e..380b402d51 100644 --- a/src/slic3r/GUI/Notebook.cpp +++ b/src/slic3r/GUI/Notebook.cpp @@ -95,10 +95,6 @@ void ButtonsListCtrl::UpdateMode() void ButtonsListCtrl::Rescale() { - m_mode_sizer->msw_rescale(); - for (ScalableButton* btn : m_pageButtons) - btn->msw_rescale(); - int em = em_unit(this); m_btn_margin = std::lround(0.3 * em); m_line_margin = std::lround(0.1 * em); @@ -108,6 +104,14 @@ void ButtonsListCtrl::Rescale() m_sizer->Layout(); } +void ButtonsListCtrl::OnColorsChanged() +{ + for (ScalableButton* btn : m_pageButtons) + btn->sys_color_changed(); + + m_sizer->Layout(); +} + void ButtonsListCtrl::SetSelection(int sel) { if (m_selection == sel) diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp index af03a6a080..bd6c5d85a8 100644 --- a/src/slic3r/GUI/Notebook.hpp +++ b/src/slic3r/GUI/Notebook.hpp @@ -21,6 +21,7 @@ public: void SetSelection(int sel); void UpdateMode(); void Rescale(); + void OnColorsChanged(); bool InsertPage(size_t n, const wxString& text, bool bSelect = false, const std::string& bmp_name = ""); void RemovePage(size_t n); bool SetPageImage(size_t n, const std::string& bmp_name) const; @@ -245,6 +246,11 @@ public: GetBtnsListCtrl()->Rescale(); } + void OnColorsChanged() + { + GetBtnsListCtrl()->OnColorsChanged(); + } + void OnNavigationKey(wxNavigationKeyEvent& event) { if (event.IsWindowChange()) { diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 836ad0c38d..6c088aa797 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -19,12 +19,12 @@ static bool is_point_in_rect(const wxPoint& pt, const wxRect& rect) rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); } -static wxSize get_bitmap_size(const wxBitmap& bmp) +static wxSize get_bitmap_size(const wxBitmapBundle* bmp, wxWindow* parent) { -#ifdef __APPLE__ - return bmp.GetScaledSize(); +#ifdef __WIN32__ + return bmp->GetBitmapFor(parent).GetSize(); #else - return bmp.GetSize(); + return bmp->GetDefaultSize(); #endif } @@ -45,8 +45,8 @@ OG_CustomCtrl::OG_CustomCtrl( wxWindow* parent, m_v_gap = lround(1.0 * m_em_unit); m_h_gap = lround(0.2 * m_em_unit); - m_bmp_mode_sz = get_bitmap_size(create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12)); - m_bmp_blinking_sz = get_bitmap_size(create_scaled_bitmap("search_blink", this)); + m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this); + m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("search_blink"), this); init_ctrl_lines();// from og.lines() @@ -416,8 +416,8 @@ void OG_CustomCtrl::msw_rescale() m_v_gap = lround(1.0 * m_em_unit); m_h_gap = lround(0.2 * m_em_unit); - m_bmp_mode_sz = create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12).GetSize(); - m_bmp_blinking_sz = create_scaled_bitmap("search_blink", this).GetSize(); + m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this); + m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("search_blink"), this); m_max_win_width = 0; @@ -497,7 +497,7 @@ void OG_CustomCtrl::CtrlLine::msw_rescale() { // if we have a single option with no label, no sidetext if (draw_just_act_buttons) - height = get_bitmap_size(create_scaled_bitmap("empty")).GetHeight(); + height = get_bitmap_size(get_bmp_bundle("empty"), ctrl).GetHeight(); if (ctrl->opt_group->label_width != 0 && !og_line.label.IsEmpty()) { wxSize label_sz = ctrl->GetTextExtent(og_line.label); @@ -666,13 +666,13 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_mode_bmp(wxDC& dc, wxCoord v_pos) ConfigOptionMode mode = og_line.get_options()[0].opt.mode; const std::string& bmp_name = mode == ConfigOptionMode::comSimple ? "mode_simple" : mode == ConfigOptionMode::comAdvanced ? "mode_advanced" : "mode_expert"; - wxBitmap bmp = create_scaled_bitmap(bmp_name, ctrl, wxOSX ? 10 : 12); - wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp).GetHeight()) / 2); + wxBitmapBundle* bmp = get_bmp_bundle(bmp_name, wxOSX ? 10 : 12); + wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp, ctrl).GetHeight()) / 2); if (og_line.get_options().front().opt.gui_type != ConfigOptionDef::GUIType::legend) - dc.DrawBitmap(bmp, 0, y_draw); + dc.DrawBitmap(bmp->GetBitmapFor(ctrl), 0, y_draw); - return get_bitmap_size(bmp).GetWidth() + ctrl->m_h_gap; + return get_bitmap_size(bmp, ctrl).GetWidth() + ctrl->m_h_gap; } wxCoord OG_CustomCtrl::CtrlLine::draw_text(wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url/* = false*/) @@ -734,33 +734,33 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_text(wxDC& dc, wxPoint pos, const wxStr wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking) { - wxBitmap bmp_blinking = create_scaled_bitmap(is_blinking ? "search_blink" : "empty", ctrl); + wxBitmapBundle* bmp_blinking = get_bmp_bundle(is_blinking ? "search_blink" : "empty"); wxCoord h_pos = pos.x; - wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking).GetHeight()) / 2); + wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking, ctrl).GetHeight()) / 2); - dc.DrawBitmap(bmp_blinking, h_pos, v_pos); + dc.DrawBitmap(bmp_blinking->GetBitmapFor(ctrl), h_pos, v_pos); - int bmp_dim = get_bitmap_size(bmp_blinking).GetWidth(); + int bmp_dim = get_bitmap_size(bmp_blinking, ctrl).GetWidth(); h_pos += bmp_dim + ctrl->m_h_gap; return wxPoint(h_pos, v_pos); } -wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id) +wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) { pos = draw_blinking_bmp(dc, pos, is_blinking); wxCoord h_pos = pos.x; wxCoord v_pos = pos.y; - dc.DrawBitmap(bmp_undo_to_sys, h_pos, v_pos); + dc.DrawBitmap(bmp_undo_to_sys.GetBitmapFor(ctrl), h_pos, v_pos); - int bmp_dim = get_bitmap_size(bmp_undo_to_sys).GetWidth(); + int bmp_dim = get_bitmap_size(&bmp_undo_to_sys, ctrl).GetWidth(); rects_undo_to_sys_icon[rect_id] = wxRect(h_pos, v_pos, bmp_dim, bmp_dim); h_pos += bmp_dim + ctrl->m_h_gap; - dc.DrawBitmap(bmp_undo, h_pos, v_pos); + dc.DrawBitmap(bmp_undo.GetBitmapFor(ctrl), h_pos, v_pos); - bmp_dim = get_bitmap_size(bmp_undo).GetWidth(); + bmp_dim = get_bitmap_size(&bmp_undo, ctrl).GetWidth(); rects_undo_icon[rect_id] = wxRect(h_pos, v_pos, bmp_dim, bmp_dim); h_pos += bmp_dim + ctrl->m_h_gap; diff --git a/src/slic3r/GUI/OG_CustomCtrl.hpp b/src/slic3r/GUI/OG_CustomCtrl.hpp index c15132fec2..0308322f7f 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.hpp +++ b/src/slic3r/GUI/OG_CustomCtrl.hpp @@ -63,7 +63,7 @@ class OG_CustomCtrl :public wxPanel wxCoord draw_mode_bmp(wxDC& dc, wxCoord v_pos); wxCoord draw_text (wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url = false); wxPoint draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking); - wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id = 0); + wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id = 0); bool launch_browser() const; bool is_separator() const { return og_line.is_separator(); } diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 0869c16054..ba70f0ebdb 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -56,7 +56,7 @@ const std::map INFO_ITEMS{ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmap& bmp, + const wxBitmapBundle& bmp, const wxString& extruder, const int idx/* = -1*/, const std::string& warning_icon_name /*= std::string*/) : @@ -101,7 +101,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent } else if (type == itLayerRoot) { - m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr + m_bmp = *get_bmp_bundle(LayerRootIcon); m_name = _(L("Layers")); } else if (type == itInfo) @@ -132,7 +132,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent } const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str(); m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; - m_bmp = create_scaled_bitmap(LayerIcon); // FIXME: pass window ptr + m_bmp = *get_bmp_bundle(LayerIcon); set_action_and_extruder_icons(); init_container(); @@ -151,7 +151,7 @@ void ObjectDataViewModelNode::set_action_and_extruder_icons() { m_action_icon_name = m_type & itObject ? "advanced_plus" : m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj"; - m_action_icon = create_scaled_bitmap(m_action_icon_name); // FIXME: pass window ptr + m_action_icon = *get_bmp_bundle(m_action_icon_name); // set extruder bitmap set_extruder_icon(); @@ -170,7 +170,7 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) { m_printable = printable; m_printable_icon = m_printable == piUndef ? m_empty_bmp : - create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); + *get_bmp_bundle(m_printable == piPrintable ? "eye_open" : "eye_closed"); } void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name) @@ -185,14 +185,14 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps() m_bmp = m_empty_bmp; std::string scaled_bitmap_name = m_name.ToUTF8().data(); - scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : ""); + scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : ""); - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); if (bmp == nullptr) { - std::vector bmps; + std::vector bmps; for (auto& category : m_opt_categories) - bmps.emplace_back(SettingsFactory::get_category_bitmap(category, false)); - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + bmps.emplace_back(SettingsFactory::get_category_bitmap(category)); + bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps); } m_bmp = *bmp; @@ -216,13 +216,13 @@ bool ObjectDataViewModelNode::update_settings_digest(const std::vectorGetExtruder().IsEmpty()) @@ -1676,14 +1676,14 @@ wxDataViewItem ObjectDataViewModel::SetObjectPrintableState( return obj_item; } -void ObjectDataViewModel::Rescale() +void ObjectDataViewModel::UpdateBitmaps() { m_volume_bmps = MenuFactory::get_volume_bitmaps(); - m_warning_bmp = create_scaled_bitmap(WarningIcon); - m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon); + m_warning_bmp = *get_bmp_bundle(WarningIcon); + m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon); for (auto item : INFO_ITEMS) - m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); + m_info_bmps[item.first] = get_bmp_bundle(item.second.bmp_name); wxDataViewItemArray all_items; GetAllChildren(wxDataViewItem(0), all_items); @@ -1694,7 +1694,7 @@ void ObjectDataViewModel::Rescale() continue; ObjectDataViewModelNode *node = static_cast(item.GetID()); - node->msw_rescale(); + node->sys_color_changed(); switch (node->m_type) { @@ -1705,13 +1705,13 @@ void ObjectDataViewModel::Rescale() node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name); break; case itLayerRoot: - node->m_bmp = create_scaled_bitmap(LayerRootIcon); + node->m_bmp = *get_bmp_bundle(LayerRootIcon); break; case itLayer: - node->m_bmp = create_scaled_bitmap(LayerIcon); + node->m_bmp = *get_bmp_bundle(LayerIcon); break; case itInfo: - node->m_bmp = m_info_bmps.at(node->m_info_item_type); + node->m_bmp = *m_info_bmps.at(node->m_info_item_type); break; default: break; } @@ -1720,22 +1720,22 @@ void ObjectDataViewModel::Rescale() } } -wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/) +wxBitmapBundle ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/) { if (warning_icon_name.empty()) - return m_volume_bmps[static_cast(vol_type)]; + return *m_volume_bmps[static_cast(vol_type)]; std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast(vol_type)); scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm"); - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); if (bmp == nullptr) { - std::vector bmps; + std::vector bmps; - bmps.emplace_back(GetWarningBitmap(warning_icon_name)); + bmps.emplace_back(&GetWarningBitmap(warning_icon_name)); bmps.emplace_back(m_volume_bmps[static_cast(vol_type)]); - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps); } return *bmp; @@ -1770,7 +1770,7 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo return; if (node->GetType() & itVolume) { - node->SetWarningBitmap(m_volume_bmps[static_cast(node->volume_type())], ""); + node->SetWarningBitmap(*m_volume_bmps[static_cast(node->volume_type())], ""); return; } diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index f8885b2060..7014acccb1 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -63,21 +63,21 @@ class ObjectDataViewModelNode { ObjectDataViewModelNode* m_parent; MyObjectTreeModelNodePtrArray m_children; - wxBitmap m_empty_bmp; + wxBitmapBundle m_empty_bmp; size_t m_volumes_cnt = 0; std::vector< std::string > m_opt_categories; t_layer_height_range m_layer_range = { 0.0f, 0.0f }; wxString m_name; - wxBitmap& m_bmp = m_empty_bmp; + wxBitmapBundle& m_bmp = m_empty_bmp; ItemType m_type; int m_idx = -1; bool m_container = false; wxString m_extruder = "default"; - wxBitmap m_extruder_bmp; - wxBitmap m_action_icon; + wxBitmapBundle m_extruder_bmp; + wxBitmapBundle m_action_icon; PrintIndicator m_printable {piUndef}; - wxBitmap m_printable_icon; + wxBitmapBundle m_printable_icon; std::string m_warning_icon_name{ "" }; std::string m_action_icon_name = ""; @@ -99,7 +99,7 @@ public: ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmap& bmp, + const wxBitmapBundle& bmp, const wxString& extruder, const int idx = -1, const std::string& warning_icon_name = std::string()); @@ -179,10 +179,10 @@ public: bool SetValue(const wxVariant &variant, unsigned int col); void SetVolumeType(ModelVolumeType type) { m_volume_type = type; } - void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } + void SetBitmap(const wxBitmapBundle &icon) { m_bmp = icon; } void SetExtruder(const wxString &extruder) { m_extruder = extruder; } - void SetWarningBitmap(const wxBitmap& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; } - const wxBitmap& GetBitmap() const { return m_bmp; } + void SetWarningBitmap(const wxBitmapBundle& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; } + const wxBitmapBundle& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } InfoItemType GetInfoItemType() const { return m_info_item_type; } @@ -234,7 +234,7 @@ public: void update_settings_digest_bitmaps(); bool update_settings_digest(const std::vector& categories); int volume_type() const { return int(m_volume_type); } - void msw_rescale(); + void sys_color_changed(); #ifndef NDEBUG bool valid(); @@ -257,11 +257,11 @@ wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); class ObjectDataViewModel :public wxDataViewModel { std::vector m_objects; - std::vector m_volume_bmps; - std::map m_info_bmps; - wxBitmap m_empty_bmp; - wxBitmap m_warning_bmp; - wxBitmap m_warning_manifold_bmp; + std::vector m_volume_bmps; + std::map m_info_bmps; + wxBitmapBundle m_empty_bmp; + wxBitmapBundle m_warning_bmp; + wxBitmapBundle m_warning_manifold_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -315,7 +315,7 @@ public: // helper method for wxLog wxString GetName(const wxDataViewItem &item) const; - wxBitmap& GetBitmap(const wxDataViewItem &item) const; + wxBitmapBundle& GetBitmap(const wxDataViewItem &item) const; wxString GetExtruder(const wxDataViewItem &item) const; int GetExtruderNumber(const wxDataViewItem &item) const; @@ -383,9 +383,9 @@ public: void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } // Rescale bitmaps for existing Items - void Rescale(); + void UpdateBitmaps(); - wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, + wxBitmapBundle GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name = std::string()); void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); @@ -403,7 +403,7 @@ private: wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); void AddAllChildren(const wxDataViewItem& parent); - wxBitmap& GetWarningBitmap(const std::string& warning_icon_name); + wxBitmapBundle& GetWarningBitmap(const std::string& warning_icon_name); }; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 257fe2532c..6055a8e786 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -728,7 +728,6 @@ void ConfigOptionsGroup::msw_rescale() // check if window is ScalableButton ScalableButton* sc_btn = dynamic_cast(win); if (sc_btn) { - sc_btn->msw_rescale(); sc_btn->SetSize(sc_btn->GetBestSize()); return; } @@ -773,7 +772,7 @@ void ConfigOptionsGroup::sys_color_changed() wxWindow* win = item->GetWindow(); // check if window is ScalableButton if (ScalableButton* sc_btn = dynamic_cast(win)) { - sc_btn->msw_rescale(); + sc_btn->sys_color_changed(); return; } wxGetApp().UpdateDarkUI(win, dynamic_cast(win) != nullptr); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 83c7e592ae..dfebad4424 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -142,10 +142,10 @@ void PresetForPrinter::AllowDelete() m_presets_list->update(); } -void PresetForPrinter::msw_rescale() +void PresetForPrinter::on_sys_color_changed() { - m_presets_list->msw_rescale(); - m_delete_preset_btn->msw_rescale(); + m_presets_list->sys_color_changed(); + m_delete_preset_btn->sys_color_changed(); } @@ -603,19 +603,10 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); - m_add_preset_btn->msw_rescale(); - m_printhost_browse_btn->msw_rescale(); - m_printhost_test_btn->msw_rescale(); - if (m_printhost_cafile_browse_btn) - m_printhost_cafile_browse_btn->msw_rescale(); - m_optgroup->msw_rescale(); msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - for (PresetForPrinter* preset : m_presets) - preset->msw_rescale(); - const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(size); @@ -623,6 +614,18 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) Refresh(); } +void PhysicalPrinterDialog::on_sys_color_changed() +{ + m_add_preset_btn->sys_color_changed(); + m_printhost_browse_btn->sys_color_changed(); + m_printhost_test_btn->sys_color_changed(); + if (m_printhost_cafile_browse_btn) + m_printhost_cafile_browse_btn->sys_color_changed(); + + for (PresetForPrinter* preset : m_presets) + preset->on_sys_color_changed(); +} + void PhysicalPrinterDialog::OnOK(wxEvent& event) { wxString printer_name = m_printer_name->GetValue(); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp index cb9a48b3e2..d8bb70d3c4 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.hpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp @@ -48,8 +48,7 @@ public: void SuppressDelete(); void AllowDelete(); - void msw_rescale(); - void on_sys_color_changed() {}; + void on_sys_color_changed(); }; @@ -98,7 +97,7 @@ public: void DeletePreset(PresetForPrinter* preset_for_printer); protected: void on_dpi_changed(const wxRect& suggested_rect) override; - void on_sys_color_changed() override {}; + void on_sys_color_changed() override; bool had_all_mk3; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d8c3689981..7133db3fa3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -180,7 +180,6 @@ public: bool showing_manifold_warning_icon; void show_sizer(bool show); - void msw_rescale(); void update_warning_icon(const std::string& warning_icon_name); }; @@ -210,7 +209,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : init_info_label(&info_size, _L("Size")); - info_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("info")); + info_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle("info")); info_icon->SetToolTip(_L("For a multipart object, this value isn't accurate.\n" "It doesn't take account of intersections and negative volumes.")); auto* volume_info_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -223,7 +222,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold->SetFont(wxGetApp().small_font()); - manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name)); + manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle(m_warning_icon_name)); auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); @@ -242,17 +241,11 @@ void ObjectInfo::show_sizer(bool show) manifold_warning_icon->Show(showing_manifold_warning_icon && show); } -void ObjectInfo::msw_rescale() -{ - manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); - info_icon->SetBitmap(create_scaled_bitmap("info")); -} - void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) { if ((showing_manifold_warning_icon = !warning_icon_name.empty())) { m_warning_icon_name = warning_icon_name; - manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); + manifold_warning_icon->SetBitmap(*get_bmp_bundle(m_warning_icon_name)); } } @@ -350,9 +343,6 @@ void FreqChangedParams::msw_rescale() { m_og->msw_rescale(); m_og_sla->msw_rescale(); - - for (auto btn: m_empty_buttons) - btn->msw_rescale(); } void FreqChangedParams::sys_color_changed() @@ -361,7 +351,7 @@ void FreqChangedParams::sys_color_changed() m_og_sla->sys_color_changed(); for (auto btn: m_empty_buttons) - btn->msw_rescale(); + btn->sys_color_changed(); wxGetApp().UpdateDarkUI(m_wiping_dialog_button, true); } @@ -450,7 +440,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : */ auto empty_widget = [this] (wxWindow* parent) { auto sizer = new wxBoxSizer(wxHORIZONTAL); - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString, + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); m_empty_buttons.push_back(btn); @@ -508,7 +498,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : } })); - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString, + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); @@ -1103,9 +1093,6 @@ void Sidebar::msw_rescale() { SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1)); - if (p->mode_sizer) - p->mode_sizer->msw_rescale(); - for (PlaterPresetComboBox* combo : std::vector { p->combo_print, p->combo_sla_print, p->combo_sla_material, @@ -1117,14 +1104,8 @@ void Sidebar::msw_rescale() p->frequently_changed_parameters->msw_rescale(); p->object_list->msw_rescale(); p->object_manipulation->msw_rescale(); - p->object_settings->msw_rescale(); p->object_layers->msw_rescale(); - p->object_info->msw_rescale(); - - p->btn_send_gcode->msw_rescale(); -// p->btn_eject_device->msw_rescale(); - p->btn_export_gcode_removable->msw_rescale(); #ifdef _WIN32 const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight(); #else @@ -1145,14 +1126,13 @@ void Sidebar::sys_color_changed() for (wxWindow* win : std::vector{ this, p->sliced_info->GetStaticBox(), p->object_info->GetStaticBox(), p->btn_reslice, p->btn_export_gcode }) wxGetApp().UpdateDarkUI(win); - p->object_info->msw_rescale(); for (wxWindow* win : std::vector{ p->scrolled, p->presets_panel }) wxGetApp().UpdateAllStaticTextDarkUI(win); for (wxWindow* btn : std::vector{ p->btn_reslice, p->btn_export_gcode }) wxGetApp().UpdateDarkUI(btn, true); if (p->mode_sizer) - p->mode_sizer->msw_rescale(); + p->mode_sizer->sys_color_changed(); p->frequently_changed_parameters->sys_color_changed(); p->object_settings->sys_color_changed(); #endif @@ -1170,11 +1150,12 @@ void Sidebar::sys_color_changed() p->object_layers->sys_color_changed(); // btn...->msw_rescale() updates icon on button, so use it - p->btn_send_gcode->msw_rescale(); + p->btn_send_gcode->sys_color_changed(); // p->btn_eject_device->msw_rescale(); - p->btn_export_gcode_removable->msw_rescale(); + p->btn_export_gcode_removable->sys_color_changed(); p->scrolled->Layout(); + p->scrolled->Refresh(); p->searcher.dlg_sys_color_changed(); } @@ -6974,8 +6955,6 @@ void Plater::msw_rescale() p->sidebar->msw_rescale(); - p->menus.msw_rescale(); - Layout(); GetParent()->Layout(); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index ed4888a878..597c30312e 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -108,8 +108,8 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const default: break; } - m_bitmapCompatible = ScalableBitmap(this, "flag_green"); - m_bitmapIncompatible = ScalableBitmap(this, "flag_red"); + m_bitmapCompatible = get_bmp_bundle("flag_green"); + m_bitmapIncompatible = get_bmp_bundle("flag_red"); // parameters for an icon's drawing fill_width_height(); @@ -242,12 +242,12 @@ void PresetComboBox::update(std::string select_preset_name) const std::deque& presets = m_collection->get_presets(); - std::map> nonsys_presets; - std::map incomp_presets; + std::map> nonsys_presets; + std::map incomp_presets; wxString selected = ""; if (!presets.front().is_visible) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { @@ -268,7 +268,7 @@ void PresetComboBox::update(std::string select_preset_name) } std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + auto bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); if (!is_enabled) @@ -280,17 +280,17 @@ void PresetComboBox::update(std::string select_preset_name) } else { - nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); + nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); if (preset.name == select_preset_name || (select_preset_name.empty() && is_enabled)) selected = get_preset_name(preset); } if (i + 1 == m_collection->num_default_presets()) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); } if (!nonsys_presets.empty()) { - set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + set_label_marker(Append(separator(L("User presets")), NullBitmapBndl())); + for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { int item_id = Append(it->first, *it->second.first); bool is_enabled = it->second.second; if (!is_enabled) @@ -300,8 +300,8 @@ void PresetComboBox::update(std::string select_preset_name) } if (!incomp_presets.empty()) { - set_label_marker(Append(separator(L("Incompatible presets")), wxNullBitmap)); - for (std::map::iterator it = incomp_presets.begin(); it != incomp_presets.end(); ++it) { + set_label_marker(Append(separator(L("Incompatible presets")), NullBitmapBndl())); + for (std::map::iterator it = incomp_presets.begin(); it != incomp_presets.end(); ++it) { set_label_marker(Append(it->first, *it->second), LABEL_ITEM_DISABLED); } } @@ -337,7 +337,6 @@ bool PresetComboBox::del_physical_printer(const wxString& note_string/* = wxEmpt msg += note_string + "\n"; msg += format_wxstr(_L("Are you sure you want to delete \"%1%\" printer?"), printer_name); - //if (wxMessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES) if (MessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES) return false; @@ -364,7 +363,8 @@ void PresetComboBox::show_all(bool show_all) void PresetComboBox::update() { - this->update(into_u8(this->GetString(this->GetSelection()))); + int n = this->GetSelection(); + this->update(n < 0 ? "" : into_u8(this->GetString(n))); } void PresetComboBox::update_from_bundle() @@ -375,42 +375,31 @@ void PresetComboBox::update_from_bundle() void PresetComboBox::msw_rescale() { m_em_unit = em_unit(this); +} - m_bitmapIncompatible.msw_rescale(); - m_bitmapCompatible.msw_rescale(); - - // parameters for an icon's drawing - fill_width_height(); +void PresetComboBox::sys_color_changed() +{ + m_bitmapCompatible = get_bmp_bundle("flag_green"); + m_bitmapIncompatible = get_bmp_bundle("flag_red"); + wxGetApp().UpdateDarkUI(this); // update the control to redraw the icons update(); } -void PresetComboBox::sys_color_changed() -{ - wxGetApp().UpdateDarkUI(this); - msw_rescale(); -} - void PresetComboBox::fill_width_height() { - // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so - // set a bitmap's height to m_bitmapCompatible->GetHeight() and norm_icon_width to m_bitmapCompatible->GetWidth() - icon_height = m_bitmapCompatible.GetBmpHeight(); - norm_icon_width = m_bitmapCompatible.GetBmpWidth(); + icon_height = 16; + norm_icon_width = 16; - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const float scale_f = (float)m_em_unit * 0.1f; - - thin_icon_width = lroundf(8 * scale_f); // analogue to 8px; + thin_icon_width = 8; wide_icon_width = norm_icon_width + thin_icon_width; - space_icon_width = lroundf(2 * scale_f); - thin_space_icon_width = lroundf(4 * scale_f); - wide_space_icon_width = lroundf(6 * scale_f); + null_icon_width = 2 * norm_icon_width; + + space_icon_width = 2; + thin_space_icon_width = 4; + wide_space_icon_width = 6; } wxString PresetComboBox::separator(const std::string& label) @@ -418,7 +407,8 @@ wxString PresetComboBox::separator(const std::string& label) return wxString::FromUTF8(separator_head()) + _(label) + wxString::FromUTF8(separator_tail()); } -wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, + +wxBitmapBundle* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, bool is_compatible/* = true*/, bool is_system/* = false*/, bool is_single_bar/* = false*/, const std::string& filament_rgb/* = ""*/, const std::string& extruder_rgb/* = ""*/, const std::string& material_rgb/* = ""*/) { @@ -434,45 +424,41 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con bitmap_key += ",dark"; bitmap_key += material_rgb; - wxBitmap* bmp = bitmap_cache().find(bitmap_key); - if (bmp == nullptr) { + wxBitmapBundle* bmp_bndl = bitmap_cache().find_bndl(bitmap_key); + if (bmp_bndl == nullptr) { // Create the bitmap with color bars. - std::vector bmps; + std::vector bmps; if (wide_icons) // Paint a red flag for incompatible presets. - bmps.emplace_back(is_compatible ? bitmap_cache().mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp()); + bmps.emplace_back(is_compatible ? get_empty_bmp_bundle(norm_icon_width, icon_height) : m_bitmapIncompatible); if (m_type == Preset::TYPE_FILAMENT && !filament_rgb.empty()) { // Paint the color bars. - ColorRGB color; - decode_color(filament_rgb, color); - bmps.emplace_back(bitmap_cache().mksolid(is_single_bar ? wide_icon_width : norm_icon_width, icon_height, color, false, 1, dark_mode)); - if (!is_single_bar) { - decode_color(extruder_rgb, color); - bmps.emplace_back(bitmap_cache().mksolid(thin_icon_width, icon_height, color, false, 1, dark_mode)); - } + bmps.emplace_back(get_solid_bmp_bundle(is_single_bar ? wide_icon_width : norm_icon_width, icon_height, filament_rgb)); + if (!is_single_bar) + bmps.emplace_back(get_solid_bmp_bundle(thin_icon_width, icon_height, extruder_rgb)); // Paint a lock at the system presets. - bmps.emplace_back(bitmap_cache().mkclear(space_icon_width, icon_height)); + bmps.emplace_back(get_empty_bmp_bundle(space_icon_width, icon_height)); } else { // Paint the color bars. - bmps.emplace_back(bitmap_cache().mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(get_empty_bmp_bundle(thin_space_icon_width, icon_height)); if (m_type == Preset::TYPE_SLA_MATERIAL) - bmps.emplace_back(create_scaled_bitmap(main_icon_name, this, 16, false, material_rgb)); + bmps.emplace_back(bitmap_cache().from_svg(main_icon_name, 16, 16, dark_mode, material_rgb)); else - bmps.emplace_back(create_scaled_bitmap(main_icon_name)); + bmps.emplace_back(get_bmp_bundle(main_icon_name)); // Paint a lock at the system presets. - bmps.emplace_back(bitmap_cache().mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(get_empty_bmp_bundle(wide_space_icon_width, icon_height)); } - bmps.emplace_back(is_system ? create_scaled_bitmap("lock_closed") : bitmap_cache().mkclear(norm_icon_width, icon_height)); - bmp = bitmap_cache().insert(bitmap_key, bmps); + bmps.emplace_back(is_system ? get_bmp_bundle("lock_closed") : get_empty_bmp_bundle(norm_icon_width, icon_height)); + bmp_bndl = bitmap_cache().insert_bndl(bitmap_key, bmps); } - return bmp; + return bmp_bndl; } -wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, +wxBitmapBundle* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, bool is_enabled/* = true*/, bool is_compatible/* = true*/, bool is_system/* = false*/) { bitmap_key += !is_enabled ? "_disabled" : ""; @@ -482,20 +468,26 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& m if (wxGetApp().dark_mode()) bitmap_key += ",dark"; - wxBitmap* bmp = bitmap_cache().find(bitmap_key); + wxBitmapBundle* bmp = bitmap_cache().find_bndl(bitmap_key); if (bmp == nullptr) { // Create the bitmap with color bars. - std::vector bmps; - bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? create_scaled_bitmap(main_icon_name, this, 16, !is_enabled) : - is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); + std::vector bmps; + bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? get_bmp_bundle(main_icon_name) : + is_compatible ? m_bitmapCompatible : m_bitmapIncompatible); // Paint a lock at the system presets. - bmps.emplace_back(is_system ? create_scaled_bitmap(next_icon_name, this, 16, !is_enabled) : bitmap_cache().mkclear(norm_icon_width, icon_height)); - bmp = bitmap_cache().insert(bitmap_key, bmps); + bmps.emplace_back(is_system ? get_bmp_bundle(next_icon_name) : get_empty_bmp_bundle(norm_icon_width, icon_height)); + bmp = bitmap_cache().insert_bndl(bitmap_key, bmps); } return bmp; } +wxBitmapBundle PresetComboBox::NullBitmapBndl() +{ + assert(null_icon_width > 0); + return *get_empty_bmp_bundle(null_icon_width, icon_height); +} + bool PresetComboBox::is_selected_physical_printer() { auto selected_item = this->GetSelection(); @@ -782,14 +774,16 @@ void PlaterPresetComboBox::update() // and draw a red flag in front of the selected preset. bool wide_icons = selected_preset && !selected_preset->is_compatible; - std::map nonsys_presets; + null_icon_width = (wide_icons ? 3 : 2) * norm_icon_width + thin_space_icon_width + wide_space_icon_width; + + std::map nonsys_presets; wxString selected_user_preset; wxString tooltip; const std::deque& presets = m_collection->get_presets(); if (!presets.front().is_visible) - this->set_label_marker(this->Append(separator(L("System presets")), wxNullBitmap)); + this->set_label_marker(this->Append(separator(L("System presets")), NullBitmapBndl())); for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { @@ -823,7 +817,7 @@ void PlaterPresetComboBox::update() material_rgb = print_config_def.get("material_colour")->get_default_value()->value; } - wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, + auto bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, preset.is_compatible, preset.is_system || preset.is_default, single_bar, filament_rgb, extruder_rgb, material_rgb); assert(bmp); @@ -844,12 +838,12 @@ void PlaterPresetComboBox::update() } } if (i + 1 == m_collection->num_default_presets()) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); } if (!nonsys_presets.empty()) { - set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + set_label_marker(Append(separator(L("User presets")), NullBitmapBndl())); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { Append(it->first, *it->second); validate_selection(it->first == selected_user_preset); } @@ -859,7 +853,7 @@ void PlaterPresetComboBox::update() { // add Physical printers, if any exists if (!m_preset_bundle->physical_printers.empty()) { - set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + set_label_marker(Append(separator(L("Physical printers")), NullBitmapBndl())); const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { @@ -868,7 +862,7 @@ void PlaterPresetComboBox::update() if (!preset || !preset->is_visible) continue; std::string main_icon_name, bitmap_key = main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(main_icon_name, wide_icons, main_icon_name); + auto bmp = get_bmp(main_icon_name, wide_icons, main_icon_name); assert(bmp); set_label_marker(Append(from_u8(it->get_full_name(preset_name) + suffix(preset)), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); @@ -879,7 +873,7 @@ void PlaterPresetComboBox::update() } if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) { - wxBitmap* bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); + auto bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); assert(bmp); if (m_type == Preset::TYPE_FILAMENT) @@ -916,9 +910,20 @@ void PlaterPresetComboBox::update() void PlaterPresetComboBox::msw_rescale() { PresetComboBox::msw_rescale(); - edit_btn->msw_rescale(); +#ifdef __WXMSW__ + // Use this part of code just on Windows to avoid of some layout issues on Linux + // see https://github.com/prusa3d/PrusaSlicer/issues/5163 and https://github.com/prusa3d/PrusaSlicer/issues/5505 + // Update control min size after rescale (changed Display DPI under MSW) + if (GetMinWidth() != 20 * m_em_unit) + SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight())); +#endif //__WXMSW__ } +void PlaterPresetComboBox::sys_color_changed() +{ + PresetComboBox::sys_color_changed(); + edit_btn->sys_color_changed(); +} // --------------------------------- // *** TabPresetComboBox *** @@ -981,10 +986,10 @@ void TabPresetComboBox::update() const std::deque& presets = m_collection->get_presets(); - std::map> nonsys_presets; + std::map> nonsys_presets; wxString selected = ""; if (!presets.front().is_visible) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); size_t idx_selected = m_collection->get_selected_idx(); if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { @@ -1011,7 +1016,7 @@ void TabPresetComboBox::update() } std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + auto bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); if (preset.is_default || preset.is_system) { @@ -1022,18 +1027,18 @@ void TabPresetComboBox::update() } else { - std::pair pair(bmp, is_enabled); - nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); + std::pair pair(bmp, is_enabled); + nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); if (i == idx_selected) selected = get_preset_name(preset); } if (i + 1 == m_collection->num_default_presets()) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); } if (!nonsys_presets.empty()) { - set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + set_label_marker(Append(separator(L("User presets")), NullBitmapBndl())); + for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { int item_id = Append(it->first, *it->second.first); bool is_enabled = it->second.second; if (!is_enabled) @@ -1046,7 +1051,7 @@ void TabPresetComboBox::update() { // add Physical printers, if any exists if (!m_preset_bundle->physical_printers.empty()) { - set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + set_label_marker(Append(separator(L("Physical printers")), NullBitmapBndl())); const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { @@ -1056,7 +1061,7 @@ void TabPresetComboBox::update() continue; std::string main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(main_icon_name, main_icon_name, "", true, true, false); + auto bmp = get_bmp(main_icon_name, main_icon_name, "", true, true, false); assert(bmp); set_label_marker(Append(from_u8(it->get_full_name(preset_name) + suffix(preset)), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); @@ -1067,7 +1072,7 @@ void TabPresetComboBox::update() // add "Add/Remove printers" item std::string icon_name = "edit_uni"; - wxBitmap* bmp = get_bmp("edit_preset_list, tab,", icon_name, ""); + auto bmp = get_bmp("edit_preset_list, tab,", icon_name, ""); assert(bmp); set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 40a0cfc28b..9fe75c1265 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_PresetComboBoxes_hpp_ #define slic3r_PresetComboBoxes_hpp_ -//#include +#include #include #include "libslic3r/Preset.hpp" @@ -88,9 +88,9 @@ protected: static BitmapCache& bitmap_cache(); // Indicator, that the preset is compatible with the selected printer. - ScalableBitmap m_bitmapCompatible; + wxBitmapBundle* m_bitmapCompatible; // Indicator, that the preset is NOT compatible with the selected printer. - ScalableBitmap m_bitmapIncompatible; + wxBitmapBundle* m_bitmapIncompatible; int m_last_selected; int m_em_unit; @@ -99,6 +99,7 @@ protected: // parameters for an icon's drawing int icon_height; int norm_icon_width; + int null_icon_width; int thin_icon_width; int wide_icon_width; int space_icon_width; @@ -120,13 +121,15 @@ protected: #endif // __linux__ static wxString separator(const std::string& label); - wxBitmap* get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, + wxBitmapBundle* get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, bool is_compatible = true, bool is_system = false, bool is_single_bar = false, const std::string& filament_rgb = "", const std::string& extruder_rgb = "", const std::string& material_rgb = ""); - wxBitmap* get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, + wxBitmapBundle* get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, bool is_enabled = true, bool is_compatible = true, bool is_system = false); + wxBitmapBundle NullBitmapBndl(); + private: void fill_width_height(); }; @@ -155,6 +158,7 @@ public: wxString get_preset_name(const Preset& preset) override; void update() override; void msw_rescale() override; + void sys_color_changed() override; void OnSelect(wxCommandEvent& evt) override; private: diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 460b30126e..2a2e3bb100 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -56,7 +56,7 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(tab->title())).str())); - m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent)); + m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("tick_mark")); m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1)); for (const std::string& value : values) @@ -173,7 +173,7 @@ void SavePresetDialog::Item::update_valid_bmp() { std::string bmp_name = m_valid_type == Warning ? "exclamation" : m_valid_type == NoValid ? "cross" : "tick_mark" ; - m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent)); + m_valid_bmp->SetBitmap(*get_bmp_bundle(bmp_name)); } void SavePresetDialog::Item::accept() diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 6b5edc30e0..52e58193c7 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -720,12 +720,9 @@ void SearchDialog::msw_rescale() { const int& em = em_unit(); - search_list_model->msw_rescale(); search_list->GetColumn(SearchListModel::colIcon )->SetWidth(3 * em); search_list->GetColumn(SearchListModel::colMarkedText)->SetWidth(45 * em); - msw_buttons_rescale(this, em, { wxID_CANCEL }); - const wxSize& size = wxSize(40 * em, 30 * em); SetMinSize(size); @@ -743,7 +740,7 @@ void SearchDialog::on_sys_color_changed() #endif // msw_rescale updates just icons, so use it - search_list_model->msw_rescale(); + search_list_model->sys_color_changed(); Refresh(); } @@ -776,10 +773,10 @@ void SearchListModel::Prepend(const std::string& label) RowPrepended(); } -void SearchListModel::msw_rescale() +void SearchListModel::sys_color_changed() { for (ScalableBitmap& bmp : m_icon) - bmp.msw_rescale(); + bmp.sys_color_changed(); } wxString SearchListModel::GetColumnType(unsigned int col) const @@ -795,7 +792,7 @@ void SearchListModel::GetValueByRow(wxVariant& variant, switch (col) { case colIcon: - variant << m_icon[m_values[row].second].bmp(); + variant << m_icon[m_values[row].second].bmp().GetBitmapFor(m_icon[m_values[row].second].parent()); break; case colMarkedText: variant = m_values[row].first; diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 49866d0661..0d78511324 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -213,7 +213,7 @@ public: void Clear(); void Prepend(const std::string& text); - void msw_rescale(); + void sys_color_changed(); // implementation of base class virtuals to define model diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index 53e7d637d1..d4d83dfda5 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -102,8 +102,10 @@ SysInfoDialog::SysInfoDialog() main_sizer->Add(hsizer, 1, wxEXPAND | wxALL, 10); // logo - m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 192); - m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bmp.bmp()); + //m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 192); + //m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bmp.bmp()); + m_logo = new wxStaticBitmap(this, wxID_ANY, *get_bmp_bundle(wxGetApp().logo_name(), 192)); + hsizer->Add(m_logo, 0, wxALIGN_CENTER_VERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); @@ -194,8 +196,8 @@ SysInfoDialog::SysInfoDialog() void SysInfoDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_logo_bmp.msw_rescale(); - m_logo->SetBitmap(m_logo_bmp.bmp()); + //m_logo_bmp.msw_rescale(); + //m_logo->SetBitmap(m_logo_bmp.bmp()); wxFont font = get_default_font(this); const int fs = font.GetPointSize() - 1; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 142f86a539..a5b4c7f7de 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -155,10 +155,8 @@ void Tab::create_preset_tab() add_scaled_button(panel, &m_btn_edit_ph_printer, "cog"); m_show_incompatible_presets = false; - add_scaled_bitmap(this, m_bmp_show_incompatible_presets, "flag_red"); - add_scaled_bitmap(this, m_bmp_hide_incompatible_presets, "flag_green"); - add_scaled_button(panel, &m_btn_hide_incompatible_presets, m_bmp_hide_incompatible_presets.name()); + add_scaled_button(panel, &m_btn_hide_incompatible_presets, "flag_green"); m_btn_compare_preset->SetToolTip(_L("Compare this preset with some another")); // TRN "Save current Settings" @@ -207,7 +205,7 @@ void Tab::create_preset_tab() #endif m_mode_sizer = new ModeSizer(panel, int (0.5*em_unit(this))); - const float scale_factor = /*wxGetApp().*/em_unit(this)*0.1;// GetContentScaleFactor(); + const float scale_factor = em_unit(this)*0.1;// GetContentScaleFactor(); m_hsizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3); m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); @@ -254,11 +252,8 @@ void Tab::create_preset_tab() m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); m_left_sizer->Add(m_treectrl, 1, wxEXPAND); - const int img_sz = int(16 * scale_factor + 0.5f); - m_icons = new wxImageList(img_sz, img_sz, true, 1); - // Index of the last icon inserted into $self->{icons}. + // Index of the last icon inserted into m_treectrl m_icon_count = -1; - m_treectrl->AssignImageList(m_icons); m_treectrl->AddRoot("root"); m_treectrl->SetIndent(0); wxGetApp().UpdateDarkUI(m_treectrl); @@ -321,6 +316,14 @@ void Tab::create_preset_tab() // Initialize the DynamicPrintConfig by default keys/values. build(); + if (!m_scaled_icons_list.empty()) { + // update icons for tree_ctrl + wxVector img_bundles; + for (const ScalableBitmap& bmp : m_scaled_icons_list) + img_bundles.push_back(bmp.bmp()); + m_treectrl->SetImages(img_bundles); + } + // ys_FIXME: Following should not be needed, the function will be called later // (update_mode->update_visibility->rebuild_page_tree). This does not work, during the // second call of rebuild_page_tree m_treectrl->GetFirstVisibleItem(); returns zero @@ -336,7 +339,7 @@ void Tab::add_scaled_button(wxWindow* parent, const wxString& label/* = wxEmptyString*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style, true); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style); m_scaled_buttons.push_back(*btn); } @@ -366,7 +369,6 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str if (icon_idx == -1) { // Add a new icon to the icon list. m_scaled_icons_list.push_back(ScalableBitmap(this, icon)); - m_icons->Add(m_scaled_icons_list.back().bmp()); icon_idx = ++m_icon_count; m_icon_index[icon] = icon_idx; } @@ -656,16 +658,6 @@ void TabPrinter::init_options_list() m_options_list.emplace("extruders_count", m_opt_status_value); } -void TabPrinter::msw_rescale() -{ - Tab::msw_rescale(); - - if (m_reset_to_filament_color) - m_reset_to_filament_color->msw_rescale(); - - Layout(); -} - void TabSLAMaterial::init_options_list() { if (!m_options_list.empty()) @@ -772,8 +764,11 @@ void Tab::update_changed_tree_ui() void Tab::update_undo_buttons() { - m_undo_btn-> SetBitmap_(m_is_modified_values ? m_bmp_value_revert: m_bmp_white_bullet); - m_undo_to_sys_btn-> SetBitmap_(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock); + m_undo_btn-> SetBitmap_(m_is_modified_values ? m_bmp_value_revert.name(): m_bmp_white_bullet.name()); + m_undo_to_sys_btn-> SetBitmap_(m_is_nonsys_values ? m_bmp_non_system->name() : m_bmp_value_lock.name()); + + //m_undo_btn-> SetBitmap_(m_is_modified_values ? m_bmp_value_revert: m_bmp_white_bullet); + //m_undo_to_sys_btn-> SetBitmap_(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock); m_undo_btn->SetToolTip(m_is_modified_values ? m_ttg_value_revert : m_ttg_white_bullet); m_undo_to_sys_btn->SetToolTip(m_is_nonsys_values ? *m_ttg_non_system : m_ttg_value_lock); @@ -921,31 +916,9 @@ void Tab::msw_rescale() { m_em_unit = em_unit(m_parent); - if (m_mode_sizer) - m_mode_sizer->msw_rescale(); m_presets_choice->msw_rescale(); - m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1)); - // rescale buttons and cached bitmaps - for (const auto btn : m_scaled_buttons) - btn->msw_rescale(); - for (const auto bmp : m_scaled_bitmaps) - bmp->msw_rescale(); - - if (m_detach_preset_btn) - m_detach_preset_btn->msw_rescale(); - - // rescale icons for tree_ctrl - for (ScalableBitmap& bmp : m_scaled_icons_list) - bmp.msw_rescale(); - // recreate and set new ImageList for tree_ctrl - m_icons->RemoveAll(); - m_icons = new wxImageList(m_scaled_icons_list.front().bmp().GetWidth(), m_scaled_icons_list.front().bmp().GetHeight()); - for (ScalableBitmap& bmp : m_scaled_icons_list) - m_icons->Add(bmp.bmp()); - m_treectrl->AssignImageList(m_icons); - // rescale options_groups if (m_active_page) m_active_page->msw_rescale(); @@ -959,28 +932,28 @@ void Tab::sys_color_changed() // update buttons and cached bitmaps for (const auto btn : m_scaled_buttons) - btn->msw_rescale(); + btn->sys_color_changed(); for (const auto bmp : m_scaled_bitmaps) - bmp->msw_rescale(); + bmp->sys_color_changed(); if (m_detach_preset_btn) - m_detach_preset_btn->msw_rescale(); + m_detach_preset_btn->sys_color_changed(); + + update_show_hide_incompatible_button(); // update icons for tree_ctrl - for (ScalableBitmap& bmp : m_scaled_icons_list) - bmp.msw_rescale(); - // recreate and set new ImageList for tree_ctrl - m_icons->RemoveAll(); - m_icons = new wxImageList(m_scaled_icons_list.front().bmp().GetWidth(), m_scaled_icons_list.front().bmp().GetHeight()); - for (ScalableBitmap& bmp : m_scaled_icons_list) - m_icons->Add(bmp.bmp()); - m_treectrl->AssignImageList(m_icons); + wxVector img_bundles; + for (ScalableBitmap& bmp : m_scaled_icons_list) { + bmp.sys_color_changed(); + img_bundles.push_back(bmp.bmp()); + } + m_treectrl->SetImages(img_bundles); // Colors for ui "decoration" update_label_colours(); #ifdef _WIN32 wxWindowUpdateLocker noUpdates(this); if (m_mode_sizer) - m_mode_sizer->msw_rescale(); + m_mode_sizer->sys_color_changed(); wxGetApp().UpdateDarkUI(this); wxGetApp().UpdateDarkUI(m_treectrl); #endif @@ -991,6 +964,7 @@ void Tab::sys_color_changed() m_active_page->sys_color_changed(); Layout(); + Refresh(); } Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const @@ -1254,7 +1228,7 @@ void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup) auto detach_preset_btn = [this](wxWindow* parent) { m_detach_preset_btn = new ScalableButton(parent, wxID_ANY, "lock_open_sys", _L("Detach from system preset"), - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); ScalableButton* btn = m_detach_preset_btn; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); @@ -1678,14 +1652,14 @@ void TabPrint::build() option.opt.height = 5;//50; optgroup->append_single_option_line(option); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("notes"); option.opt.full_width = true; option.opt.height = 25;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) { @@ -1925,7 +1899,7 @@ void TabFilament::build() m_presets = &m_preset_bundle->filaments; load_initial_data(); - auto page = add_options_page(L("Filament"), "spool.png"); + auto page = add_options_page(L("Filament"), "spool"); auto optgroup = page->new_optgroup(L("Filament")); optgroup->append_single_option_line("filament_colour"); optgroup->append_single_option_line("filament_diameter"); @@ -2065,7 +2039,7 @@ void TabFilament::build() option.opt.height = gcode_field_height;// 150; optgroup->append_single_option_line(option); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); optgroup->label_width = 0; option = optgroup->get_option("filament_notes"); @@ -2073,7 +2047,7 @@ void TabFilament::build() option.opt.height = notes_field_height;// 250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_printers); @@ -2457,14 +2431,14 @@ void TabPrinter::build_fff() option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("printer_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); build_preset_description_line(optgroup.get()); @@ -2535,14 +2509,14 @@ void TabPrinter::build_sla() const int notes_field_height = 25; // 250 - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("printer_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); build_preset_description_line(optgroup.get()); @@ -2800,7 +2774,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/) auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) { m_reset_to_filament_color = new ScalableButton(parent, wxID_ANY, "undo", _L("Reset to Filament Color"), - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); ScalableButton* btn = m_reset_to_filament_color; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); btn->SetSize(btn->GetBestSize()); @@ -3777,8 +3751,7 @@ void Tab::toggle_show_hide_incompatible() void Tab::update_show_hide_incompatible_button() { - m_btn_hide_incompatible_presets->SetBitmap_(m_show_incompatible_presets ? - m_bmp_show_incompatible_presets : m_bmp_hide_incompatible_presets); + m_btn_hide_incompatible_presets->SetBitmap(*get_bmp_bundle(m_show_incompatible_presets ? "flag_red" : "flag_green")); m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ? "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); @@ -3827,7 +3800,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font()); wxGetApp().UpdateDarkUI(deps.checkbox, false, true); deps.btn = new ScalableButton(parent, wxID_ANY, "printer", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); deps.btn->SetSize(deps.btn->GetBestSize()); @@ -4136,7 +4109,7 @@ bool SubstitutionManager::is_empty_substitutions() wxSizer* TabPrint::create_manage_substitution_widget(wxWindow* parent) { auto create_btn = [parent](ScalableButton** btn, const wxString& label, const std::string& icon_name) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, " " + label + " ", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, " " + label + " ", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); (*btn)->SetFont(wxGetApp().normal_font()); (*btn)->SetSize((*btn)->GetBestSize()); }; @@ -4189,7 +4162,7 @@ wxSizer* TabPrint::create_substitutions_widget(wxWindow* parent) wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) { ScalableButton* btn = new ScalableButton(parent, wxID_ANY, "printer", " " + _(L("Set")) + " " + dots, - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); btn->SetFont(wxGetApp().normal_font()); btn->SetSize(btn->GetBestSize()); @@ -4583,7 +4556,7 @@ void TabSLAMaterial::build() optgroup->append_line(line); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); optgroup->label_width = 0; Option option = optgroup->get_option("material_notes"); @@ -4591,7 +4564,7 @@ void TabSLAMaterial::build() option.opt.height = 25;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) { @@ -4612,7 +4585,7 @@ void TabSLAMaterial::build() build_preset_description_line(optgroup.get()); - page = add_options_page(L("Material printing profile"), "note.png"); + page = add_options_page(L("Material printing profile"), "note"); optgroup = page->new_optgroup(L("Material printing profile")); option = optgroup->get_option("material_print_speed"); optgroup->append_single_option_line(option); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index d6f6e01124..6625f71f62 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -174,7 +174,6 @@ protected: wxBoxSizer* m_hsizer; wxBoxSizer* m_left_sizer; wxTreeCtrl* m_treectrl; - wxImageList* m_icons; wxScrolledWindow* m_page_view {nullptr}; wxBoxSizer* m_page_sizer {nullptr}; @@ -203,10 +202,6 @@ protected: ScalableButton* m_undo_to_sys_btn; ScalableButton* m_question_btn; - // Cached bitmaps. - // A "flag" icon to be displayned next to the preset name in the Tab's combo box. - ScalableBitmap m_bmp_show_incompatible_presets; - ScalableBitmap m_bmp_hide_incompatible_presets; // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. ScalableBitmap m_bmp_value_lock; ScalableBitmap m_bmp_value_unlock; @@ -505,7 +500,6 @@ public: void build_unregular_pages(bool from_initial_build = false); void on_preset_loaded() override; void init_options_list() override; - void msw_rescale() override; bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; } wxSizer* create_bed_shape_widget(wxWindow* parent); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index b6555585b0..e9b4976382 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -115,29 +115,21 @@ wxIcon ModelNode::get_bitmap(const wxString& color) wxBitmap ModelNode::get_bitmap(const wxString& color) #endif // __linux__ { - /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const double em = em_unit(m_parent_win); - const int icon_width = lround(6.4 * em); - const int icon_height = lround(1.6 * em); - - BitmapCache bmp_cache; - ColorRGB rgb; - decode_color(into_u8(color), rgb); - // there is no need to scale created solid bitmap + wxBitmap bmp = get_solid_bmp_bundle(64, 16, into_u8(color))->GetBitmapFor(m_parent_win); + if (!m_toggle) + bmp = bmp.ConvertToDisabled(); #ifndef __linux__ - return bmp_cache.mksolid(icon_width, icon_height, rgb, true); + return bmp; #else wxIcon icon; - icon.CopyFromBitmap(bmp_cache.mksolid(icon_width, icon_height, rgb, true)); + icon.CopyFromBitmap(bmp); return icon; #endif // __linux__ } // option node ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value) : + m_parent_win(parent->m_parent_win), m_parent(parent), m_old_color(old_value.StartsWith("#") ? old_value : ""), m_new_color(new_value.StartsWith("#") ? new_value : ""), @@ -202,18 +194,22 @@ void ModelNode::UpdateIcons() { // update icons for the colors, if any exists if (!m_old_color.IsEmpty()) - m_old_color_bmp = get_bitmap(m_toggle ? m_old_color : wxString::FromUTF8(grey.c_str())); + m_old_color_bmp = get_bitmap(m_old_color); if (!m_new_color.IsEmpty()) - m_new_color_bmp = get_bitmap(m_toggle ? m_new_color : wxString::FromUTF8(grey.c_str())); + m_new_color_bmp = get_bitmap(m_new_color); // update main icon, if any exists if (m_icon_name.empty()) return; + wxBitmap bmp = get_bmp_bundle(m_icon_name)->GetBitmapFor(m_parent_win); + if (!m_toggle) + bmp = bmp.ConvertToDisabled(); + #ifdef __linux__ - m_icon.CopyFromBitmap(create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle)); + m_icon.CopyFromBitmap(bmp); #else - m_icon = create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle); + m_icon = bmp; #endif //__linux__ } @@ -838,7 +834,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ auto add_btn = [this, buttons, btn_font, dependent_presets](ScalableButton** btn, int& btn_id, const std::string& icon_name, Action close_act, const wxString& label, bool process_enable = true) { - *btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24); + *btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, 24); buttons->Add(*btn, 1, wxLEFT, 5); (*btn)->SetFont(btn_font); @@ -876,7 +872,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ if (ActionButtons::SAVE & m_buttons) add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save")); - ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24); + ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, 24); buttons->Add(cancel_btn, 1, wxLEFT|wxRIGHT, 5); cancel_btn->SetFont(btn_font); cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); }); @@ -1310,8 +1306,6 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) int em = em_unit(); msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); - for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } ) - if (btn) btn->msw_rescale(); const wxSize& size = wxSize(70 * em, 30 * em); SetMinSize(size); @@ -1325,7 +1319,7 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) void UnsavedChangesDialog::on_sys_color_changed() { for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } ) - btn->msw_rescale(); + btn->sys_color_changed(); // msw_rescale updates just icons, so use it m_tree->Rescale(); @@ -1726,10 +1720,16 @@ void DiffPresetDialog::on_dpi_changed(const wxRect&) const wxSize& size = wxSize(80 * em, 30 * em); SetMinSize(size); + auto rescale = [em](PresetComboBox* pcb) { + pcb->msw_rescale(); + wxSize sz = wxSize(35 * em, -1); + pcb->SetMinSize(sz); + pcb->SetSize(sz); + }; + for (auto preset_combos : m_preset_combos) { - preset_combos.presets_left->msw_rescale(); - preset_combos.equal_bmp->msw_rescale(); - preset_combos.presets_right->msw_rescale(); + rescale(preset_combos.presets_left); + rescale(preset_combos.presets_right); } m_tree->Rescale(em); @@ -1747,9 +1747,9 @@ void DiffPresetDialog::on_sys_color_changed() #endif for (auto preset_combos : m_preset_combos) { - preset_combos.presets_left->msw_rescale(); - preset_combos.equal_bmp->msw_rescale(); - preset_combos.presets_right->msw_rescale(); + preset_combos.presets_left->sys_color_changed(); + preset_combos.equal_bmp->sys_color_changed(); + preset_combos.presets_right->sys_color_changed(); } // msw_rescale updates just icons, so use it m_tree->Rescale(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 8763985109..c397b4b397 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -16,6 +16,7 @@ #include "Plater.hpp" #include "../Utils/MacDarkMode.hpp" #include "BitmapComboBox.hpp" +#include "libslic3r/Utils.hpp" #include "OG_CustomCtrl.hpp" #include "libslic3r/Color.hpp" @@ -23,16 +24,15 @@ #ifndef __linux__ // msw_menuitem_bitmaps is used for MSW and OSX static std::map msw_menuitem_bitmaps; -#ifdef __WXMSW__ -void msw_rescale_menu(wxMenu* menu) +void sys_color_changed_menu(wxMenu* menu) { struct update_icons { static void run(wxMenuItem* item) { const auto it = msw_menuitem_bitmaps.find(item->GetId()); if (it != msw_menuitem_bitmaps.end()) { - const wxBitmap& item_icon = create_menu_bitmap(it->second); - if (item_icon.IsOk()) - item->SetBitmap(item_icon); + wxBitmapBundle* item_icon = get_bmp_bundle(it->second); + if (item_icon->IsOk()) + item->SetBitmap(*item_icon); } if (item->IsSubMenu()) for (wxMenuItem *sub_item : item->GetSubMenu()->GetMenuItems()) @@ -43,35 +43,24 @@ void msw_rescale_menu(wxMenu* menu) for (wxMenuItem *item : menu->GetMenuItems()) update_icons::run(item); } -#endif /* __WXMSW__ */ -#endif /* no __WXGTK__ */ +#endif /* no __linux__ */ void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item, wxWindow* win) { const bool enable = cb_condition(); evt.Enable(enable); - -#ifdef __WXOSX__ - const auto it = msw_menuitem_bitmaps.find(item->GetId()); - if (it != msw_menuitem_bitmaps.end()) - { - const wxBitmap& item_icon = create_scaled_bitmap(it->second, win, 16, !enable); - if (item_icon.IsOk()) - item->SetBitmap(item_icon); - } -#endif // __WXOSX__ } wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler, + std::function cb, wxBitmapBundle* icon, wxEvtHandler* event_handler, std::function const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/) { if (id == wxID_ANY) id = wxNewId(); auto *item = new wxMenuItem(menu, id, string, description); - if (icon.IsOk()) { - item->SetBitmap(icon); + if (icon && icon->IsOk()) { + item->SetBitmap(*icon); } if (insert_pos == wxNOT_FOUND) menu->Append(item); @@ -100,12 +89,12 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const if (id == wxID_ANY) id = wxNewId(); - const wxBitmap& bmp = !icon.empty() ? create_menu_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr -//#ifdef __WXMSW__ -#ifndef __WXGTK__ - if (bmp.IsOk()) + wxBitmapBundle* bmp = icon.empty() ? nullptr : get_bmp_bundle(icon); + +#ifndef __linux__ + if (bmp && bmp->IsOk()) msw_menuitem_bitmaps[id] = icon; -#endif /* __WXMSW__ */ +#endif /* no __linux__ */ return append_menu_item(menu, id, string, description, cb, bmp, event_handler, cb_condition, parent, insert_pos); } @@ -118,11 +107,11 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* item = new wxMenuItem(menu, id, string, description); if (!icon.empty()) { - item->SetBitmap(create_menu_bitmap(icon)); // FIXME: pass window ptr -//#ifdef __WXMSW__ -#ifndef __WXGTK__ + item->SetBitmap(*get_bmp_bundle(icon)); + +#ifndef __linux__ msw_menuitem_bitmaps[id] = icon; -#endif /* __WXMSW__ */ +#endif // no __linux__ } item->SetSubMenu(sub_menu); @@ -420,9 +409,53 @@ int mode_icon_px_size() #endif } -wxBitmap create_menu_bitmap(const std::string& bmp_name) +#ifdef __WXGTK2__ +static int scale() { - return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true); + return int(em_unit(nullptr) * 0.1f + 0.5f); +} +#endif // __WXGTK2__ + +wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16*/) +{ +#ifdef __WXGTK2__ + px_cnt *= scale(); +#endif // __WXGTK2__ + + static Slic3r::GUI::BitmapCache cache; + + std::string bmp_name = bmp_name_in; + boost::replace_last(bmp_name, ".png", ""); + + // Try loading an SVG first, then PNG if SVG is not found: + wxBitmapBundle* bmp = cache.from_svg(bmp_name, px_cnt, px_cnt, Slic3r::GUI::wxGetApp().dark_mode()); + if (bmp == nullptr) { + bmp = cache.from_png(bmp_name, px_cnt, px_cnt); + if (!bmp) + // Neither SVG nor PNG has been found, raise error + throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name); + } + return bmp; +} + +wxBitmapBundle* get_empty_bmp_bundle(int width, int height) +{ + static Slic3r::GUI::BitmapCache cache; +#ifdef __WXGTK2__ + return cache.mkclear_bndl(width * scale(), height * scale()); +#else + return cache.mkclear_bndl(width, height); +#endif // __WXGTK2__ +} + +wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color ) +{ + static Slic3r::GUI::BitmapCache cache; +#ifdef __WXGTK2__ + return cache.mksolid_bndl(width * scale(), height * scale(), color, 1, Slic3r::GUI::wxGetApp().dark_mode()); +#else + return cache.mksolid_bndl(width, height, color, 1, Slic3r::GUI::wxGetApp().dark_mode()); +#endif // __WXGTK2__ } // win is used to get a correct em_unit value @@ -463,41 +496,17 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in, return *bmp; } -std::vector get_extruder_color_icons(bool thin_icon/* = false*/) +std::vector get_extruder_color_icons(bool thin_icon/* = false*/) { - static Slic3r::GUI::BitmapCache bmp_cache; - // Create the bitmap with color bars. - std::vector bmps; + std::vector bmps; std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); if (colors.empty()) return bmps; - /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const double em = Slic3r::GUI::wxGetApp().em_unit(); - const int icon_width = lround((thin_icon ? 1.6 : 3.2) * em); - const int icon_height = lround(1.6 * em); - - bool dark_mode = Slic3r::GUI::wxGetApp().dark_mode(); - for (const std::string& color : colors) - { - std::string bitmap_key = color + "-h" + std::to_string(icon_height) + "-w" + std::to_string(icon_width); - - wxBitmap* bitmap = bmp_cache.find(bitmap_key); - if (bitmap == nullptr) { - // Paint the color icon. - Slic3r::ColorRGB rgb; - Slic3r::decode_color(color, rgb); - // there is no neede to scale created solid bitmap - bitmap = bmp_cache.insert(bitmap_key, bmp_cache.mksolid(icon_width, icon_height, rgb, true, 1, dark_mode)); - } - bmps.emplace_back(bitmap); - } + bmps.emplace_back(get_solid_bmp_bundle(thin_icon ? 16 : 32, 16, color)); return bmps; } @@ -510,7 +519,7 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl, wxSize size/* = wxDefaultSize*/, bool use_thin_icon/* = false*/) { - std::vector icons = get_extruder_color_icons(use_thin_icon); + std::vector icons = get_extruder_color_icons(use_thin_icon); if (!*ctrl) { *ctrl = new Slic3r::GUI::BitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size, 0, nullptr, wxCB_READONLY); @@ -536,7 +545,7 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl, int i = 0; wxString str = _(L("Extruder")); - for (wxBitmap* bmp : icons) { + for (wxBitmapBundle* bmp : icons) { if (i == 0) { if (!first_item.empty()) (*ctrl)->Append(_(first_item), *bmp); @@ -570,7 +579,7 @@ LockButton::LockButton( wxWindow *parent, Slic3r::GUI::wxGetApp().UpdateDarkUI(this); SetBitmap(m_bmp_lock_open.bmp()); SetBitmapDisabled(m_bmp_lock_open.bmp()); - SetBitmapHover(m_bmp_lock_closed_f.bmp()); + SetBitmapCurrent(m_bmp_lock_closed_f.bmp()); //button events Bind(wxEVT_BUTTON, &LockButton::OnButton, this); @@ -599,12 +608,14 @@ void LockButton::SetLock(bool lock) } } -void LockButton::msw_rescale() +void LockButton::sys_color_changed() { - m_bmp_lock_closed.msw_rescale(); - m_bmp_lock_closed_f.msw_rescale(); - m_bmp_lock_open.msw_rescale(); - m_bmp_lock_open_f.msw_rescale(); + Slic3r::GUI::wxGetApp().UpdateDarkUI(this); + + m_bmp_lock_closed.sys_color_changed(); + m_bmp_lock_closed_f.sys_color_changed(); + m_bmp_lock_open.sys_color_changed(); + m_bmp_lock_open_f.sys_color_changed(); update_button_bitmaps(); } @@ -612,7 +623,7 @@ void LockButton::msw_rescale() void LockButton::update_button_bitmaps() { SetBitmap(m_is_pushed ? m_bmp_lock_closed.bmp() : m_bmp_lock_open.bmp()); - SetBitmapHover(m_is_pushed ? m_bmp_lock_closed_f.bmp() : m_bmp_lock_open_f.bmp()); + SetBitmapCurrent(m_is_pushed ? m_bmp_lock_closed_f.bmp() : m_bmp_lock_open_f.bmp()); Refresh(); Update(); @@ -639,7 +650,7 @@ ModeButton::ModeButton( wxWindow* parent, const wxString& mode/* = wxEmptyString*/, const std::string& icon_name/* = ""*/, int px_cnt/* = 16*/) : - ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT) + ScalableButton(parent, wxID_ANY, icon_name, mode, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT, px_cnt) { Init(mode); } @@ -749,11 +760,10 @@ void ModeSizer::set_items_border(int border) item->SetBorder(border); } -void ModeSizer::msw_rescale() +void ModeSizer::sys_color_changed() { - this->SetHGap(std::lround(m_hgap_unscaled * em_unit(m_parent))); for (size_t m = 0; m < m_mode_btns.size(); m++) - m_mode_btns[m]->msw_rescale(); + m_mode_btns[m]->sys_color_changed(); } // ---------------------------------------------------------------------------- @@ -793,40 +803,13 @@ ScalableBitmap::ScalableBitmap( wxWindow *parent, m_parent(parent), m_icon_name(icon_name), m_px_cnt(px_cnt) { - m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt, grayscale); + m_bmp = *get_bmp_bundle(icon_name, px_cnt); + m_bitmap = m_bmp.GetBitmapFor(m_parent); } -wxSize ScalableBitmap::GetBmpSize() const +void ScalableBitmap::sys_color_changed() { -#ifdef __APPLE__ - return m_bmp.GetScaledSize(); -#else - return m_bmp.GetSize(); -#endif -} - -int ScalableBitmap::GetBmpWidth() const -{ -#ifdef __APPLE__ - return m_bmp.GetScaledWidth(); -#else - return m_bmp.GetWidth(); -#endif -} - -int ScalableBitmap::GetBmpHeight() const -{ -#ifdef __APPLE__ - return m_bmp.GetScaledHeight(); -#else - return m_bmp.GetHeight(); -#endif -} - - -void ScalableBitmap::msw_rescale() -{ - m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt, m_grayscale); + m_bmp = *get_bmp_bundle(m_icon_name, m_px_cnt); } // ---------------------------------------------------------------------------- @@ -840,11 +823,9 @@ ScalableButton::ScalableButton( wxWindow * parent, const wxSize& size /* = wxDefaultSize*/, const wxPoint& pos /* = wxDefaultPosition*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/, - bool use_default_disabled_bitmap/* = false*/, int bmp_px_cnt/* = 16*/) : m_parent(parent), m_current_icon_name(icon_name), - m_use_default_disabled_bitmap (use_default_disabled_bitmap), m_px_cnt(bmp_px_cnt), m_has_border(!(style & wxNO_BORDER)) { @@ -852,9 +833,7 @@ ScalableButton::ScalableButton( wxWindow * parent, Slic3r::GUI::wxGetApp().UpdateDarkUI(this); if (!icon_name.empty()) { - SetBitmap(create_scaled_bitmap(icon_name, parent, m_px_cnt)); - if (m_use_default_disabled_bitmap) - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); + SetBitmap(*get_bmp_bundle(icon_name, m_px_cnt)); if (!label.empty()) SetBitmapMargins(int(0.5* em_unit(parent)), 0); } @@ -896,13 +875,12 @@ bool ScalableButton::SetBitmap_(const std::string& bmp_name) if (m_current_icon_name.empty()) return false; - wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); + wxBitmapBundle bmp = *get_bmp_bundle(m_current_icon_name, m_px_cnt); SetBitmap(bmp); SetBitmapCurrent(bmp); SetBitmapPressed(bmp); SetBitmapFocus(bmp); - if (m_use_default_disabled_bitmap) - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); + SetBitmapDisabled(bmp); return true; } @@ -921,37 +899,21 @@ int ScalableButton::GetBitmapHeight() #endif } -void ScalableButton::UseDefaultBitmapDisabled() -{ - m_use_default_disabled_bitmap = true; - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); -} - -void ScalableButton::msw_rescale() +void ScalableButton::sys_color_changed() { Slic3r::GUI::wxGetApp().UpdateDarkUI(this, m_has_border); - if (!m_current_icon_name.empty()) { - wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); - SetBitmap(bmp); - SetBitmapCurrent(bmp); - SetBitmapPressed(bmp); - SetBitmapFocus(bmp); - if (!m_disabled_icon_name.empty()) - SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt)); - else if (m_use_default_disabled_bitmap) - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); - } - - if (m_width > 0 || m_height>0) - { - const int em = em_unit(m_parent); - wxSize size(m_width * em, m_height * em); - SetMinSize(size); - } + wxBitmapBundle bmp = *get_bmp_bundle(m_current_icon_name, m_px_cnt); + SetBitmap(bmp); + SetBitmapCurrent(bmp); + SetBitmapPressed(bmp); + SetBitmapFocus(bmp); + if (!m_disabled_icon_name.empty()) + SetBitmapDisabled(*get_bmp_bundle(m_disabled_icon_name, m_px_cnt)); + if (!GetLabelText().IsEmpty()) + SetBitmapMargins(int(0.5 * em_unit(m_parent)), 0); } - // ---------------------------------------------------------------------------- // BlinkingBitmap // ---------------------------------------------------------------------------- @@ -962,13 +924,6 @@ BlinkingBitmap::BlinkingBitmap(wxWindow* parent, const std::string& icon_name) : bmp = ScalableBitmap(parent, icon_name); } -void BlinkingBitmap::msw_rescale() -{ - bmp.msw_rescale(); - this->SetSize(bmp.GetBmpSize()); - this->SetMinSize(bmp.GetBmpSize()); -} - void BlinkingBitmap::invalidate() { this->SetBitmap(wxNullBitmap); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index f3921919ee..db05af9eb1 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,14 +16,14 @@ #include -#ifdef __WXMSW__ -void msw_rescale_menu(wxMenu* menu); -#else /* __WXMSW__ */ -inline void msw_rescale_menu(wxMenu* /* menu */) {} -#endif /* __WXMSW__ */ +#ifndef __linux__ +void sys_color_changed_menu(wxMenu* menu); +#else +inline void sys_color_changed_menu(wxMenu* /* menu */) {} +#endif // no __linux__ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler = nullptr, + std::function cb, wxBitmapBundle* icon, wxEvtHandler* event_handler = nullptr, std::function const cb_condition = []() { return true;}, wxWindow* parent = nullptr, int insert_pos = wxNOT_FOUND); wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr, @@ -49,14 +50,16 @@ void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector< int em_unit(wxWindow* win); int mode_icon_px_size(); -wxBitmap create_menu_bitmap(const std::string& bmp_name); +wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name, int px_cnt = 16); +wxBitmapBundle* get_empty_bmp_bundle(int width, int height); +wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color); wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr, const int px_cnt = 16, const bool grayscale = false, const std::string& new_color = std::string(), // color witch will used instead of orange const bool menu_bitmap = false); -std::vector get_extruder_color_icons(bool thin_icon = false); +std::vector get_extruder_color_icons(bool thin_icon = false); namespace Slic3r { namespace GUI { @@ -145,21 +148,28 @@ public: ~ScalableBitmap() {} - wxSize GetBmpSize() const; - int GetBmpWidth() const; - int GetBmpHeight() const; + void sys_color_changed(); - void msw_rescale(); + const wxBitmapBundle& bmp() const { return m_bmp; } + wxBitmap get_bitmap() { return m_bmp.GetBitmapFor(m_parent); } + wxWindow* parent() const { return m_parent;} + const std::string& name() const { return m_icon_name; } + int px_cnt() const { return m_px_cnt;} - const wxBitmap& bmp() const { return m_bmp; } - wxBitmap& bmp() { return m_bmp; } - const std::string& name() const{ return m_icon_name; } - - int px_cnt()const {return m_px_cnt;} + wxSize GetSize() const { +#ifdef __WIN32__ + return m_bmp.GetPreferredBitmapSizeFor(m_parent); +#else + return m_bmp.GetDefaultSize(); +#endif + } + int GetWidth() const { return GetSize().GetWidth(); } + int GetHeight() const { return GetSize().GetHeight(); } private: wxWindow* m_parent{ nullptr }; - wxBitmap m_bmp = wxBitmap(); + wxBitmapBundle m_bmp = wxBitmapBundle(); + wxBitmap m_bitmap = wxBitmap(); std::string m_icon_name = ""; int m_px_cnt {16}; bool m_grayscale {false}; @@ -189,7 +199,7 @@ public: void enable() { m_disabled = false; } void disable() { m_disabled = true; } - void msw_rescale(); + void sys_color_changed(); protected: void update_button_bitmaps(); @@ -221,7 +231,6 @@ public: const wxSize& size = wxDefaultSize, const wxPoint& pos = wxDefaultPosition, long style = wxBU_EXACTFIT | wxNO_BORDER, - bool use_default_disabled_bitmap = false, int bmp_px_cnt = 16); ScalableButton( @@ -237,9 +246,8 @@ public: bool SetBitmap_(const std::string& bmp_name); void SetBitmapDisabled_(const ScalableBitmap &bmp); int GetBitmapHeight(); - void UseDefaultBitmapDisabled(); - void msw_rescale(); + void sys_color_changed(); private: wxWindow* m_parent { nullptr }; @@ -248,8 +256,6 @@ private: int m_width {-1}; // should be multiplied to em_unit int m_height{-1}; // should be multiplied to em_unit - bool m_use_default_disabled_bitmap {false}; - // bitmap dimensions int m_px_cnt{ 16 }; bool m_has_border {false}; @@ -315,7 +321,7 @@ public: void set_items_flag(int flag); void set_items_border(int border); - void msw_rescale(); + void sys_color_changed(); const std::vector& get_btns() { return m_mode_btns; } private: @@ -363,12 +369,11 @@ public: ~BlinkingBitmap() {} - void msw_rescale(); void invalidate(); void activate(); void blink(); - const wxBitmap& get_bmp() const { return bmp.bmp(); } + const wxBitmapBundle& get_bmp() const { return bmp.bmp(); } private: ScalableBitmap bmp;