paragraph_builder.cc 11.0 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "flutter/lib/ui/text/paragraph_builder.h"
6

7
#include "flutter/common/settings.h"
8
#include "flutter/common/task_runners.h"
G
Gary Qian 已提交
9
#include "flutter/fml/logging.h"
10
#include "flutter/fml/task_runner.h"
11
#include "flutter/lib/ui/text/font_collection.h"
12
#include "flutter/lib/ui/ui_dart_state.h"
13
#include "flutter/lib/ui/window/window.h"
14 15 16 17 18
#include "flutter/third_party/txt/src/txt/font_style.h"
#include "flutter/third_party/txt/src/txt/font_weight.h"
#include "flutter/third_party/txt/src/txt/paragraph_style.h"
#include "flutter/third_party/txt/src/txt/text_decoration.h"
#include "flutter/third_party/txt/src/txt/text_style.h"
J
Jason Simmons 已提交
19
#include "third_party/icu/source/common/unicode/ustring.h"
G
Gary Qian 已提交
20
#include "third_party/skia/include/core/SkColor.h"
21 22 23 24
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
G
Gary Qian 已提交
25
#include "third_party/tonic/typed_data/dart_byte_data.h"
26

27
namespace blink {
28 29
namespace {

A
Adam Barth 已提交
30 31
// TextStyle

32 33 34 35 36 37
const int tsColorIndex = 1;
const int tsTextDecorationIndex = 2;
const int tsTextDecorationColorIndex = 3;
const int tsTextDecorationStyleIndex = 4;
const int tsFontWeightIndex = 5;
const int tsFontStyleIndex = 6;
A
Adam Barth 已提交
38 39 40 41 42 43
const int tsTextBaselineIndex = 7;
const int tsFontFamilyIndex = 8;
const int tsFontSizeIndex = 9;
const int tsLetterSpacingIndex = 10;
const int tsWordSpacingIndex = 11;
const int tsHeightIndex = 12;
44
const int tsLocaleIndex = 13;
45
const int tsBackgroundIndex = 14;
46
const int tsForegroundIndex = 15;
G
Gary Qian 已提交
47
const int tsTextShadowsIndex = 16;
48 49 50 51 52 53 54

const int tsColorMask = 1 << tsColorIndex;
const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex;
const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex;
const int tsFontWeightMask = 1 << tsFontWeightIndex;
const int tsFontStyleMask = 1 << tsFontStyleIndex;
A
Adam Barth 已提交
55
const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
56 57 58 59
const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
const int tsFontSizeMask = 1 << tsFontSizeIndex;
const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
A
Adam Barth 已提交
60
const int tsHeightMask = 1 << tsHeightIndex;
61
const int tsLocaleMask = 1 << tsLocaleIndex;
62
const int tsBackgroundMask = 1 << tsBackgroundIndex;
63
const int tsForegroundMask = 1 << tsForegroundIndex;
G
Gary Qian 已提交
64
const int tsTextShadowsMask = 1 << tsTextShadowsIndex;
A
Adam Barth 已提交
65 66 67

// ParagraphStyle

68
const int psTextAlignIndex = 1;
69 70 71 72 73 74 75 76
const int psTextDirectionIndex = 2;
const int psFontWeightIndex = 3;
const int psFontStyleIndex = 4;
const int psMaxLinesIndex = 5;
const int psFontFamilyIndex = 6;
const int psFontSizeIndex = 7;
const int psLineHeightIndex = 8;
const int psEllipsisIndex = 9;
77
const int psLocaleIndex = 10;
A
Adam Barth 已提交
78

79
const int psTextAlignMask = 1 << psTextAlignIndex;
80
const int psTextDirectionMask = 1 << psTextDirectionIndex;
A
Adam Barth 已提交
81 82
const int psFontWeightMask = 1 << psFontWeightIndex;
const int psFontStyleMask = 1 << psFontStyleIndex;
83
const int psMaxLinesMask = 1 << psMaxLinesIndex;
A
Adam Barth 已提交
84 85
const int psFontFamilyMask = 1 << psFontFamilyIndex;
const int psFontSizeMask = 1 << psFontSizeIndex;
86
const int psLineHeightMask = 1 << psLineHeightIndex;
87
const int psEllipsisMask = 1 << psEllipsisIndex;
88
const int psLocaleMask = 1 << psLocaleIndex;
A
Adam Barth 已提交
89

G
Gary Qian 已提交
90 91 92 93 94 95 96 97 98 99
// TextShadows decoding

constexpr uint32_t kColorDefault = 0xFF000000;
constexpr uint32_t kBytesPerShadow = 16;
constexpr uint32_t kShadowPropertiesCount = 4;
constexpr uint32_t kColorOffset = 0;
constexpr uint32_t kXOffset = 1;
constexpr uint32_t kYOffset = 2;
constexpr uint32_t kBlurOffset = 3;

100
}  // namespace
101

A
Adam Barth 已提交
102 103 104 105
static void ParagraphBuilder_constructor(Dart_NativeArguments args) {
  DartCallConstructor(&ParagraphBuilder::create, args);
}

106
IMPLEMENT_WRAPPERTYPEINFO(ui, ParagraphBuilder);
A
Adam Barth 已提交
107

108
#define FOR_EACH_BINDING(V)      \
A
Adam Barth 已提交
109
  V(ParagraphBuilder, pushStyle) \
110 111
  V(ParagraphBuilder, pop)       \
  V(ParagraphBuilder, addText)   \
A
Adam Barth 已提交
112 113 114 115
  V(ParagraphBuilder, build)

FOR_EACH_BINDING(DART_NATIVE_CALLBACK)

116
void ParagraphBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) {
117
  natives->Register(
118
      {{"ParagraphBuilder_constructor", ParagraphBuilder_constructor, 7, true},
119
       FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
A
Adam Barth 已提交
120 121
}

122
fml::RefPtr<ParagraphBuilder> ParagraphBuilder::create(
123 124 125 126
    tonic::Int32List& encoded,
    const std::string& fontFamily,
    double fontSize,
    double lineHeight,
127
    const std::u16string& ellipsis,
J
Jason Simmons 已提交
128
    const std::string& locale) {
129
  return fml::MakeRefCounted<ParagraphBuilder>(encoded, fontFamily, fontSize,
130
                                               lineHeight, ellipsis, locale);
131 132 133 134 135 136
}

ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded,
                                   const std::string& fontFamily,
                                   double fontSize,
                                   double lineHeight,
137
                                   const std::u16string& ellipsis,
J
Jason Simmons 已提交
138 139 140 141 142
                                   const std::string& locale) {
  int32_t mask = encoded[0];
  txt::ParagraphStyle style;
  if (mask & psTextAlignMask)
    style.text_align = txt::TextAlign(encoded[psTextAlignIndex]);
143

J
Jason Simmons 已提交
144 145
  if (mask & psTextDirectionMask)
    style.text_direction = txt::TextDirection(encoded[psTextDirectionIndex]);
146

J
Jason Simmons 已提交
147 148 149
  if (mask & psFontWeightMask)
    style.font_weight =
        static_cast<txt::FontWeight>(encoded[psFontWeightIndex]);
150

J
Jason Simmons 已提交
151 152
  if (mask & psFontStyleMask)
    style.font_style = static_cast<txt::FontStyle>(encoded[psFontStyleIndex]);
153

J
Jason Simmons 已提交
154 155
  if (mask & psFontFamilyMask)
    style.font_family = fontFamily;
156

J
Jason Simmons 已提交
157 158
  if (mask & psFontSizeMask)
    style.font_size = fontSize;
159

J
Jason Simmons 已提交
160 161
  if (mask & psLineHeightMask)
    style.line_height = lineHeight;
162

J
Jason Simmons 已提交
163 164
  if (mask & psMaxLinesMask)
    style.max_lines = encoded[psMaxLinesIndex];
165

G
Gary Qian 已提交
166
  if (mask & psEllipsisMask)
J
Jason Simmons 已提交
167
    style.ellipsis = ellipsis;
168

G
Gary Qian 已提交
169
  if (mask & psLocaleMask)
J
Jason Simmons 已提交
170
    style.locale = locale;
171

172 173
  FontCollection& font_collection =
      UIDartState::Current()->window()->client()->GetFontCollection();
J
Jason Simmons 已提交
174
  m_paragraphBuilder = std::make_unique<txt::ParagraphBuilder>(
175
      style, font_collection.GetFontCollection());
176
}  // namespace blink
177

J
Jason Simmons 已提交
178
ParagraphBuilder::~ParagraphBuilder() = default;
179

G
Gary Qian 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
void decodeTextShadows(Dart_Handle shadows_data,
                       std::vector<txt::TextShadow>& decoded_shadows) {
  decoded_shadows.clear();

  tonic::DartByteData byte_data(shadows_data);
  FML_CHECK(byte_data.length_in_bytes() % kBytesPerShadow == 0);

  const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
  const float* float_data = static_cast<const float*>(byte_data.data());

  size_t shadow_count = byte_data.length_in_bytes() / kBytesPerShadow;
  size_t shadow_count_offset = 0;
  for (size_t shadow_index = 0; shadow_index < shadow_count; ++shadow_index) {
    shadow_count_offset = shadow_index * kShadowPropertiesCount;
    SkColor color =
        uint_data[shadow_count_offset + kColorOffset] ^ kColorDefault;
    decoded_shadows.emplace_back(
        color,
        SkPoint::Make(float_data[shadow_count_offset + kXOffset],
                      float_data[shadow_count_offset + kYOffset]),
        float_data[shadow_count_offset + kBlurOffset]);
  }
}

204 205 206 207 208
void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
                                 const std::string& fontFamily,
                                 double fontSize,
                                 double letterSpacing,
                                 double wordSpacing,
209
                                 double height,
210 211
                                 const std::string& locale,
                                 Dart_Handle background_objects,
212 213
                                 Dart_Handle background_data,
                                 Dart_Handle foreground_objects,
G
Gary Qian 已提交
214 215
                                 Dart_Handle foreground_data,
                                 Dart_Handle shadows_data) {
216
  FML_DCHECK(encoded.num_elements() == 8);
217 218 219

  int32_t mask = encoded[0];

J
Jason Simmons 已提交
220 221 222
  // Set to use the properties of the previous style if the property is not
  // explicitly given.
  txt::TextStyle style = m_paragraphBuilder->PeekStyle();
223

G
Gary Qian 已提交
224 225
  // Only change the style property from the previous value if a new explicitly
  // set value is available
J
Jason Simmons 已提交
226 227
  if (mask & tsColorMask)
    style.color = encoded[tsColorIndex];
228

J
Jason Simmons 已提交
229 230 231 232
  if (mask & tsTextDecorationMask) {
    style.decoration =
        static_cast<txt::TextDecoration>(encoded[tsTextDecorationIndex]);
  }
233

J
Jason Simmons 已提交
234 235
  if (mask & tsTextDecorationColorMask)
    style.decoration_color = encoded[tsTextDecorationColorIndex];
236

J
Jason Simmons 已提交
237 238 239
  if (mask & tsTextDecorationStyleMask)
    style.decoration_style = static_cast<txt::TextDecorationStyle>(
        encoded[tsTextDecorationStyleIndex]);
240

J
Jason Simmons 已提交
241 242 243 244
  if (mask & tsTextBaselineMask) {
    // TODO(abarth): Implement TextBaseline. The CSS version of this
    // property wasn't wired up either.
  }
245

J
Jason Simmons 已提交
246 247 248 249 250
  if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontFamilyMask |
              tsFontSizeMask | tsLetterSpacingMask | tsWordSpacingMask)) {
    if (mask & tsFontWeightMask)
      style.font_weight =
          static_cast<txt::FontWeight>(encoded[tsFontWeightIndex]);
251

J
Jason Simmons 已提交
252
    if (mask & tsFontStyleMask)
253
      style.font_style = static_cast<txt::FontStyle>(encoded[tsFontStyleIndex]);
254

J
Jason Simmons 已提交
255 256
    if (mask & tsFontFamilyMask)
      style.font_family = fontFamily;
257

J
Jason Simmons 已提交
258 259
    if (mask & tsFontSizeMask)
      style.font_size = fontSize;
260

J
Jason Simmons 已提交
261 262
    if (mask & tsLetterSpacingMask)
      style.letter_spacing = letterSpacing;
263

J
Jason Simmons 已提交
264 265 266
    if (mask & tsWordSpacingMask)
      style.word_spacing = wordSpacing;
  }
267

J
Jason Simmons 已提交
268 269 270
  if (mask & tsHeightMask) {
    style.height = height;
  }
271

J
Jason Simmons 已提交
272 273 274
  if (mask & tsLocaleMask) {
    style.locale = locale;
  }
275

J
Jason Simmons 已提交
276 277 278 279 280
  if (mask & tsBackgroundMask) {
    Paint background(background_objects, background_data);
    if (background.paint()) {
      style.has_background = true;
      style.background = *background.paint();
281 282
    }
  }
J
Jason Simmons 已提交
283

284 285 286 287 288 289 290 291
  if (mask & tsForegroundMask) {
    Paint foreground(foreground_objects, foreground_data);
    if (foreground.paint()) {
      style.has_foreground = true;
      style.foreground = *foreground.paint();
    }
  }

G
Gary Qian 已提交
292 293 294 295
  if (mask & tsTextShadowsMask) {
    decodeTextShadows(shadows_data, style.text_shadows);
  }

J
Jason Simmons 已提交
296
  m_paragraphBuilder->PushStyle(style);
297 298
}

299
void ParagraphBuilder::pop() {
J
Jason Simmons 已提交
300
  m_paragraphBuilder->Pop();
301 302
}

303 304 305 306 307 308 309 310 311 312 313 314 315
Dart_Handle ParagraphBuilder::addText(const std::u16string& text) {
  if (text.empty())
    return Dart_Null();

  // Use ICU to validate the UTF-16 input.  Calling u_strToUTF8 with a null
  // output buffer will return U_BUFFER_OVERFLOW_ERROR if the input is well
  // formed.
  const UChar* text_ptr = reinterpret_cast<const UChar*>(text.data());
  UErrorCode error_code = U_ZERO_ERROR;
  u_strToUTF8(nullptr, 0, nullptr, text_ptr, text.size(), &error_code);
  if (error_code != U_BUFFER_OVERFLOW_ERROR)
    return tonic::ToDart("string is not well-formed UTF-16");

J
Jason Simmons 已提交
316
  m_paragraphBuilder->AddText(text);
317 318

  return Dart_Null();
319 320
}

321
fml::RefPtr<Paragraph> ParagraphBuilder::build() {
J
Jason Simmons 已提交
322
  return Paragraph::Create(m_paragraphBuilder->Build());
A
Adam Barth 已提交
323 324
}

325
}  // namespace blink