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
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkData.h"
10 #include "include/core/SkImageEncoder.h"
11 #include "include/core/SkImageFilter.h"
12 #include "include/core/SkImageGenerator.h"
13 #include "include/core/SkPicture.h"
14 #include "include/core/SkSurface.h"
15 #include "src/core/SkBitmapCache.h"
16 #include "src/core/SkCachedData.h"
17 #include "src/core/SkColorSpacePriv.h"
18 #include "src/core/SkImageFilterCache.h"
19 #include "src/core/SkImageFilter_Base.h"
20 #include "src/core/SkImagePriv.h"
21 #include "src/core/SkMipmap.h"
22 #include "src/core/SkMipmapBuilder.h"
23 #include "src/core/SkNextID.h"
24 #include "src/core/SkSpecialImage.h"
25 #include "src/image/SkImage_Base.h"
26 #include "src/image/SkReadPixelsRec.h"
27 #include "src/image/SkRescaleAndReadPixels.h"
28 #include "src/shaders/SkImageShader.h"
29
30 #if SK_SUPPORT_GPU
31 #include "include/gpu/GrBackendSurface.h"
32 #include "include/gpu/GrContextThreadSafeProxy.h"
33 #include "include/gpu/GrDirectContext.h"
34 #include "src/gpu/GrDirectContextPriv.h"
35 #include "src/gpu/GrFragmentProcessor.h"
36 #include "src/gpu/GrImageContextPriv.h"
37 #include "src/gpu/GrProxyProvider.h"
38 #include "src/gpu/GrRecordingContextPriv.h"
39 #include "src/gpu/SkGr.h"
40 #include "src/gpu/effects/GrBicubicEffect.h"
41 #include "src/gpu/effects/GrTextureEffect.h"
42 #include "src/image/SkImage_Gpu.h"
43 #endif
44
SkImage(const SkImageInfo & info,uint32_t uniqueID)45 SkImage::SkImage(const SkImageInfo& info, uint32_t uniqueID)
46 : fInfo(info)
47 #ifdef SUPPORT_OPAQUE_OPTIMIZATION
48 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID)
49 , fSupportOpaqueOpt(false) {
50 #else
51 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID) {
52 #endif
53 SkASSERT(info.width() > 0);
54 SkASSERT(info.height() > 0);
55 }
56
57 bool SkImage::peekPixels(SkPixmap* pm) const {
58 SkPixmap tmp;
59 if (!pm) {
60 pm = &tmp;
61 }
62 return as_IB(this)->onPeekPixels(pm);
63 }
64
65 bool SkImage::readPixels(GrDirectContext* dContext, const SkImageInfo& dstInfo, void* dstPixels,
66 size_t dstRowBytes, int srcX, int srcY, CachingHint chint) const {
67 return as_IB(this)->onReadPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
68 }
69
70 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
71 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels,
72 size_t dstRowBytes, int srcX, int srcY, CachingHint chint) const {
73 auto dContext = as_IB(this)->directContext();
74 return this->readPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
75 }
76 #endif
77
78 void SkImage::asyncRescaleAndReadPixels(const SkImageInfo& info,
79 const SkIRect& srcRect,
80 RescaleGamma rescaleGamma,
81 RescaleMode rescaleMode,
82 ReadPixelsCallback callback,
83 ReadPixelsContext context) const {
84 if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
85 !SkImageInfoIsValid(info)) {
86 callback(context, nullptr);
87 return;
88 }
89 as_IB(this)->onAsyncRescaleAndReadPixels(
90 info, srcRect, rescaleGamma, rescaleMode, callback, context);
91 }
92
93 void SkImage::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
94 sk_sp<SkColorSpace> dstColorSpace,
95 const SkIRect& srcRect,
96 const SkISize& dstSize,
97 RescaleGamma rescaleGamma,
98 RescaleMode rescaleMode,
99 ReadPixelsCallback callback,
100 ReadPixelsContext context) const {
101 if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
102 (dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
103 callback(context, nullptr);
104 return;
105 }
106 as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
107 std::move(dstColorSpace),
108 srcRect,
109 dstSize,
110 rescaleGamma,
111 rescaleMode,
112 callback,
113 context);
114 }
115
116 bool SkImage::scalePixels(const SkPixmap& dst, const SkSamplingOptions& sampling,
117 CachingHint chint) const {
118 // Context TODO: Elevate GrDirectContext requirement to public API.
119 auto dContext = as_IB(this)->directContext();
120 if (this->width() == dst.width() && this->height() == dst.height()) {
121 return this->readPixels(dContext, dst, 0, 0, chint);
122 }
123
124 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
125 // can scale more efficiently) we should take advantage of it here.
126 //
127 SkBitmap bm;
128 if (as_IB(this)->getROPixels(dContext, &bm, chint)) {
129 SkPixmap pmap;
130 // Note: By calling the pixmap scaler, we never cache the final result, so the chint
131 // is (currently) only being applied to the getROPixels. If we get a request to
132 // also attempt to cache the final (scaled) result, we would add that logic here.
133 //
134 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, sampling);
135 }
136 return false;
137 }
138
139 ///////////////////////////////////////////////////////////////////////////////////////////////////
140
141 SkColorType SkImage::colorType() const { return fInfo.colorType(); }
142
143 SkAlphaType SkImage::alphaType() const { return fInfo.alphaType(); }
144
145 SkColorSpace* SkImage::colorSpace() const { return fInfo.colorSpace(); }
146
147 sk_sp<SkColorSpace> SkImage::refColorSpace() const { return fInfo.refColorSpace(); }
148
149 sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
150 const SkSamplingOptions& sampling,
151 const SkMatrix* localMatrix) const {
152 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
153 sampling, localMatrix);
154 }
155
156 sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
157 // Context TODO: Elevate GrDirectContext requirement to public API.
158 auto dContext = as_IB(this)->directContext();
159 SkBitmap bm;
160 if (as_IB(this)->getROPixels(dContext, &bm)) {
161 return SkEncodeBitmap(bm, type, quality);
162 }
163 return nullptr;
164 }
165
166 sk_sp<SkData> SkImage::encodeToData() const {
167 if (auto encoded = this->refEncodedData()) {
168 return encoded;
169 }
170
171 return this->encodeToData(SkEncodedImageFormat::kPNG, 100);
172 }
173
174 sk_sp<SkData> SkImage::refEncodedData() const {
175 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
176 }
177
178 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded) {
179 if (nullptr == encoded || 0 == encoded->size()) {
180 return nullptr;
181 }
182 return SkImage::MakeFromGenerator(SkImageGenerator::MakeFromEncoded(std::move(encoded)));
183 }
184
185 ///////////////////////////////////////////////////////////////////////////////////////////////////
186
187 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset, GrDirectContext* direct) const {
188 if (subset.isEmpty()) {
189 return nullptr;
190 }
191
192 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
193 if (!bounds.contains(subset)) {
194 return nullptr;
195 }
196
197 #if SK_SUPPORT_GPU
198 auto myContext = as_IB(this)->context();
199 // This check is also performed in the subclass, but we do it here for the short-circuit below.
200 if (myContext && !myContext->priv().matches(direct)) {
201 return nullptr;
202 }
203 #endif
204
205 // optimization : return self if the subset == our bounds
206 if (bounds == subset) {
207 return sk_ref_sp(const_cast<SkImage*>(this));
208 }
209
210 return as_IB(this)->onMakeSubset(subset, direct);
211 }
212
213 #if SK_SUPPORT_GPU
214
215 bool SkImage::isTextureBacked() const { return as_IB(this)->onIsTextureBacked(); }
216
217 size_t SkImage::textureSize() const { return as_IB(this)->onTextureSize(); }
218
219 GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
220 GrSurfaceOrigin* origin) const {
221 return as_IB(this)->onGetBackendTexture(flushPendingGrContextIO, origin);
222 }
223
224 bool SkImage::isValid(GrRecordingContext* rContext) const {
225 if (rContext && rContext->abandoned()) {
226 return false;
227 }
228 return as_IB(this)->onIsValid(rContext);
229 }
230
231 GrSemaphoresSubmitted SkImage::flush(GrDirectContext* dContext,
232 const GrFlushInfo& flushInfo) const {
233 return as_IB(this)->onFlush(dContext, flushInfo);
234 }
235
236 void SkImage::flushAndSubmit(GrDirectContext* dContext) const {
237 this->flush(dContext, {});
238 dContext->submit();
239 }
240
241 #else
242
243 bool SkImage::isTextureBacked() const { return false; }
244
245 bool SkImage::isValid(GrRecordingContext* rContext) const {
246 if (rContext) {
247 return false;
248 }
249 return as_IB(this)->onIsValid(nullptr);
250 }
251
252 #endif
253
254 ///////////////////////////////////////////////////////////////////////////////
255
256 SkImage_Base::SkImage_Base(const SkImageInfo& info, uint32_t uniqueID)
257 : INHERITED(info, uniqueID), fAddedToRasterCache(false) {}
258
259 SkImage_Base::~SkImage_Base() {
260 if (fAddedToRasterCache.load()) {
261 SkNotifyBitmapGenIDIsStale(this->uniqueID());
262 }
263 }
264
265 void SkImage_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
266 const SkIRect& origSrcRect,
267 RescaleGamma rescaleGamma,
268 RescaleMode rescaleMode,
269 ReadPixelsCallback callback,
270 ReadPixelsContext context) const {
271 SkBitmap src;
272 SkPixmap peek;
273 SkIRect srcRect;
274 if (this->peekPixels(&peek)) {
275 src.installPixels(peek);
276 srcRect = origSrcRect;
277 } else {
278 // Context TODO: Elevate GrDirectContext requirement to public API.
279 auto dContext = as_IB(this)->directContext();
280 src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
281 src.allocPixels();
282 if (!this->readPixels(dContext, src.pixmap(), origSrcRect.x(), origSrcRect.y())) {
283 callback(context, nullptr);
284 return;
285 }
286 srcRect = SkIRect::MakeSize(src.dimensions());
287 }
288 return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleMode, callback, context);
289 }
290
291 void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
292 sk_sp<SkColorSpace> dstColorSpace,
293 const SkIRect& srcRect,
294 const SkISize& dstSize,
295 RescaleGamma,
296 RescaleMode,
297 ReadPixelsCallback callback,
298 ReadPixelsContext context) const {
299 // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
300 // call client's callback.
301 callback(context, nullptr);
302 }
303
304 #if SK_SUPPORT_GPU
305 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Base::asView(GrRecordingContext* context,
306 GrMipmapped mipmapped,
307 GrImageTexGenPolicy policy) const {
308 if (!context) {
309 return {};
310 }
311 if (!context->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1) {
312 mipmapped = GrMipmapped::kNo;
313 }
314 return this->onAsView(context, mipmapped, policy);
315 }
316
317 std::unique_ptr<GrFragmentProcessor> SkImage_Base::asFragmentProcessor(
318 GrRecordingContext* rContext,
319 SkSamplingOptions sampling,
320 const SkTileMode tileModes[2],
321 const SkMatrix& m,
322 const SkRect* subset,
323 const SkRect* domain) const {
324 if (!rContext) {
325 return {};
326 }
327 if (sampling.useCubic && !GrValidCubicResampler(sampling.cubic)) {
328 return {};
329 }
330 if (sampling.mipmap != SkMipmapMode::kNone &&
331 (!rContext->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1)) {
332 sampling = SkSamplingOptions(sampling.filter);
333 }
334 return this->onAsFragmentProcessor(rContext, sampling, tileModes, m, subset, domain);
335 }
336
337 std::unique_ptr<GrFragmentProcessor> SkImage_Base::MakeFragmentProcessorFromView(
338 GrRecordingContext* rContext,
339 GrSurfaceProxyView view,
340 SkAlphaType at,
341 SkSamplingOptions sampling,
342 const SkTileMode tileModes[2],
343 const SkMatrix& m,
344 const SkRect* subset,
345 const SkRect* domain) {
346 if (!view) {
347 return nullptr;
348 }
349 const GrCaps& caps = *rContext->priv().caps();
350 auto wmx = SkTileModeToWrapMode(tileModes[0]);
351 auto wmy = SkTileModeToWrapMode(tileModes[1]);
352 if (sampling.useCubic) {
353 if (subset) {
354 if (domain) {
355 return GrBicubicEffect::MakeSubset(std::move(view),
356 at,
357 m,
358 wmx,
359 wmy,
360 *subset,
361 *domain,
362 sampling.cubic,
363 GrBicubicEffect::Direction::kXY,
364 *rContext->priv().caps());
365 }
366 return GrBicubicEffect::MakeSubset(std::move(view),
367 at,
368 m,
369 wmx,
370 wmy,
371 *subset,
372 sampling.cubic,
373 GrBicubicEffect::Direction::kXY,
374 *rContext->priv().caps());
375 }
376 return GrBicubicEffect::Make(std::move(view),
377 at,
378 m,
379 wmx,
380 wmy,
381 sampling.cubic,
382 GrBicubicEffect::Direction::kXY,
383 *rContext->priv().caps());
384 }
385 if (view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kNo) {
386 sampling = SkSamplingOptions(sampling.filter);
387 }
388 GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);
389 if (subset) {
390 if (domain) {
391 return GrTextureEffect::MakeSubset(std::move(view),
392 at,
393 m,
394 sampler,
395 *subset,
396 *domain,
397 caps);
398 }
399 return GrTextureEffect::MakeSubset(std::move(view),
400 at,
401 m,
402 sampler,
403 *subset,
404 caps);
405 } else {
406 return GrTextureEffect::Make(std::move(view), at, m, sampler, caps);
407 }
408 }
409
410 GrSurfaceProxyView SkImage_Base::FindOrMakeCachedMipmappedView(GrRecordingContext* rContext,
411 GrSurfaceProxyView view,
412 uint32_t imageUniqueID) {
413 SkASSERT(rContext);
414 SkASSERT(imageUniqueID != SK_InvalidUniqueID);
415
416 if (!view || view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kYes) {
417 return view;
418 }
419 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
420
421 GrUniqueKey baseKey;
422 GrMakeKeyFromImageID(&baseKey, imageUniqueID, SkIRect::MakeSize(view.dimensions()));
423 SkASSERT(baseKey.isValid());
424 GrUniqueKey mipmappedKey;
425 static const GrUniqueKey::Domain kMipmappedDomain = GrUniqueKey::GenerateDomain();
426 { // No extra values beyond the domain are required. Must name the var to please
427 // clang-tidy.
428 GrUniqueKey::Builder b(&mipmappedKey, baseKey, kMipmappedDomain, 0);
429 }
430 SkASSERT(mipmappedKey.isValid());
431 if (sk_sp<GrTextureProxy> cachedMippedView =
432 proxyProvider->findOrCreateProxyByUniqueKey(mipmappedKey)) {
433 return {std::move(cachedMippedView), view.origin(), view.swizzle()};
434 }
435
436 auto copy = GrCopyBaseMipMapToView(rContext, view);
437 if (!copy) {
438 return view;
439 }
440 // TODO: If we move listeners up from SkImage_Lazy to SkImage_Base then add one here.
441 proxyProvider->assignUniqueKeyToProxy(mipmappedKey, copy.asTextureProxy());
442 return copy;
443 }
444
445 GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
446 GrSurfaceOrigin* origin) const {
447 return GrBackendTexture(); // invalid
448 }
449
450 #endif // SK_SUPPORT_GPU
451
452 GrDirectContext* SkImage_Base::directContext() const {
453 #if SK_SUPPORT_GPU
454 return GrAsDirectContext(this->context());
455 #else
456 return nullptr;
457 #endif
458 }
459
460 bool SkImage::readPixels(GrDirectContext* dContext, const SkPixmap& pmap, int srcX, int srcY,
461 CachingHint chint) const {
462 return this->readPixels(dContext, pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX,
463 srcY, chint);
464 }
465
466 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
467 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
468 auto dContext = as_IB(this)->directContext();
469 return this->readPixels(dContext, pmap, srcX, srcY, chint);
470 }
471 #endif
472
473 ///////////////////////////////////////////////////////////////////////////////////////////////////
474
475 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
476 if (!bm.pixelRef()) {
477 return nullptr;
478 }
479
480 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
481 }
482
483 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
484 // Context TODO: Elevate GrDirectContext requirement to public API.
485 auto dContext = as_IB(this)->directContext();
486 return as_IB(this)->onAsLegacyBitmap(dContext, bitmap);
487 }
488
489 bool SkImage_Base::onAsLegacyBitmap(GrDirectContext* dContext, SkBitmap* bitmap) const {
490 // As the base-class, all we can do is make a copy (regardless of mode).
491 // Subclasses that want to be more optimal should override.
492 SkImageInfo info = fInfo.makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
493 if (!bitmap->tryAllocPixels(info)) {
494 return false;
495 }
496
497 if (!this->readPixels(dContext, bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
498 0, 0)) {
499 bitmap->reset();
500 return false;
501 }
502
503 bitmap->setImmutable();
504 return true;
505 }
506
507 sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
508 const SkMatrix* matrix, const SkPaint* paint,
509 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
510 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture),
511 matrix, paint, bitDepth,
512 std::move(colorSpace)));
513 }
514
515 sk_sp<SkImage> SkImage::makeWithFilter(GrRecordingContext* rContext, const SkImageFilter* filter,
516 const SkIRect& subset, const SkIRect& clipBounds,
517 SkIRect* outSubset, SkIPoint* offset) const {
518
519 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
520 return nullptr;
521 }
522 sk_sp<SkSpecialImage> srcSpecialImage;
523 #if SK_SUPPORT_GPU
524 auto myContext = as_IB(this)->context();
525 if (myContext && !myContext->priv().matches(rContext)) {
526 return nullptr;
527 }
528 srcSpecialImage = SkSpecialImage::MakeFromImage(rContext, subset,
529 sk_ref_sp(const_cast<SkImage*>(this)),
530 SkSurfaceProps());
531 #else
532 srcSpecialImage = SkSpecialImage::MakeFromImage(nullptr, subset,
533 sk_ref_sp(const_cast<SkImage*>(this)),
534 SkSurfaceProps());
535 #endif
536 if (!srcSpecialImage) {
537 return nullptr;
538 }
539
540 sk_sp<SkImageFilterCache> cache(
541 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
542
543 // The filters operate in the local space of the src image, where (0,0) corresponds to the
544 // subset's top left corner. But the clip bounds and any crop rects on the filters are in the
545 // original coordinate system, so configure the CTM to correct crop rects and explicitly adjust
546 // the clip bounds (since it is assumed to already be in image space).
547 SkImageFilter_Base::Context context(SkMatrix::Translate(-subset.x(), -subset.y()),
548 clipBounds.makeOffset(-subset.topLeft()),
549 cache.get(), fInfo.colorType(), fInfo.colorSpace(),
550 srcSpecialImage.get());
551
552 sk_sp<SkSpecialImage> result = as_IFB(filter)->filterImage(context).imageAndOffset(offset);
553 if (!result) {
554 return nullptr;
555 }
556
557 // The output image and offset are relative to the subset rectangle, so the offset needs to
558 // be shifted to put it in the correct spot with respect to the original coordinate system
559 offset->fX += subset.x();
560 offset->fY += subset.y();
561
562 // Final clip against the exact clipBounds (the clip provided in the context gets adjusted
563 // to account for pixel-moving filters so doesn't always exactly match when finished). The
564 // clipBounds are translated into the clippedDstRect coordinate space, including the
565 // result->subset() ensures that the result's image pixel origin does not affect results.
566 SkIRect dstRect = result->subset();
567 SkIRect clippedDstRect = dstRect;
568 if (!clippedDstRect.intersect(clipBounds.makeOffset(result->subset().topLeft() - *offset))) {
569 return nullptr;
570 }
571
572 // Adjust the geometric offset if the top-left corner moved as well
573 offset->fX += (clippedDstRect.x() - dstRect.x());
574 offset->fY += (clippedDstRect.y() - dstRect.y());
575 *outSubset = clippedDstRect;
576 return result->asImage();
577 }
578
579 bool SkImage::isLazyGenerated() const {
580 return as_IB(this)->onIsLazyGenerated();
581 }
582
583 bool SkImage::isAlphaOnly() const { return SkColorTypeIsAlphaOnly(fInfo.colorType()); }
584
585 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target, GrDirectContext* direct) const {
586 return this->makeColorTypeAndColorSpace(this->colorType(), std::move(target), direct);
587 }
588
589 sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
590 sk_sp<SkColorSpace> targetColorSpace,
591 GrDirectContext* dContext) const {
592 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
593 return nullptr;
594 }
595
596 #if SK_SUPPORT_GPU
597 auto myContext = as_IB(this)->context();
598 // This check is also performed in the subclass, but we do it here for the short-circuit below.
599 if (myContext && !myContext->priv().matches(dContext)) {
600 return nullptr;
601 }
602 #endif
603
604 SkColorType colorType = this->colorType();
605 SkColorSpace* colorSpace = this->colorSpace();
606 if (!colorSpace) {
607 colorSpace = sk_srgb_singleton();
608 }
609 if (colorType == targetColorType &&
610 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
611 return sk_ref_sp(const_cast<SkImage*>(this));
612 }
613
614 return as_IB(this)->onMakeColorTypeAndColorSpace(targetColorType,
615 std::move(targetColorSpace), dContext);
616 }
617
618 sk_sp<SkImage> SkImage::reinterpretColorSpace(sk_sp<SkColorSpace> target) const {
619 if (!target) {
620 return nullptr;
621 }
622
623 // No need to create a new image if:
624 // (1) The color spaces are equal.
625 // (2) The color type is kAlpha8.
626 SkColorSpace* colorSpace = this->colorSpace();
627 if (!colorSpace) {
628 colorSpace = sk_srgb_singleton();
629 }
630 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
631 return sk_ref_sp(const_cast<SkImage*>(this));
632 }
633
634 return as_IB(this)->onReinterpretColorSpace(std::move(target));
635 }
636
637 void SkImage::dump(std::string& desc, int depth) const {
638 std::string split(depth, '\t');
639 desc += split + "\n SkImage:{ \n";
640 fInfo.dump(desc, depth + 1);
641 desc += split + "\t fUniqueID: " + std::to_string(fUniqueID) + "\n";
642 desc += split + "}\n";
643 }
644
645 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
646 if (!this->isTextureBacked()) {
647 return sk_ref_sp(const_cast<SkImage*>(this));
648 }
649 return this->makeRasterImage();
650 }
651
652 sk_sp<SkImage> SkImage::makeRasterImage(CachingHint chint) const {
653 SkPixmap pm;
654 if (this->peekPixels(&pm)) {
655 return sk_ref_sp(const_cast<SkImage*>(this));
656 }
657
658 const size_t rowBytes = fInfo.minRowBytes();
659 size_t size = fInfo.computeByteSize(rowBytes);
660 if (SkImageInfo::ByteSizeOverflowed(size)) {
661 return nullptr;
662 }
663
664 // Context TODO: Elevate GrDirectContext requirement to public API.
665 auto dContext = as_IB(this)->directContext();
666 sk_sp<SkData> data = SkData::MakeUninitialized(size);
667 pm = {fInfo.makeColorSpace(nullptr), data->writable_data(), fInfo.minRowBytes()};
668 if (!this->readPixels(dContext, pm, 0, 0, chint)) {
669 return nullptr;
670 }
671
672 return SkImage::MakeRasterData(fInfo, std::move(data), rowBytes);
673 }
674
675 bool SkImage_pinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
676 SkASSERT(image);
677 SkASSERT(rContext);
678 return as_IB(image)->onPinAsTexture(rContext);
679 }
680
681 void SkImage_unpinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
682 SkASSERT(image);
683 SkASSERT(rContext);
684 as_IB(image)->onUnpinAsTexture(rContext);
685 }
686
687 ///////////////////////////////////////////////////////////////////////////////////////////////////
688
689 SkMipmapBuilder::SkMipmapBuilder(const SkImageInfo& info) {
690 fMM = sk_sp<SkMipmap>(SkMipmap::Build({info, nullptr, 0}, nullptr, false));
691 }
692
693 SkMipmapBuilder::~SkMipmapBuilder() {}
694
695 int SkMipmapBuilder::countLevels() const {
696 return fMM ? fMM->countLevels() : 0;
697 }
698
699 SkPixmap SkMipmapBuilder::level(int index) const {
700 SkPixmap pm;
701
702 SkMipmap::Level level;
703 if (fMM && fMM->getLevel(index, &level)) {
704 pm = level.fPixmap;
705 }
706 return pm;
707 }
708
709 bool SkImage::hasMipmaps() const { return as_IB(this)->onHasMipmaps(); }
710
711 sk_sp<SkImage> SkImage::withMipmaps(sk_sp<SkMipmap> mips) const {
712 if (mips == nullptr || mips->validForRootLevel(this->imageInfo())) {
713 if (auto result = as_IB(this)->onMakeWithMipmaps(std::move(mips))) {
714 return result;
715 }
716 }
717 return sk_ref_sp((const_cast<SkImage*>(this)));
718 }
719
720 sk_sp<SkImage> SkImage::withDefaultMipmaps() const {
721 return this->withMipmaps(nullptr);
722 }
723
724 sk_sp<SkImage> SkMipmapBuilder::attachTo(const SkImage* src) {
725 return src->withMipmaps(fMM);
726 }
727
728 //////////////////////////////////////////////////////////////////////////////////////////////////
729
730 #include "src/core/SkReadBuffer.h"
731 #include "src/core/SkSamplingPriv.h"
732 #include "src/core/SkWriteBuffer.h"
733
734 SkSamplingOptions SkSamplingPriv::FromFQ(SkLegacyFQ fq, SkMediumAs behavior) {
735 switch (fq) {
736 case kHigh_SkLegacyFQ:
737 return SkSamplingOptions(SkCubicResampler{1/3.0f, 1/3.0f});
738 case kMedium_SkLegacyFQ:
739 return SkSamplingOptions(SkFilterMode::kLinear,
740 behavior == kNearest_SkMediumAs ? SkMipmapMode::kNearest
741 : SkMipmapMode::kLinear);
742 case kLow_SkLegacyFQ:
743 return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
744 case kNone_SkLegacyFQ:
745 break;
746 }
747 return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);
748 }
749
750 SkSamplingOptions SkSamplingPriv::Read(SkReadBuffer& buffer) {
751 if (buffer.readBool()) {
752 SkScalar B = buffer.readScalar(),
753 C = buffer.readScalar();
754 return SkSamplingOptions({B,C});
755 } else {
756 auto filter = buffer.read32LE<SkFilterMode>(SkFilterMode::kLinear);
757 auto mipmap = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
758 return SkSamplingOptions(filter, mipmap);
759 }
760 }
761
762 void SkSamplingPriv::Write(SkWriteBuffer& buffer, const SkSamplingOptions& sampling) {
763 buffer.writeBool(sampling.useCubic);
764 if (sampling.useCubic) {
765 buffer.writeScalar(sampling.cubic.B);
766 buffer.writeScalar(sampling.cubic.C);
767 } else {
768 buffer.writeUInt((unsigned)sampling.filter);
769 buffer.writeUInt((unsigned)sampling.mipmap);
770 }
771 }
772
773 #ifdef SUPPORT_OPAQUE_OPTIMIZATION
774 void SkImage::setSupportOpaqueOpt(bool supportOpaqueOpt) {
775 fSupportOpaqueOpt = supportOpaqueOpt;
776 }
777
778 bool SkImage::getSupportOpaqueOpt() const {
779 return fSupportOpaqueOpt;
780 }
781 #endif