[WTF] Import std::optional reference implementation as WTF::Optional
[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 "TextStream.h"
34 #include <wtf/MathExtras.h>
35
36 namespace WebCore {
37 namespace DisplayList {
38
39 Recorder::Recorder(GraphicsContext& context, DisplayList& displayList, const FloatRect& initialClip, const AffineTransform& baseCTM)
40     : m_graphicsContext(context)
41     , m_displayList(displayList)
42 {
43     LOG_WITH_STREAM(DisplayLists, stream << "\nRecording with clip " << initialClip);
44     m_graphicsContext.setDisplayListRecorder(this);
45     m_stateStack.append(ContextState(baseCTM, initialClip));
46 }
47
48 Recorder::~Recorder()
49 {
50     ASSERT(m_stateStack.size() == 1); // If this fires, it indicates mismatched save/restore.
51     LOG(DisplayLists, "Recorded display list:\n%s", m_displayList.description().data());
52 }
53
54 void Recorder::willAppendItem(const Item& item)
55 {
56     if (item.isDrawingItem()
57 #if USE(CG)
58         || item.type() == ItemType::ApplyStrokePattern || item.type() == ItemType::ApplyStrokePattern
59 #endif
60     ) {
61         GraphicsContextStateChange& stateChanges = currentState().stateChange;
62         GraphicsContextState::StateChangeFlags changesFromLastState = stateChanges.changesFromState(currentState().lastDrawingState);
63         if (changesFromLastState) {
64             LOG_WITH_STREAM(DisplayLists, stream << "pre-drawing, saving state " << GraphicsContextStateChange(stateChanges.m_state, changesFromLastState));
65             m_displayList.append(SetState::create(stateChanges.m_state, changesFromLastState));
66             stateChanges.m_changeFlags = 0;
67             currentState().lastDrawingState = stateChanges.m_state;
68         }
69         currentState().wasUsedForDrawing = true;
70     }
71 }
72
73 void Recorder::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
74 {
75     currentState().stateChange.accumulate(state, flags);
76 }
77
78 void Recorder::clearShadow()
79 {
80     appendItem(ClearShadow::create());
81 }
82
83 void Recorder::setLineCap(LineCap lineCap)
84 {
85     appendItem(SetLineCap::create(lineCap));
86 }
87
88 void Recorder::setLineDash(const DashArray& dashArray, float dashOffset)
89 {
90     appendItem(SetLineDash::create(dashArray, dashOffset));
91 }
92
93 void Recorder::setLineJoin(LineJoin lineJoin)
94 {
95     appendItem(SetLineJoin::create(lineJoin));
96 }
97
98 void Recorder::setMiterLimit(float miterLimit)
99 {
100     appendItem(SetMiterLimit::create(miterLimit));
101 }
102
103 void Recorder::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& startPoint, FontSmoothingMode smoothingMode)
104 {
105     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawGlyphs::create(font, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs, FloatPoint(), toFloatSize(startPoint), smoothingMode)));
106     updateItemExtent(newItem);
107 }
108
109 void Recorder::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
110 {
111     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawImage::create(image, destination, source, imagePaintingOptions)));
112     updateItemExtent(newItem);
113 }
114
115 void 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 }
120
121 #if USE(CG) || USE(CAIRO)
122 void Recorder::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
123 {
124     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawNativeImage::create(image, imageSize, destRect, srcRect, op, blendMode, orientation)));
125     updateItemExtent(newItem);
126 }
127 #endif
128
129 void Recorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
130 {
131     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawTiledScaledImage::create(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions)));
132     updateItemExtent(newItem);
133 }
134
135 void Recorder::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
136 {
137     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPattern::create(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode)));
138     updateItemExtent(newItem);
139 }
140
141 void Recorder::save()
142 {
143     appendItem(Save::create());
144     m_stateStack.append(m_stateStack.last().cloneForSave(m_displayList.itemCount() - 1));
145 }
146
147 void Recorder::restore()
148 {
149     if (!m_stateStack.size())
150         return;
151     
152     bool stateUsedForDrawing = currentState().wasUsedForDrawing;
153     size_t saveIndex = currentState().saveItemIndex;
154     
155     m_stateStack.removeLast();
156     // Have to avoid eliding nested Save/Restore when a descendant state contains drawing items.
157     currentState().wasUsedForDrawing |= stateUsedForDrawing;
158
159     if (!stateUsedForDrawing && saveIndex) {
160         // This Save/Restore didn't contain any drawing items. Roll back to just before the last save.
161         m_displayList.removeItemsFromIndex(saveIndex);
162         return;
163     }
164
165     appendItem(Restore::create());
166     
167     if (saveIndex) {
168         Save& saveItem = downcast<Save>(m_displayList.itemAt(saveIndex));
169         saveItem.setRestoreIndex(m_displayList.itemCount() - 1);
170     }
171 }
172
173 void Recorder::translate(float x, float y)
174 {
175     currentState().translate(x, y);
176     appendItem(Translate::create(x, y));
177 }
178
179 void Recorder::rotate(float angleInRadians)
180 {
181     currentState().rotate(angleInRadians);
182     appendItem(Rotate::create(angleInRadians));
183 }
184
185 void Recorder::scale(const FloatSize& size)
186 {
187     currentState().scale(size);
188     appendItem(Scale::create(size));
189 }
190
191 void Recorder::concatCTM(const AffineTransform& transform)
192 {
193     currentState().concatCTM(transform);
194     appendItem(ConcatenateCTM::create(transform));
195 }
196
197 void Recorder::beginTransparencyLayer(float opacity)
198 {
199     DrawingItem& newItem = downcast<DrawingItem>(appendItem(BeginTransparencyLayer::create(opacity)));
200     updateItemExtent(newItem);
201 }
202
203 void Recorder::endTransparencyLayer()
204 {
205     appendItem(EndTransparencyLayer::create());
206 }
207
208 void Recorder::drawRect(const FloatRect& rect, float borderThickness)
209 {
210     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawRect::create(rect, borderThickness)));
211     updateItemExtent(newItem);
212 }
213
214 void Recorder::drawLine(const FloatPoint& point1, const FloatPoint& point2)
215 {
216     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLine::create(point1, point2)));
217     updateItemExtent(newItem);
218 }
219
220 void Recorder::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, float strokeThickness)
221 {
222     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLinesForText::create(FloatPoint(), toFloatSize(point), widths, printing, doubleLines, strokeThickness)));
223     updateItemExtent(newItem);
224 }
225
226 void Recorder::drawLineForDocumentMarker(const FloatPoint& point, float width, GraphicsContext::DocumentMarkerLineStyle style)
227 {
228     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawLineForDocumentMarker::create(point, width, style)));
229     updateItemExtent(newItem);
230 }
231
232 void Recorder::drawEllipse(const FloatRect& rect)
233 {
234     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawEllipse::create(rect)));
235     updateItemExtent(newItem);
236 }
237
238 void Recorder::drawPath(const Path& path)
239 {
240     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawPath::create(path)));
241     updateItemExtent(newItem);
242 }
243
244 void Recorder::drawFocusRing(const Path& path, int width, int offset, const Color& color)
245 {
246     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingPath::create(path, width, offset, color)));
247     updateItemExtent(newItem);
248 }
249
250 void Recorder::drawFocusRing(const Vector<FloatRect>& rects, int width, int offset, const Color& color)
251 {
252     DrawingItem& newItem = downcast<DrawingItem>(appendItem(DrawFocusRingRects::create(rects, width, offset, color)));
253     updateItemExtent(newItem);
254 }
255
256 void Recorder::fillRect(const FloatRect& rect)
257 {
258     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRect::create(rect)));
259     updateItemExtent(newItem);
260 }
261
262 void Recorder::fillRect(const FloatRect& rect, const Color& color)
263 {
264     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithColor::create(rect, color)));
265     updateItemExtent(newItem);
266 }
267
268 void Recorder::fillRect(const FloatRect& rect, Gradient& gradient)
269 {
270     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithGradient::create(rect, gradient)));
271     updateItemExtent(newItem);
272 }
273
274 void Recorder::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
275 {
276     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillCompositedRect::create(rect, color, op, blendMode)));
277     updateItemExtent(newItem);
278 }
279
280 void Recorder::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
281 {
282     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRoundedRect::create(rect, color, blendMode)));
283     updateItemExtent(newItem);
284 }
285
286 void Recorder::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
287 {
288     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillRectWithRoundedHole::create(rect, roundedHoleRect, color)));
289     updateItemExtent(newItem);
290 }
291
292 void Recorder::fillPath(const Path& path)
293 {
294     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillPath::create(path)));
295     updateItemExtent(newItem);
296 }
297
298 void Recorder::fillEllipse(const FloatRect& rect)
299 {
300     DrawingItem& newItem = downcast<DrawingItem>(appendItem(FillEllipse::create(rect)));
301     updateItemExtent(newItem);
302 }
303
304 void Recorder::strokeRect(const FloatRect& rect, float lineWidth)
305 {
306     DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeRect::create(rect, lineWidth)));
307     updateItemExtent(newItem);
308 }
309
310 void Recorder::strokePath(const Path& path)
311 {
312     DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokePath::create(path)));
313     updateItemExtent(newItem);
314 }
315
316 void Recorder::strokeEllipse(const FloatRect& rect)
317 {
318     DrawingItem& newItem = downcast<DrawingItem>(appendItem(StrokeEllipse::create(rect)));
319     updateItemExtent(newItem);
320 }
321
322 void Recorder::clearRect(const FloatRect& rect)
323 {
324     DrawingItem& newItem = downcast<DrawingItem>(appendItem(ClearRect::create(rect)));
325     updateItemExtent(newItem);
326 }
327
328 #if USE(CG)
329 void Recorder::applyStrokePattern()
330 {
331     appendItem(ApplyStrokePattern::create());
332 }
333
334 void Recorder::applyFillPattern()
335 {
336     appendItem(ApplyFillPattern::create());
337 }
338 #endif
339
340 void Recorder::clip(const FloatRect& rect)
341 {
342     currentState().clipBounds.intersect(rect);
343     appendItem(Clip::create(rect));
344 }
345
346 void Recorder::clipOut(const FloatRect& rect)
347 {
348     appendItem(ClipOut::create(rect));
349 }
350
351 void Recorder::clipOut(const Path& path)
352 {
353     appendItem(ClipOutToPath::create(path));
354 }
355
356 void Recorder::clipPath(const Path& path, WindRule windRule)
357 {
358     currentState().clipBounds.intersect(path.fastBoundingRect());
359     appendItem(ClipPath::create(path, windRule));
360 }
361
362 void Recorder::applyDeviceScaleFactor(float deviceScaleFactor)
363 {
364     // FIXME: this changes the baseCTM, which will invalidate all of our cached extents.
365     // Assert that it's only called early on?
366     appendItem(ApplyDeviceScaleFactor::create(deviceScaleFactor));
367 }
368
369 Item& Recorder::appendItem(Ref<Item>&& item)
370 {
371     willAppendItem(item.get());
372     return m_displayList.append(WTFMove(item));
373 }
374
375 void Recorder::updateItemExtent(DrawingItem& item) const
376 {
377     if (std::optional<FloatRect> rect = item.localBounds(m_graphicsContext))
378         item.setExtent(extentFromLocalBounds(rect.value()));
379 }
380
381 // FIXME: share with ShadowData
382 static inline float shadowPaintingExtent(float blurRadius)
383 {
384     // Blurring uses a Gaussian function whose std. deviation is m_radius/2, and which in theory
385     // extends to infinity. In 8-bit contexts, however, rounding causes the effect to become
386     // undetectable at around 1.4x the radius.
387     const float radiusExtentMultiplier = 1.4;
388     return ceilf(blurRadius * radiusExtentMultiplier);
389 }
390
391 FloatRect Recorder::extentFromLocalBounds(const FloatRect& rect) const
392 {
393     FloatRect bounds = rect;
394     const ContextState& state = currentState();
395
396     FloatSize shadowOffset;
397     float shadowRadius;
398     Color shadowColor;
399     if (m_graphicsContext.getShadow(shadowOffset, shadowRadius, shadowColor)) {
400         FloatRect shadowExtent= bounds;
401         shadowExtent.move(shadowOffset);
402         shadowExtent.inflate(shadowPaintingExtent(shadowRadius));
403         bounds.unite(shadowExtent);
404     }
405     
406     FloatRect clippedExtent = intersection(state.clipBounds, bounds);
407     return state.ctm.mapRect(clippedExtent);
408 }
409
410 const Recorder::ContextState& Recorder::currentState() const
411 {
412     ASSERT(m_stateStack.size());
413     return m_stateStack.last();
414 }
415
416 Recorder::ContextState& Recorder::currentState()
417 {
418     ASSERT(m_stateStack.size());
419     return m_stateStack.last();
420 }
421
422 const AffineTransform& Recorder::ctm() const
423 {
424     return currentState().ctm;
425 }
426
427 const FloatRect& Recorder::clipBounds() const
428 {
429     return currentState().clipBounds;
430 }
431
432 void Recorder::ContextState::translate(float x, float y)
433 {
434     ctm.translate(x, y);
435     clipBounds.move(-x, -y);
436 }
437
438 void Recorder::ContextState::rotate(float angleInRadians)
439 {
440     double angleInDegrees = rad2deg(static_cast<double>(angleInRadians));
441     ctm.rotate(angleInDegrees);
442     
443     AffineTransform rotation;
444     rotation.rotate(angleInDegrees);
445
446     if (std::optional<AffineTransform> inverse = rotation.inverse())
447         clipBounds = inverse.value().mapRect(clipBounds);
448 }
449
450 void Recorder::ContextState::scale(const FloatSize& size)
451 {
452     ctm.scale(size);
453     clipBounds.scale(1 / size.width(), 1 / size.height());
454 }
455
456 void Recorder::ContextState::concatCTM(const AffineTransform& matrix)
457 {
458     ctm *= matrix;
459
460     if (std::optional<AffineTransform> inverse = matrix.inverse())
461         clipBounds = inverse.value().mapRect(clipBounds);
462 }
463
464 } // namespace DisplayList
465 } // namespace WebCore