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 "third_party/skia/include/core/SkPaint.h"
16 #include "third_party/skia/include/core/SkPath.h"
17 #include "ui/gfx/geometry/size_conversions.h"
23 ColorPicker::ColorPicker(ColorPickedCallback callback
)
24 : callback_(callback
),
30 mouse_event_callback_
= base::Bind(
31 &ColorPicker::HandleMouseEvent
,
32 base::Unretained(this));
35 ColorPicker::~ColorPicker() {
38 void ColorPicker::SetRenderWidgetHost(RenderWidgetHostImpl
* host
) {
42 if (enabled_
&& host_
)
43 host_
->RemoveMouseEventCallback(mouse_event_callback_
);
47 host
->AddMouseEventCallback(mouse_event_callback_
);
50 void ColorPicker::SetEnabled(bool enabled
) {
51 if (enabled_
== enabled
)
59 host_
->AddMouseEventCallback(mouse_event_callback_
);
62 host_
->RemoveMouseEventCallback(mouse_event_callback_
);
65 WebCursor pointer_cursor
;
66 WebCursor::CursorInfo cursor_info
;
67 cursor_info
.type
= blink::WebCursorInfo::TypePointer
;
68 pointer_cursor
.InitFromCursorInfo(cursor_info
);
69 host_
->SetCursor(pointer_cursor
);
73 void ColorPicker::OnSwapCompositorFrame() {
78 void ColorPicker::UpdateFrame() {
81 RenderWidgetHostViewBase
* view
=
82 static_cast<RenderWidgetHostViewBase
*>(host_
->GetView());
86 gfx::Size size
= view
->GetViewBounds().size();
87 view
->CopyFromCompositingSurface(
88 gfx::Rect(size
), size
,
89 base::Bind(&ColorPicker::FrameUpdated
,
90 weak_factory_
.GetWeakPtr()),
94 void ColorPicker::ResetFrame() {
100 void ColorPicker::FrameUpdated(const SkBitmap
& bitmap
,
101 ReadbackResponse response
) {
105 if (response
== READBACK_SUCCESS
) {
111 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent
& event
) {
112 last_cursor_x_
= event
.x
;
113 last_cursor_y_
= event
.y
;
114 if (frame_
.drawsNothing())
117 if (event
.button
== blink::WebMouseEvent::ButtonLeft
&&
118 event
.type
== blink::WebInputEvent::MouseDown
) {
119 if (last_cursor_x_
< 0 || last_cursor_x_
>= frame_
.width() ||
120 last_cursor_y_
< 0 || last_cursor_y_
>= frame_
.height()) {
124 SkAutoLockPixels
lock_image(frame_
);
125 SkColor sk_color
= frame_
.getColor(last_cursor_x_
, last_cursor_y_
);
126 callback_
.Run(SkColorGetR(sk_color
), SkColorGetG(sk_color
),
127 SkColorGetB(sk_color
), SkColorGetA(sk_color
));
133 void ColorPicker::UpdateCursor() {
134 if (!host_
|| frame_
.drawsNothing())
137 if (last_cursor_x_
< 0 || last_cursor_x_
>= frame_
.width() ||
138 last_cursor_y_
< 0 || last_cursor_y_
>= frame_
.height()) {
142 RenderWidgetHostViewBase
* view
= static_cast<RenderWidgetHostViewBase
*>(
147 // Due to platform limitations, we are using two different cursors
148 // depending on the platform. Mac and Win have large cursors with two circles
149 // for original spot and its magnified projection; Linux gets smaller (64 px)
150 // magnified projection only with centered hotspot.
151 // Mac Retina requires cursor to be > 120px in order to render smoothly.
153 #if defined(OS_LINUX)
154 const float kCursorSize
= 63;
155 const float kDiameter
= 63;
156 const float kHotspotOffset
= 32;
157 const float kHotspotRadius
= 0;
158 const float kPixelSize
= 9;
160 const float kCursorSize
= 150;
161 const float kDiameter
= 110;
162 const float kHotspotOffset
= 25;
163 const float kHotspotRadius
= 5;
164 const float kPixelSize
= 10;
167 blink::WebScreenInfo screen_info
;
168 view
->GetScreenInfo(&screen_info
);
169 double device_scale_factor
= screen_info
.deviceScaleFactor
;
172 result
.allocN32Pixels(kCursorSize
* device_scale_factor
,
173 kCursorSize
* device_scale_factor
);
174 result
.eraseARGB(0, 0, 0, 0);
176 SkCanvas
canvas(result
);
177 canvas
.scale(device_scale_factor
, device_scale_factor
);
178 canvas
.translate(0.5f
, 0.5f
);
182 // Paint original spot with cross.
183 if (kHotspotRadius
) {
184 paint
.setStrokeWidth(1);
185 paint
.setAntiAlias(false);
186 paint
.setColor(SK_ColorDKGRAY
);
187 paint
.setStyle(SkPaint::kStroke_Style
);
189 canvas
.drawLine(kHotspotOffset
, kHotspotOffset
- 2 * kHotspotRadius
,
190 kHotspotOffset
, kHotspotOffset
- kHotspotRadius
,
192 canvas
.drawLine(kHotspotOffset
, kHotspotOffset
+ kHotspotRadius
,
193 kHotspotOffset
, kHotspotOffset
+ 2 * kHotspotRadius
,
195 canvas
.drawLine(kHotspotOffset
- 2 * kHotspotRadius
, kHotspotOffset
,
196 kHotspotOffset
- kHotspotRadius
, kHotspotOffset
,
198 canvas
.drawLine(kHotspotOffset
+ kHotspotRadius
, kHotspotOffset
,
199 kHotspotOffset
+ 2 * kHotspotRadius
, kHotspotOffset
,
202 paint
.setStrokeWidth(2);
203 paint
.setAntiAlias(true);
204 canvas
.drawCircle(kHotspotOffset
, kHotspotOffset
, kHotspotRadius
, paint
);
207 // Clip circle for magnified projection.
208 float padding
= (kCursorSize
- kDiameter
) / 2;
210 clip_path
.addOval(SkRect::MakeXYWH(padding
, padding
, kDiameter
, kDiameter
));
212 canvas
.clipPath(clip_path
, SkRegion::kIntersect_Op
, true);
215 int pixel_count
= kDiameter
/ kPixelSize
;
216 SkRect src_rect
= SkRect::MakeXYWH(last_cursor_x_
- pixel_count
/ 2,
217 last_cursor_y_
- pixel_count
/ 2,
218 pixel_count
, pixel_count
);
219 SkRect dst_rect
= SkRect::MakeXYWH(padding
, padding
, kDiameter
, kDiameter
);
220 canvas
.drawBitmapRect(frame_
, src_rect
, dst_rect
, NULL
);
223 paint
.setStrokeWidth(1);
224 paint
.setAntiAlias(false);
225 paint
.setColor(SK_ColorGRAY
);
226 for (int i
= 0; i
< pixel_count
; ++i
) {
227 canvas
.drawLine(padding
+ i
* kPixelSize
, padding
,
228 padding
+ i
* kPixelSize
, kCursorSize
- padding
, paint
);
229 canvas
.drawLine(padding
, padding
+ i
* kPixelSize
,
230 kCursorSize
- padding
, padding
+ i
* kPixelSize
, paint
);
233 // Paint central pixel in red.
234 SkRect pixel
= SkRect::MakeXYWH((kCursorSize
- kPixelSize
) / 2,
235 (kCursorSize
- kPixelSize
) / 2,
236 kPixelSize
, kPixelSize
);
237 paint
.setColor(SK_ColorRED
);
238 paint
.setStyle(SkPaint::kStroke_Style
);
239 canvas
.drawRect(pixel
, paint
);
242 paint
.setStrokeWidth(2);
243 paint
.setColor(SK_ColorDKGRAY
);
244 paint
.setAntiAlias(true);
245 canvas
.drawCircle(kCursorSize
/ 2, kCursorSize
/ 2, kDiameter
/ 2, paint
);
248 WebCursor::CursorInfo cursor_info
;
249 cursor_info
.type
= blink::WebCursorInfo::TypeCustom
;
250 cursor_info
.image_scale_factor
= device_scale_factor
;
251 cursor_info
.custom_image
= result
;
252 cursor_info
.hotspot
=
253 gfx::Point(kHotspotOffset
* device_scale_factor
,
254 kHotspotOffset
* device_scale_factor
);
256 cursor_info
.external_handle
= 0;
259 cursor
.InitFromCursorInfo(cursor_info
);
261 host_
->SetCursor(cursor
);
265 } // namespace devtools
266 } // namespace content