22 #include "base/integral_types.h"
32 using base::DataContainer;
42 using math::Vector2ui;
44 typedef base::AllocMap<uint32, uint32> RegionMap;
47 static const uint32 kMarked = 0xff000000;
51 static void ReadStretchRegions(
const ImagePtr&
image, RegionMap* regions,
52 uint32
length, uint32 stride) {
53 bool in_stretch_region =
false;
54 uint32 stretch_region_start = 0;
57 DCHECK_EQ(Image::kRgba8888, image->GetFormat());
58 const uint32* data = image->GetData()->GetData<uint32>();
61 for (uint32 i = 1U; i < length - 1U; ++i) {
62 const bool marked = data[i * stride] == kMarked;
63 if (marked && !in_stretch_region) {
64 stretch_region_start = i;
65 in_stretch_region =
true;
66 }
else if (!marked && in_stretch_region) {
67 (*regions)[stretch_region_start] = i - stretch_region_start;
68 in_stretch_region =
false;
73 if (in_stretch_region)
74 (*regions)[stretch_region_start] = (length - 1) - stretch_region_start;
83 DCHECK_EQ(Image::kRgba8888, image->GetFormat());
84 const uint32* data = image->GetData()->GetData<uint32>();
85 const uint32
width = image->GetWidth();
86 const uint32 height = image->GetHeight();
88 const uint32* row = &data[width * (height - 1U)];
90 const uint32* col = &data[width - 1U];
94 for (uint32 x = 1U; x < width - 1U; ++x) {
95 if (row[x] == kMarked) {
101 for (uint32 y = 1U; y < height - 1U; ++y) {
102 if (col[width * y] == kMarked) {
109 if (start != Point2ui::Zero() || end != Point2ui::Zero()) {
119 static uint32 GetPixel(
const ImagePtr& image, uint32 x, uint32 y) {
120 if (x < image->GetWidth() && y < image->GetHeight()) {
121 const uint32* pixels = image->GetData()->GetData<uint32>();
122 return pixels[y * image->GetWidth() + x];
128 static void SetPixel(
const ImagePtr& image, uint32 x, uint32 y, uint32
value) {
129 if (x < image->GetWidth() && y < image->GetHeight()) {
130 uint32* pixels = image->GetData()->GetMutableData<uint32>();
131 pixels[y * image->GetWidth() + x] =
value;
137 static void CopyRegion(
const Range2ui& source_rect,
const Range2f& dest_rect,
140 const Point2f source_start(static_cast<float>(source_rect.GetMinPoint()[0]),
141 static_cast<float>(source_rect.GetMinPoint()[1]));
143 const Point2ui& source_end = source_rect.GetMaxPoint();
145 const Vector2f source_size(
146 static_cast<float>(source_end[0]) - source_start[0],
147 static_cast<float>(source_end[1]) - source_start[1]);
149 const Point2f& dest_start_f = dest_rect.GetMinPoint();
150 const Point2f& dest_end_f = dest_rect.GetMaxPoint();
152 const Vector2f inv_dest_size(1.f / (dest_end_f[0] - dest_start_f[0]),
153 1.f / (dest_end_f[1] - dest_start_f[1]));
155 const Vector2ui dest_start(static_cast<uint32>(dest_start_f[0]),
156 static_cast<uint32>(dest_start_f[1]));
157 const Vector2ui dest_end(static_cast<uint32>(dest_end_f[0]),
158 static_cast<uint32>(dest_end_f[1]));
162 for (uint32 y = dest_start[1]; y < dest_end[1]; ++y) {
164 const float y_distance =
165 static_cast<float>(y - dest_start[1]) * inv_dest_size[1];
167 const uint32 source_y =
168 static_cast<uint32
>(source_start[1] + y_distance * source_size[1]);
170 for (uint32 x = dest_start[0]; x < dest_end[0]; ++x) {
172 const float x_distance =
173 static_cast<float>(x - dest_start[0]) * inv_dest_size[0];
175 const uint32 source_x =
176 static_cast<uint32
>(source_start[0] + x_distance * source_size[0]);
178 const uint32 value = GetPixel(source, source_x, source_y);
179 SetPixel(dest, x, y, value);
186 struct NinePatch::Region {
187 Region() : stretch_h(false), stretch_v(false) {}
188 Region(
const Point2ui& source_start,
const Point2f& dest_start)
189 : stretch_h(false), stretch_v(false) {
190 source.SetMinPoint(source_start);
191 dest.SetMinPoint(dest_start);
204 : regions_h_(*this), regions_v_(*this), wipeable_(true) {
205 if (image.Get() && image->GetWidth() && image->GetHeight() &&
206 image->GetFormat() == Image::kRgba8888 && image->GetData().Get() &&
207 image->GetData()->GetData()) {
210 ReadStretchRegions(image_, ®ions_v_, image_->GetWidth(), 1U);
211 ReadStretchRegions(image_, ®ions_h_, image_->GetHeight(),
213 padding_ = ReadPaddingBox(image_);
217 NinePatch::~NinePatch() {}
225 ImagePtr output(
new (allocator) Image);
226 const uint32 length = 4U * width * height;
227 uint32* pixels =
reinterpret_cast<uint32*
>(allocator->
AllocateMemory(length));
228 output->Set(Image::kRgba8888, width, height,
229 DataContainer::Create<uint32>(
230 pixels, std::bind(DataContainer::AllocatorDeleter, allocator,
231 std::placeholders::_1),
232 wipeable_, allocator));
233 memset(pixels, 0, length);
238 const size_t count = regions.size();
239 for (
size_t i = 0; i < count; ++i)
240 CopyRegion(regions[i].source, regions[i].dest, image_, output);
249 Vector2ui min_size(2U, 2U);
251 min_size.Set(image_->GetWidth(), image_->GetHeight());
252 for (RegionMap::const_iterator it = regions_v_.begin();
253 it != regions_v_.end(); ++it)
254 min_size[0] -= it->second;
255 for (RegionMap::const_iterator it = regions_h_.begin();
256 it != regions_h_.end(); ++it)
257 min_size[1] -= it->second;
260 min_size[0] = std::max(2U, min_size[0]);
261 min_size[1] = std::max(2U, min_size[1]);
265 return min_size - Vector2ui(2U, 2U);
269 if (padding_.IsEmpty() || !image_.
Get()) {
274 return Range2ui(Point2ui::Zero(), Point2ui(width, height));
279 const Vector2ui clamped_size(std::max(min_size[0], width),
280 std::max(min_size[1], height));
283 const Vector2ui natural_size(image_->GetWidth() - 2U,
284 image_->GetHeight() - 2U);
287 Range2ui box(padding_.GetMinPoint() - Vector2ui(1U, 1U),
288 padding_.GetMaxPoint() - Vector2ui(1U, 1U));
292 const Vector2ui
offset(clamped_size[0] - natural_size[0],
293 clamped_size[1] - natural_size[1]);
294 box.SetMaxPoint(box.GetMaxPoint() +
offset);
299 uint32 content_width, uint32 content_height)
const {
300 if (padding_.IsEmpty() || !image_.
Get()) {
305 return Vector2ui(content_width, content_height);
309 const Vector2ui natural_size(image_->GetWidth() - 2U,
310 image_->GetHeight() - 2U);
311 const Vector2ui natural_pad_size = padding_.GetSize();
316 const Vector2ui size(natural_size[0] + content_width - natural_pad_size[0],
317 natural_size[1] + content_height - natural_pad_size[1]);
321 const Vector2ui clamped_size(std::max(min_size[0], size[0]),
322 std::max(min_size[1], size[1]));
327 uint32 width, uint32 height)
const {
332 const Vector2ui natural_size(image_->GetWidth(), image_->GetHeight());
336 if (natural_size[0] == 2U + min_size[0] ||
337 natural_size[1] == 2U + min_size[1])
341 const Point2f stretch_ratio(
342 static_cast<float>(width - min_size[0]) /
343 static_cast<float>(natural_size[0] - 2U - min_size[0]),
344 static_cast<float>(height - min_size[1]) /
345 static_cast<float>(natural_size[1] - 2U - min_size[1]));
346 Point2ui next_stretch(0U, 0U);
347 Point2f next_dest(0.f, 0.f);
350 uint32 region_start_y = 1;
351 while (region_start_y < natural_size[1] - 1U) {
352 uint32 region_start_x = 1;
353 RegionMap::const_iterator y_iter = regions_h_.lower_bound(region_start_y);
354 if (y_iter == regions_h_.end()) {
356 next_stretch[1] = natural_size[1] - 1U;
358 next_stretch[1] = y_iter->first;
360 while (region_start_x < natural_size[0] - 1U) {
361 RegionMap::const_iterator x_iter = regions_v_.lower_bound(region_start_x);
362 if (x_iter == regions_v_.end()) {
364 next_stretch[0] = natural_size[0] - 1U;
366 next_stretch[0] = x_iter->first;
370 const bool is_static_region_x = region_start_x < next_stretch[0];
371 const bool is_static_region_y = region_start_y < next_stretch[1];
373 Vector2ui source_size;
375 Region region(Point2ui(region_start_x, region_start_y),
376 Point2f(next_dest[0], next_dest[1]));
377 if (is_static_region_x) {
381 region.stretch_h =
false;
382 source_size[0] = next_stretch[0] - region_start_x;
383 dest_size[0] =
static_cast<float>(source_size[0]);
386 region_start_x = next_stretch[0];
391 region.stretch_h =
true;
392 source_size[0] = regions_v_.find(region_start_x)->second;
393 dest_size[0] =
static_cast<float>(source_size[0]) * stretch_ratio[0];
396 region_start_x += regions_v_.find(region_start_x)->second;
400 if (is_static_region_y) {
401 region.stretch_v =
false;
402 source_size[1] = next_stretch[1] - region_start_y;
403 dest_size[1] =
static_cast<float>(source_size[1]);
405 region.stretch_v =
true;
406 source_size[1] = regions_h_.find(region_start_y)->second;
407 dest_size[1] =
static_cast<float>(source_size[1]) * stretch_ratio[1];
411 region.source.SetMaxPoint(region.source.GetMinPoint() + source_size);
412 region.dest.SetMaxPoint(region.dest.GetMinPoint() + dest_size);
416 next_dest[0] += dest_size[0];
418 regions.push_back(region);
424 next_dest[1] += regions.back().dest.GetSize()[1];
425 const bool is_static_region_y = region_start_y < next_stretch[1];
428 if (is_static_region_y)
429 region_start_y = next_stretch[1];
431 region_start_y += regions_h_.find(region_start_y)->second;
base::ReferentPtr< Image >::Type ImagePtr
const gfx::ImagePtr BuildImage(uint32 width, uint32 height, const base::AllocatorPtr &alloc) const
Creates and returns an Image using the supplied Allocator and sets the wipability of the image as req...
Range< 2, float > Range2f
const math::Range2ui GetPaddingBox(uint32 width, uint32 height) const
Returns the padding box for this nine-patch for an image of the requested size.
const math::Vector2ui GetMinimumSize() const
Returns the minimum size at which this nine-patch can be drawn, i.e., the size at which all stretch r...
Range< 1, uint32 > Range1ui
T * Get() const
Returns a raw pointer to the instance, which may be NULL.
static const AllocatorPtr & GetNonNullAllocator(const AllocatorPtr &allocator)
This convenience function can be used where a non-NULL Allocator pointer is needed.
const math::Vector2ui GetSizeToFitContent(uint32 content_width, uint32 content_height) const
Returns the minimum size image required to fit the desired content inside the "drawable area" (paddin...
const Grid & image
The original monochrome image data, as doubles (0 - 1).
Copyright 2016 Google Inc.
NinePatch(const gfx::ImagePtr &image)
Sets the base image data of the NinePatch to the passed image, extracting stretch and padding informa...
#define DCHECK_EQ(val1, val2)
void * AllocateMemory(size_t size)
Allocates memory of the given size.
Range< 2, uint32 > Range2ui
base::ReferentPtr< DataContainer >::Type DataContainerPtr
#define DCHECK_LT(val1, val2)
This class can be used in place of std::vector to allow an Ion Allocator to be used for memory alloca...