diff --git a/gfx/2d/DrawTargetTiled.cpp b/gfx/2d/DrawTargetTiled.cpp index 641b3cef7047..2ac4e8979d28 100644 --- a/gfx/2d/DrawTargetTiled.cpp +++ b/gfx/2d/DrawTargetTiled.cpp @@ -3,11 +3,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#define _USE_MATH_DEFINES -#include - #include "DrawTargetTiled.h" #include "Logging.h" +#include "PathHelpers.h" using namespace std; @@ -107,8 +105,6 @@ TILED_COMMAND(Flush) TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&) TILED_COMMAND1(ClearRect, const Rect&) TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&) -TILED_COMMAND4(StrokeRect, const Rect&, const Pattern&, const StrokeOptions&, const DrawOptions&) -TILED_COMMAND5(StrokeLine, const Point&, const Point&, const Pattern&, const StrokeOptions&, const DrawOptions&) TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*) TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&) @@ -232,40 +228,12 @@ DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const Draw } } -// The logic for this comes from _cairo_stroke_style_max_distance_from_path -static Rect -PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions, - const Rect &aRect, - const Matrix &aTransform) -{ - double styleExpansionFactor = 0.5f; - - if (aStrokeOptions.mLineCap == CapStyle::SQUARE) { - styleExpansionFactor = M_SQRT1_2; - } - - if (aStrokeOptions.mLineJoin == JoinStyle::MITER && - styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) { - styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit; - } - - styleExpansionFactor *= aStrokeOptions.mLineWidth; - - double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21); - double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12); - - Rect result = aRect; - result.Inflate(dx, dy); - return result; -} - void DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions) { // Approximate the stroke extents, since Path::GetStrokeExtents can be slow - Rect deviceRect = PathExtentsToMaxStrokeExtents(aStrokeOptions, - aPath->GetBounds(mTransform), - mTransform); + Rect deviceRect = aPath->GetBounds(mTransform); + deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform)); for (size_t i = 0; i < mTiles.size(); i++) { if (!mTiles[i].mClippedOut && deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, @@ -277,6 +245,51 @@ DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const Stroke } } +void +DrawTargetTiled::StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions) +{ + Rect deviceRect = mTransform.TransformBounds(aRect); + Margin strokeMargin = MaxStrokeExtents(aStrokeOptions, mTransform); + Rect outerRect = deviceRect; + outerRect.Inflate(strokeMargin); + Rect innerRect; + if (mTransform.IsRectilinear()) { + // If rects are mapped to rects, we can compute the inner rect + // of the stroked rect. + innerRect = deviceRect; + innerRect.Deflate(strokeMargin); + } + for (size_t i = 0; i < mTiles.size(); i++) { + if (mTiles[i].mClippedOut) { + continue; + } + Rect tileRect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height); + if (outerRect.Intersects(tileRect) && !innerRect.Contains(tileRect)) { + mTiles[i].mDrawTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aDrawOptions); + } + } +} + +void +DrawTargetTiled::StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions) +{ + Rect lineBounds = Rect(aStart, Size()).UnionEdges(Rect(aEnd, Size())); + Rect deviceRect = mTransform.TransformBounds(lineBounds); + deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform)); + for (size_t i = 0; i < mTiles.size(); i++) { + if (!mTiles[i].mClippedOut && + deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x, + mTiles[i].mTileOrigin.y, + mTiles[i].mDrawTarget->GetSize().width, + mTiles[i].mDrawTarget->GetSize().height))) { + mTiles[i].mDrawTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aDrawOptions); + } + } +} + void DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions) { diff --git a/gfx/2d/PathHelpers.cpp b/gfx/2d/PathHelpers.cpp index b9c0a3c1fd44..e28f4f053f4a 100644 --- a/gfx/2d/PathHelpers.cpp +++ b/gfx/2d/PathHelpers.cpp @@ -3,6 +3,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#define _USE_MATH_DEFINES +#include + #include "PathHelpers.h" namespace mozilla { @@ -238,6 +241,29 @@ StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget, aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); } +// The logic for this comes from _cairo_stroke_style_max_distance_from_path +Margin +MaxStrokeExtents(const StrokeOptions& aStrokeOptions, + const Matrix& aTransform) +{ + double styleExpansionFactor = 0.5f; + + if (aStrokeOptions.mLineCap == CapStyle::SQUARE) { + styleExpansionFactor = M_SQRT1_2; + } + + if (aStrokeOptions.mLineJoin == JoinStyle::MITER && + styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) { + styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit; + } + + styleExpansionFactor *= aStrokeOptions.mLineWidth; + + double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21); + double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12); + return Margin(dy, dx, dy, dx); +} + } // namespace gfx } // namespace mozilla diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index d7a732648dd6..6f55a7fa2d53 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -283,6 +283,16 @@ GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect, const ColorPattern& aColor, const StrokeOptions& aStrokeOptions); +/** + * Return the margin, in device space, by which a stroke can extend beyond the + * rendered shape. + * @param aStrokeOptions The stroke options that the stroke is drawn with. + * @param aTransform The user space to device space transform. + * @return The stroke margin. + */ +GFX2D_API Margin MaxStrokeExtents(const StrokeOptions& aStrokeOptions, + const Matrix& aTransform); + extern UserDataKey sDisablePixelSnapping; /** diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index ebff459dd4b0..3df42d79d20e 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -109,6 +109,7 @@ UNIFIED_SOURCES += [ 'DrawTargetCapture.cpp', 'DrawTargetDual.cpp', 'DrawTargetRecording.cpp', + 'DrawTargetTiled.cpp', 'Factory.cpp', 'FilterNodeSoftware.cpp', 'FilterProcessing.cpp', @@ -117,7 +118,6 @@ UNIFIED_SOURCES += [ 'Matrix.cpp', 'Path.cpp', 'PathCairo.cpp', - 'PathHelpers.cpp', 'PathRecording.cpp', 'RecordedEvent.cpp', 'Scale.cpp', @@ -128,7 +128,7 @@ UNIFIED_SOURCES += [ ] SOURCES += [ - 'DrawTargetTiled.cpp', + 'PathHelpers.cpp', # Uses _USE_MATH_DEFINES ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':