Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Side by Side Diff: ios/chrome/browser/webp_transcode/webp_decoder.mm

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

Powered by Google App Engine
This is Rietveld 408576698