From 841101d348bbfae2ca44e0a0f267c974d38c9891 Mon Sep 17 00:00:00 2001 From: Mike Klein Date: Fri, 10 Mar 2017 09:55:51 -0500 Subject: SkColorSpaceXformCanvas TODO: images shaders color filters image filters a couple stray color arrays Change-Id: Ib91639bb0a6a00af737dd5186180011fe5120860 Reviewed-on: https://skia-review.googlesource.com/9529 Reviewed-by: Brian Osman Reviewed-by: Matt Sarett Commit-Queue: Mike Klein --- dm/DM.cpp | 6 + dm/DMSrcSink.cpp | 13 ++ dm/DMSrcSink.h | 10 +- gn/core.gni | 1 + include/core/SkColorSpaceXformCanvas.h | 20 +++ src/core/SkColorSpaceXformCanvas.cpp | 288 +++++++++++++++++++++++++++++++++ 6 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 include/core/SkColorSpaceXformCanvas.h create mode 100644 src/core/SkColorSpaceXformCanvas.cpp diff --git a/dm/DM.cpp b/dm/DM.cpp index bec5dd3ddc..cc662bc44f 100644 --- a/dm/DM.cpp +++ b/dm/DM.cpp @@ -890,8 +890,14 @@ static Sink* create_sink(const GrContextOptions& grCtxOptions, const SkCommandLi return nullptr; } +static sk_sp adobe_rgb() { + return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kAdobeRGB_Gamut); +} + static Sink* create_via(const SkString& tag, Sink* wrapped) { #define VIA(t, via, ...) if (tag.equals(t)) { return new via(__VA_ARGS__); } + VIA("adobe", ViaCSXform, wrapped, adobe_rgb()); VIA("lite", ViaLite, wrapped); VIA("pipe", ViaPipe, wrapped); VIA("twice", ViaTwice, wrapped); diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 6561ad9da3..4f5cd43af8 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -13,6 +13,7 @@ #include "SkCodecImageGenerator.h" #include "SkColorSpace.h" #include "SkColorSpaceXform.h" +#include "SkColorSpaceXformCanvas.h" #include "SkColorSpace_XYZ.h" #include "SkCommonFlags.h" #include "SkData.h" @@ -1839,4 +1840,16 @@ Error ViaLite::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin }); } +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +ViaCSXform::ViaCSXform(Sink* sink, sk_sp cs) : Via(sink), fCS(std::move(cs)) {} + +Error ViaCSXform::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const { + return draw_to_canvas(fSink.get(), bitmap, stream, log, src.size(), + [&](SkCanvas* canvas) -> Error { + auto proxy = SkCreateColorSpaceXformCanvas(canvas, fCS); + return src.draw(proxy.get()); + }); +} + } // namespace DM diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index d4fe29de4e..c973e3b393 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -349,7 +349,7 @@ public: class PipeSink : public Sink { public: PipeSink(); - + Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; const char* fileExtension() const override { return "skpipe"; } SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } @@ -494,6 +494,14 @@ public: Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; }; +class ViaCSXform : public Via { +public: + explicit ViaCSXform(Sink*, sk_sp); + Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; +private: + sk_sp fCS; +}; + } // namespace DM #endif//DMSrcSink_DEFINED diff --git a/gn/core.gni b/gn/core.gni index 3474843508..59d71ab405 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -86,6 +86,7 @@ skia_core_sources = [ "$_src/core/SkColorSpace_XYZ.h", "$_src/core/SkColorSpace_ICC.cpp", "$_src/core/SkColorSpaceXform.cpp", + "$_src/core/SkColorSpaceXformCanvas.cpp", "$_src/core/SkColorSpaceXform_A2B.cpp", "$_src/core/SkColorSpaceXform_A2B.h", "$_src/core/SkColorTable.cpp", diff --git a/include/core/SkColorSpaceXformCanvas.h b/include/core/SkColorSpaceXformCanvas.h new file mode 100644 index 0000000000..08981703c5 --- /dev/null +++ b/include/core/SkColorSpaceXformCanvas.h @@ -0,0 +1,20 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorSpaceXformCanvas_DEFINED +#define SkColorSpaceXformCanvas_DEFINED + +#include +#include +#include + +// Proxy SkCanvas calls to unowned target, transforming colors into targetCS as it goes. +std::unique_ptr SkCreateColorSpaceXformCanvas(SkCanvas* target, + sk_sp targetCS); + + +#endif//SkColorSpaceXformCanvas_DEFINED diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp new file mode 100644 index 0000000000..7267a2893f --- /dev/null +++ b/src/core/SkColorSpaceXformCanvas.cpp @@ -0,0 +1,288 @@ +/* + * 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 "SkColorSpaceXform.h" +#include "SkColorSpaceXformCanvas.h" +#include "SkMakeUnique.h" +#include "SkNoDrawCanvas.h" +#include "SkSurface.h" +#include "SkTLazy.h" + +class SkColorSpaceXformCanvas : public SkNoDrawCanvas { +public: + SkColorSpaceXformCanvas(SkCanvas* target, + sk_sp targetCS) + : SkNoDrawCanvas(SkIRect::MakeSize(target->getBaseLayerSize())) + , fTarget(target) + , fTargetCS(std::move(targetCS)) { + + fFromSRGB = SkColorSpaceXform::New(SkColorSpace::MakeSRGB().get(), fTargetCS.get()); + } + + SkColor xform(SkColor srgb) const { + SkColor xformed; + SkAssertResult(fFromSRGB->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &xformed, + SkColorSpaceXform::kBGRA_8888_ColorFormat, &srgb, + 1, kUnpremul_SkAlphaType)); + return xformed; + } + + const SkPaint& xform(const SkPaint& paint, SkTLazy* 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())); + } + + // TODO: + // - shaders + // - color filters + // - image filters? + + return *result; + } + + sk_sp xform(const SkImage* img) const { + // TODO: for real + return sk_ref_sp(img); + } + + const SkPaint* xform(const SkPaint* paint, SkTLazy* lazy) const { + return paint ? &this->xform(*paint, lazy) : nullptr; + } + + void onDrawPaint(const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawPaint(this->xform(paint, &lazy)); + } + + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawRect(rect, this->xform(paint, &lazy)); + } + void onDrawOval(const SkRect& oval, const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawOval(oval, this->xform(paint, &lazy)); + } + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawRRect(rrect, this->xform(paint, &lazy)); + } + void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawDRRect(outer, inner, this->xform(paint, &lazy)); + } + void onDrawPath(const SkPath& path, const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawPath(path, this->xform(paint, &lazy)); + } + void onDrawArc(const SkRect& oval, SkScalar start, SkScalar sweep, bool useCenter, + const SkPaint& paint) override { + SkTLazy lazy; + fTarget->drawArc(oval, start, sweep, useCenter, this->xform(paint, &lazy)); + } + void onDrawRegion(const SkRegion& region, const SkPaint& paint) override { + SkTLazy 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 { + // TODO: colors + SkTLazy 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 lazy; + fTarget->drawPoints(mode, count, pts, this->xform(paint, &lazy)); + } + void onDrawVertices(VertexMode vmode, int count, + const SkPoint* verts, const SkPoint* texs, const SkColor* colors, + SkBlendMode mode, + const uint16_t* indices, int indexCount, const SkPaint& paint) override { + // TODO: colors + SkTLazy lazy; + fTarget->drawVertices(vmode, count, verts, texs, colors, mode, indices, indexCount, + this->xform(paint, &lazy)); + } + + void onDrawText(const void* ptr, size_t len, + SkScalar x, SkScalar y, + const SkPaint& paint) override { + SkTLazy 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 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 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 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 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 lazy; + fTarget->drawTextBlob(blob, x, y, this->xform(paint, &lazy)); + } + + void onDrawImage(const SkImage* img, + SkScalar l, SkScalar t, + const SkPaint* paint) override { + SkTLazy 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 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 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 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 { + // TODO: colors + SkTLazy 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 lazy; + SkCanvas::onDrawPicture(pic, matrix, this->xform(paint, &lazy)); + } + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + SkTLazy lazy; + SkCanvas::onDrawDrawable(drawable, matrix); + } + + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override { + SkTLazy 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 { + fTarget->clipRect(clip, op, style); + } + void onClipRRect(const SkRRect& clip, SkClipOp op, ClipEdgeStyle style) override { + fTarget->clipRRect(clip, op, style); + } + void onClipPath(const SkPath& clip, SkClipOp op, ClipEdgeStyle style) override { + fTarget->clipPath(clip, op, style); + } + void onClipRegion(const SkRegion& clip, SkClipOp op) override { + fTarget->clipRegion(clip, op); + } + + void onDrawAnnotation(const SkRect& rect, const char* key, SkData* val) override { + fTarget->drawAnnotation(rect, key, val); + } + + sk_sp onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) override { + return fTarget->makeSurface(info, &props); + } + +private: + SkCanvas* fTarget; + sk_sp fTargetCS; + std::unique_ptr fFromSRGB; +}; + +std::unique_ptr SkCreateColorSpaceXformCanvas(SkCanvas* target, + sk_sp targetCS) { + return skstd::make_unique(target, std::move(targetCS)); +} -- cgit v1.2.3