1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "base/geometry/dimension.h" 17 18#include "core/pipeline/pipeline_base.h" 19 20namespace OHOS::Ace { 21 22namespace { 23struct CalcDimensionParam { 24 float value = 0.0f; 25 float vpScale = 0.0f; 26 float fpScale = 0.0f; 27 float lpxScale = 0.0f; 28 float parentLength = 0.0f; 29}; 30 31using CalcDimensionFunc = std::function<bool(const CalcDimensionParam& param, double& result)>; 32bool CalcDimensionNone(const CalcDimensionParam& param, double& result) 33{ 34 result = param.value; 35 return true; 36} 37 38bool CalcDimensionPx(const CalcDimensionParam& param, double& result) 39{ 40 result = param.value; 41 return true; 42} 43 44bool CalcDimensionPercent(const CalcDimensionParam& param, double& result) 45{ 46 if (NonNegative(param.parentLength)) { 47 result = param.value * param.parentLength; 48 return true; 49 } 50 return false; 51} 52 53bool CalcDimensionVp(const CalcDimensionParam& param, double& result) 54{ 55 if (Positive(param.vpScale)) { 56 result = param.value * param.vpScale; 57 return true; 58 } 59 return false; 60} 61 62bool CalcDimensionFp(const CalcDimensionParam& param, double& result) 63{ 64 if (Positive(param.fpScale) && Positive(param.vpScale)) { 65 result = param.value * param.fpScale * param.vpScale; 66 return true; 67 } 68 return false; 69} 70 71bool CalcDimensionLpx(const CalcDimensionParam& param, double& result) 72{ 73 if (Positive(param.lpxScale)) { 74 result = param.value * param.lpxScale; 75 return true; 76 } 77 return false; 78} 79 80std::unordered_map<DimensionUnit, CalcDimensionFunc> calcDimensionFuncMap_ = { 81 { DimensionUnit::NONE, &CalcDimensionNone }, { DimensionUnit::PX, &CalcDimensionPx }, 82 { DimensionUnit::PERCENT, &CalcDimensionPercent }, { DimensionUnit::VP, &CalcDimensionVp }, 83 { DimensionUnit::FP, &CalcDimensionFp }, { DimensionUnit::LPX, &CalcDimensionLpx } 84}; 85} // namespace 86 87double Dimension::ConvertToVp() const 88{ 89 if (unit_ == DimensionUnit::VP) { 90 return value_; 91 } 92 93 auto pipeline = PipelineBase::GetCurrentContextSafely(); 94 CHECK_NULL_RETURN(pipeline, 0.0); 95 if (unit_ == DimensionUnit::NONE) { 96 return value_ / pipeline->GetDipScale(); 97 } 98 if (unit_ == DimensionUnit::PX) { 99 return value_ / pipeline->GetDipScale(); 100 } 101 if (unit_ == DimensionUnit::FP) { 102 return ConvertToVpByAppFontScale(); 103 } 104 if (unit_ == DimensionUnit::LPX) { 105 return value_ * pipeline->GetLogicScale() / pipeline->GetDipScale(); 106 } 107 return 0.0; 108} 109 110double Dimension::ConvertToPx() const 111{ 112 if (unit_ == DimensionUnit::NONE) { 113 return value_; 114 } 115 if (unit_ == DimensionUnit::PX) { 116 return value_; 117 } 118 119 auto pipeline = PipelineBase::GetCurrentContextSafely(); 120 CHECK_NULL_RETURN(pipeline, 0.0); 121 if (unit_ == DimensionUnit::VP) { 122 return value_ * pipeline->GetDipScale(); 123 } 124 if (unit_ == DimensionUnit::FP) { 125 return ConvertToPxByAppFontScale(0.0f); 126 } 127 if (unit_ == DimensionUnit::LPX) { 128 return value_ * pipeline->GetLogicScale(); 129 } 130 return 0.0; 131} 132 133double Dimension::ConvertToFp() const 134{ 135 if (unit_ == DimensionUnit::FP) { 136 return value_; 137 } 138 auto pipeline = PipelineBase::GetCurrentContextSafely(); 139 CHECK_NULL_RETURN(pipeline, 0.0); 140 auto fontScale = std::clamp(pipeline->GetFontScale(), 0.0f, pipeline->GetMaxAppFontScale()); 141 if (LessOrEqual(fontScale, 0.0)) { 142 return 0.0; 143 } 144 if (unit_ == DimensionUnit::NONE) { 145 return value_ / pipeline->GetDipScale() / fontScale; 146 } 147 if (unit_ == DimensionUnit::PX) { 148 return value_ / pipeline->GetDipScale() / fontScale; 149 } 150 if (unit_ == DimensionUnit::VP) { 151 return value_ / fontScale; 152 } 153 if (unit_ == DimensionUnit::LPX) { 154 return value_ * pipeline->GetLogicScale() / pipeline->GetDipScale() / fontScale; 155 } 156 return 0.0; 157} 158 159double Dimension::ConvertToPxWithSize(double size) const 160{ 161 if (unit_ == DimensionUnit::PERCENT) { 162 return value_ * size; 163 } 164 return ConvertToPx(); 165} 166 167DimensionUnit Dimension::GetAdaptDimensionUnit(const Dimension& dimension) 168{ 169 return static_cast<int32_t>(unit_) <= static_cast<int32_t>(dimension.unit_) ? unit_ : dimension.unit_; 170} 171 172double Dimension::ConvertToPxDistribute( 173 std::optional<float> minOptional, std::optional<float> maxOptional, bool allowScale) const 174{ 175 if (unit_ != DimensionUnit::FP) { 176 return ConvertToPx(); 177 } 178 auto pipeline = PipelineBase::GetCurrentContextSafely(); 179 CHECK_NULL_RETURN(pipeline, value_); 180 if (!allowScale) { 181 return value_ * pipeline->GetDipScale(); 182 } 183 auto minFontScale = minOptional.value_or(0.0f); 184 auto maxFontScale = maxOptional.value_or(static_cast<float>(INT32_MAX)); 185 if (!maxOptional.has_value()) { 186 return ConvertToPxByAppFontScale(minFontScale); 187 } 188 return ConvertToPxByCustomFontScale(minFontScale, maxFontScale); 189} 190 191double Dimension::ConvertToPxByCustomFontScale(float minFontScale, float maxFontScale) const 192{ 193 auto pipeline = PipelineBase::GetCurrentContextSafely(); 194 CHECK_NULL_RETURN(pipeline, value_); 195 float fontScale = std::clamp(pipeline->GetFontScale(), minFontScale, maxFontScale); 196 return value_ * pipeline->GetDipScale() * fontScale; 197} 198 199double Dimension::ConvertToPxByAppFontScale(float minFontScale) const 200{ 201 auto pipeline = PipelineBase::GetCurrentContextSafely(); 202 CHECK_NULL_RETURN(pipeline, value_); 203 float maxFontScale = pipeline->GetMaxAppFontScale(); 204 float fontScale = std::clamp(pipeline->GetFontScale(), minFontScale, maxFontScale); 205 return value_ * pipeline->GetDipScale() * fontScale; 206} 207 208double Dimension::ConvertToVpByAppFontScale() const 209{ 210 auto pipeline = PipelineBase::GetCurrentContextSafely(); 211 CHECK_NULL_RETURN(pipeline, value_); 212 CHECK_NULL_RETURN(pipeline->IsFollowSystem(), value_); 213 float maxFontScale = pipeline->GetMaxAppFontScale(); 214 float fontScale = std::clamp(pipeline->GetFontScale(), 0.0f, maxFontScale); 215 return value_ * fontScale; 216} 217 218std::string Dimension::ToString() const 219{ 220 static const int32_t unitsNum = 6; 221 static const int32_t percentIndex = 3; 222 static const int32_t percentUnit = 100; 223 static std::array<std::string, unitsNum> units = { "px", "vp", "fp", "%", "lpx", "auto" }; 224 if (static_cast<int32_t>(unit_) >= unitsNum || 225 static_cast<int32_t>(unit_) < static_cast<int32_t>(DimensionUnit::INVALID)) { 226 return StringUtils::DoubleToString(value_).append("px"); 227 } 228 if (unit_ == DimensionUnit::NONE) { 229 return StringUtils::DoubleToString(value_).append("none"); 230 } 231 if (unit_ == DimensionUnit::INVALID) { 232 return StringUtils::DoubleToString(value_).append("invalid"); 233 } 234 if (units[static_cast<int>(unit_)] == units[percentIndex]) { 235 return StringUtils::DoubleToString(value_ * percentUnit).append(units[static_cast<int>(unit_)]); 236 } 237 return StringUtils::DoubleToString(value_).append(units[static_cast<int>(unit_)]); 238} 239 240// for example str = 0.00px 241Dimension Dimension::FromString(const std::string& str) 242{ 243 static const int32_t percentUnit = 100; 244 static const std::unordered_map<std::string, DimensionUnit> uMap { 245 { "px", DimensionUnit::PX }, 246 { "vp", DimensionUnit::VP }, 247 { "fp", DimensionUnit::FP }, 248 { "%", DimensionUnit::PERCENT }, 249 { "lpx", DimensionUnit::LPX }, 250 { "auto", DimensionUnit::AUTO }, 251 }; 252 253 double value = 0.0; 254 DimensionUnit unit = DimensionUnit::FP; 255 256 if (str.empty()) { 257 LOGE("UITree |ERROR| empty string"); 258 return Dimension(value, unit); 259 } 260 261 for (int32_t i = static_cast<int32_t>(str.length() - 1); i >= 0; --i) { 262 if (str[i] >= '0' && str[i] <= '9') { 263 value = StringUtils::StringToDouble(str.substr(0, i + 1)); 264 auto subStr = str.substr(i + 1); 265 auto iter = uMap.find(subStr); 266 if (iter != uMap.end()) { 267 unit = iter->second; 268 } 269 value = unit == DimensionUnit::PERCENT ? value / percentUnit : value; 270 break; 271 } 272 } 273 return Dimension(value, unit); 274} 275 276bool Dimension::NormalizeToPx( 277 double vpScale, double fpScale, double lpxScale, double parentLength, double& result) const 278{ 279 auto func = calcDimensionFuncMap_.find(unit_); 280 if (func != calcDimensionFuncMap_.end()) { 281 CalcDimensionParam param = { value_, vpScale, fpScale, lpxScale, parentLength }; 282 return func->second(param, result); 283 } 284 return false; 285} 286} // namespace OHOS::Ace 287