Index: ios/web/public/image_fetcher/webp_decoder.mm |
diff --git a/ios/web/public/image_fetcher/webp_decoder.mm b/ios/web/public/image_fetcher/webp_decoder.mm |
deleted file mode 100644 |
index d5e7d923add86c93ae77a8ed9a6417e2673f0639..0000000000000000000000000000000000000000 |
--- a/ios/web/public/image_fetcher/webp_decoder.mm |
+++ /dev/null |
@@ -1,309 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#import "ios/web/public/image_fetcher/webp_decoder.h" |
- |
-#import <Foundation/Foundation.h> |
-#include <stdint.h> |
-#import <UIKit/UIKit.h> |
- |
-#include "base/logging.h" |
-#include "base/metrics/histogram_macros.h" |
- |
-#if !defined(__has_feature) || !__has_feature(objc_arc) |
-#error "This file requires ARC support." |
-#endif |
- |
-namespace { |
- |
-class WebpDecoderDelegate : public webp_transcode::WebpDecoder::Delegate { |
- public: |
- WebpDecoderDelegate() = default; |
- |
- NSData* data() const { return decoded_image_; } |
- |
- // WebpDecoder::Delegate methods |
- void OnFinishedDecoding(bool success) override { |
- if (!success) |
- decoded_image_ = nil; |
- } |
- |
- void SetImageFeatures( |
- size_t total_size, |
- webp_transcode::WebpDecoder::DecodedImageFormat format) override { |
- decoded_image_ = [[NSMutableData alloc] initWithCapacity:total_size]; |
- } |
- |
- void OnDataDecoded(NSData* data) override { |
- DCHECK(decoded_image_); |
- [decoded_image_ appendData:data]; |
- } |
- |
- private: |
- ~WebpDecoderDelegate() override {} |
- NSMutableData* decoded_image_; |
- |
- DISALLOW_COPY_AND_ASSIGN(WebpDecoderDelegate); |
-}; |
- |
-// Content-type header for WebP images. |
-const char kWEBPFirstMagicPattern[] = "RIFF"; |
-const char kWEBPSecondMagicPattern[] = "WEBP"; |
- |
-const uint8_t kNumIfdEntries = 15; |
-const unsigned int kExtraDataSize = 16; |
-// 10b for signature/header + n * 12b entries + 4b for IFD terminator: |
-const unsigned int kExtraDataOffset = 10 + 12 * kNumIfdEntries + 4; |
-const unsigned int kHeaderSize = kExtraDataOffset + kExtraDataSize; |
-const int kRecompressionThreshold = 64 * 64; // Threshold in pixels. |
-const CGFloat kJpegQuality = 0.85; |
- |
-// Adapted from libwebp example dwebp.c. |
-void PutLE16(uint8_t* const dst, uint32_t value) { |
- dst[0] = (value >> 0) & 0xff; |
- dst[1] = (value >> 8) & 0xff; |
-} |
- |
-void PutLE32(uint8_t* const dst, uint32_t value) { |
- PutLE16(dst + 0, (value >> 0) & 0xffff); |
- PutLE16(dst + 2, (value >> 16) & 0xffff); |
-} |
- |
-void WriteTiffHeader(uint8_t* dst, |
- int width, |
- int height, |
- int bytes_per_px, |
- bool has_alpha) { |
- // For non-alpha case, we omit tag 0x152 (ExtraSamples). |
- const uint8_t num_ifd_entries = |
- has_alpha ? kNumIfdEntries : kNumIfdEntries - 1; |
- uint8_t tiff_header[kHeaderSize] = { |
- 0x49, 0x49, 0x2a, 0x00, // little endian signature |
- 8, 0, 0, 0, // offset to the unique IFD that follows |
- // IFD (offset = 8). Entries must be written in increasing tag order. |
- num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each). |
- 0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD) |
- 0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD) |
- 0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888 |
- kExtraDataOffset + 0, 0, 0, 0, 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, |
- 0, // 46: Compression: none |
- 0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB |
- 0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset: |
- kHeaderSize, 0, 0, 0, // data follows header |
- 0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft |
- 0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels |
- bytes_per_px, 0, 0, 0, 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, |
- 0, // 106: Rows per strip (TBD) |
- 0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD) |
- 0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution |
- kExtraDataOffset + 8, 0, 0, 0, 0x1b, 0x01, 5, 0, 1, 0, 0, |
- 0, // 142: Y-resolution |
- kExtraDataOffset + 8, 0, 0, 0, 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, |
- 0, // 154: PlanarConfiguration |
- 0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch) |
- 0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA |
- 0, 0, 0, 0, // 190: IFD terminator |
- // kExtraDataOffset: |
- 8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample |
- 72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution |
- }; |
- |
- // Fill placeholders in IFD: |
- PutLE32(tiff_header + 10 + 8, width); |
- PutLE32(tiff_header + 22 + 8, height); |
- PutLE32(tiff_header + 106 + 8, height); |
- PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height); |
- if (!has_alpha) |
- PutLE32(tiff_header + 178, 0); |
- |
- memcpy(dst, tiff_header, kHeaderSize); |
-} |
- |
-} // namespace |
- |
-namespace webp_transcode { |
- |
-// static |
-NSData* WebpDecoder::DecodeWebpImage(NSData* webp_image) { |
- scoped_refptr<WebpDecoderDelegate> delegate(new WebpDecoderDelegate); |
- |
- scoped_refptr<webp_transcode::WebpDecoder> decoder( |
- new webp_transcode::WebpDecoder(delegate.get())); |
- |
- decoder->OnDataReceived(webp_image); |
- DLOG_IF(ERROR, !delegate->data()) << "WebP image decoding failed."; |
- return delegate->data(); |
-} |
- |
-// static |
-bool WebpDecoder::IsWebpImage(const std::string& image_data) { |
- if (image_data.length() < 12) |
- return false; |
- return image_data.compare(0, 4, kWEBPFirstMagicPattern) == 0 && |
- image_data.compare(8, 4, kWEBPSecondMagicPattern) == 0; |
-} |
- |
-// static |
-size_t WebpDecoder::GetHeaderSize() { |
- return kHeaderSize; |
-} |
- |
-WebpDecoder::WebpDecoder(WebpDecoder::Delegate* delegate) |
- : delegate_(delegate), state_(READING_FEATURES), has_alpha_(0) { |
- DCHECK(delegate_.get()); |
- const bool rv = WebPInitDecoderConfig(&config_); |
- DCHECK(rv); |
-} |
- |
-WebpDecoder::~WebpDecoder() { |
- WebPFreeDecBuffer(&config_.output); |
-} |
- |
-void WebpDecoder::OnDataReceived(NSData* data) { |
- DCHECK(data); |
- switch (state_) { |
- case READING_FEATURES: |
- DoReadFeatures(data); |
- break; |
- case READING_DATA: |
- DoReadData(data); |
- break; |
- case DONE: |
- DLOG(WARNING) << "Received WebP data but decoding is finished. Ignoring."; |
- break; |
- } |
-} |
- |
-void WebpDecoder::Stop() { |
- if (state_ != DONE) { |
- state_ = DONE; |
- DLOG(WARNING) << "Unexpected end of WebP data."; |
- delegate_->OnFinishedDecoding(false); |
- } |
-} |
- |
-void WebpDecoder::DoReadFeatures(NSData* data) { |
- DCHECK_EQ(READING_FEATURES, state_); |
- DCHECK(data); |
- if (features_) |
- [features_ appendData:data]; |
- else |
- features_.reset([[NSMutableData alloc] initWithData:data]); |
- VP8StatusCode status = |
- WebPGetFeatures(static_cast<const uint8_t*>([features_ bytes]), |
- [features_ length], &config_.input); |
- switch (status) { |
- case VP8_STATUS_OK: { |
- has_alpha_ = config_.input.has_alpha; |
- const uint32_t width = config_.input.width; |
- const uint32_t height = config_.input.height; |
- const size_t bytes_per_px = has_alpha_ ? 4 : 3; |
- const int stride = bytes_per_px * width; |
- const size_t image_data_size = stride * height; |
- const size_t total_size = image_data_size + kHeaderSize; |
- // Force pre-multiplied alpha. |
- config_.output.colorspace = has_alpha_ ? MODE_rgbA : MODE_RGB; |
- config_.output.u.RGBA.stride = stride; |
- // Create the output buffer. |
- config_.output.u.RGBA.size = image_data_size; |
- uint8_t* dst = static_cast<uint8_t*>(malloc(total_size)); |
- if (!dst) { |
- DLOG(ERROR) << "Could not allocate WebP decoding buffer (size = " |
- << total_size << ")."; |
- delegate_->OnFinishedDecoding(false); |
- state_ = DONE; |
- break; |
- } |
- WriteTiffHeader(dst, width, height, bytes_per_px, has_alpha_); |
- output_buffer_.reset([[NSData alloc] initWithBytesNoCopy:dst |
- length:total_size |
- freeWhenDone:YES]); |
- config_.output.is_external_memory = 1; |
- config_.output.u.RGBA.rgba = dst + kHeaderSize; |
- // Start decoding. |
- state_ = READING_DATA; |
- incremental_decoder_.reset(WebPINewDecoder(&config_.output)); |
- DoReadData(features_); |
- features_.reset(); |
- break; |
- } |
- case VP8_STATUS_NOT_ENOUGH_DATA: |
- // Do nothing. |
- break; |
- default: |
- DLOG(ERROR) << "Error in WebP image features."; |
- delegate_->OnFinishedDecoding(false); |
- state_ = DONE; |
- break; |
- } |
-} |
- |
-void WebpDecoder::DoReadData(NSData* data) { |
- DCHECK_EQ(READING_DATA, state_); |
- DCHECK(incremental_decoder_); |
- DCHECK(data); |
- VP8StatusCode status = |
- WebPIAppend(incremental_decoder_.get(), |
- static_cast<const uint8_t*>([data bytes]), [data length]); |
- switch (status) { |
- case VP8_STATUS_SUSPENDED: |
- // Do nothing: re-compression to JPEG or PNG cannot be done incrementally. |
- // Wait for the whole image to be decoded. |
- break; |
- case VP8_STATUS_OK: { |
- bool rv = DoSendData(); |
- DLOG_IF(ERROR, !rv) << "Error in WebP image conversion."; |
- state_ = DONE; |
- delegate_->OnFinishedDecoding(rv); |
- break; |
- } |
- default: |
- DLOG(ERROR) << "Error in WebP image decoding."; |
- delegate_->OnFinishedDecoding(false); |
- state_ = DONE; |
- break; |
- } |
-} |
- |
-bool WebpDecoder::DoSendData() { |
- DCHECK_EQ(READING_DATA, state_); |
- int width, height; |
- uint8_t* data_ptr = WebPIDecGetRGB(incremental_decoder_.get(), nullptr, |
- &width, &height, nullptr); |
- if (!data_ptr) |
- return false; |
- DCHECK_EQ(static_cast<const uint8_t*>([output_buffer_ bytes]) + kHeaderSize, |
- data_ptr); |
- base::scoped_nsobject<NSData> result_data; |
- // When the WebP image is larger than |kRecompressionThreshold| it is |
- // compressed to JPEG or PNG. Otherwise, the uncompressed TIFF is used. |
- DecodedImageFormat format = TIFF; |
- if (width * height > kRecompressionThreshold) { |
- base::scoped_nsobject<UIImage> tiff_image( |
- [[UIImage alloc] initWithData:output_buffer_]); |
- if (!tiff_image) |
- return false; |
- // Compress to PNG if the image is transparent, JPEG otherwise. |
- // TODO(droger): Use PNG instead of JPEG if the WebP image is lossless. |
- if (has_alpha_) { |
- result_data.reset(UIImagePNGRepresentation(tiff_image)); |
- format = PNG; |
- } else { |
- result_data.reset(UIImageJPEGRepresentation(tiff_image, kJpegQuality)); |
- format = JPEG; |
- } |
- if (!result_data) |
- return false; |
- } else { |
- result_data.reset(output_buffer_); |
- } |
- UMA_HISTOGRAM_ENUMERATION("WebP.DecodedImageFormat", format, |
- DECODED_FORMAT_COUNT); |
- delegate_->SetImageFeatures([result_data length], format); |
- delegate_->OnDataDecoded(result_data); |
- output_buffer_.reset(); |
- return true; |
-} |
- |
-} // namespace webp_transcode |