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/geometry/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::SetRenderWidgetHost(RenderWidgetHostImpl
* host
) {
40 if (enabled_
&& 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(const SkBitmap
& bitmap
,
99 ReadbackResponse response
) {
103 if (response
== READBACK_SUCCESS
) {
109 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent
& event
) {
110 last_cursor_x_
= event
.x
;
111 last_cursor_y_
= event
.y
;
112 if (frame_
.drawsNothing())
115 if (event
.button
== blink::WebMouseEvent::ButtonLeft
&&
116 event
.type
== blink::WebInputEvent::MouseDown
) {
117 if (last_cursor_x_
< 0 || last_cursor_x_
>= frame_
.width() ||
118 last_cursor_y_
< 0 || last_cursor_y_
>= frame_
.height()) {
122 SkAutoLockPixels
lock_image(frame_
);
123 SkColor sk_color
= frame_
.getColor(last_cursor_x_
, last_cursor_y_
);
124 callback_
.Run(SkColorGetR(sk_color
), SkColorGetG(sk_color
),
125 SkColorGetB(sk_color
), SkColorGetA(sk_color
));
131 void ColorPicker::UpdateCursor() {
132 if (!host_
|| frame_
.drawsNothing())
135 if (last_cursor_x_
< 0 || last_cursor_x_
>= frame_
.width() ||
136 last_cursor_y_
< 0 || last_cursor_y_
>= frame_
.height()) {
140 RenderWidgetHostViewBase
* view
= static_cast<RenderWidgetHostViewBase
*>(
145 // Due to platform limitations, we are using two different cursors
146 // depending on the platform. Mac and Win have large cursors with two circles
147 // for original spot and its magnified projection; Linux gets smaller (64 px)
148 // magnified projection only with centered hotspot.
149 // Mac Retina requires cursor to be > 120px in order to render smoothly.
151 #if defined(OS_LINUX)
152 const float kCursorSize
= 63;
153 const float kDiameter
= 63;
154 const float kHotspotOffset
= 32;
155 const float kHotspotRadius
= 0;
156 const float kPixelSize
= 9;
158 const float kCursorSize
= 150;
159 const float kDiameter
= 110;
160 const float kHotspotOffset
= 25;
161 const float kHotspotRadius
= 5;
162 const float kPixelSize
= 10;
165 blink::WebScreenInfo screen_info
;
166 view
->GetScreenInfo(&screen_info
);
167 double device_scale_factor
= screen_info
.deviceScaleFactor
;
170 result
.allocN32Pixels(kCursorSize
* device_scale_factor
,
171 kCursorSize
* device_scale_factor
);
172 result
.eraseARGB(0, 0, 0, 0);
174 SkCanvas
canvas(result
);
175 canvas
.scale(device_scale_factor
, device_scale_factor
);
176 canvas
.translate(0.5f
, 0.5f
);
180 // Paint original spot with cross.
181 if (kHotspotRadius
) {
182 paint
.setStrokeWidth(1);
183 paint
.setAntiAlias(false);
184 paint
.setColor(SK_ColorDKGRAY
);
185 paint
.setStyle(SkPaint::kStroke_Style
);
187 canvas
.drawLine(kHotspotOffset
, kHotspotOffset
- 2 * kHotspotRadius
,
188 kHotspotOffset
, kHotspotOffset
- kHotspotRadius
,
190 canvas
.drawLine(kHotspotOffset
, kHotspotOffset
+ kHotspotRadius
,
191 kHotspotOffset
, kHotspotOffset
+ 2 * kHotspotRadius
,
193 canvas
.drawLine(kHotspotOffset
- 2 * kHotspotRadius
, kHotspotOffset
,
194 kHotspotOffset
- kHotspotRadius
, kHotspotOffset
,
196 canvas
.drawLine(kHotspotOffset
+ kHotspotRadius
, kHotspotOffset
,
197 kHotspotOffset
+ 2 * kHotspotRadius
, kHotspotOffset
,
200 paint
.setStrokeWidth(2);
201 paint
.setAntiAlias(true);
202 canvas
.drawCircle(kHotspotOffset
, kHotspotOffset
, kHotspotRadius
, paint
);
205 // Clip circle for magnified projection.
206 float padding
= (kCursorSize
- kDiameter
) / 2;
208 clip_path
.addOval(SkRect::MakeXYWH(padding
, padding
, kDiameter
, kDiameter
));
210 canvas
.clipPath(clip_path
, SkRegion::kIntersect_Op
, true);
213 int pixel_count
= kDiameter
/ kPixelSize
;
214 SkRect src_rect
= SkRect::MakeXYWH(last_cursor_x_
- pixel_count
/ 2,
215 last_cursor_y_
- pixel_count
/ 2,
216 pixel_count
, pixel_count
);
217 SkRect dst_rect
= SkRect::MakeXYWH(padding
, padding
, kDiameter
, kDiameter
);
218 canvas
.drawBitmapRectToRect(frame_
, &src_rect
, dst_rect
);
221 paint
.setStrokeWidth(1);
222 paint
.setAntiAlias(false);
223 paint
.setColor(SK_ColorGRAY
);
224 for (int i
= 0; i
< pixel_count
; ++i
) {
225 canvas
.drawLine(padding
+ i
* kPixelSize
, padding
,
226 padding
+ i
* kPixelSize
, kCursorSize
- padding
, paint
);
227 canvas
.drawLine(padding
, padding
+ i
* kPixelSize
,
228 kCursorSize
- padding
, padding
+ i
* kPixelSize
, paint
);
231 // Paint central pixel in red.
232 SkRect pixel
= SkRect::MakeXYWH((kCursorSize
- kPixelSize
) / 2,
233 (kCursorSize
- kPixelSize
) / 2,
234 kPixelSize
, kPixelSize
);
235 paint
.setColor(SK_ColorRED
);
236 paint
.setStyle(SkPaint::kStroke_Style
);
237 canvas
.drawRect(pixel
, paint
);
240 paint
.setStrokeWidth(2);
241 paint
.setColor(SK_ColorDKGRAY
);
242 paint
.setAntiAlias(true);
243 canvas
.drawCircle(kCursorSize
/ 2, kCursorSize
/ 2, kDiameter
/ 2, paint
);
246 WebCursor::CursorInfo cursor_info
;
247 cursor_info
.type
= blink::WebCursorInfo::TypeCustom
;
248 cursor_info
.image_scale_factor
= device_scale_factor
;
249 cursor_info
.custom_image
= result
;
250 cursor_info
.hotspot
=
251 gfx::Point(kHotspotOffset
* device_scale_factor
,
252 kHotspotOffset
* device_scale_factor
);
254 cursor_info
.external_handle
= 0;
257 cursor
.InitFromCursorInfo(cursor_info
);
259 host_
->SetCursor(cursor
);
263 } // namespace devtools
264 } // namespace content