blob: 060c605b32300a4b8be170199c769fc0d3e9ad1f [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS 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 "window_manager/compositor/xrender/xrender_visitor.h"
#include <X11/extensions/Xrender.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "window_manager/image_container.h"
#include "window_manager/image_enums.h"
#include "window_manager/util.h"
#include "window_manager/x11/x_connection.h"
#ifndef COMPOSITOR_XRENDER
#error Need COMPOSITOR_XRENDER defined to compile this file
#endif
namespace window_manager {
const int kRGBPictureBitDepth = 24;
const int kRGBAPictureBitDepth = 32;
class XRenderPictureData : public TextureData {
public:
XRenderPictureData(XConnection* xconn)
: pixmap_(0),
xconn_(xconn) {
}
virtual ~XRenderPictureData() {
xconn_->RenderFreePicture(texture());
}
// Initialize our texture and make it contain the current contents of the
// passed-in pixmap. False is returned if the process fails (in
// which case this object should be thrown away).
bool Init(XPixmap pixmap, int bpp) {
XPicture picture = xconn_->RenderCreatePicture(pixmap, bpp);
set_texture(picture);
return (picture != None);
}
private:
// The actor's X pixmap. Ownership of the pixmap remains with the caller.
XPixmap pixmap_;
XConnection* xconn_; // Not owned.
};
XRenderDrawVisitor::XRenderDrawVisitor(RealCompositor* compositor,
Compositor::StageActor* stage)
: root_window_(None),
back_picture_(None),
back_pixmap_(None),
stage_picture_(None),
compositor_(NULL),
xconn_(compositor->x_conn()),
stage_(NULL),
ancestor_opacity_(1.0),
has_fullscreen_actor_(false) {
// Check for the XRender extension.
CHECK(xconn_->RenderQueryExtension());
CHECK(AllocateXResources(stage));
}
XRenderDrawVisitor::~XRenderDrawVisitor() {
CHECK(FreeXResources());
}
void XRenderDrawVisitor::BindImage(const ImageContainer& container,
RealCompositor::ImageActor* actor) {
XPixmap pixmap = xconn_->CreatePixmapFromContainer(container);
scoped_ptr<window_manager::XRenderPictureData> data(
new XRenderPictureData(this->xconn_));
data->Init(pixmap, kRGBAPictureBitDepth);
actor->set_texture_data(data.release());
}
void XRenderDrawVisitor::VisitStage(RealCompositor::StageActor* actor) {
if (!actor->IsVisible())
return;
stage_ = actor;
if (actor->was_resized()) {
CHECK(FreeXResources());
CHECK(AllocateXResources(actor));
actor->unset_was_resized();
}
// If we don't have a full screen actor we do a fill with the stage color.
if (!has_fullscreen_actor_) {
const Compositor::Color& color = actor->stage_color();
xconn_->RenderFillRectangle(back_picture_,
color.red,
color.green,
color.blue,
Point(0, 0),
root_geometry_.bounds.size());
}
#ifdef EXTRA_LOGGING
DLOG(INFO) << "Starting Render pass.";
#endif
ancestor_opacity_ = actor->opacity();
// Walk the actors and render them
VisitContainer(actor);
#ifdef EXTRA_LOGGING
DLOG(INFO) << "Ending Render pass.";
#endif
if (!damaged_region_.empty()) {
Matrix4 identity = Matrix4::identity();
identity[0][0] = damaged_region_.width;
identity[1][1] = damaged_region_.height;
identity[3][0] = damaged_region_.x;
identity[3][1] = root_geometry_.bounds.height -
damaged_region_.y - damaged_region_.height;
xconn_->RenderComposite(false,
back_picture_,
None,
stage_picture_,
Point(damaged_region_.x,
root_geometry_.bounds.height -
damaged_region_.y -
damaged_region_.height),
Point(0, 0),
identity,
damaged_region_.size());
} else {
Matrix4 identity = Vectormath::Aos::Matrix4::identity();
identity[0][0] = root_geometry_.bounds.width;
identity[1][1] = root_geometry_.bounds.height;
xconn_->RenderComposite(false,
back_picture_,
None,
stage_picture_,
Point(0, 0),
Point(0, 0),
identity,
root_geometry_.bounds.size());
}
stage_ = NULL;
}
void XRenderDrawVisitor::VisitContainer(RealCompositor::ContainerActor* actor) {
if (!actor->IsVisible())
return;
#ifdef EXTRA_LOGGING
DLOG(INFO) << "Drawing container " << actor->name() << ".";
DLOG(INFO) << " at: (" << actor->x() << ", " << actor->y()
<< ", " << actor->z() << ") with scale: ("
<< actor->scale_x() << ", " << actor->scale_y() << ") at size ("
<< actor->width() << "x" << actor->height() << ")";
#endif
RealCompositor::ActorVector children = actor->GetChildren();
float original_opacity = ancestor_opacity_;
ancestor_opacity_ *= actor->opacity();
// Walk backwards so we go back to front.
RealCompositor::ActorVector::const_reverse_iterator iterator;
for (iterator = children.rbegin(); iterator != children.rend();
++iterator) {
RealCompositor::Actor* child = *iterator;
if (child->IsVisible()) {
#ifdef EXTRA_LOGGING
DLOG(INFO) << "Drawing child " << child->name()
<< " (visible: " << child->IsVisible()
<< ", has_children: " << child->has_children()
<< ", opacity: " << child->opacity()
<< ", ancestor_opacity: " << ancestor_opacity_
<< ", is_opaque: " << child->is_opaque() << ")";
#endif
child->Accept(this);
} else {
#ifdef EXTRA_LOGGING
DLOG(INFO) << "NOT drawing child " << child->name()
<< " (visible: " << child->IsVisible()
<< ", has_children: " << child->has_children()
<< ", opacity: " << child->opacity()
<< ", ancestor_opacity: " << ancestor_opacity_
<< ", is_opaque: " << child->is_opaque() << ")";
#endif
}
// Reset ancestor opacity.
ancestor_opacity_ = original_opacity;
}
}
void XRenderDrawVisitor::VisitImage(RealCompositor::ImageActor* actor) {
if (!actor->IsVisible())
return;
// All ImageActors are also QuadActors, and so we let the
// QuadActor do all the actual drawing.
VisitQuad(actor);
}
void XRenderDrawVisitor::VisitTexturePixmap(
RealCompositor::TexturePixmapActor* actor) {
if (!actor->IsVisible())
return;
// Make sure we have an XRender pic for this pixmap
if (!actor->texture_data()) {
if (actor->pixmap()) {
scoped_ptr<window_manager::XRenderPictureData>
data(new XRenderPictureData(this->xconn_));
data->Init(actor->pixmap(),
actor->pixmap_is_opaque() ?
kRGBPictureBitDepth : kRGBAPictureBitDepth);
actor->set_texture_data(data.release());
}
}
// All texture pixmaps are also QuadActors, and so we let the
// QuadActor do all the actual drawing.
VisitQuad(actor);
}
void XRenderDrawVisitor::VisitQuad(RealCompositor::QuadActor* actor) {
if (!actor->IsVisible())
return;
#ifdef EXTRA_LOGGING
DLOG(INFO) << "Drawing quad " << actor->name() << ".";
#endif
// Calculate the vertex colors, taking into account the actor color,
// opacity and the dimming gradient.
float actor_opacity = actor->is_opaque() ?
1.f :
actor->opacity() * ancestor_opacity_;
float dimmed_transparency_begin = 1.f - actor->dimmed_opacity_begin();
float dimmed_transparency_end = 1.f - actor->dimmed_opacity_end();
float red = actor->color().red;
float green = actor->color().green;
float blue = actor->color().blue;
DCHECK_LE(actor_opacity, 1.f);
DCHECK_GE(actor_opacity, 0.f);
DCHECK_LE(dimmed_transparency_begin, 1.f);
DCHECK_GE(dimmed_transparency_begin, 0.f);
DCHECK_LE(dimmed_transparency_end, 1.f);
DCHECK_GE(dimmed_transparency_end, 0.f);
DCHECK_LE(red, 1.f);
DCHECK_GE(red, 0.f);
DCHECK_LE(green, 1.f);
DCHECK_GE(green, 0.f);
DCHECK_LE(blue, 1.f);
DCHECK_GE(blue, 0.f);
xconn_->RenderComposite(
!actor->is_opaque(),
static_cast<XPicture>(actor->texture_data()->texture()),
static_cast<XPicture>(None),
back_picture_,
Point(0, 0),
Point(0, 0),
actor->model_view(),
actor->GetBounds().size());
}
bool XRenderDrawVisitor::FreeXResources() {
return xconn_->FreePixmap(back_pixmap_) &&
xconn_->RenderFreePicture(back_picture_) &&
xconn_->RenderFreePicture(stage_picture_);
}
bool XRenderDrawVisitor::AllocateXResources(Compositor::StageActor* stage) {
// Find root window geometry.
root_window_ = stage->GetStageXWindow();
xconn_->GetWindowGeometry(root_window_, &root_geometry_);
// Create back pixmap.
back_pixmap_ =
xconn_->CreatePixmap(
root_window_,
root_geometry_.bounds.size(),
root_geometry_.depth);
// Create back picture.
back_picture_ = xconn_->RenderCreatePicture(back_pixmap_,
kRGBPictureBitDepth);
// Create stage picture.
stage_picture_ = xconn_->RenderCreatePicture(root_window_,
kRGBPictureBitDepth);
return (back_pixmap_ != None) &&
(back_picture_ != None) &&
(stage_picture_ != None);
}
} // namespace window_manager