blob: 17438038ce9e232fe92b2eb84e05def94defdb5d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "core/paint/LayoutObjectDrawingRecorder.h"
#include "core/layout/LayoutTestHelper.h"
#include "core/layout/LayoutView.h"
#include "core/paint/PaintControllerPaintTest.h"
#include "core/paint/PaintLayer.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/paint/DrawingDisplayItem.h"
#include "platform/graphics/paint/PaintController.h"
#include <gtest/gtest.h>
namespace blink {
using LayoutObjectDrawingRecorderTest = PaintControllerPaintTest;
namespace {
void drawNothing(GraphicsContext& context, const LayoutView& layoutView, PaintPhase phase, const LayoutRect& bound)
{
if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutView, phase, LayoutPoint()))
return;
LayoutObjectDrawingRecorder drawingRecorder(context, layoutView, phase, bound, LayoutPoint());
}
void drawRect(GraphicsContext& context, LayoutView& layoutView, PaintPhase phase, const LayoutRect& bound)
{
if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutView, phase, LayoutPoint()))
return;
LayoutObjectDrawingRecorder drawingRecorder(context, layoutView, phase, bound, LayoutPoint());
IntRect rect(0, 0, 10, 10);
context.drawRect(rect);
}
TEST_F(LayoutObjectDrawingRecorderTest, Nothing)
{
rootPaintController().invalidateAll();
GraphicsContext context(rootPaintController());
LayoutRect bound = layoutView().viewRect();
drawNothing(context, layoutView(), PaintPhaseForeground, bound);
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 1,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
EXPECT_FALSE(static_cast<const DrawingDisplayItem&>(rootPaintController().displayItemList()[0]).picture());
}
TEST_F(LayoutObjectDrawingRecorderTest, Rect)
{
rootPaintController().invalidateAll();
GraphicsContext context(rootPaintController());
LayoutRect bound = layoutView().viewRect();
drawRect(context, layoutView(), PaintPhaseForeground, bound);
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 1,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
}
TEST_F(LayoutObjectDrawingRecorderTest, Cached)
{
rootPaintController().invalidateAll();
GraphicsContext context(rootPaintController());
LayoutRect bound = layoutView().viewRect();
drawNothing(context, layoutView(), PaintPhaseBlockBackground, bound);
drawRect(context, layoutView(), PaintPhaseForeground, bound);
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 2,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseBlockBackground)),
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
drawNothing(context, layoutView(), PaintPhaseBlockBackground, bound);
drawRect(context, layoutView(), PaintPhaseForeground, bound);
EXPECT_DISPLAY_LIST(rootPaintController().newDisplayItemList(), 2,
TestDisplayItem(layoutView(), DisplayItem::drawingTypeToCachedDrawingType(DisplayItem::paintPhaseToDrawingType(PaintPhaseBlockBackground))),
TestDisplayItem(layoutView(), DisplayItem::drawingTypeToCachedDrawingType(DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground))));
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 2,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseBlockBackground)),
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
}
template <typename T>
FloatRect drawAndGetCullRect(PaintController& controller, const LayoutObject& layoutObject, const T& bounds)
{
controller.invalidateAll();
{
// Draw some things which will produce a non-null picture.
GraphicsContext context(controller);
LayoutObjectDrawingRecorder recorder(
context, layoutObject, DisplayItem::BoxDecorationBackground, bounds, LayoutPoint());
context.drawRect(enclosedIntRect(FloatRect(bounds)));
}
controller.commitNewDisplayItems();
const auto& drawing = static_cast<const DrawingDisplayItem&>(controller.displayItemList()[0]);
return drawing.picture()->cullRect();
}
TEST_F(LayoutObjectDrawingRecorderTest, CullRectMatchesProvidedClip)
{
// It's safe for the picture's cull rect to be expanded (though doing so
// excessively may harm performance), but it cannot be contracted.
// For now, this test expects the two rects to match completely.
//
// This rect is chosen so that in the x direction, pixel snapping rounds in
// the opposite direction to enclosing, and in the y direction, the edges
// are exactly on a half-pixel boundary. The numbers chosen map nicely to
// both float and LayoutUnit, to make equality checking reliable.
FloatRect rect(20.75, -5.5, 5.375, 10);
EXPECT_EQ(rect, drawAndGetCullRect(rootPaintController(), layoutView(), rect));
EXPECT_EQ(rect, drawAndGetCullRect(rootPaintController(), layoutView(), LayoutRect(rect)));
}
TEST_F(LayoutObjectDrawingRecorderTest, PaintOffsetCache)
{
RuntimeEnabledFeatures::setSlimmingPaintOffsetCachingEnabled(true);
GraphicsContext context(rootPaintController());
LayoutRect bounds = layoutView().viewRect();
LayoutPoint paintOffset(1, 2);
rootPaintController().invalidateAll();
EXPECT_FALSE(LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutView(), PaintPhaseForeground, paintOffset));
{
LayoutObjectDrawingRecorder drawingRecorder(context, layoutView(), PaintPhaseForeground, bounds, paintOffset);
IntRect rect(0, 0, 10, 10);
context.drawRect(rect);
}
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 1,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
// Ensure we cannot use the cache with a new paint offset.
LayoutPoint newPaintOffset(2, 3);
EXPECT_FALSE(LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutView(), PaintPhaseForeground, newPaintOffset));
// Test that a new paint offset is recorded.
{
LayoutObjectDrawingRecorder drawingRecorder(context, layoutView(), PaintPhaseForeground, bounds, newPaintOffset);
IntRect rect(0, 0, 10, 10);
context.drawRect(rect);
}
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 1,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
// Ensure the old paint offset cannot be used.
EXPECT_FALSE(LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutView(), PaintPhaseForeground, paintOffset));
// Ensure the new paint offset can be used.
EXPECT_TRUE(LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutView(), PaintPhaseForeground, newPaintOffset));
rootPaintController().commitNewDisplayItems();
EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 1,
TestDisplayItem(layoutView(), DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
}
} // namespace
} // namespace blink