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