gfx/skia/trunk/src/gpu/GrClipMaskManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/gpu/GrClipMaskManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1153 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2012 Google Inc.
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +#include "GrClipMaskManager.h"
    1.13 +#include "GrAAConvexPathRenderer.h"
    1.14 +#include "GrAAHairLinePathRenderer.h"
    1.15 +#include "GrAARectRenderer.h"
    1.16 +#include "GrDrawTargetCaps.h"
    1.17 +#include "GrGpu.h"
    1.18 +#include "GrPaint.h"
    1.19 +#include "GrPathRenderer.h"
    1.20 +#include "GrRenderTarget.h"
    1.21 +#include "GrStencilBuffer.h"
    1.22 +#include "GrSWMaskHelper.h"
    1.23 +#include "effects/GrTextureDomain.h"
    1.24 +#include "effects/GrConvexPolyEffect.h"
    1.25 +#include "effects/GrRRectEffect.h"
    1.26 +#include "SkRasterClip.h"
    1.27 +#include "SkStrokeRec.h"
    1.28 +#include "SkTLazy.h"
    1.29 +
    1.30 +#define GR_AA_CLIP 1
    1.31 +
    1.32 +typedef SkClipStack::Element Element;
    1.33 +
    1.34 +using namespace GrReducedClip;
    1.35 +
    1.36 +////////////////////////////////////////////////////////////////////////////////
    1.37 +namespace {
    1.38 +// set up the draw state to enable the aa clipping mask. Besides setting up the
    1.39 +// stage matrix this also alters the vertex layout
    1.40 +void setup_drawstate_aaclip(GrGpu* gpu,
    1.41 +                            GrTexture* result,
    1.42 +                            const SkIRect &devBound) {
    1.43 +    GrDrawState* drawState = gpu->drawState();
    1.44 +    SkASSERT(drawState);
    1.45 +
    1.46 +    SkMatrix mat;
    1.47 +    // We want to use device coords to compute the texture coordinates. We set our matrix to be
    1.48 +    // equal to the view matrix followed by an offset to the devBound, and then a scaling matrix to
    1.49 +    // normalized coords. We apply this matrix to the vertex positions rather than local coords.
    1.50 +    mat.setIDiv(result->width(), result->height());
    1.51 +    mat.preTranslate(SkIntToScalar(-devBound.fLeft),
    1.52 +                     SkIntToScalar(-devBound.fTop));
    1.53 +    mat.preConcat(drawState->getViewMatrix());
    1.54 +
    1.55 +    SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
    1.56 +    // This could be a long-lived effect that is cached with the alpha-mask.
    1.57 +    drawState->addCoverageEffect(
    1.58 +        GrTextureDomainEffect::Create(result,
    1.59 +                                      mat,
    1.60 +                                      GrTextureDomain::MakeTexelDomain(result, domainTexels),
    1.61 +                                      GrTextureDomain::kDecal_Mode,
    1.62 +                                      GrTextureParams::kNone_FilterMode,
    1.63 +                                      kPosition_GrCoordSet))->unref();
    1.64 +}
    1.65 +
    1.66 +bool path_needs_SW_renderer(GrContext* context,
    1.67 +                            GrGpu* gpu,
    1.68 +                            const SkPath& origPath,
    1.69 +                            const SkStrokeRec& stroke,
    1.70 +                            bool doAA) {
    1.71 +    // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
    1.72 +    SkTCopyOnFirstWrite<SkPath> path(origPath);
    1.73 +    if (path->isInverseFillType()) {
    1.74 +        path.writable()->toggleInverseFillType();
    1.75 +    }
    1.76 +    // last (false) parameter disallows use of the SW path renderer
    1.77 +    GrPathRendererChain::DrawType type = doAA ?
    1.78 +                                         GrPathRendererChain::kColorAntiAlias_DrawType :
    1.79 +                                         GrPathRendererChain::kColor_DrawType;
    1.80 +
    1.81 +    return NULL == context->getPathRenderer(*path, stroke, gpu, false, type);
    1.82 +}
    1.83 +
    1.84 +}
    1.85 +
    1.86 +/*
    1.87 + * This method traverses the clip stack to see if the GrSoftwarePathRenderer
    1.88 + * will be used on any element. If so, it returns true to indicate that the
    1.89 + * entire clip should be rendered in SW and then uploaded en masse to the gpu.
    1.90 + */
    1.91 +bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) {
    1.92 +
    1.93 +    // TODO: generalize this function so that when
    1.94 +    // a clip gets complex enough it can just be done in SW regardless
    1.95 +    // of whether it would invoke the GrSoftwarePathRenderer.
    1.96 +    SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
    1.97 +
    1.98 +    for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
    1.99 +        const Element* element = iter.get();
   1.100 +        // rects can always be drawn directly w/o using the software path
   1.101 +        // Skip rrects once we're drawing them directly.
   1.102 +        if (Element::kRect_Type != element->getType()) {
   1.103 +            SkPath path;
   1.104 +            element->asPath(&path);
   1.105 +            if (path_needs_SW_renderer(this->getContext(), fGpu, path, stroke, element->isAA())) {
   1.106 +                return true;
   1.107 +            }
   1.108 +        }
   1.109 +    }
   1.110 +    return false;
   1.111 +}
   1.112 +
   1.113 +bool GrClipMaskManager::installClipEffects(const ElementList& elements,
   1.114 +                                           GrDrawState::AutoRestoreEffects* are,
   1.115 +                                           const SkVector& clipToRTOffset,
   1.116 +                                           const SkRect* drawBounds) {
   1.117 +
   1.118 +    GrDrawState* drawState = fGpu->drawState();
   1.119 +    SkRect boundsInClipSpace;
   1.120 +    if (NULL != drawBounds) {
   1.121 +        boundsInClipSpace = *drawBounds;
   1.122 +        boundsInClipSpace.offset(-clipToRTOffset.fX, -clipToRTOffset.fY);
   1.123 +    }
   1.124 +
   1.125 +    are->set(drawState);
   1.126 +    GrRenderTarget* rt = drawState->getRenderTarget();
   1.127 +    ElementList::Iter iter(elements);
   1.128 +
   1.129 +    bool setARE = false;
   1.130 +    bool failed = false;
   1.131 +
   1.132 +    while (NULL != iter.get()) {
   1.133 +        SkRegion::Op op = iter.get()->getOp();
   1.134 +        bool invert;
   1.135 +        bool skip = false;
   1.136 +        switch (op) {
   1.137 +            case SkRegion::kReplace_Op:
   1.138 +                SkASSERT(iter.get() == elements.head());
   1.139 +                // Fallthrough, handled same as intersect.
   1.140 +            case SkRegion::kIntersect_Op:
   1.141 +                invert = false;
   1.142 +                if (NULL != drawBounds && iter.get()->contains(boundsInClipSpace)) {
   1.143 +                    skip = true;
   1.144 +                }
   1.145 +                break;
   1.146 +            case SkRegion::kDifference_Op:
   1.147 +                invert = true;
   1.148 +                // We don't currently have a cheap test for whether a rect is fully outside an
   1.149 +                // element's primitive, so don't attempt to set skip.
   1.150 +                break;
   1.151 +            default:
   1.152 +                failed = true;
   1.153 +                break;
   1.154 +        }
   1.155 +        if (failed) {
   1.156 +            break;
   1.157 +        }
   1.158 +
   1.159 +        if (!skip) {
   1.160 +            GrEffectEdgeType edgeType;
   1.161 +            if (GR_AA_CLIP && iter.get()->isAA()) {
   1.162 +                if (rt->isMultisampled()) {
   1.163 +                    // Coverage based AA clips don't place nicely with MSAA.
   1.164 +                    failed = true;
   1.165 +                    break;
   1.166 +                }
   1.167 +                edgeType = invert ? kInverseFillAA_GrEffectEdgeType : kFillAA_GrEffectEdgeType;
   1.168 +            } else {
   1.169 +                edgeType = invert ? kInverseFillBW_GrEffectEdgeType : kFillBW_GrEffectEdgeType;
   1.170 +            }
   1.171 +            SkAutoTUnref<GrEffectRef> effect;
   1.172 +            switch (iter.get()->getType()) {
   1.173 +                case SkClipStack::Element::kPath_Type:
   1.174 +                    effect.reset(GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(),
   1.175 +                        &clipToRTOffset));
   1.176 +                    break;
   1.177 +                case SkClipStack::Element::kRRect_Type: {
   1.178 +                    SkRRect rrect = iter.get()->getRRect();
   1.179 +                    rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
   1.180 +                    effect.reset(GrRRectEffect::Create(edgeType, rrect));
   1.181 +                    break;
   1.182 +                }
   1.183 +                case SkClipStack::Element::kRect_Type: {
   1.184 +                    SkRect rect = iter.get()->getRect();
   1.185 +                    rect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
   1.186 +                    effect.reset(GrConvexPolyEffect::Create(edgeType, rect));
   1.187 +                    break;
   1.188 +                }
   1.189 +                default:
   1.190 +                    break;
   1.191 +            }
   1.192 +            if (effect) {
   1.193 +                if (!setARE) {
   1.194 +                    are->set(fGpu->drawState());
   1.195 +                    setARE = true;
   1.196 +                }
   1.197 +                fGpu->drawState()->addCoverageEffect(effect);
   1.198 +            } else {
   1.199 +                failed = true;
   1.200 +                break;
   1.201 +            }
   1.202 +        }
   1.203 +        iter.next();
   1.204 +    }
   1.205 +
   1.206 +    if (failed) {
   1.207 +        are->set(NULL);
   1.208 +    }
   1.209 +
   1.210 +    return !failed;
   1.211 +}
   1.212 +
   1.213 +////////////////////////////////////////////////////////////////////////////////
   1.214 +// sort out what kind of clip mask needs to be created: alpha, stencil,
   1.215 +// scissor, or entirely software
   1.216 +bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn,
   1.217 +                                      GrDrawState::AutoRestoreEffects* are,
   1.218 +                                      const SkRect* devBounds) {
   1.219 +    fCurrClipMaskType = kNone_ClipMaskType;
   1.220 +
   1.221 +    ElementList elements(16);
   1.222 +    int32_t genID;
   1.223 +    InitialState initialState;
   1.224 +    SkIRect clipSpaceIBounds;
   1.225 +    bool requiresAA;
   1.226 +    bool isRect = false;
   1.227 +
   1.228 +    GrDrawState* drawState = fGpu->drawState();
   1.229 +
   1.230 +    const GrRenderTarget* rt = drawState->getRenderTarget();
   1.231 +    // GrDrawTarget should have filtered this for us
   1.232 +    SkASSERT(NULL != rt);
   1.233 +
   1.234 +    bool ignoreClip = !drawState->isClipState() || clipDataIn->fClipStack->isWideOpen();
   1.235 +
   1.236 +    if (!ignoreClip) {
   1.237 +        SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
   1.238 +        clipSpaceRTIBounds.offset(clipDataIn->fOrigin);
   1.239 +        ReduceClipStack(*clipDataIn->fClipStack,
   1.240 +                        clipSpaceRTIBounds,
   1.241 +                        &elements,
   1.242 +                        &genID,
   1.243 +                        &initialState,
   1.244 +                        &clipSpaceIBounds,
   1.245 +                        &requiresAA);
   1.246 +        if (elements.isEmpty()) {
   1.247 +            if (kAllIn_InitialState == initialState) {
   1.248 +                ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds;
   1.249 +                isRect = true;
   1.250 +            } else {
   1.251 +                return false;
   1.252 +            }
   1.253 +        }
   1.254 +    }
   1.255 +
   1.256 +    if (ignoreClip) {
   1.257 +        fGpu->disableScissor();
   1.258 +        this->setGpuStencil();
   1.259 +        return true;
   1.260 +    }
   1.261 +
   1.262 +    // An element count of 4 was chosen because of the common pattern in Blink of:
   1.263 +    //   isect RR
   1.264 +    //   diff  RR
   1.265 +    //   isect convex_poly
   1.266 +    //   isect convex_poly
   1.267 +    // when drawing rounded div borders. This could probably be tuned based on a
   1.268 +    // configuration's relative costs of switching RTs to generate a mask vs
   1.269 +    // longer shaders.
   1.270 +    if (elements.count() <= 4) {
   1.271 +        SkVector clipToRTOffset = { SkIntToScalar(-clipDataIn->fOrigin.fX),
   1.272 +                                    SkIntToScalar(-clipDataIn->fOrigin.fY) };
   1.273 +        if (elements.isEmpty() ||
   1.274 +            this->installClipEffects(elements, are, clipToRTOffset, devBounds)) {
   1.275 +            SkIRect scissorSpaceIBounds(clipSpaceIBounds);
   1.276 +            scissorSpaceIBounds.offset(-clipDataIn->fOrigin);
   1.277 +            if (NULL == devBounds ||
   1.278 +                !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) {
   1.279 +                fGpu->enableScissor(scissorSpaceIBounds);
   1.280 +            } else {
   1.281 +                fGpu->disableScissor();
   1.282 +            }
   1.283 +            this->setGpuStencil();
   1.284 +            return true;
   1.285 +        }
   1.286 +    }
   1.287 +
   1.288 +#if GR_AA_CLIP
   1.289 +    // If MSAA is enabled we can do everything in the stencil buffer.
   1.290 +    if (0 == rt->numSamples() && requiresAA) {
   1.291 +        GrTexture* result = NULL;
   1.292 +
   1.293 +        if (this->useSWOnlyPath(elements)) {
   1.294 +            // The clip geometry is complex enough that it will be more efficient to create it
   1.295 +            // entirely in software
   1.296 +            result = this->createSoftwareClipMask(genID,
   1.297 +                                                  initialState,
   1.298 +                                                  elements,
   1.299 +                                                  clipSpaceIBounds);
   1.300 +        } else {
   1.301 +            result = this->createAlphaClipMask(genID,
   1.302 +                                               initialState,
   1.303 +                                               elements,
   1.304 +                                               clipSpaceIBounds);
   1.305 +        }
   1.306 +
   1.307 +        if (NULL != result) {
   1.308 +            // The mask's top left coord should be pinned to the rounded-out top left corner of
   1.309 +            // clipSpace bounds. We determine the mask's position WRT to the render target here.
   1.310 +            SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
   1.311 +            rtSpaceMaskBounds.offset(-clipDataIn->fOrigin);
   1.312 +            are->set(fGpu->drawState());
   1.313 +            setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds);
   1.314 +            fGpu->disableScissor();
   1.315 +            this->setGpuStencil();
   1.316 +            return true;
   1.317 +        }
   1.318 +        // if alpha clip mask creation fails fall through to the non-AA code paths
   1.319 +    }
   1.320 +#endif // GR_AA_CLIP
   1.321 +
   1.322 +    // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't
   1.323 +    // be created. In either case, free up the texture in the anti-aliased mask cache.
   1.324 +    // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g.,
   1.325 +    // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be
   1.326 +    // "incorrectly" clearing the AA cache.
   1.327 +    fAACache.reset();
   1.328 +
   1.329 +    // If the clip is a rectangle then just set the scissor. Otherwise, create
   1.330 +    // a stencil mask.
   1.331 +    if (isRect) {
   1.332 +        SkIRect clipRect = clipSpaceIBounds;
   1.333 +        clipRect.offset(-clipDataIn->fOrigin);
   1.334 +        fGpu->enableScissor(clipRect);
   1.335 +        this->setGpuStencil();
   1.336 +        return true;
   1.337 +    }
   1.338 +
   1.339 +    // use the stencil clip if we can't represent the clip as a rectangle.
   1.340 +    SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin;
   1.341 +    this->createStencilClipMask(genID,
   1.342 +                                initialState,
   1.343 +                                elements,
   1.344 +                                clipSpaceIBounds,
   1.345 +                                clipSpaceToStencilSpaceOffset);
   1.346 +
   1.347 +    // This must occur after createStencilClipMask. That function may change the scissor. Also, it
   1.348 +    // only guarantees that the stencil mask is correct within the bounds it was passed, so we must
   1.349 +    // use both stencil and scissor test to the bounds for the final draw.
   1.350 +    SkIRect scissorSpaceIBounds(clipSpaceIBounds);
   1.351 +    scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
   1.352 +    fGpu->enableScissor(scissorSpaceIBounds);
   1.353 +    this->setGpuStencil();
   1.354 +    return true;
   1.355 +}
   1.356 +
   1.357 +#define VISUALIZE_COMPLEX_CLIP 0
   1.358 +
   1.359 +#if VISUALIZE_COMPLEX_CLIP
   1.360 +    #include "SkRandom.h"
   1.361 +    SkRandom gRandom;
   1.362 +    #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
   1.363 +#else
   1.364 +    #define SET_RANDOM_COLOR
   1.365 +#endif
   1.366 +
   1.367 +namespace {
   1.368 +
   1.369 +////////////////////////////////////////////////////////////////////////////////
   1.370 +// set up the OpenGL blend function to perform the specified
   1.371 +// boolean operation for alpha clip mask creation
   1.372 +void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
   1.373 +
   1.374 +    switch (op) {
   1.375 +        case SkRegion::kReplace_Op:
   1.376 +            drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
   1.377 +            break;
   1.378 +        case SkRegion::kIntersect_Op:
   1.379 +            drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
   1.380 +            break;
   1.381 +        case SkRegion::kUnion_Op:
   1.382 +            drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
   1.383 +            break;
   1.384 +        case SkRegion::kXOR_Op:
   1.385 +            drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff);
   1.386 +            break;
   1.387 +        case SkRegion::kDifference_Op:
   1.388 +            drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
   1.389 +            break;
   1.390 +        case SkRegion::kReverseDifference_Op:
   1.391 +            drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff);
   1.392 +            break;
   1.393 +        default:
   1.394 +            SkASSERT(false);
   1.395 +            break;
   1.396 +    }
   1.397 +}
   1.398 +
   1.399 +}
   1.400 +
   1.401 +////////////////////////////////////////////////////////////////////////////////
   1.402 +bool GrClipMaskManager::drawElement(GrTexture* target,
   1.403 +                                    const SkClipStack::Element* element,
   1.404 +                                    GrPathRenderer* pr) {
   1.405 +    GrDrawState* drawState = fGpu->drawState();
   1.406 +
   1.407 +    drawState->setRenderTarget(target->asRenderTarget());
   1.408 +
   1.409 +    // TODO: Draw rrects directly here.
   1.410 +    switch (element->getType()) {
   1.411 +        case Element::kEmpty_Type:
   1.412 +            SkDEBUGFAIL("Should never get here with an empty element.");
   1.413 +            break;
   1.414 +        case Element::kRect_Type:
   1.415 +            // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the
   1.416 +            // entire mask bounds and writes 0 outside the rect.
   1.417 +            if (element->isAA()) {
   1.418 +                getContext()->getAARectRenderer()->fillAARect(fGpu,
   1.419 +                                                              fGpu,
   1.420 +                                                              element->getRect(),
   1.421 +                                                              SkMatrix::I(),
   1.422 +                                                              element->getRect(),
   1.423 +                                                              false);
   1.424 +            } else {
   1.425 +                fGpu->drawSimpleRect(element->getRect(), NULL);
   1.426 +            }
   1.427 +            return true;
   1.428 +        default: {
   1.429 +            SkPath path;
   1.430 +            element->asPath(&path);
   1.431 +            if (path.isInverseFillType()) {
   1.432 +                path.toggleInverseFillType();
   1.433 +            }
   1.434 +            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
   1.435 +            if (NULL == pr) {
   1.436 +                GrPathRendererChain::DrawType type;
   1.437 +                type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType :
   1.438 +                                         GrPathRendererChain::kColor_DrawType;
   1.439 +                pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type);
   1.440 +            }
   1.441 +            if (NULL == pr) {
   1.442 +                return false;
   1.443 +            }
   1.444 +            pr->drawPath(path, stroke, fGpu, element->isAA());
   1.445 +            break;
   1.446 +        }
   1.447 +    }
   1.448 +    return true;
   1.449 +}
   1.450 +
   1.451 +bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target,
   1.452 +                                                 const SkClipStack::Element* element,
   1.453 +                                                 GrPathRenderer** pr) {
   1.454 +    GrDrawState* drawState = fGpu->drawState();
   1.455 +    drawState->setRenderTarget(target->asRenderTarget());
   1.456 +
   1.457 +    if (Element::kRect_Type == element->getType()) {
   1.458 +        return true;
   1.459 +    } else {
   1.460 +        // We shouldn't get here with an empty clip element.
   1.461 +        SkASSERT(Element::kEmpty_Type != element->getType());
   1.462 +        SkPath path;
   1.463 +        element->asPath(&path);
   1.464 +        if (path.isInverseFillType()) {
   1.465 +            path.toggleInverseFillType();
   1.466 +        }
   1.467 +        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
   1.468 +        GrPathRendererChain::DrawType type = element->isAA() ?
   1.469 +            GrPathRendererChain::kStencilAndColorAntiAlias_DrawType :
   1.470 +            GrPathRendererChain::kStencilAndColor_DrawType;
   1.471 +        *pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type);
   1.472 +        return NULL != *pr;
   1.473 +    }
   1.474 +}
   1.475 +
   1.476 +void GrClipMaskManager::mergeMask(GrTexture* dstMask,
   1.477 +                                  GrTexture* srcMask,
   1.478 +                                  SkRegion::Op op,
   1.479 +                                  const SkIRect& dstBound,
   1.480 +                                  const SkIRect& srcBound) {
   1.481 +    GrDrawState::AutoViewMatrixRestore avmr;
   1.482 +    GrDrawState* drawState = fGpu->drawState();
   1.483 +    SkAssertResult(avmr.setIdentity(drawState));
   1.484 +    GrDrawState::AutoRestoreEffects are(drawState);
   1.485 +
   1.486 +    drawState->setRenderTarget(dstMask->asRenderTarget());
   1.487 +
   1.488 +    setup_boolean_blendcoeffs(drawState, op);
   1.489 +
   1.490 +    SkMatrix sampleM;
   1.491 +    sampleM.setIDiv(srcMask->width(), srcMask->height());
   1.492 +
   1.493 +    drawState->addColorEffect(
   1.494 +        GrTextureDomainEffect::Create(srcMask,
   1.495 +                                      sampleM,
   1.496 +                                      GrTextureDomain::MakeTexelDomain(srcMask, srcBound),
   1.497 +                                      GrTextureDomain::kDecal_Mode,
   1.498 +                                      GrTextureParams::kNone_FilterMode))->unref();
   1.499 +    fGpu->drawSimpleRect(SkRect::Make(dstBound), NULL);
   1.500 +}
   1.501 +
   1.502 +// get a texture to act as a temporary buffer for AA clip boolean operations
   1.503 +// TODO: given the expense of createTexture we may want to just cache this too
   1.504 +void GrClipMaskManager::getTemp(int width, int height, GrAutoScratchTexture* temp) {
   1.505 +    if (NULL != temp->texture()) {
   1.506 +        // we've already allocated the temp texture
   1.507 +        return;
   1.508 +    }
   1.509 +
   1.510 +    GrTextureDesc desc;
   1.511 +    desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
   1.512 +    desc.fWidth = width;
   1.513 +    desc.fHeight = height;
   1.514 +    desc.fConfig = kAlpha_8_GrPixelConfig;
   1.515 +
   1.516 +    temp->set(this->getContext(), desc);
   1.517 +}
   1.518 +
   1.519 +////////////////////////////////////////////////////////////////////////////////
   1.520 +// Handles caching & allocation (if needed) of a clip alpha-mask texture for both the sw-upload
   1.521 +// or gpu-rendered cases. Returns true if there is no more work to be done (i.e., we got a cache
   1.522 +// hit)
   1.523 +bool GrClipMaskManager::getMaskTexture(int32_t elementsGenID,
   1.524 +                                       const SkIRect& clipSpaceIBounds,
   1.525 +                                       GrTexture** result,
   1.526 +                                       bool willUpload) {
   1.527 +    bool cached = fAACache.canReuse(elementsGenID, clipSpaceIBounds);
   1.528 +    if (!cached) {
   1.529 +
   1.530 +        // There isn't a suitable entry in the cache so we create a new texture to store the mask.
   1.531 +        // Since we are setting up the cache we know the last lookup was a miss. Free up the
   1.532 +        // currently cached mask so it can be reused.
   1.533 +        fAACache.reset();
   1.534 +
   1.535 +        GrTextureDesc desc;
   1.536 +        desc.fFlags = willUpload ? kNone_GrTextureFlags : kRenderTarget_GrTextureFlagBit;
   1.537 +        desc.fWidth = clipSpaceIBounds.width();
   1.538 +        desc.fHeight = clipSpaceIBounds.height();
   1.539 +        desc.fConfig = kRGBA_8888_GrPixelConfig;
   1.540 +        if (willUpload || this->getContext()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
   1.541 +            // We would always like A8 but it isn't supported on all platforms
   1.542 +            desc.fConfig = kAlpha_8_GrPixelConfig;
   1.543 +        }
   1.544 +
   1.545 +        fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds);
   1.546 +    }
   1.547 +
   1.548 +    *result = fAACache.getLastMask();
   1.549 +    return cached;
   1.550 +}
   1.551 +
   1.552 +////////////////////////////////////////////////////////////////////////////////
   1.553 +// Create a 8-bit clip mask in alpha
   1.554 +GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
   1.555 +                                                  InitialState initialState,
   1.556 +                                                  const ElementList& elements,
   1.557 +                                                  const SkIRect& clipSpaceIBounds) {
   1.558 +    SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
   1.559 +
   1.560 +    GrTexture* result;
   1.561 +    if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, false)) {
   1.562 +        fCurrClipMaskType = kAlpha_ClipMaskType;
   1.563 +        return result;
   1.564 +    }
   1.565 +
   1.566 +    if (NULL == result) {
   1.567 +        fAACache.reset();
   1.568 +        return NULL;
   1.569 +    }
   1.570 +
   1.571 +    // The top-left of the mask corresponds to the top-left corner of the bounds.
   1.572 +    SkVector clipToMaskOffset = {
   1.573 +        SkIntToScalar(-clipSpaceIBounds.fLeft),
   1.574 +        SkIntToScalar(-clipSpaceIBounds.fTop)
   1.575 +    };
   1.576 +    // The texture may be larger than necessary, this rect represents the part of the texture
   1.577 +    // we populate with a rasterization of the clip.
   1.578 +    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
   1.579 +
   1.580 +    // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
   1.581 +    SkMatrix translate;
   1.582 +    translate.setTranslate(clipToMaskOffset);
   1.583 +    GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &translate);
   1.584 +
   1.585 +    GrDrawState* drawState = fGpu->drawState();
   1.586 +
   1.587 +    // We're drawing a coverage mask and want coverage to be run through the blend function.
   1.588 +    drawState->enableState(GrDrawState::kCoverageDrawing_StateBit);
   1.589 +
   1.590 +    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
   1.591 +    // clear the part that we care about.
   1.592 +    fGpu->clear(&maskSpaceIBounds,
   1.593 +                kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
   1.594 +                true,
   1.595 +                result->asRenderTarget());
   1.596 +
   1.597 +    // When we use the stencil in the below loop it is important to have this clip installed.
   1.598 +    // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
   1.599 +    // pass must not set values outside of this bounds or stencil values outside the rect won't be
   1.600 +    // cleared.
   1.601 +    GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds);
   1.602 +    drawState->enableState(GrDrawState::kClip_StateBit);
   1.603 +
   1.604 +    GrAutoScratchTexture temp;
   1.605 +    // walk through each clip element and perform its set op
   1.606 +    for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
   1.607 +        const Element* element = iter.get();
   1.608 +        SkRegion::Op op = element->getOp();
   1.609 +        bool invert = element->isInverseFilled();
   1.610 +
   1.611 +        if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
   1.612 +            GrPathRenderer* pr = NULL;
   1.613 +            bool useTemp = !this->canStencilAndDrawElement(result, element, &pr);
   1.614 +            GrTexture* dst;
   1.615 +            // This is the bounds of the clip element in the space of the alpha-mask. The temporary
   1.616 +            // mask buffer can be substantially larger than the actually clip stack element. We
   1.617 +            // touch the minimum number of pixels necessary and use decal mode to combine it with
   1.618 +            // the accumulator.
   1.619 +            SkIRect maskSpaceElementIBounds;
   1.620 +
   1.621 +            if (useTemp) {
   1.622 +                if (invert) {
   1.623 +                    maskSpaceElementIBounds = maskSpaceIBounds;
   1.624 +                } else {
   1.625 +                    SkRect elementBounds = element->getBounds();
   1.626 +                    elementBounds.offset(clipToMaskOffset);
   1.627 +                    elementBounds.roundOut(&maskSpaceElementIBounds);
   1.628 +                }
   1.629 +
   1.630 +                this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
   1.631 +                if (NULL == temp.texture()) {
   1.632 +                    fAACache.reset();
   1.633 +                    return NULL;
   1.634 +                }
   1.635 +                dst = temp.texture();
   1.636 +                // clear the temp target and set blend to replace
   1.637 +                fGpu->clear(&maskSpaceElementIBounds,
   1.638 +                            invert ? 0xffffffff : 0x00000000,
   1.639 +                            true,
   1.640 +                            dst->asRenderTarget());
   1.641 +                setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
   1.642 +
   1.643 +            } else {
   1.644 +                // draw directly into the result with the stencil set to make the pixels affected
   1.645 +                // by the clip shape be non-zero.
   1.646 +                dst = result;
   1.647 +                GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
   1.648 +                                             kReplace_StencilOp,
   1.649 +                                             kReplace_StencilOp,
   1.650 +                                             kAlways_StencilFunc,
   1.651 +                                             0xffff,
   1.652 +                                             0xffff,
   1.653 +                                             0xffff);
   1.654 +                drawState->setStencil(kStencilInElement);
   1.655 +                setup_boolean_blendcoeffs(drawState, op);
   1.656 +            }
   1.657 +
   1.658 +            drawState->setAlpha(invert ? 0x00 : 0xff);
   1.659 +
   1.660 +            if (!this->drawElement(dst, element, pr)) {
   1.661 +                fAACache.reset();
   1.662 +                return NULL;
   1.663 +            }
   1.664 +
   1.665 +            if (useTemp) {
   1.666 +                // Now draw into the accumulator using the real operation and the temp buffer as a
   1.667 +                // texture
   1.668 +                this->mergeMask(result,
   1.669 +                                temp.texture(),
   1.670 +                                op,
   1.671 +                                maskSpaceIBounds,
   1.672 +                                maskSpaceElementIBounds);
   1.673 +            } else {
   1.674 +                // Draw to the exterior pixels (those with a zero stencil value).
   1.675 +                drawState->setAlpha(invert ? 0xff : 0x00);
   1.676 +                GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
   1.677 +                                             kZero_StencilOp,
   1.678 +                                             kZero_StencilOp,
   1.679 +                                             kEqual_StencilFunc,
   1.680 +                                             0xffff,
   1.681 +                                             0x0000,
   1.682 +                                             0xffff);
   1.683 +                drawState->setStencil(kDrawOutsideElement);
   1.684 +                fGpu->drawSimpleRect(clipSpaceIBounds);
   1.685 +                drawState->disableStencil();
   1.686 +            }
   1.687 +        } else {
   1.688 +            // all the remaining ops can just be directly draw into the accumulation buffer
   1.689 +            drawState->setAlpha(0xff);
   1.690 +            setup_boolean_blendcoeffs(drawState, op);
   1.691 +            this->drawElement(result, element);
   1.692 +        }
   1.693 +    }
   1.694 +
   1.695 +    fCurrClipMaskType = kAlpha_ClipMaskType;
   1.696 +    return result;
   1.697 +}
   1.698 +
   1.699 +////////////////////////////////////////////////////////////////////////////////
   1.700 +// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
   1.701 +// (as opposed to canvas) coordinates
   1.702 +bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID,
   1.703 +                                              InitialState initialState,
   1.704 +                                              const ElementList& elements,
   1.705 +                                              const SkIRect& clipSpaceIBounds,
   1.706 +                                              const SkIPoint& clipSpaceToStencilOffset) {
   1.707 +
   1.708 +    SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
   1.709 +
   1.710 +    GrDrawState* drawState = fGpu->drawState();
   1.711 +    SkASSERT(drawState->isClipState());
   1.712 +
   1.713 +    GrRenderTarget* rt = drawState->getRenderTarget();
   1.714 +    SkASSERT(NULL != rt);
   1.715 +
   1.716 +    // TODO: dynamically attach a SB when needed.
   1.717 +    GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
   1.718 +    if (NULL == stencilBuffer) {
   1.719 +        return false;
   1.720 +    }
   1.721 +
   1.722 +    if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
   1.723 +
   1.724 +        stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
   1.725 +
   1.726 +        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
   1.727 +        SkVector translate = {
   1.728 +            SkIntToScalar(clipSpaceToStencilOffset.fX),
   1.729 +            SkIntToScalar(clipSpaceToStencilOffset.fY)
   1.730 +        };
   1.731 +        SkMatrix matrix;
   1.732 +        matrix.setTranslate(translate);
   1.733 +        GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix);
   1.734 +        drawState = fGpu->drawState();
   1.735 +
   1.736 +        drawState->setRenderTarget(rt);
   1.737 +
   1.738 +        // We set the current clip to the bounds so that our recursive draws are scissored to them.
   1.739 +        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
   1.740 +        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
   1.741 +        GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds);
   1.742 +        drawState->enableState(GrDrawState::kClip_StateBit);
   1.743 +
   1.744 +#if !VISUALIZE_COMPLEX_CLIP
   1.745 +        drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
   1.746 +#endif
   1.747 +
   1.748 +        int clipBit = stencilBuffer->bits();
   1.749 +        SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
   1.750 +        clipBit = (1 << (clipBit-1));
   1.751 +
   1.752 +        fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState);
   1.753 +
   1.754 +        // walk through each clip element and perform its set op
   1.755 +        // with the existing clip.
   1.756 +        for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) {
   1.757 +            const Element* element = iter.get();
   1.758 +            bool fillInverted = false;
   1.759 +            // enabled at bottom of loop
   1.760 +            drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
   1.761 +            // if the target is MSAA then we want MSAA enabled when the clip is soft
   1.762 +            if (rt->isMultisampled()) {
   1.763 +                drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA());
   1.764 +            }
   1.765 +
   1.766 +            // This will be used to determine whether the clip shape can be rendered into the
   1.767 +            // stencil with arbitrary stencil settings.
   1.768 +            GrPathRenderer::StencilSupport stencilSupport;
   1.769 +
   1.770 +            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
   1.771 +
   1.772 +            SkRegion::Op op = element->getOp();
   1.773 +
   1.774 +            GrPathRenderer* pr = NULL;
   1.775 +            SkPath clipPath;
   1.776 +            if (Element::kRect_Type == element->getType()) {
   1.777 +                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
   1.778 +                fillInverted = false;
   1.779 +            } else {
   1.780 +                element->asPath(&clipPath);
   1.781 +                fillInverted = clipPath.isInverseFillType();
   1.782 +                if (fillInverted) {
   1.783 +                    clipPath.toggleInverseFillType();
   1.784 +                }
   1.785 +                pr = this->getContext()->getPathRenderer(clipPath,
   1.786 +                                                         stroke,
   1.787 +                                                         fGpu,
   1.788 +                                                         false,
   1.789 +                                                         GrPathRendererChain::kStencilOnly_DrawType,
   1.790 +                                                         &stencilSupport);
   1.791 +                if (NULL == pr) {
   1.792 +                    return false;
   1.793 +                }
   1.794 +            }
   1.795 +
   1.796 +            int passes;
   1.797 +            GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
   1.798 +
   1.799 +            bool canRenderDirectToStencil =
   1.800 +                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
   1.801 +            bool canDrawDirectToClip; // Given the renderer, the element,
   1.802 +                                      // fill rule, and set operation can
   1.803 +                                      // we render the element directly to
   1.804 +                                      // stencil bit used for clipping.
   1.805 +            canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,
   1.806 +                                                                   canRenderDirectToStencil,
   1.807 +                                                                   clipBit,
   1.808 +                                                                   fillInverted,
   1.809 +                                                                   &passes,
   1.810 +                                                                   stencilSettings);
   1.811 +
   1.812 +            // draw the element to the client stencil bits if necessary
   1.813 +            if (!canDrawDirectToClip) {
   1.814 +                GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
   1.815 +                                             kIncClamp_StencilOp,
   1.816 +                                             kIncClamp_StencilOp,
   1.817 +                                             kAlways_StencilFunc,
   1.818 +                                             0xffff,
   1.819 +                                             0x0000,
   1.820 +                                             0xffff);
   1.821 +                SET_RANDOM_COLOR
   1.822 +                if (Element::kRect_Type == element->getType()) {
   1.823 +                    *drawState->stencil() = gDrawToStencil;
   1.824 +                    fGpu->drawSimpleRect(element->getRect(), NULL);
   1.825 +                } else {
   1.826 +                    if (!clipPath.isEmpty()) {
   1.827 +                        if (canRenderDirectToStencil) {
   1.828 +                            *drawState->stencil() = gDrawToStencil;
   1.829 +                            pr->drawPath(clipPath, stroke, fGpu, false);
   1.830 +                        } else {
   1.831 +                            pr->stencilPath(clipPath, stroke, fGpu);
   1.832 +                        }
   1.833 +                    }
   1.834 +                }
   1.835 +            }
   1.836 +
   1.837 +            // now we modify the clip bit by rendering either the clip
   1.838 +            // element directly or a bounding rect of the entire clip.
   1.839 +            drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
   1.840 +            for (int p = 0; p < passes; ++p) {
   1.841 +                *drawState->stencil() = stencilSettings[p];
   1.842 +                if (canDrawDirectToClip) {
   1.843 +                    if (Element::kRect_Type == element->getType()) {
   1.844 +                        SET_RANDOM_COLOR
   1.845 +                        fGpu->drawSimpleRect(element->getRect(), NULL);
   1.846 +                    } else {
   1.847 +                        SET_RANDOM_COLOR
   1.848 +                        pr->drawPath(clipPath, stroke, fGpu, false);
   1.849 +                    }
   1.850 +                } else {
   1.851 +                    SET_RANDOM_COLOR
   1.852 +                    // The view matrix is setup to do clip space -> stencil space translation, so
   1.853 +                    // draw rect in clip space.
   1.854 +                    fGpu->drawSimpleRect(SkRect::Make(clipSpaceIBounds), NULL);
   1.855 +                }
   1.856 +            }
   1.857 +        }
   1.858 +    }
   1.859 +    // set this last because recursive draws may overwrite it back to kNone.
   1.860 +    SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
   1.861 +    fCurrClipMaskType = kStencil_ClipMaskType;
   1.862 +    return true;
   1.863 +}
   1.864 +
   1.865 +
   1.866 +// mapping of clip-respecting stencil funcs to normal stencil funcs
   1.867 +// mapping depends on whether stencil-clipping is in effect.
   1.868 +static const GrStencilFunc
   1.869 +    gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = {
   1.870 +    {// Stencil-Clipping is DISABLED,  we are effectively always inside the clip
   1.871 +        // In the Clip Funcs
   1.872 +        kAlways_StencilFunc,          // kAlwaysIfInClip_StencilFunc
   1.873 +        kEqual_StencilFunc,           // kEqualIfInClip_StencilFunc
   1.874 +        kLess_StencilFunc,            // kLessIfInClip_StencilFunc
   1.875 +        kLEqual_StencilFunc,          // kLEqualIfInClip_StencilFunc
   1.876 +        // Special in the clip func that forces user's ref to be 0.
   1.877 +        kNotEqual_StencilFunc,        // kNonZeroIfInClip_StencilFunc
   1.878 +                                      // make ref 0 and do normal nequal.
   1.879 +    },
   1.880 +    {// Stencil-Clipping is ENABLED
   1.881 +        // In the Clip Funcs
   1.882 +        kEqual_StencilFunc,           // kAlwaysIfInClip_StencilFunc
   1.883 +                                      // eq stencil clip bit, mask
   1.884 +                                      // out user bits.
   1.885 +
   1.886 +        kEqual_StencilFunc,           // kEqualIfInClip_StencilFunc
   1.887 +                                      // add stencil bit to mask and ref
   1.888 +
   1.889 +        kLess_StencilFunc,            // kLessIfInClip_StencilFunc
   1.890 +        kLEqual_StencilFunc,          // kLEqualIfInClip_StencilFunc
   1.891 +                                      // for both of these we can add
   1.892 +                                      // the clip bit to the mask and
   1.893 +                                      // ref and compare as normal
   1.894 +        // Special in the clip func that forces user's ref to be 0.
   1.895 +        kLess_StencilFunc,            // kNonZeroIfInClip_StencilFunc
   1.896 +                                      // make ref have only the clip bit set
   1.897 +                                      // and make comparison be less
   1.898 +                                      // 10..0 < 1..user_bits..
   1.899 +    }
   1.900 +};
   1.901 +
   1.902 +namespace {
   1.903 +// Sets the settings to clip against the stencil buffer clip while ignoring the
   1.904 +// client bits.
   1.905 +const GrStencilSettings& basic_apply_stencil_clip_settings() {
   1.906 +    // stencil settings to use when clip is in stencil
   1.907 +    GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
   1.908 +        kKeep_StencilOp,
   1.909 +        kKeep_StencilOp,
   1.910 +        kAlwaysIfInClip_StencilFunc,
   1.911 +        0x0000,
   1.912 +        0x0000,
   1.913 +        0x0000);
   1.914 +    return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
   1.915 +}
   1.916 +}
   1.917 +
   1.918 +void GrClipMaskManager::setGpuStencil() {
   1.919 +    // We make two copies of the StencilSettings here (except in the early
   1.920 +    // exit scenario. One copy from draw state to the stack var. Then another
   1.921 +    // from the stack var to the gpu. We could make this class hold a ptr to
   1.922 +    // GrGpu's fStencilSettings and eliminate the stack copy here.
   1.923 +
   1.924 +    const GrDrawState& drawState = fGpu->getDrawState();
   1.925 +
   1.926 +    // use stencil for clipping if clipping is enabled and the clip
   1.927 +    // has been written into the stencil.
   1.928 +    GrClipMaskManager::StencilClipMode clipMode;
   1.929 +    if (this->isClipInStencil() && drawState.isClipState()) {
   1.930 +        clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
   1.931 +        // We can't be modifying the clip and respecting it at the same time.
   1.932 +        SkASSERT(!drawState.isStateFlagEnabled(
   1.933 +                    GrGpu::kModifyStencilClip_StateBit));
   1.934 +    } else if (drawState.isStateFlagEnabled(
   1.935 +                    GrGpu::kModifyStencilClip_StateBit)) {
   1.936 +        clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
   1.937 +    } else {
   1.938 +        clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
   1.939 +    }
   1.940 +
   1.941 +    GrStencilSettings settings;
   1.942 +    // The GrGpu client may not be using the stencil buffer but we may need to
   1.943 +    // enable it in order to respect a stencil clip.
   1.944 +    if (drawState.getStencil().isDisabled()) {
   1.945 +        if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) {
   1.946 +            settings = basic_apply_stencil_clip_settings();
   1.947 +        } else {
   1.948 +            fGpu->disableStencil();
   1.949 +            return;
   1.950 +        }
   1.951 +    } else {
   1.952 +        settings = drawState.getStencil();
   1.953 +    }
   1.954 +
   1.955 +    // TODO: dynamically attach a stencil buffer
   1.956 +    int stencilBits = 0;
   1.957 +    GrStencilBuffer* stencilBuffer =
   1.958 +        drawState.getRenderTarget()->getStencilBuffer();
   1.959 +    if (NULL != stencilBuffer) {
   1.960 +        stencilBits = stencilBuffer->bits();
   1.961 +    }
   1.962 +
   1.963 +    SkASSERT(fGpu->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp());
   1.964 +    SkASSERT(fGpu->caps()->twoSidedStencilSupport() || !settings.isTwoSided());
   1.965 +    this->adjustStencilParams(&settings, clipMode, stencilBits);
   1.966 +    fGpu->setStencilSettings(settings);
   1.967 +}
   1.968 +
   1.969 +void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings,
   1.970 +                                            StencilClipMode mode,
   1.971 +                                            int stencilBitCnt) {
   1.972 +    SkASSERT(stencilBitCnt > 0);
   1.973 +
   1.974 +    if (kModifyClip_StencilClipMode == mode) {
   1.975 +        // We assume that this clip manager itself is drawing to the GrGpu and
   1.976 +        // has already setup the correct values.
   1.977 +        return;
   1.978 +    }
   1.979 +
   1.980 +    unsigned int clipBit = (1 << (stencilBitCnt - 1));
   1.981 +    unsigned int userBits = clipBit - 1;
   1.982 +
   1.983 +    GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
   1.984 +    bool twoSided = fGpu->caps()->twoSidedStencilSupport();
   1.985 +
   1.986 +    bool finished = false;
   1.987 +    while (!finished) {
   1.988 +        GrStencilFunc func = settings->func(face);
   1.989 +        uint16_t writeMask = settings->writeMask(face);
   1.990 +        uint16_t funcMask = settings->funcMask(face);
   1.991 +        uint16_t funcRef = settings->funcRef(face);
   1.992 +
   1.993 +        SkASSERT((unsigned) func < kStencilFuncCount);
   1.994 +
   1.995 +        writeMask &= userBits;
   1.996 +
   1.997 +        if (func >= kBasicStencilFuncCount) {
   1.998 +            int respectClip = kRespectClip_StencilClipMode == mode;
   1.999 +            if (respectClip) {
  1.1000 +                // The GrGpu class should have checked this
  1.1001 +                SkASSERT(this->isClipInStencil());
  1.1002 +                switch (func) {
  1.1003 +                    case kAlwaysIfInClip_StencilFunc:
  1.1004 +                        funcMask = clipBit;
  1.1005 +                        funcRef = clipBit;
  1.1006 +                        break;
  1.1007 +                    case kEqualIfInClip_StencilFunc:
  1.1008 +                    case kLessIfInClip_StencilFunc:
  1.1009 +                    case kLEqualIfInClip_StencilFunc:
  1.1010 +                        funcMask = (funcMask & userBits) | clipBit;
  1.1011 +                        funcRef  = (funcRef  & userBits) | clipBit;
  1.1012 +                        break;
  1.1013 +                    case kNonZeroIfInClip_StencilFunc:
  1.1014 +                        funcMask = (funcMask & userBits) | clipBit;
  1.1015 +                        funcRef = clipBit;
  1.1016 +                        break;
  1.1017 +                    default:
  1.1018 +                        GrCrash("Unknown stencil func");
  1.1019 +                }
  1.1020 +            } else {
  1.1021 +                funcMask &= userBits;
  1.1022 +                funcRef &= userBits;
  1.1023 +            }
  1.1024 +            const GrStencilFunc* table =
  1.1025 +                gSpecialToBasicStencilFunc[respectClip];
  1.1026 +            func = table[func - kBasicStencilFuncCount];
  1.1027 +            SkASSERT(func >= 0 && func < kBasicStencilFuncCount);
  1.1028 +        } else {
  1.1029 +            funcMask &= userBits;
  1.1030 +            funcRef &= userBits;
  1.1031 +        }
  1.1032 +
  1.1033 +        settings->setFunc(face, func);
  1.1034 +        settings->setWriteMask(face, writeMask);
  1.1035 +        settings->setFuncMask(face, funcMask);
  1.1036 +        settings->setFuncRef(face, funcRef);
  1.1037 +
  1.1038 +        if (GrStencilSettings::kFront_Face == face) {
  1.1039 +            face = GrStencilSettings::kBack_Face;
  1.1040 +            finished = !twoSided;
  1.1041 +        } else {
  1.1042 +            finished = true;
  1.1043 +        }
  1.1044 +    }
  1.1045 +    if (!twoSided) {
  1.1046 +        settings->copyFrontSettingsToBack();
  1.1047 +    }
  1.1048 +}
  1.1049 +
  1.1050 +////////////////////////////////////////////////////////////////////////////////
  1.1051 +GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID,
  1.1052 +                                                     GrReducedClip::InitialState initialState,
  1.1053 +                                                     const GrReducedClip::ElementList& elements,
  1.1054 +                                                     const SkIRect& clipSpaceIBounds) {
  1.1055 +    SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
  1.1056 +
  1.1057 +    GrTexture* result;
  1.1058 +    if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, true)) {
  1.1059 +        return result;
  1.1060 +    }
  1.1061 +
  1.1062 +    if (NULL == result) {
  1.1063 +        fAACache.reset();
  1.1064 +        return NULL;
  1.1065 +    }
  1.1066 +
  1.1067 +    // The mask texture may be larger than necessary. We round out the clip space bounds and pin
  1.1068 +    // the top left corner of the resulting rect to the top left of the texture.
  1.1069 +    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
  1.1070 +
  1.1071 +    GrSWMaskHelper helper(this->getContext());
  1.1072 +
  1.1073 +    SkMatrix matrix;
  1.1074 +    matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft),
  1.1075 +                        SkIntToScalar(-clipSpaceIBounds.fTop));
  1.1076 +    helper.init(maskSpaceIBounds, &matrix);
  1.1077 +
  1.1078 +    helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00);
  1.1079 +
  1.1080 +    SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
  1.1081 +
  1.1082 +    for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) {
  1.1083 +
  1.1084 +        const Element* element = iter.get();
  1.1085 +        SkRegion::Op op = element->getOp();
  1.1086 +
  1.1087 +        if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
  1.1088 +            // Intersect and reverse difference require modifying pixels outside of the geometry
  1.1089 +            // that is being "drawn". In both cases we erase all the pixels outside of the geometry
  1.1090 +            // but leave the pixels inside the geometry alone. For reverse difference we invert all
  1.1091 +            // the pixels before clearing the ones outside the geometry.
  1.1092 +            if (SkRegion::kReverseDifference_Op == op) {
  1.1093 +                SkRect temp = SkRect::Make(clipSpaceIBounds);
  1.1094 +                // invert the entire scene
  1.1095 +                helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF);
  1.1096 +            }
  1.1097 +
  1.1098 +            SkPath clipPath;
  1.1099 +            element->asPath(&clipPath);
  1.1100 +            clipPath.toggleInverseFillType();
  1.1101 +            helper.draw(clipPath, stroke, SkRegion::kReplace_Op, element->isAA(), 0x00);
  1.1102 +
  1.1103 +            continue;
  1.1104 +        }
  1.1105 +
  1.1106 +        // The other ops (union, xor, diff) only affect pixels inside
  1.1107 +        // the geometry so they can just be drawn normally
  1.1108 +        if (Element::kRect_Type == element->getType()) {
  1.1109 +            helper.draw(element->getRect(), op, element->isAA(), 0xFF);
  1.1110 +        } else {
  1.1111 +            SkPath path;
  1.1112 +            element->asPath(&path);
  1.1113 +            helper.draw(path, stroke, op, element->isAA(), 0xFF);
  1.1114 +        }
  1.1115 +    }
  1.1116 +
  1.1117 +    helper.toTexture(result);
  1.1118 +
  1.1119 +    fCurrClipMaskType = kAlpha_ClipMaskType;
  1.1120 +    return result;
  1.1121 +}
  1.1122 +
  1.1123 +////////////////////////////////////////////////////////////////////////////////
  1.1124 +void GrClipMaskManager::releaseResources() {
  1.1125 +    fAACache.releaseResources();
  1.1126 +}
  1.1127 +
  1.1128 +void GrClipMaskManager::setGpu(GrGpu* gpu) {
  1.1129 +    fGpu = gpu;
  1.1130 +    fAACache.setContext(gpu->getContext());
  1.1131 +}
  1.1132 +
  1.1133 +void GrClipMaskManager::adjustPathStencilParams(GrStencilSettings* settings) {
  1.1134 +    const GrDrawState& drawState = fGpu->getDrawState();
  1.1135 +    GrClipMaskManager::StencilClipMode clipMode;
  1.1136 +    if (this->isClipInStencil() && drawState.isClipState()) {
  1.1137 +        clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
  1.1138 +        // We can't be modifying the clip and respecting it at the same time.
  1.1139 +        SkASSERT(!drawState.isStateFlagEnabled(
  1.1140 +                    GrGpu::kModifyStencilClip_StateBit));
  1.1141 +    } else if (drawState.isStateFlagEnabled(
  1.1142 +                    GrGpu::kModifyStencilClip_StateBit)) {
  1.1143 +        clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
  1.1144 +    } else {
  1.1145 +        clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
  1.1146 +    }
  1.1147 +
  1.1148 +    // TODO: dynamically attach a stencil buffer
  1.1149 +    int stencilBits = 0;
  1.1150 +    GrStencilBuffer* stencilBuffer =
  1.1151 +        drawState.getRenderTarget()->getStencilBuffer();
  1.1152 +    if (NULL != stencilBuffer) {
  1.1153 +        stencilBits = stencilBuffer->bits();
  1.1154 +        this->adjustStencilParams(settings, clipMode, stencilBits);
  1.1155 +    }
  1.1156 +}

mercurial