| 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 |