X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fcanvas%2Frectangle.cc;h=c9edc0b0d8a8b2e825b6f790b0049ef531b69a5f;hb=cec5ce8f4520c6bf066d1f521f26316997fedd53;hp=d5808b9bb0e26cce35d934ed2a109c6cb5ee6884;hpb=9aacefc17010a889222425f97b99050171165038;p=ardour.git diff --git a/libs/canvas/rectangle.cc b/libs/canvas/rectangle.cc index d5808b9bb0..c9edc0b0d8 100644 --- a/libs/canvas/rectangle.cc +++ b/libs/canvas/rectangle.cc @@ -1,21 +1,22 @@ /* - Copyright (C) 2011-2013 Paul Davis - Author: Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * Copyright (C) 2012 Carl Hetherington + * Copyright (C) 2013-2018 Paul Davis + * Copyright (C) 2014-2017 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #include #include @@ -25,43 +26,53 @@ #include "canvas/canvas.h" #include "canvas/rectangle.h" #include "canvas/debug.h" -#include "canvas/utils.h" using namespace std; using namespace ArdourCanvas; -Rectangle::Rectangle (Group* parent) +Rectangle::Rectangle (Canvas* c) + : Item (c) + , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM)) +{ +} + +Rectangle::Rectangle (Canvas* c, Rect const & rect) + : Item (c) + , _rect (rect) + , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM)) +{ +} + +Rectangle::Rectangle (Item* parent) : Item (parent) - , Outline (parent) - , Fill (parent) , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM)) { } -Rectangle::Rectangle (Group* parent, Rect const & rect) +Rectangle::Rectangle (Item* parent, Rect const & rect) : Item (parent) - , Outline (parent) - , Fill (parent) , _rect (rect) , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM)) { - } void Rectangle::render (Rect const & area, Cairo::RefPtr context) const { - Rect self = item_to_window (_rect); - boost::optional r = self.intersection (area); + /* In general, a Rectangle will have a _position of (0,0) within its + parent, and its extent is actually defined by _rect. But in the + unusual case that _position is set to something other than (0,0), + we should take that into account when rendering. + */ + + Rect self (item_to_window (_rect.translate (_position), false)); + const Rect draw = self.intersection (area); - if (!r) { - std::cerr << whatami() << '/' << name << " not covered by render area! ... " << self << " vs. " << area << std::endl; + if (!draw) { return; } - Rect draw = r.get (); - - if (_fill) { + if (_fill && !_transparent) { if (_stops.empty()) { setup_fill_context (context); } else { @@ -70,55 +81,57 @@ Rectangle::render (Rect const & area, Cairo::RefPtr context) con context->rectangle (draw.x0, draw.y0, draw.width(), draw.height()); context->fill (); - } - - if (_outline) { + } + + if (_outline && _outline_width && _outline_what) { setup_outline_context (context); - + + /* the goal here is that if the border is 1 pixel + * thick, it will precisely align with the corner + * coordinates of the rectangle. So if the rectangle + * has a left edge at 0 and a right edge at 10, then + * the left edge must span 0..1, the right edge + * must span 10..11 because the first and final pixels + * to be colored are actually "at" 0.5 and 10.5 (midway + * between the integer coordinates). + * + * See the Cairo FAQ on single pixel lines for more + * detail. + */ + + if (fmod (_outline_width, 2.0) != 0.0) { + const double shift = _outline_width * 0.5; + self = self.translate (Duple (shift, shift)); + } + if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) { - - /* outline must be on pixels (hence 0.5 offset) and - must be WITHIN coordinates of rect, not outside it - (hence the -2.0 size adjustment, since we use 1 - pixel on each side for the outline) - */ - context->rectangle (self.x0 + 0.5, self.y0 + 0.5, self.width() - 1.0, self.height() - 1.0); + context->rectangle (self.x0, self.y0, self.width(), self.height()); } else { - // context->set_line_cap (Cairo::LINE_CAP_SQUARE); - - /* see the cairo FAQ on single pixel lines to see why we do - * this expansion of the perimeter. - */ - if (_outline_what & LEFT) { - /* vertical line: move x-coordinate by 0.5 pixels */ - context->move_to (self.x0 + 0.5, self.y0); - context->line_to (self.x0 + 0.5, self.y1); + context->move_to (self.x0, self.y0); + context->line_to (self.x0, self.y1); } - + + if (_outline_what & TOP) { + context->move_to (self.x0, self.y0); + context->line_to (self.x1, self.y0); + } + if (_outline_what & BOTTOM) { - /* horizontal line: move y-coordinate by 0.5 pixels */ - context->move_to (self.x0, self.y1 - 0.5); - context->line_to (self.x1, self.y1 - 0.5); + context->move_to (self.x0, self.y1); + context->line_to (self.x1, self.y1); } - + if (_outline_what & RIGHT) { - /* vertical line: move x-coordinate by 0.5 pixels */ - context->move_to (self.x1 - 0.5, self.y0); - context->line_to (self.x1 - 0.5, self.y1); - } - - if (_outline_what & TOP) { - /* horizontal line: move y-coordinate by 0.5 pixels */ - context->move_to (self.x0, self.y0 + 0.5); - context->line_to (self.x1, self.y0 + 0.5); + context->move_to (self.x1, self.y0); + context->line_to (self.x1, self.y1); } } - + context->stroke (); } } @@ -129,14 +142,33 @@ Rectangle::compute_bounding_box () const if (!_rect.empty()) { Rect r = _rect.fix (); - /* our outlines are always inside our coordinates, but we have - * to ensure that our bounding box fully *contains* the - * rectangle - * - * XXX: or something like that, waffle. - * - */ - _bounding_box = _rect.fix (); + /* if the outline is 1 pixel, then the actual + bounding box is 0.5 pixels outside the stated + corners of the rectangle. + + if the outline is 2 pixels, then the actual + bounding box is 1.0 pixels outside the stated + corners of the rectangle (so that the middle + of the 2 pixel wide border passes through + the corners, alternatively described as 1 row + of pixels outside of the corners, and 1 row + inside). + + if the outline is 3 pixels, then the actual + bounding box is 1.5 outside the stated corners + of the rectangle (so that the middle row of + pixels of the border passes through the corners). + + if the outline is 4 pixels, then the actual bounding + box is 2.0 pixels outside the stated corners + of the rectangle, so that the border consists + of 2 pixels outside the corners and 2 pixels inside. + + hence ... the bounding box is width * 0.5 larger + than the rectangle itself. + */ + + _bounding_box = r.expand (1.0 + _outline_width * 0.5); } _bounding_box_dirty = false; @@ -148,82 +180,99 @@ Rectangle::set (Rect const & r) /* We don't update the bounding box here; it's just as cheap to do it when asked. */ - - begin_change (); - - _rect = r; - - _bounding_box_dirty = true; - end_change (); - - DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (set)\n"); + + if (r != _rect) { + + begin_change (); + + _rect = r; + + _bounding_box_dirty = true; + end_change (); + } } void Rectangle::set_x0 (Coord x0) { - begin_change (); - - _rect.x0 = x0; + if (x0 != _rect.x0) { + begin_change (); - _bounding_box_dirty = true; - end_change (); + _rect.x0 = x0; - DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x0)\n"); + _bounding_box_dirty = true; + end_change (); + } } void Rectangle::set_y0 (Coord y0) { - begin_change (); - - _rect.y0 = y0; + if (y0 != _rect.y0) { + begin_change (); - _bounding_box_dirty = true; - end_change(); + _rect.y0 = y0; - DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y0)\n"); + _bounding_box_dirty = true; + end_change(); + } } void Rectangle::set_x1 (Coord x1) { - begin_change (); - - _rect.x1 = x1; + if (x1 != _rect.x1) { + begin_change (); - _bounding_box_dirty = true; - end_change (); + _rect.x1 = x1; - DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x1)\n"); + _bounding_box_dirty = true; + end_change (); + } } void Rectangle::set_y1 (Coord y1) { - begin_change (); + if (y1 != _rect.y1) { + begin_change (); - _rect.y1 = y1; + _rect.y1 = y1; - _bounding_box_dirty = true; - end_change (); - - DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y1)\n"); + _bounding_box_dirty = true; + end_change (); + } } void Rectangle::set_outline_what (What what) { - begin_change (); - - _outline_what = what; - - end_change (); + if (what != _outline_what) { + begin_visual_change (); + _outline_what = what; + end_visual_change (); + } } -void -Rectangle::set_outline_what (int what) +double +Rectangle::vertical_fraction (double y) const { - set_outline_what ((What) what); -} + /* y is in canvas coordinates */ + + Duple i (canvas_to_item (Duple (0, y))); + Rect r = bounding_box(); + if (!r) { + return 0; /* not really correct, but what else can we do? */ + } + + Rect bbox (r); + if (i.y < bbox.y0 || i.y >= bbox.y1) { + return 0; + } + + /* convert to fit Cairo origin model (origin at upper left) + */ + + return 1.0 - ((i.y - bbox.y0) / bbox.height()); +}