blob: f53870f2f417593ba9734504b958d6d2338855bd [file] [log] [blame]
/*
* Copyright (C) 2016-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "GraphicsContext.h"
#include "COMPtr.h"
#include "Direct2DOperations.h"
#include "Direct2DUtilities.h"
#include "DisplayListRecorder.h"
#include "FloatRoundedRect.h"
#include "GraphicsContextPlatformPrivateDirect2D.h"
#include "ImageBuffer.h"
#include "ImageDecoderDirect2D.h"
#include "Logging.h"
#include "NotImplemented.h"
#include "PlatformContextDirect2D.h"
#include <d2d1.h>
#include <d2d1effects.h>
#include <dwrite_3.h>
#include <wtf/URL.h>
#pragma warning (disable : 4756)
namespace WebCore {
GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha)
{
platformInit(hdc, hasAlpha);
}
GraphicsContext::GraphicsContext(HDC hdc, ID2D1DCRenderTarget** renderTarget, RECT rect, bool hasAlpha)
{
// Create a DC render target.
auto targetProperties = Direct2D::renderTargetProperties();
HRESULT hr = GraphicsContext::systemFactory()->CreateDCRenderTarget(&targetProperties, renderTarget);
RELEASE_ASSERT(SUCCEEDED(hr));
(*renderTarget)->BindDC(hdc, &rect);
auto ownedPlatformContext = makeUnique<PlatformContextDirect2D>(*renderTarget);
m_data = new GraphicsContextPlatformPrivate(WTFMove(ownedPlatformContext), BitmapRenderingContextType::GPUMemory);
m_data->m_hdc = hdc;
}
GraphicsContext::GraphicsContext(PlatformContextDirect2D* platformGraphicsContext, BitmapRenderingContextType rendererType)
{
platformInit(platformGraphicsContext, rendererType);
}
ID2D1Factory* GraphicsContext::systemFactory()
{
static ID2D1Factory* direct2DFactory = nullptr;
if (!direct2DFactory) {
#ifndef NDEBUG
D2D1_FACTORY_OPTIONS options = { };
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, options, &direct2DFactory);
#else
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &direct2DFactory);
#endif
RELEASE_ASSERT(SUCCEEDED(hr));
}
return direct2DFactory;
}
ID2D1RenderTarget* GraphicsContext::defaultRenderTarget()
{
static ID2D1RenderTarget* defaultRenderTarget = nullptr;
if (!defaultRenderTarget) {
auto renderTargetProperties = D2D1::RenderTargetProperties();
renderTargetProperties.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
auto hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(::GetDesktopWindow(), D2D1::SizeU(10, 10));
HRESULT hr = systemFactory()->CreateHwndRenderTarget(&renderTargetProperties, &hwndRenderTargetProperties, reinterpret_cast<ID2D1HwndRenderTarget**>(&defaultRenderTarget));
RELEASE_ASSERT(SUCCEEDED(hr));
defaultRenderTarget->AddRef();
}
return defaultRenderTarget;
}
void GraphicsContext::platformInit(HDC hdc, bool hasAlpha)
{
if (!hdc)
return;
HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
DIBPixelData pixelData(bitmap);
auto targetProperties = Direct2D::renderTargetProperties();
COMPtr<ID2D1DCRenderTarget> renderTarget;
HRESULT hr = systemFactory()->CreateDCRenderTarget(&targetProperties, &renderTarget);
if (!SUCCEEDED(hr))
return;
RECT clientRect = IntRect(IntPoint(), pixelData.size());
hr = renderTarget->BindDC(hdc, &clientRect);
if (!SUCCEEDED(hr))
return;
auto ownedPlatformContext = makeUnique<PlatformContextDirect2D>(renderTarget.get());
m_data = new GraphicsContextPlatformPrivate(WTFMove(ownedPlatformContext), BitmapRenderingContextType::GPUMemory);
m_data->m_hdc = hdc;
// Make sure the context starts in sync with our state.
setPlatformFillColor(fillColor());
setPlatformStrokeColor(strokeColor());
setPlatformStrokeThickness(strokeThickness());
// FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
}
void GraphicsContext::platformInit(PlatformContextDirect2D* platformContext)
{
platformInit(platformContext, BitmapRenderingContextType::GPUMemory);
}
void GraphicsContext::platformInit(PlatformContextDirect2D* platformContext, BitmapRenderingContextType renderingType)
{
if (!platformContext)
return;
m_data = new GraphicsContextPlatformPrivate(*platformContext, renderingType);
// Make sure the context starts in sync with our state.
setPlatformFillColor(fillColor());
setPlatformStrokeColor(strokeColor());
setPlatformStrokeThickness(strokeThickness());
// FIXME: m_state.imageInterpolationQuality = convertInterpolationQuality(CGContextGetInterpolationQuality(platformContext()));
}
void GraphicsContext::platformDestroy()
{
delete m_data;
}
PlatformContextDirect2D* GraphicsContext::platformContext() const
{
if (m_impl)
return m_impl->platformContext();
ASSERT(!paintingDisabled());
return &m_data->platformContext();
}
void GraphicsContextPlatformPrivate::syncContext(PlatformContextDirect2D&)
{
notImplemented();
}
ID2D1RenderTarget* GraphicsContextPlatformPrivate::renderTarget()
{
if (!m_platformContext.m_transparencyLayerStack.isEmpty())
return m_platformContext.m_transparencyLayerStack.last().renderTarget.get();
return m_platformContext.renderTarget();
}
void GraphicsContextPlatformPrivate::setAlpha(float alpha)
{
ASSERT(m_platformContext.m_transparencyLayerStack.isEmpty());
m_alpha = alpha;
}
float GraphicsContextPlatformPrivate::currentGlobalAlpha() const
{
if (!m_platformContext.m_transparencyLayerStack.isEmpty())
return m_platformContext.m_transparencyLayerStack.last().opacity;
return m_alpha;
}
void GraphicsContext::savePlatformState()
{
ASSERT(hasPlatformContext());
Direct2D::save(*platformContext());
}
void GraphicsContext::restorePlatformState()
{
ASSERT(hasPlatformContext());
Direct2D::restore(*platformContext());
}
void GraphicsContext::drawNativeImage(const COMPtr<ID2D1Bitmap>& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->drawNativeImage(image, imageSize, destRect, srcRect, options);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
Direct2D::drawNativeImage(*platformContext(), image.get(), imageSize, destRect, srcRect, options, state.alpha, Direct2D::ShadowState(state));
}
void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend)
{
bool createdBitmap = m_impl || !m_data->m_hdc || isInTransparencyLayer();
if (!createdBitmap) {
ASSERT(hasPlatformContext());
Direct2D::restore(*platformContext());
return;
}
if (!hdc || dstRect.isEmpty())
return;
auto sourceBitmap = adoptGDIObject(static_cast<HBITMAP>(::GetCurrentObject(hdc, OBJ_BITMAP)));
DIBPixelData pixelData(sourceBitmap.get());
ASSERT(pixelData.bitsPerPixel() == 32);
auto bitmapProperties = Direct2D::bitmapProperties();
ASSERT(hasPlatformContext());
auto& platformContext = *this->platformContext();
COMPtr<ID2D1Bitmap> bitmap;
HRESULT hr = platformContext.renderTarget()->CreateBitmap(pixelData.size(), pixelData.buffer(), pixelData.bytesPerRow(), &bitmapProperties, &bitmap);
ASSERT(SUCCEEDED(hr));
PlatformContextStateSaver stateSaver(platformContext);
// Note: The content in the HDC is inverted compared to Direct2D, so it needs to be flipped.
auto context = platformContext.renderTarget();
D2D1_MATRIX_3X2_F currentTransform;
context->GetTransform(&currentTransform);
AffineTransform transform(currentTransform);
transform.translate(dstRect.location());
transform.scale(1.0, -1.0);
transform.translate(0, -dstRect.height());
context->SetTransform(transform);
context->DrawBitmap(bitmap.get(), D2D1::RectF(0, 0, dstRect.width(), dstRect.height()));
::DeleteDC(hdc);
}
void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& point)
{
}
void GraphicsContext::drawFocusRing(const Path& path, float width, float offset, const Color& color)
{
}
void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
{
}
void GraphicsContext::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
{
}
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(PlatformContextDirect2D& platformContext, GraphicsContext::BitmapRenderingContextType renderingType)
: m_platformContext(platformContext)
, m_rendererType(renderingType)
{
if (!m_platformContext.renderTarget())
return;
if (m_rendererType == GraphicsContext::BitmapRenderingContextType::GPUMemory)
beginDraw();
}
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(std::unique_ptr<PlatformContextDirect2D>&& ownedPlatformContext, GraphicsContext::BitmapRenderingContextType renderingType)
: m_ownedPlatformContext(WTFMove(ownedPlatformContext))
, m_platformContext(*m_ownedPlatformContext)
, m_rendererType(renderingType)
{
if (!m_platformContext.renderTarget())
return;
if (m_rendererType == GraphicsContext::BitmapRenderingContextType::GPUMemory)
beginDraw();
}
GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
{
if (!m_platformContext.renderTarget())
return;
if (m_platformContext.beginDrawCount)
endDraw();
}
ID2D1SolidColorBrush* GraphicsContext::brushWithColor(const Color& color)
{
ASSERT(hasPlatformContext());
return platformContext()->brushWithColor(colorWithGlobalAlpha(color)).get();
}
void GraphicsContextPlatformPrivate::clip(const FloatRect& rect)
{
}
void GraphicsContextPlatformPrivate::clip(const Path& path)
{
}
void GraphicsContextPlatformPrivate::clip(ID2D1Geometry* path)
{
}
void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& affineTransform)
{
}
void GraphicsContextPlatformPrivate::flush()
{
}
void GraphicsContextPlatformPrivate::beginDraw()
{
m_platformContext.beginDraw();
}
void GraphicsContextPlatformPrivate::endDraw()
{
m_platformContext.endDraw();
}
void GraphicsContextPlatformPrivate::restore()
{
}
void GraphicsContextPlatformPrivate::save()
{
}
void GraphicsContextPlatformPrivate::scale(const FloatSize& size)
{
}
void GraphicsContextPlatformPrivate::setCTM(const AffineTransform& transform)
{
ASSERT(m_platformContext.renderTarget());
m_platformContext.renderTarget()->SetTransform(transform);
}
void GraphicsContextPlatformPrivate::translate(float x, float y)
{
}
void GraphicsContextPlatformPrivate::rotate(float angle)
{
}
D2D1_COLOR_F GraphicsContext::colorWithGlobalAlpha(const Color& color) const
{
float colorAlpha = color.alphaAsFloat();
float globalAlpha = m_data->currentGlobalAlpha();
return D2D1::ColorF(color.rgb(), globalAlpha * colorAlpha);
}
ID2D1Brush* GraphicsContext::solidStrokeBrush() const
{
return platformContext()->m_solidStrokeBrush.get();
}
ID2D1Brush* GraphicsContext::solidFillBrush() const
{
return platformContext()->m_solidFillBrush.get();
}
ID2D1Brush* GraphicsContext::patternStrokeBrush() const
{
return platformContext()->m_patternStrokeBrush.get();
}
ID2D1Brush* GraphicsContext::patternFillBrush() const
{
return platformContext()->m_patternFillBrush.get();
}
void GraphicsContext::beginDraw()
{
ASSERT(hasPlatformContext());
platformContext()->beginDraw();
}
void GraphicsContext::endDraw()
{
ASSERT(hasPlatformContext());
platformContext()->endDraw();
}
void GraphicsContext::flush()
{
ASSERT(hasPlatformContext());
Direct2D::flush(*platformContext());
}
void GraphicsContext::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions& options)
{
if (paintingDisabled() || !patternTransform.isInvertible())
return;
if (m_impl) {
m_impl->drawPattern(image, destRect, tileRect, patternTransform, phase, spacing, options);
return;
}
ASSERT(hasPlatformContext());
if (auto tileImage = image.nativeImageForCurrentFrame(this))
Direct2D::drawPattern(*platformContext(), WTFMove(tileImage), IntSize(image.size()), destRect, tileRect, patternTransform, phase, options.compositeOperator(), options.blendMode());
}
void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
{
if (paintingDisabled())
return;
FloatSize bufferDestinationSize = buffer.sizeForDestinationSize(destRect.size());
notImplemented();
}
// Draws a filled rectangle with a stroked border.
void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->drawRect(rect, borderThickness);
return;
}
ASSERT(!rect.isEmpty());
ASSERT(hasPlatformContext());
auto& state = this->state();
Direct2D::drawRect(*platformContext(), rect, borderThickness, state.fillColor, state.strokeStyle, state.strokeColor);
}
void GraphicsContextPlatformPrivate::setLineCap(LineCap)
{
}
void GraphicsContextPlatformPrivate::setLineJoin(LineJoin)
{
}
void GraphicsContextPlatformPrivate::setStrokeStyle(StrokeStyle)
{
}
void GraphicsContextPlatformPrivate::setMiterLimit(float canvasMiterLimit)
{
m_platformContext.setDashOffset(canvasMiterLimit);
}
void GraphicsContextPlatformPrivate::setDashOffset(float dashOffset)
{
m_platformContext.setDashOffset(dashOffset);
}
void GraphicsContextPlatformPrivate::setPatternWidth(float patternWidth)
{
m_platformContext.setPatternWidth(patternWidth);
}
void GraphicsContextPlatformPrivate::setPatternOffset(float patternOffset)
{
m_platformContext.setPatternOffset(patternOffset);
}
void GraphicsContextPlatformPrivate::setStrokeThickness(float thickness)
{
m_platformContext.setStrokeThickness(thickness);
}
void GraphicsContextPlatformPrivate::setDashes(const DashArray& dashes)
{
m_platformContext.setDashes(dashes);
}
D2D1_STROKE_STYLE_PROPERTIES GraphicsContextPlatformPrivate::strokeStyleProperties() const
{
return D2D1::StrokeStyleProperties(m_platformContext.m_lineCap, m_platformContext.m_lineCap, m_platformContext.m_lineCap, m_platformContext.m_lineJoin, m_platformContext.m_miterLimit, D2D1_DASH_STYLE_SOLID, 0.0f);
}
ID2D1StrokeStyle* GraphicsContext::platformStrokeStyle() const
{
ASSERT(hasPlatformContext());
return platformContext()->strokeStyle();
}
// This is only used to draw borders.
void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2)
{
if (paintingDisabled())
return;
if (strokeStyle() == NoStroke)
return;
if (m_impl) {
m_impl->drawLine(point1, point2);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
Direct2D::drawLine(*platformContext(), point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias);
}
void GraphicsContext::drawEllipse(const FloatRect& rect)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->drawEllipse(rect);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
Direct2D::fillEllipse(*platformContext(), rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness);
}
void GraphicsContext::applyStrokePattern()
{
if (paintingDisabled())
return;
auto context = platformContext();
AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
const float patternAlpha = 1;
platformContext()->m_patternStrokeBrush = adoptCOM(m_state.strokePattern->createPlatformPattern(*this, patternAlpha, userToBaseCTM));
}
void GraphicsContext::applyFillPattern()
{
if (paintingDisabled())
return;
auto context = platformContext();
AffineTransform userToBaseCTM; // FIXME: This isn't really needed on Windows
const float patternAlpha = 1;
platformContext()->m_patternFillBrush = adoptCOM(m_state.fillPattern->createPlatformPattern(*this, patternAlpha, userToBaseCTM));
}
void GraphicsContext::drawPath(const Path& path)
{
if (paintingDisabled() || path.isEmpty())
return;
if (m_impl) {
m_impl->drawPath(path);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::drawPath(context, path, Direct2D::StrokeSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContext::fillPath(const Path& path)
{
if (paintingDisabled() || path.isEmpty())
return;
if (m_impl) {
m_impl->fillPath(path);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillPath(context, path, Direct2D::FillSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContext::strokePath(const Path& path)
{
if (paintingDisabled() || path.isEmpty())
return;
if (m_impl) {
m_impl->strokePath(path);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::strokePath(context, path, Direct2D::StrokeSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContext::fillRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->fillRect(rect);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillRect(context, rect, Direct2D::FillSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->fillRect(rect, color);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillRect(context, rect, color, Direct2D::ShadowState(state));
}
void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
ASSERT(hasPlatformContext());
auto& state = this->state();
Direct2D::fillRoundedRect(*platformContext(), rect, color, Direct2D::ShadowState(state));
}
void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->fillRectWithRoundedHole(rect, roundedHoleRect, color);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillRectWithRoundedHole(context, rect, roundedHoleRect, Direct2D::FillSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->clip(rect);
return;
}
ASSERT(hasPlatformContext());
Direct2D::clip(*platformContext(), rect);
}
void GraphicsContext::clipOut(const FloatRect& rect)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->clipOut(rect);
return;
}
ASSERT(hasPlatformContext());
Direct2D::clipOut(*platformContext(), rect);
}
void GraphicsContext::clipOut(const Path& path)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->clipOut(path);
return;
}
ASSERT(hasPlatformContext());
Direct2D::clipOut(*platformContext(), path);
}
void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->clipPath(path, clipRule);
return;
}
ASSERT(hasPlatformContext());
Direct2D::clipPath(*platformContext(), path, clipRule);
}
IntRect GraphicsContext::clipBounds() const
{
if (paintingDisabled())
return IntRect();
if (m_impl)
return m_impl->clipBounds();
ASSERT(hasPlatformContext());
return Direct2D::State::getClipBounds(*platformContext());
}
void GraphicsContextPlatformPrivate::beginTransparencyLayer(float opacity)
{
}
void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
save();
m_state.alpha = opacity;
ASSERT(hasPlatformContext());
Direct2D::beginTransparencyLayer(*platformContext(), opacity);
}
void GraphicsContextPlatformPrivate::endTransparencyLayer()
{
}
void GraphicsContext::endPlatformTransparencyLayer()
{
if (paintingDisabled())
return;
ASSERT(hasPlatformContext());
Direct2D::endTransparencyLayer(*platformContext());
ASSERT(!m_impl);
m_state.alpha = m_data->currentGlobalAlpha();
restore();
}
bool GraphicsContext::supportsTransparencyLayers()
{
return false;
}
void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color)
{
(void)offset;
(void)blur;
(void)color;
notImplemented();
}
void GraphicsContext::clearPlatformShadow()
{
if (paintingDisabled())
return;
notImplemented();
}
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
{
if (paintingDisabled())
return;
ASSERT(hasPlatformContext());
Direct2D::State::setStrokeStyle(*platformContext(), style);
}
void GraphicsContext::setMiterLimit(float limit)
{
if (paintingDisabled())
return;
if (m_impl) {
// Maybe this should be part of the state.
m_impl->setMiterLimit(limit);
return;
}
ASSERT(hasPlatformContext());
Direct2D::setMiterLimit(*platformContext(), limit);
}
void GraphicsContext::clearRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->clearRect(rect);
return;
}
ASSERT(hasPlatformContext());
Direct2D::clearRect(*platformContext(), rect);
}
void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->strokeRect(rect, lineWidth);
return;
}
ASSERT(hasPlatformContext());
auto& state = this->state();
auto& context = *platformContext();
Direct2D::strokeRect(context, rect, lineWidth, Direct2D::StrokeSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContext::setLineCap(LineCap cap)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->setLineCap(cap);
return;
}
ASSERT(hasPlatformContext());
Direct2D::setLineCap(*platformContext(), cap);
}
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->setLineDash(dashes, dashOffset);
return;
}
if (dashOffset < 0) {
float length = 0;
for (size_t i = 0; i < dashes.size(); ++i)
length += static_cast<float>(dashes[i]);
if (length)
dashOffset = fmod(dashOffset, length) + length;
}
ASSERT(hasPlatformContext());
Direct2D::setLineDash(*platformContext(), dashes, dashOffset);
}
void GraphicsContext::setLineJoin(LineJoin join)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->setLineJoin(join);
return;
}
ASSERT(hasPlatformContext());
Direct2D::setLineJoin(*platformContext(), join);
}
void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
{
clipPath(path, fillRule);
}
void GraphicsContext::scale(const FloatSize& size)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->scale(size);
return;
}
ASSERT(hasPlatformContext());
Direct2D::scale(*platformContext(), size);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContext::rotate(float angle)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->rotate(angle);
return;
}
ASSERT(hasPlatformContext());
Direct2D::rotate(*platformContext(), angle);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContext::translate(float x, float y)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->translate(x, y);
return;
}
ASSERT(hasPlatformContext());
Direct2D::translate(*platformContext(), x, y);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContext::concatCTM(const AffineTransform& transform)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->concatCTM(transform);
return;
}
ASSERT(hasPlatformContext());
Direct2D::concatCTM(*platformContext(), transform);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContext::setCTM(const AffineTransform& transform)
{
if (paintingDisabled())
return;
if (m_impl) {
m_impl->setCTM(transform);
return;
}
ASSERT(hasPlatformContext());
Direct2D::State::setCTM(*platformContext(), transform);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
{
if (paintingDisabled())
return AffineTransform();
if (m_impl)
return m_impl->getCTM(includeScale);
ASSERT(hasPlatformContext());
return Direct2D::State::getCTM(*platformContext());
}
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
{
if (paintingDisabled())
return rect;
if (m_impl)
return m_impl->roundToDevicePixels(rect, roundingMode);
return Direct2D::State::roundToDevicePixels(*platformContext(), rect);
}
void GraphicsContext::drawLineForText(const FloatRect& rect, bool printing, bool doubleLines, StrokeStyle strokeStyle)
{
DashArray widths;
widths.append(0);
widths.append(rect.width());
drawLinesForText(rect.location(), rect.height(), widths, printing, doubleLines, strokeStyle);
}
void GraphicsContext::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines, StrokeStyle strokeStyle)
{
if (paintingDisabled())
return;
if (!widths.size())
return;
if (m_impl) {
m_impl->drawLinesForText(point, thickness, widths, printing, doubleUnderlines);
return;
}
ASSERT(hasPlatformContext());
Direct2D::drawLinesForText(*platformContext(), point, thickness, widths, printing, doubleUnderlines, m_state.strokeColor);
}
void GraphicsContext::setURLForRect(const URL& link, const FloatRect& destRect)
{
if (paintingDisabled())
return;
if (m_impl) {
WTFLogAlways("GraphicsContext::setURLForRect() is not yet compatible with recording contexts.");
return; // FIXME for display lists.
}
notImplemented();
}
void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality mode)
{
ASSERT(!paintingDisabled());
D2D1_INTERPOLATION_MODE quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
switch (mode) {
case InterpolationDefault:
quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
break;
case InterpolationNone:
quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
break;
case InterpolationLow:
quality = D2D1_INTERPOLATION_MODE_LINEAR;
break;
case InterpolationMedium:
quality = D2D1_INTERPOLATION_MODE_CUBIC;
break;
case InterpolationHigh:
quality = D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
break;
}
// FIXME: SetInterpolationQuality(platformContext(), quality);
}
void GraphicsContext::setIsCALayerContext(bool isLayerContext)
{
if (paintingDisabled())
return;
if (m_impl)
return;
// This function is probabaly not needed.
notImplemented();
}
bool GraphicsContext::isCALayerContext() const
{
if (paintingDisabled())
return false;
// FIXME
if (m_impl)
return false;
// This function is probabaly not needed.
notImplemented();
return false;
}
void GraphicsContext::setIsAcceleratedContext(bool isAccelerated)
{
if (paintingDisabled())
return;
// FIXME
if (m_impl)
return;
notImplemented();
}
bool GraphicsContext::isAcceleratedContext() const
{
if (!hasPlatformContext())
return false;
return Direct2D::State::isAcceleratedContext(*platformContext());
}
void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
{
(void)mode;
notImplemented();
}
void GraphicsContext::setPlatformStrokeColor(const Color& color)
{
ASSERT(m_state.strokeColor == color);
ASSERT(hasPlatformContext());
platformContext()->m_solidStrokeBrush = brushWithColor(strokeColor());
}
void GraphicsContext::setPlatformStrokeThickness(float thickness)
{
ASSERT(m_state.strokeThickness == thickness);
m_data->setStrokeThickness(thickness);
}
void GraphicsContext::setPlatformFillColor(const Color& color)
{
ASSERT(m_state.fillColor == color);
ASSERT(hasPlatformContext());
platformContext()->m_solidFillBrush = brushWithColor(fillColor());
}
void GraphicsContext::setPlatformShouldAntialias(bool enable)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
ASSERT(hasPlatformContext());
Direct2D::State::setShouldAntialias(*platformContext(), enable);
}
void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
auto fontSmoothingMode = enable ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
platformContext()->renderTarget()->SetTextAntialiasMode(fontSmoothingMode);
}
void GraphicsContext::setPlatformAlpha(float alpha)
{
if (paintingDisabled())
return;
ASSERT(m_state.alpha == alpha);
m_data->setAlpha(alpha);
}
void GraphicsContext::setPlatformCompositeOperation(CompositeOperator compositeOperator, BlendMode blendMode)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
ASSERT(hasPlatformContext());
Direct2D::State::setCompositeOperation(*platformContext(), compositeOperator, blendMode);
}
void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
{
// This is a no-op for Direct2D.
}
void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
if (m_state.fillGradient || m_state.fillPattern) {
// FIXME: We should be able to fill ellipses with pattern/gradient brushes in D2D.
fillEllipseAsPath(ellipse);
return;
}
ASSERT(hasPlatformContext());
Direct2D::fillEllipse(*platformContext(), ellipse, m_state.fillColor, m_state.strokeStyle, m_state.strokeColor, m_state.strokeThickness);
}
void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
{
if (paintingDisabled())
return;
ASSERT(!m_impl);
if (m_state.strokeGradient || m_state.strokePattern) {
// FIXME: We should be able to stroke ellipses with pattern/gradient brushes in D2D.
strokeEllipseAsPath(ellipse);
return;
}
ASSERT(hasPlatformContext());
Direct2D::drawEllipse(*platformContext(), ellipse, m_state.strokeStyle, m_state.strokeColor, m_state.strokeThickness);
}
}