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.
5 #include "content/browser/devtools/protocol/color_picker.h"
8 #include "content/browser/renderer_host/render_view_host_impl.h"
9 #include "content/browser/renderer_host/render_widget_host_view_base.h"
10 #include "content/common/cursors/webcursor.h"
11 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
12 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
13 #include "third_party/WebKit/public/web/WebInputEvent.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "ui/gfx/size_conversions.h"
21 ColorPicker::ColorPicker(ColorPickedCallback callback)
22 : callback_(callback),
28 mouse_event_callback_ = base::Bind(
29 &ColorPicker::HandleMouseEvent,
30 base::Unretained(this));
33 ColorPicker::~ColorPicker() {
36 void ColorPicker::SetRenderViewHost(RenderViewHostImpl* host) {
41 host_->RemoveMouseEventCallback(mouse_event_callback_);
45 host->AddMouseEventCallback(mouse_event_callback_);
48 void ColorPicker::SetEnabled(bool enabled) {
49 if (enabled_ == enabled)
57 host_->AddMouseEventCallback(mouse_event_callback_);
60 host_->RemoveMouseEventCallback(mouse_event_callback_);
63 WebCursor pointer_cursor;
64 WebCursor::CursorInfo cursor_info;
65 cursor_info.type = blink::WebCursorInfo::TypePointer;
66 pointer_cursor.InitFromCursorInfo(cursor_info);
67 host_->SetCursor(pointer_cursor);
71 void ColorPicker::OnSwapCompositorFrame() {
76 void ColorPicker::UpdateFrame() {
79 RenderWidgetHostViewBase* view =
80 static_cast<RenderWidgetHostViewBase*>(host_->GetView());
84 gfx::Size size = view->GetViewBounds().size();
85 view->CopyFromCompositingSurface(
86 gfx::Rect(size), size,
87 base::Bind(&ColorPicker::FrameUpdated,
88 weak_factory_.GetWeakPtr()),
92 void ColorPicker::ResetFrame() {
98 void ColorPicker::FrameUpdated(bool succeeded, const SkBitmap& bitmap) {
108 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent& event) {
109 last_cursor_x_ = event.x;
110 last_cursor_y_ = event.y;
111 if (frame_.drawsNothing())
114 if (event.button == blink::WebMouseEvent::ButtonLeft &&
115 event.type == blink::WebInputEvent::MouseDown) {
116 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
117 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
121 SkAutoLockPixels lock_image(frame_);
122 SkColor sk_color = frame_.getColor(last_cursor_x_, last_cursor_y_);
123 callback_.Run(SkColorGetR(sk_color), SkColorGetG(sk_color),
124 SkColorGetB(sk_color), SkColorGetA(sk_color));
130 void ColorPicker::UpdateCursor() {
131 if (!host_ || frame_.drawsNothing())
134 if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
135 last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
139 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
144 // Due to platform limitations, we are using two different cursors
145 // depending on the platform. Mac and Win have large cursors with two circles
146 // for original spot and its magnified projection; Linux gets smaller (64 px)
147 // magnified projection only with centered hotspot.
148 // Mac Retina requires cursor to be > 120px in order to render smoothly.
150 #if defined(OS_LINUX)
151 const float kCursorSize = 63;
152 const float kDiameter = 63;
153 const float kHotspotOffset = 32;
154 const float kHotspotRadius = 0;
155 const float kPixelSize = 9;
157 const float kCursorSize = 150;
158 const float kDiameter = 110;
159 const float kHotspotOffset = 25;
160 const float kHotspotRadius = 5;
161 const float kPixelSize = 10;
164 blink::WebScreenInfo screen_info;
165 view->GetScreenInfo(&screen_info);
166 double device_scale_factor = screen_info.deviceScaleFactor;
168 skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(SkCanvas::NewRasterN32(
169 kCursorSize * device_scale_factor,
170 kCursorSize * device_scale_factor));
171 canvas->scale(device_scale_factor, device_scale_factor);
172 canvas->translate(0.5f, 0.5f);
176 // Paint original spot with cross.
177 if (kHotspotRadius) {
178 paint.setStrokeWidth(1);
179 paint.setAntiAlias(false);
180 paint.setColor(SK_ColorDKGRAY);
181 paint.setStyle(SkPaint::kStroke_Style);
183 canvas->drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius,
184 kHotspotOffset, kHotspotOffset - kHotspotRadius,
186 canvas->drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius,
187 kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius,
189 canvas->drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset,
190 kHotspotOffset - kHotspotRadius, kHotspotOffset,
192 canvas->drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset,
193 kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset,
196 paint.setStrokeWidth(2);
197 paint.setAntiAlias(true);
198 canvas->drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint);
201 // Clip circle for magnified projection.
202 float padding = (kCursorSize - kDiameter) / 2;
204 clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter));
206 canvas->clipPath(clip_path, SkRegion::kIntersect_Op, true);
209 int pixel_count = kDiameter / kPixelSize;
210 SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2,
211 last_cursor_y_ - pixel_count / 2,
212 pixel_count, pixel_count);
213 SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter);
214 canvas->drawBitmapRectToRect(frame_, &src_rect, dst_rect);
217 paint.setStrokeWidth(1);
218 paint.setAntiAlias(false);
219 paint.setColor(SK_ColorGRAY);
220 for (int i = 0; i < pixel_count; ++i) {
221 canvas->drawLine(padding + i * kPixelSize, padding,
222 padding + i * kPixelSize, kCursorSize - padding, paint);
223 canvas->drawLine(padding, padding + i * kPixelSize,
224 kCursorSize - padding, padding + i * kPixelSize, paint);
227 // Paint central pixel in red.
228 SkRect pixel = SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2,
229 (kCursorSize - kPixelSize) / 2,
230 kPixelSize, kPixelSize);
231 paint.setColor(SK_ColorRED);
232 paint.setStyle(SkPaint::kStroke_Style);
233 canvas->drawRect(pixel, paint);
236 paint.setStrokeWidth(2);
237 paint.setColor(SK_ColorDKGRAY);
238 paint.setAntiAlias(true);
239 canvas->drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint);
242 result.allocN32Pixels(kCursorSize * device_scale_factor,
243 kCursorSize * device_scale_factor);
244 canvas->readPixels(&result, 0, 0);
247 WebCursor::CursorInfo cursor_info;
248 cursor_info.type = blink::WebCursorInfo::TypeCustom;
249 cursor_info.image_scale_factor = device_scale_factor;
250 cursor_info.custom_image = result;
251 cursor_info.hotspot =
252 gfx::Point(kHotspotOffset * device_scale_factor,
253 kHotspotOffset * device_scale_factor);
255 cursor_info.external_handle = 0;
258 cursor.InitFromCursorInfo(cursor_info);
260 host_->SetCursor(cursor);
264 } // namespace devtools
265 } // namespace content