| // Copyright (c) 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 "chrome/browser/image_decoder.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "ipc/ipc_channel.h" |
| #include "services/data_decoder/public/cpp/decode_image.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace { |
| |
| const int64_t kMaxImageSizeInBytes = |
| static_cast<int64_t>(IPC::Channel::kMaximumMessageSize); |
| |
| // Note that this is always called on the thread which initiated the |
| // corresponding data_decoder::DecodeImage request. |
| void OnDecodeImageDone( |
| base::Callback<void(int)> fail_callback, |
| base::Callback<void(const SkBitmap&, int)> success_callback, |
| int request_id, |
| const SkBitmap& image) { |
| if (!image.isNull() && !image.empty()) |
| success_callback.Run(image, request_id); |
| else |
| fail_callback.Run(request_id); |
| } |
| |
| void BindToBrowserConnector(service_manager::mojom::ConnectorRequest request) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| content::ServiceManagerConnection::GetForProcess()->GetConnector() |
| ->BindConnectorRequest(std::move(request)); |
| } |
| |
| void RunDecodeCallbackOnTaskRunner( |
| data_decoder::mojom::ImageDecoder::DecodeImageCallback callback, |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| const SkBitmap& image) { |
| task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), image)); |
| } |
| |
| void DecodeImage( |
| std::vector<uint8_t> image_data, |
| data_decoder::mojom::ImageCodec codec, |
| bool shrink_to_fit, |
| const gfx::Size& desired_image_frame_size, |
| data_decoder::mojom::ImageDecoder::DecodeImageCallback callback, |
| scoped_refptr<base::SequencedTaskRunner> callback_task_runner) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| service_manager::mojom::ConnectorRequest connector_request; |
| std::unique_ptr<service_manager::Connector> connector = |
| service_manager::Connector::Create(&connector_request); |
| base::PostTaskWithTraits( |
| FROM_HERE, {content::BrowserThread::UI}, |
| base::BindOnce(&BindToBrowserConnector, std::move(connector_request))); |
| |
| data_decoder::DecodeImage( |
| connector.get(), image_data, codec, shrink_to_fit, kMaxImageSizeInBytes, |
| desired_image_frame_size, |
| base::BindOnce(&RunDecodeCallbackOnTaskRunner, std::move(callback), |
| std::move(callback_task_runner))); |
| } |
| |
| } // namespace |
| |
| ImageDecoder::ImageRequest::ImageRequest() |
| : task_runner_(base::ThreadTaskRunnerHandle::Get()) { |
| DCHECK(sequence_checker_.CalledOnValidSequence()); |
| } |
| |
| ImageDecoder::ImageRequest::ImageRequest( |
| const scoped_refptr<base::SequencedTaskRunner>& task_runner) |
| : task_runner_(task_runner) { |
| DCHECK(sequence_checker_.CalledOnValidSequence()); |
| } |
| |
| ImageDecoder::ImageRequest::~ImageRequest() { |
| DCHECK(sequence_checker_.CalledOnValidSequence()); |
| ImageDecoder::Cancel(this); |
| } |
| |
| // static |
| ImageDecoder* ImageDecoder::GetInstance() { |
| static auto* image_decoder = new ImageDecoder(); |
| return image_decoder; |
| } |
| |
| // static |
| void ImageDecoder::Start(ImageRequest* image_request, |
| std::vector<uint8_t> image_data) { |
| StartWithOptions(image_request, std::move(image_data), DEFAULT_CODEC, false, |
| gfx::Size()); |
| } |
| |
| // static |
| void ImageDecoder::Start(ImageRequest* image_request, |
| const std::string& image_data) { |
| Start(image_request, |
| std::vector<uint8_t>(image_data.begin(), image_data.end())); |
| } |
| |
| // static |
| void ImageDecoder::StartWithOptions(ImageRequest* image_request, |
| std::vector<uint8_t> image_data, |
| ImageCodec image_codec, |
| bool shrink_to_fit, |
| const gfx::Size& desired_image_frame_size) { |
| ImageDecoder::GetInstance()->StartWithOptionsImpl( |
| image_request, std::move(image_data), image_codec, shrink_to_fit, |
| desired_image_frame_size); |
| } |
| |
| // static |
| void ImageDecoder::StartWithOptions(ImageRequest* image_request, |
| const std::string& image_data, |
| ImageCodec image_codec, |
| bool shrink_to_fit) { |
| StartWithOptions(image_request, |
| std::vector<uint8_t>(image_data.begin(), image_data.end()), |
| image_codec, shrink_to_fit, gfx::Size()); |
| } |
| |
| ImageDecoder::ImageDecoder() : image_request_id_counter_(0) {} |
| |
| void ImageDecoder::StartWithOptionsImpl( |
| ImageRequest* image_request, |
| std::vector<uint8_t> image_data, |
| ImageCodec image_codec, |
| bool shrink_to_fit, |
| const gfx::Size& desired_image_frame_size) { |
| DCHECK(image_request); |
| DCHECK(image_request->task_runner()); |
| |
| int request_id; |
| { |
| base::AutoLock lock(map_lock_); |
| request_id = image_request_id_counter_++; |
| image_request_id_map_.insert(std::make_pair(request_id, image_request)); |
| } |
| |
| data_decoder::mojom::ImageCodec codec = |
| data_decoder::mojom::ImageCodec::DEFAULT; |
| #if defined(OS_CHROMEOS) |
| if (image_codec == ROBUST_JPEG_CODEC) |
| codec = data_decoder::mojom::ImageCodec::ROBUST_JPEG; |
| if (image_codec == ROBUST_PNG_CODEC) |
| codec = data_decoder::mojom::ImageCodec::ROBUST_PNG; |
| #endif // defined(OS_CHROMEOS) |
| |
| auto callback = base::Bind( |
| &OnDecodeImageDone, |
| base::Bind(&ImageDecoder::OnDecodeImageFailed, base::Unretained(this)), |
| base::Bind(&ImageDecoder::OnDecodeImageSucceeded, base::Unretained(this)), |
| request_id); |
| |
| // NOTE: There exist ImageDecoder consumers which implicitly rely on this |
| // operation happening on a thread which always has a ThreadTaskRunnerHandle. |
| // We arbitrarily use the IO thread here to match details of the legacy |
| // implementation. |
| base::PostTaskWithTraits( |
| FROM_HERE, {content::BrowserThread::IO}, |
| base::BindOnce(&DecodeImage, std::move(image_data), codec, shrink_to_fit, |
| desired_image_frame_size, callback, |
| base::WrapRefCounted(image_request->task_runner()))); |
| } |
| |
| // static |
| void ImageDecoder::Cancel(ImageRequest* image_request) { |
| DCHECK(image_request); |
| ImageDecoder::GetInstance()->CancelImpl(image_request); |
| } |
| |
| void ImageDecoder::CancelImpl(ImageRequest* image_request) { |
| base::AutoLock lock(map_lock_); |
| for (auto it = image_request_id_map_.begin(); |
| it != image_request_id_map_.end();) { |
| if (it->second == image_request) { |
| image_request_id_map_.erase(it++); |
| } else { |
| ++it; |
| } |
| } |
| } |
| |
| void ImageDecoder::OnDecodeImageSucceeded( |
| const SkBitmap& decoded_image, |
| int request_id) { |
| ImageRequest* image_request; |
| { |
| base::AutoLock lock(map_lock_); |
| auto it = image_request_id_map_.find(request_id); |
| if (it == image_request_id_map_.end()) |
| return; |
| image_request = it->second; |
| image_request_id_map_.erase(it); |
| } |
| |
| DCHECK(image_request->task_runner()->RunsTasksInCurrentSequence()); |
| image_request->OnImageDecoded(decoded_image); |
| } |
| |
| void ImageDecoder::OnDecodeImageFailed(int request_id) { |
| ImageRequest* image_request; |
| { |
| base::AutoLock lock(map_lock_); |
| auto it = image_request_id_map_.find(request_id); |
| if (it == image_request_id_map_.end()) |
| return; |
| image_request = it->second; |
| image_request_id_map_.erase(it); |
| } |
| |
| DCHECK(image_request->task_runner()->RunsTasksInCurrentSequence()); |
| image_request->OnDecodeImageFailed(); |
| } |