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