1 // Copyright 2016 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/fxge/cfx_defaultrenderdevice.h"
6 #include "core/fxge/cfx_font.h"
7 #include "core/fxge/cfx_graphstatedata.h"
8 #include "core/fxge/cfx_pathdata.h"
9 #include "core/fxge/cfx_renderdevice.h"
10 #include "core/fxge/skia/fx_skia_device.h"
11 #include "core/fxge/text_char_pos.h"
12 #include "fpdfsdk/cpdfsdk_helpers.h"
13 #include "public/cpp/fpdf_scopers.h"
14 #include "public/fpdfview.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/skia/include/core/SkPictureRecorder.h"
17
18 namespace {
19
20 struct State {
21 enum class Change { kNo, kYes };
22 enum class Save { kNo, kYes };
23 enum class Clip { kNo, kSame, kDifferentPath, kDifferentMatrix };
24 enum class Graphic { kNone, kPath, kText };
25
26 Change m_change;
27 Save m_save;
28 Clip m_clip;
29 Graphic m_graphic;
30 uint32_t m_pixel;
31 };
32
EmptyTest(CFX_SkiaDeviceDriver * driver,const State &)33 void EmptyTest(CFX_SkiaDeviceDriver* driver, const State&) {
34 driver->SaveState();
35 driver->RestoreState(true);
36 driver->RestoreState(false);
37 }
38
CommonTest(CFX_SkiaDeviceDriver * driver,const State & state)39 void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) {
40 TextCharPos charPos[1];
41 charPos[0].m_Origin = CFX_PointF(0, 1);
42 charPos[0].m_GlyphIndex = 1;
43 charPos[0].m_FontCharWidth = 4u;
44
45 CFX_Font font;
46 float fontSize = 1;
47 CFX_PathData clipPath, clipPath2;
48 clipPath.AppendRect(0, 0, 3, 1);
49 clipPath2.AppendRect(0, 0, 2, 1);
50 CFX_Matrix clipMatrix;
51 CFX_Matrix clipMatrix2(1, 0, 0, 1, 0, 1);
52 driver->SaveState();
53 CFX_PathData path1;
54 path1.AppendRect(0, 0, 1, 2);
55
56 CFX_Matrix matrix;
57 CFX_Matrix matrix2;
58 matrix2.Translate(1, 0);
59 CFX_GraphStateData graphState;
60 if (state.m_save == State::Save::kYes)
61 driver->SaveState();
62 if (state.m_clip != State::Clip::kNo)
63 driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
64 if (state.m_graphic == State::Graphic::kPath) {
65 driver->DrawPath(&path1, &matrix, &graphState, 0xFF112233, 0,
66 FXFILL_WINDING, BlendMode::kNormal);
67 } else if (state.m_graphic == State::Graphic::kText) {
68 driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix,
69 fontSize, 0xFF445566);
70 }
71 if (state.m_save == State::Save::kYes)
72 driver->RestoreState(true);
73 CFX_PathData path2;
74 path2.AppendRect(0, 0, 2, 2);
75 if (state.m_change == State::Change::kYes) {
76 if (state.m_graphic == State::Graphic::kPath)
77 graphState.m_LineCap = CFX_GraphStateData::LineCapRound;
78 else if (state.m_graphic == State::Graphic::kText)
79 fontSize = 2;
80 }
81 if (state.m_clip == State::Clip::kSame)
82 driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
83 else if (state.m_clip == State::Clip::kDifferentPath)
84 driver->SetClip_PathFill(&clipPath2, &clipMatrix, 0);
85 else if (state.m_clip == State::Clip::kDifferentMatrix)
86 driver->SetClip_PathFill(&clipPath, &clipMatrix2, 0);
87 if (state.m_graphic == State::Graphic::kPath) {
88 driver->DrawPath(&path2, &matrix2, &graphState, 0xFF112233, 0,
89 FXFILL_WINDING, BlendMode::kNormal);
90 } else if (state.m_graphic == State::Graphic::kText) {
91 driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix2,
92 fontSize, 0xFF445566);
93 }
94 if (state.m_save == State::Save::kYes)
95 driver->RestoreState(false);
96 driver->RestoreState(false);
97 }
98
OutOfSequenceClipTest(CFX_SkiaDeviceDriver * driver,const State &)99 void OutOfSequenceClipTest(CFX_SkiaDeviceDriver* driver, const State&) {
100 CFX_PathData clipPath;
101 clipPath.AppendRect(1, 0, 3, 1);
102 CFX_Matrix clipMatrix;
103 driver->SaveState();
104 driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
105 driver->RestoreState(true);
106 driver->SaveState();
107 driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
108 driver->RestoreState(false);
109 driver->RestoreState(false);
110
111 driver->SaveState();
112 driver->SaveState();
113 driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
114 driver->RestoreState(true);
115 driver->SetClip_PathFill(&clipPath, &clipMatrix, 0);
116 driver->RestoreState(false);
117 driver->RestoreState(false);
118 }
119
Harness(void (* Test)(CFX_SkiaDeviceDriver *,const State &),const State & state)120 void Harness(void (*Test)(CFX_SkiaDeviceDriver*, const State&),
121 const State& state) {
122 constexpr int kWidth = 4;
123 constexpr int kHeight = 1;
124 ScopedFPDFBitmap bitmap(FPDFBitmap_Create(kWidth, kHeight, 1));
125 ASSERT_TRUE(bitmap);
126 FPDFBitmap_FillRect(bitmap.get(), 0, 0, kWidth, kHeight, 0x00000000);
127 CFX_DefaultRenderDevice device;
128 RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap.get()));
129 device.Attach(pBitmap, false, nullptr, false);
130 auto* driver = static_cast<CFX_SkiaDeviceDriver*>(device.GetDeviceDriver());
131 (*Test)(driver, state);
132 driver->Flush();
133 uint32_t pixel = pBitmap->GetPixel(0, 0);
134 EXPECT_EQ(state.m_pixel, pixel);
135 #ifdef SK_DEBUG
136 if (!driver) // force dump to be linked in so it can be called from debugger
137 driver->Dump();
138 #endif
139 }
140
141 } // namespace
142
TEST(fxge,SkiaStateEmpty)143 TEST(fxge, SkiaStateEmpty) {
144 Harness(&EmptyTest, {});
145 }
146
TEST(fxge,SkiaStatePath)147 TEST(fxge, SkiaStatePath) {
148 Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
149 State::Clip::kSame, State::Graphic::kPath, 0xFF112233});
150 Harness(&CommonTest,
151 {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentPath,
152 State::Graphic::kPath, 0xFF112233});
153 Harness(&CommonTest, {State::Change::kNo, State::Save::kYes, State::Clip::kNo,
154 State::Graphic::kPath, 0xFF112233});
155 Harness(&CommonTest, {State::Change::kYes, State::Save::kNo, State::Clip::kNo,
156 State::Graphic::kPath, 0xFF112233});
157 Harness(&CommonTest, {State::Change::kNo, State::Save::kNo, State::Clip::kNo,
158 State::Graphic::kPath, 0xFF112233});
159 }
160
161 #ifdef _SKIA_SUPPORT_
162 // TODO(crbug.com/pdfium/11): Fix this test and enable.
TEST(fxge,DISABLED_SkiaStateText)163 TEST(fxge, DISABLED_SkiaStateText) {
164 Harness(&CommonTest,
165 {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentMatrix,
166 State::Graphic::kText, 0xFF445566});
167 Harness(&CommonTest, {State::Change::kNo, State::Save::kYes,
168 State::Clip::kSame, State::Graphic::kText, 0xFF445566});
169 }
170 #endif
171
TEST(fxge,SkiaStateOOSClip)172 TEST(fxge, SkiaStateOOSClip) {
173 Harness(&OutOfSequenceClipTest, {});
174 }
175