Home | History | Annotate | Download | only in shadows
      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 "SkSpotShadowMaskFilter.h"
      9 #include "SkReadBuffer.h"
     10 #include "SkStringUtils.h"
     11 #include "SkWriteBuffer.h"
     12 
     13 #if SK_SUPPORT_GPU
     14 #include "GrContext.h"
     15 #include "GrRenderTargetContext.h"
     16 #include "GrFragmentProcessor.h"
     17 #include "GrStyle.h"
     18 #include "GrTexture.h"
     19 #include "GrTextureProxy.h"
     20 #include "SkStrokeRec.h"
     21 #endif
     22 
     23 class SkSpotShadowMaskFilterImpl : public SkMaskFilter {
     24 public:
     25     SkSpotShadowMaskFilterImpl(SkScalar occluderHeight, const SkPoint3& lightPos,
     26                                SkScalar lightRadius, SkScalar spotAlpha, uint32_t flags);
     27 
     28     // overrides from SkMaskFilter
     29     SkMask::Format getFormat() const override;
     30     bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
     31                     SkIPoint* margin) const override;
     32 
     33 #if SK_SUPPORT_GPU
     34     bool canFilterMaskGPU(const SkRRect& devRRect,
     35                           const SkIRect& clipBounds,
     36                           const SkMatrix& ctm,
     37                           SkRect* maskRect) const override;
     38     bool directFilterMaskGPU(GrContext*,
     39                              GrRenderTargetContext* drawContext,
     40                              GrPaint&&,
     41                              const GrClip&,
     42                              const SkMatrix& viewMatrix,
     43                              const SkStrokeRec& strokeRec,
     44                              const SkPath& path) const override;
     45     bool directFilterRRectMaskGPU(GrContext*,
     46                                   GrRenderTargetContext* drawContext,
     47                                   GrPaint&&,
     48                                   const GrClip&,
     49                                   const SkMatrix& viewMatrix,
     50                                   const SkStrokeRec& strokeRec,
     51                                   const SkRRect& rrect,
     52                                   const SkRRect& devRRect) const override;
     53     sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
     54                                         sk_sp<GrTextureProxy> srcProxy,
     55                                         const SkMatrix& ctm,
     56                                         const SkIRect& maskRect) const override;
     57 #endif
     58 
     59     void computeFastBounds(const SkRect&, SkRect*) const override;
     60 
     61     SK_TO_STRING_OVERRIDE()
     62     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotShadowMaskFilterImpl)
     63 
     64 private:
     65     SkScalar fOccluderHeight;
     66     SkPoint3 fLightPos;
     67     SkScalar fLightRadius;
     68     SkScalar fSpotAlpha;
     69     uint32_t fFlags;
     70 
     71     SkSpotShadowMaskFilterImpl(SkReadBuffer&);
     72     void flatten(SkWriteBuffer&) const override;
     73 
     74     friend class SkSpotShadowMaskFilter;
     75 
     76     typedef SkMaskFilter INHERITED;
     77 };
     78 
     79 sk_sp<SkMaskFilter> SkSpotShadowMaskFilter::Make(SkScalar occluderHeight, const SkPoint3& lightPos,
     80                                                  SkScalar lightRadius, SkScalar spotAlpha,
     81                                                  uint32_t flags) {
     82     // add some param checks here for early exit
     83 
     84     return sk_sp<SkMaskFilter>(new SkSpotShadowMaskFilterImpl(occluderHeight, lightPos,
     85                                                               lightRadius, spotAlpha, flags));
     86 }
     87 
     88 ///////////////////////////////////////////////////////////////////////////////////////////////////
     89 
     90 SkSpotShadowMaskFilterImpl::SkSpotShadowMaskFilterImpl(SkScalar occluderHeight,
     91                                                        const SkPoint3& lightPos,
     92                                                        SkScalar lightRadius,
     93                                                        SkScalar spotAlpha,
     94                                                        uint32_t flags)
     95     : fOccluderHeight(occluderHeight)
     96     , fLightPos(lightPos)
     97     , fLightRadius(lightRadius)
     98     , fSpotAlpha(spotAlpha)
     99     , fFlags(flags) {
    100     SkASSERT(fOccluderHeight > 0);
    101     SkASSERT(fLightPos.z() > 0 && fLightPos.z() > fOccluderHeight);
    102     SkASSERT(fLightRadius > 0);
    103     SkASSERT(fSpotAlpha >= 0);
    104 }
    105 
    106 SkMask::Format SkSpotShadowMaskFilterImpl::getFormat() const {
    107     return SkMask::kA8_Format;
    108 }
    109 
    110 bool SkSpotShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
    111                                             const SkMatrix& matrix,
    112                                             SkIPoint* margin) const {
    113     // TODO something
    114     return false;
    115 }
    116 
    117 void SkSpotShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
    118     // TODO compute based on ambient + spot data
    119     dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
    120 }
    121 
    122 sk_sp<SkFlattenable> SkSpotShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
    123     const SkScalar occluderHeight = buffer.readScalar();
    124     const SkScalar lightX = buffer.readScalar();
    125     const SkScalar lightY = buffer.readScalar();
    126     const SkScalar lightZ = buffer.readScalar();
    127     const SkPoint3 lightPos = SkPoint3::Make(lightX, lightY, lightZ);
    128     const SkScalar lightRadius = buffer.readScalar();
    129     const SkScalar spotAlpha = buffer.readScalar();
    130     const uint32_t flags = buffer.readUInt();
    131 
    132     return SkSpotShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
    133                                         spotAlpha, flags);
    134 }
    135 
    136 void SkSpotShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
    137     buffer.writeScalar(fOccluderHeight);
    138     buffer.writeScalar(fLightPos.fX);
    139     buffer.writeScalar(fLightPos.fY);
    140     buffer.writeScalar(fLightPos.fZ);
    141     buffer.writeScalar(fLightRadius);
    142     buffer.writeScalar(fSpotAlpha);
    143     buffer.writeUInt(fFlags);
    144 }
    145 
    146 #if SK_SUPPORT_GPU
    147 
    148 ///////////////////////////////////////////////////////////////////////////////////////////////////
    149 
    150 bool SkSpotShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
    151                                                   const SkIRect& clipBounds,
    152                                                   const SkMatrix& ctm,
    153                                                   SkRect* maskRect) const {
    154     // TODO
    155     *maskRect = devRRect.rect();
    156     return true;
    157 }
    158 
    159 bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
    160                                                      GrRenderTargetContext* rtContext,
    161                                                      GrPaint&& paint,
    162                                                      const GrClip& clip,
    163                                                      const SkMatrix& viewMatrix,
    164                                                      const SkStrokeRec& strokeRec,
    165                                                      const SkPath& path) const {
    166     SkASSERT(rtContext);
    167     // TODO: this will not handle local coordinates properly
    168 
    169     if (fSpotAlpha <= 0.0f) {
    170         return true;
    171     }
    172 
    173     // only convex paths for now
    174     if (!path.isConvex()) {
    175         return false;
    176     }
    177 
    178     if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
    179         return false;
    180     }
    181 
    182     // if circle
    183     // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
    184     // have our own GeometryProc.
    185     if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
    186         SkRRect rrect = SkRRect::MakeOval(path.getBounds());
    187         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
    188                                               SkMatrix::I(), strokeRec, rrect, rrect);
    189     } else if (path.isRect(nullptr)) {
    190         SkRRect rrect = SkRRect::MakeRect(path.getBounds());
    191         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
    192                                               SkMatrix::I(), strokeRec, rrect, rrect);
    193     }
    194 
    195     return false;
    196 }
    197 
    198 bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
    199                                                           GrRenderTargetContext* rtContext,
    200                                                           GrPaint&& paint,
    201                                                           const GrClip& clip,
    202                                                           const SkMatrix& viewMatrix,
    203                                                           const SkStrokeRec& strokeRec,
    204                                                           const SkRRect& rrect,
    205                                                           const SkRRect& devRRect) const {
    206     // It's likely the caller has already done these checks, but we have to be sure.
    207     // TODO: support analytic blurring of general rrect
    208 
    209     // Fast path only supports filled rrects for now.
    210     // TODO: fill and stroke as well.
    211     if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
    212         return false;
    213     }
    214     // Fast path only supports simple rrects with circular corners.
    215     SkASSERT(devRRect.allCornersCircular());
    216     if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
    217         return false;
    218     }
    219     // Fast path only supports uniform scale.
    220     SkScalar scaleFactors[2];
    221     if (!viewMatrix.getMinMaxScales(scaleFactors)) {
    222         // matrix is degenerate
    223         return false;
    224     }
    225     if (scaleFactors[0] != scaleFactors[1]) {
    226         return false;
    227     }
    228     SkScalar scaleFactor = scaleFactors[0];
    229 
    230     // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
    231     const SkScalar minRadius = 0.5f / scaleFactor;
    232     bool isRect = rrect.getSimpleRadii().fX <= minRadius;
    233 
    234     // TODO: take flags into account when generating shadow data
    235 
    236     if (fSpotAlpha > 0.0f) {
    237         float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
    238 
    239         SkScalar srcSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
    240 
    241         SkRRect spotRRect;
    242         if (isRect) {
    243             spotRRect = SkRRect::MakeRectXY(rrect.rect(), minRadius, minRadius);
    244         } else {
    245             spotRRect = rrect;
    246         }
    247 
    248         SkRRect spotShadowRRect;
    249         // Compute the scale and translation for the spot shadow.
    250         const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
    251         spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
    252 
    253         SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
    254                                        spotShadowRRect.rect().centerY());
    255         SkMatrix ctmInverse;
    256         if (!viewMatrix.invert(&ctmInverse)) {
    257             SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
    258             //**** TODO: this is not good
    259             return true;
    260         }
    261         SkPoint lightPos2D = SkPoint::Make(fLightPos.fX, fLightPos.fY);
    262         ctmInverse.mapPoints(&lightPos2D, 1);
    263         const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
    264                                                  zRatio*(center.fY - lightPos2D.fY));
    265 
    266         // We want to extend the stroked area in so that it meets up with the caster
    267         // geometry. The stroked geometry will, by definition already be inset half the
    268         // stroke width but we also have to account for the scaling.
    269         SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(rrect.rect().fLeft),
    270                                                               SkTAbs(rrect.rect().fRight)),
    271                                                        SkTMax(SkTAbs(rrect.rect().fTop),
    272                                                               SkTAbs(rrect.rect().fBottom)));
    273         SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) + scaleOffset;
    274 
    275         // Compute area
    276         SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
    277         SkScalar strokedArea = 2.0f*strokeWidth *
    278                                (spotShadowRRect.width() + spotShadowRRect.height());
    279         SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius) *
    280                               (spotShadowRRect.width() + srcSpaceSpotRadius);
    281 
    282         GrColor4f color = paint.getColor4f();
    283         color.fRGBA[3] *= fSpotAlpha;
    284         paint.setColor4f(color);
    285 
    286         SkStrokeRec spotStrokeRec(SkStrokeRec::kFill_InitStyle);
    287         // If the area of the stroked geometry is larger than the fill geometry,
    288         // or if the caster is transparent, just fill it.
    289         if (strokedArea > filledArea ||
    290             fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
    291             spotStrokeRec.setStrokeStyle(srcSpaceSpotRadius, true);
    292         } else {
    293             // Since we can't have unequal strokes, inset the shadow rect so the inner
    294             // and outer edges of the stroke will land where we want.
    295             SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount / 2.0f,
    296                                                                 insetAmount / 2.0f);
    297             SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount / 2.0f,
    298                                        minRadius);
    299             spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
    300             spotStrokeRec.setStrokeStyle(strokeWidth, false);
    301         }
    302 
    303         // handle scale of radius and pad due to CTM
    304         const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
    305 
    306         spotShadowRRect.offset(spotOffset.fX, spotOffset.fY);
    307 
    308         rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
    309                                    devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
    310     }
    311 
    312     return true;
    313 }
    314 
    315 sk_sp<GrTextureProxy> SkSpotShadowMaskFilterImpl::filterMaskGPU(GrContext*,
    316                                                                 sk_sp<GrTextureProxy> srcProxy,
    317                                                                 const SkMatrix& ctm,
    318                                                                 const SkIRect& maskRect) const {
    319     // This filter is generative and doesn't operate on pre-existing masks
    320     return nullptr;
    321 }
    322 
    323 #endif
    324 
    325 #ifndef SK_IGNORE_TO_STRING
    326 void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
    327     str->append("SkSpotShadowMaskFilterImpl: (");
    328 
    329     str->append("occluderHeight: ");
    330     str->appendScalar(fOccluderHeight);
    331     str->append(" ");
    332 
    333     str->append("lightPos: (");
    334     str->appendScalar(fLightPos.fX);
    335     str->append(", ");
    336     str->appendScalar(fLightPos.fY);
    337     str->append(", ");
    338     str->appendScalar(fLightPos.fZ);
    339     str->append(") ");
    340 
    341     str->append("lightRadius: ");
    342     str->appendScalar(fLightRadius);
    343     str->append(" ");
    344 
    345     str->append("spotAlpha: ");
    346     str->appendScalar(fSpotAlpha);
    347     str->append(" ");
    348 
    349     str->append("flags: (");
    350     if (fFlags) {
    351         bool needSeparator = false;
    352         SkAddFlagToString(str,
    353                           SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
    354                           "TransparentOccluder", &needSeparator);
    355         SkAddFlagToString(str,
    356                           SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
    357                           "GaussianEdge", &needSeparator);
    358         SkAddFlagToString(str,
    359                           SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
    360                           "LargerUmbra", &needSeparator);
    361     } else {
    362         str->append("None");
    363     }
    364     str->append("))");
    365 }
    366 #endif
    367 
    368 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkSpotShadowMaskFilter)
    369 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotShadowMaskFilterImpl)
    370 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
    371