5307f025fb21803229958caf92b539a52cdc6622
[WebKit-https.git] / Source / WebCore / platform / graphics / displaylists / DisplayListRecorder.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DisplayListRecorder.h"
28
29 #include "DisplayList.h"
30 #include "DisplayListItems.h"
31 #include "GraphicsContext.h"
32 #include "Logging.h"
33 #include <wtf/MathExtras.h>
34 #include <wtf/text/TextStream.h>
35
36 namespace WebCore {
37 namespace DisplayList {
38
39 Recorder::Recorder(GraphicsContext& context, DisplayList& displayList, const GraphicsContextState& state, const FloatRect& initialClip, const AffineTransform& baseCTM)
40     : GraphicsContextImpl(context, initialClip, baseCTM)
41     , m_displayList(displayList)
42 {
43     LOG_WITH_STREAM(DisplayLists, stream << "\nRecording with clip " << initialClip);
44     m_stateStack.append(ContextState(state, baseCTM, initialClip));
45 }
46
47 Recorder::~Recorder()
48 {
49     ASSERT(m_stateStack.size() == 1); // If this fires, it indicates mismatched save/restore.
50     LOG(DisplayLists, "Recorded display list:\n%s", m_displayList.description().data());
51 }
52
53 void Recorder::willAppendItem(const Item& item)
54 {
55     if (item.isDrawingItem()
56 #if USE(CG)
57         || item.type() == ItemType::ApplyStrokePattern || item.type() == ItemType::ApplyStrokePattern
58 #endif
59     ) {
60         GraphicsContextStateChange& stateChanges = currentState().stateChange;
61         GraphicsContextState::StateChangeFlags changesFromLastState = stateChanges.changesFromState(currentState().lastDrawingState);
62         if (changesFromLastState) {
63             LOG_WITH_STREAM(DisplayLists, stream << "pre-drawing, saving state " << GraphicsContextStateChange(stateChanges.m_state, changesFromLastState));
64             m_displayList.append(SetState::create(stateChanges.m_state, changesFromLastState));
65             stateChanges.m_changeFlags = 0;
66             currentState().lastDrawingState = stateChanges.m_state;
67         }
68         currentState().wasUsedForDrawing = true;
69     }
70 }
71
72 void Recorder::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
73 {
74     currentState().stateChange.accumulate(state, flags);
75 }
76
77 void Recorder::clearShadow()
78 {
79     appendItem(ClearShadow::create());
80 }
81
82 void Recorder::setLineCap(LineCap lineCap)
83 {
84     appendItem(SetLineCap::create(lineCap));
85 }
86
87 void Recorder::setLineDash(const DashArray& dashArray, float dashOffset)
88 {
89     appendItem(SetLineDash::create(dashArray, dashOffset));
90 }
91
92 void Recorder::setLineJoin(LineJoin lineJoin)
93 {
94     appendItem(SetLineJoin::create(lineJoin));
95 }
96
97 void Recorder::setMiterLimit(float miterLimit)
98 {
99     appendItem(SetMiterLimit::create(miterLimit));
100 }
101
102 void Recorder::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& startPoint, FontSmoothingMode smoothingMode)
103 {
104     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawGlyphs::create(font, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs, FloatPoint(), toFloatSize(startPoint), smoothingMode)));
105     updateItemExtent(newItem);
106 }
107
108 ImageDrawResult Recorder::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
109 {
110     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawImage::create(image, destination, source, imagePaintingOptions)));
111     updateItemExtent(newItem);
112     return ImageDrawResult::DidRecord;
113 }
114
115 ImageDrawResult Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
116 {
117     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledImage::create(image, destination, source, tileSize, spacing, imagePaintingOptions)));
118     updateItemExtent(newItem);
119     return ImageDrawResult::DidRecord;
120 }
121
122 ImageDrawResult Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
123 {
124     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledScaledImage::create(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions)));
125     updateItemExtent(newItem);
126     return ImageDrawResult::DidRecord;
127 }
128
129 #if USE(CG) || USE(CAIRO)
130 void Recorder::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
131 {
132     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawNativeImage::create(image, imageSize, destRect, srcRect, op, blendMode, orientation)));
133     updateItemExtent(newItem);
134 }
135 #endif
136
137 void Recorder::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
138 {
139     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPattern::create(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode)));
140     updateItemExtent(newItem);
141 }
142
143 void Recorder::save()
144 {
145     appendItem(Save::create());
146     m_stateStack.append(m_stateStack.last().cloneForSave(m_displayList.itemCount() - 1));
147 }
148
149 void Recorder::restore()
150 {
151     if (!m_stateStack.size())
152         return;
153
154     bool stateUsedForDrawing = currentState().wasUsedForDrawing;
155     size_t saveIndex = currentState().saveItemIndex;
156
157     m_stateStack.removeLast();
158     // Have to avoid eliding nested Save/Restore when a descendant state contains drawing items.
159     currentState().wasUsedForDrawing |= stateUsedForDrawing;
160
161     if (!stateUsedForDrawing && saveIndex) {
162         // This Save/Restore didn't contain any drawing items. Roll back to just before the last save.
163         m_displayList.removeItemsFromIndex(saveIndex);
164         return;
165     }
166
167     appendItem(Restore::create());
168
169     if (saveIndex) {
170         Save& saveItem = downcast<Save>(m_displayList.itemAt(saveIndex));
171         saveItem.setRestoreIndex(m_displayList.itemCount() - 1);
172     }
173 }
174
175 void Recorder::translate(float x, float y)
176 {
177     currentState().translate(x, y);
178     appendItem(Translate::create(x, y));
179 }
180
181 void Recorder::rotate(float angleInRadians)
182 {
183     currentState().rotate(angleInRadians);
184     appendItem(Rotate::create(angleInRadians));
185 }
186
187 void Recorder::scale(const FloatSize& size)
188 {
189     currentState().scale(size);
190     appendItem(Scale::create(size));
191 }
192
193 void Recorder::concatCTM(const AffineTransform& transform)
194 {
195     currentState().concatCTM(transform);
196     appendItem(ConcatenateCTM::create(transform));
197 }
198
199 void Recorder::setCTM(const AffineTransform&)
200 {
201     WTFLogAlways("GraphicsContext::setCTM() is not compatible with DisplayList::Recorder.");
202 }
203
204 AffineTransform Recorder::getCTM(GraphicsContext::IncludeDeviceScale)
205 {
206     WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with DisplayList::Recorder.");
207     return { };
208 }
209
210 void Recorder::beginTransparencyLayer(float opacity)
211 {
212     DrawingItem& newItem = downcast<DrawingItem>(appendItem(BeginTransparencyLayer::create(opacity)));
213     updateItemExtent(newItem);
214 }
215
216 void Recorder::endTransparencyLayer()
217 {
218     appendItem(EndTransparencyLayer::create());
219 }
220
221 void Recorder::drawRect(const FloatRect& rect, float borderThickness)
222 {
223     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawRect::create(rect, borderThickness)));
224     updateItemExtent(newItem);
225 }
226
227 void Recorder::drawLine(const FloatPoint& point1, const FloatPoint& point2)
228 {
229     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLine::create(point1, point2)));
230     updateItemExtent(newItem);
231 }
232
233 void Recorder::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, float strokeThickness)
234 {
235     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLinesForText::create(FloatPoint(), toFloatSize(point), widths, printing, doubleLines, strokeThickness)));
236     updateItemExtent(newItem);
237 }
238
239 void Recorder::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
240 {
241     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawDotsForDocumentMarker::create(rect, style)));
242     updateItemExtent(newItem);
243 }
244
245 void Recorder::drawEllipse(const FloatRect& rect)
246 {
247     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawEllipse::create(rect)));
248     updateItemExtent(newItem);
249 }
250
251 void Recorder::drawPath(const Path& path)
252 {
253     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPath::create(path)));
254     updateItemExtent(newItem);
255 }
256
257 void Recorder::drawFocusRing(const Path& path, float width, float offset, const Color& color)
258 {
259     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingPath::create(path, width, offset, color)));
260     updateItemExtent(newItem);
261 }
262
263 void Recorder::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
264 {
265     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingRects::create(rects, width, offset, color)));
266     updateItemExtent(newItem);
267 }
268
269 void Recorder::fillRect(const FloatRect& rect)
270 {
271     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRect::create(rect)));
272     updateItemExtent(newItem);
273 }
274
275 void Recorder::fillRect(const FloatRect& rect, const Color& color)
276 {
277     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithColor::create(rect, color)));
278     updateItemExtent(newItem);
279 }
280
281 void Recorder::fillRect(const FloatRect& rect, Gradient& gradient)
282 {
283     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithGradient::create(rect, gradient)));
284     updateItemExtent(newItem);
285 }
286
287 void Recorder::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
288 {
289     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillCompositedRect::create(rect, color, op, blendMode)));
290     updateItemExtent(newItem);
291 }
292
293 void Recorder::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
294 {
295     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRoundedRect::create(rect, color, blendMode)));
296     updateItemExtent(newItem);
297 }
298
299 void Recorder::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
300 {
301     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithRoundedHole::create(rect, roundedHoleRect, color)));
302     updateItemExtent(newItem);
303 }
304
305 void Recorder::fillPath(const Path& path)
306 {
307     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillPath::create(path)));
308     updateItemExtent(newItem);
309 }
310
311 void Recorder::fillEllipse(const FloatRect& rect)
312 {
313     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillEllipse::create(rect)));
314     updateItemExtent(newItem);
315 }
316
317 void Recorder::strokeRect(const FloatRect& rect, float lineWidth)
318 {
319     DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeRect::create(rect, lineWidth)));
320     updateItemExtent(newItem);
321 }
322
323 void Recorder::strokePath(const Path& path)
324 {
325     DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokePath::create(path)));
326     updateItemExtent(newItem);
327 }
328
329 void Recorder::strokeEllipse(const FloatRect& rect)
330 {
331     DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeEllipse::create(rect)));
332     updateItemExtent(newItem);
333 }
334
335 void Recorder::clearRect(const FloatRect& rect)
336 {
337     DrawingItem& newItem = downcast<DrawingItem>(appendItem(ClearRect::create(rect)));
338     updateItemExtent(newItem);
339 }
340
341 #if USE(CG)
342 void Recorder::applyStrokePattern()
343 {
344     appendItem(ApplyStrokePattern::create());
345 }
346
347 void Recorder::applyFillPattern()
348 {
349     appendItem(ApplyFillPattern::create());
350 }
351 #endif
352
353 void Recorder::clip(const FloatRect& rect)
354 {
355     currentState().clipBounds.intersect(rect);
356     appendItem(Clip::create(rect));
357 }
358
359 void Recorder::clipOut(const FloatRect& rect)
360 {
361     appendItem(ClipOut::create(rect));
362 }
363
364 void Recorder::clipOut(const Path& path)
365 {
366     appendItem(ClipOutToPath::create(path));
367 }
368
369 void Recorder::clipPath(const Path& path, WindRule windRule)
370 {
371     currentState().clipBounds.intersect(path.fastBoundingRect());
372     appendItem(ClipPath::create(path, windRule));
373 }
374
375 IntRect Recorder::clipBounds()
376 {
377     WTFLogAlways("Getting the clip bounds not yet supported with DisplayList::Recorder.");
378     return IntRect(-2048, -2048, 4096, 4096);
379 }
380
381 void Recorder::clipToImageBuffer(ImageBuffer&, const FloatRect&)
382 {
383     WTFLogAlways("GraphicsContext::clipToImageBuffer is not compatible with DisplayList::Recorder.");
384 }
385
386 void Recorder::applyDeviceScaleFactor(float deviceScaleFactor)
387 {
388     // FIXME: this changes the baseCTM, which will invalidate all of our cached extents.
389     // Assert that it's only called early on?
390     appendItem(ApplyDeviceScaleFactor::create(deviceScaleFactor));
391 }
392
393 FloatRect Recorder::roundToDevicePixels(const FloatRect& rect, GraphicsContext::RoundingMode)
394 {
395     WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with DisplayList::Recorder.");
396     return rect;
397 }
398
399 Item& Recorder::appendItem(Ref<Item>&& item)
400 {
401     willAppendItem(item.get());
402     return m_displayList.append(WTFMove(item));
403 }
404
405 void Recorder::updateItemExtent(DrawingItem& item) const
406 {
407     if (std::optional<FloatRect> rect = item.localBounds(graphicsContext()))
408         item.setExtent(extentFromLocalBounds(rect.value()));
409 }
410
411 // FIXME: share with ShadowData
412 static inline float shadowPaintingExtent(float blurRadius)
413 {
414     // Blurring uses a Gaussian function whose std. deviation is m_radius/2, and which in theory
415     // extends to infinity. In 8-bit contexts, however, rounding causes the effect to become
416     // undetectable at around 1.4x the radius.
417     const float radiusExtentMultiplier = 1.4;
418     return ceilf(blurRadius * radiusExtentMultiplier);
419 }
420
421 FloatRect Recorder::extentFromLocalBounds(const FloatRect& rect) const
422 {
423     FloatRect bounds = rect;
424     const ContextState& state = currentState();
425
426     FloatSize shadowOffset;
427     float shadowRadius;
428     Color shadowColor;
429     if (graphicsContext().getShadow(shadowOffset, shadowRadius, shadowColor)) {
430         FloatRect shadowExtent= bounds;
431         shadowExtent.move(shadowOffset);
432         shadowExtent.inflate(shadowPaintingExtent(shadowRadius));
433         bounds.unite(shadowExtent);
434     }
435     
436     FloatRect clippedExtent = intersection(state.clipBounds, bounds);
437     return state.ctm.mapRect(clippedExtent);
438 }
439
440 const Recorder::ContextState& Recorder::currentState() const
441 {
442     ASSERT(m_stateStack.size());
443     return m_stateStack.last();
444 }
445
446 Recorder::ContextState& Recorder::currentState()
447 {
448     ASSERT(m_stateStack.size());
449     return m_stateStack.last();
450 }
451
452 const AffineTransform& Recorder::ctm() const
453 {
454     return currentState().ctm;
455 }
456
457 const FloatRect& Recorder::clipBounds() const
458 {
459     return currentState().clipBounds;
460 }
461
462 void Recorder::ContextState::translate(float x, float y)
463 {
464     ctm.translate(x, y);
465     clipBounds.move(-x, -y);
466 }
467
468 void Recorder::ContextState::rotate(float angleInRadians)
469 {
470     double angleInDegrees = rad2deg(static_cast<double>(angleInRadians));
471     ctm.rotate(angleInDegrees);
472     
473     AffineTransform rotation;
474     rotation.rotate(angleInDegrees);
475
476     if (std::optional<AffineTransform> inverse = rotation.inverse())
477         clipBounds = inverse.value().mapRect(clipBounds);
478 }
479
480 void Recorder::ContextState::scale(const FloatSize& size)
481 {
482     ctm.scale(size);
483     clipBounds.scale(1 / size.width(), 1 / size.height());
484 }
485
486 void Recorder::ContextState::concatCTM(const AffineTransform& matrix)
487 {
488     ctm *= matrix;
489
490     if (std::optional<AffineTransform> inverse = matrix.inverse())
491         clipBounds = inverse.value().mapRect(clipBounds);
492 }
493
494 } // namespace DisplayList
495 } // namespace WebCore