diff --git a/src/attributes.cpp b/src/attributes.cpp index aa9ead2b12d7419b0806a8ca5d6403955cffec0c..c311a6dc1640009923a89e6d954d1d97dc635da5 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -565,6 +565,10 @@ static SPStyleProp const props[] = { /* LivePathEffect */ {SPAttr::PATH_EFFECT, "effect"}, + // canvas blackout color + {SPAttr::INKSCAPE_BLACKOUTCOLOR, "inkscape:blackoutcolor"}, + {SPAttr::INKSCAPE_BLACKOUTOPACITY, "inkscape:blackoutopacity"}, + }; #define n_attrs (sizeof(props) / sizeof(props[0])) diff --git a/src/attributes.h b/src/attributes.h index 492ea1c6710c6419e88fb744eadfb38cc41d4642..d66069bd869fa25fc56b1e2e853d6c21d653e374 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -567,6 +567,10 @@ enum class SPAttr { /* LivePathEffect */ PATH_EFFECT, + // canvas blackout color + INKSCAPE_BLACKOUTCOLOR, + INKSCAPE_BLACKOUTOPACITY, + // sentinel SPAttr_SIZE }; diff --git a/src/desktop.cpp b/src/desktop.cpp index 954283fa1bde05833694413a1bc75ae7b991369b..4365042b5268c49ed05233180f5b539b681cb3ef 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -234,12 +234,19 @@ SPDesktop::init (SPNamedView *nv, Inkscape::UI::Widget::Canvas *acanvas, SPDeskt Geom::Rect const d(Geom::Point(0.0, 0.0), Geom::Point(document->getWidth().value("px"), document->getHeight().value("px"))); + canvas_background = new Inkscape::CanvasItemRect(canvas_group_drawing, d); + canvas_background->set_name("CanvasItemRect:PageBackground"); + canvas_background->set_stroke(0x00000000); + canvas_background->set_background(0x00000000); + + // canvas_page is used to render page border only (no fill, no background) as it can be placed on top of drawing canvas_page = new Inkscape::CanvasItemRect(canvas_group_drawing, d); canvas_page->set_name( "CanvasItemRect:Page" ); canvas_page->set_stroke(0x00000000); canvas_shadow = new Inkscape::CanvasItemRect(canvas_group_drawing, d); canvas_shadow->set_name( "CanvasItemRect:Shadow" ); + canvas_shadow->set_stroke(0x00000000); if ( namedview->pageshadow != 0 && namedview->showpageshadow ) { canvas_shadow->set_shadow(0x3f3f3fff, namedview->pageshadow); } @@ -1661,6 +1668,7 @@ SPDesktop::onDocumentResized (gdouble width, gdouble height) assert(canvas->get_affine() == _current_affine.d2w()); Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height)); + canvas_background->set_rect(a); canvas_page->set_rect(a); canvas_shadow->set_rect(a); } @@ -1771,10 +1779,21 @@ static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop) SPNamedView *nv=SP_NAMEDVIEW(obj); if (flags & SP_OBJECT_MODIFIED_FLAG) { + guint32 blackout_color = 0; + { + // blend page and blackout colors by "painting" with blackout on top of opaque page: + const auto a = SP_RGBA32_A_F(nv->blackoutcolor); + const auto r = SP_RGBA32_R_F(nv->pagecolor) * (1 - a) + SP_RGBA32_R_F(nv->blackoutcolor) * a; + const auto g = SP_RGBA32_G_F(nv->pagecolor) * (1 - a) + SP_RGBA32_G_F(nv->blackoutcolor) * a; + const auto b = SP_RGBA32_B_F(nv->pagecolor) * (1 - a) + SP_RGBA32_B_F(nv->blackoutcolor) * a; + blackout_color = SP_RGBA32_F_COMPOSE(r, g, b, 1); + } if (nv->pagecheckerboard) { - desktop->getCanvas()->set_background_checkerboard(nv->pagecolor); + desktop->getCanvas()->set_background_checkerboard(blackout_color); + desktop->getCanvasPageBackground()->set_background_checkerboard(nv->pagecolor); } else { - desktop->getCanvas()->set_background_color(nv->pagecolor); + desktop->getCanvas()->set_background_color(blackout_color); + desktop->getCanvasPageBackground()->set_background(nv->pagecolor | 0xff); } /* Show/hide page border */ diff --git a/src/desktop.h b/src/desktop.h index 40c22f77bbd534755c718684a7cb66e43b2238a2..6c1bbc1aa05fb923a1dedd467f2bfbc726a57941 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -170,6 +170,7 @@ public: Inkscape::CanvasItemGroup *getCanvasTemp() const { return canvas_group_temp; } Inkscape::CanvasItemCatchall *getCanvasCatchall() const { return canvas_catchall; } + Inkscape::CanvasItemRect *getCanvasPageBackground() const { return canvas_background; } Inkscape::CanvasItemRect *getCanvasPage() const { return canvas_page; } Inkscape::CanvasItemRect *getCanvasShadow() const { return canvas_shadow; } Inkscape::CanvasItemDrawing *getCanvasDrawing() const { return canvas_drawing; } @@ -186,7 +187,8 @@ private: // Individual items Inkscape::CanvasItemCatchall *canvas_catchall = nullptr; ///< The bottom item for unclaimed events. - Inkscape::CanvasItemRect *canvas_page = nullptr; ///< Page background + Inkscape::CanvasItemRect *canvas_background = nullptr; ///< Page background + Inkscape::CanvasItemRect *canvas_page = nullptr; ///< Page border Inkscape::CanvasItemRect *canvas_shadow = nullptr; ///< Page shadow Inkscape::CanvasItemDrawing *canvas_drawing = nullptr; ///< The actual SVG drawing (a.k.a. arena). Inkscape::CanvasItemRotate *canvas_rotate = nullptr; ///< Quick preview of canvas rotation. diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index cdd2750dba9229ef1541809273958534c78b18ba..851e56ec35efad88dc636fc699b55d29e5752cd9 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -1531,6 +1531,91 @@ ink_cairo_pattern_create_checkerboard(guint32 rgba) return p; } + +/** + * Draw drop shadow around the 'rect' with given 'size' and 'color'; shadow extends to the right and bottom of rect. + */ +void ink_cairo_draw_drop_shadow(Cairo::RefPtr ctx, const Geom::Rect& rect, int size, guint32 color, double color_alpha) { + // draw fake drop shadow built from gradients + const auto r = SP_RGBA32_R_F(color); + const auto g = SP_RGBA32_G_F(color); + const auto b = SP_RGBA32_B_F(color); + const auto a = color_alpha; + const Geom::Point corners[] = { rect.corner(0), rect.corner(1), rect.corner(2), rect.corner(3) }; + // space for gradient shadow + double sw = size; + double half = sw / 2; + using Geom::X; + using Geom::Y; + // 8 gradients total: 4 sides + 4 corners + auto grad_top = Cairo::LinearGradient::create(0, corners[0][Y] + half, 0, corners[0][Y] - half); + auto grad_right = Cairo::LinearGradient::create(corners[1][X], 0, corners[1][X] + sw, 0); + auto grad_bottom = Cairo::LinearGradient::create(0, corners[2][Y], 0, corners[2][Y] + sw); + auto grad_left = Cairo::LinearGradient::create(corners[0][X] + half, 0, corners[0][X] - half, 0); + auto grad_btm_right = Cairo::RadialGradient::create(corners[2][X], corners[2][Y], 0, corners[2][X], corners[2][Y], sw); + auto grad_top_right = Cairo::RadialGradient::create(corners[1][X], corners[1][Y] + half, 0, corners[1][X], corners[1][Y] + half, sw); + auto grad_btm_left = Cairo::RadialGradient::create(corners[3][X] + half, corners[3][Y], 0, corners[3][X] + half, corners[3][Y], sw); + auto grad_top_left = Cairo::RadialGradient::create(corners[0][X], corners[0][Y], 0, corners[0][X], corners[0][Y], half); + const int N = 15; // number of gradient stops; stops used to make it non-linear + // using easing function here: (exp(a*(1-t)) - 1) / (exp(a) - 1); + // it has a nice property of growing from 0 to 1 for t in [0..1] + const auto A = 4.0; // this coefficient changes how steep the curve is and controls shadow drop-off + const auto denominator = exp(A) - 1; + for (int i = 0; i <= N; ++i) { + auto pos = static_cast(i) / N; + // exponential decay for drop shadow - long tail, with values from 100% down to 0% opacity + auto t = 1 - pos; // reverse 't' so alpha drops from 1 to 0 + auto alpha = (exp(A * t) - 1) / denominator; + grad_top->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_bottom->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_right->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_left->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_btm_right->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_top_right->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_btm_left->add_color_stop_rgba(pos, r, g, b, alpha * a); + // this left/top corner is just a silver of the shadow: half of it is "hidden" beneath the page + if (pos >= 0.5) { + grad_top_left->add_color_stop_rgba(2 * (pos - 0.5), r, g, b, alpha * a); + } + } + + // shadow at the top (faint) + ctx->rectangle(corners[0][X], corners[0][Y] - half, std::max(corners[1][X] - corners[0][X], 0.0), half); + ctx->set_source(grad_top); + ctx->fill(); + + // right side + ctx->rectangle(corners[1][X], corners[1][Y] + half, sw, std::max(corners[2][Y] - corners[1][Y] - half, 0.0)); + ctx->set_source(grad_right); + ctx->fill(); + + // bottom side + ctx->rectangle(corners[0][X] + half, corners[2][Y], std::max(corners[1][X] - corners[0][X] - half, 0.0), sw); + ctx->set_source(grad_bottom); + ctx->fill(); + + // left side (faint) + ctx->rectangle(corners[0][X] - half, corners[0][Y], half, std::max(corners[2][Y] - corners[1][Y], 0.0)); + ctx->set_source(grad_left); + ctx->fill(); + + ctx->rectangle(corners[2][X], corners[2][Y], sw, sw); + ctx->set_source(grad_btm_right); + ctx->fill(); + + ctx->rectangle(corners[1][X], corners[1][Y] - half, sw, sw); + ctx->set_source(grad_top_right); + ctx->fill(); + + ctx->rectangle(corners[3][X] - half, corners[3][Y], sw, sw); + ctx->set_source(grad_btm_left); + ctx->fill(); + + ctx->rectangle(corners[0][X] - half, corners[0][Y] - half, half, half); + ctx->set_source(grad_top_left); + ctx->fill(); +} + /** * Converts the Cairo surface to a GdkPixbuf pixel format, * without allocating extra memory. diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h index 4d623ea98bf7b9e59fad3d5fb06edd7a4c159b64..73184654aea11c3d3cce5cdf64a3bea75c2181c5 100644 --- a/src/display/cairo-utils.h +++ b/src/display/cairo-utils.h @@ -178,6 +178,8 @@ int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface); int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface); cairo_pattern_t *ink_cairo_pattern_create_checkerboard(guint32 rgba = 0xC4C4C4FF); +// draw drop shadow around the 'rect' with given 'size' and 'color'; shadow extends to the right and bottom of rect +void ink_cairo_draw_drop_shadow(Cairo::RefPtr ctx, const Geom::Rect& rect, int size, guint32 color, double color_alpha); GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s); void convert_pixels_pixbuf_to_argb32(guchar *data, int w, int h, int rs); diff --git a/src/display/control/canvas-item-rect.cpp b/src/display/control/canvas-item-rect.cpp index f295e1942e66025ff80979b9571fa6c5727dedea..3e69e542c589b432273089750febb22d2e26be0a 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -22,6 +22,7 @@ #include "color.h" // SP_RGBA_x_F #include "inkscape.h" // #include "ui/widget/canvas.h" +#include "display/cairo-utils.h" // Checkerboard background. namespace Inkscape { @@ -117,8 +118,10 @@ void CanvasItemRect::update(Geom::Affine const &affine) // Enlarge bbox by twice shadow size (to allow for shadow on any side with a 45deg rotation). _bounds = _rect; + // note: add shadow size before applying transformation, since get_shadow_size accounts for scale + _bounds.expandBy(2 * get_shadow_size()); _bounds *= _affine; - _bounds.expandBy(2*_shadow_width + 2); // Room for stroke. + _bounds.expandBy(2); // Room for stroke. // Queue redraw of new area _canvas->redraw_area(_bounds); @@ -160,6 +163,24 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) rect_transformed[i] = _rect.corner(i) * _affine; } + // canvas scale; impacts shadow size + const auto scale = sqrt(_affine[0] * _affine[0] + _affine[1] * _affine[1]); + + auto rect = _rect; + + using Geom::X; + using Geom::Y; + + if (axis_aligned) { + auto temp = _rect * _affine; + auto min = temp.min(); + auto max = temp.max(); + auto pixgrid = Geom::Rect( + Geom::Point(floor(min[X]) + 0.5, floor(min[Y]) + 0.5), + Geom::Point(floor(max[X]) + 0.5, floor(max[Y]) + 0.5)); + rect = pixgrid * _affine.inverse(); + } + buf->cr->save(); buf->cr->translate(-buf->rect.left(), -buf->rect.top()); @@ -167,8 +188,19 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) // buf->cr->set_operator(Cairo::OPERATOR_XOR); // Blend mode operators do not have C++ bindings! cairo_set_operator(buf->cr->cobj(), CAIRO_OPERATOR_DIFFERENCE); } - using Geom::X; - using Geom::Y; + + // fill background? + if (_background) { + buf->cr->save(); + Cairo::Matrix m(_affine[0], _affine[1], _affine[2], _affine[3], _affine[4], _affine[5]); + buf->cr->transform(m); + buf->cr->rectangle(rect.corner(0)[X], rect.corner(0)[Y], rect.width(), rect.height()); + // counter fill scaling (necessary for checkerboard pattern) + buf->cr->scale(1 / scale, 1 / scale); + buf->cr->set_source(_background); + buf->cr->fill(); + buf->cr->restore(); + } cairo_pattern_t *pattern = _canvas->get_background_store()->cobj(); guint32 backcolor = ink_cairo_pattern_get_argb32(pattern); @@ -176,40 +208,16 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) // Draw shadow first. Shadow extends under rectangle to reduce aliasing effects. if (_shadow_width > 0 && !_dashed) { - - Geom::Point const * corners = rect_transformed; - double shadowydir = _affine.det() > 0 ? -1 : 1; - - // Is the desktop y-axis downwards? - if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->is_yaxisdown()) { - ++corners; // Need corners 1/2/3 instead of 0/1/2. - shadowydir *= -1; - } - - // Offset by half stroke width (_shadow_width is in window coordinates). - // Need to handle change in handedness with flips. - Geom::Point shadow( _shadow_width/2.0, shadowydir * _shadow_width/2.0 ); - shadow *= Geom::Rotate( rotation ); - - if (axis_aligned) { - // Snap to pixel grid (add 0.5 to center on pixel). - buf->cr->move_to(floor(corners[0][X] + shadow[X]+0.5) + 0.5, - floor(corners[0][Y] + shadow[Y]+0.5) + 0.5 ); - buf->cr->line_to(floor(corners[1][X] + shadow[X]+0.5) + 0.5, - floor(corners[1][Y] + shadow[Y]+0.5) + 0.5 ); - buf->cr->line_to(floor(corners[2][X] + shadow[X]+0.5) + 0.5, - floor(corners[2][Y] + shadow[Y]+0.5) + 0.5 ); - } else { - buf->cr->move_to(corners[0][X] + shadow[X], corners[0][Y] + shadow[Y] ); - buf->cr->line_to(corners[1][X] + shadow[X], corners[1][Y] + shadow[Y] ); - buf->cr->line_to(corners[2][X] + shadow[X], corners[2][Y] + shadow[Y] ); - } - buf->cr->set_line_width(_shadow_width + 1); - buf->cr->set_source_rgba(SP_RGBA32_R_F(_shadow_color), SP_RGBA32_G_F(_shadow_color), - SP_RGBA32_B_F(_shadow_color), SP_RGBA32_A_F(_shadow_color)); - buf->cr->stroke(); + // there's only one UI knob to adjust border and shadow color, so instead of using border color + // transparency as is, it is boosted by this function, since shadow attenuates it + const auto a = (exp(-3 * SP_RGBA32_A_F(_shadow_color)) - 1) / (exp(-3) - 1); + buf->cr->save(); + Cairo::Matrix m(_affine[0], _affine[1], _affine[2], _affine[3], _affine[4], _affine[5]); + buf->cr->transform(m); + ink_cairo_draw_drop_shadow(buf->cr, rect, get_shadow_size(), _shadow_color, a); + buf->cr->restore(); } - + // Setup rectangle path if (axis_aligned) { @@ -254,7 +262,7 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) // Geom::Rect bounds = _bounds; // bounds.expandBy(-1); // bounds -= buf->rect.min(); - // buf->cr->set_source_rgba(1.0, 0.0, 0.0, 1.0); + // buf->cr->set_source_rgba(1.0, 0.0, _shadow_width / 3.0, 1.0); // buf->cr->rectangle(bounds.min().x(), bounds.min().y(), bounds.width(), bounds.height()); // buf->cr->stroke(); @@ -286,6 +294,48 @@ void CanvasItemRect::set_shadow(guint32 color, int width) } } +void CanvasItemRect::set_background(guint32 background) { + _set_background(Cairo::SolidPattern::create_rgba(SP_RGBA32_R_F(background), SP_RGBA32_G_F(background), SP_RGBA32_B_F(background), SP_RGBA32_A_F(background))); +} + +void CanvasItemRect::_set_background(Cairo::RefPtr background) { + if (_background != background) { + _background = background; + _canvas->redraw_area(_bounds); + } +} + +double CanvasItemRect::get_scale() const { + return sqrt(_affine[0] * _affine[0] + _affine[1] * _affine[1]); +} + +double CanvasItemRect::get_shadow_size() const { + // gradient drop shadow needs much more room than solid one, so inflating the size; + // fudge factor of 6 used to make sizes baked in svg documents work as steps: + // typical value of 2 will work out to 12 pixels which is a narrow shadow (b/c of exponential fall of) + auto size = _shadow_width * 6; + if (size < 0) { + size = 0; + } + else if (size > 120) { + // arbitrarily selected max size, so Cairo gradient doesn't blow up if document has bogus shadow values + size = 120; + } + auto scale = get_scale(); + + // calculate space for gradient shadow; if divided by 'scale' it would be zoom independent (fixed in size); + // if 'scale' is not used, drop shadow will be getting smaller with document zoom; + // here hybrid approach is used: "unscaling" with square root of scale allows shadows to diminish + // more slowly at small zoom levels (so it's still perceptible) and grow more slowly at high mag (where it doesn't matter, b/c it's typically off-screen) + return size / (scale > 0 ? sqrt(scale) : 1); +} + +void CanvasItemRect::set_background_checkerboard(guint32 rgba) { + auto pattern = ink_cairo_pattern_create_checkerboard(rgba); + auto background = Cairo::RefPtr(new Cairo::Pattern(pattern)); + _set_background(background); +} + } // namespace Inkscape /* diff --git a/src/display/control/canvas-item-rect.h b/src/display/control/canvas-item-rect.h index 16ad07e34675e54675837a8195c8a3875a7941bb..7c2361c0b122a1827690d47aeb73625ec198f0ac 100644 --- a/src/display/control/canvas-item-rect.h +++ b/src/display/control/canvas-item-rect.h @@ -48,13 +48,20 @@ public: void set_dashed(bool dash = true); void set_inverted(bool inverted = false); void set_shadow(guint32 color, int width); + void set_background(guint32 background); + void set_background_checkerboard(guint32 rgba); protected: + void _set_background(Cairo::RefPtr background); + double get_shadow_size() const; + double get_scale() const; + Geom::Rect _rect; bool _dashed = false; bool _inverted = false; int _shadow_width = 0; guint32 _shadow_color = 0x00000000; + Cairo::RefPtr _background; // optional background }; diff --git a/src/document.cpp b/src/document.cpp index 2d4049c7fb119ff58458496ca07ec7beca52db57..e3d816290087df1d6c6b5652c5db9b160c21ae36 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -395,6 +395,8 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, nv->setDefaultAttribute("inkscape:pageshadow", "/template/base/pageshadow", "2"); nv->setDefaultAttribute("inkscape:pageopacity", "/template/base/pageopacity", "0.0"); nv->setDefaultAttribute("inkscape:pagecheckerboard", "/template/base/pagecheckerboard", "0"); + nv->setDefaultAttribute("blackoutcolor", "/template/base/blackoutcolor", ""); + nv->setDefaultAttribute("inkscape:blackoutopacity", "/template/base/blackoutopacity", "0.0"); // If no units are set in the document, try and guess them from the width/height if (document->root->width.isAbsolute()) { diff --git a/src/object/sp-namedview.cpp b/src/object/sp-namedview.cpp index 1cdeebefa79665400833d0d456a93e435f12eb1c..255cd3fc1da09d24c825c932382c48f1fc4b3a35 100644 --- a/src/object/sp-namedview.cpp +++ b/src/object/sp-namedview.cpp @@ -51,6 +51,7 @@ using Inkscape::Util::unit_table; #define DEFAULTGUIDEHICOLOR 0xff00007f #define DEFAULTBORDERCOLOR 0x000000ff #define DEFAULTPAGECOLOR 0xffffff00 +#define DEFAULTBLACKOUTCOLOR 0x00000000 static void sp_namedview_setup_guides(SPNamedView * nv); static void sp_namedview_lock_guides(SPNamedView * nv); @@ -81,6 +82,7 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this, get_snapping_pr this->window_height = 0; this->window_maximized = 0; this->bordercolor = 0; + blackoutcolor = 0; this->editable = TRUE; this->showguides = TRUE; @@ -212,8 +214,10 @@ void SPNamedView::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr(SPAttr::BORDERCOLOR); this->readAttr(SPAttr::BORDEROPACITY); this->readAttr(SPAttr::PAGECOLOR); + this->readAttr(SPAttr::INKSCAPE_BLACKOUTCOLOR); this->readAttr(SPAttr::INKSCAPE_PAGECHECKERBOARD); this->readAttr(SPAttr::INKSCAPE_PAGEOPACITY); + this->readAttr(SPAttr::INKSCAPE_BLACKOUTOPACITY); this->readAttr(SPAttr::INKSCAPE_PAGESHADOW); this->readAttr(SPAttr::INKSCAPE_ZOOM); this->readAttr(SPAttr::INKSCAPE_ROTATION); @@ -400,6 +404,13 @@ void SPNamedView::set(SPAttr key, const gchar* value) { } this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SPAttr::INKSCAPE_BLACKOUTCOLOR: + blackoutcolor = (blackoutcolor & 0xff) | (DEFAULTBLACKOUTCOLOR & 0xffffff00); + if (value) { + blackoutcolor = (blackoutcolor & 0xff) | sp_svg_read_color(value, blackoutcolor); + } + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SPAttr::INKSCAPE_PAGECHECKERBOARD: this->pagecheckerboard = (value) ? sp_str_to_bool (value) : false; this->requestModified(SP_OBJECT_MODIFIED_FLAG); @@ -409,6 +420,11 @@ void SPNamedView::set(SPAttr key, const gchar* value) { sp_nv_read_opacity(value, &this->pagecolor); this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SPAttr::INKSCAPE_BLACKOUTOPACITY: + blackoutcolor = (blackoutcolor & 0xffffff00) | (DEFAULTBLACKOUTCOLOR & 0xff); + sp_nv_read_opacity(value, &blackoutcolor); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SPAttr::INKSCAPE_PAGESHADOW: this->pageshadow = value? atoi(value) : 2; // 2 is the default this->requestModified(SP_OBJECT_MODIFIED_FLAG); diff --git a/src/object/sp-namedview.h b/src/object/sp-namedview.h index 0d4a495137f571770dad02691479ab17bc1e3ac9..dafbfd3546b054b7b11428209de7c6bc97b28727 100644 --- a/src/object/sp-namedview.h +++ b/src/object/sp-namedview.h @@ -74,6 +74,7 @@ public: guint32 guidehicolor; guint32 bordercolor; guint32 pagecolor; + guint32 blackoutcolor; guint32 pageshadow; std::vector guides; diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 845d44f896af539d219c78f84ffbc2dbaf6ea771..0fdae7fe8a1f2098f433fb592ccc948ba5d8c003 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -70,8 +70,8 @@ static char const preferences_skeleton[] = pagecolor="#ffffff" pageopacity="0.0" pagecheckerboard="0" - bordercolor="#666666" - borderopacity="1.0" + bordercolor="#000000" + borderopacity="0.25" objecttolerance="10.0" gridtolerance="10.0" guidetolerance="10.0" diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 7ae40cf8bba1956e01ce683747815f1360f7125c..9ba004c33d891271f4f2eb59f3796deac06513d3 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -105,6 +105,7 @@ DocumentProperties::DocumentProperties() , _rcb_shad(_("_Show border shadow"), _("If set, page border shows a shadow on its right and lower side"), "inkscape:showpageshadow", _wr, false) , _rcb_shwd(_("_Border shadow width"), _("Width of page border when set"), "", "inkscape:pageshadow", _wr) , _rcp_bg(_("Back_ground color:"), _("Background color"), _("Color of the canvas background. Note: opacity is ignored except when exporting to bitmap."), "pagecolor", "inkscape:pageopacity", _wr) + , _rcp_blkout(_("Desk blackout color:"), _("Blackout color"), _("Color of the desk background."), "inkscape:blackoutcolor", "inkscape:blackoutopacity", _wr) , _rcp_bord(_("Border _color:"), _("Page border color"), _("Color of the page border"), "bordercolor", "borderopacity", _wr) , _rum_deflt(_("Display _units:"), "inkscape:document-units", _wr) , _page_sizer(_wr) @@ -303,6 +304,7 @@ void DocumentProperties::build_page() label_bkg, nullptr, nullptr, &_rcb_checkerboard, nullptr, &_rcp_bg, + nullptr, &_rcp_blkout, label_dsp, nullptr, nullptr, &_rcb_antialias, }; @@ -339,6 +341,7 @@ void DocumentProperties::build_guides() _rum_deflt.set_margin_start(0); _rcp_bg.set_margin_start(0); + _rcp_blkout.set_margin_start(0); _rcp_bord.set_margin_start(0); _rcp_gui.set_margin_start(0); _rcp_hgui.set_margin_start(0); @@ -1389,6 +1392,7 @@ void DocumentProperties::update_widgets() //-----------------------------------------------------------page page _rcb_checkerboard.setActive (nv->pagecheckerboard); _rcp_bg.setRgba32 (nv->pagecolor); + _rcp_blkout.setRgba32(nv->blackoutcolor); _rcb_canb.setActive (nv->showborder); _rcb_bord.setActive (nv->borderlayer == SP_BORDER_LAYER_TOP); _rcp_bord.setRgba32 (nv->bordercolor); @@ -1486,6 +1490,7 @@ void DocumentProperties::on_response (int id) if (id == Gtk::RESPONSE_DELETE_EVENT || id == Gtk::RESPONSE_CLOSE) { _rcp_bg.closeWindow(); + _rcp_blkout.closeWindow(); _rcp_bord.closeWindow(); _rcp_gui.closeWindow(); _rcp_hgui.closeWindow(); diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index 97c018982c46f54e1a3d791f2ee753fbfdb39690..7cec80ca539c888ff4dffd46aae92811d57f2405 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -129,6 +129,7 @@ protected: UI::Widget::RegisteredCheckButton _rcb_shad; UI::Widget::RegisteredSuffixedInteger _rcb_shwd; UI::Widget::RegisteredColorPicker _rcp_bg; + UI::Widget::RegisteredColorPicker _rcp_blkout; UI::Widget::RegisteredColorPicker _rcp_bord; UI::Widget::RegisteredUnitMenu _rum_deflt; UI::Widget::PageSizer _page_sizer; diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp index c5ef20d428ac36fe41acfd845d7ca7726d31d75d..4ae46c78bb4d4818e6c94ee6b0f35b220beb4457 100644 --- a/testfiles/src/attributes-test.cpp +++ b/testfiles/src/attributes-test.cpp @@ -385,6 +385,8 @@ std::vector getKnownAttrs() AttributeInfo("id", true), // AttributeInfo("inkscape:bbox-nodes", true), // AttributeInfo("inkscape:bbox-paths", true), + AttributeInfo("inkscape:blackoutcolor", true), + AttributeInfo("inkscape:blackoutopacity", true), AttributeInfo("inkscape:box3dsidetype", true), AttributeInfo("inkscape:collect", true), AttributeInfo("inkscape:color", true),