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

Side by Side Diff: ios/web/public/webp_decoder_unittest.mm

Issue 771723002: Add support for decoding WebP images (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@chrome-browser-state
Patch Set: Rebase Created 6 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
« no previous file with comments | « ios/web/public/webp_decoder.mm ('k') | ios/web/test/data/test.jpg » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012 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/web/public/webp_decoder.h"
6
7 #import <CoreGraphics/CoreGraphics.h>
8 #import <Foundation/Foundation.h>
9
10 #include "base/base_paths.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/mac/scoped_nsobject.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/path_service.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace web {
22 namespace {
23
24 class WebpDecoderDelegate : public WebpDecoder::Delegate {
25 public:
26 WebpDecoderDelegate() : image_([[NSMutableData alloc] init]) {
27 }
28
29 NSData* GetImage() const { return image_; }
30
31 // WebpDecoder::Delegate methods.
32 MOCK_METHOD1(OnFinishedDecoding, void(bool success));
33 MOCK_METHOD2(SetImageFeatures,
34 void(size_t total_size, WebpDecoder::DecodedImageFormat format));
35 virtual void OnDataDecoded(NSData* data) { [image_ appendData:data]; }
36
37 private:
38 virtual ~WebpDecoderDelegate() {}
39
40 base::scoped_nsobject<NSMutableData> image_;
41 };
42
43 class WebpDecoderTest : public testing::Test {
44 public:
45 WebpDecoderTest()
46 : delegate_(new WebpDecoderDelegate),
47 decoder_(new WebpDecoder(delegate_.get())) {}
48
49 NSData* LoadImage(const base::FilePath& filename) {
50 base::FilePath path;
51 PathService::Get(base::DIR_SOURCE_ROOT, &path);
52 path = path.AppendASCII("ios/web/test/data").Append(filename);
53 return
54 [NSData dataWithContentsOfFile:base::SysUTF8ToNSString(path.value())];
55 }
56
57 std::vector<uint8_t>* DecompressData(NSData* data,
58 WebpDecoder::DecodedImageFormat format) {
59 base::ScopedCFTypeRef<CGDataProviderRef> provider(
60 CGDataProviderCreateWithCFData((CFDataRef)data));
61 base::ScopedCFTypeRef<CGImageRef> image;
62 switch (format) {
63 case WebpDecoder::JPEG:
64 image.reset(CGImageCreateWithJPEGDataProvider(
65 provider, NULL, false, kCGRenderingIntentDefault));
66 break;
67 case WebpDecoder::PNG:
68 image.reset(CGImageCreateWithPNGDataProvider(
69 provider, NULL, false, kCGRenderingIntentDefault));
70 break;
71 case WebpDecoder::TIFF:
72 ADD_FAILURE() << "Data already decompressed";
73 return nil;
74 case WebpDecoder::DECODED_FORMAT_COUNT:
75 ADD_FAILURE() << "Unknown format";
76 return nil;
77 }
78 size_t width = CGImageGetWidth(image);
79 size_t height = CGImageGetHeight(image);
80 base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
81 CGColorSpaceCreateDeviceRGB());
82 size_t bytes_per_pixel = 4;
83 size_t bytes_per_row = bytes_per_pixel * width;
84 size_t bits_per_component = 8;
85 std::vector<uint8_t>* result =
86 new std::vector<uint8_t>(width * height * bytes_per_pixel, 0);
87 base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
88 &result->front(), width, height, bits_per_component, bytes_per_row,
89 color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big));
90 CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
91 // Check that someting has been written in |result|.
92 std::vector<uint8_t> zeroes(width * height * bytes_per_pixel, 0);
93 EXPECT_NE(0, memcmp(&result->front(), &zeroes.front(), zeroes.size()))
94 << "Decompression failed.";
95 return result;
96 }
97
98 // Compares data, allowing an averaged absolute difference of 1.
99 bool CompareUncompressedData(const uint8_t* ptr_1,
100 const uint8_t* ptr_2,
101 size_t size) {
102 uint64_t difference = 0;
103 for (size_t i = 0; i < size; ++i) {
104 // Casting to int to avoid overflow.
105 int error = abs(int(ptr_1[i]) - int(ptr_2[i]));
106 EXPECT_GE(difference + error, difference)
107 << "Image difference too big (overflow).";
108 difference += error;
109 }
110 double average_difference = double(difference) / double(size);
111 DLOG(INFO) << "Average image difference: " << average_difference;
112 return average_difference < 1.5;
113 }
114
115 bool CheckCompressedImagesEqual(NSData* data_1,
116 NSData* data_2,
117 WebpDecoder::DecodedImageFormat format) {
118 scoped_ptr<std::vector<uint8_t> > uncompressed_1(
119 DecompressData(data_1, format));
120 scoped_ptr<std::vector<uint8_t> > uncompressed_2(
121 DecompressData(data_2, format));
122 if (uncompressed_1->size() != uncompressed_2->size()) {
123 DLOG(ERROR) << "Image sizes don't match";
124 return false;
125 }
126 return CompareUncompressedData(&uncompressed_1->front(),
127 &uncompressed_2->front(),
128 uncompressed_1->size());
129 }
130
131 bool CheckTiffImagesEqual(NSData* image_1, NSData* image_2) {
132 if ([image_1 length] != [image_2 length]) {
133 DLOG(ERROR) << "Image lengths don't match";
134 return false;
135 }
136 // Compare headers.
137 const size_t kHeaderSize = WebpDecoder::GetHeaderSize();
138 NSData* header_1 = [image_1 subdataWithRange:NSMakeRange(0, kHeaderSize)];
139 NSData* header_2 = [image_2 subdataWithRange:NSMakeRange(0, kHeaderSize)];
140 if (!header_1 || !header_2)
141 return false;
142 if (![header_1 isEqualToData:header_2]) {
143 DLOG(ERROR) << "Headers don't match.";
144 return false;
145 }
146 return CompareUncompressedData(
147 static_cast<const uint8_t*>([image_1 bytes]) + kHeaderSize,
148 static_cast<const uint8_t*>([image_2 bytes]) + kHeaderSize,
149 [image_1 length] - kHeaderSize);
150 }
151
152 protected:
153 scoped_refptr<WebpDecoderDelegate> delegate_;
154 scoped_refptr<WebpDecoder> decoder_;
155 };
156
157 } // namespace
158
159 TEST_F(WebpDecoderTest, DecodeToJpeg) {
160 // Load a WebP image from disk.
161 base::scoped_nsobject<NSData> webp_image(
162 [LoadImage(base::FilePath("test.webp")) retain]);
163 ASSERT_TRUE(webp_image != nil);
164 // Load reference image.
165 base::scoped_nsobject<NSData> jpg_image(
166 [LoadImage(base::FilePath("test.jpg")) retain]);
167 ASSERT_TRUE(jpg_image != nil);
168 // Convert to JPEG.
169 EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
170 EXPECT_CALL(*delegate_,
171 SetImageFeatures(testing::_, WebpDecoder::JPEG)).Times(1);
172 decoder_->OnDataReceived(webp_image);
173 // Compare to reference image.
174 EXPECT_TRUE(CheckCompressedImagesEqual(jpg_image, delegate_->GetImage(),
175 WebpDecoder::JPEG));
176 }
177
178 TEST_F(WebpDecoderTest, DecodeToPng) {
179 // Load a WebP image from disk.
180 base::scoped_nsobject<NSData> webp_image(
181 [LoadImage(base::FilePath("test_alpha.webp")) retain]);
182 ASSERT_TRUE(webp_image != nil);
183 // Load reference image.
184 base::scoped_nsobject<NSData> png_image(
185 [LoadImage(base::FilePath("test_alpha.png")) retain]);
186 ASSERT_TRUE(png_image != nil);
187 // Convert to PNG.
188 EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
189 EXPECT_CALL(*delegate_,
190 SetImageFeatures(testing::_, WebpDecoder::PNG)).Times(1);
191 decoder_->OnDataReceived(webp_image);
192 // Compare to reference image.
193 EXPECT_TRUE(CheckCompressedImagesEqual(png_image, delegate_->GetImage(),
194 WebpDecoder::PNG));
195 }
196
197 TEST_F(WebpDecoderTest, DecodeToTiff) {
198 // Load a WebP image from disk.
199 base::scoped_nsobject<NSData> webp_image(
200 [LoadImage(base::FilePath("test_small.webp")) retain]);
201 ASSERT_TRUE(webp_image != nil);
202 // Load reference image.
203 base::scoped_nsobject<NSData> tiff_image(
204 [LoadImage(base::FilePath("test_small.tiff")) retain]);
205 ASSERT_TRUE(tiff_image != nil);
206 // Convert to TIFF.
207 EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
208 EXPECT_CALL(*delegate_, SetImageFeatures([tiff_image length],
209 WebpDecoder::TIFF)).Times(1);
210 decoder_->OnDataReceived(webp_image);
211 // Compare to reference image.
212 EXPECT_TRUE(CheckTiffImagesEqual(tiff_image, delegate_->GetImage()));
213 }
214
215 TEST_F(WebpDecoderTest, StreamedDecode) {
216 // Load a WebP image from disk.
217 base::scoped_nsobject<NSData> webp_image(
218 [LoadImage(base::FilePath("test.webp")) retain]);
219 ASSERT_TRUE(webp_image != nil);
220 // Load reference image.
221 base::scoped_nsobject<NSData> jpg_image(
222 [LoadImage(base::FilePath("test.jpg")) retain]);
223 ASSERT_TRUE(jpg_image != nil);
224 // Convert to JPEG in chunks.
225 EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
226 EXPECT_CALL(*delegate_,
227 SetImageFeatures(testing::_, WebpDecoder::JPEG)).Times(1);
228 const size_t kChunkSize = 10;
229 unsigned int num_chunks = 0;
230 while ([webp_image length] > kChunkSize) {
231 base::scoped_nsobject<NSData> chunk(
232 [[webp_image subdataWithRange:NSMakeRange(0, kChunkSize)] retain]);
233 decoder_->OnDataReceived(chunk);
234 webp_image.reset([[webp_image subdataWithRange:NSMakeRange(
235 kChunkSize, [webp_image length] - kChunkSize)] retain]);
236 ++num_chunks;
237 }
238 if ([webp_image length] > 0u) {
239 decoder_->OnDataReceived(webp_image);
240 ++num_chunks;
241 }
242 ASSERT_GT(num_chunks, 3u) << "Not enough chunks";
243 // Compare to reference image.
244 EXPECT_TRUE(CheckCompressedImagesEqual(jpg_image, delegate_->GetImage(),
245 WebpDecoder::JPEG));
246 }
247
248 TEST_F(WebpDecoderTest, InvalidFormat) {
249 EXPECT_CALL(*delegate_, OnFinishedDecoding(false)).Times(1);
250 const char dummy_image[] = "(>'-')> <('-'<) ^('-')^ <('-'<) (>'-')>";
251 base::scoped_nsobject<NSData> data(
252 [[NSData alloc] initWithBytes:dummy_image length:arraysize(dummy_image)]);
253 decoder_->OnDataReceived(data);
254 EXPECT_EQ(0u, [delegate_->GetImage() length]);
255 }
256
257 TEST_F(WebpDecoderTest, DecodeAborted) {
258 EXPECT_CALL(*delegate_, OnFinishedDecoding(false)).Times(1);
259 decoder_->Stop();
260 EXPECT_EQ(0u, [delegate_->GetImage() length]);
261 }
262
263 } // namespace web
OLDNEW
« no previous file with comments | « ios/web/public/webp_decoder.mm ('k') | ios/web/test/data/test.jpg » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698