Loading libs/renderengine/skia/SkiaGLRenderEngine.cpp +72 −51 Original line number Diff line number Diff line Loading @@ -1127,83 +1127,104 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, const FloatRect& cropRect, const float cornerRadius) { const SkRect bounds = getSkRect(boundsRect); const SkRect crop = getSkRect(cropRect); SkRRect clip; if (cornerRadius > 0) { // it the crop and the bounds are equivalent or there is no crop then we don't need a clip if (bounds == crop || crop.isEmpty()) { return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; } // This makes an effort to speed up common, simple bounds + clip combinations by // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { bool intersectionIsRoundRect = true; // check each cropped corner to ensure that it exactly matches the crop or is full SkVector radii[4]; const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); /** * Verifies that common, simple bounds + clip combinations can be converted into * a single RRect draw call returning true if possible. If true the radii parameter * will be filled with the correct radii values that combined with bounds param will * produce the insected roundRect. If false, the returned state of the radii param is undefined. */ static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, const SkRect& insetCrop, float cornerRadius, SkVector radii[4]) { const bool leftEqual = bounds.fLeft == crop.fLeft; const bool topEqual = bounds.fTop == crop.fTop; const bool rightEqual = bounds.fRight == crop.fRight; const bool bottomEqual = bounds.fBottom == crop.fBottom; // In the event that the corners of the bounds only partially align with the crop we // need to ensure that the resulting shape can still be represented as a round rect. // In particular the round rect implementation will scale the value of all corner radii // if the sum of the radius along any edge is greater than the length of that edge. // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap const bool requiredWidth = bounds.width() > (cornerRadius * 2); const bool requiredHeight = bounds.height() > (cornerRadius * 2); if (!requiredWidth || !requiredHeight) { return false; } // Check each cropped corner to ensure that it exactly matches the crop or its corner is // contained within the cropped shape and does not need rounded. // compute the UpperLeft corner radius if (leftEqual && topEqual) { radii[0].set(cornerRadius, cornerRadius); } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || (topEqual && bounds.fLeft >= insetCrop.fLeft) || insetCrop.contains(bounds.fLeft, bounds.fTop)) { (topEqual && bounds.fLeft >= insetCrop.fLeft)) { radii[0].set(0, 0); } else { intersectionIsRoundRect = false; return false; } // compute the UpperRight corner radius if (rightEqual && topEqual) { radii[1].set(cornerRadius, cornerRadius); } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || (topEqual && bounds.fRight <= insetCrop.fRight) || insetCrop.contains(bounds.fRight, bounds.fTop)) { (topEqual && bounds.fRight <= insetCrop.fRight)) { radii[1].set(0, 0); } else { intersectionIsRoundRect = false; return false; } // compute the BottomRight corner radius if (rightEqual && bottomEqual) { radii[2].set(cornerRadius, cornerRadius); } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || (bottomEqual && bounds.fRight <= insetCrop.fRight) || insetCrop.contains(bounds.fRight, bounds.fBottom)) { (bottomEqual && bounds.fRight <= insetCrop.fRight)) { radii[2].set(0, 0); } else { intersectionIsRoundRect = false; return false; } // compute the BottomLeft corner radius if (leftEqual && bottomEqual) { radii[3].set(cornerRadius, cornerRadius); } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || (bottomEqual && bounds.fLeft >= insetCrop.fLeft) || insetCrop.contains(bounds.fLeft, bounds.fBottom)) { (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { radii[3].set(0, 0); } else { intersectionIsRoundRect = false; return false; } return true; } inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, const FloatRect& cropRect, const float cornerRadius) { const SkRect bounds = getSkRect(boundsRect); const SkRect crop = getSkRect(cropRect); SkRRect clip; if (cornerRadius > 0) { // it the crop and the bounds are equivalent or there is no crop then we don't need a clip if (bounds == crop || crop.isEmpty()) { return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; } if (intersectionIsRoundRect) { // This makes an effort to speed up common, simple bounds + clip combinations by // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); if (insetCrop.contains(bounds)) { return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required } SkVector radii[4]; if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) { SkRRect intersectionBounds; intersectionBounds.setRectRadii(bounds, radii); return {intersectionBounds, clip}; } } // we didn't it any of our fast paths so set the clip to the cropRect // we didn't hit any of our fast paths so set the clip to the cropRect clip.setRectXY(crop, cornerRadius, cornerRadius); } Loading libs/renderengine/tests/RenderEngineTest.cpp +34 −0 Original line number Diff line number Diff line Loading @@ -1869,6 +1869,40 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { initializeRenderEngine(); renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings redLayer; redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32); redLayer.geometry.roundedCornersRadius = 64; redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128); // Red background. redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); redLayer.alpha = 1.0f; layers.push_back(&redLayer); invokeDraw(settings, layers); // Due to roundedCornersRadius, the top corners are untouched. expectBufferColor(Point(0, 0), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); // ensure that the entire height of the red layer was clipped by the rounded corners crop. expectBufferColor(Point(0, 31), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0); // the bottom middle should be red expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255); } TEST_P(RenderEngineTest, testClear) { initializeRenderEngine(); Loading Loading
libs/renderengine/skia/SkiaGLRenderEngine.cpp +72 −51 Original line number Diff line number Diff line Loading @@ -1127,83 +1127,104 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, const FloatRect& cropRect, const float cornerRadius) { const SkRect bounds = getSkRect(boundsRect); const SkRect crop = getSkRect(cropRect); SkRRect clip; if (cornerRadius > 0) { // it the crop and the bounds are equivalent or there is no crop then we don't need a clip if (bounds == crop || crop.isEmpty()) { return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; } // This makes an effort to speed up common, simple bounds + clip combinations by // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { bool intersectionIsRoundRect = true; // check each cropped corner to ensure that it exactly matches the crop or is full SkVector radii[4]; const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); /** * Verifies that common, simple bounds + clip combinations can be converted into * a single RRect draw call returning true if possible. If true the radii parameter * will be filled with the correct radii values that combined with bounds param will * produce the insected roundRect. If false, the returned state of the radii param is undefined. */ static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, const SkRect& insetCrop, float cornerRadius, SkVector radii[4]) { const bool leftEqual = bounds.fLeft == crop.fLeft; const bool topEqual = bounds.fTop == crop.fTop; const bool rightEqual = bounds.fRight == crop.fRight; const bool bottomEqual = bounds.fBottom == crop.fBottom; // In the event that the corners of the bounds only partially align with the crop we // need to ensure that the resulting shape can still be represented as a round rect. // In particular the round rect implementation will scale the value of all corner radii // if the sum of the radius along any edge is greater than the length of that edge. // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap const bool requiredWidth = bounds.width() > (cornerRadius * 2); const bool requiredHeight = bounds.height() > (cornerRadius * 2); if (!requiredWidth || !requiredHeight) { return false; } // Check each cropped corner to ensure that it exactly matches the crop or its corner is // contained within the cropped shape and does not need rounded. // compute the UpperLeft corner radius if (leftEqual && topEqual) { radii[0].set(cornerRadius, cornerRadius); } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || (topEqual && bounds.fLeft >= insetCrop.fLeft) || insetCrop.contains(bounds.fLeft, bounds.fTop)) { (topEqual && bounds.fLeft >= insetCrop.fLeft)) { radii[0].set(0, 0); } else { intersectionIsRoundRect = false; return false; } // compute the UpperRight corner radius if (rightEqual && topEqual) { radii[1].set(cornerRadius, cornerRadius); } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || (topEqual && bounds.fRight <= insetCrop.fRight) || insetCrop.contains(bounds.fRight, bounds.fTop)) { (topEqual && bounds.fRight <= insetCrop.fRight)) { radii[1].set(0, 0); } else { intersectionIsRoundRect = false; return false; } // compute the BottomRight corner radius if (rightEqual && bottomEqual) { radii[2].set(cornerRadius, cornerRadius); } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || (bottomEqual && bounds.fRight <= insetCrop.fRight) || insetCrop.contains(bounds.fRight, bounds.fBottom)) { (bottomEqual && bounds.fRight <= insetCrop.fRight)) { radii[2].set(0, 0); } else { intersectionIsRoundRect = false; return false; } // compute the BottomLeft corner radius if (leftEqual && bottomEqual) { radii[3].set(cornerRadius, cornerRadius); } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || (bottomEqual && bounds.fLeft >= insetCrop.fLeft) || insetCrop.contains(bounds.fLeft, bounds.fBottom)) { (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { radii[3].set(0, 0); } else { intersectionIsRoundRect = false; return false; } return true; } inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, const FloatRect& cropRect, const float cornerRadius) { const SkRect bounds = getSkRect(boundsRect); const SkRect crop = getSkRect(cropRect); SkRRect clip; if (cornerRadius > 0) { // it the crop and the bounds are equivalent or there is no crop then we don't need a clip if (bounds == crop || crop.isEmpty()) { return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; } if (intersectionIsRoundRect) { // This makes an effort to speed up common, simple bounds + clip combinations by // converting them to a single RRect draw. It is possible there are other cases // that can be converted. if (crop.contains(bounds)) { const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); if (insetCrop.contains(bounds)) { return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required } SkVector radii[4]; if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) { SkRRect intersectionBounds; intersectionBounds.setRectRadii(bounds, radii); return {intersectionBounds, clip}; } } // we didn't it any of our fast paths so set the clip to the cropRect // we didn't hit any of our fast paths so set the clip to the cropRect clip.setRectXY(crop, cornerRadius, cornerRadius); } Loading
libs/renderengine/tests/RenderEngineTest.cpp +34 −0 Original line number Diff line number Diff line Loading @@ -1869,6 +1869,40 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { initializeRenderEngine(); renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; std::vector<const renderengine::LayerSettings*> layers; renderengine::LayerSettings redLayer; redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32); redLayer.geometry.roundedCornersRadius = 64; redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128); // Red background. redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); redLayer.alpha = 1.0f; layers.push_back(&redLayer); invokeDraw(settings, layers); // Due to roundedCornersRadius, the top corners are untouched. expectBufferColor(Point(0, 0), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); // ensure that the entire height of the red layer was clipped by the rounded corners crop. expectBufferColor(Point(0, 31), 0, 0, 0, 0); expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0); // the bottom middle should be red expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255); } TEST_P(RenderEngineTest, testClear) { initializeRenderEngine(); Loading