summaryrefslogtreecommitdiffstats
path: root/src/display/sodipodi-ctrlrect.cpp
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2017-03-17 09:01:27 +0000
committertavmjong-free <tavmjong@free.fr>2017-03-17 09:01:27 +0000
commita3dda4d25b4b6ed4d23e3003fa4538cb1c5df0e1 (patch)
treefbd7e8434b91b3e4343fc5876816d2e1b8fc744c /src/display/sodipodi-ctrlrect.cpp
parentRemove some compiling warns (diff)
downloadinkscape-a3dda4d25b4b6ed4d23e3003fa4538cb1c5df0e1.tar.gz
inkscape-a3dda4d25b4b6ed4d23e3003fa4538cb1c5df0e1.zip
Modifiy CtrlRect to work with rotated canvas.
(bzr r15600)
Diffstat (limited to 'src/display/sodipodi-ctrlrect.cpp')
-rw-r--r--src/display/sodipodi-ctrlrect.cpp261
1 files changed, 119 insertions, 142 deletions
diff --git a/src/display/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp
index ecc952c48..b27a81ab3 100644
--- a/src/display/sodipodi-ctrlrect.cpp
+++ b/src/display/sodipodi-ctrlrect.cpp
@@ -6,9 +6,11 @@
* bulia byak <buliabyak@users.sf.net>
* Carl Hetherington <inkscape@carlh.net>
* Jon A. Cruz <jon@joncruz.org>
+ * Tavmjong Bah <tavmjong@free.fr>
*
* Copyright (C) 1999-2001 Lauris Kaplinski
* Copyright (C) 2000-2001 Ximian, Inc.
+ * Copyright (C) 2017 Tavmjong Bah
*
* Released under GNU GPL
*
@@ -18,6 +20,7 @@
#include "sp-canvas-util.h"
#include "display/cairo-utils.h"
#include "display/sp-canvas.h"
+#include <2geom/transforms.h>
/*
* Currently we do not have point method, as it should always be painted
@@ -76,14 +79,12 @@ void CtrlRect::init()
_dashed = false;
_checkerboard = false;
- _shadow = 0;
+ _shadow_width = 0;
_area = Geom::OptIntRect();
_rect = Geom::Rect(Geom::Point(0,0),Geom::Point(0,0));
- _shadow_size = 0;
-
_border_color = 0x000000ff;
_fill_color = 0xffffffff;
_shadow_color = 0x000000ff;
@@ -98,52 +99,119 @@ void CtrlRect::render(SPCanvasBuf *buf)
if (!_area) {
return;
}
- Geom::IntRect area = *_area;
- Geom::IntRect area_w_shadow (area[X].min(), area[Y].min(),
- area[X].max() + _shadow_size, area[Y].max() + _shadow_size);
- if ( area_w_shadow.intersects(buf->rect) )
+
+ Geom::IntRect area = *_area; // _area already includes space for shadow.
+ if ( area.intersects(buf->rect) )
{
- static double const dashes[2] = {4.0, 4.0};
+
cairo_save(buf->ct);
cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top());
- cairo_set_line_width(buf->ct, 1);
- if (_dashed) cairo_set_dash(buf->ct, dashes, 2, 0);
- cairo_rectangle(buf->ct, 0.5 + area[X].min(), 0.5 + area[Y].min(),
- area[X].max() - area[X].min(), area[Y].max() - area[Y].min());
+
+ // Get canvas rotation (scale is isotropic).
+ double rotation = atan2( _affine[1], _affine[0] );
+
+ // Are we axis aligned?
+ double mod_rot = fmod(rotation * M_2_PI, 1);
+ bool axis_aligned = Geom::are_near( mod_rot, 0 ) || Geom::are_near( mod_rot, 1.0 );
+
+ // Get the points we need transformed to window coordinates.
+ Geom::Point rect_transformed[4];
+ for (unsigned i = 0; i < 4; ++i) {
+ rect_transformed[i] = _rect.corner(i) * _affine;
+ }
+
+ // Draw shadow first. Shadow extends under rectangle to reduce aliasing effects.
+ if (_shadow_width > 0 && !_dashed) {
+
+ // Offset by half stroke width (_shadow_width is in window coordinates).
+ Geom::Point shadow( _shadow_width/2.0, _shadow_width/2.0 );
+ shadow *= Geom::Rotate( rotation );
+
+ if (axis_aligned) {
+ // Snap to pixel grid (add 0.5 to center on pixel).
+ cairo_move_to( buf->ct,
+ floor(rect_transformed[0][X]+shadow[X]+0.5) + 0.5,
+ floor(rect_transformed[0][Y]+shadow[Y]+0.5) + 0.5 );
+ cairo_line_to( buf->ct,
+ floor(rect_transformed[1][X]+shadow[X]+0.5) + 0.5,
+ floor(rect_transformed[1][Y]+shadow[Y]+0.5) + 0.5 );
+ cairo_line_to( buf->ct,
+ floor(rect_transformed[2][X]+shadow[X]+0.5) + 0.5,
+ floor(rect_transformed[2][Y]+shadow[Y]+0.5) + 0.5 );
+ } else {
+ cairo_move_to( buf->ct,
+ rect_transformed[0][X]+shadow[X],
+ rect_transformed[0][Y]+shadow[Y] );
+ cairo_line_to( buf->ct,
+ rect_transformed[1][X]+shadow[X],
+ rect_transformed[1][Y]+shadow[Y] );
+ cairo_line_to( buf->ct,
+ rect_transformed[2][X]+shadow[X],
+ rect_transformed[2][Y]+shadow[Y] );
+ }
+
+ ink_cairo_set_source_rgba32( buf->ct, _shadow_color );
+ cairo_set_line_width( buf->ct, _shadow_width + 1 );
+ cairo_stroke( buf->ct );
+ }
+
+ // Setup rectangle path
+ if (axis_aligned) {
+
+ // Snap to pixel grid
+ Geom::Rect outline( _rect.min() * _affine, _rect.max() * _affine);
+ // Check if there is a simpler way to go from 'outline' to 'int_rect'.
+ Geom::IntRect int_rect ( (int) floor(outline.min()[X] + 0.5),
+ (int) floor(outline.min()[Y] + 0.5),
+ (int) floor(outline.max()[X] + 0.5),
+ (int) floor(outline.max()[Y] + 0.5) );
+ cairo_rectangle (buf->ct,
+ 0.5 + int_rect[X].min(),
+ 0.5 + int_rect[Y].min(),
+ int_rect[X].max() - int_rect[X].min(),
+ int_rect[Y].max() - int_rect[Y].min() );
+ } else {
+
+ // Angled
+ cairo_move_to( buf->ct, rect_transformed[0][X], rect_transformed[0][Y] );
+ cairo_line_to( buf->ct, rect_transformed[1][X], rect_transformed[1][Y] );
+ cairo_line_to( buf->ct, rect_transformed[2][X], rect_transformed[2][Y] );
+ cairo_line_to( buf->ct, rect_transformed[3][X], rect_transformed[3][Y] );
+ cairo_close_path( buf->ct );
+ }
+
+ // This doesn't seem to be used anywhere. If it is, then we should
+ // probably rotate the coordinate system and fill using a cairo_rectangle().
if (_checkerboard) {
cairo_pattern_t *cb = ink_cairo_pattern_create_checkerboard();
cairo_set_source(buf->ct, cb);
cairo_pattern_destroy(cb);
cairo_fill_preserve(buf->ct);
}
+
if (_has_fill) {
ink_cairo_set_source_rgba32(buf->ct, _fill_color);
cairo_fill_preserve(buf->ct);
}
+ // Set up stroke.
ink_cairo_set_source_rgba32(buf->ct, _border_color);
- cairo_stroke(buf->ct);
+ cairo_set_line_width(buf->ct, 1);
+ static double const dashes[2] = {4.0, 4.0};
+ if (_dashed) cairo_set_dash(buf->ct, dashes, 2, 0);
- if (_shadow_size == 1) { // highlight the border by drawing it in _shadow_color
- if (_dashed) {
- cairo_set_dash(buf->ct, dashes, 2, 4);
- cairo_rectangle(buf->ct, 0.5 + area[X].min(), 0.5 + area[Y].min(),
- area[X].max() - area[X].min(), area[Y].max() - area[Y].min());
- } else {
- cairo_rectangle(buf->ct, -0.5 + area[X].min(), -0.5 + area[Y].min(),
- area[X].max() - area[X].min(), area[Y].max() - area[Y].min());
- }
- ink_cairo_set_source_rgba32(buf->ct, _shadow_color);
- cairo_stroke(buf->ct);
- } else if (_shadow_size > 1) { // fill the shadow
+ // Stroke rectangle.
+ cairo_stroke_preserve(buf->ct);
+
+ // Highlight the border by drawing it in _shadow_color.
+ if (_shadow_width == 1 && _dashed) {
ink_cairo_set_source_rgba32(buf->ct, _shadow_color);
- cairo_rectangle(buf->ct, 1 + area[X].max(), area[Y].min() + _shadow_size,
- _shadow_size, area[Y].max() - area[Y].min() + 1); // right shadow
- cairo_rectangle(buf->ct, area[X].min() + _shadow_size, 1 + area[Y].max(),
- area[X].max() - area[X].min() - _shadow_size + 1, _shadow_size);
- cairo_fill(buf->ct);
+ cairo_set_dash(buf->ct, dashes, 2, 4); // Dash offset by dash length.
+ cairo_stroke_preserve(buf->ct);
}
+
+ cairo_new_path( buf->ct ); // Clear path or get wierd artifacts.
cairo_restore(buf->ct);
}
}
@@ -158,124 +226,33 @@ void CtrlRect::update(Geom::Affine const &affine, unsigned int flags)
(SP_CANVAS_ITEM_CLASS(sp_ctrlrect_parent_class))->update(this, affine, flags);
}
- sp_canvas_item_reset_bounds(this);
+ // Note: There is no harm if 'area' is too large other than a possible small slow down in
+ // rendering speed.
- Geom::Rect bbox(_rect.min() * affine, _rect.max() * affine);
+ // Calculate an axis-aligned bounding box that include all of transformed _rect.
+ Geom::Rect bbox = _rect;
+ bbox *= affine;
- Geom::OptIntRect _area_old = _area;
- Geom::IntRect area ( (int) floor(bbox.min()[Geom::X] + 0.5),
- (int) floor(bbox.min()[Geom::Y] + 0.5),
- (int) floor(bbox.max()[Geom::X] + 0.5),
- (int) floor(bbox.max()[Geom::Y] + 0.5) );
- _area = area;
- Geom::IntRect area_old(0,0,0,0);
- if (_area_old) { // this weird construction is because the code below assumes _area_old to be 'valid'
- area_old = *_area_old;
- }
+ // Enlarge bbox by twice shadow size (to allow for shadow on any side with a 45deg rotation).
+ bbox.expandBy( 2.0 *_shadow_width );
- gint _shadow_size_old = _shadow_size;
- _shadow_size = _shadow;
-
- // FIXME: we don't process a possible change in _has_fill
- if (_has_fill) {
- if (_area_old) {
- canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].min() - 1,
- area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1);
- }
- if (_area) {
- canvas->requestRedraw(area[X].min() - 1, area[Y].min() - 1,
- area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1);
- }
- } else { // clear box, be smart about what part of the frame to redraw
-
- /* Top */
- if (area[Y].min() != area_old[Y].min()) { // different level, redraw fully old and new
- if (area_old[X].min() != area_old[X].max())
- canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].min() - 1,
- area_old[X].max() + 1, area_old[Y].min() + 1);
-
- if (area[X].min() != area[X].max())
- canvas->requestRedraw(area[X].min() - 1, area[Y].min() - 1,
- area[X].max() + 1, area[Y].min() + 1);
- } else { // same level, redraw only the ends
- if (area[X].min() != area_old[X].min()) {
- canvas->requestRedraw(MIN(area_old[X].min(),area[X].min()) - 1, area[Y].min() - 1,
- MAX(area_old[X].min(),area[X].min()) + 1, area[Y].min() + 1);
- }
- if (area[X].max() != area_old[X].max()) {
- canvas->requestRedraw(MIN(area_old[X].max(),area[X].max()) - 1, area[Y].min() - 1,
- MAX(area_old[X].max(),area[X].max()) + 1, area[Y].min() + 1);
- }
- }
-
- /* Left */
- if (area[X].min() != area_old[X].min()) { // different level, redraw fully old and new
- if (area_old[Y].min() != area_old[Y].max())
- canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].min() - 1,
- area_old[X].min() + 1, area_old[Y].max() + 1);
-
- if (area[Y].min() != area[Y].max())
- canvas->requestRedraw(area[X].min() - 1, area[Y].min() - 1,
- area[X].min() + 1, area[Y].max() + 1);
- } else { // same level, redraw only the ends
- if (area[Y].min() != area_old[Y].min()) {
- canvas->requestRedraw(area[X].min() - 1, MIN(area_old[Y].min(),area[Y].min()) - 1,
- area[X].min() + 1, MAX(area_old[Y].min(),area[Y].min()) + 1);
- }
- if (area[Y].max() != area_old[Y].max()) {
- canvas->requestRedraw(area[X].min() - 1, MIN(area_old[Y].max(),area[Y].max()) - 1,
- area[X].min() + 1, MAX(area_old[Y].max(),area[Y].max()) + 1);
- }
- }
-
- /* Right */
- if (area[X].max() != area_old[X].max() || _shadow_size_old != _shadow_size) {
- if (area_old[Y].min() != area_old[Y].max())
- canvas->requestRedraw(area_old[X].max() - 1, area_old[Y].min() - 1,
- area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1);
-
- if (area[Y].min() != area[Y].max())
- canvas->requestRedraw(area[X].max() - 1, area[Y].min() - 1,
- area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1);
- } else { // same level, redraw only the ends
- if (area[Y].min() != area_old[Y].min()) {
- canvas->requestRedraw(area[X].max() - 1, MIN(area_old[Y].min(),area[Y].min()) - 1,
- area[X].max() + _shadow_size + 1, MAX(area_old[Y].min(),area[Y].min()) + _shadow_size + 1);
- }
- if (area[Y].max() != area_old[Y].max()) {
- canvas->requestRedraw(area[X].max() - 1, MIN(area_old[Y].max(),area[Y].max()) - 1,
- area[X].max() + _shadow_size + 1, MAX(area_old[Y].max(),area[Y].max()) + _shadow_size + 1);
- }
- }
+ // Generate an integer rectangle that includes bbox.
+ Geom::OptIntRect _area_old = _area;
+ _area = bbox.roundOutwards();
- /* Bottom */
- if (area[Y].max() != area_old[Y].max() || _shadow_size_old != _shadow_size) {
- if (area_old[X].min() != area_old[X].max())
- canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].max() - 1,
- area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1);
-
- if (area[X].min() != area[X].max())
- canvas->requestRedraw(area[X].min() - 1, area[Y].max() - 1,
- area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1);
- } else { // same level, redraw only the ends
- if (area[X].min() != area_old[X].min()) {
- canvas->requestRedraw(MIN(area_old[X].min(),area[X].min()) - 1, area[Y].max() - 1,
- MAX(area_old[X].min(),area[X].min()) + _shadow_size + 1, area[Y].max() + _shadow_size + 1);
- }
- if (area[X].max() != area_old[X].max()) {
- canvas->requestRedraw(MIN(area_old[X].max(),area[X].max()) - 1, area[Y].max() - 1,
- MAX(area_old[X].max(),area[X].max()) + _shadow_size + 1, area[Y].max() + _shadow_size + 1);
- }
- }
- }
+ // std::cout << " _rect: " << _rect << std::endl;
+ // std::cout << " bbox: " << bbox << std::endl;
+ // std::cout << " area: " << *_area << std::endl;
- // update SPCanvasItem box
if (_area) {
- x1 = area[X].min() - 1;
- y1 = area[Y].min() - 1;
- x2 = area[X].max() + _shadow_size + 1;
- y2 = area[Y].max() + _shadow_size + 1;
+ Geom::IntRect area = *_area;
+ sp_canvas_update_bbox( this, area[X].min(), area[Y].min(), area[X].max(), area[Y].max() );
+ } else {
+ std::cerr << "CtrlRect::update: No area!!" << std::endl;
}
+
+ // At rendering stage, we need to know affine:
+ _affine = affine;
}
@@ -289,7 +266,7 @@ void CtrlRect::setColor(guint32 b, bool h, guint f)
void CtrlRect::setShadow(int s, guint c)
{
- _shadow = s;
+ _shadow_width = s;
_shadow_color = c;
_requestUpdate();
}