b6438b1ca6462f3cc014cd1bf18410957101a119 - chromium/src

20 views
Skip to first unread message

esec...@chromium.org

unread,
Jan 24, 2017, 10:19:51 AM1/24/17
to chromium...@chromium.org
commit b6438b1ca6462f3cc014cd1bf18410957101a119
Author: eseckler <esec...@chromium.org>
AuthorDate: Tue Jan 24 15:18:55 2017
Commit: Commit bot <commi...@chromium.org>
CommitDate: Tue Jan 24 15:18:55 2017

[devtools] Support different encodings for Page.CaptureScreenshot.

This adds support for jpeg encoding of screenshots, similar to that for
screencasts. For this purpose, the patch modifies ui::Grab*Snapshot*
methods to return a gfx::Image instead of a png. Utility methods can
then encode the gfx::Image to jpeg/png depending on its
platform-specific backing representation.

BUG=676282

Review-Url: https://codereview.chromium.org/2592983002
Cr-Commit-Position: refs/heads/master@{#445730}

diff --git a/chrome/browser/android/feedback/screenshot_task.cc b/chrome/browser/android/feedback/screenshot_task.cc
index 4e8d3e2..0381101 100644
--- a/chrome/browser/android/feedback/screenshot_task.cc
+++ b/chrome/browser/android/feedback/screenshot_task.cc
@@ -31,7 +31,7 @@

void SnapshotCallback(JNIEnv* env,
const JavaRef<jobject>& callback,
- scoped_refptr<base::RefCountedBytes> png_data) {
+ scoped_refptr<base::RefCountedMemory> png_data) {
jbyteArray jbytes = nullptr;
if (png_data.get()) {
size_t size = png_data->size();
@@ -50,7 +50,7 @@
WindowAndroid* window_android = reinterpret_cast<WindowAndroid*>(
native_window_android);
gfx::Rect window_bounds(window_width, window_height);
- ui::GrabWindowSnapshotAsync(
+ ui::GrabWindowSnapshotAsyncPNG(
window_android, window_bounds, base::ThreadTaskRunnerHandle::Get(),
base::Bind(&SnapshotCallback, env,
ScopedJavaGlobalRef<jobject>(env, jcallback)));
diff --git a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
index 4849de4..92dc833 100644
--- a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
+++ b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.cc
@@ -187,10 +187,9 @@
LOG(ERROR) << "Can't create directory" << image_path.DirName().value();
return false;
}
- if (static_cast<size_t>(
- base::WriteFile(image_path,
- reinterpret_cast<char*>(&(png_data->data()[0])),
- png_data->size())) != png_data->size()) {
+ if (static_cast<size_t>(base::WriteFile(
+ image_path, reinterpret_cast<const char*>(png_data->front()),
+ png_data->size())) != png_data->size()) {
LOG(ERROR) << "Can't save screenshot " << image_path.BaseName().value()
<< ".";
return false;
@@ -200,10 +199,9 @@
return true;
}

-void ScreenshotTester::ReturnScreenshot(const PNGFile& screenshot,
- PNGFile png_data) {
+void ScreenshotTester::ReturnScreenshot(PNGFile* screenshot, PNGFile png_data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- screenshot->data() = png_data->data();
+ *screenshot = png_data;
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, run_loop_quitter_);
}
@@ -212,13 +210,11 @@
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
aura::Window* primary_window = ash::Shell::GetPrimaryRootWindow();
gfx::Rect rect = primary_window->bounds();
- PNGFile screenshot = new base::RefCountedBytes;
- ui::GrabWindowSnapshotAsync(primary_window,
- rect,
- content::BrowserThread::GetBlockingPool(),
- base::Bind(&ScreenshotTester::ReturnScreenshot,
- weak_factory_.GetWeakPtr(),
- screenshot));
+ PNGFile screenshot;
+ ui::GrabWindowSnapshotAsyncPNG(
+ primary_window, rect, content::BrowserThread::GetBlockingPool(),
+ base::Bind(&ScreenshotTester::ReturnScreenshot,
+ weak_factory_.GetWeakPtr(), &screenshot));
base::RunLoop run_loop;
run_loop_quitter_ = run_loop.QuitClosure();
run_loop.Run();
@@ -240,7 +236,7 @@
if (golden_screenshot_size == -1) {
CHECK(false) << "Can't get golden screenshot size";
}
- PNGFile png_data = new base::RefCountedBytes;
+ scoped_refptr<base::RefCountedBytes> png_data = new base::RefCountedBytes;
png_data->data().resize(golden_screenshot_size);
base::ReadFile(image_path,
reinterpret_cast<char*>(&(png_data->data()[0])),
@@ -252,9 +248,7 @@
SkBitmap ScreenshotTester::ProcessImageForComparison(const PNGFile& image) {
CHECK(image.get());
SkBitmap current_bitmap;
- gfx::PNGCodec::Decode(reinterpret_cast<unsigned char*>(&(image->data()[0])),
- image->data().size(),
- &current_bitmap);
+ gfx::PNGCodec::Decode(image->front(), image->size(), &current_bitmap);
EraseIgnoredAreas(current_bitmap);
return current_bitmap;
}
@@ -329,11 +323,12 @@

testing_result.similarity = result.result;

- testing_result.diff_image = new base::RefCountedBytes;
- testing_result.diff_image->data().resize(result.rgbDiffBitmap.getSize());
- CHECK(gfx::PNGCodec::EncodeBGRASkBitmap(
- result.rgbDiffBitmap, false, &testing_result.diff_image->data()))
+ scoped_refptr<base::RefCountedBytes> diff_image(new base::RefCountedBytes);
+ diff_image->data().resize(result.rgbDiffBitmap.getSize());
+ CHECK(gfx::PNGCodec::EncodeBGRASkBitmap(result.rgbDiffBitmap, false,
+ &diff_image->data()))
<< "Could not encode difference to PNG";
+ testing_result.diff_image = diff_image;

return testing_result;
}
diff --git a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.h b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.h
index 6474157..eafd43e 100644
--- a/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.h
+++ b/chrome/browser/chromeos/login/screenshot_testing/screenshot_tester.h
@@ -24,7 +24,7 @@
ScreenshotTester();
virtual ~ScreenshotTester();

- typedef scoped_refptr<base::RefCountedBytes> PNGFile;
+ typedef scoped_refptr<base::RefCountedMemory> PNGFile;

// Contains the results of comparison
struct Result {
@@ -106,7 +106,7 @@
void LogComparisonResults(const ScreenshotTester::Result& result);

// Saves |png_data| as a current screenshot.
- void ReturnScreenshot(const PNGFile& screenshot, PNGFile png_data);
+ void ReturnScreenshot(PNGFile* screenshot, PNGFile png_data);

// Loads golden screenshot from the disk, assuming it lies at |image_path|.
// Fails if there is no such a file.
@@ -138,7 +138,7 @@
base::FilePath artifacts_dir_;

// |run_loop_quitter_| is used to stop waiting when
- // ui::GrabWindowSnapshotAsync completes.
+ // ui::GrabWindowSnapshotAsyncPNG completes.
base::Closure run_loop_quitter_;

// Is true when we're in test mode:
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc
index 2e0e156..249f3d9 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc
@@ -60,7 +60,7 @@
void RunStoreScreenshotOnTaskRunner(
const ui::GrabWindowSnapshotAsyncPNGCallback& store_screenshot_callback,
scoped_refptr<base::TaskRunner> task_runner,
- scoped_refptr<base::RefCountedBytes> png_data) {
+ scoped_refptr<base::RefCountedMemory> png_data) {
task_runner->PostTask(FROM_HERE,
base::Bind(store_screenshot_callback, png_data));
}
@@ -157,7 +157,7 @@

void DeviceCommandScreenshotJob::StoreScreenshot(
size_t screen,
- scoped_refptr<base::RefCountedBytes> png_data) {
+ scoped_refptr<base::RefCountedMemory> png_data) {
screenshots_.insert(std::make_pair(screen, png_data));
DCHECK_LT(0, num_pending_screenshots_);
--num_pending_screenshots_;
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h
index b1b588d..da124bd 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h
@@ -110,7 +110,7 @@
void TerminateImpl() override;

void StoreScreenshot(size_t screen,
- scoped_refptr<base::RefCountedBytes> png_data);
+ scoped_refptr<base::RefCountedMemory> png_data);

void StartScreenshotUpload();

@@ -128,7 +128,7 @@
int num_pending_screenshots_;

// Caches the already completed screenshots for the different displays.
- std::map<int, scoped_refptr<base::RefCountedBytes>> screenshots_;
+ std::map<int, scoped_refptr<base::RefCountedMemory>> screenshots_;

// The Delegate is used to acquire screenshots and create UploadJobs.
std::unique_ptr<Delegate> screenshot_delegate_;
diff --git a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
index fe9264c..eec0979 100644
--- a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.cc
@@ -42,7 +42,7 @@
gfx::NativeWindow window,
const gfx::Rect& source_rect,
const ui::GrabWindowSnapshotAsyncPNGCallback& callback) {
- ui::GrabWindowSnapshotAsync(
+ ui::GrabWindowSnapshotAsyncPNG(
window, source_rect, blocking_task_runner_,
base::Bind(&ScreenshotDelegate::StoreScreenshot,
weak_ptr_factory_.GetWeakPtr(), callback));
@@ -69,7 +69,7 @@

void ScreenshotDelegate::StoreScreenshot(
const ui::GrabWindowSnapshotAsyncPNGCallback& callback,
- scoped_refptr<base::RefCountedBytes> png_data) {
+ scoped_refptr<base::RefCountedMemory> png_data) {
callback.Run(png_data);
}

diff --git a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.h b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.h
index 1301fd2..6dbba85 100644
--- a/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.h
+++ b/chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.h
@@ -21,7 +21,7 @@
namespace policy {

// An implementation of the |DeviceCommandScreenshotJob::Delegate| that uses
-// aura's GrabWindowSnapshotAsync() to acquire the window snapshot.
+// aura's GrabWindowSnapshotAsyncPNG() to acquire the window snapshot.
class ScreenshotDelegate : public DeviceCommandScreenshotJob::Delegate {
public:
explicit ScreenshotDelegate(
@@ -40,7 +40,7 @@

private:
void StoreScreenshot(const ui::GrabWindowSnapshotAsyncPNGCallback& callback,
- scoped_refptr<base::RefCountedBytes> png_data);
+ scoped_refptr<base::RefCountedMemory> png_data);

scoped_refptr<base::TaskRunner> blocking_task_runner_;

diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index b7a73c0..50358c2 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -49,10 +49,12 @@
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/layout.h"
#include "ui/compositor/compositor_switches.h"
+#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/skia_util.h"
+#include "ui/snapshot/snapshot.h"

#define EXPECT_SIZE_EQ(expected, actual) \
do { \
@@ -531,10 +533,37 @@
bitmap);
}

+std::unique_ptr<SkBitmap> DecodeJPEG(std::string base64_data) {
+ std::string jpeg_data;
+ if (!base::Base64Decode(base64_data, &jpeg_data))
+ return nullptr;
+ return gfx::JPEGCodec::Decode(
+ reinterpret_cast<unsigned const char*>(jpeg_data.data()),
+ jpeg_data.size());
+}
+
+bool ColorsMatchWithinLimit(SkColor color1,
+ SkColor color2,
+ int32_t error_limit) {
+ auto a_distance = std::abs(static_cast<int32_t>(SkColorGetA(color1)) -
+ static_cast<int32_t>(SkColorGetA(color2)));
+ auto r_distance = std::abs(static_cast<int32_t>(SkColorGetR(color1)) -
+ static_cast<int32_t>(SkColorGetR(color2)));
+ auto g_distance = std::abs(static_cast<int32_t>(SkColorGetG(color1)) -
+ static_cast<int32_t>(SkColorGetG(color2)));
+ auto b_distance = std::abs(static_cast<int32_t>(SkColorGetB(color1)) -
+ static_cast<int32_t>(SkColorGetB(color2)));
+
+ return a_distance * a_distance + r_distance * r_distance +
+ g_distance * g_distance + b_distance * b_distance <=
+ error_limit * error_limit;
+}
+
// Adapted from cc::ExactPixelComparator.
bool MatchesBitmap(const SkBitmap& expected_bmp,
const SkBitmap& actual_bmp,
- const gfx::Rect& matching_mask) {
+ const gfx::Rect& matching_mask,
+ int error_limit) {
// Number of pixels with an error
int error_pixels_count = 0;

@@ -557,7 +586,7 @@
for (int y = matching_mask.y(); y < matching_mask.height(); ++y) {
SkColor actual_color = actual_bmp.getColor(x, y);
SkColor expected_color = expected_bmp.getColor(x, y);
- if (actual_color != expected_color) {
+ if (!ColorsMatchWithinLimit(actual_color, expected_color, error_limit)) {
if (error_pixels_count < 10) {
LOG(ERROR) << "Pixel (" << x << "," << y << "): expected "
<< expected_color << " actual " << actual_color;
@@ -580,19 +609,38 @@

class CaptureScreenshotTest : public DevToolsProtocolTest {
protected:
- void CaptureScreenshotAndCompareTo(const SkBitmap& expected_bitmap) {
- SendCommand("Page.captureScreenshot", nullptr);
+ enum ScreenshotEncoding { ENCODING_PNG, ENCODING_JPEG };
+ void CaptureScreenshotAndCompareTo(const SkBitmap& expected_bitmap,
+ ScreenshotEncoding encoding) {
+ std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+ params->SetString("format", encoding == ENCODING_PNG ? "png" : "jpeg");
+ params->SetInteger("quality", 100);
+ SendCommand("Page.captureScreenshot", std::move(params));
+
std::string base64;
EXPECT_TRUE(result_->GetString("data", &base64));
- SkBitmap result_bitmap;
- EXPECT_TRUE(DecodePNG(base64, &result_bitmap));
+ std::unique_ptr<SkBitmap> result_bitmap;
+ int error_limit = 0;
+ if (encoding == ENCODING_PNG) {
+ result_bitmap.reset(new SkBitmap());
+ EXPECT_TRUE(DecodePNG(base64, result_bitmap.get()));
+ } else {
+ result_bitmap = DecodeJPEG(base64);
+ // Even with quality 100, jpeg isn't lossless. So, we allow some skew in
+ // pixel values. Not that this assumes that there is no skew in pixel
+ // positions, so will only work reliably if all pixels have equal values.
+ error_limit = 3;
+ }
+ EXPECT_TRUE(result_bitmap);
+
gfx::Rect matching_mask(gfx::SkIRectToRect(expected_bitmap.bounds()));
#if defined(OS_MACOSX)
// Mask out the corners, which may be drawn differently on Mac because of
// rounded corners.
matching_mask.Inset(4, 4, 4, 4);
#endif
- EXPECT_TRUE(MatchesBitmap(expected_bitmap, result_bitmap, matching_mask));
+ EXPECT_TRUE(MatchesBitmap(expected_bitmap, *result_bitmap, matching_mask,
+ error_limit));
}

// Takes a screenshot of a colored box that is positioned inside the frame.
@@ -651,7 +699,7 @@
expected_bitmap.allocN32Pixels(scaled_box_size.width(),
scaled_box_size.height());
expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff));
- CaptureScreenshotAndCompareTo(expected_bitmap);
+ CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_PNG);

// Reset for next screenshot.
SendCommand("Emulation.resetViewport", nullptr);
@@ -669,13 +717,13 @@
IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, CaptureScreenshot) {
// This test fails consistently on low-end Android devices.
// See crbug.com/653637.
+ // TODO(eseckler): Reenable with error limit if necessary.
if (base::SysInfo::IsLowEndDevice()) return;

- shell()->LoadURL(GURL("about:blank"));
+ shell()->LoadURL(
+ GURL("data:text/html,<body style='background:#123456'></body>"));
+ WaitForLoadStop(shell()->web_contents());
Attach();
- EXPECT_TRUE(
- content::ExecuteScript(shell()->web_contents()->GetRenderViewHost(),
- "document.body.style.background = '#123456'"));
SkBitmap expected_bitmap;
// We compare against the actual physical backing size rather than the
// view size, because the view size is stored adjusted for DPI and only in
@@ -685,7 +733,30 @@
->GetPhysicalBackingSize();
expected_bitmap.allocN32Pixels(view_size.width(), view_size.height());
expected_bitmap.eraseColor(SkColorSetRGB(0x12, 0x34, 0x56));
- CaptureScreenshotAndCompareTo(expected_bitmap);
+ CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_PNG);
+}
+
+IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, CaptureScreenshotJpeg) {
+ // This test fails consistently on low-end Android devices.
+ // See crbug.com/653637.
+ // TODO(eseckler): Reenable with error limit if necessary.
+ if (base::SysInfo::IsLowEndDevice())
+ return;
+
+ shell()->LoadURL(
+ GURL("data:text/html,<body style='background:#123456'></body>"));
+ WaitForLoadStop(shell()->web_contents());
+ Attach();
+ SkBitmap expected_bitmap;
+ // We compare against the actual physical backing size rather than the
+ // view size, because the view size is stored adjusted for DPI and only in
+ // integer precision.
+ gfx::Size view_size = static_cast<RenderWidgetHostViewBase*>(
+ shell()->web_contents()->GetRenderWidgetHostView())
+ ->GetPhysicalBackingSize();
+ expected_bitmap.allocN32Pixels(view_size.width(), view_size.height());
+ expected_bitmap.eraseColor(SkColorSetRGB(0x12, 0x34, 0x56));
+ CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_JPEG);
}

// Setting frame size (through RWHV) is not supported on Android.
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index f7ec85b..740c0a2 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -9,6 +9,8 @@
#include "base/base64.h"
#include "base/bind.h"
#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
@@ -37,6 +39,8 @@
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_util.h"
#include "ui/snapshot/snapshot.h"
#include "url/gurl.h"

@@ -52,37 +56,27 @@
static int kCaptureRetryLimit = 2;
static int kMaxScreencastFramesInFlight = 2;

-std::string EncodeScreencastFrame(const SkBitmap& bitmap,
- const std::string& format,
- int quality) {
- std::vector<unsigned char> data;
- SkAutoLockPixels lock_image(bitmap);
- bool encoded;
+std::string EncodeImage(const gfx::Image& image,
+ const std::string& format,
+ int quality) {
+ DCHECK(!image.IsEmpty());
+
+ scoped_refptr<base::RefCountedMemory> data;
if (format == kPng) {
- encoded = gfx::PNGCodec::Encode(
- reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
- gfx::PNGCodec::FORMAT_SkBitmap,
- gfx::Size(bitmap.width(), bitmap.height()),
- bitmap.width() * bitmap.bytesPerPixel(),
- false, std::vector<gfx::PNGCodec::Comment>(), &data);
+ data = image.As1xPNGBytes();
} else if (format == kJpeg) {
- encoded = gfx::JPEGCodec::Encode(
- reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
- gfx::JPEGCodec::FORMAT_SkBitmap,
- bitmap.width(),
- bitmap.height(),
- bitmap.width() * bitmap.bytesPerPixel(),
- quality, &data);
- } else {
- encoded = false;
+ scoped_refptr<base::RefCountedBytes> bytes(new base::RefCountedBytes());
+ if (gfx::JPEG1xEncodedDataFromImage(image, quality, &bytes->data()))
+ data = bytes;
}

- if (!encoded)
+ if (!data || !data->front())
return std::string();

std::string base_64_data;
base::Base64Encode(
- base::StringPiece(reinterpret_cast<char*>(&data[0]), data.size()),
+ base::StringPiece(reinterpret_cast<const char*>(data->front()),
+ data->size()),
&base_64_data);

return base_64_data;
@@ -285,15 +279,21 @@
}

void PageHandler::CaptureScreenshot(
+ Maybe<std::string> format,
+ Maybe<int> quality,
std::unique_ptr<CaptureScreenshotCallback> callback) {
if (!host_ || !host_->GetRenderWidgetHost()) {
callback->sendFailure(Response::InternalError());
return;
}

+ std::string screenshot_format = format.fromMaybe(kPng);
+ int screenshot_quality = quality.fromMaybe(kDefaultScreenshotQuality);
+
host_->GetRenderWidgetHost()->GetSnapshotFromBrowser(
- base::Bind(&PageHandler::ScreenshotCaptured,
- weak_factory_.GetWeakPtr(), base::Passed(std::move(callback))));
+ base::Bind(&PageHandler::ScreenshotCaptured, weak_factory_.GetWeakPtr(),
+ base::Passed(std::move(callback)), screenshot_format,
+ screenshot_quality));
}

Response PageHandler::StartScreencast(Maybe<std::string> format,
@@ -519,8 +519,8 @@
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, base::TaskTraits().WithShutdownBehavior(
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
- base::Bind(&EncodeScreencastFrame, bitmap, screencast_format_,
- screencast_quality_),
+ base::Bind(&EncodeImage, gfx::Image::CreateFrom1xBitmap(bitmap),
+ screencast_format_, screencast_quality_),
base::Bind(&PageHandler::ScreencastFrameEncoded,
weak_factory_.GetWeakPtr(), base::Passed(&metadata),
base::Time::Now()));
@@ -561,18 +561,15 @@

void PageHandler::ScreenshotCaptured(
std::unique_ptr<CaptureScreenshotCallback> callback,
- const unsigned char* png_data,
- size_t png_size) {
- if (!png_data || !png_size) {
+ const std::string& format,
+ int quality,
+ const gfx::Image& image) {
+ if (image.IsEmpty()) {
callback->sendFailure(Response::Error("Unable to capture screenshot"));
return;
}

- std::string base_64_data;
- base::Base64Encode(
- base::StringPiece(reinterpret_cast<const char*>(png_data), png_size),
- &base_64_data);
- callback->sendSuccess(base_64_data);
+ callback->sendSuccess(EncodeImage(image, format, quality));
}

void PageHandler::OnColorPicked(int r, int g, int b, int a) {
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index fdb1ac5..5ebaa0c 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -20,6 +20,10 @@

class SkBitmap;

+namespace gfx {
+class Image;
+} // namespace gfx
+
namespace content {

class DevToolsSession;
@@ -65,6 +69,8 @@
Response NavigateToHistoryEntry(int entry_id) override;

void CaptureScreenshot(
+ Maybe<std::string> format,
+ Maybe<int> quality,
std::unique_ptr<CaptureScreenshotCallback> callback) override;
Response StartScreencast(Maybe<std::string> format,
Maybe<int> quality,
@@ -91,6 +97,8 @@
void NavigationRequested(const PageNavigationThrottle* throttle);

private:
+ enum EncodingFormat { PNG, JPEG };
+
WebContentsImpl* GetWebContents();
void NotifyScreencastVisibility(bool visible);
void InnerSwapCompositorFrame();
@@ -101,10 +109,10 @@
const base::Time& timestamp,
const std::string& data);

- void ScreenshotCaptured(
- std::unique_ptr<CaptureScreenshotCallback> callback,
- const unsigned char* png_data,
- size_t png_size);
+ void ScreenshotCaptured(std::unique_ptr<CaptureScreenshotCallback> callback,
+ const std::string& format,
+ int quality,
+ const gfx::Image& image);

void OnColorPicked(int r, int g, int b, int a);

diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 38be9707..23d1519 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -87,6 +87,7 @@
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
+#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/skbitmap_operations.h"
#include "ui/snapshot/snapshot.h"
@@ -2374,49 +2375,36 @@
gfx::Rect snapshot_bounds(GetView()->GetViewBounds().size());
#endif

- std::vector<unsigned char> png;
- if (ui::GrabViewSnapshot(
- GetView()->GetNativeView(), &png, snapshot_bounds)) {
- OnSnapshotDataReceived(snapshot_id, &png.front(), png.size());
+ gfx::Image image;
+ if (ui::GrabViewSnapshot(GetView()->GetNativeView(), snapshot_bounds,
+ &image)) {
+ OnSnapshotReceived(snapshot_id, image);
return;
}

ui::GrabViewSnapshotAsync(
- GetView()->GetNativeView(),
- snapshot_bounds,
- base::ThreadTaskRunnerHandle::Get(),
- base::Bind(&RenderWidgetHostImpl::OnSnapshotDataReceivedAsync,
- weak_factory_.GetWeakPtr(),
- snapshot_id));
+ GetView()->GetNativeView(), snapshot_bounds,
+ base::Bind(&RenderWidgetHostImpl::OnSnapshotReceived,
+ weak_factory_.GetWeakPtr(), snapshot_id));
}

-void RenderWidgetHostImpl::OnSnapshotDataReceived(int snapshot_id,
- const unsigned char* data,
- size_t size) {
+void RenderWidgetHostImpl::OnSnapshotReceived(int snapshot_id,
+ const gfx::Image& image) {
// Any pending snapshots with a lower ID than the one received are considered
// to be implicitly complete, and returned the same snapshot data.
PendingSnapshotMap::iterator it = pending_browser_snapshots_.begin();
while (it != pending_browser_snapshots_.end()) {
- if (it->first <= snapshot_id) {
- it->second.Run(data, size);
- pending_browser_snapshots_.erase(it++);
- } else {
- ++it;
- }
+ if (it->first <= snapshot_id) {
+ it->second.Run(image);
+ pending_browser_snapshots_.erase(it++);
+ } else {
+ ++it;
+ }
}
#if defined(OS_MACOSX)
if (pending_browser_snapshots_.empty())
power_save_blocker_.reset();
#endif
-}
-
-void RenderWidgetHostImpl::OnSnapshotDataReceivedAsync(
- int snapshot_id,
- scoped_refptr<base::RefCountedBytes> png_data) {
- if (png_data.get())
- OnSnapshotDataReceived(snapshot_id, png_data->front(), png_data->size());
- else
- OnSnapshotDataReceived(snapshot_id, NULL, 0);
}

// static
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 60cca7c..4e90a82 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -53,10 +53,6 @@
struct ViewHostMsg_SelectionBounds_Params;
struct ViewHostMsg_UpdateRect_Params;

-namespace base {
-class RefCountedBytes;
-}
-
namespace blink {
class WebInputEvent;
class WebMouseEvent;
@@ -70,6 +66,7 @@
#endif

namespace gfx {
+class Image;
class Range;
}

@@ -208,9 +205,12 @@
void NotifyScreenInfoChanged();

// Forces redraw in the renderer and when the update reaches the browser
- // grabs snapshot from the compositor. Returns PNG-encoded snapshot.
+ // grabs snapshot from the compositor. On MacOS, the snapshot is taken from
+ // the Cocoa view for end-to-end testing purposes. Returns a gfx::Image that
+ // is backed by an NSImage on MacOS or by an SkBitmap otherwise. The
+ // gfx::Image may be empty if the snapshot failed.
using GetSnapshotFromBrowserCallback =
- base::Callback<void(const unsigned char*, size_t)>;
+ base::Callback<void(const gfx::Image&)>;
void GetSnapshotFromBrowser(const GetSnapshotFromBrowserCallback& callback);

const NativeWebKeyboardEvent* GetLastKeyboardEvent() const;
@@ -701,13 +701,7 @@

void WindowSnapshotReachedScreen(int snapshot_id);

- void OnSnapshotDataReceived(int snapshot_id,
- const unsigned char* png,
- size_t size);
-
- void OnSnapshotDataReceivedAsync(
- int snapshot_id,
- scoped_refptr<base::RefCountedBytes> png_data);
+ void OnSnapshotReceived(int snapshot_id, const gfx::Image& image);

// 1. Grants permissions to URL (if any)
// 2. Grants permissions to filenames
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index e71cdc8..1780ab1 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -3125,6 +3125,12 @@
"swarming": {
"can_use_on_swarming_builders": true
},
+ "test": "snapshot_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
"test": "sql_unittests"
},
{
@@ -3715,6 +3721,12 @@
"swarming": {
"can_use_on_swarming_builders": true
},
+ "test": "snapshot_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
"test": "sql_unittests"
},
{
@@ -4102,6 +4114,12 @@
"swarming": {
"can_use_on_swarming_builders": true
},
+ "test": "snapshot_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
"test": "sql_unittests"
},
{
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 7213398..710920b 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -13,6 +13,7 @@
"pdf_unittests",
"printing_unittests",
"skia_unittests",
+ "snapshot_unittests",
"sql_unittests",
"storage_unittests",
"ui_base_unittests",
@@ -297,6 +298,12 @@
"can_use_on_swarming_builders": true
},
"test": "skia_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
+ "test": "snapshot_unittests"
},
{
"swarming": {
@@ -648,6 +655,12 @@
"can_use_on_swarming_builders": true
},
"test": "skia_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
+ "test": "snapshot_unittests"
},
{
"swarming": {
@@ -1005,6 +1018,12 @@
"swarming": {
"can_use_on_swarming_builders": true
},
+ "test": "snapshot_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
"test": "sql_unittests"
},
{
@@ -1345,6 +1364,12 @@
"swarming": {
"can_use_on_swarming_builders": true
},
+ "test": "snapshot_unittests"
+ },
+ {
+ "swarming": {
+ "can_use_on_swarming_builders": true
+ },
"test": "sql_unittests"
},
{
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index e6f84ad..a7f6d29 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -758,6 +758,10 @@
"label": "//skia:skia_unittests",
"type": "console_test_launcher",
},
+ "snapshot_unittests": {
+ "label": "//ui/snapshot:snapshot_unittests",
+ "type": "windowed_test_launcher",
+ },
"sql_unittests": {
"label": "//sql:sql_unittests",
"type": "console_test_launcher",
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index e25a9c2..e3cfff5 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -416,8 +416,12 @@
{
"name": "captureScreenshot",
"description": "Capture page screenshot.",
+ "parameters": [
+ { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format (defaults to png)." },
+ { "name": "quality", "type": "integer", "optional": true, "description": "Compression quality from range [0..100] (jpeg only)." }
+ ],
"returns": [
- { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." }
+ { "name": "data", "type": "string", "description": "Base64-encoded image data." }
],
"experimental": true
},
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeView.js b/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeView.js
index e554590..4fea93b 100644
--- a/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeView.js
@@ -376,7 +376,7 @@
this._model.deviceOutlineSetting().set(false);
}

- mainTarget.pageAgent().captureScreenshot(screenshotCaptured.bind(this));
+ mainTarget.pageAgent().captureScreenshot('png', 100, screenshotCaptured.bind(this));

/**
* @param {?Protocol.Error} error
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 461a41e..f1fdaa7c 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -119,6 +119,7 @@
"image/image_util.cc",
"image/image_util.h",
"image/image_util_ios.mm",
+ "image/image_util_mac.mm",
"interpolated_transform.cc",
"interpolated_transform.h",
"ios/NSString+CrStringDrawing.h",
diff --git a/ui/gfx/image/image_util.cc b/ui/gfx/image/image_util.cc
index d0d79b2..3fca705 100644
--- a/ui/gfx/image/image_util.cc
+++ b/ui/gfx/image/image_util.cc
@@ -44,8 +44,18 @@
return Image();
}

-bool JPEG1xEncodedDataFromImage(const Image& image, int quality,
+// The MacOS implementation of this function is in image_utils_mac.mm.
+#if !defined(OS_MACOSX)
+bool JPEG1xEncodedDataFromImage(const Image& image,
+ int quality,
std::vector<unsigned char>* dst) {
+ return JPEG1xEncodedDataFromSkiaRepresentation(image, quality, dst);
+}
+#endif // !defined(OS_MACOSX)
+
+bool JPEG1xEncodedDataFromSkiaRepresentation(const Image& image,
+ int quality,
+ std::vector<unsigned char>* dst) {
const gfx::ImageSkiaRep& image_skia_rep =
image.AsImageSkia().GetRepresentation(1.0f);
if (image_skia_rep.scale() != 1.0f)
diff --git a/ui/gfx/image/image_util.h b/ui/gfx/image/image_util.h
index 8f17c36..cee54a3 100644
--- a/ui/gfx/image/image_util.h
+++ b/ui/gfx/image/image_util.h
@@ -33,6 +33,10 @@
int quality,
std::vector<unsigned char>* dst);

+bool JPEG1xEncodedDataFromSkiaRepresentation(const Image& image,
+ int quality,
+ std::vector<unsigned char>* dst);
+
// Computes the width of any nearly-transparent regions at the sides of the
// image and returns them in |left| and |right|. This checks each column of
// pixels from the outsides in, looking for anything with alpha above a
diff --git a/ui/gfx/image/image_util_mac.mm b/ui/gfx/image/image_util_mac.mm
new file mode 100644
index 0000000..5be20b0
--- /dev/null
+++ b/ui/gfx/image/image_util_mac.mm
@@ -0,0 +1,40 @@
+// Copyright (c) 2017 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 "ui/gfx/image/image_util.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "ui/gfx/image/image.h"
+
+namespace gfx {
+
+bool JPEG1xEncodedDataFromImage(const Image& image,
+ int quality,
+ std::vector<unsigned char>* dst) {
+ if (!image.HasRepresentation(gfx::Image::kImageRepCocoa))
+ return JPEG1xEncodedDataFromSkiaRepresentation(image, quality, dst);
+
+ NSImage* nsImage = image.ToNSImage();
+
+ CGImageRef cgImage =
+ [nsImage CGImageForProposedRect:nil context:nil hints:nil];
+ base::scoped_nsobject<NSBitmapImageRep> rep(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgImage]);
+
+ float compressionFactor = quality / 100.0;
+ NSDictionary* options = @{ NSImageCompressionFactor : @(compressionFactor)};
+ NSData* data =
+ [rep representationUsingType:NSJPEGFileType properties:options];
+
+ if ([data length] == 0)
+ return false;
+
+ dst->resize([data length]);
+ [data getBytes:&dst->at(0) length:[data length]];
+ return true;
+}
+
+} // end namespace gfx
diff --git a/ui/snapshot/BUILD.gn b/ui/snapshot/BUILD.gn
index d6ae901..4685e1b 100644
--- a/ui/snapshot/BUILD.gn
+++ b/ui/snapshot/BUILD.gn
@@ -10,6 +10,7 @@
"screenshot_grabber.cc",
"screenshot_grabber.h",
"screenshot_grabber_observer.h",
+ "snapshot.cc",
"snapshot.h",
"snapshot_android.cc",
"snapshot_async.cc",
@@ -84,6 +85,7 @@
"//ui/base",
"//ui/compositor:test_support",
"//ui/gfx",
+ "//ui/gfx:test_support",
"//ui/gfx/geometry",
"//ui/gl",
]
diff --git a/ui/snapshot/screenshot_grabber.cc b/ui/snapshot/screenshot_grabber.cc
index 02e91df..da79cb2 100644
--- a/ui/snapshot/screenshot_grabber.cc
+++ b/ui/snapshot/screenshot_grabber.cc
@@ -43,7 +43,7 @@
void SaveScreenshot(scoped_refptr<base::TaskRunner> ui_task_runner,
const ShowNotificationCallback& callback,
const base::FilePath& screenshot_path,
- scoped_refptr<base::RefCountedBytes> png_data,
+ scoped_refptr<base::RefCountedMemory> png_data,
ScreenshotGrabberDelegate::FileResult result,
const base::FilePath& local_path) {
DCHECK(!base::MessageLoopForUI::IsCurrent());
@@ -57,7 +57,7 @@
// Successfully got a local file to write to, write png data.
DCHECK_GT(static_cast<int>(png_data->size()), 0);
if (static_cast<size_t>(base::WriteFile(
- local_path, reinterpret_cast<char*>(&(png_data->data()[0])),
+ local_path, reinterpret_cast<const char*>(png_data->front()),
static_cast<int>(png_data->size()))) != png_data->size()) {
LOG(ERROR) << "Failed to save to " << local_path.value();
screenshot_result =
@@ -168,7 +168,7 @@

cursor_hider_ = ScopedCursorHider::Create(aura_window->GetRootWindow());
#endif
- ui::GrabWindowSnapshotAsync(
+ ui::GrabWindowSnapshotAsyncPNG(
window, rect, blocking_task_runner_,
base::Bind(&ScreenshotGrabber::GrabWindowSnapshotAsyncCallback,
factory_.GetWeakPtr(), window_identifier, screenshot_path,
@@ -209,7 +209,7 @@
const std::string& window_identifier,
base::FilePath screenshot_path,
bool is_partial,
- scoped_refptr<base::RefCountedBytes> png_data) {
+ scoped_refptr<base::RefCountedMemory> png_data) {
DCHECK(base::MessageLoopForUI::IsCurrent());
if (!png_data.get()) {
if (is_partial) {
diff --git a/ui/snapshot/screenshot_grabber.h b/ui/snapshot/screenshot_grabber.h
index 555f863..7a0a947 100644
--- a/ui/snapshot/screenshot_grabber.h
+++ b/ui/snapshot/screenshot_grabber.h
@@ -84,7 +84,7 @@
const std::string& window_identifier,
base::FilePath screenshot_path,
bool is_partial,
- scoped_refptr<base::RefCountedBytes> png_data);
+ scoped_refptr<base::RefCountedMemory> png_data);

// A weak pointer to the screenshot taker client.
ScreenshotGrabberDelegate* client_;
diff --git a/ui/snapshot/snapshot.cc b/ui/snapshot/snapshot.cc
new file mode 100644
index 0000000..aaee01f
--- /dev/null
+++ b/ui/snapshot/snapshot.cc
@@ -0,0 +1,41 @@
+// Copyright 2017 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 "ui/snapshot/snapshot.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/task_runner_util.h"
+#include "ui/gfx/image/image.h"
+
+namespace ui {
+
+namespace {
+
+scoped_refptr<base::RefCountedMemory> EncodeImage(const gfx::Image& image) {
+ return image.As1xPNGBytes();
+}
+
+void EncodeImageAndSchedulePNGCallback(
+ scoped_refptr<base::TaskRunner> background_task_runner,
+ const GrabWindowSnapshotAsyncPNGCallback& callback,
+ const gfx::Image& image) {
+ base::PostTaskAndReplyWithResult(background_task_runner.get(), FROM_HERE,
+ base::Bind(EncodeImage, image), callback);
+}
+
+} // namespace
+
+void GrabWindowSnapshotAsyncPNG(
+ gfx::NativeWindow window,
+ const gfx::Rect& source_rect,
+ scoped_refptr<base::TaskRunner> background_task_runner,
+ const GrabWindowSnapshotAsyncPNGCallback& callback) {
+ GrabWindowSnapshotAsync(
+ window, source_rect,
+ base::Bind(EncodeImageAndSchedulePNGCallback,
+ std::move(background_task_runner), callback));
+}
+
+} // namespace ui
diff --git a/ui/snapshot/snapshot.h b/ui/snapshot/snapshot.h
index 4b2733c..3c83a92 100644
--- a/ui/snapshot/snapshot.h
+++ b/ui/snapshot/snapshot.h
@@ -31,16 +31,13 @@
// used in a result of user action. Support for async vs synchronous
// GrabWindowSnapshot differs by platform. To be most general, use the
// synchronous function first and if it returns false call the async one.
-SNAPSHOT_EXPORT bool GrabWindowSnapshot(
- gfx::NativeWindow window,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds);
+SNAPSHOT_EXPORT bool GrabWindowSnapshot(gfx::NativeWindow window,
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image);

-SNAPSHOT_EXPORT bool GrabViewSnapshot(
- gfx::NativeView view,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds);
-
+SNAPSHOT_EXPORT bool GrabViewSnapshot(gfx::NativeView view,
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image);

// These functions take a snapshot of |source_rect|, specified in layer space
// coordinates (DIP for desktop, physical pixels for Android), and scale the
@@ -54,16 +51,21 @@
scoped_refptr<base::TaskRunner> background_task_runner,
const GrabWindowSnapshotAsyncCallback& callback);

-typedef base::Callback<void(scoped_refptr<base::RefCountedBytes> png_data)>
- GrabWindowSnapshotAsyncPNGCallback;
SNAPSHOT_EXPORT void GrabWindowSnapshotAsync(
gfx::NativeWindow window,
const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback);
+ const GrabWindowSnapshotAsyncCallback& callback);
+
SNAPSHOT_EXPORT void GrabViewSnapshotAsync(
gfx::NativeView view,
const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback);
+
+typedef base::Callback<void(scoped_refptr<base::RefCountedMemory> png_data)>
+ GrabWindowSnapshotAsyncPNGCallback;
+SNAPSHOT_EXPORT void GrabWindowSnapshotAsyncPNG(
+ gfx::NativeWindow window,
+ const gfx::Rect& source_rect,
scoped_refptr<base::TaskRunner> background_task_runner,
const GrabWindowSnapshotAsyncPNGCallback& callback);

diff --git a/ui/snapshot/snapshot_android.cc b/ui/snapshot/snapshot_android.cc
index f687aa2..3de1f5a 100644
--- a/ui/snapshot/snapshot_android.cc
+++ b/ui/snapshot/snapshot_android.cc
@@ -24,15 +24,14 @@
// Sync versions are not supported in Android. Callers should fall back
// to the async version.
bool GrabViewSnapshot(gfx::NativeView view,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
- return GrabWindowSnapshot(
- view->GetWindowAndroid(), png_representation, snapshot_bounds);
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
+ return GrabWindowSnapshot(view->GetWindowAndroid(), snapshot_bounds, image);
}

bool GrabWindowSnapshot(gfx::NativeWindow window,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
return false;
}

@@ -64,28 +63,20 @@
background_task_runner));
}

-void GrabWindowSnapshotAsync(
- gfx::NativeWindow window,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- MakeAsyncCopyRequest(window,
- source_rect,
- base::Bind(&SnapshotAsync::EncodeCopyOutputResult,
- callback,
- background_task_runner));
+void GrabWindowSnapshotAsync(gfx::NativeWindow window,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ MakeAsyncCopyRequest(
+ window, source_rect,
+ base::Bind(&SnapshotAsync::RunCallbackWithCopyOutputResult, callback));
}

-void GrabViewSnapshotAsync(
- gfx::NativeView view,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- MakeAsyncCopyRequest(view->GetWindowAndroid(),
- source_rect,
- base::Bind(&SnapshotAsync::EncodeCopyOutputResult,
- callback,
- background_task_runner));
+void GrabViewSnapshotAsync(gfx::NativeView view,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ MakeAsyncCopyRequest(
+ view->GetWindowAndroid(), source_rect,
+ base::Bind(&SnapshotAsync::RunCallbackWithCopyOutputResult, callback));
}

} // namespace ui
diff --git a/ui/snapshot/snapshot_async.cc b/ui/snapshot/snapshot_async.cc
index a4f0eb0..173f70a 100644
--- a/ui/snapshot/snapshot_async.cc
+++ b/ui/snapshot/snapshot_async.cc
@@ -10,8 +10,6 @@
#include "base/task_runner_util.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkPixelRef.h"
-#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/skbitmap_operations.h"
@@ -32,29 +30,6 @@
target_size.width(),
target_size.height(),
static_cast<SkBitmap::Allocator*>(NULL));
-}
-
-scoped_refptr<base::RefCountedBytes> EncodeBitmap(const SkBitmap& bitmap) {
- scoped_refptr<base::RefCountedBytes> png_data(new base::RefCountedBytes);
- SkAutoLockPixels lock(bitmap);
- unsigned char* pixels = reinterpret_cast<unsigned char*>(bitmap.getPixels());
-#if SK_A32_SHIFT == 24 && SK_R32_SHIFT == 16 && SK_G32_SHIFT == 8
- gfx::PNGCodec::ColorFormat kColorFormat = gfx::PNGCodec::FORMAT_BGRA;
-#elif SK_A32_SHIFT == 24 && SK_B32_SHIFT == 16 && SK_G32_SHIFT == 8
- gfx::PNGCodec::ColorFormat kColorFormat = gfx::PNGCodec::FORMAT_RGBA;
-#else
-#error Unknown color format
-#endif
- if (!gfx::PNGCodec::Encode(pixels,
- kColorFormat,
- gfx::Size(bitmap.width(), bitmap.height()),
- base::checked_cast<int>(bitmap.rowBytes()),
- true,
- std::vector<gfx::PNGCodec::Comment>(),
- &png_data->data())) {
- return scoped_refptr<base::RefCountedBytes>();
- }
- return png_data;
}

} // namespace
@@ -80,24 +55,15 @@
base::Bind(&OnFrameScalingFinished, callback));
}

-void SnapshotAsync::EncodeCopyOutputResult(
- const GrabWindowSnapshotAsyncPNGCallback& callback,
- scoped_refptr<base::TaskRunner> background_task_runner,
+void SnapshotAsync::RunCallbackWithCopyOutputResult(
+ const GrabWindowSnapshotAsyncCallback& callback,
std::unique_ptr<cc::CopyOutputResult> result) {
if (result->IsEmpty()) {
- callback.Run(scoped_refptr<base::RefCountedBytes>());
+ callback.Run(gfx::Image());
return;
}

- // TODO(sergeyu): Potentially images can be scaled on GPU before reading it
- // from GPU. Image scaling is implemented in content::GlHelper, but it's can't
- // be used here because it's not in content/public. Move the scaling code
- // somewhere so that it can be reused here.
- base::PostTaskAndReplyWithResult(
- background_task_runner.get(),
- FROM_HERE,
- base::Bind(EncodeBitmap, *result->TakeBitmap()),
- callback);
+ callback.Run(gfx::Image::CreateFrom1xBitmap(*result->TakeBitmap()));
}

} // namespace ui
diff --git a/ui/snapshot/snapshot_async.h b/ui/snapshot/snapshot_async.h
index 364f76c..79928e0 100644
--- a/ui/snapshot/snapshot_async.h
+++ b/ui/snapshot/snapshot_async.h
@@ -31,9 +31,8 @@
scoped_refptr<base::TaskRunner> background_task_runner,
std::unique_ptr<cc::CopyOutputResult> result);

- static void EncodeCopyOutputResult(
- const GrabWindowSnapshotAsyncPNGCallback& callback,
- scoped_refptr<base::TaskRunner> background_task_runner,
+ static void RunCallbackWithCopyOutputResult(
+ const GrabWindowSnapshotAsyncCallback& callback,
std::unique_ptr<cc::CopyOutputResult> result);

private:
diff --git a/ui/snapshot/snapshot_aura.cc b/ui/snapshot/snapshot_aura.cc
index d2df3f0..0ff547c 100644
--- a/ui/snapshot/snapshot_aura.cc
+++ b/ui/snapshot/snapshot_aura.cc
@@ -22,14 +22,14 @@
namespace ui {

bool GrabViewSnapshot(gfx::NativeView view,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
- return GrabWindowSnapshot(view, png_representation, snapshot_bounds);
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
+ return GrabWindowSnapshot(view, snapshot_bounds, image);
}

bool GrabWindowSnapshot(gfx::NativeWindow window,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
// Not supported in Aura. Callers should fall back to the async version.
return false;
}
@@ -94,22 +94,18 @@
background_task_runner));
}

-void GrabWindowSnapshotAsync(
- gfx::NativeWindow window,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- MakeInitialAsyncCopyRequest(window, source_rect,
- base::Bind(&SnapshotAsync::EncodeCopyOutputResult,
- callback, background_task_runner));
+void GrabWindowSnapshotAsync(gfx::NativeWindow window,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ MakeInitialAsyncCopyRequest(
+ window, source_rect,
+ base::Bind(&SnapshotAsync::RunCallbackWithCopyOutputResult, callback));
}

-void GrabViewSnapshotAsync(
- gfx::NativeView view,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- GrabWindowSnapshotAsync(view, source_rect, background_task_runner, callback);
+void GrabViewSnapshotAsync(gfx::NativeView view,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ GrabWindowSnapshotAsync(view, source_rect, callback);
}


diff --git a/ui/snapshot/snapshot_aura_unittest.cc b/ui/snapshot/snapshot_aura_unittest.cc
index 984a231..97d4ac4 100644
--- a/ui/snapshot/snapshot_aura_unittest.cc
+++ b/ui/snapshot/snapshot_aura_unittest.cc
@@ -141,28 +141,14 @@
aura::Window::ConvertRectToTarget(
test_window(), root_window(), &source_rect);

- scoped_refptr<base::TestSimpleTaskRunner> task_runner(
- new base::TestSimpleTaskRunner());
scoped_refptr<SnapshotHolder> holder(new SnapshotHolder);
ui::GrabWindowSnapshotAsync(
- root_window(),
- source_rect,
- task_runner,
+ root_window(), source_rect,
base::Bind(&SnapshotHolder::SnapshotCallback, holder));

- // Wait for copy response.
- WaitForDraw();
- // Run internal snapshot callback to scale/rotate response image.
- task_runner->RunUntilIdle();
- // Run SnapshotHolder callback.
- helper_->RunAllPendingInMessageLoop();
-
- if (holder->completed())
- return holder->image();
-
- // Callback never called.
- NOTREACHED();
- return gfx::Image();
+ holder->WaitForSnapshot();
+ DCHECK(holder->completed());
+ return holder->image();
}

private:
@@ -170,12 +156,13 @@
public:
SnapshotHolder() : completed_(false) {}

- void SnapshotCallback(scoped_refptr<base::RefCountedBytes> png_data) {
+ void SnapshotCallback(const gfx::Image& image) {
DCHECK(!completed_);
- image_ = gfx::Image::CreateFrom1xPNGBytes(&(png_data->data()[0]),
- png_data->size());
+ image_ = image;
completed_ = true;
+ run_loop_.Quit();
}
+ void WaitForSnapshot() { run_loop_.Run(); }
bool completed() const {
return completed_;
};
@@ -186,6 +173,7 @@

virtual ~SnapshotHolder() {}

+ base::RunLoop run_loop_;
gfx::Image image_;
bool completed_;
};
@@ -231,7 +219,7 @@
}

TEST_F(SnapshotAuraTest, UIScale) {
- const float kUIScale = 1.25f;
+ const float kUIScale = 0.5f;
test_screen()->SetUIScale(kUIScale);

gfx::Rect test_bounds(100, 100, 300, 200);
@@ -240,11 +228,12 @@

// Snapshot always captures the physical pixels.
gfx::SizeF snapshot_size(test_bounds.size());
+ snapshot_size.Scale(1 / kUIScale);

gfx::Image snapshot = GrabSnapshotForTestWindow();
EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(),
snapshot.Size().ToString());
- EXPECT_EQ(0u, GetFailedPixelsCount(snapshot));
+ EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 1 / kUIScale));
}

TEST_F(SnapshotAuraTest, DeviceScaleFactor) {
@@ -265,7 +254,7 @@
}

TEST_F(SnapshotAuraTest, RotateAndUIScale) {
- const float kUIScale = 1.25f;
+ const float kUIScale = 0.5f;
test_screen()->SetUIScale(kUIScale);
test_screen()->SetDisplayRotation(display::Display::ROTATE_90);

@@ -275,16 +264,17 @@

// Snapshot always captures the physical pixels.
gfx::SizeF snapshot_size(test_bounds.size());
+ snapshot_size.Scale(1 / kUIScale);

gfx::Image snapshot = GrabSnapshotForTestWindow();
EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(),
snapshot.Size().ToString());
- EXPECT_EQ(0u, GetFailedPixelsCount(snapshot));
+ EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 1 / kUIScale));
}

TEST_F(SnapshotAuraTest, RotateAndUIScaleAndScaleFactor) {
test_screen()->SetDeviceScaleFactor(2.0f);
- const float kUIScale = 1.25f;
+ const float kUIScale = 0.5f;
test_screen()->SetUIScale(kUIScale);
test_screen()->SetDisplayRotation(display::Display::ROTATE_90);

@@ -294,12 +284,12 @@

// Snapshot always captures the physical pixels.
gfx::SizeF snapshot_size(test_bounds.size());
- snapshot_size.Scale(2.0f);
+ snapshot_size.Scale(2.0f / kUIScale);

gfx::Image snapshot = GrabSnapshotForTestWindow();
EXPECT_EQ(gfx::ToRoundedSize(snapshot_size).ToString(),
snapshot.Size().ToString());
- EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 2));
+ EXPECT_EQ(0u, GetFailedPixelsCountWithScaleFactor(snapshot, 2 / kUIScale));
}

} // namespace ui
diff --git a/ui/snapshot/snapshot_ios.mm b/ui/snapshot/snapshot_ios.mm
index d75f204..d239b71 100644
--- a/ui/snapshot/snapshot_ios.mm
+++ b/ui/snapshot/snapshot_ios.mm
@@ -11,15 +11,15 @@
namespace ui {

bool GrabViewSnapshot(gfx::NativeView view,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
// TODO(bajones): Implement iOS snapshot functionality
return false;
}

bool GrabWindowSnapshot(gfx::NativeWindow window,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
// TODO(bajones): Implement iOS snapshot functionality
return false;
}
@@ -33,20 +33,16 @@
callback.Run(gfx::Image());
}

-void GrabViewSnapshotAsync(
- gfx::NativeView view,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- callback.Run(scoped_refptr<base::RefCountedBytes>());
+void GrabViewSnapshotAsync(gfx::NativeView view,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ callback.Run(gfx::Image());
}

-void GrabWindowSnapshotAsync(
- gfx::NativeWindow window,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- callback.Run(scoped_refptr<base::RefCountedBytes>());
+void GrabWindowSnapshotAsync(gfx::NativeWindow window,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ callback.Run(gfx::Image());
}

} // namespace ui
diff --git a/ui/snapshot/snapshot_mac.mm b/ui/snapshot/snapshot_mac.mm
index 9761cb7..40cfaaf 100644
--- a/ui/snapshot/snapshot_mac.mm
+++ b/ui/snapshot/snapshot_mac.mm
@@ -9,7 +9,6 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
-#include "base/mac/scoped_nsobject.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/task_runner.h"
#include "ui/gfx/geometry/rect.h"
@@ -18,8 +17,8 @@
namespace ui {

bool GrabViewSnapshot(gfx::NativeView view,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
NSWindow* window = [view window];
NSScreen* screen = [[NSScreen screens] firstObject];
gfx::Rect screen_bounds = gfx::Rect(NSRectToCGRect([screen frame]));
@@ -43,8 +42,6 @@
DCHECK_LE(screen_snapshot_bounds.right(), view_bounds.right());
DCHECK_LE(screen_snapshot_bounds.bottom(), view_bounds.bottom());

- png_representation->clear();
-
base::ScopedCFTypeRef<CGImageRef> windowSnapshot(
CGWindowListCreateImage(screen_snapshot_bounds.ToCGRect(),
kCGWindowListOptionIncludingWindow,
@@ -53,27 +50,19 @@
if (CGImageGetWidth(windowSnapshot) <= 0)
return false;

- base::scoped_nsobject<NSBitmapImageRep> rep(
- [[NSBitmapImageRep alloc] initWithCGImage:windowSnapshot]);
- NSData* data = [rep representationUsingType:NSPNGFileType properties:@{}];
- const unsigned char* buf = static_cast<const unsigned char*>([data bytes]);
- NSUInteger length = [data length];
- if (buf == NULL || length == 0)
- return false;
-
- png_representation->assign(buf, buf + length);
- DCHECK(!png_representation->empty());
-
+ NSImage* nsImage =
+ [[NSImage alloc] initWithCGImage:windowSnapshot size:NSZeroSize];
+ *image = gfx::Image(nsImage);
return true;
}

bool GrabWindowSnapshot(gfx::NativeWindow window,
- std::vector<unsigned char>* png_representation,
- const gfx::Rect& snapshot_bounds) {
+ const gfx::Rect& snapshot_bounds,
+ gfx::Image* image) {
// Make sure to grab the "window frame" view so we get current tab +
// tabstrip.
- return GrabViewSnapshot([[window contentView] superview], png_representation,
- snapshot_bounds);
+ return GrabViewSnapshot([[window contentView] superview], snapshot_bounds,
+ image);
}

void GrabWindowSnapshotAndScaleAsync(
@@ -85,21 +74,17 @@
callback.Run(gfx::Image());
}

-void GrabViewSnapshotAsync(
- gfx::NativeView view,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
- callback.Run(scoped_refptr<base::RefCountedBytes>());
+void GrabViewSnapshotAsync(gfx::NativeView view,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
+ callback.Run(gfx::Image());
}

-void GrabWindowSnapshotAsync(
- gfx::NativeWindow window,
- const gfx::Rect& source_rect,
- scoped_refptr<base::TaskRunner> background_task_runner,
- const GrabWindowSnapshotAsyncPNGCallback& callback) {
+void GrabWindowSnapshotAsync(gfx::NativeWindow window,
+ const gfx::Rect& source_rect,
+ const GrabWindowSnapshotAsyncCallback& callback) {
return GrabViewSnapshotAsync([[window contentView] superview], source_rect,
- background_task_runner, callback);
+ callback);
}

} // namespace ui
diff --git a/ui/snapshot/snapshot_mac_unittest.mm b/ui/snapshot/snapshot_mac_unittest.mm
index d1d9250..eea1067 100644
--- a/ui/snapshot/snapshot_mac_unittest.mm
+++ b/ui/snapshot/snapshot_mac_unittest.mm
@@ -12,34 +12,32 @@
#include "base/mac/sdk_forward_declarations.h"
#include "testing/platform_test.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/image/image.h"
+#import "ui/gfx/test/ui_cocoa_test_helper.h"

namespace ui {
namespace {

-typedef PlatformTest GrabWindowSnapshotTest;
+typedef CocoaTest GrabWindowSnapshotTest;

TEST_F(GrabWindowSnapshotTest, TestGrabWindowSnapshot) {
// Launch a test window so we can take a snapshot.
NSRect frame = NSMakeRect(0, 0, 400, 400);
- base::scoped_nsobject<NSWindow> window(
- [[NSWindow alloc] initWithContentRect:frame
- styleMask:NSBorderlessWindowMask
- backing:NSBackingStoreBuffered
- defer:NO]);
+ NSWindow* window = test_window();
+ [window setFrame:frame display:false];
[window setBackgroundColor:[NSColor whiteColor]];
[window makeKeyAndOrderFront:NSApp];
+ [window display];

- std::unique_ptr<std::vector<unsigned char>> png_representation(
- new std::vector<unsigned char>);
+ gfx::Image image;
gfx::Rect bounds = gfx::Rect(0, 0, frame.size.width, frame.size.height);
- EXPECT_TRUE(ui::GrabWindowSnapshot(window, png_representation.get(),
- bounds));
+ EXPECT_TRUE(ui::GrabWindowSnapshot(window, bounds, &image));

- // Copy png back into NSData object so we can make sure we grabbed a png.
- base::scoped_nsobject<NSData> image_data(
- [[NSData alloc] initWithBytes:&(*png_representation)[0]
- length:png_representation->size()]);
- NSBitmapImageRep* rep = [NSBitmapImageRep imageRepWithData:image_data.get()];
+ NSImage* nsImage = image.ToNSImage();
+ CGImageRef cgImage =
+ [nsImage CGImageForProposedRect:nil context:nil hints:nil];
+ base::scoped_nsobject<NSBitmapImageRep> rep(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgImage]);
EXPECT_TRUE([rep isKindOfClass:[NSBitmapImageRep class]]);
CGFloat scaleFactor = 1.0f;
if ([window respondsToSelector:@selector(backingScaleFactor)])
Reply all
Reply to author
Forward
0 new messages