OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrStencilAndCoverTextContext.h" | 8 #include "GrStencilAndCoverTextContext.h" |
9 #include "GrAtlasTextContext.h" | 9 #include "GrAtlasTextContext.h" |
10 #include "GrContext.h" | 10 #include "GrContext.h" |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | 228 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); |
229 | 229 |
230 TextBlob::Iter iter(blob); | 230 TextBlob::Iter iter(blob); |
231 for (TextRun* run = iter.get(); run; run = iter.next()) { | 231 for (TextRun* run = iter.get(); run; run = iter.next()) { |
232 run->draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, p
rops, x, y, | 232 run->draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, p
rops, x, y, |
233 clipBounds, fFallbackTextContext, skPaint); | 233 clipBounds, fFallbackTextContext, skPaint); |
234 run->releaseGlyphCache(); | 234 run->releaseGlyphCache(); |
235 } | 235 } |
236 } | 236 } |
237 | 237 |
| 238 static inline int style_key_cnt(const GrStyle& style) { |
| 239 int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec); |
| 240 // We should be able to make a key because we filtered out arbitrary path ef
fects. |
| 241 SkASSERT(cnt > 0); |
| 242 return cnt; |
| 243 } |
| 244 |
| 245 static inline void write_style_key(uint32_t* dst, const GrStyle& style) { |
| 246 // Pass 1 for the scale since the GPU will apply the style not GrStyle::appl
yToPath(). |
| 247 GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Sc
alar1); |
| 248 } |
| 249 |
238 const GrStencilAndCoverTextContext::TextBlob& | 250 const GrStencilAndCoverTextContext::TextBlob& |
239 GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob, | 251 GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob, |
240 const SkPaint& skPaint) { | 252 const SkPaint& skPaint) { |
241 // The font-related parameters are baked into the text blob and will overrid
e this skPaint, so | 253 // The font-related parameters are baked into the text blob and will overrid
e this skPaint, so |
242 // the only remaining properties that can affect a TextBlob are the ones rel
ated to stroke. | 254 // the only remaining properties that can affect a TextBlob are the ones rel
ated to stroke. |
243 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path. | 255 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path. |
244 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) { | 256 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) { |
245 fLRUList.remove(*found); | 257 fLRUList.remove(*found); |
246 fLRUList.addToTail(*found); | 258 fLRUList.addToTail(*found); |
247 return **found; | 259 return **found; |
248 } | 260 } |
249 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint); | 261 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint); |
250 this->purgeToFit(*blob); | 262 this->purgeToFit(*blob); |
251 fBlobIdCache.set(skBlob->uniqueID(), blob); | 263 fBlobIdCache.set(skBlob->uniqueID(), blob); |
252 fLRUList.addToTail(blob); | 264 fLRUList.addToTail(blob); |
253 fCacheSize += blob->cpuMemorySize(); | 265 fCacheSize += blob->cpuMemorySize(); |
254 return *blob; | 266 return *blob; |
255 } else { | 267 } else { |
256 GrStrokeInfo stroke(skPaint); | 268 GrStyle style(skPaint); |
257 SkSTArray<4, uint32_t, true> key; | 269 SkSTArray<4, uint32_t, true> key; |
258 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt()); | 270 key.reset(1 + style_key_cnt(style)); |
259 key[0] = skBlob->uniqueID(); | 271 key[0] = skBlob->uniqueID(); |
260 stroke.asUniqueKeyFragment(&key[1]); | 272 write_style_key(&key[1], style); |
261 if (TextBlob** found = fBlobKeyCache.find(key)) { | 273 if (TextBlob** found = fBlobKeyCache.find(key)) { |
262 fLRUList.remove(*found); | 274 fLRUList.remove(*found); |
263 fLRUList.addToTail(*found); | 275 fLRUList.addToTail(*found); |
264 return **found; | 276 return **found; |
265 } | 277 } |
266 TextBlob* blob = new TextBlob(key, skBlob, skPaint); | 278 TextBlob* blob = new TextBlob(key, skBlob, skPaint); |
267 this->purgeToFit(*blob); | 279 this->purgeToFit(*blob); |
268 fBlobKeyCache.set(blob); | 280 fBlobKeyCache.set(blob); |
269 fLRUList.addToTail(blob); | 281 fLRUList.addToTail(blob); |
270 fCacheSize += blob->cpuMemorySize(); | 282 fCacheSize += blob->cpuMemorySize(); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
346 SkPaint fFont; | 358 SkPaint fFont; |
347 int fBuffIdx; | 359 int fBuffIdx; |
348 int fCount; | 360 int fCount; |
349 uint16_t fGlyphIds[kWriteBufferSize]; | 361 uint16_t fGlyphIds[kWriteBufferSize]; |
350 SkPoint fPositions[kWriteBufferSize]; | 362 SkPoint fPositions[kWriteBufferSize]; |
351 }; | 363 }; |
352 | 364 |
353 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | 365 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
354 | 366 |
355 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) | 367 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) |
356 : fStroke(fontAndStroke), | 368 : fStyle(fontAndStroke), |
357 fFont(fontAndStroke), | 369 fFont(fontAndStroke), |
358 fTotalGlyphCount(0), | 370 fTotalGlyphCount(0), |
359 fFallbackGlyphCount(0), | 371 fFallbackGlyphCount(0), |
360 fDetachedGlyphCache(nullptr), | 372 fDetachedGlyphCache(nullptr), |
361 fLastDrawnGlyphsID(SK_InvalidUniqueID) { | 373 fLastDrawnGlyphsID(SK_InvalidUniqueID) { |
362 SkASSERT(fFont.getTextSize() > 0); | 374 SkASSERT(fFont.getTextSize() > 0); |
363 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. | 375 SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supp
orted. |
| 376 SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported. |
364 | 377 |
365 // Setting to "fill" ensures that no strokes get baked into font outlines. (
We use the GPU path | 378 // Setting to "fill" ensures that no strokes get baked into font outlines. (
We use the GPU path |
366 // rendering API for stroking). | 379 // rendering API for stroking). |
367 fFont.setStyle(SkPaint::kFill_Style); | 380 fFont.setStyle(SkPaint::kFill_Style); |
368 | 381 |
369 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle
()) { | 382 if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) { |
| 383 const SkStrokeRec& stroke = fStyle.strokeRec(); |
370 // Instead of letting fake bold get baked into the glyph outlines, do it
with GPU stroke. | 384 // Instead of letting fake bold get baked into the glyph outlines, do it
with GPU stroke. |
371 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), | 385 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), |
372 kStdFakeBoldInterpKeys, | 386 kStdFakeBoldInterpKeys, |
373 kStdFakeBoldInterpValues, | 387 kStdFakeBoldInterpValues, |
374 kStdFakeBoldInterpLength); | 388 kStdFakeBoldInterpLength); |
375 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale); | 389 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale); |
376 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extr
a : extra, | |
377 true /*strokeAndFill*/); | |
378 | 390 |
| 391 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle); |
| 392 strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extr
a : extra, |
| 393 true /*strokeAndFill*/); |
| 394 fStyle = GrStyle(strokeRec, fStyle.pathEffect()); |
379 fFont.setFakeBoldText(false); | 395 fFont.setFakeBoldText(false); |
380 } | 396 } |
381 | 397 |
382 if (!fFont.getPathEffect() && !fStroke.isDashed()) { | 398 if (!fFont.getPathEffect() && !fStyle.isDashed()) { |
| 399 const SkStrokeRec& stroke = fStyle.strokeRec(); |
383 // We can draw the glyphs from canonically sized paths. | 400 // We can draw the glyphs from canonically sized paths. |
384 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; | 401 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; |
385 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextS
ize(); | 402 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextS
ize(); |
386 | 403 |
387 // Compensate for the glyphs being scaled by fTextRatio. | 404 // Compensate for the glyphs being scaled by fTextRatio. |
388 if (!fStroke.isFillStyle()) { | 405 if (!fStyle.isSimpleFill()) { |
389 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, | 406 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle); |
390 SkStrokeRec::kStrokeAndFill_Style == fStroke.
getStyle()); | 407 strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio, |
| 408 SkStrokeRec::kStrokeAndFill_Style == stroke
.getStyle()); |
| 409 fStyle = GrStyle(strokeRec, fStyle.pathEffect()); |
391 } | 410 } |
392 | 411 |
393 fFont.setLinearText(true); | 412 fFont.setLinearText(true); |
394 fFont.setLCDRenderText(false); | 413 fFont.setLCDRenderText(false); |
395 fFont.setAutohinted(false); | 414 fFont.setAutohinted(false); |
396 fFont.setHinting(SkPaint::kNo_Hinting); | 415 fFont.setHinting(SkPaint::kNo_Hinting); |
397 fFont.setSubpixelText(true); | 416 fFont.setSubpixelText(true); |
398 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); | 417 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); |
399 | 418 |
400 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && | 419 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && |
401 0 == fFont.getTextSkewX() && | 420 0 == fFont.getTextSkewX() && |
402 !fFont.isFakeBoldText() && | 421 !fFont.isFakeBoldText() && |
403 !fFont.isVerticalText(); | 422 !fFont.isVerticalText(); |
404 } else { | 423 } else { |
405 fTextRatio = fTextInverseRatio = 1.0f; | 424 fTextRatio = fTextInverseRatio = 1.0f; |
406 fUsingRawGlyphPaths = false; | 425 fUsingRawGlyphPaths = false; |
407 } | 426 } |
408 | 427 |
409 // Generate the key that will be used to cache the GPU glyph path objects. | 428 // Generate the key that will be used to cache the GPU glyph path objects. |
410 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) { | 429 if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) { |
411 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::
GenerateDomain(); | 430 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::
GenerateDomain(); |
412 | 431 |
413 const SkTypeface* typeface = fFont.getTypeface(); | 432 const SkTypeface* typeface = fFont.getTypeface(); |
414 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1
); | 433 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1
); |
415 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID(
) : 0; | 434 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID(
) : 0; |
416 } else { | 435 } else { |
417 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::Generat
eDomain(); | 436 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::Generat
eDomain(); |
418 | 437 |
419 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt(); | 438 int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffec
tAndStrokeRec); |
| 439 // Key should be valid since we opted out of drawing arbitrary path effe
cts. |
| 440 SkASSERT(styleDataCount >= 0); |
420 if (fUsingRawGlyphPaths) { | 441 if (fUsingRawGlyphPaths) { |
421 const SkTypeface* typeface = fFont.getTypeface(); | 442 const SkTypeface* typeface = fFont.getTypeface(); |
422 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 +
strokeDataCount); | 443 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 +
styleDataCount); |
423 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; | 444 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; |
424 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount; | 445 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount; |
425 fStroke.asUniqueKeyFragment(&builder[2]); | 446 if (styleDataCount) { |
| 447 write_style_key(&builder[2], fStyle); |
| 448 } |
426 } else { | 449 } else { |
427 SkGlyphCache* glyphCache = this->getGlyphCache(); | 450 SkGlyphCache* glyphCache = this->getGlyphCache(); |
428 const SkTypeface* typeface = glyphCache->getScalerContext()->getType
face(); | 451 const SkTypeface* typeface = glyphCache->getScalerContext()->getType
face(); |
429 const SkDescriptor* desc = &glyphCache->getDescriptor(); | 452 const SkDescriptor* desc = &glyphCache->getDescriptor(); |
430 int descDataCount = (desc->getLength() + 3) / 4; | 453 int descDataCount = (desc->getLength() + 3) / 4; |
431 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, | 454 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, |
432 2 + strokeDataCount + descDataCount); | 455 2 + styleDataCount + descDataCount); |
433 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; | 456 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; |
434 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDat
aCount << 16); | 457 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descData
Count << 16); |
435 fStroke.asUniqueKeyFragment(&builder[2]); | 458 if (styleDataCount) { |
436 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength()); | 459 write_style_key(&builder[2], fStyle); |
| 460 } |
| 461 memcpy(&builder[2 + styleDataCount], desc, desc->getLength()); |
437 } | 462 } |
438 } | 463 } |
439 } | 464 } |
440 | 465 |
441 GrStencilAndCoverTextContext::TextRun::~TextRun() { | 466 GrStencilAndCoverTextContext::TextRun::~TextRun() { |
442 this->releaseGlyphCache(); | 467 this->releaseGlyphCache(); |
443 } | 468 } |
444 | 469 |
445 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by
teLength, | 470 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by
teLength, |
446 SkScalar x, SkScalar y) { | 471 SkScalar x, SkScalar y) { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
534 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); | 559 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); |
535 } | 560 } |
536 | 561 |
537 GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx)
const { | 562 GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx)
const { |
538 GrPathRange* glyphs = static_cast<GrPathRange*>( | 563 GrPathRange* glyphs = static_cast<GrPathRange*>( |
539 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKe
y)); | 564 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKe
y)); |
540 if (nullptr == glyphs) { | 565 if (nullptr == glyphs) { |
541 if (fUsingRawGlyphPaths) { | 566 if (fUsingRawGlyphPaths) { |
542 SkScalerContextEffects noeffects; | 567 SkScalerContextEffects noeffects; |
543 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(),
noeffects, | 568 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(),
noeffects, |
544 nullptr, fStroke); | 569 nullptr, fStyle); |
545 } else { | 570 } else { |
546 SkGlyphCache* cache = this->getGlyphCache(); | 571 SkGlyphCache* cache = this->getGlyphCache(); |
547 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerConte
xt()->getTypeface(), | 572 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerConte
xt()->getTypeface(), |
548 cache->getScalerConte
xt()->getEffects(), | 573 cache->getScalerConte
xt()->getEffects(), |
549 &cache->getDescriptor
(), | 574 &cache->getDescriptor
(), |
550 fStroke); | 575 fStyle); |
551 } | 576 } |
552 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyph
s); | 577 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyph
s); |
553 } | 578 } |
554 return glyphs; | 579 return glyphs; |
555 } | 580 } |
556 | 581 |
557 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& gl
yph, | 582 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& gl
yph, |
558 const SkPoint& po
s, | 583 const SkPoint& po
s, |
559 FallbackBlobBuild
er* fallback) { | 584 FallbackBlobBuild
er* fallback) { |
560 // Stick the glyphs we can't draw into the fallback text blob. | 585 // Stick the glyphs we can't draw into the fallback text blob. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
614 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRat
io * x, | 639 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRat
io * x, |
615 fTextInverseRatio * y, color, | 640 fTextInverseRatio * y, color, |
616 GrPathRendering::kWinding_FillType, gly
phs, fInstanceData, | 641 GrPathRendering::kWinding_FillType, gly
phs, fInstanceData, |
617 bounds)); | 642 bounds)); |
618 | 643 |
619 dc->drawPathBatch(*pipelineBuilder, batch); | 644 dc->drawPathBatch(*pipelineBuilder, batch); |
620 } | 645 } |
621 | 646 |
622 if (fFallbackTextBlob) { | 647 if (fFallbackTextBlob) { |
623 SkPaint fallbackSkPaint(originalSkPaint); | 648 SkPaint fallbackSkPaint(originalSkPaint); |
624 fStroke.applyToPaint(&fallbackSkPaint); | 649 fStyle.strokeRec().applyToPaint(&fallbackSkPaint); |
625 if (!fStroke.isFillStyle()) { | 650 if (!fStyle.isSimpleFill()) { |
626 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); | 651 fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fText
Ratio); |
627 } | 652 } |
628 | 653 |
629 fallbackTextContext->drawTextBlob(ctx, dc, pipelineBuilder->clip(), fall
backSkPaint, | 654 fallbackTextContext->drawTextBlob(ctx, dc, pipelineBuilder->clip(), fall
backSkPaint, |
630 viewMatrix, props, fFallbackTextBlob,
x, y, nullptr, | 655 viewMatrix, props, fFallbackTextBlob,
x, y, nullptr, |
631 clipBounds); | 656 clipBounds); |
632 } | 657 } |
633 } | 658 } |
634 | 659 |
635 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const { | 660 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const { |
636 if (!fDetachedGlyphCache) { | 661 if (!fDetachedGlyphCache) { |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
703 } | 728 } |
704 | 729 |
705 const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeed
ed(int *count) { | 730 const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeed
ed(int *count) { |
706 *count = fCount; | 731 *count = fCount; |
707 if (fCount) { | 732 if (fCount) { |
708 this->flush(); | 733 this->flush(); |
709 return fBuilder->build(); | 734 return fBuilder->build(); |
710 } | 735 } |
711 return nullptr; | 736 return nullptr; |
712 } | 737 } |
OLD | NEW |