// Copyright 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 "ui/ozone/platform/dri/dri_surface_factory.h" #include #include "base/debug/trace_event.h" #include "base/message_loop/message_loop.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkDevice.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/ozone/surface_ozone_canvas.h" #include "ui/ozone/platform/dri/dri_surface.h" #include "ui/ozone/platform/dri/dri_util.h" #include "ui/ozone/platform/dri/dri_vsync_provider.h" #include "ui/ozone/platform/dri/dri_wrapper.h" #include "ui/ozone/platform/dri/hardware_display_controller.h" #include "ui/ozone/platform/dri/screen_manager.h" namespace ui { namespace { // TODO(dnicoara) Read the cursor plane size from the hardware. const gfx::Size kCursorSize(64, 64); void UpdateCursorImage(DriSurface* cursor, const SkBitmap& image) { SkRect damage; image.getBounds(&damage); // Clear to transparent in case |image| is smaller than the canvas. SkCanvas* canvas = cursor->GetDrawableForWidget(); canvas->clear(SK_ColorTRANSPARENT); SkRect clip; clip.set( 0, 0, canvas->getDeviceSize().width(), canvas->getDeviceSize().height()); canvas->clipRect(clip, SkRegion::kReplace_Op); canvas->drawBitmapRectToRect(image, &damage, damage); } class DriSurfaceAdapter : public gfx::SurfaceOzoneCanvas { public: DriSurfaceAdapter(const base::WeakPtr& controller); virtual ~DriSurfaceAdapter(); // SurfaceOzoneCanvas: virtual skia::RefPtr GetCanvas() OVERRIDE; virtual void ResizeCanvas(const gfx::Size& viewport_size) OVERRIDE; virtual void PresentCanvas(const gfx::Rect& damage) OVERRIDE; virtual scoped_ptr CreateVSyncProvider() OVERRIDE; private: void UpdateNativeSurface(const gfx::Rect& damage); skia::RefPtr surface_; gfx::Rect last_damage_; base::WeakPtr controller_; DISALLOW_COPY_AND_ASSIGN(DriSurfaceAdapter); }; DriSurfaceAdapter::DriSurfaceAdapter( const base::WeakPtr& controller) : controller_(controller) { } DriSurfaceAdapter::~DriSurfaceAdapter() { } skia::RefPtr DriSurfaceAdapter::GetCanvas() { return skia::SharePtr(surface_->getCanvas()); } void DriSurfaceAdapter::ResizeCanvas(const gfx::Size& viewport_size) { SkImageInfo info = SkImageInfo::MakeN32( viewport_size.width(), viewport_size.height(), kOpaque_SkAlphaType); surface_ = skia::AdoptRef(SkSurface::NewRaster(info)); } void DriSurfaceAdapter::PresentCanvas(const gfx::Rect& damage) { CHECK(base::MessageLoopForUI::IsCurrent()); if (!controller_) return; UpdateNativeSurface(damage); controller_->SchedulePageFlip(); controller_->WaitForPageFlipEvent(); } scoped_ptr DriSurfaceAdapter::CreateVSyncProvider() { return scoped_ptr(new DriVSyncProvider(controller_)); } void DriSurfaceAdapter::UpdateNativeSurface(const gfx::Rect& damage) { SkCanvas* canvas = static_cast(controller_->surface()) ->GetDrawableForWidget(); // The DriSurface is double buffered, so the current back buffer is // missing the previous update. Expand damage region. SkRect real_damage = RectToSkRect(UnionRects(damage, last_damage_)); // Copy damage region. skia::RefPtr image = skia::AdoptRef(surface_->newImageSnapshot()); image->draw(canvas, &real_damage, real_damage, NULL); last_damage_ = damage; } } // namespace // static const gfx::AcceleratedWidget DriSurfaceFactory::kDefaultWidgetHandle = 1; DriSurfaceFactory::DriSurfaceFactory(DriWrapper* drm, ScreenManager* screen_manager) : drm_(drm), screen_manager_(screen_manager), state_(UNINITIALIZED), allocated_widgets_(0) { } DriSurfaceFactory::~DriSurfaceFactory() { if (state_ == INITIALIZED) ShutdownHardware(); } gfx::SurfaceFactoryOzone::HardwareState DriSurfaceFactory::InitializeHardware() { if (state_ != UNINITIALIZED) return state_; if (drm_->get_fd() < 0) { LOG(ERROR) << "Failed to create DRI connection"; state_ = FAILED; return state_; } cursor_surface_.reset(CreateSurface(kCursorSize)); if (!cursor_surface_->Initialize()) { LOG(ERROR) << "Failed to initialize cursor surface"; state_ = FAILED; return state_; } state_ = INITIALIZED; return state_; } void DriSurfaceFactory::ShutdownHardware() { CHECK(state_ == INITIALIZED); state_ = UNINITIALIZED; } gfx::AcceleratedWidget DriSurfaceFactory::GetAcceleratedWidget() { CHECK(state_ != FAILED); // We're not using 0 since other code assumes that a 0 AcceleratedWidget is an // invalid widget. return ++allocated_widgets_; } scoped_ptr DriSurfaceFactory::CreateCanvasForWidget( gfx::AcceleratedWidget w) { CHECK(state_ == INITIALIZED); // Initial cursor set. ResetCursor(w); return scoped_ptr( new DriSurfaceAdapter(screen_manager_->GetDisplayController(w))); } bool DriSurfaceFactory::LoadEGLGLES2Bindings( AddGLLibraryCallback add_gl_library, SetGLGetProcAddressProcCallback set_gl_get_proc_address) { return false; } gfx::Size DriSurfaceFactory::GetWidgetSize(gfx::AcceleratedWidget w) { base::WeakPtr controller = screen_manager_->GetDisplayController(w); if (controller) return gfx::Size(controller->get_mode().hdisplay, controller->get_mode().vdisplay); return gfx::Size(0, 0); } void DriSurfaceFactory::SetHardwareCursor(gfx::AcceleratedWidget window, const SkBitmap& image, const gfx::Point& location) { cursor_bitmap_ = image; cursor_location_ = location; if (state_ != INITIALIZED) return; ResetCursor(window); } void DriSurfaceFactory::MoveHardwareCursor(gfx::AcceleratedWidget window, const gfx::Point& location) { cursor_location_ = location; if (state_ != INITIALIZED) return; base::WeakPtr controller = screen_manager_->GetDisplayController(window); if (controller) controller->MoveCursor(location); } void DriSurfaceFactory::UnsetHardwareCursor(gfx::AcceleratedWidget window) { cursor_bitmap_.reset(); if (state_ != INITIALIZED) return; ResetCursor(window); } //////////////////////////////////////////////////////////////////////////////// // DriSurfaceFactory private DriSurface* DriSurfaceFactory::CreateSurface(const gfx::Size& size) { return new DriSurface(drm_, size); } void DriSurfaceFactory::ResetCursor(gfx::AcceleratedWidget w) { base::WeakPtr controller = screen_manager_->GetDisplayController(w); if (!cursor_bitmap_.empty()) { // Draw new cursor into backbuffer. UpdateCursorImage(cursor_surface_.get(), cursor_bitmap_); // Reset location & buffer. if (controller) { controller->MoveCursor(cursor_location_); controller->SetCursor(cursor_surface_.get()); } } else { // No cursor set. if (controller) controller->UnsetCursor(); } } } // namespace ui