1 // Copyright 2015 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.
5 #include "cc/playback/discardable_image_map.h"
10 #include "cc/base/math_util.h"
11 #include "cc/playback/display_item_list.h"
12 #include "cc/playback/picture.h"
13 #include "skia/ext/discardable_image_utils.h"
14 #include "ui/gfx/geometry/rect_conversions.h"
18 DiscardableImageMap::DiscardableImageMap(const gfx::Size
& cell_size
)
19 : cell_size_(cell_size
) {
20 DCHECK(!cell_size
.IsEmpty());
23 DiscardableImageMap::~DiscardableImageMap() {}
25 void DiscardableImageMap::GatherImagesFromPicture(SkPicture
* picture
,
26 const gfx::Rect
& layer_rect
) {
29 int min_x
= std::numeric_limits
<int>::max();
30 int min_y
= std::numeric_limits
<int>::max();
34 skia::DiscardableImageList images
;
35 skia::DiscardableImageUtils::GatherDiscardableImages(picture
, &images
);
36 for (skia::DiscardableImageList::const_iterator it
= images
.begin();
37 it
!= images
.end(); ++it
) {
38 // The image rect is in space relative to the picture, but it can extend far
39 // beyond the picture itself (since it represents the rect of actual image
40 // contained within the picture, not clipped to picture bounds). We only
41 // care about image queries that intersect the picture, so insert only into
42 // the intersection of the two rects.
43 gfx::Rect rect_clipped_to_picture
= gfx::IntersectRects(
44 gfx::ToEnclosingRect(gfx::SkRectToRectF(it
->image_rect
)),
45 gfx::Rect(layer_rect
.size()));
47 gfx::Point
min(MathUtil::UncheckedRoundDown(rect_clipped_to_picture
.x(),
49 MathUtil::UncheckedRoundDown(rect_clipped_to_picture
.y(),
50 cell_size_
.height()));
51 gfx::Point
max(MathUtil::UncheckedRoundDown(rect_clipped_to_picture
.right(),
53 MathUtil::UncheckedRoundDown(
54 rect_clipped_to_picture
.bottom(), cell_size_
.height()));
56 // We recorded the picture as if it was at (0, 0) by translating by layer
57 // rect origin. Add the rect origin back here. It really doesn't make much
58 // of a difference, since the query for pixel refs doesn't use this
59 // information. However, since picture pile / display list also returns this
60 // information, it would be nice to express it relative to the layer, not
61 // relative to the particular implementation of the raster source.
62 skia::PositionImage position_image
= *it
;
63 position_image
.image_rect
.offset(layer_rect
.x(), layer_rect
.y());
65 for (int y
= min
.y(); y
<= max
.y(); y
+= cell_size_
.height()) {
66 for (int x
= min
.x(); x
<= max
.x(); x
+= cell_size_
.width()) {
67 ImageMapKey
key(x
, y
);
68 data_hash_map_
[key
].push_back(position_image
);
72 min_x
= std::min(min_x
, min
.x());
73 min_y
= std::min(min_y
, min
.y());
74 max_x
= std::max(max_x
, max
.x());
75 max_y
= std::max(max_y
, max
.y());
78 min_pixel_cell_
= gfx::Point(min_x
, min_y
);
79 max_pixel_cell_
= gfx::Point(max_x
, max_y
);
82 base::LazyInstance
<Images
> DiscardableImageMap::Iterator::empty_images_
;
84 DiscardableImageMap::Iterator::Iterator()
85 : target_image_map_(NULL
),
86 current_images_(empty_images_
.Pointer()),
93 DiscardableImageMap::Iterator::Iterator(const gfx::Rect
& rect
,
94 const Picture
* picture
)
95 : target_image_map_(&(picture
->images_
)),
96 current_images_(empty_images_
.Pointer()),
98 map_layer_rect_
= picture
->layer_rect_
;
99 PointToFirstImage(rect
);
102 DiscardableImageMap::Iterator::Iterator(const gfx::Rect
& rect
,
103 const DisplayItemList
* display_list
)
104 : target_image_map_(display_list
->images_
.get()),
105 current_images_(empty_images_
.Pointer()),
107 map_layer_rect_
= display_list
->layer_rect_
;
108 PointToFirstImage(rect
);
111 DiscardableImageMap::Iterator::~Iterator() {}
113 DiscardableImageMap::Iterator
& DiscardableImageMap::Iterator::operator++() {
115 // If we're not at the end of the list, then we have the next item.
116 if (current_index_
< current_images_
->size())
119 DCHECK(current_y_
<= max_point_
.y());
121 gfx::Size cell_size
= target_image_map_
->cell_size_
;
123 // Advance the current grid cell.
124 current_x_
+= cell_size
.width();
125 if (current_x_
> max_point_
.x()) {
126 current_y_
+= cell_size
.height();
127 current_x_
= min_point_
.x();
128 if (current_y_
> max_point_
.y()) {
129 current_images_
= empty_images_
.Pointer();
135 // If there are no pixel refs at this grid cell, keep incrementing.
136 ImageMapKey
key(current_x_
, current_y_
);
137 ImageHashmap::const_iterator iter
=
138 target_image_map_
->data_hash_map_
.find(key
);
139 if (iter
== target_image_map_
->data_hash_map_
.end())
142 // We found a non-empty list: store it and get the first pixel ref.
143 current_images_
= &iter
->second
;
150 void DiscardableImageMap::Iterator::PointToFirstImage(const gfx::Rect
& rect
) {
151 gfx::Rect
query_rect(rect
);
152 // Early out if the query rect doesn't intersect this picture.
153 if (!query_rect
.Intersects(map_layer_rect_
) || !target_image_map_
) {
154 min_point_
= gfx::Point(0, 0);
155 max_point_
= gfx::Point(0, 0);
161 // First, subtract the layer origin as cells are stored in layer space.
162 query_rect
.Offset(-map_layer_rect_
.OffsetFromOrigin());
164 DCHECK(!target_image_map_
->cell_size_
.IsEmpty());
165 gfx::Size
cell_size(target_image_map_
->cell_size_
);
166 // We have to find a cell_size aligned point that corresponds to
167 // query_rect. Point is a multiple of cell_size.
168 min_point_
= gfx::Point(
169 MathUtil::UncheckedRoundDown(query_rect
.x(), cell_size
.width()),
170 MathUtil::UncheckedRoundDown(query_rect
.y(), cell_size
.height()));
171 max_point_
= gfx::Point(
172 MathUtil::UncheckedRoundDown(query_rect
.right() - 1, cell_size
.width()),
173 MathUtil::UncheckedRoundDown(query_rect
.bottom() - 1,
174 cell_size
.height()));
176 // Limit the points to known pixel ref boundaries.
177 min_point_
= gfx::Point(
178 std::max(min_point_
.x(), target_image_map_
->min_pixel_cell_
.x()),
179 std::max(min_point_
.y(), target_image_map_
->min_pixel_cell_
.y()));
180 max_point_
= gfx::Point(
181 std::min(max_point_
.x(), target_image_map_
->max_pixel_cell_
.x()),
182 std::min(max_point_
.y(), target_image_map_
->max_pixel_cell_
.y()));
184 // Make the current x be cell_size.width() less than min point, so that
185 // the first increment will point at min_point_.
186 current_x_
= min_point_
.x() - cell_size
.width();
187 current_y_
= min_point_
.y();
188 if (current_y_
<= max_point_
.y())