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