summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhubbe@chromium.org <hubbe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-31 03:37:46 +0000
committerhubbe@chromium.org <hubbe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-31 03:37:46 +0000
commit9d2839eead7e39387a950b87d626296f9a1f2e51 (patch)
tree8dcb3a3069fdbd848cf8a9b28e2058b018ce554c
parent49d69ba3a43aa6da00f672d6178dad11d197e94a (diff)
downloadchromium_src-9d2839eead7e39387a950b87d626296f9a1f2e51.zip
chromium_src-9d2839eead7e39387a950b87d626296f9a1f2e51.tar.gz
chromium_src-9d2839eead7e39387a950b87d626296f9a1f2e51.tar.bz2
GPU RGBA -> Planar YUV conversion + uses new scaler pipeline
Review URL: https://chromiumcodereview.appspot.com/15944003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203322 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/login/chrome_restart_request.cc2
-rw-r--r--content/browser/renderer_host/render_widget_host_view_aura.cc76
-rw-r--r--content/browser/renderer_host/render_widget_host_view_aura.h5
-rw-r--r--content/common/gpu/client/gl_helper.cc387
-rw-r--r--content/common/gpu/client/gl_helper.h41
-rw-r--r--content/common/gpu/client/gl_helper_scaling.cc89
-rw-r--r--content/common/gpu/client/gl_helper_scaling.h8
-rw-r--r--content/common/gpu/client/gl_helper_unittests.cc301
-rw-r--r--content/public/common/content_switches.cc7
-rw-r--r--content/public/common/content_switches.h2
10 files changed, 827 insertions, 91 deletions
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 7e52fee..57e05b2 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -108,6 +108,8 @@ std::string DeriveCommandLine(const GURL& start_url,
::switches::kPpapiInProcess,
::switches::kRendererStartupDialog,
::switches::kEnableShareGroupAsyncTextureUpload,
+ ::switches::kTabCaptureUpscaleQuality,
+ ::switches::kTabCaptureDownscaleQuality,
#if defined(USE_XI2_MT)
::switches::kTouchCalibration,
#endif
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index fe288f5..1195f9c 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -345,28 +345,6 @@ void AcknowledgeBufferForGpu(
route_id, gpu_host_id, ack);
}
-void CopySnapshotToVideoFrame(const scoped_refptr<media::VideoFrame>& target,
- const gfx::Rect& region_in_frame,
- const base::Callback<void(bool)>& callback,
- bool succeed,
- const SkBitmap& bitmap) {
- if (!succeed) {
- callback.Run(false);
- return;
- }
-
- DCHECK(region_in_frame.size() == gfx::Size(bitmap.width(), bitmap.height()));
- {
- SkAutoLockPixels lock(bitmap);
- media::CopyRGBToVideoFrame(
- reinterpret_cast<const uint8*>(bitmap.getPixels()),
- bitmap.rowBytes(),
- region_in_frame,
- target.get());
- }
- callback.Run(true);
-}
-
} // namespace
// We need to watch for mouse events outside a Web Popup or its parent
@@ -1255,15 +1233,55 @@ void RenderWidgetHostViewAura::CopyFromCompositingSurfaceToVideoFrame(
region_in_frame.y() & ~1,
region_in_frame.width() & ~1,
region_in_frame.height() & ~1);
+ ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
+ GLHelper* gl_helper = factory->GetGLHelper();
+ if (!gl_helper) {
+ return;
+ }
+ // Convert |src_subrect| from the views coordinate (upper-left origin) into
+ // the OpenGL coordinate (lower-left origin).
+ gfx::Rect src_subrect_in_gl = src_subrect;
+ src_subrect_in_gl.set_y(GetViewBounds().height() - src_subrect.bottom());
+ gfx::Rect src_subrect_in_pixel =
+ ConvertRectToPixel(current_surface_->device_scale_factor(),
+ src_subrect_in_gl);
- scoped_callback_runner.Release();
+ if (!yuv_readback_pipeline_ ||
+ yuv_readback_pipeline_->scaler()->SrcSize() != current_surface_->size() ||
+ yuv_readback_pipeline_->scaler()->SrcSubrect() != src_subrect_in_pixel ||
+ yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) {
+ GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST;
+ std::string quality_switch = switches::kTabCaptureDownscaleQuality;
+ // If we're scaling up, we can use the "best" quality.
+ if (current_surface_->size().width() < region_in_frame.size().width() &&
+ current_surface_->size().height() < region_in_frame.size().height()) {
+ quality_switch = switches::kTabCaptureUpscaleQuality;
+ }
- CopyFromCompositingSurfaceHelper(src_subrect,
- region_in_frame.size(),
- base::Bind(CopySnapshotToVideoFrame,
- target,
- region_in_frame,
- callback));
+ std::string switch_value =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(quality_switch);
+ if (switch_value == "fast") {
+ quality = GLHelper::SCALER_QUALITY_FAST;
+ } else if (switch_value == "good") {
+ quality = GLHelper::SCALER_QUALITY_GOOD;
+ } else if (switch_value == "best") {
+ quality = GLHelper::SCALER_QUALITY_BEST;
+ }
+
+ yuv_readback_pipeline_.reset(
+ gl_helper->CreateReadbackPipelineYUV(quality,
+ current_surface_->size(),
+ src_subrect_in_pixel,
+ target->coded_size(),
+ region_in_frame,
+ true));
+ }
+
+ scoped_callback_runner.Release();
+ yuv_readback_pipeline_->ReadbackYUV(
+ current_surface_->PrepareTexture(),
+ target,
+ callback);
}
bool RenderWidgetHostViewAura::CanCopyToVideoFrame() const {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 091cbd1..3d376d1 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -19,6 +19,7 @@
#include "content/browser/renderer_host/image_transport_factory.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/content_export.h"
+#include "content/common/gpu/client/gl_helper.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/aura/client/activation_change_observer.h"
#include "ui/aura/client/activation_delegate.h"
@@ -670,6 +671,10 @@ class RenderWidgetHostViewAura
// Subscriber that listens to frame presentation events.
scoped_ptr<RenderWidgetHostViewFrameSubscriber> frame_subscriber_;
+ // YUV readback pipeline.
+ scoped_ptr<content::ReadbackYUVInterface>
+ yuv_readback_pipeline_;
+
TouchEditingClient* touch_editing_client_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura);
diff --git a/content/common/gpu/client/gl_helper.cc b/content/common/gpu/client/gl_helper.cc
index de9822e..79a2ca36 100644
--- a/content/common/gpu/client/gl_helper.cc
+++ b/content/common/gpu/client/gl_helper.cc
@@ -16,6 +16,8 @@
#include "base/time.h"
#include "cc/resources/sync_point_helper.h"
#include "content/common/gpu/client/gl_helper_scaling.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_util.h"
#include "third_party/WebKit/public/platform/WebCString.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/gfx/rect.h"
@@ -25,6 +27,57 @@
using WebKit::WebGLId;
using WebKit::WebGraphicsContext3D;
+namespace {
+
+// Helper class for holding a scaler, a texture for the output of that
+// scaler and an associated frame buffer. This is inteded to be used
+// when the output of a scaler is t be sent to a readback.
+class ScalerHolder {
+ public:
+ ScalerHolder(WebGraphicsContext3D* context,
+ content::GLHelper::ScalerInterface *scaler)
+ : scaler_(scaler),
+ texture_(context, context->createTexture()),
+ framebuffer_(context, context->createFramebuffer()) {
+ content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context,
+ texture_);
+ context->texImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ scaler_->DstSize().width(),
+ scaler_->DstSize().height(),
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ NULL);
+ content::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
+ context,
+ framebuffer_);
+ context->framebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ texture_,
+ 0);
+ }
+
+ void Scale(WebKit::WebGLId src_texture) {
+ scaler_->Scale(src_texture, texture_);
+ }
+
+ content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); }
+ WebGLId texture() const { return texture_.id(); }
+ WebGLId framebuffer() const { return framebuffer_.id(); }
+
+ public:
+ scoped_ptr<content::GLHelper::ScalerInterface> scaler_;
+ content::ScopedTexture texture_;
+ content::ScopedFramebuffer framebuffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScalerHolder);
+};
+
+} // namespace
+
namespace content {
// Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates
@@ -56,12 +109,29 @@ class GLHelper::CopyTextureToImpl :
const gfx::Rect& src_rect,
unsigned char* out);
+ // Reads back bytes from the currently bound frame buffer.
+ // Note that dst_size is specified in bytes, not pixels.
+ void ReadbackAsync(
+ const gfx::Size& dst_size,
+ int32 bytes_per_row, // generally dst_size.width() * 4
+ int32 row_stride_bytes, // generally dst_size.width() * 4
+ unsigned char* out,
+ const base::Callback<void(bool)>& callback);
+
WebKit::WebGLId CopyAndScaleTexture(WebGLId texture,
const gfx::Size& src_size,
const gfx::Size& dst_size,
bool vertically_flip_texture,
GLHelper::ScalerQuality quality);
+ ReadbackYUVInterface* CreateReadbackPipelineYUV(
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const gfx::Rect& dst_subrect,
+ bool flip_vertically);
+
private:
// A single request to CropScaleReadbackAndCleanTexture.
// The main thread can cancel the request, before it's handled by the helper
@@ -71,25 +141,66 @@ class GLHelper::CopyTextureToImpl :
// In either case, the callback must be called exactly once, and the texture
// must be deleted by the main thread context.
struct Request {
- Request(WebGLId texture_,
- const gfx::Size& size_,
+ Request(const gfx::Size& size_,
+ int32 bytes_per_row_,
+ int32 row_stride_bytes_,
unsigned char* pixels_,
const base::Callback<void(bool)>& callback_)
: size(size_),
- callback(callback_),
- texture(texture_),
+ bytes_per_row(bytes_per_row_),
+ row_stride_bytes(row_stride_bytes_),
pixels(pixels_),
+ callback(callback_),
buffer(0) {
}
gfx::Size size;
- base::Callback<void(bool)> callback;
-
- WebGLId texture;
+ int bytes_per_row;
+ int row_stride_bytes;
unsigned char* pixels;
+ base::Callback<void(bool)> callback;
GLuint buffer;
};
+ // A readback pipeline that also converts the data to YUV before
+ // reading it back.
+ class ReadbackYUVImpl : public ReadbackYUVInterface {
+ public:
+ ReadbackYUVImpl(WebGraphicsContext3D* context,
+ CopyTextureToImpl* copy_impl,
+ GLHelperScaling* scaler_impl,
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const gfx::Rect& dst_subrect,
+ bool flip_vertically);
+
+ virtual void ReadbackYUV(
+ WebKit::WebGLId src_texture,
+ media::VideoFrame* target,
+ const base::Callback<void(bool)>& callback) OVERRIDE;
+
+ virtual ScalerInterface* scaler() OVERRIDE {
+ return scaler_.scaler();
+ }
+
+ private:
+ void ReadbackPlane(ScalerHolder* scaler,
+ media::VideoFrame* target,
+ int plane,
+ int size_shift,
+ const base::Callback<void(bool)>& callback);
+
+ WebGraphicsContext3D* context_;
+ CopyTextureToImpl* copy_impl_;
+ gfx::Size dst_size_;
+ gfx::Rect dst_subrect_;
+ ScalerHolder scaler_;
+ ScalerHolder y_;
+ ScalerHolder u_;
+ ScalerHolder v_;
+ };
// Copies the block of pixels specified with |src_subrect| from |src_texture|,
// scales it to |dst_size|, writes it into a texture, and returns its ID.
@@ -102,13 +213,14 @@ class GLHelper::CopyTextureToImpl :
bool swizzle,
GLHelper::ScalerQuality quality);
+ static void nullcallback(bool success) {}
void ReadbackDone(Request* request);
void FinishRequest(Request* request, bool result);
void CancelRequests();
- // Interleaved array of 2-dimentional vertex positions (x, y) and
- // 2-dimentional texture coordinates (s, t).
- static const WebKit::WGC3Dfloat kVertexAttributes[];
+ static const float kRGBtoYColorWeights[];
+ static const float kRGBtoUColorWeights[];
+ static const float kRGBtoVColorWeights[];
WebGraphicsContext3D* context_;
GLHelper* helper_;
@@ -173,6 +285,36 @@ WebGLId GLHelper::CopyTextureToImpl::ScaleTexture(
return dst_texture;
}
+void GLHelper::CopyTextureToImpl::ReadbackAsync(
+ const gfx::Size& dst_size,
+ int32 bytes_per_row,
+ int32 row_stride_bytes,
+ unsigned char* out,
+ const base::Callback<void(bool)>& callback) {
+ Request* request = new Request(dst_size,
+ bytes_per_row,
+ row_stride_bytes,
+ out,
+ callback);
+ request_queue_.push(request);
+ request->buffer = context_->createBuffer();
+ context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
+ request->buffer);
+ context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
+ 4 * dst_size.GetArea(),
+ NULL,
+ GL_STREAM_READ);
+
+ context_->readPixels(0, 0, dst_size.width(), dst_size.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
+ cc::SyncPointHelper::SignalSyncPoint(
+ context_,
+ context_->insertSyncPoint(),
+ base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request));
+}
+
+
void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture(
WebGLId src_texture,
const gfx::Size& src_size,
@@ -192,38 +334,21 @@ void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture(
false,
#endif
quality);
- context_->flush();
- Request* request = new Request(texture, dst_size, out, callback);
- request_queue_.push(request);
-
- ScopedFlush flush(context_);
ScopedFramebuffer dst_framebuffer(context_, context_->createFramebuffer());
- gfx::Size size;
- size = request->size;
-
ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
dst_framebuffer);
- ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, request->texture);
+ ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
context_->framebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
- request->texture,
+ texture,
0);
- request->buffer = context_->createBuffer();
- context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
- request->buffer);
- context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
- 4 * size.GetArea(),
- NULL,
- GL_STREAM_READ);
-
- context_->readPixels(0, 0, size.width(), size.height(),
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
- cc::SyncPointHelper::SignalSyncPoint(
- context_,
- context_->insertSyncPoint(),
- base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request));
+ ReadbackAsync(dst_size,
+ dst_size.width() * 4,
+ dst_size.width() * 4,
+ out,
+ callback);
+ context_->deleteTexture(texture);
}
void GLHelper::CopyTextureToImpl::ReadbackTextureSync(
@@ -265,19 +390,29 @@ WebKit::WebGLId GLHelper::CopyTextureToImpl::CopyAndScaleTexture(
void GLHelper::CopyTextureToImpl::ReadbackDone(Request* request) {
TRACE_EVENT0("mirror",
- "GLHelper::CopyTextureToImpl::CheckReadBackFramebufferComplete");
+ "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
DCHECK(request == request_queue_.front());
bool result = false;
if (request->buffer != 0) {
context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
request->buffer);
- void* data = context_->mapBufferCHROMIUM(
- GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY);
-
+ unsigned char* data = static_cast<unsigned char *>(
+ context_->mapBufferCHROMIUM(
+ GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
if (data) {
result = true;
- memcpy(request->pixels, data, request->size.GetArea() * 4);
+ if (request->bytes_per_row == request->size.width() * 4 &&
+ request->bytes_per_row == request->row_stride_bytes) {
+ memcpy(request->pixels, data, request->size.GetArea() * 4);
+ } else {
+ unsigned char* out = request->pixels;
+ for (int y = 0; y < request->size.height(); y++) {
+ memcpy(out, data, request->bytes_per_row);
+ out += request->row_stride_bytes;
+ data += request->size.width() * 4;
+ }
+ }
context_->unmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
}
context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
@@ -292,10 +427,6 @@ void GLHelper::CopyTextureToImpl::FinishRequest(Request* request,
request_queue_.pop();
request->callback.Run(result);
ScopedFlush flush(context_);
- if (request->texture != 0) {
- context_->deleteTexture(request->texture);
- request->texture = 0;
- }
if (request->buffer != 0) {
context_->deleteBuffer(request->buffer);
request->buffer = 0;
@@ -336,8 +467,8 @@ void GLHelper::CropScaleReadbackAndCleanTexture(
}
void GLHelper::ReadbackTextureSync(WebKit::WebGLId texture,
- const gfx::Rect& src_rect,
- unsigned char* out) {
+ const gfx::Rect& src_rect,
+ unsigned char* out) {
InitCopyTextToImpl();
copy_texture_to_impl_->ReadbackTextureSync(texture,
src_rect,
@@ -423,4 +554,168 @@ void GLHelper::CopySubBufferDamage(WebKit::WebGLId texture,
}
}
+const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = {
+ 0.257f, 0.504f, 0.098f, 0.0625f
+};
+const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = {
+ -0.148f, -0.291f, 0.439f, 0.5f
+};
+const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = {
+ 0.439f, -0.368f, -0.071f, 0.5f
+};
+
+// YUV readback constructors. Initiates the main scaler pipeline and
+// one planar scaler for each of the Y, U and V planes.
+GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
+ WebGraphicsContext3D* context,
+ CopyTextureToImpl* copy_impl,
+ GLHelperScaling* scaler_impl,
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const gfx::Rect& dst_subrect,
+ bool flip_vertically)
+ : context_(context),
+ copy_impl_(copy_impl),
+ dst_size_(dst_size),
+ dst_subrect_(dst_subrect),
+ scaler_(context, scaler_impl->CreateScaler(
+ quality,
+ src_size,
+ src_subrect,
+ dst_subrect.size(),
+ flip_vertically,
+ false)),
+ y_(context, scaler_impl->CreatePlanarScaler(
+ dst_subrect.size(),
+ gfx::Rect(0, 0,
+ (dst_subrect.width() + 3) & ~3,
+ dst_subrect.height()),
+ gfx::Size((dst_subrect.width() + 3) / 4,
+ dst_subrect.height()),
+ false,
+ kRGBtoYColorWeights)),
+ u_(context, scaler_impl->CreatePlanarScaler(
+ dst_subrect.size(),
+ gfx::Rect(0, 0,
+ (dst_subrect.width() + 7) & ~7,
+ (dst_subrect.height() + 1) & ~1),
+ gfx::Size((dst_subrect.width() + 7) / 8,
+ (dst_subrect.height() + 1) / 2),
+ false,
+ kRGBtoUColorWeights)),
+ v_(context, scaler_impl->CreatePlanarScaler(
+ dst_subrect.size(),
+ gfx::Rect(0, 0,
+ (dst_subrect.width() + 7) & ~7,
+ (dst_subrect.height() + 1) & ~1),
+ gfx::Size((dst_subrect.width() + 7) / 8,
+ (dst_subrect.height() + 1) / 2),
+ false,
+ kRGBtoVColorWeights)) {
+ DCHECK(!(dst_size.width() & 1));
+ DCHECK(!(dst_size.height() & 1));
+ DCHECK(!(dst_subrect.width() & 1));
+ DCHECK(!(dst_subrect.height() & 1));
+ DCHECK(!(dst_subrect.x() & 1));
+ DCHECK(!(dst_subrect.y() & 1));
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
+ WebKit::WebGLId src_texture,
+ media::VideoFrame *target,
+ const base::Callback<void(bool)>& callback) {
+ // Scale texture to right size.
+ scaler_.Scale(src_texture);
+
+ // Convert the scaled texture in to Y, U and V planes.
+ y_.Scale(scaler_.texture_);
+ u_.Scale(scaler_.texture_);
+ v_.Scale(scaler_.texture_);
+
+ if (target->coded_size() != dst_size_) {
+ DCHECK(target->coded_size() == dst_size_);
+ LOG(ERROR) << "ReadbackYUV size error!";
+ callback.Run(false);
+ return;
+ }
+
+ // Read back planes, one at a time.
+ ReadbackPlane(&y_,
+ target,
+ media::VideoFrame::kYPlane,
+ 0,
+ base::Bind(&nullcallback));
+ ReadbackPlane(&u_,
+ target,
+ media::VideoFrame::kUPlane,
+ 1,
+ base::Bind(&nullcallback));
+ ReadbackPlane(&v_,
+ target,
+ media::VideoFrame::kVPlane,
+ 1,
+ callback);
+ context_->bindFramebuffer(GL_FRAMEBUFFER, 0);
+ media::LetterboxYUV(target, dst_subrect_);
+}
+
+void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackPlane(
+ ScalerHolder *scaler,
+ media::VideoFrame *target,
+ int plane,
+ int size_shift,
+ const base::Callback<void(bool)>& callback) {
+ context_->bindFramebuffer(GL_FRAMEBUFFER, scaler->framebuffer());
+ copy_impl_->ReadbackAsync(
+ scaler->scaler()->DstSize(),
+ dst_subrect_.width() >> size_shift,
+ target->stride(plane),
+ target->data(plane) +
+ target->stride(plane) * (dst_subrect_.y() >> size_shift) +
+ (dst_subrect_.x() >> size_shift),
+ callback);
+}
+
+
+ReadbackYUVInterface*
+GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(
+ GLHelper::ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const gfx::Rect& dst_subrect,
+ bool flip_vertically) {
+ helper_->InitScalerImpl();
+ return new ReadbackYUVImpl(
+ context_,
+ this,
+ helper_->scaler_impl_.get(),
+ quality,
+ src_size,
+ src_subrect,
+ dst_size,
+ dst_subrect,
+ flip_vertically);
+}
+
+ReadbackYUVInterface*
+GLHelper::CreateReadbackPipelineYUV(
+ ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const gfx::Rect& dst_subrect,
+ bool flip_vertically) {
+ InitCopyTextToImpl();
+ return copy_texture_to_impl_->CreateReadbackPipelineYUV(
+ quality,
+ src_size,
+ src_subrect,
+ dst_size,
+ dst_subrect,
+ flip_vertically);
+}
+
} // namespace content
diff --git a/content/common/gpu/client/gl_helper.h b/content/common/gpu/client/gl_helper.h
index 7e4112c..95e27e6 100644
--- a/content/common/gpu/client/gl_helper.h
+++ b/content/common/gpu/client/gl_helper.h
@@ -17,6 +17,10 @@ class Rect;
class Size;
}
+namespace media {
+class VideoFrame;
+};
+
class SkRegion;
namespace content {
@@ -178,6 +182,9 @@ class ScopedFlush {
DISALLOW_COPY_AND_ASSIGN(ScopedFlush);
};
+
+class ReadbackYUVInterface;
+
// Provides higher level operations on top of the WebKit::WebGraphicsContext3D
// interfaces.
class CONTENT_EXPORT GLHelper {
@@ -284,6 +291,20 @@ class CONTENT_EXPORT GLHelper {
bool vertically_flip_texture,
bool swizzle);
+ // Create a readback pipeline that will scale a subsection of the source
+ // texture, then convert it to YUV422 planar form and then read back that.
+ // This reduces the amount of memory read from GPU to CPU memory by a factor
+ // 2.6, which can be quite handy since readbacks have very limited speed
+ // on some platforms. All values in |dst_size| and |dst_subrect| must be
+ // a multiple of two.
+ ReadbackYUVInterface* CreateReadbackPipelineYUV(
+ ScalerQuality quality,
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ const gfx::Rect& dst_subrect,
+ bool flip_vertically);
+
protected:
class CopyTextureToImpl;
@@ -299,6 +320,26 @@ class CONTENT_EXPORT GLHelper {
DISALLOW_COPY_AND_ASSIGN(GLHelper);
};
+// Similar to a ScalerInterface, a yuv readback pipeline will
+// cache a scaler and all intermediate textures and frame buffers
+// needed to scale, crop, letterbox and read back a texture from
+// the GPU into CPU-accessible RAM. A single readback pipeline
+// can handle multiple outstanding readbacks at the same time, but
+// if the source or destination sizes change, you'll need to create
+// a new readback pipeline.
+class CONTENT_EXPORT ReadbackYUVInterface {
+public:
+ ReadbackYUVInterface() {}
+ virtual ~ReadbackYUVInterface() {}
+
+ // Note that |target| must use YV12 format.
+ virtual void ReadbackYUV(
+ WebKit::WebGLId texture,
+ media::VideoFrame* target,
+ const base::Callback<void(bool)>& callback) = 0;
+ virtual GLHelper::ScalerInterface* scaler() = 0;
+};
+
} // namespace content
#endif // CONTENT_COMMON_GPU_CLIENT_GL_HELPER_H_
diff --git a/content/common/gpu/client/gl_helper_scaling.cc b/content/common/gpu/client/gl_helper_scaling.cc
index 4983f22..220845e 100644
--- a/content/common/gpu/client/gl_helper_scaling.cc
+++ b/content/common/gpu/client/gl_helper_scaling.cc
@@ -61,7 +61,8 @@ class ShaderProgram : public base::RefCounted<ShaderProgram> {
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
bool scale_x,
- bool flip_y);
+ bool flip_y,
+ GLfloat color_weights[4]);
private:
friend class base::RefCounted<ShaderProgram>;
@@ -88,6 +89,8 @@ class ShaderProgram : public base::RefCounted<ShaderProgram> {
WebKit::WGC3Dint dst_pixelsize_location_;
// Location of vector for scaling direction.
WebKit::WGC3Dint scaling_vector_location_;
+ // Location of color weights.
+ WebKit::WGC3Dint color_weights_location_;
DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
};
@@ -107,16 +110,30 @@ class ScalerImpl : public GLHelper::ScalerInterface {
// If we are scaling in both X and Y, |scale_x| is ignored.
// If |vertically_flip_texture| is true, output will be upside-down.
// If |swizzle| is true, RGBA will be transformed into BGRA.
+ // |color_weights| are only used together with SHADER_PLANAR to specify
+ // how to convert RGB colors into a single value.
ScalerImpl(WebGraphicsContext3D* context,
GLHelperScaling* scaler_helper,
const GLHelperScaling::ScalerStage &scaler_stage,
- ScalerImpl* subscaler) :
+ ScalerImpl* subscaler,
+ const float* color_weights) :
context_(context),
scaler_helper_(scaler_helper),
spec_(scaler_stage),
intermediate_texture_(0),
dst_framebuffer_(context, context_->createFramebuffer()),
subscaler_(subscaler) {
+ if (color_weights) {
+ color_weights_[0] = color_weights[0];
+ color_weights_[1] = color_weights[1];
+ color_weights_[2] = color_weights[2];
+ color_weights_[3] = color_weights[3];
+ } else {
+ color_weights_[0] = 0.0;
+ color_weights_[1] = 0.0;
+ color_weights_[2] = 0.0;
+ color_weights_[3] = 0.0;
+ }
shader_program_ = scaler_helper_->GetShaderProgram(spec_.shader,
spec_.swizzle);
@@ -182,7 +199,8 @@ class ScalerImpl : public GLHelper::ScalerInterface {
spec_.src_subrect,
spec_.dst_size,
spec_.scale_x,
- spec_.vertically_flip_texture);
+ spec_.vertically_flip_texture,
+ color_weights_);
context_->viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height());
// Conduct texture mapping by drawing a quad composed of two triangles.
@@ -209,6 +227,7 @@ class ScalerImpl : public GLHelper::ScalerInterface {
WebGraphicsContext3D* context_;
GLHelperScaling* scaler_helper_;
GLHelperScaling::ScalerStage spec_;
+ GLfloat color_weights_[4];
WebKit::WebGLId intermediate_texture_;
scoped_refptr<ShaderProgram> shader_program_;
ScopedFramebuffer dst_framebuffer_;
@@ -427,11 +446,28 @@ GLHelperScaling::CreateScaler(GLHelper::ScalerQuality quality,
ScalerImpl* ret = NULL;
for (unsigned int i = 0; i < scaler_stages.size(); i++) {
- ret = new ScalerImpl(context_, this, scaler_stages[i], ret);
+ ret = new ScalerImpl(context_, this, scaler_stages[i], ret, NULL);
}
return ret;
}
+GLHelper::ScalerInterface*
+GLHelperScaling::CreatePlanarScaler(
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ const float color_weights[4]) {
+ ScalerStage stage(SHADER_PLANAR,
+ src_size,
+ src_subrect,
+ dst_size,
+ true,
+ vertically_flip_texture,
+ false);
+ return new ScalerImpl(context_, this, stage, NULL, color_weights);
+}
+
const WebKit::WGC3Dfloat GLHelperScaling::kVertexAttributes[] = {
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
@@ -478,7 +514,8 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
vertex_program.append(
" gl_Position = vec4(a_position, 0.0, 1.0);\n"
- " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
+ " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"
+ " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n");
switch (type) {
case SHADER_BILINEAR:
@@ -495,7 +532,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
shared_variables.append(
"varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad
vertex_program.append(
- " vec2 step = scaling_vector / dst_pixelsize / 4.0;\n"
+ " step /= 4.0;\n"
" v_texcoords.xy = texcoord + step;\n"
" v_texcoords.zw = texcoord - step;\n");
@@ -511,7 +548,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
"varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad
"varying vec2 v_texcoords2;\n");
vertex_program.append(
- " vec2 step = scaling_vector / dst_pixelsize / 3.0;\n"
+ " step /= 3.0;\n"
" v_texcoords1.xy = texcoord + step;\n"
" v_texcoords1.zw = texcoord;\n"
" v_texcoords2 = texcoord - step;\n");
@@ -527,7 +564,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
shared_variables.append(
"varying vec4 v_texcoords[2];\n");
vertex_program.append(
- " vec2 step = scaling_vector / dst_pixelsize / 8.0;\n"
+ " step /= 8.0;\n"
" v_texcoords[0].xy = texcoord - step * 3.0;\n"
" v_texcoords[0].zw = texcoord - step;\n"
" v_texcoords[1].xy = texcoord + step;\n"
@@ -548,7 +585,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
shared_variables.append(
"varying vec4 v_texcoords[2];\n");
vertex_program.append(
- " vec2 step = 1.0 / 4.0 / dst_pixelsize;\n"
+ " step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
" v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
" v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
" v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
@@ -572,7 +609,7 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
"const float LobeWeight = -3.0 / 64.0;\n"
"varying vec4 v_texcoords[2];\n");
vertex_program.append(
- " vec2 step = scaling_vector / src_pixelsize;\n"
+ " step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
" v_texcoords[0].xy = texcoord - LobeDist * step;\n"
" v_texcoords[0].zw = texcoord - CenterDist * step;\n"
" v_texcoords[1].xy = texcoord + CenterDist * step;\n"
@@ -627,6 +664,32 @@ GLHelperScaling::GetShaderProgram(ShaderType type,
" vec2 step = scaling_vector / src_pixelsize;\n"
" gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
break;
+
+ case SHADER_PLANAR:
+ // Converts four RGBA pixels into one pixel. Each RGBA
+ // pixel will be dot-multiplied with the color weights and
+ // then placed into a component of the output. This is used to
+ // convert RGBA textures into Y, U and V textures. We do this
+ // because single-component textures are not renderable on all
+ // architectures.
+ shared_variables.append(
+ "varying vec4 v_texcoords[2];\n"
+ "uniform vec4 color_weights;\n");
+ vertex_program.append(
+ " step /= 4.0;\n"
+ " v_texcoords[0].xy = texcoord - step * 1.5;\n"
+ " v_texcoords[0].zw = texcoord - step * 0.5;\n"
+ " v_texcoords[1].xy = texcoord + step * 0.5;\n"
+ " v_texcoords[1].zw = texcoord + step * 1.5;\n");
+ fragment_program.append(
+ " gl_FragColor = color_weights * mat4(\n"
+ " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
+ " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
+ " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
+ " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
+ // Swizzle makes no sense for this shader.
+ DCHECK(!swizzle);
+ break;
}
if (swizzle) {
fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n");
@@ -688,6 +751,8 @@ bool ShaderProgram::Setup(const WebKit::WGC3Dchar* vertex_shader_text,
"dst_pixelsize");
scaling_vector_location_ = context_->getUniformLocation(program_,
"scaling_vector");
+ color_weights_location_ = context_->getUniformLocation(program_,
+ "color_weights");
return true;
}
@@ -696,7 +761,8 @@ void ShaderProgram::UseProgram(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
bool scale_x,
- bool flip_y) {
+ bool flip_y,
+ GLfloat color_weights[4]) {
context_->useProgram(program_);
WebKit::WGC3Dintptr offset = 0;
@@ -742,6 +808,7 @@ void ShaderProgram::UseProgram(
context_->uniform2f(scaling_vector_location_,
scale_x ? 1.0 : 0.0,
scale_x ? 0.0 : 1.0);
+ context_->uniform4fv(color_weights_location_, 1, color_weights);
}
} // namespace content
diff --git a/content/common/gpu/client/gl_helper_scaling.h b/content/common/gpu/client/gl_helper_scaling.h
index ff5ba7c..097dfb6 100644
--- a/content/common/gpu/client/gl_helper_scaling.h
+++ b/content/common/gpu/client/gl_helper_scaling.h
@@ -28,6 +28,7 @@ class CONTENT_EXPORT GLHelperScaling {
SHADER_BILINEAR2X2,
SHADER_BICUBIC_UPSCALE,
SHADER_BICUBIC_HALF_1D,
+ SHADER_PLANAR,
};
typedef std::pair<ShaderType, bool> ShaderProgramKeyType;
@@ -45,6 +46,13 @@ class CONTENT_EXPORT GLHelperScaling {
bool vertically_flip_texture,
bool swizzle);
+ GLHelper::ScalerInterface* CreatePlanarScaler(
+ const gfx::Size& src_size,
+ const gfx::Rect& src_subrect,
+ const gfx::Size& dst_size,
+ bool vertically_flip_texture,
+ const float color_weights[4]);
+
private:
// A ScaleOp represents a pass in a scaler pipeline, in one dimension.
// Note that when quality is GOOD, multiple scaler passes will be
diff --git a/content/common/gpu/client/gl_helper_unittests.cc b/content/common/gpu/client/gl_helper_unittests.cc
index 21a77f7..4e53258 100644
--- a/content/common/gpu/client/gl_helper_unittests.cc
+++ b/content/common/gpu/client/gl_helper_unittests.cc
@@ -12,14 +12,18 @@
#include <GLES2/gl2extchromium.h>
#include "base/at_exit.h"
+#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/run_loop.h"
#include "base/stringprintf.h"
#include "base/time.h"
#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/client/gl_helper_scaling.h"
#include "content/public/test/unittest_test_suite.h"
#include "content/test/content_test_suite.h"
+#include "media/base/video_frame.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkTypes.h"
@@ -59,7 +63,6 @@ class GLHelperTest : public testing::Test {
webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl::
CreateOffscreenContext(attributes));
context_->makeContextCurrent();
-
helper_.reset(new content::GLHelper(context_.get()));
helper_scaling_.reset(new content::GLHelperScaling(
context_.get(),
@@ -156,6 +159,9 @@ class GLHelperTest : public testing::Test {
ret.append("bicubic 1/2");
xy_matters = true;
break;
+ case GLHelperScaling::SHADER_PLANAR:
+ ret.append("planar");
+ break;
}
if (xy_matters) {
@@ -233,6 +239,10 @@ class GLHelperTest : public testing::Test {
// Codify valid scale operations.
switch (scaler_stages[i].shader) {
+ case GLHelperScaling::SHADER_PLANAR:
+ EXPECT_TRUE(false) << "Invalid shader.";
+ break;
+
case GLHelperScaling::SHADER_BILINEAR:
if (quality != content::GLHelper::SCALER_QUALITY_FAST) {
x_samples = 1;
@@ -503,19 +513,19 @@ class GLHelperTest : public testing::Test {
for (int x = 0; x < xsize; ++x) {
for (int y = 0; y < ysize; ++y) {
switch (test_pattern) {
- case 0: // Smooth test pattern
+ case 0: // Smooth test pattern
SetChannel(&input_pixels, x, y, 0, x * 10);
SetChannel(&input_pixels, x, y, 1, y * 10);
SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
SetChannel(&input_pixels, x, y, 3, 255);
break;
- case 1: // Small blocks
+ case 1: // Small blocks
SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
SetChannel(&input_pixels, x, y, 3, 255);
break;
- case 2: // Medium blocks
+ case 2: // Medium blocks
SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50);
SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50);
SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5);
@@ -647,6 +657,249 @@ class GLHelperTest : public testing::Test {
EXPECT_EQ(PrintStages(stages), description);
}
+ // Note: Left/Right means Top/Bottom when used for Y dimension.
+ enum Margin {
+ MarginLeft,
+ MarginMiddle,
+ MarginRight,
+ MarginInvalid,
+ };
+
+ static Margin NextMargin(Margin m) {
+ switch (m) {
+ case MarginLeft:
+ return MarginMiddle;
+ case MarginMiddle:
+ return MarginRight;
+ case MarginRight:
+ return MarginInvalid;
+ default:
+ return MarginInvalid;
+ }
+ }
+
+ int compute_margin(int insize, int outsize, Margin m) {
+ int available = outsize - insize;
+ switch (m) {
+ default:
+ EXPECT_TRUE(false) << "This should not happen.";
+ return 0;
+ case MarginLeft:
+ return 0;
+ case MarginMiddle:
+ return (available / 2) & ~1;
+ case MarginRight:
+ return available;
+ }
+ }
+
+ // Convert 0.0 - 1.0 to 0 - 255
+ int float_to_byte(float v) {
+ int ret = static_cast<int>(floorf(v * 255.0f + 0.5f));
+ if (ret < 0) {
+ return 0;
+ }
+ if (ret > 255) {
+ return 255;
+ }
+ return ret;
+ }
+
+ static void callcallback(const base::Callback<void()>& callback,
+ bool result) {
+ callback.Run();
+ }
+
+ void PrintPlane(unsigned char *plane, int xsize, int stride, int ysize) {
+ for (int y = 0; y < ysize; y++) {
+ for (int x = 0; x < xsize ; x++) {
+ printf("%3d, ", plane[y * stride + x]);
+ }
+ printf(" (%p)\n", plane + y * stride);
+ }
+ }
+
+ // Compare two planes make sure that each component of each pixel
+ // is no more than |maxdiff| apart.
+ void ComparePlane(unsigned char* truth,
+ unsigned char* other,
+ int maxdiff,
+ int xsize,
+ int stride,
+ int ysize,
+ SkBitmap* source,
+ std::string message) {
+ for (int x = 0; x < xsize; x++) {
+ for (int y = 0; y < ysize; y++) {
+ int a = other[y * stride + x];
+ int b = truth[y * stride + x];
+ EXPECT_NEAR(a, b, maxdiff)
+ << " x=" << x
+ << " y=" << y
+ << " " << message;
+ if (std::abs(a - b) > maxdiff) {
+ printf("-------expected--------\n");
+ PrintPlane(truth, xsize, stride, ysize);
+ printf("-------actual--------\n");
+ PrintPlane(other, xsize, stride, ysize);
+ if (source) {
+ printf("-------before yuv conversion: red--------\n");
+ PrintChannel(source, 0);
+ printf("-------before yuv conversion: green------\n");
+ PrintChannel(source, 1);
+ printf("-------before yuv conversion: blue-------\n");
+ PrintChannel(source, 2);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ // YUV readback test. Create a test pattern, convert to YUV
+ // with reference implementation and compare to what gl_helper
+ // returns.
+ void TestYUVReadback(int xsize,
+ int ysize,
+ int output_xsize,
+ int output_ysize,
+ int xmargin,
+ int ymargin,
+ int test_pattern) {
+ WebGLId src_texture = context_->createTexture();
+ SkBitmap input_pixels;
+ input_pixels.setConfig(SkBitmap::kARGB_8888_Config, xsize, ysize);
+ input_pixels.allocPixels();
+ SkAutoLockPixels lock(input_pixels);
+
+ for (int x = 0; x < xsize; ++x) {
+ for (int y = 0; y < ysize; ++y) {
+ switch (test_pattern) {
+ case 0: // Smooth test pattern
+ SetChannel(&input_pixels, x, y, 0, x * 10);
+ SetChannel(&input_pixels, x, y, 1, y * 10);
+ SetChannel(&input_pixels, x, y, 2, (x + y) * 10);
+ SetChannel(&input_pixels, x, y, 3, 255);
+ break;
+ case 1: // Small blocks
+ SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0);
+ SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0);
+ SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0);
+ SetChannel(&input_pixels, x, y, 3, 255);
+ break;
+ case 2: // Medium blocks
+ SetChannel(&input_pixels, x, y, 0, 10 + x/2 * 50);
+ SetChannel(&input_pixels, x, y, 1, 10 + y/3 * 50);
+ SetChannel(&input_pixels, x, y, 2, (x + y)/5 * 50 + 5);
+ SetChannel(&input_pixels, x, y, 3, 255);
+ break;
+ }
+ }
+ }
+
+ context_->bindTexture(GL_TEXTURE_2D, src_texture);
+ context_->texImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ xsize,
+ ysize,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ input_pixels.getPixels());
+
+ std::string message = base::StringPrintf("input size: %dx%d "
+ "output size: %dx%d "
+ "margin: %dx%d "
+ "pattern: %d",
+ xsize, ysize,
+ output_xsize, output_ysize,
+ xmargin, ymargin,
+ test_pattern);
+ scoped_ptr<ReadbackYUVInterface> yuv_reader(
+ helper_->CreateReadbackPipelineYUV(
+ content::GLHelper::SCALER_QUALITY_GOOD,
+ gfx::Size(xsize, ysize),
+ gfx::Rect(0, 0, xsize, ysize),
+ gfx::Size(output_xsize, output_ysize),
+ gfx::Rect(xmargin, ymargin, xsize, ysize),
+ false));
+
+ scoped_refptr<media::VideoFrame> output_frame =
+ media::VideoFrame::CreateFrame(
+ media::VideoFrame::YV12,
+ gfx::Size(output_xsize, output_ysize),
+ gfx::Rect(0, 0, output_xsize, output_ysize),
+ gfx::Size(output_xsize, output_ysize),
+ base::TimeDelta::FromSeconds(0));
+ scoped_refptr<media::VideoFrame> truth_frame =
+ media::VideoFrame::CreateFrame(
+ media::VideoFrame::YV12,
+ gfx::Size(output_xsize, output_ysize),
+ gfx::Rect(0, 0, output_xsize, output_ysize),
+ gfx::Size(output_xsize, output_ysize),
+ base::TimeDelta::FromSeconds(0));
+
+ base::RunLoop run_loop;
+ yuv_reader->ReadbackYUV(
+ src_texture,
+ output_frame.get(),
+ base::Bind(&callcallback, run_loop.QuitClosure()));
+ run_loop.Run();
+
+ unsigned char* Y = truth_frame->data(media::VideoFrame::kYPlane);
+ unsigned char* U = truth_frame->data(media::VideoFrame::kUPlane);
+ unsigned char* V = truth_frame->data(media::VideoFrame::kVPlane);
+ int32 y_stride = truth_frame->stride(media::VideoFrame::kYPlane);
+ int32 u_stride = truth_frame->stride(media::VideoFrame::kUPlane);
+ int32 v_stride = truth_frame->stride(media::VideoFrame::kVPlane);
+ memset(Y, 0x00, y_stride * output_ysize);
+ memset(U, 0x80, u_stride * output_ysize / 2);
+ memset(V, 0x80, v_stride * output_ysize / 2);
+
+ for (int y = 0; y < ysize; y++) {
+ for (int x = 0; x < xsize; x++) {
+ Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte(
+ ChannelAsFloat(&input_pixels, x, y, 0) * 0.257 +
+ ChannelAsFloat(&input_pixels, x, y, 1) * 0.504 +
+ ChannelAsFloat(&input_pixels, x, y, 2) * 0.098 +
+ 0.0625);
+ }
+ }
+
+ for (int y = 0; y < ysize / 2; y++) {
+ for (int x = 0; x < xsize / 2; x++) {
+ U[(y + ymargin / 2) * u_stride + x + xmargin / 2] =
+ float_to_byte(
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * -0.148 +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.291 +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * 0.439 +
+ 0.5);
+ V[(y + ymargin / 2) * v_stride + x + xmargin / 2] =
+ float_to_byte(
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * 0.439 +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * -0.368 +
+ Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * -0.071 +
+ 0.5);
+ }
+ }
+
+ ComparePlane(Y, output_frame->data(media::VideoFrame::kYPlane), 2,
+ output_xsize, y_stride, output_ysize,
+ &input_pixels,
+ message + " Y plane");
+ ComparePlane(U, output_frame->data(media::VideoFrame::kUPlane), 2,
+ output_xsize / 2, u_stride, output_ysize / 2,
+ &input_pixels,
+ message + " U plane");
+ ComparePlane(V, output_frame->data(media::VideoFrame::kVPlane), 2,
+ output_xsize / 2, v_stride, output_ysize / 2,
+ &input_pixels,
+ message + " V plane");
+
+ context_->deleteTexture(src_texture);
+ }
+
void TestAddOps(int src,
int dst,
bool scale_x,
@@ -844,6 +1097,42 @@ class GLHelperTest : public testing::Test {
std::deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
};
+TEST_F(GLHelperTest, YUVReadbackTest) {
+ int sizes[] = { 2, 4, 14 };
+ for (unsigned int x = 0; x < arraysize(sizes); x++) {
+ for (unsigned int y = 0; y < arraysize(sizes); y++) {
+ for (unsigned int ox = x; ox < arraysize(sizes); ox++) {
+ for (unsigned int oy = y; oy < arraysize(sizes); oy++) {
+ // If output is a subsection of the destination frame, (letterbox)
+ // then try different variations of where the subsection goes.
+ for (Margin xm = x < ox ? MarginLeft : MarginRight;
+ xm <= MarginRight;
+ xm = NextMargin(xm)) {
+ for (Margin ym = y < oy ? MarginLeft : MarginRight;
+ ym <= MarginRight;
+ ym = NextMargin(ym)) {
+ for (int pattern = 0; pattern < 3; pattern++) {
+ TestYUVReadback(
+ sizes[x],
+ sizes[y],
+ sizes[ox],
+ sizes[oy],
+ compute_margin(sizes[x], sizes[ox], xm),
+ compute_margin(sizes[y], sizes[oy], ym),
+ pattern);
+ if (HasFailure()) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
// Per pixel tests, all sizes are small so that we can print
// out the generated bitmaps.
TEST_F(GLHelperTest, ScaleTest) {
@@ -947,5 +1236,7 @@ int main(int argc, char** argv) {
#endif
gfx::GLSurface::InitializeOneOff();
- return content::UnitTestTestSuite(suite).Run();
+ content::UnitTestTestSuite runner(suite);
+ base::MessageLoop message_loop;
+ return runner.Run();
}
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index a2c93f3..de747d1c 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -685,6 +685,13 @@ const char kSitePerProcess[] = "site-per-process";
// content. The switch is intended only for tests.
const char kSkipGpuDataLoading[] = "skip-gpu-data-loading";
+// Scaling quality for capturing tab. Should be one of "fast", "good" or "best".
+// One flag for upscaling, one for downscaling.
+// Upscale defaults to "best".
+const char kTabCaptureUpscaleQuality[] = "tab-capture-upscale-quality";
+// Upscale defaults to "good".
+const char kTabCaptureDownscaleQuality[] = "tab-capture-downscale-quality";
+
// GestureTapDown events are deferred by this many miillseconds before
// sending them to the renderer.
const char kTapDownDeferralTimeMs[] = "tap-down-deferral-time";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 00a6c75..d3c4443 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -205,6 +205,8 @@ CONTENT_EXPORT extern const char kSimulateTouchScreenWithMouse[];
CONTENT_EXPORT extern const char kSingleProcess[];
CONTENT_EXPORT extern const char kSitePerProcess[];
CONTENT_EXPORT extern const char kSkipGpuDataLoading[];
+extern const char kTabCaptureUpscaleQuality[];
+extern const char kTabCaptureDownscaleQuality[];
extern const char kTapDownDeferralTimeMs[];
CONTENT_EXPORT extern const char kTestSandbox[];
CONTENT_EXPORT extern const char kTestingFixedHttpPort[];