| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/devtools/protocol/color_picker.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "build/build_config.h" | |
| 9 #include "content/browser/renderer_host/render_view_host_impl.h" | |
| 10 #include "content/browser/renderer_host/render_widget_host_view_base.h" | |
| 11 #include "content/common/cursors/webcursor.h" | |
| 12 #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/skia/include/core/SkCanvas.h" | |
| 16 #include "third_party/skia/include/core/SkPaint.h" | |
| 17 #include "third_party/skia/include/core/SkPath.h" | |
| 18 #include "ui/gfx/geometry/size_conversions.h" | |
| 19 | |
| 20 namespace content { | |
| 21 namespace protocol { | |
| 22 | |
| 23 ColorPicker::ColorPicker(ColorPickedCallback callback) | |
| 24 : callback_(callback), | |
| 25 enabled_(false), | |
| 26 last_cursor_x_(-1), | |
| 27 last_cursor_y_(-1), | |
| 28 host_(nullptr), | |
| 29 weak_factory_(this) { | |
| 30 mouse_event_callback_ = base::Bind( | |
| 31 &ColorPicker::HandleMouseEvent, | |
| 32 base::Unretained(this)); | |
| 33 } | |
| 34 | |
| 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(); | |
| 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 } | |
| 71 } | |
| 72 | |
| 73 void ColorPicker::OnSwapCompositorFrame() { | |
| 74 if (enabled_) | |
| 75 UpdateFrame(); | |
| 76 } | |
| 77 | |
| 78 void ColorPicker::UpdateFrame() { | |
| 79 if (!host_) | |
| 80 return; | |
| 81 RenderWidgetHostViewBase* view = | |
| 82 static_cast<RenderWidgetHostViewBase*>(host_->GetView()); | |
| 83 if (!view) | |
| 84 return; | |
| 85 | |
| 86 // 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 | |
| 88 // that the resulting bitmap's pixel coordinates line-up with the | |
| 89 // blink::WebMouseEvent coordinates. http://crbug.com/73362 | |
| 90 gfx::Size should_be_rendering_size = view->GetViewBounds().size(); | |
| 91 view->CopyFromSurface( | |
| 92 gfx::Rect(), should_be_rendering_size, | |
| 93 base::Bind(&ColorPicker::FrameUpdated, weak_factory_.GetWeakPtr()), | |
| 94 kN32_SkColorType); | |
| 95 } | |
| 96 | |
| 97 void ColorPicker::ResetFrame() { | |
| 98 frame_.reset(); | |
| 99 last_cursor_x_ = -1; | |
| 100 last_cursor_y_ = -1; | |
| 101 } | |
| 102 | |
| 103 void ColorPicker::FrameUpdated(const SkBitmap& bitmap, | |
| 104 ReadbackResponse response) { | |
| 105 if (!enabled_) | |
| 106 return; | |
| 107 | |
| 108 if (response == READBACK_SUCCESS) { | |
| 109 frame_ = bitmap; | |
| 110 UpdateCursor(); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent& event) { | |
| 115 last_cursor_x_ = event.PositionInWidget().x; | |
| 116 last_cursor_y_ = event.PositionInWidget().y; | |
| 117 if (frame_.drawsNothing()) | |
| 118 return true; | |
| 119 | |
| 120 if (event.button == blink::WebMouseEvent::Button::kLeft && | |
| 121 (event.GetType() == blink::WebInputEvent::kMouseDown || | |
| 122 event.GetType() == blink::WebInputEvent::kMouseMove)) { | |
| 123 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() || | |
| 124 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) { | |
| 125 return true; | |
| 126 } | |
| 127 | |
| 128 SkAutoLockPixels lock_image(frame_); | |
| 129 SkColor sk_color = frame_.getColor(last_cursor_x_, last_cursor_y_); | |
| 130 callback_.Run(SkColorGetR(sk_color), SkColorGetG(sk_color), | |
| 131 SkColorGetB(sk_color), SkColorGetA(sk_color)); | |
| 132 } | |
| 133 UpdateCursor(); | |
| 134 return true; | |
| 135 } | |
| 136 | |
| 137 void ColorPicker::UpdateCursor() { | |
| 138 if (!host_ || frame_.drawsNothing()) | |
| 139 return; | |
| 140 | |
| 141 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() || | |
| 142 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) { | |
| 143 return; | |
| 144 } | |
| 145 | |
| 146 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>( | |
| 147 host_->GetView()); | |
| 148 if (!view) | |
| 149 return; | |
| 150 | |
| 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 | |
| 157 #if defined(OS_LINUX) | |
| 158 const float kCursorSize = 63; | |
| 159 const float kDiameter = 63; | |
| 160 const float kHotspotOffset = 32; | |
| 161 const float kHotspotRadius = 0; | |
| 162 const float kPixelSize = 9; | |
| 163 #else | |
| 164 const float kCursorSize = 150; | |
| 165 const float kDiameter = 110; | |
| 166 const float kHotspotOffset = 25; | |
| 167 const float kHotspotRadius = 5; | |
| 168 const float kPixelSize = 10; | |
| 169 #endif | |
| 170 | |
| 171 content::ScreenInfo screen_info; | |
| 172 host_->GetScreenInfo(&screen_info); | |
| 173 double device_scale_factor = screen_info.device_scale_factor; | |
| 174 | |
| 175 SkBitmap result; | |
| 176 result.allocN32Pixels(kCursorSize * device_scale_factor, | |
| 177 kCursorSize * device_scale_factor); | |
| 178 result.eraseARGB(0, 0, 0, 0); | |
| 179 | |
| 180 SkCanvas canvas(result); | |
| 181 canvas.scale(device_scale_factor, device_scale_factor); | |
| 182 canvas.translate(0.5f, 0.5f); | |
| 183 | |
| 184 SkPaint paint; | |
| 185 | |
| 186 // Paint original spot with cross. | |
| 187 if (kHotspotRadius > 0) { | |
| 188 paint.setStrokeWidth(1); | |
| 189 paint.setAntiAlias(false); | |
| 190 paint.setColor(SK_ColorDKGRAY); | |
| 191 paint.setStyle(SkPaint::kStroke_Style); | |
| 192 | |
| 193 canvas.drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius, | |
| 194 kHotspotOffset, kHotspotOffset - kHotspotRadius, | |
| 195 paint); | |
| 196 canvas.drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius, | |
| 197 kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius, | |
| 198 paint); | |
| 199 canvas.drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset, | |
| 200 kHotspotOffset - kHotspotRadius, kHotspotOffset, | |
| 201 paint); | |
| 202 canvas.drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset, | |
| 203 kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset, | |
| 204 paint); | |
| 205 | |
| 206 paint.setStrokeWidth(2); | |
| 207 paint.setAntiAlias(true); | |
| 208 canvas.drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint); | |
| 209 } | |
| 210 | |
| 211 // Clip circle for magnified projection. | |
| 212 float padding = (kCursorSize - kDiameter) / 2; | |
| 213 SkPath clip_path; | |
| 214 clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter)); | |
| 215 clip_path.close(); | |
| 216 canvas.clipPath(clip_path, SkClipOp::kIntersect, true); | |
| 217 | |
| 218 // Project pixels. | |
| 219 int pixel_count = kDiameter / kPixelSize; | |
| 220 SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2, | |
| 221 last_cursor_y_ - pixel_count / 2, | |
| 222 pixel_count, pixel_count); | |
| 223 SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter); | |
| 224 canvas.drawBitmapRect(frame_, src_rect, dst_rect, NULL); | |
| 225 | |
| 226 // Paint grid. | |
| 227 paint.setStrokeWidth(1); | |
| 228 paint.setAntiAlias(false); | |
| 229 paint.setColor(SK_ColorGRAY); | |
| 230 for (int i = 0; i < pixel_count; ++i) { | |
| 231 canvas.drawLine(padding + i * kPixelSize, padding, | |
| 232 padding + i * kPixelSize, kCursorSize - padding, paint); | |
| 233 canvas.drawLine(padding, padding + i * kPixelSize, | |
| 234 kCursorSize - padding, padding + i * kPixelSize, paint); | |
| 235 } | |
| 236 | |
| 237 // Paint central pixel in red. | |
| 238 SkRect pixel = SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2, | |
| 239 (kCursorSize - kPixelSize) / 2, | |
| 240 kPixelSize, kPixelSize); | |
| 241 paint.setColor(SK_ColorRED); | |
| 242 paint.setStyle(SkPaint::kStroke_Style); | |
| 243 canvas.drawRect(pixel, paint); | |
| 244 | |
| 245 // Paint outline. | |
| 246 paint.setStrokeWidth(2); | |
| 247 paint.setColor(SK_ColorDKGRAY); | |
| 248 paint.setAntiAlias(true); | |
| 249 canvas.drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint); | |
| 250 | |
| 251 WebCursor cursor; | |
| 252 CursorInfo cursor_info; | |
| 253 cursor_info.type = blink::WebCursorInfo::kTypeCustom; | |
| 254 cursor_info.image_scale_factor = device_scale_factor; | |
| 255 cursor_info.custom_image = result; | |
| 256 cursor_info.hotspot = | |
| 257 gfx::Point(kHotspotOffset * device_scale_factor, | |
| 258 kHotspotOffset * device_scale_factor); | |
| 259 | |
| 260 cursor.InitFromCursorInfo(cursor_info); | |
| 261 DCHECK(host_); | |
| 262 host_->SetCursor(cursor); | |
| 263 } | |
| 264 | |
| 265 } // namespace protocol | |
| 266 } // namespace content | |
| OLD | NEW |