1/* 2 * Copyright (c) 2020-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 "draw/draw_label.h" 17#include "common/typed_text.h" 18#include "draw/draw_utils.h" 19#include "engines/gfx/gfx_engine_manager.h" 20#include "font/ui_font.h" 21#include "font/ui_font_header.h" 22#include "gfx_utils/graphic_log.h" 23 24namespace OHOS { 25uint16_t DrawLabel::DrawTextOneLine(BufferInfo& gfxDstBuffer, const LabelLineInfo& labelLine, uint16_t& letterIndex) 26{ 27 if (labelLine.text == nullptr) { 28 return 0; 29 } 30 UIFont* fontEngine = UIFont::GetInstance(); 31 if (labelLine.direct == TEXT_DIRECT_RTL) { 32 labelLine.pos.x -= labelLine.offset.x; 33 } else { 34 labelLine.pos.x += labelLine.offset.x; 35 } 36 37 uint32_t i = 0; 38 uint16_t retOffsetY = 0; // ret value elipse offsetY 39 uint16_t offsetPosY = 0; 40 uint8_t maxLetterSize = GetLineMaxLetterSize(labelLine.text, labelLine.lineLength, labelLine.fontId, 41 labelLine.fontSize, letterIndex, labelLine.spannableString); 42 GlyphNode glyphNode; 43 while (i < labelLine.lineLength) { 44 uint32_t letter = TypedText::GetUTF8Next(labelLine.text, i, i); 45 uint16_t fontId = labelLine.fontId; 46 uint8_t fontSize = labelLine.fontSize; 47#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE 48 TextStyle textStyle = TEXT_STYLE_NORMAL; 49 if (labelLine.textStyles) { 50 textStyle = labelLine.textStyles[letterIndex]; 51 } 52#endif 53 bool haveLineBackgroundColor = false; 54 ColorType lineBackgroundColor; 55 bool havebackgroundColor = false; 56 ColorType backgroundColor; 57 ColorType foregroundColor = labelLine.style.textColor_; 58 59 if (labelLine.spannableString != nullptr && labelLine.spannableString->GetSpannable(letterIndex)) { 60 labelLine.spannableString->GetFontId(letterIndex, fontId); 61 labelLine.spannableString->GetFontSize(letterIndex, fontSize); 62 havebackgroundColor = labelLine.spannableString->GetBackgroundColor(letterIndex, backgroundColor); 63 labelLine.spannableString->GetForegroundColor(letterIndex, foregroundColor); 64#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE 65 labelLine.spannableString->GetTextStyle(letterIndex, textStyle); 66#endif 67 haveLineBackgroundColor = 68 labelLine.spannableString->GetLineBackgroundColor(letterIndex, lineBackgroundColor); 69 } 70 LabelLetterInfo letterInfo{labelLine.pos, 71 labelLine.mask, 72 foregroundColor, 73 labelLine.opaScale, 74 0, 75 0, 76 letter, 77 labelLine.direct, 78 fontId, 79 0, 80 fontSize, 81#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE 82 textStyle, 83#endif 84 labelLine.baseLine, 85 labelLine.style.letterSpace_, 86 labelLine.style.lineSpace_, 87 havebackgroundColor, 88 backgroundColor, 89 haveLineBackgroundColor, 90 lineBackgroundColor 91 }; 92#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE 93 glyphNode.textStyle = letterInfo.textStyle; 94#endif 95 glyphNode.advance = 0; 96 uint8_t* fontMap = fontEngine->GetBitmap(letterInfo.letter, glyphNode, letterInfo.fontId, letterInfo.fontSize, 97 letterInfo.shapingId); 98 if (fontMap != nullptr) { 99 uint8_t weight = fontEngine->GetFontWeight(glyphNode.fontId); 100 // 16: rgb565->16 rgba8888->32 font with rgba 101 if (weight >= 16) { 102 DrawUtils::GetInstance()->DrawColorLetter(gfxDstBuffer, letterInfo, fontMap, 103 glyphNode, labelLine.lineHeight); 104 } else { 105 letterInfo.offsetY = labelLine.ellipsisOssetY == 0 ? offsetPosY : labelLine.ellipsisOssetY; 106 retOffsetY = offsetPosY; 107 DrawUtils::GetInstance()->DrawNormalLetter(gfxDstBuffer, letterInfo, fontMap, glyphNode, maxLetterSize); 108 } 109 } 110 if (labelLine.direct == TEXT_DIRECT_RTL) { 111 labelLine.pos.x -= (glyphNode.advance + labelLine.style.letterSpace_); 112 } else { 113 labelLine.pos.x += (glyphNode.advance + labelLine.style.letterSpace_); 114 } 115 letterIndex++; 116 } 117 return retOffsetY; 118} 119 120uint8_t DrawLabel::GetLineMaxLetterSize(const char* text, uint16_t lineLength, uint16_t fontId, uint8_t fontSize, 121 uint16_t letterIndex, SpannableString* spannableString) 122{ 123 if (spannableString == nullptr) { 124 return fontSize; 125 } 126 uint32_t i = 0; 127 uint8_t maxLetterSize = fontSize; 128 while (i < lineLength) { 129 uint32_t unicode = TypedText::GetUTF8Next(text, i, i); 130 if (TypedText::IsColourWord(unicode, fontId, fontSize)) { 131 letterIndex++; 132 continue; 133 } 134 if (spannableString != nullptr && spannableString->GetSpannable(letterIndex)) { 135 uint8_t tempSize = fontSize; 136 spannableString->GetFontSize(letterIndex, tempSize); 137 if (tempSize > maxLetterSize) { 138 maxLetterSize = tempSize; 139 } 140 } 141 letterIndex++; 142 } 143 return maxLetterSize; 144} 145 146void DrawLabel::DrawArcText(BufferInfo& gfxDstBuffer, 147 const Rect& mask, 148 const char* text, 149 const Point& arcCenter, 150 uint16_t fontId, 151 uint8_t fontSize, 152 const ArcTextInfo arcTextInfo, 153 const float changeAngle, 154 TextOrientation orientation, 155 const Style& style, 156 OpacityType opaScale, 157 bool compatibilityMode) 158{ 159 if ((text == nullptr) || (arcTextInfo.lineStart == arcTextInfo.lineEnd) || (arcTextInfo.radius == 0)) { 160 GRAPHIC_LOGE("DrawLabel::DrawArcText invalid parameter\n"); 161 return; 162 } 163 OpacityType opa = DrawUtils::GetMixOpacity(opaScale, style.textOpa_); 164 if (opa == OPA_TRANSPARENT) { 165 return; 166 } 167 uint16_t letterWidth; 168 UIFont* fontEngine = UIFont::GetInstance(); 169 170 uint16_t letterHeight = fontEngine->GetHeight(fontId, fontSize); 171 uint32_t i = arcTextInfo.lineStart; 172 bool orientationFlag = (orientation == TextOrientation::INSIDE); 173 bool directFlag = (arcTextInfo.direct == TEXT_DIRECT_LTR); 174 bool xorFlag = !directFlag; 175 if (compatibilityMode) { 176 xorFlag = !((orientationFlag && directFlag) || (!orientationFlag && !directFlag)); 177 } 178 float angle = directFlag ? (arcTextInfo.startAngle + changeAngle) : (arcTextInfo.startAngle - changeAngle); 179 180 float posX; 181 float posY; 182 float rotateAngle; 183 while (i < arcTextInfo.lineEnd) { 184 uint32_t tmp = i; 185 uint32_t letter = TypedText::GetUTF8Next(text, tmp, i); 186 if (letter == 0) { 187 continue; 188 } 189 if ((letter == '\r') || (letter == '\n')) { 190 break; 191 } 192 letterWidth = fontEngine->GetWidth(letter, fontId, fontSize, 0); 193 if (!DrawLabel::CalculateAngle(letterWidth, letterHeight, style.letterSpace_, 194 arcTextInfo, xorFlag, tmp, orientation, 195 posX, posY, rotateAngle, angle, 196 arcCenter, compatibilityMode)) { 197 continue; 198 } 199 200 ArcLetterInfo letterInfo; 201 letterInfo.InitData(fontId, fontSize, letter, { MATH_ROUND(posX), MATH_ROUND(posY) }, 202 static_cast<int16_t>(rotateAngle), style.textColor_, opaScale, arcTextInfo.startAngle, 203 arcTextInfo.endAngle, angle, arcTextInfo.radius, compatibilityMode, 204 directFlag, orientationFlag, arcTextInfo.hasAnimator); 205 206 DrawLetterWithRotate(gfxDstBuffer, mask, letterInfo, posX, posY); 207 } 208} 209 210bool DrawLabel::CalculateAngle(uint16_t letterWidth, 211 uint16_t letterHeight, 212 int16_t letterSpace, 213 const ArcTextInfo arcTextInfo, 214 bool xorFlag, 215 uint32_t index, 216 TextOrientation orientation, 217 float& posX, 218 float& posY, 219 float& rotateAngle, 220 float& angle, 221 const Point& arcCenter, 222 bool compatibilityMode) 223{ 224 const int DIVIDER_BY_TWO = 2; 225 if (compatibilityMode) { 226 if ((index == arcTextInfo.lineStart) && xorFlag) { 227 angle += TypedText::GetAngleForArcLen(static_cast<float>(letterWidth), letterHeight, arcTextInfo.radius, 228 arcTextInfo.direct, orientation); 229 } 230 uint16_t arcLen = letterWidth + letterSpace; 231 if (arcLen == 0) { 232 return false; 233 } 234 float incrementAngle = TypedText::GetAngleForArcLen(static_cast<float>(arcLen), letterHeight, 235 arcTextInfo.radius, arcTextInfo.direct, orientation); 236 237 rotateAngle = (orientation == TextOrientation::INSIDE) ? angle : (angle - SEMICIRCLE_IN_DEGREE); 238 239 // 2: half 240 float fineTuningAngle = incrementAngle * (static_cast<float>(letterWidth) / (DIVIDER_BY_TWO * arcLen)); 241 rotateAngle += (xorFlag ? -fineTuningAngle : fineTuningAngle); 242 TypedText::GetArcLetterPos(arcCenter, arcTextInfo.radius, angle, posX, posY); 243 angle += incrementAngle; 244 } else { 245 float incrementAngle = TypedText::GetAngleForArcLen(letterWidth, letterSpace, arcTextInfo.radius); 246 if (incrementAngle >= -EPSINON && incrementAngle <= EPSINON) { 247 return false; 248 } 249 250 float fineTuningAngle = incrementAngle / DIVIDER_BY_TWO; 251 rotateAngle = xorFlag ? (angle - SEMICIRCLE_IN_DEGREE - fineTuningAngle) : (angle + fineTuningAngle); 252 TypedText::GetArcLetterPos(arcCenter, arcTextInfo.radius, angle, posX, posY); 253 angle = xorFlag ? (angle - incrementAngle) : (angle + incrementAngle); 254 } 255 256 return true; 257} 258 259void DrawLabel::DrawLetterWithRotate(BufferInfo& gfxDstBuffer, 260 const Rect& mask, 261 const ArcLetterInfo& letterInfo, 262 float posX, 263 float posY) 264{ 265 UIFont* fontEngine = UIFont::GetInstance(); 266 FontHeader head; 267 GlyphNode node; 268#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE 269 node.textStyle = TEXT_STYLE_NORMAL; 270#endif 271 if (fontEngine->GetFontHeader(head, letterInfo.fontId, letterInfo.fontSize) != 0) { 272 return; 273 } 274 275 const uint8_t* fontMap = fontEngine->GetBitmap(letterInfo.letter, node, 276 letterInfo.fontId, letterInfo.fontSize, 0); 277 if (fontMap == nullptr) { 278 return; 279 } 280 uint8_t fontWeight = fontEngine->GetFontWeight(letterInfo.fontId); 281 ColorMode colorMode = fontEngine->GetColorType(letterInfo.fontId); 282 283 int16_t offset = letterInfo.compatibilityMode ? head.ascender : 0; 284 Rect rectLetter; 285 rectLetter.Resize(node.cols, node.rows); 286 TransformMap transMap(rectLetter); 287 // Avoiding errors caused by rounding calculations 288 transMap.Translate(Vector2<float>(posX + node.left, posY + offset - node.top)); 289 transMap.Rotate(letterInfo.rotateAngle, Vector2<float>(posX, posY)); 290 291 TransformDataInfo letterTranDataInfo = {ImageHeader{colorMode, 0, 0, 0, node.cols, node.rows}, fontMap, fontWeight, 292 BlurLevel::LEVEL0, TransformAlgorithm::BILINEAR}; 293 294 uint8_t* buffer = nullptr; 295 if (letterInfo.hasAnimator) { 296 bool inRange = DrawLabel::CalculatedTransformDataInfo(&buffer, letterTranDataInfo, letterInfo); 297 if (inRange == false) { 298 if (buffer != nullptr) { 299 UIFree(buffer); 300 buffer = nullptr; 301 } 302 return; 303 } 304 } 305 306 BaseGfxEngine::GetInstance()->DrawTransform(gfxDstBuffer, mask, Point { 0, 0 }, letterInfo.color, 307 letterInfo.opaScale, transMap, letterTranDataInfo); 308 if (buffer != nullptr) { 309 UIFree(buffer); 310 buffer = nullptr; 311 } 312} 313 314bool DrawLabel::CalculatedClipAngle(const ArcLetterInfo& letterInfo, float& angle) 315{ 316 if (letterInfo.directFlag) { 317 if ((letterInfo.compatibilityMode && letterInfo.orientationFlag) || !letterInfo.compatibilityMode) { 318 if (letterInfo.currentAngle > letterInfo.endAngle) { 319 angle = letterInfo.currentAngle - letterInfo.endAngle; 320 } else if (letterInfo.currentAngle > letterInfo.startAngle) { 321 angle = letterInfo.currentAngle - letterInfo.startAngle; 322 } else { 323 return false; 324 } 325 } else { 326 if (letterInfo.currentAngle > letterInfo.endAngle) { 327 angle = letterInfo.currentAngle - letterInfo.endAngle; 328 } else if (letterInfo.currentAngle > letterInfo.startAngle) { 329 angle = letterInfo.currentAngle - letterInfo.startAngle; 330 } else { 331 return false; 332 } 333 } 334 } else { 335 if (letterInfo.compatibilityMode && letterInfo.orientationFlag) { 336 if (letterInfo.currentAngle < letterInfo.endAngle) { 337 angle = letterInfo.endAngle - letterInfo.currentAngle; 338 } else if (letterInfo.currentAngle < letterInfo.startAngle) { 339 angle = letterInfo.startAngle - letterInfo.currentAngle; 340 } else { 341 return false; 342 } 343 } else if ((letterInfo.compatibilityMode && !letterInfo.orientationFlag) || !letterInfo.compatibilityMode) { 344 if (letterInfo.currentAngle < letterInfo.endAngle) { 345 angle = letterInfo.endAngle - letterInfo.currentAngle; 346 } else if (letterInfo.currentAngle < letterInfo.startAngle) { 347 angle = letterInfo.startAngle - letterInfo.currentAngle; 348 } else { 349 return false; 350 } 351 } 352 } 353 354 return true; 355} 356 357void DrawLabel::OnCalculatedClockwise(const ArcLetterInfo& letterInfo, const uint16_t sizePerPx, 358 const uint16_t cols, const int16_t offsetX, uint16_t& begin, 359 uint16_t& copyCols, TextInRange& range) 360{ 361 if (!letterInfo.directFlag) { 362 return; 363 } 364 if ((letterInfo.compatibilityMode && letterInfo.orientationFlag) || !letterInfo.compatibilityMode) { 365 if (letterInfo.currentAngle > letterInfo.endAngle) { 366 if (offsetX >= cols) { 367 range = TextInRange::OUT_RANGE; 368 } 369 copyCols = cols - offsetX; 370 } else if (letterInfo.currentAngle > letterInfo.startAngle) { 371 if (offsetX >= cols) { 372 range = TextInRange::IN_RANGE; 373 } 374 copyCols = offsetX; 375 begin = (cols - offsetX) * sizePerPx; 376 } 377 } else { 378 if (letterInfo.currentAngle > letterInfo.endAngle) { 379 if (offsetX >= cols) { 380 range = TextInRange::OUT_RANGE; 381 } 382 copyCols = cols - offsetX; 383 begin = offsetX * sizePerPx; 384 } else if (letterInfo.currentAngle > letterInfo.startAngle) { 385 if (offsetX >= cols) { 386 range = TextInRange::IN_RANGE; 387 } 388 copyCols = offsetX; 389 } 390 } 391} 392 393void DrawLabel::OnCalculatedAnticlockwise(const ArcLetterInfo& letterInfo, const uint16_t sizePerPx, 394 const uint16_t cols, const int16_t offsetX, uint16_t& begin, 395 uint16_t& copyCols, TextInRange& range) 396{ 397 if (letterInfo.directFlag) { 398 return; 399 } 400 if (letterInfo.compatibilityMode && letterInfo.orientationFlag) { 401 if (letterInfo.currentAngle < letterInfo.endAngle) { 402 if (offsetX >= cols) { 403 range = TextInRange::OUT_RANGE; 404 } 405 copyCols = cols - offsetX; 406 begin = offsetX * sizePerPx; 407 } else if (letterInfo.currentAngle < letterInfo.startAngle) { 408 if (offsetX >= cols) { 409 range = TextInRange::IN_RANGE; 410 } 411 copyCols = offsetX; 412 } 413 } else if ((letterInfo.compatibilityMode && !letterInfo.orientationFlag) || !letterInfo.compatibilityMode) { 414 if (letterInfo.currentAngle < letterInfo.endAngle) { 415 if (offsetX >= cols) { 416 range = TextInRange::OUT_RANGE; 417 } 418 copyCols = cols - offsetX; 419 } else if (letterInfo.currentAngle < letterInfo.startAngle) { 420 if (offsetX >= cols) { 421 range = TextInRange::IN_RANGE; 422 } 423 copyCols = offsetX; 424 begin = (cols - offsetX) * sizePerPx; 425 } 426 } 427} 428 429void DrawLabel::CalculatedBeginAndCopySize(const ArcLetterInfo& letterInfo, const uint16_t sizePerPx, 430 const uint16_t cols, const int16_t offsetX, uint16_t& begin, 431 uint16_t& copyCols, TextInRange& range) 432{ 433 if (letterInfo.directFlag) { 434 OnCalculatedClockwise(letterInfo, sizePerPx, cols, offsetX, begin, copyCols, range); 435 } else { 436 OnCalculatedAnticlockwise(letterInfo, sizePerPx, cols, offsetX, begin, copyCols, range); 437 } 438} 439 440bool DrawLabel::CalculatedTransformDataInfo(uint8_t** buffer, TransformDataInfo& letterTranDataInfo, 441 const ArcLetterInfo& letterInfo) 442{ 443 float angle = 0.0f; 444 if (DrawLabel::CalculatedClipAngle(letterInfo, angle) == false) { 445 return false; 446 } 447 if (angle >= -EPSINON && angle <= EPSINON) { 448 return true; 449 } 450 451 int16_t offsetX = static_cast<uint16_t>(angle * letterInfo.radius * UI_PI / SEMICIRCLE_IN_DEGREE); 452 uint16_t copyCols = 0; 453 uint16_t begin = 0; 454 uint16_t sizePerPx = letterTranDataInfo.pxSize / 8; // 8 bit 455 TextInRange range = TextInRange::NEED_CLIP; 456 uint16_t cols = letterTranDataInfo.header.width; 457 uint16_t rows = letterTranDataInfo.header.height; 458 DrawLabel::CalculatedBeginAndCopySize(letterInfo, sizePerPx, cols, offsetX, begin, copyCols, range); 459 if (range == TextInRange::IN_RANGE) { 460 return true; 461 } else if (range == TextInRange::OUT_RANGE) { 462 return false; 463 } 464 465 const uint8_t* fontMap = letterTranDataInfo.data; 466 467 uint32_t size = cols * rows * sizePerPx; 468 *buffer = static_cast<uint8_t*>(UIMalloc(size)); 469 if (*buffer == nullptr) { 470 return false; 471 } 472 473 if (memset_s(*buffer, size, 0, size) != EOK) { 474 UIFree(*buffer); 475 return false; 476 } 477 478 for (uint16_t i = 0; i < rows; i++) { 479 uint16_t beginSize = i * cols * sizePerPx + begin; 480 uint16_t copySize = copyCols * sizePerPx; 481 if (memcpy_s(*buffer + beginSize, copySize, fontMap + beginSize, copySize) != EOK) { 482 UIFree(*buffer); 483 return false; 484 } 485 } 486 letterTranDataInfo.data = *buffer; 487 return true; 488} 489 490void DrawLabel::GetLineBackgroundColor(uint16_t letterIndex, List<LineBackgroundColor>* linebackgroundColor, 491 bool& havelinebackground, ColorType& linebgColor) 492{ 493 if (linebackgroundColor->Size() > 0) { 494 ListNode<LineBackgroundColor>* lbColor = linebackgroundColor->Begin(); 495 for (; lbColor != linebackgroundColor->End(); lbColor = lbColor->next_) { 496 uint32_t start = lbColor->data_.start; 497 uint32_t end = lbColor->data_.end; 498 if (letterIndex >= start && letterIndex <= end) { 499 havelinebackground = true; 500 linebgColor = lbColor->data_.linebackgroundColor ; 501 } 502 } 503 } 504}; 505 506void DrawLabel::GetBackgroundColor(uint16_t letterIndex, List<BackgroundColor>* backgroundColor, 507 bool& havebackground, ColorType& bgColor) 508{ 509 if (backgroundColor->Size() > 0) { 510 ListNode<BackgroundColor>* bColor = backgroundColor->Begin(); 511 for (; bColor != backgroundColor->End(); bColor = bColor->next_) { 512 uint16_t start = bColor->data_.start; 513 uint16_t end = bColor->data_.end; 514 if (letterIndex >= start && letterIndex <= end) { 515 havebackground = true; 516 bgColor = bColor->data_.backgroundColor ; 517 } 518 } 519 } 520}; 521 522void DrawLabel::GetForegroundColor(uint16_t letterIndex, List<ForegroundColor>* foregroundColor, ColorType& fgColor) 523{ 524 if (foregroundColor->Size() > 0) { 525 ListNode<ForegroundColor>* fColor = foregroundColor->Begin(); 526 for (; fColor != foregroundColor->End(); fColor = fColor->next_) { 527 uint32_t start = fColor->data_.start; 528 uint32_t end = fColor->data_.end; 529 if (letterIndex >= start && letterIndex <= end) { 530 fgColor = fColor->data_.fontColor; 531 } 532 } 533 } 534}; 535 536void DrawLabel::DrawLineBackgroundColor(BufferInfo& gfxDstBuffer, uint16_t letterIndex, const LabelLineInfo& labelLine) 537{ 538 uint32_t i = 0; 539 while (i < labelLine.lineLength) { 540 TypedText::GetUTF8Next(labelLine.text, i, i); 541 bool havelinebackground = false; 542 ColorType linebackgroundColor; 543 if (labelLine.spannableString != nullptr && 544 labelLine.spannableString->GetSpannable(letterIndex)) { 545 havelinebackground = 546 labelLine.spannableString->GetLineBackgroundColor( 547 letterIndex, linebackgroundColor); 548 } 549 if (havelinebackground) { 550 Style style; 551 style.bgColor_ = linebackgroundColor; 552 Rect linebackground(labelLine.mask.GetLeft(), labelLine.pos.y, 553 labelLine.mask.GetRight(), 554 labelLine.pos.y + labelLine.lineHeight); 555 BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, labelLine.mask, 556 linebackground, style, 557 linebackgroundColor.alpha); 558 } 559 letterIndex++; 560 } 561}; 562} // namespace OHOS 563