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
42 #include "third_party/stblib/stb_image.h"
43 #include "third_party/stblib/stb_image_write.h"
47 unsigned char*,
int,
int,
int,
int,
int*);
61 const size_t kIonRawImageHeaderSizeInBytes = 16;
76 return data.
Get() != NULL && data->GetData() != NULL;
80 static bool ImageHasAlpha(
const Image& image) {
81 return Image::GetNumComponentsForFormat(image.GetFormat()) == 4;
94 static const ImagePtr CompressWithCompressor(
95 const Image& image, Image::Format compressed_format,
96 image_codec_compression::Compressor* compressor,
bool is_wipeable,
98 using image_codec_compression::CompressedImage;
99 const CompressedImage::Format format =
100 ImageHasAlpha(image) ? CompressedImage::kRGBA : CompressedImage::kRGB;
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()));
122 static const ImagePtr DecompressWithCompressor(
123 const Image& image, image_codec_compression::Compressor* compressor,
125 using image_codec_compression::CompressedImage;
128 CompressedImage::Format compressed_format;
129 Image::Format decompressed_format;
130 if (ImageHasAlpha(image)) {
131 compressed_format = CompressedImage::kRGBA;
132 decompressed_format = Image::kRgba8888;
134 compressed_format = CompressedImage::kRGB;
135 decompressed_format = Image::kRgb888;
137 const uint32
width = image.GetWidth();
138 const uint32 height = image.GetHeight();
141 const void*
image_data = image.GetData()->GetData();
143 image_codec_compression::CompressedImage compressed_image(
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));
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()));
166 static const ImagePtr CompressImage(
const Image& image,
167 Image::Format target_format,
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);
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);
189 static const ImagePtr DecompressImage(
const Image& image,
bool is_wipeable,
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);
197 DCHECK(format == Image::kDxt1 || format == Image::kDxt5);
198 image_codec_compression::DxtcCompressor compressor;
199 return DecompressWithCompressor(image, &compressor, is_wipeable, allocator);
203 static bool PngHasTransparencyChunk(
const uint8* png_data,
size_t data_size) {
204 const size_t kFirstChunk = 33;
205 const unsigned char* chunk = &png_data[kFirstChunk];
206 const uint8* end_ptr = png_data + data_size;
208 bool has_trns_chunk =
false;
209 while (chunk < end_ptr) {
210 if (lodepng_chunk_type_equals(chunk,
"IEND")) {
213 }
else if (lodepng_chunk_type_equals(chunk,
"tRNS")) {
214 has_trns_chunk =
true;
217 chunk = lodepng_chunk_next_const(chunk);
219 return has_trns_chunk;
224 static const ImagePtr DataToImageLodePng(
const void* data,
226 bool flip_vertically,
230 uint32
width = 0, height = 0;
231 const uint8* data_in =
static_cast<const uint8*
>(data);
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);
241 uint8* data_out = NULL;
243 LodePNGColorType colortype = state.info_png.color.colortype;
244 if (colortype == LCT_PALETTE) {
245 colortype = PngHasTransparencyChunk(data_in, data_size) ?
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;
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;
262 format = Image::kRgba8888;
266 format = Image::kRgb888;
270 format = Image::kLuminanceAlpha;
274 format = Image::kLuminance;
278 DCHECK(
false) <<
"Unexpected PNG color type";
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()));
287 if (flip_vertically) {
297 static const ImagePtr DataToImageStb(
const void* data,
299 bool flip_vertically,
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] = {
310 Image::kLuminanceAlpha,
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);
324 if (flip_vertically) {
332 static const ImagePtr DataToImageIonRaw(
const void* data,
334 bool flip_vertically,
341 const uint16* header_ui16 =
static_cast<const uint16*
>(data);
342 const uint32* header_ui32 =
static_cast<const uint32*
>(data);
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;
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;
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) {
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()));
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]);
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]);
388 DCHECK(
false) <<
"Byte swap not supported yet for num_bytes_per_pixel = "
389 << num_bytes_per_pixel;
393 if (flip_vertically) {
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);
414 Image::Format format, uint32
width, uint32 height,
bool is_wipeable,
416 ImagePtr result(
new(allocator) Image);
420 const size_t size = Image::ComputeDataSize(format, width, height);
421 uint8* new_buffer =
reinterpret_cast<uint8*
>(
423 result->Set(format, width, height,
424 base::DataContainer::Create<uint8>(
427 std::placeholders::_1),
434 static const ImagePtr ExtractRedChannel(
const Image& image,
437 DCHECK(image.GetFormat() == Image::kRgb888 ||
438 image.GetFormat() == Image::kRgba8888);
439 const uint32 width = image.GetWidth();
440 const uint32 height = image.GetHeight();
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;
456 static const ImagePtr LuminanceToRgb(
const Image& image,
457 Image::Format target_format,
460 DCHECK(image.GetFormat() == Image::kLuminance ||
461 image.GetFormat() == Image::kLuminanceAlpha);
462 DCHECK(target_format == Image::kRgb888 ||
463 target_format == Image::kRgba8888);
465 const uint32 width = image.GetWidth();
466 const uint32 height = image.GetHeight();
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;
481 if (dst_alpha && src_alpha) {
482 *dst_data++ = *src_data++;
483 }
else if (dst_alpha && !src_alpha) {
485 }
else if (!dst_alpha && src_alpha) {
494 static const ImagePtr ImageToImage(
const Image& image,
495 Image::Format target_format,
499 const Image::Format source_format = image.GetFormat();
504 switch (source_format) {
507 if (target_format == Image::kRgb888)
508 result = DecompressImage(image, is_wipeable, allocator);
512 if (target_format == Image::kRgba8888)
513 result = DecompressImage(image, is_wipeable, allocator);
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);
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);
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);
543 case Image::kPvrtc1Rgb2:
544 case Image::kPvrtc1Rgb4:
545 case Image::kPvrtc1Rgba2:
546 case Image::kPvrtc1Rgba4:
548 case Image::kRgba4444:
549 case Image::kRgba5551:
557 Image::Format canonical_format = target_format;
558 switch (Image::GetNumComponentsForFormat(source_format)) {
560 canonical_format = Image::kLuminance;
563 canonical_format = Image::kLuminanceAlpha;
566 canonical_format = Image::kRgb888;
569 canonical_format = Image::kRgba8888;
574 if (source_format != canonical_format && canonical_format != target_format)
576 ImageToImage(image, canonical_format, is_wipeable,
577 temp_allocator, temp_allocator),
578 target_format, is_wipeable, allocator, temp_allocator);
590 static const ImagePtr DataToImage(
const void* data,
size_t data_size,
591 bool flip_vertically,
bool is_wipeable,
595 ImagePtr image = DataToImageLodePng(
596 data, data_size, flip_vertically, is_wipeable, allocator);
597 if (image.Get() != NULL) {
601 image = DataToImageStb(
602 data, data_size, flip_vertically, is_wipeable, allocator);
603 if (image.Get() != NULL) {
607 return DataToImageIonRaw(
608 data, data_size, flip_vertically, is_wipeable, allocator);
613 static const std::vector<uint8> ImageToData(
615 bool flip_vertically) {
616 std::vector<uint8> result;
619 if (external_format ==
kPng) {
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>(),
630 image.GetAllocator()));
632 image_data =
const_cast<uint8*
>(
633 flipped_image->GetData()->GetData<uint8>());
635 image_data =
const_cast<uint8*
>(image.GetData()->GetData<uint8>());
640 image_data, 0, image.GetWidth(), image.GetHeight(),
641 Image::GetNumComponentsForFormat(image.GetFormat()),
643 result = std::vector<uint8>(result_data, result_data + num_bytes);
644 stbi_image_free(result_data);
653 static const ImagePtr DownsampleWithCompressor(
655 image_codec_compression::Compressor* compressor,
658 using image_codec_compression::CompressedImage;
659 const CompressedImage::Format format = image.GetFormat() == Image::kDxt5 ?
660 CompressedImage::kRGBA : CompressedImage::kRGB;
662 const uint32 width = image.GetWidth();
663 const uint32 height = image.GetHeight();
666 const void*
image_data = image.GetData()->GetData();
668 image_codec_compression::CompressedImage compressed_image(
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));
678 CompressedImage downsampled_image;
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()));
698 static const ImagePtr Downsample2xSimple8bpc(
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;
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;
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) {
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);
743 const gfx::Image& image, uint32 out_width, uint32 out_height,
746 image.
GetFormat(), out_width, out_height, is_wipeable, allocator);
748 static_cast<float>(image.
GetWidth()) / static_cast<float>(out_width);
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;
774 static_cast<size_t>(std::max(0.f, std::floor(src_x - 0.5f)));
776 static_cast<size_t>(std::max(0.f, std::floor(src_y - 0.5f)));
777 size_t src_x1 = std::min(
779 static_cast<size_t>(std::floor(src_x + 0.5f)));
780 size_t src_y1 = std::min(
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));
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);
817 static float GetRectArea(
const Range2f& rect) {
818 math::Vector2f size = rect.GetSize();
819 return size[0] * size[1];
827 const gfx::Image& image, uint32 out_width, uint32 out_height,
829 const int kMaxChannels = 4;
831 const int num_channels = Image::GetNumComponentsForFormat(image.
GetFormat());
833 <<
"Unsupported number of channels for resize.";
835 image.
GetFormat(), out_width, out_height, is_wipeable, allocator);
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);
850 Point2f(dst_xf, dst_yf) * rect_scale,
851 Point2f(dst_xf + 1.f, dst_yf + 1.f) * rect_scale);
854 std::floor(src_rectf.GetMinPoint()[0]),
855 std::floor(src_rectf.GetMinPoint()[1]));
857 std::ceil(src_rectf.GetMaxPoint()[0]),
858 std::ceil(src_rectf.GetMaxPoint()[1]));
861 float check_area_total = 0.f;
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) {
868 GetPixelRectIntersection(src_x, src_y, src_rectf);
869 float part_area = GetRectArea(src_dest_intersect);
871 check_area_total += part_area;
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]);
883 DCHECK_LT(std::abs(check_area_total - src_area), 1e-3);
885 uint8* dst_pixel = dst_data + dst_y * dst_stride + dst_x * num_channels;
886 for (
int chan = 0; chan < num_channels; ++chan) {
888 static_cast<uint8
>(std::floor(dst_value[chan] / src_area + 0.5f));
906 const ImagePtr&
image, Image::Format target_format,
bool is_wipeable,
909 if (!ImageHasData(image))
912 if (image->GetFormat() == target_format) {
913 if (image->GetData()->IsWipeable() == is_wipeable) {
918 result.Reset(
new(allocator) Image);
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()));
932 return ImageToImage(*image, target_format, is_wipeable, al, temp_al);
936 const void* data,
size_t data_size,
bool flip_vertically,
bool is_wipeable,
938 if (!data || data_size == 0)
946 return DataToImage(data, data_size, flip_vertically, is_wipeable, al);
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' &&
954 ((header_ui8[4] == 0x00 && header_ui8[5] == 0x01) ||
955 (header_ui8[4] == 0x01 && header_ui8[5] == 0x00)) ;
960 bool flip_vertically) {
961 if (!ImageHasData(image))
962 return std::vector<uint8>();
964 return ImageToData(*image, external_format, flip_vertically);
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);
986 << Image::GetFormatString(image->GetFormat())
987 <<
" not supported.";
997 if (!ImageHasData(image)) {
1000 if (!Image::Is8BitPerChannelFormat(image->GetFormat())) {
1002 << Image::GetFormatString(image->GetFormat())
1003 <<
" not supported.";
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);
1014 result = ResizeBilinear8bpc(
1015 *image, out_width, out_height, is_wipeable, allocator);
1022 if (!ImageHasData(image) || image->GetHeight() <= 1U) {
1025 if (image->IsCompressed()) {
1026 DLOG(
WARNING) <<
"Flipping compressed images is not supported.";
1029 const size_t height = image->GetHeight();
1031 uint8* image_bytes = data->GetMutableData<uint8>();
1032 const size_t row_size_bytes = image->GetDataSize() / height;
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;
1055 if (!ImageHasData(image) || image->GetWidth() <= 1U) {
1058 if (image->IsCompressed()) {
1059 DLOG(
WARNING) <<
"Flipping compressed images is not supported.";
1062 const size_t height = image->GetHeight();
1063 const size_t width = image->GetWidth();
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;
1069 for (
size_t row = 0; row < height; ++row) {
1070 const size_t row_start = row * row_size_bytes;
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]);
1083 if (!ImageHasData(image) || image->GetWidth() <= 1U) {
1087 const size_t byte_count = image->GetDataSize();
1089 uint8* image_bytes = data->GetMutableData<uint8>();
1091 switch (image->GetFormat()) {
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)
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);
1106 DLOG(
WARNING) <<
"Converting premultiplied alpha to straight alpha from"
1107 <<
" formats other than Rgba8888 is not supported.";
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
#define DLOG(severity)
Same as LOG(severity), but only logs in debug mode.
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
Range< 2, float > Range2f
ExternalImageFormat
External image formats supported by ConvertToExternalImageData().
const ImagePtr ION_API DownsampleImage2x(const ImagePtr &image, bool is_wipeable, const base::AllocatorPtr &allocator)
An Image represents 2D image data that can be used in a texture supplied to a shader.
T * Get() const
Returns a raw pointer to the instance, which may be NULL.
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).
#define DCHECK_GE(val1, val2)
static void AllocatorDeleter(AllocatorPtr allocator, void *data_to_delete)
A deleter for data allocated by an Allocator.
Copyright 2016 Google Inc.
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)
void * AllocateMemory(size_t size)
Allocates memory of the given size.
const base::DataContainerPtr & GetData() const
FontImage::ImageData image_data
The wrapped ImageData instance.
A SharedPtr is a smart shared pointer to an instance of some class that implements reference counting...
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)