1 /*
2     Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3     Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
4     Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com)
5     Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16 
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20     Boston, MA 02110-1301, USA.
21 */
22 
23 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LENGTH_H_
24 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LENGTH_H_
25 
26 #include <cstring>
27 
28 #include "base/notreached.h"
29 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
30 #include "third_party/blink/renderer/platform/platform_export.h"
31 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
32 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
33 
34 namespace blink {
35 
36 enum ValueRange { kValueRangeAll, kValueRangeNonNegative };
37 
38 struct PixelsAndPercent {
39   DISALLOW_NEW();
PixelsAndPercentPixelsAndPercent40   PixelsAndPercent(float pixels, float percent)
41       : pixels(pixels), percent(percent) {}
42   float pixels;
43   float percent;
44 };
45 
46 class CalculationValue;
47 
48 class PLATFORM_EXPORT Length {
49   DISALLOW_NEW();
50 
51  public:
52   // FIXME: This enum makes it hard to tell in general what values may be
53   // appropriate for any given Length.
54   enum Type : unsigned char {
55     kAuto,
56     kPercent,
57     kFixed,
58     kMinContent,
59     kMaxContent,
60     kMinIntrinsic,
61     kFillAvailable,
62     kFitContent,
63     kCalculated,
64     kExtendToZoom,
65     kDeviceWidth,
66     kDeviceHeight,
67     kNone
68   };
69 
Length()70   Length() : int_value_(0), quirk_(false), type_(kAuto), is_float_(false) {}
71 
Length(Length::Type t)72   explicit Length(Length::Type t)
73       : int_value_(0), quirk_(false), type_(t), is_float_(false) {
74     DCHECK_NE(t, kCalculated);
75   }
76 
77   Length(int v, Length::Type t, bool q = false)
int_value_(v)78       : int_value_(v), quirk_(q), type_(t), is_float_(false) {
79     DCHECK_NE(t, kCalculated);
80   }
81 
82   Length(LayoutUnit v, Length::Type t, bool q = false)
83       : float_value_(v.ToFloat()), quirk_(q), type_(t), is_float_(true) {
84     DCHECK_NE(t, kCalculated);
85   }
86 
87   Length(float v, Length::Type t, bool q = false)
float_value_(v)88       : float_value_(v), quirk_(q), type_(t), is_float_(true) {
89     DCHECK_NE(t, kCalculated);
90   }
91 
92   Length(double v, Length::Type t, bool q = false)
quirk_(q)93       : quirk_(q), type_(t), is_float_(true) {
94     float_value_ = clampTo<float>(v);
95   }
96 
97   explicit Length(scoped_refptr<CalculationValue>);
98 
Length(const Length & length)99   Length(const Length& length) {
100     memcpy(this, &length, sizeof(Length));
101     if (IsCalculated())
102       IncrementCalculatedRef();
103   }
104 
105   Length& operator=(const Length& length) {
106     if (length.IsCalculated())
107       length.IncrementCalculatedRef();
108     if (IsCalculated())
109       DecrementCalculatedRef();
110     memcpy(this, &length, sizeof(Length));
111     return *this;
112   }
113 
~Length()114   ~Length() {
115     if (IsCalculated())
116       DecrementCalculatedRef();
117   }
118 
119   bool operator==(const Length& o) const {
120     return (type_ == o.type_) && (quirk_ == o.quirk_) &&
121            (IsNone() || (GetFloatValue() == o.GetFloatValue()) ||
122             IsCalculatedEqual(o));
123   }
124   bool operator!=(const Length& o) const { return !(*this == o); }
125 
126   const Length& operator*=(float v) {
127     if (IsCalculated()) {
128       NOTREACHED();
129       return *this;
130     }
131 
132     if (is_float_)
133       float_value_ = static_cast<float>(float_value_ * v);
134     else
135       int_value_ = static_cast<int>(int_value_ * v);
136 
137     return *this;
138   }
139 
140   template <typename NUMBER_TYPE>
Fixed(NUMBER_TYPE number)141   static Length Fixed(NUMBER_TYPE number) {
142     return Length(number, kFixed);
143   }
Fixed()144   static Length Fixed() { return Length(kFixed); }
Auto()145   static Length Auto() { return Length(kAuto); }
FillAvailable()146   static Length FillAvailable() { return Length(kFillAvailable); }
MinContent()147   static Length MinContent() { return Length(kMinContent); }
MaxContent()148   static Length MaxContent() { return Length(kMaxContent); }
MinIntrinsic()149   static Length MinIntrinsic() { return Length(kMinIntrinsic); }
ExtendToZoom()150   static Length ExtendToZoom() { return Length(kExtendToZoom); }
DeviceWidth()151   static Length DeviceWidth() { return Length(kDeviceWidth); }
DeviceHeight()152   static Length DeviceHeight() { return Length(kDeviceHeight); }
None()153   static Length None() { return Length(kNone); }
FitContent()154   static Length FitContent() { return Length(kFitContent); }
155   template <typename NUMBER_TYPE>
Percent(NUMBER_TYPE number)156   static Length Percent(NUMBER_TYPE number) {
157     return Length(number, kPercent);
158   }
159 
160   // FIXME: Make this private (if possible) or at least rename it
161   // (http://crbug.com/432707).
Value()162   inline float Value() const {
163     DCHECK(!IsCalculated());
164     return GetFloatValue();
165   }
166 
IntValue()167   int IntValue() const {
168     if (IsCalculated()) {
169       NOTREACHED();
170       return 0;
171     }
172     return GetIntValue();
173   }
174 
Pixels()175   float Pixels() const {
176     DCHECK_EQ(GetType(), kFixed);
177     return GetFloatValue();
178   }
179 
Percent()180   float Percent() const {
181     DCHECK_EQ(GetType(), kPercent);
182     return GetFloatValue();
183   }
184 
185   PixelsAndPercent GetPixelsAndPercent() const;
186 
187   CalculationValue& GetCalculationValue() const;
188 
189   // If |this| is calculated, returns the underlying |CalculationValue|. If not,
190   // returns a |CalculationValue| constructed from |GetPixelsAndPercent()|. Hits
191   // a DCHECK if |this| is not a specified value (e.g., 'auto').
192   scoped_refptr<CalculationValue> AsCalculationValue() const;
193 
GetType()194   Length::Type GetType() const { return static_cast<Length::Type>(type_); }
Quirk()195   bool Quirk() const { return quirk_; }
196 
SetQuirk(bool quirk)197   void SetQuirk(bool quirk) { quirk_ = quirk; }
198 
IsNone()199   bool IsNone() const { return GetType() == kNone; }
200 
201   // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated
202   // Length always contains a percentage, and without a maxValue passed to these
203   // functions it's impossible to determine the sign or zero-ness. We assume all
204   // calc values are positive and non-zero for now.
IsZero()205   bool IsZero() const {
206     DCHECK(!IsNone());
207     if (IsCalculated())
208       return false;
209 
210     return is_float_ ? !float_value_ : !int_value_;
211   }
IsPositive()212   bool IsPositive() const {
213     if (IsNone())
214       return false;
215     if (IsCalculated())
216       return true;
217 
218     return GetFloatValue() > 0;
219   }
IsNegative()220   bool IsNegative() const {
221     if (IsNone() || IsCalculated())
222       return false;
223 
224     return GetFloatValue() < 0;
225   }
226 
227   // For the layout purposes, if this |Length| is a block-axis size, see
228   // |IsAutoOrContentOrIntrinsic()|, it is usually a better choice.
IsAuto()229   bool IsAuto() const { return GetType() == kAuto; }
IsFixed()230   bool IsFixed() const { return GetType() == kFixed; }
231 
232   // For the block axis, intrinsic sizes such as `min-content` behave the same
233   // as `auto`. https://www.w3.org/TR/css-sizing-3/#valdef-width-min-content
IsContentOrIntrinsic()234   bool IsContentOrIntrinsic() const {
235     return GetType() == kMinContent || GetType() == kMaxContent ||
236            GetType() == kFitContent || GetType() == kMinIntrinsic;
237   }
IsAutoOrContentOrIntrinsic()238   bool IsAutoOrContentOrIntrinsic() const {
239     return GetType() == kAuto || IsContentOrIntrinsic();
240   }
241 
242   // NOTE: This shouldn't be use in NG code.
IsContentOrIntrinsicOrFillAvailable()243   bool IsContentOrIntrinsicOrFillAvailable() const {
244     return IsContentOrIntrinsic() || GetType() == kFillAvailable;
245   }
246 
IsSpecified()247   bool IsSpecified() const {
248     return GetType() == kFixed || GetType() == kPercent ||
249            GetType() == kCalculated;
250   }
251 
IsCalculated()252   bool IsCalculated() const { return GetType() == kCalculated; }
253   bool IsCalculatedEqual(const Length&) const;
IsMinContent()254   bool IsMinContent() const { return GetType() == kMinContent; }
IsMaxContent()255   bool IsMaxContent() const { return GetType() == kMaxContent; }
IsMinIntrinsic()256   bool IsMinIntrinsic() const { return GetType() == kMinIntrinsic; }
IsFillAvailable()257   bool IsFillAvailable() const { return GetType() == kFillAvailable; }
IsFitContent()258   bool IsFitContent() const { return GetType() == kFitContent; }
IsPercent()259   bool IsPercent() const { return GetType() == kPercent; }
IsPercentOrCalc()260   bool IsPercentOrCalc() const {
261     return GetType() == kPercent || GetType() == kCalculated;
262   }
IsExtendToZoom()263   bool IsExtendToZoom() const { return GetType() == kExtendToZoom; }
IsDeviceWidth()264   bool IsDeviceWidth() const { return GetType() == kDeviceWidth; }
IsDeviceHeight()265   bool IsDeviceHeight() const { return GetType() == kDeviceHeight; }
266 
Blend(const Length & from,double progress,ValueRange range)267   Length Blend(const Length& from, double progress, ValueRange range) const {
268     DCHECK(IsSpecified());
269     DCHECK(from.IsSpecified());
270 
271     if (progress == 0.0)
272       return from;
273 
274     if (progress == 1.0)
275       return *this;
276 
277     if (from.GetType() == kCalculated || GetType() == kCalculated)
278       return BlendMixedTypes(from, progress, range);
279 
280     if (!from.IsZero() && !IsZero() && from.GetType() != GetType())
281       return BlendMixedTypes(from, progress, range);
282 
283     if (from.IsZero() && IsZero())
284       return *this;
285 
286     return BlendSameTypes(from, progress, range);
287   }
288 
GetFloatValue()289   float GetFloatValue() const {
290     DCHECK(!IsNone());
291     return is_float_ ? float_value_ : int_value_;
292   }
293   float NonNanCalculatedValue(LayoutUnit max_value) const;
294 
295   Length SubtractFromOneHundredPercent() const;
296 
297   Length Zoom(double factor) const;
298 
299  private:
GetIntValue()300   int GetIntValue() const {
301     DCHECK(!IsNone());
302     return is_float_ ? static_cast<int>(float_value_) : int_value_;
303   }
304 
305   Length BlendMixedTypes(const Length& from, double progress, ValueRange) const;
306 
307   Length BlendSameTypes(const Length& from, double progress, ValueRange) const;
308 
CalculationHandle()309   int CalculationHandle() const {
310     DCHECK(IsCalculated());
311     return GetIntValue();
312   }
313   void IncrementCalculatedRef() const;
314   void DecrementCalculatedRef() const;
315 
316   union {
317     int int_value_;
318     float float_value_;
319   };
320   bool quirk_;
321   unsigned char type_;
322   bool is_float_;
323 };
324 
325 }  // namespace blink
326 
327 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LENGTH_H_
328