summaryrefslogtreecommitdiffstats
path: root/o3d
diff options
context:
space:
mode:
Diffstat (limited to 'o3d')
-rw-r--r--o3d/core/cross/cairo/pattern.cc2
-rw-r--r--o3d/core/cross/cairo/renderer_cairo.cc84
-rw-r--r--o3d/core/cross/cairo/renderer_cairo.h5
-rw-r--r--o3d/core/cross/cairo/texture_cairo.cc189
-rw-r--r--o3d/core/cross/cairo/texture_cairo.h10
5 files changed, 195 insertions, 95 deletions
diff --git a/o3d/core/cross/cairo/pattern.cc b/o3d/core/cross/cairo/pattern.cc
index 9711dfa..0488292 100644
--- a/o3d/core/cross/cairo/pattern.cc
+++ b/o3d/core/cross/cairo/pattern.cc
@@ -50,7 +50,7 @@ Pattern* Pattern::CreateTexturePattern(Pack* pack, Texture* texture) {
TextureCairo* texture_cairo = down_cast<TextureCairo*>(texture);
return WrapCairoPattern(
pack,
- cairo_pattern_create_for_surface(texture_cairo->image_surface()),
+ cairo_pattern_create_for_surface(texture_cairo->surface()),
texture_cairo);
}
diff --git a/o3d/core/cross/cairo/renderer_cairo.cc b/o3d/core/cross/cairo/renderer_cairo.cc
index dcc3e82..dee852c 100644
--- a/o3d/core/cross/cairo/renderer_cairo.cc
+++ b/o3d/core/cross/cairo/renderer_cairo.cc
@@ -50,7 +50,7 @@
#include "core/cross/cairo/layer.h"
#include "core/cross/cairo/texture_cairo.h"
-#ifdef OS_MACOSX
+#if defined(OS_MACOSX) || defined(OS_WIN)
// As of OS X 10.6.4, the Quartz 2D drawing API has hardware acceleration
// disabled by default, and if you force-enable it then it actually hurts
// performance instead of improving it (really, go google it). It also turns out
@@ -58,6 +58,11 @@
// faster than the OS X software implementation (measured as CPU usage per
// rendered frame), so we do all compositing with Pixman via an image surface
// and only use the OS to paint the final frame to the screen.
+
+// On Windows COMPOSITING_TO_IMAGE is also slightly faster than compositing with
+// GDI.
+// TODO(tschmelcher): Profile Windows without COMPOSITING_TO_IMAGE and see if we
+// can make it faster.
#define COMPOSITING_TO_IMAGE 1
#endif
@@ -508,42 +513,73 @@ void RendererCairo::DestroyDisplaySurface() {
}
void RendererCairo::CreateOffscreenSurface() {
+ offscreen_surface_ = CreateSimilarSurface(CAIRO_CONTENT_COLOR,
+ display_width(),
+ display_height());
+ if (!offscreen_surface_) {
+ DLOG(ERROR) << "Failed to create offscreen surface";
+ }
+}
+
+void RendererCairo::DestroyOffscreenSurface() {
+ if (offscreen_surface_) {
+ cairo_surface_destroy(offscreen_surface_);
+ offscreen_surface_ = NULL;
+ }
+}
+
+#if defined(COMPOSITING_TO_IMAGE) || defined(OS_MACOSX)
+static cairo_format_t CairoFormatFromCairoContent(cairo_content_t content) {
+ switch (content) {
+ case CAIRO_CONTENT_COLOR:
+ return CAIRO_FORMAT_RGB24;
+ case CAIRO_CONTENT_ALPHA:
+ return CAIRO_FORMAT_A8;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ return CAIRO_FORMAT_ARGB32;
+ default:
+ DCHECK(false);
+ return CAIRO_FORMAT_ARGB32;
+ }
+}
+#endif
+
+cairo_surface_t* RendererCairo::CreateSimilarSurface(cairo_content_t content,
+ int width,
+ int height) {
+ cairo_surface_t* similar;
#if defined(COMPOSITING_TO_IMAGE)
- offscreen_surface_ = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
- display_width(),
- display_height());
+ similar = cairo_image_surface_create(CairoFormatFromCairoContent(content),
+ width,
+ height);
#elif defined(OS_LINUX) || defined(OS_WIN)
if (!display_surface_) {
- DLOG(INFO) << "No display surface, cannot create offscreen surface";
- return;
+ DLOG(INFO) << "No display surface, cannot create similar surface";
+ return NULL;
}
- offscreen_surface_ = cairo_surface_create_similar(display_surface_,
- CAIRO_CONTENT_COLOR,
- display_width(),
- display_height());
+ similar = cairo_surface_create_similar(display_surface_,
+ content,
+ width,
+ height);
#else // OS_MACOSX
// On OSX we can't use cairo_surface_create_similar() because display_surface_
// is only valid during Paint(), so instead hard-code what a
- // cairo_surface_create_similar() call would do.
+ // cairo_surface_create_similar() call would do. Conveniently the OSX
+ // implementation doesn't actually make use of the surface argument.
// (Note that this code path is not actually taken right now because we use
// COMPOSITING_TO_IMAGE on OSX.)
- offscreen_surface_ = cairo_quartz_surface_create(CAIRO_FORMAT_RGB24,
- display_width(),
- display_height());
+ similar = cairo_quartz_surface_create(CairoFormatFromCairoContent(content),
+ width,
+ height);
#endif
- if (CAIRO_STATUS_SUCCESS != cairo_surface_status(offscreen_surface_)) {
- DLOG(ERROR) << "Failed to create offscreen surface";
- DestroyOffscreenSurface();
- return;
+ if (CAIRO_STATUS_SUCCESS != cairo_surface_status(similar)) {
+ DLOG(ERROR) << "Failed to create similar surface";
+ cairo_surface_destroy(similar);
+ similar = NULL;
}
-}
-void RendererCairo::DestroyOffscreenSurface() {
- if (offscreen_surface_) {
- cairo_surface_destroy(offscreen_surface_);
- offscreen_surface_ = NULL;
- }
+ return similar;
}
void RendererCairo::AddDisplayRegion(cairo_t* cr) {
diff --git a/o3d/core/cross/cairo/renderer_cairo.h b/o3d/core/cross/cairo/renderer_cairo.h
index 2162990..de6bef3 100644
--- a/o3d/core/cross/cairo/renderer_cairo.h
+++ b/o3d/core/cross/cairo/renderer_cairo.h
@@ -80,6 +80,11 @@ class RendererCairo : public Renderer {
// Remove the given Layer from the array.
void RemoveLayer(Layer* image);
+ // Create a cairo surface with a similar back-end as the display surface.
+ cairo_surface_t* CreateSimilarSurface(cairo_content_t content,
+ int width,
+ int height);
+
// Handles the plugin resize event.
virtual void Resize(int width, int height);
diff --git a/o3d/core/cross/cairo/texture_cairo.cc b/o3d/core/cross/cairo/texture_cairo.cc
index 8e1b148..da77732 100644
--- a/o3d/core/cross/cairo/texture_cairo.cc
+++ b/o3d/core/cross/cairo/texture_cairo.cc
@@ -49,29 +49,24 @@ Texture::RGBASwizzleIndices g_gl_abgr32f_swizzle_indices = {0, 1, 2, 3};
namespace o2d {
-// This is equivalent to the CAIRO_FORMAT_INVALID enum value, but we can't refer
-// to that because it was only recently added to the Cairo headers and thus it
-// is not present on the Linux buildbots (where we use the OS version of Cairo
-// rather than the one in third_party).
-static const cairo_format_t kCairoFormatInvalid =
- static_cast<cairo_format_t>(-1);
-
-static cairo_format_t CairoFormatFromO3DFormat(
+static const cairo_content_t kCairoContentInvalid =
+ static_cast<cairo_content_t>(-1);
+
+static cairo_content_t CairoContentFromO3DFormat(
Texture::Format format) {
switch (format) {
case Texture::ARGB8:
- return CAIRO_FORMAT_ARGB32;
+ return CAIRO_CONTENT_COLOR_ALPHA;
case Texture::XRGB8:
- return CAIRO_FORMAT_RGB24;
+ return CAIRO_CONTENT_COLOR;
default:
- return kCairoFormatInvalid;
+ return kCairoContentInvalid;
}
- // Cairo also supports two other pure-alpha formats, but we don't expose those
- // capabilities.
+ // Cairo also supports a pure-alpha surface, but we don't expose that.
}
TextureCairo::TextureCairo(ServiceLocator* service_locator,
- cairo_surface_t* image_surface,
+ cairo_surface_t* surface,
Texture::Format format,
int levels,
int width,
@@ -83,7 +78,7 @@ TextureCairo::TextureCairo(ServiceLocator* service_locator,
format,
levels,
enable_render_surfaces),
- image_surface_(image_surface),
+ surface_(surface),
content_dirty_(false) {
DLOG(INFO) << "Texture2D Construct";
DCHECK_NE(format, Texture::UNKNOWN_FORMAT);
@@ -96,35 +91,34 @@ TextureCairo* TextureCairo::Create(ServiceLocator* service_locator,
int width,
int height,
bool enable_render_surfaces) {
- cairo_format_t cairo_format = CairoFormatFromO3DFormat(format);
- cairo_surface_t* image_surface;
- cairo_status_t status;
- if (kCairoFormatInvalid == cairo_format) {
+ cairo_content_t content = CairoContentFromO3DFormat(format);
+ if (kCairoContentInvalid == content) {
DLOG(ERROR) << "Texture format " << format << " not supported by Cairo";
- goto fail0;
+ return NULL;
}
- image_surface = cairo_image_surface_create(
- cairo_format,
+
+ RendererCairo* renderer = static_cast<RendererCairo*>(
+ service_locator->GetService<Renderer>());
+ // NOTE(tschmelcher): Strangely, on Windows if COMPOSITING_TO_IMAGE is turned
+ // off in RendererCairo then we actually get much better performance if we use
+ // an image surface for textures rather than using CreateSimilarSurface(). But
+ // the performance of COMPOSITING_TO_IMAGE narrowly takes first place.
+ cairo_surface_t* surface = renderer->CreateSimilarSurface(
+ content,
width,
height);
- status = cairo_surface_status(image_surface);
- if (CAIRO_STATUS_SUCCESS != status) {
- DLOG(ERROR) << "Error creating Cairo image surface: " << status;
- goto fail1;
+ if (!surface) {
+ DLOG(ERROR) << "Failed to create texture surface";
+ return NULL;
}
return new TextureCairo(service_locator,
- image_surface,
+ surface,
format,
levels,
width,
height,
enable_render_surfaces);
-
- fail1:
- cairo_surface_destroy(image_surface);
- fail0:
- return NULL;
}
// In 2D: is not really used
@@ -134,10 +128,39 @@ const Texture::RGBASwizzleIndices& TextureCairo::GetABGR32FSwizzleIndices() {
}
TextureCairo::~TextureCairo() {
- cairo_surface_destroy(image_surface_);
+ cairo_surface_destroy(surface_);
DLOG(INFO) << "Texture2DCairo Destruct";
}
+static void CopyNonPremultipliedAlphaToPremultipliedAlpha(
+ const unsigned char* src_data,
+ int src_pitch,
+ unsigned char* dst_data,
+ int dst_pitch,
+ unsigned width,
+ unsigned height) {
+ // Cairo supports only premultiplied alpha, but we get images as
+ // non-premultiplied alpha, so we have to convert.
+ for (unsigned i = 0; i < height; ++i) {
+ for (unsigned j = 0; j < width; ++j) {
+ // NOTE: This assumes a little-endian architecture (e.g., x86). It
+ // works for RGBA or BGRA where alpha is in byte 3.
+ // Get alpha.
+ uint8 alpha = src_data[3];
+ // Convert each colour.
+ for (int i = 0; i < 3; i++) {
+ dst_data[i] = (src_data[i] * alpha + 128U) / 255U;
+ }
+ // Copy alpha.
+ dst_data[3] = alpha;
+ src_data += 4;
+ dst_data += 4;
+ }
+ src_data += src_pitch - width * 4;
+ dst_data += dst_pitch - width * 4;
+ }
+}
+
// Set the image data to the renderer
void TextureCairo::SetRect(int level,
unsigned dst_left,
@@ -153,48 +176,84 @@ void TextureCairo::SetRect(int level,
return;
}
- cairo_surface_flush(image_surface_);
+ unsigned char* src_data =
+ reinterpret_cast<unsigned char*>(const_cast<void*>(src_data_void));
+
+ if (cairo_surface_get_type(surface_) == CAIRO_SURFACE_TYPE_IMAGE) {
+ // Copy directly to the image surface's data buffer.
- const unsigned char* src_data = reinterpret_cast<const unsigned char*>(
- src_data_void);
+ cairo_surface_flush(surface_);
- unsigned char* dst_data = cairo_image_surface_get_data(image_surface_);
+ unsigned char* dst_data = cairo_image_surface_get_data(surface_);
- int dst_pitch = cairo_image_surface_get_stride(image_surface_);
+ int dst_pitch = cairo_image_surface_get_stride(surface_);
- dst_data += dst_top * dst_pitch + dst_left * 4;
+ dst_data += dst_top * dst_pitch + dst_left * 4;
- if (ARGB8 == format()) {
- // Cairo supports only premultiplied alpha, but we get the images as
- // non-premultiplied alpha, so we have to convert.
- for (unsigned i = 0; i < src_height; ++i) {
- for (unsigned j = 0; j < src_width; ++j) {
- // NOTE: This assumes a little-endian architecture (e.g., x86). It works
- // for RGBA or BGRA where alpha is in byte 3.
- // Get alpha.
- uint8 alpha = src_data[3];
- // Convert each colour.
- for (int i = 0; i < 3; i++) {
- dst_data[i] = (src_data[i] * alpha + 128U) / 255U;
- }
- // Copy alpha.
- dst_data[3] = alpha;
- src_data += 4;
- dst_data += 4;
+ if (ARGB8 == format()) {
+ CopyNonPremultipliedAlphaToPremultipliedAlpha(src_data,
+ src_pitch,
+ dst_data,
+ dst_pitch,
+ src_width,
+ src_height);
+ } else {
+ for (unsigned i = 0; i < src_height; ++i) {
+ memcpy(dst_data, src_data, src_width * 4);
+ src_data += src_pitch;
+ dst_data += dst_pitch;
}
- src_data += src_pitch - src_width * 4;
- dst_data += dst_pitch - src_width * 4;
}
+
+ cairo_surface_mark_dirty(surface_);
} else {
- // Just copy the data.
- for (unsigned i = 0; i < src_height; ++i) {
- memcpy(dst_data, src_data, src_width * 4);
- src_data += src_pitch;
- dst_data += dst_pitch;
+ // No way to get a pointer to a data buffer for the surface, so we have to
+ // update it using cairo operations.
+
+ // Create a source surface for the paint operation.
+ cairo_surface_t* source_surface;
+ if (ARGB8 == format()) {
+ // Have to convert to premultiplied alpha, so we need to make a temporary
+ // image surface and copy to it.
+ source_surface = cairo_image_surface_create(
+ CAIRO_FORMAT_ARGB32,
+ src_width,
+ src_height);
+
+ cairo_surface_flush(source_surface);
+
+ unsigned char* dst_data = cairo_image_surface_get_data(source_surface);
+
+ int dst_pitch = cairo_image_surface_get_stride(source_surface);
+
+ CopyNonPremultipliedAlphaToPremultipliedAlpha(src_data,
+ src_pitch,
+ dst_data,
+ dst_pitch,
+ src_width,
+ src_height);
+
+ cairo_surface_mark_dirty(source_surface);
+ } else {
+ // No conversion needed, so we can paint directly from the input buffer.
+ source_surface = cairo_image_surface_create_for_data(
+ src_data,
+ CAIRO_FORMAT_RGB24,
+ src_width,
+ src_height,
+ src_pitch);
}
- }
- cairo_surface_mark_dirty(image_surface_);
+ // Now paint it to this texture's surface.
+ cairo_t* context = cairo_create(surface_);
+ cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface(context, source_surface, dst_left, dst_top);
+ cairo_rectangle(context, dst_left, dst_top, src_width, src_height);
+ cairo_fill(context);
+ cairo_destroy(context);
+
+ cairo_surface_destroy(source_surface);
+ }
set_content_dirty(true);
diff --git a/o3d/core/cross/cairo/texture_cairo.h b/o3d/core/cross/cairo/texture_cairo.h
index 2a73a0f..70829e7 100644
--- a/o3d/core/cross/cairo/texture_cairo.h
+++ b/o3d/core/cross/cairo/texture_cairo.h
@@ -77,8 +77,8 @@ class TextureCairo : public Texture2D {
// RGBA to the internal format used by the rendering API.
virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices();
- cairo_surface_t* image_surface() const {
- return image_surface_;
+ cairo_surface_t* surface() const {
+ return surface_;
}
bool content_dirty() const {
@@ -106,15 +106,15 @@ class TextureCairo : public Texture2D {
private:
// Initializes the Texture2D.
TextureCairo(ServiceLocator* service_locator,
- cairo_surface_t* image_surface,
+ cairo_surface_t* surface,
Texture::Format format,
int levels,
int width,
int height,
bool enable_render_surfaces);
- // The Cairo image for this texture.
- cairo_surface_t* image_surface_;
+ // The Cairo surface for this texture.
+ cairo_surface_t* surface_;
// Whether or not this texture's content has changed since it was last updated
// on-screen.