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