Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
conversionutils.cc
Go to the documentation of this file.
1 
19 
20 #include "ion/base/logging.h" // Ensures Ion logging code is used.
21 
22 #include "base/port.h"
24 #include "ion/base/datacontainer.h"
25 #include "ion/math/range.h"
26 #include "third_party/image_compression/image_compression/public/compressed_image.h"
27 #include "third_party/image_compression/image_compression/public/dxtc_compressor.h"
28 #include "third_party/image_compression/image_compression/public/etc_compressor.h"
29 #include "third_party/image_compression/image_compression/public/pvrtc_compressor.h"
30 #define LODEPNG_NO_COMPILE_ENCODER
31 #define LODEPNG_NO_COMPILE_DISK
32 #define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
33 #define LODEPNG_NO_COMPILE_ERROR_TEXT
34 #define LODEPNG_NO_COMPILE_CPP
35 #include "third_party/lodepng/lodepng.h"
36 #undef LODEPNG_NO_COMPILE_ENCODER
37 #undef LODEPNG_NO_COMPILE_DISK
38 #undef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
39 #undef LODEPNG_NO_COMPILE_ERROR_TEXT
40 #undef LODEPNG_NO_COMPILE_CPP
41 
42 #include "third_party/stblib/stb_image.h"
43 #include "third_party/stblib/stb_image_write.h"
44 
46 extern "C" unsigned char* stbi_write_png_to_mem(
47  unsigned char*, int, int, int, int, int*);
48 
49 namespace ion {
50 namespace image {
51 
52 using gfx::Image;
53 using gfx::ImagePtr;
54 using math::Point2f;
55 using math::Range2f;
56 
57 namespace {
58 
61 const size_t kIonRawImageHeaderSizeInBytes = 16;
62 
64 
69 
70 
72 static bool ImageHasData(const ImagePtr& image) {
73  if (!image.Get())
74  return false;
75  const base::DataContainerPtr& data = image->GetData();
76  return data.Get() != NULL && data->GetData() != NULL;
77 }
78 
80 static bool ImageHasAlpha(const Image& image) {
81  return Image::GetNumComponentsForFormat(image.GetFormat()) == 4;
82 }
83 
85 
90 
91 
94 static const ImagePtr CompressWithCompressor(
95  const Image& image, Image::Format compressed_format,
96  image_codec_compression::Compressor* compressor, bool is_wipeable,
97  const base::AllocatorPtr& allocator) {
98  using image_codec_compression::CompressedImage;
99  const CompressedImage::Format format =
100  ImageHasAlpha(image) ? CompressedImage::kRGBA : CompressedImage::kRGB;
101 
103  ImagePtr result;
104  CompressedImage compressed_image;
105  const uint8* uncompressed_data =
106  reinterpret_cast<const uint8*>(image.GetData()->GetData());
107  if (compressor->Compress(format, image.GetHeight(), image.GetWidth(), 0,
108  uncompressed_data, &compressed_image)) {
109  const CompressedImage::Metadata& metadata = compressed_image.GetMetadata();
110  result.Reset(new(allocator) Image);
111  result->Set(compressed_format, metadata.compressed_width,
112  metadata.compressed_height,
113  base::DataContainer::CreateAndCopy<uint8>(
114  compressed_image.GetData(), compressed_image.GetDataSize(),
115  is_wipeable, result->GetAllocator()));
116  }
117  return result;
118 }
119 
122 static const ImagePtr DecompressWithCompressor(
123  const Image& image, image_codec_compression::Compressor* compressor,
124  bool is_wipeable, const base::AllocatorPtr& allocator) {
125  using image_codec_compression::CompressedImage;
126 
128  CompressedImage::Format compressed_format;
129  Image::Format decompressed_format;
130  if (ImageHasAlpha(image)) {
131  compressed_format = CompressedImage::kRGBA;
132  decompressed_format = Image::kRgba8888;
133  } else {
134  compressed_format = CompressedImage::kRGB;
135  decompressed_format = Image::kRgb888;
136  }
137  const uint32 width = image.GetWidth();
138  const uint32 height = image.GetHeight();
139 
141  const void* image_data = image.GetData()->GetData();
142  DCHECK(image_data);
143  image_codec_compression::CompressedImage compressed_image(
144  image.GetDataSize(),
145  const_cast<uint8*>(reinterpret_cast<const uint8*>(image_data)));
146  const char* compressor_name = image.GetFormat() == Image::kEtc1 ?
147  "etc" : (image.GetFormat() == Image::kPvrtc1Rgba2 ? "pvrtc" : "dxtc");
148  compressed_image.SetMetadata(CompressedImage::Metadata(
149  compressed_format, compressor_name,
150  height, width, height, width, 0));
151 
153  ImagePtr result_image;
154  std::vector<uint8> decompressed_data;
155  if (compressor->Decompress(compressed_image, &decompressed_data)) {
156  result_image.Reset(new(allocator) Image);
157  result_image->Set(decompressed_format, width, height,
158  base::DataContainer::CreateAndCopy<uint8>(
159  &decompressed_data[0], decompressed_data.size(),
160  is_wipeable, result_image->GetAllocator()));
161  }
162  return result_image;
163 }
164 
166 static const ImagePtr CompressImage(const Image& image,
167  Image::Format target_format,
168  bool is_wipeable,
169  const base::AllocatorPtr& allocator) {
170  if (target_format == Image::kEtc1) {
171  image_codec_compression::EtcCompressor compressor;
172  compressor.SetCompressionStrategy(
173  image_codec_compression::EtcCompressor::kHeuristic);
174  return CompressWithCompressor(image, target_format, &compressor,
175  is_wipeable, allocator);
176  } else if (target_format == Image::kPvrtc1Rgba2) {
177  image_codec_compression::PvrtcCompressor compressor;
178  return CompressWithCompressor(image, target_format, &compressor,
179  is_wipeable, allocator);
180  } else {
181  DCHECK(target_format == Image::kDxt1 || target_format == Image::kDxt5);
182  image_codec_compression::DxtcCompressor compressor;
183  return CompressWithCompressor(image, target_format, &compressor,
184  is_wipeable, allocator);
185  }
186 }
187 
189 static const ImagePtr DecompressImage(const Image& image, bool is_wipeable,
190  const base::AllocatorPtr& allocator) {
192  const Image::Format format = image.GetFormat();
193  if (format == Image::kEtc1) {
194  image_codec_compression::EtcCompressor compressor;
195  return DecompressWithCompressor(image, &compressor, is_wipeable, allocator);
196  } else {
197  DCHECK(format == Image::kDxt1 || format == Image::kDxt5);
198  image_codec_compression::DxtcCompressor compressor;
199  return DecompressWithCompressor(image, &compressor, is_wipeable, allocator);
200  }
201 }
202 
203 static bool PngHasTransparencyChunk(const uint8* png_data, size_t data_size) {
204  const size_t kFirstChunk = 33; // first byte of the first chunk after header.
205  const unsigned char* chunk = &png_data[kFirstChunk];
206  const uint8* end_ptr = png_data + data_size;
207 
208  bool has_trns_chunk = false;
209  while (chunk < end_ptr) {
210  if (lodepng_chunk_type_equals(chunk, "IEND")) {
212  break;
213  } else if (lodepng_chunk_type_equals(chunk, "tRNS")) {
214  has_trns_chunk = true;
215  break;
216  }
217  chunk = lodepng_chunk_next_const(chunk);
218  }
219  return has_trns_chunk;
220 }
221 
224 static const ImagePtr DataToImageLodePng(const void* data,
225  size_t data_size,
226  bool flip_vertically,
227  bool is_wipeable,
228  const base::AllocatorPtr& allocator) {
229  ImagePtr image;
230  uint32 width = 0, height = 0;
231  const uint8* data_in = static_cast<const uint8*>(data);
232  LodePNGState state;
233  lodepng_state_init(&state);
234  static const unsigned kLodePngSuccess = 0;
235  unsigned lodepng_error_code = lodepng_inspect(
236  &width, &height, &state, data_in, data_size);
237  if (lodepng_error_code != kLodePngSuccess) {
238  lodepng_state_cleanup(&state);
239  return image;
240  }
241  uint8* data_out = NULL;
242 
243  LodePNGColorType colortype = state.info_png.color.colortype;
244  if (colortype == LCT_PALETTE) {
245  colortype = PngHasTransparencyChunk(data_in, data_size) ?
246  LCT_RGBA : LCT_RGB;
247  } else if (colortype == LCT_GREY || colortype == LCT_RGB) {
250  if (PngHasTransparencyChunk(data_in, data_size)) {
251  colortype = (colortype == LCT_GREY) ? LCT_GREY_ALPHA : LCT_RGBA;
252  }
253  }
254  lodepng_error_code = lodepng_decode_memory(&data_out, &width, &height,
255  data_in, data_size, colortype, 8U);
256  lodepng_state_cleanup(&state);
257  if (lodepng_error_code == kLodePngSuccess) {
258  Image::Format format = Image::kRgba8888;
259  uint32 num_channels = 4U;
260  switch (colortype) {
261  case LCT_RGBA:
262  format = Image::kRgba8888;
263  num_channels = 4;
264  break;
265  case LCT_RGB:
266  format = Image::kRgb888;
267  num_channels = 3;
268  break;
269  case LCT_GREY_ALPHA:
270  format = Image::kLuminanceAlpha;
271  num_channels = 2;
272  break;
273  case LCT_GREY:
274  format = Image::kLuminance;
275  num_channels = 1;
276  break;
277  default:
278  DCHECK(false) << "Unexpected PNG color type";
279  }
280  image.Reset(new(allocator) Image);
281  image->Set(format, width, height,
282  base::DataContainer::CreateAndCopy<uint8>(
283  data_out, width * height * num_channels, is_wipeable,
284  image->GetAllocator()));
285  free(data_out);
286 
287  if (flip_vertically) {
288  FlipImage(image);
289  }
290  }
291  return image;
292 }
293 
297 static const ImagePtr DataToImageStb(const void* data,
298  size_t data_size,
299  bool flip_vertically,
300  bool is_wipeable,
301  const base::AllocatorPtr& allocator) {
302  ImagePtr image;
303  int width, height, num_components;
304  if (stbi_uc* result_data = stbi_load_from_memory(
305  static_cast<const stbi_uc*>(data), static_cast<int>(data_size),
306  &width, &height, &num_components, 0)) {
308  static Image::Format formats[4] = {
309  Image::kLuminance,
310  Image::kLuminanceAlpha,
311  Image::kRgb888,
312  Image::kRgba8888,
313  };
314  DCHECK_GE(num_components, 1) << "Unsupported component count in image.";
315  DCHECK_LE(num_components, 4) << "Unsupported component count in image.";
316  image.Reset(new(allocator) Image);
317  image->Set(formats[num_components - 1], width, height,
318  base::DataContainer::CreateAndCopy<uint8>(
319  result_data, width * height * num_components, is_wipeable,
320  image->GetAllocator()));
321  stbi_image_free(result_data);
322  }
323 
324  if (flip_vertically) {
325  FlipImage(image);
326  }
327  return image;
328 }
329 
332 static const ImagePtr DataToImageIonRaw(const void* data,
333  size_t data_size,
334  bool flip_vertically,
335  bool is_wipeable,
336  const base::AllocatorPtr& allocator) {
337  if (!IsIonRawImageFormat(data, data_size)) {
338  return ImagePtr(); // NULL
339  }
340 
341  const uint16* header_ui16 = static_cast<const uint16*>(data);
342  const uint32* header_ui32 = static_cast<const uint32*>(data);
343 
344  const bool byte_swap_required = (header_ui16[2] != 1);
345  const uint16 format_indicator = byte_swap_required ?
346  bswap_16(header_ui16[3]) : header_ui16[3];
347  const uint32 width = byte_swap_required ?
348  bswap_32(header_ui32[2]) : header_ui32[2];
349  const uint32 height = byte_swap_required ?
350  bswap_32(header_ui32[3]) : header_ui32[3];
351  const size_t num_pixels = width * height;
352 
353  Image::Format format;
354  switch (format_indicator) {
355  case 0: format = Image::kRgba8888; break;
356  case 1: format = Image::kRgb565; break;
357  case 2: format = Image::kRgba4444; break;
358  case 3: format = Image::kAlpha; break;
359  default: return ImagePtr(); // NULL
360  }
361 
362  size_t num_bytes_per_pixel = Image::ComputeDataSize(format, 1, 1);
363  size_t payload_size_bytes = num_pixels * num_bytes_per_pixel;
364  if (payload_size_bytes == 0 ||
365  data_size - kIonRawImageHeaderSizeInBytes != payload_size_bytes) {
366  return ImagePtr(); // NULL
367  }
368 
369  ImagePtr image;
370  image.Reset(new(allocator) Image);
371  const uint8* payload = reinterpret_cast<const uint8*>(&header_ui32[4]);
372  image->Set(format, width, height, base::DataContainer::CreateAndCopy<uint8>(
373  payload, payload_size_bytes, is_wipeable, image->GetAllocator()));
374 
375  if (byte_swap_required && num_bytes_per_pixel > 1) {
376  uint8* mutable_payload = image->GetData()->GetMutableData<uint8>();
377  if (num_bytes_per_pixel == 4) {
378  uint32* mutable_payload_ui32 = reinterpret_cast<uint32*>(mutable_payload);
379  for (size_t i = 0; i < num_pixels; i++) {
380  mutable_payload_ui32[i] = bswap_32(mutable_payload_ui32[i]);
381  }
382  } else if (num_bytes_per_pixel == 2) {
383  uint16* mutable_payload_ui16 = reinterpret_cast<uint16*>(mutable_payload);
384  for (size_t i = 0; i < num_pixels; i++) {
385  mutable_payload_ui16[i] = bswap_16(mutable_payload_ui16[i]);
386  }
387  } else {
388  DCHECK(false) << "Byte swap not supported yet for num_bytes_per_pixel = "
389  << num_bytes_per_pixel;
390  }
391  }
392 
393  if (flip_vertically) {
394  FlipImage(image);
395  }
396 
397  return image;
398 }
399 
401 
406 
407 
408 static uint8 MultiplyUint8ByFloat(const uint8 int_value,
409  const float float_value) {
410  return static_cast<uint8>(static_cast<float>(int_value) * float_value);
411 }
412 
413 static const ImagePtr AllocImage(
414  Image::Format format, uint32 width, uint32 height, bool is_wipeable,
415  const base::AllocatorPtr& allocator) {
416  ImagePtr result(new(allocator) Image);
419  const base::AllocatorPtr& use_allocator = result->GetAllocator();
420  const size_t size = Image::ComputeDataSize(format, width, height);
421  uint8* new_buffer = reinterpret_cast<uint8*>(
422  use_allocator->AllocateMemory(size));
423  result->Set(format, width, height,
424  base::DataContainer::Create<uint8>(
425  new_buffer,
426  std::bind(base::DataContainer::AllocatorDeleter, use_allocator,
427  std::placeholders::_1),
428  is_wipeable,
429  use_allocator));
430  return result;
431 }
432 
434 static const ImagePtr ExtractRedChannel(const Image& image,
435  bool is_wipeable,
436  const base::AllocatorPtr& allocator) {
437  DCHECK(image.GetFormat() == Image::kRgb888 ||
438  image.GetFormat() == Image::kRgba8888);
439  const uint32 width = image.GetWidth();
440  const uint32 height = image.GetHeight();
441  ImagePtr result = AllocImage(
442  Image::kR8, width, height, is_wipeable, allocator);
443  const uint8* src_data = image.GetData()->GetData<uint8>();
444  uint8* dst_data = result->GetData()->GetMutableData<uint8>();
445  const size_t src_stride = Image::GetNumComponentsForFormat(image.GetFormat());
446  const uint8* src_end = src_data + image.GetDataSize();
447  while (src_data < src_end) {
448  *dst_data = src_data[0];
449  src_data += src_stride;
450  dst_data++;
451  }
452  return result;
453 }
454 
456 static const ImagePtr LuminanceToRgb(const Image& image,
457  Image::Format target_format,
458  bool is_wipeable,
459  const base::AllocatorPtr& allocator) {
460  DCHECK(image.GetFormat() == Image::kLuminance ||
461  image.GetFormat() == Image::kLuminanceAlpha);
462  DCHECK(target_format == Image::kRgb888 ||
463  target_format == Image::kRgba8888);
464 
465  const uint32 width = image.GetWidth();
466  const uint32 height = image.GetHeight();
467  ImagePtr result = AllocImage(
468  target_format, width, height, is_wipeable, allocator);
469  const uint8* src_data = image.GetData()->GetData<uint8>();
470  uint8* dst_data = result->GetData()->GetMutableData<uint8>();
471  const uint8* src_end = src_data + image.GetDataSize();
472  bool src_alpha = image.GetFormat() == Image::kLuminanceAlpha;
473  bool dst_alpha = target_format == Image::kRgba8888;
474  while (src_data < src_end) {
476  *dst_data++ = *src_data;
477  *dst_data++ = *src_data;
478  *dst_data++ = *src_data;
479  src_data++;
481  if (dst_alpha && src_alpha) {
482  *dst_data++ = *src_data++;
483  } else if (dst_alpha && !src_alpha) {
484  *dst_data++ = 255;
485  } else if (!dst_alpha && src_alpha) {
486  src_data++;
487  }
488  }
489  return result;
490 }
491 
494 static const ImagePtr ImageToImage(const Image& image,
495  Image::Format target_format,
496  bool is_wipeable,
497  const base::AllocatorPtr& allocator,
498  const base::AllocatorPtr& temp_allocator) {
499  const Image::Format source_format = image.GetFormat();
500 
502 
503  ImagePtr result;
504  switch (source_format) {
505  case Image::kDxt1:
506  case Image::kEtc1:
507  if (target_format == Image::kRgb888)
508  result = DecompressImage(image, is_wipeable, allocator);
509  break;
510 
511  case Image::kDxt5:
512  if (target_format == Image::kRgba8888)
513  result = DecompressImage(image, is_wipeable, allocator);
514  break;
515 
516  case Image::kRgb888:
517  if (target_format == Image::kDxt1 || target_format == Image::kEtc1) {
518  result = CompressImage(image, target_format, is_wipeable, allocator);
519  } else if (target_format == Image::kR8) {
520  result = ExtractRedChannel(image, is_wipeable, allocator);
521  }
522  break;
523 
524  case Image::kRgba8888:
525  if (target_format == Image::kDxt5 ||
526  target_format == Image::kPvrtc1Rgba2) {
527  result = CompressImage(image, target_format, is_wipeable, allocator);
528  } else if (target_format == Image::kR8) {
529  result = ExtractRedChannel(image, is_wipeable, allocator);
530  }
531  break;
532 
533  case Image::kLuminance:
534  case Image::kLuminanceAlpha:
535  if (target_format == Image::kRgba8888 ||
536  target_format == Image::kRgb888) {
537  result = LuminanceToRgb(image, target_format, is_wipeable, allocator);
538  }
539  break;
540 
542  case Image::kAlpha:
543  case Image::kPvrtc1Rgb2:
544  case Image::kPvrtc1Rgb4:
545  case Image::kPvrtc1Rgba2:
546  case Image::kPvrtc1Rgba4:
547  case Image::kRgb565:
548  case Image::kRgba4444:
549  case Image::kRgba5551:
550  default:
551  break;
552  }
553 
556  if (!result.Get()) {
557  Image::Format canonical_format = target_format;
558  switch (Image::GetNumComponentsForFormat(source_format)) {
559  case 1:
560  canonical_format = Image::kLuminance;
561  break;
562  case 2:
563  canonical_format = Image::kLuminanceAlpha;
564  break;
565  case 3:
566  canonical_format = Image::kRgb888;
567  break;
568  case 4:
569  canonical_format = Image::kRgba8888;
570  break;
571  default:
572  break;
573  }
574  if (source_format != canonical_format && canonical_format != target_format)
575  result = ConvertImage(
576  ImageToImage(image, canonical_format, is_wipeable,
577  temp_allocator, temp_allocator),
578  target_format, is_wipeable, allocator, temp_allocator);
579  }
580 
581  return result;
582 }
583 
590 static const ImagePtr DataToImage(const void* data, size_t data_size,
591  bool flip_vertically, bool is_wipeable,
592  const base::AllocatorPtr& allocator) {
595  ImagePtr image = DataToImageLodePng(
596  data, data_size, flip_vertically, is_wipeable, allocator);
597  if (image.Get() != NULL) {
598  return image;
599  }
600 
601  image = DataToImageStb(
602  data, data_size, flip_vertically, is_wipeable, allocator);
603  if (image.Get() != NULL) {
604  return image;
605  }
606 
607  return DataToImageIonRaw(
608  data, data_size, flip_vertically, is_wipeable, allocator);
609 }
610 
613 static const std::vector<uint8> ImageToData(
614  const Image& image, ExternalImageFormat external_format,
615  bool flip_vertically) {
616  std::vector<uint8> result;
617 
619  if (external_format == kPng) {
620  uint8* image_data = nullptr;
621  ImagePtr flipped_image;
622 
623  if (flip_vertically) {
624  flipped_image.Reset(new Image);
625  flipped_image->Set(image.GetFormat(), image.GetWidth(), image.GetHeight(),
626  base::DataContainer::CreateAndCopy<uint8>(
627  image.GetData()->GetData<uint8>(),
628  image.GetDataSize(),
629  false,
630  image.GetAllocator()));
631  FlipImage(flipped_image);
632  image_data = const_cast<uint8*>(
633  flipped_image->GetData()->GetData<uint8>());
634  } else {
635  image_data = const_cast<uint8*>(image.GetData()->GetData<uint8>());
636  }
637 
638  int num_bytes;
639  if (unsigned char* result_data = stbi_write_png_to_mem(
640  image_data, 0, image.GetWidth(), image.GetHeight(),
641  Image::GetNumComponentsForFormat(image.GetFormat()),
642  &num_bytes)) {
643  result = std::vector<uint8>(result_data, result_data + num_bytes);
644  stbi_image_free(result_data);
645  }
646  }
647  return result;
648 }
649 
653 static const ImagePtr DownsampleWithCompressor(
654  const Image& image,
655  image_codec_compression::Compressor* compressor,
656  bool is_wipeable,
657  const base::AllocatorPtr& allocator) {
658  using image_codec_compression::CompressedImage;
659  const CompressedImage::Format format = image.GetFormat() == Image::kDxt5 ?
660  CompressedImage::kRGBA : CompressedImage::kRGB;
661 
662  const uint32 width = image.GetWidth();
663  const uint32 height = image.GetHeight();
664 
666  const void* image_data = image.GetData()->GetData();
667  DCHECK(image_data);
668  image_codec_compression::CompressedImage compressed_image(
669  image.GetDataSize(),
670  const_cast<uint8*>(reinterpret_cast<const uint8*>(image_data)));
671  const char* compressor_name =
672  image.GetFormat() == Image::kEtc1 ? "etc" : "dxtc";
673  compressed_image.SetMetadata(CompressedImage::Metadata(
674  format, compressor_name,
675  height, width, height, width, 0));
676 
678  CompressedImage downsampled_image;
679  ImagePtr result;
680  if (compressor->Downsample(compressed_image, &downsampled_image)) {
681  const CompressedImage::Metadata& metadata = downsampled_image.GetMetadata();
682  result.Reset(new(allocator) Image);
683  result->Set(image.GetFormat(), metadata.uncompressed_width,
684  metadata.uncompressed_height,
685  base::DataContainer::CreateAndCopy<uint8>(
686  downsampled_image.GetData(),
687  downsampled_image.GetDataSize(),
688  is_wipeable, result->GetAllocator()));
689  }
690  return result;
691 }
692 
698 static const ImagePtr Downsample2xSimple8bpc(
699  const Image& image,
700  bool is_wipeable,
701  const base::AllocatorPtr& allocator) {
702  const uint32 num_channels =
703  Image::GetNumComponentsForFormat(image.GetFormat());
705  const uint32 new_width = (image.GetWidth() + 1) >> 1;
706  const uint32 new_height = (image.GetHeight() + 1) >> 1;
707  ImagePtr result = AllocImage(
708  image.GetFormat(), new_width, new_height, is_wipeable, allocator);
709  const uint8* src_data = image.GetData()->GetData<uint8>();
710  uint8* dst_data = result->GetData()->GetMutableData<uint8>();
711  const size_t src_stride = image.GetWidth() * num_channels;
712  const size_t dst_stride = new_width * num_channels;
713  for (size_t src_row = 0, src_rows = image.GetHeight();
714  src_row < src_rows; src_row += 2) {
715  const size_t dst_row = src_row >> 1;
717  const size_t next_row = (src_row == src_rows - 1) ? 0 : src_stride;
718  for (size_t src_col = 0, src_cols = image.GetWidth();
719  src_col < src_cols; src_col += 2) {
720  const uint8* src_pixel =
721  src_data + src_row * src_stride + src_col * num_channels;
722  const size_t dst_col = src_col >> 1;
723  uint8* dst_pixel =
724  dst_data + dst_row * dst_stride + dst_col * num_channels;
726  const size_t next_col = (src_col == src_cols - 1) ? 0 : num_channels;
727  for (size_t chan = 0; chan < num_channels; ++chan) {
728  dst_pixel[chan] =
729  static_cast<uint8>((src_pixel[chan] +
730  src_pixel[chan + next_col] +
731  src_pixel[chan + next_row] +
732  src_pixel[chan + next_col + next_row] + 1) >> 2);
733  }
734  }
735  }
736  return result;
737 }
738 
742 static const gfx::ImagePtr ResizeBilinear8bpc(
743  const gfx::Image& image, uint32 out_width, uint32 out_height,
744  bool is_wipeable, const base::AllocatorPtr& allocator) {
745  ImagePtr result = AllocImage(
746  image.GetFormat(), out_width, out_height, is_wipeable, allocator);
747  const float xscale =
748  static_cast<float>(image.GetWidth()) / static_cast<float>(out_width);
749  const float yscale =
750  static_cast<float>(image.GetHeight()) / static_cast<float>(out_height);
751  const uint8* src_data = image.GetData()->GetData<uint8>();
752  uint8* dst_data = result->GetData()->GetMutableData<uint8>();
753  int num_channels = Image::GetNumComponentsForFormat(image.GetFormat());
754  const size_t src_stride = image.GetWidth() * num_channels;
755  const size_t dst_stride = out_width * num_channels;
756  const size_t max_src_x = static_cast<size_t>(image.GetWidth() - 1);
757  const size_t max_src_y = static_cast<size_t>(image.GetHeight() - 1);
761  for (uint32 dst_y = 0; dst_y < out_height; ++dst_y) {
762  for (uint32 dst_x = 0; dst_x < out_width; ++dst_x) {
763  uint8* dst_pixel = dst_data + dst_y * dst_stride + dst_x * num_channels;
764  float src_x = (static_cast<float>(dst_x) + 0.5f) * xscale;
765  float src_y = (static_cast<float>(dst_y) + 0.5f) * yscale;
768  float s1 = src_x + 0.5f - std::floor(src_x + 0.5f);
769  float s0 = 1.0f - s1;
770  float t1 = src_y + 0.5f - std::floor(src_y + 0.5f);
771  float t0 = 1.0f - t1;
773  size_t src_x0 =
774  static_cast<size_t>(std::max(0.f, std::floor(src_x - 0.5f)));
775  size_t src_y0 =
776  static_cast<size_t>(std::max(0.f, std::floor(src_y - 0.5f)));
777  size_t src_x1 = std::min(
778  max_src_x,
779  static_cast<size_t>(std::floor(src_x + 0.5f)));
780  size_t src_y1 = std::min(
781  max_src_y,
782  static_cast<size_t>(std::floor(src_y + 0.5f)));
783  const uint8* src_00 =
784  src_data + src_x0 * num_channels + src_y0 * src_stride;
785  const uint8* src_10 =
786  src_data + src_x1 * num_channels + src_y0 * src_stride;
787  const uint8* src_01 =
788  src_data + src_x0 * num_channels + src_y1 * src_stride;
789  const uint8* src_11 =
790  src_data + src_x1 * num_channels + src_y1 * src_stride;
791  for (int chan = 0; chan < num_channels; ++chan) {
792  dst_pixel[chan] = static_cast<uint8>(std::floor(
793  s0 * t0 * static_cast<float>(src_00[chan]) +
794  s1 * t0 * static_cast<float>(src_10[chan]) +
795  s0 * t1 * static_cast<float>(src_01[chan]) +
796  s1 * t1 * static_cast<float>(src_11[chan]) + 0.5f));
797  }
798  }
799  }
800  return result;
801 }
802 
805 static Range2f GetPixelRectIntersection(
806  float src_x, float src_y, const Range2f& src_rectf) {
807  Point2f min_pt = src_rectf.GetMinPoint();
808  Point2f max_pt = src_rectf.GetMaxPoint();
809  min_pt[0] = std::max(src_x, min_pt[0]);
810  min_pt[1] = std::max(src_y, min_pt[1]);
811  max_pt[0] = std::min(src_x + 1.f, max_pt[0]);
812  max_pt[1] = std::min(src_y + 1.f, max_pt[1]);
813  return Range2f(min_pt, max_pt);
814 }
815 
817 static float GetRectArea(const Range2f& rect) {
818  math::Vector2f size = rect.GetSize();
819  return size[0] * size[1];
820 }
821 
826 static const gfx::ImagePtr ResizeBoxFilter8bpc(
827  const gfx::Image& image, uint32 out_width, uint32 out_height,
828  bool is_wipeable, const base::AllocatorPtr& allocator) {
829  const int kMaxChannels = 4;
830  ImagePtr result;
831  const int num_channels = Image::GetNumComponentsForFormat(image.GetFormat());
832  DCHECK_LE(num_channels, kMaxChannels)
833  << "Unsupported number of channels for resize.";
834  result = AllocImage(
835  image.GetFormat(), out_width, out_height, is_wipeable, allocator);
836  Point2f rect_scale(
837  static_cast<float>(image.GetWidth()) / static_cast<float>(out_width),
838  static_cast<float>(image.GetHeight()) / static_cast<float>(out_height));
839  const uint8* src_data = image.GetData()->GetData<uint8>();
840  uint8* dst_data = result->GetData()->GetMutableData<uint8>();
841  const size_t src_stride = image.GetWidth() * num_channels;
842  const size_t dst_stride = out_width * num_channels;
843  float src_area = GetRectArea(Range2f(Point2f::Zero(), rect_scale));
844  for (uint32 dst_y = 0; dst_y < out_height; ++dst_y) {
845  for (uint32 dst_x = 0; dst_x < out_width; ++dst_x) {
847  float dst_xf = static_cast<float>(dst_x);
848  float dst_yf = static_cast<float>(dst_y);
849  Range2f src_rectf = Range2f(
850  Point2f(dst_xf, dst_yf) * rect_scale,
851  Point2f(dst_xf + 1.f, dst_yf + 1.f) * rect_scale);
853  Point2f src_min(
854  std::floor(src_rectf.GetMinPoint()[0]),
855  std::floor(src_rectf.GetMinPoint()[1]));
856  Point2f src_max(
857  std::ceil(src_rectf.GetMaxPoint()[0]),
858  std::ceil(src_rectf.GetMaxPoint()[1]));
859 
860 #if ION_DEBUG
861  float check_area_total = 0.f;
862 #endif
863  float dst_value[kMaxChannels] = { 0.f };
864  for (float src_y = src_min[1]; src_y < src_max[1]; ++src_y) {
865  for (float src_x = src_min[0]; src_x < src_max[0]; ++src_x) {
867  Range2f src_dest_intersect =
868  GetPixelRectIntersection(src_x, src_y, src_rectf);
869  float part_area = GetRectArea(src_dest_intersect);
870 #if ION_DEBUG
871  check_area_total += part_area;
872 #endif
873  size_t src_xi = static_cast<size_t>(src_x);
874  size_t src_yi = static_cast<size_t>(src_y);
875  const uint8* src_pixel =
876  src_data + src_yi * src_stride + src_xi * num_channels;
877  for (int chan = 0; chan < num_channels; ++chan) {
878  dst_value[chan] += part_area * static_cast<float>(src_pixel[chan]);
879  }
880  }
881  }
882 #if ION_DEBUG
883  DCHECK_LT(std::abs(check_area_total - src_area), 1e-3);
884 #endif
885  uint8* dst_pixel = dst_data + dst_y * dst_stride + dst_x * num_channels;
886  for (int chan = 0; chan < num_channels; ++chan) {
887  dst_pixel[chan] =
888  static_cast<uint8>(std::floor(dst_value[chan] / src_area + 0.5f));
889  }
890  }
891  }
892  return result;
893 }
894 
895 } // anonymous namespace
896 
898 
903 
904 
905 const ImagePtr ION_API ConvertImage(
906  const ImagePtr& image, Image::Format target_format, bool is_wipeable,
907  const base::AllocatorPtr& allocator,
908  const base::AllocatorPtr& temporary_allocator) {
909  if (!ImageHasData(image))
910  return ImagePtr();
911 
912  if (image->GetFormat() == target_format) {
913  if (image->GetData()->IsWipeable() == is_wipeable) {
914  return image;
915  } else {
917  ImagePtr result;
918  result.Reset(new(allocator) Image);
919  result->Set(
920  image->GetFormat(), image->GetWidth(), image->GetHeight(),
921  base::DataContainer::CreateAndCopy<uint8>(
922  reinterpret_cast<const uint8*>(image->GetData()->GetData()),
923  image->GetDataSize(), is_wipeable, result->GetAllocator()));
924  return result;
925  }
926  }
927 
928  const base::AllocatorPtr& al =
930  const base::AllocatorPtr& temp_al =
932  return ImageToImage(*image, target_format, is_wipeable, al, temp_al);
933 }
934 
936  const void* data, size_t data_size, bool flip_vertically, bool is_wipeable,
937  const base::AllocatorPtr& allocator) {
938  if (!data || data_size == 0)
939  return ImagePtr();
940 
944  const base::AllocatorPtr& al =
946  return DataToImage(data, data_size, flip_vertically, is_wipeable, al);
947 }
948 
949 bool ION_API IsIonRawImageFormat(const void* data, size_t data_size) {
950  const uint8* header_ui8 = static_cast<const uint8*>(data);
951  return data_size >= kIonRawImageHeaderSizeInBytes &&
952  header_ui8[0] == 0x89 && header_ui8[1] == 'R' &&
953  header_ui8[2] == 'A' && header_ui8[3] == 'W' /* const magic cues */ &&
954  ((header_ui8[4] == 0x00 && header_ui8[5] == 0x01) ||
955  (header_ui8[4] == 0x01 && header_ui8[5] == 0x00)) /* Endianness cue */;
956 }
957 
958 const std::vector<uint8> ION_API ConvertToExternalImageData(
959  const ImagePtr& image, ExternalImageFormat external_format,
960  bool flip_vertically) {
961  if (!ImageHasData(image))
962  return std::vector<uint8>();
963 
964  return ImageToData(*image, external_format, flip_vertically);
965 }
966 
968  const ImagePtr& image, bool is_wipeable,
969  const base::AllocatorPtr& allocator) {
970  ImagePtr result;
971  if (ImageHasData(image) &&
972  image->GetWidth() > 1U && image->GetHeight() > 1U) {
973  if (image->GetFormat() == Image::kEtc1) {
974  image_codec_compression::EtcCompressor compressor;
975  result = DownsampleWithCompressor(
976  *image, &compressor, is_wipeable, allocator);
977  } else if (image->GetFormat() == Image::kDxt1 ||
978  image->GetFormat() == Image::kDxt5) {
979  image_codec_compression::DxtcCompressor compressor;
980  result = DownsampleWithCompressor(
981  *image, &compressor, is_wipeable, allocator);
982  } else if (Image::Is8BitPerChannelFormat(image->GetFormat())) {
983  result = Downsample2xSimple8bpc(*image, is_wipeable, allocator);
984  } else {
985  LOG(WARNING) << "Downsampling image format "
986  << Image::GetFormatString(image->GetFormat())
987  << " not supported.";
988  }
989  }
990  return result;
991 }
992 
994  const gfx::ImagePtr& image, uint32 out_width, uint32 out_height,
995  bool is_wipeable, const base::AllocatorPtr& allocator) {
996  ImagePtr result;
997  if (!ImageHasData(image)) {
998  return result;
999  }
1000  if (!Image::Is8BitPerChannelFormat(image->GetFormat())) {
1001  LOG(WARNING) << "Resizing image format "
1002  << Image::GetFormatString(image->GetFormat())
1003  << " not supported.";
1004  return result;
1005  }
1006 
1007  uint32 image_width = image->GetWidth();
1008  uint32 image_height = image->GetHeight();
1009  if (out_width < image_width && out_height < image_height) {
1010  result = ResizeBoxFilter8bpc(
1011  *image, out_width, out_height, is_wipeable, allocator);
1012  } else {
1014  result = ResizeBilinear8bpc(
1015  *image, out_width, out_height, is_wipeable, allocator);
1016  }
1017  return result;
1018 }
1019 
1020 
1021 ION_API void FlipImage(const gfx::ImagePtr& image) {
1022  if (!ImageHasData(image) || image->GetHeight() <= 1U) {
1023  return;
1024  }
1025  if (image->IsCompressed()) {
1026  DLOG(WARNING) << "Flipping compressed images is not supported.";
1027  return;
1028  }
1029  const size_t height = image->GetHeight();
1030  base::DataContainerPtr data = image->GetData();
1031  uint8* image_bytes = data->GetMutableData<uint8>();
1032  const size_t row_size_bytes = image->GetDataSize() / height;
1033 
1037  const size_t kBufferSize = 512U;
1038  uint8 tmp_buf[kBufferSize];
1039  for (size_t j = 0, target_j = height - 1; j < height / 2; ++j, --target_j) {
1040  size_t copied_bytes = 0;
1041  while (copied_bytes < row_size_bytes) {
1042  const size_t copy_size =
1043  std::min(row_size_bytes - copied_bytes, kBufferSize);
1044  uint8* src_row = image_bytes + j * row_size_bytes + copied_bytes;
1045  uint8* tgt_row = image_bytes + target_j * row_size_bytes + copied_bytes;
1046  memcpy(tmp_buf, tgt_row, copy_size);
1047  memcpy(tgt_row, src_row, copy_size);
1048  memcpy(src_row, tmp_buf, copy_size);
1049  copied_bytes += kBufferSize;
1050  }
1051  }
1052 }
1053 
1055  if (!ImageHasData(image) || image->GetWidth() <= 1U) {
1056  return;
1057  }
1058  if (image->IsCompressed()) {
1059  DLOG(WARNING) << "Flipping compressed images is not supported.";
1060  return;
1061  }
1062  const size_t height = image->GetHeight();
1063  const size_t width = image->GetWidth();
1064  base::DataContainerPtr data = image->GetData();
1065  uint8* image_bytes = data->GetMutableData<uint8>();
1066  const size_t row_size_bytes = image->GetDataSize() / height;
1067  const size_t pixel_size_bytes = row_size_bytes / width;
1068 
1069  for (size_t row = 0; row < height; ++row) {
1070  const size_t row_start = row * row_size_bytes;
1071 
1072  for (size_t column = 0; column < width / 2; ++column) {
1073  for (size_t byte = 0; byte < pixel_size_bytes; ++byte) {
1074  const size_t left = column, right = width - 1 - column;
1075  std::swap(image_bytes[row_start + left * pixel_size_bytes + byte],
1076  image_bytes[row_start + right * pixel_size_bytes + byte]);
1077  }
1078  }
1079  }
1080 }
1081 
1083  if (!ImageHasData(image) || image->GetWidth() <= 1U) {
1084  return;
1085  }
1086 
1087  const size_t byte_count = image->GetDataSize();
1088  base::DataContainerPtr data = image->GetData();
1089  uint8* image_bytes = data->GetMutableData<uint8>();
1090 
1091  switch (image->GetFormat()) {
1092  case gfx::Image::kRgba8888:
1093  for (size_t i = 0; i < byte_count; i += 4) {
1094  const uint8 alpha_byte = image_bytes[i + 3];
1095  if (alpha_byte == 0)
1096  continue;
1097  const float inverse_alpha = 255.f / static_cast<float>(alpha_byte);
1098  image_bytes[i] = MultiplyUint8ByFloat(image_bytes[i], inverse_alpha);
1099  image_bytes[i + 1] =
1100  MultiplyUint8ByFloat(image_bytes[i + 1], inverse_alpha);
1101  image_bytes[i + 2] =
1102  MultiplyUint8ByFloat(image_bytes[i + 2], inverse_alpha);
1103  }
1104  break;
1105  default:
1106  DLOG(WARNING) << "Converting premultiplied alpha to straight alpha from"
1107  << " formats other than Rgba8888 is not supported.";
1108  }
1109 }
1110 
1111 } // namespace image
1112 } // namespace ion
uint32 GetWidth() const
Definition: image.h:236
bool ION_API IsIonRawImageFormat(const void *data, size_t data_size)
Returns true if "Ion raw" format header is detected in data.
const ImagePtr ION_API ConvertImage(const ImagePtr &image, Image::Format target_format, bool is_wipeable, const base::AllocatorPtr &allocator, const base::AllocatorPtr &temporary_allocator)
Public functions.
base::ReferentPtr< Image >::Type ImagePtr
Definition: image.h:29
#define DLOG(severity)
Same as LOG(severity), but only logs in debug mode.
Definition: logging.h:230
#define DCHECK(expr)
Definition: logging.h:331
Format GetFormat() const
Definition: image.h:233
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
Definition: logging.h:216
Range< 2, float > Range2f
Definition: range.h:373
ExternalImageFormat
External image formats supported by ConvertToExternalImageData().
const ImagePtr ION_API DownsampleImage2x(const ImagePtr &image, bool is_wipeable, const base::AllocatorPtr &allocator)
uint32 GetHeight() const
Definition: image.h:237
An Image represents 2D image data that can be used in a texture supplied to a shader.
Definition: image.h:35
T * Get() const
Returns a raw pointer to the instance, which may be NULL.
Definition: sharedptr.h:89
const ImagePtr ION_API ConvertFromExternalImageData(const void *data, size_t data_size, bool flip_vertically, bool is_wipeable, const base::AllocatorPtr &allocator)
Converts external image data to an ImagePtr with data in canonical format.
static const AllocatorPtr & GetNonNullAllocator(const AllocatorPtr &allocator)
This convenience function can be used where a non-NULL Allocator pointer is needed.
ION_API void StraightAlphaFromPremultipliedAlpha(const gfx::ImagePtr &image)
Converts a "pre-multiplied alpha" RGBA image into a "straight alpha" RGBA image.
ION_API void FlipImageHorizontally(const gfx::ImagePtr &image)
Flips an image horizontally in place.
const Grid & image
The original monochrome image data, as doubles (0 - 1).
Definition: sdfutils.cc:90
#define DCHECK_GE(val1, val2)
Definition: logging.h:336
static void AllocatorDeleter(AllocatorPtr allocator, void *data_to_delete)
A deleter for data allocated by an Allocator.
Definition: datacontainer.h:94
Copyright 2016 Google Inc.
int width
unsigned char * stbi_write_png_to_mem(unsigned char *, int, int, int, int, int *)
This function is not declared as extern in the header, but it is accessible.
#define DCHECK_LE(val1, val2)
Definition: logging.h:334
void * AllocateMemory(size_t size)
Allocates memory of the given size.
Definition: allocator.cc:32
const base::DataContainerPtr & GetData() const
Definition: image.h:240
FontImage::ImageData image_data
The wrapped ImageData instance.
Definition: fontimage.cc:84
A SharedPtr is a smart shared pointer to an instance of some class that implements reference counting...
Definition: sharedptr.h:60
const std::vector< uint8 > ION_API ConvertToExternalImageData(const ImagePtr &image, ExternalImageFormat external_format, bool flip_vertically)
ION_API void FlipImage(const gfx::ImagePtr &image)
Flips an image vertically in place.
const gfx::ImagePtr ResizeImage(const gfx::ImagePtr &image, uint32 out_width, uint32 out_height, bool is_wipeable, const base::AllocatorPtr &allocator)
Returns a copy of image scaled to the specified dimensions.
#define DCHECK_LT(val1, val2)
Definition: logging.h:335