Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / content / browser / devtools / protocol / color_picker.cc
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 "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"
16
17 namespace content {
18 namespace devtools {
19 namespace page {
20
21 ColorPicker::ColorPicker(ColorPickedCallback callback)
22     : callback_(callback),
23       enabled_(false),
24       last_cursor_x_(-1),
25       last_cursor_y_(-1),
26       host_(nullptr),
27       weak_factory_(this) {
28   mouse_event_callback_ = base::Bind(
29       &ColorPicker::HandleMouseEvent,
30       base::Unretained(this));
31 }
32
33 ColorPicker::~ColorPicker() {
34 }
35
36 void ColorPicker::SetRenderViewHost(RenderViewHostImpl* host) {
37   if (host_ == host)
38     return;
39
40   if (host_)
41     host_->RemoveMouseEventCallback(mouse_event_callback_);
42   ResetFrame();
43   host_ = host;
44   if (enabled_ && host)
45     host->AddMouseEventCallback(mouse_event_callback_);
46 }
47
48 void ColorPicker::SetEnabled(bool enabled) {
49   if (enabled_ == enabled)
50     return;
51
52   enabled_ = enabled;
53   if (!host_)
54     return;
55
56   if (enabled) {
57     host_->AddMouseEventCallback(mouse_event_callback_);
58     UpdateFrame();
59   } else {
60     host_->RemoveMouseEventCallback(mouse_event_callback_);
61     ResetFrame();
62
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);
68   }
69 }
70
71 void ColorPicker::OnSwapCompositorFrame() {
72   if (enabled_)
73     UpdateFrame();
74 }
75
76 void ColorPicker::UpdateFrame() {
77   if (!host_)
78     return;
79   RenderWidgetHostViewBase* view =
80       static_cast<RenderWidgetHostViewBase*>(host_->GetView());
81   if (!view)
82     return;
83
84   gfx::Size size = view->GetViewBounds().size();
85   view->CopyFromCompositingSurface(
86       gfx::Rect(size), size,
87       base::Bind(&ColorPicker::FrameUpdated,
88                  weak_factory_.GetWeakPtr()),
89       kN32_SkColorType);
90 }
91
92 void ColorPicker::ResetFrame() {
93   frame_.reset();
94   last_cursor_x_ = -1;
95   last_cursor_y_ = -1;
96 }
97
98 void ColorPicker::FrameUpdated(bool succeeded, const SkBitmap& bitmap) {
99   if (!enabled_)
100     return;
101
102   if (succeeded) {
103     frame_ = bitmap;
104     UpdateCursor();
105   }
106 }
107
108 bool ColorPicker::HandleMouseEvent(const blink::WebMouseEvent& event) {
109   last_cursor_x_ = event.x;
110   last_cursor_y_ = event.y;
111   if (frame_.drawsNothing())
112     return true;
113
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()) {
118       return true;
119     }
120
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));
125   }
126   UpdateCursor();
127   return true;
128 }
129
130 void ColorPicker::UpdateCursor() {
131   if (!host_ || frame_.drawsNothing())
132     return;
133
134   if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
135       last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
136     return;
137   }
138
139   RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
140       host_->GetView());
141   if (!view)
142     return;
143
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.
149
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;
156 #else
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;
162 #endif
163
164   blink::WebScreenInfo screen_info;
165   view->GetScreenInfo(&screen_info);
166   double device_scale_factor = screen_info.deviceScaleFactor;
167
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);
173
174   SkPaint paint;
175
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);
182
183     canvas->drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius,
184                      kHotspotOffset, kHotspotOffset - kHotspotRadius,
185                      paint);
186     canvas->drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius,
187                      kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius,
188                      paint);
189     canvas->drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset,
190                      kHotspotOffset - kHotspotRadius, kHotspotOffset,
191                      paint);
192     canvas->drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset,
193                      kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset,
194                      paint);
195
196     paint.setStrokeWidth(2);
197     paint.setAntiAlias(true);
198     canvas->drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint);
199   }
200
201   // Clip circle for magnified projection.
202   float padding = (kCursorSize - kDiameter) / 2;
203   SkPath clip_path;
204   clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter));
205   clip_path.close();
206   canvas->clipPath(clip_path, SkRegion::kIntersect_Op, true);
207
208   // Project pixels.
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);
215
216   // Paint grid.
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);
225   }
226
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);
234
235   // Paint outline.
236   paint.setStrokeWidth(2);
237   paint.setColor(SK_ColorDKGRAY);
238   paint.setAntiAlias(true);
239   canvas->drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint);
240
241   SkBitmap result;
242   result.allocN32Pixels(kCursorSize * device_scale_factor,
243                         kCursorSize * device_scale_factor);
244   canvas->readPixels(&result, 0, 0);
245
246   WebCursor cursor;
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);
254 #if defined(OS_WIN)
255   cursor_info.external_handle = 0;
256 #endif
257
258   cursor.InitFromCursorInfo(cursor_info);
259   DCHECK(host_);
260   host_->SetCursor(cursor);
261 }
262
263 }  // namespace page
264 }  // namespace devtools
265 }  // namespace content