blob: 4f50c4d09fb02e53c0e616607b8fc19505087b94 [file] [log] [blame]
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkColorFilter.h"
#include "SkColorSpaceXform.h"
#include "SkColorSpaceXformCanvas.h"
#include "SkGradientShader.h"
#include "SkImage_Base.h"
#include "SkMakeUnique.h"
#include "SkNoDrawCanvas.h"
#include "SkSurface.h"
#include "SkTLazy.h"
class SkColorSpaceXformCanvas : public SkNoDrawCanvas {
public:
SkColorSpaceXformCanvas(SkCanvas* target,
sk_sp<SkColorSpace> targetCS)
: SkNoDrawCanvas(SkIRect::MakeSize(target->getBaseLayerSize()))
, fTarget(target)
, fTargetCS(std::move(targetCS)) {
fFromSRGB = SkColorSpaceXform::New(SkColorSpace::MakeSRGB().get(), fTargetCS.get());
}
sk_sp<const SkImage> xform(const SkImage* img) const {
return as_IB(img)->makeColorSpace(fTargetCS);
}
void xform(SkColor* xformed, const SkColor* srgb, int n) const {
SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, xformed,
SkColorSpaceXform::kBGRA_8888_ColorFormat, srgb,
n, kUnpremul_SkAlphaType));
}
SkColor xform(SkColor srgb) const {
SkColor xformed;
this->xform(&xformed, &srgb, 1);
return xformed;
}
// TODO: Is this introspection going to be enough, or do we need a new SkShader method?
sk_sp<SkShader> xform(const SkShader* shader) const {
SkColor color;
if (shader->isConstant() && shader->asLuminanceColor(&color)) {
return SkShader::MakeColorShader(this->xform(color));
}
SkShader::TileMode xy[2];
SkMatrix local;
if (auto img = shader->isAImage(&local, xy)) {
return this->xform(img)->makeShader(xy[0], xy[1], &local);
}
SkShader::ComposeRec compose;
if (shader->asACompose(&compose)) {
auto A = this->xform(compose.fShaderA),
B = this->xform(compose.fShaderB);
if (A && B) {
return SkShader::MakeComposeShader(std::move(A), std::move(B), compose.fBlendMode);
}
}
SkShader::GradientInfo gradient;
sk_bzero(&gradient, sizeof(gradient));
if (auto type = shader->asAGradient(&gradient)) {
SkSTArray<8, SkColor> colors(gradient.fColorCount);
SkSTArray<8, SkScalar> pos(gradient.fColorCount);
gradient.fColors = colors.begin();
gradient.fColorOffsets = pos.begin();
shader->asAGradient(&gradient);
SkSTArray<8, SkColor> xformed(gradient.fColorCount);
this->xform(xformed.begin(), gradient.fColors, gradient.fColorCount);
switch (type) {
case SkShader::kNone_GradientType:
case SkShader::kColor_GradientType:
SkASSERT(false); // Should be unreachable.
break;
case SkShader::kLinear_GradientType:
return SkGradientShader::MakeLinear(gradient.fPoint,
xformed.begin(),
gradient.fColorOffsets,
gradient.fColorCount,
gradient.fTileMode,
gradient.fGradientFlags,
&shader->getLocalMatrix());
case SkShader::kRadial_GradientType:
return SkGradientShader::MakeRadial(gradient.fPoint[0],
gradient.fRadius[0],
xformed.begin(),
gradient.fColorOffsets,
gradient.fColorCount,
gradient.fTileMode,
gradient.fGradientFlags,
&shader->getLocalMatrix());
case SkShader::kSweep_GradientType:
return SkGradientShader::MakeSweep(gradient.fPoint[0].fX,
gradient.fPoint[0].fY,
xformed.begin(),
gradient.fColorOffsets,
gradient.fColorCount,
gradient.fGradientFlags,
&shader->getLocalMatrix());
case SkShader::kConical_GradientType:
return SkGradientShader::MakeTwoPointConical(gradient.fPoint[0],
gradient.fRadius[0],
gradient.fPoint[1],
gradient.fRadius[1],
xformed.begin(),
gradient.fColorOffsets,
gradient.fColorCount,
gradient.fTileMode,
gradient.fGradientFlags,
&shader->getLocalMatrix());
}
}
return nullptr;
}
const SkPaint& xform(const SkPaint& paint, SkTLazy<SkPaint>* lazy) const {
const SkPaint* result = &paint;
auto get_lazy = [&] {
if (!lazy->isValid()) {
lazy->init(paint);
result = lazy->get();
}
return lazy->get();
};
// All SkColorSpaces have the same black point.
if (paint.getColor() & 0xffffff) {
get_lazy()->setColor(this->xform(paint.getColor()));
}
if (auto shader = paint.getShader()) {
if (auto replacement = this->xform(shader)) {
get_lazy()->setShader(std::move(replacement));
}
}
// As far as I know, SkModeColorFilter is the only color filter that holds a color.
if (auto cf = paint.getColorFilter()) {
SkColor color;
SkBlendMode mode;
if (cf->asColorMode(&color, &mode)) {
get_lazy()->setColorFilter(SkColorFilter::MakeModeFilter(this->xform(color), mode));
}
}
// TODO:
// - image filters?
return *result;
}
const SkPaint* xform(const SkPaint* paint, SkTLazy<SkPaint>* lazy) const {
return paint ? &this->xform(*paint, lazy) : nullptr;
}
void onDrawPaint(const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawPaint(this->xform(paint, &lazy));
}
void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawRect(rect, this->xform(paint, &lazy));
}
void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawOval(oval, this->xform(paint, &lazy));
}
void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawRRect(rrect, this->xform(paint, &lazy));
}
void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawDRRect(outer, inner, this->xform(paint, &lazy));
}
void onDrawPath(const SkPath& path, const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawPath(path, this->xform(paint, &lazy));
}
void onDrawArc(const SkRect& oval, SkScalar start, SkScalar sweep, bool useCenter,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawArc(oval, start, sweep, useCenter, this->xform(paint, &lazy));
}
void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawRegion(region, this->xform(paint, &lazy));
}
void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texs[4],
SkBlendMode mode, const SkPaint& paint) override {
SkColor xformed[4];
if (colors) {
this->xform(xformed, colors, 4);
colors = xformed;
}
SkTLazy<SkPaint> lazy;
fTarget->drawPatch(cubics, colors, texs, mode, this->xform(paint, &lazy));
}
void onDrawPoints(PointMode mode, size_t count, const SkPoint* pts,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawPoints(mode, count, pts, this->xform(paint, &lazy));
}
void onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode,
const SkPaint& paint) override {
sk_sp<SkVertices> copy;
if (vertices->hasColors()) {
int count = vertices->vertexCount();
SkSTArray<8, SkColor> xformed(count);
this->xform(xformed.begin(), vertices->colors(), count);
copy = SkVertices::MakeCopy(vertices->mode(), count, vertices->positions(),
vertices->texCoords(), xformed.begin(),
vertices->indexCount(), vertices->indices());
vertices = copy.get();
}
SkTLazy<SkPaint> lazy;
fTarget->drawVertices(vertices, mode, this->xform(paint, &lazy));
}
void onDrawText(const void* ptr, size_t len,
SkScalar x, SkScalar y,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawText(ptr, len, x, y, this->xform(paint, &lazy));
}
void onDrawPosText(const void* ptr, size_t len,
const SkPoint* xys,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawPosText(ptr, len, xys, this->xform(paint, &lazy));
}
void onDrawPosTextH(const void* ptr, size_t len,
const SkScalar* xs, SkScalar y,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawPosTextH(ptr, len, xs, y, this->xform(paint, &lazy));
}
void onDrawTextOnPath(const void* ptr, size_t len,
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawTextOnPath(ptr, len, path, matrix, this->xform(paint, &lazy));
}
void onDrawTextRSXform(const void* ptr, size_t len,
const SkRSXform* xforms, const SkRect* cull,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawTextRSXform(ptr, len, xforms, cull, this->xform(paint, &lazy));
}
void onDrawTextBlob(const SkTextBlob* blob,
SkScalar x, SkScalar y,
const SkPaint& paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawTextBlob(blob, x, y, this->xform(paint, &lazy));
}
void onDrawImage(const SkImage* img,
SkScalar l, SkScalar t,
const SkPaint* paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawImage(this->xform(img).get(),
l, t,
this->xform(paint, &lazy));
}
void onDrawImageRect(const SkImage* img,
const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawImageRect(this->xform(img).get(),
src ? *src : dst, dst,
this->xform(paint, &lazy), constraint);
}
void onDrawImageNine(const SkImage* img,
const SkIRect& center, const SkRect& dst,
const SkPaint* paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawImageNine(this->xform(img).get(),
center, dst,
this->xform(paint, &lazy));
}
void onDrawImageLattice(const SkImage* img,
const Lattice& lattice, const SkRect& dst,
const SkPaint* paint) override {
SkTLazy<SkPaint> lazy;
fTarget->drawImageLattice(this->xform(img).get(),
lattice, dst,
this->xform(paint, &lazy));
}
void onDrawAtlas(const SkImage* atlas, const SkRSXform* xforms, const SkRect* tex,
const SkColor* colors, int count, SkBlendMode mode,
const SkRect* cull, const SkPaint* paint) override {
SkSTArray<8, SkColor> xformed;
if (colors) {
xformed.reset(count);
this->xform(xformed.begin(), colors, count);
colors = xformed.begin();
}
SkTLazy<SkPaint> lazy;
fTarget->drawAtlas(this->xform(atlas).get(), xforms, tex, colors, count, mode, cull,
this->xform(paint, &lazy));
}
void onDrawBitmap(const SkBitmap& bitmap,
SkScalar l, SkScalar t,
const SkPaint* paint) override {
if (auto image = SkImage::MakeFromBitmap(bitmap)) {
this->onDrawImage(image.get(), l, t, paint);
}
}
void onDrawBitmapRect(const SkBitmap& bitmap,
const SkRect* src, const SkRect& dst,
const SkPaint* paint, SrcRectConstraint constraint) override {
if (auto image = SkImage::MakeFromBitmap(bitmap)) {
this->onDrawImageRect(image.get(), src, dst, paint, constraint);
}
}
void onDrawBitmapNine(const SkBitmap& bitmap,
const SkIRect& center, const SkRect& dst,
const SkPaint* paint) override {
if (auto image = SkImage::MakeFromBitmap(bitmap)) {
this->onDrawImageNine(image.get(), center, dst, paint);
}
}
void onDrawBitmapLattice(const SkBitmap& bitmap,
const Lattice& lattice, const SkRect& dst,
const SkPaint* paint) override {
if (auto image = SkImage::MakeFromBitmap(bitmap)) {
this->onDrawImageLattice(image.get(), lattice, dst, paint);
}
}
// TODO: May not be ideal to unfurl pictures.
void onDrawPicture(const SkPicture* pic,
const SkMatrix* matrix,
const SkPaint* paint) override {
SkTLazy<SkPaint> lazy;
SkCanvas::onDrawPicture(pic, matrix, this->xform(paint, &lazy));
}
void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
SkTLazy<SkPaint> lazy;
SkCanvas::onDrawDrawable(drawable, matrix);
}
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
SkTLazy<SkPaint> lazy;
fTarget->saveLayer({
rec.fBounds,
this->xform(rec.fPaint, &lazy),
rec.fBackdrop, // TODO: this is an image filter
rec.fSaveLayerFlags,
});
return kNoLayer_SaveLayerStrategy;
}
// Everything from here on should be uninteresting strictly proxied state-change calls.
void willSave() override { fTarget->save(); }
void willRestore() override { fTarget->restore(); }
void didConcat (const SkMatrix& m) override { fTarget->concat (m); }
void didSetMatrix(const SkMatrix& m) override { fTarget->setMatrix(m); }
void onClipRect(const SkRect& clip, SkClipOp op, ClipEdgeStyle style) override {
SkCanvas::onClipRect(clip, op, style);
fTarget->clipRect(clip, op, style);
}
void onClipRRect(const SkRRect& clip, SkClipOp op, ClipEdgeStyle style) override {
SkCanvas::onClipRRect(clip, op, style);
fTarget->clipRRect(clip, op, style);
}
void onClipPath(const SkPath& clip, SkClipOp op, ClipEdgeStyle style) override {
SkCanvas::onClipPath(clip, op, style);
fTarget->clipPath(clip, op, style);
}
void onClipRegion(const SkRegion& clip, SkClipOp op) override {
SkCanvas::onClipRegion(clip, op);
fTarget->clipRegion(clip, op);
}
void onDrawAnnotation(const SkRect& rect, const char* key, SkData* val) override {
fTarget->drawAnnotation(rect, key, val);
}
sk_sp<SkSurface> onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) override {
return fTarget->makeSurface(info, &props);
}
private:
SkCanvas* fTarget;
sk_sp<SkColorSpace> fTargetCS;
std::unique_ptr<SkColorSpaceXform> fFromSRGB;
};
std::unique_ptr<SkCanvas> SkCreateColorSpaceXformCanvas(SkCanvas* target,
sk_sp<SkColorSpace> targetCS) {
return skstd::make_unique<SkColorSpaceXformCanvas>(target, std::move(targetCS));
}