blob: 401e22a393d28070ae76ac81f1e3e53010c67ccf [file] [log] [blame]
estadec570b6312017-03-03 20:49:001// Copyright 2017 The Chromium 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 "ui/gfx/paint_vector_icon.h"
6
7#include <gtest/gtest.h>
Daniel Chengf9920542022-02-27 01:15:088
estadec570b6312017-03-03 20:49:009#include <vector>
10
Sammie Quonf7fde4b2017-09-27 21:36:4711#include "base/i18n/rtl.h"
enned2501572017-03-09 19:59:1712#include "cc/paint/paint_record.h"
13#include "cc/paint/paint_recorder.h"
estadec570b6312017-03-03 20:49:0014#include "third_party/skia/include/core/SkCanvas.h"
15#include "third_party/skia/include/core/SkPath.h"
16#include "ui/gfx/canvas.h"
17#include "ui/gfx/vector_icon_types.h"
18
19namespace gfx {
20
21namespace {
22
Patti5084aac2018-04-23 02:18:4723SkColor GetColorAtTopLeft(const Canvas& canvas) {
24 return canvas.GetBitmap().getColor(0, 0);
25}
26
estadec570b6312017-03-03 20:49:0027class MockCanvas : public SkCanvas {
28 public:
29 MockCanvas(int width, int height) : SkCanvas(width, height) {}
30
Peter Boström03d27022021-09-27 19:45:3931 MockCanvas(const MockCanvas&) = delete;
32 MockCanvas& operator=(const MockCanvas&) = delete;
33
estadec570b6312017-03-03 20:49:0034 // SkCanvas overrides:
35 void onDrawPath(const SkPath& path, const SkPaint& paint) override {
36 paths_.push_back(path);
37 }
38
39 const std::vector<SkPath>& paths() const { return paths_; }
40
41 private:
42 std::vector<SkPath> paths_;
estadec570b6312017-03-03 20:49:0043};
44
45// Tests that a relative move to command (R_MOVE_TO) after a close command
46// (CLOSE) uses the correct starting point. See crbug.com/697497
47TEST(VectorIconTest, RelativeMoveToAfterClose) {
enned2501572017-03-09 19:59:1748 cc::PaintRecorder recorder;
49 Canvas canvas(recorder.beginRecording(100, 100), 1.0f);
estadec570b6312017-03-03 20:49:0050
51 const PathElement elements[] = {
Evan Stade6e09c9a2018-03-15 17:03:0352 MOVE_TO, 4, 5, LINE_TO, 10, 11, CLOSE,
estadec570b6312017-03-03 20:49:0053 // This move should use (4, 5) as the start point rather than (10, 11).
Evan Stade6e09c9a2018-03-15 17:03:0354 R_MOVE_TO, 20, 21, R_LINE_TO, 50, 51};
Daniel Chengf9920542022-02-27 01:15:0855 const VectorIconRep rep_list[] = {{elements, std::size(elements)}};
Peter Kasting5380f8a72022-05-10 15:41:3656 const VectorIcon icon(rep_list, 1u, nullptr);
estadec570b6312017-03-03 20:49:0057
58 PaintVectorIcon(&canvas, icon, 100, SK_ColorMAGENTA);
enned2501572017-03-09 19:59:1759 sk_sp<cc::PaintRecord> record = recorder.finishRecordingAsPicture();
60
61 MockCanvas mock(100, 100);
Vladimir Levinea95edf2017-06-16 00:13:1062 record->Playback(&mock);
enned2501572017-03-09 19:59:1763
estadec570b6312017-03-03 20:49:0064 ASSERT_EQ(1U, mock.paths().size());
65 SkPoint last_point;
66 EXPECT_TRUE(mock.paths()[0].getLastPt(&last_point));
67 EXPECT_EQ(SkIntToScalar(74), last_point.x());
68 EXPECT_EQ(SkIntToScalar(77), last_point.y());
69}
70
Sammie Quonf7fde4b2017-09-27 21:36:4771TEST(VectorIconTest, FlipsInRtl) {
72 // Set the locale to a rtl language otherwise FLIPS_IN_RTL will do nothing.
73 base::i18n::SetICUDefaultLocale("he");
74 ASSERT_TRUE(base::i18n::IsRTL());
75
76 const int canvas_size = 20;
77 const SkColor color = SK_ColorWHITE;
78
79 Canvas canvas(gfx::Size(canvas_size, canvas_size), 1.0f, true);
80
81 // Create a 20x20 square icon which has FLIPS_IN_RTL, and CANVAS_DIMENSIONS
82 // are twice as large as |canvas|.
Evan Stade6e09c9a2018-03-15 17:03:0383 const PathElement elements[] = {CANVAS_DIMENSIONS,
84 2 * canvas_size,
85 FLIPS_IN_RTL,
86 MOVE_TO,
87 10,
88 10,
89 R_H_LINE_TO,
90 20,
91 R_V_LINE_TO,
92 20,
93 R_H_LINE_TO,
94 -20,
95 CLOSE};
Daniel Chengf9920542022-02-27 01:15:0896 const VectorIconRep rep_list[] = {{elements, std::size(elements)}};
Peter Kasting5380f8a72022-05-10 15:41:3697 const VectorIcon icon(rep_list, 1u, nullptr);
Sammie Quonf7fde4b2017-09-27 21:36:4798 PaintVectorIcon(&canvas, icon, canvas_size, color);
99
100 // Count the number of pixels in the canvas.
101 auto bitmap = canvas.GetBitmap();
102 int colored_pixel_count = 0;
103 for (int i = 0; i < bitmap.width(); ++i) {
104 for (int j = 0; j < bitmap.height(); ++j) {
105 if (bitmap.getColor(i, j) == color)
106 colored_pixel_count++;
107 }
108 }
109
110 // Verify that the amount of colored pixels on the canvas bitmap should be a
111 // quarter of the original icon, since each side should be scaled down by a
112 // factor of two.
113 EXPECT_EQ(100, colored_pixel_count);
114}
115
Patti5084aac2018-04-23 02:18:47116TEST(VectorIconTest, CorrectSizePainted) {
117 // Create a set of 5 icons reps, sized {48, 32, 24, 20, 16} for the test icon.
118 // Color each of them differently so they can be differentiated (the parts of
119 // an icon painted with PATH_COLOR_ARGB will not be overwritten by the color
120 // provided to it at creation time).
Evan Stade9a444ea2019-11-22 17:47:58121 const SkColor kPath48Color = SK_ColorRED;
Patti5084aac2018-04-23 02:18:47122 const PathElement elements48[] = {CANVAS_DIMENSIONS,
123 48,
124 PATH_COLOR_ARGB,
125 0xFF,
Evan Stade9a444ea2019-11-22 17:47:58126 SkColorGetR(kPath48Color),
127 SkColorGetG(kPath48Color),
128 SkColorGetB(kPath48Color),
Patti5084aac2018-04-23 02:18:47129 MOVE_TO,
130 0,
131 0,
132 H_LINE_TO,
133 48,
134 V_LINE_TO,
135 48,
136 H_LINE_TO,
137 0,
138 V_LINE_TO,
139 0,
140 CLOSE};
Evan Stade9a444ea2019-11-22 17:47:58141 const SkColor kPath32Color = SK_ColorGREEN;
Patti5084aac2018-04-23 02:18:47142 const PathElement elements32[] = {CANVAS_DIMENSIONS,
143 32,
144 PATH_COLOR_ARGB,
145 0xFF,
Evan Stade9a444ea2019-11-22 17:47:58146 SkColorGetR(kPath32Color),
147 SkColorGetG(kPath32Color),
148 SkColorGetB(kPath32Color),
Patti5084aac2018-04-23 02:18:47149 MOVE_TO,
150 0,
151 0,
152 H_LINE_TO,
153 32,
154 V_LINE_TO,
155 32,
156 H_LINE_TO,
157 0,
158 V_LINE_TO,
159 0,
160 CLOSE};
Evan Stade9a444ea2019-11-22 17:47:58161 const SkColor kPath24Color = SK_ColorBLUE;
Patti5084aac2018-04-23 02:18:47162 const PathElement elements24[] = {CANVAS_DIMENSIONS,
163 24,
164 PATH_COLOR_ARGB,
165 0xFF,
Evan Stade9a444ea2019-11-22 17:47:58166 SkColorGetR(kPath24Color),
167 SkColorGetG(kPath24Color),
168 SkColorGetB(kPath24Color),
Patti5084aac2018-04-23 02:18:47169 MOVE_TO,
170 0,
171 0,
172 H_LINE_TO,
173 24,
174 V_LINE_TO,
175 24,
176 H_LINE_TO,
177 0,
178 V_LINE_TO,
179 0,
180 CLOSE};
Evan Stade9a444ea2019-11-22 17:47:58181 const SkColor kPath20Color = SK_ColorYELLOW;
Patti5084aac2018-04-23 02:18:47182 const PathElement elements20[] = {CANVAS_DIMENSIONS,
183 20,
184 PATH_COLOR_ARGB,
185 0xFF,
Evan Stade9a444ea2019-11-22 17:47:58186 SkColorGetR(kPath20Color),
187 SkColorGetG(kPath20Color),
188 SkColorGetB(kPath20Color),
Patti5084aac2018-04-23 02:18:47189 MOVE_TO,
190 0,
191 0,
192 H_LINE_TO,
193 20,
194 V_LINE_TO,
195 20,
196 H_LINE_TO,
197 0,
198 V_LINE_TO,
199 0,
200 CLOSE};
Evan Stade9a444ea2019-11-22 17:47:58201 const SkColor kPath16Color = SK_ColorCYAN;
Patti5084aac2018-04-23 02:18:47202 const PathElement elements16[] = {CANVAS_DIMENSIONS,
203 16,
204 PATH_COLOR_ARGB,
205 0xFF,
Evan Stade9a444ea2019-11-22 17:47:58206 SkColorGetR(kPath16Color),
207 SkColorGetG(kPath16Color),
208 SkColorGetB(kPath16Color),
Patti5084aac2018-04-23 02:18:47209 MOVE_TO,
210 0,
211 0,
212 H_LINE_TO,
213 16,
214 V_LINE_TO,
215 16,
216 H_LINE_TO,
217 0,
218 V_LINE_TO,
219 0,
220 CLOSE};
221 // VectorIconReps are always sorted in descending order of size.
Daniel Chengf9920542022-02-27 01:15:08222 const VectorIconRep rep_list[] = {{elements48, std::size(elements48)},
223 {elements32, std::size(elements32)},
224 {elements24, std::size(elements24)},
225 {elements20, std::size(elements20)},
226 {elements16, std::size(elements16)}};
Peter Kasting5380f8a72022-05-10 15:41:36227 const VectorIcon icon(rep_list, 5u, nullptr);
Patti5084aac2018-04-23 02:18:47228
229 // Test exact sizes paint the correctly sized icon, including the largest and
230 // smallest icon.
231 Canvas canvas_100(gfx::Size(100, 100), 1.0, true);
232 PaintVectorIcon(&canvas_100, icon, 48, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58233 EXPECT_EQ(kPath48Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47234 PaintVectorIcon(&canvas_100, icon, 32, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58235 EXPECT_EQ(kPath32Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47236 PaintVectorIcon(&canvas_100, icon, 16, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58237 EXPECT_EQ(kPath16Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47238
Evan Stade9a444ea2019-11-22 17:47:58239 // The largest icon may be upscaled to a size larger than what it was
Patti5084aac2018-04-23 02:18:47240 // designed for.
241 PaintVectorIcon(&canvas_100, icon, 50, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58242 EXPECT_EQ(kPath48Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47243
Evan Stade9a444ea2019-11-22 17:47:58244 // Other requests will be satisfied by downscaling.
Patti5084aac2018-04-23 02:18:47245 PaintVectorIcon(&canvas_100, icon, 27, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58246 EXPECT_EQ(kPath32Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47247 PaintVectorIcon(&canvas_100, icon, 8, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58248 EXPECT_EQ(kPath16Color, GetColorAtTopLeft(canvas_100));
249
250 // Except in cases where an exact divisor is found.
251 PaintVectorIcon(&canvas_100, icon, 40, SK_ColorBLACK);
252 EXPECT_EQ(kPath20Color, GetColorAtTopLeft(canvas_100));
253 PaintVectorIcon(&canvas_100, icon, 64, SK_ColorBLACK);
254 EXPECT_EQ(kPath32Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47255
256 // Test icons at a scale factor < 100%, still with an exact size, paint the
257 // correctly sized icon.
258 Canvas canvas_75(gfx::Size(100, 100), 0.75, true);
259 PaintVectorIcon(&canvas_75, icon, 32, SK_ColorBLACK); // 32 * 0.75 = 24.
Evan Stade9a444ea2019-11-22 17:47:58260 EXPECT_EQ(kPath24Color, GetColorAtTopLeft(canvas_75));
Patti5084aac2018-04-23 02:18:47261
262 // Test icons at a scale factor > 100%, still with an exact size, paint the
263 // correctly sized icon.
264 Canvas canvas_125(gfx::Size(100, 100), 1.25, true);
265 PaintVectorIcon(&canvas_125, icon, 16, SK_ColorBLACK); // 16 * 1.25 = 20.
Evan Stade9a444ea2019-11-22 17:47:58266 EXPECT_EQ(kPath20Color, GetColorAtTopLeft(canvas_125));
Patti5084aac2018-04-23 02:18:47267
268 // Inexact sizes at scale factors < 100%.
269 PaintVectorIcon(&canvas_75, icon, 12, SK_ColorBLACK); // 12 * 0.75 = 9.
Evan Stade9a444ea2019-11-22 17:47:58270 EXPECT_EQ(kPath16Color, GetColorAtTopLeft(canvas_75));
Patti5084aac2018-04-23 02:18:47271 PaintVectorIcon(&canvas_75, icon, 28, SK_ColorBLACK); // 28 * 0.75 = 21.
Evan Stade9a444ea2019-11-22 17:47:58272 EXPECT_EQ(kPath24Color, GetColorAtTopLeft(canvas_75));
Patti5084aac2018-04-23 02:18:47273
274 // Inexact sizes at scale factors > 100%.
275 PaintVectorIcon(&canvas_125, icon, 12, SK_ColorBLACK); // 12 * 1.25 = 15.
Evan Stade9a444ea2019-11-22 17:47:58276 EXPECT_EQ(kPath16Color, GetColorAtTopLeft(canvas_125));
Patti5084aac2018-04-23 02:18:47277 PaintVectorIcon(&canvas_125, icon, 28, SK_ColorBLACK); // 28 * 1.25 = 35.
Evan Stade9a444ea2019-11-22 17:47:58278 EXPECT_EQ(kPath48Color, GetColorAtTopLeft(canvas_125));
Patti5084aac2018-04-23 02:18:47279
280 // Painting without a requested size will default to the smallest icon rep.
281 PaintVectorIcon(&canvas_100, icon, SK_ColorBLACK);
Evan Stade9a444ea2019-11-22 17:47:58282 EXPECT_EQ(kPath16Color, GetColorAtTopLeft(canvas_100));
Patti5084aac2018-04-23 02:18:47283 // But doing this in another scale factor should assume the smallest icon rep
284 // size, then scale it up by the DSF.
285 PaintVectorIcon(&canvas_125, icon, SK_ColorBLACK); // 16 * 1.25 = 20.
Evan Stade9a444ea2019-11-22 17:47:58286 EXPECT_EQ(kPath20Color, GetColorAtTopLeft(canvas_125));
Patti5084aac2018-04-23 02:18:47287}
288
estadec570b6312017-03-03 20:49:00289} // namespace
290
291} // namespace gfx