OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 * (C) 2000 Dirk Mueller (mueller@kde.org) | 4 * (C) 2000 Dirk Mueller (mueller@kde.org) |
5 * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved. | 5 * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved. |
6 * Copyright (c) 2007, 2008, 2010 Google Inc. All rights reserved. | 6 * Copyright (c) 2007, 2008, 2010 Google Inc. All rights reserved. |
7 * | 7 * |
8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
(...skipping 13 matching lines...) Expand all Loading... |
24 | 24 |
25 #include "platform/fonts/Font.h" | 25 #include "platform/fonts/Font.h" |
26 | 26 |
27 #include "platform/LayoutTestSupport.h" | 27 #include "platform/LayoutTestSupport.h" |
28 #include "platform/LayoutUnit.h" | 28 #include "platform/LayoutUnit.h" |
29 #include "platform/RuntimeEnabledFeatures.h" | 29 #include "platform/RuntimeEnabledFeatures.h" |
30 #include "platform/fonts/CharacterRange.h" | 30 #include "platform/fonts/CharacterRange.h" |
31 #include "platform/fonts/FontCache.h" | 31 #include "platform/fonts/FontCache.h" |
32 #include "platform/fonts/FontFallbackIterator.h" | 32 #include "platform/fonts/FontFallbackIterator.h" |
33 #include "platform/fonts/FontFallbackList.h" | 33 #include "platform/fonts/FontFallbackList.h" |
34 #include "platform/fonts/GlyphBuffer.h" | |
35 #include "platform/fonts/SimpleFontData.h" | 34 #include "platform/fonts/SimpleFontData.h" |
36 #include "platform/fonts/shaping/CachingWordShaper.h" | 35 #include "platform/fonts/shaping/CachingWordShaper.h" |
| 36 #include "platform/fonts/shaping/ShapeResultBloberizer.h" |
37 #include "platform/geometry/FloatRect.h" | 37 #include "platform/geometry/FloatRect.h" |
38 #include "platform/graphics/paint/PaintCanvas.h" | 38 #include "platform/graphics/paint/PaintCanvas.h" |
39 #include "platform/graphics/paint/PaintFlags.h" | 39 #include "platform/graphics/paint/PaintFlags.h" |
40 #include "platform/text/BidiResolver.h" | 40 #include "platform/text/BidiResolver.h" |
41 #include "platform/text/Character.h" | 41 #include "platform/text/Character.h" |
42 #include "platform/text/TextRun.h" | 42 #include "platform/text/TextRun.h" |
43 #include "platform/text/TextRunIterator.h" | 43 #include "platform/text/TextRunIterator.h" |
44 #include "platform/transforms/AffineTransform.h" | 44 #include "platform/transforms/AffineTransform.h" |
45 #include "third_party/skia/include/core/SkTextBlob.h" | 45 #include "third_party/skia/include/core/SkTextBlob.h" |
46 #include "wtf/StdLibExtras.h" | 46 #include "wtf/StdLibExtras.h" |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 // but it ends up being reasonably safe (because inherited fonts in the render | 98 // but it ends up being reasonably safe (because inherited fonts in the render |
99 // tree pick up the new style anyway. Other copies are transient, e.g., the | 99 // tree pick up the new style anyway. Other copies are transient, e.g., the |
100 // state in the GraphicsContext, and won't stick around long enough to get you | 100 // state in the GraphicsContext, and won't stick around long enough to get you |
101 // in trouble). Still, this is pretty disgusting, and could eventually be | 101 // in trouble). Still, this is pretty disgusting, and could eventually be |
102 // rectified by using RefPtrs for Fonts themselves. | 102 // rectified by using RefPtrs for Fonts themselves. |
103 if (!m_fontFallbackList) | 103 if (!m_fontFallbackList) |
104 m_fontFallbackList = FontFallbackList::create(); | 104 m_fontFallbackList = FontFallbackList::create(); |
105 m_fontFallbackList->invalidate(fontSelector); | 105 m_fontFallbackList->invalidate(fontSelector); |
106 } | 106 } |
107 | 107 |
108 float Font::buildGlyphBuffer(const TextRunPaintInfo& runInfo, | 108 namespace { |
109 GlyphBuffer& glyphBuffer, | 109 |
110 const GlyphData* emphasisData) const { | 110 void drawBlobs(PaintCanvas* canvas, |
111 float width; | 111 const PaintFlags& flags, |
112 CachingWordShaper shaper(*this); | 112 const ShapeResultBloberizer::BlobBuffer& blobs, |
113 if (emphasisData) { | 113 const FloatPoint& point) { |
114 width = shaper.fillGlyphBufferForTextEmphasis(runInfo.run, | 114 for (const auto& blobInfo : blobs) { |
115 emphasisData, &glyphBuffer, | 115 DCHECK(blobInfo.blob); |
116 runInfo.from, runInfo.to); | 116 PaintCanvasAutoRestore autoRestore(canvas, false); |
117 } else { | 117 if (blobInfo.rotation == ShapeResultBloberizer::BlobRotation::CCWRotation) { |
118 width = shaper.fillGlyphBuffer(runInfo.run, &glyphBuffer, | 118 canvas->save(); |
119 runInfo.from, runInfo.to); | 119 |
| 120 SkMatrix m; |
| 121 m.setSinCos(-1, 0, point.x(), point.y()); |
| 122 canvas->concat(m); |
| 123 } |
| 124 |
| 125 canvas->drawTextBlob(blobInfo.blob, point.x(), point.y(), flags); |
120 } | 126 } |
121 return width; | |
122 } | 127 } |
123 | 128 |
| 129 } // anonymous ns |
| 130 |
124 bool Font::drawText(PaintCanvas* canvas, | 131 bool Font::drawText(PaintCanvas* canvas, |
125 const TextRunPaintInfo& runInfo, | 132 const TextRunPaintInfo& runInfo, |
126 const FloatPoint& point, | 133 const FloatPoint& point, |
127 float deviceScaleFactor, | 134 float deviceScaleFactor, |
128 const PaintFlags& flags) const { | 135 const PaintFlags& flags) const { |
129 // Don't draw anything while we are using custom fonts that are in the process | 136 // Don't draw anything while we are using custom fonts that are in the process |
130 // of loading. | 137 // of loading. |
131 if (shouldSkipDrawing()) | 138 if (shouldSkipDrawing()) |
132 return false; | 139 return false; |
133 | 140 |
134 GlyphBuffer glyphBuffer; | 141 ShapeResultBloberizer bloberizer(*this, deviceScaleFactor); |
135 buildGlyphBuffer(runInfo, glyphBuffer); | 142 CachingWordShaper(*this).fillGlyphs(runInfo, bloberizer); |
136 | 143 drawBlobs(canvas, flags, bloberizer.blobs(), point); |
137 drawGlyphBuffer(canvas, flags, glyphBuffer, point, deviceScaleFactor); | |
138 return true; | 144 return true; |
139 } | 145 } |
140 | 146 |
141 bool Font::drawBidiText(PaintCanvas* canvas, | 147 bool Font::drawBidiText(PaintCanvas* canvas, |
142 const TextRunPaintInfo& runInfo, | 148 const TextRunPaintInfo& runInfo, |
143 const FloatPoint& point, | 149 const FloatPoint& point, |
144 CustomFontNotReadyAction customFontNotReadyAction, | 150 CustomFontNotReadyAction customFontNotReadyAction, |
145 float deviceScaleFactor, | 151 float deviceScaleFactor, |
146 const PaintFlags& flags) const { | 152 const PaintFlags& flags) const { |
147 // Don't draw anything while we are using custom fonts that are in the process | 153 // Don't draw anything while we are using custom fonts that are in the process |
(...skipping 23 matching lines...) Expand all Loading... |
171 while (bidiRun) { | 177 while (bidiRun) { |
172 TextRun subrun = | 178 TextRun subrun = |
173 run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); | 179 run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); |
174 bool isRTL = bidiRun->level() % 2; | 180 bool isRTL = bidiRun->level() % 2; |
175 subrun.setDirection(isRTL ? TextDirection::kRtl : TextDirection::kLtr); | 181 subrun.setDirection(isRTL ? TextDirection::kRtl : TextDirection::kLtr); |
176 subrun.setDirectionalOverride(bidiRun->dirOverride(false)); | 182 subrun.setDirectionalOverride(bidiRun->dirOverride(false)); |
177 | 183 |
178 TextRunPaintInfo subrunInfo(subrun); | 184 TextRunPaintInfo subrunInfo(subrun); |
179 subrunInfo.bounds = runInfo.bounds; | 185 subrunInfo.bounds = runInfo.bounds; |
180 | 186 |
181 // TODO: investigate blob consolidation/caching (technically, | 187 ShapeResultBloberizer bloberizer(*this, deviceScaleFactor); |
182 // all subruns could be part of the same blob). | 188 float runWidth = |
183 GlyphBuffer glyphBuffer; | 189 CachingWordShaper(*this).fillGlyphs(subrunInfo, bloberizer); |
184 float runWidth = buildGlyphBuffer(subrunInfo, glyphBuffer); | 190 drawBlobs(canvas, flags, bloberizer.blobs(), currPoint); |
185 drawGlyphBuffer(canvas, flags, glyphBuffer, currPoint, deviceScaleFactor); | |
186 | 191 |
187 bidiRun = bidiRun->next(); | 192 bidiRun = bidiRun->next(); |
188 currPoint.move(runWidth, 0); | 193 currPoint.move(runWidth, 0); |
189 } | 194 } |
190 | 195 |
191 bidiRuns.deleteRuns(); | 196 bidiRuns.deleteRuns(); |
192 return true; | 197 return true; |
193 } | 198 } |
194 | 199 |
195 void Font::drawEmphasisMarks(PaintCanvas* canvas, | 200 void Font::drawEmphasisMarks(PaintCanvas* canvas, |
196 const TextRunPaintInfo& runInfo, | 201 const TextRunPaintInfo& runInfo, |
197 const AtomicString& mark, | 202 const AtomicString& mark, |
198 const FloatPoint& point, | 203 const FloatPoint& point, |
199 float deviceScaleFactor, | 204 float deviceScaleFactor, |
200 const PaintFlags& flags) const { | 205 const PaintFlags& flags) const { |
201 if (shouldSkipDrawing()) | 206 if (shouldSkipDrawing()) |
202 return; | 207 return; |
203 | 208 |
204 FontCachePurgePreventer purgePreventer; | 209 FontCachePurgePreventer purgePreventer; |
205 | 210 |
206 const auto emphasisGlyphData = getEmphasisMarkGlyphData(mark); | 211 const auto emphasisGlyphData = getEmphasisMarkGlyphData(mark); |
207 if (!emphasisGlyphData.fontData) | 212 if (!emphasisGlyphData.fontData) |
208 return; | 213 return; |
209 | 214 |
210 GlyphBuffer glyphBuffer; | 215 ShapeResultBloberizer bloberizer(*this, deviceScaleFactor); |
211 buildGlyphBuffer(runInfo, glyphBuffer, &emphasisGlyphData); | 216 CachingWordShaper(*this).fillTextEmphasisGlyphs(runInfo, emphasisGlyphData, |
212 | 217 bloberizer); |
213 if (glyphBuffer.isEmpty()) | 218 drawBlobs(canvas, flags, bloberizer.blobs(), point); |
214 return; | |
215 | |
216 drawGlyphBuffer(canvas, flags, glyphBuffer, point, deviceScaleFactor); | |
217 } | 219 } |
218 | 220 |
219 float Font::width(const TextRun& run, | 221 float Font::width(const TextRun& run, |
220 HashSet<const SimpleFontData*>* fallbackFonts, | 222 HashSet<const SimpleFontData*>* fallbackFonts, |
221 FloatRect* glyphBounds) const { | 223 FloatRect* glyphBounds) const { |
222 FontCachePurgePreventer purgePreventer; | 224 FontCachePurgePreventer purgePreventer; |
223 CachingWordShaper shaper(*this); | 225 CachingWordShaper shaper(*this); |
224 return shaper.width(run, fallbackFonts, glyphBounds); | 226 return shaper.width(run, fallbackFonts, glyphBounds); |
225 } | 227 } |
226 | 228 |
227 namespace { | 229 static int getInterceptsFromBlobs( |
228 | 230 const ShapeResultBloberizer::BlobBuffer& blobs, |
229 enum BlobRotation { | 231 const SkPaint& paint, |
230 NoRotation, | 232 const std::tuple<float, float>& bounds, |
231 CCWRotation, | 233 SkScalar* interceptsBuffer) { |
232 }; | |
233 | |
234 class GlyphBufferBloberizer { | |
235 STACK_ALLOCATED() | |
236 public: | |
237 GlyphBufferBloberizer(const GlyphBuffer& buffer, | |
238 const Font* font, | |
239 float deviceScaleFactor) | |
240 : m_buffer(buffer), | |
241 m_font(font), | |
242 m_deviceScaleFactor(deviceScaleFactor), | |
243 m_hasVerticalOffsets(buffer.hasVerticalOffsets()), | |
244 m_index(0), | |
245 m_endIndex(m_buffer.size()), | |
246 m_rotation(buffer.isEmpty() ? NoRotation : computeBlobRotation( | |
247 buffer.fontDataAt(0))) {} | |
248 | |
249 bool done() const { return m_index >= m_endIndex; } | |
250 | |
251 std::pair<sk_sp<SkTextBlob>, BlobRotation> next() { | |
252 ASSERT(!done()); | |
253 const BlobRotation currentRotation = m_rotation; | |
254 | |
255 while (m_index < m_endIndex) { | |
256 const SimpleFontData* fontData = m_buffer.fontDataAt(m_index); | |
257 ASSERT(fontData); | |
258 | |
259 const BlobRotation newRotation = computeBlobRotation(fontData); | |
260 if (newRotation != m_rotation) { | |
261 // We're switching to an orientation which requires a different rotation | |
262 // => emit the pending blob (and start a new one with the new | |
263 // rotation). | |
264 m_rotation = newRotation; | |
265 break; | |
266 } | |
267 | |
268 const unsigned start = m_index++; | |
269 while (m_index < m_endIndex && m_buffer.fontDataAt(m_index) == fontData) | |
270 m_index++; | |
271 | |
272 appendRun(start, m_index - start, fontData); | |
273 } | |
274 | |
275 return std::make_pair(m_builder.make(), currentRotation); | |
276 } | |
277 | |
278 private: | |
279 static BlobRotation computeBlobRotation(const SimpleFontData* font) { | |
280 // For vertical upright text we need to compensate the inherited 90deg CW | |
281 // rotation (using a 90deg CCW rotation). | |
282 return (font->platformData().isVerticalAnyUpright() && font->verticalData()) | |
283 ? CCWRotation | |
284 : NoRotation; | |
285 } | |
286 | |
287 void appendRun(unsigned start, | |
288 unsigned count, | |
289 const SimpleFontData* fontData) { | |
290 SkPaint paint; | |
291 fontData->platformData().setupPaint(&paint, m_deviceScaleFactor, m_font); | |
292 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
293 | |
294 const SkTextBlobBuilder::RunBuffer& buffer = | |
295 m_hasVerticalOffsets ? m_builder.allocRunPos(paint, count) | |
296 : m_builder.allocRunPosH(paint, count, 0); | |
297 | |
298 const uint16_t* glyphs = m_buffer.glyphs(start); | |
299 const float* offsets = m_buffer.offsets(start); | |
300 std::copy(glyphs, glyphs + count, buffer.glyphs); | |
301 | |
302 if (m_rotation == NoRotation) { | |
303 std::copy(offsets, offsets + (m_hasVerticalOffsets ? 2 * count : count), | |
304 buffer.pos); | |
305 } else { | |
306 ASSERT(m_hasVerticalOffsets); | |
307 | |
308 const float verticalBaselineXOffset = | |
309 fontData->getFontMetrics().floatAscent() - | |
310 fontData->getFontMetrics().floatAscent(IdeographicBaseline); | |
311 | |
312 // TODO(fmalita): why don't we apply this adjustment when building the | |
313 // glyph buffer? | |
314 for (unsigned i = 0; i < 2 * count; i += 2) { | |
315 buffer.pos[i] = SkFloatToScalar(offsets[i] + verticalBaselineXOffset); | |
316 buffer.pos[i + 1] = SkFloatToScalar(offsets[i + 1]); | |
317 } | |
318 } | |
319 } | |
320 | |
321 const GlyphBuffer& m_buffer; | |
322 const Font* m_font; | |
323 const float m_deviceScaleFactor; | |
324 const bool m_hasVerticalOffsets; | |
325 | |
326 SkTextBlobBuilder m_builder; | |
327 unsigned m_index; | |
328 unsigned m_endIndex; | |
329 BlobRotation m_rotation; | |
330 }; | |
331 | |
332 } // anonymous namespace | |
333 | |
334 void Font::drawGlyphBuffer(PaintCanvas* canvas, | |
335 const PaintFlags& flags, | |
336 const GlyphBuffer& glyphBuffer, | |
337 const FloatPoint& point, | |
338 float deviceScaleFactor) const { | |
339 GlyphBufferBloberizer bloberizer(glyphBuffer, this, deviceScaleFactor); | |
340 | |
341 while (!bloberizer.done()) { | |
342 auto blob = bloberizer.next(); | |
343 ASSERT(blob.first); | |
344 | |
345 PaintCanvasAutoRestore autoRestore(canvas, false); | |
346 if (blob.second == CCWRotation) { | |
347 canvas->save(); | |
348 | |
349 SkMatrix m; | |
350 m.setSinCos(-1, 0, point.x(), point.y()); | |
351 canvas->concat(m); | |
352 } | |
353 | |
354 canvas->drawTextBlob(blob.first, point.x(), point.y(), flags); | |
355 } | |
356 } | |
357 | |
358 static int getInterceptsFromBloberizer(const GlyphBuffer& glyphBuffer, | |
359 const Font* font, | |
360 const SkPaint& paint, | |
361 float deviceScaleFactor, | |
362 const std::tuple<float, float>& bounds, | |
363 SkScalar* interceptsBuffer) { | |
364 SkScalar boundsArray[2] = {std::get<0>(bounds), std::get<1>(bounds)}; | 234 SkScalar boundsArray[2] = {std::get<0>(bounds), std::get<1>(bounds)}; |
365 GlyphBufferBloberizer bloberizer(glyphBuffer, font, deviceScaleFactor); | |
366 | 235 |
367 int numIntervals = 0; | 236 int numIntervals = 0; |
368 while (!bloberizer.done()) { | 237 for (const auto& blobInfo : blobs) { |
369 auto blob = bloberizer.next(); | 238 DCHECK(blobInfo.blob); |
370 DCHECK(blob.first); | |
371 | 239 |
372 // GlyphBufferBloberizer splits for a new blob rotation, but does not split | 240 // ShapeResultBloberizer splits for a new blob rotation, but does not split |
373 // for a change in font. A TextBlob can contain runs with differing fonts | 241 // for a change in font. A TextBlob can contain runs with differing fonts |
374 // and the getTextBlobIntercepts method handles multiple fonts for us. For | 242 // and the getTextBlobIntercepts method handles multiple fonts for us. For |
375 // upright in vertical blobs we currently have to bail, see crbug.com/655154 | 243 // upright in vertical blobs we currently have to bail, see crbug.com/655154 |
376 if (blob.second == BlobRotation::CCWRotation) | 244 if (blobInfo.rotation == ShapeResultBloberizer::BlobRotation::CCWRotation) |
377 continue; | 245 continue; |
378 | 246 |
379 SkScalar* offsetInterceptsBuffer = nullptr; | 247 SkScalar* offsetInterceptsBuffer = nullptr; |
380 if (interceptsBuffer) | 248 if (interceptsBuffer) |
381 offsetInterceptsBuffer = &interceptsBuffer[numIntervals]; | 249 offsetInterceptsBuffer = &interceptsBuffer[numIntervals]; |
382 numIntervals += paint.getTextBlobIntercepts(blob.first.get(), boundsArray, | 250 numIntervals += paint.getTextBlobIntercepts( |
383 offsetInterceptsBuffer); | 251 blobInfo.blob.get(), boundsArray, offsetInterceptsBuffer); |
384 } | 252 } |
385 return numIntervals; | 253 return numIntervals; |
386 } | 254 } |
387 | 255 |
388 void Font::getTextIntercepts(const TextRunPaintInfo& runInfo, | 256 void Font::getTextIntercepts(const TextRunPaintInfo& runInfo, |
389 float deviceScaleFactor, | 257 float deviceScaleFactor, |
390 const PaintFlags& flags, | 258 const PaintFlags& flags, |
391 const std::tuple<float, float>& bounds, | 259 const std::tuple<float, float>& bounds, |
392 Vector<TextIntercept>& intercepts) const { | 260 Vector<TextIntercept>& intercepts) const { |
393 if (shouldSkipDrawing()) | 261 if (shouldSkipDrawing()) |
394 return; | 262 return; |
395 | 263 |
396 GlyphBuffer glyphBuffer(GlyphBuffer::Type::TextIntercepts); | 264 ShapeResultBloberizer bloberizer(*this, deviceScaleFactor, |
397 buildGlyphBuffer(runInfo, glyphBuffer); | 265 ShapeResultBloberizer::Type::TextIntercepts); |
| 266 CachingWordShaper(*this).fillGlyphs(runInfo, bloberizer); |
| 267 const auto& blobs = bloberizer.blobs(); |
398 | 268 |
399 // Get the number of intervals, without copying the actual values by | 269 // Get the number of intervals, without copying the actual values by |
400 // specifying nullptr for the buffer, following the Skia allocation model for | 270 // specifying nullptr for the buffer, following the Skia allocation model for |
401 // retrieving text intercepts. | 271 // retrieving text intercepts. |
402 SkPaint paint(ToSkPaint(flags)); | 272 SkPaint paint(ToSkPaint(flags)); |
403 int numIntervals = getInterceptsFromBloberizer( | 273 int numIntervals = getInterceptsFromBlobs(blobs, paint, bounds, nullptr); |
404 glyphBuffer, this, paint, deviceScaleFactor, bounds, nullptr); | |
405 if (!numIntervals) | 274 if (!numIntervals) |
406 return; | 275 return; |
407 DCHECK_EQ(numIntervals % 2, 0); | 276 DCHECK_EQ(numIntervals % 2, 0); |
408 intercepts.resize(numIntervals / 2); | 277 intercepts.resize(numIntervals / 2); |
409 | 278 |
410 getInterceptsFromBloberizer(glyphBuffer, this, paint, deviceScaleFactor, | 279 getInterceptsFromBlobs(blobs, paint, bounds, |
411 bounds, | 280 reinterpret_cast<SkScalar*>(intercepts.data())); |
412 reinterpret_cast<SkScalar*>(intercepts.data())); | |
413 } | 281 } |
414 | 282 |
415 static inline FloatRect pixelSnappedSelectionRect(FloatRect rect) { | 283 static inline FloatRect pixelSnappedSelectionRect(FloatRect rect) { |
416 // Using roundf() rather than ceilf() for the right edge as a compromise to | 284 // Using roundf() rather than ceilf() for the right edge as a compromise to |
417 // ensure correct caret positioning. | 285 // ensure correct caret positioning. |
418 float roundedX = roundf(rect.x()); | 286 float roundedX = roundf(rect.x()); |
419 return FloatRect(roundedX, rect.y(), roundf(rect.maxX() - roundedX), | 287 return FloatRect(roundedX, rect.y(), roundf(rect.maxX() - roundedX), |
420 rect.height()); | 288 rect.height()); |
421 } | 289 } |
422 | 290 |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
551 | 419 |
552 bool Font::loadingCustomFonts() const { | 420 bool Font::loadingCustomFonts() const { |
553 return m_fontFallbackList && m_fontFallbackList->loadingCustomFonts(); | 421 return m_fontFallbackList && m_fontFallbackList->loadingCustomFonts(); |
554 } | 422 } |
555 | 423 |
556 bool Font::isFallbackValid() const { | 424 bool Font::isFallbackValid() const { |
557 return !m_fontFallbackList || m_fontFallbackList->isValid(); | 425 return !m_fontFallbackList || m_fontFallbackList->isValid(); |
558 } | 426 } |
559 | 427 |
560 } // namespace blink | 428 } // namespace blink |
OLD | NEW |