Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2011 The Chromium Authors. All rights reserved. | 1 // Copyright 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 | 6 |
| 7 #include "cc/layer_tree_host_common.h" | 7 #include "cc/layer_tree_host_common.h" |
| 8 | 8 |
| 9 #include "cc/layer.h" | 9 #include "cc/layer.h" |
| 10 #include "cc/layer_impl.h" | 10 #include "cc/layer_impl.h" |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 22 namespace cc { | 22 namespace cc { |
| 23 | 23 |
| 24 ScrollAndScaleSet::ScrollAndScaleSet() | 24 ScrollAndScaleSet::ScrollAndScaleSet() |
| 25 { | 25 { |
| 26 } | 26 } |
| 27 | 27 |
| 28 ScrollAndScaleSet::~ScrollAndScaleSet() | 28 ScrollAndScaleSet::~ScrollAndScaleSet() |
| 29 { | 29 { |
| 30 } | 30 } |
| 31 | 31 |
| 32 bool LayerTreeHostCommon::canUseLCDText() | |
| 33 { | |
| 34 #if OS(ANDROID) | |
| 35 return false; | |
| 36 #else | |
| 37 return true; | |
| 38 #endif | |
| 39 } | |
| 40 | |
| 32 gfx::Rect LayerTreeHostCommon::calculateVisibleRect(const gfx::Rect& targetSurfa ceRect, const gfx::Rect& layerBoundRect, const WebTransformationMatrix& transfor m) | 41 gfx::Rect LayerTreeHostCommon::calculateVisibleRect(const gfx::Rect& targetSurfa ceRect, const gfx::Rect& layerBoundRect, const WebTransformationMatrix& transfor m) |
| 33 { | 42 { |
| 34 // Is this layer fully contained within the target surface? | 43 // Is this layer fully contained within the target surface? |
| 35 gfx::Rect layerInSurfaceSpace = MathUtil::mapClippedRect(transform, layerBou ndRect); | 44 gfx::Rect layerInSurfaceSpace = MathUtil::mapClippedRect(transform, layerBou ndRect); |
| 36 if (targetSurfaceRect.Contains(layerInSurfaceSpace)) | 45 if (targetSurfaceRect.Contains(layerInSurfaceSpace)) |
| 37 return layerBoundRect; | 46 return layerBoundRect; |
| 38 | 47 |
| 39 // If the layer doesn't fill up the entire surface, then find the part of | 48 // If the layer doesn't fill up the entire surface, then find the part of |
| 40 // the surface rect where the layer could be visible. This avoids trying to | 49 // the surface rect where the layer could be visible. This avoids trying to |
| 41 // project surface rect points that are behind the projection point. | 50 // project surface rect points that are behind the projection point. |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 381 replicaMaskLayer->setContentsScale(contentsScale); | 390 replicaMaskLayer->setContentsScale(contentsScale); |
| 382 } | 391 } |
| 383 | 392 |
| 384 // Recursively walks the layer tree starting at the given node and computes all the | 393 // Recursively walks the layer tree starting at the given node and computes all the |
| 385 // necessary transformations, clipRects, render surfaces, etc. | 394 // necessary transformations, clipRects, render surfaces, etc. |
| 386 template<typename LayerType, typename LayerList, typename RenderSurfaceType, typ ename LayerSorter> | 395 template<typename LayerType, typename LayerList, typename RenderSurfaceType, typ ename LayerSorter> |
| 387 static void calculateDrawTransformsInternal(LayerType* layer, const WebTransform ationMatrix& parentMatrix, | 396 static void calculateDrawTransformsInternal(LayerType* layer, const WebTransform ationMatrix& parentMatrix, |
| 388 const WebTransformationMatrix& fullHierarchyMatrix, const WebTransformationM atrix& currentScrollCompensationMatrix, | 397 const WebTransformationMatrix& fullHierarchyMatrix, const WebTransformationM atrix& currentScrollCompensationMatrix, |
| 389 const gfx::Rect& clipRectFromAncestor, bool ancestorClipsSubtree, | 398 const gfx::Rect& clipRectFromAncestor, bool ancestorClipsSubtree, |
| 390 RenderSurfaceType* nearestAncestorThatMovesPixels, LayerList& renderSurfaceL ayerList, LayerList& layerList, | 399 RenderSurfaceType* nearestAncestorThatMovesPixels, LayerList& renderSurfaceL ayerList, LayerList& layerList, |
| 391 LayerSorter* layerSorter, int maxTextureSize, float deviceScaleFactor, float pageScaleFactor, gfx::Rect& drawableContentRectOfSubtree) | 400 LayerSorter* layerSorter, int maxTextureSize, float deviceScaleFactor, float pageScaleFactor, bool canUseLCDText, |
| 401 gfx::Rect& drawableContentRectOfSubtree) | |
| 392 { | 402 { |
| 393 // This function computes the new matrix transformations recursively for thi s | 403 // This function computes the new matrix transformations recursively for thi s |
| 394 // layer and all its descendants. It also computes the appropriate render su rfaces. | 404 // layer and all its descendants. It also computes the appropriate render su rfaces. |
| 395 // Some important points to remember: | 405 // Some important points to remember: |
| 396 // | 406 // |
| 397 // 0. Here, transforms are notated in Matrix x Vector order, and in words we describe what | 407 // 0. Here, transforms are notated in Matrix x Vector order, and in words we describe what |
| 398 // the transform does from left to right. | 408 // the transform does from left to right. |
| 399 // | 409 // |
| 400 // 1. In our terminology, the "layer origin" refers to the top-left corner o f a layer, and the | 410 // 1. In our terminology, the "layer origin" refers to the top-left corner o f a layer, and the |
| 401 // positive Y-axis points downwards. This interpretation is valid because the orthographic | 411 // positive Y-axis points downwards. This interpretation is valid because the orthographic |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 535 layerScreenSpaceTransform.multiply(drawTransform); | 545 layerScreenSpaceTransform.multiply(drawTransform); |
| 536 layer->setScreenSpaceTransform(layerScreenSpaceTransform); | 546 layer->setScreenSpaceTransform(layerScreenSpaceTransform); |
| 537 | 547 |
| 538 bool animatingTransformToTarget = layer->transformIsAnimating(); | 548 bool animatingTransformToTarget = layer->transformIsAnimating(); |
| 539 bool animatingTransformToScreen = animatingTransformToTarget; | 549 bool animatingTransformToScreen = animatingTransformToTarget; |
| 540 if (layer->parent()) { | 550 if (layer->parent()) { |
| 541 animatingTransformToTarget |= layer->parent()->drawTransformIsAnimating( ); | 551 animatingTransformToTarget |= layer->parent()->drawTransformIsAnimating( ); |
| 542 animatingTransformToScreen |= layer->parent()->screenSpaceTransformIsAni mating(); | 552 animatingTransformToScreen |= layer->parent()->screenSpaceTransformIsAni mating(); |
| 543 } | 553 } |
| 544 | 554 |
| 555 // Adjusting text AA method during animation may cause repaints, which in-tu rn jank. | |
| 556 bool canAdjustTextAA = !drawOpacityIsAnimating && !animatingTransformToScree n; | |
| 557 // To avoid color fringing, LCD text should only be used on opaque layers wi th just integral translation. | |
| 558 bool layerCanUseLCDText = canAdjustTextAA ? canUseLCDText && (drawOpacity == 1.0) && drawTransform.isIntegerTranslation() : false; | |
|
danakj
2012/11/07 18:15:00
This statement is weird to me. It is essentially:
alokp
2012/11/08 22:34:07
You are right. The logic to NOT mess with text AA
| |
| 559 | |
| 545 gfx::RectF contentRect(gfx::PointF(), layer->contentBounds()); | 560 gfx::RectF contentRect(gfx::PointF(), layer->contentBounds()); |
| 546 | 561 |
| 547 // fullHierarchyMatrix is the matrix that transforms objects between screen space (except projection matrix) and the most recent RenderSurfaceImpl's space. | 562 // fullHierarchyMatrix is the matrix that transforms objects between screen space (except projection matrix) and the most recent RenderSurfaceImpl's space. |
| 548 // nextHierarchyMatrix will only change if this layer uses a new RenderSurfa ceImpl, otherwise remains the same. | 563 // nextHierarchyMatrix will only change if this layer uses a new RenderSurfa ceImpl, otherwise remains the same. |
| 549 WebTransformationMatrix nextHierarchyMatrix = fullHierarchyMatrix; | 564 WebTransformationMatrix nextHierarchyMatrix = fullHierarchyMatrix; |
| 550 WebTransformationMatrix sublayerMatrix; | 565 WebTransformationMatrix sublayerMatrix; |
| 551 | 566 |
| 552 gfx::Vector2dF renderSurfaceSublayerScale = MathUtil::computeTransform2dScal eComponents(combinedTransform); | 567 gfx::Vector2dF renderSurfaceSublayerScale = MathUtil::computeTransform2dScal eComponents(combinedTransform); |
| 553 | 568 |
| 554 if (subtreeShouldRenderToSeparateSurface(layer, isScaleOrTranslation(combine dTransform))) { | 569 if (subtreeShouldRenderToSeparateSurface(layer, isScaleOrTranslation(combine dTransform))) { |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 620 nearestAncestorThatMovesPixels = renderSurface; | 635 nearestAncestorThatMovesPixels = renderSurface; |
| 621 | 636 |
| 622 // The render surface clipRect is expressed in the space where this surf ace draws, i.e. the same space as clipRectFromAncestor. | 637 // The render surface clipRect is expressed in the space where this surf ace draws, i.e. the same space as clipRectFromAncestor. |
| 623 if (ancestorClipsSubtree) | 638 if (ancestorClipsSubtree) |
| 624 renderSurface->setClipRect(clipRectFromAncestor); | 639 renderSurface->setClipRect(clipRectFromAncestor); |
| 625 else | 640 else |
| 626 renderSurface->setClipRect(gfx::Rect()); | 641 renderSurface->setClipRect(gfx::Rect()); |
| 627 | 642 |
| 628 renderSurface->setNearestAncestorThatMovesPixels(nearestAncestorThatMove sPixels); | 643 renderSurface->setNearestAncestorThatMovesPixels(nearestAncestorThatMove sPixels); |
| 629 | 644 |
| 645 // If the new render surface is drawn translucent or with a non-integral translation | |
| 646 // then the subtree that gets drawn on this render surface cannot use LC D text. | |
| 647 canUseLCDText = layerCanUseLCDText; | |
| 648 | |
| 630 renderSurfaceLayerList.push_back(layer); | 649 renderSurfaceLayerList.push_back(layer); |
| 631 } else { | 650 } else { |
| 632 DCHECK(layer->parent()); | 651 DCHECK(layer->parent()); |
| 633 | 652 |
| 634 layer->setDrawTransform(drawTransform); | 653 layer->setDrawTransform(drawTransform); |
| 635 layer->setDrawTransformIsAnimating(animatingTransformToTarget); | 654 layer->setDrawTransformIsAnimating(animatingTransformToTarget); |
| 636 layer->setScreenSpaceTransformIsAnimating(animatingTransformToScreen); | 655 layer->setScreenSpaceTransformIsAnimating(animatingTransformToScreen); |
| 637 sublayerMatrix = combinedTransform; | 656 sublayerMatrix = combinedTransform; |
| 638 | 657 |
| 639 layer->setDrawOpacity(drawOpacity); | 658 layer->setDrawOpacity(drawOpacity); |
| 640 layer->setDrawOpacityIsAnimating(drawOpacityIsAnimating); | 659 layer->setDrawOpacityIsAnimating(drawOpacityIsAnimating); |
| 641 | 660 |
| 642 layer->clearRenderSurface(); | 661 layer->clearRenderSurface(); |
| 643 | 662 |
| 644 // Layers without renderSurfaces directly inherit the ancestor's clip st atus. | 663 // Layers without renderSurfaces directly inherit the ancestor's clip st atus. |
| 645 subtreeShouldBeClipped = ancestorClipsSubtree; | 664 subtreeShouldBeClipped = ancestorClipsSubtree; |
| 646 if (ancestorClipsSubtree) | 665 if (ancestorClipsSubtree) |
| 647 clipRectForSubtree = clipRectFromAncestor; | 666 clipRectForSubtree = clipRectFromAncestor; |
| 648 | 667 |
| 649 // Layers that are not their own renderTarget will render into the targe t of their nearest ancestor. | 668 // Layers that are not their own renderTarget will render into the targe t of their nearest ancestor. |
| 650 layer->setRenderTarget(layer->parent()->renderTarget()); | 669 layer->setRenderTarget(layer->parent()->renderTarget()); |
| 651 } | 670 } |
| 652 | 671 |
| 672 if (canAdjustTextAA) | |
| 673 layer->setCanUseLCDText(layerCanUseLCDText); | |
| 674 | |
| 653 gfx::Rect rectInTargetSpace = ToEnclosingRect(MathUtil::mapClippedRect(layer ->drawTransform(), contentRect)); | 675 gfx::Rect rectInTargetSpace = ToEnclosingRect(MathUtil::mapClippedRect(layer ->drawTransform(), contentRect)); |
| 654 | 676 |
| 655 if (layerClipsSubtree(layer)) { | 677 if (layerClipsSubtree(layer)) { |
| 656 subtreeShouldBeClipped = true; | 678 subtreeShouldBeClipped = true; |
| 657 if (ancestorClipsSubtree && !layer->renderSurface()) { | 679 if (ancestorClipsSubtree && !layer->renderSurface()) { |
| 658 clipRectForSubtree = clipRectFromAncestor; | 680 clipRectForSubtree = clipRectFromAncestor; |
| 659 clipRectForSubtree.Intersect(rectInTargetSpace); | 681 clipRectForSubtree.Intersect(rectInTargetSpace); |
| 660 } else | 682 } else |
| 661 clipRectForSubtree = rectInTargetSpace; | 683 clipRectForSubtree = rectInTargetSpace; |
| 662 } | 684 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 679 descendants.push_back(layer); | 701 descendants.push_back(layer); |
| 680 | 702 |
| 681 WebTransformationMatrix nextScrollCompensationMatrix = computeScrollCompensa tionMatrixForChildren(layer, parentMatrix, currentScrollCompensationMatrix);; | 703 WebTransformationMatrix nextScrollCompensationMatrix = computeScrollCompensa tionMatrixForChildren(layer, parentMatrix, currentScrollCompensationMatrix);; |
| 682 | 704 |
| 683 gfx::Rect accumulatedDrawableContentRectOfChildren; | 705 gfx::Rect accumulatedDrawableContentRectOfChildren; |
| 684 for (size_t i = 0; i < layer->children().size(); ++i) { | 706 for (size_t i = 0; i < layer->children().size(); ++i) { |
| 685 LayerType* child = LayerTreeHostCommon::getChildAsRawPtr(layer->children (), i); | 707 LayerType* child = LayerTreeHostCommon::getChildAsRawPtr(layer->children (), i); |
| 686 gfx::Rect drawableContentRectOfChildSubtree; | 708 gfx::Rect drawableContentRectOfChildSubtree; |
| 687 calculateDrawTransformsInternal<LayerType, LayerList, RenderSurfaceType, LayerSorter>(child, sublayerMatrix, nextHierarchyMatrix, nextScrollCompensation Matrix, | 709 calculateDrawTransformsInternal<LayerType, LayerList, RenderSurfaceType, LayerSorter>(child, sublayerMatrix, nextHierarchyMatrix, nextScrollCompensation Matrix, |
| 688 clipRectForSubtree, subtreeShouldBeClipped, nearestAncestorThatMov esPixels, | 710 clipRectForSubtree, subtreeShouldBeClipped, nearestAncestorThatMov esPixels, |
| 689 renderSurfaceLayerList, descendants, layerSorter, maxTextureSize, deviceScaleFactor, pageScaleFactor, drawableContentRectOfChildSubtree); | 711 renderSurfaceLayerList, descendants, layerSorter, maxTextureSize, deviceScaleFactor, pageScaleFactor, canUseLCDText, |
| 712 drawableContentRectOfChildSubtree); | |
| 690 if (!drawableContentRectOfChildSubtree.IsEmpty()) { | 713 if (!drawableContentRectOfChildSubtree.IsEmpty()) { |
| 691 accumulatedDrawableContentRectOfChildren.Union(drawableContentRectOf ChildSubtree); | 714 accumulatedDrawableContentRectOfChildren.Union(drawableContentRectOf ChildSubtree); |
| 692 if (child->renderSurface()) | 715 if (child->renderSurface()) |
| 693 descendants.push_back(child); | 716 descendants.push_back(child); |
| 694 } | 717 } |
| 695 } | 718 } |
| 696 | 719 |
| 697 // Compute the total drawableContentRect for this subtree (the rect is in ta rgetSurface space) | 720 // Compute the total drawableContentRect for this subtree (the rect is in ta rgetSurface space) |
| 698 gfx::Rect localDrawableContentRectOfSubtree = accumulatedDrawableContentRect OfChildren; | 721 gfx::Rect localDrawableContentRectOfSubtree = accumulatedDrawableContentRect OfChildren; |
| 699 if (layer->drawsContent()) | 722 if (layer->drawsContent()) |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 817 bool subtreeShouldBeClipped = true; | 840 bool subtreeShouldBeClipped = true; |
| 818 gfx::Rect deviceViewportRect(gfx::Point(), deviceViewportSize); | 841 gfx::Rect deviceViewportRect(gfx::Point(), deviceViewportSize); |
| 819 | 842 |
| 820 // This function should have received a root layer. | 843 // This function should have received a root layer. |
| 821 DCHECK(isRootLayer(rootLayer)); | 844 DCHECK(isRootLayer(rootLayer)); |
| 822 | 845 |
| 823 cc::calculateDrawTransformsInternal<Layer, std::vector<scoped_refptr<Layer> >, RenderSurface, void>( | 846 cc::calculateDrawTransformsInternal<Layer, std::vector<scoped_refptr<Layer> >, RenderSurface, void>( |
| 824 rootLayer, deviceScaleTransform, identityMatrix, identityMatrix, | 847 rootLayer, deviceScaleTransform, identityMatrix, identityMatrix, |
| 825 deviceViewportRect, subtreeShouldBeClipped, 0, renderSurfaceLayerList, | 848 deviceViewportRect, subtreeShouldBeClipped, 0, renderSurfaceLayerList, |
| 826 dummyLayerList, 0, maxTextureSize, | 849 dummyLayerList, 0, maxTextureSize, |
| 827 deviceScaleFactor, pageScaleFactor, totalDrawableContentRect); | 850 deviceScaleFactor, pageScaleFactor, canUseLCDText(), totalDrawableConten tRect); |
| 828 | 851 |
| 829 // The dummy layer list should not have been used. | 852 // The dummy layer list should not have been used. |
| 830 DCHECK(dummyLayerList.size() == 0); | 853 DCHECK(dummyLayerList.size() == 0); |
| 831 // A root layer renderSurface should always exist after calculateDrawTransfo rms. | 854 // A root layer renderSurface should always exist after calculateDrawTransfo rms. |
| 832 DCHECK(rootLayer->renderSurface()); | 855 DCHECK(rootLayer->renderSurface()); |
| 833 } | 856 } |
| 834 | 857 |
| 835 void LayerTreeHostCommon::calculateDrawTransforms(LayerImpl* rootLayer, const gf x::Size& deviceViewportSize, float deviceScaleFactor, float pageScaleFactor, Lay erSorter* layerSorter, int maxTextureSize, std::vector<LayerImpl*>& renderSurfac eLayerList) | 858 void LayerTreeHostCommon::calculateDrawTransforms(LayerImpl* rootLayer, const gf x::Size& deviceViewportSize, float deviceScaleFactor, float pageScaleFactor, Lay erSorter* layerSorter, int maxTextureSize, std::vector<LayerImpl*>& renderSurfac eLayerList) |
| 836 { | 859 { |
| 837 gfx::Rect totalDrawableContentRect; | 860 gfx::Rect totalDrawableContentRect; |
| 838 WebTransformationMatrix identityMatrix; | 861 WebTransformationMatrix identityMatrix; |
| 839 WebTransformationMatrix deviceScaleTransform; | 862 WebTransformationMatrix deviceScaleTransform; |
| 840 deviceScaleTransform.scale(deviceScaleFactor); | 863 deviceScaleTransform.scale(deviceScaleFactor); |
| 841 std::vector<LayerImpl*> dummyLayerList; | 864 std::vector<LayerImpl*> dummyLayerList; |
| 842 | 865 |
| 843 // The root layer's renderSurface should receive the deviceViewport as the i nitial clipRect. | 866 // The root layer's renderSurface should receive the deviceViewport as the i nitial clipRect. |
| 844 bool subtreeShouldBeClipped = true; | 867 bool subtreeShouldBeClipped = true; |
| 845 gfx::Rect deviceViewportRect(gfx::Point(), deviceViewportSize); | 868 gfx::Rect deviceViewportRect(gfx::Point(), deviceViewportSize); |
| 846 | 869 |
| 847 // This function should have received a root layer. | 870 // This function should have received a root layer. |
| 848 DCHECK(isRootLayer(rootLayer)); | 871 DCHECK(isRootLayer(rootLayer)); |
| 849 | 872 |
| 850 cc::calculateDrawTransformsInternal<LayerImpl, std::vector<LayerImpl*>, Rend erSurfaceImpl, LayerSorter>( | 873 cc::calculateDrawTransformsInternal<LayerImpl, std::vector<LayerImpl*>, Rend erSurfaceImpl, LayerSorter>( |
| 851 rootLayer, deviceScaleTransform, identityMatrix, identityMatrix, | 874 rootLayer, deviceScaleTransform, identityMatrix, identityMatrix, |
| 852 deviceViewportRect, subtreeShouldBeClipped, 0, renderSurfaceLayerList, | 875 deviceViewportRect, subtreeShouldBeClipped, 0, renderSurfaceLayerList, |
| 853 dummyLayerList, layerSorter, maxTextureSize, | 876 dummyLayerList, layerSorter, maxTextureSize, |
| 854 deviceScaleFactor, pageScaleFactor, totalDrawableContentRect); | 877 deviceScaleFactor, pageScaleFactor, canUseLCDText(), totalDrawableConten tRect); |
| 855 | 878 |
| 856 // The dummy layer list should not have been used. | 879 // The dummy layer list should not have been used. |
| 857 DCHECK(dummyLayerList.size() == 0); | 880 DCHECK(dummyLayerList.size() == 0); |
| 858 // A root layer renderSurface should always exist after calculateDrawTransfo rms. | 881 // A root layer renderSurface should always exist after calculateDrawTransfo rms. |
| 859 DCHECK(rootLayer->renderSurface()); | 882 DCHECK(rootLayer->renderSurface()); |
| 860 } | 883 } |
| 861 | 884 |
| 862 static bool pointHitsRect(const gfx::PointF& screenSpacePoint, const WebTransfor mationMatrix& localSpaceToScreenSpaceTransform, gfx::RectF localSpaceRect) | 885 static bool pointHitsRect(const gfx::PointF& screenSpacePoint, const WebTransfor mationMatrix& localSpaceToScreenSpaceTransform, gfx::RectF localSpaceRect) |
| 863 { | 886 { |
| 864 // If the transform is not invertible, then assume that this point doesn't h it this rect. | 887 // If the transform is not invertible, then assume that this point doesn't h it this rect. |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 924 | 947 |
| 925 foundLayer = currentLayer; | 948 foundLayer = currentLayer; |
| 926 break; | 949 break; |
| 927 } | 950 } |
| 928 | 951 |
| 929 // This can potentially return 0, which means the screenSpacePoint did not s uccessfully hit test any layers, not even the root layer. | 952 // This can potentially return 0, which means the screenSpacePoint did not s uccessfully hit test any layers, not even the root layer. |
| 930 return foundLayer; | 953 return foundLayer; |
| 931 } | 954 } |
| 932 | 955 |
| 933 } // namespace cc | 956 } // namespace cc |
| OLD | NEW |