1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DrawTargetTiled.h"
10 #include "PathHelpers.h"
15 DrawTargetTiled::DrawTargetTiled() = default;
17 bool DrawTargetTiled::Init(const TileSet
& aTiles
) {
18 if (!aTiles
.mTileCount
) {
22 mTiles
.reserve(aTiles
.mTileCount
);
23 for (size_t i
= 0; i
< aTiles
.mTileCount
; ++i
) {
24 mTiles
.push_back(TileInternal(aTiles
.mTiles
[i
]));
25 if (!aTiles
.mTiles
[i
].mDrawTarget
) {
28 if (mTiles
[0].mDrawTarget
->GetFormat() !=
29 mTiles
.back().mDrawTarget
->GetFormat() ||
30 mTiles
[0].mDrawTarget
->GetBackendType() !=
31 mTiles
.back().mDrawTarget
->GetBackendType()) {
35 std::max(mRect
.XMost(), mTiles
[i
].mTileOrigin
.x
+
36 mTiles
[i
].mDrawTarget
->GetSize().width
);
38 std::max(mRect
.YMost(), mTiles
[i
].mTileOrigin
.y
+
39 mTiles
[i
].mDrawTarget
->GetSize().height
);
41 mRect
.MoveTo(mTiles
[0].mTileOrigin
.x
, mTiles
[0].mTileOrigin
.y
);
43 mRect
.MoveTo(std::min(mRect
.X(), mTiles
[i
].mTileOrigin
.x
),
44 std::min(mRect
.Y(), mTiles
[i
].mTileOrigin
.y
));
46 mRect
.SetRightEdge(newXMost
);
47 mRect
.SetBottomEdge(newYMost
);
48 mTiles
[i
].mDrawTarget
->SetTransform(Matrix::Translation(
49 -mTiles
[i
].mTileOrigin
.x
, -mTiles
[i
].mTileOrigin
.y
));
51 mFormat
= mTiles
[0].mDrawTarget
->GetFormat();
52 SetPermitSubpixelAA(IsOpaque(mFormat
));
56 already_AddRefed
<SourceSurface
> DrawTargetTiled::Snapshot() {
57 return MakeAndAddRef
<SnapshotTiled
>(mTiles
, mRect
);
60 void DrawTargetTiled::DetachAllSnapshots() {}
62 // Skip the mClippedOut check since this is only used for Flush() which
63 // should happen even if we're clipped.
64 #define TILED_COMMAND(command) \
65 void DrawTargetTiled::command() { \
66 for (size_t i = 0; i < mTiles.size(); i++) { \
67 mTiles[i].mDrawTarget->command(); \
70 #define TILED_COMMAND1(command, type1) \
71 void DrawTargetTiled::command(type1 arg1) { \
72 for (size_t i = 0; i < mTiles.size(); i++) { \
73 if (!mTiles[i].mClippedOut) mTiles[i].mDrawTarget->command(arg1); \
76 #define TILED_COMMAND3(command, type1, type2, type3) \
77 void DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) { \
78 for (size_t i = 0; i < mTiles.size(); i++) { \
79 if (!mTiles[i].mClippedOut) \
80 mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
83 #define TILED_COMMAND4(command, type1, type2, type3, type4) \
84 void DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, \
86 for (size_t i = 0; i < mTiles.size(); i++) { \
87 if (!mTiles[i].mClippedOut) \
88 mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
91 #define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
92 void DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, \
93 type4 arg4, type5 arg5) { \
94 for (size_t i = 0; i < mTiles.size(); i++) { \
95 if (!mTiles[i].mClippedOut) \
96 mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
101 TILED_COMMAND4(DrawFilter
, FilterNode
*, const Rect
&, const Point
&,
103 TILED_COMMAND1(ClearRect
, const Rect
&)
104 TILED_COMMAND4(MaskSurface
, const Pattern
&, SourceSurface
*, Point
,
106 TILED_COMMAND4(FillGlyphs
, ScaledFont
*, const GlyphBuffer
&, const Pattern
&,
108 TILED_COMMAND3(Mask
, const Pattern
&, const Pattern
&, const DrawOptions
&)
110 void DrawTargetTiled::PushClip(const Path
* aPath
) {
111 if (!mClippedOutTilesStack
.append(std::vector
<bool>(mTiles
.size()))) {
112 MOZ_CRASH("out of memory");
114 std::vector
<bool>& clippedTiles
= mClippedOutTilesStack
.back();
116 Rect deviceRect
= aPath
->GetBounds(mTransform
);
118 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
119 if (!mTiles
[i
].mClippedOut
) {
120 if (deviceRect
.Intersects(
121 Rect(mTiles
[i
].mTileOrigin
.x
, mTiles
[i
].mTileOrigin
.y
,
122 mTiles
[i
].mDrawTarget
->GetSize().width
,
123 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
124 mTiles
[i
].mDrawTarget
->PushClip(aPath
);
126 mTiles
[i
].mClippedOut
= true;
127 clippedTiles
[i
] = true;
133 void DrawTargetTiled::PushClipRect(const Rect
& aRect
) {
134 if (!mClippedOutTilesStack
.append(std::vector
<bool>(mTiles
.size()))) {
135 MOZ_CRASH("out of memory");
137 std::vector
<bool>& clippedTiles
= mClippedOutTilesStack
.back();
139 Rect deviceRect
= mTransform
.TransformBounds(aRect
);
141 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
142 if (!mTiles
[i
].mClippedOut
) {
143 if (deviceRect
.Intersects(
144 Rect(mTiles
[i
].mTileOrigin
.x
, mTiles
[i
].mTileOrigin
.y
,
145 mTiles
[i
].mDrawTarget
->GetSize().width
,
146 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
147 mTiles
[i
].mDrawTarget
->PushClipRect(aRect
);
149 mTiles
[i
].mClippedOut
= true;
150 clippedTiles
[i
] = true;
156 void DrawTargetTiled::PopClip() {
157 std::vector
<bool>& clippedTiles
= mClippedOutTilesStack
.back();
158 MOZ_ASSERT(clippedTiles
.size() == mTiles
.size());
159 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
160 if (!mTiles
[i
].mClippedOut
) {
161 mTiles
[i
].mDrawTarget
->PopClip();
162 } else if (clippedTiles
[i
]) {
163 mTiles
[i
].mClippedOut
= false;
167 mClippedOutTilesStack
.popBack();
170 void DrawTargetTiled::CopySurface(SourceSurface
* aSurface
,
171 const IntRect
& aSourceRect
,
172 const IntPoint
& aDestination
) {
173 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
174 IntPoint tileOrigin
= mTiles
[i
].mTileOrigin
;
175 IntSize tileSize
= mTiles
[i
].mDrawTarget
->GetSize();
176 if (!IntRect(aDestination
, aSourceRect
.Size())
177 .Intersects(IntRect(tileOrigin
, tileSize
))) {
180 // CopySurface ignores the transform, account for that here.
181 mTiles
[i
].mDrawTarget
->CopySurface(aSurface
, aSourceRect
,
182 aDestination
- tileOrigin
);
186 void DrawTargetTiled::SetTransform(const Matrix
& aTransform
) {
187 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
188 Matrix mat
= aTransform
;
189 mat
.PostTranslate(Float(-mTiles
[i
].mTileOrigin
.x
),
190 Float(-mTiles
[i
].mTileOrigin
.y
));
191 mTiles
[i
].mDrawTarget
->SetTransform(mat
);
193 DrawTarget::SetTransform(aTransform
);
196 void DrawTargetTiled::SetPermitSubpixelAA(bool aPermitSubpixelAA
) {
197 DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA
);
198 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
199 mTiles
[i
].mDrawTarget
->SetPermitSubpixelAA(aPermitSubpixelAA
);
203 void DrawTargetTiled::DrawSurface(SourceSurface
* aSurface
, const Rect
& aDest
,
205 const DrawSurfaceOptions
& aSurfaceOptions
,
206 const DrawOptions
& aDrawOptions
) {
207 Rect deviceRect
= mTransform
.TransformBounds(aDest
);
208 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
209 if (!mTiles
[i
].mClippedOut
&&
210 deviceRect
.Intersects(Rect(mTiles
[i
].mTileOrigin
.x
,
211 mTiles
[i
].mTileOrigin
.y
,
212 mTiles
[i
].mDrawTarget
->GetSize().width
,
213 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
214 mTiles
[i
].mDrawTarget
->DrawSurface(aSurface
, aDest
, aSource
,
215 aSurfaceOptions
, aDrawOptions
);
220 void DrawTargetTiled::FillRect(const Rect
& aRect
, const Pattern
& aPattern
,
221 const DrawOptions
& aDrawOptions
) {
222 Rect deviceRect
= mTransform
.TransformBounds(aRect
);
223 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
224 if (!mTiles
[i
].mClippedOut
&&
225 deviceRect
.Intersects(Rect(mTiles
[i
].mTileOrigin
.x
,
226 mTiles
[i
].mTileOrigin
.y
,
227 mTiles
[i
].mDrawTarget
->GetSize().width
,
228 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
229 mTiles
[i
].mDrawTarget
->FillRect(aRect
, aPattern
, aDrawOptions
);
234 void DrawTargetTiled::Stroke(const Path
* aPath
, const Pattern
& aPattern
,
235 const StrokeOptions
& aStrokeOptions
,
236 const DrawOptions
& aDrawOptions
) {
237 // Approximate the stroke extents, since Path::GetStrokeExtents can be slow
238 Rect deviceRect
= aPath
->GetBounds(mTransform
);
239 deviceRect
.Inflate(MaxStrokeExtents(aStrokeOptions
, mTransform
));
240 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
241 if (!mTiles
[i
].mClippedOut
&&
242 deviceRect
.Intersects(Rect(mTiles
[i
].mTileOrigin
.x
,
243 mTiles
[i
].mTileOrigin
.y
,
244 mTiles
[i
].mDrawTarget
->GetSize().width
,
245 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
246 mTiles
[i
].mDrawTarget
->Stroke(aPath
, aPattern
, aStrokeOptions
,
252 void DrawTargetTiled::StrokeRect(const Rect
& aRect
, const Pattern
& aPattern
,
253 const StrokeOptions
& aStrokeOptions
,
254 const DrawOptions
& aDrawOptions
) {
255 Rect deviceRect
= mTransform
.TransformBounds(aRect
);
256 Margin strokeMargin
= MaxStrokeExtents(aStrokeOptions
, mTransform
);
257 Rect outerRect
= deviceRect
;
258 outerRect
.Inflate(strokeMargin
);
260 if (mTransform
.IsRectilinear()) {
261 // If rects are mapped to rects, we can compute the inner rect
262 // of the stroked rect.
263 innerRect
= deviceRect
;
264 innerRect
.Deflate(strokeMargin
);
266 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
267 if (mTiles
[i
].mClippedOut
) {
270 Rect
tileRect(mTiles
[i
].mTileOrigin
.x
, mTiles
[i
].mTileOrigin
.y
,
271 mTiles
[i
].mDrawTarget
->GetSize().width
,
272 mTiles
[i
].mDrawTarget
->GetSize().height
);
273 if (outerRect
.Intersects(tileRect
) && !innerRect
.Contains(tileRect
)) {
274 mTiles
[i
].mDrawTarget
->StrokeRect(aRect
, aPattern
, aStrokeOptions
,
280 void DrawTargetTiled::StrokeLine(const Point
& aStart
, const Point
& aEnd
,
281 const Pattern
& aPattern
,
282 const StrokeOptions
& aStrokeOptions
,
283 const DrawOptions
& aDrawOptions
) {
284 Rect lineBounds
= Rect(aStart
, Size()).UnionEdges(Rect(aEnd
, Size()));
285 Rect deviceRect
= mTransform
.TransformBounds(lineBounds
);
286 deviceRect
.Inflate(MaxStrokeExtents(aStrokeOptions
, mTransform
));
287 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
288 if (!mTiles
[i
].mClippedOut
&&
289 deviceRect
.Intersects(Rect(mTiles
[i
].mTileOrigin
.x
,
290 mTiles
[i
].mTileOrigin
.y
,
291 mTiles
[i
].mDrawTarget
->GetSize().width
,
292 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
293 mTiles
[i
].mDrawTarget
->StrokeLine(aStart
, aEnd
, aPattern
, aStrokeOptions
,
299 void DrawTargetTiled::Fill(const Path
* aPath
, const Pattern
& aPattern
,
300 const DrawOptions
& aDrawOptions
) {
301 Rect deviceRect
= aPath
->GetBounds(mTransform
);
302 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
303 if (!mTiles
[i
].mClippedOut
&&
304 deviceRect
.Intersects(Rect(mTiles
[i
].mTileOrigin
.x
,
305 mTiles
[i
].mTileOrigin
.y
,
306 mTiles
[i
].mDrawTarget
->GetSize().width
,
307 mTiles
[i
].mDrawTarget
->GetSize().height
))) {
308 mTiles
[i
].mDrawTarget
->Fill(aPath
, aPattern
, aDrawOptions
);
313 void DrawTargetTiled::PushLayer(bool aOpaque
, Float aOpacity
,
314 SourceSurface
* aMask
,
315 const Matrix
& aMaskTransform
,
316 const IntRect
& aBounds
, bool aCopyBackground
) {
317 // XXX - not sure this is what we want or whether we want to continue drawing
318 // to a larger intermediate surface, that would require tweaking the code in
319 // here a little though.
320 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
321 if (!mTiles
[i
].mClippedOut
) {
322 IntRect bounds
= aBounds
;
323 bounds
.MoveBy(-mTiles
[i
].mTileOrigin
);
324 mTiles
[i
].mDrawTarget
->PushLayer(aOpaque
, aOpacity
, aMask
, aMaskTransform
,
325 bounds
, aCopyBackground
);
329 PushedLayer
layer(GetPermitSubpixelAA());
330 mPushedLayers
.push_back(layer
);
331 SetPermitSubpixelAA(aOpaque
);
334 void DrawTargetTiled::PushLayerWithBlend(bool aOpaque
, Float aOpacity
,
335 SourceSurface
* aMask
,
336 const Matrix
& aMaskTransform
,
337 const IntRect
& aBounds
,
338 bool aCopyBackground
,
340 // XXX - not sure this is what we want or whether we want to continue drawing
341 // to a larger intermediate surface, that would require tweaking the code in
342 // here a little though.
343 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
344 if (!mTiles
[i
].mClippedOut
) {
345 IntRect bounds
= aBounds
;
346 bounds
.MoveBy(-mTiles
[i
].mTileOrigin
);
347 mTiles
[i
].mDrawTarget
->PushLayerWithBlend(aOpaque
, aOpacity
, aMask
,
348 aMaskTransform
, bounds
,
349 aCopyBackground
, aOp
);
353 PushedLayer
layer(GetPermitSubpixelAA());
354 mPushedLayers
.push_back(layer
);
355 SetPermitSubpixelAA(aOpaque
);
358 void DrawTargetTiled::PopLayer() {
359 // XXX - not sure this is what we want or whether we want to continue drawing
360 // to a larger intermediate surface, that would require tweaking the code in
361 // here a little though.
362 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
363 if (!mTiles
[i
].mClippedOut
) {
364 mTiles
[i
].mDrawTarget
->PopLayer();
368 MOZ_ASSERT(mPushedLayers
.size());
369 const PushedLayer
& layer
= mPushedLayers
.back();
370 SetPermitSubpixelAA(layer
.mOldPermitSubpixelAA
);
371 mPushedLayers
.pop_back();
374 RefPtr
<DrawTarget
> DrawTargetTiled::CreateClippedDrawTarget(
375 const Rect
& aBounds
, SurfaceFormat aFormat
) {
376 Rect deviceRect
= mTransform
.TransformBounds(aBounds
);
378 // Build up an approximation of the current clip rect by unioning
379 // the tiles that are not clipped
380 Rect clipRectApproximation
;
381 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
382 if (!mTiles
[i
].mClippedOut
) {
383 clipRectApproximation
= clipRectApproximation
.Union(
384 Rect(mTiles
[i
].mTileOrigin
.x
, mTiles
[i
].mTileOrigin
.y
,
385 mTiles
[i
].mDrawTarget
->GetSize().width
,
386 mTiles
[i
].mDrawTarget
->GetSize().height
));
391 if (!aBounds
.IsEmpty()) {
392 clipBounds
= IntRect::RoundOut(deviceRect
.Intersect(clipRectApproximation
));
394 clipBounds
= IntRect::RoundOut(clipRectApproximation
);
397 RefPtr
<DrawTarget
> result
;
398 if (!clipBounds
.IsEmpty()) {
399 RefPtr
<DrawTarget
> dt
= CreateSimilarDrawTarget(
400 IntSize(clipBounds
.width
, clipBounds
.height
), aFormat
);
401 result
= gfx::Factory::CreateOffsetDrawTarget(
402 dt
, IntPoint(clipBounds
.x
, clipBounds
.y
));
403 result
->SetTransform(mTransform
);
405 // Everything is clipped but we still want some kind of surface
406 result
= CreateSimilarDrawTarget(IntSize(1, 1), aFormat
);
411 void DrawTargetTiled::PadEdges(const IntRegion
& aRegion
) {
412 for (size_t i
= 0; i
< mTiles
.size(); i
++) {
413 if (mTiles
[i
].mClippedOut
) {
418 RoundedOut(Rect(mTiles
[i
].mTileOrigin
.x
, mTiles
[i
].mTileOrigin
.y
,
419 mTiles
[i
].mDrawTarget
->GetSize().width
,
420 mTiles
[i
].mDrawTarget
->GetSize().height
));
422 // We only need to pad edges on tiles that intersect the edge of the region
423 if (aRegion
.Intersects(tileRect
) && !aRegion
.Contains(tileRect
)) {
424 IntRegion padRegion
= aRegion
;
425 padRegion
.MoveBy(-mTiles
[i
].mTileOrigin
);
426 padRegion
.AndWith(IntRect(0, 0, mTiles
[i
].mDrawTarget
->GetSize().width
,
427 mTiles
[i
].mDrawTarget
->GetSize().height
));
428 mTiles
[i
].mDrawTarget
->PadEdges(padRegion
);
434 } // namespace mozilla