OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrClipMaskManager.h" | 8 #include "GrClipMaskManager.h" |
9 #include "GrAAConvexPathRenderer.h" | 9 #include "GrAAConvexPathRenderer.h" |
10 #include "GrAAHairLinePathRenderer.h" | 10 #include "GrAAHairLinePathRenderer.h" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 mat, | 49 mat, |
50 GrTextureDomain::MakeTexelDomain(result, d
omainTexels), | 50 GrTextureDomain::MakeTexelDomain(result, d
omainTexels), |
51 GrTextureDomain::kDecal_Mode, | 51 GrTextureDomain::kDecal_Mode, |
52 GrTextureParams::kNone_FilterMode, | 52 GrTextureParams::kNone_FilterMode, |
53 kDevice_GrCoordSet))->unref(); | 53 kDevice_GrCoordSet))->unref(); |
54 } | 54 } |
55 | 55 |
56 bool path_needs_SW_renderer(GrContext* context, | 56 bool path_needs_SW_renderer(GrContext* context, |
57 const GrDrawTarget* gpu, | 57 const GrDrawTarget* gpu, |
58 const GrDrawState* drawState, | 58 const GrDrawState* drawState, |
| 59 const SkMatrix& viewMatrix, |
59 const SkPath& origPath, | 60 const SkPath& origPath, |
60 const SkStrokeRec& stroke, | 61 const SkStrokeRec& stroke, |
61 bool doAA) { | 62 bool doAA) { |
62 // the gpu alpha mask will draw the inverse paths as non-inverse to a temp b
uffer | 63 // the gpu alpha mask will draw the inverse paths as non-inverse to a temp b
uffer |
63 SkTCopyOnFirstWrite<SkPath> path(origPath); | 64 SkTCopyOnFirstWrite<SkPath> path(origPath); |
64 if (path->isInverseFillType()) { | 65 if (path->isInverseFillType()) { |
65 path.writable()->toggleInverseFillType(); | 66 path.writable()->toggleInverseFillType(); |
66 } | 67 } |
67 // last (false) parameter disallows use of the SW path renderer | 68 // last (false) parameter disallows use of the SW path renderer |
68 GrPathRendererChain::DrawType type = doAA ? | 69 GrPathRendererChain::DrawType type = doAA ? |
69 GrPathRendererChain::kColorAntiAlias_Dr
awType : | 70 GrPathRendererChain::kColorAntiAlias_Dr
awType : |
70 GrPathRendererChain::kColor_DrawType; | 71 GrPathRendererChain::kColor_DrawType; |
71 | 72 |
72 return NULL == context->getPathRenderer(gpu, drawState, *path, stroke, false
, type); | 73 return NULL == context->getPathRenderer(gpu, drawState, viewMatrix, *path, s
troke, false, type); |
73 } | 74 } |
74 } | 75 } |
75 | 76 |
76 /* | 77 /* |
77 * This method traverses the clip stack to see if the GrSoftwarePathRenderer | 78 * This method traverses the clip stack to see if the GrSoftwarePathRenderer |
78 * will be used on any element. If so, it returns true to indicate that the | 79 * will be used on any element. If so, it returns true to indicate that the |
79 * entire clip should be rendered in SW and then uploaded en masse to the gpu. | 80 * entire clip should be rendered in SW and then uploaded en masse to the gpu. |
80 */ | 81 */ |
81 bool GrClipMaskManager::useSWOnlyPath(const GrDrawState* drawState, | 82 bool GrClipMaskManager::useSWOnlyPath(const GrDrawState* drawState, |
| 83 const SkVector& clipToMaskOffset, |
82 const GrReducedClip::ElementList& elements
) { | 84 const GrReducedClip::ElementList& elements
) { |
83 // TODO: generalize this function so that when | 85 // TODO: generalize this function so that when |
84 // a clip gets complex enough it can just be done in SW regardless | 86 // a clip gets complex enough it can just be done in SW regardless |
85 // of whether it would invoke the GrSoftwarePathRenderer. | 87 // of whether it would invoke the GrSoftwarePathRenderer. |
86 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); | 88 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
87 | 89 |
| 90 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip |
| 91 // space. |
| 92 SkMatrix translate; |
| 93 translate.setTranslate(clipToMaskOffset); |
| 94 |
88 for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get();
iter.next()) { | 95 for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get();
iter.next()) { |
89 const Element* element = iter.get(); | 96 const Element* element = iter.get(); |
90 // rects can always be drawn directly w/o using the software path | 97 // rects can always be drawn directly w/o using the software path |
91 // Skip rrects once we're drawing them directly. | 98 // Skip rrects once we're drawing them directly. |
92 if (Element::kRect_Type != element->getType()) { | 99 if (Element::kRect_Type != element->getType()) { |
93 SkPath path; | 100 SkPath path; |
94 element->asPath(&path); | 101 element->asPath(&path); |
95 if (path_needs_SW_renderer(this->getContext(), fClipTarget, drawStat
e, path, stroke, | 102 if (path_needs_SW_renderer(this->getContext(), fClipTarget, drawStat
e, translate, |
96 element->isAA())) { | 103 path, stroke, element->isAA())) { |
97 return true; | 104 return true; |
98 } | 105 } |
99 } | 106 } |
100 } | 107 } |
101 return false; | 108 return false; |
102 } | 109 } |
103 | 110 |
104 bool GrClipMaskManager::installClipEffects(GrDrawState* drawState, | 111 bool GrClipMaskManager::installClipEffects(GrDrawState* drawState, |
105 GrDrawState::AutoRestoreEffects* are, | 112 GrDrawState::AutoRestoreEffects* are, |
106 const GrReducedClip::ElementList& ele
ments, | 113 const GrReducedClip::ElementList& ele
ments, |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 this->setDrawStateStencil(drawState, ars); | 273 this->setDrawStateStencil(drawState, ars); |
267 return true; | 274 return true; |
268 } | 275 } |
269 } | 276 } |
270 | 277 |
271 #if GR_AA_CLIP | 278 #if GR_AA_CLIP |
272 // If MSAA is enabled we can do everything in the stencil buffer. | 279 // If MSAA is enabled we can do everything in the stencil buffer. |
273 if (0 == rt->numSamples() && requiresAA) { | 280 if (0 == rt->numSamples() && requiresAA) { |
274 GrTexture* result = NULL; | 281 GrTexture* result = NULL; |
275 | 282 |
276 if (this->useSWOnlyPath(drawState, elements)) { | 283 // The top-left of the mask corresponds to the top-left corner of the bo
unds. |
| 284 SkVector clipToMaskOffset = { |
| 285 SkIntToScalar(-clipSpaceIBounds.fLeft), |
| 286 SkIntToScalar(-clipSpaceIBounds.fTop) |
| 287 }; |
| 288 |
| 289 if (this->useSWOnlyPath(drawState, clipToMaskOffset, elements)) { |
277 // The clip geometry is complex enough that it will be more efficien
t to create it | 290 // The clip geometry is complex enough that it will be more efficien
t to create it |
278 // entirely in software | 291 // entirely in software |
279 result = this->createSoftwareClipMask(genID, | 292 result = this->createSoftwareClipMask(genID, |
280 initialState, | 293 initialState, |
281 elements, | 294 elements, |
| 295 clipToMaskOffset, |
282 clipSpaceIBounds); | 296 clipSpaceIBounds); |
283 } else { | 297 } else { |
284 result = this->createAlphaClipMask(genID, | 298 result = this->createAlphaClipMask(genID, |
285 initialState, | 299 initialState, |
286 elements, | 300 elements, |
| 301 clipToMaskOffset, |
287 clipSpaceIBounds); | 302 clipSpaceIBounds); |
288 } | 303 } |
289 | 304 |
290 if (result) { | 305 if (result) { |
291 // The mask's top left coord should be pinned to the rounded-out top
left corner of | 306 // The mask's top left coord should be pinned to the rounded-out top
left corner of |
292 // clipSpace bounds. We determine the mask's position WRT to the ren
der target here. | 307 // clipSpace bounds. We determine the mask's position WRT to the ren
der target here. |
293 SkIRect rtSpaceMaskBounds = clipSpaceIBounds; | 308 SkIRect rtSpaceMaskBounds = clipSpaceIBounds; |
294 rtSpaceMaskBounds.offset(-clipDataIn->fOrigin); | 309 rtSpaceMaskBounds.offset(-clipDataIn->fOrigin); |
295 setup_drawstate_aaclip(rtSpaceMaskBounds, drawState, result); | 310 setup_drawstate_aaclip(rtSpaceMaskBounds, drawState, result); |
296 this->setDrawStateStencil(drawState, ars); | 311 this->setDrawStateStencil(drawState, ars); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 //////////////////////////////////////////////////////////////////////////////// | 345 //////////////////////////////////////////////////////////////////////////////// |
331 // Set a coverage drawing XPF on the drawState for the given op and invertCovera
ge mode | 346 // Set a coverage drawing XPF on the drawState for the given op and invertCovera
ge mode |
332 void set_coverage_drawing_xpf(SkRegion::Op op, bool invertCoverage, GrDrawState*
drawState) { | 347 void set_coverage_drawing_xpf(SkRegion::Op op, bool invertCoverage, GrDrawState*
drawState) { |
333 SkASSERT(op <= SkRegion::kLastOp); | 348 SkASSERT(op <= SkRegion::kLastOp); |
334 drawState->setCoverageSetOpXPFactory(op, invertCoverage); | 349 drawState->setCoverageSetOpXPFactory(op, invertCoverage); |
335 } | 350 } |
336 } | 351 } |
337 | 352 |
338 //////////////////////////////////////////////////////////////////////////////// | 353 //////////////////////////////////////////////////////////////////////////////// |
339 bool GrClipMaskManager::drawElement(GrDrawState* drawState, | 354 bool GrClipMaskManager::drawElement(GrDrawState* drawState, |
| 355 const SkMatrix& viewMatrix, |
340 GrTexture* target, | 356 GrTexture* target, |
341 const SkClipStack::Element* element, | 357 const SkClipStack::Element* element, |
342 GrPathRenderer* pr) { | 358 GrPathRenderer* pr) { |
343 GrDrawTarget::AutoGeometryPush agp(fClipTarget); | 359 GrDrawTarget::AutoGeometryPush agp(fClipTarget); |
344 | 360 |
345 drawState->setRenderTarget(target->asRenderTarget()); | 361 drawState->setRenderTarget(target->asRenderTarget()); |
346 | 362 |
347 // The color we use to draw does not matter since we will always be using a
GrCoverageSetOpXP | 363 // The color we use to draw does not matter since we will always be using a
GrCoverageSetOpXP |
348 // which ignores color. | 364 // which ignores color. |
349 GrColor color = GrColor_WHITE; | 365 GrColor color = GrColor_WHITE; |
350 | 366 |
351 // TODO: Draw rrects directly here. | 367 // TODO: Draw rrects directly here. |
352 switch (element->getType()) { | 368 switch (element->getType()) { |
353 case Element::kEmpty_Type: | 369 case Element::kEmpty_Type: |
354 SkDEBUGFAIL("Should never get here with an empty element."); | 370 SkDEBUGFAIL("Should never get here with an empty element."); |
355 break; | 371 break; |
356 case Element::kRect_Type: | 372 case Element::kRect_Type: |
357 // TODO: Do rects directly to the accumulator using a aa-rect GrProc
essor that covers | 373 // TODO: Do rects directly to the accumulator using a aa-rect GrProc
essor that covers |
358 // the entire mask bounds and writes 0 outside the rect. | 374 // the entire mask bounds and writes 0 outside the rect. |
359 if (element->isAA()) { | 375 if (element->isAA()) { |
| 376 SkRect devRect = element->getRect(); |
| 377 viewMatrix.mapRect(&devRect); |
360 this->getContext()->getAARectRenderer()->fillAARect(fClipTarget, | 378 this->getContext()->getAARectRenderer()->fillAARect(fClipTarget, |
361 drawState, | 379 drawState, |
362 color, | 380 color, |
363 SkMatrix::I(
), | 381 viewMatrix, |
364 element->get
Rect(), | 382 element->get
Rect(), |
365 SkMatrix::I(
), | 383 devRect); |
366 element->get
Rect()); | |
367 } else { | 384 } else { |
368 fClipTarget->drawSimpleRect(drawState, color, element->getRect()
); | 385 fClipTarget->drawSimpleRect(drawState, color, viewMatrix, elemen
t->getRect()); |
369 } | 386 } |
370 return true; | 387 return true; |
371 default: { | 388 default: { |
372 SkPath path; | 389 SkPath path; |
373 element->asPath(&path); | 390 element->asPath(&path); |
374 path.setIsVolatile(true); | 391 path.setIsVolatile(true); |
375 if (path.isInverseFillType()) { | 392 if (path.isInverseFillType()) { |
376 path.toggleInverseFillType(); | 393 path.toggleInverseFillType(); |
377 } | 394 } |
378 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); | 395 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
379 if (NULL == pr) { | 396 if (NULL == pr) { |
380 GrPathRendererChain::DrawType type; | 397 GrPathRendererChain::DrawType type; |
381 type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_Dr
awType : | 398 type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_Dr
awType : |
382 GrPathRendererChain::kColor_DrawType; | 399 GrPathRendererChain::kColor_DrawType; |
383 pr = this->getContext()->getPathRenderer(fClipTarget, drawState,
path, stroke, | 400 pr = this->getContext()->getPathRenderer(fClipTarget, drawState,
viewMatrix, path, |
384 false, type); | 401 stroke, false, type); |
385 } | 402 } |
386 if (NULL == pr) { | 403 if (NULL == pr) { |
387 return false; | 404 return false; |
388 } | 405 } |
389 | 406 |
390 pr->drawPath(fClipTarget, drawState, color, path, stroke, element->i
sAA()); | 407 pr->drawPath(fClipTarget, drawState, color, viewMatrix, path, stroke
, element->isAA()); |
391 break; | 408 break; |
392 } | 409 } |
393 } | 410 } |
394 return true; | 411 return true; |
395 } | 412 } |
396 | 413 |
397 bool GrClipMaskManager::canStencilAndDrawElement(GrDrawState* drawState, | 414 bool GrClipMaskManager::canStencilAndDrawElement(GrDrawState* drawState, |
398 GrTexture* target, | 415 GrTexture* target, |
399 GrPathRenderer** pr, | 416 GrPathRenderer** pr, |
400 const SkClipStack::Element* ele
ment) { | 417 const SkClipStack::Element* ele
ment) { |
401 drawState->setRenderTarget(target->asRenderTarget()); | 418 drawState->setRenderTarget(target->asRenderTarget()); |
402 | 419 |
403 if (Element::kRect_Type == element->getType()) { | 420 if (Element::kRect_Type == element->getType()) { |
404 return true; | 421 return true; |
405 } else { | 422 } else { |
406 // We shouldn't get here with an empty clip element. | 423 // We shouldn't get here with an empty clip element. |
407 SkASSERT(Element::kEmpty_Type != element->getType()); | 424 SkASSERT(Element::kEmpty_Type != element->getType()); |
408 SkPath path; | 425 SkPath path; |
409 element->asPath(&path); | 426 element->asPath(&path); |
410 if (path.isInverseFillType()) { | 427 if (path.isInverseFillType()) { |
411 path.toggleInverseFillType(); | 428 path.toggleInverseFillType(); |
412 } | 429 } |
413 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); | 430 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
414 GrPathRendererChain::DrawType type = element->isAA() ? | 431 GrPathRendererChain::DrawType type = element->isAA() ? |
415 GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : | 432 GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : |
416 GrPathRendererChain::kStencilAndColor_DrawType; | 433 GrPathRendererChain::kStencilAndColor_DrawType; |
417 *pr = this->getContext()->getPathRenderer(fClipTarget, drawState, path,
stroke, false, | 434 *pr = this->getContext()->getPathRenderer(fClipTarget, drawState, SkMatr
ix::I(), path, |
418 type); | 435 stroke, false, type); |
419 return SkToBool(*pr); | 436 return SkToBool(*pr); |
420 } | 437 } |
421 } | 438 } |
422 | 439 |
423 void GrClipMaskManager::mergeMask(GrDrawState* drawState, | 440 void GrClipMaskManager::mergeMask(GrDrawState* drawState, |
424 GrTexture* dstMask, | 441 GrTexture* dstMask, |
425 GrTexture* srcMask, | 442 GrTexture* srcMask, |
426 SkRegion::Op op, | 443 SkRegion::Op op, |
427 const SkIRect& dstBound, | 444 const SkIRect& dstBound, |
428 const SkIRect& srcBound) { | 445 const SkIRect& srcBound) { |
429 drawState->setRenderTarget(dstMask->asRenderTarget()); | 446 drawState->setRenderTarget(dstMask->asRenderTarget()); |
430 | 447 |
431 // We want to invert the coverage here | 448 // We want to invert the coverage here |
432 set_coverage_drawing_xpf(op, false, drawState); | 449 set_coverage_drawing_xpf(op, false, drawState); |
433 | 450 |
434 SkMatrix sampleM; | 451 SkMatrix sampleM; |
435 sampleM.setIDiv(srcMask->width(), srcMask->height()); | 452 sampleM.setIDiv(srcMask->width(), srcMask->height()); |
436 | 453 |
437 drawState->addCoverageProcessor( | 454 drawState->addCoverageProcessor( |
438 GrTextureDomainEffect::Create(srcMask, | 455 GrTextureDomainEffect::Create(srcMask, |
439 sampleM, | 456 sampleM, |
440 GrTextureDomain::MakeTexelDomain(srcMask,
srcBound), | 457 GrTextureDomain::MakeTexelDomain(srcMask,
srcBound), |
441 GrTextureDomain::kDecal_Mode, | 458 GrTextureDomain::kDecal_Mode, |
442 GrTextureParams::kNone_FilterMode))->unref
(); | 459 GrTextureParams::kNone_FilterMode))->unref
(); |
443 // The color passed in here does not matter since the coverageSetOpXP won't
read it. | 460 // The color passed in here does not matter since the coverageSetOpXP won't
read it. |
444 fClipTarget->drawSimpleRect(drawState, GrColor_WHITE, SkRect::Make(dstBound)
); | 461 fClipTarget->drawSimpleRect(drawState, GrColor_WHITE, SkMatrix::I(), SkRect:
:Make(dstBound)); |
445 } | 462 } |
446 | 463 |
447 GrTexture* GrClipMaskManager::createTempMask(int width, int height) { | 464 GrTexture* GrClipMaskManager::createTempMask(int width, int height) { |
448 GrSurfaceDesc desc; | 465 GrSurfaceDesc desc; |
449 desc.fFlags = kRenderTarget_GrSurfaceFlag; | 466 desc.fFlags = kRenderTarget_GrSurfaceFlag; |
450 desc.fWidth = width; | 467 desc.fWidth = width; |
451 desc.fHeight = height; | 468 desc.fHeight = height; |
452 if (this->getContext()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { | 469 if (this->getContext()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { |
453 desc.fConfig = kAlpha_8_GrPixelConfig; | 470 desc.fConfig = kAlpha_8_GrPixelConfig; |
454 } else { | 471 } else { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 | 509 |
493 fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds); | 510 fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds); |
494 return fAACache.getLastMask(); | 511 return fAACache.getLastMask(); |
495 } | 512 } |
496 | 513 |
497 //////////////////////////////////////////////////////////////////////////////// | 514 //////////////////////////////////////////////////////////////////////////////// |
498 // Create a 8-bit clip mask in alpha | 515 // Create a 8-bit clip mask in alpha |
499 GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, | 516 GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, |
500 GrReducedClip::InitialState in
itialState, | 517 GrReducedClip::InitialState in
itialState, |
501 const GrReducedClip::ElementLi
st& elements, | 518 const GrReducedClip::ElementLi
st& elements, |
| 519 const SkVector& clipToMaskOffs
et, |
502 const SkIRect& clipSpaceIBound
s) { | 520 const SkIRect& clipSpaceIBound
s) { |
503 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); | 521 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); |
504 | 522 |
505 // First, check for cached texture | 523 // First, check for cached texture |
506 GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBoun
ds); | 524 GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBoun
ds); |
507 if (result) { | 525 if (result) { |
508 fCurrClipMaskType = kAlpha_ClipMaskType; | 526 fCurrClipMaskType = kAlpha_ClipMaskType; |
509 return result; | 527 return result; |
510 } | 528 } |
511 | 529 |
512 // There's no texture in the cache. Let's try to allocate it then. | 530 // There's no texture in the cache. Let's try to allocate it then. |
513 result = this->allocMaskTexture(elementsGenID, clipSpaceIBounds, false); | 531 result = this->allocMaskTexture(elementsGenID, clipSpaceIBounds, false); |
514 if (NULL == result) { | 532 if (NULL == result) { |
515 fAACache.reset(); | 533 fAACache.reset(); |
516 return NULL; | 534 return NULL; |
517 } | 535 } |
518 | 536 |
519 // The top-left of the mask corresponds to the top-left corner of the bounds
. | 537 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip |
520 SkVector clipToMaskOffset = { | 538 // space. |
521 SkIntToScalar(-clipSpaceIBounds.fLeft), | 539 SkMatrix translate; |
522 SkIntToScalar(-clipSpaceIBounds.fTop) | 540 translate.setTranslate(clipToMaskOffset); |
523 }; | 541 |
524 // The texture may be larger than necessary, this rect represents the part o
f the texture | 542 // The texture may be larger than necessary, this rect represents the part o
f the texture |
525 // we populate with a rasterization of the clip. | 543 // we populate with a rasterization of the clip. |
526 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpa
ceIBounds.height()); | 544 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpa
ceIBounds.height()); |
527 | 545 |
528 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip space. | |
529 SkMatrix translate; | |
530 translate.setTranslate(clipToMaskOffset); | |
531 | |
532 // The scratch texture that we are drawing into can be substantially larger
than the mask. Only | 546 // The scratch texture that we are drawing into can be substantially larger
than the mask. Only |
533 // clear the part that we care about. | 547 // clear the part that we care about. |
534 fClipTarget->clear(&maskSpaceIBounds, | 548 fClipTarget->clear(&maskSpaceIBounds, |
535 GrReducedClip::kAllIn_InitialState == initialState ? 0xff
ffffff : 0x00000000, | 549 GrReducedClip::kAllIn_InitialState == initialState ? 0xff
ffffff : 0x00000000, |
536 true, | 550 true, |
537 result->asRenderTarget()); | 551 result->asRenderTarget()); |
538 | 552 |
539 // When we use the stencil in the below loop it is important to have this cl
ip installed. | 553 // When we use the stencil in the below loop it is important to have this cl
ip installed. |
540 // The second pass that zeros the stencil buffer renders the rect maskSpaceI
Bounds so the first | 554 // The second pass that zeros the stencil buffer renders the rect maskSpaceI
Bounds so the first |
541 // pass must not set values outside of this bounds or stencil values outside
the rect won't be | 555 // pass must not set values outside of this bounds or stencil values outside
the rect won't be |
542 // cleared. | 556 // cleared. |
543 GrDrawTarget::AutoClipRestore acr(fClipTarget, maskSpaceIBounds); | 557 GrDrawTarget::AutoClipRestore acr(fClipTarget, maskSpaceIBounds); |
544 SkAutoTUnref<GrTexture> temp; | 558 SkAutoTUnref<GrTexture> temp; |
545 | 559 |
546 // walk through each clip element and perform its set op | 560 // walk through each clip element and perform its set op |
547 for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get()
; iter.next()) { | 561 for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get()
; iter.next()) { |
548 const Element* element = iter.get(); | 562 const Element* element = iter.get(); |
549 SkRegion::Op op = element->getOp(); | 563 SkRegion::Op op = element->getOp(); |
550 bool invert = element->isInverseFilled(); | 564 bool invert = element->isInverseFilled(); |
551 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDiffere
nce_Op == op) { | 565 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDiffere
nce_Op == op) { |
552 GrDrawState drawState(translate); | 566 GrDrawState drawState; |
553 drawState.enableState(GrDrawState::kClip_StateBit); | 567 drawState.enableState(GrDrawState::kClip_StateBit); |
554 | 568 |
555 GrPathRenderer* pr = NULL; | 569 GrPathRenderer* pr = NULL; |
556 bool useTemp = !this->canStencilAndDrawElement(&drawState, result, &
pr, element); | 570 bool useTemp = !this->canStencilAndDrawElement(&drawState, result, &
pr, element); |
557 GrTexture* dst; | 571 GrTexture* dst; |
558 // This is the bounds of the clip element in the space of the alpha-
mask. The temporary | 572 // This is the bounds of the clip element in the space of the alpha-
mask. The temporary |
559 // mask buffer can be substantially larger than the actually clip st
ack element. We | 573 // mask buffer can be substantially larger than the actually clip st
ack element. We |
560 // touch the minimum number of pixels necessary and use decal mode t
o combine it with | 574 // touch the minimum number of pixels necessary and use decal mode t
o combine it with |
561 // the accumulator. | 575 // the accumulator. |
562 SkIRect maskSpaceElementIBounds; | 576 SkIRect maskSpaceElementIBounds; |
(...skipping 30 matching lines...) Expand all Loading... |
593 kReplace_StencilOp, | 607 kReplace_StencilOp, |
594 kReplace_StencilOp, | 608 kReplace_StencilOp, |
595 kAlways_StencilFunc, | 609 kAlways_StencilFunc, |
596 0xffff, | 610 0xffff, |
597 0xffff, | 611 0xffff, |
598 0xffff); | 612 0xffff); |
599 drawState.setStencil(kStencilInElement); | 613 drawState.setStencil(kStencilInElement); |
600 set_coverage_drawing_xpf(op, invert, &drawState); | 614 set_coverage_drawing_xpf(op, invert, &drawState); |
601 } | 615 } |
602 | 616 |
603 if (!this->drawElement(&drawState, dst, element, pr)) { | 617 if (!this->drawElement(&drawState, translate, dst, element, pr)) { |
604 fAACache.reset(); | 618 fAACache.reset(); |
605 return NULL; | 619 return NULL; |
606 } | 620 } |
607 | 621 |
608 if (useTemp) { | 622 if (useTemp) { |
609 GrDrawState backgroundDrawState; | 623 GrDrawState backgroundDrawState; |
610 backgroundDrawState.enableState(GrDrawState::kClip_StateBit); | 624 backgroundDrawState.enableState(GrDrawState::kClip_StateBit); |
611 backgroundDrawState.setRenderTarget(result->asRenderTarget()); | 625 backgroundDrawState.setRenderTarget(result->asRenderTarget()); |
612 | 626 |
613 // Now draw into the accumulator using the real operation and th
e temp buffer as a | 627 // Now draw into the accumulator using the real operation and th
e temp buffer as a |
614 // texture | 628 // texture |
615 this->mergeMask(&backgroundDrawState, | 629 this->mergeMask(&backgroundDrawState, |
616 result, | 630 result, |
617 temp, | 631 temp, |
618 op, | 632 op, |
619 maskSpaceIBounds, | 633 maskSpaceIBounds, |
620 maskSpaceElementIBounds); | 634 maskSpaceElementIBounds); |
621 } else { | 635 } else { |
622 GrDrawState backgroundDrawState(translate); | 636 GrDrawState backgroundDrawState; |
623 backgroundDrawState.enableState(GrDrawState::kClip_StateBit); | 637 backgroundDrawState.enableState(GrDrawState::kClip_StateBit); |
624 backgroundDrawState.setRenderTarget(result->asRenderTarget()); | 638 backgroundDrawState.setRenderTarget(result->asRenderTarget()); |
625 | 639 |
626 set_coverage_drawing_xpf(op, !invert, &backgroundDrawState); | 640 set_coverage_drawing_xpf(op, !invert, &backgroundDrawState); |
627 // Draw to the exterior pixels (those with a zero stencil value)
. | 641 // Draw to the exterior pixels (those with a zero stencil value)
. |
628 GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, | 642 GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, |
629 kZero_StencilOp, | 643 kZero_StencilOp, |
630 kZero_StencilOp, | 644 kZero_StencilOp, |
631 kEqual_StencilFunc, | 645 kEqual_StencilFunc, |
632 0xffff, | 646 0xffff, |
633 0x0000, | 647 0x0000, |
634 0xffff); | 648 0xffff); |
635 backgroundDrawState.setStencil(kDrawOutsideElement); | 649 backgroundDrawState.setStencil(kDrawOutsideElement); |
636 // The color passed in here does not matter since the coverageSe
tOpXP won't read it. | 650 // The color passed in here does not matter since the coverageSe
tOpXP won't read it. |
637 fClipTarget->drawSimpleRect(&backgroundDrawState, GrColor_WHITE,
clipSpaceIBounds); | 651 fClipTarget->drawSimpleRect(&backgroundDrawState, GrColor_WHITE,
translate, |
| 652 clipSpaceIBounds); |
638 } | 653 } |
639 } else { | 654 } else { |
640 GrDrawState drawState(translate); | 655 GrDrawState drawState; |
641 drawState.enableState(GrDrawState::kClip_StateBit); | 656 drawState.enableState(GrDrawState::kClip_StateBit); |
642 | 657 |
643 // all the remaining ops can just be directly draw into the accumula
tion buffer | 658 // all the remaining ops can just be directly draw into the accumula
tion buffer |
644 set_coverage_drawing_xpf(op, false, &drawState); | 659 set_coverage_drawing_xpf(op, false, &drawState); |
645 // The color passed in here does not matter since the coverageSetOpX
P won't read it. | 660 // The color passed in here does not matter since the coverageSetOpX
P won't read it. |
646 this->drawElement(&drawState, result, element); | 661 this->drawElement(&drawState, translate, result, element); |
647 } | 662 } |
648 } | 663 } |
649 | 664 |
650 fCurrClipMaskType = kAlpha_ClipMaskType; | 665 fCurrClipMaskType = kAlpha_ClipMaskType; |
651 return result; | 666 return result; |
652 } | 667 } |
653 | 668 |
654 //////////////////////////////////////////////////////////////////////////////// | 669 //////////////////////////////////////////////////////////////////////////////// |
655 // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device | 670 // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device |
656 // (as opposed to canvas) coordinates | 671 // (as opposed to canvas) coordinates |
(...skipping 12 matching lines...) Expand all Loading... |
669 return false; | 684 return false; |
670 } | 685 } |
671 | 686 |
672 if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpace
ToStencilOffset)) { | 687 if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpace
ToStencilOffset)) { |
673 stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToS
tencilOffset); | 688 stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToS
tencilOffset); |
674 // Set the matrix so that rendered clip elements are transformed from cl
ip to stencil space. | 689 // Set the matrix so that rendered clip elements are transformed from cl
ip to stencil space. |
675 SkVector translate = { | 690 SkVector translate = { |
676 SkIntToScalar(clipSpaceToStencilOffset.fX), | 691 SkIntToScalar(clipSpaceToStencilOffset.fX), |
677 SkIntToScalar(clipSpaceToStencilOffset.fY) | 692 SkIntToScalar(clipSpaceToStencilOffset.fY) |
678 }; | 693 }; |
679 SkMatrix matrix; | 694 SkMatrix viewMatrix; |
680 matrix.setTranslate(translate); | 695 viewMatrix.setTranslate(translate); |
681 | 696 |
682 // We set the current clip to the bounds so that our recursive draws are
scissored to them. | 697 // We set the current clip to the bounds so that our recursive draws are
scissored to them. |
683 SkIRect stencilSpaceIBounds(clipSpaceIBounds); | 698 SkIRect stencilSpaceIBounds(clipSpaceIBounds); |
684 stencilSpaceIBounds.offset(clipSpaceToStencilOffset); | 699 stencilSpaceIBounds.offset(clipSpaceToStencilOffset); |
685 GrDrawTarget::AutoClipRestore acr(fClipTarget, stencilSpaceIBounds); | 700 GrDrawTarget::AutoClipRestore acr(fClipTarget, stencilSpaceIBounds); |
686 | 701 |
687 int clipBit = stencilBuffer->bits(); | 702 int clipBit = stencilBuffer->bits(); |
688 SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil
buffers"); | 703 SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil
buffers"); |
689 clipBit = (1 << (clipBit-1)); | 704 clipBit = (1 << (clipBit-1)); |
690 | 705 |
691 fClipTarget->clearStencilClip(stencilSpaceIBounds, | 706 fClipTarget->clearStencilClip(stencilSpaceIBounds, |
692 GrReducedClip::kAllIn_InitialState == init
ialState, | 707 GrReducedClip::kAllIn_InitialState == init
ialState, |
693 rt); | 708 rt); |
694 | 709 |
695 // walk through each clip element and perform its set op | 710 // walk through each clip element and perform its set op |
696 // with the existing clip. | 711 // with the existing clip. |
697 for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.ge
t(); iter.next()) { | 712 for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.ge
t(); iter.next()) { |
698 const Element* element = iter.get(); | 713 const Element* element = iter.get(); |
699 | 714 |
700 GrDrawState drawState(matrix); | 715 GrDrawState drawState; |
701 drawState.setRenderTarget(rt); | 716 drawState.setRenderTarget(rt); |
702 drawState.enableState(GrDrawState::kClip_StateBit); | 717 drawState.enableState(GrDrawState::kClip_StateBit); |
703 | 718 |
704 drawState.setDisableColorXPFactory(); | 719 drawState.setDisableColorXPFactory(); |
705 | 720 |
706 // if the target is MSAA then we want MSAA enabled when the clip is
soft | 721 // if the target is MSAA then we want MSAA enabled when the clip is
soft |
707 if (rt->isMultisampled()) { | 722 if (rt->isMultisampled()) { |
708 drawState.setState(GrDrawState::kHWAntialias_StateBit, element->
isAA()); | 723 drawState.setState(GrDrawState::kHWAntialias_StateBit, element->
isAA()); |
709 } | 724 } |
710 | 725 |
(...skipping 14 matching lines...) Expand all Loading... |
725 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; | 740 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; |
726 fillInverted = false; | 741 fillInverted = false; |
727 } else { | 742 } else { |
728 element->asPath(&clipPath); | 743 element->asPath(&clipPath); |
729 fillInverted = clipPath.isInverseFillType(); | 744 fillInverted = clipPath.isInverseFillType(); |
730 if (fillInverted) { | 745 if (fillInverted) { |
731 clipPath.toggleInverseFillType(); | 746 clipPath.toggleInverseFillType(); |
732 } | 747 } |
733 pr = this->getContext()->getPathRenderer(fClipTarget, | 748 pr = this->getContext()->getPathRenderer(fClipTarget, |
734 &drawState, | 749 &drawState, |
| 750 viewMatrix, |
735 clipPath, | 751 clipPath, |
736 stroke, | 752 stroke, |
737 false, | 753 false, |
738 GrPathRendererChain::kS
tencilOnly_DrawType, | 754 GrPathRendererChain::kS
tencilOnly_DrawType, |
739 &stencilSupport); | 755 &stencilSupport); |
740 if (NULL == pr) { | 756 if (NULL == pr) { |
741 return false; | 757 return false; |
742 } | 758 } |
743 } | 759 } |
744 | 760 |
(...skipping 17 matching lines...) Expand all Loading... |
762 if (!canDrawDirectToClip) { | 778 if (!canDrawDirectToClip) { |
763 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, | 779 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, |
764 kIncClamp_StencilOp, | 780 kIncClamp_StencilOp, |
765 kIncClamp_StencilOp, | 781 kIncClamp_StencilOp, |
766 kAlways_StencilFunc, | 782 kAlways_StencilFunc, |
767 0xffff, | 783 0xffff, |
768 0x0000, | 784 0x0000, |
769 0xffff); | 785 0xffff); |
770 if (Element::kRect_Type == element->getType()) { | 786 if (Element::kRect_Type == element->getType()) { |
771 *drawState.stencil() = gDrawToStencil; | 787 *drawState.stencil() = gDrawToStencil; |
772 fClipTarget->drawSimpleRect(&drawState, GrColor_WHITE, eleme
nt->getRect()); | 788 fClipTarget->drawSimpleRect(&drawState, GrColor_WHITE, viewM
atrix, |
| 789 element->getRect()); |
773 } else { | 790 } else { |
774 if (!clipPath.isEmpty()) { | 791 if (!clipPath.isEmpty()) { |
775 GrDrawTarget::AutoGeometryPush agp(fClipTarget); | 792 GrDrawTarget::AutoGeometryPush agp(fClipTarget); |
776 if (canRenderDirectToStencil) { | 793 if (canRenderDirectToStencil) { |
777 *drawState.stencil() = gDrawToStencil; | 794 *drawState.stencil() = gDrawToStencil; |
778 pr->drawPath(fClipTarget, &drawState, GrColor_WHITE,
clipPath, stroke, | 795 pr->drawPath(fClipTarget, &drawState, GrColor_WHITE,
viewMatrix, |
779 false); | 796 clipPath, stroke, false); |
780 } else { | 797 } else { |
781 pr->stencilPath(fClipTarget, &drawState, clipPath, s
troke); | 798 pr->stencilPath(fClipTarget, &drawState, viewMatrix,
clipPath, stroke); |
782 } | 799 } |
783 } | 800 } |
784 } | 801 } |
785 } | 802 } |
786 | 803 |
787 // now we modify the clip bit by rendering either the clip | 804 // now we modify the clip bit by rendering either the clip |
788 // element directly or a bounding rect of the entire clip. | 805 // element directly or a bounding rect of the entire clip. |
789 fClipMode = kModifyClip_StencilClipMode; | 806 fClipMode = kModifyClip_StencilClipMode; |
790 for (int p = 0; p < passes; ++p) { | 807 for (int p = 0; p < passes; ++p) { |
791 GrDrawState drawStateCopy(drawState); | 808 GrDrawState drawStateCopy(drawState); |
792 *drawStateCopy.stencil() = stencilSettings[p]; | 809 *drawStateCopy.stencil() = stencilSettings[p]; |
793 | 810 |
794 if (canDrawDirectToClip) { | 811 if (canDrawDirectToClip) { |
795 if (Element::kRect_Type == element->getType()) { | 812 if (Element::kRect_Type == element->getType()) { |
796 fClipTarget->drawSimpleRect(&drawStateCopy, GrColor_WHIT
E, | 813 fClipTarget->drawSimpleRect(&drawStateCopy, GrColor_WHIT
E, viewMatrix, |
797 element->getRect()); | 814 element->getRect()); |
798 } else { | 815 } else { |
799 GrDrawTarget::AutoGeometryPush agp(fClipTarget); | 816 GrDrawTarget::AutoGeometryPush agp(fClipTarget); |
800 pr->drawPath(fClipTarget, &drawStateCopy, GrColor_WHITE,
clipPath, stroke, false); | 817 pr->drawPath(fClipTarget, &drawStateCopy, GrColor_WHITE,
viewMatrix, |
| 818 clipPath, stroke, false); |
801 } | 819 } |
802 } else { | 820 } else { |
803 // The view matrix is setup to do clip space -> stencil spac
e translation, so | 821 // The view matrix is setup to do clip space -> stencil spac
e translation, so |
804 // draw rect in clip space. | 822 // draw rect in clip space. |
805 fClipTarget->drawSimpleRect(&drawStateCopy, GrColor_WHITE, | 823 fClipTarget->drawSimpleRect(&drawStateCopy, GrColor_WHITE, v
iewMatrix, |
806 SkRect::Make(clipSpaceIBounds)); | 824 SkRect::Make(clipSpaceIBounds)); |
807 } | 825 } |
808 } | 826 } |
809 } | 827 } |
810 } | 828 } |
811 // set this last because recursive draws may overwrite it back to kNone. | 829 // set this last because recursive draws may overwrite it back to kNone. |
812 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); | 830 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); |
813 fCurrClipMaskType = kStencil_ClipMaskType; | 831 fCurrClipMaskType = kStencil_ClipMaskType; |
814 fClipMode = kRespectClip_StencilClipMode; | 832 fClipMode = kRespectClip_StencilClipMode; |
815 return true; | 833 return true; |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
982 } | 1000 } |
983 if (!twoSided) { | 1001 if (!twoSided) { |
984 settings->copyFrontSettingsToBack(); | 1002 settings->copyFrontSettingsToBack(); |
985 } | 1003 } |
986 } | 1004 } |
987 | 1005 |
988 //////////////////////////////////////////////////////////////////////////////// | 1006 //////////////////////////////////////////////////////////////////////////////// |
989 GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID, | 1007 GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID, |
990 GrReducedClip::InitialState
initialState, | 1008 GrReducedClip::InitialState
initialState, |
991 const GrReducedClip::Elemen
tList& elements, | 1009 const GrReducedClip::Elemen
tList& elements, |
| 1010 const SkVector& clipToMaskO
ffset, |
992 const SkIRect& clipSpaceIBo
unds) { | 1011 const SkIRect& clipSpaceIBo
unds) { |
993 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); | 1012 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); |
994 | 1013 |
995 GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBoun
ds); | 1014 GrTexture* result = this->getCachedMaskTexture(elementsGenID, clipSpaceIBoun
ds); |
996 if (result) { | 1015 if (result) { |
997 return result; | 1016 return result; |
998 } | 1017 } |
999 | 1018 |
1000 // The mask texture may be larger than necessary. We round out the clip spac
e bounds and pin | 1019 // The mask texture may be larger than necessary. We round out the clip spac
e bounds and pin |
1001 // the top left corner of the resulting rect to the top left of the texture. | 1020 // the top left corner of the resulting rect to the top left of the texture. |
1002 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpa
ceIBounds.height()); | 1021 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpa
ceIBounds.height()); |
1003 | 1022 |
1004 GrSWMaskHelper helper(this->getContext()); | 1023 GrSWMaskHelper helper(this->getContext()); |
1005 | 1024 |
1006 SkMatrix matrix; | 1025 // Set the matrix so that rendered clip elements are transformed to mask spa
ce from clip |
1007 matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft), | 1026 // space. |
1008 SkIntToScalar(-clipSpaceIBounds.fTop)); | 1027 SkMatrix translate; |
| 1028 translate.setTranslate(clipToMaskOffset); |
1009 | 1029 |
1010 helper.init(maskSpaceIBounds, &matrix, false); | 1030 helper.init(maskSpaceIBounds, &translate, false); |
1011 helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x0
0); | 1031 helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x0
0); |
1012 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); | 1032 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); |
1013 | 1033 |
1014 for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get()
; iter.next()) { | 1034 for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get()
; iter.next()) { |
1015 const Element* element = iter.get(); | 1035 const Element* element = iter.get(); |
1016 SkRegion::Op op = element->getOp(); | 1036 SkRegion::Op op = element->getOp(); |
1017 | 1037 |
1018 if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op ==
op) { | 1038 if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op ==
op) { |
1019 // Intersect and reverse difference require modifying pixels outside
of the geometry | 1039 // Intersect and reverse difference require modifying pixels outside
of the geometry |
1020 // that is being "drawn". In both cases we erase all the pixels outs
ide of the geometry | 1040 // that is being "drawn". In both cases we erase all the pixels outs
ide of the geometry |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1066 } | 1086 } |
1067 | 1087 |
1068 void GrClipMaskManager::adjustPathStencilParams(const GrStencilBuffer* stencilBu
ffer, | 1088 void GrClipMaskManager::adjustPathStencilParams(const GrStencilBuffer* stencilBu
ffer, |
1069 GrStencilSettings* settings) { | 1089 GrStencilSettings* settings) { |
1070 // TODO: dynamically attach a stencil buffer | 1090 // TODO: dynamically attach a stencil buffer |
1071 if (stencilBuffer) { | 1091 if (stencilBuffer) { |
1072 int stencilBits = stencilBuffer->bits(); | 1092 int stencilBits = stencilBuffer->bits(); |
1073 this->adjustStencilParams(settings, fClipMode, stencilBits); | 1093 this->adjustStencilParams(settings, fClipMode, stencilBits); |
1074 } | 1094 } |
1075 } | 1095 } |
OLD | NEW |