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
makeRawShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const151 sk_sp<SkShader> SkImage::makeRawShader(SkTileMode tmx, SkTileMode tmy,
152 const SkSamplingOptions& sampling,
153 const SkMatrix* localMatrix) const {
154 return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
155 sampling, localMatrix);
156 }
157
encodeToData(SkEncodedImageFormat type,int quality) const158 sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
159 // Context TODO: Elevate GrDirectContext requirement to public API.
160 auto dContext = as_IB(this)->directContext();
161 SkBitmap bm;
162 if (as_IB(this)->getROPixels(dContext, &bm)) {
163 return SkEncodeBitmap(bm, type, quality);
164 }
165 return nullptr;
166 }
167
encodeToData() const168 sk_sp<SkData> SkImage::encodeToData() const {
169 if (auto encoded = this->refEncodedData()) {
170 return encoded;
171 }
172
173 return this->encodeToData(SkEncodedImageFormat::kPNG, 100);
174 }
175
refEncodedData() const176 sk_sp<SkData> SkImage::refEncodedData() const {
177 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
178 }
179
MakeFromEncoded(sk_sp<SkData> encoded,std::optional<SkAlphaType> alphaType)180 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded,
181 std::optional<SkAlphaType> alphaType) {
182 if (nullptr == encoded || 0 == encoded->size()) {
183 return nullptr;
184 }
185 return SkImage::MakeFromGenerator(
186 SkImageGenerator::MakeFromEncoded(std::move(encoded), alphaType));
187 }
188
189 ///////////////////////////////////////////////////////////////////////////////////////////////////
190
makeSubset(const SkIRect & subset,GrDirectContext * direct) const191 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset, GrDirectContext* direct) const {
192 if (subset.isEmpty()) {
193 return nullptr;
194 }
195
196 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
197 if (!bounds.contains(subset)) {
198 return nullptr;
199 }
200
201 #if SK_SUPPORT_GPU
202 auto myContext = as_IB(this)->context();
203 // This check is also performed in the subclass, but we do it here for the short-circuit below.
204 if (myContext && !myContext->priv().matches(direct)) {
205 return nullptr;
206 }
207 #endif
208
209 // optimization : return self if the subset == our bounds
210 if (bounds == subset) {
211 return sk_ref_sp(const_cast<SkImage*>(this));
212 }
213
214 return as_IB(this)->onMakeSubset(subset, direct);
215 }
216
217 #if SK_SUPPORT_GPU
218
isTextureBacked() const219 bool SkImage::isTextureBacked() const { return as_IB(this)->onIsTextureBacked(); }
220
textureSize() const221 size_t SkImage::textureSize() const { return as_IB(this)->onTextureSize(); }
222
getBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const223 GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
224 GrSurfaceOrigin* origin) const {
225 return as_IB(this)->onGetBackendTexture(flushPendingGrContextIO, origin);
226 }
227
isValid(GrRecordingContext * rContext) const228 bool SkImage::isValid(GrRecordingContext* rContext) const {
229 if (rContext && rContext->abandoned()) {
230 return false;
231 }
232 return as_IB(this)->onIsValid(rContext);
233 }
234
flush(GrDirectContext * dContext,const GrFlushInfo & flushInfo) const235 GrSemaphoresSubmitted SkImage::flush(GrDirectContext* dContext,
236 const GrFlushInfo& flushInfo) const {
237 return as_IB(this)->onFlush(dContext, flushInfo);
238 }
239
flushAndSubmit(GrDirectContext * dContext) const240 void SkImage::flushAndSubmit(GrDirectContext* dContext) const {
241 this->flush(dContext, {});
242 dContext->submit();
243 }
244
245 #else
246
isTextureBacked() const247 bool SkImage::isTextureBacked() const { return false; }
248
isValid(GrRecordingContext * rContext) const249 bool SkImage::isValid(GrRecordingContext* rContext) const {
250 if (rContext) {
251 return false;
252 }
253 return as_IB(this)->onIsValid(nullptr);
254 }
255
256 #endif
257
258 ///////////////////////////////////////////////////////////////////////////////
259
SkImage_Base(const SkImageInfo & info,uint32_t uniqueID)260 SkImage_Base::SkImage_Base(const SkImageInfo& info, uint32_t uniqueID)
261 : INHERITED(info, uniqueID), fAddedToRasterCache(false) {}
262
~SkImage_Base()263 SkImage_Base::~SkImage_Base() {
264 if (fAddedToRasterCache.load()) {
265 SkNotifyBitmapGenIDIsStale(this->uniqueID());
266 }
267 }
268
onAsyncRescaleAndReadPixels(const SkImageInfo & info,const SkIRect & origSrcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const269 void SkImage_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
270 const SkIRect& origSrcRect,
271 RescaleGamma rescaleGamma,
272 RescaleMode rescaleMode,
273 ReadPixelsCallback callback,
274 ReadPixelsContext context) const {
275 SkBitmap src;
276 SkPixmap peek;
277 SkIRect srcRect;
278 if (this->peekPixels(&peek)) {
279 src.installPixels(peek);
280 srcRect = origSrcRect;
281 } else {
282 // Context TODO: Elevate GrDirectContext requirement to public API.
283 auto dContext = as_IB(this)->directContext();
284 src.setInfo(this->imageInfo().makeDimensions(origSrcRect.size()));
285 src.allocPixels();
286 if (!this->readPixels(dContext, src.pixmap(), origSrcRect.x(), origSrcRect.y())) {
287 callback(context, nullptr);
288 return;
289 }
290 srcRect = SkIRect::MakeSize(src.dimensions());
291 }
292 return SkRescaleAndReadPixels(src, info, srcRect, rescaleGamma, rescaleMode, callback, context);
293 }
294
onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,RescaleGamma,RescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const295 void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace,
296 sk_sp<SkColorSpace> dstColorSpace,
297 const SkIRect& srcRect,
298 const SkISize& dstSize,
299 RescaleGamma,
300 RescaleMode,
301 ReadPixelsCallback callback,
302 ReadPixelsContext context) const {
303 // TODO: Call non-YUV asyncRescaleAndReadPixels and then make our callback convert to YUV and
304 // call client's callback.
305 callback(context, nullptr);
306 }
307
308 #if SK_SUPPORT_GPU
asView(GrRecordingContext * context,GrMipmapped mipmapped,GrImageTexGenPolicy policy) const309 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Base::asView(GrRecordingContext* context,
310 GrMipmapped mipmapped,
311 GrImageTexGenPolicy policy) const {
312 if (!context) {
313 return {};
314 }
315 if (!context->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1) {
316 mipmapped = GrMipmapped::kNo;
317 }
318 return this->onAsView(context, mipmapped, policy);
319 }
320
asFragmentProcessor(GrRecordingContext * rContext,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain) const321 std::unique_ptr<GrFragmentProcessor> SkImage_Base::asFragmentProcessor(
322 GrRecordingContext* rContext,
323 SkSamplingOptions sampling,
324 const SkTileMode tileModes[2],
325 const SkMatrix& m,
326 const SkRect* subset,
327 const SkRect* domain) const {
328 if (!rContext) {
329 return {};
330 }
331 if (sampling.useCubic && !GrValidCubicResampler(sampling.cubic)) {
332 return {};
333 }
334 if (sampling.mipmap != SkMipmapMode::kNone &&
335 (!rContext->priv().caps()->mipmapSupport() || this->dimensions().area() <= 1)) {
336 sampling = SkSamplingOptions(sampling.filter);
337 }
338 return this->onAsFragmentProcessor(rContext, sampling, tileModes, m, subset, domain);
339 }
340
MakeFragmentProcessorFromView(GrRecordingContext * rContext,GrSurfaceProxyView view,SkAlphaType at,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain)341 std::unique_ptr<GrFragmentProcessor> SkImage_Base::MakeFragmentProcessorFromView(
342 GrRecordingContext* rContext,
343 GrSurfaceProxyView view,
344 SkAlphaType at,
345 SkSamplingOptions sampling,
346 const SkTileMode tileModes[2],
347 const SkMatrix& m,
348 const SkRect* subset,
349 const SkRect* domain) {
350 if (!view) {
351 return nullptr;
352 }
353 const GrCaps& caps = *rContext->priv().caps();
354 auto wmx = SkTileModeToWrapMode(tileModes[0]);
355 auto wmy = SkTileModeToWrapMode(tileModes[1]);
356 if (sampling.useCubic) {
357 if (subset) {
358 if (domain) {
359 return GrBicubicEffect::MakeSubset(std::move(view),
360 at,
361 m,
362 wmx,
363 wmy,
364 *subset,
365 *domain,
366 sampling.cubic,
367 GrBicubicEffect::Direction::kXY,
368 *rContext->priv().caps());
369 }
370 return GrBicubicEffect::MakeSubset(std::move(view),
371 at,
372 m,
373 wmx,
374 wmy,
375 *subset,
376 sampling.cubic,
377 GrBicubicEffect::Direction::kXY,
378 *rContext->priv().caps());
379 }
380 return GrBicubicEffect::Make(std::move(view),
381 at,
382 m,
383 wmx,
384 wmy,
385 sampling.cubic,
386 GrBicubicEffect::Direction::kXY,
387 *rContext->priv().caps());
388 }
389 if (view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kNo) {
390 sampling = SkSamplingOptions(sampling.filter);
391 }
392 GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);
393 if (subset) {
394 if (domain) {
395 return GrTextureEffect::MakeSubset(std::move(view),
396 at,
397 m,
398 sampler,
399 *subset,
400 *domain,
401 caps);
402 }
403 return GrTextureEffect::MakeSubset(std::move(view),
404 at,
405 m,
406 sampler,
407 *subset,
408 caps);
409 } else {
410 return GrTextureEffect::Make(std::move(view), at, m, sampler, caps);
411 }
412 }
413
FindOrMakeCachedMipmappedView(GrRecordingContext * rContext,GrSurfaceProxyView view,uint32_t imageUniqueID)414 GrSurfaceProxyView SkImage_Base::FindOrMakeCachedMipmappedView(GrRecordingContext* rContext,
415 GrSurfaceProxyView view,
416 uint32_t imageUniqueID) {
417 SkASSERT(rContext);
418 SkASSERT(imageUniqueID != SK_InvalidUniqueID);
419
420 if (!view || view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kYes) {
421 return view;
422 }
423 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
424
425 skgpu::UniqueKey baseKey;
426 GrMakeKeyFromImageID(&baseKey, imageUniqueID, SkIRect::MakeSize(view.dimensions()));
427 SkASSERT(baseKey.isValid());
428 skgpu::UniqueKey mipmappedKey;
429 static const skgpu::UniqueKey::Domain kMipmappedDomain = skgpu::UniqueKey::GenerateDomain();
430 { // No extra values beyond the domain are required. Must name the var to please
431 // clang-tidy.
432 skgpu::UniqueKey::Builder b(&mipmappedKey, baseKey, kMipmappedDomain, 0);
433 }
434 SkASSERT(mipmappedKey.isValid());
435 if (sk_sp<GrTextureProxy> cachedMippedView =
436 proxyProvider->findOrCreateProxyByUniqueKey(mipmappedKey)) {
437 return {std::move(cachedMippedView), view.origin(), view.swizzle()};
438 }
439
440 auto copy = GrCopyBaseMipMapToView(rContext, view);
441 if (!copy) {
442 return view;
443 }
444 // TODO: If we move listeners up from SkImage_Lazy to SkImage_Base then add one here.
445 proxyProvider->assignUniqueKeyToProxy(mipmappedKey, copy.asTextureProxy());
446 return copy;
447 }
448
onGetBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const449 GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
450 GrSurfaceOrigin* origin) const {
451 return GrBackendTexture(); // invalid
452 }
453
454 #endif // SK_SUPPORT_GPU
455
directContext() const456 GrDirectContext* SkImage_Base::directContext() const {
457 #if SK_SUPPORT_GPU
458 return GrAsDirectContext(this->context());
459 #else
460 return nullptr;
461 #endif
462 }
463
readPixels(GrDirectContext * dContext,const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const464 bool SkImage::readPixels(GrDirectContext* dContext, const SkPixmap& pmap, int srcX, int srcY,
465 CachingHint chint) const {
466 return this->readPixels(dContext, pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX,
467 srcY, chint);
468 }
469
470 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const471 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
472 auto dContext = as_IB(this)->directContext();
473 return this->readPixels(dContext, pmap, srcX, srcY, chint);
474 }
475 #endif
476
477 ///////////////////////////////////////////////////////////////////////////////////////////////////
478
MakeFromBitmap(const SkBitmap & bm)479 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
480 if (!bm.pixelRef()) {
481 return nullptr;
482 }
483
484 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
485 }
486
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode) const487 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
488 // Context TODO: Elevate GrDirectContext requirement to public API.
489 auto dContext = as_IB(this)->directContext();
490 return as_IB(this)->onAsLegacyBitmap(dContext, bitmap);
491 }
492
onAsLegacyBitmap(GrDirectContext * dContext,SkBitmap * bitmap) const493 bool SkImage_Base::onAsLegacyBitmap(GrDirectContext* dContext, SkBitmap* bitmap) const {
494 // As the base-class, all we can do is make a copy (regardless of mode).
495 // Subclasses that want to be more optimal should override.
496 SkImageInfo info = fInfo.makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
497 if (!bitmap->tryAllocPixels(info)) {
498 return false;
499 }
500
501 if (!this->readPixels(dContext, bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
502 0, 0)) {
503 bitmap->reset();
504 return false;
505 }
506
507 bitmap->setImmutable();
508 return true;
509 }
510
MakeFromPicture(sk_sp<SkPicture> picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint,BitDepth bitDepth,sk_sp<SkColorSpace> colorSpace)511 sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
512 const SkMatrix* matrix, const SkPaint* paint,
513 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
514 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture),
515 matrix, paint, bitDepth,
516 std::move(colorSpace)));
517 }
518
makeWithFilter(GrRecordingContext * rContext,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const519 sk_sp<SkImage> SkImage::makeWithFilter(GrRecordingContext* rContext, const SkImageFilter* filter,
520 const SkIRect& subset, const SkIRect& clipBounds,
521 SkIRect* outSubset, SkIPoint* offset) const {
522
523 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
524 return nullptr;
525 }
526 sk_sp<SkSpecialImage> srcSpecialImage;
527 #if SK_SUPPORT_GPU
528 auto myContext = as_IB(this)->context();
529 if (myContext && !myContext->priv().matches(rContext)) {
530 return nullptr;
531 }
532 srcSpecialImage = SkSpecialImage::MakeFromImage(rContext, subset,
533 sk_ref_sp(const_cast<SkImage*>(this)),
534 SkSurfaceProps());
535 #else
536 srcSpecialImage = SkSpecialImage::MakeFromImage(nullptr, subset,
537 sk_ref_sp(const_cast<SkImage*>(this)),
538 SkSurfaceProps());
539 #endif
540 if (!srcSpecialImage) {
541 return nullptr;
542 }
543
544 sk_sp<SkImageFilterCache> cache(
545 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
546
547 // The filters operate in the local space of the src image, where (0,0) corresponds to the
548 // subset's top left corner. But the clip bounds and any crop rects on the filters are in the
549 // original coordinate system, so configure the CTM to correct crop rects and explicitly adjust
550 // the clip bounds (since it is assumed to already be in image space).
551 SkImageFilter_Base::Context context(SkMatrix::Translate(-subset.x(), -subset.y()),
552 clipBounds.makeOffset(-subset.topLeft()),
553 cache.get(), fInfo.colorType(), fInfo.colorSpace(),
554 srcSpecialImage.get());
555
556 sk_sp<SkSpecialImage> result = as_IFB(filter)->filterImage(context).imageAndOffset(offset);
557 if (!result) {
558 return nullptr;
559 }
560
561 // The output image and offset are relative to the subset rectangle, so the offset needs to
562 // be shifted to put it in the correct spot with respect to the original coordinate system
563 offset->fX += subset.x();
564 offset->fY += subset.y();
565
566 // Final clip against the exact clipBounds (the clip provided in the context gets adjusted
567 // to account for pixel-moving filters so doesn't always exactly match when finished). The
568 // clipBounds are translated into the clippedDstRect coordinate space, including the
569 // result->subset() ensures that the result's image pixel origin does not affect results.
570 SkIRect dstRect = result->subset();
571 SkIRect clippedDstRect = dstRect;
572 if (!clippedDstRect.intersect(clipBounds.makeOffset(result->subset().topLeft() - *offset))) {
573 return nullptr;
574 }
575
576 // Adjust the geometric offset if the top-left corner moved as well
577 offset->fX += (clippedDstRect.x() - dstRect.x());
578 offset->fY += (clippedDstRect.y() - dstRect.y());
579 *outSubset = clippedDstRect;
580 return result->asImage();
581 }
582
isLazyGenerated() const583 bool SkImage::isLazyGenerated() const {
584 return as_IB(this)->onIsLazyGenerated();
585 }
586
isAlphaOnly() const587 bool SkImage::isAlphaOnly() const { return SkColorTypeIsAlphaOnly(fInfo.colorType()); }
588
makeColorSpace(sk_sp<SkColorSpace> target,GrDirectContext * direct) const589 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target, GrDirectContext* direct) const {
590 return this->makeColorTypeAndColorSpace(this->colorType(), std::move(target), direct);
591 }
592
makeColorTypeAndColorSpace(SkColorType targetColorType,sk_sp<SkColorSpace> targetColorSpace,GrDirectContext * dContext) const593 sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
594 sk_sp<SkColorSpace> targetColorSpace,
595 GrDirectContext* dContext) const {
596 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
597 return nullptr;
598 }
599
600 #if SK_SUPPORT_GPU
601 auto myContext = as_IB(this)->context();
602 // This check is also performed in the subclass, but we do it here for the short-circuit below.
603 if (myContext && !myContext->priv().matches(dContext)) {
604 return nullptr;
605 }
606 #endif
607
608 SkColorType colorType = this->colorType();
609 SkColorSpace* colorSpace = this->colorSpace();
610 if (!colorSpace) {
611 colorSpace = sk_srgb_singleton();
612 }
613 if (colorType == targetColorType &&
614 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
615 return sk_ref_sp(const_cast<SkImage*>(this));
616 }
617
618 return as_IB(this)->onMakeColorTypeAndColorSpace(targetColorType,
619 std::move(targetColorSpace), dContext);
620 }
621
reinterpretColorSpace(sk_sp<SkColorSpace> target) const622 sk_sp<SkImage> SkImage::reinterpretColorSpace(sk_sp<SkColorSpace> target) const {
623 if (!target) {
624 return nullptr;
625 }
626
627 // No need to create a new image if:
628 // (1) The color spaces are equal.
629 // (2) The color type is kAlpha8.
630 SkColorSpace* colorSpace = this->colorSpace();
631 if (!colorSpace) {
632 colorSpace = sk_srgb_singleton();
633 }
634 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
635 return sk_ref_sp(const_cast<SkImage*>(this));
636 }
637
638 return as_IB(this)->onReinterpretColorSpace(std::move(target));
639 }
640
makeNonTextureImage() const641 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
642 if (!this->isTextureBacked()) {
643 return sk_ref_sp(const_cast<SkImage*>(this));
644 }
645 return this->makeRasterImage();
646 }
647
makeRasterImage(CachingHint chint) const648 sk_sp<SkImage> SkImage::makeRasterImage(CachingHint chint) const {
649 SkPixmap pm;
650 if (this->peekPixels(&pm)) {
651 return sk_ref_sp(const_cast<SkImage*>(this));
652 }
653
654 const size_t rowBytes = fInfo.minRowBytes();
655 size_t size = fInfo.computeByteSize(rowBytes);
656 if (SkImageInfo::ByteSizeOverflowed(size)) {
657 return nullptr;
658 }
659
660 // Context TODO: Elevate GrDirectContext requirement to public API.
661 auto dContext = as_IB(this)->directContext();
662 sk_sp<SkData> data = SkData::MakeUninitialized(size);
663 pm = {fInfo.makeColorSpace(nullptr), data->writable_data(), fInfo.minRowBytes()};
664 if (!this->readPixels(dContext, pm, 0, 0, chint)) {
665 return nullptr;
666 }
667
668 return SkImage::MakeRasterData(fInfo, std::move(data), rowBytes);
669 }
670
SkImage_pinAsTexture(const SkImage * image,GrRecordingContext * rContext)671 bool SkImage_pinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
672 SkASSERT(image);
673 SkASSERT(rContext);
674 return as_IB(image)->onPinAsTexture(rContext);
675 }
676
SkImage_unpinAsTexture(const SkImage * image,GrRecordingContext * rContext)677 void SkImage_unpinAsTexture(const SkImage* image, GrRecordingContext* rContext) {
678 SkASSERT(image);
679 SkASSERT(rContext);
680 as_IB(image)->onUnpinAsTexture(rContext);
681 }
682
683 ///////////////////////////////////////////////////////////////////////////////////////////////////
684
SkMipmapBuilder(const SkImageInfo & info)685 SkMipmapBuilder::SkMipmapBuilder(const SkImageInfo& info) {
686 fMM = sk_sp<SkMipmap>(SkMipmap::Build({info, nullptr, 0}, nullptr, false));
687 }
688
~SkMipmapBuilder()689 SkMipmapBuilder::~SkMipmapBuilder() {}
690
countLevels() const691 int SkMipmapBuilder::countLevels() const {
692 return fMM ? fMM->countLevels() : 0;
693 }
694
level(int index) const695 SkPixmap SkMipmapBuilder::level(int index) const {
696 SkPixmap pm;
697
698 SkMipmap::Level level;
699 if (fMM && fMM->getLevel(index, &level)) {
700 pm = level.fPixmap;
701 }
702 return pm;
703 }
704
hasMipmaps() const705 bool SkImage::hasMipmaps() const { return as_IB(this)->onHasMipmaps(); }
706
withMipmaps(sk_sp<SkMipmap> mips) const707 sk_sp<SkImage> SkImage::withMipmaps(sk_sp<SkMipmap> mips) const {
708 if (mips == nullptr || mips->validForRootLevel(this->imageInfo())) {
709 if (auto result = as_IB(this)->onMakeWithMipmaps(std::move(mips))) {
710 return result;
711 }
712 }
713 return sk_ref_sp((const_cast<SkImage*>(this)));
714 }
715
withDefaultMipmaps() const716 sk_sp<SkImage> SkImage::withDefaultMipmaps() const {
717 return this->withMipmaps(nullptr);
718 }
719
attachTo(const SkImage * src)720 sk_sp<SkImage> SkMipmapBuilder::attachTo(const SkImage* src) {
721 return src->withMipmaps(fMM);
722 }
723
724 //////////////////////////////////////////////////////////////////////////////////////////////////
725
726 #include "src/core/SkReadBuffer.h"
727 #include "src/core/SkSamplingPriv.h"
728 #include "src/core/SkWriteBuffer.h"
729
FromFQ(SkLegacyFQ fq,SkMediumAs behavior)730 SkSamplingOptions SkSamplingPriv::FromFQ(SkLegacyFQ fq, SkMediumAs behavior) {
731 switch (fq) {
732 case kHigh_SkLegacyFQ:
733 return SkSamplingOptions(SkCubicResampler{1/3.0f, 1/3.0f});
734 case kMedium_SkLegacyFQ:
735 return SkSamplingOptions(SkFilterMode::kLinear,
736 behavior == kNearest_SkMediumAs ? SkMipmapMode::kNearest
737 : SkMipmapMode::kLinear);
738 case kLow_SkLegacyFQ:
739 return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
740 case kNone_SkLegacyFQ:
741 break;
742 }
743 return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);
744 }
745
Read(SkReadBuffer & buffer)746 SkSamplingOptions SkSamplingPriv::Read(SkReadBuffer& buffer) {
747 if (buffer.readBool()) {
748 SkScalar B = buffer.readScalar(),
749 C = buffer.readScalar();
750 return SkSamplingOptions({B,C});
751 } else {
752 auto filter = buffer.read32LE<SkFilterMode>(SkFilterMode::kLinear);
753 auto mipmap = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
754 return SkSamplingOptions(filter, mipmap);
755 }
756 }
757
Write(SkWriteBuffer & buffer,const SkSamplingOptions & sampling)758 void SkSamplingPriv::Write(SkWriteBuffer& buffer, const SkSamplingOptions& sampling) {
759 buffer.writeBool(sampling.useCubic);
760 if (sampling.useCubic) {
761 buffer.writeScalar(sampling.cubic.B);
762 buffer.writeScalar(sampling.cubic.C);
763 } else {
764 buffer.writeUInt((unsigned)sampling.filter);
765 buffer.writeUInt((unsigned)sampling.mipmap);
766 }
767 }
768