Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ninepatch.cc
Go to the documentation of this file.
1 
18 #include "ion/image/ninepatch.h"
19 
20 #include <algorithm>
21 
22 #include "base/integral_types.h"
24 #include "ion/base/datacontainer.h"
25 #include "ion/base/logging.h"
26 
27 namespace ion {
28 namespace image {
29 
30 namespace {
31 
32 using base::DataContainer;
34 using gfx::Image;
35 using gfx::ImagePtr;
36 using math::Point2f;
37 using math::Point2ui;
38 using math::Range1ui;
39 using math::Range2f;
40 using math::Range2ui;
41 using math::Vector2f;
42 using math::Vector2ui;
43 
44 typedef base::AllocMap<uint32, uint32> RegionMap;
45 
47 static const uint32 kMarked = 0xff000000;
48 
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;
55 
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;
69  }
70  }
73  if (in_stretch_region)
74  (*regions)[stretch_region_start] = (length - 1) - stretch_region_start;
75 }
76 
78 static Range2ui ReadPaddingBox(const ImagePtr& image) {
79  Point2ui start;
80  Point2ui end;
81  Range2ui box;
82 
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];
91 
94  for (uint32 x = 1U; x < width - 1U; ++x) {
95  if (row[x] == kMarked) {
96  if (!start[0])
97  start[0] = x;
98  end[0] = x;
99  }
100  }
101  for (uint32 y = 1U; y < height - 1U; ++y) {
102  if (col[width * y] == kMarked) {
103  if (!start[1])
104  start[1] = y;
105  end[1] = y;
106  }
107  }
109  if (start != Point2ui::Zero() || end != Point2ui::Zero()) {
111  ++end[0];
112  ++end[1];
113  box.Set(start, end);
114  }
115  return box;
116 }
117 
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];
123  }
124  return 0U;
125 }
126 
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;
132  }
133 }
134 
137 static void CopyRegion(const Range2ui& source_rect, const Range2f& dest_rect,
138  const ImagePtr& source, const ImagePtr& dest) {
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]));
159 
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]);
169  DCHECK_LT(source_y, source_end[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]);
177  DCHECK_LT(source_x, source_end[0]);
178  const uint32 value = GetPixel(source, source_x, source_y);
179  SetPixel(dest, x, y, value);
180  }
181  }
182 }
183 
184 } // anonymous namespace
185 
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);
192  }
193 
194  bool stretch_h;
195  bool stretch_v;
197  Range2ui source;
200  Range2f dest;
201 };
202 
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()) {
208  image_ = image;
210  ReadStretchRegions(image_, &regions_v_, image_->GetWidth(), 1U);
211  ReadStretchRegions(image_, &regions_h_, image_->GetHeight(),
212  image_->GetWidth());
213  padding_ = ReadPaddingBox(image_);
214  }
215 }
216 
217 NinePatch::~NinePatch() {}
218 
219 const ImagePtr NinePatch::BuildImage(uint32 width, uint32 height,
220  const base::AllocatorPtr& alloc) const {
221  base::AllocatorPtr allocator =
223 
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);
234 
235  if (image_.Get()) {
237  const base::AllocVector<Region> regions = GetRegionsForSize(width, height);
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);
241  }
242  return output;
243 }
244 
245 const Vector2ui NinePatch::GetMinimumSize() const {
249  Vector2ui min_size(2U, 2U);
250  if (image_.Get()) {
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;
258 
260  min_size[0] = std::max(2U, min_size[0]);
261  min_size[1] = std::max(2U, min_size[1]);
262  }
265  return min_size - Vector2ui(2U, 2U);
266 }
267 
268 const Range2ui NinePatch::GetPaddingBox(uint32 width, uint32 height) const {
269  if (padding_.IsEmpty() || !image_.Get()) {
274  return Range2ui(Point2ui::Zero(), Point2ui(width, height));
275  }
277  const Vector2ui min_size = GetMinimumSize();
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);
295  return box;
296 }
297 
299  uint32 content_width, uint32 content_height) const {
300  if (padding_.IsEmpty() || !image_.Get()) {
305  return Vector2ui(content_width, content_height);
306  }
309  const Vector2ui natural_size(image_->GetWidth() - 2U,
310  image_->GetHeight() - 2U);
311  const Vector2ui natural_pad_size = padding_.GetSize();
312 
316  const Vector2ui size(natural_size[0] + content_width - natural_pad_size[0],
317  natural_size[1] + content_height - natural_pad_size[1]);
318 
320  const Vector2ui min_size = GetMinimumSize();
321  const Vector2ui clamped_size(std::max(min_size[0], size[0]),
322  std::max(min_size[1], size[1]));
323  return clamped_size;
324 }
325 
326 const base::AllocVector<NinePatch::Region> NinePatch::GetRegionsForSize(
327  uint32 width, uint32 height) const {
328  DCHECK(image_.Get());
329  base::AllocVector<Region> regions(*this);
330 
331  const Vector2ui min_size = GetMinimumSize();
332  const Vector2ui natural_size(image_->GetWidth(), image_->GetHeight());
333 
336  if (natural_size[0] == 2U + min_size[0] ||
337  natural_size[1] == 2U + min_size[1])
338  return regions;
339 
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);
348 
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;
357  } else {
358  next_stretch[1] = y_iter->first;
359  }
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;
365  } else {
366  next_stretch[0] = x_iter->first;
367  }
368 
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];
372 
373  Vector2ui source_size;
374  Vector2f dest_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];
387  } else {
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;
397  }
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]);
404  } else {
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];
408  }
409 
411  region.source.SetMaxPoint(region.source.GetMinPoint() + source_size);
412  region.dest.SetMaxPoint(region.dest.GetMinPoint() + dest_size);
413 
416  next_dest[0] += dest_size[0];
417 
418  regions.push_back(region);
419  }
423  next_dest[0] = 0.0;
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];
430  else
431  region_start_y += regions_h_.find(region_start_y)->second;
432  }
433  return regions;
434 }
435 
436 } // namespace image
437 } // namespace ion
base::ReferentPtr< Image >::Type ImagePtr
Definition: image.h:29
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...
Definition: ninepatch.cc:219
#define DCHECK(expr)
Definition: logging.h:331
double value
uint32 offset
Range< 2, float > Range2f
Definition: range.h:373
const math::Range2ui GetPaddingBox(uint32 width, uint32 height) const
Returns the padding box for this nine-patch for an image of the requested size.
Definition: ninepatch.cc:268
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...
Definition: ninepatch.cc:245
uint32 length
Range< 1, uint32 > Range1ui
Definition: range.h:364
T * Get() const
Returns a raw pointer to the instance, which may be NULL.
Definition: sharedptr.h:89
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...
Definition: ninepatch.cc:298
const Grid & image
The original monochrome image data, as doubles (0 - 1).
Definition: sdfutils.cc:90
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...
Definition: ninepatch.cc:203
int width
#define DCHECK_EQ(val1, val2)
Definition: logging.h:332
void * AllocateMemory(size_t size)
Allocates memory of the given size.
Definition: allocator.cc:32
Range< 2, uint32 > Range2ui
Definition: range.h:372
base::ReferentPtr< DataContainer >::Type DataContainerPtr
Definition: datacontainer.h:38
#define DCHECK_LT(val1, val2)
Definition: logging.h:335
This class can be used in place of std::vector to allow an Ion Allocator to be used for memory alloca...
Definition: allocvector.h:50