chromium/ash/wallpaper/views/wallpaper_base_view.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/wallpaper/views/wallpaper_base_view.h"

#include "ash/public/cpp/wallpaper/wallpaper_types.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/style/ash_color_id.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "base/numerics/safe_conversions.h"
#include "cc/paint/paint_flags.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/canvas.h"

namespace ash {

namespace {

// Gets the shield color based on the state. This is used for the login, lock,
// overview and tablet mode.
SkColor GetWallpaperShieldColor(const views::Widget* widget) {
  ui::ColorId color;

  auto* controller = Shell::Get()->wallpaper_controller();
  if (controller->IsOobeWallpaper()) {
    color = cros_tokens::kCrosSysScrim2;
  } else if (Shell::Get()->session_controller()->IsUserSessionBlocked()) {
    color = kColorAshShieldAndBase80;
  } else {
    color = cros_tokens::kCrosSysScrim2;
  }

  DCHECK(widget);
  return widget->GetColorProvider()->GetColor(color);
}

}  // namespace

void WallpaperBaseView::OnPaint(gfx::Canvas* canvas) {
  // Scale the image while maintaining the aspect ratio, cropping as necessary
  // to fill the wallpaper. Ideally the image should be larger than the largest
  // display supported, if not we will scale and center it if the layout is
  // WALLPAPER_LAYOUT_CENTER_CROPPED.
  auto* controller = Shell::Get()->wallpaper_controller();
  gfx::ImageSkia wallpaper = controller->GetWallpaper();
  WallpaperLayout layout = controller->GetWallpaperLayout();

  if (wallpaper.isNull()) {
    return;
  }

  cc::PaintFlags flags;
  switch (layout) {
    case WALLPAPER_LAYOUT_CENTER_CROPPED: {
      // The dimension with the smallest ratio must be cropped, the other one
      // is preserved. Both are set in gfx::Size cropped_size.
      double horizontal_ratio =
          static_cast<double>(width()) / static_cast<double>(wallpaper.width());
      double vertical_ratio = static_cast<double>(height()) /
                              static_cast<double>(wallpaper.height());

      gfx::Size cropped_size;
      if (vertical_ratio > horizontal_ratio) {
        cropped_size = gfx::Size(
            base::ClampFloor(static_cast<double>(width()) / vertical_ratio),
            wallpaper.height());
      } else {
        cropped_size = gfx::Size(
            wallpaper.width(),
            base::ClampFloor(static_cast<double>(height()) / horizontal_ratio));
      }

      gfx::Rect wallpaper_cropped_rect(wallpaper.size());
      wallpaper_cropped_rect.ClampToCenteredSize(cropped_size);
      DrawWallpaper(wallpaper, wallpaper_cropped_rect, gfx::Rect(size()), flags,
                    canvas);
      break;
    }
    case WALLPAPER_LAYOUT_TILE: {
      canvas->TileImageInt(wallpaper, 0, 0, 0, 0, width(), height(), 1.0f,
                           SkTileMode::kRepeat, SkTileMode::kRepeat, &flags);
      break;
    }
    case WALLPAPER_LAYOUT_STRETCH: {
      // This is generally not recommended as it may show artifacts.
      DrawWallpaper(wallpaper, gfx::Rect(wallpaper.size()), gfx::Rect(size()),
                    flags, canvas);
      break;
    }
    case WALLPAPER_LAYOUT_CENTER: {
      const float image_scale = canvas->image_scale();
      // Simply centered and not scaled (but may be clipped).
      gfx::Rect wallpaper_rect = gfx::ScaleToRoundedRect(
          gfx::Rect(wallpaper.size()),
          centered_layout_image_scale_.x() / image_scale,
          centered_layout_image_scale_.y() / image_scale);
      wallpaper_rect.set_x((width() - wallpaper_rect.width()) / 2);
      wallpaper_rect.set_y((height() - wallpaper_rect.height()) / 2);
      // Fill background with black if wallpaper does not cover the entire view.
      if (!wallpaper_rect.Contains(GetLocalBounds())) {
        canvas->FillRect(GetLocalBounds(), SK_ColorBLACK);
      }
      DrawWallpaper(wallpaper, gfx::Rect(wallpaper.size()), wallpaper_rect,
                    flags, canvas);
      break;
    }
    default: {
      NOTREACHED();
    }
  }

  if (controller->ShouldApplyShield()) {
    canvas->FillRect(GetLocalBounds(), GetWallpaperShieldColor(GetWidget()));
  }
}

void WallpaperBaseView::OnThemeChanged() {
  views::View::OnThemeChanged();
  SchedulePaint();
}

void WallpaperBaseView::DrawWallpaper(const gfx::ImageSkia& wallpaper,
                                      const gfx::Rect& src,
                                      const gfx::Rect& dst,
                                      const cc::PaintFlags& flags,
                                      gfx::Canvas* canvas) {
  canvas->DrawImageInt(wallpaper, src.x(), src.y(), src.width(), src.height(),
                       dst.x(), dst.y(), dst.width(), dst.height(),
                       /*filter=*/true, flags);
}

BEGIN_METADATA(WallpaperBaseView)
END_METADATA

}  // namespace ash