/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file */ #include "src/core/SkSpecialImage.h" #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" #include "include/core/SkColorType.h" #include "include/core/SkImage.h" #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" #include "include/core/SkShader.h" #include "include/private/base/SkAssert.h" #include "src/core/SkNextID.h" #include "src/image/SkImage_Base.h" #include "src/shaders/SkImageShader.h" // Currently, the raster imagefilters can only handle certain imageinfos. Call this to know if // a given info is supported. static bool valid_for_imagefilters(const SkImageInfo& info) { // no support for other swizzles/depths yet return info.colorType() == kN32_SkColorType; } SkSpecialImage::SkSpecialImage(const SkIRect& subset, uint32_t uniqueID, const SkColorInfo& colorInfo, const SkSurfaceProps& props) : fSubset(subset) , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) , fColorInfo(colorInfo) , fProps(props) { } void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, const SkPaint* paint, bool strict) const { SkRect dst = SkRect::MakeXYWH(x, y, this->subset().width(), this->subset().height()); canvas->drawImageRect(this->asImage(), SkRect::Make(this->subset()), dst, sampling, paint, strict ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint); } // TODO(skbug.com/12784): Once bitmap images work with SkImageShader::MakeSubset(), this does not // need to be virtual anymore. sk_sp SkSpecialImage::asShader(SkTileMode tileMode, const SkSamplingOptions& sampling, const SkMatrix& lm, bool strict) const { // The special image's logical (0,0) is at its subset's topLeft() so we need to account for // that in the local matrix used when sampling. SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft()); subsetOrigin.postConcat(lm); if (strict) { // However, we don't need to modify the subset itself since that is defined with respect // to the base image, and the local matrix is applied before any tiling/clamping. const SkRect subset = SkRect::Make(this->subset()); // asImage() w/o a subset makes no copy; create the SkImageShader directly to remember // the subset used to access the image. return SkImageShader::MakeSubset( this->asImage(), subset, tileMode, tileMode, sampling, &subsetOrigin); } else { // Ignore 'subset' other than its origin translation applied to the local matrix. return this->asImage()->makeShader(tileMode, tileMode, sampling, subsetOrigin); } } class SkSpecialImage_Raster final : public SkSpecialImage { public: SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps& props) : SkSpecialImage(subset, bm.getGenerationID(), bm.info().colorInfo(), props) , fBitmap(bm) { SkASSERT(bm.pixelRef()); SkASSERT(fBitmap.getPixels()); } bool getROPixels(SkBitmap* bm) const { return fBitmap.extractSubset(bm, this->subset()); } SkISize backingStoreDimensions() const override { return fBitmap.dimensions(); } size_t getSize() const override { return fBitmap.computeByteSize(); } sk_sp asImage() const override { return fBitmap.asImage(); } sk_sp onMakeBackingStoreSubset(const SkIRect& subset) const override { // No need to extract subset, onGetROPixels handles that when needed return SkSpecialImages::MakeFromRaster(subset, fBitmap, this->props()); } sk_sp asShader(SkTileMode tileMode, const SkSamplingOptions& sampling, const SkMatrix& lm, bool strict) const override { if (strict) { // TODO(skbug.com/12784): SkImage::makeShader() doesn't support a subset yet, but // SkBitmap supports subset views so create the shader from the subset bitmap instead of // fBitmap. SkBitmap subsetBM; if (!this->getROPixels(&subsetBM)) { return nullptr; } return subsetBM.makeShader(tileMode, tileMode, sampling, lm); } else { // The special image's logical (0,0) is at its subset's topLeft() so we need to // account for that in the local matrix used when sampling. SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft()); subsetOrigin.postConcat(lm); return fBitmap.makeShader(tileMode, tileMode, sampling, subsetOrigin); } } private: SkBitmap fBitmap; }; namespace SkSpecialImages { sk_sp MakeFromRaster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps& props) { SkASSERT(bm.bounds().contains(subset)); if (!bm.pixelRef()) { return nullptr; } const SkBitmap* srcBM = &bm; SkBitmap tmp; // ImageFilters only handle N32 at the moment, so force our src to be that if (!valid_for_imagefilters(bm.info())) { if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) || !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0)) { return nullptr; } srcBM = &tmp; } return sk_make_sp(subset, *srcBM, props); } sk_sp CopyFromRaster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps& props) { SkASSERT(bm.bounds().contains(subset)); if (!bm.pixelRef()) { return nullptr; } SkBitmap tmp; SkImageInfo info = bm.info().makeDimensions(subset.size()); // As in MakeFromRaster, must force src to N32 for ImageFilters if (!valid_for_imagefilters(bm.info())) { info = info.makeColorType(kN32_SkColorType); } if (!tmp.tryAllocPixels(info)) { return nullptr; } if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) { return nullptr; } // Since we're making a copy of the raster, the resulting special image is the exact size // of the requested subset of the original and no longer needs to be offset by subset's left // and top, since those were relative to the original's buffer. return sk_make_sp( SkIRect::MakeWH(subset.width(), subset.height()), tmp, props); } sk_sp MakeFromRaster(const SkIRect& subset, sk_sp image, const SkSurfaceProps& props) { if (!image || subset.isEmpty()) { return nullptr; } SkASSERT(image->bounds().contains(subset)); SkASSERT(!image->isTextureBacked()); // This will not work if the image is uploaded to a GPU render target. SkBitmap bm; if (as_IB(image)->getROPixels(nullptr, &bm)) { return MakeFromRaster(subset, bm, props); } return nullptr; } bool AsBitmap(const SkSpecialImage* img, SkBitmap* result) { if (!img || img->isGaneshBacked() || img->isGraphiteBacked()) { return false; } auto rasterImg = static_cast(img); return rasterImg->getROPixels(result); } } // namespace SkSpecialImages