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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2012 Google Inc.
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9 #include "GrClipMaskManager.h"
michael@0 10 #include "GrAAConvexPathRenderer.h"
michael@0 11 #include "GrAAHairLinePathRenderer.h"
michael@0 12 #include "GrAARectRenderer.h"
michael@0 13 #include "GrDrawTargetCaps.h"
michael@0 14 #include "GrGpu.h"
michael@0 15 #include "GrPaint.h"
michael@0 16 #include "GrPathRenderer.h"
michael@0 17 #include "GrRenderTarget.h"
michael@0 18 #include "GrStencilBuffer.h"
michael@0 19 #include "GrSWMaskHelper.h"
michael@0 20 #include "effects/GrTextureDomain.h"
michael@0 21 #include "effects/GrConvexPolyEffect.h"
michael@0 22 #include "effects/GrRRectEffect.h"
michael@0 23 #include "SkRasterClip.h"
michael@0 24 #include "SkStrokeRec.h"
michael@0 25 #include "SkTLazy.h"
michael@0 26
michael@0 27 #define GR_AA_CLIP 1
michael@0 28
michael@0 29 typedef SkClipStack::Element Element;
michael@0 30
michael@0 31 using namespace GrReducedClip;
michael@0 32
michael@0 33 ////////////////////////////////////////////////////////////////////////////////
michael@0 34 namespace {
michael@0 35 // set up the draw state to enable the aa clipping mask. Besides setting up the
michael@0 36 // stage matrix this also alters the vertex layout
michael@0 37 void setup_drawstate_aaclip(GrGpu* gpu,
michael@0 38 GrTexture* result,
michael@0 39 const SkIRect &devBound) {
michael@0 40 GrDrawState* drawState = gpu->drawState();
michael@0 41 SkASSERT(drawState);
michael@0 42
michael@0 43 SkMatrix mat;
michael@0 44 // We want to use device coords to compute the texture coordinates. We set our matrix to be
michael@0 45 // equal to the view matrix followed by an offset to the devBound, and then a scaling matrix to
michael@0 46 // normalized coords. We apply this matrix to the vertex positions rather than local coords.
michael@0 47 mat.setIDiv(result->width(), result->height());
michael@0 48 mat.preTranslate(SkIntToScalar(-devBound.fLeft),
michael@0 49 SkIntToScalar(-devBound.fTop));
michael@0 50 mat.preConcat(drawState->getViewMatrix());
michael@0 51
michael@0 52 SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
michael@0 53 // This could be a long-lived effect that is cached with the alpha-mask.
michael@0 54 drawState->addCoverageEffect(
michael@0 55 GrTextureDomainEffect::Create(result,
michael@0 56 mat,
michael@0 57 GrTextureDomain::MakeTexelDomain(result, domainTexels),
michael@0 58 GrTextureDomain::kDecal_Mode,
michael@0 59 GrTextureParams::kNone_FilterMode,
michael@0 60 kPosition_GrCoordSet))->unref();
michael@0 61 }
michael@0 62
michael@0 63 bool path_needs_SW_renderer(GrContext* context,
michael@0 64 GrGpu* gpu,
michael@0 65 const SkPath& origPath,
michael@0 66 const SkStrokeRec& stroke,
michael@0 67 bool doAA) {
michael@0 68 // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
michael@0 69 SkTCopyOnFirstWrite<SkPath> path(origPath);
michael@0 70 if (path->isInverseFillType()) {
michael@0 71 path.writable()->toggleInverseFillType();
michael@0 72 }
michael@0 73 // last (false) parameter disallows use of the SW path renderer
michael@0 74 GrPathRendererChain::DrawType type = doAA ?
michael@0 75 GrPathRendererChain::kColorAntiAlias_DrawType :
michael@0 76 GrPathRendererChain::kColor_DrawType;
michael@0 77
michael@0 78 return NULL == context->getPathRenderer(*path, stroke, gpu, false, type);
michael@0 79 }
michael@0 80
michael@0 81 }
michael@0 82
michael@0 83 /*
michael@0 84 * This method traverses the clip stack to see if the GrSoftwarePathRenderer
michael@0 85 * will be used on any element. If so, it returns true to indicate that the
michael@0 86 * entire clip should be rendered in SW and then uploaded en masse to the gpu.
michael@0 87 */
michael@0 88 bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) {
michael@0 89
michael@0 90 // TODO: generalize this function so that when
michael@0 91 // a clip gets complex enough it can just be done in SW regardless
michael@0 92 // of whether it would invoke the GrSoftwarePathRenderer.
michael@0 93 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
michael@0 94
michael@0 95 for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
michael@0 96 const Element* element = iter.get();
michael@0 97 // rects can always be drawn directly w/o using the software path
michael@0 98 // Skip rrects once we're drawing them directly.
michael@0 99 if (Element::kRect_Type != element->getType()) {
michael@0 100 SkPath path;
michael@0 101 element->asPath(&path);
michael@0 102 if (path_needs_SW_renderer(this->getContext(), fGpu, path, stroke, element->isAA())) {
michael@0 103 return true;
michael@0 104 }
michael@0 105 }
michael@0 106 }
michael@0 107 return false;
michael@0 108 }
michael@0 109
michael@0 110 bool GrClipMaskManager::installClipEffects(const ElementList& elements,
michael@0 111 GrDrawState::AutoRestoreEffects* are,
michael@0 112 const SkVector& clipToRTOffset,
michael@0 113 const SkRect* drawBounds) {
michael@0 114
michael@0 115 GrDrawState* drawState = fGpu->drawState();
michael@0 116 SkRect boundsInClipSpace;
michael@0 117 if (NULL != drawBounds) {
michael@0 118 boundsInClipSpace = *drawBounds;
michael@0 119 boundsInClipSpace.offset(-clipToRTOffset.fX, -clipToRTOffset.fY);
michael@0 120 }
michael@0 121
michael@0 122 are->set(drawState);
michael@0 123 GrRenderTarget* rt = drawState->getRenderTarget();
michael@0 124 ElementList::Iter iter(elements);
michael@0 125
michael@0 126 bool setARE = false;
michael@0 127 bool failed = false;
michael@0 128
michael@0 129 while (NULL != iter.get()) {
michael@0 130 SkRegion::Op op = iter.get()->getOp();
michael@0 131 bool invert;
michael@0 132 bool skip = false;
michael@0 133 switch (op) {
michael@0 134 case SkRegion::kReplace_Op:
michael@0 135 SkASSERT(iter.get() == elements.head());
michael@0 136 // Fallthrough, handled same as intersect.
michael@0 137 case SkRegion::kIntersect_Op:
michael@0 138 invert = false;
michael@0 139 if (NULL != drawBounds && iter.get()->contains(boundsInClipSpace)) {
michael@0 140 skip = true;
michael@0 141 }
michael@0 142 break;
michael@0 143 case SkRegion::kDifference_Op:
michael@0 144 invert = true;
michael@0 145 // We don't currently have a cheap test for whether a rect is fully outside an
michael@0 146 // element's primitive, so don't attempt to set skip.
michael@0 147 break;
michael@0 148 default:
michael@0 149 failed = true;
michael@0 150 break;
michael@0 151 }
michael@0 152 if (failed) {
michael@0 153 break;
michael@0 154 }
michael@0 155
michael@0 156 if (!skip) {
michael@0 157 GrEffectEdgeType edgeType;
michael@0 158 if (GR_AA_CLIP && iter.get()->isAA()) {
michael@0 159 if (rt->isMultisampled()) {
michael@0 160 // Coverage based AA clips don't place nicely with MSAA.
michael@0 161 failed = true;
michael@0 162 break;
michael@0 163 }
michael@0 164 edgeType = invert ? kInverseFillAA_GrEffectEdgeType : kFillAA_GrEffectEdgeType;
michael@0 165 } else {
michael@0 166 edgeType = invert ? kInverseFillBW_GrEffectEdgeType : kFillBW_GrEffectEdgeType;
michael@0 167 }
michael@0 168 SkAutoTUnref<GrEffectRef> effect;
michael@0 169 switch (iter.get()->getType()) {
michael@0 170 case SkClipStack::Element::kPath_Type:
michael@0 171 effect.reset(GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(),
michael@0 172 &clipToRTOffset));
michael@0 173 break;
michael@0 174 case SkClipStack::Element::kRRect_Type: {
michael@0 175 SkRRect rrect = iter.get()->getRRect();
michael@0 176 rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
michael@0 177 effect.reset(GrRRectEffect::Create(edgeType, rrect));
michael@0 178 break;
michael@0 179 }
michael@0 180 case SkClipStack::Element::kRect_Type: {
michael@0 181 SkRect rect = iter.get()->getRect();
michael@0 182 rect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
michael@0 183 effect.reset(GrConvexPolyEffect::Create(edgeType, rect));
michael@0 184 break;
michael@0 185 }
michael@0 186 default:
michael@0 187 break;
michael@0 188 }
michael@0 189 if (effect) {
michael@0 190 if (!setARE) {
michael@0 191 are->set(fGpu->drawState());
michael@0 192 setARE = true;
michael@0 193 }
michael@0 194 fGpu->drawState()->addCoverageEffect(effect);
michael@0 195 } else {
michael@0 196 failed = true;
michael@0 197 break;
michael@0 198 }
michael@0 199 }
michael@0 200 iter.next();
michael@0 201 }
michael@0 202
michael@0 203 if (failed) {
michael@0 204 are->set(NULL);
michael@0 205 }
michael@0 206
michael@0 207 return !failed;
michael@0 208 }
michael@0 209
michael@0 210 ////////////////////////////////////////////////////////////////////////////////
michael@0 211 // sort out what kind of clip mask needs to be created: alpha, stencil,
michael@0 212 // scissor, or entirely software
michael@0 213 bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn,
michael@0 214 GrDrawState::AutoRestoreEffects* are,
michael@0 215 const SkRect* devBounds) {
michael@0 216 fCurrClipMaskType = kNone_ClipMaskType;
michael@0 217
michael@0 218 ElementList elements(16);
michael@0 219 int32_t genID;
michael@0 220 InitialState initialState;
michael@0 221 SkIRect clipSpaceIBounds;
michael@0 222 bool requiresAA;
michael@0 223 bool isRect = false;
michael@0 224
michael@0 225 GrDrawState* drawState = fGpu->drawState();
michael@0 226
michael@0 227 const GrRenderTarget* rt = drawState->getRenderTarget();
michael@0 228 // GrDrawTarget should have filtered this for us
michael@0 229 SkASSERT(NULL != rt);
michael@0 230
michael@0 231 bool ignoreClip = !drawState->isClipState() || clipDataIn->fClipStack->isWideOpen();
michael@0 232
michael@0 233 if (!ignoreClip) {
michael@0 234 SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
michael@0 235 clipSpaceRTIBounds.offset(clipDataIn->fOrigin);
michael@0 236 ReduceClipStack(*clipDataIn->fClipStack,
michael@0 237 clipSpaceRTIBounds,
michael@0 238 &elements,
michael@0 239 &genID,
michael@0 240 &initialState,
michael@0 241 &clipSpaceIBounds,
michael@0 242 &requiresAA);
michael@0 243 if (elements.isEmpty()) {
michael@0 244 if (kAllIn_InitialState == initialState) {
michael@0 245 ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds;
michael@0 246 isRect = true;
michael@0 247 } else {
michael@0 248 return false;
michael@0 249 }
michael@0 250 }
michael@0 251 }
michael@0 252
michael@0 253 if (ignoreClip) {
michael@0 254 fGpu->disableScissor();
michael@0 255 this->setGpuStencil();
michael@0 256 return true;
michael@0 257 }
michael@0 258
michael@0 259 // An element count of 4 was chosen because of the common pattern in Blink of:
michael@0 260 // isect RR
michael@0 261 // diff RR
michael@0 262 // isect convex_poly
michael@0 263 // isect convex_poly
michael@0 264 // when drawing rounded div borders. This could probably be tuned based on a
michael@0 265 // configuration's relative costs of switching RTs to generate a mask vs
michael@0 266 // longer shaders.
michael@0 267 if (elements.count() <= 4) {
michael@0 268 SkVector clipToRTOffset = { SkIntToScalar(-clipDataIn->fOrigin.fX),
michael@0 269 SkIntToScalar(-clipDataIn->fOrigin.fY) };
michael@0 270 if (elements.isEmpty() ||
michael@0 271 this->installClipEffects(elements, are, clipToRTOffset, devBounds)) {
michael@0 272 SkIRect scissorSpaceIBounds(clipSpaceIBounds);
michael@0 273 scissorSpaceIBounds.offset(-clipDataIn->fOrigin);
michael@0 274 if (NULL == devBounds ||
michael@0 275 !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) {
michael@0 276 fGpu->enableScissor(scissorSpaceIBounds);
michael@0 277 } else {
michael@0 278 fGpu->disableScissor();
michael@0 279 }
michael@0 280 this->setGpuStencil();
michael@0 281 return true;
michael@0 282 }
michael@0 283 }
michael@0 284
michael@0 285 #if GR_AA_CLIP
michael@0 286 // If MSAA is enabled we can do everything in the stencil buffer.
michael@0 287 if (0 == rt->numSamples() && requiresAA) {
michael@0 288 GrTexture* result = NULL;
michael@0 289
michael@0 290 if (this->useSWOnlyPath(elements)) {
michael@0 291 // The clip geometry is complex enough that it will be more efficient to create it
michael@0 292 // entirely in software
michael@0 293 result = this->createSoftwareClipMask(genID,
michael@0 294 initialState,
michael@0 295 elements,
michael@0 296 clipSpaceIBounds);
michael@0 297 } else {
michael@0 298 result = this->createAlphaClipMask(genID,
michael@0 299 initialState,
michael@0 300 elements,
michael@0 301 clipSpaceIBounds);
michael@0 302 }
michael@0 303
michael@0 304 if (NULL != result) {
michael@0 305 // The mask's top left coord should be pinned to the rounded-out top left corner of
michael@0 306 // clipSpace bounds. We determine the mask's position WRT to the render target here.
michael@0 307 SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
michael@0 308 rtSpaceMaskBounds.offset(-clipDataIn->fOrigin);
michael@0 309 are->set(fGpu->drawState());
michael@0 310 setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds);
michael@0 311 fGpu->disableScissor();
michael@0 312 this->setGpuStencil();
michael@0 313 return true;
michael@0 314 }
michael@0 315 // if alpha clip mask creation fails fall through to the non-AA code paths
michael@0 316 }
michael@0 317 #endif // GR_AA_CLIP
michael@0 318
michael@0 319 // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't
michael@0 320 // be created. In either case, free up the texture in the anti-aliased mask cache.
michael@0 321 // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g.,
michael@0 322 // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be
michael@0 323 // "incorrectly" clearing the AA cache.
michael@0 324 fAACache.reset();
michael@0 325
michael@0 326 // If the clip is a rectangle then just set the scissor. Otherwise, create
michael@0 327 // a stencil mask.
michael@0 328 if (isRect) {
michael@0 329 SkIRect clipRect = clipSpaceIBounds;
michael@0 330 clipRect.offset(-clipDataIn->fOrigin);
michael@0 331 fGpu->enableScissor(clipRect);
michael@0 332 this->setGpuStencil();
michael@0 333 return true;
michael@0 334 }
michael@0 335
michael@0 336 // use the stencil clip if we can't represent the clip as a rectangle.
michael@0 337 SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin;
michael@0 338 this->createStencilClipMask(genID,
michael@0 339 initialState,
michael@0 340 elements,
michael@0 341 clipSpaceIBounds,
michael@0 342 clipSpaceToStencilSpaceOffset);
michael@0 343
michael@0 344 // This must occur after createStencilClipMask. That function may change the scissor. Also, it
michael@0 345 // only guarantees that the stencil mask is correct within the bounds it was passed, so we must
michael@0 346 // use both stencil and scissor test to the bounds for the final draw.
michael@0 347 SkIRect scissorSpaceIBounds(clipSpaceIBounds);
michael@0 348 scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
michael@0 349 fGpu->enableScissor(scissorSpaceIBounds);
michael@0 350 this->setGpuStencil();
michael@0 351 return true;
michael@0 352 }
michael@0 353
michael@0 354 #define VISUALIZE_COMPLEX_CLIP 0
michael@0 355
michael@0 356 #if VISUALIZE_COMPLEX_CLIP
michael@0 357 #include "SkRandom.h"
michael@0 358 SkRandom gRandom;
michael@0 359 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
michael@0 360 #else
michael@0 361 #define SET_RANDOM_COLOR
michael@0 362 #endif
michael@0 363
michael@0 364 namespace {
michael@0 365
michael@0 366 ////////////////////////////////////////////////////////////////////////////////
michael@0 367 // set up the OpenGL blend function to perform the specified
michael@0 368 // boolean operation for alpha clip mask creation
michael@0 369 void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
michael@0 370
michael@0 371 switch (op) {
michael@0 372 case SkRegion::kReplace_Op:
michael@0 373 drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
michael@0 374 break;
michael@0 375 case SkRegion::kIntersect_Op:
michael@0 376 drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
michael@0 377 break;
michael@0 378 case SkRegion::kUnion_Op:
michael@0 379 drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
michael@0 380 break;
michael@0 381 case SkRegion::kXOR_Op:
michael@0 382 drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff);
michael@0 383 break;
michael@0 384 case SkRegion::kDifference_Op:
michael@0 385 drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
michael@0 386 break;
michael@0 387 case SkRegion::kReverseDifference_Op:
michael@0 388 drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff);
michael@0 389 break;
michael@0 390 default:
michael@0 391 SkASSERT(false);
michael@0 392 break;
michael@0 393 }
michael@0 394 }
michael@0 395
michael@0 396 }
michael@0 397
michael@0 398 ////////////////////////////////////////////////////////////////////////////////
michael@0 399 bool GrClipMaskManager::drawElement(GrTexture* target,
michael@0 400 const SkClipStack::Element* element,
michael@0 401 GrPathRenderer* pr) {
michael@0 402 GrDrawState* drawState = fGpu->drawState();
michael@0 403
michael@0 404 drawState->setRenderTarget(target->asRenderTarget());
michael@0 405
michael@0 406 // TODO: Draw rrects directly here.
michael@0 407 switch (element->getType()) {
michael@0 408 case Element::kEmpty_Type:
michael@0 409 SkDEBUGFAIL("Should never get here with an empty element.");
michael@0 410 break;
michael@0 411 case Element::kRect_Type:
michael@0 412 // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the
michael@0 413 // entire mask bounds and writes 0 outside the rect.
michael@0 414 if (element->isAA()) {
michael@0 415 getContext()->getAARectRenderer()->fillAARect(fGpu,
michael@0 416 fGpu,
michael@0 417 element->getRect(),
michael@0 418 SkMatrix::I(),
michael@0 419 element->getRect(),
michael@0 420 false);
michael@0 421 } else {
michael@0 422 fGpu->drawSimpleRect(element->getRect(), NULL);
michael@0 423 }
michael@0 424 return true;
michael@0 425 default: {
michael@0 426 SkPath path;
michael@0 427 element->asPath(&path);
michael@0 428 if (path.isInverseFillType()) {
michael@0 429 path.toggleInverseFillType();
michael@0 430 }
michael@0 431 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
michael@0 432 if (NULL == pr) {
michael@0 433 GrPathRendererChain::DrawType type;
michael@0 434 type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType :
michael@0 435 GrPathRendererChain::kColor_DrawType;
michael@0 436 pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type);
michael@0 437 }
michael@0 438 if (NULL == pr) {
michael@0 439 return false;
michael@0 440 }
michael@0 441 pr->drawPath(path, stroke, fGpu, element->isAA());
michael@0 442 break;
michael@0 443 }
michael@0 444 }
michael@0 445 return true;
michael@0 446 }
michael@0 447
michael@0 448 bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target,
michael@0 449 const SkClipStack::Element* element,
michael@0 450 GrPathRenderer** pr) {
michael@0 451 GrDrawState* drawState = fGpu->drawState();
michael@0 452 drawState->setRenderTarget(target->asRenderTarget());
michael@0 453
michael@0 454 if (Element::kRect_Type == element->getType()) {
michael@0 455 return true;
michael@0 456 } else {
michael@0 457 // We shouldn't get here with an empty clip element.
michael@0 458 SkASSERT(Element::kEmpty_Type != element->getType());
michael@0 459 SkPath path;
michael@0 460 element->asPath(&path);
michael@0 461 if (path.isInverseFillType()) {
michael@0 462 path.toggleInverseFillType();
michael@0 463 }
michael@0 464 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
michael@0 465 GrPathRendererChain::DrawType type = element->isAA() ?
michael@0 466 GrPathRendererChain::kStencilAndColorAntiAlias_DrawType :
michael@0 467 GrPathRendererChain::kStencilAndColor_DrawType;
michael@0 468 *pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type);
michael@0 469 return NULL != *pr;
michael@0 470 }
michael@0 471 }
michael@0 472
michael@0 473 void GrClipMaskManager::mergeMask(GrTexture* dstMask,
michael@0 474 GrTexture* srcMask,
michael@0 475 SkRegion::Op op,
michael@0 476 const SkIRect& dstBound,
michael@0 477 const SkIRect& srcBound) {
michael@0 478 GrDrawState::AutoViewMatrixRestore avmr;
michael@0 479 GrDrawState* drawState = fGpu->drawState();
michael@0 480 SkAssertResult(avmr.setIdentity(drawState));
michael@0 481 GrDrawState::AutoRestoreEffects are(drawState);
michael@0 482
michael@0 483 drawState->setRenderTarget(dstMask->asRenderTarget());
michael@0 484
michael@0 485 setup_boolean_blendcoeffs(drawState, op);
michael@0 486
michael@0 487 SkMatrix sampleM;
michael@0 488 sampleM.setIDiv(srcMask->width(), srcMask->height());
michael@0 489
michael@0 490 drawState->addColorEffect(
michael@0 491 GrTextureDomainEffect::Create(srcMask,
michael@0 492 sampleM,
michael@0 493 GrTextureDomain::MakeTexelDomain(srcMask, srcBound),
michael@0 494 GrTextureDomain::kDecal_Mode,
michael@0 495 GrTextureParams::kNone_FilterMode))->unref();
michael@0 496 fGpu->drawSimpleRect(SkRect::Make(dstBound), NULL);
michael@0 497 }
michael@0 498
michael@0 499 // get a texture to act as a temporary buffer for AA clip boolean operations
michael@0 500 // TODO: given the expense of createTexture we may want to just cache this too
michael@0 501 void GrClipMaskManager::getTemp(int width, int height, GrAutoScratchTexture* temp) {
michael@0 502 if (NULL != temp->texture()) {
michael@0 503 // we've already allocated the temp texture
michael@0 504 return;
michael@0 505 }
michael@0 506
michael@0 507 GrTextureDesc desc;
michael@0 508 desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
michael@0 509 desc.fWidth = width;
michael@0 510 desc.fHeight = height;
michael@0 511 desc.fConfig = kAlpha_8_GrPixelConfig;
michael@0 512
michael@0 513 temp->set(this->getContext(), desc);
michael@0 514 }
michael@0 515
michael@0 516 ////////////////////////////////////////////////////////////////////////////////
michael@0 517 // Handles caching & allocation (if needed) of a clip alpha-mask texture for both the sw-upload
michael@0 518 // or gpu-rendered cases. Returns true if there is no more work to be done (i.e., we got a cache
michael@0 519 // hit)
michael@0 520 bool GrClipMaskManager::getMaskTexture(int32_t elementsGenID,
michael@0 521 const SkIRect& clipSpaceIBounds,
michael@0 522 GrTexture** result,
michael@0 523 bool willUpload) {
michael@0 524 bool cached = fAACache.canReuse(elementsGenID, clipSpaceIBounds);
michael@0 525 if (!cached) {
michael@0 526
michael@0 527 // There isn't a suitable entry in the cache so we create a new texture to store the mask.
michael@0 528 // Since we are setting up the cache we know the last lookup was a miss. Free up the
michael@0 529 // currently cached mask so it can be reused.
michael@0 530 fAACache.reset();
michael@0 531
michael@0 532 GrTextureDesc desc;
michael@0 533 desc.fFlags = willUpload ? kNone_GrTextureFlags : kRenderTarget_GrTextureFlagBit;
michael@0 534 desc.fWidth = clipSpaceIBounds.width();
michael@0 535 desc.fHeight = clipSpaceIBounds.height();
michael@0 536 desc.fConfig = kRGBA_8888_GrPixelConfig;
michael@0 537 if (willUpload || this->getContext()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
michael@0 538 // We would always like A8 but it isn't supported on all platforms
michael@0 539 desc.fConfig = kAlpha_8_GrPixelConfig;
michael@0 540 }
michael@0 541
michael@0 542 fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds);
michael@0 543 }
michael@0 544
michael@0 545 *result = fAACache.getLastMask();
michael@0 546 return cached;
michael@0 547 }
michael@0 548
michael@0 549 ////////////////////////////////////////////////////////////////////////////////
michael@0 550 // Create a 8-bit clip mask in alpha
michael@0 551 GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
michael@0 552 InitialState initialState,
michael@0 553 const ElementList& elements,
michael@0 554 const SkIRect& clipSpaceIBounds) {
michael@0 555 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
michael@0 556
michael@0 557 GrTexture* result;
michael@0 558 if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, false)) {
michael@0 559 fCurrClipMaskType = kAlpha_ClipMaskType;
michael@0 560 return result;
michael@0 561 }
michael@0 562
michael@0 563 if (NULL == result) {
michael@0 564 fAACache.reset();
michael@0 565 return NULL;
michael@0 566 }
michael@0 567
michael@0 568 // The top-left of the mask corresponds to the top-left corner of the bounds.
michael@0 569 SkVector clipToMaskOffset = {
michael@0 570 SkIntToScalar(-clipSpaceIBounds.fLeft),
michael@0 571 SkIntToScalar(-clipSpaceIBounds.fTop)
michael@0 572 };
michael@0 573 // The texture may be larger than necessary, this rect represents the part of the texture
michael@0 574 // we populate with a rasterization of the clip.
michael@0 575 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
michael@0 576
michael@0 577 // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
michael@0 578 SkMatrix translate;
michael@0 579 translate.setTranslate(clipToMaskOffset);
michael@0 580 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &translate);
michael@0 581
michael@0 582 GrDrawState* drawState = fGpu->drawState();
michael@0 583
michael@0 584 // We're drawing a coverage mask and want coverage to be run through the blend function.
michael@0 585 drawState->enableState(GrDrawState::kCoverageDrawing_StateBit);
michael@0 586
michael@0 587 // The scratch texture that we are drawing into can be substantially larger than the mask. Only
michael@0 588 // clear the part that we care about.
michael@0 589 fGpu->clear(&maskSpaceIBounds,
michael@0 590 kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
michael@0 591 true,
michael@0 592 result->asRenderTarget());
michael@0 593
michael@0 594 // When we use the stencil in the below loop it is important to have this clip installed.
michael@0 595 // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
michael@0 596 // pass must not set values outside of this bounds or stencil values outside the rect won't be
michael@0 597 // cleared.
michael@0 598 GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds);
michael@0 599 drawState->enableState(GrDrawState::kClip_StateBit);
michael@0 600
michael@0 601 GrAutoScratchTexture temp;
michael@0 602 // walk through each clip element and perform its set op
michael@0 603 for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
michael@0 604 const Element* element = iter.get();
michael@0 605 SkRegion::Op op = element->getOp();
michael@0 606 bool invert = element->isInverseFilled();
michael@0 607
michael@0 608 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
michael@0 609 GrPathRenderer* pr = NULL;
michael@0 610 bool useTemp = !this->canStencilAndDrawElement(result, element, &pr);
michael@0 611 GrTexture* dst;
michael@0 612 // This is the bounds of the clip element in the space of the alpha-mask. The temporary
michael@0 613 // mask buffer can be substantially larger than the actually clip stack element. We
michael@0 614 // touch the minimum number of pixels necessary and use decal mode to combine it with
michael@0 615 // the accumulator.
michael@0 616 SkIRect maskSpaceElementIBounds;
michael@0 617
michael@0 618 if (useTemp) {
michael@0 619 if (invert) {
michael@0 620 maskSpaceElementIBounds = maskSpaceIBounds;
michael@0 621 } else {
michael@0 622 SkRect elementBounds = element->getBounds();
michael@0 623 elementBounds.offset(clipToMaskOffset);
michael@0 624 elementBounds.roundOut(&maskSpaceElementIBounds);
michael@0 625 }
michael@0 626
michael@0 627 this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
michael@0 628 if (NULL == temp.texture()) {
michael@0 629 fAACache.reset();
michael@0 630 return NULL;
michael@0 631 }
michael@0 632 dst = temp.texture();
michael@0 633 // clear the temp target and set blend to replace
michael@0 634 fGpu->clear(&maskSpaceElementIBounds,
michael@0 635 invert ? 0xffffffff : 0x00000000,
michael@0 636 true,
michael@0 637 dst->asRenderTarget());
michael@0 638 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
michael@0 639
michael@0 640 } else {
michael@0 641 // draw directly into the result with the stencil set to make the pixels affected
michael@0 642 // by the clip shape be non-zero.
michael@0 643 dst = result;
michael@0 644 GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
michael@0 645 kReplace_StencilOp,
michael@0 646 kReplace_StencilOp,
michael@0 647 kAlways_StencilFunc,
michael@0 648 0xffff,
michael@0 649 0xffff,
michael@0 650 0xffff);
michael@0 651 drawState->setStencil(kStencilInElement);
michael@0 652 setup_boolean_blendcoeffs(drawState, op);
michael@0 653 }
michael@0 654
michael@0 655 drawState->setAlpha(invert ? 0x00 : 0xff);
michael@0 656
michael@0 657 if (!this->drawElement(dst, element, pr)) {
michael@0 658 fAACache.reset();
michael@0 659 return NULL;
michael@0 660 }
michael@0 661
michael@0 662 if (useTemp) {
michael@0 663 // Now draw into the accumulator using the real operation and the temp buffer as a
michael@0 664 // texture
michael@0 665 this->mergeMask(result,
michael@0 666 temp.texture(),
michael@0 667 op,
michael@0 668 maskSpaceIBounds,
michael@0 669 maskSpaceElementIBounds);
michael@0 670 } else {
michael@0 671 // Draw to the exterior pixels (those with a zero stencil value).
michael@0 672 drawState->setAlpha(invert ? 0xff : 0x00);
michael@0 673 GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
michael@0 674 kZero_StencilOp,
michael@0 675 kZero_StencilOp,
michael@0 676 kEqual_StencilFunc,
michael@0 677 0xffff,
michael@0 678 0x0000,
michael@0 679 0xffff);
michael@0 680 drawState->setStencil(kDrawOutsideElement);
michael@0 681 fGpu->drawSimpleRect(clipSpaceIBounds);
michael@0 682 drawState->disableStencil();
michael@0 683 }
michael@0 684 } else {
michael@0 685 // all the remaining ops can just be directly draw into the accumulation buffer
michael@0 686 drawState->setAlpha(0xff);
michael@0 687 setup_boolean_blendcoeffs(drawState, op);
michael@0 688 this->drawElement(result, element);
michael@0 689 }
michael@0 690 }
michael@0 691
michael@0 692 fCurrClipMaskType = kAlpha_ClipMaskType;
michael@0 693 return result;
michael@0 694 }
michael@0 695
michael@0 696 ////////////////////////////////////////////////////////////////////////////////
michael@0 697 // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
michael@0 698 // (as opposed to canvas) coordinates
michael@0 699 bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID,
michael@0 700 InitialState initialState,
michael@0 701 const ElementList& elements,
michael@0 702 const SkIRect& clipSpaceIBounds,
michael@0 703 const SkIPoint& clipSpaceToStencilOffset) {
michael@0 704
michael@0 705 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
michael@0 706
michael@0 707 GrDrawState* drawState = fGpu->drawState();
michael@0 708 SkASSERT(drawState->isClipState());
michael@0 709
michael@0 710 GrRenderTarget* rt = drawState->getRenderTarget();
michael@0 711 SkASSERT(NULL != rt);
michael@0 712
michael@0 713 // TODO: dynamically attach a SB when needed.
michael@0 714 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
michael@0 715 if (NULL == stencilBuffer) {
michael@0 716 return false;
michael@0 717 }
michael@0 718
michael@0 719 if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
michael@0 720
michael@0 721 stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
michael@0 722
michael@0 723 // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
michael@0 724 SkVector translate = {
michael@0 725 SkIntToScalar(clipSpaceToStencilOffset.fX),
michael@0 726 SkIntToScalar(clipSpaceToStencilOffset.fY)
michael@0 727 };
michael@0 728 SkMatrix matrix;
michael@0 729 matrix.setTranslate(translate);
michael@0 730 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix);
michael@0 731 drawState = fGpu->drawState();
michael@0 732
michael@0 733 drawState->setRenderTarget(rt);
michael@0 734
michael@0 735 // We set the current clip to the bounds so that our recursive draws are scissored to them.
michael@0 736 SkIRect stencilSpaceIBounds(clipSpaceIBounds);
michael@0 737 stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
michael@0 738 GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds);
michael@0 739 drawState->enableState(GrDrawState::kClip_StateBit);
michael@0 740
michael@0 741 #if !VISUALIZE_COMPLEX_CLIP
michael@0 742 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
michael@0 743 #endif
michael@0 744
michael@0 745 int clipBit = stencilBuffer->bits();
michael@0 746 SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
michael@0 747 clipBit = (1 << (clipBit-1));
michael@0 748
michael@0 749 fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState);
michael@0 750
michael@0 751 // walk through each clip element and perform its set op
michael@0 752 // with the existing clip.
michael@0 753 for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) {
michael@0 754 const Element* element = iter.get();
michael@0 755 bool fillInverted = false;
michael@0 756 // enabled at bottom of loop
michael@0 757 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
michael@0 758 // if the target is MSAA then we want MSAA enabled when the clip is soft
michael@0 759 if (rt->isMultisampled()) {
michael@0 760 drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA());
michael@0 761 }
michael@0 762
michael@0 763 // This will be used to determine whether the clip shape can be rendered into the
michael@0 764 // stencil with arbitrary stencil settings.
michael@0 765 GrPathRenderer::StencilSupport stencilSupport;
michael@0 766
michael@0 767 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
michael@0 768
michael@0 769 SkRegion::Op op = element->getOp();
michael@0 770
michael@0 771 GrPathRenderer* pr = NULL;
michael@0 772 SkPath clipPath;
michael@0 773 if (Element::kRect_Type == element->getType()) {
michael@0 774 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
michael@0 775 fillInverted = false;
michael@0 776 } else {
michael@0 777 element->asPath(&clipPath);
michael@0 778 fillInverted = clipPath.isInverseFillType();
michael@0 779 if (fillInverted) {
michael@0 780 clipPath.toggleInverseFillType();
michael@0 781 }
michael@0 782 pr = this->getContext()->getPathRenderer(clipPath,
michael@0 783 stroke,
michael@0 784 fGpu,
michael@0 785 false,
michael@0 786 GrPathRendererChain::kStencilOnly_DrawType,
michael@0 787 &stencilSupport);
michael@0 788 if (NULL == pr) {
michael@0 789 return false;
michael@0 790 }
michael@0 791 }
michael@0 792
michael@0 793 int passes;
michael@0 794 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
michael@0 795
michael@0 796 bool canRenderDirectToStencil =
michael@0 797 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
michael@0 798 bool canDrawDirectToClip; // Given the renderer, the element,
michael@0 799 // fill rule, and set operation can
michael@0 800 // we render the element directly to
michael@0 801 // stencil bit used for clipping.
michael@0 802 canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,
michael@0 803 canRenderDirectToStencil,
michael@0 804 clipBit,
michael@0 805 fillInverted,
michael@0 806 &passes,
michael@0 807 stencilSettings);
michael@0 808
michael@0 809 // draw the element to the client stencil bits if necessary
michael@0 810 if (!canDrawDirectToClip) {
michael@0 811 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
michael@0 812 kIncClamp_StencilOp,
michael@0 813 kIncClamp_StencilOp,
michael@0 814 kAlways_StencilFunc,
michael@0 815 0xffff,
michael@0 816 0x0000,
michael@0 817 0xffff);
michael@0 818 SET_RANDOM_COLOR
michael@0 819 if (Element::kRect_Type == element->getType()) {
michael@0 820 *drawState->stencil() = gDrawToStencil;
michael@0 821 fGpu->drawSimpleRect(element->getRect(), NULL);
michael@0 822 } else {
michael@0 823 if (!clipPath.isEmpty()) {
michael@0 824 if (canRenderDirectToStencil) {
michael@0 825 *drawState->stencil() = gDrawToStencil;
michael@0 826 pr->drawPath(clipPath, stroke, fGpu, false);
michael@0 827 } else {
michael@0 828 pr->stencilPath(clipPath, stroke, fGpu);
michael@0 829 }
michael@0 830 }
michael@0 831 }
michael@0 832 }
michael@0 833
michael@0 834 // now we modify the clip bit by rendering either the clip
michael@0 835 // element directly or a bounding rect of the entire clip.
michael@0 836 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
michael@0 837 for (int p = 0; p < passes; ++p) {
michael@0 838 *drawState->stencil() = stencilSettings[p];
michael@0 839 if (canDrawDirectToClip) {
michael@0 840 if (Element::kRect_Type == element->getType()) {
michael@0 841 SET_RANDOM_COLOR
michael@0 842 fGpu->drawSimpleRect(element->getRect(), NULL);
michael@0 843 } else {
michael@0 844 SET_RANDOM_COLOR
michael@0 845 pr->drawPath(clipPath, stroke, fGpu, false);
michael@0 846 }
michael@0 847 } else {
michael@0 848 SET_RANDOM_COLOR
michael@0 849 // The view matrix is setup to do clip space -> stencil space translation, so
michael@0 850 // draw rect in clip space.
michael@0 851 fGpu->drawSimpleRect(SkRect::Make(clipSpaceIBounds), NULL);
michael@0 852 }
michael@0 853 }
michael@0 854 }
michael@0 855 }
michael@0 856 // set this last because recursive draws may overwrite it back to kNone.
michael@0 857 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
michael@0 858 fCurrClipMaskType = kStencil_ClipMaskType;
michael@0 859 return true;
michael@0 860 }
michael@0 861
michael@0 862
michael@0 863 // mapping of clip-respecting stencil funcs to normal stencil funcs
michael@0 864 // mapping depends on whether stencil-clipping is in effect.
michael@0 865 static const GrStencilFunc
michael@0 866 gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = {
michael@0 867 {// Stencil-Clipping is DISABLED, we are effectively always inside the clip
michael@0 868 // In the Clip Funcs
michael@0 869 kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc
michael@0 870 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
michael@0 871 kLess_StencilFunc, // kLessIfInClip_StencilFunc
michael@0 872 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
michael@0 873 // Special in the clip func that forces user's ref to be 0.
michael@0 874 kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc
michael@0 875 // make ref 0 and do normal nequal.
michael@0 876 },
michael@0 877 {// Stencil-Clipping is ENABLED
michael@0 878 // In the Clip Funcs
michael@0 879 kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc
michael@0 880 // eq stencil clip bit, mask
michael@0 881 // out user bits.
michael@0 882
michael@0 883 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
michael@0 884 // add stencil bit to mask and ref
michael@0 885
michael@0 886 kLess_StencilFunc, // kLessIfInClip_StencilFunc
michael@0 887 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
michael@0 888 // for both of these we can add
michael@0 889 // the clip bit to the mask and
michael@0 890 // ref and compare as normal
michael@0 891 // Special in the clip func that forces user's ref to be 0.
michael@0 892 kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc
michael@0 893 // make ref have only the clip bit set
michael@0 894 // and make comparison be less
michael@0 895 // 10..0 < 1..user_bits..
michael@0 896 }
michael@0 897 };
michael@0 898
michael@0 899 namespace {
michael@0 900 // Sets the settings to clip against the stencil buffer clip while ignoring the
michael@0 901 // client bits.
michael@0 902 const GrStencilSettings& basic_apply_stencil_clip_settings() {
michael@0 903 // stencil settings to use when clip is in stencil
michael@0 904 GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
michael@0 905 kKeep_StencilOp,
michael@0 906 kKeep_StencilOp,
michael@0 907 kAlwaysIfInClip_StencilFunc,
michael@0 908 0x0000,
michael@0 909 0x0000,
michael@0 910 0x0000);
michael@0 911 return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
michael@0 912 }
michael@0 913 }
michael@0 914
michael@0 915 void GrClipMaskManager::setGpuStencil() {
michael@0 916 // We make two copies of the StencilSettings here (except in the early
michael@0 917 // exit scenario. One copy from draw state to the stack var. Then another
michael@0 918 // from the stack var to the gpu. We could make this class hold a ptr to
michael@0 919 // GrGpu's fStencilSettings and eliminate the stack copy here.
michael@0 920
michael@0 921 const GrDrawState& drawState = fGpu->getDrawState();
michael@0 922
michael@0 923 // use stencil for clipping if clipping is enabled and the clip
michael@0 924 // has been written into the stencil.
michael@0 925 GrClipMaskManager::StencilClipMode clipMode;
michael@0 926 if (this->isClipInStencil() && drawState.isClipState()) {
michael@0 927 clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
michael@0 928 // We can't be modifying the clip and respecting it at the same time.
michael@0 929 SkASSERT(!drawState.isStateFlagEnabled(
michael@0 930 GrGpu::kModifyStencilClip_StateBit));
michael@0 931 } else if (drawState.isStateFlagEnabled(
michael@0 932 GrGpu::kModifyStencilClip_StateBit)) {
michael@0 933 clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
michael@0 934 } else {
michael@0 935 clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
michael@0 936 }
michael@0 937
michael@0 938 GrStencilSettings settings;
michael@0 939 // The GrGpu client may not be using the stencil buffer but we may need to
michael@0 940 // enable it in order to respect a stencil clip.
michael@0 941 if (drawState.getStencil().isDisabled()) {
michael@0 942 if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) {
michael@0 943 settings = basic_apply_stencil_clip_settings();
michael@0 944 } else {
michael@0 945 fGpu->disableStencil();
michael@0 946 return;
michael@0 947 }
michael@0 948 } else {
michael@0 949 settings = drawState.getStencil();
michael@0 950 }
michael@0 951
michael@0 952 // TODO: dynamically attach a stencil buffer
michael@0 953 int stencilBits = 0;
michael@0 954 GrStencilBuffer* stencilBuffer =
michael@0 955 drawState.getRenderTarget()->getStencilBuffer();
michael@0 956 if (NULL != stencilBuffer) {
michael@0 957 stencilBits = stencilBuffer->bits();
michael@0 958 }
michael@0 959
michael@0 960 SkASSERT(fGpu->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp());
michael@0 961 SkASSERT(fGpu->caps()->twoSidedStencilSupport() || !settings.isTwoSided());
michael@0 962 this->adjustStencilParams(&settings, clipMode, stencilBits);
michael@0 963 fGpu->setStencilSettings(settings);
michael@0 964 }
michael@0 965
michael@0 966 void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings,
michael@0 967 StencilClipMode mode,
michael@0 968 int stencilBitCnt) {
michael@0 969 SkASSERT(stencilBitCnt > 0);
michael@0 970
michael@0 971 if (kModifyClip_StencilClipMode == mode) {
michael@0 972 // We assume that this clip manager itself is drawing to the GrGpu and
michael@0 973 // has already setup the correct values.
michael@0 974 return;
michael@0 975 }
michael@0 976
michael@0 977 unsigned int clipBit = (1 << (stencilBitCnt - 1));
michael@0 978 unsigned int userBits = clipBit - 1;
michael@0 979
michael@0 980 GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
michael@0 981 bool twoSided = fGpu->caps()->twoSidedStencilSupport();
michael@0 982
michael@0 983 bool finished = false;
michael@0 984 while (!finished) {
michael@0 985 GrStencilFunc func = settings->func(face);
michael@0 986 uint16_t writeMask = settings->writeMask(face);
michael@0 987 uint16_t funcMask = settings->funcMask(face);
michael@0 988 uint16_t funcRef = settings->funcRef(face);
michael@0 989
michael@0 990 SkASSERT((unsigned) func < kStencilFuncCount);
michael@0 991
michael@0 992 writeMask &= userBits;
michael@0 993
michael@0 994 if (func >= kBasicStencilFuncCount) {
michael@0 995 int respectClip = kRespectClip_StencilClipMode == mode;
michael@0 996 if (respectClip) {
michael@0 997 // The GrGpu class should have checked this
michael@0 998 SkASSERT(this->isClipInStencil());
michael@0 999 switch (func) {
michael@0 1000 case kAlwaysIfInClip_StencilFunc:
michael@0 1001 funcMask = clipBit;
michael@0 1002 funcRef = clipBit;
michael@0 1003 break;
michael@0 1004 case kEqualIfInClip_StencilFunc:
michael@0 1005 case kLessIfInClip_StencilFunc:
michael@0 1006 case kLEqualIfInClip_StencilFunc:
michael@0 1007 funcMask = (funcMask & userBits) | clipBit;
michael@0 1008 funcRef = (funcRef & userBits) | clipBit;
michael@0 1009 break;
michael@0 1010 case kNonZeroIfInClip_StencilFunc:
michael@0 1011 funcMask = (funcMask & userBits) | clipBit;
michael@0 1012 funcRef = clipBit;
michael@0 1013 break;
michael@0 1014 default:
michael@0 1015 GrCrash("Unknown stencil func");
michael@0 1016 }
michael@0 1017 } else {
michael@0 1018 funcMask &= userBits;
michael@0 1019 funcRef &= userBits;
michael@0 1020 }
michael@0 1021 const GrStencilFunc* table =
michael@0 1022 gSpecialToBasicStencilFunc[respectClip];
michael@0 1023 func = table[func - kBasicStencilFuncCount];
michael@0 1024 SkASSERT(func >= 0 && func < kBasicStencilFuncCount);
michael@0 1025 } else {
michael@0 1026 funcMask &= userBits;
michael@0 1027 funcRef &= userBits;
michael@0 1028 }
michael@0 1029
michael@0 1030 settings->setFunc(face, func);
michael@0 1031 settings->setWriteMask(face, writeMask);
michael@0 1032 settings->setFuncMask(face, funcMask);
michael@0 1033 settings->setFuncRef(face, funcRef);
michael@0 1034
michael@0 1035 if (GrStencilSettings::kFront_Face == face) {
michael@0 1036 face = GrStencilSettings::kBack_Face;
michael@0 1037 finished = !twoSided;
michael@0 1038 } else {
michael@0 1039 finished = true;
michael@0 1040 }
michael@0 1041 }
michael@0 1042 if (!twoSided) {
michael@0 1043 settings->copyFrontSettingsToBack();
michael@0 1044 }
michael@0 1045 }
michael@0 1046
michael@0 1047 ////////////////////////////////////////////////////////////////////////////////
michael@0 1048 GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID,
michael@0 1049 GrReducedClip::InitialState initialState,
michael@0 1050 const GrReducedClip::ElementList& elements,
michael@0 1051 const SkIRect& clipSpaceIBounds) {
michael@0 1052 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
michael@0 1053
michael@0 1054 GrTexture* result;
michael@0 1055 if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, true)) {
michael@0 1056 return result;
michael@0 1057 }
michael@0 1058
michael@0 1059 if (NULL == result) {
michael@0 1060 fAACache.reset();
michael@0 1061 return NULL;
michael@0 1062 }
michael@0 1063
michael@0 1064 // The mask texture may be larger than necessary. We round out the clip space bounds and pin
michael@0 1065 // the top left corner of the resulting rect to the top left of the texture.
michael@0 1066 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
michael@0 1067
michael@0 1068 GrSWMaskHelper helper(this->getContext());
michael@0 1069
michael@0 1070 SkMatrix matrix;
michael@0 1071 matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft),
michael@0 1072 SkIntToScalar(-clipSpaceIBounds.fTop));
michael@0 1073 helper.init(maskSpaceIBounds, &matrix);
michael@0 1074
michael@0 1075 helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00);
michael@0 1076
michael@0 1077 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
michael@0 1078
michael@0 1079 for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) {
michael@0 1080
michael@0 1081 const Element* element = iter.get();
michael@0 1082 SkRegion::Op op = element->getOp();
michael@0 1083
michael@0 1084 if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
michael@0 1085 // Intersect and reverse difference require modifying pixels outside of the geometry
michael@0 1086 // that is being "drawn". In both cases we erase all the pixels outside of the geometry
michael@0 1087 // but leave the pixels inside the geometry alone. For reverse difference we invert all
michael@0 1088 // the pixels before clearing the ones outside the geometry.
michael@0 1089 if (SkRegion::kReverseDifference_Op == op) {
michael@0 1090 SkRect temp = SkRect::Make(clipSpaceIBounds);
michael@0 1091 // invert the entire scene
michael@0 1092 helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF);
michael@0 1093 }
michael@0 1094
michael@0 1095 SkPath clipPath;
michael@0 1096 element->asPath(&clipPath);
michael@0 1097 clipPath.toggleInverseFillType();
michael@0 1098 helper.draw(clipPath, stroke, SkRegion::kReplace_Op, element->isAA(), 0x00);
michael@0 1099
michael@0 1100 continue;
michael@0 1101 }
michael@0 1102
michael@0 1103 // The other ops (union, xor, diff) only affect pixels inside
michael@0 1104 // the geometry so they can just be drawn normally
michael@0 1105 if (Element::kRect_Type == element->getType()) {
michael@0 1106 helper.draw(element->getRect(), op, element->isAA(), 0xFF);
michael@0 1107 } else {
michael@0 1108 SkPath path;
michael@0 1109 element->asPath(&path);
michael@0 1110 helper.draw(path, stroke, op, element->isAA(), 0xFF);
michael@0 1111 }
michael@0 1112 }
michael@0 1113
michael@0 1114 helper.toTexture(result);
michael@0 1115
michael@0 1116 fCurrClipMaskType = kAlpha_ClipMaskType;
michael@0 1117 return result;
michael@0 1118 }
michael@0 1119
michael@0 1120 ////////////////////////////////////////////////////////////////////////////////
michael@0 1121 void GrClipMaskManager::releaseResources() {
michael@0 1122 fAACache.releaseResources();
michael@0 1123 }
michael@0 1124
michael@0 1125 void GrClipMaskManager::setGpu(GrGpu* gpu) {
michael@0 1126 fGpu = gpu;
michael@0 1127 fAACache.setContext(gpu->getContext());
michael@0 1128 }
michael@0 1129
michael@0 1130 void GrClipMaskManager::adjustPathStencilParams(GrStencilSettings* settings) {
michael@0 1131 const GrDrawState& drawState = fGpu->getDrawState();
michael@0 1132 GrClipMaskManager::StencilClipMode clipMode;
michael@0 1133 if (this->isClipInStencil() && drawState.isClipState()) {
michael@0 1134 clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
michael@0 1135 // We can't be modifying the clip and respecting it at the same time.
michael@0 1136 SkASSERT(!drawState.isStateFlagEnabled(
michael@0 1137 GrGpu::kModifyStencilClip_StateBit));
michael@0 1138 } else if (drawState.isStateFlagEnabled(
michael@0 1139 GrGpu::kModifyStencilClip_StateBit)) {
michael@0 1140 clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
michael@0 1141 } else {
michael@0 1142 clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
michael@0 1143 }
michael@0 1144
michael@0 1145 // TODO: dynamically attach a stencil buffer
michael@0 1146 int stencilBits = 0;
michael@0 1147 GrStencilBuffer* stencilBuffer =
michael@0 1148 drawState.getRenderTarget()->getStencilBuffer();
michael@0 1149 if (NULL != stencilBuffer) {
michael@0 1150 stencilBits = stencilBuffer->bits();
michael@0 1151 this->adjustStencilParams(settings, clipMode, stencilBits);
michael@0 1152 }
michael@0 1153 }

mercurial