Index: athena/content/content_proxy.cc |
diff --git a/athena/content/content_proxy.cc b/athena/content/content_proxy.cc |
index a63aa35ed08ddf20da32a8e2ab0e3595d071db28..7614a7c9e078219eb4b21b384924c9f78feeb2e2 100644 |
--- a/athena/content/content_proxy.cc |
+++ b/athena/content/content_proxy.cc |
@@ -6,72 +6,104 @@ |
#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_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> { |
oshima
2014/09/22 16:10:28
If this is to be used by overview mode, it will be
Mr4D (OOO till 08-26)
2014/09/23 19:07:34
No. The problem is that every activity will have t
|
+ public: |
+ ProxyImageData() { |
+ } |
+ |
+ void EncodeImage(const SkBitmap& bitmap, base::Closure callback) { |
+ if (!base::WorkerPool::PostTaskAndReply(FROM_HERE, |
+ base::Bind(&ProxyImageData::EncodeOnWorker, |
+ this, |
+ bitmap), |
+ callback, |
+ true)) { |
+ callback.Run(); |
oshima
2014/09/22 16:10:28
should this be DCHECK? Does it make sense to run c
Mr4D (OOO till 08-26)
2014/09/23 19:07:33
Adding a DCHECK is possibly a good idea to see a f
oshima
2014/09/24 00:40:00
Acknowledged.
|
+ } |
+ } |
+ |
+ 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); |
+}; |
sadrul
2014/09/22 16:13:32
Any chance this can be consolidated with Navigatio
Mr4D (OOO till 08-26)
2014/09/23 19:07:34
I looked into that at the beginning, but there are
|
+ |
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), |
+ 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() { |
content_loaded_ = false; |
} |
-gfx::ImageSkia ContentProxy::GetContentImage() { |
- // For the time being we keep this here and return an empty image. |
- return image_; |
+scoped_ptr<gfx::Image> ContentProxy::GetContentImage() { |
+ // While we compress to PNG, we use the original read back. |
+ if (image_.IsEmpty()) |
oshima
2014/09/22 16:10:28
shouldn't this be !image_.IsEmpty(), or
maybe chec
Mr4D (OOO till 08-26)
2014/09/23 19:07:34
Done.
|
+ return scoped_ptr<gfx::Image>(new gfx::Image(image_)); |
+ |
+ // Otherwise we convert the PNG. |
+ std::vector<gfx::ImagePNGRep> image_reps; |
+ image_reps.push_back(gfx::ImagePNGRep(png_data_, 2.0f)); |
oshima
2014/09/22 16:10:28
why this is using 2x scale factor?
Mr4D (OOO till 08-26)
2014/09/23 19:07:33
I wasn't able to test this yet, but according to t
oshima
2014/09/24 00:40:00
The thumbnail should be "unscaled" image (that is,
Mr4D (OOO till 08-26)
2014/09/24 16:18:25
Done.
I tested and the result seems to work. Note
|
+ return scoped_ptr<gfx::Image>(new gfx::Image(image_reps)); |
} |
void ContentProxy::EvictContent() { |
- HideProxyContent(); |
- CreateSolidProxyContent(); |
- ShowProxyContent(); |
+ image_ = gfx::Image(); |
+ png_data_->Release(); |
} |
-void ContentProxy::Reparent(aura::Window* new_parent_window) { |
- if (new_parent_window == window_) |
- return; |
- |
+void ContentProxy::ContentGetsDestroyed() { |
// 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_) { |
oshima
2014/09/22 16:10:28
don't you have to change content_visible_?
Rathe
Mr4D (OOO till 08-26)
2014/09/23 19:07:34
Yes, it's better to set it.
I didn't want to use
|
// Show the original |web_view_| again. |
web_view_->SetFastResize(false); |
// If the content is loaded, we ask it to relayout itself since the |
@@ -85,7 +117,7 @@ void ContentProxy::ShowOriginalContent() { |
} |
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 |
@@ -94,39 +126,55 @@ void ContentProxy::HideOriginalContent() { |
web_view_->SetVisible(false); |
// Don't allow the content to get resized with window size changes. |
web_view_->SetFastResize(true); |
oshima
2014/09/22 18:26:05
We may not want to resize at all when hidden? It m
Mr4D (OOO till 08-26)
2014/09/23 19:07:33
An excellent idea. I was thinking of maybe doing w
|
+ 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(); |
-} |
+ // 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::ContentReadBackAsImage, |
+ proxy_content_to_image_factory_.GetWeakPtr()), |
sadrul
2014/09/22 16:13:32
InvalidateWeakPtrs() first, so that we ignore an e
Mr4D (OOO till 08-26)
2014/09/23 19:07:34
This function gets only called once through the co
|
+ 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::ContentReadBackAsImage(bool success, |
+ const SkBitmap& bitmap) { |
+ // Now we can hide the content. |
+ HideOriginalContent(); |
sadrul
2014/09/22 16:13:32
Should we hide only if the PNG encoding works? (i.
Mr4D (OOO till 08-26)
2014/09/23 19:07:34
I chose this location since I think its the best l
|
+ |
+ 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. |
+ image_ = gfx::Image::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. |
+ image_ = gfx::Image(); |
sadrul
2014/09/22 16:13:33
Call image_ something else, to clarify that it is
Mr4D (OOO till 08-26)
2014/09/23 19:07:33
Done.
|
} |
} // namespace athena |