blob: 589b2082d065f83c4989a83d825689d217686005 [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.
*/
#include "config.h"
#include "GraphicsContextState.h"
#include <wtf/MathExtras.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
GraphicsContextState::GraphicsContextState(const ChangeFlags& changeFlags, InterpolationQuality imageInterpolationQuality)
: m_changeFlags(changeFlags)
, m_imageInterpolationQuality(imageInterpolationQuality)
{
}
GraphicsContextState GraphicsContextState::cloneForRecording() const
{
auto clone = *this;
clone.m_changeFlags = { };
return clone;
}
bool GraphicsContextState::containsOnlyInlineChanges() const
{
if (m_changeFlags != (m_changeFlags & basicChangeFlags))
return false;
if (m_changeFlags.contains(Change::StrokeBrush) && !m_strokeBrush.isInlineColor())
return false;
if (m_changeFlags.contains(Change::FillBrush) && !m_fillBrush.isInlineColor())
return false;
return true;
}
constexpr unsigned toIndex(GraphicsContextState::Change change)
{
return WTF::ctzConstexpr(WTF::enumToUnderlyingType(change));
}
void GraphicsContextState::mergeChanges(const GraphicsContextState& state, const std::optional<GraphicsContextState>& lastDrawingState)
{
for (auto change : state.changes()) {
auto mergeChange = [&](auto GraphicsContextState::*property) {
if (this->*property == state.*property)
return;
this->*property = state.*property;
m_changeFlags.set(change, !lastDrawingState || (*lastDrawingState).*property != this->*property);
};
switch (toIndex(change)) {
case toIndex(Change::FillBrush): mergeChange(&GraphicsContextState::m_fillBrush); break;
case toIndex(Change::FillRule): mergeChange(&GraphicsContextState::m_fillRule); break;
case toIndex(Change::StrokeBrush): mergeChange(&GraphicsContextState::m_strokeBrush); break;
case toIndex(Change::StrokeThickness): mergeChange(&GraphicsContextState::m_strokeThickness); break;
case toIndex(Change::StrokeStyle): mergeChange(&GraphicsContextState::m_strokeStyle); break;
case toIndex(Change::CompositeMode): mergeChange(&GraphicsContextState::m_compositeMode); break;
case toIndex(Change::DropShadow): mergeChange(&GraphicsContextState::m_dropShadow); break;
case toIndex(Change::Alpha): mergeChange(&GraphicsContextState::m_alpha); break;
case toIndex(Change::TextDrawingMode): mergeChange(&GraphicsContextState::m_textDrawingMode); break;
case toIndex(Change::ImageInterpolationQuality): mergeChange(&GraphicsContextState::m_imageInterpolationQuality); break;
case toIndex(Change::ShouldAntialias): mergeChange(&GraphicsContextState::m_shouldAntialias); break;
case toIndex(Change::ShouldSmoothFonts): mergeChange(&GraphicsContextState::m_shouldSmoothFonts); break;
case toIndex(Change::ShouldSubpixelQuantizeFonts): mergeChange(&GraphicsContextState::m_shouldSubpixelQuantizeFonts); break;
case toIndex(Change::ShadowsIgnoreTransforms): mergeChange(&GraphicsContextState::m_shadowsIgnoreTransforms); break;
case toIndex(Change::DrawLuminanceMask): mergeChange(&GraphicsContextState::m_drawLuminanceMask); break;
#if HAVE(OS_DARK_MODE_SUPPORT)
case toIndex(Change::UseDarkAppearance): mergeChange(&GraphicsContextState::m_useDarkAppearance); break;
#endif
}
}
}
void GraphicsContextState::didBeginTransparencyLayer()
{
#if USE(CG)
// CGContextBeginTransparencyLayer() sets the CG global alpha to 1. Keep our alpha in sync.
m_alpha = 1;
#endif
}
void GraphicsContextState::didEndTransparencyLayer(float originalOpacity)
{
#if USE(CG)
// CGContextBeginTransparencyLayer() sets the CG global alpha to 1. Resore our alpha now.
m_alpha = originalOpacity;
#else
UNUSED_PARAM(originalOpacity);
#endif
}
static const char* stateChangeName(GraphicsContextState::Change change)
{
switch (change) {
case GraphicsContextState::Change::FillBrush:
return "fill-brush";
case GraphicsContextState::Change::FillRule:
return "fill-rule";
case GraphicsContextState::Change::StrokeBrush:
return "stroke-brush";
case GraphicsContextState::Change::StrokeThickness:
return "stroke-thickness";
case GraphicsContextState::Change::StrokeStyle:
return "stroke-style";
case GraphicsContextState::Change::CompositeMode:
return "composite-mode";
case GraphicsContextState::Change::DropShadow:
return "drop-shadow";
case GraphicsContextState::Change::Alpha:
return "alpha";
case GraphicsContextState::Change::ImageInterpolationQuality:
return "image-interpolation-quality";
case GraphicsContextState::Change::TextDrawingMode:
return "text-drawing-mode";
case GraphicsContextState::Change::ShouldAntialias:
return "should-antialias";
case GraphicsContextState::Change::ShouldSmoothFonts:
return "should-smooth-fonts";
case GraphicsContextState::Change::ShouldSubpixelQuantizeFonts:
return "should-subpixel-quantize-fonts";
case GraphicsContextState::Change::ShadowsIgnoreTransforms:
return "shadows-ignore-transforms";
case GraphicsContextState::Change::DrawLuminanceMask:
return "draw-luminance-mask";
#if HAVE(OS_DARK_MODE_SUPPORT)
case GraphicsContextState::Change::UseDarkAppearance:
return "use-dark-appearance";
#endif
}
RELEASE_ASSERT_NOT_REACHED();
}
TextStream& GraphicsContextState::dump(TextStream& ts) const
{
auto dump = [&](Change change, auto GraphicsContextState::*property) {
if (m_changeFlags.contains(change))
ts.dumpProperty(stateChangeName(change), this->*property);
};
ts.dumpProperty("change-flags", m_changeFlags);
dump(Change::FillBrush, &GraphicsContextState::m_fillBrush);
dump(Change::FillRule, &GraphicsContextState::m_fillRule);
dump(Change::StrokeBrush, &GraphicsContextState::m_strokeBrush);
dump(Change::StrokeThickness, &GraphicsContextState::m_strokeThickness);
dump(Change::StrokeStyle, &GraphicsContextState::m_strokeStyle);
dump(Change::CompositeMode, &GraphicsContextState::m_compositeMode);
dump(Change::DropShadow, &GraphicsContextState::m_dropShadow);
dump(Change::Alpha, &GraphicsContextState::m_alpha);
dump(Change::ImageInterpolationQuality, &GraphicsContextState::m_imageInterpolationQuality);
dump(Change::TextDrawingMode, &GraphicsContextState::m_textDrawingMode);
dump(Change::ShouldAntialias, &GraphicsContextState::m_shouldAntialias);
dump(Change::ShouldSmoothFonts, &GraphicsContextState::m_shouldSmoothFonts);
dump(Change::ShouldSubpixelQuantizeFonts, &GraphicsContextState::m_shouldSubpixelQuantizeFonts);
dump(Change::ShadowsIgnoreTransforms, &GraphicsContextState::m_shadowsIgnoreTransforms);
dump(Change::DrawLuminanceMask, &GraphicsContextState::m_drawLuminanceMask);
#if HAVE(OS_DARK_MODE_SUPPORT)
dump(Change::UseDarkAppearance, &GraphicsContextState::m_useDarkAppearance);
#endif
return ts;
}
TextStream& operator<<(TextStream& ts, GraphicsContextState::Change change)
{
ts << stateChangeName(change);
return ts;
}
TextStream& operator<<(TextStream& ts, const GraphicsContextState& state)
{
return state.dump(ts);
}
} // namespace WebCore