| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| |
| #include "chrome/browser/thumbnail/cc/jpeg_thumbnail_helper.h" |
| |
| #include <algorithm> |
| |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/thread_pool.h" |
| #include "skia/ext/image_operations.h" |
| #include "ui/gfx/codec/jpeg_codec.h" |
| |
| namespace thumbnail { |
| namespace { |
| |
| SkBitmap ResizeBitmap(const SkBitmap& bitmap) { |
| constexpr int kScale = 2; |
| int width = bitmap.width() / kScale; |
| int height = bitmap.height() / kScale; |
| |
| SkIRect dest_subset = {0, 0, width, height}; |
| |
| SkBitmap output = skia::ImageOperations::Resize( |
| bitmap, skia::ImageOperations::RESIZE_BETTER, width, height, dest_subset); |
| output.setImmutable(); |
| return output; |
| } |
| |
| void CompressTask( |
| const SkBitmap& bitmap, |
| base::OnceCallback<void(std::vector<uint8_t>)> post_processing_task) { |
| constexpr int kCompressionQuality = 97; |
| std::optional<std::vector<uint8_t>> data = |
| gfx::JPEGCodec::Encode(ResizeBitmap(bitmap), kCompressionQuality); |
| |
| std::move(post_processing_task).Run(std::move(data.value())); |
| } |
| |
| void WriteTask(base::FilePath file_path, |
| std::vector<uint8_t> compressed_data, |
| base::OnceCallback<void(bool)> post_write_task) { |
| DCHECK(!compressed_data.empty()); |
| |
| if (!base::WriteFile(file_path, compressed_data)) { |
| base::DeleteFile(file_path); |
| std::move(post_write_task).Run(false); |
| return; |
| } |
| |
| std::move(post_write_task).Run(true); |
| } |
| |
| void ReadTask(base::FilePath file_path, |
| base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> |
| post_read_task) { |
| std::optional<std::vector<uint8_t>> read_data = |
| base::ReadFileToBytes(file_path); |
| |
| if (!read_data.has_value()) { |
| base::DeleteFile(file_path); |
| } |
| |
| std::move(post_read_task).Run(std::move(read_data)); |
| } |
| |
| void DeleteTask(base::FilePath file_path) { |
| if (base::PathExists(file_path)) { |
| base::DeleteFile(file_path); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| JpegThumbnailHelper::JpegThumbnailHelper( |
| const base::FilePath& base_path, |
| scoped_refptr<base::SequencedTaskRunner> file_task_runner) |
| : base_path_(base_path), |
| default_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), |
| file_task_runner_(file_task_runner) {} |
| |
| JpegThumbnailHelper::~JpegThumbnailHelper() { |
| DCHECK(default_task_runner_->RunsTasksInCurrentSequence()); |
| } |
| |
| void JpegThumbnailHelper::Compress( |
| const SkBitmap& bitmap, |
| base::OnceCallback<void(std::vector<uint8_t>)> post_processing_task) { |
| DCHECK(default_task_runner_->RunsTasksInCurrentSequence()); |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce(&CompressTask, bitmap, |
| base::BindPostTask(default_task_runner_, |
| std::move(post_processing_task)))); |
| } |
| |
| void JpegThumbnailHelper::Write( |
| TabId tab_id, |
| std::vector<uint8_t> compressed_data, |
| base::OnceCallback<void(bool)> post_write_task) { |
| DCHECK(default_task_runner_->RunsTasksInCurrentSequence()); |
| base::FilePath file_path = GetJpegFilePath(tab_id); |
| file_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&WriteTask, file_path, compressed_data, |
| base::BindPostTask(default_task_runner_, |
| std::move(post_write_task)))); |
| } |
| |
| void JpegThumbnailHelper::Read( |
| TabId tab_id, |
| base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> |
| post_read_task) { |
| DCHECK(default_task_runner_->RunsTasksInCurrentSequence()); |
| base::FilePath file_path = GetJpegFilePath(tab_id); |
| file_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&ReadTask, file_path, |
| base::BindPostTask(default_task_runner_, |
| std::move(post_read_task)))); |
| } |
| |
| void JpegThumbnailHelper::Delete(TabId tab_id) { |
| DCHECK(default_task_runner_->RunsTasksInCurrentSequence()); |
| base::FilePath file_path = GetJpegFilePath(tab_id); |
| file_task_runner_->PostTask(FROM_HERE, |
| base::BindOnce(&DeleteTask, file_path)); |
| } |
| |
| base::FilePath JpegThumbnailHelper::GetJpegFilePath(TabId tab_id) { |
| base::FilePath file_path = base_path_.Append(base::NumberToString(tab_id)); |
| return file_path.AddExtension(".jpeg"); |
| } |
| |
| } // namespace thumbnail |