Revert "remove support for serialized pictures before aug 2019"

This reverts commit 380fba6e4b.

Reason for revert: try to fix android-pie-arm64-rel for chrome roll

Original change's description:
> remove support for serialized pictures before aug 2019
> 
> Change-Id: I9b2a2dbac4110665e06882b9cbbc6f59e6bc0c21
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/302397
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Reviewed-by: Florin Malita <fmalita@chromium.org>
> Commit-Queue: Mike Reed <reed@google.com>

TBR=bungeman@google.com,rmistry@google.com,fmalita@chromium.org,reed@google.com,michaelludwig@google.com

Change-Id: Iad0c262b1aaa966cfdb2756737b2460247f830dd
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/302679
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2020-07-15 00:10:23 +00:00
parent a56da9ee92
commit 59e1602a52
17 changed files with 310 additions and 18 deletions

View File

@ -701,6 +701,11 @@ sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
uint32_t flags = buffer.read32LE(0x3); // historically we only recorded 2 bits
bool respectCTM = !(flags & 1); // historically we stored ignoreCTM in low bit
if (buffer.isVersionLT(SkPicturePriv::kRemoveOccluderFromBlurMaskFilter)) {
SkRect unused;
buffer.readRect(&unused);
}
return SkMaskFilter::MakeBlur((SkBlurStyle)style, sigma, respectCTM);
}

View File

@ -53,7 +53,8 @@ sk_sp<SkFlattenable> SkColorFilter_Matrix::CreateProc(SkReadBuffer& buffer) {
return nullptr;
}
auto is_rgba = buffer.readBool();
auto is_rgba = buffer.isVersionLT(SkPicturePriv::kMatrixColorFilterDomain_Version) ||
buffer.readBool();
return is_rgba ? SkColorFilters::Matrix(matrix)
: SkColorFilters::HSLAMatrix(matrix);
}

View File

@ -20,6 +20,7 @@ enum {
// These count backwards from 0xFF, so as not to collide with the SFNT
// defines for names in its 'name' table.
kFontAxes = 0xFB,
kFontAxes_bad = 0xFC, // Broken negative axes, remove when MIN_PICTURE_VERSION > 62.
kFontIndex = 0xFD,
kSentinel = 0xFF,
};
@ -82,6 +83,15 @@ bool SkFontDescriptor::Deserialize(SkStream* stream, SkFontDescriptor* result) {
if (!stream->readS32(&axis[i])) { return false; }
}
break;
case kFontAxes_bad:
if (!stream->readPackedUInt(&axisCount)) { return false; }
axis.reset(axisCount);
for (size_t i = 0; i < axisCount; ++i) {
size_t packedAxis;
if (!stream->readPackedUInt(&packedAxis)) { return false; }
axis[i] = packedAxis;
}
break;
case kFontIndex:
if (!stream->readPackedUInt(&index)) { return false; }
break;

View File

@ -207,9 +207,62 @@ enum FlatFlags {
kFlatFlagMask = 0x3,
};
enum BitsPerField {
kFlags_BPF = 16,
kHint_BPF = 2,
kFilter_BPF = 2,
kFlatFlags_BPF = 3,
};
static inline int BPF_Mask(int bits) {
return (1 << bits) - 1;
}
// SkPaint originally defined flags, some of which now apply to SkFont. These are renames
// of those flags, split into categories depending on which objects they (now) apply to.
enum PaintFlagsForPaint {
kAA_PaintFlagForPaint = 0x01,
kDither_PaintFlagForPaint = 0x04,
};
enum PaintFlagsForFont {
kFakeBold_PaintFlagForFont = 0x20,
kLinear_PaintFlagForFont = 0x40,
kSubpixel_PaintFlagForFont = 0x80,
kLCD_PaintFlagForFont = 0x200,
kEmbeddedBitmap_PaintFlagForFont = 0x400,
kAutoHinting_PaintFlagForFont = 0x800,
};
static FlatFlags unpack_paint_flags(SkPaint* paint, uint32_t packed, SkFont* font) {
uint32_t f = packed >> 16;
paint->setAntiAlias((f & kAA_PaintFlagForPaint) != 0);
paint->setDither((f & kDither_PaintFlagForPaint) != 0);
if (font) {
font->setEmbolden((f & kFakeBold_PaintFlagForFont) != 0);
font->setLinearMetrics((f & kLinear_PaintFlagForFont) != 0);
font->setSubpixel((f & kSubpixel_PaintFlagForFont) != 0);
font->setEmbeddedBitmaps((f & kEmbeddedBitmap_PaintFlagForFont) != 0);
font->setForceAutoHinting((f & kAutoHinting_PaintFlagForFont) != 0);
font->setHinting((SkFontHinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
if (f & kAA_PaintFlagForPaint) {
if (f & kLCD_PaintFlagForFont) {
font->setEdging(SkFont::Edging::kSubpixelAntiAlias);
} else {
font->setEdging(SkFont::Edging::kAntiAlias);
}
} else {
font->setEdging(SkFont::Edging::kAlias);
}
}
paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF)));
return (FlatFlags)(packed & kFlatFlagMask);
}
template <typename T> uint32_t shift_bits(T value, unsigned shift, unsigned bits) {
SkASSERT(shift + bits <= 32);
uint32_t v = static_cast<uint32_t>(value);
@ -287,7 +340,74 @@ void SkPaintPriv::Flatten(const SkPaint& paint, SkWriteBuffer& buffer) {
}
}
SkReadPaintResult SkPaintPriv::Unflatten_PreV68(SkPaint* paint, SkReadBuffer& buffer, SkFont* font) {
SkSafeRange safe;
{
SkScalar sz = buffer.readScalar();
SkScalar sx = buffer.readScalar();
SkScalar kx = buffer.readScalar();
if (font) {
font->setSize(sz);
font->setScaleX(sx);
font->setSkewX(kx);
}
}
paint->setStrokeWidth(buffer.readScalar());
paint->setStrokeMiter(buffer.readScalar());
if (buffer.isVersionLT(SkPicturePriv::kFloat4PaintColor_Version)) {
paint->setColor(buffer.readColor());
} else {
SkColor4f color;
buffer.readColor4f(&color);
paint->setColor(color, sk_srgb_singleton());
}
unsigned flatFlags = unpack_paint_flags(paint, buffer.readUInt(), font);
uint32_t tmp = buffer.readUInt();
paint->setStrokeCap(safe.checkLE((tmp >> 24) & 0xFF, SkPaint::kLast_Cap));
paint->setStrokeJoin(safe.checkLE((tmp >> 16) & 0xFF, SkPaint::kLast_Join));
paint->setStyle(safe.checkLE((tmp >> 12) & 0xF, SkPaint::kStrokeAndFill_Style));
paint->setBlendMode(safe.checkLE(tmp & 0xFF, SkBlendMode::kLastMode));
sk_sp<SkTypeface> tf;
if (flatFlags & kHasTypeface_FlatFlag) {
tf = buffer.readTypeface();
}
if (font) {
font->setTypeface(tf);
}
if (flatFlags & kHasEffects_FlatFlag) {
paint->setPathEffect(buffer.readPathEffect());
paint->setShader(buffer.readShader());
paint->setMaskFilter(buffer.readMaskFilter());
paint->setColorFilter(buffer.readColorFilter());
(void)buffer.read32(); // use to be SkRasterizer
(void)buffer.read32(); // used to be drawlooper
paint->setImageFilter(buffer.readImageFilter());
} else {
paint->setPathEffect(nullptr);
paint->setShader(nullptr);
paint->setMaskFilter(nullptr);
paint->setColorFilter(nullptr);
paint->setImageFilter(nullptr);
}
if (!buffer.validate(safe)) {
paint->reset();
return kFailed_ReadPaint;
}
return kSuccess_PaintAndFont;
}
SkReadPaintResult SkPaintPriv::Unflatten(SkPaint* paint, SkReadBuffer& buffer, SkFont* font) {
if (buffer.isVersionLT(SkPicturePriv::kPaintDoesntSerializeFonts_Version)) {
return Unflatten_PreV68(paint, buffer, font);
}
SkSafeRange safe;
paint->setStrokeWidth(buffer.readScalar());

View File

@ -73,6 +73,9 @@ public:
// Since we may be filtering now, we need to know what color space to filter in,
// typically the color space of the device we're drawing into.
static void RemoveColorFilter(SkPaint*, SkColorSpace* dstCS);
private:
static SkReadPaintResult Unflatten_PreV68(SkPaint* paint, SkReadBuffer& buffer, SkFont*);
};
#endif

View File

@ -84,6 +84,9 @@ bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
if (!stream->readScalar(&info.fCullRect.fTop )) { return false; }
if (!stream->readScalar(&info.fCullRect.fRight )) { return false; }
if (!stream->readScalar(&info.fCullRect.fBottom)) { return false; }
if (info.getVersion() < SkPicturePriv::kRemoveHeaderFlags_Version) {
if (!stream->readU32(nullptr)) { return false; }
}
if (!IsValidPictInfo(info)) { return false; }
@ -103,6 +106,9 @@ bool SkPicture::BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
info.setVersion(buffer->readUInt());
buffer->readRect(&info.fCullRect);
if (info.getVersion() < SkPicturePriv::kRemoveHeaderFlags_Version) {
(void)buffer->readUInt(); // used to be flags
}
if (IsValidPictInfo(info)) {
if (pInfo) { *pInfo = info; }

View File

@ -274,7 +274,12 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
reader->readRect(&rect);
SkCanvas::QuadAAFlags aaFlags = static_cast<SkCanvas::QuadAAFlags>(reader->read32());
SkColor4f color;
reader->readColor4f(&color);
if (reader->isVersionLT(SkPicturePriv::kEdgeAAQuadColor4f_Version)) {
// Old version stored color as 8888
color = SkColor4f::FromColor(reader->read32());
} else {
reader->readColor4f(&color);
}
SkBlendMode blend = static_cast<SkBlendMode>(reader->read32());
bool hasClip = reader->readInt();
SkPoint* clip = nullptr;
@ -522,8 +527,16 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
reader->readPoint3(&rec.fZPlaneParams);
reader->readPoint3(&rec.fLightPos);
rec.fLightRadius = reader->readScalar();
rec.fAmbientColor = reader->read32();
rec.fSpotColor = reader->read32();
if (reader->isVersionLT(SkPicturePriv::kTwoColorDrawShadow_Version)) {
SkScalar ambientAlpha = reader->readScalar();
SkScalar spotAlpha = reader->readScalar();
SkColor color = reader->read32();
rec.fAmbientColor = SkColorSetA(color, SkColorGetA(color)*ambientAlpha);
rec.fSpotColor = SkColorSetA(color, SkColorGetA(color)*spotAlpha);
} else {
rec.fAmbientColor = reader->read32();
rec.fSpotColor = reader->read32();
}
rec.fFlags = reader->read32();
BREAK_ON_READ_ERROR(reader);

View File

@ -79,15 +79,35 @@ public:
// V76: Add filtering enum to ImageShader
enum Version {
kTileModeInBlurImageFilter_Version = 56,
kTileInfoInSweepGradient_Version = 57,
k2PtConicalNoFlip_Version = 58,
kRemovePictureImageFilterLocalSpace = 59,
kRemoveHeaderFlags_Version = 60,
kTwoColorDrawShadow_Version = 61,
kDontNegateImageSize_Version = 62,
kStoreImageBounds_Version = 63,
kRemoveOccluderFromBlurMaskFilter = 64,
kFloat4PaintColor_Version = 65,
kSaveBehind_Version = 66,
kSerializeFonts_Version = 67,
kPaintDoesntSerializeFonts_Version = 68,
kCleanupImageFilterEnums_Version = 69,
kHideImageFilterImpls_Version = 70,
kUnifyErodeDilateImpls_Version = 71,
kMatrixColorFilterDomain_Version = 72,
kEdgeAAQuadColor4f_Version = 73,
kMorphologyTakesScalar_Version = 74,
kVerticesUseReadBuffer_Version = 75,
kFilterEnumInImageShader_Version = 76,
kFilterOptionsInImageShader_Version = 77,
// Only SKPs within the min/current picture version range (inclusive) can be read.
kMin_Version = kMorphologyTakesScalar_Version,
kMin_Version = kTileModeInBlurImageFilter_Version,
kCurrent_Version = kFilterOptionsInImageShader_Version
};
static_assert(kMin_Version <= 62, "Remove kFontAxes_bad from SkFontDescriptor.cpp");
};
#endif

View File

@ -290,8 +290,13 @@ uint32_t SkReadBuffer::getArrayCount() {
*/
sk_sp<SkImage> SkReadBuffer::readImage() {
SkIRect bounds;
this->readIRect(&bounds);
if (this->isVersionLT(SkPicturePriv::kStoreImageBounds_Version)) {
bounds.fLeft = bounds.fTop = 0;
bounds.fRight = this->read32();
bounds.fBottom = this->read32();
} else {
this->readIRect(&bounds);
}
const int width = bounds.width();
const int height = bounds.height();
if (width <= 0 || height <= 0) { // SkImage never has a zero dimension
@ -330,6 +335,10 @@ sk_sp<SkImage> SkReadBuffer::readImage() {
this->validate(false);
return nullptr;
}
if (this->isVersionLT(SkPicturePriv::kDontNegateImageSize_Version)) {
(void)this->read32(); // originX
(void)this->read32(); // originY
}
sk_sp<SkImage> image;
if (fProcs.fImageProc) {

View File

@ -714,7 +714,12 @@ sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) {
SkPoint offset;
reader.readPoint(&offset);
SkFont font;
SkFontPriv::Unflatten(&font, reader);
if (reader.isVersionLT(SkPicturePriv::kSerializeFonts_Version)) {
SkPaint paint;
reader.readPaint(&paint, &font);
} else {
SkFontPriv::Unflatten(&font, reader);
}
// Compute the expected size of the buffer and ensure we have enough to deserialize
// a run before allocating it.

View File

@ -104,7 +104,14 @@ sk_sp<SkFlattenable> SkBlurImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
SkScalar sigmaX = buffer.readScalar();
SkScalar sigmaY = buffer.readScalar();
SkTileMode tileMode = buffer.read32LE(SkTileMode::kLastTileMode);
SkTileMode tileMode;
if (buffer.isVersionLT(SkPicturePriv::kTileModeInBlurImageFilter_Version)) {
tileMode = SkTileMode::kDecal;
} else if (buffer.isVersionLT(SkPicturePriv::kCleanupImageFilterEnums_Version)) {
tileMode = to_sktilemode(buffer.read32LE(SkBlurImageFilter::kLast_TileMode));
} else {
tileMode = buffer.read32LE(SkTileMode::kLastTileMode);
}
static_assert(SkBlurImageFilter::kLast_TileMode == 2, "CreateProc");

View File

@ -160,9 +160,18 @@ void SkDisplacementMapEffect::RegisterFlattenables() {
sk_sp<SkFlattenable> SkDisplacementMapEffectImpl::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
SkColorChannel xsel = buffer.read32LE(SkColorChannel::kLastEnum);
SkColorChannel ysel = buffer.read32LE(SkColorChannel::kLastEnum);
SkScalar scale = buffer.readScalar();
SkColorChannel xsel, ysel;
if (buffer.isVersionLT(SkPicturePriv::kCleanupImageFilterEnums_Version)) {
xsel = convert_channel_type(buffer.read32LE(
SkDisplacementMapEffect::kLast_ChannelSelectorType));
ysel = convert_channel_type(buffer.read32LE(
SkDisplacementMapEffect::kLast_ChannelSelectorType));
} else {
xsel = buffer.read32LE(SkColorChannel::kLastEnum);
ysel = buffer.read32LE(SkColorChannel::kLastEnum);
}
SkScalar scale = buffer.readScalar();
return SkDisplacementMapEffect::Make(xsel, ysel, scale, common.getInput(0), common.getInput(1),
&common.cropRect());

View File

@ -234,7 +234,12 @@ sk_sp<SkFlattenable> SkMatrixConvolutionImageFilterImpl::CreateProc(SkReadBuffer
kernelOffset.fX = buffer.readInt();
kernelOffset.fY = buffer.readInt();
SkTileMode tileMode = buffer.read32LE(SkTileMode::kLastTileMode);
SkTileMode tileMode;
if (buffer.isVersionLT(SkPicturePriv::kCleanupImageFilterEnums_Version)) {
tileMode = to_sktilemode(buffer.read32LE(SkMatrixConvolutionImageFilter::kLast_TileMode));
} else {
tileMode = buffer.read32LE(SkTileMode::kLastTileMode);
}
bool convolveAlpha = buffer.readBool();
if (!buffer.isValid()) {

View File

@ -77,6 +77,17 @@ private:
friend void SkDilateImageFilter::RegisterFlattenables();
SK_FLATTENABLE_HOOKS(SkMorphologyImageFilterImpl)
// Historically the morphology op was implicitly encoded in the factory type used to decode
// the image filter, so provide backwards compatible functions for old SKPs.
static sk_sp<SkFlattenable> CreateProcWithType(SkReadBuffer&, const MorphType*);
static sk_sp<SkFlattenable> DilateCreateProc(SkReadBuffer& buffer) {
static const MorphType kType = MorphType::kDilate;
return CreateProcWithType(buffer, &kType);
}
static sk_sp<SkFlattenable> ErodeCreateProc(SkReadBuffer& buffer) {
static const MorphType kType = MorphType::kErode;
return CreateProcWithType(buffer, &kType);
}
MorphType fType;
SkSize fRadius;
@ -108,11 +119,19 @@ sk_sp<SkImageFilter> SkErodeImageFilter::Make(SkScalar radiusX, SkScalar radiusY
void SkDilateImageFilter::RegisterFlattenables() {
SK_REGISTER_FLATTENABLE(SkMorphologyImageFilterImpl);
// TODO (michaelludwig) - Remove after grace period for SKPs to stop using old names
SkFlattenable::Register("SkDilateImageFilter", SkMorphologyImageFilterImpl::DilateCreateProc);
SkFlattenable::Register(
"SkDilateImageFilterImpl", SkMorphologyImageFilterImpl::DilateCreateProc);
SkFlattenable::Register("SkErodeImageFilter", SkMorphologyImageFilterImpl::ErodeCreateProc);
SkFlattenable::Register("SkErodeImageFilterImpl", SkMorphologyImageFilterImpl::ErodeCreateProc);
}
///////////////////////////////////////////////////////////////////////////////
sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
// 'type' acts as a signal that old-style deserialization is required. It is temporary.
sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProcWithType(SkReadBuffer& buffer,
const MorphType* type) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
SkScalar width;
SkScalar height;
@ -124,7 +143,14 @@ sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProc(SkReadBuffer& buffe
height = buffer.readScalar();
}
MorphType filterType = buffer.read32LE(MorphType::kLastType);
MorphType filterType;
if (type) {
// The old create procs that have an associated op should only be used on old SKPs
SkASSERT(buffer.isVersionLT(SkPicturePriv::kUnifyErodeDilateImpls_Version));
filterType = *type;
} else {
filterType = buffer.read32LE(MorphType::kLastType);
}
if (filterType == MorphType::kDilate) {
return SkDilateImageFilter::Make(width, height, common.getInput(0), &common.cropRect());
@ -135,6 +161,11 @@ sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProc(SkReadBuffer& buffe
}
}
sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
// Pass null to have the create proc read the op from the buffer
return CreateProcWithType(buffer, nullptr);
}
void SkMorphologyImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeScalar(fRadius.fWidth);

View File

@ -66,6 +66,18 @@ void SkPictureImageFilter::RegisterFlattenables() {
///////////////////////////////////////////////////////////////////////////////////////////////////
enum PictureResolution {
kDeviceSpace_PictureResolution,
kLocalSpace_PictureResolution
};
static sk_sp<SkImageFilter> make_localspace_filter(sk_sp<SkPicture> pic, const SkRect& cropRect,
SkFilterQuality fq) {
SkISize dim = { SkScalarRoundToInt(cropRect.width()), SkScalarRoundToInt(cropRect.height()) };
auto img = SkImage::MakeFromPicture(std::move(pic), dim, nullptr, nullptr,
SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB());
return SkImageSource::Make(img, cropRect, cropRect, fq);
}
sk_sp<SkFlattenable> SkPictureImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
sk_sp<SkPicture> picture;
SkRect cropRect;
@ -75,6 +87,14 @@ sk_sp<SkFlattenable> SkPictureImageFilterImpl::CreateProc(SkReadBuffer& buffer)
}
buffer.readRect(&cropRect);
if (buffer.isVersionLT(SkPicturePriv::kRemovePictureImageFilterLocalSpace)) {
PictureResolution pictureResolution = buffer.checkRange<PictureResolution>(
kDeviceSpace_PictureResolution, kLocalSpace_PictureResolution);
if (kLocalSpace_PictureResolution == pictureResolution) {
return make_localspace_filter(std::move(picture), cropRect,
buffer.checkFilterQuality());
}
}
return SkPictureImageFilter::Make(std::move(picture), cropRect);
}

View File

@ -40,9 +40,13 @@ sk_sp<SkFlattenable> SkSweepGradient::CreateProc(SkReadBuffer& buffer) {
}
const SkPoint center = buffer.readPoint();
const auto tBias = buffer.readScalar(),
tScale = buffer.readScalar();
auto [startAngle, endAngle] = angles_from_t_coeff(tBias, tScale);
SkScalar startAngle = 0,
endAngle = 360;
if (!buffer.isVersionLT(SkPicturePriv::kTileInfoInSweepGradient_Version)) {
const auto tBias = buffer.readScalar(),
tScale = buffer.readScalar();
std::tie(startAngle, endAngle) = angles_from_t_coeff(tBias, tScale);
}
return SkGradientShader::MakeSweep(center.x(), center.y(), desc.fColors,
std::move(desc.fColorSpace), desc.fPos, desc.fCount,

View File

@ -136,6 +136,30 @@ sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer)
SkScalar r1 = buffer.readScalar();
SkScalar r2 = buffer.readScalar();
if (buffer.isVersionLT(SkPicturePriv::k2PtConicalNoFlip_Version) && buffer.readBool()) {
using std::swap;
// legacy flipped gradient
swap(c1, c2);
swap(r1, r2);
SkColor4f* colors = desc.mutableColors();
SkScalar* pos = desc.mutablePos();
const int last = desc.fCount - 1;
const int half = desc.fCount >> 1;
for (int i = 0; i < half; ++i) {
swap(colors[i], colors[last - i]);
if (pos) {
SkScalar tmp = pos[i];
pos[i] = SK_Scalar1 - pos[last - i];
pos[last - i] = SK_Scalar1 - tmp;
}
}
if (pos) {
if (desc.fCount & 1) {
pos[half] = SK_Scalar1 - pos[half];
}
}
}
if (!buffer.isValid()) {
return nullptr;
}