| /* |
| * 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 |