Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkSVGGradient.h"
      9 #include "SkSVGRenderContext.h"
     10 #include "SkSVGStop.h"
     11 #include "SkSVGValue.h"
     12 
     13 void SkSVGGradient::setHref(const SkSVGStringType& href) {
     14     fHref = std::move(href);
     15 }
     16 
     17 void SkSVGGradient::setGradientTransform(const SkSVGTransformType& t) {
     18     fGradientTransform = t;
     19 }
     20 
     21 void SkSVGGradient::setSpreadMethod(const SkSVGSpreadMethod& spread) {
     22     fSpreadMethod = spread;
     23 }
     24 
     25 void SkSVGGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
     26     switch (attr) {
     27     case SkSVGAttribute::kGradientTransform:
     28         if (const auto* t = v.as<SkSVGTransformValue>()) {
     29             this->setGradientTransform(*t);
     30         }
     31         break;
     32     case SkSVGAttribute::kHref:
     33         if (const auto* href = v.as<SkSVGStringValue>()) {
     34             this->setHref(*href);
     35         }
     36         break;
     37     case SkSVGAttribute::kSpreadMethod:
     38         if (const auto* spread = v.as<SkSVGSpreadMethodValue>()) {
     39             this->setSpreadMethod(*spread);
     40         }
     41         break;
     42     default:
     43         this->INHERITED::onSetAttribute(attr, v);
     44     }
     45 }
     46 
     47 // https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementHrefAttribute
     48 void SkSVGGradient::collectColorStops(const SkSVGRenderContext& ctx,
     49                                       StopPositionArray* pos,
     50                                       StopColorArray* colors) const {
     51     // Used to resolve percentage offsets.
     52     const SkSVGLengthContext ltx(SkSize::Make(1, 1));
     53 
     54     for (const auto& child : fChildren) {
     55         if (child->tag() != SkSVGTag::kStop) {
     56             continue;
     57         }
     58 
     59         const auto& stop = static_cast<const SkSVGStop&>(*child);
     60         colors->push_back(SkColorSetA(stop.stopColor(),
     61                                       SkScalarRoundToInt(stop.stopOpacity() * 255)));
     62         pos->push_back(SkTPin(ltx.resolve(stop.offset(), SkSVGLengthContext::LengthType::kOther),
     63                               0.f, 1.f));
     64     }
     65 
     66     SkASSERT(colors->count() == pos->count());
     67 
     68     if (pos->empty() && !fHref.value().isEmpty()) {
     69         const auto* ref = ctx.findNodeById(fHref);
     70         if (ref && (ref->tag() == SkSVGTag::kLinearGradient ||
     71                     ref->tag() == SkSVGTag::kRadialGradient)) {
     72             static_cast<const SkSVGGradient*>(ref)->collectColorStops(ctx, pos, colors);
     73         }
     74     }
     75 }
     76 
     77 bool SkSVGGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
     78     StopColorArray colors;
     79     StopPositionArray pos;
     80 
     81     this->collectColorStops(ctx, &pos, &colors);
     82 
     83     // TODO:
     84     //       * stop (lazy?) sorting
     85     //       * href loop detection
     86     //       * href attribute inheritance (not just color stops)
     87     //       * objectBoundingBox units support
     88 
     89     static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kPad) ==
     90                   SkShader::kClamp_TileMode, "SkSVGSpreadMethod::Type is out of sync");
     91     static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kRepeat) ==
     92                   SkShader::kRepeat_TileMode, "SkSVGSpreadMethod::Type is out of sync");
     93     static_assert(static_cast<SkShader::TileMode>(SkSVGSpreadMethod::Type::kReflect) ==
     94                   SkShader::kMirror_TileMode, "SkSVGSpreadMethod::Type is out of sync");
     95     const auto tileMode = static_cast<SkShader::TileMode>(fSpreadMethod.type());
     96 
     97     paint->setShader(this->onMakeShader(ctx, colors.begin(), pos.begin(), colors.count(), tileMode,
     98                                         fGradientTransform.value()));
     99     return true;
    100 }
    101