| /* |
| * Copyright (C) 2022 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. |
| */ |
| |
| #pragma once |
| |
| #include "GraphicsTypes.h" |
| #include "SourceBrush.h" |
| #include "WindRule.h" |
| #include <wtf/OptionSet.h> |
| |
| namespace WebCore { |
| |
| class GraphicsContextState { |
| friend class GraphicsContextCairo; |
| public: |
| enum class Change : uint32_t { |
| FillBrush = 1 << 0, |
| FillRule = 1 << 1, |
| |
| StrokeBrush = 1 << 2, |
| StrokeThickness = 1 << 3, |
| StrokeStyle = 1 << 4, |
| |
| CompositeMode = 1 << 5, |
| DropShadow = 1 << 6, |
| |
| Alpha = 1 << 7, |
| TextDrawingMode = 1 << 8, |
| ImageInterpolationQuality = 1 << 9, |
| |
| ShouldAntialias = 1 << 10, |
| ShouldSmoothFonts = 1 << 11, |
| ShouldSubpixelQuantizeFonts = 1 << 12, |
| ShadowsIgnoreTransforms = 1 << 13, |
| DrawLuminanceMask = 1 << 14, |
| #if HAVE(OS_DARK_MODE_SUPPORT) |
| UseDarkAppearance = 1 << 15, |
| #endif |
| }; |
| using ChangeFlags = OptionSet<Change>; |
| |
| static constexpr ChangeFlags basicChangeFlags { Change::StrokeThickness, Change::StrokeBrush, Change::FillBrush }; |
| |
| WEBCORE_EXPORT GraphicsContextState(const ChangeFlags& = { }, InterpolationQuality = InterpolationQuality::Default); |
| |
| ChangeFlags changes() const { return m_changeFlags; } |
| void didApplyChanges() { m_changeFlags = { }; } |
| |
| GraphicsContextState cloneForRecording() const; |
| |
| const SourceBrush& fillBrush() const { return m_fillBrush; } |
| void setFillBrush(const SourceBrush& brush) { setProperty(Change::FillBrush, &GraphicsContextState::m_fillBrush, brush); } |
| void setFillColor(const Color& color) { setProperty(Change::FillBrush, &GraphicsContextState::m_fillBrush, { color }); } |
| void setFillGradient(Ref<Gradient>&& gradient, const AffineTransform& spaceTransform) { m_fillBrush.setGradient(WTFMove(gradient), spaceTransform); m_changeFlags.add(Change::FillBrush); } |
| void setFillPattern(Ref<Pattern>&& pattern) { m_fillBrush.setPattern(WTFMove(pattern)); m_changeFlags.add(Change::FillBrush); } |
| |
| WindRule fillRule() const { return m_fillRule; } |
| void setFillRule(WindRule fillRule) { setProperty(Change::FillRule, &GraphicsContextState::m_fillRule, fillRule); } |
| |
| const SourceBrush& strokeBrush() const { return m_strokeBrush; } |
| void setStrokeBrush(const SourceBrush& brush) { setProperty(Change::StrokeBrush, &GraphicsContextState::m_strokeBrush, brush); } |
| void setStrokeColor(const Color& color) { setProperty(Change::StrokeBrush, &GraphicsContextState::m_strokeBrush, { color }); } |
| void setStrokeGradient(Ref<Gradient>&& gradient, const AffineTransform& spaceTransform) { m_strokeBrush.setGradient(WTFMove(gradient), spaceTransform); m_changeFlags.add(Change::StrokeBrush); } |
| void setStrokePattern(Ref<Pattern>&& pattern) { m_strokeBrush.setPattern(WTFMove(pattern)); m_changeFlags.add(Change::StrokeBrush); } |
| |
| float strokeThickness() const { return m_strokeThickness; } |
| void setStrokeThickness(float strokeThickness) { setProperty(Change::StrokeThickness, &GraphicsContextState::m_strokeThickness, strokeThickness); } |
| |
| StrokeStyle strokeStyle() const { return m_strokeStyle; } |
| void setStrokeStyle(StrokeStyle strokeStyle) { setProperty(Change::StrokeStyle, &GraphicsContextState::m_strokeStyle, strokeStyle); } |
| |
| const CompositeMode& compositeMode() const { return m_compositeMode; } |
| void setCompositeMode(CompositeMode compositeMode) { setProperty(Change::CompositeMode, &GraphicsContextState::m_compositeMode, compositeMode); } |
| |
| const DropShadow& dropShadow() const { return m_dropShadow; } |
| void setDropShadow(const DropShadow& dropShadow) { setProperty(Change::DropShadow, &GraphicsContextState::m_dropShadow, dropShadow); } |
| |
| float alpha() const { return m_alpha; } |
| void setAlpha(float alpha) { setProperty(Change::Alpha, &GraphicsContextState::m_alpha, alpha); } |
| |
| InterpolationQuality imageInterpolationQuality() const { return m_imageInterpolationQuality; } |
| void setImageInterpolationQuality(InterpolationQuality imageInterpolationQuality) { setProperty(Change::ImageInterpolationQuality, &GraphicsContextState::m_imageInterpolationQuality, imageInterpolationQuality); } |
| |
| TextDrawingModeFlags textDrawingMode() const { return m_textDrawingMode; } |
| void setTextDrawingMode(TextDrawingModeFlags textDrawingMode) { setProperty(Change::TextDrawingMode, &GraphicsContextState::m_textDrawingMode, textDrawingMode); } |
| |
| bool shouldAntialias() const { return m_shouldAntialias; } |
| void setShouldAntialias(bool shouldAntialias) { setProperty(Change::ShouldAntialias, &GraphicsContextState::m_shouldAntialias, shouldAntialias); } |
| |
| bool shouldSmoothFonts() const { return m_shouldSmoothFonts; } |
| void setShouldSmoothFonts(bool shouldSmoothFonts) { setProperty(Change::ShouldSmoothFonts, &GraphicsContextState::m_shouldSmoothFonts, shouldSmoothFonts); } |
| |
| bool shouldSubpixelQuantizeFonts() const { return m_shouldSubpixelQuantizeFonts; } |
| void setShouldSubpixelQuantizeFonts(bool shouldSubpixelQuantizeFonts) { setProperty(Change::ShouldSubpixelQuantizeFonts, &GraphicsContextState::m_shouldSubpixelQuantizeFonts, shouldSubpixelQuantizeFonts); } |
| |
| bool shadowsIgnoreTransforms() const { return m_shadowsIgnoreTransforms; } |
| void setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms) { setProperty(Change::ShadowsIgnoreTransforms, &GraphicsContextState::m_shadowsIgnoreTransforms, shadowsIgnoreTransforms); } |
| |
| bool drawLuminanceMask() const { return m_drawLuminanceMask; } |
| void setDrawLuminanceMask(bool drawLuminanceMask) { setProperty(Change::DrawLuminanceMask, &GraphicsContextState::m_drawLuminanceMask, drawLuminanceMask); } |
| |
| #if HAVE(OS_DARK_MODE_SUPPORT) |
| bool useDarkAppearance() const { return m_useDarkAppearance; } |
| void setUseDarkAppearance(bool useDarkAppearance) { setProperty(Change::UseDarkAppearance, &GraphicsContextState::m_useDarkAppearance, useDarkAppearance); } |
| #endif |
| |
| bool containsOnlyInlineChanges() const; |
| void mergeChanges(const GraphicsContextState&, const std::optional<GraphicsContextState>& lastDrawingState = std::nullopt); |
| |
| void didBeginTransparencyLayer(); |
| void didEndTransparencyLayer(float originalOpacity); |
| |
| WTF::TextStream& dump(WTF::TextStream&) const; |
| |
| template<class Encoder> void encode(Encoder&) const; |
| template<class Decoder> static std::optional<GraphicsContextState> decode(Decoder&); |
| |
| private: |
| template<typename T> |
| void setProperty(Change change, T GraphicsContextState::*property, const T& value) |
| { |
| #if !USE(CAIRO) |
| if (this->*property == value) |
| return; |
| #endif |
| this->*property = value; |
| m_changeFlags.add(change); |
| } |
| |
| template<typename T> |
| void setProperty(Change change, T GraphicsContextState::*property, T&& value) |
| { |
| #if !USE(CAIRO) |
| if (this->*property == value) |
| return; |
| #endif |
| this->*property = WTFMove(value); |
| m_changeFlags.add(change); |
| } |
| |
| ChangeFlags m_changeFlags; |
| |
| SourceBrush m_fillBrush { Color::black }; |
| WindRule m_fillRule { WindRule::NonZero }; |
| |
| SourceBrush m_strokeBrush { Color::black }; |
| float m_strokeThickness { 0 }; |
| StrokeStyle m_strokeStyle { SolidStroke }; |
| |
| CompositeMode m_compositeMode { CompositeOperator::SourceOver, BlendMode::Normal }; |
| DropShadow m_dropShadow; |
| |
| float m_alpha { 1 }; |
| InterpolationQuality m_imageInterpolationQuality { InterpolationQuality::Default }; |
| TextDrawingModeFlags m_textDrawingMode { TextDrawingMode::Fill }; |
| |
| bool m_shouldAntialias { true }; |
| bool m_shouldSmoothFonts { true }; |
| bool m_shouldSubpixelQuantizeFonts { true }; |
| bool m_shadowsIgnoreTransforms { false }; |
| bool m_drawLuminanceMask { false }; |
| #if HAVE(OS_DARK_MODE_SUPPORT) |
| bool m_useDarkAppearance { false }; |
| #endif |
| }; |
| |
| template<class Encoder> |
| void GraphicsContextState::encode(Encoder& encoder) const |
| { |
| auto encode = [&](Change change, auto GraphicsContextState::*property) { |
| if (m_changeFlags.contains(change)) |
| encoder << this->*property; |
| }; |
| |
| encoder << m_changeFlags; |
| |
| encode(Change::FillBrush, &GraphicsContextState::m_fillBrush); |
| encode(Change::FillRule, &GraphicsContextState::m_fillRule); |
| |
| encode(Change::StrokeBrush, &GraphicsContextState::m_strokeBrush); |
| encode(Change::StrokeThickness, &GraphicsContextState::m_strokeThickness); |
| encode(Change::StrokeStyle, &GraphicsContextState::m_strokeStyle); |
| |
| encode(Change::CompositeMode, &GraphicsContextState::m_compositeMode); |
| encode(Change::DropShadow, &GraphicsContextState::m_dropShadow); |
| |
| encode(Change::Alpha, &GraphicsContextState::m_alpha); |
| encode(Change::ImageInterpolationQuality, &GraphicsContextState::m_imageInterpolationQuality); |
| encode(Change::TextDrawingMode, &GraphicsContextState::m_textDrawingMode); |
| |
| encode(Change::ShouldAntialias, &GraphicsContextState::m_shouldAntialias); |
| encode(Change::ShouldSmoothFonts, &GraphicsContextState::m_shouldSmoothFonts); |
| encode(Change::ShouldSubpixelQuantizeFonts, &GraphicsContextState::m_shouldSubpixelQuantizeFonts); |
| encode(Change::ShadowsIgnoreTransforms, &GraphicsContextState::m_shadowsIgnoreTransforms); |
| encode(Change::DrawLuminanceMask, &GraphicsContextState::m_drawLuminanceMask); |
| #if HAVE(OS_DARK_MODE_SUPPORT) |
| encode(Change::UseDarkAppearance, &GraphicsContextState::m_useDarkAppearance); |
| #endif |
| } |
| |
| template<class Decoder> |
| std::optional<GraphicsContextState> GraphicsContextState::decode(Decoder& decoder) |
| { |
| auto decode = [&](GraphicsContextState& state, Change change, auto GraphicsContextState::*property) { |
| if (!state.changes().contains(change)) |
| return true; |
| |
| using PropertyType = typename std::remove_reference<decltype(std::declval<GraphicsContextState>().*property)>::type; |
| std::optional<PropertyType> value; |
| decoder >> value; |
| if (!value) |
| return false; |
| |
| state.*property = *value; |
| return true; |
| }; |
| |
| std::optional<ChangeFlags> changeFlags; |
| decoder >> changeFlags; |
| if (!changeFlags) |
| return std::nullopt; |
| |
| GraphicsContextState state(*changeFlags); |
| |
| if (!decode(state, Change::FillBrush, &GraphicsContextState::m_fillBrush)) |
| return std::nullopt; |
| if (!decode(state, Change::FillRule, &GraphicsContextState::m_fillRule)) |
| return std::nullopt; |
| |
| if (!decode(state, Change::StrokeBrush, &GraphicsContextState::m_strokeBrush)) |
| return std::nullopt; |
| if (!decode(state, Change::StrokeThickness, &GraphicsContextState::m_strokeThickness)) |
| return std::nullopt; |
| if (!decode(state, Change::StrokeStyle, &GraphicsContextState::m_strokeStyle)) |
| return std::nullopt; |
| |
| if (!decode(state, Change::CompositeMode, &GraphicsContextState::m_compositeMode)) |
| return std::nullopt; |
| if (!decode(state, Change::DropShadow, &GraphicsContextState::m_dropShadow)) |
| return std::nullopt; |
| |
| if (!decode(state, Change::Alpha, &GraphicsContextState::m_alpha)) |
| return std::nullopt; |
| if (!decode(state, Change::ImageInterpolationQuality, &GraphicsContextState::m_imageInterpolationQuality)) |
| return std::nullopt; |
| if (!decode(state, Change::TextDrawingMode, &GraphicsContextState::m_textDrawingMode)) |
| return std::nullopt; |
| |
| if (!decode(state, Change::ShouldAntialias, &GraphicsContextState::m_shouldAntialias)) |
| return std::nullopt; |
| if (!decode(state, Change::ShouldSmoothFonts, &GraphicsContextState::m_shouldSmoothFonts)) |
| return std::nullopt; |
| if (!decode(state, Change::ShouldSubpixelQuantizeFonts, &GraphicsContextState::m_shouldSubpixelQuantizeFonts)) |
| return std::nullopt; |
| if (!decode(state, Change::ShadowsIgnoreTransforms, &GraphicsContextState::m_shadowsIgnoreTransforms)) |
| return std::nullopt; |
| if (!decode(state, Change::DrawLuminanceMask, &GraphicsContextState::m_drawLuminanceMask)) |
| return std::nullopt; |
| #if HAVE(OS_DARK_MODE_SUPPORT) |
| if (!decode(state, Change::UseDarkAppearance, &GraphicsContextState::m_useDarkAppearance)) |
| return std::nullopt; |
| #endif |
| |
| return state; |
| } |
| |
| TextStream& operator<<(TextStream&, GraphicsContextState::Change); |
| TextStream& operator<<(TextStream&, const GraphicsContextState&); |
| |
| } // namespace WebCore |
| |
| namespace WTF { |
| |
| template<> struct EnumTraits<WebCore::GraphicsContextState::Change> { |
| using values = EnumValues< |
| WebCore::GraphicsContextState::Change, |
| WebCore::GraphicsContextState::Change::FillBrush, |
| WebCore::GraphicsContextState::Change::FillRule, |
| |
| WebCore::GraphicsContextState::Change::StrokeBrush, |
| WebCore::GraphicsContextState::Change::StrokeThickness, |
| WebCore::GraphicsContextState::Change::StrokeStyle, |
| |
| WebCore::GraphicsContextState::Change::CompositeMode, |
| WebCore::GraphicsContextState::Change::DropShadow, |
| |
| WebCore::GraphicsContextState::Change::Alpha, |
| WebCore::GraphicsContextState::Change::TextDrawingMode, |
| WebCore::GraphicsContextState::Change::ImageInterpolationQuality, |
| |
| WebCore::GraphicsContextState::Change::ShouldAntialias, |
| WebCore::GraphicsContextState::Change::ShouldSmoothFonts, |
| WebCore::GraphicsContextState::Change::ShouldSubpixelQuantizeFonts, |
| WebCore::GraphicsContextState::Change::ShadowsIgnoreTransforms, |
| WebCore::GraphicsContextState::Change::DrawLuminanceMask |
| #if HAVE(OS_DARK_MODE_SUPPORT) |
| , WebCore::GraphicsContextState::Change::UseDarkAppearance |
| #endif |
| >; |
| }; |
| |
| } // namespace WTF |