diff options
author | simonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-20 10:25:53 +0000 |
---|---|---|
committer | simonmorris@chromium.org <simonmorris@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-20 10:25:53 +0000 |
commit | 73a822fc4edecfd7995319f791f0fdd2fe60da60 (patch) | |
tree | ba9da5ad28528835015e5ec810110fdd6fc48ad2 /remoting | |
parent | 55cb9d5693f8b8e9ebac337d55c2dcab4cd27dd0 (diff) | |
download | chromium_src-73a822fc4edecfd7995319f791f0fdd2fe60da60.zip chromium_src-73a822fc4edecfd7995319f791f0fdd2fe60da60.tar.gz chromium_src-73a822fc4edecfd7995319f791f0fdd2fe60da60.tar.bz2 |
If the user selects scale-to-fit, then PepperView starts
maintaining and displaying a scaled copy of its pixel backing
store.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6811043
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82279 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/client/appengine/chromoting_session.html | 2 | ||||
-rw-r--r-- | remoting/client/appengine/static_files/chromoting_session.js | 8 | ||||
-rw-r--r-- | remoting/client/appengine/static_files/main.css | 5 | ||||
-rw-r--r-- | remoting/client/chromoting_view.h | 5 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_instance.cc | 15 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_instance.h | 7 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_scriptable_object.cc | 20 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_scriptable_object.h | 6 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_input_handler.cc | 11 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view.cc | 253 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view.h | 77 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view_proxy.cc | 21 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view_proxy.h | 22 | ||||
-rw-r--r-- | remoting/client/x11_view.cc | 6 | ||||
-rw-r--r-- | remoting/client/x11_view.h | 18 |
15 files changed, 423 insertions, 53 deletions
diff --git a/remoting/client/appengine/chromoting_session.html b/remoting/client/appengine/chromoting_session.html index eba943d..8af2190 100644 --- a/remoting/client/appengine/chromoting_session.html +++ b/remoting/client/appengine/chromoting_session.html @@ -29,6 +29,8 @@ found in the LICENSE file. <body class="chromoting_body" onload="init();"> <div id="status_msg_div"> <span id="status_msg" class="status_msg">Initializing...</span> + <input type="button" value="Scale to fit" class="scale_to_fit_toggle" + id="scale_to_fit_toggle" onclick="toggleScaleToFit();"/> <input type="button" value="Show Debug Log" class="debug_log_toggle" id="debug_log_toggle" onclick="toggleDebugLog();"/> </div> diff --git a/remoting/client/appengine/static_files/chromoting_session.js b/remoting/client/appengine/static_files/chromoting_session.js index cb7813b..50c9079 100644 --- a/remoting/client/appengine/static_files/chromoting_session.js +++ b/remoting/client/appengine/static_files/chromoting_session.js @@ -10,6 +10,7 @@ var MAX_DEBUG_LOG_SIZE = 1000; // old messages. This starts at 1 and is incremented for each new message. chromoting.messageId = 1; +chromoting.scaleToFit = false; // Default to trying to sandboxed connections. chromoting.connectMethod = 'sandboxed'; @@ -145,6 +146,13 @@ function toggleDebugLog() { } } +function toggleScaleToFit() { + chromoting.scaleToFit = !chromoting.scaleToFit; + document.getElementById("scale_to_fit_toggle").value = + chromoting.scaleToFit ? "No scaling" : "Scale to fit"; + chromoting.plugin.setScaleToFit(chromoting.scaleToFit); +} + function submitLogin() { var username = document.getElementById("username").value; var password = document.getElementById("password").value; diff --git a/remoting/client/appengine/static_files/main.css b/remoting/client/appengine/static_files/main.css index 7fb6ed1..38a9e9a 100644 --- a/remoting/client/appengine/static_files/main.css +++ b/remoting/client/appengine/static_files/main.css @@ -148,6 +148,11 @@ a.hostentry { float: right; } +.scale_to_fit_toggle { + line-height: 0.8em; + float: right; + } + .gaia_login_panel { -webkit-user-select: none; font-family: arial,sans-serif; diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h index 5dfd408..0a8c721 100644 --- a/remoting/client/chromoting_view.h +++ b/remoting/client/chromoting_view.h @@ -9,6 +9,7 @@ #include "base/memory/ref_counted.h" #include "media/base/video_frame.h" +#include "ui/gfx/point.h" class MessageLoop; @@ -72,6 +73,10 @@ class ChromotingView { // extends past the end of the backing store, it is filled with black. virtual void SetViewport(int x, int y, int width, int height) = 0; + // Converts screen co-ordinates to host co-ordinates, and clips to the host + // screen. + virtual gfx::Point ConvertScreenToHost(const gfx::Point& p) const = 0; + protected: // Framebuffer for the decoder. scoped_refptr<media::VideoFrame> frame_; diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index a23598c..874e336 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -185,6 +185,15 @@ void ChromotingInstance::ViewChanged(const pp::Rect& position, view_->Paint(); } +void ChromotingInstance::DidChangeView(const pp::Rect& position, + const pp::Rect& clip) { + // This lets |view_| implement scale-to-fit. But it only specifies a + // sub-rectangle of the plugin window as the rectangle on which the host + // screen can be displayed, so |view_| has to make sure the plugin window + // is large. + view_->SetScreenSize(clip.width(), clip.height()); +} + bool ChromotingInstance::HandleInputEvent(const PP_InputEvent& event) { DCHECK(CurrentlyOnPluginThread()); @@ -261,6 +270,10 @@ void ChromotingInstance::SubmitLoginInfo(const std::string& username, new DeleteTask<protocol::LocalLoginCredentials>(credentials)); } +void ChromotingInstance::SetScaleToFit(bool scale_to_fit) { + view_proxy_->SetScaleToFit(scale_to_fit); +} + void ChromotingInstance::LogDebugInfo(const std::string& info) { GetScriptableObject()->LogDebugInfo(info); } diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index 8c5aee8..3458c78 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -70,6 +70,10 @@ class ChromotingInstance : public pp::Instance { virtual pp::Var GetInstanceObject(); virtual void ViewChanged(const pp::Rect& position, const pp::Rect& clip); + // pp::Instance interface. + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) + OVERRIDE; + // Convenience wrapper to get the ChromotingScriptableObject. ChromotingScriptableObject* GetScriptableObject(); @@ -77,6 +81,9 @@ class ChromotingInstance : public pp::Instance { void SubmitLoginInfo(const std::string& username, const std::string& password); + // Called by ChromotingScriptableObject to set scale-to-fit. + void SetScaleToFit(bool scale_to_fit); + void LogDebugInfo(const std::string& info); // Return statistics record by ChromotingClient. diff --git a/remoting/client/plugin/chromoting_scriptable_object.cc b/remoting/client/plugin/chromoting_scriptable_object.cc index 714be1f..e2b5fe9 100644 --- a/remoting/client/plugin/chromoting_scriptable_object.cc +++ b/remoting/client/plugin/chromoting_scriptable_object.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -87,6 +87,7 @@ void ChromotingScriptableObject::Init() { &ChromotingScriptableObject::DoConnectSandboxed); AddMethod("disconnect", &ChromotingScriptableObject::DoDisconnect); AddMethod("submitLoginInfo", &ChromotingScriptableObject::DoSubmitLogin); + AddMethod("setScaleToFit", &ChromotingScriptableObject::DoSetScaleToFit); AddMethod("onIq", &ChromotingScriptableObject::DoOnIq); } @@ -416,6 +417,23 @@ Var ChromotingScriptableObject::DoSubmitLogin(const std::vector<Var>& args, return Var(); } +Var ChromotingScriptableObject::DoSetScaleToFit(const std::vector<Var>& args, + Var* exception) { + if (args.size() != 1) { + *exception = Var("Usage: setScaleToFit(scale_to_fit)"); + return Var(); + } + + if (!args[0].is_bool()) { + *exception = Var("scale_to_fit must be a boolean."); + return Var(); + } + + LogDebugInfo("Setting scale-to-fit."); + instance_->SetScaleToFit(args[0].AsBool()); + return Var(); +} + Var ChromotingScriptableObject::DoOnIq(const std::vector<Var>& args, Var* exception) { if (args.size() != 1) { diff --git a/remoting/client/plugin/chromoting_scriptable_object.h b/remoting/client/plugin/chromoting_scriptable_object.h index bf997b3..62f9c68 100644 --- a/remoting/client/plugin/chromoting_scriptable_object.h +++ b/remoting/client/plugin/chromoting_scriptable_object.h @@ -80,6 +80,9 @@ // // // Method for submitting login information. // void submitLoginInfo(string username, string password); +// +// // Method for setting scale-to-fit. +// void setScaleToFit(bool scale_to_fit); // } #ifndef REMOTING_CLIENT_PLUGIN_CHROMOTING_SCRIPTABLE_OBJECT_H_ @@ -193,6 +196,9 @@ class ChromotingScriptableObject // This method is called by JS to provide login information. pp::Var DoSubmitLogin(const std::vector<pp::Var>& args, pp::Var* exception); + // This method is called by JS to set scale-to-fit. + pp::Var DoSetScaleToFit(const std::vector<pp::Var>& args, pp::Var* exception); + // This method is caleld by Javascript to provide responses to sendIq() // requests when establishing a sandboxed Chromoting connection. pp::Var DoOnIq(const std::vector<pp::Var>& args, pp::Var* exception); diff --git a/remoting/client/plugin/pepper_input_handler.cc b/remoting/client/plugin/pepper_input_handler.cc index 16a5738..452668e 100644 --- a/remoting/client/plugin/pepper_input_handler.cc +++ b/remoting/client/plugin/pepper_input_handler.cc @@ -1,10 +1,12 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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 "remoting/client/plugin/pepper_input_handler.h" #include "ppapi/c/pp_input_event.h" +#include "remoting/client/chromoting_view.h" +#include "ui/gfx/point.h" namespace remoting { @@ -35,8 +37,11 @@ void PepperInputHandler::HandleCharacterEvent( void PepperInputHandler::HandleMouseMoveEvent( const PP_InputEvent_Mouse& event) { - SendMouseMoveEvent(static_cast<int>(event.x), - static_cast<int>(event.y)); + gfx::Point p(static_cast<int>(event.x), static_cast<int>(event.y)); + // Pepper gives co-ordinates in the plugin instance's co-ordinate system, + // which may be different from the host desktop's co-ordinate system. + p = view_->ConvertScreenToHost(p); + SendMouseMoveEvent(p.x(), p.y()); } void PepperInputHandler::HandleMouseButtonEvent( diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc index 6dc9a51..54b8344 100644 --- a/remoting/client/plugin/pepper_view.cc +++ b/remoting/client/plugin/pepper_view.cc @@ -22,8 +22,12 @@ namespace remoting { PepperView::PepperView(ChromotingInstance* instance, ClientContext* context) : instance_(instance), context_(context), - viewport_width_(0), - viewport_height_(0), + screen_scale_(1.0), + scale_to_fit_(false), + host_width_(0), + host_height_(0), + client_width_(0), + client_height_(0), is_static_fill_(false), static_fill_color_(0), ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { @@ -51,11 +55,11 @@ void PepperView::Paint() { if (is_static_fill_) { LOG(ERROR) << "Static filling " << static_fill_color_; pp::ImageData image(instance_, pp::ImageData::GetNativeImageDataFormat(), - pp::Size(viewport_width_, viewport_height_), + pp::Size(host_width_, host_height_), false); if (image.is_null()) { LOG(ERROR) << "Unable to allocate image of size: " - << viewport_width_ << "x" << viewport_height_; + << host_width_ << "x" << host_height_; return; } @@ -72,8 +76,8 @@ void PepperView::Paint() { task_factory_.NewRunnableMethod(&PepperView::OnPaintDone, base::Time::Now()))); } else { - // TODO(ajwong): We need to keep a backing store image of the viewport that - // has the data here which can be redrawn. + // TODO(ajwong): We need to keep a backing store image of the host screen + // that has the data here which can be redrawn. return; } TraceContext::tracer()->PrintString("End Paint."); @@ -94,6 +98,11 @@ void PepperView::PaintFrame(media::VideoFrame* frame, UpdatedRects* rects) { LOG(ERROR) << "Backing store is not available."; return; } + if (DoScaling()) { + if (!scaled_backing_store_.get() || scaled_backing_store_->is_null()) { + LOG(ERROR) << "Scaled backing store is not available."; + } + } // Copy updated regions to the backing store and then paint the regions. for (size_t i = 0; i < rects->size(); ++i) { @@ -116,10 +125,17 @@ void PepperView::PaintFrame(media::VideoFrame* frame, UpdatedRects* rects) { out += backing_store_->stride(); } - // Pepper Graphics 2D has a strange and badly documented API that the - // point here is the offset from the source rect. Why? - graphics2d_.PaintImageData(*backing_store_.get(), pp::Point(0, 0), - pp::Rect(r.x(), r.y(), r.width(), r.height())); + if (DoScaling()) { + gfx::Rect r_scaled = UpdateScaledBackingStore(r); + graphics2d_.PaintImageData(*scaled_backing_store_.get(), pp::Point(0, 0), + pp::Rect(r_scaled.x(), r_scaled.y(), + r_scaled.width(), r_scaled.height())); + } else { + // Pepper Graphics 2D has a strange and badly documented API that the + // point here is the offset from the source rect. Why? + graphics2d_.PaintImageData(*backing_store_.get(), pp::Point(0, 0), + pp::Rect(r.x(), r.y(), r.width(), r.height())); + } } graphics2d_.Flush(TaskToCompletionCallback( @@ -129,6 +145,76 @@ void PepperView::PaintFrame(media::VideoFrame* frame, UpdatedRects* rects) { TraceContext::tracer()->PrintString("End Paint Frame."); } +gfx::Rect PepperView::UpdateScaledBackingStore(const gfx::Rect& r) { + const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32); + // Find the updated rectangle in the scaled backing store. + gfx::Point top_left = ConvertHostToScreen(r.origin()); + gfx::Point bottom_right = ConvertHostToScreen(gfx::Point(r.right(), + r.bottom())); + int r_scaled_left = top_left.x(); + int r_scaled_right = bottom_right.x(); + int r_scaled_top = top_left.y(); + int r_scaled_bottom = bottom_right.y(); + if (r_scaled_right <= r_scaled_left || r_scaled_bottom <= r_scaled_top) + return gfx::Rect(r_scaled_left, r_scaled_top, 0, 0); + + // Allow for the fact that ConvertHostToScreen and ConvertScreenToHost aren't + // exact inverses. + r_scaled_right++; + r_scaled_bottom++; + + // Clip the scaled rectangle. + r_scaled_left = std::max(r_scaled_left, 0); + r_scaled_left = std::min(r_scaled_left, client_width_); + r_scaled_right = std::max(r_scaled_right, 0); + r_scaled_right = std::min(r_scaled_right, client_width_); + r_scaled_top = std::max(r_scaled_top, 0); + r_scaled_top = std::min(r_scaled_top, client_height_); + r_scaled_bottom = std::max(r_scaled_bottom, 0); + r_scaled_bottom = std::min(r_scaled_bottom, client_height_); + + // Blit from the backing store to the scaled backing store. + for (int y_scaled = r_scaled_top; y_scaled < r_scaled_bottom; y_scaled++) { + int y = ConvertScreenToHost(gfx::Point(0, y_scaled)).y(); + // Special case where each pixel is a word. + if ((kBytesPerPixel == 4) && (backing_store_->stride() % 4 == 0) && + (scaled_backing_store_->stride() % 4 == 0)) { + uint32* from = reinterpret_cast<uint32*>(backing_store_->data()) + + (y * backing_store_->stride() / 4); + uint32* to = reinterpret_cast<uint32*>(scaled_backing_store_->data()) + + (y_scaled * scaled_backing_store_->stride() / 4) + r_scaled_left; + uint32* to_max = to + (r_scaled_right - r_scaled_left); + const int* offset = &screen_x_to_host_x_[r_scaled_left]; + while (to < to_max) + *to++ = from[*offset++]; + } else { + // Currently that special case is the only case that's ever encountered. + NOTREACHED(); + uint8* from = reinterpret_cast<uint8*>(backing_store_->data()) + + (y * backing_store_->stride()); + uint8* to = reinterpret_cast<uint8*>(scaled_backing_store_->data()) + + (y_scaled * scaled_backing_store_->stride()) + + (r_scaled_left * kBytesPerPixel); + for (int x_sc = r_scaled_left; x_sc < r_scaled_right; x_sc++) { + int x = screen_x_to_host_x_[x_sc]; + memcpy(to, from + (x * kBytesPerPixel), kBytesPerPixel); + to += kBytesPerPixel; + } + } + } + return gfx::Rect(r_scaled_left, r_scaled_top, r_scaled_right - r_scaled_left, + r_scaled_bottom - r_scaled_top); +} + +void PepperView::BlankRect(pp::ImageData& image_data, const gfx::Rect& rect) { + const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32); + for (int y = rect.y(); y < rect.bottom(); y++) { + uint8* to = reinterpret_cast<uint8*>(image_data.data()) + + (y * image_data.stride()) + (rect.x() * kBytesPerPixel); + memset(to, 0xff, rect.width() * kBytesPerPixel); + } +} + void PepperView::SetSolidFill(uint32 color) { DCHECK(CurrentlyOnPluginThread()); @@ -184,28 +270,151 @@ void PepperView::UpdateLoginStatus(bool success, const std::string& info) { void PepperView::SetViewport(int x, int y, int width, int height) { DCHECK(CurrentlyOnPluginThread()); - if ((width == viewport_width_) && (height == viewport_height_)) + if ((width == host_width_) && (height == host_height_)) return; - viewport_width_ = width; - viewport_height_ = height; + host_width_ = width; + host_height_ = height; + + ResizeInternals(); + instance_->GetScriptableObject()->SetDesktopSize(host_width_, host_height_); +} + +gfx::Point PepperView::ConvertScreenToHost(const gfx::Point& p) const { + DCHECK(CurrentlyOnPluginThread()); + + int x = static_cast<int>(p.x() / screen_scale_); + x = std::min(x, host_width_ - 1); + x = std::max(x, 0); + int y = static_cast<int>(p.y() / screen_scale_); + y = std::min(y, host_height_ - 1); + y = std::max(y, 0); + return gfx::Point(x, y); +} + +gfx::Point PepperView::ConvertHostToScreen(const gfx::Point& p) const { + return gfx::Point(static_cast<int>(p.x() * screen_scale_), + static_cast<int>(p.y() * screen_scale_)); +} + +void PepperView::SetScreenScale(double screen_scale) { + if (screen_scale == screen_scale_) + return; + screen_scale_ = screen_scale; + ResizeInternals(); + RefreshPaint(); +} + +void PepperView::RefreshPaint() { + if (DoScaling()) { + // Render from the whole backing store, to the scaled backing store, at the + // current scale. + gfx::Rect updated_rect = UpdateScaledBackingStore(gfx::Rect( + host_width_, host_height_)); + // If the scale has just decreased, then there is stale raster data + // to the right of, and below, the scaled copy of the host screen that's + // just been rendered into the scaled backing store. + // So blank out everything to the east of that copy... + BlankRect(*scaled_backing_store_, gfx::Rect( + updated_rect.right(), 0, + scaled_backing_store_->size().width() - updated_rect.right(), + updated_rect.bottom())); + // ...and everything to the south and south-east. + BlankRect(*scaled_backing_store_, gfx::Rect( + 0, updated_rect.bottom(), scaled_backing_store_->size().width(), + scaled_backing_store_->size().height() - updated_rect.bottom())); + graphics2d_.PaintImageData(*scaled_backing_store_.get(), pp::Point(0, 0), + pp::Rect(scaled_backing_store_->size())); + graphics2d_.Flush(TaskToCompletionCallback( + task_factory_.NewRunnableMethod(&PepperView::OnRefreshPaintDone))); + } else { + graphics2d_.PaintImageData(*backing_store_.get(), pp::Point(0, 0), + pp::Rect(backing_store_->size())); + graphics2d_.Flush(TaskToCompletionCallback( + task_factory_.NewRunnableMethod(&PepperView::OnRefreshPaintDone))); + } +} + +void PepperView::ResizeInternals() { graphics2d_ = pp::Graphics2D(instance_, - pp::Size(viewport_width_, viewport_height_), + pp::Size(host_width_, host_height_), false); if (!instance_->BindGraphics(graphics2d_)) { LOG(ERROR) << "Couldn't bind the device context."; return; } + if (host_width_ == 0 && host_height_ == 0) + return; + // Allocate the backing store to save the desktop image. - backing_store_.reset( - new pp::ImageData(instance_, pp::ImageData::GetNativeImageDataFormat(), - pp::Size(viewport_width_, viewport_height_), false)); - DCHECK(backing_store_.get() && !backing_store_->is_null()) - << "Not enough memory for backing store."; + pp::Size host_size(host_width_, host_height_); + if ((backing_store_.get() == NULL) || (backing_store_->size() != host_size)) { + backing_store_.reset( + new pp::ImageData(instance_, pp::ImageData::GetNativeImageDataFormat(), + host_size, false)); + DCHECK(backing_store_.get() && !backing_store_->is_null()) + << "Not enough memory for backing store."; + } - instance_->GetScriptableObject()->SetDesktopSize(width, height); + // Allocate the scaled backing store. + // This is the same size as |graphics2d_|, so that it can be used to blank out + // stale regions of |graphics2d_|, as well as to update |graphics2d_| with + // fresh data. + if (DoScaling()) { + if ((scaled_backing_store_.get() == NULL) || + (scaled_backing_store_->size() != host_size)) { + scaled_backing_store_.reset( + new pp::ImageData(instance_, + pp::ImageData::GetNativeImageDataFormat(), + host_size, false)); + DCHECK(scaled_backing_store_.get() && !scaled_backing_store_->is_null()) + << "Not enough memory for scaled backing store."; + } + } else { + scaled_backing_store_.reset(); + } + + // Cache the horizontal component of the map from client screen co-ordinates + // to host screen co-ordinates. + screen_x_to_host_x_.reset(new int[client_width_]); + for (int x = 0; x < client_width_; x++) + screen_x_to_host_x_[x] = ConvertScreenToHost(gfx::Point(x, 0)).x(); +} + +bool PepperView::DoScaling() const { + return (screen_scale_ != 1.0); +} + +void PepperView::SetScreenSize(int width, int height) { + DCHECK(CurrentlyOnPluginThread()); + + client_width_ = width; + client_height_ = height; + if (!scale_to_fit_) + return; + if (host_width_ == 0 || host_height_ == 0) { + SetScreenScale(1.0); + return; + } + double scale_x = double(client_width_) / double(host_width_); + double scale_y = double(client_height_) / double(host_height_); + double scale = std::min(scale_x, scale_y); + SetScreenScale(scale); +} + +void PepperView::SetScaleToFit(bool scale_to_fit) { + DCHECK(CurrentlyOnPluginThread()); + + if (scale_to_fit == scale_to_fit_) + return; + scale_to_fit_ = scale_to_fit; + if (scale_to_fit_) { + SetScreenSize(client_width_, client_height_); + } else { + SetScreenScale(1.0); + } } void PepperView::AllocateFrame(media::VideoFrame::Format format, @@ -258,4 +467,8 @@ void PepperView::OnPaintDone(base::Time paint_start) { return; } +void PepperView::OnRefreshPaintDone() { + DCHECK(CurrentlyOnPluginThread()); +} + } // namespace remoting diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h index 30f2527..e869d91 100644 --- a/remoting/client/plugin/pepper_view.h +++ b/remoting/client/plugin/pepper_view.h @@ -31,14 +31,16 @@ class PepperView : public ChromotingView, virtual ~PepperView(); // ChromotingView implementation. - virtual bool Initialize(); - virtual void TearDown(); - virtual void Paint(); - virtual void SetSolidFill(uint32 color); - virtual void UnsetSolidFill(); - virtual void SetConnectionState(ConnectionState state); - virtual void UpdateLoginStatus(bool success, const std::string& info); - virtual void SetViewport(int x, int y, int width, int height); + virtual bool Initialize() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual void Paint() OVERRIDE; + virtual void SetSolidFill(uint32 color) OVERRIDE; + virtual void UnsetSolidFill() OVERRIDE; + virtual void SetConnectionState(ConnectionState state) OVERRIDE; + virtual void UpdateLoginStatus(bool success, const std::string& info) + OVERRIDE; + virtual void SetViewport(int x, int y, int width, int height) OVERRIDE; + virtual gfx::Point ConvertScreenToHost(const gfx::Point& p) const OVERRIDE; // FrameConsumer implementation. virtual void AllocateFrame(media::VideoFrame::Format format, @@ -53,12 +55,45 @@ class PepperView : public ChromotingView, UpdatedRects* rects, Task* done); + // Sets the size of the visible screen area that this object can render into. + void SetScreenSize(int width, int height); + + // Sets whether the host screen is scaled to fit the visible screen area. + void SetScaleToFit(bool enabled); + private: void OnPaintDone(base::Time paint_start); + void OnRefreshPaintDone(); void PaintFrame(media::VideoFrame* frame, UpdatedRects* rects); + // Blits the pixels in |r| in the backing store into the corresponding + // rectangle in the scaled backing store. Returns that rectangle. + gfx::Rect UpdateScaledBackingStore(const gfx::Rect& r); + + // Blanks out a rectangle in an image. + void BlankRect(pp::ImageData& image_data, const gfx::Rect& rect); + + // Converts host co-ordinates to screen co-ordinates. + gfx::Point ConvertHostToScreen(const gfx::Point& p) const; + + // Sets the screen scale. A value of 1.0 indicates no scaling. + void SetScreenScale(double screen_scale); + + // Paints the entire host screen. + // This is called, for example, when the screen scale is changed. + void RefreshPaint(); + + // Resizes internals of this object after the host screen size has changed, + // or the scale applied to that screen has changed. + // More specifically, pepper's graphics object, the backing stores, and the + // scaling cache are resized and/or recalculated. + void ResizeInternals(); + + // Whether to scale the screen. + bool DoScaling() const; + // Reference to the creating plugin instance. Needed for interacting with - // pepper. Marking explciitly as const since it must be initialized at + // pepper. Marking explicitly as const since it must be initialized at // object creation, and never change. ChromotingInstance* const instance_; @@ -70,8 +105,28 @@ class PepperView : public ChromotingView, // A backing store that saves the current desktop image. scoped_ptr<pp::ImageData> backing_store_; - int viewport_width_; - int viewport_height_; + // A scaled copy of the backing store. + scoped_ptr<pp::ImageData> scaled_backing_store_; + + // The factor by which to scale the host screen. A value of 1.0 indicates + // no scaling. + double screen_scale_; + + // An array containing the x co-ordinate of the point on the host screen + // corresponding to the point (i, 0) on the client screen. + scoped_array<int> screen_x_to_host_x_; + + // Whether to scale to fit. + bool scale_to_fit_; + + // The size of the host screen. + int host_width_; + int host_height_; + + // The size of the visible area on the client screen that can be used to + // display the host screen. + int client_width_; + int client_height_; bool is_static_fill_; uint32 static_fill_color_; diff --git a/remoting/client/plugin/pepper_view_proxy.cc b/remoting/client/plugin/pepper_view_proxy.cc index e01ee9f..5f523a1 100644 --- a/remoting/client/plugin/pepper_view_proxy.cc +++ b/remoting/client/plugin/pepper_view_proxy.cc @@ -103,6 +103,16 @@ void PepperViewProxy::SetViewport(int x, int y, int width, int height) { view_->SetViewport(x, y, width, height); } +gfx::Point PepperViewProxy::ConvertScreenToHost(const gfx::Point& p) const { + // This method returns a value, so must run synchronously, so must be + // called only on the pepper thread. + DCHECK(CurrentlyOnPluginThread()); + + if (view_) + return view_->ConvertScreenToHost(p); + return gfx::Point(); +} + void PepperViewProxy::AllocateFrame( media::VideoFrame::Format format, size_t width, @@ -150,6 +160,17 @@ void PepperViewProxy::OnPartialFrameOutput(media::VideoFrame* frame, view_->OnPartialFrameOutput(frame, rects, done); } +void PepperViewProxy::SetScaleToFit(bool scale_to_fit) { + if (instance_ && !CurrentlyOnPluginThread()) { + RunTaskOnPluginThread( + NewTracedMethod(this, &PepperViewProxy::SetScaleToFit, scale_to_fit)); + return; + } + + if (view_) + view_->SetScaleToFit(scale_to_fit); +} + void PepperViewProxy::Detach() { DCHECK(CurrentlyOnPluginThread()); instance_ = NULL; diff --git a/remoting/client/plugin/pepper_view_proxy.h b/remoting/client/plugin/pepper_view_proxy.h index f6212d3..d9075442 100644 --- a/remoting/client/plugin/pepper_view_proxy.h +++ b/remoting/client/plugin/pepper_view_proxy.h @@ -32,14 +32,18 @@ class PepperViewProxy : public base::RefCountedThreadSafe<PepperViewProxy>, virtual ~PepperViewProxy(); // ChromotingView implementation. - virtual bool Initialize(); - virtual void TearDown(); - virtual void Paint(); - virtual void SetSolidFill(uint32 color); - virtual void UnsetSolidFill(); - virtual void SetConnectionState(ConnectionState state); - virtual void UpdateLoginStatus(bool success, const std::string& info); - virtual void SetViewport(int x, int y, int width, int height); + virtual bool Initialize() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual void Paint() OVERRIDE; + virtual void SetSolidFill(uint32 color) OVERRIDE; + virtual void UnsetSolidFill() OVERRIDE; + virtual void SetConnectionState(ConnectionState state) OVERRIDE; + virtual void UpdateLoginStatus(bool success, const std::string& info) + OVERRIDE; + virtual void SetViewport(int x, int y, int width, int height) OVERRIDE; + // This method returns a value, so must run synchronously, so must be + // called only on the pepper thread. + virtual gfx::Point ConvertScreenToHost(const gfx::Point& p) const OVERRIDE; // FrameConsumer implementation. virtual void AllocateFrame(media::VideoFrame::Format format, @@ -54,6 +58,8 @@ class PepperViewProxy : public base::RefCountedThreadSafe<PepperViewProxy>, UpdatedRects* rects, Task* done); + void SetScaleToFit(bool scale_to_fit); + // Remove the reference to |instance_| and |view_| by setting the value to // NULL. // This method should only be called on pepper thread. diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc index 2bbddd3..a7501d8 100644 --- a/remoting/client/x11_view.cc +++ b/remoting/client/x11_view.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -145,6 +145,10 @@ void X11View::SetViewport(int x, int y, int width, int height) { // TODO(garykac): Implement. } +gfx::Point X11View::ConvertScreenToHost(const gfx::Point& p) const { + return p; +} + void X11View::InitPaintTarget() { // Testing XRender support. int dummy; diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h index 21b6438..963e9b8 100644 --- a/remoting/client/x11_view.h +++ b/remoting/client/x11_view.h @@ -24,14 +24,16 @@ class X11View : public ChromotingView, public FrameConsumer { virtual ~X11View(); // ChromotingView implementations. - virtual bool Initialize(); - virtual void TearDown(); - virtual void Paint(); - virtual void SetSolidFill(uint32 color); - virtual void UnsetSolidFill(); - virtual void SetConnectionState(ConnectionState s); - virtual void UpdateLoginStatus(bool success, const std::string& info); - virtual void SetViewport(int x, int y, int width, int height); + virtual bool Initialize() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual void Paint() OVERRIDE; + virtual void SetSolidFill(uint32 color) OVERRIDE; + virtual void UnsetSolidFill() OVERRIDE; + virtual void SetConnectionState(ConnectionState s) OVERRIDE; + virtual void UpdateLoginStatus(bool success, const std::string& info) + OVERRIDE; + virtual void SetViewport(int x, int y, int width, int height) OVERRIDE; + virtual gfx::Point ConvertScreenToHost(const gfx::Point& p) const OVERRIDE; // FrameConsumer implementation. virtual void AllocateFrame(media::VideoFrame::Format format, |