text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/chromeos/display/overscan_calibrator.h"
  
  #include <stdint.h>
  
  #include <limits>
  
  #include "ash/common/display/display_info.h"
  #include "ash/common/shell_window_ids.h"
  #include "ash/display/display_manager.h"
  #include "ash/display/window_tree_host_manager.h"
  #include "ash/shell.h"
  #include "base/callback.h"
  #include "third_party/skia/include/core/SkColor.h"
  #include "third_party/skia/include/core/SkPaint.h"
  #include "third_party/skia/include/core/SkPath.h"
  #include "ui/aura/window.h"
  #include "ui/compositor/layer.h"
  #include "ui/compositor/paint_recorder.h"
  #include "ui/gfx/canvas.h"
  
  namespace chromeos {
  namespace {
  
  // The opacity for the arrows of the overscan calibration.
  const float kArrowOpacity = 0.8;
  
  // The height in pixel for the arrows to show the overscan calibration.
  const int kCalibrationArrowHeight = 70;
  
  // The gap between the boundary and calibration arrows.
  const int kArrowGapWidth = 0;
  
  // Draw the arrow for the overscan calibration to |canvas|.
  void DrawTriangle(int x_offset,
                    int y_offset,
                    double rotation_degree,
                    gfx::Canvas* canvas) {
    // Draw triangular arrows.
    SkPaint content_paint;
    content_paint.setStyle(SkPaint::kFill_Style);
    content_paint.setColor(SkColorSetA(
        SK_ColorBLACK, std::numeric_limits<uint8_t>::max() * kArrowOpacity));
    SkPaint border_paint;
    border_paint.setStyle(SkPaint::kStroke_Style);
    border_paint.setColor(SkColorSetA(
        SK_ColorWHITE, std::numeric_limits<uint8_t>::max() * kArrowOpacity));
  
    SkPath base_path;
    base_path.moveTo(0, 0);
    base_path.lineTo(SkIntToScalar(-kCalibrationArrowHeight),
                     SkIntToScalar(-kCalibrationArrowHeight));
    base_path.lineTo(SkIntToScalar(kCalibrationArrowHeight),
                     SkIntToScalar(-kCalibrationArrowHeight));
    base_path.close();
  
    SkPath path;
    gfx::Transform rotate_transform;
    rotate_transform.Rotate(rotation_degree);
    gfx::Transform move_transform;
    move_transform.Translate(x_offset, y_offset);
    rotate_transform.ConcatTransform(move_transform);
    base_path.transform(rotate_transform.matrix(), &path);
  
    canvas->DrawPath(path, content_paint);
    canvas->DrawPath(path, border_paint);
  }
  
  }  // namespace
  
  OverscanCalibrator::OverscanCalibrator(const display::Display& target_display,
                                         const gfx::Insets& initial_insets)
      : display_(target_display),
        insets_(initial_insets),
        initial_insets_(initial_insets),
        committed_(false) {
    // Undo the overscan calibration temporarily so that the user can see
    // dark boundary and current overscan region.
    ash::Shell::GetInstance()->window_tree_host_manager()->SetOverscanInsets(
        display_.id(), gfx::Insets());
  
    ash::DisplayInfo info =
        ash::Shell::GetInstance()->display_manager()->GetDisplayInfo(
            display_.id());
  
    aura::Window* root = ash::Shell::GetInstance()
                             ->window_tree_host_manager()
                             ->GetRootWindowForDisplayId(display_.id());
    ui::Layer* parent_layer =
        ash::Shell::GetContainer(root, ash::kShellWindowId_OverlayContainer)
            ->layer();
  
    calibration_layer_.reset(new ui::Layer());
    calibration_layer_->SetOpacity(0.5f);
    calibration_layer_->SetBounds(parent_layer->bounds());
    calibration_layer_->set_delegate(this);
    parent_layer->Add(calibration_layer_.get());
  }
  
  OverscanCalibrator::~OverscanCalibrator() {
    // Overscan calibration has finished without commit, so the display has to
    // be the original offset.
    if (!committed_) {
      ash::Shell::GetInstance()->window_tree_host_manager()->SetOverscanInsets(
          display_.id(), initial_insets_);
    }
  }
  
  void OverscanCalibrator::Commit() {
    ash::Shell::GetInstance()->window_tree_host_manager()->SetOverscanInsets(
        display_.id(), insets_);
    committed_ = true;
  }
  
  void OverscanCalibrator::Reset() {
    insets_ = initial_insets_;
    calibration_layer_->SchedulePaint(calibration_layer_->bounds());
  }
  
  void OverscanCalibrator::UpdateInsets(const gfx::Insets& insets) {
    insets_.Set(std::max(insets.top(), 0),
                std::max(insets.left(), 0),
                std::max(insets.bottom(), 0),
                std::max(insets.right(), 0));
    calibration_layer_->SchedulePaint(calibration_layer_->bounds());
  }
  
  void OverscanCalibrator::OnPaintLayer(const ui::PaintContext& context) {
    ui::PaintRecorder recorder(context, calibration_layer_->size());
    static const SkColor kTransparent = SkColorSetARGB(0, 0, 0, 0);
    gfx::Rect full_bounds = calibration_layer_->bounds();
    gfx::Rect inner_bounds = full_bounds;
    inner_bounds.Inset(insets_);
    recorder.canvas()->FillRect(full_bounds, SK_ColorBLACK);
    recorder.canvas()->FillRect(inner_bounds, kTransparent,
                                SkXfermode::kClear_Mode);
  
    gfx::Point center = inner_bounds.CenterPoint();
    int vertical_offset = inner_bounds.height() / 2 - kArrowGapWidth;
    int horizontal_offset = inner_bounds.width() / 2 - kArrowGapWidth;
  
    gfx::Canvas* canvas = recorder.canvas();
    DrawTriangle(center.x(), center.y() + vertical_offset, 0, canvas);
    DrawTriangle(center.x(), center.y() - vertical_offset, 180, canvas);
    DrawTriangle(center.x() - horizontal_offset, center.y(), 90, canvas);
    DrawTriangle(center.x() + horizontal_offset, center.y(), -90, canvas);
  }
  
  void OverscanCalibrator::OnDelegatedFrameDamage(
      const gfx::Rect& damage_rect_in_dip) {
  }
  
  void OverscanCalibrator::OnDeviceScaleFactorChanged(
      float device_scale_factor) {
    // TODO(mukai): Cancel the overscan calibration when the device
    // configuration has changed.
  }
  
  base::Closure OverscanCalibrator::PrepareForLayerBoundsChange() {
    return base::Closure();
  }
  
  }  // namespace chromeos
  