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