Commit 053d5793 authored by pfeldman@chromium.org's avatar pfeldman@chromium.org

DevTools: [Android] implement RenderWidgetHostViewAndroid::CopyFromCompositingSurface

BUG=242299

Review URL: https://chromiumcodereview.appspot.com/21777003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217073 0039d316-1c4b-4281-b951-d872f2087c98
parent 78e1f682
......@@ -36,6 +36,9 @@ namespace navigate {
} // navigate
namespace captureScreenshot {
const char kName[] = "Page.captureScreenshot";
const char kParamFormat[] = "format";
const char kParamQuality[] = "quality";
const char kParamScale[] = "scale";
const char kResponseData[] = "data";
} // captureScreenshot
} // Page
......
......@@ -43,6 +43,9 @@ namespace navigate {
} // navigate
namespace captureScreenshot {
extern const char kName[];
extern const char kParamFormat[];
extern const char kParamQuality[];
extern const char kParamScale[];
extern const char kResponseData[];
} // captureScreenshot
} // Page
......
......@@ -16,6 +16,7 @@
#include "content/browser/devtools/devtools_protocol_constants.h"
#include "content/browser/devtools/devtools_tracing_handler.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/port/browser/render_widget_host_view_port.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/javascript_dialog_manager.h"
......@@ -27,9 +28,22 @@
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/page_transition_types.h"
#include "content/public/common/referrer.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/size_conversions.h"
#include "ui/snapshot/snapshot.h"
#include "url/gurl.h"
using base::TimeTicks;
namespace {
static const char kPng[] = "png";
static const char kJpeg[] = "jpeg";
static int kDefaultScreenshotQuality = 80;
} // namespace
namespace content {
RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent)
......@@ -141,44 +155,119 @@ RendererOverridesHandler::PageNavigate(
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageCaptureScreenshot(
scoped_refptr<DevToolsProtocol::Command> command) {
// Emulate async processing.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&RendererOverridesHandler::CaptureScreenshot,
weak_factory_.GetWeakPtr(),
command));
return command->AsyncResponsePromise();
}
void RendererOverridesHandler::CaptureScreenshot(
scoped_refptr<DevToolsProtocol::Command> command) {
// Parse input parameters.
std::string format;
int quality = kDefaultScreenshotQuality;
double scale = 1;
base::DictionaryValue* params = command->params();
if (params) {
params->GetString(devtools::Page::captureScreenshot::kParamFormat,
&format);
params->GetInteger(devtools::Page::captureScreenshot::kParamQuality,
&quality);
params->GetDouble(devtools::Page::captureScreenshot::kParamScale,
&scale);
}
if (format.empty())
format = kPng;
if (quality < 0 || quality > 100)
quality = kDefaultScreenshotQuality;
if (scale <= 0 || scale > 1)
scale = 1;
RenderViewHost* host = agent_->GetRenderViewHost();
gfx::Rect view_bounds = host->GetView()->GetViewBounds();
gfx::Rect snapshot_bounds(view_bounds.size());
gfx::Size snapshot_size = snapshot_bounds.size();
// Grab screen pixels if available for current platform.
// TODO(pfeldman): support format, scale and quality in ui::GrabViewSnapshot.
std::vector<unsigned char> png;
if (ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
&png,
snapshot_bounds)) {
std::string base_64_data;
bool is_unscaled_png = scale == 1 && format == kPng;
if (is_unscaled_png && ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
&png, view_bounds)) {
std::string base64_data;
bool success = base::Base64Encode(
base::StringPiece(reinterpret_cast<char*>(&*png.begin()), png.size()),
&base_64_data);
&base64_data);
if (success) {
base::DictionaryValue* result = new base::DictionaryValue();
result->SetString(
devtools::Page::captureScreenshot::kResponseData, base_64_data);
scoped_refptr<DevToolsProtocol::Response> response =
command->SuccessResponse(result);
SendRawMessage(response->Serialize());
return;
devtools::Page::captureScreenshot::kResponseData, base64_data);
return command->SuccessResponse(result);
}
return command->InternalErrorResponse("Unable to base64encode screenshot");
}
// Fallback to copying from compositing surface.
RenderWidgetHostViewPort* view_port =
RenderWidgetHostViewPort::FromRWHV(host->GetView());
gfx::Size snapshot_size = gfx::ToFlooredSize(
gfx::ScaleSize(view_bounds.size(), scale));
view_port->CopyFromCompositingSurface(
view_bounds, snapshot_size,
base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
weak_factory_.GetWeakPtr(), command, format, quality, scale));
return command->AsyncResponsePromise();
}
void RendererOverridesHandler::ScreenshotCaptured(
scoped_refptr<DevToolsProtocol::Command> command,
const std::string& format,
int quality,
double scale,
bool success,
const SkBitmap& bitmap) {
if (!success) {
SendRawMessage(
command->InternalErrorResponse("Unable to capture screenshot")->
Serialize());
return;
}
std::vector<unsigned char> data;
SkAutoLockPixels lock_image(bitmap);
bool encoded;
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);
} 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;
}
if (!encoded) {
SendRawMessage(
command->InternalErrorResponse("Unable to encode screenshot")->
Serialize());
return;
}
SendRawMessage(command->
InternalErrorResponse("Unable to capture a screenshot")->Serialize());
std::string base_64_data;
if (!base::Base64Encode(base::StringPiece(
reinterpret_cast<char*>(&data[0]),
data.size()),
&base_64_data)) {
SendRawMessage(
command->InternalErrorResponse("Unable to base64 encode screenshot")->
Serialize());
return;
}
base::DictionaryValue* response = new base::DictionaryValue();
response->SetString(
devtools::Page::captureScreenshot::kResponseData, base_64_data);
SendRawMessage(command->SuccessResponse(response)->Serialize());
}
} // namespace content
......@@ -11,6 +11,8 @@
#include "base/memory/weak_ptr.h"
#include "content/browser/devtools/devtools_protocol.h"
class SkBitmap;
namespace content {
class DevToolsAgentHost;
......@@ -35,7 +37,13 @@ class RendererOverridesHandler : public DevToolsProtocol::Handler {
scoped_refptr<DevToolsProtocol::Response> PageCaptureScreenshot(
scoped_refptr<DevToolsProtocol::Command> command);
void CaptureScreenshot(scoped_refptr<DevToolsProtocol::Command> command);
void ScreenshotCaptured(
scoped_refptr<DevToolsProtocol::Command> command,
const std::string& format,
int quality,
double scale,
bool success,
const SkBitmap& bitmap);
DevToolsAgentHost* agent_;
base::WeakPtrFactory<RendererOverridesHandler> weak_factory_;
......
......@@ -18,6 +18,8 @@
#include "cc/layers/texture_layer.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/trees/layer_tree_host.h"
#include "content/browser/accessibility/browser_accessibility_manager_android.h"
#include "content/browser/android/content_view_core_impl.h"
......@@ -25,6 +27,7 @@
#include "content/browser/android/overscroll_glow.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/surface_texture_transport_client_android.h"
......@@ -34,6 +37,7 @@
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/common/content_switches.h"
#include "skia/ext/image_operations.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gfx/android/device_display_info.h"
......@@ -82,6 +86,17 @@ void SendImeEventAck(RenderWidgetHostImpl* host) {
host->Send(new ViewMsg_ImeEventAck(host->GetRoutingID()));
}
void CopyFromCompositingSurfaceFinished(
const base::Callback<void(bool, const SkBitmap&)>& callback,
const cc::TextureMailbox::ReleaseCallback& release_callback,
scoped_ptr<SkBitmap> bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result) {
bitmap_pixels_lock.reset();
release_callback.Run(0, false);
callback.Run(result, *bitmap);
}
} // anonymous namespace
RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid(
......@@ -329,8 +344,7 @@ bool RenderWidgetHostViewAndroid::HasFocus() const {
}
bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const {
NOTIMPLEMENTED();
return false;
return HasValidFrame();
}
void RenderWidgetHostViewAndroid::Show() {
......@@ -540,8 +554,36 @@ void RenderWidgetHostViewAndroid::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
const base::Callback<void(bool, const SkBitmap&)>& callback) {
NOTIMPLEMENTED();
callback.Run(false, SkBitmap());
if (!IsSurfaceAvailableForCopy()) {
callback.Run(false, SkBitmap());
return;
}
const gfx::Display& display =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
float device_scale_factor = display.device_scale_factor();
DCHECK_EQ(device_scale_factor,
ui::GetScaleFactorScale(GetScaleFactorForView(this)));
const gfx::Size& dst_size_in_pixel = ConvertViewSizeToPixel(this, dst_size);
gfx::Rect src_subrect_in_pixel =
ConvertRectToPixel(device_scale_factor, src_subrect);
scoped_ptr<cc::CopyOutputRequest> request;
if (src_subrect_in_pixel.size() == dst_size_in_pixel) {
request = cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
&RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult,
dst_size_in_pixel,
callback));
} else {
request = cc::CopyOutputRequest::CreateRequest(base::Bind(
&RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult,
dst_size_in_pixel,
callback));
}
request->set_area(src_subrect_in_pixel);
layer_->RequestCopyOfOutput(request.Pass());
}
void RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceToVideoFrame(
......@@ -1152,6 +1194,80 @@ void RenderWidgetHostViewAndroid::OnLostResources() {
RunAckCallbacks();
}
// static
void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result) {
DCHECK(result->HasTexture());
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
if (!result->HasTexture() || result->IsEmpty() || result->size().IsEmpty())
return;
scoped_ptr<SkBitmap> bitmap(new SkBitmap);
bitmap->setConfig(SkBitmap::kARGB_8888_Config,
dst_size_in_pixel.width(), dst_size_in_pixel.height());
if (!bitmap->allocPixels())
return;
bitmap->setIsOpaque(true);
ImageTransportFactoryAndroid* factory =
ImageTransportFactoryAndroid::GetInstance();
GLHelper* gl_helper = factory->GetGLHelper();
if (!gl_helper)
return;
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
new SkAutoLockPixels(*bitmap));
uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
scoped_ptr<cc::TextureMailbox> texture_mailbox = result->TakeTexture();
DCHECK(texture_mailbox->IsTexture());
if (!texture_mailbox->IsTexture())
return;
scoped_callback_runner.Release();
gl_helper->CropScaleReadbackAndCleanMailbox(
texture_mailbox->name(),
texture_mailbox->sync_point(),
result->size(),
gfx::Rect(result->size()),
dst_size_in_pixel,
pixels,
base::Bind(&CopyFromCompositingSurfaceFinished,
callback,
texture_mailbox->callback(),
base::Passed(&bitmap),
base::Passed(&bitmap_pixels_lock)));
}
// static
void RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result) {
DCHECK(result->HasBitmap());
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
if (!result->HasBitmap() || result->IsEmpty() || result->size().IsEmpty())
return;
scoped_ptr<SkBitmap> source = result->TakeBitmap();
DCHECK(source);
if (!source)
return;
DCHECK_EQ(source->width(), dst_size_in_pixel.width());
DCHECK_EQ(source->height(), dst_size_in_pixel.height());
scoped_callback_runner.Release();
callback.Run(true, *source);
}
// static
void RenderWidgetHostViewPort::GetDefaultScreenInfo(
WebKit::WebScreenInfo* results) {
......
......@@ -33,6 +33,7 @@ struct GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params;
struct GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params;
namespace cc {
class CopyOutputResult;
class DelegatedRendererLayer;
class Layer;
class TextureLayer;
......@@ -256,6 +257,17 @@ class RenderWidgetHostViewAndroid
void UpdateAnimationSize(const cc::CompositorFrameMetadata& frame_metadata);
void ScheduleAnimationIfNecessary();
// Called after async screenshot task completes. Scales and crops the result
// of the copy.
static void PrepareTextureCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result);
static void PrepareBitmapCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result);
// The model object.
RenderWidgetHostImpl* host_;
......
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