Index: athena/content/content_proxy.cc |
diff --git a/athena/content/content_proxy.cc b/athena/content/content_proxy.cc |
index a63aa35ed08ddf20da32a8e2ab0e3595d071db28..7dce0e9266ec0ede53b7d2f90095e9215afa6245 100644 |
--- a/athena/content/content_proxy.cc |
+++ b/athena/content/content_proxy.cc |
@@ -6,32 +6,74 @@ |
#include "athena/activity/public/activity.h" |
#include "athena/activity/public/activity_view_model.h" |
+#include "base/bind.h" |
+#include "base/threading/worker_pool.h" |
+#include "content/public/browser/render_view_host.h" |
+#include "content/public/browser/render_widget_host_view.h" |
#include "content/public/browser/web_contents.h" |
#include "ui/aura/window.h" |
-#include "ui/views/background.h" |
+#include "ui/gfx/codec/png_codec.h" |
+#include "ui/gfx/geometry/rect.h" |
+#include "ui/gfx/image/image.h" |
+#include "ui/gfx/image/image_png_rep.h" |
#include "ui/views/controls/webview/webview.h" |
#include "ui/views/widget/widget.h" |
namespace athena { |
+// Encodes an A8 SkBitmap to grayscale PNG in a worker thread. |
+class ProxyImageData : public base::RefCountedThreadSafe<ProxyImageData> { |
+ public: |
+ ProxyImageData() { |
+ } |
+ |
+ void EncodeImage(const SkBitmap& bitmap, base::Closure callback) { |
+ if (!base::WorkerPool::PostTaskAndReply(FROM_HERE, |
+ base::Bind(&ProxyImageData::EncodeOnWorker, |
+ this, |
+ bitmap), |
+ callback, |
+ true)) { |
+ // When coming here, the resulting image will be empty. |
+ DCHECK(false) << "Cannot start bitmap encode task."; |
+ callback.Run(); |
+ } |
+ } |
+ |
+ scoped_refptr<base::RefCountedBytes> data() const { return data_; } |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<ProxyImageData>; |
+ virtual ~ProxyImageData() { |
+ } |
+ |
+ void EncodeOnWorker(const SkBitmap& bitmap) { |
+ DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType); |
+ // Encode the A8 bitmap to grayscale PNG treating alpha as color intensity. |
+ std::vector<unsigned char> data; |
+ if (gfx::PNGCodec::EncodeA8SkBitmap(bitmap, &data)) |
+ data_ = new base::RefCountedBytes(data); |
+ } |
+ |
+ scoped_refptr<base::RefCountedBytes> data_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ProxyImageData); |
+}; |
+ |
ContentProxy::ContentProxy(views::WebView* web_view, Activity* activity) |
: web_view_(web_view), |
- window_(activity->GetWindow()), |
- background_color_( |
- activity->GetActivityViewModel()->GetRepresentativeColor()), |
- content_loaded_(true) { |
+ content_visible_(true), |
+ content_loaded_(true), |
+ content_creation_called_(false), |
+ proxy_content_to_image_factory_(this) { |
+ // Note: The content will be hidden once the image got created. |
CreateProxyContent(); |
- HideOriginalContent(); |
} |
ContentProxy::~ContentProxy() { |
// If we still have a connection to the original Activity, we make it visible |
// again. |
ShowOriginalContent(); |
- // At this point we should either have no view - or the view should not be |
- // shown by its parent anymore. |
- DCHECK(!proxy_content_.get() || !proxy_content_->parent()); |
- proxy_content_.reset(); |
} |
void ContentProxy::ContentWillUnload() { |
@@ -39,39 +81,33 @@ void ContentProxy::ContentWillUnload() { |
} |
gfx::ImageSkia ContentProxy::GetContentImage() { |
- // For the time being we keep this here and return an empty image. |
- return image_; |
+ // While we compress to PNG, we use the original read back. |
+ if (!raw_image_.isNull() || !png_data_.get()) |
+ return raw_image_; |
+ |
+ // Otherwise we convert the PNG. |
+ std::vector<gfx::ImagePNGRep> image_reps; |
+ image_reps.push_back(gfx::ImagePNGRep(png_data_, 0.0f)); |
+ return *(gfx::Image(image_reps).ToImageSkia()); |
} |
void ContentProxy::EvictContent() { |
- HideProxyContent(); |
- CreateSolidProxyContent(); |
- ShowProxyContent(); |
+ raw_image_ = gfx::ImageSkia(); |
+ png_data_->Release(); |
} |
-void ContentProxy::Reparent(aura::Window* new_parent_window) { |
- if (new_parent_window == window_) |
- return; |
- |
+void ContentProxy::OnPreContentDestroyed() { |
// Since we are breaking now the connection to the old content, we make the |
// content visible again before we continue. |
// Note: Since the owning window is invisible, it does not matter that we |
// make the web content visible if the window gets destroyed shortly after. |
ShowOriginalContent(); |
- // Transfer the |proxy_content_| to the passed window. |
- window_ = new_parent_window; |
web_view_ = NULL; |
- |
- // Move the view to the new window and show it there. |
- HideOriginalContent(); |
} |
void ContentProxy::ShowOriginalContent() { |
- // Hide our content. |
- HideProxyContent(); |
- |
- if (web_view_) { |
+ if (web_view_ && !content_visible_) { |
// Show the original |web_view_| again. |
web_view_->SetFastResize(false); |
// If the content is loaded, we ask it to relayout itself since the |
@@ -81,52 +117,73 @@ void ContentProxy::ShowOriginalContent() { |
web_view_->Layout(); |
web_view_->GetWebContents()->GetNativeView()->Show(); |
web_view_->SetVisible(true); |
+ content_visible_ = true; |
} |
} |
void ContentProxy::HideOriginalContent() { |
- if (web_view_) { |
+ if (web_view_ && content_visible_) { |
// Hide the |web_view_|. |
// TODO(skuhne): We might consider removing the view from the window while |
// it's hidden - it should work the same way as show/hide and does not have |
- // any window re-ordering effect. |
+ // any window re-ordering effect. Furthermore we want possibly to suppress |
+ // any resizing of content (not only fast resize) here to avoid jank on |
+ // rotation. |
web_view_->GetWebContents()->GetNativeView()->Hide(); |
web_view_->SetVisible(false); |
// Don't allow the content to get resized with window size changes. |
web_view_->SetFastResize(true); |
+ content_visible_ = false; |
} |
- |
- // Show our replacement content. |
- ShowProxyContent(); |
} |
void ContentProxy::CreateProxyContent() { |
- // For the time being we create only a solid color here. |
- // TODO(skuhne): Replace with copying the drawn content into |proxy_content_| |
- // instead. |
- CreateSolidProxyContent(); |
-} |
+ DCHECK(!content_creation_called_); |
+ content_creation_called_ = true; |
+ // Unit tests might not have a |web_view_|. |
+ if (!web_view_) |
+ return; |
-void ContentProxy::CreateSolidProxyContent() { |
- proxy_content_.reset(new views::View()); |
- proxy_content_->set_owned_by_client(); |
- proxy_content_->set_background( |
- views::Background::CreateSolidBackground(background_color_)); |
+ content::RenderViewHost* host = |
+ web_view_->GetWebContents()->GetRenderViewHost(); |
+ DCHECK(host && host->GetView()); |
+ gfx::Size source = host->GetView()->GetViewBounds().size(); |
+ gfx::Size target = gfx::Size(source.width() / 2, source.height() / 2); |
+ host->CopyFromBackingStore( |
+ gfx::Rect(), |
+ target, |
+ base::Bind(&ContentProxy::OnContentImageRead, |
+ proxy_content_to_image_factory_.GetWeakPtr()), |
+ kAlpha_8_SkColorType); |
} |
-void ContentProxy::ShowProxyContent() { |
- views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_); |
- DCHECK(widget); |
- views::View* client_view = widget->client_view(); |
- // Place the view in front of all others. |
- client_view->AddChildView(proxy_content_.get()); |
- proxy_content_->SetSize(client_view->bounds().size()); |
+void ContentProxy::OnContentImageRead(bool success, const SkBitmap& bitmap) { |
+ // Now we can hide the content. Note that after hiding we are freeing memory |
+ // and if something goes wrong we will end up with an empty page. |
+ HideOriginalContent(); |
+ |
+ if (!success || bitmap.empty() || bitmap.isNull()) |
+ return; |
+ |
+ // While we are encoding the image, we keep the current image as reference |
+ // to have something for the overview mode to grab. Once we have the encoded |
+ // PNG, we will get rid of this. |
+ raw_image_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); |
+ |
+ scoped_refptr<ProxyImageData> png_image = new ProxyImageData(); |
+ png_image->EncodeImage( |
+ bitmap, |
+ base::Bind(&ContentProxy::OnContentImageEncodeComplete, |
+ proxy_content_to_image_factory_.GetWeakPtr(), |
+ png_image)); |
} |
-void ContentProxy::HideProxyContent() { |
- views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_); |
- views::View* client_view = widget->client_view(); |
- client_view->RemoveChildView(proxy_content_.get()); |
+void ContentProxy::OnContentImageEncodeComplete( |
+ scoped_refptr<ProxyImageData> image) { |
+ png_data_ = image->data(); |
+ |
+ // From now on we decode the image as needed to save memory. |
+ raw_image_ = gfx::ImageSkia(); |
} |
} // namespace athena |