2 Copyright (C) 2011-2013 Paul Davis
3 Author: Carl Hetherington <cth@carlh.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <cairomm/context.h>
22 #include "pbd/stacktrace.h"
23 #include "pbd/compose.h"
25 #include "canvas/canvas.h"
26 #include "canvas/rectangle.h"
27 #include "canvas/debug.h"
28 #include "canvas/utils.h"
31 using namespace ArdourCanvas;
33 Rectangle::Rectangle (Canvas* c)
35 , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
39 Rectangle::Rectangle (Canvas* c, Rect const & rect)
42 , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
46 Rectangle::Rectangle (Item* parent)
48 , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
52 Rectangle::Rectangle (Item* parent, Rect const & rect)
55 , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
60 Rectangle::get_self_for_render () const
62 /* In general, a Rectangle will have a _position of (0,0) within its
63 parent, and its extent is actually defined by _rect. But in the
64 unusual case that _position is set to something other than (0,0),
65 we should take that into account when rendering.
68 return item_to_window (_rect.translate (_position), false);
72 Rectangle::render_self (Rect const & area, Cairo::RefPtr<Cairo::Context> context, Rect self) const
74 boost::optional<Rect> r = self.intersection (area);
82 if (_fill && !_transparent) {
84 setup_fill_context (context);
86 setup_gradient_context (context, self, Duple (draw.x0, draw.y0));
89 context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
95 setup_outline_context (context);
97 /* the goal here is that if the border is 1 pixel
98 * thick, it will precisely align with the corner
99 * coordinates of the rectangle. So if the rectangle
100 * has a left edge at 0 and a right edge at 10, then
101 * the left edge must span 0..1, the right edge
102 * must span 9..10 because the first and final pixels
103 * to be colored are actually "at" 0.5 and 9.5 (midway
104 * between the integer coordinates.
107 if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
109 self = self.shrink (0.5);
110 context->rectangle (self.x0, self.y0, self.width(), self.height());
114 if (_outline_what & LEFT) {
115 context->move_to (self.x0+0.5, self.y0);
116 context->line_to (self.x0+0.5, self.y1);
119 if (_outline_what & TOP) {
120 context->move_to (self.x0, self.y0+0.5);
121 context->line_to (self.x1, self.y0+0.5);
124 if (_outline_what & BOTTOM) {
125 context->move_to (self.x0, self.y1-0.5);
126 context->line_to (self.x1, self.y1-0.5);
129 if (_outline_what & RIGHT) {
130 context->move_to (self.x1-0.5, self.y0);
131 context->line_to (self.x1-0.5, self.y1);
140 Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
142 render_self (area, context, get_self_for_render ());
146 Rectangle::compute_bounding_box () const
148 if (!_rect.empty()) {
149 Rect r = _rect.fix ();
151 /* if the outline is 1 pixel, then the actual
152 bounding box is 0.5 pixels outside the stated
153 corners of the rectangle.
155 if the outline is 2 pixels, then the actual
156 bounding box is 1.0 pixels outside the stated
157 corners of the rectangle (so that the middle
158 of the 2 pixel wide border passes through
159 the corners, alternatively described as 1 row
160 of pixels outside of the corners, and 1 row
163 if the outline is 3 pixels, then the actual
164 bounding box is 1.5 outside the stated corners
165 of the rectangle (so that the middle row of
166 pixels of the border passes through the corners).
168 if the outline is 4 pixels, then the actual bounding
169 box is 2.0 pixels outside the stated corners
170 of the rectangle, so that the border consists
171 of 2 pixels outside the corners and 2 pixels inside.
173 hence ... the bounding box is width * 0.5 larger
174 than the rectangle itself.
177 _bounding_box = r.expand (_outline_width * 0.5);
180 _bounding_box_dirty = false;
184 Rectangle::set (Rect const & r)
186 /* We don't update the bounding box here; it's just
187 as cheap to do it when asked.
196 _bounding_box_dirty = true;
202 Rectangle::set_x0 (Coord x0)
204 if (x0 != _rect.x0) {
209 _bounding_box_dirty = true;
215 Rectangle::set_y0 (Coord y0)
217 if (y0 != _rect.y0) {
222 _bounding_box_dirty = true;
228 Rectangle::set_x1 (Coord x1)
230 if (x1 != _rect.x1) {
235 _bounding_box_dirty = true;
241 Rectangle::set_y1 (Coord y1)
243 if (y1 != _rect.y1) {
248 _bounding_box_dirty = true;
254 Rectangle::set_outline_what (What what)
256 if (what != _outline_what) {
257 begin_visual_change ();
258 _outline_what = what;
259 end_visual_change ();
264 /*-------------------*/
267 TimeRectangle::compute_bounding_box () const
269 Rectangle::compute_bounding_box ();
272 Rect r = _bounding_box.get ();
274 /* This is a TimeRectangle, so its right edge is drawn 1 pixel beyond
275 * (larger x-axis coordinates) than a normal Rectangle.
278 r.x1 += 1.0; /* this should be using safe_add() */
285 TimeRectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
287 Rect self = get_self_for_render ();
289 /* This is a TimeRectangle, so its right edge is drawn 1 pixel beyond
290 * (larger x-axis coordinates) than a normal Rectangle.
293 self.x1 += 1.0; /* this should be using safe_add() */
294 render_self (area, context, self);