Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "DrawTargetTiled.h"
7 : #include "Logging.h"
8 : #include "PathHelpers.h"
9 :
10 : using namespace std;
11 :
12 : namespace mozilla {
13 : namespace gfx {
14 :
15 0 : DrawTargetTiled::DrawTargetTiled()
16 : {
17 0 : }
18 :
19 : bool
20 0 : DrawTargetTiled::Init(const TileSet& aTiles)
21 : {
22 0 : if (!aTiles.mTileCount) {
23 0 : return false;
24 : }
25 :
26 0 : mTiles.reserve(aTiles.mTileCount);
27 0 : for (size_t i = 0; i < aTiles.mTileCount; ++i) {
28 0 : mTiles.push_back(TileInternal(aTiles.mTiles[i]));
29 0 : if (!aTiles.mTiles[i].mDrawTarget) {
30 0 : return false;
31 : }
32 0 : if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() ||
33 0 : mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) {
34 0 : return false;
35 : }
36 0 : uint32_t newXMost = max(mRect.XMost(),
37 0 : mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
38 0 : uint32_t newYMost = max(mRect.YMost(),
39 0 : mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
40 0 : mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
41 0 : mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
42 0 : mRect.width = newXMost - mRect.x;
43 0 : mRect.height = newYMost - mRect.y;
44 0 : mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(mTiles[i].mTileOrigin.x,
45 0 : mTiles[i].mTileOrigin.y));
46 : }
47 0 : mFormat = mTiles[0].mDrawTarget->GetFormat();
48 0 : SetPermitSubpixelAA(IsOpaque(mFormat));
49 0 : return true;
50 : }
51 :
52 : already_AddRefed<SourceSurface>
53 0 : DrawTargetTiled::Snapshot()
54 : {
55 0 : return MakeAndAddRef<SnapshotTiled>(mTiles, mRect);
56 : }
57 :
58 : void
59 0 : DrawTargetTiled::DetachAllSnapshots()
60 0 : {}
61 :
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 \
66 : DrawTargetTiled::command() \
67 : { \
68 : for (size_t i = 0; i < mTiles.size(); i++) { \
69 : mTiles[i].mDrawTarget->command(); \
70 : } \
71 : }
72 : #define TILED_COMMAND1(command, type1) \
73 : void \
74 : DrawTargetTiled::command(type1 arg1) \
75 : { \
76 : for (size_t i = 0; i < mTiles.size(); i++) { \
77 : if (!mTiles[i].mClippedOut) \
78 : mTiles[i].mDrawTarget->command(arg1); \
79 : } \
80 : }
81 : #define TILED_COMMAND3(command, type1, type2, type3) \
82 : void \
83 : DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \
84 : { \
85 : for (size_t i = 0; i < mTiles.size(); i++) { \
86 : if (!mTiles[i].mClippedOut) \
87 : mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
88 : } \
89 : }
90 : #define TILED_COMMAND4(command, type1, type2, type3, type4) \
91 : void \
92 : DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
93 : { \
94 : for (size_t i = 0; i < mTiles.size(); i++) { \
95 : if (!mTiles[i].mClippedOut) \
96 : mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
97 : } \
98 : }
99 : #define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
100 : void \
101 : DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
102 : { \
103 : for (size_t i = 0; i < mTiles.size(); i++) { \
104 : if (!mTiles[i].mClippedOut) \
105 : mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
106 : } \
107 : }
108 :
109 0 : TILED_COMMAND(Flush)
110 0 : TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
111 0 : TILED_COMMAND1(ClearRect, const Rect&)
112 0 : TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
113 0 : TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
114 0 : TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
115 :
116 : void
117 0 : DrawTargetTiled::PushClip(const Path* aPath)
118 : {
119 0 : mClippedOutTilesStack.push_back(std::vector<uint32_t>());
120 0 : std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
121 :
122 0 : Rect deviceRect = aPath->GetBounds(mTransform);
123 :
124 0 : for (size_t i = 0; i < mTiles.size(); i++) {
125 0 : if (!mTiles[i].mClippedOut) {
126 0 : if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
127 0 : mTiles[i].mTileOrigin.y,
128 0 : mTiles[i].mDrawTarget->GetSize().width,
129 0 : mTiles[i].mDrawTarget->GetSize().height))) {
130 0 : mTiles[i].mDrawTarget->PushClip(aPath);
131 : } else {
132 0 : mTiles[i].mClippedOut = true;
133 0 : clippedTiles.push_back(i);
134 : }
135 : }
136 : }
137 0 : }
138 :
139 : void
140 0 : DrawTargetTiled::PushClipRect(const Rect& aRect)
141 : {
142 0 : mClippedOutTilesStack.push_back(std::vector<uint32_t>());
143 0 : std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
144 :
145 0 : Rect deviceRect = mTransform.TransformBounds(aRect);
146 :
147 0 : for (size_t i = 0; i < mTiles.size(); i++) {
148 0 : if (!mTiles[i].mClippedOut) {
149 0 : if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
150 0 : mTiles[i].mTileOrigin.y,
151 0 : mTiles[i].mDrawTarget->GetSize().width,
152 0 : mTiles[i].mDrawTarget->GetSize().height))) {
153 0 : mTiles[i].mDrawTarget->PushClipRect(aRect);
154 : } else {
155 0 : mTiles[i].mClippedOut = true;
156 0 : clippedTiles.push_back(i);
157 : }
158 : }
159 : }
160 0 : }
161 :
162 : void
163 0 : DrawTargetTiled::PopClip()
164 : {
165 0 : for (size_t i = 0; i < mTiles.size(); i++) {
166 0 : if (!mTiles[i].mClippedOut) {
167 0 : mTiles[i].mDrawTarget->PopClip();
168 : }
169 : }
170 :
171 0 : std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
172 0 : for (size_t i = 0; i < clippedTiles.size(); i++) {
173 0 : mTiles[clippedTiles[i]].mClippedOut = false;
174 : }
175 :
176 0 : mClippedOutTilesStack.pop_back();
177 0 : }
178 :
179 : void
180 0 : DrawTargetTiled::CopySurface(SourceSurface *aSurface,
181 : const IntRect &aSourceRect,
182 : const IntPoint &aDestination)
183 : {
184 0 : for (size_t i = 0; i < mTiles.size(); i++) {
185 0 : IntPoint tileOrigin = mTiles[i].mTileOrigin;
186 0 : IntSize tileSize = mTiles[i].mDrawTarget->GetSize();
187 0 : if (!IntRect(aDestination, aSourceRect.Size()).Intersects(IntRect(tileOrigin, tileSize))) {
188 0 : continue;
189 : }
190 : // CopySurface ignores the transform, account for that here.
191 0 : mTiles[i].mDrawTarget->CopySurface(aSurface, aSourceRect, aDestination - tileOrigin);
192 : }
193 0 : }
194 :
195 : void
196 0 : DrawTargetTiled::SetTransform(const Matrix& aTransform)
197 : {
198 0 : for (size_t i = 0; i < mTiles.size(); i++) {
199 0 : Matrix mat = aTransform;
200 0 : mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y));
201 0 : mTiles[i].mDrawTarget->SetTransform(mat);
202 : }
203 0 : DrawTarget::SetTransform(aTransform);
204 0 : }
205 :
206 : void
207 0 : DrawTargetTiled::SetPermitSubpixelAA(bool aPermitSubpixelAA)
208 : {
209 0 : DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
210 0 : for (size_t i = 0; i < mTiles.size(); i++) {
211 0 : mTiles[i].mDrawTarget->SetPermitSubpixelAA(aPermitSubpixelAA);
212 : }
213 0 : }
214 :
215 : void
216 0 : DrawTargetTiled::DrawSurface(SourceSurface* aSurface, const Rect& aDest, const Rect& aSource, const DrawSurfaceOptions& aSurfaceOptions, const DrawOptions& aDrawOptions)
217 : {
218 0 : Rect deviceRect = mTransform.TransformBounds(aDest);
219 0 : for (size_t i = 0; i < mTiles.size(); i++) {
220 0 : if (!mTiles[i].mClippedOut &&
221 0 : deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
222 0 : mTiles[i].mTileOrigin.y,
223 0 : mTiles[i].mDrawTarget->GetSize().width,
224 0 : mTiles[i].mDrawTarget->GetSize().height))) {
225 0 : mTiles[i].mDrawTarget->DrawSurface(aSurface, aDest, aSource, aSurfaceOptions, aDrawOptions);
226 : }
227 : }
228 0 : }
229 :
230 : void
231 0 : DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aDrawOptions)
232 : {
233 0 : Rect deviceRect = mTransform.TransformBounds(aRect);
234 0 : for (size_t i = 0; i < mTiles.size(); i++) {
235 0 : if (!mTiles[i].mClippedOut &&
236 0 : deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
237 0 : mTiles[i].mTileOrigin.y,
238 0 : mTiles[i].mDrawTarget->GetSize().width,
239 0 : mTiles[i].mDrawTarget->GetSize().height))) {
240 0 : mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions);
241 : }
242 : }
243 0 : }
244 :
245 : void
246 0 : DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions)
247 : {
248 : // Approximate the stroke extents, since Path::GetStrokeExtents can be slow
249 0 : Rect deviceRect = aPath->GetBounds(mTransform);
250 0 : deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
251 0 : for (size_t i = 0; i < mTiles.size(); i++) {
252 0 : if (!mTiles[i].mClippedOut &&
253 0 : deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
254 0 : mTiles[i].mTileOrigin.y,
255 0 : mTiles[i].mDrawTarget->GetSize().width,
256 0 : mTiles[i].mDrawTarget->GetSize().height))) {
257 0 : mTiles[i].mDrawTarget->Stroke(aPath, aPattern, aStrokeOptions, aDrawOptions);
258 : }
259 : }
260 0 : }
261 :
262 : void
263 0 : DrawTargetTiled::StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
264 : {
265 0 : Rect deviceRect = mTransform.TransformBounds(aRect);
266 0 : Margin strokeMargin = MaxStrokeExtents(aStrokeOptions, mTransform);
267 0 : Rect outerRect = deviceRect;
268 0 : outerRect.Inflate(strokeMargin);
269 0 : Rect innerRect;
270 0 : if (mTransform.IsRectilinear()) {
271 : // If rects are mapped to rects, we can compute the inner rect
272 : // of the stroked rect.
273 0 : innerRect = deviceRect;
274 0 : innerRect.Deflate(strokeMargin);
275 : }
276 0 : for (size_t i = 0; i < mTiles.size(); i++) {
277 0 : if (mTiles[i].mClippedOut) {
278 0 : continue;
279 : }
280 0 : Rect tileRect(mTiles[i].mTileOrigin.x,
281 0 : mTiles[i].mTileOrigin.y,
282 0 : mTiles[i].mDrawTarget->GetSize().width,
283 0 : mTiles[i].mDrawTarget->GetSize().height);
284 0 : if (outerRect.Intersects(tileRect) && !innerRect.Contains(tileRect)) {
285 0 : mTiles[i].mDrawTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aDrawOptions);
286 : }
287 : }
288 0 : }
289 :
290 : void
291 0 : DrawTargetTiled::StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
292 : {
293 0 : Rect lineBounds = Rect(aStart, Size()).UnionEdges(Rect(aEnd, Size()));
294 0 : Rect deviceRect = mTransform.TransformBounds(lineBounds);
295 0 : deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
296 0 : for (size_t i = 0; i < mTiles.size(); i++) {
297 0 : if (!mTiles[i].mClippedOut &&
298 0 : deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
299 0 : mTiles[i].mTileOrigin.y,
300 0 : mTiles[i].mDrawTarget->GetSize().width,
301 0 : mTiles[i].mDrawTarget->GetSize().height))) {
302 0 : mTiles[i].mDrawTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aDrawOptions);
303 : }
304 : }
305 0 : }
306 :
307 : void
308 0 : DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions)
309 : {
310 0 : Rect deviceRect = aPath->GetBounds(mTransform);
311 0 : for (size_t i = 0; i < mTiles.size(); i++) {
312 0 : if (!mTiles[i].mClippedOut &&
313 0 : deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
314 0 : mTiles[i].mTileOrigin.y,
315 0 : mTiles[i].mDrawTarget->GetSize().width,
316 0 : mTiles[i].mDrawTarget->GetSize().height))) {
317 0 : mTiles[i].mDrawTarget->Fill(aPath, aPattern, aDrawOptions);
318 : }
319 : }
320 0 : }
321 :
322 : void
323 0 : DrawTargetTiled::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
324 : const Matrix& aMaskTransform, const IntRect& aBounds,
325 : bool aCopyBackground)
326 : {
327 : // XXX - not sure this is what we want or whether we want to continue drawing to a larger
328 : // intermediate surface, that would require tweaking the code in here a little though.
329 0 : for (size_t i = 0; i < mTiles.size(); i++) {
330 0 : if (!mTiles[i].mClippedOut) {
331 0 : IntRect bounds = aBounds;
332 0 : bounds.MoveBy(-mTiles[i].mTileOrigin);
333 0 : mTiles[i].mDrawTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, bounds, aCopyBackground);
334 : }
335 : }
336 :
337 0 : PushedLayer layer(GetPermitSubpixelAA());
338 0 : mPushedLayers.push_back(layer);
339 0 : SetPermitSubpixelAA(aOpaque);
340 0 : }
341 :
342 : void
343 0 : DrawTargetTiled::PopLayer()
344 : {
345 : // XXX - not sure this is what we want or whether we want to continue drawing to a larger
346 : // intermediate surface, that would require tweaking the code in here a little though.
347 0 : for (size_t i = 0; i < mTiles.size(); i++) {
348 0 : if (!mTiles[i].mClippedOut) {
349 0 : mTiles[i].mDrawTarget->PopLayer();
350 : }
351 : }
352 :
353 0 : MOZ_ASSERT(mPushedLayers.size());
354 0 : const PushedLayer& layer = mPushedLayers.back();
355 0 : SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
356 0 : }
357 :
358 : } // namespace gfx
359 : } // namespace mozilla
|