SkSurface.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /*
  2. * Copyright 2012 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include <atomic>
  8. #include <cmath>
  9. #include "include/core/SkCanvas.h"
  10. #include "include/core/SkFontLCDConfig.h"
  11. #include "include/gpu/GrBackendSurface.h"
  12. #include "src/core/SkAutoPixmapStorage.h"
  13. #include "src/core/SkImagePriv.h"
  14. #include "src/image/SkSurface_Base.h"
  15. static SkPixelGeometry compute_default_geometry() {
  16. SkFontLCDConfig::LCDOrder order = SkFontLCDConfig::GetSubpixelOrder();
  17. if (SkFontLCDConfig::kNONE_LCDOrder == order) {
  18. return kUnknown_SkPixelGeometry;
  19. } else {
  20. // Bit0 is RGB(0), BGR(1)
  21. // Bit1 is H(0), V(1)
  22. const SkPixelGeometry gGeo[] = {
  23. kRGB_H_SkPixelGeometry,
  24. kBGR_H_SkPixelGeometry,
  25. kRGB_V_SkPixelGeometry,
  26. kBGR_V_SkPixelGeometry,
  27. };
  28. int index = 0;
  29. if (SkFontLCDConfig::kBGR_LCDOrder == order) {
  30. index |= 1;
  31. }
  32. if (SkFontLCDConfig::kVertical_LCDOrientation == SkFontLCDConfig::GetSubpixelOrientation()){
  33. index |= 2;
  34. }
  35. return gGeo[index];
  36. }
  37. }
  38. SkSurfaceProps::SkSurfaceProps() : fFlags(0), fPixelGeometry(kUnknown_SkPixelGeometry) {}
  39. SkSurfaceProps::SkSurfaceProps(InitType) : fFlags(0), fPixelGeometry(compute_default_geometry()) {}
  40. SkSurfaceProps::SkSurfaceProps(uint32_t flags, InitType)
  41. : fFlags(flags)
  42. , fPixelGeometry(compute_default_geometry())
  43. {}
  44. SkSurfaceProps::SkSurfaceProps(uint32_t flags, SkPixelGeometry pg)
  45. : fFlags(flags), fPixelGeometry(pg)
  46. {}
  47. SkSurfaceProps::SkSurfaceProps(const SkSurfaceProps& other)
  48. : fFlags(other.fFlags)
  49. , fPixelGeometry(other.fPixelGeometry)
  50. {}
  51. ///////////////////////////////////////////////////////////////////////////////
  52. SkSurface_Base::SkSurface_Base(int width, int height, const SkSurfaceProps* props)
  53. : INHERITED(width, height, props) {
  54. }
  55. SkSurface_Base::SkSurface_Base(const SkImageInfo& info, const SkSurfaceProps* props)
  56. : INHERITED(info, props) {
  57. }
  58. SkSurface_Base::~SkSurface_Base() {
  59. // in case the canvas outsurvives us, we null the callback
  60. if (fCachedCanvas) {
  61. fCachedCanvas->setSurfaceBase(nullptr);
  62. }
  63. }
  64. GrBackendTexture SkSurface_Base::onGetBackendTexture(BackendHandleAccess) {
  65. return GrBackendTexture(); // invalid
  66. }
  67. GrBackendRenderTarget SkSurface_Base::onGetBackendRenderTarget(BackendHandleAccess) {
  68. return GrBackendRenderTarget(); // invalid
  69. }
  70. bool SkSurface_Base::onReplaceBackendTexture(const GrBackendTexture&,
  71. GrSurfaceOrigin,
  72. TextureReleaseProc,
  73. ReleaseContext) {
  74. return false;
  75. }
  76. void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) {
  77. auto image = this->makeImageSnapshot();
  78. if (image) {
  79. canvas->drawImage(image, x, y, paint);
  80. }
  81. }
  82. void SkSurface_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
  83. SkSurface::RescaleGamma rescaleGamma,
  84. SkFilterQuality rescaleQuality,
  85. SkSurface::ReadPixelsCallback callback,
  86. SkSurface::ReadPixelsContext context) {
  87. int srcW = srcRect.width();
  88. int srcH = srcRect.height();
  89. float sx = (float)info.width() / srcW;
  90. float sy = (float)info.height() / srcH;
  91. // How many bilerp/bicubic steps to do in X and Y. + means upscaling, - means downscaling.
  92. int stepsX;
  93. int stepsY;
  94. if (rescaleQuality > kNone_SkFilterQuality) {
  95. stepsX = static_cast<int>((sx > 1.f) ? std::ceil(std::log2f(sx))
  96. : std::floor(std::log2f(sx)));
  97. stepsY = static_cast<int>((sy > 1.f) ? std::ceil(std::log2f(sy))
  98. : std::floor(std::log2f(sy)));
  99. } else {
  100. stepsX = sx != 1.f;
  101. stepsY = sy != 1.f;
  102. }
  103. SkPaint paint;
  104. paint.setBlendMode(SkBlendMode::kSrc);
  105. if (stepsX < 0 || stepsY < 0) {
  106. // Don't trigger MIP generation. We don't currently have a way to trigger bicubic for
  107. // downscaling draws.
  108. rescaleQuality = std::min(rescaleQuality, kLow_SkFilterQuality);
  109. }
  110. paint.setFilterQuality(rescaleQuality);
  111. sk_sp<SkSurface> src(SkRef(this));
  112. int srcX = srcRect.fLeft;
  113. int srcY = srcRect.fTop;
  114. SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
  115. // Assume we should ignore the rescale linear request if the surface has no color space since
  116. // it's unclear how we'd linearize from an unknown color space.
  117. if (rescaleGamma == SkSurface::RescaleGamma::kLinear &&
  118. this->getCanvas()->imageInfo().colorSpace() &&
  119. !this->getCanvas()->imageInfo().colorSpace()->gammaIsLinear()) {
  120. auto cs = this->getCanvas()->imageInfo().colorSpace()->makeLinearGamma();
  121. // Promote to F16 color type to preserve precision.
  122. auto ii = SkImageInfo::Make(srcW, srcH, kRGBA_F16_SkColorType,
  123. this->getCanvas()->imageInfo().alphaType(), std::move(cs));
  124. auto linearSurf = this->makeSurface(ii);
  125. if (!linearSurf) {
  126. // Maybe F16 isn't supported? Try again with original color type.
  127. ii = ii.makeColorType(this->getCanvas()->imageInfo().colorType());
  128. linearSurf = this->makeSurface(ii);
  129. if (!linearSurf) {
  130. callback(context, nullptr, 0);
  131. return;
  132. }
  133. }
  134. this->draw(linearSurf->getCanvas(), -srcX, -srcY, &paint);
  135. src = std::move(linearSurf);
  136. srcX = 0;
  137. srcY = 0;
  138. constraint = SkCanvas::kFast_SrcRectConstraint;
  139. }
  140. while (stepsX || stepsY) {
  141. int nextW = info.width();
  142. int nextH = info.height();
  143. if (stepsX < 0) {
  144. nextW = info.width() << (-stepsX - 1);
  145. stepsX++;
  146. } else if (stepsX != 0) {
  147. if (stepsX > 1) {
  148. nextW = srcW * 2;
  149. }
  150. --stepsX;
  151. }
  152. if (stepsY < 0) {
  153. nextH = info.height() << (-stepsY - 1);
  154. stepsY++;
  155. } else if (stepsY != 0) {
  156. if (stepsY > 1) {
  157. nextH = srcH * 2;
  158. }
  159. --stepsY;
  160. }
  161. auto ii = src->getCanvas()->imageInfo().makeWH(nextW, nextH);
  162. if (!stepsX && !stepsY) {
  163. // Might as well fold conversion to final info in the last step.
  164. ii = info;
  165. }
  166. auto next = this->makeSurface(ii);
  167. if (!next) {
  168. callback(context, nullptr, 0);
  169. return;
  170. }
  171. next->getCanvas()->drawImageRect(
  172. src->makeImageSnapshot(), SkIRect::MakeXYWH(srcX, srcY, srcW, srcH),
  173. SkRect::MakeWH((float)nextW, (float)nextH), &paint, constraint);
  174. src = std::move(next);
  175. srcX = srcY = 0;
  176. srcW = nextW;
  177. srcH = nextH;
  178. constraint = SkCanvas::kFast_SrcRectConstraint;
  179. }
  180. SkAutoPixmapStorage pm;
  181. pm.alloc(info);
  182. if (src->readPixels(pm, srcX, srcY)) {
  183. callback(context, pm.addr(), pm.rowBytes());
  184. } else {
  185. callback(context, nullptr, 0);
  186. }
  187. }
  188. void SkSurface_Base::onAsyncRescaleAndReadPixelsYUV420(
  189. SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
  190. int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
  191. ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
  192. // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
  193. // call client's callback.
  194. callback(context, nullptr, nullptr);
  195. }
  196. bool SkSurface_Base::outstandingImageSnapshot() const {
  197. return fCachedImage && !fCachedImage->unique();
  198. }
  199. void SkSurface_Base::aboutToDraw(ContentChangeMode mode) {
  200. this->dirtyGenerationID();
  201. SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
  202. if (fCachedImage) {
  203. // the surface may need to fork its backend, if its sharing it with
  204. // the cached image. Note: we only call if there is an outstanding owner
  205. // on the image (besides us).
  206. bool unique = fCachedImage->unique();
  207. if (!unique) {
  208. this->onCopyOnWrite(mode);
  209. }
  210. // regardless of copy-on-write, we must drop our cached image now, so
  211. // that the next request will get our new contents.
  212. fCachedImage.reset();
  213. if (unique) {
  214. // Our content isn't held by any image now, so we can consider that content mutable.
  215. // Raster surfaces need to be told it's safe to consider its pixels mutable again.
  216. // We make this call after the ->unref() so the subclass can assert there are no images.
  217. this->onRestoreBackingMutability();
  218. }
  219. } else if (kDiscard_ContentChangeMode == mode) {
  220. this->onDiscard();
  221. }
  222. }
  223. uint32_t SkSurface_Base::newGenerationID() {
  224. SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
  225. static std::atomic<uint32_t> nextID{1};
  226. return nextID++;
  227. }
  228. static SkSurface_Base* asSB(SkSurface* surface) {
  229. return static_cast<SkSurface_Base*>(surface);
  230. }
  231. static const SkSurface_Base* asConstSB(const SkSurface* surface) {
  232. return static_cast<const SkSurface_Base*>(surface);
  233. }
  234. ///////////////////////////////////////////////////////////////////////////////
  235. SkSurface::SkSurface(int width, int height, const SkSurfaceProps* props)
  236. : fProps(SkSurfacePropsCopyOrDefault(props)), fWidth(width), fHeight(height)
  237. {
  238. SkASSERT(fWidth > 0);
  239. SkASSERT(fHeight > 0);
  240. fGenerationID = 0;
  241. }
  242. SkSurface::SkSurface(const SkImageInfo& info, const SkSurfaceProps* props)
  243. : fProps(SkSurfacePropsCopyOrDefault(props)), fWidth(info.width()), fHeight(info.height())
  244. {
  245. SkASSERT(fWidth > 0);
  246. SkASSERT(fHeight > 0);
  247. fGenerationID = 0;
  248. }
  249. SkImageInfo SkSurface::imageInfo() {
  250. // TODO: do we need to go through canvas for this?
  251. return this->getCanvas()->imageInfo();
  252. }
  253. uint32_t SkSurface::generationID() {
  254. if (0 == fGenerationID) {
  255. fGenerationID = asSB(this)->newGenerationID();
  256. }
  257. return fGenerationID;
  258. }
  259. void SkSurface::notifyContentWillChange(ContentChangeMode mode) {
  260. asSB(this)->aboutToDraw(mode);
  261. }
  262. SkCanvas* SkSurface::getCanvas() {
  263. return asSB(this)->getCachedCanvas();
  264. }
  265. sk_sp<SkImage> SkSurface::makeImageSnapshot() {
  266. return asSB(this)->refCachedImage();
  267. }
  268. sk_sp<SkImage> SkSurface::makeImageSnapshot(const SkIRect& srcBounds) {
  269. const SkIRect surfBounds = { 0, 0, fWidth, fHeight };
  270. SkIRect bounds = srcBounds;
  271. if (!bounds.intersect(surfBounds)) {
  272. return nullptr;
  273. }
  274. SkASSERT(!bounds.isEmpty());
  275. if (bounds == surfBounds) {
  276. return this->makeImageSnapshot();
  277. } else {
  278. return asSB(this)->onNewImageSnapshot(&bounds);
  279. }
  280. }
  281. sk_sp<SkSurface> SkSurface::makeSurface(const SkImageInfo& info) {
  282. return asSB(this)->onNewSurface(info);
  283. }
  284. sk_sp<SkSurface> SkSurface::makeSurface(int width, int height) {
  285. return this->makeSurface(this->imageInfo().makeWH(width, height));
  286. }
  287. void SkSurface::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
  288. const SkPaint* paint) {
  289. return asSB(this)->onDraw(canvas, x, y, paint);
  290. }
  291. bool SkSurface::peekPixels(SkPixmap* pmap) {
  292. return this->getCanvas()->peekPixels(pmap);
  293. }
  294. bool SkSurface::readPixels(const SkPixmap& pm, int srcX, int srcY) {
  295. return this->getCanvas()->readPixels(pm, srcX, srcY);
  296. }
  297. bool SkSurface::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
  298. int srcX, int srcY) {
  299. return this->readPixels({dstInfo, dstPixels, dstRowBytes}, srcX, srcY);
  300. }
  301. bool SkSurface::readPixels(const SkBitmap& bitmap, int srcX, int srcY) {
  302. SkPixmap pm;
  303. return bitmap.peekPixels(&pm) && this->readPixels(pm, srcX, srcY);
  304. }
  305. void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
  306. RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
  307. ReadPixelsCallback callback, ReadPixelsContext context) {
  308. if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
  309. !SkImageInfoIsValid(info)) {
  310. callback(context, nullptr, 0);
  311. return;
  312. }
  313. asSB(this)->onAsyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality, callback,
  314. context);
  315. }
  316. void SkSurface::asyncRescaleAndReadPixelsYUV420(
  317. SkYUVColorSpace yuvColorSpace, sk_sp<SkColorSpace> dstColorSpace, const SkIRect& srcRect,
  318. int dstW, int dstH, RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
  319. ReadPixelsCallbackYUV420 callback, ReadPixelsContext context) {
  320. if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || (dstW & 0b1) ||
  321. (dstH & 0b1)) {
  322. callback(context, nullptr, nullptr);
  323. return;
  324. }
  325. asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace, std::move(dstColorSpace), srcRect,
  326. dstW, dstH, rescaleGamma, rescaleQuality,
  327. callback, context);
  328. }
  329. void SkSurface::writePixels(const SkPixmap& pmap, int x, int y) {
  330. if (pmap.addr() == nullptr || pmap.width() <= 0 || pmap.height() <= 0) {
  331. return;
  332. }
  333. const SkIRect srcR = SkIRect::MakeXYWH(x, y, pmap.width(), pmap.height());
  334. const SkIRect dstR = SkIRect::MakeWH(this->width(), this->height());
  335. if (SkIRect::Intersects(srcR, dstR)) {
  336. ContentChangeMode mode = kRetain_ContentChangeMode;
  337. if (srcR.contains(dstR)) {
  338. mode = kDiscard_ContentChangeMode;
  339. }
  340. asSB(this)->aboutToDraw(mode);
  341. asSB(this)->onWritePixels(pmap, x, y);
  342. }
  343. }
  344. void SkSurface::writePixels(const SkBitmap& src, int x, int y) {
  345. SkPixmap pm;
  346. if (src.peekPixels(&pm)) {
  347. this->writePixels(pm, x, y);
  348. }
  349. }
  350. GrBackendTexture SkSurface::getBackendTexture(BackendHandleAccess access) {
  351. return asSB(this)->onGetBackendTexture(access);
  352. }
  353. GrBackendRenderTarget SkSurface::getBackendRenderTarget(BackendHandleAccess access) {
  354. return asSB(this)->onGetBackendRenderTarget(access);
  355. }
  356. bool SkSurface::replaceBackendTexture(const GrBackendTexture& backendTexture,
  357. GrSurfaceOrigin origin,
  358. TextureReleaseProc textureReleaseProc,
  359. ReleaseContext releaseContext) {
  360. return asSB(this)->onReplaceBackendTexture(backendTexture, origin, textureReleaseProc,
  361. releaseContext);
  362. }
  363. void SkSurface::flush() {
  364. this->flush(BackendSurfaceAccess::kNoAccess, GrFlushInfo());
  365. }
  366. GrSemaphoresSubmitted SkSurface::flush(BackendSurfaceAccess access, const GrFlushInfo& flushInfo) {
  367. return asSB(this)->onFlush(access, flushInfo);
  368. }
  369. GrSemaphoresSubmitted SkSurface::flush(BackendSurfaceAccess access, GrFlushFlags flags,
  370. int numSemaphores, GrBackendSemaphore signalSemaphores[],
  371. GrGpuFinishedProc finishedProc,
  372. GrGpuFinishedContext finishedContext) {
  373. GrFlushInfo info;
  374. info.fFlags = flags;
  375. info.fNumSemaphores = numSemaphores;
  376. info.fSignalSemaphores = signalSemaphores;
  377. info.fFinishedProc = finishedProc;
  378. info.fFinishedContext = finishedContext;
  379. return this->flush(access, info);
  380. }
  381. GrSemaphoresSubmitted SkSurface::flush(BackendSurfaceAccess access, FlushFlags flags,
  382. int numSemaphores, GrBackendSemaphore signalSemaphores[]) {
  383. GrFlushFlags grFlags = flags == kSyncCpu_FlushFlag ? kSyncCpu_GrFlushFlag : kNone_GrFlushFlags;
  384. GrFlushInfo info;
  385. info.fFlags = grFlags;
  386. info.fNumSemaphores = numSemaphores;
  387. info.fSignalSemaphores = signalSemaphores;
  388. return this->flush(access, info);
  389. }
  390. GrSemaphoresSubmitted SkSurface::flushAndSignalSemaphores(int numSemaphores,
  391. GrBackendSemaphore signalSemaphores[]) {
  392. GrFlushInfo info;
  393. info.fNumSemaphores = numSemaphores;
  394. info.fSignalSemaphores = signalSemaphores;
  395. return this->flush(BackendSurfaceAccess::kNoAccess, info);
  396. }
  397. bool SkSurface::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
  398. return asSB(this)->onWait(numSemaphores, waitSemaphores);
  399. }
  400. bool SkSurface::characterize(SkSurfaceCharacterization* characterization) const {
  401. return asConstSB(this)->onCharacterize(characterization);
  402. }
  403. bool SkSurface::isCompatible(const SkSurfaceCharacterization& characterization) const {
  404. return asConstSB(this)->onIsCompatible(characterization);
  405. }
  406. bool SkSurface::draw(SkDeferredDisplayList* ddl) {
  407. return asSB(this)->onDraw(ddl);
  408. }
  409. //////////////////////////////////////////////////////////////////////////////////////
  410. #include "include/utils/SkNoDrawCanvas.h"
  411. class SkNullSurface : public SkSurface_Base {
  412. public:
  413. SkNullSurface(int width, int height) : SkSurface_Base(width, height, nullptr) {}
  414. protected:
  415. SkCanvas* onNewCanvas() override {
  416. return new SkNoDrawCanvas(this->width(), this->height());
  417. }
  418. sk_sp<SkSurface> onNewSurface(const SkImageInfo& info) override {
  419. return MakeNull(info.width(), info.height());
  420. }
  421. sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subsetOrNull) override { return nullptr; }
  422. void onWritePixels(const SkPixmap&, int x, int y) override {}
  423. void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {}
  424. void onCopyOnWrite(ContentChangeMode) override {}
  425. };
  426. sk_sp<SkSurface> SkSurface::MakeNull(int width, int height) {
  427. if (width < 1 || height < 1) {
  428. return nullptr;
  429. }
  430. return sk_sp<SkSurface>(new SkNullSurface(width, height));
  431. }
  432. //////////////////////////////////////////////////////////////////////////////////////
  433. #if !SK_SUPPORT_GPU
  434. sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext*, SkBudgeted, const SkImageInfo&, int,
  435. GrSurfaceOrigin, const SkSurfaceProps*, bool) {
  436. return nullptr;
  437. }
  438. sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrRecordingContext*, const SkSurfaceCharacterization&,
  439. SkBudgeted) {
  440. return nullptr;
  441. }
  442. sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext*, const GrBackendTexture&,
  443. GrSurfaceOrigin origin, int sampleCnt,
  444. SkColorType, sk_sp<SkColorSpace>,
  445. const SkSurfaceProps*,
  446. TextureReleaseProc, ReleaseContext) {
  447. return nullptr;
  448. }
  449. sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext*,
  450. const GrBackendRenderTarget&,
  451. GrSurfaceOrigin origin,
  452. SkColorType,
  453. sk_sp<SkColorSpace>,
  454. const SkSurfaceProps*,
  455. RenderTargetReleaseProc, ReleaseContext) {
  456. return nullptr;
  457. }
  458. sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext*,
  459. const GrBackendTexture&,
  460. GrSurfaceOrigin origin,
  461. int sampleCnt,
  462. SkColorType,
  463. sk_sp<SkColorSpace>,
  464. const SkSurfaceProps*) {
  465. return nullptr;
  466. }
  467. #endif