OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/devtools/protocol/color_picker.h" | 5 #include "chrome/browser/devtools/devtools_eye_dropper.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "build/build_config.h" | 8 #include "build/build_config.h" |
9 #include "content/browser/renderer_host/render_view_host_impl.h" | 9 #include "content/public/browser/render_view_host.h" |
10 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 10 #include "content/public/browser/render_widget_host.h" |
11 #include "content/common/cursors/webcursor.h" | 11 #include "content/public/browser/render_widget_host_view.h" |
| 12 #include "content/public/browser/web_contents.h" |
| 13 #include "content/public/common/cursor_info.h" |
12 #include "content/public/common/screen_info.h" | 14 #include "content/public/common/screen_info.h" |
13 #include "third_party/WebKit/public/platform/WebCursorInfo.h" | |
14 #include "third_party/WebKit/public/platform/WebInputEvent.h" | 15 #include "third_party/WebKit/public/platform/WebInputEvent.h" |
| 16 #include "third_party/WebKit/public/platform/WebMouseEvent.h" |
15 #include "third_party/skia/include/core/SkCanvas.h" | 17 #include "third_party/skia/include/core/SkCanvas.h" |
16 #include "third_party/skia/include/core/SkPaint.h" | 18 #include "third_party/skia/include/core/SkPaint.h" |
17 #include "third_party/skia/include/core/SkPath.h" | 19 #include "third_party/skia/include/core/SkPath.h" |
18 #include "ui/gfx/geometry/size_conversions.h" | 20 #include "ui/gfx/geometry/size_conversions.h" |
19 | 21 |
20 namespace content { | 22 DevToolsEyeDropper::DevToolsEyeDropper(content::WebContents* web_contents, |
21 namespace protocol { | 23 EyeDropperCallback callback) |
22 | 24 : content::WebContentsObserver(web_contents), |
23 ColorPicker::ColorPicker(ColorPickedCallback callback) | 25 callback_(callback), |
24 : callback_(callback), | |
25 enabled_(false), | |
26 last_cursor_x_(-1), | 26 last_cursor_x_(-1), |
27 last_cursor_y_(-1), | 27 last_cursor_y_(-1), |
28 host_(nullptr), | 28 host_(nullptr), |
29 weak_factory_(this) { | 29 weak_factory_(this) { |
30 mouse_event_callback_ = base::Bind( | 30 mouse_event_callback_ = |
31 &ColorPicker::HandleMouseEvent, | 31 base::Bind(&DevToolsEyeDropper::HandleMouseEvent, base::Unretained(this)); |
32 base::Unretained(this)); | 32 content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); |
33 } | 33 if (rvh) { |
34 | 34 AttachToHost(rvh->GetWidget()); |
35 ColorPicker::~ColorPicker() { | |
36 } | |
37 | |
38 void ColorPicker::SetRenderWidgetHost(RenderWidgetHostImpl* host) { | |
39 if (host_ == host) | |
40 return; | |
41 | |
42 if (enabled_ && host_) | |
43 host_->RemoveMouseEventCallback(mouse_event_callback_); | |
44 ResetFrame(); | |
45 host_ = host; | |
46 if (enabled_ && host) | |
47 host->AddMouseEventCallback(mouse_event_callback_); | |
48 } | |
49 | |
50 void ColorPicker::SetEnabled(bool enabled) { | |
51 if (enabled_ == enabled) | |
52 return; | |
53 | |
54 enabled_ = enabled; | |
55 if (!host_) | |
56 return; | |
57 | |
58 if (enabled) { | |
59 host_->AddMouseEventCallback(mouse_event_callback_); | |
60 UpdateFrame(); | 35 UpdateFrame(); |
61 } else { | |
62 host_->RemoveMouseEventCallback(mouse_event_callback_); | |
63 ResetFrame(); | |
64 | |
65 WebCursor pointer_cursor; | |
66 CursorInfo cursor_info; | |
67 cursor_info.type = blink::WebCursorInfo::kTypePointer; | |
68 pointer_cursor.InitFromCursorInfo(cursor_info); | |
69 host_->SetCursor(pointer_cursor); | |
70 } | 36 } |
71 } | 37 } |
72 | 38 |
73 void ColorPicker::OnSwapCompositorFrame() { | 39 DevToolsEyeDropper::~DevToolsEyeDropper() { |
74 if (enabled_) | 40 DetachFromHost(); |
75 UpdateFrame(); | |
76 } | 41 } |
77 | 42 |
78 void ColorPicker::UpdateFrame() { | 43 void DevToolsEyeDropper::AttachToHost(content::RenderWidgetHost* host) { |
| 44 host_ = host; |
| 45 host_->AddMouseEventCallback(mouse_event_callback_); |
| 46 } |
| 47 |
| 48 void DevToolsEyeDropper::DetachFromHost() { |
79 if (!host_) | 49 if (!host_) |
80 return; | 50 return; |
81 RenderWidgetHostViewBase* view = | 51 host_->RemoveMouseEventCallback(mouse_event_callback_); |
82 static_cast<RenderWidgetHostViewBase*>(host_->GetView()); | 52 content::CursorInfo cursor_info; |
83 if (!view) | 53 cursor_info.type = blink::WebCursorInfo::kTypePointer; |
| 54 host_->SetCursor(cursor_info); |
| 55 host_ = nullptr; |
| 56 } |
| 57 |
| 58 void DevToolsEyeDropper::RenderViewCreated(content::RenderViewHost* host) { |
| 59 if (!host_) { |
| 60 AttachToHost(host->GetWidget()); |
| 61 UpdateFrame(); |
| 62 } |
| 63 } |
| 64 |
| 65 void DevToolsEyeDropper::RenderViewDeleted(content::RenderViewHost* host) { |
| 66 if (host->GetWidget() == host_) { |
| 67 DetachFromHost(); |
| 68 ResetFrame(); |
| 69 } |
| 70 } |
| 71 |
| 72 void DevToolsEyeDropper::RenderViewHostChanged( |
| 73 content::RenderViewHost* old_host, |
| 74 content::RenderViewHost* new_host) { |
| 75 if ((old_host && old_host->GetWidget() == host_) || (!old_host && !host_)) { |
| 76 DetachFromHost(); |
| 77 AttachToHost(new_host->GetWidget()); |
| 78 UpdateFrame(); |
| 79 } |
| 80 } |
| 81 |
| 82 void DevToolsEyeDropper::DidReceiveCompositorFrame() { |
| 83 UpdateFrame(); |
| 84 } |
| 85 |
| 86 void DevToolsEyeDropper::UpdateFrame() { |
| 87 if (!host_ || !host_->GetView()) |
84 return; | 88 return; |
85 | 89 |
86 // TODO(miu): This is the wrong size. It's the size of the view on-screen, and | 90 // TODO(miu): This is the wrong size. It's the size of the view on-screen, and |
87 // not the rendering size of the view. The latter is what is wanted here, so | 91 // not the rendering size of the view. The latter is what is wanted here, so |
88 // that the resulting bitmap's pixel coordinates line-up with the | 92 // that the resulting bitmap's pixel coordinates line-up with the |
89 // blink::WebMouseEvent coordinates. http://crbug.com/73362 | 93 // blink::WebMouseEvent coordinates. http://crbug.com/73362 |
90 gfx::Size should_be_rendering_size = view->GetViewBounds().size(); | 94 gfx::Size should_be_rendering_size = host_->GetView()->GetViewBounds().size(); |
91 view->CopyFromSurface( | 95 host_->GetView()->CopyFromSurface( |
92 gfx::Rect(), should_be_rendering_size, | 96 gfx::Rect(), should_be_rendering_size, |
93 base::Bind(&ColorPicker::FrameUpdated, weak_factory_.GetWeakPtr()), | 97 base::Bind(&DevToolsEyeDropper::FrameUpdated, weak_factory_.GetWeakPtr()), |
94 kN32_SkColorType); | 98 kN32_SkColorType); |
95 } | 99 } |
96 | 100 |
97 void ColorPicker::ResetFrame() { | 101 void DevToolsEyeDropper::ResetFrame() { |
98 frame_.reset(); | 102 frame_.reset(); |
99 last_cursor_x_ = -1; | 103 last_cursor_x_ = -1; |
100 last_cursor_y_ = -1; | 104 last_cursor_y_ = -1; |
101 } | 105 } |
102 | 106 |
103 void ColorPicker::FrameUpdated(const SkBitmap& bitmap, | 107 void DevToolsEyeDropper::FrameUpdated(const SkBitmap& bitmap, |
104 ReadbackResponse response) { | 108 content::ReadbackResponse response) { |
105 if (!enabled_) | 109 if (response == content::READBACK_SUCCESS) { |
106 return; | |
107 | |
108 if (response == READBACK_SUCCESS) { | |
109 frame_ = bitmap; | 110 frame_ = bitmap; |
110 UpdateCursor(); | 111 UpdateCursor(); |
111 } | 112 } |
112 } | 113 } |
113 | 114 |
114 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent& event) { | 115 bool DevToolsEyeDropper::HandleMouseEvent(const blink::WebMouseEvent& event) { |
115 last_cursor_x_ = event.PositionInWidget().x; | 116 last_cursor_x_ = event.PositionInWidget().x; |
116 last_cursor_y_ = event.PositionInWidget().y; | 117 last_cursor_y_ = event.PositionInWidget().y; |
117 if (frame_.drawsNothing()) | 118 if (frame_.drawsNothing()) |
118 return true; | 119 return true; |
119 | 120 |
120 if (event.button == blink::WebMouseEvent::Button::kLeft && | 121 if (event.button == blink::WebMouseEvent::Button::kLeft && |
121 (event.GetType() == blink::WebInputEvent::kMouseDown || | 122 (event.GetType() == blink::WebInputEvent::kMouseDown || |
122 event.GetType() == blink::WebInputEvent::kMouseMove)) { | 123 event.GetType() == blink::WebInputEvent::kMouseMove)) { |
123 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() || | 124 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() || |
124 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) { | 125 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) { |
125 return true; | 126 return true; |
126 } | 127 } |
127 | 128 |
128 SkAutoLockPixels lock_image(frame_); | 129 SkAutoLockPixels lock_image(frame_); |
129 SkColor sk_color = frame_.getColor(last_cursor_x_, last_cursor_y_); | 130 SkColor sk_color = frame_.getColor(last_cursor_x_, last_cursor_y_); |
130 callback_.Run(SkColorGetR(sk_color), SkColorGetG(sk_color), | 131 callback_.Run(SkColorGetR(sk_color), SkColorGetG(sk_color), |
131 SkColorGetB(sk_color), SkColorGetA(sk_color)); | 132 SkColorGetB(sk_color), SkColorGetA(sk_color)); |
132 } | 133 } |
133 UpdateCursor(); | 134 UpdateCursor(); |
134 return true; | 135 return true; |
135 } | 136 } |
136 | 137 |
137 void ColorPicker::UpdateCursor() { | 138 void DevToolsEyeDropper::UpdateCursor() { |
138 if (!host_ || frame_.drawsNothing()) | 139 if (!host_ || frame_.drawsNothing()) |
139 return; | 140 return; |
140 | 141 |
141 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() || | 142 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() || |
142 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) { | 143 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) { |
143 return; | 144 return; |
144 } | 145 } |
145 | 146 |
146 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>( | 147 // Due to platform limitations, we are using two different cursors |
147 host_->GetView()); | 148 // depending on the platform. Mac and Win have large cursors with two circles |
148 if (!view) | 149 // for original spot and its magnified projection; Linux gets smaller (64 px) |
149 return; | 150 // magnified projection only with centered hotspot. |
150 | 151 // Mac Retina requires cursor to be > 120px in order to render smoothly. |
151 // Due to platform limitations, we are using two different cursors | |
152 // depending on the platform. Mac and Win have large cursors with two circles | |
153 // for original spot and its magnified projection; Linux gets smaller (64 px) | |
154 // magnified projection only with centered hotspot. | |
155 // Mac Retina requires cursor to be > 120px in order to render smoothly. | |
156 | 152 |
157 #if defined(OS_LINUX) | 153 #if defined(OS_LINUX) |
158 const float kCursorSize = 63; | 154 const float kCursorSize = 63; |
159 const float kDiameter = 63; | 155 const float kDiameter = 63; |
160 const float kHotspotOffset = 32; | 156 const float kHotspotOffset = 32; |
161 const float kHotspotRadius = 0; | 157 const float kHotspotRadius = 0; |
162 const float kPixelSize = 9; | 158 const float kPixelSize = 9; |
163 #else | 159 #else |
164 const float kCursorSize = 150; | 160 const float kCursorSize = 150; |
165 const float kDiameter = 110; | 161 const float kDiameter = 110; |
(...skipping 18 matching lines...) Expand all Loading... |
184 SkPaint paint; | 180 SkPaint paint; |
185 | 181 |
186 // Paint original spot with cross. | 182 // Paint original spot with cross. |
187 if (kHotspotRadius > 0) { | 183 if (kHotspotRadius > 0) { |
188 paint.setStrokeWidth(1); | 184 paint.setStrokeWidth(1); |
189 paint.setAntiAlias(false); | 185 paint.setAntiAlias(false); |
190 paint.setColor(SK_ColorDKGRAY); | 186 paint.setColor(SK_ColorDKGRAY); |
191 paint.setStyle(SkPaint::kStroke_Style); | 187 paint.setStyle(SkPaint::kStroke_Style); |
192 | 188 |
193 canvas.drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius, | 189 canvas.drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius, |
194 kHotspotOffset, kHotspotOffset - kHotspotRadius, | 190 kHotspotOffset, kHotspotOffset - kHotspotRadius, paint); |
195 paint); | |
196 canvas.drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius, | 191 canvas.drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius, |
197 kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius, | 192 kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius, paint); |
198 paint); | |
199 canvas.drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset, | 193 canvas.drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset, |
200 kHotspotOffset - kHotspotRadius, kHotspotOffset, | 194 kHotspotOffset - kHotspotRadius, kHotspotOffset, paint); |
201 paint); | |
202 canvas.drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset, | 195 canvas.drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset, |
203 kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset, | 196 kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset, paint); |
204 paint); | |
205 | 197 |
206 paint.setStrokeWidth(2); | 198 paint.setStrokeWidth(2); |
207 paint.setAntiAlias(true); | 199 paint.setAntiAlias(true); |
208 canvas.drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint); | 200 canvas.drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint); |
209 } | 201 } |
210 | 202 |
211 // Clip circle for magnified projection. | 203 // Clip circle for magnified projection. |
212 float padding = (kCursorSize - kDiameter) / 2; | 204 float padding = (kCursorSize - kDiameter) / 2; |
213 SkPath clip_path; | 205 SkPath clip_path; |
214 clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter)); | 206 clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter)); |
215 clip_path.close(); | 207 clip_path.close(); |
216 canvas.clipPath(clip_path, SkClipOp::kIntersect, true); | 208 canvas.clipPath(clip_path, SkClipOp::kIntersect, true); |
217 | 209 |
218 // Project pixels. | 210 // Project pixels. |
219 int pixel_count = kDiameter / kPixelSize; | 211 int pixel_count = kDiameter / kPixelSize; |
220 SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2, | 212 SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2, |
221 last_cursor_y_ - pixel_count / 2, | 213 last_cursor_y_ - pixel_count / 2, |
222 pixel_count, pixel_count); | 214 pixel_count, pixel_count); |
223 SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter); | 215 SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter); |
224 canvas.drawBitmapRect(frame_, src_rect, dst_rect, NULL); | 216 canvas.drawBitmapRect(frame_, src_rect, dst_rect, NULL); |
225 | 217 |
226 // Paint grid. | 218 // Paint grid. |
227 paint.setStrokeWidth(1); | 219 paint.setStrokeWidth(1); |
228 paint.setAntiAlias(false); | 220 paint.setAntiAlias(false); |
229 paint.setColor(SK_ColorGRAY); | 221 paint.setColor(SK_ColorGRAY); |
230 for (int i = 0; i < pixel_count; ++i) { | 222 for (int i = 0; i < pixel_count; ++i) { |
231 canvas.drawLine(padding + i * kPixelSize, padding, | 223 canvas.drawLine(padding + i * kPixelSize, padding, padding + i * kPixelSize, |
232 padding + i * kPixelSize, kCursorSize - padding, paint); | 224 kCursorSize - padding, paint); |
233 canvas.drawLine(padding, padding + i * kPixelSize, | 225 canvas.drawLine(padding, padding + i * kPixelSize, kCursorSize - padding, |
234 kCursorSize - padding, padding + i * kPixelSize, paint); | 226 padding + i * kPixelSize, paint); |
235 } | 227 } |
236 | 228 |
237 // Paint central pixel in red. | 229 // Paint central pixel in red. |
238 SkRect pixel = SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2, | 230 SkRect pixel = |
239 (kCursorSize - kPixelSize) / 2, | 231 SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2, |
240 kPixelSize, kPixelSize); | 232 (kCursorSize - kPixelSize) / 2, kPixelSize, kPixelSize); |
241 paint.setColor(SK_ColorRED); | 233 paint.setColor(SK_ColorRED); |
242 paint.setStyle(SkPaint::kStroke_Style); | 234 paint.setStyle(SkPaint::kStroke_Style); |
243 canvas.drawRect(pixel, paint); | 235 canvas.drawRect(pixel, paint); |
244 | 236 |
245 // Paint outline. | 237 // Paint outline. |
246 paint.setStrokeWidth(2); | 238 paint.setStrokeWidth(2); |
247 paint.setColor(SK_ColorDKGRAY); | 239 paint.setColor(SK_ColorDKGRAY); |
248 paint.setAntiAlias(true); | 240 paint.setAntiAlias(true); |
249 canvas.drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint); | 241 canvas.drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint); |
250 | 242 |
251 WebCursor cursor; | 243 content::CursorInfo cursor_info; |
252 CursorInfo cursor_info; | |
253 cursor_info.type = blink::WebCursorInfo::kTypeCustom; | 244 cursor_info.type = blink::WebCursorInfo::kTypeCustom; |
254 cursor_info.image_scale_factor = device_scale_factor; | 245 cursor_info.image_scale_factor = device_scale_factor; |
255 cursor_info.custom_image = result; | 246 cursor_info.custom_image = result; |
256 cursor_info.hotspot = | 247 cursor_info.hotspot = gfx::Point(kHotspotOffset * device_scale_factor, |
257 gfx::Point(kHotspotOffset * device_scale_factor, | 248 kHotspotOffset * device_scale_factor); |
258 kHotspotOffset * device_scale_factor); | 249 host_->SetCursor(cursor_info); |
259 | |
260 cursor.InitFromCursorInfo(cursor_info); | |
261 DCHECK(host_); | |
262 host_->SetCursor(cursor); | |
263 } | 250 } |
264 | |
265 } // namespace protocol | |
266 } // namespace content | |
OLD | NEW |