Implement Color Filters for SKIA SVG backend

Implement color filters for SKIA SVG Backend using SkColorFilter object in SkPaint.
Color filter is applied using the combination of feFlood and feComposite SVG primitives.
This change only implements blend mode kSrcIn in SkBlendMode object.

R=fmalita@chromium.org

Bug: skia::7914
Change-Id: I0b0dcaf72b654b8ba7f1fd7de52141decfdf17b6
Reviewed-on: https://skia-review.googlesource.com/142986
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Anas AlJabri 2018-07-31 15:53:05 -07:00 committed by Skia Commit-Bot
parent 578ef2847b
commit 84dd183a82
2 changed files with 101 additions and 1 deletions

View File

@ -10,9 +10,11 @@
#include "SkAnnotationKeys.h"
#include "SkBase64.h"
#include "SkBitmap.h"
#include "SkBlendMode.h"
#include "SkChecksum.h"
#include "SkClipOpPriv.h"
#include "SkClipStack.h"
#include "SkColorFilter.h"
#include "SkData.h"
#include "SkDraw.h"
#include "SkImage.h"
@ -117,6 +119,7 @@ struct Resources {
SkString fPaintServer;
SkString fClip;
SkString fColorFilter;
};
static SkTypeface::Encoding to_encoding(SkPaint::TextEncoding e) {
@ -272,7 +275,12 @@ bool RequiresViewportReset(const SkPaint& paint) {
class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
public:
ResourceBucket()
: fGradientCount(0), fClipCount(0), fPathCount(0), fImageCount(0), fPatternCount(0) {}
: fGradientCount(0)
, fClipCount(0)
, fPathCount(0)
, fImageCount(0)
, fPatternCount(0)
, fColorFilterCount(0) {}
SkString addLinearGradient() {
return SkStringPrintf("gradient_%d", fGradientCount++);
@ -290,6 +298,8 @@ public:
return SkStringPrintf("img_%d", fImageCount++);
}
SkString addColorFilter() { return SkStringPrintf("cfilter_%d", fColorFilterCount++); }
SkString addPattern() {
return SkStringPrintf("pattern_%d", fPatternCount++);
}
@ -300,6 +310,7 @@ private:
uint32_t fPathCount;
uint32_t fImageCount;
uint32_t fPatternCount;
uint32_t fColorFilterCount;
};
struct SkSVGDevice::MxCp {
@ -375,6 +386,7 @@ private:
void addShaderResources(const SkPaint& paint, Resources* resources);
void addGradientShaderResources(const SkShader* shader, const SkPaint& paint,
Resources* resources);
void addColorFilterResources(const SkColorFilter& cf, Resources* resources);
void addImageShaderResources(const SkShader* shader, const SkPaint& paint,
Resources* resources);
@ -403,6 +415,10 @@ void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r
this->addAttribute("fill", "none");
}
if (!resources.fColorFilter.isEmpty()) {
this->addAttribute("filter", resources.fColorFilter.c_str());
}
if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) {
this->addAttribute("stroke", resources.fPaintServer);
@ -454,6 +470,13 @@ Resources SkSVGDevice::AutoElement::addResources(const MxCp& mc, const SkPaint&
}
}
if (const SkColorFilter* cf = paint.getColorFilter()) {
// TODO: Implement skia color filters for blend modes other than SrcIn
SkBlendMode mode;
if (cf->asColorMode(nullptr, &mode) && mode == SkBlendMode::kSrcIn) {
this->addColorFilterResources(*cf, &resources);
}
}
return resources;
}
@ -480,6 +503,41 @@ void SkSVGDevice::AutoElement::addGradientShaderResources(const SkShader* shader
resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).c_str());
}
void SkSVGDevice::AutoElement::addColorFilterResources(const SkColorFilter& cf,
Resources* resources) {
SkString colorfilterID = fResourceBucket->addColorFilter();
{
AutoElement filterElement("filter", fWriter);
filterElement.addAttribute("id", colorfilterID);
filterElement.addAttribute("x", "0%");
filterElement.addAttribute("y", "0%");
filterElement.addAttribute("width", "100%");
filterElement.addAttribute("height", "100%");
SkColor filterColor;
SkBlendMode mode;
bool asColorMode = cf.asColorMode(&filterColor, &mode);
SkAssertResult(asColorMode);
SkASSERT(mode == SkBlendMode::kSrcIn);
{
// first flood with filter color
AutoElement floodElement("feFlood", fWriter);
floodElement.addAttribute("flood-color", svg_color(filterColor));
floodElement.addAttribute("flood-opacity", svg_opacity(filterColor));
floodElement.addAttribute("result", "flood");
}
{
// apply the transform to filter color
AutoElement compositeElement("feComposite", fWriter);
compositeElement.addAttribute("in", "flood");
compositeElement.addAttribute("operator", "in");
}
}
resources->fColorFilter.printf("url(#%s)", colorfilterID.c_str());
}
// Returns data uri from bytes.
// it will use any cached data if available, otherwise will
// encode as png.

View File

@ -5,8 +5,17 @@
* found in the LICENSE file.
*/
#define ABORT_TEST(r, cond, ...) \
do { \
if (cond) { \
REPORT_FAILURE(r, #cond, SkStringPrintf(__VA_ARGS__)); \
return; \
} \
} while (0)
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorFilter.h"
#include "SkData.h"
#include "SkImage.h"
#include "SkImageShader.h"
@ -340,4 +349,37 @@ DEF_TEST(SVGDevice_image_shader_tileboth, reporter) {
REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "height")) == imageHeight);
}
DEF_TEST(SVGDevice_ColorFilters, reporter) {
SkDOM dom;
SkPaint paint;
paint.setColorFilter(SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kSrcIn));
{
SkXMLParserWriter writer(dom.beginParsing());
std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
SkRect bounds{0, 0, SkIntToScalar(100), SkIntToScalar(100)};
svgCanvas->drawRect(bounds, paint);
}
const SkDOM::Node* rootElement = dom.finishParsing();
ABORT_TEST(reporter, !rootElement, "root element not found");
const SkDOM::Node* filterElement = dom.getFirstChild(rootElement, "filter");
ABORT_TEST(reporter, !filterElement, "filter element not found");
const SkDOM::Node* floodElement = dom.getFirstChild(filterElement, "feFlood");
ABORT_TEST(reporter, !floodElement, "feFlood element not found");
const SkDOM::Node* compositeElement = dom.getFirstChild(filterElement, "feComposite");
ABORT_TEST(reporter, !compositeElement, "feComposite element not found");
REPORTER_ASSERT(reporter, strcmp(dom.findAttr(filterElement, "width"), "100%") == 0);
REPORTER_ASSERT(reporter, strcmp(dom.findAttr(filterElement, "height"), "100%") == 0);
REPORTER_ASSERT(reporter,
strcmp(dom.findAttr(floodElement, "flood-color"), "rgb(255,0,0)") == 0);
REPORTER_ASSERT(reporter, atoi(dom.findAttr(floodElement, "flood-opacity")) == 1);
REPORTER_ASSERT(reporter, strcmp(dom.findAttr(compositeElement, "in"), "flood") == 0);
REPORTER_ASSERT(reporter, strcmp(dom.findAttr(compositeElement, "operator"), "in") == 0);
}
#endif