From 02164b76be3ecab43d3514a0b79b25ff8e27102b Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 25 Apr 2021 11:17:07 -0700 Subject: [PATCH 01/13] more realistic drop shadow --- src/attributes.cpp | 3 + src/attributes.h | 4 + src/desktop.cpp | 23 +++- src/desktop.h | 4 +- src/display/control/canvas-item-rect.cpp | 154 ++++++++++++++++++----- src/display/control/canvas-item-rect.h | 7 ++ src/document.cpp | 2 + src/object/sp-namedview.cpp | 16 +++ src/object/sp-namedview.h | 1 + src/ui/dialog/document-properties.cpp | 5 + src/ui/dialog/document-properties.h | 1 + testfiles/src/attributes-test.cpp | 2 + 12 files changed, 185 insertions(+), 37 deletions(-) diff --git a/src/attributes.cpp b/src/attributes.cpp index 021205c232..b598772815 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -560,6 +560,9 @@ static SPStyleProp const props[] = { /* LivePathEffect */ {SPAttr::PATH_EFFECT, "effect"}, + {SPAttr::BLACKOUTCOLOR, "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 2dc50d4c30..cc49db6c54 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -562,6 +562,10 @@ enum class SPAttr { /* LivePathEffect */ PATH_EFFECT, + // canvas blackout color + BLACKOUTCOLOR, + INKSCAPE_BLACKOUTOPACITY, + // sentinel SPAttr_SIZE }; diff --git a/src/desktop.cpp b/src/desktop.cpp index 1099d77397..70ca4dd84e 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -235,12 +235,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); } @@ -1669,6 +1676,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); } @@ -1793,10 +1801,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 3835e13a1f..a9f34643cf 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -168,6 +168,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; } @@ -184,7 +185,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/control/canvas-item-rect.cpp b/src/display/control/canvas-item-rect.cpp index 29f96fab01..f561370173 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -21,6 +21,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 { @@ -116,8 +117,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); @@ -159,6 +162,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,46 +188,76 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) 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(); + } // Draw shadow first. Shadow extends under rectangle to reduce aliasing effects. if (_shadow_width > 0 && !_dashed) { + // draw fake drop shadow built from gradients + const auto r = SP_RGBA32_R_F(_shadow_color); + const auto g = SP_RGBA32_G_F(_shadow_color); + const auto b = SP_RGBA32_B_F(_shadow_color); + const auto a = 0.5 + SP_RGBA32_A_F(_shadow_color) / 2; + buf->cr->save(); + Cairo::Matrix m(_affine[0], _affine[1], _affine[2], _affine[3], _affine[4], _affine[5]); + buf->cr->transform(m); + const Geom::Point corners[] = {rect.corner(0), rect.corner(1), rect.corner(2), rect.corner(3) }; + // space for gradient shadow + double sw = get_shadow_size(); + double half = sw / 2; + auto grad_vert = Cairo::LinearGradient::create(corners[1][X], 0, corners[1][X] + sw, 0); + auto grad_horz = Cairo::LinearGradient::create(0, corners[2][Y], 0, corners[2][Y] + sw); + auto grad_corner = 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); + const int N = 15; // number of gradient stops; stops used to make it non-linear + for (int i = 0; i < N; ++i) { + // exponential decay for drop shadow - long tail, with values from 71% down to 1% opacity, + // cutting it off at the end + auto alpha = i + 1 < N ? exp(-(i + 1) * 0.333) : 0.0; + auto pos = static_cast(i) / N; + grad_horz->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_vert->add_color_stop_rgba(pos, r, g, b, alpha * a); + grad_corner->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); + } - Geom::Point const * corners = rect_transformed; - double shadowydir = _affine.det() > 0 ? -1 : 1; + buf->cr->rectangle(corners[1][X], corners[1][Y] + half, sw, std::max(corners[2][Y] - corners[1][Y] - half, 0.0)); + buf->cr->set_source(grad_vert); + buf->cr->fill(); - // 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; - } + buf->cr->rectangle(corners[0][X] + half, corners[2][Y], std::max(corners[1][X] - corners[0][X] - half, 0.0), sw); + buf->cr->set_source(grad_horz); + buf->cr->fill(); - // 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->rectangle(corners[2][X], corners[2][Y], sw, sw); + buf->cr->set_source(grad_corner); + buf->cr->fill(); - 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->set_line_width(_shadow_width + 1); - buf->cr->stroke(); + buf->cr->rectangle(corners[1][X], corners[1][Y] - half, sw, sw); + buf->cr->set_source(grad_top_right); + buf->cr->fill(); + + buf->cr->rectangle(corners[3][X] - half, corners[3][Y], sw, sw); + buf->cr->set_source(grad_btm_left); + // buf->cr->set_source_rgb(1, 0 , 0); + buf->cr->fill(); + buf->cr->restore(); + + // g_warning("%f %f - %f %f - %f %f - %f %f, %f, rgba %x %f %f - %d %f", corners[0][X], corners[0][Y], corners[1][X], corners[1][Y], corners[2][X], corners[2][Y], corners[3][X], corners[3][Y], sw, _shadow_color, r, g, _shadow_width, sw); } - + // Setup rectangle path if (axis_aligned) { @@ -251,7 +302,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(); @@ -283,6 +334,41 @@ 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 then 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; + 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 16ad07e346..7c2361c0b1 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 9e6fd7efbf..8524865153 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -389,6 +389,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 0ac67d3132..0b3377f683 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) { this->window_height = 0; this->window_maximized = 0; this->bordercolor = 0; + blackoutcolor = 0; this->editable = TRUE; this->showguides = TRUE; @@ -210,8 +212,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::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); @@ -387,6 +391,13 @@ void SPNamedView::set(SPAttr key, const gchar* value) { } this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SPAttr::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); @@ -396,6 +407,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 0d4a495137..dafbfd3546 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/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index dbf0673471..bce405b946 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -104,6 +104,7 @@ DocumentProperties::DocumentProperties() , _rcb_bord(_("Border on _top of drawing"), _("If set, border is always on top of the drawing"), "borderlayer", _wr, false) , _rcb_shad(_("_Show border shadow"), _("If set, page border shows a shadow on its right and lower side"), "inkscape:showpageshadow", _wr, false) , _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."), "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) @@ -293,6 +294,7 @@ void DocumentProperties::build_page() label_bkg, nullptr, nullptr, &_rcb_checkerboard, nullptr, &_rcp_bg, + nullptr, &_rcp_blkout, label_dsp, nullptr, nullptr, &_rcb_antialias, }; @@ -324,6 +326,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); @@ -1359,6 +1362,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); @@ -1453,6 +1457,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 9a8a6bbd96..c200e09d46 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -127,6 +127,7 @@ protected: UI::Widget::RegisteredCheckButton _rcb_bord; UI::Widget::RegisteredCheckButton _rcb_shad; 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 f3e44aa3d7..4b29c988e1 100644 --- a/testfiles/src/attributes-test.cpp +++ b/testfiles/src/attributes-test.cpp @@ -424,6 +424,8 @@ std::vector getKnownAttrs() AttributeInfo("inkscape:original-d", true), AttributeInfo("inkscape:pagecheckerboard", true), AttributeInfo("inkscape:pageopacity", true), + AttributeInfo("inkscape:blackcolor", true), + AttributeInfo("inkscape:blackoutopacity", true), AttributeInfo("inkscape:pageshadow", true), AttributeInfo("inkscape:path-effect", true), AttributeInfo("inkscape:persp3d", true), -- GitLab From ec88f4463443aedf908426797401b6181fad4775 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Fri, 30 Apr 2021 20:46:52 -0700 Subject: [PATCH 02/13] new easing fn --- src/display/control/canvas-item-rect.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/display/control/canvas-item-rect.cpp b/src/display/control/canvas-item-rect.cpp index f561370173..6a6e161705 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -221,11 +221,15 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) 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); const int N = 15; // number of gradient stops; stops used to make it non-linear - for (int i = 0; i < N; ++i) { - // exponential decay for drop shadow - long tail, with values from 71% down to 1% opacity, - // cutting it off at the end - auto alpha = i + 1 < N ? exp(-(i + 1) * 0.333) : 0.0; + // using easing function here: (exp(a*(1-t)) - 1) / (exp(a) - 1) + const a = 4; + const 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; + auto alpha = (exp(a * t) - 1) / denominator; + // auto alpha = i + 1 < N ? exp(-(i + 1) * 0.333) : 0.0; grad_horz->add_color_stop_rgba(pos, r, g, b, alpha * a); grad_vert->add_color_stop_rgba(pos, r, g, b, alpha * a); grad_corner->add_color_stop_rgba(pos, r, g, b, alpha * a); -- GitLab From ce1796c3841d7f6c4a4ceef25e2ec8391fea8998 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sat, 1 May 2021 07:54:19 -0700 Subject: [PATCH 03/13] new easing function --- src/display/control/canvas-item-rect.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/display/control/canvas-item-rect.cpp b/src/display/control/canvas-item-rect.cpp index 9932d4cbd7..e1761f41b5 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -212,7 +212,9 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) const auto r = SP_RGBA32_R_F(_shadow_color); const auto g = SP_RGBA32_G_F(_shadow_color); const auto b = SP_RGBA32_B_F(_shadow_color); - const auto a = 0.5 + SP_RGBA32_A_F(_shadow_color) / 2; + // 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); @@ -226,15 +228,15 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) 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); 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) - const a = 4; - const denominator = exp(a) - 1; + // 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; - auto alpha = (exp(a * t) - 1) / denominator; - // auto alpha = i + 1 < N ? exp(-(i + 1) * 0.333) : 0.0; + auto t = 1 - pos; // reverse 't' so alpha drops from 1 to 0 + auto alpha = (exp(A * t) - 1) / denominator; grad_horz->add_color_stop_rgba(pos, r, g, b, alpha * a); grad_vert->add_color_stop_rgba(pos, r, g, b, alpha * a); grad_corner->add_color_stop_rgba(pos, r, g, b, alpha * a); @@ -260,11 +262,8 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) buf->cr->rectangle(corners[3][X] - half, corners[3][Y], sw, sw); buf->cr->set_source(grad_btm_left); - // buf->cr->set_source_rgb(1, 0 , 0); buf->cr->fill(); buf->cr->restore(); - - // g_warning("%f %f - %f %f - %f %f - %f %f, %f, rgba %x %f %f - %d %f", corners[0][X], corners[0][Y], corners[1][X], corners[1][Y], corners[2][X], corners[2][Y], corners[3][X], corners[3][Y], sw, _shadow_color, r, g, _shadow_width, sw); } // Setup rectangle path -- GitLab From cfb357ba2366d566c63f00277be36434df32c625 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 16 May 2021 21:28:12 -0700 Subject: [PATCH 04/13] draw more realistic shadow on all sides --- src/display/control/canvas-item-rect.cpp | 50 ++++++++++++++++++------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/display/control/canvas-item-rect.cpp b/src/display/control/canvas-item-rect.cpp index e1761f41b5..9034813c3b 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -218,15 +218,19 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) buf->cr->save(); Cairo::Matrix m(_affine[0], _affine[1], _affine[2], _affine[3], _affine[4], _affine[5]); buf->cr->transform(m); - const Geom::Point corners[] = {rect.corner(0), rect.corner(1), rect.corner(2), rect.corner(3) }; + const Geom::Point corners[] = { rect.corner(0), rect.corner(1), rect.corner(2), rect.corner(3) }; // space for gradient shadow double sw = get_shadow_size(); double half = sw / 2; - auto grad_vert = Cairo::LinearGradient::create(corners[1][X], 0, corners[1][X] + sw, 0); - auto grad_horz = Cairo::LinearGradient::create(0, corners[2][Y], 0, corners[2][Y] + sw); - auto grad_corner = Cairo::RadialGradient::create(corners[2][X], corners[2][Y], 0, corners[2][X], corners[2][Y], sw); + // 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_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] @@ -237,23 +241,41 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) // 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_horz->add_color_stop_rgba(pos, r, g, b, alpha * a); - grad_vert->add_color_stop_rgba(pos, r, g, b, alpha * a); - grad_corner->add_color_stop_rgba(pos, r, g, b, alpha * a); + 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) + buf->cr->rectangle(corners[0][X], corners[0][Y] - half, std::max(corners[1][X] - corners[0][X], 0.0), half); + buf->cr->set_source(grad_top); + buf->cr->fill(); + + // right side buf->cr->rectangle(corners[1][X], corners[1][Y] + half, sw, std::max(corners[2][Y] - corners[1][Y] - half, 0.0)); - buf->cr->set_source(grad_vert); + buf->cr->set_source(grad_right); buf->cr->fill(); + // bottom side buf->cr->rectangle(corners[0][X] + half, corners[2][Y], std::max(corners[1][X] - corners[0][X] - half, 0.0), sw); - buf->cr->set_source(grad_horz); + buf->cr->set_source(grad_bottom); + buf->cr->fill(); + + // left side (faint) + buf->cr->rectangle(corners[0][X] - half, corners[0][Y], half, std::max(corners[2][Y] - corners[1][Y], 0.0)); + buf->cr->set_source(grad_left); buf->cr->fill(); buf->cr->rectangle(corners[2][X], corners[2][Y], sw, sw); - buf->cr->set_source(grad_corner); + buf->cr->set_source(grad_btm_right); buf->cr->fill(); buf->cr->rectangle(corners[1][X], corners[1][Y] - half, sw, sw); @@ -263,6 +285,10 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) buf->cr->rectangle(corners[3][X] - half, corners[3][Y], sw, sw); buf->cr->set_source(grad_btm_left); buf->cr->fill(); + + buf->cr->rectangle(corners[0][X] - half, corners[0][Y] - half, half, half); + buf->cr->set_source(grad_top_left); + buf->cr->fill(); buf->cr->restore(); } @@ -358,7 +384,7 @@ double CanvasItemRect::get_scale() const { } double CanvasItemRect::get_shadow_size() const { - // gradient drop shadow needs much more room then solid one, so inflating the size; + // 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; -- GitLab From cbfecb7fdbb5fdbcca161365ee00e99de4ae0a42 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 16 May 2021 21:31:54 -0700 Subject: [PATCH 05/13] shadow size constraints --- src/ui/shortcuts.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ui/shortcuts.cpp b/src/ui/shortcuts.cpp index 622c13d9d8..54d4a4fb04 100644 --- a/src/ui/shortcuts.cpp +++ b/src/ui/shortcuts.cpp @@ -506,15 +506,17 @@ Shortcuts::is_user_set(Glib::ustring& action) bool Shortcuts::invoke_verb(GdkEventKey const *event, UI::View::View *view) { - // std::cout << "Shortcuts::invoke_verb: " - // << std::hex << event->keyval << " " - // << std::hex << event->state << std::endl; + std::cout << "Shortcuts::invoke_verb: " + << std::hex << event->keyval << " " + << std::hex << event->state << std::endl; Gtk::AccelKey shortcut = get_from_event(event); Verb* verb = get_verb_from_shortcut(shortcut); + g_warning("verb: %p\n", verb); if (verb) { SPAction *action = verb->get_action(Inkscape::ActionContext(view)); if (action) { + g_warning("action: %p\n", action); sp_action_perform(action, nullptr); return true; } -- GitLab From dcbcb44de0675e2234c1073c0066bc2373edc151 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 16 May 2021 22:01:06 -0700 Subject: [PATCH 06/13] removed debug code --- src/display/control/canvas-item-rect.cpp | 7 +++++++ src/ui/shortcuts.cpp | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/display/control/canvas-item-rect.cpp b/src/display/control/canvas-item-rect.cpp index 9034813c3b..61fb4ef63a 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -388,6 +388,13 @@ double CanvasItemRect::get_shadow_size() const { // 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); diff --git a/src/ui/shortcuts.cpp b/src/ui/shortcuts.cpp index 54d4a4fb04..622c13d9d8 100644 --- a/src/ui/shortcuts.cpp +++ b/src/ui/shortcuts.cpp @@ -506,17 +506,15 @@ Shortcuts::is_user_set(Glib::ustring& action) bool Shortcuts::invoke_verb(GdkEventKey const *event, UI::View::View *view) { - std::cout << "Shortcuts::invoke_verb: " - << std::hex << event->keyval << " " - << std::hex << event->state << std::endl; + // std::cout << "Shortcuts::invoke_verb: " + // << std::hex << event->keyval << " " + // << std::hex << event->state << std::endl; Gtk::AccelKey shortcut = get_from_event(event); Verb* verb = get_verb_from_shortcut(shortcut); - g_warning("verb: %p\n", verb); if (verb) { SPAction *action = verb->get_action(Inkscape::ActionContext(view)); if (action) { - g_warning("action: %p\n", action); sp_action_perform(action, nullptr); return true; } -- GitLab From e5aa4a9452a35b81de481cb84071d4156dd78097 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Mon, 7 Jun 2021 22:09:28 -0700 Subject: [PATCH 07/13] delegated shadow to utilities --- src/display/cairo-utils.cpp | 85 ++++++++++++++++++++++++ src/display/cairo-utils.h | 2 + src/display/control/canvas-item-rect.cpp | 76 +-------------------- 3 files changed, 88 insertions(+), 75 deletions(-) diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index e20562fcba..06b03e78de 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 4d623ea98b..73184654ae 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 61fb4ef63a..97d910a26d 100644 --- a/src/display/control/canvas-item-rect.cpp +++ b/src/display/control/canvas-item-rect.cpp @@ -208,87 +208,13 @@ void CanvasItemRect::render(Inkscape::CanvasItemBuffer *buf) // Draw shadow first. Shadow extends under rectangle to reduce aliasing effects. if (_shadow_width > 0 && !_dashed) { - // draw fake drop shadow built from gradients - const auto r = SP_RGBA32_R_F(_shadow_color); - const auto g = SP_RGBA32_G_F(_shadow_color); - const auto b = SP_RGBA32_B_F(_shadow_color); // 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); - const Geom::Point corners[] = { rect.corner(0), rect.corner(1), rect.corner(2), rect.corner(3) }; - // space for gradient shadow - double sw = get_shadow_size(); - double half = sw / 2; - // 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) - buf->cr->rectangle(corners[0][X], corners[0][Y] - half, std::max(corners[1][X] - corners[0][X], 0.0), half); - buf->cr->set_source(grad_top); - buf->cr->fill(); - - // right side - buf->cr->rectangle(corners[1][X], corners[1][Y] + half, sw, std::max(corners[2][Y] - corners[1][Y] - half, 0.0)); - buf->cr->set_source(grad_right); - buf->cr->fill(); - - // bottom side - buf->cr->rectangle(corners[0][X] + half, corners[2][Y], std::max(corners[1][X] - corners[0][X] - half, 0.0), sw); - buf->cr->set_source(grad_bottom); - buf->cr->fill(); - - // left side (faint) - buf->cr->rectangle(corners[0][X] - half, corners[0][Y], half, std::max(corners[2][Y] - corners[1][Y], 0.0)); - buf->cr->set_source(grad_left); - buf->cr->fill(); - - buf->cr->rectangle(corners[2][X], corners[2][Y], sw, sw); - buf->cr->set_source(grad_btm_right); - buf->cr->fill(); - - buf->cr->rectangle(corners[1][X], corners[1][Y] - half, sw, sw); - buf->cr->set_source(grad_top_right); - buf->cr->fill(); - - buf->cr->rectangle(corners[3][X] - half, corners[3][Y], sw, sw); - buf->cr->set_source(grad_btm_left); - buf->cr->fill(); - - buf->cr->rectangle(corners[0][X] - half, corners[0][Y] - half, half, half); - buf->cr->set_source(grad_top_left); - buf->cr->fill(); + ink_cairo_draw_drop_shadow(buf->cr, rect, get_shadow_size(), _shadow_color, a); buf->cr->restore(); } -- GitLab From d77c6444caaa5e2f296e214abf0120bdec21e777 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Mon, 16 Aug 2021 21:53:44 -0700 Subject: [PATCH 08/13] adjusted border color --- src/preferences-skeleton.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 305e9e79d2..0b69536208 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.2" objecttolerance="10.0" gridtolerance="10.0" guidetolerance="10.0" -- GitLab From ab271f6455b6779b5135a209b946b89e4da212a5 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Mon, 16 Aug 2021 21:56:30 -0700 Subject: [PATCH 09/13] stronger border --- src/preferences-skeleton.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 0b69536208..50d59412c3 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -71,7 +71,7 @@ static char const preferences_skeleton[] = pageopacity="0.0" pagecheckerboard="0" bordercolor="#000000" - borderopacity="0.2" + borderopacity="0.25" objecttolerance="10.0" gridtolerance="10.0" guidetolerance="10.0" -- GitLab From be95da398b5ad718b6efe8e37b703c66a46c0d17 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 22 Aug 2021 15:23:32 -0700 Subject: [PATCH 10/13] fixed custom attribute name --- src/attributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.cpp b/src/attributes.cpp index 8afeb66e21..351deaa13e 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -565,7 +565,7 @@ static SPStyleProp const props[] = { /* LivePathEffect */ {SPAttr::PATH_EFFECT, "effect"}, - {SPAttr::BLACKOUTCOLOR, "blackoutcolor"}, + {SPAttr::BLACKOUTCOLOR, "inkscape:blackoutcolor"}, {SPAttr::INKSCAPE_BLACKOUTOPACITY, "inkscape:blackoutopacity"}, }; -- GitLab From 2451c04e992af21d9d75c440ed8dc68c6c1d5d5c Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 22 Aug 2021 15:23:53 -0700 Subject: [PATCH 11/13] custom attribute --- src/ui/dialog/document-properties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 5054d03dcf..131ca37c29 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -104,7 +104,7 @@ DocumentProperties::DocumentProperties() , _rcb_bord(_("Border on _top of drawing"), _("If set, border is always on top of the drawing"), "borderlayer", _wr, false) , _rcb_shad(_("_Show border shadow"), _("If set, page border shows a shadow on its right and lower side"), "inkscape:showpageshadow", _wr, false) , _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."), "blackoutcolor", "inkscape:blackoutopacity", _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) -- GitLab From 866b84cde291324dce930835e72f927a6eb58077 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 22 Aug 2021 16:18:00 -0700 Subject: [PATCH 12/13] attribute test fix --- testfiles/src/attributes-test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp index c2b562beed..3706cd03a0 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), -- GitLab From 04f9926547b3a83be20f24bc9090d6bc2dbc8bde Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 22 Aug 2021 18:25:24 -0700 Subject: [PATCH 13/13] attrib test changes --- src/attributes.cpp | 3 ++- src/attributes.h | 2 +- src/object/sp-namedview.cpp | 4 ++-- testfiles/src/attributes-test.cpp | 2 -- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/attributes.cpp b/src/attributes.cpp index 351deaa13e..a43cd145cf 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -565,7 +565,8 @@ static SPStyleProp const props[] = { /* LivePathEffect */ {SPAttr::PATH_EFFECT, "effect"}, - {SPAttr::BLACKOUTCOLOR, "inkscape:blackoutcolor"}, + // canvas blackout color + {SPAttr::INKSCAPE_BLACKOUTCOLOR, "inkscape:blackoutcolor"}, {SPAttr::INKSCAPE_BLACKOUTOPACITY, "inkscape:blackoutopacity"}, }; diff --git a/src/attributes.h b/src/attributes.h index d0be6ccf38..fec686afc6 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -568,7 +568,7 @@ enum class SPAttr { PATH_EFFECT, // canvas blackout color - BLACKOUTCOLOR, + INKSCAPE_BLACKOUTCOLOR, INKSCAPE_BLACKOUTOPACITY, // sentinel diff --git a/src/object/sp-namedview.cpp b/src/object/sp-namedview.cpp index a9730518f8..f99b33fb53 100644 --- a/src/object/sp-namedview.cpp +++ b/src/object/sp-namedview.cpp @@ -214,7 +214,7 @@ void SPNamedView::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr(SPAttr::BORDERCOLOR); this->readAttr(SPAttr::BORDEROPACITY); this->readAttr(SPAttr::PAGECOLOR); - this->readAttr(SPAttr::BLACKOUTCOLOR); + this->readAttr(SPAttr::INKSCAPE_BLACKOUTCOLOR); this->readAttr(SPAttr::INKSCAPE_PAGECHECKERBOARD); this->readAttr(SPAttr::INKSCAPE_PAGEOPACITY); this->readAttr(SPAttr::INKSCAPE_BLACKOUTOPACITY); @@ -404,7 +404,7 @@ void SPNamedView::set(SPAttr key, const gchar* value) { } this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; - case SPAttr::BLACKOUTCOLOR: + case SPAttr::INKSCAPE_BLACKOUTCOLOR: blackoutcolor = (blackoutcolor & 0xff) | (DEFAULTBLACKOUTCOLOR & 0xffffff00); if (value) { blackoutcolor = (blackoutcolor & 0xff) | sp_svg_read_color(value, blackoutcolor); diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp index 3706cd03a0..924a837c91 100644 --- a/testfiles/src/attributes-test.cpp +++ b/testfiles/src/attributes-test.cpp @@ -426,8 +426,6 @@ std::vector getKnownAttrs() AttributeInfo("inkscape:original-d", true), AttributeInfo("inkscape:pagecheckerboard", true), AttributeInfo("inkscape:pageopacity", true), - AttributeInfo("inkscape:blackcolor", true), - AttributeInfo("inkscape:blackoutopacity", true), AttributeInfo("inkscape:pageshadow", true), AttributeInfo("inkscape:path-effect", true), AttributeInfo("inkscape:persp3d", true), -- GitLab