blob: d480db73d432255e92e3d0c799e1d94dd7235418 [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 {
GraphicsContextDirect2D::GraphicsContextDirect2D(HDC hdc, bool hasAlpha)
{
platformInit(hdc, hasAlpha);
}
GraphicsContextDirect2D::GraphicsContextDirect2D(HDC hdc, ID2D1DCRenderTarget** renderTarget, RECT rect, bool hasAlpha)
{
// Create a DC render target.
auto targetProperties = Direct2D::renderTargetProperties();
HRESULT hr = GraphicsContextDirect2D::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;
}
GraphicsContextDirect2D::GraphicsContextDirect2D(PlatformContextDirect2D* platformGraphicsContext, BitmapRenderingContextType rendererType)
{
platformInit(platformGraphicsContext, rendererType);
}
ID2D1Factory* GraphicsContextDirect2D::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* GraphicsContextDirect2D::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 GraphicsContextDirect2D::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 GraphicsContextDirect2D::platformInit(PlatformContextDirect2D* platformContext)
{
platformInit(platformContext, BitmapRenderingContextType::GPUMemory);
}
void GraphicsContextDirect2D::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()));
}
GraphicsContextDirect2D::~GraphicsContextDirect2D()
{
delete m_data;
}
PlatformContextDirect2D* GraphicsContextDirect2D::platformContext() const
{
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 GraphicsContextDirect2D::save()
{
GraphicsContext::save();
Direct2D::save(*platformContext());
}
void GraphicsContextDirect2D::restore()
{
if (!stackSize())
return;
GraphicsContext::restore();
Direct2D::restore(*platformContext());
}
void GraphicsContextDirect2D::drawNativeImage(NativeImage& nativeImage, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions& options)
{
auto& state = this->state();
Direct2D::drawNativeImage(*platformContext(), nativeImage.platformImage().get(), imageSize, destRect, srcRect, options, state.alpha, Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend)
{
bool createdBitmap = !m_data->m_hdc || isInTransparencyLayer();
if (!createdBitmap) {
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 GraphicsContextDirect2D::drawFocusRing(const Path& path, float width, float offset, const Color& color)
{
}
void GraphicsContextDirect2D::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
{
}
void GraphicsContextDirect2D::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
{
Direct2D::drawDotsForDocumentMarker(m_platformContext, rect, style);
}
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(PlatformContextDirect2D& platformContext, GraphicsContextDirect2D::BitmapRenderingContextType renderingType)
: m_platformContext(platformContext)
, m_rendererType(renderingType)
{
if (!m_platformContext.renderTarget())
return;
if (m_rendererType == GraphicsContextDirect2D::BitmapRenderingContextType::GPUMemory)
beginDraw();
}
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(std::unique_ptr<PlatformContextDirect2D>&& ownedPlatformContext, GraphicsContextDirect2D::BitmapRenderingContextType renderingType)
: m_ownedPlatformContext(WTFMove(ownedPlatformContext))
, m_platformContext(*m_ownedPlatformContext)
, m_rendererType(renderingType)
{
if (!m_platformContext.renderTarget())
return;
if (m_rendererType == GraphicsContextDirect2D::BitmapRenderingContextType::GPUMemory)
beginDraw();
}
GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
{
if (!m_platformContext.renderTarget())
return;
if (m_platformContext.beginDrawCount)
endDraw();
}
ID2D1SolidColorBrush* GraphicsContextDirect2D::brushWithColor(const Color& color)
{
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 GraphicsContextDirect2D::colorWithGlobalAlpha(const Color& color) const
{
auto [r, g, b, a] = color.toSRGBALossy<float>();
return D2D1::ColorF(r, g, b, a * m_data->currentGlobalAlpha());
}
ID2D1Brush* GraphicsContextDirect2D::solidStrokeBrush() const
{
return platformContext()->m_solidStrokeBrush.get();
}
ID2D1Brush* GraphicsContextDirect2D::solidFillBrush() const
{
return platformContext()->m_solidFillBrush.get();
}
ID2D1Brush* GraphicsContextDirect2D::patternStrokeBrush() const
{
return platformContext()->m_patternStrokeBrush.get();
}
ID2D1Brush* GraphicsContextDirect2D::patternFillBrush() const
{
return platformContext()->m_patternFillBrush.get();
}
void GraphicsContextDirect2D::beginDraw()
{
platformContext()->beginDraw();
}
void GraphicsContextDirect2D::endDraw()
{
platformContext()->endDraw();
}
void GraphicsContextDirect2D::flush()
{
ASSERT(hasPlatformContext());
Direct2D::flush(*platformContext());
}
void GraphicsContextDirect2D::drawPattern(const PlatformImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, const ImagePaintingOptions& options)
{
if (!patternTransform.isInvertible())
return;
auto tileImage = image;
Direct2D::drawPattern(*platformContext(), WTFMove(tileImage), IntSize(image.size()), destRect, tileRect, patternTransform, phase, options.compositeOperator(), options.blendMode());
}
// Draws a filled rectangle with a stroked border.
void GraphicsContextDirect2D::drawRect(const FloatRect& rect, float borderThickness)
{
ASSERT(!rect.isEmpty());
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* GraphicsContextDirect2D::strokeStyle() const
{
ASSERT(hasPlatformContext());
return platformContext()->strokeStyle();
}
// This is only used to draw borders.
void GraphicsContextDirect2D::drawLine(const FloatPoint& point1, const FloatPoint& point2)
{
if (strokeStyle() == NoStroke)
return;
auto& state = this->state();
Direct2D::drawLine(*platformContext(), point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias);
}
void GraphicsContextDirect2D::drawEllipse(const FloatRect& rect)
{
auto& state = this->state();
Direct2D::fillEllipse(*platformContext(), rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness);
}
void GraphicsContextDirect2D::applyStrokePattern()
{
if (!m_state.strokePattern)
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 GraphicsContextDirect2D::applyFillPattern()
{
if (!m_state.fillPattern)
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 GraphicsContextDirect2D::drawPath(const Path& path)
{
if (path.isEmpty())
return;
auto& state = this->state();
auto& context = *platformContext();
Direct2D::drawPath(context, path, Direct2D::StrokeSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::fillPath(const Path& path)
{
if (path.isEmpty())
return;
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillPath(context, path, Direct2D::FillSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::strokePath(const Path& path)
{
if (path.isEmpty())
return;
auto& state = this->state();
auto& context = *platformContext();
Direct2D::strokePath(context, path, Direct2D::StrokeSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::fillRect(const FloatRect& rect)
{
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillRect(context, rect, Direct2D::FillSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::fillRect(const FloatRect& rect, const Color& color)
{
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillRect(context, rect, color, Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::fillRect(const FloatRect& rect, Gradient& gradient)
{
auto brush = gradient.createBrush(m_platformContext.renderTarget());
if (!brush)
return;
Direct2D::fillRectWithGradient(m_platformContext, rect, brush.get());
}
void GraphicsContextDirect2D::fillRect(const FloatRect& rect, const Color& color, CompositeOperator compositeOperator, BlendMode blendMode)
{
auto& state = graphicsContext().state();
CompositeOperator previousOperator = state.compositeOperator;
Direct2D::State::setCompositeOperation(m_platformContext, compositeOperator, blendMode);
Direct2D::fillRect(m_platformContext, rect, color, Direct2D::ShadowState(state));
Direct2D::State::setCompositeOperation(m_platformContext, previousOperator, BlendMode::Normal);
}
void GraphicsContextDirect2D::fillRoundedRectImpl(const FloatRoundedRect& rect, const Color& color)
{
auto& state = this->state();
Direct2D::fillRoundedRect(*platformContext(), rect, color, Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
{
auto& state = this->state();
auto& context = *platformContext();
Direct2D::fillRectWithRoundedHole(context, rect, roundedHoleRect, Direct2D::FillSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::clip(const FloatRect& rect)
{
Direct2D::clip(*platformContext(), rect);
}
void GraphicsContextDirect2D::clipOut(const FloatRect& rect)
{
Direct2D::clipOut(*platformContext(), rect);
}
void GraphicsContextDirect2D::clipOut(const Path& path)
{
Direct2D::clipOut(*platformContext(), path);
}
void GraphicsContextDirect2D::clipPath(const Path& path, WindRule clipRule)
{
Direct2D::clipPath(*platformContext(), path, clipRule);
}
IntRect GraphicsContextDirect2D::clipBounds() const
{
return Direct2D::State::getClipBounds(*platformContext());
}
void GraphicsContextPlatformPrivate::beginTransparencyLayer(float opacity)
{
}
void GraphicsContextDirect2D::beginTransparencyLayer(float opacity)
{
GraphicsContext::beginTransparencyLayer(opacity);
save();
m_state.alpha = opacity;
Direct2D::beginTransparencyLayer(*platformContext(), opacity);
}
void GraphicsContextPlatformPrivate::endTransparencyLayer()
{
}
void GraphicsContextDirect2D::endTransparencyLayer()
{
GraphicsContext::endTransparencyLayer();
Direct2D::endTransparencyLayer(*platformContext());
m_state.alpha = m_data->currentGlobalAlpha();
restore();
}
bool GraphicsContextDirect2D::supportsTransparencyLayers()
{
return false;
}
void GraphicsContextDirect2D::didUpdateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
{
if (flags & GraphicsContextState::StrokeThicknessChange)
Direct2D::State::setStrokeThickness(m_platformContext, state.strokeThickness);
if (flags & GraphicsContextState::StrokeStyleChange)
Direct2D::State::setStrokeStyle(m_platformContext, state.strokeStyle);
if (flags & GraphicsContextState::ShadowChange) {
if (state.shadowsIgnoreTransforms) {
// Meaning that this graphics context is associated with a CanvasRenderingContext
// We flip the height since CG and HTML5 Canvas have opposite Y axis
auto& mutableState = const_cast<GraphicsContextState&>(graphicsContext().state());
auto& shadowOffset = state.shadowOffset;
mutableState.shadowOffset = { shadowOffset.width(), -shadowOffset.height() };
}
}
if (flags & GraphicsContextState::CompositeOperationChange)
Direct2D::State::setCompositeOperation(m_platformContext, state.compositeOperator, state.blendMode);
if (flags & GraphicsContextState::ShouldAntialiasChange)
Direct2D::State::setShouldAntialias(m_platformContext, state.shouldAntialias);
if (flags.contains(GraphicsContextState::StrokeColorChange)) {
ASSERT(m_state.strokeColor == color);
platformContext()->m_solidStrokeBrush = brushWithColor(strokeColor());
}
if (flags.contains(GraphicsContextState::FillColorChange)) {
ASSERT(m_state.fillColor == color);
platformContext()->m_solidFillBrush = brushWithColor(fillColor());
}
if (flags.contains(GraphicsContextState::ShouldSmoothFontsChange)) {
auto fontSmoothingMode = enable ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
platformContext()->renderTarget()->SetTextAntialiasMode(fontSmoothingMode);
}
if (flags.contains(GraphicsContextState::AlphaChange)) {
ASSERT(m_state.alpha == alpha);
m_data->setAlpha(alpha);
}
if (flags.contains(GraphicsContextState::ImageInterpolationQualityChange)) {
D2D1_INTERPOLATION_MODE quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
switch (mode) {
case InterpolationQuality::Default:
quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
break;
case InterpolationQuality::DoNotInterpolate:
quality = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
break;
case InterpolationQuality::Low:
quality = D2D1_INTERPOLATION_MODE_LINEAR;
break;
case InterpolationQuality::Medium:
quality = D2D1_INTERPOLATION_MODE_CUBIC;
break;
case InterpolationQuality::High:
quality = D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
break;
}
// FIXME: SetInterpolationQuality(platformContext(), quality);
}
}
void GraphicsContextDirect2D::setMiterLimit(float limit)
{
Direct2D::setMiterLimit(*platformContext(), limit);
}
void GraphicsContextDirect2D::clearRect(const FloatRect& rect)
{
Direct2D::clearRect(*platformContext(), rect);
}
void GraphicsContextDirect2D::strokeRect(const FloatRect& rect, float lineWidth)
{
auto& state = this->state();
auto& context = *platformContext();
Direct2D::strokeRect(context, rect, lineWidth, Direct2D::StrokeSource(state, *this), Direct2D::ShadowState(state));
}
void GraphicsContextDirect2D::setLineCap(LineCap cap)
{
Direct2D::setLineCap(*platformContext(), cap);
}
void GraphicsContextDirect2D::setLineDash(const DashArray& dashes, float dashOffset)
{
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;
}
Direct2D::setLineDash(*platformContext(), dashes, dashOffset);
}
void GraphicsContextDirect2D::setLineJoin(LineJoin join)
{
Direct2D::setLineJoin(*platformContext(), join);
}
void GraphicsContextDirect2D::scale(const FloatSize& size)
{
Direct2D::scale(*platformContext(), size);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContextDirect2D::rotate(float angle)
{
Direct2D::rotate(*platformContext(), angle);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContextDirect2D::translate(float x, float y)
{
Direct2D::translate(*platformContext(), x, y);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContextDirect2D::concatCTM(const AffineTransform& transform)
{
Direct2D::concatCTM(*platformContext(), transform);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
void GraphicsContextDirect2D::setCTM(const AffineTransform& transform)
{
Direct2D::State::setCTM(*platformContext(), transform);
// FIXME: m_data->m_userToDeviceTransformKnownToBeIdentity = false;
}
AffineTransform GraphicsContextDirect2D::getCTM(IncludeDeviceScale includeScale) const
{
return Direct2D::State::getCTM(*platformContext());
}
FloatRect GraphicsContextDirect2D::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
{
return Direct2D::State::roundToDevicePixels(*platformContext(), rect);
}
void GraphicsContextDirect2D::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines, StrokeStyle strokeStyle)
{
if (!widths.size())
return;
Direct2D::drawLinesForText(*platformContext(), point, thickness, widths, printing, doubleUnderlines, m_state.strokeColor);
}
void GraphicsContextDirect2D::setURLForRect(const URL& link, const FloatRect& destRect)
{
notImplemented();
}
void GraphicsContextDirect2D::setIsCALayerContext(bool isLayerContext)
{
// This function is probabaly not needed.
notImplemented();
}
bool GraphicsContextDirect2D::isCALayerContext() const
{
// This function is probabaly not needed.
notImplemented();
return false;
}
void GraphicsContextDirect2D::setIsAcceleratedContext(bool isAccelerated)
{
notImplemented();
}
bool GraphicsContextDirect2D::isAcceleratedContext() const
{
return Direct2D::State::isAcceleratedContext(*platformContext());
}
void GraphicsContextDirect2D::applyDeviceScaleFactor(float deviceScaleFactor)
{
// This is a no-op for Direct2D.
}
void GraphicsContextDirect2D::fillEllipse(const FloatRect& ellipse)
{
if (m_state.fillGradient || m_state.fillPattern) {
// FIXME: We should be able to fill ellipses with pattern/gradient brushes in D2D.
fillEllipseAsPath(ellipse);
return;
}
Direct2D::fillEllipse(*platformContext(), ellipse, m_state.fillColor, m_state.strokeStyle, m_state.strokeColor, m_state.strokeThickness);
}
void GraphicsContextDirect2D::strokeEllipse(const FloatRect& ellipse)
{
if (m_state.strokeGradient || m_state.strokePattern) {
// FIXME: We should be able to stroke ellipses with pattern/gradient brushes in D2D.
strokeEllipseAsPath(ellipse);
return;
}
Direct2D::drawEllipse(*platformContext(), ellipse, m_state.strokeStyle, m_state.strokeColor, m_state.strokeThickness);
}
void GraphicsContextDirect2D::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode fontSmoothing)
{
UNUSED_PARAM(fontSmoothing);
if (!font.platformData().size())
return;
auto xOffset = point.x();
Vector<unsigned short> glyphs(numGlyphs);
Vector<float> horizontalAdvances(numGlyphs);
Vector<DWRITE_GLYPH_OFFSET> glyphOffsets(numGlyphs);
for (unsigned i = 0; i < numGlyphs; ++i) {
if (i + from >= glyphBuffer.advancesCount())
break;
auto advance = glyphBuffer.advances(i + from);
if (!advance)
continue;
glyphs[i] = glyphBuffer.glyphAt(i + from);
horizontalAdvances[i] = advance->width();
glyphOffsets[i].advanceOffset = advance->width();
glyphOffsets[i].ascenderOffset = advance->height();
}
double syntheticBoldOffset = font.syntheticBoldOffset();
auto& state = graphicsContext().state();
Direct2D::drawGlyphs(m_platformContext, Direct2D::FillSource(state, graphicsContext()), Direct2D::StrokeSource(state, graphicsContext()),
Direct2D::ShadowState(state), point, font, syntheticBoldOffset, glyphs, horizontalAdvances, glyphOffsets, xOffset,
state.textDrawingMode, state.strokeThickness, state.shadowOffset, state.shadowColor);
}
GraphicsContext::ClipToDrawingCommandsResult GraphicsContextDirect2D::clipToDrawingCommands(const FloatRect&, const DestinationColorSpace&, Function<void(GraphicsContext&)>&&)
{
// FIXME: Not implemented.
return ClipToDrawingCommandsResult::Success;
}
void GraphicsContextDirect2D::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
{
RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore);
if (!image)
return;
auto* context = &graphicsContext();
if (auto surface = image->nativeImageForCurrentFrame(context))
notImplemented();
}
#if ENABLE(VIDEO)
void GraphicsContextDirect2D::paintFrameForMedia(MediaPlayer&, const FloatRect&)
{
// FIXME: Not implemented.
}
#endif
}