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