blob: 3abd1b103d3fe1c5cfd6126d260ace7a975c603d [file] [log] [blame]
/*
* 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