• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 
21 #include "androidfw/Image.h"
22 #include "androidfw/ResourceTypes.h"
23 #include "androidfw/StringPiece.h"
24 
25 using android::StringPiece;
26 
27 namespace android {
28 
29 // Colors in the format 0xAARRGGBB (the way 9-patch expects it).
30 constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
31 constexpr static const uint32_t kColorOpaqueBlack = 0xff000000u;
32 constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
33 
34 constexpr static const uint32_t kPrimaryColor = kColorOpaqueBlack;
35 constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
36 
37 /**
38  * Returns the alpha value encoded in the 0xAARRGBB encoded pixel.
39  */
40 static uint32_t get_alpha(uint32_t color);
41 
42 /**
43  * Determines whether a color on an ImageLine is valid.
44  * A 9patch image may use a transparent color as neutral,
45  * or a fully opaque white color as neutral, based on the
46  * pixel color at (0,0) of the image. One or the other is fine,
47  * but we need to ensure consistency throughout the image.
48  */
49 class ColorValidator {
50  public:
51   virtual ~ColorValidator() = default;
52 
53   /**
54    * Returns true if the color specified is a neutral color
55    * (no padding, stretching, or optical bounds).
56    */
57   virtual bool IsNeutralColor(uint32_t color) const = 0;
58 
59   /**
60    * Returns true if the color is either a neutral color
61    * or one denoting padding, stretching, or optical bounds.
62    */
IsValidColor(uint32_t color) const63   bool IsValidColor(uint32_t color) const {
64     switch (color) {
65       case kPrimaryColor:
66       case kSecondaryColor:
67         return true;
68     }
69     return IsNeutralColor(color);
70   }
71 };
72 
73 // Walks an ImageLine and records Ranges of primary and secondary colors.
74 // The primary color is black and is used to denote a padding or stretching
75 // range,
76 // depending on which border we're iterating over.
77 // The secondary color is red and is used to denote optical bounds.
78 //
79 // An ImageLine is a templated-interface that would look something like this if
80 // it
81 // were polymorphic:
82 //
83 // class ImageLine {
84 // public:
85 //      virtual int32_t GetLength() const = 0;
86 //      virtual uint32_t GetColor(int32_t idx) const = 0;
87 // };
88 //
89 template <typename ImageLine>
FillRanges(const ImageLine * image_line,const ColorValidator * color_validator,std::vector<Range> * primary_ranges,std::vector<Range> * secondary_ranges,std::string * out_err)90 static bool FillRanges(const ImageLine* image_line, const ColorValidator* color_validator,
91                        std::vector<Range>* primary_ranges, std::vector<Range>* secondary_ranges,
92                        std::string* out_err) {
93   const int32_t length = image_line->GetLength();
94 
95   uint32_t last_color = 0xffffffffu;
96   for (int32_t idx = 1; idx < length - 1; idx++) {
97     const uint32_t color = image_line->GetColor(idx);
98     if (!color_validator->IsValidColor(color)) {
99       *out_err = "found an invalid color";
100       return false;
101     }
102 
103     if (color != last_color) {
104       // We are ending a range. Which range?
105       // note: encode the x offset without the final 1 pixel border.
106       if (last_color == kPrimaryColor) {
107         primary_ranges->back().end = idx - 1;
108       } else if (last_color == kSecondaryColor) {
109         secondary_ranges->back().end = idx - 1;
110       }
111 
112       // We are starting a range. Which range?
113       // note: encode the x offset without the final 1 pixel border.
114       if (color == kPrimaryColor) {
115         primary_ranges->push_back(Range(idx - 1, length - 2));
116       } else if (color == kSecondaryColor) {
117         secondary_ranges->push_back(Range(idx - 1, length - 2));
118       }
119       last_color = color;
120     }
121   }
122   return true;
123 }
124 
125 /**
126  * Iterates over a row in an image. Implements the templated ImageLine
127  * interface.
128  */
129 class HorizontalImageLine {
130  public:
HorizontalImageLine(uint8_t ** rows,int32_t xoffset,int32_t yoffset,int32_t length)131   explicit HorizontalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset, int32_t length)
132       : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {
133   }
134 
GetLength() const135   inline int32_t GetLength() const {
136     return length_;
137   }
138 
GetColor(int32_t idx) const139   inline uint32_t GetColor(int32_t idx) const {
140     return NinePatch::PackRGBA(rows_[yoffset_] + (idx + xoffset_) * 4);
141   }
142 
143  private:
144   uint8_t** rows_;
145   int32_t xoffset_, yoffset_, length_;
146 
147   DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
148 };
149 
150 /**
151  * Iterates over a column in an image. Implements the templated ImageLine
152  * interface.
153  */
154 class VerticalImageLine {
155  public:
VerticalImageLine(uint8_t ** rows,int32_t xoffset,int32_t yoffset,int32_t length)156   explicit VerticalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset, int32_t length)
157       : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {
158   }
159 
GetLength() const160   inline int32_t GetLength() const {
161     return length_;
162   }
163 
GetColor(int32_t idx) const164   inline uint32_t GetColor(int32_t idx) const {
165     return NinePatch::PackRGBA(rows_[yoffset_ + idx] + (xoffset_ * 4));
166   }
167 
168  private:
169   uint8_t** rows_;
170   int32_t xoffset_, yoffset_, length_;
171 
172   DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
173 };
174 
175 class DiagonalImageLine {
176  public:
DiagonalImageLine(uint8_t ** rows,int32_t xoffset,int32_t yoffset,int32_t xstep,int32_t ystep,int32_t length)177   explicit DiagonalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset, int32_t xstep,
178                              int32_t ystep, int32_t length)
179       : rows_(rows),
180         xoffset_(xoffset),
181         yoffset_(yoffset),
182         xstep_(xstep),
183         ystep_(ystep),
184         length_(length) {
185   }
186 
GetLength() const187   inline int32_t GetLength() const {
188     return length_;
189   }
190 
GetColor(int32_t idx) const191   inline uint32_t GetColor(int32_t idx) const {
192     return NinePatch::PackRGBA(rows_[yoffset_ + (idx * ystep_)] + ((idx + xoffset_) * xstep_) * 4);
193   }
194 
195  private:
196   uint8_t** rows_;
197   int32_t xoffset_, yoffset_, xstep_, ystep_, length_;
198 
199   DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
200 };
201 
202 class TransparentNeutralColorValidator : public ColorValidator {
203  public:
IsNeutralColor(uint32_t color) const204   bool IsNeutralColor(uint32_t color) const override {
205     return get_alpha(color) == 0;
206   }
207 };
208 
209 class WhiteNeutralColorValidator : public ColorValidator {
210  public:
IsNeutralColor(uint32_t color) const211   bool IsNeutralColor(uint32_t color) const override {
212     return color == kColorOpaqueWhite;
213   }
214 };
215 
get_alpha(uint32_t color)216 inline static uint32_t get_alpha(uint32_t color) {
217   return (color & 0xff000000u) >> 24;
218 }
219 
PopulateBounds(const std::vector<Range> & padding,const std::vector<Range> & layout_bounds,const std::vector<Range> & stretch_regions,const int32_t length,int32_t * padding_start,int32_t * padding_end,int32_t * layout_start,int32_t * layout_end,StringPiece edge_name,std::string * out_err)220 static bool PopulateBounds(const std::vector<Range>& padding,
221                            const std::vector<Range>& layout_bounds,
222                            const std::vector<Range>& stretch_regions, const int32_t length,
223                            int32_t* padding_start, int32_t* padding_end, int32_t* layout_start,
224                            int32_t* layout_end, StringPiece edge_name, std::string* out_err) {
225   if (padding.size() > 1) {
226     std::stringstream err_stream;
227     err_stream << "too many padding sections on " << edge_name << " border";
228     *out_err = err_stream.str();
229     return false;
230   }
231 
232   *padding_start = 0;
233   *padding_end = 0;
234   if (!padding.empty()) {
235     const Range& range = padding.front();
236     *padding_start = range.start;
237     *padding_end = length - range.end;
238   } else if (!stretch_regions.empty()) {
239     // No padding was defined. Compute the padding from the first and last
240     // stretch regions.
241     *padding_start = stretch_regions.front().start;
242     *padding_end = length - stretch_regions.back().end;
243   }
244 
245   if (layout_bounds.size() > 2) {
246     std::stringstream err_stream;
247     err_stream << "too many layout bounds sections on " << edge_name << " border";
248     *out_err = err_stream.str();
249     return false;
250   }
251 
252   *layout_start = 0;
253   *layout_end = 0;
254   if (layout_bounds.size() >= 1) {
255     const Range& range = layout_bounds.front();
256     // If there is only one layout bound segment, it might not start at 0, but
257     // then it should
258     // end at length.
259     if (range.start != 0 && range.end != length) {
260       std::stringstream err_stream;
261       err_stream << "layout bounds on " << edge_name << " border must start at edge";
262       *out_err = err_stream.str();
263       return false;
264     }
265     *layout_start = range.end;
266 
267     if (layout_bounds.size() >= 2) {
268       const Range& range = layout_bounds.back();
269       if (range.end != length) {
270         std::stringstream err_stream;
271         err_stream << "layout bounds on " << edge_name << " border must start at edge";
272         *out_err = err_stream.str();
273         return false;
274       }
275       *layout_end = length - range.start;
276     }
277   }
278   return true;
279 }
280 
CalculateSegmentCount(const std::vector<Range> & stretch_regions,int32_t length)281 static int32_t CalculateSegmentCount(const std::vector<Range>& stretch_regions, int32_t length) {
282   if (stretch_regions.size() == 0) {
283     return 0;
284   }
285 
286   const bool start_is_fixed = stretch_regions.front().start != 0;
287   const bool end_is_fixed = stretch_regions.back().end != length;
288   int32_t modifier = 0;
289   if (start_is_fixed && end_is_fixed) {
290     modifier = 1;
291   } else if (!start_is_fixed && !end_is_fixed) {
292     modifier = -1;
293   }
294   return static_cast<int32_t>(stretch_regions.size()) * 2 + modifier;
295 }
296 
GetRegionColor(uint8_t ** rows,const Bounds & region)297 static uint32_t GetRegionColor(uint8_t** rows, const Bounds& region) {
298   // Sample the first pixel to compare against.
299   const uint32_t expected_color = NinePatch::PackRGBA(rows[region.top] + region.left * 4);
300   for (int32_t y = region.top; y < region.bottom; y++) {
301     const uint8_t* row = rows[y];
302     for (int32_t x = region.left; x < region.right; x++) {
303       const uint32_t color = NinePatch::PackRGBA(row + x * 4);
304       if (get_alpha(color) == 0) {
305         // The color is transparent.
306         // If the expectedColor is not transparent, NO_COLOR.
307         if (get_alpha(expected_color) != 0) {
308           return android::Res_png_9patch::NO_COLOR;
309         }
310       } else if (color != expected_color) {
311         return android::Res_png_9patch::NO_COLOR;
312       }
313     }
314   }
315 
316   if (get_alpha(expected_color) == 0) {
317     return android::Res_png_9patch::TRANSPARENT_COLOR;
318   }
319   return expected_color;
320 }
321 
322 // Fills out_colors with each 9-patch section's color. If the whole section is
323 // transparent,
324 // it gets the special TRANSPARENT color. If the whole section is the same
325 // color, it is assigned
326 // that color. Otherwise it gets the special NO_COLOR color.
327 //
328 // Note that the rows contain the 9-patch 1px border, and the indices in the
329 // stretch regions are
330 // already offset to exclude the border. This means that each time the rows are
331 // accessed,
332 // the indices must be offset by 1.
333 //
334 // width and height also include the 9-patch 1px border.
CalculateRegionColors(uint8_t ** rows,const std::vector<Range> & horizontal_stretch_regions,const std::vector<Range> & vertical_stretch_regions,const int32_t width,const int32_t height,std::vector<uint32_t> * out_colors)335 static void CalculateRegionColors(uint8_t** rows,
336                                   const std::vector<Range>& horizontal_stretch_regions,
337                                   const std::vector<Range>& vertical_stretch_regions,
338                                   const int32_t width, const int32_t height,
339                                   std::vector<uint32_t>* out_colors) {
340   int32_t next_top = 0;
341   Bounds bounds;
342   auto row_iter = vertical_stretch_regions.begin();
343   while (next_top != height) {
344     if (row_iter != vertical_stretch_regions.end()) {
345       if (next_top != row_iter->start) {
346         // This is a fixed segment.
347         // Offset the bounds by 1 to accommodate the border.
348         bounds.top = next_top + 1;
349         bounds.bottom = row_iter->start + 1;
350         next_top = row_iter->start;
351       } else {
352         // This is a stretchy segment.
353         // Offset the bounds by 1 to accommodate the border.
354         bounds.top = row_iter->start + 1;
355         bounds.bottom = row_iter->end + 1;
356         next_top = row_iter->end;
357         ++row_iter;
358       }
359     } else {
360       // This is the end, fixed section.
361       // Offset the bounds by 1 to accommodate the border.
362       bounds.top = next_top + 1;
363       bounds.bottom = height + 1;
364       next_top = height;
365     }
366 
367     int32_t next_left = 0;
368     auto col_iter = horizontal_stretch_regions.begin();
369     while (next_left != width) {
370       if (col_iter != horizontal_stretch_regions.end()) {
371         if (next_left != col_iter->start) {
372           // This is a fixed segment.
373           // Offset the bounds by 1 to accommodate the border.
374           bounds.left = next_left + 1;
375           bounds.right = col_iter->start + 1;
376           next_left = col_iter->start;
377         } else {
378           // This is a stretchy segment.
379           // Offset the bounds by 1 to accommodate the border.
380           bounds.left = col_iter->start + 1;
381           bounds.right = col_iter->end + 1;
382           next_left = col_iter->end;
383           ++col_iter;
384         }
385       } else {
386         // This is the end, fixed section.
387         // Offset the bounds by 1 to accommodate the border.
388         bounds.left = next_left + 1;
389         bounds.right = width + 1;
390         next_left = width;
391       }
392       out_colors->push_back(GetRegionColor(rows, bounds));
393     }
394   }
395 }
396 
397 // Calculates the insets of a row/column of pixels based on where the largest
398 // alpha value begins
399 // (on both sides).
400 template <typename ImageLine>
FindOutlineInsets(const ImageLine * image_line,int32_t * out_start,int32_t * out_end)401 static void FindOutlineInsets(const ImageLine* image_line, int32_t* out_start, int32_t* out_end) {
402   *out_start = 0;
403   *out_end = 0;
404 
405   const int32_t length = image_line->GetLength();
406   if (length < 3) {
407     return;
408   }
409 
410   // If the length is odd, we want both sides to process the center pixel,
411   // so we use two different midpoints (to account for < and <= in the different
412   // loops).
413   const int32_t mid2 = length / 2;
414   const int32_t mid1 = mid2 + (length % 2);
415 
416   uint32_t max_alpha = 0;
417   for (int32_t i = 0; i < mid1 && max_alpha != 0xff; i++) {
418     uint32_t alpha = get_alpha(image_line->GetColor(i));
419     if (alpha > max_alpha) {
420       max_alpha = alpha;
421       *out_start = i;
422     }
423   }
424 
425   max_alpha = 0;
426   for (int32_t i = length - 1; i >= mid2 && max_alpha != 0xff; i--) {
427     uint32_t alpha = get_alpha(image_line->GetColor(i));
428     if (alpha > max_alpha) {
429       max_alpha = alpha;
430       *out_end = length - (i + 1);
431     }
432   }
433   return;
434 }
435 
436 template <typename ImageLine>
FindMaxAlpha(const ImageLine * image_line)437 static uint32_t FindMaxAlpha(const ImageLine* image_line) {
438   const int32_t length = image_line->GetLength();
439   uint32_t max_alpha = 0;
440   for (int32_t idx = 0; idx < length && max_alpha != 0xff; idx++) {
441     uint32_t alpha = get_alpha(image_line->GetColor(idx));
442     if (alpha > max_alpha) {
443       max_alpha = alpha;
444     }
445   }
446   return max_alpha;
447 }
448 
449 // Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
PackRGBA(const uint8_t * pixel)450 uint32_t NinePatch::PackRGBA(const uint8_t* pixel) {
451   return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
452 }
453 
Create(uint8_t ** rows,const int32_t width,const int32_t height,std::string * out_err)454 std::unique_ptr<NinePatch> NinePatch::Create(uint8_t** rows, const int32_t width,
455                                              const int32_t height, std::string* out_err) {
456   if (width < 3 || height < 3) {
457     *out_err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
458     return {};
459   }
460 
461   std::vector<Range> horizontal_padding;
462   std::vector<Range> horizontal_layout_bounds;
463   std::vector<Range> vertical_padding;
464   std::vector<Range> vertical_layout_bounds;
465   std::vector<Range> unexpected_ranges;
466   std::unique_ptr<ColorValidator> color_validator;
467 
468   if (rows[0][3] == 0) {
469     color_validator = std::make_unique<TransparentNeutralColorValidator>();
470   } else if (PackRGBA(rows[0]) == kColorOpaqueWhite) {
471     color_validator = std::make_unique<WhiteNeutralColorValidator>();
472   } else {
473     *out_err = "top-left corner pixel must be either opaque white or transparent";
474     return {};
475   }
476 
477   // Private constructor, can't use make_unique.
478   auto nine_patch = std::unique_ptr<NinePatch>(new NinePatch());
479 
480   HorizontalImageLine top_row(rows, 0, 0, width);
481   if (!FillRanges(&top_row, color_validator.get(), &nine_patch->horizontal_stretch_regions,
482                   &unexpected_ranges, out_err)) {
483     return {};
484   }
485 
486   if (!unexpected_ranges.empty()) {
487     const Range& range = unexpected_ranges[0];
488     std::stringstream err_stream;
489     err_stream << "found unexpected optical bounds (red pixel) on top border "
490                << "at x=" << range.start + 1;
491     *out_err = err_stream.str();
492     return {};
493   }
494 
495   VerticalImageLine left_col(rows, 0, 0, height);
496   if (!FillRanges(&left_col, color_validator.get(), &nine_patch->vertical_stretch_regions,
497                   &unexpected_ranges, out_err)) {
498     return {};
499   }
500 
501   if (!unexpected_ranges.empty()) {
502     const Range& range = unexpected_ranges[0];
503     std::stringstream err_stream;
504     err_stream << "found unexpected optical bounds (red pixel) on left border "
505                << "at y=" << range.start + 1;
506     return {};
507   }
508 
509   HorizontalImageLine bottom_row(rows, 0, height - 1, width);
510   if (!FillRanges(&bottom_row, color_validator.get(), &horizontal_padding,
511                   &horizontal_layout_bounds, out_err)) {
512     return {};
513   }
514 
515   if (!PopulateBounds(horizontal_padding, horizontal_layout_bounds,
516                       nine_patch->horizontal_stretch_regions, width - 2, &nine_patch->padding.left,
517                       &nine_patch->padding.right, &nine_patch->layout_bounds.left,
518                       &nine_patch->layout_bounds.right, "bottom", out_err)) {
519     return {};
520   }
521 
522   VerticalImageLine right_col(rows, width - 1, 0, height);
523   if (!FillRanges(&right_col, color_validator.get(), &vertical_padding, &vertical_layout_bounds,
524                   out_err)) {
525     return {};
526   }
527 
528   if (!PopulateBounds(vertical_padding, vertical_layout_bounds,
529                       nine_patch->vertical_stretch_regions, height - 2, &nine_patch->padding.top,
530                       &nine_patch->padding.bottom, &nine_patch->layout_bounds.top,
531                       &nine_patch->layout_bounds.bottom, "right", out_err)) {
532     return {};
533   }
534 
535   // Fill the region colors of the 9-patch.
536   const int32_t num_rows = CalculateSegmentCount(nine_patch->horizontal_stretch_regions, width - 2);
537   const int32_t num_cols = CalculateSegmentCount(nine_patch->vertical_stretch_regions, height - 2);
538   if ((int64_t)num_rows * (int64_t)num_cols > 0x7f) {
539     *out_err = "too many regions in 9-patch";
540     return {};
541   }
542 
543   nine_patch->region_colors.reserve(num_rows * num_cols);
544   CalculateRegionColors(rows, nine_patch->horizontal_stretch_regions,
545                         nine_patch->vertical_stretch_regions, width - 2, height - 2,
546                         &nine_patch->region_colors);
547 
548   // Compute the outline based on opacity.
549 
550   // Find left and right extent of 9-patch content on center row.
551   HorizontalImageLine mid_row(rows, 1, height / 2, width - 2);
552   FindOutlineInsets(&mid_row, &nine_patch->outline.left, &nine_patch->outline.right);
553 
554   // Find top and bottom extent of 9-patch content on center column.
555   VerticalImageLine mid_col(rows, width / 2, 1, height - 2);
556   FindOutlineInsets(&mid_col, &nine_patch->outline.top, &nine_patch->outline.bottom);
557 
558   const int32_t outline_width = (width - 2) - nine_patch->outline.left - nine_patch->outline.right;
559   const int32_t outline_height =
560       (height - 2) - nine_patch->outline.top - nine_patch->outline.bottom;
561 
562   // Find the largest alpha value within the outline area.
563   HorizontalImageLine outline_mid_row(rows, 1 + nine_patch->outline.left,
564                                       1 + nine_patch->outline.top + (outline_height / 2),
565                                       outline_width);
566   VerticalImageLine outline_mid_col(rows, 1 + nine_patch->outline.left + (outline_width / 2),
567                                     1 + nine_patch->outline.top, outline_height);
568   nine_patch->outline_alpha =
569       std::max(FindMaxAlpha(&outline_mid_row), FindMaxAlpha(&outline_mid_col));
570 
571   // Assuming the image is a round rect, compute the radius by marching
572   // diagonally from the top left corner towards the center.
573   DiagonalImageLine diagonal(rows, 1 + nine_patch->outline.left, 1 + nine_patch->outline.top, 1, 1,
574                              std::min(outline_width, outline_height));
575   int32_t top_left, bottom_right;
576   FindOutlineInsets(&diagonal, &top_left, &bottom_right);
577 
578   /* Determine source radius based upon inset:
579    *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
580    *     sqrt(2) * r = sqrt(2) * i + r
581    *     (sqrt(2) - 1) * r = sqrt(2) * i
582    *     r = sqrt(2) / (sqrt(2) - 1) * i
583    */
584   nine_patch->outline_radius = 3.4142f * top_left;
585   return nine_patch;
586 }
587 
SerializeBase(size_t * outLen) const588 std::unique_ptr<uint8_t[]> NinePatch::SerializeBase(size_t* outLen) const {
589   android::Res_png_9patch data;
590   data.numXDivs = static_cast<uint8_t>(horizontal_stretch_regions.size()) * 2;
591   data.numYDivs = static_cast<uint8_t>(vertical_stretch_regions.size()) * 2;
592   data.numColors = static_cast<uint8_t>(region_colors.size());
593   data.paddingLeft = padding.left;
594   data.paddingRight = padding.right;
595   data.paddingTop = padding.top;
596   data.paddingBottom = padding.bottom;
597 
598   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
599   android::Res_png_9patch::serialize(data, (const int32_t*)horizontal_stretch_regions.data(),
600                                      (const int32_t*)vertical_stretch_regions.data(),
601                                      region_colors.data(), buffer.get());
602   // Convert to file endianness.
603   reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
604 
605   *outLen = data.serializedSize();
606   return buffer;
607 }
608 
SerializeLayoutBounds(size_t * out_len) const609 std::unique_ptr<uint8_t[]> NinePatch::SerializeLayoutBounds(size_t* out_len) const {
610   size_t chunk_len = sizeof(uint32_t) * 4;
611   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
612   uint8_t* cursor = buffer.get();
613 
614   memcpy(cursor, &layout_bounds.left, sizeof(layout_bounds.left));
615   cursor += sizeof(layout_bounds.left);
616 
617   memcpy(cursor, &layout_bounds.top, sizeof(layout_bounds.top));
618   cursor += sizeof(layout_bounds.top);
619 
620   memcpy(cursor, &layout_bounds.right, sizeof(layout_bounds.right));
621   cursor += sizeof(layout_bounds.right);
622 
623   memcpy(cursor, &layout_bounds.bottom, sizeof(layout_bounds.bottom));
624   cursor += sizeof(layout_bounds.bottom);
625 
626   *out_len = chunk_len;
627   return buffer;
628 }
629 
SerializeRoundedRectOutline(size_t * out_len) const630 std::unique_ptr<uint8_t[]> NinePatch::SerializeRoundedRectOutline(size_t* out_len) const {
631   size_t chunk_len = sizeof(uint32_t) * 6;
632   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
633   uint8_t* cursor = buffer.get();
634 
635   memcpy(cursor, &outline.left, sizeof(outline.left));
636   cursor += sizeof(outline.left);
637 
638   memcpy(cursor, &outline.top, sizeof(outline.top));
639   cursor += sizeof(outline.top);
640 
641   memcpy(cursor, &outline.right, sizeof(outline.right));
642   cursor += sizeof(outline.right);
643 
644   memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
645   cursor += sizeof(outline.bottom);
646 
647   *((float*)cursor) = outline_radius;
648   cursor += sizeof(outline_radius);
649 
650   *((uint32_t*)cursor) = outline_alpha;
651 
652   *out_len = chunk_len;
653   return buffer;
654 }
655 
operator <<(::std::ostream & out,const Range & range)656 ::std::ostream& operator<<(::std::ostream& out, const Range& range) {
657   return out << "[" << range.start << ", " << range.end << ")";
658 }
659 
operator <<(::std::ostream & out,const Bounds & bounds)660 ::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
661   return out << "l=" << bounds.left << " t=" << bounds.top << " r=" << bounds.right
662              << " b=" << bounds.bottom;
663 }
664 
665 template <typename T>
operator <<(std::ostream & os,const std::vector<T> & v)666 std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
667   for (int i = 0; i < v.size(); ++i) {
668     os << v[i];
669     if (i != v.size() - 1) os << " ";
670   }
671   return os;
672 }
673 
operator <<(::std::ostream & out,const NinePatch & nine_patch)674 ::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch) {
675   return out << "horizontalStretch:" << nine_patch.horizontal_stretch_regions
676              << " verticalStretch:" << nine_patch.vertical_stretch_regions
677              << " padding: " << nine_patch.padding << ", bounds: " << nine_patch.layout_bounds
678              << ", outline: " << nine_patch.outline << " rad=" << nine_patch.outline_radius
679              << " alpha=" << nine_patch.outline_alpha;
680 }
681 
682 }  // namespace android
683