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

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

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

Powered by Google App Engine
This is Rietveld 408576698