yet another approach to unpremul scale pixels
This keeps punning things to premul (in anticipation of pulling back on any unpremul support outside read/write/scalePixels), and yet still clamps correctly for scalePixels() + unpremul + HQ and/or gamut transform. Change-Id: I75977cfdb94ffbe62c538ddee39f1abd2cc01935 Reviewed-on: https://skia-review.googlesource.com/106265 Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
528ceb9c0d
commit
1f31309d7a
3
BUILD.gn
3
BUILD.gn
@ -144,6 +144,9 @@ config("skia_public") {
|
||||
if (skia_enable_atlas_text) {
|
||||
defines += [ "SK_SUPPORT_ATLAS_TEXT=1" ]
|
||||
}
|
||||
|
||||
# Temporary.
|
||||
defines += [ "SK_LEGACY_HIGH_QUALITY_SCALING_CLAMP" ]
|
||||
}
|
||||
|
||||
# Skia internal APIs, used by Skia itself and a few test tools.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "SkConvertPixels.h"
|
||||
#include "SkData.h"
|
||||
#include "SkImageInfoPriv.h"
|
||||
#include "SkImageShader.h"
|
||||
#include "SkHalf.h"
|
||||
#include "SkMask.h"
|
||||
#include "SkNx.h"
|
||||
@ -229,54 +230,65 @@ bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_alphatype(SkPixmap* dst, const SkPixmap& src, SkAlphaType at) {
|
||||
dst->reset(src.info().makeAlphaType(at), src.addr(), src.rowBytes());
|
||||
}
|
||||
bool SkPixmap::scalePixels(const SkPixmap& actualDst, SkFilterQuality quality) const {
|
||||
// We may need to tweak how we interpret these just a little below, so we make copies.
|
||||
SkPixmap src = *this,
|
||||
dst = actualDst;
|
||||
|
||||
bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const {
|
||||
// Can't do anthing with empty src or dst
|
||||
if (this->width() <= 0 || this->height() <= 0 || dst.width() <= 0 || dst.height() <= 0) {
|
||||
if (src.width() <= 0 || src.height() <= 0 ||
|
||||
dst.width() <= 0 || dst.height() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// no scaling involved?
|
||||
if (dst.width() == this->width() && dst.height() == this->height()) {
|
||||
return this->readPixels(dst);
|
||||
if (src.width() == dst.width() && src.height() == dst.height()) {
|
||||
return src.readPixels(dst);
|
||||
}
|
||||
|
||||
// Temp storage in case we need to edit the requested alphatypes
|
||||
SkPixmap storage_src, storage_dst;
|
||||
const SkPixmap* srcPtr = this;
|
||||
const SkPixmap* dstPtr = &dst;
|
||||
// If src and dst are both unpremul, we'll fake them out to appear as if premul.
|
||||
bool clampAsIfUnpremul = false;
|
||||
if (src.alphaType() == kUnpremul_SkAlphaType &&
|
||||
dst.alphaType() == kUnpremul_SkAlphaType) {
|
||||
src.reset(src.info().makeAlphaType(kPremul_SkAlphaType), src.addr(), src.rowBytes());
|
||||
dst.reset(dst.info().makeAlphaType(kPremul_SkAlphaType), dst.addr(), dst.rowBytes());
|
||||
|
||||
// Trick: if src and dst are both unpremul, we can give the correct result if we change both
|
||||
// to premul (or opaque), since the draw will not try to blend or otherwise interpret
|
||||
// the pixels' alpha.
|
||||
if (srcPtr->alphaType() == kUnpremul_SkAlphaType &&
|
||||
dstPtr->alphaType() == kUnpremul_SkAlphaType)
|
||||
{
|
||||
set_alphatype(&storage_src, *this, kPremul_SkAlphaType);
|
||||
set_alphatype(&storage_dst, dst, kPremul_SkAlphaType);
|
||||
srcPtr = &storage_src;
|
||||
dstPtr = &storage_dst;
|
||||
// In turn, we'll need to tell the image shader to clamp to [0,1] instead
|
||||
// of the usual [0,a] when using a bicubic scaling (kHigh_SkFilterQuality)
|
||||
// or a gamut transformation.
|
||||
clampAsIfUnpremul = true;
|
||||
}
|
||||
|
||||
SkBitmap bitmap;
|
||||
if (!bitmap.installPixels(*srcPtr)) {
|
||||
if (!bitmap.installPixels(src)) {
|
||||
return false;
|
||||
}
|
||||
bitmap.setIsVolatile(true); // so we don't try to cache it
|
||||
bitmap.setImmutable(); // Don't copy when we create an image.
|
||||
bitmap.setIsVolatile(true); // Disable any caching.
|
||||
|
||||
auto surface(SkSurface::MakeRasterDirect(dstPtr->info(), dstPtr->writable_addr(), dstPtr->rowBytes()));
|
||||
if (!surface) {
|
||||
SkMatrix scale = SkMatrix::MakeRectToRect(SkRect::Make(src.bounds()),
|
||||
SkRect::Make(dst.bounds()),
|
||||
SkMatrix::kFill_ScaleToFit);
|
||||
|
||||
// We'll create a shader to do this draw so we have control over the bicubic clamp.
|
||||
sk_sp<SkShader> shader = SkImageShader::Make(SkImage::MakeFromBitmap(bitmap),
|
||||
SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode,
|
||||
&scale,
|
||||
clampAsIfUnpremul);
|
||||
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(dst.info(),
|
||||
dst.writable_addr(),
|
||||
dst.rowBytes());
|
||||
if (!shader || !surface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(quality);
|
||||
paint.setBlendMode(SkBlendMode::kSrc);
|
||||
surface->getCanvas()->drawBitmapRect(bitmap, SkRect::MakeIWH(dst.width(), dst.height()),
|
||||
&paint);
|
||||
paint.setFilterQuality(quality);
|
||||
paint.setShader(std::move(shader));
|
||||
surface->getCanvas()->drawPaint(paint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -31,23 +31,30 @@ static SkShader::TileMode optimize(SkShader::TileMode tm, int dimension) {
|
||||
#endif
|
||||
}
|
||||
|
||||
SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
|
||||
: INHERITED(matrix)
|
||||
SkImageShader::SkImageShader(sk_sp<SkImage> img,
|
||||
TileMode tmx, TileMode tmy,
|
||||
const SkMatrix* localMatrix,
|
||||
bool clampAsIfUnpremul)
|
||||
: INHERITED(localMatrix)
|
||||
, fImage(std::move(img))
|
||||
, fTileModeX(optimize(tmx, fImage->width()))
|
||||
, fTileModeY(optimize(tmy, fImage->height()))
|
||||
, fClampAsIfUnpremul(clampAsIfUnpremul)
|
||||
{}
|
||||
|
||||
// fClampAsIfUnpremul is always false when constructed through public APIs,
|
||||
// so there's no need to read or write it here.
|
||||
|
||||
sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
|
||||
const TileMode tx = (TileMode)buffer.readUInt();
|
||||
const TileMode ty = (TileMode)buffer.readUInt();
|
||||
SkMatrix matrix;
|
||||
buffer.readMatrix(&matrix);
|
||||
SkMatrix localMatrix;
|
||||
buffer.readMatrix(&localMatrix);
|
||||
sk_sp<SkImage> img = buffer.readImage();
|
||||
if (!img) {
|
||||
return nullptr;
|
||||
}
|
||||
return SkImageShader::Make(std::move(img), tx, ty, &matrix);
|
||||
return SkImageShader::Make(std::move(img), tx, ty, &localMatrix);
|
||||
}
|
||||
|
||||
void SkImageShader::flatten(SkWriteBuffer& buffer) const {
|
||||
@ -55,6 +62,7 @@ void SkImageShader::flatten(SkWriteBuffer& buffer) const {
|
||||
buffer.writeUInt(fTileModeY);
|
||||
buffer.writeMatrix(this->getLocalMatrix());
|
||||
buffer.writeImage(fImage.get());
|
||||
SkASSERT(fClampAsIfUnpremul == false);
|
||||
}
|
||||
|
||||
bool SkImageShader::isOpaque() const {
|
||||
@ -164,13 +172,14 @@ static bool bitmap_is_too_big(int w, int h) {
|
||||
return w > kMaxSize || h > kMaxSize;
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
|
||||
const SkMatrix* localMatrix) {
|
||||
sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
|
||||
TileMode tx, TileMode ty,
|
||||
const SkMatrix* localMatrix,
|
||||
bool clampAsIfUnpremul) {
|
||||
if (!image || bitmap_is_too_big(image->width(), image->height())) {
|
||||
return sk_make_sp<SkEmptyShader>();
|
||||
} else {
|
||||
return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix);
|
||||
}
|
||||
return sk_sp<SkShader>{ new SkImageShader(image, tx,ty, localMatrix, clampAsIfUnpremul) };
|
||||
}
|
||||
|
||||
#ifndef SK_IGNORE_TO_STRING
|
||||
@ -390,14 +399,16 @@ bool SkImageShader::onAppendStages(const StageRec& rec) const {
|
||||
info.alphaType() == kUnpremul_SkAlphaType) {
|
||||
p->append(SkRasterPipeline::premul);
|
||||
}
|
||||
#if defined(SK_LEGACY_HIGH_QUALITY_SCALING_CLAMP)
|
||||
#if defined(SK_LEGACY_HIGH_QUALITY_SCALING_CLAMP)
|
||||
if (quality > kLow_SkFilterQuality) {
|
||||
// Bicubic filtering naturally produces out of range values on both sides.
|
||||
p->append(SkRasterPipeline::clamp_0);
|
||||
p->append(SkRasterPipeline::clamp_a);
|
||||
p->append(fClampAsIfUnpremul ? SkRasterPipeline::clamp_1
|
||||
: SkRasterPipeline::clamp_a);
|
||||
}
|
||||
#endif
|
||||
append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS, kPremul_SkAlphaType);
|
||||
#endif
|
||||
append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS,
|
||||
fClampAsIfUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -15,8 +15,11 @@
|
||||
|
||||
class SkImageShader : public SkShaderBase {
|
||||
public:
|
||||
static sk_sp<SkShader> Make(sk_sp<SkImage>, TileMode tx, TileMode ty,
|
||||
const SkMatrix* localMatrix);
|
||||
static sk_sp<SkShader> Make(sk_sp<SkImage>,
|
||||
SkShader::TileMode tx,
|
||||
SkShader::TileMode ty,
|
||||
const SkMatrix* localMatrix,
|
||||
bool clampAsIfUnpremul = false);
|
||||
|
||||
bool isOpaque() const override;
|
||||
|
||||
@ -27,19 +30,23 @@ public:
|
||||
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
|
||||
#endif
|
||||
|
||||
SkImageShader(sk_sp<SkImage>, TileMode tx, TileMode ty, const SkMatrix* localMatrix);
|
||||
|
||||
static bool IsRasterPipelineOnly(const SkMatrix& ctm, SkColorType, SkAlphaType,
|
||||
SkShader::TileMode tx, SkShader::TileMode ty,
|
||||
const SkMatrix& localM);
|
||||
|
||||
protected:
|
||||
private:
|
||||
SkImageShader(sk_sp<SkImage>,
|
||||
SkShader::TileMode tx,
|
||||
SkShader::TileMode ty,
|
||||
const SkMatrix* localMatrix,
|
||||
bool clampAsIfUnpremul);
|
||||
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override;
|
||||
#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
|
||||
bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override;
|
||||
bool onIsABitmap(SkBitmap*, SkMatrix*, SkShader::TileMode*) const override;
|
||||
#endif
|
||||
SkImage* onIsAImage(SkMatrix*, TileMode*) const override;
|
||||
SkImage* onIsAImage(SkMatrix*, SkShader::TileMode*) const override;
|
||||
|
||||
bool onIsRasterPipelineOnly(const SkMatrix& ctm) const override;
|
||||
|
||||
@ -50,13 +57,12 @@ protected:
|
||||
&this->getLocalMatrix());
|
||||
}
|
||||
|
||||
sk_sp<SkImage> fImage;
|
||||
const TileMode fTileModeX;
|
||||
const TileMode fTileModeY;
|
||||
sk_sp<SkImage> fImage;
|
||||
const SkShader::TileMode fTileModeX;
|
||||
const SkShader::TileMode fTileModeY;
|
||||
const bool fClampAsIfUnpremul;
|
||||
|
||||
private:
|
||||
friend class SkShaderBase;
|
||||
|
||||
typedef SkShaderBase INHERITED;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user