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:
parent
2e7da8455a
commit
63a30a4744
@ -272,9 +272,9 @@ Painter::Bounds() const
|
||||
|
||||
// SetDrawState
|
||||
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
|
||||
|
||||
// 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
|
||||
// Painter methods are used directly, so this function is much less
|
||||
// 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)
|
||||
fMaskedUnpackedScanline = data->GetAlphaMask()->Generate();
|
||||
if (state->GetAlphaMask() != NULL)
|
||||
fMaskedUnpackedScanline = state->GetAlphaMask()->Generate();
|
||||
else
|
||||
fMaskedUnpackedScanline = NULL;
|
||||
|
||||
// any of these conditions means we need to use a different drawing
|
||||
// mode instance
|
||||
bool updateDrawingMode
|
||||
= !(data->GetPattern() == fPatternHandler.GetPattern())
|
||||
|| data->GetDrawingMode() != fDrawingMode
|
||||
|| (data->GetDrawingMode() == B_OP_ALPHA
|
||||
&& (data->AlphaSrcMode() != fAlphaSrcMode
|
||||
|| data->AlphaFncMode() != fAlphaFncMode));
|
||||
= !(state->GetPattern() == fPatternHandler.GetPattern())
|
||||
|| state->GetDrawingMode() != fDrawingMode
|
||||
|| (state->GetDrawingMode() == B_OP_ALPHA
|
||||
&& (state->AlphaSrcMode() != fAlphaSrcMode
|
||||
|| state->AlphaFncMode() != fAlphaFncMode));
|
||||
|
||||
fDrawingMode = data->GetDrawingMode();
|
||||
fAlphaSrcMode = data->AlphaSrcMode();
|
||||
fAlphaFncMode = data->AlphaFncMode();
|
||||
fPatternHandler.SetPattern(data->GetPattern());
|
||||
fDrawingMode = state->GetDrawingMode();
|
||||
fAlphaSrcMode = state->AlphaSrcMode();
|
||||
fAlphaFncMode = state->AlphaFncMode();
|
||||
fPatternHandler.SetPattern(state->GetPattern());
|
||||
fPatternHandler.SetOffsets(xOffset, yOffset);
|
||||
fLineCapMode = data->LineCapMode();
|
||||
fLineJoinMode = data->LineJoinMode();
|
||||
fMiterLimit = data->MiterLimit();
|
||||
fLineCapMode = state->LineCapMode();
|
||||
fLineJoinMode = state->LineJoinMode();
|
||||
fMiterLimit = state->MiterLimit();
|
||||
|
||||
// adopt the color *after* the pattern is set
|
||||
// to set the renderers to the correct color
|
||||
SetHighColor(data->HighColor());
|
||||
SetLowColor(data->LowColor());
|
||||
SetHighColor(state->HighColor());
|
||||
SetLowColor(state->LowColor());
|
||||
|
||||
if (updateDrawingMode || fPixelFormat.UsesOpCopyForText())
|
||||
_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
|
||||
void
|
||||
Painter::SetHighColor(const rgb_color& color)
|
||||
@ -460,7 +475,7 @@ Painter::StrokeLine(BPoint a, BPoint b)
|
||||
_Transform(&b, false);
|
||||
|
||||
// first, try an optimized version
|
||||
if (fPenSize == 1.0
|
||||
if (fPenSize == 1.0 && fIdentityTransform
|
||||
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
|
||||
&& fMaskedUnpackedScanline == NULL) {
|
||||
pattern pat = *fPatternHandler.GetR5Pattern();
|
||||
@ -477,7 +492,7 @@ Painter::StrokeLine(BPoint a, BPoint b)
|
||||
|
||||
if (a == b) {
|
||||
// special case dots
|
||||
if (fPenSize == 1.0 && !fSubpixelPrecise) {
|
||||
if (fPenSize == 1.0 && !fSubpixelPrecise && fIdentityTransform) {
|
||||
if (fClippingRegion->Contains(a)) {
|
||||
fPixelFormat.blend_pixel((int)a.x, (int)a.y, fRenderer.color(),
|
||||
255);
|
||||
@ -495,7 +510,7 @@ Painter::StrokeLine(BPoint a, BPoint b)
|
||||
// tweak ends to "include" the pixel at the index,
|
||||
// we need to do this in order to produce results like R5,
|
||||
// where coordinates were inclusive
|
||||
if (!fSubpixelPrecise) {
|
||||
if (!fSubpixelPrecise && fIdentityTransform) {
|
||||
bool centerOnLine = fmodf(fPenSize, 2.0) != 0.0;
|
||||
if (a.x == b.x) {
|
||||
// shift to pixel center vertically
|
||||
@ -870,7 +885,7 @@ Painter::StrokeRect(const BRect& r) const
|
||||
_Transform(&b, false);
|
||||
|
||||
// first, try an optimized version
|
||||
if (fPenSize == 1.0
|
||||
if (fPenSize == 1.0 && fIdentityTransform
|
||||
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
|
||||
&& fMaskedUnpackedScanline == NULL) {
|
||||
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
|
||||
a.x += 0.5;
|
||||
a.y += 0.5;
|
||||
@ -934,7 +949,7 @@ Painter::FillRect(const BRect& r) const
|
||||
|
||||
// first, try an optimized version
|
||||
if ((fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
|
||||
&& fMaskedUnpackedScanline == NULL) {
|
||||
&& fMaskedUnpackedScanline == NULL && fIdentityTransform) {
|
||||
pattern p = *fPatternHandler.GetR5Pattern();
|
||||
if (p == B_SOLID_HIGH) {
|
||||
BRect rect(a, b);
|
||||
@ -946,7 +961,8 @@ Painter::FillRect(const BRect& r) const
|
||||
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();
|
||||
if (p == B_SOLID_HIGH) {
|
||||
BRect rect(a, b);
|
||||
@ -994,7 +1010,7 @@ Painter::FillRect(const BRect& r, const BGradient& gradient) const
|
||||
// first, try an optimized version
|
||||
if (gradient.GetType() == BGradient::TYPE_LINEAR
|
||||
&& (fDrawingMode == B_OP_COPY || fDrawingMode == B_OP_OVER)
|
||||
&& fMaskedUnpackedScanline == NULL) {
|
||||
&& fMaskedUnpackedScanline == NULL && fIdentityTransform) {
|
||||
const BGradientLinear* linearGradient
|
||||
= dynamic_cast<const BGradientLinear*>(&gradient);
|
||||
if (linearGradient->Start().x == linearGradient->End().x
|
||||
@ -1067,7 +1083,8 @@ Painter::FillRect(const BRect& r, const rgb_color& c) const
|
||||
|
||||
// FillRectVerticalGradient
|
||||
void
|
||||
Painter::FillRectVerticalGradient(BRect r, const BGradientLinear& gradient) const
|
||||
Painter::FillRectVerticalGradient(BRect r,
|
||||
const BGradientLinear& gradient) const
|
||||
{
|
||||
if (!fValidClipping)
|
||||
return;
|
||||
@ -1153,97 +1170,15 @@ Painter::StrokeRoundRect(const BRect& r, float xRadius, float yRadius) const
|
||||
|
||||
BPoint lt(r.left, r.top);
|
||||
BPoint rb(r.right, r.bottom);
|
||||
bool centerOffset = fPenSize == 1.0;
|
||||
// TODO: use this when using _StrokePath()
|
||||
// bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
|
||||
bool centerOffset = fmodf(fPenSize, 2.0) != 0.0;
|
||||
_Transform(<, centerOffset);
|
||||
_Transform(&rb, centerOffset);
|
||||
|
||||
if (fPenSize == 1.0) {
|
||||
agg::rounded_rect rect;
|
||||
rect.rect(lt.x, lt.y, rb.x, rb.y);
|
||||
rect.radius(xRadius, yRadius);
|
||||
agg::rounded_rect rect;
|
||||
rect.rect(lt.x, lt.y, rb.x, rb.y);
|
||||
rect.radius(xRadius, yRadius);
|
||||
|
||||
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));
|
||||
return _StrokePath(rect);
|
||||
}
|
||||
|
||||
|
||||
@ -1334,61 +1269,12 @@ Painter::DrawEllipse(BRect r, bool fill) const
|
||||
if (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);
|
||||
}
|
||||
|
||||
// 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));
|
||||
else
|
||||
return _StrokePath(path);
|
||||
}
|
||||
|
||||
|
||||
@ -2908,30 +2794,14 @@ Painter::_StrokePath(VertexSource& path) const
|
||||
stroke.line_join(agg_line_join_mode_for(fLineJoinMode));
|
||||
stroke.miter_limit(fMiterLimit);
|
||||
|
||||
if (fMaskedUnpackedScanline != NULL) {
|
||||
// TODO: we can't do both alpha-masking and subpixel AA.
|
||||
fRasterizer.reset();
|
||||
fRasterizer.add_path(path);
|
||||
agg::render_scanlines(fRasterizer, *fMaskedUnpackedScanline,
|
||||
fRenderer);
|
||||
} else if (gSubpixelAntialiasing) {
|
||||
fSubpixRasterizer.reset();
|
||||
fSubpixRasterizer.add_path(stroke);
|
||||
if (fIdentityTransform)
|
||||
return _RasterizePath(stroke);
|
||||
|
||||
agg::render_scanlines(fSubpixRasterizer,
|
||||
fSubpixPackedScanline, fSubpixRenderer);
|
||||
} else {
|
||||
fRasterizer.reset();
|
||||
fRasterizer.add_path(stroke);
|
||||
stroke.approximation_scale(fTransform.scale());
|
||||
|
||||
agg::render_scanlines(fRasterizer, fPackedScanline, fRenderer);
|
||||
}
|
||||
|
||||
BRect touched = _BoundingBox(path);
|
||||
float penSize = ceilf(fPenSize / 2.0);
|
||||
touched.InsetBy(-penSize, -penSize);
|
||||
|
||||
return _Clipped(touched);
|
||||
agg::conv_transform<agg::conv_stroke<VertexSource> > transformedStroke(
|
||||
stroke, fTransform);
|
||||
return _RasterizePath(transformedStroke);
|
||||
}
|
||||
|
||||
|
||||
@ -2939,6 +2809,19 @@ Painter::_StrokePath(VertexSource& path) const
|
||||
template<class VertexSource>
|
||||
BRect
|
||||
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) {
|
||||
// TODO: we can't do both alpha-masking and subpixel AA.
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <agg_conv_curve.h>
|
||||
#include <agg_path_storage.h>
|
||||
|
||||
#include <AffineTransform.h>
|
||||
#include <Font.h>
|
||||
#include <Rect.h>
|
||||
|
||||
@ -51,15 +52,19 @@ public:
|
||||
void DetachFromBuffer();
|
||||
BRect Bounds() const;
|
||||
|
||||
void ConstrainClipping(const BRegion* region);
|
||||
const BRegion* ClippingRegion() const
|
||||
{ return fClippingRegion; }
|
||||
|
||||
void SetDrawState(const DrawState* data,
|
||||
int32 xOffset = 0,
|
||||
int32 yOffset = 0);
|
||||
|
||||
void ConstrainClipping(const BRegion* region);
|
||||
const BRegion* ClippingRegion() const
|
||||
{ return fClippingRegion; }
|
||||
|
||||
// object settings
|
||||
void SetTransform(BAffineTransform transform,
|
||||
int32 xOffset = 0,
|
||||
int32 yOffset = 0);
|
||||
|
||||
void SetHighColor(const rgb_color& color);
|
||||
inline rgb_color HighColor() const
|
||||
{ return fPatternHandler.HighColor(); }
|
||||
@ -295,6 +300,8 @@ private:
|
||||
BRect _StrokePath(VertexSource& path) const;
|
||||
template<class VertexSource>
|
||||
BRect _FillPath(VertexSource& path) const;
|
||||
template<class VertexSource>
|
||||
BRect _RasterizePath(VertexSource& path) const;
|
||||
void _CalcLinearGradientTransform(BPoint startPoint,
|
||||
BPoint endPoint, agg::trans_affine& mtx,
|
||||
float gradient_d2 = 100.0f) const;
|
||||
@ -358,7 +365,9 @@ private:
|
||||
bool fValidClipping : 1;
|
||||
bool fDrawingText : 1;
|
||||
bool fAttached : 1;
|
||||
bool fIdentityTransform : 1;
|
||||
|
||||
agg::trans_affine fTransform;
|
||||
float fPenSize;
|
||||
const BRegion* fClippingRegion;
|
||||
drawing_mode fDrawingMode;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <agg_span_gradient.h>
|
||||
#include <agg_span_interpolator_linear.h>
|
||||
#include <agg_rendering_buffer.h>
|
||||
#include <agg_trans_affine.h>
|
||||
|
||||
#include "agg_clipped_alpha_mask.h"
|
||||
#include "agg_rasterizer_scanline_aa_subpix.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user