fa9fb4cf9eeaf791c21ab1172f52fa177be2fc3c
[WebKit-https.git] / Source / WebCore / platform / graphics / win / GraphicsContextDirect2D.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 "GraphicsContext.h"
28
29 #include "COMPtr.h"
30 #include "CurrentTime.h"
31 #include "DisplayListRecorder.h"
32 #include "FloatRoundedRect.h"
33 #include "GraphicsContextPlatformPrivateDirect2D.h"
34 #include "ImageBuffer.h"
35 #include "Logging.h"
36 #include "NotImplemented.h"
37 #include "URL.h"
38 #include <d2d1.h>
39 #include <d2d1effects.h>
40 #include <dwrite.h>
41
42 #pragma warning (disable : 4756)
43
44 using namespace std;
45
46 namespace WebCore {
47
48 GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha)
49 {
50     platformInit(hdc, hasAlpha);
51 }
52
53 GraphicsContext::GraphicsContext(HDC hdc, ID2D1DCRenderTarget** renderTarget, RECT rect, bool hasAlpha)
54 {
55     m_data->m_hdc = hdc;
56
57     // Create a DC render target.
58     auto targetProperties = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
59         D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
60         0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT);
61
62     HRESULT hr = GraphicsContext::systemFactory()->CreateDCRenderTarget(&targetProperties, renderTarget);
63     RELEASE_ASSERT(SUCCEEDED(hr));
64
65     (*renderTarget)->BindDC(hdc, &rect);
66
67     m_data = new GraphicsContextPlatformPrivate(*renderTarget);
68 }
69
70 ID2D1Factory* GraphicsContext::systemFactory()
71 {
72     static ID2D1Factory* direct2DFactory = nullptr;
73     if (!direct2DFactory) {
74         HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &direct2DFactory);
75         RELEASE_ASSERT(SUCCEEDED(hr));
76     }
77
78     return direct2DFactory;
79 }
80
81 ID2D1RenderTarget* GraphicsContext::defaultRenderTarget()
82 {
83     static ID2D1RenderTarget* defaultRenderTarget = nullptr;
84     if (!defaultRenderTarget) {
85         auto renderTargetProperties = D2D1::RenderTargetProperties();
86         renderTargetProperties.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
87         auto hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(::GetDesktopWindow(), D2D1::SizeU(10, 10));
88         HRESULT hr = systemFactory()->CreateHwndRenderTarget(&renderTargetProperties, &hwndRenderTargetProperties, reinterpret_cast<ID2D1HwndRenderTarget**>(&defaultRenderTarget));
89         RELEASE_ASSERT(SUCCEEDED(hr));
90     }
91
92     return defaultRenderTarget;
93 }
94
95 void GraphicsContext::setDidBeginDraw(bool didBeginDraw)
96 {
97     RELEASE_ASSERT(m_data);
98     m_data->m_didBeginDraw = didBeginDraw;
99 }
100
101 bool GraphicsContext::didBeginDraw() const
102 {
103     return m_data->m_didBeginDraw;
104 }
105
106 void GraphicsContext::platformInit(HDC hdc, bool hasAlpha)
107 {
108     if (!hdc)
109         return;
110
111     COMPtr<ID2D1RenderTarget> renderTarget;
112     m_data = new GraphicsContextPlatformPrivate(renderTarget.get());
113     m_data->m_hdc = hdc;
114     // Make sure the context starts in sync with our state.
115     setPlatformFillColor(fillColor());
116     setPlatformStrokeColor(strokeColor());
117     setPlatformStrokeThickness(strokeThickness());
118     // FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
119 }
120
121 void GraphicsContext::platformInit(ID2D1RenderTarget* renderTarget)
122 {
123     if (!renderTarget)
124         return;
125
126     m_data = new GraphicsContextPlatformPrivate(renderTarget);
127
128     // Make sure the context starts in sync with our state.
129     setPlatformFillColor(fillColor());
130     setPlatformStrokeColor(strokeColor());
131     setPlatformStrokeThickness(strokeThickness());
132     // FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
133 }
134
135 void GraphicsContext::platformDestroy()
136 {
137     delete m_data;
138 }
139
140 ID2D1RenderTarget* GraphicsContext::platformContext() const
141 {
142     ASSERT(!paintingDisabled());
143     ASSERT(m_data->renderTarget());
144     return m_data->renderTarget();
145 }
146
147 void GraphicsContext::savePlatformState()
148 {
149     ASSERT(!paintingDisabled());
150     ASSERT(!isRecording());
151
152     // Note: Do not use this function within this class implementation, since we want to avoid the extra
153     // save of the secondary context (in GraphicsContextPlatformPrivateDirect2D.h).
154     m_data->save();
155 }
156
157 void GraphicsContext::restorePlatformState()
158 {
159     ASSERT(!paintingDisabled());
160     ASSERT(!isRecording());
161
162     // Note: Do not use this function within this class implementation, since we want to avoid the extra
163     // restore of the secondary context (in GraphicsContextPlatformPrivateDirect2D.h).
164     m_data->restore();
165     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
166 }
167
168 void GraphicsContext::drawNativeImage(const COMPtr<ID2D1Bitmap>& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
169 {
170     if (paintingDisabled())
171         return;
172
173     if (isRecording()) {
174         // FIXME: Implement DisplayListRecorder support for drawNativeImage.
175         // m_displayListRecorder->drawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation);
176         notImplemented();
177         return;
178     }
179
180     auto bitmapSize = image->GetSize();
181
182     float currHeight = orientation.usesWidthAsHeight() ? bitmapSize.width : bitmapSize.height;
183     if (currHeight <= srcRect.y())
184         return;
185
186     auto context = platformContext();
187
188     D2D1_MATRIX_3X2_F ctm;
189     context->GetTransform(&ctm);
190
191     AffineTransform transform(ctm);
192
193     D2DContextStateSaver stateSaver(*m_data);
194
195     bool shouldUseSubimage = false;
196
197     // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image
198     // and then set a clip to the portion that we want to display.
199     FloatRect adjustedDestRect = destRect;
200
201     if (srcRect.size() != imageSize) {
202         // FIXME: Implement image scaling
203         notImplemented();
204     }
205
206     // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly.
207     if (!shouldUseSubimage && currHeight < imageSize.height())
208         adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / imageSize.height());
209
210     setPlatformCompositeOperation(op, blendMode);
211
212     // ImageOrientation expects the origin to be at (0, 0).
213     transform.translate(adjustedDestRect.x(), adjustedDestRect.y());
214     context->SetTransform(transform);
215     adjustedDestRect.setLocation(FloatPoint());
216
217     if (orientation != DefaultImageOrientation) {
218         this->concatCTM(orientation.transformFromDefault(adjustedDestRect.size()));
219         if (orientation.usesWidthAsHeight()) {
220             // The destination rect will have it's width and height already reversed for the orientation of
221             // the image, as it was needed for page layout, so we need to reverse it back here.
222             adjustedDestRect = FloatRect(adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.height(), adjustedDestRect.width());
223         }
224     }
225
226     context->SetTags(1, __LINE__);
227
228     drawWithoutShadow(adjustedDestRect, [this, image, adjustedDestRect, srcRect](ID2D1RenderTarget* renderTarget) {
229         renderTarget->DrawBitmap(image.get(), adjustedDestRect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, static_cast<D2D1_RECT_F>(srcRect));
230     });
231
232     if (!stateSaver.didSave())
233         context->SetTransform(ctm);
234 }
235
236 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
237 {
238 }
239
240 void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point)
241 {
242 }
243
244 void GraphicsContext::drawFocusRing(const Path& path, float width, float offset, const Color& color)
245 {
246 }
247
248 void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
249 {
250 }
251
252 void GraphicsContext::updateDocumentMarkerResources()
253 {
254 }
255
256 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& point, float width, DocumentMarkerLineStyle style)
257 {
258 }
259
260 COMPtr<ID2D1SolidColorBrush> GraphicsContextPlatformPrivate::brushWithColor(const D2D1_COLOR_F& color)
261 {
262     RGBA32 colorKey = makeRGBA32FromFloats(color.r, color.g, color.b, color.a);
263
264     if (!colorKey) {
265         if (!m_zeroBrush)
266             m_renderTarget->CreateSolidColorBrush(color, &m_zeroBrush);
267         return m_zeroBrush;
268     }
269
270     if (colorKey == 0xFFFFFFFF) {
271         if (!m_whiteBrush)
272             m_renderTarget->CreateSolidColorBrush(color, &m_whiteBrush);
273         return m_whiteBrush;
274     }
275
276     auto existingBrush = m_solidColoredBrushCache.ensure(colorKey, [this, color] {
277         ID2D1SolidColorBrush* colorBrush = nullptr;
278         m_renderTarget->CreateSolidColorBrush(color, &colorBrush);
279         return colorBrush;
280     });
281
282     return existingBrush.iterator->value;
283 }
284
285 void GraphicsContextPlatformPrivate::clip(const FloatRect& rect)
286 {
287     if (m_renderStates.isEmpty())
288         save();
289
290     m_renderTarget->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
291     m_renderStates.last().m_clips.append(GraphicsContextPlatformPrivate::AxisAlignedClip);
292 }
293
294 void GraphicsContextPlatformPrivate::clip(const Path& path)
295 {
296     clip(path.platformPath());
297 }
298
299 void GraphicsContextPlatformPrivate::clip(ID2D1Geometry* path)
300 {
301     ASSERT(m_renderStates.size());
302     if (!m_renderStates.size())
303         return;
304
305     COMPtr<ID2D1Layer> clipLayer;
306     HRESULT hr = m_renderTarget->CreateLayer(&clipLayer);
307     ASSERT(SUCCEEDED(hr));
308     if (!SUCCEEDED(hr))
309         return;
310
311     m_renderTarget->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), path), clipLayer.get());
312     m_renderStates.last().m_clips.append(GraphicsContextPlatformPrivate::LayerClip);
313     m_renderStates.last().m_activeLayer = clipLayer;
314 }
315
316 void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& affineTransform)
317 {
318     ASSERT(m_renderTarget.get());
319
320     D2D1_MATRIX_3X2_F currentTransform;
321     m_renderTarget->GetTransform(&currentTransform);
322
323     m_renderTarget->SetTransform(affineTransform * AffineTransform(currentTransform));
324 }
325
326 void GraphicsContextPlatformPrivate::flush()
327 {
328     ASSERT(m_renderTarget.get());
329     D2D1_TAG first, second;
330     HRESULT hr = m_renderTarget->Flush(&first, &second);
331
332     RELEASE_ASSERT(SUCCEEDED(hr));
333 }
334
335 void GraphicsContextPlatformPrivate::endDraw()
336 {
337     ASSERT(m_renderTarget.get());
338     D2D1_TAG first, second;
339     HRESULT hr = m_renderTarget->EndDraw(&first, &second);
340
341     RELEASE_ASSERT(SUCCEEDED(hr));
342 }
343
344 void GraphicsContextPlatformPrivate::restore()
345 {
346     ASSERT(m_renderTarget.get());
347
348     auto restoreState = m_renderStates.takeLast();
349     m_renderTarget->RestoreDrawingState(restoreState.m_drawingStateBlock.get());
350
351     for (auto clipType = restoreState.m_clips.rbegin(); clipType != restoreState.m_clips.rend(); ++clipType) {
352         if (*clipType == GraphicsContextPlatformPrivate::AxisAlignedClip)
353             m_renderTarget->PopAxisAlignedClip();
354         else
355             m_renderTarget->PopLayer();
356     }
357 }
358
359 void GraphicsContextPlatformPrivate::save()
360 {
361     ASSERT(m_renderTarget.get());
362
363     RenderState currentState;
364     GraphicsContext::systemFactory()->CreateDrawingStateBlock(&currentState.m_drawingStateBlock);
365
366     m_renderTarget->SaveDrawingState(currentState.m_drawingStateBlock.get());
367
368     m_renderStates.append(currentState);
369 }
370
371 void GraphicsContextPlatformPrivate::scale(const FloatSize& size)
372 {
373     ASSERT(m_renderTarget.get());
374
375     D2D1_MATRIX_3X2_F currentTransform;
376     m_renderTarget->GetTransform(&currentTransform);
377
378     auto scale = D2D1::Matrix3x2F::Scale(size);
379     m_renderTarget->SetTransform(scale * currentTransform);
380 }
381
382 void GraphicsContextPlatformPrivate::setCTM(const AffineTransform& transform)
383 {
384     ASSERT(m_renderTarget.get());
385     m_renderTarget->SetTransform(transform);
386 }
387
388 void GraphicsContextPlatformPrivate::translate(float x, float y)
389 {
390     ASSERT(m_renderTarget.get());
391
392     D2D1_MATRIX_3X2_F currentTransform;
393     m_renderTarget->GetTransform(&currentTransform);
394
395     auto translation = D2D1::Matrix3x2F::Translation(x, y);
396     m_renderTarget->SetTransform(translation * currentTransform);
397 }
398
399 void GraphicsContextPlatformPrivate::rotate(float angle)
400 {
401     ASSERT(m_renderTarget.get());
402
403     D2D1_MATRIX_3X2_F currentTransform;
404     m_renderTarget->GetTransform(&currentTransform);
405
406     auto rotation = D2D1::Matrix3x2F::Rotation(rad2deg(angle));
407     m_renderTarget->SetTransform(rotation * currentTransform);
408 }
409
410 D2D1_COLOR_F GraphicsContext::colorWithGlobalAlpha(const Color& color) const
411 {
412     float colorAlpha = color.alpha() / 255.0f;
413     float globalAlpha = m_state.alpha;
414
415     return D2D1::ColorF(color.rgb(), globalAlpha * colorAlpha);
416 }
417
418 ID2D1Brush* GraphicsContext::solidStrokeBrush() const
419 {
420     return m_data->m_solidStrokeBrush.get();
421 }
422
423 ID2D1Brush* GraphicsContext::solidFillBrush() const
424 {
425     return m_data->m_solidFillBrush.get();
426 }
427
428 ID2D1Brush* GraphicsContext::patternStrokeBrush() const
429 {
430     return m_data->m_patternStrokeBrush.get();
431 }
432
433 ID2D1Brush* GraphicsContext::patternFillBrush() const
434 {
435     return m_data->m_patternFillBrush.get();
436 }
437
438 void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
439 {
440     if (paintingDisabled() || !patternTransform.isInvertible())
441         return;
442
443     if (isRecording()) {
444         m_displayListRecorder->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode);
445         return;
446     }
447
448     auto context = platformContext();
449     D2DContextStateSaver stateSaver(*m_data);
450
451     m_data->clip(destRect);
452
453     setPlatformCompositeOperation(op, blendMode);
454
455     auto bitmapBrushProperties = D2D1::BitmapBrushProperties();
456     bitmapBrushProperties.extendModeX = D2D1_EXTEND_MODE_WRAP;
457     bitmapBrushProperties.extendModeY = D2D1_EXTEND_MODE_WRAP;
458
459     // Create a brush transformation so we paint using the section of the image we care about.
460     AffineTransform transformation = patternTransform;
461     transformation.translate(destRect.location());
462
463     auto brushProperties = D2D1::BrushProperties();
464     brushProperties.transform = transformation;
465     brushProperties.opacity = 1.0f;
466
467     auto tileImage = image.nativeImageForCurrentFrame();
468
469     // If we only want a subset of the bitmap, we need to create a cropped bitmap image. According to the documentation,
470     // this does not allocate new bitmap memory.
471     if (image.width() > destRect.width() || image.height() > destRect.height()) {
472         float dpiX = 0;
473         float dpiY = 0;
474         tileImage->GetDpi(&dpiX, &dpiY);
475         auto bitmapProperties = D2D1::BitmapProperties(tileImage->GetPixelFormat(), dpiX, dpiY);
476         COMPtr<ID2D1Bitmap> subImage;
477         HRESULT hr = context->CreateBitmap(IntSize(tileRect.size()), bitmapProperties, &subImage);
478         if (SUCCEEDED(hr)) {
479             D2D1_RECT_U finishRect = IntRect(tileRect);
480             hr = subImage->CopyFromBitmap(nullptr, tileImage.get(), &finishRect);
481             if (SUCCEEDED(hr))
482                 tileImage = subImage;
483         }
484     }
485
486     COMPtr<ID2D1BitmapBrush> patternBrush;
487     HRESULT hr = context->CreateBitmapBrush(tileImage.get(), &bitmapBrushProperties, &brushProperties, &patternBrush);
488
489     drawWithoutShadow(destRect, [this, destRect, patternBrush](ID2D1RenderTarget* renderTarget) {
490         const D2D1_RECT_F d2dRect = destRect;
491         renderTarget->FillRectangle(&d2dRect, patternBrush.get());
492     });
493 }
494
495 void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
496 {
497     if (paintingDisabled())
498         return;
499
500     FloatSize bufferDestinationSize = buffer.sizeForDestinationSize(destRect.size());
501     notImplemented();
502 }
503
504 // Draws a filled rectangle with a stroked border.
505 void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
506 {
507     if (paintingDisabled())
508         return;
509
510     if (isRecording()) {
511         m_displayListRecorder->drawRect(rect, borderThickness);
512         return;
513     }
514
515     // FIXME: this function does not handle patterns and gradients like drawPath does, it probably should.
516     ASSERT(!rect.isEmpty());
517
518     auto context = platformContext();
519
520     context->SetTags(1, __LINE__);
521
522     drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
523         const D2D1_RECT_F d2dRect = rect;
524         renderTarget->FillRectangle(&d2dRect, solidFillBrush());
525         renderTarget->DrawRectangle(&d2dRect, solidStrokeBrush(), strokeThickness(), m_data->strokeStyle());
526     });
527 }
528
529 void GraphicsContextPlatformPrivate::setLineCap(LineCap cap)
530 {
531     if (m_lineCap == cap)
532         return;
533
534     D2D1_CAP_STYLE capStyle = D2D1_CAP_STYLE_FLAT;
535     switch (cap) {
536     case RoundCap:
537         capStyle = D2D1_CAP_STYLE_ROUND;
538         break;
539     case SquareCap:
540         capStyle = D2D1_CAP_STYLE_SQUARE;
541         break;
542     case ButtCap:
543     default:
544         capStyle = D2D1_CAP_STYLE_FLAT;
545         break;
546     }
547
548     m_lineCap = capStyle;
549     m_strokeSyleIsDirty = true;
550 }
551
552 void GraphicsContextPlatformPrivate::setLineJoin(LineJoin join)
553 {
554     if (m_lineJoin == join)
555         return;
556
557     D2D1_LINE_JOIN joinStyle = D2D1_LINE_JOIN_MITER;
558     switch (join) {
559     case RoundJoin:
560         joinStyle = D2D1_LINE_JOIN_ROUND;
561         break;
562     case BevelJoin:
563         joinStyle = D2D1_LINE_JOIN_BEVEL;
564         break;
565     case MiterJoin:
566     default:
567         joinStyle = D2D1_LINE_JOIN_MITER;
568         break;
569     }
570
571     m_lineJoin = joinStyle;
572     m_strokeSyleIsDirty = true;
573 }
574
575 void GraphicsContextPlatformPrivate::setStrokeStyle(StrokeStyle strokeStyle)
576 {
577     if (m_strokeStyle == strokeStyle)
578         return;
579
580     m_strokeStyle = strokeStyle;
581     m_strokeSyleIsDirty = true;
582 }
583
584 void GraphicsContextPlatformPrivate::setMiterLimit(float miterLimit)
585 {
586     if (WTF::areEssentiallyEqual(miterLimit, m_miterLimit))
587         return;
588
589     m_miterLimit = miterLimit;
590     m_strokeSyleIsDirty = true;
591 }
592
593 void GraphicsContextPlatformPrivate::setDashOffset(float dashOffset)
594 {
595     if (WTF::areEssentiallyEqual(dashOffset, m_dashOffset))
596         return;
597
598     m_dashOffset = dashOffset;
599     m_strokeSyleIsDirty = true;
600 }
601
602 void GraphicsContextPlatformPrivate::setPatternWidth(float patternWidth)
603 {
604     if (WTF::areEssentiallyEqual(patternWidth, m_patternWidth))
605         return;
606
607     m_patternWidth = patternWidth;
608     m_strokeSyleIsDirty = true;
609 }
610
611 void GraphicsContextPlatformPrivate::setPatternOffset(float patternOffset)
612 {
613     if (WTF::areEssentiallyEqual(patternOffset, m_patternOffset))
614         return;
615
616     m_patternOffset = patternOffset;
617     m_strokeSyleIsDirty = true;
618 }
619
620 void GraphicsContextPlatformPrivate::setStrokeThickness(float thickness)
621 {
622     if (WTF::areEssentiallyEqual(thickness, m_strokeThickness))
623         return;
624
625     m_strokeThickness = thickness;
626     m_strokeSyleIsDirty = true;
627 }
628
629 void GraphicsContextPlatformPrivate::setDashes(const DashArray& dashes)
630 {
631     if (m_dashes == dashes)
632         return;
633
634     m_dashes = dashes;
635     m_strokeSyleIsDirty = true;
636 }
637
638 void GraphicsContextPlatformPrivate::recomputeStrokeStyle()
639 {
640     if (!m_strokeSyleIsDirty)
641         return;
642
643     m_d2dStrokeStyle = nullptr;
644
645     if ((m_strokeStyle != SolidStroke) && (m_strokeStyle != NoStroke)) {
646         float patternOffset = m_patternOffset / m_strokeThickness;
647
648         DashArray dashes = m_dashes;
649
650         // In Direct2D, dashes and dots are defined in terms of the ratio of the dash length to the line thickness.
651         for (auto& dash : dashes)
652             dash /= m_strokeThickness;
653
654         auto strokeStyleProperties = D2D1::StrokeStyleProperties(m_lineCap, m_lineCap, m_lineCap, m_lineJoin, m_strokeThickness, D2D1_DASH_STYLE_CUSTOM, patternOffset);
655         GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes.data(), dashes.size(), &m_d2dStrokeStyle);
656     }
657
658     m_strokeSyleIsDirty = false;
659 }
660
661 ID2D1StrokeStyle* GraphicsContextPlatformPrivate::strokeStyle()
662 {
663     recomputeStrokeStyle();
664     return m_d2dStrokeStyle.get();
665 }
666
667 // This is only used to draw borders.
668 void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2)
669 {
670     if (paintingDisabled())
671         return;
672
673     if (strokeStyle() == NoStroke)
674         return;
675
676     if (isRecording()) {
677         m_displayListRecorder->drawLine(point1, point2);
678         return;
679     }
680
681     float thickness = strokeThickness();
682     bool isVerticalLine = (point1.x() + thickness == point2.x());
683     float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x();
684     if (!thickness || !strokeWidth)
685         return;
686
687     auto context = platformContext();
688
689     StrokeStyle strokeStyle = this->strokeStyle();
690     float cornerWidth = 0;
691     bool drawsDashedLine = strokeStyle == DottedStroke || strokeStyle == DashedStroke;
692
693     COMPtr<ID2D1StrokeStyle> d2dStrokeStyle;
694     D2DContextStateSaver stateSaver(*m_data, drawsDashedLine);
695     if (drawsDashedLine) {
696         // Figure out end points to ensure we always paint corners.
697         cornerWidth = dashedLineCornerWidthForStrokeWidth(strokeWidth);
698         strokeWidth -= 2 * cornerWidth;
699         float patternWidth = dashedLinePatternWidthForStrokeWidth(strokeWidth);
700         // Check if corner drawing sufficiently covers the line.
701         if (strokeWidth <= patternWidth + 1)
702             return;
703
704         float patternOffset = dashedLinePatternOffsetForPatternAndStrokeWidth(patternWidth, strokeWidth);
705         const float dashes[2] = { patternWidth, patternWidth };
706         auto strokeStyleProperties = D2D1::StrokeStyleProperties();
707         GraphicsContext::systemFactory()->CreateStrokeStyle(&strokeStyleProperties, dashes, ARRAYSIZE(dashes), &d2dStrokeStyle);
708
709         m_data->setPatternWidth(patternWidth);
710         m_data->setPatternOffset(patternOffset);
711         m_data->setDashes(DashArray(2, patternWidth));
712
713         d2dStrokeStyle = m_data->strokeStyle();
714     }
715
716     auto centeredPoints = centerLineAndCutOffCorners(isVerticalLine, cornerWidth, point1, point2);
717     auto p1 = centeredPoints[0];
718     auto p2 = centeredPoints[1];
719
720     context->SetTags(1, __LINE__);
721
722     FloatRect boundingRect(p1, p2);
723
724     drawWithoutShadow(boundingRect, [this, p1, p2, d2dStrokeStyle](ID2D1RenderTarget* renderTarget) {
725         renderTarget->DrawLine(p1, p2, solidStrokeBrush(), strokeThickness(), d2dStrokeStyle.get());
726     });
727 }
728
729 void GraphicsContext::drawEllipse(const FloatRect& rect)
730 {
731     if (paintingDisabled())
732         return;
733
734     if (isRecording()) {
735         m_displayListRecorder->drawEllipse(rect);
736         return;
737     }
738
739     auto ellipse = D2D1::Ellipse(D2D1::Point2F(rect.x(), rect.y()), rect.width(), rect.height());
740
741     auto context = platformContext();
742
743     context->SetTags(1, __LINE__);
744
745     drawWithoutShadow(rect, [this, ellipse](ID2D1RenderTarget* renderTarget) {
746         renderTarget->FillEllipse(&ellipse, solidFillBrush());
747
748         renderTarget->DrawEllipse(&ellipse, solidStrokeBrush(), strokeThickness(), m_data->strokeStyle());
749     });
750 }
751
752 void GraphicsContext::applyStrokePattern()
753 {
754     if (paintingDisabled())
755         return;
756
757     auto context = platformContext();
758     AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
759
760     const float patternAlpha = 1;
761     m_data->m_patternStrokeBrush = adoptCOM(m_state.strokePattern->createPlatformPattern(context, patternAlpha, userToBaseCTM));
762 }
763
764 void GraphicsContext::applyFillPattern()
765 {
766     if (paintingDisabled())
767         return;
768
769     auto context = platformContext();
770     AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
771
772     const float patternAlpha = 1;
773     m_data->m_patternFillBrush = adoptCOM(m_state.fillPattern->createPlatformPattern(context, patternAlpha, userToBaseCTM));
774 }
775
776 void GraphicsContext::drawPath(const Path& path)
777 {
778     if (paintingDisabled() || path.isEmpty())
779         return;
780
781     if (isRecording()) {
782         m_displayListRecorder->drawPath(path);
783         return;
784     }
785
786     auto context = platformContext();
787     const GraphicsContextState& state = m_state;
788
789     if (state.fillGradient || state.strokeGradient) {
790         // We don't have any optimized way to fill & stroke a path using gradients
791         // FIXME: Be smarter about this.
792         fillPath(path);
793         strokePath(path);
794         return;
795     }
796
797     if (state.fillPattern)
798         applyFillPattern();
799
800     if (state.strokePattern)
801         applyStrokePattern();
802
803     if (path.activePath())
804         path.activePath()->Close();
805
806     context->SetTags(1, __LINE__);
807
808     auto rect = path.fastBoundingRect();
809     drawWithoutShadow(rect, [this, &path](ID2D1RenderTarget* renderTarget) {
810         auto brush = m_state.strokePattern ? patternStrokeBrush() : solidStrokeBrush();
811         renderTarget->DrawGeometry(path.platformPath(), brush, strokeThickness(), m_data->strokeStyle());
812     });
813 }
814
815 void GraphicsContext::drawWithoutShadow(const FloatRect& /*boundingRect*/, const std::function<void(ID2D1RenderTarget*)>& drawCommands)
816 {
817     auto context = platformContext();
818
819     if (!didBeginDraw())
820         context->BeginDraw();
821
822     drawCommands(context);
823
824     if (!didBeginDraw())
825         m_data->endDraw();
826 }
827
828 void GraphicsContext::drawWithShadow(const FloatRect& boundingRect, const std::function<void(ID2D1RenderTarget*)>& drawCommands)
829 {
830     auto context = platformContext();
831
832     // Render the current geometry to a bitmap context
833     COMPtr<ID2D1BitmapRenderTarget> bitmapTarget;
834     HRESULT hr = context->CreateCompatibleRenderTarget(&bitmapTarget);
835     RELEASE_ASSERT(SUCCEEDED(hr));
836
837     bitmapTarget->BeginDraw();
838     drawCommands(bitmapTarget.get());
839     hr = bitmapTarget->EndDraw();
840     RELEASE_ASSERT(SUCCEEDED(hr));
841
842     COMPtr<ID2D1Bitmap> bitmap;
843     hr = bitmapTarget->GetBitmap(&bitmap);
844     RELEASE_ASSERT(SUCCEEDED(hr));
845
846     COMPtr<ID2D1DeviceContext> deviceContext;
847     hr = context->QueryInterface(&deviceContext);
848     RELEASE_ASSERT(SUCCEEDED(hr));
849
850     // Create the shadow effect
851     COMPtr<ID2D1Effect> shadowEffect;
852     hr = deviceContext->CreateEffect(CLSID_D2D1Shadow, &shadowEffect);
853     RELEASE_ASSERT(SUCCEEDED(hr));
854
855     shadowEffect->SetInput(0, bitmap.get());
856     shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, static_cast<D2D1_VECTOR_4F>(m_state.shadowColor));
857     shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, m_state.shadowBlur);
858
859     COMPtr<ID2D1Effect> transformEffect;
860     hr = deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &transformEffect);
861     RELEASE_ASSERT(SUCCEEDED(hr));
862
863     transformEffect->SetInputEffect(0, shadowEffect.get());
864
865     auto translation = D2D1::Matrix3x2F::Translation(m_state.shadowOffset.width(), m_state.shadowOffset.height());
866     transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, translation);
867
868     COMPtr<ID2D1Effect> compositor;
869     hr = deviceContext->CreateEffect(CLSID_D2D1Composite, &compositor);
870     RELEASE_ASSERT(SUCCEEDED(hr));
871
872     compositor->SetInputEffect(0, transformEffect.get());
873     compositor->SetInput(1, bitmap.get());
874
875     // Flip the context
876     D2D1_MATRIX_3X2_F ctm;
877     deviceContext->GetTransform(&ctm);
878     auto translate = D2D1::Matrix3x2F::Translation(0.0f, deviceContext->GetSize().height);
879     auto flip = D2D1::Matrix3x2F::Scale(D2D1::SizeF(1.0f, -1.0f));
880     deviceContext->SetTransform(ctm * flip * translate);
881
882     deviceContext->BeginDraw();
883     deviceContext->DrawImage(compositor.get(), D2D1_INTERPOLATION_MODE_LINEAR);
884     deviceContext->EndDraw();
885 }
886
887 void GraphicsContext::fillPath(const Path& path)
888 {
889     if (paintingDisabled() || path.isEmpty())
890         return;
891
892     if (isRecording()) {
893         m_displayListRecorder->fillPath(path);
894         return;
895     }
896
897     if (path.activePath()) {
898         // Make sure it's closed. This might fail if the path was already closed, so
899         // ignore the return value.
900         path.activePath()->Close();
901     }
902
903     D2DContextStateSaver stateSaver(*m_data);
904
905     auto context = platformContext();
906
907     context->SetTags(1, __LINE__);
908
909     if (m_state.fillGradient) {
910         context->SetTags(1, __LINE__);
911
912         FloatRect boundingRect = path.fastBoundingRect();
913         auto drawFunction = [this, &path](ID2D1RenderTarget* renderTarget) {
914             renderTarget->FillGeometry(path.platformPath(), m_state.fillGradient->createPlatformGradientIfNecessary(renderTarget));
915         };
916
917         if (hasShadow())
918             drawWithShadow(boundingRect, drawFunction);
919         else
920             drawWithoutShadow(boundingRect, drawFunction);
921         return;
922     }
923
924     if (m_state.fillPattern)
925         applyFillPattern();
926
927     COMPtr<ID2D1GeometryGroup> pathToFill;
928     path.createGeometryWithFillMode(fillRule(), pathToFill);
929
930     context->SetTags(1, __LINE__);
931
932     FloatRect contextRect(FloatPoint(), context->GetSize());
933     drawWithoutShadow(contextRect, [this, &pathToFill](ID2D1RenderTarget* renderTarget) {
934         auto brush = m_state.fillPattern ? patternFillBrush() : solidFillBrush();
935         renderTarget->FillGeometry(pathToFill.get(), brush);
936     });
937 }
938
939 void GraphicsContext::strokePath(const Path& path)
940 {
941     if (paintingDisabled() || path.isEmpty())
942         return;
943
944     if (isRecording()) {
945         m_displayListRecorder->strokePath(path);
946         return;
947     }
948
949     auto context = platformContext();
950     
951     context->SetTags(1, __LINE__);
952
953     if (m_state.strokeGradient) {
954         context->SetTags(1, __LINE__);
955
956         D2DContextStateSaver stateSaver(*m_data);
957         auto boundingRect = path.fastBoundingRect();
958         auto drawFunction = [this, &path](ID2D1RenderTarget* renderTarget) {
959             renderTarget->DrawGeometry(path.platformPath(), m_state.strokeGradient->createPlatformGradientIfNecessary(renderTarget));
960         };
961
962         if (hasShadow())
963             drawWithShadow(boundingRect, drawFunction);
964         else
965             drawWithoutShadow(boundingRect, drawFunction);
966
967         return;
968     }
969
970     if (m_state.strokePattern)
971         applyStrokePattern();
972
973     context->SetTags(1, __LINE__);
974
975     FloatRect contextRect(FloatPoint(), context->GetSize());
976     drawWithoutShadow(contextRect, [this, &path](ID2D1RenderTarget* renderTarget) {
977         auto brush = m_state.strokePattern ? patternStrokeBrush() : solidStrokeBrush();
978         renderTarget->DrawGeometry(path.platformPath(), brush, strokeThickness(), m_data->strokeStyle());
979     });
980 }
981
982 void GraphicsContext::fillRect(const FloatRect& rect)
983 {
984     if (paintingDisabled())
985         return;
986
987     if (isRecording()) {
988         m_displayListRecorder->fillRect(rect);
989         return;
990     }
991
992     auto context = platformContext();
993
994     if (m_state.fillGradient) {
995         context->SetTags(1, __LINE__);
996         D2DContextStateSaver stateSaver(*m_data);
997         auto drawFunction = [this, rect](ID2D1RenderTarget* renderTarget) {
998             const D2D1_RECT_F d2dRect = rect;
999             renderTarget->FillRectangle(&d2dRect, m_state.fillGradient->createPlatformGradientIfNecessary(renderTarget));
1000         };
1001
1002         if (hasShadow())
1003             drawWithShadow(rect, drawFunction);
1004         else
1005             drawWithoutShadow(rect, drawFunction);
1006         return;
1007     }
1008
1009     if (m_state.fillPattern)
1010         applyFillPattern();
1011
1012     context->SetTags(1, __LINE__);
1013
1014     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
1015     if (drawOwnShadow) {
1016         // FIXME: Get ShadowBlur working on Direct2D
1017         // ShadowBlur contextShadow(m_state);
1018         // contextShadow.drawRectShadow(*this, FloatRoundedRect(rect));
1019         notImplemented();
1020     }
1021
1022     drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
1023         const D2D1_RECT_F d2dRect = rect;
1024         auto brush = m_state.fillPattern ? patternFillBrush() : solidFillBrush();
1025         renderTarget->FillRectangle(&d2dRect, brush);
1026     });
1027 }
1028
1029 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
1030 {
1031     if (paintingDisabled())
1032         return;
1033
1034     if (isRecording()) {
1035         m_displayListRecorder->fillRect(rect, color);
1036         return;
1037     }
1038
1039     auto context = platformContext();
1040
1041     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
1042     if (drawOwnShadow) {
1043         // FIXME: Get ShadowBlur working on Direct2D
1044         // ShadowBlur contextShadow(m_state);
1045         // contextShadow.drawRectShadow(*this, FloatRoundedRect(rect));
1046         notImplemented();
1047     }
1048
1049     context->SetTags(1, __LINE__);
1050
1051     drawWithoutShadow(rect, [this, rect, color](ID2D1RenderTarget* renderTarget) {
1052         const D2D1_RECT_F d2dRect = rect;
1053         renderTarget->FillRectangle(&d2dRect, m_data->brushWithColor(colorWithGlobalAlpha(color)).get());
1054     });
1055 }
1056
1057 void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
1058 {
1059     if (paintingDisabled())
1060         return;
1061
1062     ASSERT(!isRecording());
1063
1064     auto context = platformContext();
1065
1066     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
1067     D2DContextStateSaver stateSaver(*m_data, drawOwnShadow);
1068     if (drawOwnShadow) {
1069         // FIXME: Get ShadowBlur working on Direct2D
1070         // ShadowBlur contextShadow(m_state);
1071         // contextShadow.drawRectShadow(*this, rect);
1072         notImplemented();
1073     }
1074
1075     if (!didBeginDraw())
1076         context->BeginDraw();
1077     
1078     context->SetTags(1, __LINE__);
1079
1080     const FloatRect& r = rect.rect();
1081     const FloatRoundedRect::Radii& radii = rect.radii();
1082     bool equalWidths = (radii.topLeft().width() == radii.topRight().width() && radii.topRight().width() == radii.bottomLeft().width() && radii.bottomLeft().width() == radii.bottomRight().width());
1083     bool equalHeights = (radii.topLeft().height() == radii.bottomLeft().height() && radii.bottomLeft().height() == radii.topRight().height() && radii.topRight().height() == radii.bottomRight().height());
1084     bool hasCustomFill = m_state.fillGradient || m_state.fillPattern;
1085     if (!hasCustomFill && equalWidths && equalHeights && radii.topLeft().width() * 2 == r.width() && radii.topLeft().height() * 2 == r.height()) {
1086         auto roundedRect = D2D1::RoundedRect(r, radii.topLeft().width(), radii.topLeft().height());
1087         COMPtr<ID2D1SolidColorBrush> fillBrush = m_data->brushWithColor(colorWithGlobalAlpha(color));
1088         context->FillRoundedRectangle(roundedRect, fillBrush.get());
1089     } else {
1090         D2DContextStateSaver stateSaver(*m_data);
1091         setFillColor(color);
1092
1093         Path path;
1094         path.addRoundedRect(rect);
1095         fillPath(path);
1096     }
1097
1098     if (!didBeginDraw())
1099         m_data->endDraw();
1100
1101     if (drawOwnShadow)
1102         stateSaver.restore();
1103 }
1104
1105 void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
1106 {
1107     if (paintingDisabled())
1108         return;
1109
1110     if (isRecording()) {
1111         m_displayListRecorder->fillRectWithRoundedHole(rect, roundedHoleRect, color);
1112         return;
1113     }
1114
1115     auto context = platformContext();
1116
1117     if (!didBeginDraw())
1118         context->BeginDraw();
1119
1120     context->SetTags(1, __LINE__);
1121
1122     Path path;
1123     path.addRect(rect);
1124
1125     if (!roundedHoleRect.radii().isZero())
1126         path.addRoundedRect(roundedHoleRect);
1127     else
1128         path.addRect(roundedHoleRect.rect());
1129
1130     WindRule oldFillRule = fillRule();
1131     Color oldFillColor = fillColor();
1132
1133     setFillRule(RULE_EVENODD);
1134     setFillColor(color);
1135
1136     // fillRectWithRoundedHole() assumes that the edges of rect are clipped out, so we only care about shadows cast around inside the hole.
1137     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms;
1138     D2DContextStateSaver stateSaver(*m_data, drawOwnShadow);
1139     if (drawOwnShadow) {
1140         // FIXME: Get ShadowBlur working on Direct2D
1141         // ShadowBlur contextShadow(m_state);
1142         // contextShadow.drawRectShadow(*this, rect);
1143         notImplemented();
1144     }
1145
1146     fillPath(path);
1147
1148     if (!didBeginDraw())
1149         m_data->endDraw();
1150
1151     if (drawOwnShadow)
1152         stateSaver.restore();
1153
1154     setFillRule(oldFillRule);
1155     setFillColor(oldFillColor);
1156 }
1157
1158 void GraphicsContext::clip(const FloatRect& rect)
1159 {
1160     if (paintingDisabled())
1161         return;
1162
1163     if (isRecording()) {
1164         m_displayListRecorder->clip(rect);
1165         return;
1166     }
1167
1168     m_data->clip(rect);
1169 }
1170
1171 void GraphicsContext::clipOut(const FloatRect& rect)
1172 {
1173     if (paintingDisabled())
1174         return;
1175
1176     if (isRecording()) {
1177         m_displayListRecorder->clipOut(rect);
1178         return;
1179     }
1180
1181     Path path;
1182     path.addRect(rect);
1183
1184     clipOut(path);
1185 }
1186
1187 void GraphicsContext::clipOut(const Path& path)
1188 {
1189     if (paintingDisabled())
1190         return;
1191
1192     if (isRecording()) {
1193         m_displayListRecorder->clipOut(path);
1194         return;
1195     }
1196
1197     // To clip Out we need the intersection of the infinite
1198     // clipping rect and the path we just created.
1199     D2D1_SIZE_F rendererSize = platformContext()->GetSize();
1200     FloatRect clipBounds(0, 0, rendererSize.width, rendererSize.height);
1201
1202     Path boundingRect;
1203     boundingRect.addRect(clipBounds);
1204     boundingRect.appendGeometry(path.platformPath());
1205
1206     COMPtr<ID2D1GeometryGroup> pathToClip;
1207     boundingRect.createGeometryWithFillMode(RULE_EVENODD, pathToClip);
1208
1209     m_data->clip(pathToClip.get());
1210 }
1211
1212 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
1213 {
1214     if (paintingDisabled())
1215         return;
1216
1217     if (isRecording()) {
1218         m_displayListRecorder->clipPath(path, clipRule);
1219         return;
1220     }
1221
1222     auto context = platformContext();
1223     if (path.isEmpty()) {
1224         m_data->clip(FloatRect());
1225         return;
1226     }
1227
1228     COMPtr<ID2D1GeometryGroup> pathToClip;
1229     path.createGeometryWithFillMode(clipRule, pathToClip);
1230
1231     m_data->clip(pathToClip.get());
1232 }
1233
1234 IntRect GraphicsContext::clipBounds() const
1235 {
1236     if (paintingDisabled())
1237         return IntRect();
1238
1239     if (isRecording()) {
1240         WTFLogAlways("Getting the clip bounds not yet supported with display lists");
1241         return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists.
1242     }
1243
1244     D2D1_RECT_F d2dClipBounds = D2D1::InfiniteRect();
1245     FloatRect clipBounds(d2dClipBounds.left, d2dClipBounds.top, d2dClipBounds.right - d2dClipBounds.left, d2dClipBounds.bottom - d2dClipBounds.top);
1246
1247     if (m_data->clipLayer()) {
1248         auto clipSize = m_data->clipLayer()->GetSize();
1249         clipBounds.setWidth(clipSize.width);
1250         clipBounds.setHeight(clipSize.height);
1251     }
1252
1253     return enclosingIntRect(clipBounds);
1254 }
1255
1256 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
1257 {
1258     if (paintingDisabled())
1259         return;
1260
1261     ASSERT(!isRecording());
1262
1263     save();
1264
1265     notImplemented();
1266 }
1267
1268 void GraphicsContext::endPlatformTransparencyLayer()
1269 {
1270     if (paintingDisabled())
1271         return;
1272
1273     ASSERT(!isRecording());
1274
1275     notImplemented();
1276
1277     restore();
1278 }
1279
1280 bool GraphicsContext::supportsTransparencyLayers()
1281 {
1282     return false;
1283 }
1284
1285 void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color)
1286 {
1287     (void)offset;
1288     (void)blur;
1289     (void)color;
1290     notImplemented();
1291 }
1292
1293 void GraphicsContext::clearPlatformShadow()
1294 {
1295     if (paintingDisabled())
1296         return;
1297     notImplemented();
1298 }
1299
1300 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
1301 {
1302     if (paintingDisabled())
1303         return;
1304
1305     m_data->setStrokeStyle(style);
1306 }
1307
1308 void GraphicsContext::setMiterLimit(float limit)
1309 {
1310     if (paintingDisabled())
1311         return;
1312
1313     if (isRecording()) {
1314         // Maybe this should be part of the state.
1315         m_displayListRecorder->setMiterLimit(limit);
1316         return;
1317     }
1318
1319     m_data->setMiterLimit(limit);
1320 }
1321
1322 void GraphicsContext::clearRect(const FloatRect& rect)
1323 {
1324     if (paintingDisabled())
1325         return;
1326
1327     if (isRecording()) {
1328         m_displayListRecorder->clearRect(rect);
1329         return;
1330     }
1331
1332     auto context = platformContext();
1333
1334     context->SetTags(1, __LINE__);
1335
1336     drawWithoutShadow(rect, [this, rect](ID2D1RenderTarget* renderTarget) {
1337         FloatSize renderTargetSize = renderTarget->GetSize();
1338         if (rect.size() == renderTargetSize)
1339             renderTarget->Clear();
1340         else
1341             renderTarget->FillRectangle(rect, m_data->brushWithColor(colorWithGlobalAlpha(fillColor())).get());
1342     });
1343 }
1344
1345 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1346 {
1347     (void)rect;
1348     (void)lineWidth;
1349     notImplemented();
1350 }
1351
1352 void GraphicsContext::setLineCap(LineCap cap)
1353 {
1354     if (paintingDisabled())
1355         return;
1356
1357     if (isRecording()) {
1358         m_displayListRecorder->setLineCap(cap);
1359         return;
1360     }
1361
1362     m_data->setLineCap(cap);
1363 }
1364
1365 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1366 {
1367     if (paintingDisabled())
1368         return;
1369
1370     if (isRecording()) {
1371         m_displayListRecorder->setLineDash(dashes, dashOffset);
1372         return;
1373     }
1374
1375     if (dashOffset < 0) {
1376         float length = 0;
1377         for (size_t i = 0; i < dashes.size(); ++i)
1378             length += static_cast<float>(dashes[i]);
1379         if (length)
1380             dashOffset = fmod(dashOffset, length) + length;
1381     }
1382
1383     m_data->setDashes(dashes);
1384     m_data->setDashOffset(dashOffset);
1385 }
1386
1387 void GraphicsContext::setLineJoin(LineJoin join)
1388 {
1389     if (paintingDisabled())
1390         return;
1391
1392     if (isRecording()) {
1393         m_displayListRecorder->setLineJoin(join);
1394         return;
1395     }
1396
1397     m_data->setLineJoin(join);
1398 }
1399
1400 void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
1401 {
1402     clipPath(path, fillRule);
1403 }
1404
1405 void GraphicsContext::scale(const FloatSize& size)
1406 {
1407     if (paintingDisabled())
1408         return;
1409
1410     if (isRecording()) {
1411         m_displayListRecorder->scale(size);
1412         return;
1413     }
1414
1415     m_data->scale(size);
1416     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1417 }
1418
1419 void GraphicsContext::rotate(float angle)
1420 {
1421     if (paintingDisabled())
1422         return;
1423
1424     if (isRecording()) {
1425         m_displayListRecorder->rotate(angle);
1426         return;
1427     }
1428
1429     m_data->rotate(angle);
1430     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1431 }
1432
1433 void GraphicsContext::translate(float x, float y)
1434 {
1435     if (paintingDisabled())
1436         return;
1437
1438     if (isRecording()) {
1439         m_displayListRecorder->translate(x, y);
1440         return;
1441     }
1442
1443     m_data->translate(x, y);
1444     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1445 }
1446
1447 void GraphicsContext::concatCTM(const AffineTransform& transform)
1448 {
1449     if (paintingDisabled())
1450         return;
1451
1452     if (isRecording()) {
1453         m_displayListRecorder->concatCTM(transform);
1454         return;
1455     }
1456
1457     m_data->concatCTM(transform);
1458     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1459 }
1460
1461 void GraphicsContext::setCTM(const AffineTransform& transform)
1462 {
1463     if (paintingDisabled())
1464         return;
1465
1466     if (isRecording()) {
1467         WTFLogAlways("GraphicsContext::setCTM() is not compatible with recording contexts.");
1468         return;
1469     }
1470
1471     m_data->setCTM(transform);
1472     // FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1473 }
1474
1475 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
1476 {
1477     if (paintingDisabled())
1478         return AffineTransform();
1479
1480     if (isRecording()) {
1481         WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts.");
1482         return AffineTransform();
1483     }
1484
1485     D2D1_MATRIX_3X2_F currentTransform;
1486     platformContext()->GetTransform(&currentTransform);
1487     return currentTransform;
1488 }
1489
1490 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
1491 {
1492     if (paintingDisabled())
1493         return rect;
1494
1495     if (isRecording()) {
1496         WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts.");
1497         return rect;
1498     }
1499
1500     notImplemented();
1501
1502     return rect;
1503 }
1504
1505 void GraphicsContext::drawLineForText(const FloatPoint& point, float width, bool printing, bool doubleLines, StrokeStyle strokeStyle)
1506 {
1507     DashArray widths;
1508     widths.append(width);
1509     widths.append(0);
1510     drawLinesForText(point, widths, printing, doubleLines, strokeStyle);
1511 }
1512
1513 void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, StrokeStyle strokeStyle)
1514 {
1515     if (paintingDisabled())
1516         return;
1517
1518     if (!widths.size())
1519         return;
1520
1521     if (isRecording()) {
1522         m_displayListRecorder->drawLinesForText(point, widths, printing, doubleLines, strokeThickness());
1523         return;
1524     }
1525
1526     notImplemented();
1527 }
1528
1529 void GraphicsContext::setURLForRect(const URL& link, const IntRect& destRect)
1530 {
1531     if (paintingDisabled())
1532         return;
1533
1534     if (isRecording()) {
1535         WTFLogAlways("GraphicsContext::setURLForRect() is not yet compatible with recording contexts.");
1536         return; // FIXME for display lists.
1537     }
1538
1539     RetainPtr<CFURLRef> urlRef = link.createCFURL();
1540     if (!urlRef)
1541         return;
1542
1543     notImplemented();
1544 }
1545
1546 void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality mode)
1547 {
1548     ASSERT(!paintingDisabled());
1549
1550     D2D1_INTERPOLATION_MODE quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1551
1552     switch (mode) {
1553     case InterpolationDefault:
1554         quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1555         break;
1556     case InterpolationNone:
1557         quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1558         break;
1559     case InterpolationLow:
1560         quality = D2D1_INTERPOLATION_MODE_LINEAR;
1561         break;
1562     case InterpolationMedium:
1563         quality = D2D1_INTERPOLATION_MODE_CUBIC;
1564         break;
1565     case InterpolationHigh:
1566         quality = D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
1567         break;
1568     }
1569     // FIXME: SetInterpolationQuality(platformContext(), quality);
1570 }
1571
1572 void GraphicsContext::setIsCALayerContext(bool isLayerContext)
1573 {
1574     if (paintingDisabled())
1575         return;
1576
1577     if (isRecording())
1578         return;
1579
1580     // This function is probabaly not needed.
1581     notImplemented();
1582 }
1583
1584 bool GraphicsContext::isCALayerContext() const
1585 {
1586     if (paintingDisabled())
1587         return false;
1588
1589     // FIXME
1590     if (isRecording())
1591         return false;
1592
1593     // This function is probabaly not needed.
1594     notImplemented();
1595     return false;
1596 }
1597
1598 void GraphicsContext::setIsAcceleratedContext(bool isAccelerated)
1599 {
1600     if (paintingDisabled())
1601         return;
1602
1603     // FIXME
1604     if (isRecording())
1605         return;
1606
1607     notImplemented();
1608 }
1609
1610 bool GraphicsContext::isAcceleratedContext() const
1611 {
1612     if (paintingDisabled())
1613         return false;
1614
1615     // FIXME
1616     if (isRecording())
1617         return false;
1618
1619     // This function is probabaly not needed.
1620     notImplemented();
1621     return false;
1622 }
1623
1624 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1625 {
1626     (void)mode;
1627     notImplemented();
1628 }
1629
1630 void GraphicsContext::setPlatformStrokeColor(const Color& color)
1631 {
1632     ASSERT(m_state.strokeColor == color);
1633
1634     m_data->m_solidStrokeBrush = nullptr;
1635
1636     m_data->m_solidStrokeBrush = m_data->brushWithColor(colorWithGlobalAlpha(strokeColor()));
1637 }
1638
1639 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1640 {
1641     ASSERT(m_state.strokeThickness == thickness);
1642     m_data->setStrokeThickness(thickness);
1643 }
1644
1645 void GraphicsContext::setPlatformFillColor(const Color& color)
1646 {
1647     ASSERT(m_state.fillColor == color);
1648
1649     m_data->m_solidFillBrush = nullptr;
1650
1651     m_data->m_solidFillBrush = m_data->brushWithColor(colorWithGlobalAlpha(fillColor()));
1652 }
1653
1654 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1655 {
1656     if (paintingDisabled())
1657         return;
1658
1659     ASSERT(!isRecording());
1660
1661     auto antialiasMode = enable ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED;
1662     platformContext()->SetAntialiasMode(antialiasMode);
1663 }
1664
1665 void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
1666 {
1667     if (paintingDisabled())
1668         return;
1669
1670     ASSERT(!isRecording());
1671
1672     auto fontSmoothingMode = enable ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
1673     platformContext()->SetTextAntialiasMode(fontSmoothingMode);
1674 }
1675
1676 void GraphicsContext::setPlatformAlpha(float)
1677 {
1678     /* No-op on this platform */
1679 }
1680
1681 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode, BlendMode blendMode)
1682 {
1683     if (paintingDisabled())
1684         return;
1685
1686     ASSERT(!isRecording());
1687
1688     D2D1_BLEND_MODE targetBlendMode = D2D1_BLEND_MODE_SCREEN;
1689     D2D1_COMPOSITE_MODE targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP; // ???
1690
1691     if (blendMode != BlendModeNormal) {
1692         switch (blendMode) {
1693         case BlendModeMultiply:
1694             targetBlendMode = D2D1_BLEND_MODE_MULTIPLY;
1695             break;
1696         case BlendModeScreen:
1697             targetBlendMode = D2D1_BLEND_MODE_SCREEN;
1698             break;
1699         case BlendModeOverlay:
1700             targetBlendMode = D2D1_BLEND_MODE_OVERLAY;
1701             break;
1702         case BlendModeDarken:
1703             targetBlendMode = D2D1_BLEND_MODE_DARKEN;
1704             break;
1705         case BlendModeLighten:
1706             targetBlendMode = D2D1_BLEND_MODE_LIGHTEN;
1707             break;
1708         case BlendModeColorDodge:
1709             targetBlendMode = D2D1_BLEND_MODE_COLOR_DODGE;
1710             break;
1711         case BlendModeColorBurn:
1712             targetBlendMode = D2D1_BLEND_MODE_COLOR_BURN;
1713             break;
1714         case BlendModeHardLight:
1715             targetBlendMode = D2D1_BLEND_MODE_HARD_LIGHT;
1716             break;
1717         case BlendModeSoftLight:
1718             targetBlendMode = D2D1_BLEND_MODE_SOFT_LIGHT;
1719             break;
1720         case BlendModeDifference:
1721             targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
1722             break;
1723         case BlendModeExclusion:
1724             targetBlendMode = D2D1_BLEND_MODE_EXCLUSION;
1725             break;
1726         case BlendModeHue:
1727             targetBlendMode = D2D1_BLEND_MODE_HUE;
1728             break;
1729         case BlendModeSaturation:
1730             targetBlendMode = D2D1_BLEND_MODE_SATURATION;
1731             break;
1732         case BlendModeColor:
1733             targetBlendMode = D2D1_BLEND_MODE_COLOR;
1734             break;
1735         case BlendModeLuminosity:
1736             targetBlendMode = D2D1_BLEND_MODE_LUMINOSITY;
1737             break;
1738         case BlendModePlusDarker:
1739             targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
1740             break;
1741         case BlendModePlusLighter:
1742             targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
1743             break;
1744         default:
1745             break;
1746         }
1747     } else {
1748         switch (mode) {
1749         case CompositeClear:
1750             // FIXME: targetBlendMode = D2D1_BLEND_MODE_CLEAR;
1751             break;
1752         case CompositeCopy:
1753             // FIXME: targetBlendMode = D2D1_BLEND_MODE_COPY;
1754             break;
1755         case CompositeSourceOver:
1756             // FIXME: kCGBlendModeNormal
1757             break;
1758         case CompositeSourceIn:
1759             targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_IN;
1760             break;
1761         case CompositeSourceOut:
1762             targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_OUT;
1763             break;
1764         case CompositeSourceAtop:
1765             targetCompositeMode = D2D1_COMPOSITE_MODE_SOURCE_ATOP;
1766             break;
1767         case CompositeDestinationOver:
1768             targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_OVER;
1769             break;
1770         case CompositeDestinationIn:
1771             targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_IN;
1772             break;
1773         case CompositeDestinationOut:
1774             targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_OUT;
1775             break;
1776         case CompositeDestinationAtop:
1777             targetCompositeMode = D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
1778             break;
1779         case CompositeXOR:
1780             targetCompositeMode = D2D1_COMPOSITE_MODE_XOR;
1781             break;
1782         case CompositePlusDarker:
1783             targetBlendMode = D2D1_BLEND_MODE_DARKER_COLOR;
1784             break;
1785         case CompositePlusLighter:
1786             targetBlendMode = D2D1_BLEND_MODE_LIGHTER_COLOR;
1787             break;
1788         case CompositeDifference:
1789             targetBlendMode = D2D1_BLEND_MODE_DIFFERENCE;
1790             break;
1791         }
1792     }
1793
1794     m_data->m_blendMode = targetBlendMode;
1795     m_data->m_compositeMode = targetCompositeMode;
1796 }
1797
1798 void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
1799 {
1800     // This is a no-op for Direct2D.
1801 }
1802
1803 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1804 {
1805     if (paintingDisabled())
1806         return;
1807
1808     ASSERT(!isRecording());
1809
1810     if (m_state.fillGradient || m_state.fillPattern) {
1811         // FIXME: We should be able to fill ellipses with pattern/gradient brushes in D2D.
1812         fillEllipseAsPath(ellipse);
1813         return;
1814     }
1815
1816     auto d2dEllipse = D2D1::Ellipse(D2D1::Point2F(ellipse.x(), ellipse.y()), ellipse.width(), ellipse.height());
1817
1818     platformContext()->SetTags(1, __LINE__);
1819
1820     drawWithoutShadow(ellipse, [this, d2dEllipse](ID2D1RenderTarget* renderTarget) {
1821         renderTarget->FillEllipse(&d2dEllipse, solidFillBrush());
1822     });
1823 }
1824
1825 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1826 {
1827     if (paintingDisabled())
1828         return;
1829
1830     ASSERT(!isRecording());
1831
1832     if (m_state.strokeGradient || m_state.strokePattern) {
1833         // FIXME: We should be able to stroke ellipses with pattern/gradient brushes in D2D.
1834         strokeEllipseAsPath(ellipse);
1835         return;
1836     }
1837
1838     auto d2dEllipse = D2D1::Ellipse(D2D1::Point2F(ellipse.x(), ellipse.y()), ellipse.width(), ellipse.height());
1839
1840     platformContext()->SetTags(1, __LINE__);
1841
1842     drawWithoutShadow(ellipse, [this, d2dEllipse](ID2D1RenderTarget* renderTarget) {
1843         renderTarget->DrawEllipse(&d2dEllipse, solidStrokeBrush(), strokeThickness(), m_data->strokeStyle());
1844     });
1845 }
1846
1847 }