| /* |
| * 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(¤tTransform); |
| |
| 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 |
| |
| |
| } |