Index: ios/chrome/browser/suggestions/ios_image_decoder_impl.mm |
diff --git a/ios/chrome/browser/suggestions/ios_image_decoder_impl.mm b/ios/chrome/browser/suggestions/ios_image_decoder_impl.mm |
index f28d382f1fb0e538bbe54b28161a81e245d5415b..eccb9b6c11cba2e822d47635fc817aa83572f81c 100644 |
--- a/ios/chrome/browser/suggestions/ios_image_decoder_impl.mm |
+++ b/ios/chrome/browser/suggestions/ios_image_decoder_impl.mm |
@@ -7,28 +7,115 @@ |
#include <UIKit/UIKit.h> |
#include "base/callback.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "ios/chrome/browser/webp_transcode/webp_decoder.h" |
+#include "ios/web/public/web_thread.h" |
#include "ui/gfx/image/image.h" |
+namespace { |
+ |
+class WebpDecoderDelegate : public webp_transcode::WebpDecoder::Delegate { |
+ public: |
+ NSData* data() const { return decoded_image_; } |
+ |
+ // WebpDecoder::Delegate methods |
+ void OnFinishedDecoding(bool success) override { |
+ if (!success) |
+ decoded_image_.reset(); |
+ } |
+ |
+ void SetImageFeatures( |
+ size_t total_size, |
+ webp_transcode::WebpDecoder::DecodedImageFormat format) override { |
+ decoded_image_.reset([[NSMutableData alloc] initWithCapacity:total_size]); |
+ } |
+ |
+ void OnDataDecoded(NSData* data) override { |
+ DCHECK(decoded_image_); |
+ [decoded_image_ appendData:data]; |
+ } |
+ |
+ private: |
+ ~WebpDecoderDelegate() override {} |
+ base::scoped_nsobject<NSMutableData> decoded_image_; |
+}; |
+ |
+// Returns an NSData object containing the decoded image data of the given |
+// webp_image. Returns nil in case of failure. |
+base::scoped_nsobject<NSData> DecodeWebpImage( |
+ const base::scoped_nsobject<NSData>& webp_image) { |
+ scoped_refptr<WebpDecoderDelegate> delegate(new WebpDecoderDelegate); |
+ scoped_refptr<webp_transcode::WebpDecoder> decoder( |
+ new webp_transcode::WebpDecoder(delegate.get())); |
+ decoder->OnDataReceived(webp_image); |
+ DLOG_IF(ERROR, !delegate->data()) << "WebP image decoding failed."; |
+ return base::scoped_nsobject<NSData>([delegate->data() retain]); |
+} |
+ |
+// Returns true if the given image_data is a WebP image. |
+// |
+// Every WebP file contains a 12 byte file header in the beginning of the file. |
+// A WebP file header starts with the four ASCII characters "RIFF". The next |
+// four bytes contain the image size and the last four header bytes contain the |
+// four ASCII characters "WEBP". |
+// |
+// WebP file header: |
+// 1 1 |
+// Byte Nr. 0 1 2 3 4 5 6 7 8 9 0 1 |
+// Byte value [ R I F F ? ? ? ? W E B P ] |
+// |
+// For more information see: |
+// https://developers.google.com/speed/webp/docs/riff_container#webp_file_header |
+bool IsWebpImage(const std::string& image_data) { |
+ if (image_data.length() < 12) |
+ return false; |
+ return image_data.compare(0, 4, "RIFF") == 0 && |
+ image_data.compare(8, 4, "WEBP") == 0; |
+} |
+ |
+} // namespace |
+ |
namespace suggestions { |
-IOSImageDecoderImpl::IOSImageDecoderImpl() {} |
+IOSImageDecoderImpl::IOSImageDecoderImpl( |
+ const scoped_refptr<base::TaskRunner>& task_runner) |
+ : task_runner_(task_runner), weak_factory_(this) { |
+ DCHECK(task_runner_.get()); |
+} |
IOSImageDecoderImpl::~IOSImageDecoderImpl() {} |
void IOSImageDecoderImpl::DecodeImage( |
const std::string& image_data, |
const image_fetcher::ImageDecodedCallback& callback) { |
- // Convert the |image_data| std::string to a NSData buffer. |
- NSData* data = |
- [NSData dataWithBytesNoCopy:const_cast<char*>(image_data.c_str()) |
- length:image_data.length() |
- freeWhenDone:NO]; |
- |
- // Decode the Image using UIImage. |
- if (data) { |
- // Most likely always returns 1x images. |
- UIImage* ui_image = [UIImage imageWithData:data scale:1]; |
+ // Convert the |image_data| std::string to an NSData buffer. |
+ base::scoped_nsobject<NSData> data( |
+ [[NSData dataWithBytesNoCopy:const_cast<char*>(image_data.c_str()) |
+ length:image_data.length() |
+ freeWhenDone:NO] retain]); |
+ |
+ // The WebP image format is not supported by iOS natively. Therefore WebP |
+ // images need to be decoded explicitly, |
+ if (IsWebpImage(image_data)) { |
+ base::PostTaskAndReplyWithResult( |
+ task_runner_.get(), FROM_HERE, base::Bind(&DecodeWebpImage, data), |
+ base::Bind(&IOSImageDecoderImpl::CreateUIImageAndRunCallback, |
+ weak_factory_.GetWeakPtr(), callback)); |
+ } else { |
+ CreateUIImageAndRunCallback(callback, data); |
+ } |
+} |
+ |
+void IOSImageDecoderImpl::CreateUIImageAndRunCallback( |
+ const image_fetcher::ImageDecodedCallback& callback, |
+ base::scoped_nsobject<NSData> image_data) { |
+ // Decode the image data using UIImage. |
+ if (image_data) { |
+ // "Most likely" always returns 1x images. |
+ UIImage* ui_image = [[UIImage imageWithData:image_data scale:1] retain]; |
noyau (Ping after 24h)
2016/09/09 10:18:04
Don't do the retain here
vitaliii
2016/09/09 13:03:41
Done.
|
if (ui_image) { |
+ // This constructor does not retain the image, but expects to take the |
+ // ownership, therefore, |ui_image| is not released here. |
gfx::Image gfx_image(ui_image); |
noyau (Ping after 24h)
2016/09/09 10:18:04
Do it here instead.
vitaliii
2016/09/09 13:03:41
Done.
|
callback.Run(gfx_image); |
return; |