Commit 6c92cc0f authored by Dana Fried's avatar Dana Fried Committed by Commit Bot

ThumbnailImage observers can listen on compressed data.

Exactly what it says on the tin. Compressed JPEG data is now available.
Expected to be used for Mohnstrudel tabstrip.

Change-Id: I523ad8a65a38b7a6c66bc462c8f78ca7d5012318
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1817583Reviewed-by: default avatarTaylor Bergquist <tbergquist@chromium.org>
Commit-Queue: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/master@{#700445}
parent 14d59e19
......@@ -10,26 +10,11 @@
#include "base/task/task_traits.h"
#include "ui/gfx/codec/jpeg_codec.h"
namespace {
void ThumbnailImage::Observer::OnThumbnailImageAvailable(
gfx::ImageSkia thumbnail_image) {}
std::vector<uint8_t> SkBitmapToJPEGData(SkBitmap bitmap) {
constexpr int kCompressionQuality = 97;
std::vector<uint8_t> data;
const bool result =
gfx::JPEGCodec::Encode(bitmap, kCompressionQuality, &data);
DCHECK(result);
return data;
}
gfx::ImageSkia JPEGDataToImageSkia(
scoped_refptr<base::RefCountedData<std::vector<uint8_t>>> data) {
gfx::ImageSkia result = gfx::ImageSkia::CreateFrom1xBitmap(
*gfx::JPEGCodec::Decode(data->data.data(), data->data.size()));
result.MakeThreadSafe();
return result;
}
} // namespace
void ThumbnailImage::Observer::OnCompressedThumbnailDataAvailable(
CompressedThumbnailData thumbnail_data) {}
ThumbnailImage::Delegate::~Delegate() {
if (thumbnail_)
......@@ -79,7 +64,7 @@ void ThumbnailImage::AssignSkBitmap(SkBitmap bitmap) {
FROM_HERE,
{base::ThreadPool(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&SkBitmapToJPEGData, std::move(bitmap)),
base::BindOnce(&ThumbnailImage::CompressBitmap, std::move(bitmap)),
base::BindOnce(&ThumbnailImage::AssignJPEGData,
weak_ptr_factory_.GetWeakPtr()));
}
......@@ -92,6 +77,7 @@ void ThumbnailImage::RequestThumbnailImage() {
void ThumbnailImage::AssignJPEGData(std::vector<uint8_t> data) {
data_ = base::MakeRefCounted<base::RefCountedData<std::vector<uint8_t>>>(
std::move(data));
NotifyCompressedDataObservers(data_);
ConvertJPEGDataToImageSkiaAndNotifyObservers();
}
......@@ -105,15 +91,42 @@ bool ThumbnailImage::ConvertJPEGDataToImageSkiaAndNotifyObservers() {
FROM_HERE,
{base::ThreadPool(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&JPEGDataToImageSkia, data_),
base::BindOnce(&ThumbnailImage::NotifyObservers,
base::BindOnce(&ThumbnailImage::UncompressImage, data_),
base::BindOnce(&ThumbnailImage::NotifyUncompressedDataObservers,
weak_ptr_factory_.GetWeakPtr()));
}
void ThumbnailImage::NotifyObservers(gfx::ImageSkia image) {
void ThumbnailImage::NotifyUncompressedDataObservers(gfx::ImageSkia image) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (async_operation_finished_callback_)
async_operation_finished_callback_.Run();
for (auto& observer : observers_)
observer.OnThumbnailImageAvailable(image);
}
void ThumbnailImage::NotifyCompressedDataObservers(
CompressedThumbnailData data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& observer : observers_)
observer.OnCompressedThumbnailDataAvailable(data);
}
// static
std::vector<uint8_t> ThumbnailImage::CompressBitmap(SkBitmap bitmap) {
constexpr int kCompressionQuality = 97;
std::vector<uint8_t> data;
const bool result =
gfx::JPEGCodec::Encode(bitmap, kCompressionQuality, &data);
DCHECK(result);
return data;
}
// static
gfx::ImageSkia ThumbnailImage::UncompressImage(
CompressedThumbnailData compressed) {
gfx::ImageSkia result =
gfx::ImageSkia::CreateFrom1xBitmap(*gfx::JPEGCodec::Decode(
compressed->data.data(), compressed->data.size()));
result.MakeThreadSafe();
return result;
}
......@@ -19,11 +19,21 @@
// uncompressed image to observers.
class ThumbnailImage : public base::RefCounted<ThumbnailImage> {
public:
// Observes uncompressed versions of the thumbnail image as they are
// available.
// Smart pointer to reference-counted compressed image data; in this case
// JPEG format.
using CompressedThumbnailData =
scoped_refptr<base::RefCountedData<std::vector<uint8_t>>>;
// Observes uncompressed and/or compressed versions of the thumbnail image as
// they are available.
class Observer : public base::CheckedObserver {
public:
virtual void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image) = 0;
// Receives uncompressed thumbnail image data. Default is no-op.
virtual void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image);
// Receives compressed thumbnail image data. Default is no-op.
virtual void OnCompressedThumbnailDataAvailable(
CompressedThumbnailData thumbnail_data);
};
// Represents the endpoint
......@@ -63,17 +73,22 @@ class ThumbnailImage : public base::RefCounted<ThumbnailImage> {
private:
friend class Delegate;
friend class ThumbnailImageTest;
friend class base::RefCounted<ThumbnailImage>;
virtual ~ThumbnailImage();
void AssignJPEGData(std::vector<uint8_t> data);
bool ConvertJPEGDataToImageSkiaAndNotifyObservers();
void NotifyObservers(gfx::ImageSkia image);
void NotifyUncompressedDataObservers(gfx::ImageSkia image);
void NotifyCompressedDataObservers(CompressedThumbnailData data);
static std::vector<uint8_t> CompressBitmap(SkBitmap bitmap);
static gfx::ImageSkia UncompressImage(CompressedThumbnailData compressed);
Delegate* delegate_;
scoped_refptr<base::RefCountedData<std::vector<uint8_t>>> data_;
CompressedThumbnailData data_;
base::ObserverList<Observer> observers_;
......
......@@ -15,12 +15,15 @@
#include "ui/gfx/image/image_skia.h"
namespace {
constexpr int kTestBitmapWidth = 200;
constexpr int kTestBitmapHeight = 123;
// Waits for thumbnail images and can report how many images it has received.
// Waits for thumbnail images (or compressed data) and can report how many
// images it has received.
class TestThumbnailImageObserver : public ThumbnailImage::Observer {
public:
// Wait for the uncompressed thumbnail image.
void WaitForImage() {
if (new_image_count_ > last_image_count_) {
last_image_count_ = new_image_count_;
......@@ -29,12 +32,29 @@ class TestThumbnailImageObserver : public ThumbnailImage::Observer {
// Need a fresh loop since we may have quit out of the last one.
run_loop_ = std::make_unique<base::RunLoop>();
waiting_ = true;
waiting_for_image_ = true;
run_loop_->Run();
}
// Wait for compressed thumbnail data.
void WaitForCompressedData() {
if (new_compressed_count_ > last_compressed_count_) {
last_compressed_count_ = new_compressed_count_;
return;
}
// Need a fresh loop since we may have quit out of the last one.
run_loop_ = std::make_unique<base::RunLoop>();
waiting_for_data_ = true;
run_loop_->Run();
}
int new_image_count() const { return new_image_count_; }
gfx::ImageSkia thumbnail_image() const { return thumbnail_image_; }
int new_compressed_count() const { return new_compressed_count_; }
ThumbnailImage::CompressedThumbnailData compressed_data() const {
return compressed_data_;
}
ScopedObserver<ThumbnailImage, ThumbnailImage::Observer>* scoped_observer() {
return &scoped_observer_;
......@@ -45,10 +65,21 @@ class TestThumbnailImageObserver : public ThumbnailImage::Observer {
void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image) override {
++new_image_count_;
thumbnail_image_ = thumbnail_image;
if (waiting_) {
if (waiting_for_image_) {
last_image_count_ = new_image_count_;
run_loop_->Quit();
waiting_ = false;
waiting_for_image_ = false;
}
}
void OnCompressedThumbnailDataAvailable(
ThumbnailImage::CompressedThumbnailData thumbnail_data) override {
++new_compressed_count_;
compressed_data_ = thumbnail_data;
if (waiting_for_data_) {
last_compressed_count_ = new_compressed_count_;
run_loop_->Quit();
waiting_for_data_ = false;
}
}
......@@ -57,11 +88,15 @@ class TestThumbnailImageObserver : public ThumbnailImage::Observer {
int new_image_count_ = 0;
int last_image_count_ = 0;
gfx::ImageSkia thumbnail_image_;
bool waiting_ = false;
int new_compressed_count_ = 0;
int last_compressed_count_ = 0;
ThumbnailImage::CompressedThumbnailData compressed_data_;
bool waiting_for_image_ = false;
bool waiting_for_data_ = false;
std::unique_ptr<base::RunLoop> run_loop_;
};
} // namespace
} // anonymous namespace
class ThumbnailImageTest : public testing::Test,
public ThumbnailImage::Delegate {
......@@ -76,6 +111,10 @@ class ThumbnailImageTest : public testing::Test,
return bitmap;
}
std::vector<uint8_t> Compress(SkBitmap bitmap) const {
return ThumbnailImage::CompressBitmap(bitmap);
}
bool is_being_observed() const { return is_being_observed_; }
private:
......@@ -161,6 +200,51 @@ TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesObserversAgain) {
observer.thumbnail_image().size());
}
TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesCompressedObservers) {
auto image = base::MakeRefCounted<ThumbnailImage>(this);
TestThumbnailImageObserver observer;
TestThumbnailImageObserver observer2;
observer.scoped_observer()->Add(image.get());
observer2.scoped_observer()->Add(image.get());
SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
auto compressed = Compress(bitmap);
image->AssignSkBitmap(bitmap);
observer.WaitForCompressedData();
observer2.WaitForCompressedData();
EXPECT_EQ(1, observer.new_compressed_count());
EXPECT_EQ(1, observer2.new_compressed_count());
EXPECT_TRUE(observer.compressed_data());
EXPECT_TRUE(observer2.compressed_data());
EXPECT_EQ(compressed, observer.compressed_data()->data);
EXPECT_EQ(compressed, observer2.compressed_data()->data);
}
TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesCompressedObserversAgain) {
auto image = base::MakeRefCounted<ThumbnailImage>(this);
TestThumbnailImageObserver observer;
TestThumbnailImageObserver observer2;
observer.scoped_observer()->Add(image.get());
observer2.scoped_observer()->Add(image.get());
SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
auto compressed = Compress(bitmap);
image->AssignSkBitmap(bitmap);
observer.WaitForCompressedData();
observer2.WaitForCompressedData();
image->AssignSkBitmap(bitmap);
observer.WaitForCompressedData();
observer2.WaitForCompressedData();
EXPECT_EQ(2, observer.new_compressed_count());
EXPECT_EQ(2, observer2.new_compressed_count());
EXPECT_TRUE(observer.compressed_data());
EXPECT_TRUE(observer2.compressed_data());
EXPECT_EQ(compressed, observer.compressed_data()->data);
EXPECT_EQ(compressed, observer2.compressed_data()->data);
}
TEST_F(ThumbnailImageTest, RequestThumbnailImage) {
auto image = base::MakeRefCounted<ThumbnailImage>(this);
TestThumbnailImageObserver observer;
......
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