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:
parent
578ef2847b
commit
84dd183a82
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user