Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: cc/tiles/gpu_image_decode_controller_unittest.cc

Issue 1832573004: Gpu Image Decode Controller (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: small fixes Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "cc/tiles/gpu_image_decode_controller.h"
6
7 #include "cc/playback/draw_image.h"
8 #include "cc/raster/tile_task_runner.h"
9 #include "cc/test/test_context_provider.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace cc {
13 namespace {
14
15 skia::RefPtr<SkImage> CreateImage(int width, int height) {
16 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
17 SkBitmap bitmap;
18 bitmap.allocPixels(info);
19 return skia::AdoptRef(SkImage::NewFromBitmap(bitmap));
20 }
21
22 SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) {
23 SkMatrix matrix;
24 matrix.setScale(scale.width(), scale.height());
25
26 if (!is_decomposable) {
27 // Perspective is not decomposable, add it.
28 matrix[SkMatrix::kMPersp0] = 0.1f;
29 }
30
31 return matrix;
32 }
33
34 void ScheduleTask(ImageDecodeTask* task) {
35 task->WillSchedule();
36 task->ScheduleOnOriginThread(nullptr);
37 task->DidSchedule();
38 }
39
40 void RunTask(ImageDecodeTask* task) {
41 task->WillRun();
42 task->RunOnWorkerThread();
43 task->DidRun();
44 }
45
46 void CompleteTask(ImageDecodeTask* task) {
47 task->WillComplete();
48 task->CompleteOnOriginThread(nullptr);
49 task->DidComplete();
50 }
51
52 void ProcessTask(ImageDecodeTask* task) {
53 ScheduleTask(task);
54 RunTask(task);
55 CompleteTask(task);
56 }
57
58 TEST(GpuImageDecodeControllerTest, GetTaskForImageSameImage) {
59 auto context_provider = TestContextProvider::Create();
60 context_provider->BindToCurrentThread();
61 GpuImageDecodeController controller(context_provider.get(),
62 ResourceFormat::RGBA_8888);
63 skia::RefPtr<SkImage> image = CreateImage(100, 100);
64 bool is_decomposable = true;
65 SkFilterQuality quality = kHigh_SkFilterQuality;
66 uint64_t prepare_tiles_id = 1;
67
68 DrawImage draw_image(
69 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
70 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
71 scoped_refptr<ImageDecodeTask> task;
72 bool need_unref =
73 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
74 EXPECT_TRUE(need_unref);
75 EXPECT_TRUE(task);
76
77 DrawImage another_draw_image(
78 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
79 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
vmpstr 2016/03/28 23:55:53 Can you change the scale here as well, since it sh
ericrk 2016/03/29 23:11:30 sure.
80 scoped_refptr<ImageDecodeTask> another_task;
81 need_unref = controller.GetTaskForImageAndRef(
82 another_draw_image, prepare_tiles_id, &another_task);
83 EXPECT_TRUE(need_unref);
84 EXPECT_TRUE(task.get() == another_task.get());
85
86 controller.UnrefImage(draw_image);
87 controller.UnrefImage(draw_image);
88 }
89
90 TEST(GpuImageDecodeControllerTest, GetTaskForImageDifferentImage) {
91 auto context_provider = TestContextProvider::Create();
92 context_provider->BindToCurrentThread();
93 GpuImageDecodeController controller(context_provider.get(),
94 ResourceFormat::RGBA_8888);
95 bool is_decomposable = true;
96 uint64_t prepare_tiles_id = 1;
97 SkFilterQuality quality = kHigh_SkFilterQuality;
98
99 skia::RefPtr<SkImage> first_image = CreateImage(100, 100);
100 DrawImage first_draw_image(
101 first_image.get(),
102 SkIRect::MakeWH(first_image->width(), first_image->height()), quality,
103 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
104 scoped_refptr<ImageDecodeTask> first_task;
105 bool need_unref = controller.GetTaskForImageAndRef(
106 first_draw_image, prepare_tiles_id, &first_task);
107 EXPECT_TRUE(need_unref);
108 EXPECT_TRUE(first_task);
109
110 skia::RefPtr<SkImage> second_image = CreateImage(100, 100);
111 DrawImage second_draw_image(
112 second_image.get(),
113 SkIRect::MakeWH(second_image->width(), second_image->height()), quality,
114 CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable));
115 scoped_refptr<ImageDecodeTask> second_task;
116 need_unref = controller.GetTaskForImageAndRef(second_draw_image,
117 prepare_tiles_id, &second_task);
118 EXPECT_TRUE(need_unref);
119 EXPECT_TRUE(second_task);
120 EXPECT_TRUE(first_task.get() != second_task.get());
121
122 controller.UnrefImage(first_draw_image);
123 controller.UnrefImage(second_draw_image);
124 }
125
126 TEST(GpuImageDecodeControllerTest, GetTaskForImageAlreadyDecoded) {
127 auto context_provider = TestContextProvider::Create();
128 context_provider->BindToCurrentThread();
129 GpuImageDecodeController controller(context_provider.get(),
130 ResourceFormat::RGBA_8888);
131 bool is_decomposable = true;
132 uint64_t prepare_tiles_id = 1;
133 SkFilterQuality quality = kHigh_SkFilterQuality;
134
135 skia::RefPtr<SkImage> image = CreateImage(100, 100);
136 DrawImage draw_image(
137 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
138 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
139 scoped_refptr<ImageDecodeTask> task;
140 bool need_unref =
141 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
142 EXPECT_TRUE(need_unref);
143 EXPECT_TRUE(task);
144 EXPECT_TRUE(task->dependency());
145
146 ProcessTask(task->dependency().get());
147 ScheduleTask(task.get());
148 RunTask(task.get());
149
150 scoped_refptr<ImageDecodeTask> another_task;
151 need_unref = controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id,
152 &another_task);
153 EXPECT_TRUE(need_unref);
154 EXPECT_FALSE(another_task);
155
156 CompleteTask(task.get());
157
158 controller.UnrefImage(draw_image);
159 controller.UnrefImage(draw_image);
160 }
161
162 TEST(GpuImageDecodeControllerTest, GetTaskForImageCanceledGetsNewTask) {
163 auto context_provider = TestContextProvider::Create();
164 context_provider->BindToCurrentThread();
165 GpuImageDecodeController controller(context_provider.get(),
166 ResourceFormat::RGBA_8888);
167 bool is_decomposable = true;
168 uint64_t prepare_tiles_id = 1;
169 SkFilterQuality quality = kHigh_SkFilterQuality;
170
171 skia::RefPtr<SkImage> image = CreateImage(100, 100);
172 DrawImage draw_image(
173 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
174 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
175 scoped_refptr<ImageDecodeTask> task;
176 bool need_unref =
177 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
178 EXPECT_TRUE(need_unref);
179 EXPECT_TRUE(task);
180
181 ProcessTask(task->dependency().get());
182 ScheduleTask(task.get());
183
184 scoped_refptr<ImageDecodeTask> another_task;
185 need_unref = controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id,
186 &another_task);
187 EXPECT_TRUE(need_unref);
188 EXPECT_TRUE(another_task.get() == task.get());
189
190 // Didn't run the task, complete it (it was canceled).
191 CompleteTask(task.get());
192
193 // Fully cancel everything (so the raster would unref things).
194 controller.UnrefImage(draw_image);
195 controller.UnrefImage(draw_image);
196
197 // Here a new task is created.
198 scoped_refptr<ImageDecodeTask> third_task;
199 need_unref = controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id,
200 &third_task);
201 EXPECT_TRUE(need_unref);
202 EXPECT_TRUE(third_task);
203 EXPECT_FALSE(third_task.get() == task.get());
204
205 controller.UnrefImage(draw_image);
206 }
207
208 TEST(GpuImageDecodeControllerTest,
209 GetTaskForImageCanceledWhileReffedGetsNewTask) {
210 auto context_provider = TestContextProvider::Create();
211 context_provider->BindToCurrentThread();
212 GpuImageDecodeController controller(context_provider.get(),
213 ResourceFormat::RGBA_8888);
214 bool is_decomposable = true;
215 uint64_t prepare_tiles_id = 1;
216 SkFilterQuality quality = kHigh_SkFilterQuality;
217
218 skia::RefPtr<SkImage> image = CreateImage(100, 100);
219 DrawImage draw_image(
220 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
221 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
222 scoped_refptr<ImageDecodeTask> task;
223 bool need_unref =
224 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
225 EXPECT_TRUE(need_unref);
226 EXPECT_TRUE(task);
227
228 ProcessTask(task->dependency().get());
229 ScheduleTask(task.get());
230
231 scoped_refptr<ImageDecodeTask> another_task;
232 need_unref = controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id,
233 &another_task);
234 EXPECT_TRUE(need_unref);
235 EXPECT_TRUE(another_task.get() == task.get());
236
237 // Didn't run the task, complete it (it was canceled).
238 CompleteTask(task.get());
239
240 // Note that here, everything is reffed, but a new task is created. This is
241 // possible with repeated schedule/cancel operations.
242 scoped_refptr<ImageDecodeTask> third_task;
243 need_unref = controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id,
244 &third_task);
245 EXPECT_TRUE(need_unref);
246 EXPECT_TRUE(third_task);
247 EXPECT_FALSE(third_task.get() == task.get());
248
249 // 3 Unrefs!
250 controller.UnrefImage(draw_image);
251 controller.UnrefImage(draw_image);
252 controller.UnrefImage(draw_image);
253 }
254
255 TEST(GpuImageDecodeControllerTest, GetDecodedImageForDraw) {
256 auto context_provider = TestContextProvider::Create();
257 context_provider->BindToCurrentThread();
258 GpuImageDecodeController controller(context_provider.get(),
259 ResourceFormat::RGBA_8888);
260 bool is_decomposable = true;
261 uint64_t prepare_tiles_id = 1;
262 SkFilterQuality quality = kHigh_SkFilterQuality;
263
264 skia::RefPtr<SkImage> image = CreateImage(100, 100);
265 DrawImage draw_image(
266 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
267 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
268 scoped_refptr<ImageDecodeTask> task;
269 bool need_unref =
270 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
271 EXPECT_TRUE(need_unref);
272 EXPECT_TRUE(task);
273
274 ProcessTask(task->dependency().get());
275 ProcessTask(task.get());
276
277 // Must hold context lock before calling GetDecodedImageForDraw /
278 // DrawWithImageFinished.
279 ContextProvider::ScopedContextLock context_lock(context_provider.get());
280 DecodedDrawImage decoded_draw_image =
281 controller.GetDecodedImageForDraw(draw_image);
282 EXPECT_TRUE(decoded_draw_image.image());
283 EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
284 EXPECT_FALSE(decoded_draw_image.is_at_raster_decode());
285
286 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
287 controller.UnrefImage(draw_image);
288 }
289
290 TEST(GpuImageDecodeControllerTest, GetLargeDecodedImageForDraw) {
291 auto context_provider = TestContextProvider::Create();
292 context_provider->BindToCurrentThread();
293 GpuImageDecodeController controller(context_provider.get(),
294 ResourceFormat::RGBA_8888);
295 bool is_decomposable = true;
296 uint64_t prepare_tiles_id = 1;
297 SkFilterQuality quality = kHigh_SkFilterQuality;
298
299 skia::RefPtr<SkImage> image = CreateImage(1, 24000);
300 DrawImage draw_image(
301 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
302 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
303 scoped_refptr<ImageDecodeTask> task;
304 bool need_unref =
305 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
306 EXPECT_TRUE(need_unref);
307 EXPECT_TRUE(task);
308
309 ProcessTask(task->dependency().get());
310 ProcessTask(task.get());
311
312 // Must hold context lock before calling GetDecodedImageForDraw /
313 // DrawWithImageFinished.
314 ContextProvider::ScopedContextLock context_lock(context_provider.get());
315 DecodedDrawImage decoded_draw_image =
316 controller.GetDecodedImageForDraw(draw_image);
317 EXPECT_TRUE(decoded_draw_image.image());
318 EXPECT_FALSE(decoded_draw_image.image()->isTextureBacked());
319 EXPECT_FALSE(decoded_draw_image.is_at_raster_decode());
320
321 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
322 controller.UnrefImage(draw_image);
323 }
324
325 TEST(GpuImageDecodeControllerTest, GetDecodedImageForDrawAtRasterDecode) {
326 auto context_provider = TestContextProvider::Create();
327 context_provider->BindToCurrentThread();
328 GpuImageDecodeController controller(context_provider.get(),
329 ResourceFormat::RGBA_8888);
330 bool is_decomposable = true;
331 uint64_t prepare_tiles_id = 1;
332 SkFilterQuality quality = kHigh_SkFilterQuality;
333
334 controller.SetCachedItemLimitForTesting(0);
335 controller.SetCachedBytesLimitForTesting(0);
336
337 skia::RefPtr<SkImage> image = CreateImage(100, 100);
338 DrawImage draw_image(
339 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
340 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
341
342 scoped_refptr<ImageDecodeTask> task;
343 bool need_unref =
344 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
345 EXPECT_FALSE(need_unref);
346 EXPECT_FALSE(task);
347
348 // Must hold context lock before calling GetDecodedImageForDraw /
349 // DrawWithImageFinished.
350 ContextProvider::ScopedContextLock context_lock(context_provider.get());
351 DecodedDrawImage decoded_draw_image =
352 controller.GetDecodedImageForDraw(draw_image);
353 EXPECT_TRUE(decoded_draw_image.image());
354 EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
355 EXPECT_TRUE(decoded_draw_image.is_at_raster_decode());
356
357 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
358 }
359
360 TEST(GpuImageDecodeControllerTest, AtRasterUsedDirectlyIfSpaceAllows) {
361 auto context_provider = TestContextProvider::Create();
362 context_provider->BindToCurrentThread();
363 GpuImageDecodeController controller(context_provider.get(),
364 ResourceFormat::RGBA_8888);
365 bool is_decomposable = true;
366 uint64_t prepare_tiles_id = 1;
367 SkFilterQuality quality = kHigh_SkFilterQuality;
368
369 controller.SetCachedItemLimitForTesting(0);
370 controller.SetCachedBytesLimitForTesting(0);
371
372 skia::RefPtr<SkImage> image = CreateImage(100, 100);
373 DrawImage draw_image(
374 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
375 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
376
377 scoped_refptr<ImageDecodeTask> task;
378 bool need_unref =
379 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
380 EXPECT_FALSE(need_unref);
381 EXPECT_FALSE(task);
382
383 // Must hold context lock before calling GetDecodedImageForDraw /
384 // DrawWithImageFinished.
385 ContextProvider::ScopedContextLock context_lock(context_provider.get());
386 DecodedDrawImage decoded_draw_image =
387 controller.GetDecodedImageForDraw(draw_image);
388 EXPECT_TRUE(decoded_draw_image.image());
389 EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
390 EXPECT_TRUE(decoded_draw_image.is_at_raster_decode());
391
392 controller.SetCachedItemLimitForTesting(1000);
393 controller.SetCachedBytesLimitForTesting(96 * 1024 * 1024);
394
395 scoped_refptr<ImageDecodeTask> another_task;
396 bool another_task_needs_unref =
397 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
398 EXPECT_TRUE(another_task_needs_unref);
399 EXPECT_FALSE(another_task);
400
401 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
402 controller.UnrefImage(draw_image);
403 }
404
405 TEST(GpuImageDecodeControllerTest,
406 GetDecodedImageForDrawAtRasterDecodeMultipleTimes) {
407 auto context_provider = TestContextProvider::Create();
408 context_provider->BindToCurrentThread();
409 GpuImageDecodeController controller(context_provider.get(),
410 ResourceFormat::RGBA_8888);
411 bool is_decomposable = true;
412 SkFilterQuality quality = kHigh_SkFilterQuality;
413
414 controller.SetCachedItemLimitForTesting(0);
415 controller.SetCachedBytesLimitForTesting(0);
416
417 skia::RefPtr<SkImage> image = CreateImage(100, 100);
418 DrawImage draw_image(
419 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
420 CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable));
421
422 // Must hold context lock before calling GetDecodedImageForDraw /
423 // DrawWithImageFinished.
424 ContextProvider::ScopedContextLock context_lock(context_provider.get());
425 DecodedDrawImage decoded_draw_image =
426 controller.GetDecodedImageForDraw(draw_image);
427 EXPECT_TRUE(decoded_draw_image.image());
428 EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
429 EXPECT_TRUE(decoded_draw_image.is_at_raster_decode());
430
431 DecodedDrawImage another_decoded_draw_image =
432 controller.GetDecodedImageForDraw(draw_image);
433 EXPECT_EQ(decoded_draw_image.image()->uniqueID(),
434 another_decoded_draw_image.image()->uniqueID());
435
436 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
437 controller.DrawWithImageFinished(draw_image, another_decoded_draw_image);
438 }
439
440 TEST(GpuImageDecodeControllerTest, ZeroSizedImagesAreSkipped) {
441 auto context_provider = TestContextProvider::Create();
442 context_provider->BindToCurrentThread();
443 GpuImageDecodeController controller(context_provider.get(),
444 ResourceFormat::RGBA_8888);
445 bool is_decomposable = true;
446 uint64_t prepare_tiles_id = 1;
447 SkFilterQuality quality = kHigh_SkFilterQuality;
448
449 skia::RefPtr<SkImage> image = CreateImage(100, 100);
450 DrawImage draw_image(
451 image.get(), SkIRect::MakeWH(image->width(), image->height()), quality,
452 CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable));
453
454 scoped_refptr<ImageDecodeTask> task;
455 bool need_unref =
456 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
457 EXPECT_FALSE(task);
458 EXPECT_FALSE(need_unref);
459
460 DecodedDrawImage decoded_draw_image =
461 controller.GetDecodedImageForDraw(draw_image);
462 EXPECT_FALSE(decoded_draw_image.image());
463
464 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
465 }
466
467 TEST(GpuImageDecodeControllerTest, NonOverlappingSrcRectImagesAreSkipped) {
468 auto context_provider = TestContextProvider::Create();
469 context_provider->BindToCurrentThread();
470 GpuImageDecodeController controller(context_provider.get(),
471 ResourceFormat::RGBA_8888);
472 bool is_decomposable = true;
473 uint64_t prepare_tiles_id = 1;
474 SkFilterQuality quality = kHigh_SkFilterQuality;
475
476 skia::RefPtr<SkImage> image = CreateImage(100, 100);
477 DrawImage draw_image(
478 image.get(), SkIRect::MakeXYWH(150, 150, image->width(), image->height()),
479 quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable));
480
481 scoped_refptr<ImageDecodeTask> task;
482 bool need_unref =
483 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
484 EXPECT_FALSE(task);
485 EXPECT_FALSE(need_unref);
486
487 DecodedDrawImage decoded_draw_image =
488 controller.GetDecodedImageForDraw(draw_image);
489 EXPECT_FALSE(decoded_draw_image.image());
490
491 controller.DrawWithImageFinished(draw_image, decoded_draw_image);
492 }
493
494 TEST(GpuImageDecodeControllerTest, CancelledTasksDoNotCountAgainstBudget) {
495 auto context_provider = TestContextProvider::Create();
496 context_provider->BindToCurrentThread();
497 GpuImageDecodeController controller(context_provider.get(),
498 ResourceFormat::RGBA_8888);
499 bool is_decomposable = true;
500 uint64_t prepare_tiles_id = 1;
501 SkFilterQuality quality = kHigh_SkFilterQuality;
502
503 skia::RefPtr<SkImage> image = CreateImage(100, 100);
504 DrawImage draw_image(
505 image.get(), SkIRect::MakeXYWH(0, 0, image->width(), image->height()),
506 quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable));
507
508 scoped_refptr<ImageDecodeTask> task;
509 bool need_unref =
510 controller.GetTaskForImageAndRef(draw_image, prepare_tiles_id, &task);
511 EXPECT_NE(0u, controller.GetBytesUsedForTesting());
512 EXPECT_TRUE(task);
513 EXPECT_TRUE(need_unref);
514
515 ScheduleTask(task->dependency().get());
516 CompleteTask(task->dependency().get());
517 ScheduleTask(task.get());
518 CompleteTask(task.get());
519
520 controller.UnrefImage(draw_image);
521 EXPECT_EQ(0u, controller.GetBytesUsedForTesting());
522 }
523
524 } // namespace
525 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698