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

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

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