Commit eac07688 authored by Wei-Yin Chen (陳威尹)'s avatar Wei-Yin Chen (陳威尹) Committed by Commit Bot

Introduce JPEG thumbnails for Grid Tab Switcher

The thumbnails shown in the Grid Tab Switcher (GTS) were decoded from
the ETC1 format, downsampled, and then transferred through the JNI
boundary. This is not the most efficient way.

This CL introduces JPEG thumbnails, which would be saved at the same
time ETC1 thumbnails are saved, if GTS is enabled. The GTS would
directly read and decode the JPEG files from the Java side.

Bug: 971939
Change-Id: Iaf21ea2170afbb7b0385570459f4566715786ca0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1648963Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Commit-Queue: Wei-Yin Chen (陳威尹) <wychen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#667302}
parent 9bce35e5
...@@ -8,6 +8,7 @@ import static java.lang.Math.min; ...@@ -8,6 +8,7 @@ import static java.lang.Math.min;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
...@@ -16,8 +17,10 @@ import android.view.ViewGroup.MarginLayoutParams; ...@@ -16,8 +17,10 @@ import android.view.ViewGroup.MarginLayoutParams;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.CommandLine; import org.chromium.base.CommandLine;
import org.chromium.base.PathUtils;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.native_page.FrozenNativePage; import org.chromium.chrome.browser.native_page.FrozenNativePage;
...@@ -29,6 +32,7 @@ import org.chromium.chrome.browser.util.FeatureUtilities; ...@@ -29,6 +32,7 @@ import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.display.DisplayAndroid;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -115,6 +119,7 @@ public class TabContentManager { ...@@ -115,6 +119,7 @@ public class TabContentManager {
float thumbnailScale = 1.f; float thumbnailScale = 1.f;
boolean useApproximationThumbnails; boolean useApproximationThumbnails;
boolean saveJpegThumbnails = FeatureUtilities.isGridTabSwitcherEnabled();
DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(context); DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(context);
float deviceDensity = display.getDipScale(); float deviceDensity = display.getDipScale();
if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)) { if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)) {
...@@ -133,9 +138,9 @@ public class TabContentManager { ...@@ -133,9 +138,9 @@ public class TabContentManager {
mPriorityTabIds = new int[mFullResThumbnailsMaxSize]; mPriorityTabIds = new int[mFullResThumbnailsMaxSize];
mNativeTabContentManager = nativeInit(defaultCacheSize, mNativeTabContentManager =
approximationCacheSize, compressionQueueMaxSize, writeQueueMaxSize, nativeInit(defaultCacheSize, approximationCacheSize, compressionQueueMaxSize,
useApproximationThumbnails); writeQueueMaxSize, useApproximationThumbnails, saveJpegThumbnails);
} }
/** /**
...@@ -277,13 +282,13 @@ public class TabContentManager { ...@@ -277,13 +282,13 @@ public class TabContentManager {
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return; if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
if (!forceUpdate) { if (!forceUpdate) {
nativeGetTabThumbnailWithCallback(mNativeTabContentManager, tab.getId(), callback); getTabThumbnailFromDisk(tab, callback);
return; return;
} }
// Reading thumbnail from disk is faster than taking screenshot from live Tab, so fetch // Reading thumbnail from disk is faster than taking screenshot from live Tab, so fetch
// that first even if |forceUpdate|. // that first even if |forceUpdate|.
nativeGetTabThumbnailWithCallback(mNativeTabContentManager, tab.getId(), (diskBitmap) -> { getTabThumbnailFromDisk(tab, (diskBitmap) -> {
callback.onResult(diskBitmap); callback.onResult(diskBitmap);
captureDownsampledThumbnail(tab, (bitmap) -> { captureDownsampledThumbnail(tab, (bitmap) -> {
// Null check to avoid having a Bitmap from nativeGetTabThumbnailWithCallback() but // Null check to avoid having a Bitmap from nativeGetTabThumbnailWithCallback() but
...@@ -297,6 +302,28 @@ public class TabContentManager { ...@@ -297,6 +302,28 @@ public class TabContentManager {
}); });
} }
private void getTabThumbnailFromDisk(@NonNull Tab tab, @NonNull Callback<Bitmap> callback) {
// Try JPEG thumbnail first before using the more costly nativeGetTabThumbnailWithCallback.
new AsyncTask<Bitmap>() {
@Override
public Bitmap doInBackground() {
File file = new File(PathUtils.getThumbnailCacheDirectory(), tab.getId() + ".jpeg");
if (!file.isFile()) return null;
return BitmapFactory.decodeFile(file.getPath());
}
@Override
public void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
callback.onResult(bitmap);
return;
}
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
nativeGetTabThumbnailWithCallback(mNativeTabContentManager, tab.getId(), callback);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/** /**
* Cache the content of a tab as a thumbnail. * Cache the content of a tab as a thumbnail.
* @param tab The tab whose content we will cache. * @param tab The tab whose content we will cache.
...@@ -421,7 +448,8 @@ public class TabContentManager { ...@@ -421,7 +448,8 @@ public class TabContentManager {
// Class Object Methods // Class Object Methods
private native long nativeInit(int defaultCacheSize, int approximationCacheSize, private native long nativeInit(int defaultCacheSize, int approximationCacheSize,
int compressionQueueMaxSize, int writeQueueMaxSize, boolean useApproximationThumbnail); int compressionQueueMaxSize, int writeQueueMaxSize, boolean useApproximationThumbnail,
boolean saveJpegThumbnails);
private native void nativeAttachTab(long nativeTabContentManager, Tab tab, int tabId); private native void nativeAttachTab(long nativeTabContentManager, Tab tab, int tabId);
private native void nativeDetachTab(long nativeTabContentManager, Tab tab, int tabId); private native void nativeDetachTab(long nativeTabContentManager, Tab tab, int tabId);
private native boolean nativeHasFullCachedThumbnail(long nativeTabContentManager, int tabId); private native boolean nativeHasFullCachedThumbnail(long nativeTabContentManager, int tabId);
......
...@@ -115,13 +115,15 @@ TabContentManager::TabContentManager(JNIEnv* env, ...@@ -115,13 +115,15 @@ TabContentManager::TabContentManager(JNIEnv* env,
jint approximation_cache_size, jint approximation_cache_size,
jint compression_queue_max_size, jint compression_queue_max_size,
jint write_queue_max_size, jint write_queue_max_size,
jboolean use_approximation_thumbnail) jboolean use_approximation_thumbnail,
jboolean save_jpeg_thumbnails)
: weak_java_tab_content_manager_(env, obj), weak_factory_(this) { : weak_java_tab_content_manager_(env, obj), weak_factory_(this) {
thumbnail_cache_ = std::make_unique<ThumbnailCache>( thumbnail_cache_ = std::make_unique<ThumbnailCache>(
static_cast<size_t>(default_cache_size), static_cast<size_t>(default_cache_size),
static_cast<size_t>(approximation_cache_size), static_cast<size_t>(approximation_cache_size),
static_cast<size_t>(compression_queue_max_size), static_cast<size_t>(compression_queue_max_size),
static_cast<size_t>(write_queue_max_size), use_approximation_thumbnail); static_cast<size_t>(write_queue_max_size), use_approximation_thumbnail,
save_jpeg_thumbnails);
thumbnail_cache_->AddThumbnailCacheObserver(this); thumbnail_cache_->AddThumbnailCacheObserver(this);
} }
...@@ -435,11 +437,12 @@ jlong JNI_TabContentManager_Init(JNIEnv* env, ...@@ -435,11 +437,12 @@ jlong JNI_TabContentManager_Init(JNIEnv* env,
jint approximation_cache_size, jint approximation_cache_size,
jint compression_queue_max_size, jint compression_queue_max_size,
jint write_queue_max_size, jint write_queue_max_size,
jboolean use_approximation_thumbnail) { jboolean use_approximation_thumbnail,
jboolean save_jpeg_thumbnails) {
TabContentManager* manager = new TabContentManager( TabContentManager* manager = new TabContentManager(
env, obj, default_cache_size, approximation_cache_size, env, obj, default_cache_size, approximation_cache_size,
compression_queue_max_size, write_queue_max_size, compression_queue_max_size, write_queue_max_size,
use_approximation_thumbnail); use_approximation_thumbnail, save_jpeg_thumbnails);
return reinterpret_cast<intptr_t>(manager); return reinterpret_cast<intptr_t>(manager);
} }
......
...@@ -45,7 +45,8 @@ class TabContentManager : public ThumbnailCacheObserver { ...@@ -45,7 +45,8 @@ class TabContentManager : public ThumbnailCacheObserver {
jint approximation_cache_size, jint approximation_cache_size,
jint compression_queue_max_size, jint compression_queue_max_size,
jint write_queue_max_size, jint write_queue_max_size,
jboolean use_approximation_thumbnail); jboolean use_approximation_thumbnail,
jboolean save_jpeg_thumbnails);
virtual ~TabContentManager(); virtual ~TabContentManager();
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/command_buffer/service/gpu_switches.h"
#include "skia/ext/image_operations.h"
#include "third_party/android_opengl/etc1/etc1.h" #include "third_party/android_opengl/etc1/etc1.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkCanvas.h"
...@@ -32,6 +33,7 @@ ...@@ -32,6 +33,7 @@
#include "ui/android/resources/ui_resource_provider.h" #include "ui/android/resources/ui_resource_provider.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/size_conversions.h"
namespace { namespace {
...@@ -123,12 +125,14 @@ ThumbnailCache::ThumbnailCache(size_t default_cache_size, ...@@ -123,12 +125,14 @@ ThumbnailCache::ThumbnailCache(size_t default_cache_size,
size_t approximation_cache_size, size_t approximation_cache_size,
size_t compression_queue_max_size, size_t compression_queue_max_size,
size_t write_queue_max_size, size_t write_queue_max_size,
bool use_approximation_thumbnail) bool use_approximation_thumbnail,
bool save_jpeg_thumbnails)
: file_sequenced_task_runner_( : file_sequenced_task_runner_(
base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})), base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
compression_queue_max_size_(compression_queue_max_size), compression_queue_max_size_(compression_queue_max_size),
write_queue_max_size_(write_queue_max_size), write_queue_max_size_(write_queue_max_size),
use_approximation_thumbnail_(use_approximation_thumbnail), use_approximation_thumbnail_(use_approximation_thumbnail),
save_jpeg_thumbnails_(save_jpeg_thumbnails),
compression_tasks_count_(0), compression_tasks_count_(0),
write_tasks_count_(0), write_tasks_count_(0),
read_in_progress_(false), read_in_progress_(false),
...@@ -263,6 +267,10 @@ base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) { ...@@ -263,6 +267,10 @@ base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) {
return path.Append(base::NumberToString(tab_id)); return path.Append(base::NumberToString(tab_id));
} }
base::FilePath ThumbnailCache::GetJpegFilePath(TabId tab_id) {
return GetFilePath(tab_id).AddExtension(".jpeg");
}
bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id, bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id,
const GURL& url) { const GURL& url) {
base::Time current_time = base::Time::Now(); base::Time current_time = base::Time::Now();
...@@ -351,6 +359,9 @@ void ThumbnailCache::RemoveFromDiskTask(TabId tab_id) { ...@@ -351,6 +359,9 @@ void ThumbnailCache::RemoveFromDiskTask(TabId tab_id) {
base::FilePath file_path = GetFilePath(tab_id); base::FilePath file_path = GetFilePath(tab_id);
if (base::PathExists(file_path)) if (base::PathExists(file_path))
base::DeleteFile(file_path, false); base::DeleteFile(file_path, false);
base::FilePath jpeg_file_path = GetJpegFilePath(tab_id);
if (base::PathExists(jpeg_file_path))
base::DeleteFile(jpeg_file_path, false);
} }
void ThumbnailCache::WriteThumbnailIfNecessary( void ThumbnailCache::WriteThumbnailIfNecessary(
...@@ -371,6 +382,23 @@ void ThumbnailCache::WriteThumbnailIfNecessary( ...@@ -371,6 +382,23 @@ void ThumbnailCache::WriteThumbnailIfNecessary(
content_size, post_write_task)); content_size, post_write_task));
} }
void ThumbnailCache::WriteJpegThumbnailIfNecessary(
TabId tab_id,
std::vector<uint8_t> compressed_data) {
if (compressed_data.empty())
return;
if (write_tasks_count_ >= write_queue_max_size_)
return;
write_tasks_count_++;
base::Callback<void()> post_write_task =
base::Bind(&ThumbnailCache::PostWriteTask, weak_factory_.GetWeakPtr());
file_sequenced_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ThumbnailCache::WriteJpegTask, tab_id,
std::move(compressed_data), post_write_task));
}
void ThumbnailCache::CompressThumbnailIfNecessary( void ThumbnailCache::CompressThumbnailIfNecessary(
TabId tab_id, TabId tab_id,
const base::Time& time_stamp, const base::Time& time_stamp,
...@@ -400,6 +428,19 @@ void ThumbnailCache::CompressThumbnailIfNecessary( ...@@ -400,6 +428,19 @@ void ThumbnailCache::CompressThumbnailIfNecessary(
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&ThumbnailCache::CompressionTask, bitmap, encoded_size, base::BindOnce(&ThumbnailCache::CompressionTask, bitmap, encoded_size,
post_compression_task)); post_compression_task));
if (save_jpeg_thumbnails_) {
base::Callback<void(std::vector<uint8_t>)> post_jpeg_compression_task =
base::Bind(&ThumbnailCache::WriteJpegThumbnailIfNecessary,
weak_factory_.GetWeakPtr(), tab_id);
base::PostTaskWithTraits(
FROM_HERE,
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&ThumbnailCache::JpegProcessingTask, bitmap,
post_jpeg_compression_task));
}
} }
void ThumbnailCache::ReadNextThumbnail() { void ThumbnailCache::ReadNextThumbnail() {
...@@ -569,6 +610,32 @@ void ThumbnailCache::WriteTask(TabId tab_id, ...@@ -569,6 +610,32 @@ void ThumbnailCache::WriteTask(TabId tab_id,
post_write_task); post_write_task);
} }
void ThumbnailCache::WriteJpegTask(
TabId tab_id,
std::vector<uint8_t> compressed_data,
const base::Callback<void()>& post_write_task) {
DCHECK(!compressed_data.empty());
base::FilePath file_path = GetJpegFilePath(tab_id);
base::File file(file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
bool success = file.IsValid();
if (success) {
int bytes_written =
file.Write(0, reinterpret_cast<const char*>(compressed_data.data()),
compressed_data.size());
success &= bytes_written == static_cast<int>(compressed_data.size());
file.Close();
}
if (!success)
base::DeleteFile(file_path, false);
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
post_write_task);
}
void ThumbnailCache::PostWriteTask() { void ThumbnailCache::PostWriteTask() {
write_tasks_count_--; write_tasks_count_--;
} }
...@@ -619,6 +686,33 @@ void ThumbnailCache::CompressionTask( ...@@ -619,6 +686,33 @@ void ThumbnailCache::CompressionTask(
content_size)); content_size));
} }
void ThumbnailCache::JpegProcessingTask(
SkBitmap bitmap,
const base::Callback<void(std::vector<uint8_t>)>& post_processing_task) {
// In portrait mode, we want to show thumbnails in squares.
// Therefore, the thumbnail saved in portrait mode needs to be cropped to
// a square, or it would be vertically center-aligned, and the top would
// be hidden.
// It's fine to horizontally center-align thumbnail saved in landscape
// mode.
int scale = 2;
SkIRect dest_subset = {0, 0, bitmap.width() / scale,
std::min(bitmap.width(), bitmap.height()) / scale};
SkBitmap result_bitmap = skia::ImageOperations::Resize(
bitmap, skia::ImageOperations::RESIZE_BETTER, bitmap.width() / scale,
bitmap.height() / scale, dest_subset);
constexpr int kCompressionQuality = 97;
std::vector<uint8_t> data;
const bool result =
gfx::JPEGCodec::Encode(result_bitmap, kCompressionQuality, &data);
DCHECK(result);
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(post_processing_task, std::move(data)));
}
void ThumbnailCache::PostCompressionTask( void ThumbnailCache::PostCompressionTask(
TabId tab_id, TabId tab_id,
const base::Time& time_stamp, const base::Time& time_stamp,
......
...@@ -47,7 +47,8 @@ class ThumbnailCache : ThumbnailDelegate { ...@@ -47,7 +47,8 @@ class ThumbnailCache : ThumbnailDelegate {
size_t approximation_cache_size, size_t approximation_cache_size,
size_t compression_queue_max_size, size_t compression_queue_max_size,
size_t write_queue_max_size, size_t write_queue_max_size,
bool use_approximation_thumbnail); bool use_approximation_thumbnail,
bool save_jpeg_thumbnails);
~ThumbnailCache() override; ~ThumbnailCache() override;
...@@ -77,6 +78,7 @@ class ThumbnailCache : ThumbnailDelegate { ...@@ -77,6 +78,7 @@ class ThumbnailCache : ThumbnailDelegate {
void InvalidateCachedThumbnail(Thumbnail* thumbnail) override; void InvalidateCachedThumbnail(Thumbnail* thumbnail) override;
static base::FilePath GetCacheDirectory(); static base::FilePath GetCacheDirectory();
static base::FilePath GetFilePath(TabId tab_id); static base::FilePath GetFilePath(TabId tab_id);
static base::FilePath GetJpegFilePath(TabId tab_id);
private: private:
class ThumbnailMetaData { class ThumbnailMetaData {
...@@ -100,6 +102,8 @@ class ThumbnailCache : ThumbnailDelegate { ...@@ -100,6 +102,8 @@ class ThumbnailCache : ThumbnailDelegate {
sk_sp<SkPixelRef> compressed_data, sk_sp<SkPixelRef> compressed_data,
float scale, float scale,
const gfx::Size& content_size); const gfx::Size& content_size);
void WriteJpegThumbnailIfNecessary(TabId tab_id,
std::vector<uint8_t> compressed_data);
void CompressThumbnailIfNecessary(TabId tab_id, void CompressThumbnailIfNecessary(TabId tab_id,
const base::Time& time_stamp, const base::Time& time_stamp,
const SkBitmap& bitmap, const SkBitmap& bitmap,
...@@ -112,12 +116,18 @@ class ThumbnailCache : ThumbnailDelegate { ...@@ -112,12 +116,18 @@ class ThumbnailCache : ThumbnailDelegate {
float scale, float scale,
const gfx::Size& content_size, const gfx::Size& content_size,
const base::Callback<void()>& post_write_task); const base::Callback<void()>& post_write_task);
static void WriteJpegTask(TabId tab_id,
std::vector<uint8_t> compressed_data,
const base::Callback<void()>& post_write_task);
void PostWriteTask(); void PostWriteTask();
static void CompressionTask( static void CompressionTask(
SkBitmap raw_data, SkBitmap raw_data,
gfx::Size encoded_size, gfx::Size encoded_size,
const base::Callback<void(sk_sp<SkPixelRef>, const gfx::Size&)>& const base::Callback<void(sk_sp<SkPixelRef>, const gfx::Size&)>&
post_compression_task); post_compression_task);
static void JpegProcessingTask(
SkBitmap bitmap,
const base::Callback<void(std::vector<uint8_t>)>& post_processing_task);
void PostCompressionTask(TabId tab_id, void PostCompressionTask(TabId tab_id,
const base::Time& time_stamp, const base::Time& time_stamp,
float scale, float scale,
...@@ -152,6 +162,7 @@ class ThumbnailCache : ThumbnailDelegate { ...@@ -152,6 +162,7 @@ class ThumbnailCache : ThumbnailDelegate {
const size_t compression_queue_max_size_; const size_t compression_queue_max_size_;
const size_t write_queue_max_size_; const size_t write_queue_max_size_;
const bool use_approximation_thumbnail_; const bool use_approximation_thumbnail_;
const bool save_jpeg_thumbnails_;
size_t compression_tasks_count_; size_t compression_tasks_count_;
size_t write_tasks_count_; size_t write_tasks_count_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment