Changeset 113341 in webkit


Ignore:
Timestamp:
Apr 5, 2012 11:24:16 AM (12 years ago)
Author:
shawnsingh@chromium.org
Message:

[chromium] Need to clip to homogeneous w=0 plane when applying transforms.
https://bugs.webkit.org/show_bug.cgi?id=80806

Reviewed by Adrienne Walker.

Source/WebCore:

Unit tests added to CCLayerTreeHostCommon. This change is also
covered by other existing unit tests and layout tests.

WebCore TransformationMatrix mapRect / mapQuad / projectQuad do
not properly handle the case where a surface is oriented partially
behind the camera, with a perspective projection. In this case,
projected points may appear to be valid in cartesian coordinates,
but they are indeed not valid, and this problem can only be
detected in homogeneous coordinates after applying the transform,
before the divide-by-w step.

The correct solution is to clip geometry where w < 0. This patch
makes this change local to chromium only, to fix rendering bugs
that arise from this problem. The primary fix is to correct
calculateVisibleLayerRect(), but other ancillary locations are
also fixed, in particular, the antialiasing code path is simply
skipped when this case arises.

Eventually this math needs to be merged into TransformationMatrix,
to fix hit-testing bugs that occur in both Chromium and Safari.

  • WebCore.gypi:
  • platform/graphics/chromium/LayerRendererChromium.cpp:

(WebCore::findTileProgramUniforms):
(WebCore::LayerRendererChromium::drawTileQuad):

  • platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp:

(WebCore::CCLayerTreeHostCommon::calculateVisibleRect):
(WebCore::isScaleOrTranslation):
(WebCore::calculateDrawTransformsAndVisibilityInternal):

  • platform/graphics/chromium/cc/CCMathUtil.cpp: Added.

(WebCore):
(WebCore::HomogeneousCoordinate::HomogeneousCoordinate):
(HomogeneousCoordinate):
(WebCore::HomogeneousCoordinate::shouldBeClipped):
(WebCore::HomogeneousCoordinate::cartesianPoint2d):
(WebCore::projectPoint):
(WebCore::mapPoint):
(WebCore::computeClippedPointForEdge):
(WebCore::expandBoundsToIncludePoint):
(WebCore::computeEnclosingRectOfClippedQuad):
(WebCore::computeEnclosingRect):
(WebCore::CCMathUtil::mapClippedRect):
(WebCore::CCMathUtil::projectClippedRect):
(WebCore::CCMathUtil::mapQuad):
(WebCore::CCMathUtil::projectQuad):

  • platform/graphics/chromium/cc/CCMathUtil.h: Added.

(WebCore):
(CCMathUtil):

  • platform/graphics/chromium/cc/CCOcclusionTracker.cpp:

(WebCore::computeUnoccludedContentRect):

Source/WebKit/chromium:

  • tests/CCLayerTreeHostCommonTest.cpp:

(WebKitTests::TEST):
(WebKitTests):

Location:
trunk/Source
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r113340 r113341  
     12012-04-05  Shawn Singh  <shawnsingh@chromium.org>
     2
     3        [chromium] Need to clip to homogeneous w=0 plane when applying transforms.
     4        https://bugs.webkit.org/show_bug.cgi?id=80806
     5
     6        Reviewed by Adrienne Walker.
     7
     8        Unit tests added to CCLayerTreeHostCommon. This change is also
     9        covered by other existing unit tests and layout tests.
     10
     11        WebCore TransformationMatrix mapRect / mapQuad / projectQuad do
     12        not properly handle the case where a surface is oriented partially
     13        behind the camera, with a perspective projection. In this case,
     14        projected points may appear to be valid in cartesian coordinates,
     15        but they are indeed not valid, and this problem can only be
     16        detected in homogeneous coordinates after applying the transform,
     17        before the divide-by-w step.
     18
     19        The correct solution is to clip geometry where w < 0. This patch
     20        makes this change local to chromium only, to fix rendering bugs
     21        that arise from this problem. The primary fix is to correct
     22        calculateVisibleLayerRect(), but other ancillary locations are
     23        also fixed, in particular, the antialiasing code path is simply
     24        skipped when this case arises.
     25
     26        Eventually this math needs to be merged into TransformationMatrix,
     27        to fix hit-testing bugs that occur in both Chromium and Safari.
     28
     29        * WebCore.gypi:
     30        * platform/graphics/chromium/LayerRendererChromium.cpp:
     31        (WebCore::findTileProgramUniforms):
     32        (WebCore::LayerRendererChromium::drawTileQuad):
     33        * platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp:
     34        (WebCore::CCLayerTreeHostCommon::calculateVisibleRect):
     35        (WebCore::isScaleOrTranslation):
     36        (WebCore::calculateDrawTransformsAndVisibilityInternal):
     37        * platform/graphics/chromium/cc/CCMathUtil.cpp: Added.
     38        (WebCore):
     39        (WebCore::HomogeneousCoordinate::HomogeneousCoordinate):
     40        (HomogeneousCoordinate):
     41        (WebCore::HomogeneousCoordinate::shouldBeClipped):
     42        (WebCore::HomogeneousCoordinate::cartesianPoint2d):
     43        (WebCore::projectPoint):
     44        (WebCore::mapPoint):
     45        (WebCore::computeClippedPointForEdge):
     46        (WebCore::expandBoundsToIncludePoint):
     47        (WebCore::computeEnclosingRectOfClippedQuad):
     48        (WebCore::computeEnclosingRect):
     49        (WebCore::CCMathUtil::mapClippedRect):
     50        (WebCore::CCMathUtil::projectClippedRect):
     51        (WebCore::CCMathUtil::mapQuad):
     52        (WebCore::CCMathUtil::projectQuad):
     53        * platform/graphics/chromium/cc/CCMathUtil.h: Added.
     54        (WebCore):
     55        (CCMathUtil):
     56        * platform/graphics/chromium/cc/CCOcclusionTracker.cpp:
     57        (WebCore::computeUnoccludedContentRect):
     58
    1592012-04-05  Jia Pu  <jpu@apple.com>
    260
  • trunk/Source/WebCore/WebCore.gypi

    r113297 r113341  
    36083608            'platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp',
    36093609            'platform/graphics/chromium/cc/CCLayerTreeHostImpl.h',
     3610            'platform/graphics/chromium/cc/CCMathUtil.cpp',
     3611            'platform/graphics/chromium/cc/CCMathUtil.h',
    36103612            'platform/graphics/chromium/cc/CCOcclusionTracker.cpp',
    36113613            'platform/graphics/chromium/cc/CCOcclusionTracker.h',
  • trunk/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp

    r113248 r113341  
    5656#include "cc/CCLayerImpl.h"
    5757#include "cc/CCLayerTreeHostCommon.h"
     58#include "cc/CCMathUtil.h"
    5859#include "cc/CCProxy.h"
    5960#include "cc/CCRenderPass.h"
     
    565566}
    566567
    567 static void findTileProgramUniforms(LayerRendererChromium* layerRenderer, const CCTileDrawQuad* quad, TileProgramUniforms& uniforms)
    568 {
    569     if (quad->isAntialiased()) {
     568static void findTileProgramUniforms(LayerRendererChromium* layerRenderer, const CCTileDrawQuad* quad, TileProgramUniforms& uniforms, bool quadIsClipped)
     569{
     570    // For now, we simply skip anti-aliasing with the quad is clipped. This only happens
     571    // on perspective transformed layers that go partially behind the camera.
     572    if (quad->isAntialiased() && !quadIsClipped) {
    570573        if (quad->swizzleContents()) {
    571574            const CCTiledLayerImpl::ProgramSwizzleAA* program = layerRenderer->tilerProgramSwizzleAA();
     
    630633    float fragmentTexScaleY = clampRect.height() / textureSize.height();
    631634
     635
     636    FloatQuad localQuad;
     637    TransformationMatrix deviceTransform = TransformationMatrix(windowMatrix() * projectionMatrix() * quad->quadTransform()).to2dTransform();
     638    if (!deviceTransform.isInvertible())
     639        return;
     640
     641    bool clipped = false;
     642    FloatQuad deviceLayerQuad = CCMathUtil::mapQuad(deviceTransform, FloatQuad(quad->layerRect()), clipped);
     643
    632644    TileProgramUniforms uniforms;
    633     findTileProgramUniforms(this, quad, uniforms);
     645    findTileProgramUniforms(this, quad, uniforms, clipped);
    634646
    635647    GLC(context(), context()->useProgram(uniforms.program));
     
    640652    GLC(context(), context()->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, quad->textureFilter()));
    641653
    642     FloatQuad localQuad;
    643     if (quad->isAntialiased()) {
    644         TransformationMatrix deviceTransform = TransformationMatrix(windowMatrix() * projectionMatrix() * quad->quadTransform()).to2dTransform();
    645         if (!deviceTransform.isInvertible())
    646             return;
    647 
    648         FloatQuad deviceLayerQuad = deviceTransform.mapQuad(FloatQuad(quad->layerRect()));
     654
     655    if (!clipped && quad->isAntialiased()) {
    649656
    650657        CCLayerQuad deviceLayerBounds = CCLayerQuad(FloatQuad(deviceLayerQuad.boundingBox()));
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp

    r112436 r113341  
    3838#include "cc/CCLayerIterator.h"
    3939#include "cc/CCLayerSorter.h"
     40#include "cc/CCMathUtil.h"
    4041#include "cc/CCRenderSurface.h"
    4142
     
    4546{
    4647    // Is this layer fully contained within the target surface?
    47     IntRect layerInSurfaceSpace = transform.mapRect(layerBoundRect);
     48    IntRect layerInSurfaceSpace = CCMathUtil::mapClippedRect(transform, layerBoundRect);
    4849    if (targetSurfaceRect.contains(layerInSurfaceSpace))
    4950        return layerBoundRect;
     
    6061    // Non-invertible transforms will create an empty rect here.
    6162    const TransformationMatrix surfaceToLayer = transform.inverse();
    62     IntRect layerRect = surfaceToLayer.projectQuad(FloatQuad(FloatRect(minimalSurfaceRect))).enclosingBoundingBox();
     63    IntRect layerRect = enclosingIntRect(CCMathUtil::projectClippedRect(surfaceToLayer, FloatRect(minimalSurfaceRect)));
    6364    layerRect.intersect(layerBoundRect);
    6465    return layerRect;
     
    7172           && !m.m31() && !m.m32() && !m.m43()
    7273           && m.m44();
    73 
    7474}
    7575
     
    410410        layer->setDrawTransformIsAnimating(animatingTransformToTarget);
    411411        layer->setScreenSpaceTransformIsAnimating(animatingTransformToScreen);
    412         transformedLayerRect = enclosingIntRect(layer->drawTransform().mapRect(layerRect));
     412        transformedLayerRect = enclosingIntRect(CCMathUtil::mapClippedRect(layer->drawTransform(), layerRect));
    413413
    414414        layer->setDrawOpacity(drawOpacity);
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.cpp

    r112548 r113341  
    3232#include "LayerChromium.h"
    3333#include "cc/CCLayerImpl.h"
     34#include "cc/CCMathUtil.h"
    3435
    3536#include <algorithm>
     
    276277}
    277278
    278 static FloatQuad projectQuad(const TransformationMatrix& transform, const FloatQuad& q, bool& clamped)
    279 {
    280     FloatQuad projectedQuad;
    281     bool clampedPoint;
    282     projectedQuad.setP1(transform.projectPoint(q.p1(), &clampedPoint));
    283     clamped = clampedPoint;
    284     projectedQuad.setP2(transform.projectPoint(q.p2(), &clampedPoint));
    285     clamped |= clampedPoint;
    286     projectedQuad.setP3(transform.projectPoint(q.p3(), &clampedPoint));
    287     clamped |= clampedPoint;
    288     projectedQuad.setP4(transform.projectPoint(q.p4(), &clampedPoint));
    289     clamped |= clampedPoint;
    290 
    291     return projectedQuad;
    292 }
    293 
    294279static inline IntRect computeUnoccludedContentRect(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
    295280{
     
    300285    // Take the enclosingIntRect at each step, as we want to contain any unoccluded partial pixels in the resulting IntRect.
    301286    IntRect shrunkRect = rectSubtractRegion(intersection(enclosingIntRect(transformedRect), scissorRect), occlusion);
    302     bool clamped; // FIXME: projectQuad returns invalid results when a point gets clamped. To be fixed in bug https://bugs.webkit.org/show_bug.cgi?id=80806.
    303     IntRect unoccludedRect = enclosingIntRect(projectQuad(contentSpaceTransform.inverse(), FloatQuad(FloatRect(shrunkRect)), clamped).boundingBox());
    304     if (clamped)
     287    bool clipped; // FIXME: We should be able to use projectClippedQuad instead of forcing everything to be unoccluded. https://bugs.webkit.org/show_bug.cgi?id=83217.
     288    IntRect unoccludedRect = enclosingIntRect(CCMathUtil::projectQuad(contentSpaceTransform.inverse(), FloatQuad(FloatRect(shrunkRect)), clipped).boundingBox());
     289    if (clipped)
    305290        return contentRect;
    306291    // The rect back in content space is a bounding box and may extend outside of the original contentRect, so clamp it to the contentRectBounds.
  • trunk/Source/WebKit/chromium/ChangeLog

    r113332 r113341  
     12012-04-05  Shawn Singh  <shawnsingh@chromium.org>
     2
     3        [chromium] Need to clip to homogeneous w=0 plane when applying transforms.
     4        https://bugs.webkit.org/show_bug.cgi?id=80806
     5
     6        Reviewed by Adrienne Walker.
     7
     8        * tests/CCLayerTreeHostCommonTest.cpp:
     9        (WebKitTests::TEST):
     10        (WebKitTests):
     11
    1122012-04-05  Sheriff Bot  <webkit.review.bot@gmail.com>
    213
  • trunk/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp

    r112436 r113341  
    3333#include "TranslateTransformOperation.h"
    3434#include "cc/CCLayerAnimationController.h"
     35#include "cc/CCMathUtil.h"
    3536
    3637#include <gmock/gmock.h>
     
    980981}
    981982
     983TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForIdentityTransform)
     984{
     985    // Test the calculateVisibleRect() function works correctly for identity transforms.
     986
     987    IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     988    TransformationMatrix layerToSurfaceTransform;
     989
     990    // Case 1: Layer is contained within the surface.
     991    IntRect layerContentRect = IntRect(IntPoint(10, 10), IntSize(30, 30));
     992    IntRect expected = IntRect(IntPoint(10, 10), IntSize(30, 30));
     993    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     994    EXPECT_INT_RECT_EQ(expected, actual);
     995
     996    // Case 2: Layer is outside the surface rect.
     997    layerContentRect = IntRect(IntPoint(120, 120), IntSize(30, 30));
     998    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     999    EXPECT_TRUE(actual.isEmpty());
     1000
     1001    // Case 3: Layer is partially overlapping the surface rect.
     1002    layerContentRect = IntRect(IntPoint(80, 80), IntSize(30, 30));
     1003    expected = IntRect(IntPoint(80, 80), IntSize(20, 20));
     1004    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1005    EXPECT_INT_RECT_EQ(expected, actual);
     1006}
     1007
     1008TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForTranslations)
     1009{
     1010    // Test the calculateVisibleRect() function works correctly for scaling transforms.
     1011
     1012    IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1013    IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30));
     1014    TransformationMatrix layerToSurfaceTransform;
     1015
     1016    // Case 1: Layer is contained within the surface.
     1017    layerToSurfaceTransform.makeIdentity();
     1018    layerToSurfaceTransform.translate(10, 10);
     1019    IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
     1020    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1021    EXPECT_INT_RECT_EQ(expected, actual);
     1022
     1023    // Case 2: Layer is outside the surface rect.
     1024    layerToSurfaceTransform.makeIdentity();
     1025    layerToSurfaceTransform.translate(120, 120);
     1026    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1027    EXPECT_TRUE(actual.isEmpty());
     1028
     1029    // Case 3: Layer is partially overlapping the surface rect.
     1030    layerToSurfaceTransform.makeIdentity();
     1031    layerToSurfaceTransform.translate(80, 80);
     1032    expected = IntRect(IntPoint(0, 0), IntSize(20, 20));
     1033    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1034    EXPECT_INT_RECT_EQ(expected, actual);
     1035}
     1036
     1037TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor2DRotations)
     1038{
     1039    // Test the calculateVisibleRect() function works correctly for rotations about z-axis (i.e. 2D rotations).
     1040    // Remember that calculateVisibleRect() should return the visible rect in the layer's space.
     1041
     1042    IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1043    IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30));
     1044    TransformationMatrix layerToSurfaceTransform;
     1045
     1046    // Case 1: Layer is contained within the surface.
     1047    layerToSurfaceTransform.makeIdentity();
     1048    layerToSurfaceTransform.translate(50, 50);
     1049    layerToSurfaceTransform.rotate(45);
     1050    IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
     1051    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1052    EXPECT_INT_RECT_EQ(expected, actual);
     1053
     1054    // Case 2: Layer is outside the surface rect.
     1055    layerToSurfaceTransform.makeIdentity();
     1056    layerToSurfaceTransform.translate(-50, 0);
     1057    layerToSurfaceTransform.rotate(45);
     1058    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1059    EXPECT_TRUE(actual.isEmpty());
     1060
     1061    // Case 3: The layer is rotated about its top-left corner. In surface space, the layer
     1062    //         is oriented diagonally, with the left half outside of the renderSurface. In
     1063    //         this case, the visible rect should still be the entire layer (remember the
     1064    //         visible rect is computed in layer space); both the top-left and
     1065    //         bottom-right corners of the layer are still visible.
     1066    layerToSurfaceTransform.makeIdentity();
     1067    layerToSurfaceTransform.rotate(45);
     1068    expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
     1069    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1070    EXPECT_INT_RECT_EQ(expected, actual);
     1071
     1072    // Case 4: The layer is rotated about its top-left corner, and translated upwards. In
     1073    //         surface space, the layer is oriented diagonally, with only the top corner
     1074    //         of the surface overlapping the layer. In layer space, the render surface
     1075    //         overlaps the right side of the layer. The visible rect should be the
     1076    //         layer's right half.
     1077    layerToSurfaceTransform.makeIdentity();
     1078    layerToSurfaceTransform.translate(0, -sqrt(2) * 15);
     1079    layerToSurfaceTransform.rotate(45);
     1080    expected = IntRect(IntPoint(15, 0), IntSize(15, 30)); // right half of layer bounds.
     1081    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1082    EXPECT_INT_RECT_EQ(expected, actual);
     1083}
     1084
     1085TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicTransform)
     1086{
     1087    // Test that the calculateVisibleRect() function works correctly for 3d transforms.
     1088
     1089    IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1090    IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1091    TransformationMatrix layerToSurfaceTransform;
     1092
     1093    // Case 1: Orthographic projection of a layer rotated about y-axis by 45 degrees, should be fully contained in the renderSurface.
     1094    layerToSurfaceTransform.makeIdentity();
     1095    layerToSurfaceTransform.rotate3d(0, 45, 0);
     1096    IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1097    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1098    EXPECT_INT_RECT_EQ(expected, actual);
     1099
     1100    // Case 2: Orthographic projection of a layer rotated about y-axis by 45 degrees, but
     1101    //         shifted to the side so only the right-half the layer would be visible on
     1102    //         the surface.
     1103    double halfWidthOfRotatedLayer = (100.0 / sqrt(2)) * 0.5; // 100.0 is the un-rotated layer width; divided by sqrt(2) is the rotated width.
     1104    layerToSurfaceTransform.makeIdentity();
     1105    layerToSurfaceTransform.translate(-halfWidthOfRotatedLayer, 0);
     1106    layerToSurfaceTransform.rotate3d(0, 45, 0); // rotates about the left edge of the layer
     1107    expected = IntRect(IntPoint(50, 0), IntSize(50, 100)); // right half of the layer.
     1108    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1109    EXPECT_INT_RECT_EQ(expected, actual);
     1110}
     1111
     1112TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveTransform)
     1113{
     1114    // Test the calculateVisibleRect() function works correctly when the layer has a
     1115    // perspective projection onto the target surface.
     1116
     1117    IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1118    IntRect layerContentRect = IntRect(IntPoint(-50, -50), IntSize(200, 200));
     1119    TransformationMatrix layerToSurfaceTransform;
     1120
     1121    // Case 1: Even though the layer is twice as large as the surface, due to perspective
     1122    //         foreshortening, the layer will fit fully in the surface when its translated
     1123    //         more than the perspective amount.
     1124    layerToSurfaceTransform.makeIdentity();
     1125
     1126    // The following sequence of transforms applies the perspective about the center of the surface.
     1127    layerToSurfaceTransform.translate(50, 50);
     1128    layerToSurfaceTransform.applyPerspective(9);
     1129    layerToSurfaceTransform.translate(-50, -50);
     1130
     1131    // This translate places the layer in front of the surface's projection plane.
     1132    layerToSurfaceTransform.translate3d(0, 0, -27);
     1133
     1134    IntRect expected = IntRect(IntPoint(-50, -50), IntSize(200, 200));
     1135    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1136    EXPECT_INT_RECT_EQ(expected, actual);
     1137
     1138    // Case 2: same projection as before, except that the layer is also translated to the
     1139    //         side, so that only the right half of the layer should be visible.
     1140    //
     1141    // Explanation of expected result:
     1142    // The perspective ratio is (z distance between layer and camera origin) / (z distance between projection plane and camera origin) == ((-27 - 9) / 9)
     1143    // Then, by similar triangles, if we want to move a layer by translating -50 units in projected surface units (so that only half of it is
     1144    // visible), then we would need to translate by (-36 / 9) * -50 == -200 in the layer's units.
     1145    //
     1146    layerToSurfaceTransform.translate3d(-200, 0, 0);
     1147    expected = IntRect(IntPoint(50, -50), IntSize(100, 200)); // The right half of the layer's bounding rect.
     1148    actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1149    EXPECT_INT_RECT_EQ(expected, actual);
     1150}
     1151
     1152TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicIsNotClippedBehindSurface)
     1153{
     1154    // There is currently no explicit concept of an orthographic projection plane in our
     1155    // code (nor in the CSS spec to my knowledge). Therefore, layers that are technically
     1156    // behind the surface in an orthographic world should not be clipped when they are
     1157    // flattened to the surface.
     1158
     1159    IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1160    IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1161    TransformationMatrix layerToSurfaceTransform;
     1162
     1163    // This sequence of transforms effectively rotates the layer about the y-axis at the
     1164    // center of the layer.
     1165    layerToSurfaceTransform.makeIdentity();
     1166    layerToSurfaceTransform.translate(50, 0);
     1167    layerToSurfaceTransform.rotate3d(0, 45, 0);
     1168    layerToSurfaceTransform.translate(-50, 0);
     1169
     1170    IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100));
     1171    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1172    EXPECT_INT_RECT_EQ(expected, actual);
     1173}
     1174
     1175TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveIsClipped)
     1176{
     1177    // Test the calculateVisibleRect() function works correctly when projecting a surface
     1178    // onto a layer, but the layer is partially behind the camera (not just behind the
     1179    // projection plane). In this case, the cartesian coordinates may seem to be valid,
     1180    // but actually they are not. The visibleRect needs to be properly clipped by the
     1181    // w = 0 plane in homogeneous coordinates before converting to cartesian coordinates.
     1182
     1183    IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100));
     1184    IntRect layerContentRect = IntRect(IntPoint(-10, -1), IntSize(20, 2));
     1185    TransformationMatrix layerToSurfaceTransform;
     1186
     1187    // The layer is positioned so that the right half of the layer should be in front of
     1188    // the camera, while the other half is behind the surface's projection plane. The
     1189    // following sequence of transforms applies the perspective and rotation about the
     1190    // center of the layer.
     1191    layerToSurfaceTransform.makeIdentity();
     1192    layerToSurfaceTransform.applyPerspective(1);
     1193    layerToSurfaceTransform.translate3d(0, 0, 1);
     1194    layerToSurfaceTransform.rotate3d(0, 45, 0);
     1195
     1196    // Sanity check that this transform does indeed cause w < 0 when applying the
     1197    // transform, otherwise this code is not testing the intended scenario.
     1198    bool clipped = false;
     1199    CCMathUtil::mapQuad(layerToSurfaceTransform, FloatQuad(FloatRect(layerContentRect)), clipped);
     1200    ASSERT_TRUE(clipped);
     1201
     1202    int expectedXPosition = -10;
     1203    int expectedWidth = 10;
     1204    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1205    EXPECT_EQ(expectedXPosition, actual.x());
     1206    EXPECT_EQ(expectedWidth, actual.width());
     1207}
     1208
     1209TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForPerspectiveUnprojection)
     1210{
     1211    // To determine visibleRect in layer space, there needs to be an un-projection from
     1212    // surface space to layer space. When the original transform was a perspective
     1213    // projection that was clipped, it returns a rect that encloses the clipped bounds.
     1214    // Un-projecting this new rect may require clipping again.
     1215
     1216    // This sequence of transforms causes one corner of the layer to protrude across the w = 0 plane, and should be clipped.
     1217    IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100));
     1218    IntRect layerContentRect = IntRect(IntPoint(-10, -10), IntSize(20, 20));
     1219    TransformationMatrix layerToSurfaceTransform;
     1220    layerToSurfaceTransform.makeIdentity();
     1221    layerToSurfaceTransform.applyPerspective(1);
     1222    layerToSurfaceTransform.translate3d(0, 0, -5);
     1223    layerToSurfaceTransform.rotate3d(0, 45, 0);
     1224    layerToSurfaceTransform.rotate3d(80, 0, 0);
     1225
     1226    // Sanity check that un-projection does indeed cause w < 0, otherwise this code is not
     1227    // testing the intended scenario.
     1228    bool clipped = false;
     1229    FloatRect clippedRect = CCMathUtil::mapClippedRect(layerToSurfaceTransform, layerContentRect);
     1230    CCMathUtil::projectQuad(layerToSurfaceTransform.inverse(), FloatQuad(clippedRect), clipped);
     1231    ASSERT_TRUE(clipped);
     1232
     1233    // Only the corner of the layer is not visible on the surface because of being
     1234    // clipped. But, the net result of rounding visible region to an axis-aligned rect is
     1235    // that the entire layer should still be considered visible.
     1236    IntRect expected = IntRect(IntPoint(-10, -10), IntSize(20, 20));
     1237    IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
     1238    EXPECT_INT_RECT_EQ(expected, actual);
     1239}
     1240
    9821241// FIXME:
    9831242// continue working on https://bugs.webkit.org/show_bug.cgi?id=68942
Note: See TracChangeset for help on using the changeset viewer.