Update font selection algorithm to match latest CSS spec
[WebKit-https.git] / Source / WebCore / platform / graphics / FontSelectionAlgorithm.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "FontSelectionAlgorithm.h"
28
29 namespace WebCore {
30
31 auto FontSelectionAlgorithm::stretchDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
32 {
33     auto width = capabilities.width;
34     ASSERT(width.isValid());
35     if (width.includes(m_request.width))
36         return { FontSelectionValue(), m_request.width };
37
38     if (m_request.width > normalStretchValue()) {
39         if (width.minimum > m_request.width)
40             return { width.minimum - m_request.width, width.minimum };
41         ASSERT(width.maximum < m_request.width);
42         auto threshold = std::max(m_request.width, m_capabilitiesBounds.width.maximum);
43         return { threshold - width.maximum, width.maximum };
44     }
45
46     if (width.maximum < m_request.width)
47         return { m_request.width - width.maximum, width.maximum };
48     ASSERT(width.minimum > m_request.width);
49     auto threshold = std::min(m_request.width, m_capabilitiesBounds.width.minimum);
50     return { width.minimum - threshold, width.minimum };
51 }
52
53 auto FontSelectionAlgorithm::styleDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
54 {
55     auto slope = capabilities.slope;
56     ASSERT(slope.isValid());
57     if (slope.includes(m_request.slope))
58         return { FontSelectionValue(), m_request.slope };
59
60     if (m_request.slope >= italicThreshold()) {
61         if (slope.minimum > m_request.slope)
62             return { slope.minimum - m_request.slope, slope.minimum };
63         ASSERT(m_request.slope > slope.maximum);
64         auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
65         return { threshold - slope.maximum, slope.maximum };
66     }
67
68     if (m_request.slope >= FontSelectionValue()) {
69         if (slope.maximum >= FontSelectionValue() && slope.maximum < m_request.slope)
70             return { m_request.slope - slope.maximum, slope.maximum };
71         if (slope.minimum > m_request.slope)
72             return { slope.minimum, slope.minimum };
73         ASSERT(slope.maximum < FontSelectionValue());
74         auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
75         return { threshold - slope.maximum, slope.maximum };
76     }
77
78     if (m_request.slope > -italicThreshold()) {
79         if (slope.minimum > m_request.slope && slope.minimum <= FontSelectionValue())
80             return { slope.minimum - m_request.slope, slope.minimum };
81         if (slope.maximum < m_request.slope)
82             return { -slope.maximum, slope.maximum };
83         ASSERT(slope.minimum > FontSelectionValue());
84         auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
85         return { slope.minimum - threshold, slope.minimum };
86     }
87
88     if (slope.maximum < m_request.slope)
89         return { m_request.slope - slope.maximum, slope.maximum };
90     ASSERT(slope.minimum > m_request.slope);
91     auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
92     return { slope.minimum - threshold, slope.minimum };
93 }
94
95 auto FontSelectionAlgorithm::weightDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
96 {
97     auto weight = capabilities.weight;
98     ASSERT(weight.isValid());
99     if (weight.includes(m_request.weight))
100         return { FontSelectionValue(), m_request.weight };
101
102     if (m_request.weight >= lowerWeightSearchThreshold() && m_request.weight <= upperWeightSearchThreshold()) {
103         if (weight.minimum > m_request.weight && weight.minimum <= upperWeightSearchThreshold())
104             return { weight.minimum - m_request.weight, weight.minimum };
105         if (weight.maximum < m_request.weight)
106             return { upperWeightSearchThreshold() - weight.maximum, weight.maximum };
107         ASSERT(weight.minimum > upperWeightSearchThreshold());
108         auto threshold = std::min(m_request.weight, m_capabilitiesBounds.weight.minimum);
109         return { weight.minimum - threshold, weight.minimum };
110     }
111     if (m_request.weight < lowerWeightSearchThreshold()) {
112         if (weight.maximum < m_request.weight)
113             return { m_request.weight - weight.maximum, weight.maximum };
114         ASSERT(weight.minimum > m_request.weight);
115         auto threshold = std::min(m_request.weight, m_capabilitiesBounds.weight.minimum);
116         return { weight.minimum - threshold, weight.minimum };
117     }
118     ASSERT(m_request.weight >= upperWeightSearchThreshold());
119     if (weight.minimum > m_request.weight)
120         return { weight.minimum - m_request.weight, weight.minimum };
121     ASSERT(weight.maximum < m_request.weight);
122     auto threshold = std::max(m_request.weight, m_capabilitiesBounds.weight.maximum);
123     return { threshold - weight.maximum, weight.maximum };
124 }
125
126 void FontSelectionAlgorithm::filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange)
127 {
128     std::optional<FontSelectionValue> smallestDistance;
129     FontSelectionValue closestValue;
130     iterateActiveCapabilities([&](FontSelectionCapabilities capabilities, size_t) {
131         auto distanceResult = (this->*computeDistance)(capabilities);
132         if (!smallestDistance || distanceResult.distance < smallestDistance.value()) {
133             smallestDistance = distanceResult.distance;
134             closestValue = distanceResult.value;
135         }
136     });
137     ASSERT(smallestDistance);
138     iterateActiveCapabilities([&](auto& capabilities, size_t i) {
139         if (!(capabilities.*inclusionRange).includes(closestValue))
140             m_filter[i] = false;
141     });
142 }
143
144 size_t FontSelectionAlgorithm::indexOfBestCapabilities()
145 {
146     filterCapability(&FontSelectionAlgorithm::stretchDistance, &FontSelectionCapabilities::width);
147     filterCapability(&FontSelectionAlgorithm::styleDistance, &FontSelectionCapabilities::slope);
148     filterCapability(&FontSelectionAlgorithm::weightDistance, &FontSelectionCapabilities::weight);
149
150     auto result = iterateActiveCapabilitiesWithReturn<size_t>([](FontSelectionCapabilities, size_t i) {
151         return i;
152     });
153     ASSERT(result);
154     return result.value_or(0);
155 }
156
157 }