File: BaselineAlignment.cpp

package info (click to toggle)
webkit2gtk 2.51.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 455,340 kB
  • sloc: cpp: 3,865,253; javascript: 197,710; ansic: 165,177; python: 49,241; asm: 21,868; ruby: 18,095; perl: 16,926; xml: 4,623; sh: 2,409; yacc: 2,356; java: 2,019; lex: 1,330; pascal: 372; makefile: 210
file content (189 lines) | stat: -rw-r--r-- 8,674 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * Copyright (C) 2023 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 "BaselineAlignment.h"

#include "BaselineAlignmentInlines.h"
#include "RenderBox.h"
#include "RenderBoxModelObjectInlines.h"
#include "RenderStyleInlines.h"
#include <wtf/TZoneMallocInlines.h>

namespace WebCore {

WTF_MAKE_TZONE_ALLOCATED_IMPL(BaselineGroup);
WTF_MAKE_TZONE_ALLOCATED_IMPL(BaselineAlignmentState);

BaselineGroup::BaselineGroup(FlowDirection blockFlow, ItemPosition alignmentSubjectPreference)
    : m_maxAscent(0), m_alignmentSubjects()
{
    m_blockFlow = blockFlow;
    m_preference = alignmentSubjectPreference;
}

void BaselineGroup::update(const RenderBox& alignmentSubject, LayoutUnit ascent)
{
    if (m_alignmentSubjects.add(alignmentSubject).isNewEntry)
        m_maxAscent = std::max(m_maxAscent, ascent);
}

bool BaselineGroup::isOppositeBlockFlow(FlowDirection blockFlow) const
{
    switch (blockFlow) {
    case FlowDirection::TopToBottom:
        return false;
    case FlowDirection::LeftToRight:
        return m_blockFlow == FlowDirection::RightToLeft;
    case FlowDirection::RightToLeft:
        return m_blockFlow == FlowDirection::LeftToRight;
    default:
        ASSERT_NOT_REACHED();
        return false;
    }
}

bool BaselineGroup::isOrthogonalBlockFlow(FlowDirection blockFlow) const
{
    switch (blockFlow) {
    case FlowDirection::TopToBottom:
        return m_blockFlow != FlowDirection::TopToBottom;
    case FlowDirection::LeftToRight:
    case FlowDirection::RightToLeft:
        return m_blockFlow == FlowDirection::TopToBottom;
    default:
        ASSERT_NOT_REACHED();
        return false;
    }
}

bool BaselineGroup::isCompatible(FlowDirection alignmentSubjectBlockFlow, ItemPosition alignmentSubjectPreference) const
{
    ASSERT(isBaselinePosition(alignmentSubjectPreference));
    ASSERT(computeSize() > 0);
    return ((m_blockFlow == alignmentSubjectBlockFlow || isOrthogonalBlockFlow(alignmentSubjectBlockFlow)) && m_preference == alignmentSubjectPreference)
        || (isOppositeBlockFlow(alignmentSubjectBlockFlow) && m_preference != alignmentSubjectPreference);
}

BaselineAlignmentState::BaselineAlignmentState(const RenderBox& alignmentSubject, ItemPosition preference, LayoutUnit ascent, LogicalBoxAxis alignmentContextAxis, WritingMode alignmentContainerWritingMode)
    : m_alignmentContainerWritingMode(alignmentContainerWritingMode)
    , m_alignmentContextAxis(alignmentContextAxis)
{
    ASSERT(isBaselinePosition(preference));
    updateSharedGroup(alignmentSubject, preference, ascent);
}

const BaselineGroup& BaselineAlignmentState::sharedGroup(const RenderBox& alignmentSubject, ItemPosition preference) const
{
    ASSERT(isBaselinePosition(preference));
    return const_cast<BaselineAlignmentState*>(this)->findCompatibleSharedGroup(alignmentSubject, preference);
}

Vector<BaselineGroup>& BaselineAlignmentState::sharedGroups()
{
    return m_sharedGroups;
}

void BaselineAlignmentState::updateSharedGroup(const RenderBox& alignmentSubject, ItemPosition preference, LayoutUnit ascent)
{
    ASSERT(isBaselinePosition(preference));
    BaselineGroup& group = findCompatibleSharedGroup(alignmentSubject, preference);
    group.update(alignmentSubject, ascent);
}

FontBaseline BaselineAlignmentState::dominantBaseline(WritingMode writingMode)
{
    // https://drafts.csswg.org/css-inline-3/#alignment-baseline-property
    // https://drafts.csswg.org/css-inline-3/#dominant-baseline-property
    return writingMode.prefersCentralBaseline() ? FontBaseline::Central : FontBaseline::Alphabetic;
}

LayoutUnit BaselineAlignmentState::synthesizedBaseline(const RenderBox& box, FontBaseline baselineType, WritingMode writingModeForSynthesis, LineDirection lineDirection, BaselineSynthesisEdge edge)
{
    auto boxSize = lineDirection == LineDirection::Horizontal ? box.height() : box.width();
    if (edge == BaselineSynthesisEdge::ContentBox)
        boxSize -= lineDirection == LineDirection::Horizontal ? box.verticalBorderAndPaddingExtent() : box.horizontalBorderAndPaddingExtent();
    else if (edge == BaselineSynthesisEdge::MarginBox)
        boxSize += lineDirection == LineDirection::Horizontal ? box.verticalMarginExtent() : box.horizontalMarginExtent();

    if (baselineType == FontBaseline::Alphabetic) {
        // When synthesizing the alphabetic baseline for a box we are determining the distance
        // to the line-under edge. For a box with vertical-lr writing mode the location
        // of the line-under edge should be the same as the box's block-start edge. For
        // vertical-rl writing mode we need the box's size since they are on opposiate sides.
        auto shouldTreatAsHorizontal = lineDirection == LineDirection::Horizontal || writingModeForSynthesis.computedWritingMode() == StyleWritingMode::VerticalRl;
        return shouldTreatAsHorizontal ? boxSize : LayoutUnit();
    }
    return boxSize / 2;
}

WritingMode BaselineAlignmentState::usedWritingModeForBaselineAlignment(LogicalBoxAxis alignmentContextAxis,
    WritingMode alignmentContainerWritingMode, WritingMode aligmentSubjectWritingMode)
{

    auto isAlignmentSubjectBlockFlowParallelToAlignmentContextAxis = [&] {
        if (alignmentContextAxis == LogicalBoxAxis::Block)
            return !alignmentContainerWritingMode.isOrthogonal(aligmentSubjectWritingMode);
        return alignmentContainerWritingMode.isOrthogonal(aligmentSubjectWritingMode);
    };

    // css-align-3: 9.1. Determining the Baselines of a Box
    // In general, the writing mode of the box, shape, or other object being aligned is used to determine
    // the line-under and line-over edges for synthesis...
    if (!isAlignmentSubjectBlockFlowParallelToAlignmentContextAxis())
        return aligmentSubjectWritingMode;

    // ... However, when that writing mode’s block flow direction
    // is parallel to the axis of the alignment context, an axis-compatible writing mode must be assumed:
    //

    // If the box establishing the alignment context has a block flow direction that is orthogonal to the
    // axis of the alignment context, use its writing mode.
    if (alignmentContextAxis == LogicalBoxAxis::Inline)
        return alignmentContainerWritingMode;

    // Otherwise:
    // If the box’s own writing mode is vertical, assume horizontal-tb.
    // If the box’s own writing mode is horizontal, assume vertical-lr if
    // direction is ltr and vertical-rl if direction is rtl.
    if (!aligmentSubjectWritingMode.isHorizontal())
        return { };
    auto styleWritingMode = alignmentContainerWritingMode.isBidiLTR() ? StyleWritingMode::VerticalLr : StyleWritingMode::VerticalRl;
    return { styleWritingMode, TextDirection::LTR, TextOrientation::Mixed };
}

BaselineGroup& BaselineAlignmentState::findCompatibleSharedGroup(const RenderBox& alignmentSubject, ItemPosition preference)
{
    auto usedWritingModeForBaselineAlignment = this->usedWritingModeForBaselineAlignment(m_alignmentContextAxis, m_alignmentContainerWritingMode, alignmentSubject.writingMode());
    auto blockFlowDirection = usedWritingModeForBaselineAlignment.blockDirection();
    for (auto& group : m_sharedGroups) {
        if (group.isCompatible(blockFlowDirection, preference))
            return group;
    }
    m_sharedGroups.insert(0, BaselineGroup(blockFlowDirection, preference));
    return m_sharedGroups[0];
}

} // namespace WebCore