// Copyright (c) 2014 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2014 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 "libcef/browser/osr/software_output_device_osr.h"

#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/osr/render_widget_host_view_osr.h"
#include "libcef/browser/thread_util.h"

#include "third_party/skia/src/core/SkDevice.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/skia_util.h"

CefSoftwareOutputDeviceOSR::CefSoftwareOutputDeviceOSR(
    ui::Compositor* compositor,
    bool transparent,
    const OnPaintCallback& callback)
    : transparent_(transparent), callback_(callback), active_(false) {
  CEF_REQUIRE_UIT();
  DCHECK(!callback_.is_null());
}

CefSoftwareOutputDeviceOSR::~CefSoftwareOutputDeviceOSR() {
  CEF_REQUIRE_UIT();
}

void CefSoftwareOutputDeviceOSR::Resize(const gfx::Size& viewport_pixel_size,
                                        float scale_factor) {
  CEF_REQUIRE_UIT();

  if (viewport_pixel_size_ == viewport_pixel_size)
    return;

  viewport_pixel_size_ = viewport_pixel_size;

  canvas_.reset(NULL);
  bitmap_.reset(new SkBitmap);
  bitmap_->allocN32Pixels(viewport_pixel_size.width(),
                          viewport_pixel_size.height(), !transparent_);
  if (bitmap_->drawsNothing()) {
    NOTREACHED();
    bitmap_.reset(NULL);
    return;
  }

  if (transparent_)
    bitmap_->eraseARGB(0, 0, 0, 0);

  canvas_.reset(new SkCanvas(*bitmap_.get()));
}

SkCanvas* CefSoftwareOutputDeviceOSR::BeginPaint(const gfx::Rect& damage_rect) {
  CEF_REQUIRE_UIT();
  DCHECK(canvas_.get());
  DCHECK(bitmap_.get());

  damage_rect_ = damage_rect;

  return canvas_.get();
}

void CefSoftwareOutputDeviceOSR::EndPaint() {
  CEF_REQUIRE_UIT();
  DCHECK(canvas_.get());
  DCHECK(bitmap_.get());

  if (!bitmap_.get())
    return;

  viz::SoftwareOutputDevice::EndPaint();

  if (active_)
    OnPaint(damage_rect_);
}

void CefSoftwareOutputDeviceOSR::SetActive(bool active) {
  if (active == active_)
    return;
  active_ = active;

  // Call OnPaint immediately if deactivated while a damage rect is pending.
  if (!active_ && !pending_damage_rect_.IsEmpty())
    OnPaint(pending_damage_rect_);
}

void CefSoftwareOutputDeviceOSR::Invalidate(const gfx::Rect& damage_rect) {
  if (pending_damage_rect_.IsEmpty())
    pending_damage_rect_ = damage_rect;
  else
    pending_damage_rect_.Union(damage_rect);
}

void CefSoftwareOutputDeviceOSR::OnPaint(const gfx::Rect& damage_rect) {
  gfx::Rect rect = damage_rect;
  if (!pending_damage_rect_.IsEmpty()) {
    rect.Union(pending_damage_rect_);
    pending_damage_rect_.SetRect(0, 0, 0, 0);
  }

  rect.Intersect(gfx::Rect(viewport_pixel_size_));
  if (rect.IsEmpty())
    return;

  callback_.Run(rect, bitmap_->width(), bitmap_->height(),
                bitmap_->getPixels());
}
