Implemented using the transformation in Painter (incomplete)

* Also removed wonky BeOS rendering of stroked round rects and ellipses.
   Nobody would expect it to work like this. This affects stroked round rects
   and ellipsis with a pensize greater than 1.
 * Refactored common code from _StrokePath() and _FillPath().
 * _StrokePath() returned a curious bounding box that didn't take into
   acount the miter width. Now the bounding box is computed from the actual
   stroke conversion of the path.
This commit is contained in:
Stephan Aßmus
2014-02-04 23:52:32 +01:00
parent 2e7da8455a
commit 63a30a4744
3 changed files with 89 additions and 196 deletions
+75 -192
View File
@@ -272,9 +272,9 @@ Painter::Bounds() const
// SetDrawState // SetDrawState
void void
Painter::SetDrawState(const DrawState* data, int32 xOffset, int32 yOffset) Painter::SetDrawState(const DrawState* state, int32 xOffset, int32 yOffset)
{ {
// NOTE: The custom clipping in "data" is ignored, because it has already // NOTE: The custom clipping in "state" is ignored, because it has already
// been taken into account elsewhere // been taken into account elsewhere
// NOTE: Usually this function is only used when the "current view" // NOTE: Usually this function is only used when the "current view"
@@ -282,40 +282,41 @@ Painter::SetDrawState(const DrawState* data, int32 xOffset, int32 yOffset)
// and messed up the state. For other graphics state changes, the // and messed up the state. For other graphics state changes, the
// Painter methods are used directly, so this function is much less // Painter methods are used directly, so this function is much less
// speed critical than it used to be. // speed critical than it used to be.
SetTransform(state->CombinedTransform(), xOffset, yOffset);
SetPenSize(data->PenSize()); SetPenSize(state->PenSize());
SetFont(data); SetFont(state);
fSubpixelPrecise = data->SubPixelPrecise(); fSubpixelPrecise = state->SubPixelPrecise();
if (data->GetAlphaMask() != NULL) if (state->GetAlphaMask() != NULL)
fMaskedUnpackedScanline = data->GetAlphaMask()->Generate(); fMaskedUnpackedScanline = state->GetAlphaMask()->Generate();
else else
fMaskedUnpackedScanline = NULL; fMaskedUnpackedScanline = NULL;
// any of these conditions means we need to use a different drawing // any of these conditions means we need to use a different drawing
// mode instance // mode instance
bool updateDrawingMode bool updateDrawingMode
= !(data->GetPattern() == fPatternHandler.GetPattern()) = !(state->GetPattern() == fPatternHandler.GetPattern())
|| data->GetDrawingMode() != fDrawingMode || state->GetDrawingMode() != fDrawingMode
|| (data->GetDrawingMode() == B_OP_ALPHA || (state->GetDrawingMode() == B_OP_ALPHA
&& (data->AlphaSrcMode() != fAlphaSrcMode && (state->AlphaSrcMode() != fAlphaSrcMode
|| data->AlphaFncMode() != fAlphaFncMode)); || state->AlphaFncMode() != fAlphaFncMode));
fDrawingMode = data->GetDrawingMode(); fDrawingMode = state->GetDrawingMode();
fAlphaSrcMode = data->AlphaSrcMode(); fAlphaSrcMode = state->AlphaSrcMode();
fAlphaFncMode = data->AlphaFncMode(); fAlphaFncMode = state->AlphaFncMode();
fPatternHandler.SetPattern(data->GetPattern()); fPatternHandler.SetPattern(state->GetPattern());
fPatternHandler.SetOffsets(xOffset, yOffset); fPatternHandler.SetOffsets(xOffset, yOffset);
fLineCapMode = data->LineCapMode(); fLineCapMode = state->LineCapMode();
fLineJoinMode = data->LineJoinMode(); fLineJoinMode = state->LineJoinMode();
fMiterLimit = data->MiterLimit(); fMiterLimit = state->MiterLimit();
// adopt the color *after* the pattern is set // adopt the color *after* the pattern is set
// to set the renderers to the correct color // to set the renderers to the correct color
SetHighColor(data->HighColor()); SetHighColor(state->HighColor());
SetLowColor(data->LowColor()); SetLowColor(state->LowColor());
if (updateDrawingMode || fPixelFormat.UsesOpCopyForText()) if (updateDrawingMode || fPixelFormat.UsesOpCopyForText())
_UpdateDrawingMode(); _UpdateDrawingMode();
@@ -341,6 +342,20 @@ Painter::ConstrainClipping(const BRegion* region)
} }
void
Painter::SetTransform(BAffineTransform transform, int32 xOffset, int32 yOffset)
{
fIdentityTransform = transform.IsIdentity();
if (!fIdentityTransform) {
fTransform = agg::trans_affine(transform.sx, transform.shy,
transform.shx, transform.sy, transform.tx/* + xOffset*/,
transform.ty/* + yOffset*/);
} else {
fTransform.reset();
}
}
// SetHighColor // SetHighColor
void void
Painter::SetHighColor(const rgb_color& color) Painter::SetHighColor(const rgb_color& color)
@@ -460,7 +475,7 @@ Painter::StrokeLine(BPoint a, BPoint b)
_Transform(&b, false); _Transform(&b, false);
// first, try an optimized version // first, try an optimized version
if (fPenSize == 1.0 if (fPenSize == 1.0 && fIdentityTransform
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
&& fMaskedUnpackedScanline == NULL) { && fMaskedUnpackedScanline == NULL) {
pattern pat = *fPatternHandler.GetR5Pattern(); pattern pat = *fPatternHandler.GetR5Pattern();
@@ -477,7 +492,7 @@ Painter::StrokeLine(BPoint a, BPoint b)
if (a == b) { if (a == b) {
// special case dots // special case dots
if (fPenSize == 1.0 && !fSubpixelPrecise) { if (fPenSize == 1.0 && !fSubpixelPrecise && fIdentityTransform) {
if (fClippingRegion->Contains(a)) { if (fClippingRegion->Contains(a)) {
fPixelFormat.blend_pixel((int)a.x, (int)a.y, fRenderer.color(), fPixelFormat.blend_pixel((int)a.x, (int)a.y, fRenderer.color(),
255); 255);
@@ -495,7 +510,7 @@ Painter::StrokeLine(BPoint a, BPoint b)
// tweak ends to "include" the pixel at the index, // tweak ends to "include" the pixel at the index,
// we need to do this in order to produce results like R5, // we need to do this in order to produce results like R5,
// where coordinates were inclusive // where coordinates were inclusive
if (!fSubpixelPrecise) { if (!fSubpixelPrecise && fIdentityTransform) {
bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0; bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0;
if (a.x == b.x) { if (a.x == b.x) {
// shift to pixel center vertically // shift to pixel center vertically
@@ -870,7 +885,7 @@ Painter::StrokeRect(const BRect& r) const
_Transform(&b, false); _Transform(&b, false);
// first, try an optimized version // first, try an optimized version
if (fPenSize == 1.0 if (fPenSize == 1.0 && fIdentityTransform
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
&& fMaskedUnpackedScanline == NULL) { && fMaskedUnpackedScanline == NULL) {
pattern p = *fPatternHandler.GetR5Pattern(); pattern p = *fPatternHandler.GetR5Pattern();
@@ -885,7 +900,7 @@ Painter::StrokeRect(const BRect& r) const
} }
} }
if (fmodf(fPenSize, 2.0) != 0.0) { if (fIdentityTransform && fmodf(fPenSize, 2.0) != 0.0) {
// shift coords to center of pixels // shift coords to center of pixels
a.x += 0.5; a.x += 0.5;
a.y += 0.5; a.y += 0.5;
@@ -934,7 +949,7 @@ Painter::FillRect(const BRect& r) const
// first, try an optimized version // first, try an optimized version
if ((fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) if ((fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
&& fMaskedUnpackedScanline == NULL) { && fMaskedUnpackedScanline == NULL && fIdentityTransform) {
pattern p = *fPatternHandler.GetR5Pattern(); pattern p = *fPatternHandler.GetR5Pattern();
if (p == B_SOLID_HIGH) { if (p == B_SOLID_HIGH) {
BRect rect(a, b); BRect rect(a, b);
@@ -946,7 +961,8 @@ Painter::FillRect(const BRect& r) const
return _Clipped(rect); return _Clipped(rect);
} }
} }
if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY) { if (fDrawingMode == B_OP_ALPHA && fAlphaFncMode == B_ALPHA_OVERLAY
&& fMaskedUnpackedScanline == NULL && fIdentityTransform) {
pattern p = *fPatternHandler.GetR5Pattern(); pattern p = *fPatternHandler.GetR5Pattern();
if (p == B_SOLID_HIGH) { if (p == B_SOLID_HIGH) {
BRect rect(a, b); BRect rect(a, b);
@@ -994,7 +1010,7 @@ Painter::FillRect(const BRect& r, const BGradient& gradient) const
// first, try an optimized version // first, try an optimized version
if (gradient.GetType() == BGradient::TYPE_LINEAR if (gradient.GetType() == BGradient::TYPE_LINEAR
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER) && (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
&& fMaskedUnpackedScanline == NULL) { && fMaskedUnpackedScanline == NULL && fIdentityTransform) {
const BGradientLinear* linearGradient const BGradientLinear* linearGradient
= dynamic_cast<const BGradientLinear*>(&gradient); = dynamic_cast<const BGradientLinear*>(&gradient);
if (linearGradient->Start().x == linearGradient->End().x if (linearGradient->Start().x == linearGradient->End().x
@@ -1067,7 +1083,8 @@ Painter::FillRect(const BRect& r, const rgb_color& c) const
// FillRectVerticalGradient // FillRectVerticalGradient
void void
Painter::FillRectVerticalGradient(BRect r, const BGradientLinear& gradient) const Painter::FillRectVerticalGradient(BRect r,
const BGradientLinear& gradient) const
{ {
if (!fValidClipping) if (!fValidClipping)
return; return;
@@ -1153,97 +1170,15 @@ Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
BPoint lt(r.left, r.top); BPoint lt(r.left, r.top);
BPoint rb(r.right, r.bottom); BPoint rb(r.right, r.bottom);
bool centerOffset = fPenSize == 1.0; bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
// TODO: use this when using _StrokePath()
// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
_Transform(&lt, centerOffset); _Transform(&lt, centerOffset);
_Transform(&rb, centerOffset); _Transform(&rb, centerOffset);
if (fPenSize == 1.0) { agg::rounded_rect rect;
agg::rounded_rect rect; rect.rect(lt.x, lt.y, rb.x, rb.y);
rect.rect(lt.x, lt.y, rb.x, rb.y); rect.radius(xRadius, yRadius);
rect.radius(xRadius, yRadius);
return _StrokePath(rect); return _StrokePath(rect);
}
// NOTE: This implementation might seem a little strange, but it makes
// stroked round rects look like on R5. A more correct way would be to
// use _StrokePath() as above (independent from fPenSize).
// The fact that the bounding box of the round rect is not enlarged
// by fPenSize/2 is actually on purpose, though one could argue it is
// unexpected.
// enclose the right and bottom edge
rb.x++;
rb.y++;
agg::rounded_rect outer;
outer.rect(lt.x, lt.y, rb.x, rb.y);
outer.radius(xRadius, yRadius);
if (gSubpixelAntialiasing) {
fSubpixRasterizer.reset();
fSubpixRasterizer.add_path(outer);
// don't add an inner hole if the "size is negative", this avoids
// some defects that can be observed on R5 and could be regarded
// as a bug.
if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
agg::rounded_rect inner;
inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize,
rb.y - fPenSize);
inner.radius(max_c(0.0, xRadius - fPenSize),
max_c(0.0, yRadius - fPenSize));
fSubpixRasterizer.add_path(inner);
}
// make the inner rect work as a hole
fSubpixRasterizer.filling_rule(agg::fill_even_odd);
if (fPenSize > 2) {
agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline,
fSubpixRenderer);
} else {
agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline,
fSubpixRenderer);
}
fSubpixRasterizer.filling_rule(agg::fill_non_zero);
} else {
fRasterizer.reset();
fRasterizer.add_path(outer);
// don't add an inner hole if the "size is negative", this avoids
// some defects that can be observed on R5 and could be regarded as
// a bug.
if (2 * fPenSize < rb.x - lt.x && 2 * fPenSize < rb.y - lt.y) {
agg::rounded_rect inner;
inner.rect(lt.x + fPenSize, lt.y + fPenSize, rb.x - fPenSize,
rb.y - fPenSize);
inner.radius(max_c(0.0, xRadius - fPenSize),
max_c(0.0, yRadius - fPenSize));
fRasterizer.add_path(inner);
}
// make the inner rect work as a hole
fRasterizer.filling_rule(agg::fill_even_odd);
if (fMaskedUnpackedScanline != NULL) {
agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline,
fRenderer);
} else if (fPenSize > 2)
agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
else
agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
// reset to default
fRasterizer.filling_rule(agg::fill_non_zero);
}
return _Clipped(_BoundingBox(outer));
} }
@@ -1334,61 +1269,12 @@ Painter::DrawEllipse(BRect r, bool fill) const
if (divisions > 4096) if (divisions > 4096)
divisions = 4096; divisions = 4096;
if (fill) { agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
agg::ellipse path(center.x, center.y, xRadius, yRadius, divisions);
if (fill)
return _FillPath(path); return _FillPath(path);
} else
return _StrokePath(path);
// NOTE: This implementation might seem a little strange, but it makes
// stroked ellipses look like on R5. A more correct way would be to use
// _StrokePath(), but it currently has its own set of problems with
// narrow ellipses (for small xRadii or yRadii).
float inset = fPenSize / 2.0;
agg::ellipse inner(center.x, center.y, max_c(0.0, xRadius - inset),
max_c(0.0, yRadius - inset), divisions);
agg::ellipse outer(center.x, center.y, xRadius + inset, yRadius + inset,
divisions);
if (gSubpixelAntialiasing) {
fSubpixRasterizer.reset();
fSubpixRasterizer.add_path(outer);
fSubpixRasterizer.add_path(inner);
// make the inner ellipse work as a hole
fSubpixRasterizer.filling_rule(agg::fill_even_odd);
if (fPenSize > 4) {
agg::render_scanlines(fSubpixRasterizer, fSubpixPackedScanline,
fSubpixRenderer);
} else {
agg::render_scanlines(fSubpixRasterizer, fSubpixUnpackedScanline,
fSubpixRenderer);
}
// reset to default
fSubpixRasterizer.filling_rule(agg::fill_non_zero);
} else {
fRasterizer.reset();
fRasterizer.add_path(outer);
fRasterizer.add_path(inner);
// make the inner ellipse work as a hole
fRasterizer.filling_rule(agg::fill_even_odd);
if (fMaskedUnpackedScanline != NULL) {
agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline,
fRenderer);
} else if (fPenSize > 4)
agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
else
agg::render_scanlines(fRasterizer, fUnpackedScanline, fRenderer);
// reset to default
fRasterizer.filling_rule(agg::fill_non_zero);
}
return _Clipped(_BoundingBox(outer));
} }
@@ -2908,30 +2794,14 @@ Painter::_StrokePath(VertexSource& path) const
stroke.line_join(agg_line_join_mode_for(fLineJoinMode)); stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
stroke.miter_limit(fMiterLimit); stroke.miter_limit(fMiterLimit);
if (fMaskedUnpackedScanline != NULL) { if (fIdentityTransform)
// TODO: we can't do both alpha-masking and subpixel AA. return _RasterizePath(stroke);
fRasterizer.reset();
fRasterizer.add_path(path);
agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline,
fRenderer);
} else if (gSubpixelAntialiasing) {
fSubpixRasterizer.reset();
fSubpixRasterizer.add_path(stroke);
agg::render_scanlines(fSubpixRasterizer, stroke.approximation_scale(fTransform.scale());
fSubpixPackedScanline, fSubpixRenderer);
} else {
fRasterizer.reset();
fRasterizer.add_path(stroke);
agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer); agg::conv_transform<agg::conv_stroke<VertexSource> > transformedStroke(
} stroke, fTransform);
return _RasterizePath(transformedStroke);
BRect touched = _BoundingBox(path);
float penSize = ceilf(fPenSize / 2.0);
touched.InsetBy(-penSize, -penSize);
return _Clipped(touched);
} }
@@ -2939,6 +2809,19 @@ Painter::_StrokePath(VertexSource& path) const
template<class VertexSource> template<class VertexSource>
BRect BRect
Painter::_FillPath(VertexSource& path) const Painter::_FillPath(VertexSource& path) const
{
if (fIdentityTransform)
return _RasterizePath(path);
agg::conv_transform<VertexSource> transformedPath(path, fTransform);
return _RasterizePath(transformedPath);
}
// _RasterizePath
template<class VertexSource>
BRect
Painter::_RasterizePath(VertexSource& path) const
{ {
if (fMaskedUnpackedScanline != NULL) { if (fMaskedUnpackedScanline != NULL) {
// TODO: we can't do both alpha-masking and subpixel AA. // TODO: we can't do both alpha-masking and subpixel AA.
+13 -4
View File
@@ -21,6 +21,7 @@
#include <agg_conv_curve.h> #include <agg_conv_curve.h>
#include <agg_path_storage.h> #include <agg_path_storage.h>
#include <AffineTransform.h>
#include <Font.h> #include <Font.h>
#include <Rect.h> #include <Rect.h>
@@ -51,15 +52,19 @@ public:
void DetachFromBuffer(); void DetachFromBuffer();
BRect Bounds() const; BRect Bounds() const;
void ConstrainClipping(const BRegion* region);
const BRegion* ClippingRegion() const
{ return fClippingRegion; }
void SetDrawState(const DrawState* data, void SetDrawState(const DrawState* data,
int32 xOffset = 0, int32 xOffset = 0,
int32 yOffset = 0); int32 yOffset = 0);
void ConstrainClipping(const BRegion* region);
const BRegion* ClippingRegion() const
{ return fClippingRegion; }
// object settings // object settings
void SetTransform(BAffineTransform transform,
int32 xOffset = 0,
int32 yOffset = 0);
void SetHighColor(const rgb_color& color); void SetHighColor(const rgb_color& color);
inline rgb_color HighColor() const inline rgb_color HighColor() const
{ return fPatternHandler.HighColor(); } { return fPatternHandler.HighColor(); }
@@ -295,6 +300,8 @@ private:
BRect _StrokePath(VertexSource& path) const; BRect _StrokePath(VertexSource& path) const;
template<class VertexSource> template<class VertexSource>
BRect _FillPath(VertexSource& path) const; BRect _FillPath(VertexSource& path) const;
template<class VertexSource>
BRect _RasterizePath(VertexSource& path) const;
void _CalcLinearGradientTransform(BPoint startPoint, void _CalcLinearGradientTransform(BPoint startPoint,
BPoint endPoint, agg::trans_affine& mtx, BPoint endPoint, agg::trans_affine& mtx,
float gradient_d2 = 100.0f) const; float gradient_d2 = 100.0f) const;
@@ -358,7 +365,9 @@ private:
bool fValidClipping : 1; bool fValidClipping : 1;
bool fDrawingText : 1; bool fDrawingText : 1;
bool fAttached : 1; bool fAttached : 1;
bool fIdentityTransform : 1;
agg::trans_affine fTransform;
float fPenSize; float fPenSize;
const BRegion* fClippingRegion; const BRegion* fClippingRegion;
drawing_mode fDrawingMode; drawing_mode fDrawingMode;
@@ -25,6 +25,7 @@
#include <agg_span_gradient.h> #include <agg_span_gradient.h>
#include <agg_span_interpolator_linear.h> #include <agg_span_interpolator_linear.h>
#include <agg_rendering_buffer.h> #include <agg_rendering_buffer.h>
#include <agg_trans_affine.h>
#include "agg_clipped_alpha_mask.h" #include "agg_clipped_alpha_mask.h"
#include "agg_rasterizer_scanline_aa_subpix.h" #include "agg_rasterizer_scanline_aa_subpix.h"