webp_decoder.mm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "components/image_fetcher/ios/webp_decoder.h"
  5. #import <Foundation/Foundation.h>
  6. #import <UIKit/UIKit.h>
  7. #include <stdint.h>
  8. #include "base/logging.h"
  9. #if !defined(__has_feature) || !__has_feature(objc_arc)
  10. #error "This file requires ARC support."
  11. #endif
  12. namespace {
  13. class WebpDecoderDelegate : public webp_transcode::WebpDecoder::Delegate {
  14. public:
  15. WebpDecoderDelegate() = default;
  16. WebpDecoderDelegate(const WebpDecoderDelegate&) = delete;
  17. WebpDecoderDelegate& operator=(const WebpDecoderDelegate&) = delete;
  18. NSData* data() const { return decoded_image_; }
  19. // WebpDecoder::Delegate methods
  20. void OnFinishedDecoding(bool success) override {
  21. if (!success)
  22. decoded_image_ = nil;
  23. }
  24. void SetImageFeatures(
  25. size_t total_size,
  26. webp_transcode::WebpDecoder::DecodedImageFormat format) override {
  27. decoded_image_ = [[NSMutableData alloc] initWithCapacity:total_size];
  28. }
  29. void OnDataDecoded(NSData* data) override {
  30. DCHECK(decoded_image_);
  31. [decoded_image_ appendData:data];
  32. }
  33. private:
  34. ~WebpDecoderDelegate() override {}
  35. NSMutableData* decoded_image_;
  36. };
  37. // Content-type header for WebP images.
  38. const char kWEBPFirstMagicPattern[] = "RIFF";
  39. const char kWEBPSecondMagicPattern[] = "WEBP";
  40. const uint8_t kNumIfdEntries = 15;
  41. const unsigned int kExtraDataSize = 16;
  42. // 10b for signature/header + n * 12b entries + 4b for IFD terminator:
  43. const unsigned int kExtraDataOffset = 10 + 12 * kNumIfdEntries + 4;
  44. const unsigned int kHeaderSize = kExtraDataOffset + kExtraDataSize;
  45. const int kRecompressionThreshold = 64 * 64; // Threshold in pixels.
  46. const CGFloat kJpegQuality = 0.85;
  47. // Adapted from libwebp example dwebp.c.
  48. void PutLE16(uint8_t* const dst, uint32_t value) {
  49. dst[0] = (value >> 0) & 0xff;
  50. dst[1] = (value >> 8) & 0xff;
  51. }
  52. void PutLE32(uint8_t* const dst, uint32_t value) {
  53. PutLE16(dst + 0, (value >> 0) & 0xffff);
  54. PutLE16(dst + 2, (value >> 16) & 0xffff);
  55. }
  56. void WriteTiffHeader(uint8_t* dst,
  57. int width,
  58. int height,
  59. int bytes_per_px,
  60. bool has_alpha) {
  61. // For non-alpha case, we omit tag 0x152 (ExtraSamples).
  62. const uint8_t num_ifd_entries =
  63. has_alpha ? kNumIfdEntries : kNumIfdEntries - 1;
  64. uint8_t bytes_per_px_u8 = bytes_per_px;
  65. uint8_t tiff_header[kHeaderSize] = {
  66. 0x49, 0x49, 0x2a, 0x00, // little endian signature
  67. 8, 0, 0, 0, // offset to the unique IFD that follows
  68. // IFD (offset = 8). Entries must be written in increasing tag order.
  69. num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
  70. 0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
  71. 0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
  72. 0x02, 0x01, 3, 0, bytes_per_px_u8, 0, 0, 0, // 34: BitsPerSample: 8888
  73. kExtraDataOffset + 0, 0, 0, 0, 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0,
  74. 0, // 46: Compression: none
  75. 0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
  76. 0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
  77. kHeaderSize, 0, 0, 0, // data follows header
  78. 0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
  79. 0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
  80. bytes_per_px_u8, 0, 0, 0, 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0,
  81. 0, // 106: Rows per strip (TBD)
  82. 0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
  83. 0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
  84. kExtraDataOffset + 8, 0, 0, 0, 0x1b, 0x01, 5, 0, 1, 0, 0,
  85. 0, // 142: Y-resolution
  86. kExtraDataOffset + 8, 0, 0, 0, 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0,
  87. 0, // 154: PlanarConfiguration
  88. 0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
  89. 0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
  90. 0, 0, 0, 0, // 190: IFD terminator
  91. // kExtraDataOffset:
  92. 8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
  93. 72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
  94. };
  95. // Fill placeholders in IFD:
  96. PutLE32(tiff_header + 10 + 8, width);
  97. PutLE32(tiff_header + 22 + 8, height);
  98. PutLE32(tiff_header + 106 + 8, height);
  99. PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
  100. if (!has_alpha)
  101. PutLE32(tiff_header + 178, 0);
  102. memcpy(dst, tiff_header, kHeaderSize);
  103. }
  104. } // namespace
  105. namespace webp_transcode {
  106. // static
  107. NSData* WebpDecoder::DecodeWebpImage(NSData* webp_image) {
  108. scoped_refptr<WebpDecoderDelegate> delegate(new WebpDecoderDelegate);
  109. scoped_refptr<webp_transcode::WebpDecoder> decoder(
  110. new webp_transcode::WebpDecoder(delegate.get()));
  111. decoder->OnDataReceived(webp_image);
  112. DLOG_IF(ERROR, !delegate->data()) << "WebP image decoding failed.";
  113. return delegate->data();
  114. }
  115. // static
  116. bool WebpDecoder::IsWebpImage(const std::string& image_data) {
  117. if (image_data.length() < 12)
  118. return false;
  119. return image_data.compare(0, 4, kWEBPFirstMagicPattern) == 0 &&
  120. image_data.compare(8, 4, kWEBPSecondMagicPattern) == 0;
  121. }
  122. // static
  123. size_t WebpDecoder::GetHeaderSize() {
  124. return kHeaderSize;
  125. }
  126. WebpDecoder::WebpDecoder(WebpDecoder::Delegate* delegate)
  127. : delegate_(delegate), state_(READING_FEATURES), has_alpha_(0) {
  128. DCHECK(delegate_.get());
  129. const bool rv = WebPInitDecoderConfig(&config_);
  130. DCHECK(rv);
  131. }
  132. WebpDecoder::~WebpDecoder() {
  133. WebPFreeDecBuffer(&config_.output);
  134. }
  135. void WebpDecoder::OnDataReceived(NSData* data) {
  136. DCHECK(data);
  137. switch (state_) {
  138. case READING_FEATURES:
  139. DoReadFeatures(data);
  140. break;
  141. case READING_DATA:
  142. DoReadData(data);
  143. break;
  144. case DONE:
  145. DLOG(WARNING) << "Received WebP data but decoding is finished. Ignoring.";
  146. break;
  147. }
  148. }
  149. void WebpDecoder::Stop() {
  150. if (state_ != DONE) {
  151. state_ = DONE;
  152. DLOG(WARNING) << "Unexpected end of WebP data.";
  153. delegate_->OnFinishedDecoding(false);
  154. }
  155. }
  156. void WebpDecoder::DoReadFeatures(NSData* data) {
  157. DCHECK_EQ(READING_FEATURES, state_);
  158. DCHECK(data);
  159. if (features_)
  160. [features_ appendData:data];
  161. else
  162. features_ = [[NSMutableData alloc] initWithData:data];
  163. VP8StatusCode status =
  164. WebPGetFeatures(static_cast<const uint8_t*>([features_ bytes]),
  165. [features_ length], &config_.input);
  166. switch (status) {
  167. case VP8_STATUS_OK: {
  168. has_alpha_ = config_.input.has_alpha;
  169. const uint32_t width = config_.input.width;
  170. const uint32_t height = config_.input.height;
  171. const size_t bytes_per_px = has_alpha_ ? 4 : 3;
  172. const int stride = bytes_per_px * width;
  173. const size_t image_data_size = stride * height;
  174. const size_t total_size = image_data_size + kHeaderSize;
  175. // Force pre-multiplied alpha.
  176. config_.output.colorspace = has_alpha_ ? MODE_rgbA : MODE_RGB;
  177. config_.output.u.RGBA.stride = stride;
  178. // Create the output buffer.
  179. config_.output.u.RGBA.size = image_data_size;
  180. uint8_t* dst = static_cast<uint8_t*>(malloc(total_size));
  181. if (!dst) {
  182. DLOG(ERROR) << "Could not allocate WebP decoding buffer (size = "
  183. << total_size << ").";
  184. delegate_->OnFinishedDecoding(false);
  185. state_ = DONE;
  186. break;
  187. }
  188. WriteTiffHeader(dst, width, height, bytes_per_px, has_alpha_);
  189. output_buffer_ = [[NSData alloc] initWithBytesNoCopy:dst
  190. length:total_size
  191. freeWhenDone:YES];
  192. config_.output.is_external_memory = 1;
  193. config_.output.u.RGBA.rgba = dst + kHeaderSize;
  194. // Start decoding.
  195. state_ = READING_DATA;
  196. incremental_decoder_.reset(WebPINewDecoder(&config_.output));
  197. DoReadData(features_);
  198. features_ = nil;
  199. break;
  200. }
  201. case VP8_STATUS_NOT_ENOUGH_DATA:
  202. // Do nothing.
  203. break;
  204. default:
  205. DLOG(ERROR) << "Error in WebP image features.";
  206. delegate_->OnFinishedDecoding(false);
  207. state_ = DONE;
  208. break;
  209. }
  210. }
  211. void WebpDecoder::DoReadData(NSData* data) {
  212. DCHECK_EQ(READING_DATA, state_);
  213. DCHECK(incremental_decoder_);
  214. DCHECK(data);
  215. VP8StatusCode status =
  216. WebPIAppend(incremental_decoder_.get(),
  217. static_cast<const uint8_t*>([data bytes]), [data length]);
  218. switch (status) {
  219. case VP8_STATUS_SUSPENDED:
  220. // Do nothing: re-compression to JPEG or PNG cannot be done incrementally.
  221. // Wait for the whole image to be decoded.
  222. break;
  223. case VP8_STATUS_OK: {
  224. bool rv = DoSendData();
  225. DLOG_IF(ERROR, !rv) << "Error in WebP image conversion.";
  226. state_ = DONE;
  227. delegate_->OnFinishedDecoding(rv);
  228. break;
  229. }
  230. default:
  231. DLOG(ERROR) << "Error in WebP image decoding.";
  232. delegate_->OnFinishedDecoding(false);
  233. state_ = DONE;
  234. break;
  235. }
  236. }
  237. bool WebpDecoder::DoSendData() {
  238. DCHECK_EQ(READING_DATA, state_);
  239. int width, height;
  240. uint8_t* data_ptr = WebPIDecGetRGB(incremental_decoder_.get(), nullptr,
  241. &width, &height, nullptr);
  242. if (!data_ptr)
  243. return false;
  244. DCHECK_EQ(static_cast<const uint8_t*>([output_buffer_ bytes]) + kHeaderSize,
  245. data_ptr);
  246. NSData* result_data = nil;
  247. // When the WebP image is larger than |kRecompressionThreshold| it is
  248. // compressed to JPEG or PNG. Otherwise, the uncompressed TIFF is used.
  249. DecodedImageFormat format = TIFF;
  250. if (width * height > kRecompressionThreshold) {
  251. UIImage* tiff_image = [[UIImage alloc] initWithData:output_buffer_];
  252. if (!tiff_image)
  253. return false;
  254. // Compress to PNG if the image is transparent, JPEG otherwise.
  255. // TODO(droger): Use PNG instead of JPEG if the WebP image is lossless.
  256. if (has_alpha_) {
  257. result_data = UIImagePNGRepresentation(tiff_image);
  258. format = PNG;
  259. } else {
  260. result_data = UIImageJPEGRepresentation(tiff_image, kJpegQuality);
  261. format = JPEG;
  262. }
  263. if (!result_data)
  264. return false;
  265. } else {
  266. result_data = output_buffer_;
  267. }
  268. delegate_->SetImageFeatures([result_data length], format);
  269. delegate_->OnDataDecoded(result_data);
  270. output_buffer_ = nil;
  271. return true;
  272. }
  273. } // namespace webp_transcode