1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/snapshot/snapshot_win.h"
6 
7 #include <memory>
8 
9 #include "base/callback.h"
10 #include "base/win/windows_version.h"
11 #include "skia/ext/platform_canvas.h"
12 #include "skia/ext/skia_utils_win.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_tree_host.h"
15 #include "ui/gfx/geometry/rect.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/snapshot/snapshot.h"
19 #include "ui/snapshot/snapshot_aura.h"
20 
21 namespace {
22 
23 // Windows 8.1 is the first version that supports PW_RENDERFULLCONTENT.
24 // Without that flag PrintWindow may not correctly capture what's actually
25 // onscreen.
UseAuraSnapshot()26 bool UseAuraSnapshot() {
27   return (base::win::GetVersion() < base::win::Version::WIN8_1);
28 }
29 
30 }  // namespace
31 
32 namespace ui {
33 
34 namespace internal {
35 
GrabHwndSnapshot(HWND window_handle,const gfx::Rect & snapshot_bounds_in_pixels,const gfx::Rect & clip_rect_in_pixels,gfx::Image * image)36 bool GrabHwndSnapshot(HWND window_handle,
37                       const gfx::Rect& snapshot_bounds_in_pixels,
38                       const gfx::Rect& clip_rect_in_pixels,
39                       gfx::Image* image) {
40   gfx::Rect snapshot_bounds_in_window =
41       snapshot_bounds_in_pixels + clip_rect_in_pixels.OffsetFromOrigin();
42   gfx::Size bitmap_size(snapshot_bounds_in_window.right(),
43                         snapshot_bounds_in_window.bottom());
44 
45   std::unique_ptr<SkCanvas> canvas = skia::CreatePlatformCanvas(
46       bitmap_size.width(), bitmap_size.height(), false);
47   HDC mem_hdc = skia::GetNativeDrawingContext(canvas.get());
48 
49   // Grab a copy of the window. Use PrintWindow because it works even when the
50   // window's partially occluded. The PW_RENDERFULLCONTENT flag is undocumented,
51   // but works starting in Windows 8.1. It allows for capturing the contents of
52   // the window that are drawn using DirectComposition.
53   UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
54 
55   BOOL result = PrintWindow(window_handle, mem_hdc, flags);
56   if (!result) {
57     PLOG(ERROR) << "Failed to print window";
58     return false;
59   }
60 
61   SkBitmap bitmap;
62   bitmap.allocN32Pixels(snapshot_bounds_in_window.width(),
63                         snapshot_bounds_in_window.height());
64   canvas->readPixels(bitmap, snapshot_bounds_in_window.x(),
65                      snapshot_bounds_in_window.y());
66 
67   // Clear the region of the bitmap outside the clip rect to white.
68   SkCanvas image_canvas(bitmap);
69   SkPaint paint;
70   paint.setColor(SK_ColorWHITE);
71 
72   SkRegion region;
73   gfx::Rect clip_in_bitmap(clip_rect_in_pixels.size());
74   clip_in_bitmap.Offset(-snapshot_bounds_in_pixels.OffsetFromOrigin());
75   region.setRect(
76       gfx::RectToSkIRect(gfx::Rect(snapshot_bounds_in_pixels.size())));
77   region.op(gfx::RectToSkIRect(clip_in_bitmap), SkRegion::kDifference_Op);
78   image_canvas.drawRegion(region, paint);
79 
80   *image = gfx::Image::CreateFrom1xBitmap(bitmap);
81 
82   return true;
83 }
84 
85 }  // namespace internal
86 
GrabViewSnapshot(gfx::NativeView view_handle,const gfx::Rect & snapshot_bounds,gfx::Image * image)87 bool GrabViewSnapshot(gfx::NativeView view_handle,
88                       const gfx::Rect& snapshot_bounds,
89                       gfx::Image* image) {
90   return GrabWindowSnapshot(view_handle, snapshot_bounds, image);
91 }
92 
GrabWindowSnapshot(gfx::NativeWindow window_handle,const gfx::Rect & snapshot_bounds,gfx::Image * image)93 bool GrabWindowSnapshot(gfx::NativeWindow window_handle,
94                         const gfx::Rect& snapshot_bounds,
95                         gfx::Image* image) {
96   if (UseAuraSnapshot()) {
97     // Not supported in Aura.  Callers should fall back to the async version.
98     return false;
99   }
100 
101   DCHECK(window_handle);
102   gfx::Rect window_bounds = window_handle->GetBoundsInRootWindow();
103   aura::WindowTreeHost* host = window_handle->GetHost();
104   DCHECK(host);
105   HWND hwnd = host->GetAcceleratedWidget();
106 
107   gfx::RectF window_bounds_in_pixels(window_bounds);
108   host->GetRootTransform().TransformRect(&window_bounds_in_pixels);
109   gfx::RectF snapshot_bounds_in_pixels(snapshot_bounds);
110   host->GetRootTransform().TransformRect(&snapshot_bounds_in_pixels);
111 
112   gfx::Rect expanded_window_bounds_in_pixels =
113       gfx::ToEnclosingRect(window_bounds_in_pixels);
114   RECT client_area;
115   ::GetClientRect(hwnd, &client_area);
116   gfx::Rect client_area_rect(client_area);
117   client_area_rect.set_origin(gfx::Point());
118 
119   expanded_window_bounds_in_pixels.Intersect(client_area_rect);
120 
121   return internal::GrabHwndSnapshot(
122       hwnd, gfx::ToEnclosingRect(snapshot_bounds_in_pixels),
123       expanded_window_bounds_in_pixels, image);
124 }
125 
GrabWindowSnapshotAsync(gfx::NativeWindow window,const gfx::Rect & source_rect,GrabWindowSnapshotAsyncCallback callback)126 void GrabWindowSnapshotAsync(gfx::NativeWindow window,
127                              const gfx::Rect& source_rect,
128                              GrabWindowSnapshotAsyncCallback callback) {
129   if (UseAuraSnapshot()) {
130     GrabWindowSnapshotAsyncAura(window, source_rect, std::move(callback));
131     return;
132   }
133   gfx::Image image;
134   GrabWindowSnapshot(window, source_rect, &image);
135   std::move(callback).Run(image);
136 }
137 
GrabViewSnapshotAsync(gfx::NativeView view,const gfx::Rect & source_rect,GrabWindowSnapshotAsyncCallback callback)138 void GrabViewSnapshotAsync(gfx::NativeView view,
139                            const gfx::Rect& source_rect,
140                            GrabWindowSnapshotAsyncCallback callback) {
141   if (UseAuraSnapshot()) {
142     GrabWindowSnapshotAsyncAura(view, source_rect, std::move(callback));
143     return;
144   }
145   NOTIMPLEMENTED();
146   std::move(callback).Run(gfx::Image());
147 }
148 
GrabWindowSnapshotAndScaleAsync(gfx::NativeWindow window,const gfx::Rect & source_rect,const gfx::Size & target_size,GrabWindowSnapshotAsyncCallback callback)149 void GrabWindowSnapshotAndScaleAsync(gfx::NativeWindow window,
150                                      const gfx::Rect& source_rect,
151                                      const gfx::Size& target_size,
152                                      GrabWindowSnapshotAsyncCallback callback) {
153   if (UseAuraSnapshot()) {
154     GrabWindowSnapshotAndScaleAsyncAura(window, source_rect, target_size,
155                                         std::move(callback));
156     return;
157   }
158   NOTIMPLEMENTED();
159   std::move(callback).Run(gfx::Image());
160 }
161 
162 }  // namespace ui
163