1 /* 2 * Copyright 2016 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 "SkSpecialImage.h" 9 #include "SkBitmap.h" 10 #include "SkImage.h" 11 #include "SkBitmapCache.h" 12 #include "SkCanvas.h" 13 #include "SkImage_Base.h" 14 #include "SkSpecialSurface.h" 15 #include "SkSurfacePriv.h" 16 #include "SkPixelRef.h" 17 18 #if SK_SUPPORT_GPU 19 #include "GrContext.h" 20 #include "GrContextPriv.h" 21 #include "GrProxyProvider.h" 22 #include "GrSurfaceContext.h" 23 #include "GrTextureProxy.h" 24 #include "SkImage_Gpu.h" 25 #endif 26 27 // Currently the raster imagefilters can only handle certain imageinfos. Call this to know if 28 // a given info is supported. 29 static bool valid_for_imagefilters(const SkImageInfo& info) { 30 // no support for other swizzles/depths yet 31 return info.colorType() == kN32_SkColorType; 32 } 33 34 /////////////////////////////////////////////////////////////////////////////// 35 class SkSpecialImage_Base : public SkSpecialImage { 36 public: 37 SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props) 38 : INHERITED(subset, uniqueID, props) { 39 } 40 ~SkSpecialImage_Base() override { } 41 42 virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0; 43 44 virtual bool onGetROPixels(SkBitmap*) const = 0; 45 46 virtual GrContext* onGetContext() const { return nullptr; } 47 48 virtual SkColorSpace* onGetColorSpace() const = 0; 49 50 #if SK_SUPPORT_GPU 51 virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const = 0; 52 #endif 53 54 virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0; 55 56 virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps, 57 const SkISize& size, SkAlphaType at) const = 0; 58 59 virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0; 60 61 virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps, 62 const SkISize& size, SkAlphaType at) const = 0; 63 64 private: 65 typedef SkSpecialImage INHERITED; 66 }; 67 68 /////////////////////////////////////////////////////////////////////////////// 69 static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) { 70 return static_cast<const SkSpecialImage_Base*>(image); 71 } 72 73 SkSpecialImage::SkSpecialImage(const SkIRect& subset, 74 uint32_t uniqueID, 75 const SkSurfaceProps* props) 76 : fProps(SkSurfacePropsCopyOrDefault(props)) 77 , fSubset(subset) 78 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) { 79 } 80 81 sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrContext* context) { 82 #if SK_SUPPORT_GPU 83 if (!context) { 84 return nullptr; 85 } 86 if (GrContext* curContext = as_SIB(this)->onGetContext()) { 87 return curContext == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr; 88 } 89 90 auto proxyProvider = context->contextPriv().proxyProvider(); 91 SkBitmap bmp; 92 // At this point, we are definitely not texture-backed, so we must be raster or generator 93 // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that 94 // we are strictly raster-backed (i.e. generator images become raster when they are specialized) 95 // in which case getROPixels could turn into peekPixels... 96 if (!this->getROPixels(&bmp)) { 97 return nullptr; 98 } 99 100 if (bmp.empty()) { 101 return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props()); 102 } 103 104 // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's 105 // semantics). Since this is cached though we would have to bake the fit into the cache key. 106 sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(proxyProvider, bmp); 107 if (!proxy) { 108 return nullptr; 109 } 110 111 const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height()); 112 113 // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not 114 // bother with SkBitmap::getSubset 115 return SkSpecialImage::MakeDeferredFromGpu(context, 116 rect, 117 this->uniqueID(), 118 std::move(proxy), 119 sk_ref_sp(this->getColorSpace()), 120 &this->props(), 121 this->alphaType()); 122 #else 123 return nullptr; 124 #endif 125 } 126 127 void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const { 128 return as_SIB(this)->onDraw(canvas, x, y, paint); 129 } 130 131 bool SkSpecialImage::getROPixels(SkBitmap* bm) const { 132 return as_SIB(this)->onGetROPixels(bm); 133 } 134 135 bool SkSpecialImage::isTextureBacked() const { 136 return SkToBool(as_SIB(this)->onGetContext()); 137 } 138 139 GrContext* SkSpecialImage::getContext() const { 140 return as_SIB(this)->onGetContext(); 141 } 142 143 SkColorSpace* SkSpecialImage::getColorSpace() const { 144 return as_SIB(this)->onGetColorSpace(); 145 } 146 147 #if SK_SUPPORT_GPU 148 sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrContext* context) const { 149 return as_SIB(this)->onAsTextureProxyRef(context); 150 } 151 #endif 152 153 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps, 154 const SkISize& size, SkAlphaType at) const { 155 return as_SIB(this)->onMakeSurface(outProps, size, at); 156 } 157 158 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps, 159 const SkISize& size, SkAlphaType at) const { 160 return as_SIB(this)->onMakeTightSurface(outProps, size, at); 161 } 162 163 sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const { 164 return as_SIB(this)->onMakeSubset(subset); 165 } 166 167 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const { 168 return as_SIB(this)->onAsImage(subset); 169 } 170 171 172 #ifdef SK_DEBUG 173 static bool rect_fits(const SkIRect& rect, int width, int height) { 174 if (0 == width && 0 == height) { 175 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom); 176 return true; 177 } 178 179 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight && 180 rect.fRight >= 0 && rect.fRight <= width && 181 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom && 182 rect.fBottom >= 0 && rect.fBottom <= height; 183 } 184 #endif 185 186 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset, 187 sk_sp<SkImage> image, 188 SkColorSpace* dstColorSpace, 189 const SkSurfaceProps* props) { 190 SkASSERT(rect_fits(subset, image->width(), image->height())); 191 192 #if SK_SUPPORT_GPU 193 if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef()) { 194 GrContext* context = ((SkImage_Gpu*) as_IB(image))->context(); 195 196 return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy), 197 as_IB(image)->onImageInfo().refColorSpace(), props); 198 } else 199 #endif 200 { 201 SkBitmap bm; 202 if (as_IB(image)->getROPixels(&bm, dstColorSpace)) { 203 return MakeFromRaster(subset, bm, props); 204 } 205 } 206 return nullptr; 207 } 208 209 /////////////////////////////////////////////////////////////////////////////// 210 211 class SkSpecialImage_Raster : public SkSpecialImage_Base { 212 public: 213 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props) 214 : INHERITED(subset, bm.getGenerationID(), props) 215 , fBitmap(bm) 216 { 217 SkASSERT(bm.pixelRef()); 218 SkASSERT(fBitmap.getPixels()); 219 } 220 221 SkAlphaType alphaType() const override { return fBitmap.alphaType(); } 222 223 size_t getSize() const override { return fBitmap.computeByteSize(); } 224 225 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override { 226 SkRect dst = SkRect::MakeXYWH(x, y, 227 this->subset().width(), this->subset().height()); 228 229 canvas->drawBitmapRect(fBitmap, this->subset(), 230 dst, paint, SkCanvas::kStrict_SrcRectConstraint); 231 } 232 233 bool onGetROPixels(SkBitmap* bm) const override { 234 *bm = fBitmap; 235 return true; 236 } 237 238 SkColorSpace* onGetColorSpace() const override { 239 return fBitmap.colorSpace(); 240 } 241 242 #if SK_SUPPORT_GPU 243 sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const override { 244 if (context) { 245 return GrMakeCachedBitmapProxy(context->contextPriv().proxyProvider(), fBitmap); 246 } 247 248 return nullptr; 249 } 250 #endif 251 252 // TODO: The raster implementations of image filters all currently assume that the pixels are 253 // legacy N32. Until they actually check the format and operate on sRGB or F16 data appropriately, 254 // we can't enable this. (They will continue to produce incorrect results, but less-so). 255 #define RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 0 256 257 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps, 258 const SkISize& size, SkAlphaType at) const override { 259 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 260 SkColorSpace* colorSpace = outProps.colorSpace(); 261 #else 262 SkColorSpace* colorSpace = nullptr; 263 #endif 264 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear() 265 ? kRGBA_F16_SkColorType : kN32_SkColorType; 266 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at, 267 sk_ref_sp(colorSpace)); 268 return SkSpecialSurface::MakeRaster(info, nullptr); 269 } 270 271 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override { 272 SkBitmap subsetBM; 273 274 if (!fBitmap.extractSubset(&subsetBM, subset)) { 275 return nullptr; 276 } 277 278 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()), 279 subsetBM, 280 &this->props()); 281 } 282 283 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override { 284 if (subset) { 285 SkBitmap subsetBM; 286 287 if (!fBitmap.extractSubset(&subsetBM, *subset)) { 288 return nullptr; 289 } 290 291 return SkImage::MakeFromBitmap(subsetBM); 292 } 293 294 return SkImage::MakeFromBitmap(fBitmap); 295 } 296 297 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps, 298 const SkISize& size, SkAlphaType at) const override { 299 #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 300 SkColorSpace* colorSpace = outProps.colorSpace(); 301 #else 302 SkColorSpace* colorSpace = nullptr; 303 #endif 304 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear() 305 ? kRGBA_F16_SkColorType : kN32_SkColorType; 306 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at, 307 sk_ref_sp(colorSpace)); 308 return SkSurface::MakeRaster(info); 309 } 310 311 private: 312 SkBitmap fBitmap; 313 314 typedef SkSpecialImage_Base INHERITED; 315 }; 316 317 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset, 318 const SkBitmap& bm, 319 const SkSurfaceProps* props) { 320 SkASSERT(rect_fits(subset, bm.width(), bm.height())); 321 322 if (!bm.pixelRef()) { 323 return nullptr; 324 } 325 326 const SkBitmap* srcBM = &bm; 327 SkBitmap tmp; 328 // ImageFilters only handle N32 at the moment, so force our src to be that 329 if (!valid_for_imagefilters(bm.info())) { 330 if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) || 331 !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0)) 332 { 333 return nullptr; 334 } 335 srcBM = &tmp; 336 } 337 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props); 338 } 339 340 #if SK_SUPPORT_GPU 341 /////////////////////////////////////////////////////////////////////////////// 342 static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, sk_sp<GrTextureProxy> proxy, 343 SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) { 344 return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType, 345 std::move(proxy), std::move(colorSpace), SkBudgeted::kYes); 346 } 347 348 class SkSpecialImage_Gpu : public SkSpecialImage_Base { 349 public: 350 SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset, 351 uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at, 352 sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props) 353 : INHERITED(subset, uniqueID, props) 354 , fContext(context) 355 , fTextureProxy(std::move(proxy)) 356 , fAlphaType(at) 357 , fColorSpace(std::move(colorSpace)) 358 , fAddedRasterVersionToCache(false) { 359 } 360 361 ~SkSpecialImage_Gpu() override { 362 if (fAddedRasterVersionToCache.load()) { 363 SkNotifyBitmapGenIDIsStale(this->uniqueID()); 364 } 365 } 366 367 SkAlphaType alphaType() const override { return fAlphaType; } 368 369 size_t getSize() const override { return fTextureProxy->gpuMemorySize(); } 370 371 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override { 372 SkRect dst = SkRect::MakeXYWH(x, y, 373 this->subset().width(), this->subset().height()); 374 375 // TODO: In this instance we know we're going to draw a sub-portion of the backing 376 // texture into the canvas so it is okay to wrap it in an SkImage. This poses 377 // some problems for full deferral however in that when the deferred SkImage_Gpu 378 // instantiates itself it is going to have to either be okay with having a larger 379 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs 380 // to be tightened (if it is deferred). 381 sk_sp<SkImage> img = sk_sp<SkImage>(new SkImage_Gpu(canvas->getGrContext(), 382 this->uniqueID(), fAlphaType, 383 fTextureProxy, 384 fColorSpace, SkBudgeted::kNo)); 385 386 canvas->drawImageRect(img, this->subset(), 387 dst, paint, SkCanvas::kStrict_SrcRectConstraint); 388 } 389 390 GrContext* onGetContext() const override { return fContext; } 391 392 sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext*) const override { 393 return fTextureProxy; 394 } 395 396 bool onGetROPixels(SkBitmap* dst) const override { 397 const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->width(), this->height()); 398 if (SkBitmapCache::Find(desc, dst)) { 399 SkASSERT(dst->getGenerationID() == this->uniqueID()); 400 SkASSERT(dst->isImmutable()); 401 SkASSERT(dst->getPixels()); 402 return true; 403 } 404 405 SkPixmap pmap; 406 SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(), 407 this->alphaType(), fColorSpace); 408 auto rec = SkBitmapCache::Alloc(desc, info, &pmap); 409 if (!rec) { 410 return false; 411 } 412 sk_sp<SkColorSpace> colorSpace; 413 if (GrPixelConfigIsSRGB(fTextureProxy->config())) { 414 colorSpace = SkColorSpace::MakeSRGB(); 415 } 416 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext( 417 fTextureProxy, std::move(colorSpace)); 418 if (!sContext) { 419 return false; 420 } 421 422 if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(), 0, 0)) { 423 return false; 424 } 425 426 SkBitmapCache::Add(std::move(rec), dst); 427 fAddedRasterVersionToCache.store(true); 428 return true; 429 } 430 431 SkColorSpace* onGetColorSpace() const override { 432 return fColorSpace.get(); 433 } 434 435 sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps, 436 const SkISize& size, SkAlphaType at) const override { 437 if (!fContext) { 438 return nullptr; 439 } 440 441 SkColorSpace* colorSpace = outProps.colorSpace(); 442 return SkSpecialSurface::MakeRenderTarget( 443 fContext, size.width(), size.height(), 444 GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace)); 445 } 446 447 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override { 448 return SkSpecialImage::MakeDeferredFromGpu(fContext, 449 subset, 450 this->uniqueID(), 451 fTextureProxy, 452 fColorSpace, 453 &this->props(), 454 fAlphaType); 455 } 456 457 // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy? 458 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override { 459 if (subset) { 460 // TODO: if this becomes a bottle neck we could base this logic on what the size 461 // will be when it is finally instantiated - but that is more fraught. 462 if (GrProxyProvider::IsFunctionallyExact(fTextureProxy.get()) && 463 0 == subset->fLeft && 0 == subset->fTop && 464 fTextureProxy->width() == subset->width() && 465 fTextureProxy->height() == subset->height()) { 466 // The existing GrTexture is already tight so reuse it in the SkImage 467 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace); 468 } 469 470 sk_sp<GrTextureProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fTextureProxy.get(), 471 GrMipMapped::kNo, *subset, 472 SkBudgeted::kYes)); 473 if (!subsetProxy) { 474 return nullptr; 475 } 476 477 SkASSERT(subsetProxy->priv().isExact()); 478 // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will 479 // return a kExact-backed proxy 480 return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace); 481 } 482 483 fTextureProxy->priv().exactify(); 484 485 return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace); 486 } 487 488 sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps, 489 const SkISize& size, SkAlphaType at) const override { 490 SkColorSpace* colorSpace = outProps.colorSpace(); 491 SkColorType colorType = colorSpace && colorSpace->gammaIsLinear() 492 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType; 493 SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at, 494 sk_ref_sp(colorSpace)); 495 return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info); 496 } 497 498 private: 499 GrContext* fContext; 500 sk_sp<GrTextureProxy> fTextureProxy; 501 const SkAlphaType fAlphaType; 502 sk_sp<SkColorSpace> fColorSpace; 503 mutable SkAtomic<bool> fAddedRasterVersionToCache; 504 505 typedef SkSpecialImage_Base INHERITED; 506 }; 507 508 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context, 509 const SkIRect& subset, 510 uint32_t uniqueID, 511 sk_sp<GrTextureProxy> proxy, 512 sk_sp<SkColorSpace> colorSpace, 513 const SkSurfaceProps* props, 514 SkAlphaType at) { 515 SkASSERT(rect_fits(subset, proxy->width(), proxy->height())); 516 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at, 517 std::move(colorSpace), props); 518 } 519 #endif 520