blob: 483802191f17d801fdf6190af09fbdd7b04cb8bb [file]
// Copyright 2026 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#include "core/fxcrt/fx_coordinates.h"
#include "core/fxcrt/retain_ptr.h"
#include "core/fxge/cfx_fillrenderoptions.h"
#include "core/fxge/cfx_graphstatedata.h"
#include "core/fxge/cfx_path.h"
#include "core/fxge/cfx_renderdevice.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "core/fxge/dib/fx_dib.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "core/fxge/win32/cfx_psfonttracker.h"
#endif
TEST(CFXRenderDeviceTest, GetClipBoxDefault) {
std::unique_ptr<CFX_RenderDevice> device =
CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra);
ASSERT_TRUE(device);
EXPECT_EQ(FX_RECT(0, 0, 16, 16), device->GetClipBox());
}
TEST(CFXRenderDeviceTest, GetClipBoxPathFill) {
// Matrix that transposes and translates by 1 unit on each axis.
const CFX_Matrix object_to_device(0, 1, 1, 0, 1, -1);
// Fill type cannot be none.
const CFX_FillRenderOptions fill_options(
CFX_FillRenderOptions::FillType::kEvenOdd);
std::unique_ptr<CFX_RenderDevice> device =
CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra);
ASSERT_TRUE(device);
CFX_Path path;
path.AppendRect(2, 4, 14, 12);
EXPECT_TRUE(device->SetClip_PathFill(path, &object_to_device, fill_options));
EXPECT_EQ(FX_RECT(5, 1, 13, 13), device->GetClipBox());
}
TEST(CFXRenderDeviceTest, GetClipBoxPathStroke) {
// Matrix that transposes and translates by 1 unit on each axis.
const CFX_Matrix object_to_device(0, 1, 1, 0, 1, -1);
// Default line width is 1.
const CFX_GraphStateData graphics_state;
std::unique_ptr<CFX_RenderDevice> device =
CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra);
ASSERT_TRUE(device);
CFX_Path path;
path.AppendRect(2, 4, 14, 12);
EXPECT_TRUE(
device->SetClip_PathStroke(path, &object_to_device, &graphics_state));
EXPECT_EQ(FX_RECT(4, 0, 14, 14), device->GetClipBox());
}
TEST(CFXRenderDeviceTest, GetClipBoxRect) {
std::unique_ptr<CFX_RenderDevice> device =
CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra);
ASSERT_TRUE(device);
EXPECT_TRUE(device->SetClip_Rect({2, 4, 14, 12}));
EXPECT_EQ(FX_RECT(2, 4, 14, 12), device->GetClipBox());
}
TEST(CFXRenderDeviceTest, GetClipBoxEmpty) {
std::unique_ptr<CFX_RenderDevice> device =
CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra);
ASSERT_TRUE(device);
EXPECT_TRUE(device->SetClip_Rect({2, 8, 14, 8}));
EXPECT_TRUE(device->GetClipBox().IsEmpty());
}
#if BUILDFLAG(IS_WIN)
namespace {
constexpr CFX_Matrix kIdentityMatrix;
} // namespace
class CFXWindowsRenderDeviceTest : public testing::Test {
public:
void SetUp() override {
testing::Test::SetUp();
// Get a device context with Windows GDI.
dc_handle_ = CreateCompatibleDC(nullptr);
ASSERT_TRUE(dc_handle_);
device_ =
CFX_RenderDevice::CreateForWindowsDC(dc_handle_, &psfont_tracker_);
ASSERT_TRUE(device_);
device_->SaveState();
}
void TearDown() override {
device_->RestoreState(false);
device_.reset();
DeleteDC(dc_handle_);
testing::Test::TearDown();
}
protected:
HDC dc_handle_;
CFX_PSFontTracker psfont_tracker_;
std::unique_ptr<CFX_RenderDevice> device_;
};
TEST_F(CFXWindowsRenderDeviceTest, SimpleClipTriangle) {
CFX_Path path_data;
CFX_PointF p1(0.0f, 0.0f);
CFX_PointF p2(0.0f, 100.0f);
CFX_PointF p3(100.0f, 100.0f);
path_data.AppendLine(p1, p2);
path_data.AppendLine(p2, p3);
path_data.AppendLine(p3, p1);
path_data.ClosePath();
EXPECT_TRUE(device_->SetClip_PathFill(
path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
}
TEST_F(CFXWindowsRenderDeviceTest, SimpleClipRect) {
CFX_Path path_data;
path_data.AppendRect(0.0f, 100.0f, 200.0f, 0.0f);
path_data.ClosePath();
EXPECT_TRUE(device_->SetClip_PathFill(
path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
}
TEST_F(CFXWindowsRenderDeviceTest, GargantuanClipRect) {
CFX_Path path_data;
path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
257698812.0f);
path_data.ClosePath();
// These coordinates for a clip path are valid, just very large. Using these
// for a clip path should allow IntersectClipRect() to return success;
// however they do not because the GDI API IntersectClipRect() errors out and
// affect subsequent imaging. crbug.com/1019026
EXPECT_FALSE(device_->SetClip_PathFill(
path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
}
TEST_F(CFXWindowsRenderDeviceTest, GargantuanClipRectWithBaseClip) {
CFX_Path path_data;
const FX_RECT kBaseClip(0, 0, 5100, 6600);
device_->SetBaseClip(kBaseClip);
path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f,
257698812.0f);
path_data.ClosePath();
// Use of a reasonable base clip ensures that we avoid getting an error back
// from GDI API IntersectClipRect().
EXPECT_TRUE(device_->SetClip_PathFill(
path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions()));
}
#endif