| /* |
| * Copyright 2015 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "cobalt/loader/image/image_decoder.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| #include "base/path_service.h" |
| #include "cobalt/base/cobalt_paths.h" |
| #include "cobalt/base/polymorphic_downcast.h" |
| #include "cobalt/render_tree/resource_provider_stub.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace cobalt { |
| namespace loader { |
| namespace image { |
| |
| namespace { |
| |
| struct MockImageDecoderCallback { |
| void SuccessCallback(const scoped_refptr<render_tree::Image>& value) { |
| image = value; |
| } |
| |
| MOCK_METHOD1(FailureCallback, void(const std::string& message)); |
| MOCK_METHOD1(ErrorCallback, void(const std::string& message)); |
| |
| scoped_refptr<render_tree::Image> image; |
| }; |
| |
| class MockImageDecoder : public Decoder { |
| public: |
| MockImageDecoder(); |
| ~MockImageDecoder() OVERRIDE {} |
| |
| LoadResponseType OnResponseStarted( |
| Fetcher* fetcher, |
| const scoped_refptr<net::HttpResponseHeaders>& headers) OVERRIDE; |
| |
| void DecodeChunk(const char* data, size_t size) OVERRIDE; |
| |
| void Finish() OVERRIDE; |
| bool Suspend() OVERRIDE; |
| void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE; |
| |
| scoped_refptr<render_tree::Image> Image(); |
| |
| void ExpectCallWithFailure(const std::string& message); |
| void ExpectCallWithError(const std::string& message); |
| |
| protected: |
| ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_; |
| render_tree::ResourceProviderStub resource_provider_; |
| scoped_ptr<Decoder> image_decoder_; |
| }; |
| |
| MockImageDecoder::MockImageDecoder() { |
| image_decoder_.reset( |
| new ImageDecoder(&resource_provider_, |
| base::Bind(&MockImageDecoderCallback::SuccessCallback, |
| base::Unretained(&image_decoder_callback_)), |
| base::Bind(&MockImageDecoderCallback::FailureCallback, |
| base::Unretained(&image_decoder_callback_)), |
| base::Bind(&MockImageDecoderCallback::ErrorCallback, |
| base::Unretained(&image_decoder_callback_)))); |
| } |
| |
| LoadResponseType MockImageDecoder::OnResponseStarted( |
| Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) { |
| return image_decoder_->OnResponseStarted(fetcher, headers); |
| } |
| |
| void MockImageDecoder::DecodeChunk(const char* data, size_t size) { |
| image_decoder_->DecodeChunk(data, size); |
| } |
| |
| void MockImageDecoder::Finish() { image_decoder_->Finish(); } |
| |
| bool MockImageDecoder::Suspend() { return image_decoder_->Suspend(); } |
| |
| void MockImageDecoder::Resume( |
| render_tree::ResourceProvider* resource_provider) { |
| image_decoder_->Resume(resource_provider); |
| } |
| |
| scoped_refptr<render_tree::Image> MockImageDecoder::Image() { |
| return image_decoder_callback_.image; |
| } |
| |
| void MockImageDecoder::ExpectCallWithFailure(const std::string& message) { |
| EXPECT_CALL(image_decoder_callback_, FailureCallback(message)); |
| } |
| |
| void MockImageDecoder::ExpectCallWithError(const std::string& message) { |
| EXPECT_CALL(image_decoder_callback_, ErrorCallback(message)); |
| } |
| |
| FilePath GetTestImagePath(const char* file_name) { |
| FilePath data_directory; |
| CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); |
| return data_directory.Append(FILE_PATH_LITERAL("cobalt")) |
| .Append(FILE_PATH_LITERAL("loader")) |
| .Append(FILE_PATH_LITERAL("testdata")) |
| .Append(FILE_PATH_LITERAL(file_name)); |
| } |
| |
| std::vector<uint8> GetImageData(const FilePath& file_path) { |
| int64 size; |
| std::vector<uint8> image_data; |
| |
| bool success = file_util::GetFileSize(file_path, &size); |
| |
| CHECK(success) << "Could not get file size."; |
| CHECK_GT(size, 0); |
| |
| image_data.resize(static_cast<size_t>(size)); |
| |
| int num_of_bytes = |
| file_util::ReadFile(file_path, reinterpret_cast<char*>(&image_data[0]), |
| static_cast<int>(size)); |
| |
| CHECK_EQ(num_of_bytes, image_data.size()) << "Could not read '" |
| << file_path.value() << "'."; |
| return image_data; |
| } |
| |
| // Check if pixels are the same as |test_color|. |
| bool CheckSameColor(const uint8* pixels, int width, int height, |
| uint32 test_color) { |
| // Iterate through each pixel testing that the value is the expected value. |
| for (int index = 0; index < width * height; ++index) { |
| uint32 current_color = static_cast<uint32>( |
| (pixels[0] << 24) | (pixels[1] << 16) | (pixels[2] << 8) | pixels[3]); |
| pixels += 4; |
| if (current_color != test_color) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| // TODO: Test special images like the image has gAMA chunk information, |
| // pngs with 16 bit depth, and large pngs. |
| |
| TEST(ImageDecoderTest, DecodeImageWithContentLength0) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithFailure( |
| "No content returned, but expected some."); |
| |
| const char kImageWithContentLength0Headers[] = { |
| "HTTP/1.1 200 OK\0" |
| "Content-Length: 0\0" |
| "Content-Type: image/jpeg\0" |
| "Expires: Wed, 20 Apr 2016 19:33:44 GMT\0" |
| "Date: Wed, 20 Apr 2016 18:33:44 GMT\0" |
| "Server: UploadServer\0" |
| "Accept-Ranges: bytes\0" |
| "Cache-Control: public, max-age=3600\0"}; |
| scoped_refptr<net::HttpResponseHeaders> headers(new net::HttpResponseHeaders( |
| std::string(kImageWithContentLength0Headers, |
| kImageWithContentLength0Headers + |
| sizeof(kImageWithContentLength0Headers)))); |
| image_decoder.OnResponseStarted(NULL, headers); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, DecodeNonImageTypeWithContentLength0) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithFailure( |
| "No content returned, but expected some. Not an image mime type."); |
| |
| const char kHTMLWithContentLength0Headers[] = { |
| "HTTP/1.1 200 OK\0" |
| "Content-Length: 0\0" |
| "Content-Type: text/html\0" |
| "Expires: Wed, 20 Apr 2016 19:33:44 GMT\0" |
| "Date: Wed, 20 Apr 2016 18:33:44 GMT\0" |
| "Server: UploadServer\0" |
| "Accept-Ranges: bytes\0" |
| "Cache-Control: public, max-age=3600\0"}; |
| scoped_refptr<net::HttpResponseHeaders> headers(new net::HttpResponseHeaders( |
| std::string(kHTMLWithContentLength0Headers, |
| kHTMLWithContentLength0Headers + |
| sizeof(kHTMLWithContentLength0Headers)))); |
| image_decoder.OnResponseStarted(NULL, headers); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, DecodeNonImageType) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithFailure("Not an image mime type."); |
| |
| const char kHTMLHeaders[] = { |
| "HTTP/1.1 200 OK\0" |
| "Content-Type: text/html; charset=UTF-8\0" |
| "Expires: Wed, 20 Apr 2016 19:33:44 GMT\0" |
| "Date: Wed, 20 Apr 2016 18:33:44 GMT\0" |
| "Server: gws\0" |
| "Accept-Ranges: none\0" |
| "Cache-Control: private, max-age=0\0"}; |
| scoped_refptr<net::HttpResponseHeaders> headers(new net::HttpResponseHeaders( |
| std::string(kHTMLHeaders, kHTMLHeaders + sizeof(kHTMLHeaders)))); |
| |
| const char kContent[] = { |
| "<!DOCTYPE html><html><head></head><body></body></html>"}; |
| |
| image_decoder.OnResponseStarted(NULL, headers); |
| image_decoder.DecodeChunk(kContent, sizeof(kContent)); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, DecodeNoContentType) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithFailure("Not an image mime type."); |
| |
| const char kHTMLHeaders[] = { |
| "HTTP/1.1 200 OK\0" |
| "Expires: Wed, 20 Apr 2016 19:33:44 GMT\0" |
| "Date: Wed, 20 Apr 2016 18:33:44 GMT\0" |
| "Server: gws\0" |
| "Accept-Ranges: none\0" |
| "Cache-Control: private, max-age=0\0"}; |
| scoped_refptr<net::HttpResponseHeaders> headers(new net::HttpResponseHeaders( |
| std::string(kHTMLHeaders, kHTMLHeaders + sizeof(kHTMLHeaders)))); |
| |
| const char kContent[] = { |
| "<!DOCTYPE html><html><head></head><body></body></html>"}; |
| |
| image_decoder.OnResponseStarted(NULL, headers); |
| image_decoder.DecodeChunk(kContent, sizeof(kContent)); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, DecodeImageWithNoContent) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithFailure( |
| "No content returned. Not an image mime type."); |
| |
| const char kHTMLWithNoContentHeaders[] = { |
| "HTTP/1.1 204 No Content\0" |
| "Expires: Tue, 27 Apr 1971 19:44:06 EST\0" |
| "Content-Length: 0\0" |
| "Content-Type: text/html; charset=utf-8\0" |
| "Date: Wed, 20 Apr 2016 18:33:44 GMT\0" |
| "Server: Ytfe_Worker\0" |
| "Accept-Ranges: bytes\0" |
| "Cache-Control: no-cache\0"}; |
| scoped_refptr<net::HttpResponseHeaders> headers( |
| new net::HttpResponseHeaders(std::string( |
| kHTMLWithNoContentHeaders, |
| kHTMLWithNoContentHeaders + sizeof(kHTMLWithNoContentHeaders)))); |
| image_decoder.OnResponseStarted(NULL, headers); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, DecodeImageWithLessThanHeaderBytes) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithError("No enough image data for header."); |
| |
| const char kPartialWebPHeader[] = {"RIFF"}; |
| image_decoder.DecodeChunk(kPartialWebPHeader, sizeof(kPartialWebPHeader)); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, FailedToDecodeImage) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithError("PNGImageDecoder failed to decode image."); |
| |
| const char kPartialPNGImage[] = { |
| "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00" |
| "\x02\x6C\x00\x00\x01\x2C"}; |
| image_decoder.DecodeChunk(kPartialPNGImage, sizeof(kPartialPNGImage)); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| TEST(ImageDecoderTest, UnsupportedImageFormat) { |
| MockImageDecoder image_decoder; |
| image_decoder.ExpectCallWithError("Unsupported image format."); |
| |
| const char kPartialICOImage[] = { |
| "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00"}; |
| image_decoder.DecodeChunk(kPartialICOImage, sizeof(kPartialICOImage)); |
| image_decoder.Finish(); |
| |
| EXPECT_FALSE(image_decoder.Image()); |
| } |
| |
| // Test that we can properly decode the PNG image. |
| TEST(ImageDecoderTest, DecodePNGImage) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("non_interlaced_png.png")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), |
| image_data.size()); |
| image_decoder.Finish(); |
| |
| // All pixels in the PNG image should have premultiplied alpha RGBA |
| // values of (127, 0, 0, 127). |
| uint8 r = 127; |
| uint8 g = 0; |
| uint8 b = 0; |
| uint8 a = 127; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the PNG image with multiple chunks. |
| TEST(ImageDecoderTest, DecodePNGImageWithMultipleChunks) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("non_interlaced_png.png")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), 4); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[4]), 2); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[6]), 94); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[100]), 100); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[200]), |
| image_data.size() - 200); |
| image_decoder.Finish(); |
| |
| // All pixels in the PNG image should have premultiplied alpha RGBA |
| // values of (127, 0, 0, 127). |
| uint8 r = 127; |
| uint8 g = 0; |
| uint8 b = 0; |
| uint8 a = 127; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the the interlaced PNG. |
| TEST(ImageDecoderTest, DecodeInterlacedPNGImage) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("interlaced_png.png")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), |
| image_data.size()); |
| image_decoder.Finish(); |
| |
| // All pixels in the PNG image should have premultiplied alpha RGBA |
| // values of (128, 88, 0, 255). |
| uint8 r = 128; |
| uint8 g = 88; |
| uint8 b = 0; |
| uint8 a = 255; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the interlaced PNG with multiple chunks. |
| TEST(ImageDecoderTest, DecodeInterlacedPNGImageWithMultipleChunks) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("interlaced_png.png")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), 4); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[4]), 2); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[6]), 94); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[100]), 100); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[200]), |
| image_data.size() - 200); |
| image_decoder.Finish(); |
| |
| // All pixels in the PNG image should have premultiplied alpha RGBA |
| // values of (128, 88, 0, 255). |
| uint8 r = 128; |
| uint8 g = 88; |
| uint8 b = 0; |
| uint8 a = 255; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the JPEG image. |
| TEST(ImageDecoderTest, DecodeJPEGImage) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("baseline_jpeg.jpg")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), |
| image_data.size()); |
| image_decoder.Finish(); |
| |
| // All pixels in the JPEG image should have RGBA values of (128, 88, 0, 255). |
| uint8 r = 128; |
| uint8 g = 88; |
| uint8 b = 0; |
| uint8 a = 255; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the JPEG image with multiple chunks. |
| TEST(ImageDecoderTest, DecodeJPEGImageWithMultipleChunks) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("baseline_jpeg.jpg")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), 2); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[2]), 4); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[6]), 94); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[100]), 200); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[300]), |
| image_data.size() - 300); |
| image_decoder.Finish(); |
| |
| // All pixels in the JPEG image should have RGBA values of (128, 88, 0, 255). |
| uint8 r = 128; |
| uint8 g = 88; |
| uint8 b = 0; |
| uint8 a = 255; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the progressive JPEG image. |
| TEST(ImageDecoderTest, DecodeProgressiveJPEGImage) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("progressive_jpeg.jpg")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), |
| image_data.size()); |
| image_decoder.Finish(); |
| |
| // All pixels in the JPEG image should have RGBA values of (64, 32, 17, 255). |
| uint8 r = 64; |
| uint8 g = 32; |
| uint8 b = 17; |
| uint8 a = 255; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the progressive JPEG with multiple chunks. |
| TEST(ImageDecoderTest, DecodeProgressiveJPEGImageWithMultipleChunks) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("progressive_jpeg.jpg")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), 2); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[2]), 4); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[6]), 94); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[100]), 200); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[300]), |
| image_data.size() - 300); |
| image_decoder.Finish(); |
| |
| // All pixels in the JPEG image should have RGBA values of (64, 32, 17, 255). |
| uint8 r = 64; |
| uint8 g = 32; |
| uint8 b = 17; |
| uint8 a = 255; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the WEBP image. |
| TEST(ImageDecoderTest, DecodeWEBPImage) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("webp_image.webp")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), |
| image_data.size()); |
| image_decoder.Finish(); |
| |
| // All pixels in the WEBP image should have RGBA values of (16, 8, 70, 70). |
| uint8 r = 16; |
| uint8 g = 8; |
| uint8 b = 70; |
| uint8 a = 70; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| // Test that we can properly decode the WEBP image with multiple chunks. |
| TEST(ImageDecoderTest, DecodeWEBPImageWithMultipleChunks) { |
| MockImageDecoder image_decoder; |
| |
| std::vector<uint8> image_data = |
| GetImageData(GetTestImagePath("webp_image.webp")); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]), 2); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[2]), 4); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[6]), 94); |
| image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[100]), |
| image_data.size() - 100); |
| image_decoder.Finish(); |
| |
| // All pixels in the WEBP image should have RGBA values of (16, 8, 70, 70). |
| uint8 r = 16; |
| uint8 g = 8; |
| uint8 b = 70; |
| uint8 a = 70; |
| uint32 expected_color = |
| static_cast<uint32>((r << 24) | (g << 16) | (b << 8) | a); |
| |
| ASSERT_TRUE(image_decoder.Image()); |
| render_tree::ImageStub* data = |
| base::polymorphic_downcast<render_tree::ImageStub*>( |
| image_decoder.Image().get()); |
| |
| math::Size size = data->GetSize(); |
| uint8* pixels = data->GetImageData()->GetMemory(); |
| |
| EXPECT_TRUE( |
| CheckSameColor(pixels, size.width(), size.height(), expected_color)); |
| } |
| |
| } // namespace image |
| } // namespace loader |
| } // namespace cobalt |