1// Copyright (c) 2012 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 "chrome/browser/chromeos/display/overscan_calibrator.h"
6
7#include "ash/display/display_controller.h"
8#include "ash/display/display_info.h"
9#include "ash/display/display_manager.h"
10#include "ash/shell.h"
11#include "ash/shell_window_ids.h"
12#include "base/callback.h"
13#include "ui/aura/window.h"
14#include "ui/compositor/layer.h"
15#include "ui/gfx/canvas.h"
16
17namespace chromeos {
18namespace {
19
20// The opacity for the arrows of the overscan calibration.
21const float kArrowOpacity = 0.8;
22
23// The height in pixel for the arrows to show the overscan calibration.
24const int kCalibrationArrowHeight = 50;
25
26// The gap between the boundary and calibration arrows.
27const int kArrowGapWidth = 20;
28
29// Draw the arrow for the overscan calibration to |canvas|.
30void DrawTriangle(int x_offset,
31                  int y_offset,
32                  double rotation_degree,
33                  gfx::Canvas* canvas) {
34  // Draw triangular arrows.
35  SkPaint content_paint;
36  content_paint.setStyle(SkPaint::kFill_Style);
37  content_paint.setColor(SkColorSetA(SK_ColorBLACK, kuint8max * kArrowOpacity));
38  SkPaint border_paint;
39  border_paint.setStyle(SkPaint::kStroke_Style);
40  border_paint.setColor(SkColorSetA(SK_ColorWHITE, kuint8max * kArrowOpacity));
41
42  SkPath base_path;
43  base_path.moveTo(0, SkIntToScalar(-kCalibrationArrowHeight));
44  base_path.lineTo(SkIntToScalar(-kCalibrationArrowHeight), 0);
45  base_path.lineTo(SkIntToScalar(kCalibrationArrowHeight), 0);
46  base_path.close();
47
48  SkPath path;
49  gfx::Transform rotate_transform;
50  rotate_transform.Rotate(rotation_degree);
51  gfx::Transform move_transform;
52  move_transform.Translate(x_offset, y_offset);
53  rotate_transform.ConcatTransform(move_transform);
54  base_path.transform(rotate_transform.matrix(), &path);
55
56  canvas->DrawPath(path, content_paint);
57  canvas->DrawPath(path, border_paint);
58}
59
60}  // namespace
61
62OverscanCalibrator::OverscanCalibrator(
63    const gfx::Display& target_display, const gfx::Insets& initial_insets)
64    : display_(target_display),
65      insets_(initial_insets),
66      initial_insets_(initial_insets),
67      committed_(false) {
68  // Undo the overscan calibration temporarily so that the user can see
69  // dark boundary and current overscan region.
70  ash::Shell::GetInstance()->display_controller()->SetOverscanInsets(
71      display_.id(), gfx::Insets());
72
73  ash::internal::DisplayInfo info = ash::Shell::GetInstance()->
74      display_manager()->GetDisplayInfo(display_.id());
75
76  aura::Window* root = ash::Shell::GetInstance()->display_controller()->
77      GetRootWindowForDisplayId(display_.id());
78  ui::Layer* parent_layer = ash::Shell::GetContainer(
79      root, ash::internal::kShellWindowId_OverlayContainer)->layer();
80
81  calibration_layer_.reset(new ui::Layer());
82  calibration_layer_->SetOpacity(0.5f);
83  calibration_layer_->SetBounds(parent_layer->bounds());
84  calibration_layer_->set_delegate(this);
85  parent_layer->Add(calibration_layer_.get());
86}
87
88OverscanCalibrator::~OverscanCalibrator() {
89  // Overscan calibration has finished without commit, so the display has to
90  // be the original offset.
91  if (!committed_) {
92    ash::Shell::GetInstance()->display_controller()->SetOverscanInsets(
93        display_.id(), initial_insets_);
94  }
95}
96
97void OverscanCalibrator::Commit() {
98  ash::Shell::GetInstance()->display_controller()->SetOverscanInsets(
99      display_.id(), insets_);
100  committed_ = true;
101}
102
103void OverscanCalibrator::Reset() {
104  insets_ = initial_insets_;
105  calibration_layer_->SchedulePaint(calibration_layer_->bounds());
106}
107
108void OverscanCalibrator::UpdateInsets(const gfx::Insets& insets) {
109  insets_.Set(std::max(insets.top(), 0),
110              std::max(insets.left(), 0),
111              std::max(insets.bottom(), 0),
112              std::max(insets.right(), 0));
113  calibration_layer_->SchedulePaint(calibration_layer_->bounds());
114}
115
116void OverscanCalibrator::OnPaintLayer(gfx::Canvas* canvas) {
117  static const SkColor kTransparent = SkColorSetARGB(0, 0, 0, 0);
118  gfx::Rect full_bounds = calibration_layer_->bounds();
119  gfx::Rect inner_bounds = full_bounds;
120  inner_bounds.Inset(insets_);
121  canvas->FillRect(full_bounds, SK_ColorBLACK);
122  canvas->FillRect(inner_bounds, kTransparent, SkXfermode::kClear_Mode);
123
124  gfx::Point center = inner_bounds.CenterPoint();
125  int vertical_offset = inner_bounds.height() / 2 - kArrowGapWidth;
126  int horizontal_offset = inner_bounds.width() / 2 - kArrowGapWidth;
127
128  DrawTriangle(center.x(), center.y() + vertical_offset, 0, canvas);
129  DrawTriangle(center.x(), center.y() - vertical_offset, 180, canvas);
130  DrawTriangle(center.x() - horizontal_offset, center.y(), 90, canvas);
131  DrawTriangle(center.x() + horizontal_offset, center.y(), -90, canvas);
132}
133
134void OverscanCalibrator::OnDeviceScaleFactorChanged(
135    float device_scale_factor) {
136  // TODO(mukai): Cancel the overscan calibration when the device
137  // configuration has changed.
138}
139
140base::Closure OverscanCalibrator::PrepareForLayerBoundsChange() {
141  return base::Closure();
142}
143
144}  // namespace chromeos
145