Bug 1168527 - Replay clips into the system cairo on GTK3. r=lsalzman

--HG--
extra : rebase_source : 013e1811f6b3fa1f599531153c292d7a5136f087
extra : histedit_source : 7c3f596514ea87840a0bd231b247b119ed9d3048
This commit is contained in:
Andrew Comminos 2015-06-09 13:46:09 -04:00
Родитель 198ba930fe
Коммит 40b78be093
1 изменённых файлов: 123 добавлений и 41 удалений

Просмотреть файл

@ -35,6 +35,7 @@
#include "gfxGdkNativeRenderer.h"
#include "mozilla/gfx/BorrowedContext.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "mozilla/gfx/PathHelpers.h"
#ifdef MOZ_X11
# ifdef CAIRO_HAS_XLIB_SURFACE
@ -715,6 +716,85 @@ ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX,
return NS_OK;
}
#else
class SystemCairoClipper : public ClipExporter {
public:
SystemCairoClipper(cairo_t* aContext) : mContext(aContext)
{
}
void
BeginClip(const Matrix& aTransform) override
{
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(aTransform, mat);
cairo_set_matrix(mContext, &mat);
cairo_new_path(mContext);
}
void
MoveTo(const Point &aPoint) override
{
cairo_move_to(mContext, aPoint.x, aPoint.y);
mCurrentPoint = aPoint;
}
void
LineTo(const Point &aPoint) override
{
cairo_line_to(mContext, aPoint.x, aPoint.y);
mCurrentPoint = aPoint;
}
void
BezierTo(const Point &aCP1, const Point &aCP2, const Point &aCP3) override
{
cairo_curve_to(mContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
mCurrentPoint = aCP3;
}
void
QuadraticBezierTo(const Point &aCP1, const Point &aCP2) override
{
Point CP0 = CurrentPoint();
Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
Point CP3 = aCP2;
cairo_curve_to(mContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
mCurrentPoint = aCP2;
}
void
Arc(const Point &aOrigin, float aRadius, float aStartAngle, float aEndAngle,
bool aAntiClockwise) override
{
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
aAntiClockwise);
}
void
Close() override
{
cairo_close_path(mContext);
}
void
EndClip() override
{
cairo_clip(mContext);
}
Point
CurrentPoint() const override
{
return mCurrentPoint;
}
private:
cairo_t* mContext;
Point mCurrentPoint;
};
static void
DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
@ -722,19 +802,29 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
bool aSnapped, const Point& aDrawOrigin, const nsIntSize& aDrawSize,
GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency)
{
Point drawOffset;
Matrix transform;
if (!aSnapped) {
// If we are not snapped, we depend on the DT for translation.
drawOffset = aDrawOrigin;
transform = aDrawTarget->GetTransform().PreTranslate(aDrawOrigin);
} else {
// Otherwise, we only need to take the device offset into account.
drawOffset = aDrawOrigin - aContext->GetDeviceOffset();
transform = Matrix::Translation(drawOffset);
}
if (aScaleFactor != 1)
transform.PreScale(aScaleFactor, aScaleFactor);
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(transform, mat);
#ifndef MOZ_TREE_CAIRO
// Directly use the Cairo draw target to render the widget if using system Cairo everywhere.
BorrowedCairoContext borrow(aDrawTarget);
if (borrow.mCairo) {
if (aSnapped) {
cairo_identity_matrix(borrow.mCairo);
}
if (aDrawOrigin != Point(0, 0)) {
cairo_translate(borrow.mCairo, aDrawOrigin.x, aDrawOrigin.y);
}
if (aScaleFactor != 1) {
cairo_scale(borrow.mCairo, aScaleFactor, aScaleFactor);
}
cairo_set_matrix(borrow.mCairo, &mat);
moz_gtk_widget_paint(aGTKWidgetType, borrow.mCairo, &aGDKRect, &aState, aFlags, aDirection);
@ -744,40 +834,33 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
#endif
// A direct Cairo draw target is not available, so we need to create a temporary one.
bool needClip = !aSnapped || aContext->HasComplexClip();
#if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
if (!needClip) {
// If using a Cairo xlib surface, then try to reuse it.
BorrowedXlibDrawable borrow(aDrawTarget);
if (borrow.GetDrawable()) {
nsIntSize size = aDrawTarget->GetSize();
cairo_surface_t* surf = nullptr;
// Check if the surface is using XRender.
// If using a Cairo xlib surface, then try to reuse it.
BorrowedXlibDrawable borrow(aDrawTarget);
if (borrow.GetDrawable()) {
nsIntSize size = aDrawTarget->GetSize();
cairo_surface_t* surf = nullptr;
// Check if the surface is using XRender.
#ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
if (borrow.GetXRenderFormat()) {
surf = cairo_xlib_surface_create_with_xrender_format(
if (borrow.GetXRenderFormat()) {
surf = cairo_xlib_surface_create_with_xrender_format(
borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetScreen(),
borrow.GetXRenderFormat(), size.width, size.height);
} else {
} else {
#else
if (! borrow.GetXRenderFormat()) {
#endif
surf = cairo_xlib_surface_create(
borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetVisual(),
size.width, size.height);
borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetVisual(),
size.width, size.height);
}
if (!NS_WARN_IF(!surf)) {
cairo_t* cr = cairo_create(surf);
if (!NS_WARN_IF(!cr)) {
cairo_new_path(cr);
cairo_rectangle(cr, aDrawOrigin.x, aDrawOrigin.y, aDrawSize.width, aDrawSize.height);
cairo_clip(cr);
if (aDrawOrigin != Point(0, 0)) {
cairo_translate(cr, aDrawOrigin.x, aDrawOrigin.y);
}
if (aScaleFactor != 1) {
cairo_scale(cr, aScaleFactor, aScaleFactor);
}
RefPtr<SystemCairoClipper> clipper = new SystemCairoClipper(cr);
aContext->ExportClip(*clipper);
cairo_set_matrix(cr, &mat);
moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
@ -788,7 +871,6 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
borrow.Finish();
return;
}
}
#endif
// Check if the widget requires complex masking that must be composited.
@ -797,18 +879,18 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
nsIntSize size;
int32_t stride;
SurfaceFormat format;
if (!needClip && aDrawTarget->LockBits(&data, &size, &stride, &format)) {
if (aDrawTarget->LockBits(&data, &size, &stride, &format)) {
// Create a Cairo image surface context the device rectangle.
cairo_surface_t* surf =
cairo_image_surface_create_for_data(
data + int32_t(aDrawOrigin.y) * stride + int32_t(aDrawOrigin.x) * BytesPerPixel(format),
GfxFormatToCairoFormat(format), aDrawSize.width, aDrawSize.height, stride);
data, GfxFormatToCairoFormat(format), size.width, size.height, stride);
if (!NS_WARN_IF(!surf)) {
cairo_t* cr = cairo_create(surf);
if (!NS_WARN_IF(!cr)) {
if (aScaleFactor != 1) {
cairo_scale(cr, aScaleFactor, aScaleFactor);
}
RefPtr<SystemCairoClipper> clipper = new SystemCairoClipper(cr);
aContext->ExportClip(*clipper);
cairo_set_matrix(cr, &mat);
moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
@ -845,17 +927,17 @@ DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
dataSurface->Unmap();
if (cr) {
if (needClip || aTransparency != nsITheme::eOpaque) {
if (!aSnapped || aTransparency != nsITheme::eOpaque) {
// The widget either needs to be masked or has transparency, so use the slower drawing path.
aDrawTarget->DrawSurface(dataSurface,
Rect(aSnapped ? aDrawOrigin - aDrawTarget->GetTransform().GetTranslation() : aDrawOrigin,
Rect(aSnapped ? drawOffset - aDrawTarget->GetTransform().GetTranslation() : drawOffset,
Size(aDrawSize)),
Rect(0, 0, aDrawSize.width, aDrawSize.height));
} else {
// The widget is a simple opaque rectangle, so just copy it out.
aDrawTarget->CopySurface(dataSurface,
IntRect(0, 0, aDrawSize.width, aDrawSize.height),
TruncatedToInt(aDrawOrigin));
TruncatedToInt(drawOffset));
}
cairo_destroy(cr);