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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/web/public/webp_decoder_unittest.mm
diff --git a/ios/web/public/webp_decoder_unittest.mm b/ios/web/public/webp_decoder_unittest.mm
new file mode 100644
index 0000000000000000000000000000000000000000..40d20a16d413f1eea5a06785fa06a85ef8e1587f
--- /dev/null
+++ b/ios/web/public/webp_decoder_unittest.mm
@@ -0,0 +1,263 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/public/webp_decoder.h"
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <Foundation/Foundation.h>
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/strings/sys_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web {
+namespace {
+
+class WebpDecoderDelegate : public WebpDecoder::Delegate {
+ public:
+ WebpDecoderDelegate() : image_([[NSMutableData alloc] init]) {
+ }
+
+ NSData* GetImage() const { return image_; }
+
+ // WebpDecoder::Delegate methods.
+ MOCK_METHOD1(OnFinishedDecoding, void(bool success));
+ MOCK_METHOD2(SetImageFeatures,
+ void(size_t total_size, WebpDecoder::DecodedImageFormat format));
+ virtual void OnDataDecoded(NSData* data) { [image_ appendData:data]; }
+
+ private:
+ virtual ~WebpDecoderDelegate() {}
+
+ base::scoped_nsobject<NSMutableData> image_;
+};
+
+class WebpDecoderTest : public testing::Test {
+ public:
+ WebpDecoderTest()
+ : delegate_(new WebpDecoderDelegate),
+ decoder_(new WebpDecoder(delegate_.get())) {}
+
+ NSData* LoadImage(const base::FilePath& filename) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("ios/web/test/data").Append(filename);
+ return
+ [NSData dataWithContentsOfFile:base::SysUTF8ToNSString(path.value())];
+ }
+
+ std::vector<uint8_t>* DecompressData(NSData* data,
+ WebpDecoder::DecodedImageFormat format) {
+ base::ScopedCFTypeRef<CGDataProviderRef> provider(
+ CGDataProviderCreateWithCFData((CFDataRef)data));
+ base::ScopedCFTypeRef<CGImageRef> image;
+ switch (format) {
+ case WebpDecoder::JPEG:
+ image.reset(CGImageCreateWithJPEGDataProvider(
+ provider, NULL, false, kCGRenderingIntentDefault));
+ break;
+ case WebpDecoder::PNG:
+ image.reset(CGImageCreateWithPNGDataProvider(
+ provider, NULL, false, kCGRenderingIntentDefault));
+ break;
+ case WebpDecoder::TIFF:
+ ADD_FAILURE() << "Data already decompressed";
+ return nil;
+ case WebpDecoder::DECODED_FORMAT_COUNT:
+ ADD_FAILURE() << "Unknown format";
+ return nil;
+ }
+ size_t width = CGImageGetWidth(image);
+ size_t height = CGImageGetHeight(image);
+ base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateDeviceRGB());
+ size_t bytes_per_pixel = 4;
+ size_t bytes_per_row = bytes_per_pixel * width;
+ size_t bits_per_component = 8;
+ std::vector<uint8_t>* result =
+ new std::vector<uint8_t>(width * height * bytes_per_pixel, 0);
+ base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
+ &result->front(), width, height, bits_per_component, bytes_per_row,
+ color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big));
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
+ // Check that someting has been written in |result|.
+ std::vector<uint8_t> zeroes(width * height * bytes_per_pixel, 0);
+ EXPECT_NE(0, memcmp(&result->front(), &zeroes.front(), zeroes.size()))
+ << "Decompression failed.";
+ return result;
+ }
+
+ // Compares data, allowing an averaged absolute difference of 1.
+ bool CompareUncompressedData(const uint8_t* ptr_1,
+ const uint8_t* ptr_2,
+ size_t size) {
+ uint64_t difference = 0;
+ for (size_t i = 0; i < size; ++i) {
+ // Casting to int to avoid overflow.
+ int error = abs(int(ptr_1[i]) - int(ptr_2[i]));
+ EXPECT_GE(difference + error, difference)
+ << "Image difference too big (overflow).";
+ difference += error;
+ }
+ double average_difference = double(difference) / double(size);
+ DLOG(INFO) << "Average image difference: " << average_difference;
+ return average_difference < 1.5;
+ }
+
+ bool CheckCompressedImagesEqual(NSData* data_1,
+ NSData* data_2,
+ WebpDecoder::DecodedImageFormat format) {
+ scoped_ptr<std::vector<uint8_t> > uncompressed_1(
+ DecompressData(data_1, format));
+ scoped_ptr<std::vector<uint8_t> > uncompressed_2(
+ DecompressData(data_2, format));
+ if (uncompressed_1->size() != uncompressed_2->size()) {
+ DLOG(ERROR) << "Image sizes don't match";
+ return false;
+ }
+ return CompareUncompressedData(&uncompressed_1->front(),
+ &uncompressed_2->front(),
+ uncompressed_1->size());
+ }
+
+ bool CheckTiffImagesEqual(NSData* image_1, NSData* image_2) {
+ if ([image_1 length] != [image_2 length]) {
+ DLOG(ERROR) << "Image lengths don't match";
+ return false;
+ }
+ // Compare headers.
+ const size_t kHeaderSize = WebpDecoder::GetHeaderSize();
+ NSData* header_1 = [image_1 subdataWithRange:NSMakeRange(0, kHeaderSize)];
+ NSData* header_2 = [image_2 subdataWithRange:NSMakeRange(0, kHeaderSize)];
+ if (!header_1 || !header_2)
+ return false;
+ if (![header_1 isEqualToData:header_2]) {
+ DLOG(ERROR) << "Headers don't match.";
+ return false;
+ }
+ return CompareUncompressedData(
+ static_cast<const uint8_t*>([image_1 bytes]) + kHeaderSize,
+ static_cast<const uint8_t*>([image_2 bytes]) + kHeaderSize,
+ [image_1 length] - kHeaderSize);
+ }
+
+ protected:
+ scoped_refptr<WebpDecoderDelegate> delegate_;
+ scoped_refptr<WebpDecoder> decoder_;
+};
+
+} // namespace
+
+TEST_F(WebpDecoderTest, DecodeToJpeg) {
+ // Load a WebP image from disk.
+ base::scoped_nsobject<NSData> webp_image(
+ [LoadImage(base::FilePath("test.webp")) retain]);
+ ASSERT_TRUE(webp_image != nil);
+ // Load reference image.
+ base::scoped_nsobject<NSData> jpg_image(
+ [LoadImage(base::FilePath("test.jpg")) retain]);
+ ASSERT_TRUE(jpg_image != nil);
+ // Convert to JPEG.
+ EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
+ EXPECT_CALL(*delegate_,
+ SetImageFeatures(testing::_, WebpDecoder::JPEG)).Times(1);
+ decoder_->OnDataReceived(webp_image);
+ // Compare to reference image.
+ EXPECT_TRUE(CheckCompressedImagesEqual(jpg_image, delegate_->GetImage(),
+ WebpDecoder::JPEG));
+}
+
+TEST_F(WebpDecoderTest, DecodeToPng) {
+ // Load a WebP image from disk.
+ base::scoped_nsobject<NSData> webp_image(
+ [LoadImage(base::FilePath("test_alpha.webp")) retain]);
+ ASSERT_TRUE(webp_image != nil);
+ // Load reference image.
+ base::scoped_nsobject<NSData> png_image(
+ [LoadImage(base::FilePath("test_alpha.png")) retain]);
+ ASSERT_TRUE(png_image != nil);
+ // Convert to PNG.
+ EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
+ EXPECT_CALL(*delegate_,
+ SetImageFeatures(testing::_, WebpDecoder::PNG)).Times(1);
+ decoder_->OnDataReceived(webp_image);
+ // Compare to reference image.
+ EXPECT_TRUE(CheckCompressedImagesEqual(png_image, delegate_->GetImage(),
+ WebpDecoder::PNG));
+}
+
+TEST_F(WebpDecoderTest, DecodeToTiff) {
+ // Load a WebP image from disk.
+ base::scoped_nsobject<NSData> webp_image(
+ [LoadImage(base::FilePath("test_small.webp")) retain]);
+ ASSERT_TRUE(webp_image != nil);
+ // Load reference image.
+ base::scoped_nsobject<NSData> tiff_image(
+ [LoadImage(base::FilePath("test_small.tiff")) retain]);
+ ASSERT_TRUE(tiff_image != nil);
+ // Convert to TIFF.
+ EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
+ EXPECT_CALL(*delegate_, SetImageFeatures([tiff_image length],
+ WebpDecoder::TIFF)).Times(1);
+ decoder_->OnDataReceived(webp_image);
+ // Compare to reference image.
+ EXPECT_TRUE(CheckTiffImagesEqual(tiff_image, delegate_->GetImage()));
+}
+
+TEST_F(WebpDecoderTest, StreamedDecode) {
+ // Load a WebP image from disk.
+ base::scoped_nsobject<NSData> webp_image(
+ [LoadImage(base::FilePath("test.webp")) retain]);
+ ASSERT_TRUE(webp_image != nil);
+ // Load reference image.
+ base::scoped_nsobject<NSData> jpg_image(
+ [LoadImage(base::FilePath("test.jpg")) retain]);
+ ASSERT_TRUE(jpg_image != nil);
+ // Convert to JPEG in chunks.
+ EXPECT_CALL(*delegate_, OnFinishedDecoding(true)).Times(1);
+ EXPECT_CALL(*delegate_,
+ SetImageFeatures(testing::_, WebpDecoder::JPEG)).Times(1);
+ const size_t kChunkSize = 10;
+ unsigned int num_chunks = 0;
+ while ([webp_image length] > kChunkSize) {
+ base::scoped_nsobject<NSData> chunk(
+ [[webp_image subdataWithRange:NSMakeRange(0, kChunkSize)] retain]);
+ decoder_->OnDataReceived(chunk);
+ webp_image.reset([[webp_image subdataWithRange:NSMakeRange(
+ kChunkSize, [webp_image length] - kChunkSize)] retain]);
+ ++num_chunks;
+ }
+ if ([webp_image length] > 0u) {
+ decoder_->OnDataReceived(webp_image);
+ ++num_chunks;
+ }
+ ASSERT_GT(num_chunks, 3u) << "Not enough chunks";
+ // Compare to reference image.
+ EXPECT_TRUE(CheckCompressedImagesEqual(jpg_image, delegate_->GetImage(),
+ WebpDecoder::JPEG));
+}
+
+TEST_F(WebpDecoderTest, InvalidFormat) {
+ EXPECT_CALL(*delegate_, OnFinishedDecoding(false)).Times(1);
+ const char dummy_image[] = "(>'-')> <('-'<) ^('-')^ <('-'<) (>'-')>";
+ base::scoped_nsobject<NSData> data(
+ [[NSData alloc] initWithBytes:dummy_image length:arraysize(dummy_image)]);
+ decoder_->OnDataReceived(data);
+ EXPECT_EQ(0u, [delegate_->GetImage() length]);
+}
+
+TEST_F(WebpDecoderTest, DecodeAborted) {
+ EXPECT_CALL(*delegate_, OnFinishedDecoding(false)).Times(1);
+ decoder_->Stop();
+ EXPECT_EQ(0u, [delegate_->GetImage() length]);
+}
+
+} // namespace web
« 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