зеркало из https://github.com/mozilla/gecko-dev.git
Bug 327878. Add cairo_draw_with_xlib API, and use it to render GTK2 native themes (including for HTML content)
This commit is contained in:
Родитель
ad5f1c79ec
Коммит
3650a89de7
|
@ -41,7 +41,7 @@ endif
|
|||
endif
|
||||
|
||||
ifeq ($(MOZ_GFX_TOOLKIT),gtk2)
|
||||
EXPORTS += gfxXlibSurface.h gfxPlatformGtk.h
|
||||
EXPORTS += gfxXlibSurface.h gfxPlatformGtk.h gfxXlibNativeRenderer.h
|
||||
EXPORTS += gfxPangoFonts.h
|
||||
EXPORTS += gfxPDFSurface.h gfxPSSurface.h
|
||||
|
||||
|
|
|
@ -59,6 +59,12 @@ struct NS_EXPORT gfxRect {
|
|||
return (pos != s.pos) || (size != s.size);
|
||||
}
|
||||
|
||||
const gfxPoint& TopLeft() { return pos; }
|
||||
gfxFloat Width() { return size.width; }
|
||||
gfxFloat Height() { return size.height; }
|
||||
gfxFloat X() { return pos.x; }
|
||||
gfxFloat Y() { return pos.y; }
|
||||
|
||||
// XXX figure out what methods (intersect, union, etc) we use and add them.
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Novell code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Novell.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* rocallahan@novell.com
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef GFXXLIBNATIVERENDER_H_
|
||||
#define GFXXLIBNATIVERENDER_H_
|
||||
|
||||
#include "gfxColor.h"
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
class gfxASurface;
|
||||
class gfxContext;
|
||||
|
||||
/**
|
||||
* This class lets us take code that draws into an X drawable and lets us
|
||||
* use it to draw into any Thebes context. The user should subclass this class,
|
||||
* override NativeDraw, and then call Draw(). The drawing will be subjected
|
||||
* to all Thebes transformations, clipping etc.
|
||||
*/
|
||||
class NS_EXPORT gfxXlibNativeRenderer {
|
||||
public:
|
||||
/**
|
||||
* Perform the native drawing.
|
||||
* @param offsetX draw at this offset into the given drawable
|
||||
* @param offsetY draw at this offset into the given drawable
|
||||
* @param clipRects an array of rects; clip to the union
|
||||
* @param numClipRects the number of rects in the array, or zero if
|
||||
* no clipping is required
|
||||
*/
|
||||
virtual nsresult NativeDraw(Display* dpy, Drawable drawable, Visual* visual,
|
||||
short offsetX, short offsetY,
|
||||
XRectangle* clipRects, PRUint32 numClipRects) = 0;
|
||||
|
||||
enum {
|
||||
// If set, then Draw() is opaque, i.e., every pixel in the intersection
|
||||
// of the clipRect and (offset.x,offset.y,bounds.width,bounds.height)
|
||||
// will be set and there is no dependence on what the existing pixels
|
||||
// in the drawable are set to.
|
||||
DRAW_IS_OPAQUE = 0x01,
|
||||
// If set, then offset may be non-zero; if not set, then Draw() can
|
||||
// only be called with offset==(0,0)
|
||||
DRAW_SUPPORTS_OFFSET = 0x02,
|
||||
// If set, then numClipRects can be zero or one
|
||||
DRAW_SUPPORTS_CLIP_RECT = 0x04,
|
||||
// If set, then numClipRects can be any value. If neither this
|
||||
// nor CLIP_RECT are set, then numClipRects will be zero
|
||||
DRAW_SUPPORTS_CLIP_LIST = 0x08,
|
||||
// If set, then the visual passed in can be any visual, otherwise the
|
||||
// visual passed in must be the default visual for dpy's default screen
|
||||
DRAW_SUPPORTS_NONDEFAULT_VISUAL = 0x08,
|
||||
// If set, then the display 'dpy' in the callback can be different from
|
||||
// the display passed to 'Draw'
|
||||
DRAW_SUPPORTS_ALTERNATE_DISPLAY = 0x10
|
||||
};
|
||||
|
||||
struct DrawOutput {
|
||||
gfxASurface* mSurface;
|
||||
PRPackedBool mUniformAlpha;
|
||||
PRPackedBool mUniformColor;
|
||||
gfxRGBA mColor;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param flags see above
|
||||
* @param bounds Draw()'s drawing is guaranteed to be restricted to
|
||||
* the rectangle (offset.x,offset.y,bounds.width,bounds.height)
|
||||
* @param dpy a display to use for the drawing if ctx doesn't have one
|
||||
* @param resultSurface if non-null, we will try to capture a copy of the
|
||||
* rendered image into a surface similar to the surface of ctx; if
|
||||
* successful, a pointer to the new gfxASurface is stored in *resultSurface,
|
||||
* otherwise *resultSurface is set to nsnull.
|
||||
*/
|
||||
nsresult Draw(Display* dpy, gfxContext* ctx, int width, int height,
|
||||
PRUint32 flags, DrawOutput* output);
|
||||
};
|
||||
|
||||
#endif /*GFXXLIBNATIVERENDER_H_*/
|
|
@ -26,8 +26,7 @@ CPPSRCS = \
|
|||
gfxFont.cpp \
|
||||
gfxPlatform.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DIST)/lib/$(LIB_PREFIX)mozcairo.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)mozlibpixman.$(LIB_SUFFIX) \
|
||||
|
@ -50,9 +49,10 @@ OS_LIBS += $(call EXPAND_LIBNAME,$(_OS_LIBS))
|
|||
endif
|
||||
|
||||
ifeq ($(MOZ_GFX_TOOLKIT),gtk2)
|
||||
CPPSRCS += gfxXlibSurface.cpp gfxPlatformGtk.cpp
|
||||
CPPSRCS += gfxXlibSurface.cpp gfxPlatformGtk.cpp gfxXlibNativeRenderer.cpp
|
||||
CPPSRCS += gfxPangoFonts.cpp
|
||||
CPPSRCS += gfxPDFSurface.cpp gfxPSSurface.cpp
|
||||
CSRCS = cairo-xlib-utils.c
|
||||
EXTRA_DSO_LDOPTS += $(MOZ_PANGO_LIBS) $(ZLIB_LIBS)
|
||||
endif
|
||||
|
||||
|
|
|
@ -0,0 +1,594 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Novell code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Novell.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* rocallahan@novell.com
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "cairo-xlib-utils.h"
|
||||
|
||||
#include "cairo-xlib.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#define CAIRO_XLIB_DRAWING_NOTE(m) fprintf(stderr, m)
|
||||
/* #define CAIRO_XLIB_DRAWING_NOTE(m) do {} while (0) */
|
||||
|
||||
static cairo_surface_t *_get_current_target (cairo_t *cr)
|
||||
{
|
||||
cairo_surface_t *target = cairo_get_group_target (cr);
|
||||
if (target == NULL) {
|
||||
target = cairo_get_target (cr);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
static cairo_user_data_key_t pixmap_free_key;
|
||||
|
||||
typedef struct {
|
||||
Display *dpy;
|
||||
Pixmap pixmap;
|
||||
} pixmap_free_struct;
|
||||
|
||||
static void pixmap_free_func (void *data)
|
||||
{
|
||||
pixmap_free_struct *pfs = (pixmap_free_struct *) data;
|
||||
XFreePixmap (pfs->dpy, pfs->pixmap);
|
||||
free (pfs);
|
||||
}
|
||||
|
||||
/* We have three basic strategies available:
|
||||
1) 'direct': cr targets an xlib surface, and other conditions are met: we can
|
||||
pass the underlying drawable directly to the callback
|
||||
2) 'opaque': the image is opaque: we can create a temporary cairo xlib surface,
|
||||
pass its underlying drawable to the callback, and paint the result
|
||||
using cairo
|
||||
3) 'default': create a temporary cairo xlib surface, fill with black, pass its
|
||||
underlying drawable to the callback, copy the results to a cairo
|
||||
image surface, repeat with a white background, update the on-black
|
||||
image alpha values by comparing the two images, then paint the on-black
|
||||
image using cairo
|
||||
Sure would be nice to have an X extension to do 3 for us on the server...
|
||||
*/
|
||||
|
||||
static cairo_bool_t
|
||||
_convert_coord_to_short (double coord, short *v)
|
||||
{
|
||||
*v = (short)coord;
|
||||
/* XXX allow some tolerance here? */
|
||||
return *v == coord;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
_convert_coord_to_unsigned_short (double coord, unsigned short *v)
|
||||
{
|
||||
*v = (unsigned short)coord;
|
||||
/* XXX allow some tolerance here? */
|
||||
return *v == coord;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
_intersect_interval (double a_begin, double a_end, double b_begin, double b_end,
|
||||
double *out_begin, double *out_end)
|
||||
{
|
||||
*out_begin = a_begin;
|
||||
if (*out_begin < b_begin) {
|
||||
*out_begin = b_begin;
|
||||
}
|
||||
*out_end = a_end;
|
||||
if (*out_end > b_end) {
|
||||
*out_end = b_end;
|
||||
}
|
||||
return *out_begin < *out_end;
|
||||
}
|
||||
|
||||
#define MAX_STATIC_CLIP_RECTANGLES 50
|
||||
static cairo_bool_t
|
||||
_get_rectangular_clip (cairo_t *cr,
|
||||
double device_offset_x, double device_offset_y,
|
||||
int bounds_x, int bounds_y,
|
||||
int bounds_width, int bounds_height,
|
||||
cairo_bool_t *need_clip,
|
||||
XRectangle *rectangles, int max_rectangles,
|
||||
int *num_rectangles)
|
||||
{
|
||||
cairo_clip_rect_t clips[MAX_STATIC_CLIP_RECTANGLES];
|
||||
int count;
|
||||
int i;
|
||||
double b_x = bounds_x;
|
||||
double b_y = bounds_y;
|
||||
double b_x_most = bounds_x + bounds_width;
|
||||
double b_y_most = bounds_y + bounds_height;
|
||||
int rect_count = 0;
|
||||
|
||||
if (!cairo_has_clip (cr)) {
|
||||
*need_clip = False;
|
||||
return True;
|
||||
}
|
||||
|
||||
if (!cairo_extract_clip_rectangles (cr, MAX_STATIC_CLIP_RECTANGLES, clips, &count))
|
||||
return False;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
double intersect_x, intersect_y, intersect_x_most, intersect_y_most;
|
||||
|
||||
/* get the clip rect into surface coordinates */
|
||||
clips[i].x += device_offset_x;
|
||||
clips[i].y += device_offset_y;
|
||||
|
||||
if (b_x >= clips[i].x && b_x_most <= clips[i].x + clips[i].width &&
|
||||
b_y >= clips[i].y && b_y_most <= clips[i].y + clips[i].height) {
|
||||
/* the bounds are entirely inside the clip region so we don't need to clip. */
|
||||
*need_clip = False;
|
||||
return True;
|
||||
}
|
||||
|
||||
if (_intersect_interval (b_x, b_x_most, clips[i].x, clips[i].x + clips[i].width,
|
||||
&intersect_x, &intersect_x_most) &&
|
||||
_intersect_interval (b_y, b_y_most, clips[i].y, clips[i].y + clips[i].height,
|
||||
&intersect_y, &intersect_y_most)) {
|
||||
XRectangle *rect = &rectangles[rect_count];
|
||||
|
||||
if (rect_count >= max_rectangles)
|
||||
return False;
|
||||
|
||||
if (!_convert_coord_to_short (intersect_x, &rect->x) ||
|
||||
!_convert_coord_to_short (intersect_y, &rect->y) ||
|
||||
!_convert_coord_to_unsigned_short (intersect_x_most - intersect_x, &rect->width) ||
|
||||
!_convert_coord_to_unsigned_short (intersect_y_most - intersect_y, &rect->height))
|
||||
return False;
|
||||
|
||||
++rect_count;
|
||||
}
|
||||
}
|
||||
|
||||
*need_clip = True;
|
||||
*num_rectangles = rect_count;
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try the direct path.
|
||||
* @param status the status returned by the callback, if we took the direct path
|
||||
* @return True if we took the direct path
|
||||
*/
|
||||
static cairo_bool_t
|
||||
_draw_with_xlib_direct (cairo_t *cr,
|
||||
Display *default_display,
|
||||
cairo_xlib_drawing_callback callback,
|
||||
void *closure,
|
||||
int bounds_width, int bounds_height,
|
||||
cairo_xlib_drawing_support_t capabilities)
|
||||
{
|
||||
cairo_surface_t *target = _get_current_target (cr);
|
||||
Drawable d = cairo_xlib_surface_get_drawable (target);
|
||||
cairo_matrix_t matrix;
|
||||
short offset_x, offset_y;
|
||||
cairo_bool_t needs_clip;
|
||||
XRectangle rectangles[MAX_STATIC_CLIP_RECTANGLES];
|
||||
int rect_count;
|
||||
double device_offset_x, device_offset_y;
|
||||
int max_rectangles;
|
||||
Display *dpy;
|
||||
Visual *visual;
|
||||
|
||||
cairo_get_matrix (cr, &matrix);
|
||||
cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
|
||||
|
||||
/* Check that the matrix is a pure translation */
|
||||
/* XXX test some approximation to == 1.0 here? */
|
||||
if (matrix.xx != 1.0 || matrix.yy != 1.0 || matrix.xy != 0.0 || matrix.yx != 0.0) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: matrix not a pure translation\n");
|
||||
return False;
|
||||
}
|
||||
/* Check that the matrix translation offsets (adjusted for
|
||||
device offset) are integers */
|
||||
if (!_convert_coord_to_short (matrix.x0 + device_offset_x, &offset_x) ||
|
||||
!_convert_coord_to_short (matrix.y0 + device_offset_y, &offset_y)) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: non-integer offset\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
max_rectangles = 0;
|
||||
if (capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT) {
|
||||
max_rectangles = 1;
|
||||
}
|
||||
if (capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST) {
|
||||
max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
|
||||
}
|
||||
|
||||
/* Check that the clip is rectangular and aligned on unit boundaries */
|
||||
if (!_get_rectangular_clip (cr, device_offset_x, device_offset_y,
|
||||
offset_x, offset_y, bounds_width, bounds_height,
|
||||
&needs_clip,
|
||||
rectangles, max_rectangles, &rect_count)) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: unsupported clip\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Stop now if everything is clipped out */
|
||||
if (needs_clip && rect_count == 0) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING FAST PATH: all clipped\n");
|
||||
return True;
|
||||
}
|
||||
|
||||
/* Check that the operator is OVER */
|
||||
if (cairo_get_operator (cr) != CAIRO_OPERATOR_OVER) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: non-OVER operator\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Check that the offset is supported */
|
||||
if (!(capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET) &&
|
||||
(offset_x != 0 || offset_y != 0))
|
||||
return False;
|
||||
|
||||
/* Check that the target surface is an xlib surface. Do this late because
|
||||
we might complete early above when when the object to be drawn is
|
||||
completely clipped out. */
|
||||
if (!d) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: non-X surface\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Check that the display is supported */
|
||||
dpy = cairo_xlib_surface_get_display (target);
|
||||
if (!(capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_DISPLAY) &&
|
||||
dpy != default_display) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: non-default display\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Check that the visual is supported */
|
||||
visual = cairo_xlib_surface_get_visual (target);
|
||||
if (!(capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL) &&
|
||||
DefaultVisual (dpy, DefaultScreen (dpy)) != visual) {
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING SLOW PATH: non-default visual\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
/* we're good to go! */
|
||||
CAIRO_XLIB_DRAWING_NOTE("TAKING FAST PATH\n");
|
||||
cairo_surface_flush (target);
|
||||
callback (closure, dpy, d, visual, offset_x, offset_y, rectangles,
|
||||
needs_clip ? rect_count : 0);
|
||||
cairo_surface_mark_dirty (target);
|
||||
return True;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
_create_temp_xlib_surface (cairo_t *cr, Display *dpy, int width, int height,
|
||||
cairo_xlib_drawing_support_t capabilities)
|
||||
{
|
||||
/* base the temp surface on the *screen* surface, not any intermediate
|
||||
* group surface, because the screen surface is more likely to have
|
||||
* characteristics that the xlib-using code is likely to be happy with */
|
||||
cairo_surface_t *target = cairo_get_target (cr);
|
||||
Drawable target_drawable = cairo_xlib_surface_get_drawable (target);
|
||||
|
||||
int screen_index = DefaultScreen (dpy);
|
||||
Drawable drawable = RootWindow (dpy, screen_index);
|
||||
Screen *screen = DefaultScreenOfDisplay (dpy);
|
||||
Visual *visual = DefaultVisual (dpy, screen_index);
|
||||
int depth = DefaultDepth (dpy, screen_index);
|
||||
|
||||
Pixmap pixmap;
|
||||
cairo_surface_t *result;
|
||||
pixmap_free_struct *pfs;
|
||||
|
||||
/* make the temporary surface match target_drawable to the extent supported
|
||||
by the native rendering code */
|
||||
if (target_drawable) {
|
||||
Display *target_display = cairo_xlib_surface_get_display (target);
|
||||
Visual *target_visual = cairo_xlib_surface_get_visual (target);
|
||||
if ((target_display == dpy ||
|
||||
(capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_DISPLAY)) &&
|
||||
(target_visual == visual ||
|
||||
(capabilities & CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL))) {
|
||||
drawable = target_drawable;
|
||||
dpy = target_display;
|
||||
visual = target_visual;
|
||||
screen = cairo_xlib_surface_get_screen (target);
|
||||
depth = cairo_xlib_surface_get_depth (target);
|
||||
}
|
||||
}
|
||||
|
||||
pfs = malloc (sizeof(pixmap_free_struct));
|
||||
if (pfs == NULL)
|
||||
return NULL;
|
||||
|
||||
pixmap = XCreatePixmap (dpy, drawable, width, height, depth);
|
||||
if (!pixmap) {
|
||||
free (pfs);
|
||||
return NULL;
|
||||
}
|
||||
pfs->dpy = dpy;
|
||||
pfs->pixmap = pixmap;
|
||||
|
||||
result = cairo_xlib_surface_create (dpy, pixmap, visual, width, height);
|
||||
if (cairo_surface_status (result) != CAIRO_STATUS_SUCCESS) {
|
||||
pixmap_free_func (pfs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cairo_surface_set_user_data (result, &pixmap_free_key, pfs, pixmap_free_func);
|
||||
return result;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
_draw_onto_temp_xlib_surface (cairo_surface_t *temp_xlib_surface,
|
||||
cairo_xlib_drawing_callback callback,
|
||||
void *closure,
|
||||
double background_gray_value)
|
||||
{
|
||||
Drawable d = cairo_xlib_surface_get_drawable (temp_xlib_surface);
|
||||
Display *dpy = cairo_xlib_surface_get_display (temp_xlib_surface);
|
||||
Visual *visual = cairo_xlib_surface_get_visual (temp_xlib_surface);
|
||||
cairo_bool_t result;
|
||||
cairo_t *cr = cairo_create (temp_xlib_surface);
|
||||
cairo_set_source_rgb (cr, background_gray_value, background_gray_value,
|
||||
background_gray_value);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (cr);
|
||||
cairo_destroy (cr);
|
||||
|
||||
cairo_surface_flush (temp_xlib_surface);
|
||||
/* no clipping is needed because the callback can't draw outside the native
|
||||
surface anyway */
|
||||
result = callback (closure, dpy, d, visual, 0, 0, NULL, 0);
|
||||
cairo_surface_mark_dirty (temp_xlib_surface);
|
||||
return result;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
_copy_xlib_surface_to_image (cairo_surface_t *temp_xlib_surface,
|
||||
cairo_format_t format,
|
||||
int width, int height,
|
||||
unsigned char **data_out)
|
||||
{
|
||||
unsigned char *data;
|
||||
cairo_surface_t *result;
|
||||
cairo_t *cr;
|
||||
|
||||
*data_out = data = (unsigned char*)malloc (width*height*4);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
result = cairo_image_surface_create_for_data (data, format, width, height, width*4);
|
||||
cr = cairo_create (result);
|
||||
cairo_set_source_surface (cr, temp_xlib_surface, 0, 0);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (cr);
|
||||
cairo_destroy (cr);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define SET_ALPHA(v, a) (((v) & ~(0xFF << 24)) | ((a) << 24))
|
||||
#define GREEN_OF(v) (((v) >> 8) & 0xFF)
|
||||
|
||||
/**
|
||||
* Given the RGB data for two image surfaces, one a source image composited
|
||||
* with OVER onto a black background, and one a source image composited with
|
||||
* OVER onto a white background, reconstruct the original image data into
|
||||
* black_data.
|
||||
*
|
||||
* Consider a single color channel and a given pixel. Suppose the original
|
||||
* premultiplied color value was C and the alpha value was A. Let the final
|
||||
* on-black color be B and the final on-white color be W. All values range
|
||||
* over 0-255.
|
||||
* Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get
|
||||
* A=255 - (W - C). Therefore it suffices to leave the black_data color
|
||||
* data alone and set the alpha values using that simple formula. It shouldn't
|
||||
* matter what color channel we pick for the alpha computation, but we'll
|
||||
* pick green because if we went through a color channel downsample the green
|
||||
* bits are likely to be the most accurate.
|
||||
*/
|
||||
static void
|
||||
_compute_alpha_values (uint32_t *black_data,
|
||||
uint32_t *white_data,
|
||||
int width, int height,
|
||||
cairo_xlib_drawing_result_t *analysis)
|
||||
{
|
||||
int num_pixels = width*height;
|
||||
int i;
|
||||
uint32_t first;
|
||||
uint32_t deltas = 0;
|
||||
unsigned char first_alpha;
|
||||
|
||||
if (num_pixels == 0) {
|
||||
if (analysis) {
|
||||
analysis->uniform_alpha = True;
|
||||
analysis->uniform_color = True;
|
||||
/* whatever we put here will be true */
|
||||
analysis->alpha = 1.0;
|
||||
analysis->r = analysis->g = analysis->b = 0.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
first_alpha = 255 - (GREEN_OF(*white_data) - GREEN_OF(*black_data));
|
||||
/* set the alpha value of 'first' */
|
||||
first = SET_ALPHA(*black_data, first_alpha);
|
||||
|
||||
for (i = 0; i < num_pixels; ++i) {
|
||||
uint32_t black = *black_data;
|
||||
uint32_t white = *white_data;
|
||||
unsigned char pixel_alpha = 255 - (GREEN_OF(white) - GREEN_OF(black));
|
||||
|
||||
black = SET_ALPHA(black, pixel_alpha);
|
||||
*black_data = black;
|
||||
deltas |= (first ^ black);
|
||||
|
||||
black_data++;
|
||||
white_data++;
|
||||
}
|
||||
|
||||
if (analysis) {
|
||||
analysis->uniform_alpha = (deltas >> 24) == 0;
|
||||
if (analysis->uniform_alpha) {
|
||||
analysis->alpha = first_alpha/255.0;
|
||||
/* we only set uniform_color when the alpha is already uniform.
|
||||
it's only useful in that case ... and if the alpha was nonuniform
|
||||
then computing whether the color is uniform would require unpremultiplying
|
||||
every pixel */
|
||||
analysis->uniform_color = (deltas & ~(0xFF << 24)) == 0;
|
||||
if (analysis->uniform_color) {
|
||||
if (first_alpha == 0) {
|
||||
/* can't unpremultiply, this is OK */
|
||||
analysis->r = analysis->g = analysis->b = 0.0;
|
||||
} else {
|
||||
double d_first_alpha = first_alpha;
|
||||
analysis->r = (first & 0xFF)/d_first_alpha;
|
||||
analysis->g = ((first >> 8) & 0xFF)/d_first_alpha;
|
||||
analysis->b = ((first >> 16) & 0xFF)/d_first_alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cairo_draw_with_xlib (cairo_t *cr,
|
||||
cairo_xlib_drawing_callback callback,
|
||||
void *closure,
|
||||
Display *dpy,
|
||||
unsigned int width, unsigned int height,
|
||||
cairo_xlib_drawing_opacity_t is_opaque,
|
||||
cairo_xlib_drawing_support_t capabilities,
|
||||
cairo_xlib_drawing_result_t *result)
|
||||
{
|
||||
cairo_surface_t *temp_xlib_surface;
|
||||
cairo_surface_t *black_image_surface;
|
||||
cairo_surface_t *white_image_surface;
|
||||
unsigned char *black_data;
|
||||
unsigned char *white_data;
|
||||
|
||||
if (result) {
|
||||
result->surface = NULL;
|
||||
result->uniform_alpha = False;
|
||||
result->uniform_color = False;
|
||||
}
|
||||
|
||||
/* exit early if there's no work to do. This is actually important
|
||||
because we'll die with an X error if we try to create an empty temporary
|
||||
pixmap */
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
if (_draw_with_xlib_direct (cr, dpy, callback, closure, width, height,
|
||||
capabilities))
|
||||
return;
|
||||
|
||||
temp_xlib_surface = _create_temp_xlib_surface (cr, dpy, width, height,
|
||||
capabilities);
|
||||
if (temp_xlib_surface == NULL)
|
||||
return;
|
||||
/* update 'dpy' to refer to the display that actually got used. Might
|
||||
be different now, if cr was referring to an xlib surface on a different display */
|
||||
dpy = cairo_xlib_surface_get_display (temp_xlib_surface);
|
||||
|
||||
if (!_draw_onto_temp_xlib_surface (temp_xlib_surface, callback, closure, 0.0)) {
|
||||
cairo_surface_destroy (temp_xlib_surface);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_opaque == CAIRO_XLIB_DRAWING_OPAQUE) {
|
||||
cairo_set_source_surface (cr, temp_xlib_surface, 0.0, 0.0);
|
||||
cairo_paint (cr);
|
||||
if (result) {
|
||||
result->surface = temp_xlib_surface;
|
||||
/* fill in the result with what we know, which is really just what our
|
||||
assumption was */
|
||||
result->uniform_alpha = True;
|
||||
result->alpha = 1.0;
|
||||
} else {
|
||||
cairo_surface_destroy (temp_xlib_surface);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
black_image_surface =
|
||||
_copy_xlib_surface_to_image (temp_xlib_surface, CAIRO_FORMAT_ARGB32,
|
||||
width, height, &black_data);
|
||||
|
||||
_draw_onto_temp_xlib_surface (temp_xlib_surface, callback, closure, 1.0);
|
||||
white_image_surface =
|
||||
_copy_xlib_surface_to_image (temp_xlib_surface, CAIRO_FORMAT_RGB24,
|
||||
width, height, &white_data);
|
||||
|
||||
cairo_surface_destroy (temp_xlib_surface);
|
||||
|
||||
if (black_image_surface && white_image_surface &&
|
||||
cairo_surface_status (black_image_surface) == CAIRO_STATUS_SUCCESS &&
|
||||
cairo_surface_status (white_image_surface) == CAIRO_STATUS_SUCCESS &&
|
||||
black_data != NULL && white_data != NULL) {
|
||||
cairo_surface_flush (black_image_surface);
|
||||
cairo_surface_flush (white_image_surface);
|
||||
_compute_alpha_values ((uint32_t*)black_data, (uint32_t*)white_data, width, height, result);
|
||||
cairo_surface_mark_dirty (black_image_surface);
|
||||
|
||||
cairo_set_source_surface (cr, black_image_surface, 0.0, 0.0);
|
||||
/* if the caller wants to retrieve the rendered image, put it into
|
||||
a 'similar' surface, and use that as the source for the drawing right
|
||||
now. This means we always return a surface similar to the surface
|
||||
used for 'cr', which is ideal if it's going to be cached and reused.
|
||||
We do not return an image if the result has uniform color and alpha. */
|
||||
if (result && (!result->uniform_alpha || !result->uniform_color)) {
|
||||
cairo_surface_t *target = _get_current_target (cr);
|
||||
cairo_surface_t *similar_surface =
|
||||
cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR_ALPHA,
|
||||
width, height);
|
||||
cairo_t *copy_cr = cairo_create (similar_surface);
|
||||
cairo_set_source_surface (copy_cr, black_image_surface, 0.0, 0.0);
|
||||
cairo_set_operator (copy_cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint (copy_cr);
|
||||
cairo_destroy (copy_cr);
|
||||
|
||||
cairo_set_source_surface (cr, similar_surface, 0.0, 0.0);
|
||||
|
||||
result->surface = similar_surface;
|
||||
}
|
||||
|
||||
cairo_paint (cr);
|
||||
}
|
||||
|
||||
if (black_image_surface) {
|
||||
cairo_surface_destroy (black_image_surface);
|
||||
}
|
||||
if (white_image_surface) {
|
||||
cairo_surface_destroy (white_image_surface);
|
||||
}
|
||||
free (black_data);
|
||||
free (white_data);
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Novell code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Novell.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* rocallahan@novell.com
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef CAIROXLIBUTILS_H_
|
||||
#define CAIROXLIBUTILS_H_
|
||||
|
||||
#include "cairo.h"
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
CAIRO_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* This callback encapsulates Xlib-based rendering. We assume that the
|
||||
* execution of the callback is equivalent to compositing some RGBA image of
|
||||
* size (bounds_width, bounds_height) onto the drawable at offset (offset_x,
|
||||
* offset_y), clipped to the union of the clip_rects if num_rects is greater
|
||||
* than zero. This includes the assumption that the same RGBA image
|
||||
* is composited if you call the callback multiple times with the same closure,
|
||||
* display and visual during a single cairo_draw_with_xlib call.
|
||||
*
|
||||
* @return True on success, False on non-recoverable error
|
||||
*/
|
||||
typedef cairo_bool_t (* cairo_xlib_drawing_callback)
|
||||
(void *closure,
|
||||
Display *dpy,
|
||||
Drawable drawable,
|
||||
Visual *visual,
|
||||
short offset_x, short offset_y,
|
||||
XRectangle* clip_rects, unsigned int num_rects);
|
||||
|
||||
/**
|
||||
* This structure captures the result of the native drawing, in case the
|
||||
* caller may wish to reapply the drawing efficiently later.
|
||||
*/
|
||||
typedef struct {
|
||||
cairo_surface_t *surface;
|
||||
cairo_bool_t uniform_alpha;
|
||||
cairo_bool_t uniform_color;
|
||||
double alpha; /* valid only if uniform_alpha is TRUE */
|
||||
double r, g, b; /* valid only if uniform_color is TRUE */
|
||||
} cairo_xlib_drawing_result_t;
|
||||
|
||||
/**
|
||||
* This type specifies whether the native drawing callback draws all pixels
|
||||
* in its bounds opaquely, independent of the contents of the target drawable,
|
||||
* or whether it leaves pixels transparent/translucent or depends on the
|
||||
* existing contents of the target drawable in some way.
|
||||
*/
|
||||
typedef enum _cairo_xlib_drawing_opacity {
|
||||
CAIRO_XLIB_DRAWING_OPAQUE,
|
||||
CAIRO_XLIB_DRAWING_TRANSPARENT
|
||||
} cairo_xlib_drawing_opacity_t;
|
||||
|
||||
/**
|
||||
* This type encodes the capabilities of the native drawing callback.
|
||||
*
|
||||
* If CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET is set, 'offset_x' and 'offset_y'
|
||||
* can be nonzero in the call to the callback; otherwise they will be zero.
|
||||
*
|
||||
* If CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT is set, then 'num_rects' can be
|
||||
* zero or one in the call to the callback. If
|
||||
* CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST is set, then 'num_rects' can be
|
||||
* anything in the call to the callback. Otherwise 'num_rects' will be zero.
|
||||
* Do not set both of these values.
|
||||
*
|
||||
* If CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_DISPLAY is set, then 'dpy' can be
|
||||
* any display, otherwise it will be the display passed into
|
||||
* cairo_draw_with_xlib.
|
||||
*
|
||||
* If CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL is set, then 'visual' can be
|
||||
* any visual, otherwise it will be equal to
|
||||
* DefaultVisual (dpy, DefaultScreen (dpy)).
|
||||
*/
|
||||
typedef enum {
|
||||
CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET = 0x01,
|
||||
CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT = 0x02,
|
||||
CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST = 0x04,
|
||||
CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_DISPLAY = 0x08,
|
||||
CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL = 0x10
|
||||
} cairo_xlib_drawing_support_t;
|
||||
|
||||
/**
|
||||
* Draw Xlib output into any cairo context. All cairo transforms and effects
|
||||
* are honored, including the current operator. This is equivalent to a
|
||||
* cairo_set_source_surface and then cairo_paint.
|
||||
* @param cr the context to draw into
|
||||
* @param callback the code to perform Xlib rendering
|
||||
* @param closure associated data
|
||||
* @param dpy an X display to use in case the cairo context has no associated X display
|
||||
* @param width the width of the putative image drawn by the callback
|
||||
* @param height the height of the putative image drawn by the callback
|
||||
* @param is_opaque set to CAIRO_XLIB_DRAWING_IS_OPAQUE to indicate
|
||||
* that all alpha values of the putative image will be 1.0; the pixels drawn into
|
||||
* the Drawable must not depend on the prior contents of the Drawable
|
||||
* in any way
|
||||
* @param capabilities the capabilities of the callback as described above.
|
||||
* @param result if non-NULL, we *may* fill in the struct with information about
|
||||
* the rendered image. 'surface' may be filled in with a surface representing
|
||||
* the image, similar to the target of 'cr'. If 'uniform_alpha' is True then
|
||||
* every pixel of the image has the same alpha value 'alpha'. If
|
||||
* 'uniform_color' is True then every pixel of the image has the same RGB
|
||||
* color (r, g, b). If the image has uniform color and alpha (or alpha is zero,
|
||||
* in which case the color is always uniform) then we won't bother returning
|
||||
* a surface for it.
|
||||
*/
|
||||
void cairo_draw_with_xlib (cairo_t *cr,
|
||||
cairo_xlib_drawing_callback callback,
|
||||
void *closure,
|
||||
Display *dpy,
|
||||
unsigned int width, unsigned int height,
|
||||
cairo_xlib_drawing_opacity_t is_opaque,
|
||||
cairo_xlib_drawing_support_t capabilities,
|
||||
cairo_xlib_drawing_result_t *result);
|
||||
|
||||
CAIRO_END_DECLS
|
||||
|
||||
#endif /*CAIROXLIBUTILS_H_*/
|
|
@ -0,0 +1,121 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Novell code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Novell.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* rocallahan@novell.com
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "gfxXlibNativeRenderer.h"
|
||||
#include "gfxContext.h"
|
||||
|
||||
#include "cairo-xlib-utils.h"
|
||||
|
||||
typedef struct {
|
||||
gfxXlibNativeRenderer* mRenderer;
|
||||
nsresult mRV;
|
||||
} NativeRenderingClosure;
|
||||
|
||||
static cairo_bool_t
|
||||
NativeRendering(void *closure,
|
||||
Display *dpy,
|
||||
Drawable drawable,
|
||||
Visual *visual,
|
||||
short offset_x, short offset_y,
|
||||
XRectangle* rectangles, unsigned int num_rects)
|
||||
{
|
||||
NativeRenderingClosure* cl = (NativeRenderingClosure*)closure;
|
||||
nsresult rv = cl->mRenderer->
|
||||
NativeDraw(dpy, drawable, visual, offset_x, offset_y,
|
||||
rectangles, num_rects);
|
||||
cl->mRV = rv;
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxXlibNativeRenderer::Draw(Display* dpy, gfxContext* ctx, int width, int height,
|
||||
PRUint32 flags, DrawOutput* output)
|
||||
{
|
||||
NativeRenderingClosure closure = { this, NS_OK };
|
||||
cairo_xlib_drawing_result_t result;
|
||||
|
||||
if (output) {
|
||||
output->mSurface = NULL;
|
||||
output->mUniformAlpha = PR_FALSE;
|
||||
output->mUniformColor = PR_FALSE;
|
||||
}
|
||||
|
||||
int cairoFlags = 0;
|
||||
if (flags & DRAW_SUPPORTS_OFFSET) {
|
||||
cairoFlags |= CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET;
|
||||
}
|
||||
if (flags & DRAW_SUPPORTS_CLIP_RECT) {
|
||||
cairoFlags |= CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT;
|
||||
}
|
||||
if (flags & DRAW_SUPPORTS_CLIP_LIST) {
|
||||
cairoFlags |= CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST;
|
||||
}
|
||||
if (flags & DRAW_SUPPORTS_ALTERNATE_DISPLAY) {
|
||||
cairoFlags |= CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_DISPLAY;
|
||||
}
|
||||
if (flags & DRAW_SUPPORTS_NONDEFAULT_VISUAL) {
|
||||
cairoFlags |= CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL;
|
||||
}
|
||||
cairo_draw_with_xlib(ctx->GetCairo(), NativeRendering, &closure, dpy,
|
||||
width, height,
|
||||
(flags & DRAW_IS_OPAQUE) ? CAIRO_XLIB_DRAWING_OPAQUE
|
||||
: CAIRO_XLIB_DRAWING_TRANSPARENT,
|
||||
(cairo_xlib_drawing_support_t)cairoFlags,
|
||||
output ? &result : NULL);
|
||||
if (NS_FAILED(closure.mRV)) {
|
||||
if (result.surface) {
|
||||
cairo_surface_destroy (result.surface);
|
||||
}
|
||||
return closure.mRV;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
if (result.surface) {
|
||||
output->mSurface = new gfxUnknownSurface(result.surface);
|
||||
if (!output->mSurface) {
|
||||
cairo_surface_destroy (result.surface);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
output->mUniformAlpha = result.uniform_alpha;
|
||||
output->mUniformColor = result.uniform_color;
|
||||
output->mColor = gfxRGBA(result.r, result.g, result.b, result.alpha);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
|
@ -56,14 +56,15 @@
|
|||
#include "nsIMenuFrame.h"
|
||||
#include "nsIMenuParent.h"
|
||||
#include "prlink.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
|
||||
#include <gdk/gdkprivate.h>
|
||||
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatformGtk.h"
|
||||
#include "gfxXlibNativeRenderer.h"
|
||||
#endif
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsNativeThemeGTK, nsITheme, nsIObserver)
|
||||
|
@ -204,15 +205,21 @@ nsNativeThemeGTK::GetGtkWidgetAndState(PRUint8 aWidgetType, nsIFrame* aFrame,
|
|||
// widgets, so don't adjust stateFrame here.
|
||||
aFrame = aFrame->GetParent();
|
||||
}
|
||||
} else if (content->Tag() == mInputAtom) {
|
||||
atom = mInputCheckedAtom;
|
||||
}
|
||||
|
||||
if (aWidgetFlags) {
|
||||
if (!atom) {
|
||||
atom = (aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_CHECKBOX_LABEL) ? mCheckedAtom : mSelectedAtom;
|
||||
if (aWidgetFlags) {
|
||||
if (!atom) {
|
||||
atom = (aWidgetType == NS_THEME_CHECKBOX || aWidgetType == NS_THEME_CHECKBOX_LABEL) ? mCheckedAtom : mSelectedAtom;
|
||||
}
|
||||
*aWidgetFlags = CheckBooleanAttr(aFrame, atom);
|
||||
}
|
||||
} else {
|
||||
if (aWidgetFlags) {
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(content));
|
||||
if (inputElt) {
|
||||
PRBool isHTMLChecked;
|
||||
inputElt->GetChecked(&isHTMLChecked);
|
||||
*aWidgetFlags = isHTMLChecked;
|
||||
}
|
||||
}
|
||||
*aWidgetFlags = CheckBooleanAttr(aFrame, atom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,6 +422,105 @@ NativeThemeErrorHandler(Display* dpy, XErrorEvent* error) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MOZ_CAIRO_GFX
|
||||
class ThemeRenderer : public gfxXlibNativeRenderer {
|
||||
public:
|
||||
ThemeRenderer(GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
|
||||
gint aFlags, const GdkRectangle& aGDKRect,
|
||||
const GdkRectangle& aGDKClip)
|
||||
: mState(aState), mGTKWidgetType(aGTKWidgetType), mFlags(aFlags),
|
||||
mGDKRect(aGDKRect), mGDKClip(aGDKClip) {}
|
||||
nsresult NativeDraw(Display* dpy, Drawable drawable, Visual* visual,
|
||||
short offsetX, short offsetY,
|
||||
XRectangle* clipRects, PRUint32 numClipRects);
|
||||
private:
|
||||
GtkWidgetState mState;
|
||||
GtkThemeWidgetType mGTKWidgetType;
|
||||
gint mFlags;
|
||||
GdkWindow* mWindow;
|
||||
const GdkRectangle& mGDKRect;
|
||||
const GdkRectangle& mGDKClip;
|
||||
};
|
||||
|
||||
nsresult
|
||||
ThemeRenderer::NativeDraw(Display* dpy, Drawable drawable, Visual* visual,
|
||||
short offsetX, short offsetY,
|
||||
XRectangle* clipRects, PRUint32 numClipRects)
|
||||
{
|
||||
GdkRectangle gdk_rect = mGDKRect;
|
||||
gdk_rect.x += offsetX;
|
||||
gdk_rect.y += offsetY;
|
||||
|
||||
GdkRectangle gdk_clip = mGDKClip;
|
||||
gdk_clip.x += offsetX;
|
||||
gdk_clip.y += offsetY;
|
||||
if (numClipRects > 0) {
|
||||
NS_ASSERTION(numClipRects == 1, "gfxXlibNativeRenderer cheated on us");
|
||||
GdkRectangle extraClip = {clipRects->x, clipRects->y,
|
||||
clipRects->width, clipRects->height};
|
||||
if (!gdk_rectangle_intersect(&gdk_clip, &extraClip, &gdk_clip))
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GdkDisplay* gdkDpy = gdk_x11_lookup_xdisplay(dpy);
|
||||
if (!gdkDpy)
|
||||
return NS_ERROR_FAILURE;
|
||||
GdkPixmap* gdkPixmap = gdk_pixmap_lookup_for_display(gdkDpy, drawable);
|
||||
if (gdkPixmap) {
|
||||
g_object_ref(G_OBJECT(gdkPixmap));
|
||||
} else {
|
||||
gdkPixmap = gdk_pixmap_foreign_new_for_display(gdkDpy, drawable);
|
||||
if (!gdkPixmap)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (visual) {
|
||||
GdkScreen* gdkScreen = gdk_display_get_default_screen(gdkDpy);
|
||||
GdkVisual* gdkVisual = gdk_x11_screen_lookup_visual(gdkScreen, visual->visualid);
|
||||
Colormap cmap = DefaultScreenOfDisplay(dpy)->cmap;
|
||||
GdkColormap* colormap =
|
||||
gdk_x11_colormap_foreign_new(gdkVisual, cmap);
|
||||
|
||||
gdk_drawable_set_colormap(gdkPixmap, colormap);
|
||||
}
|
||||
}
|
||||
|
||||
moz_gtk_widget_paint(mGTKWidgetType, gdkPixmap, &gdk_rect, &gdk_clip, &mState,
|
||||
mFlags);
|
||||
|
||||
g_object_unref(G_OBJECT(gdkPixmap));
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PRBool
|
||||
GetExtraSizeForWidget(PRUint8 aWidgetType, nsIntMargin* aExtra)
|
||||
{
|
||||
*aExtra = nsIntMargin(0,0,0,0);
|
||||
// Allow an extra one pixel above and below the thumb for certain
|
||||
// GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
|
||||
// see moz_gtk_scrollbar_thumb_paint in gtk2drawing.c
|
||||
switch (aWidgetType) {
|
||||
case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
|
||||
aExtra->top = aExtra->bottom = 1;
|
||||
return PR_TRUE;
|
||||
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
|
||||
aExtra->left = aExtra->right = 1;
|
||||
return PR_TRUE;
|
||||
default:
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GdkRectangle
|
||||
ConvertToGdkRect(const nsRect &aRect, float aT2P)
|
||||
{
|
||||
GdkRectangle gdk_rect;
|
||||
gdk_rect.x = NSToCoordRound(aRect.x * aT2P);
|
||||
gdk_rect.y = NSToCoordRound(aRect.y * aT2P);
|
||||
gdk_rect.width = NSToCoordRound(aRect.XMost() * aT2P) - gdk_rect.x;
|
||||
gdk_rect.height = NSToCoordRound(aRect.YMost() * aT2P) - gdk_rect.y;
|
||||
return gdk_rect;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
|
||||
nsIFrame* aFrame,
|
||||
|
@ -428,7 +534,7 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
|
|||
if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state,
|
||||
&flags))
|
||||
return NS_OK;
|
||||
|
||||
|
||||
#ifndef MOZ_CAIRO_GFX
|
||||
GdkWindow* window = NS_STATIC_CAST(GdkWindow*,
|
||||
aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_GDK_DRAWABLE));
|
||||
|
@ -478,97 +584,26 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
|
|||
}
|
||||
}
|
||||
#else
|
||||
// Thebes gfx rendering; if we can get a GdkWindow and if we don't have any transform
|
||||
// other than a translation, then just render directly. Otherwise, we can't do
|
||||
// native themes yet, until we figure out how to intercept gtk rendering
|
||||
|
||||
nsRefPtr<gfxContext> ctx = (gfxContext*) aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
|
||||
|
||||
nsIDeviceContext *dctx = nsnull;
|
||||
aContext->GetDeviceContext(dctx);
|
||||
double t2p = dctx->AppUnitsToDevUnits();
|
||||
double p2t = dctx->DevUnitsToAppUnits();
|
||||
|
||||
GdkRectangle gdk_rect = { aRect.x * t2p,
|
||||
aRect.y * t2p,
|
||||
aRect.width * t2p,
|
||||
aRect.height * t2p };
|
||||
GdkRectangle gdk_clip = { aClipRect.x * t2p,
|
||||
aClipRect.y * t2p,
|
||||
aClipRect.width * t2p,
|
||||
aClipRect.height * t2p };
|
||||
// This is the rectangle that will actually be drawn, in appunits
|
||||
nsRect drawingRect(aClipRect);
|
||||
nsIntMargin extraSize;
|
||||
// remember whether the widget might draw outside its given clip rect
|
||||
PRBool overDrawing = GetExtraSizeForWidget(aWidgetType, &extraSize);
|
||||
// inflate drawing rect to account for the overdraw
|
||||
nsMargin extraSizeInTwips(NSToCoordRound(extraSize.left*p2t),
|
||||
NSToCoordRound(extraSize.top*p2t),
|
||||
NSToCoordRound(extraSize.right*p2t),
|
||||
NSToCoordRound(extraSize.bottom*p2t));
|
||||
drawingRect.Inflate(extraSizeInTwips);
|
||||
|
||||
|
||||
GdkWindow* gdkwin = (GdkWindow*) gfxPlatformGtk::GetPlatform()->GetSurfaceGdkDrawable(ctx->CurrentSurface());
|
||||
GdkDrawable *target_drawable = nsnull;
|
||||
PRBool needs_thebes_composite = PR_FALSE;
|
||||
|
||||
if (gdkwin && !ctx->CurrentMatrix().HasNonTranslation()) {
|
||||
// we can draw straight to the gdkwin drawable; need to offset gdk_rect/gdk_clip
|
||||
// by the current translation
|
||||
target_drawable = gdkwin;
|
||||
|
||||
gfxPoint t = ctx->CurrentMatrix().GetTranslation();
|
||||
gdk_rect.x += t.x;
|
||||
gdk_rect.y += t.y;
|
||||
gdk_clip.x += t.x;
|
||||
gdk_clip.y += t.y;
|
||||
|
||||
ctx->CurrentSurface()->Flush();
|
||||
} else {
|
||||
// we can't draw directly to the gdkwin drawable because we're being transformed;
|
||||
// so draw to our temporary pixmap and composite
|
||||
|
||||
// .. except that we can't draw to the temporary pixmap just yet, so bail
|
||||
SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType);
|
||||
RefreshWidgetWindow(aFrame);
|
||||
return NS_OK;
|
||||
|
||||
#if 0
|
||||
PRUint32 maxsz = ((PRUint32)(aRect.width < aRect.height ? (aRect.height * t2p) : (aRect.width * t2p))) + 1;
|
||||
|
||||
if (mGdkPixmapSize < maxsz) {
|
||||
if (mGdkPixmap) {
|
||||
mThebesSurface = nsnull;
|
||||
g_object_unref(mGdkPixmap);
|
||||
mGdkPixmap = nsnull;
|
||||
}
|
||||
|
||||
fprintf (stderr, "maxsz: %d\n", maxsz);
|
||||
mGdkPixmap = gdk_pixmap_new (gdk_get_default_root_window(),
|
||||
maxsz,
|
||||
maxsz,
|
||||
32);
|
||||
|
||||
mThebesSurface = new gfxXlibSurface (gdk_x11_get_default_xdisplay(),
|
||||
GDK_PIXMAP_XID(mGdkPixmap),
|
||||
gfxXlibSurface::FindRenderFormat(gdk_x11_get_default_xdisplay(),
|
||||
gfxASurface::ImageFormatARGB32),
|
||||
maxsz, maxsz);
|
||||
|
||||
mGdkPixmapSize = maxsz;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> gdkCtx = new gfxContext(mThebesSurface);
|
||||
gdkCtx->SetOperator(gfxContext::OPERATOR_CLEAR);
|
||||
gdkCtx->Paint();
|
||||
|
||||
target_drawable = mGdkPixmap;
|
||||
needs_thebes_composite = PR_TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gdk_clip.x < 0) {
|
||||
gdk_clip.width += gdk_clip.x;
|
||||
gdk_clip.x = 0;
|
||||
}
|
||||
|
||||
if (gdk_clip.y < 0) {
|
||||
gdk_clip.height += gdk_clip.y;
|
||||
gdk_clip.y = 0;
|
||||
}
|
||||
|
||||
if (gdk_clip.width < 0 || gdk_clip.height < 0)
|
||||
return NS_OK;
|
||||
// translate everything so (0,0) is the top left of the drawingRect
|
||||
nsIRenderingContext::AutoPushTranslation
|
||||
translation(aContext, drawingRect.x, drawingRect.y);
|
||||
|
||||
NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
|
||||
"Trying to render an unsafe widget!");
|
||||
|
@ -580,10 +615,37 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
|
|||
oldHandler = XSetErrorHandler(NativeThemeErrorHandler);
|
||||
}
|
||||
|
||||
// We do not support clip lists (because we can't currently tweak the actual
|
||||
// Gdk GC used), and we require the use of the default display and visual
|
||||
// because I'm afraid that otherwise the GTK theme may explode.
|
||||
PRUint32 rendererFlags = gfxXlibNativeRenderer::DRAW_SUPPORTS_OFFSET;
|
||||
if (!overDrawing) {
|
||||
rendererFlags |= gfxXlibNativeRenderer::DRAW_SUPPORTS_CLIP_RECT;
|
||||
}
|
||||
GdkRectangle gdk_rect = ConvertToGdkRect(aRect - drawingRect.TopLeft(), t2p);
|
||||
GdkRectangle gdk_clip = ConvertToGdkRect(aClipRect - drawingRect.TopLeft(), t2p);
|
||||
ThemeRenderer renderer(state, gtkWidgetType, flags, gdk_rect, gdk_clip);
|
||||
|
||||
moz_gtk_widget_paint(gtkWidgetType, target_drawable, &gdk_rect, &gdk_clip, &state,
|
||||
flags);
|
||||
|
||||
gfxContext* ctx =
|
||||
(gfxContext*)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
|
||||
gfxRect rect(0, 0, drawingRect.width*t2p, drawingRect.height*t2p);
|
||||
// Don't snap if it's a non-unit scale factor. We're going to have to take
|
||||
// slow paths then in any case.
|
||||
gfxMatrix current = ctx->CurrentMatrix();
|
||||
PRBool snapXY = ctx->UserToDevicePixelSnapped(rect) &&
|
||||
!current.HasNonTranslation();
|
||||
if (snapXY) {
|
||||
gfxMatrix translation;
|
||||
translation.Translate(rect.TopLeft());
|
||||
ctx->SetMatrix(translation);
|
||||
renderer.Draw(gdk_x11_get_default_xdisplay(), ctx,
|
||||
NSToCoordRound(rect.Width()), NSToCoordRound(rect.Height()),
|
||||
rendererFlags, nsnull);
|
||||
ctx->SetMatrix(current);
|
||||
} else {
|
||||
renderer.Draw(gdk_x11_get_default_xdisplay(), ctx,
|
||||
drawingRect.width, drawingRect.height, rendererFlags, nsnull);
|
||||
}
|
||||
|
||||
if (!safeState) {
|
||||
gdk_flush();
|
||||
|
@ -605,26 +667,7 @@ nsNativeThemeGTK::DrawWidgetBackground(nsIRenderingContext* aContext,
|
|||
SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state);
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_thebes_composite) {
|
||||
#if 0
|
||||
ctx->Save();
|
||||
ctx->NewPath();
|
||||
ctx->Translate(gfxPoint(aRect.x * t2p,
|
||||
aRect.y * t2p));
|
||||
nsRefPtr<gfxPattern> pat = new gfxPattern(mThebesSurface);
|
||||
|
||||
ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0,
|
||||
aRect.width * t2p,
|
||||
aRect.height * t2p),
|
||||
pat);
|
||||
ctx->Fill();
|
||||
ctx->Restore();
|
||||
#endif
|
||||
} else {
|
||||
ctx->CurrentSurface()->MarkDirty();
|
||||
}
|
||||
#endif /* MOZ_CAIRO_GFX */
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -692,21 +735,9 @@ nsNativeThemeGTK::GetWidgetOverflow(nsIDeviceContext* aContext,
|
|||
nsIFrame* aFrame, PRUint8 aWidgetType,
|
||||
nsRect* aResult)
|
||||
{
|
||||
nsIntMargin extraSize(0,0,0,0);
|
||||
// Allow an extra one pixel above and below the thumb for certain
|
||||
// GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
|
||||
// see moz_gtk_scrollbar_thumb_paint in gtk2drawing.c
|
||||
switch (aWidgetType) {
|
||||
case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
|
||||
extraSize.top = extraSize.bottom = 1;
|
||||
break;
|
||||
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
|
||||
extraSize.left = extraSize.right = 1;
|
||||
break;
|
||||
default:
|
||||
nsIntMargin extraSize;
|
||||
if (!GetExtraSizeForWidget(aWidgetType, &extraSize))
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
float p2t = aContext->DevUnitsToAppUnits();
|
||||
nsMargin m(NSIntPixelsToTwips(extraSize.left, p2t),
|
||||
NSIntPixelsToTwips(extraSize.top, p2t),
|
||||
|
@ -874,11 +905,13 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
|
|||
nsIFrame* aFrame,
|
||||
PRUint8 aWidgetType)
|
||||
{
|
||||
#ifndef MOZ_CAIRO_GFX
|
||||
if (aFrame) {
|
||||
// For now don't support HTML.
|
||||
// don't support HTML in non-cairo builds
|
||||
if (aFrame->GetContent()->IsContentOfType(nsIContent::eHTML))
|
||||
return PR_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType))
|
||||
return PR_FALSE;
|
||||
|
|
|
@ -228,15 +228,10 @@ static const nsModuleComponentInfo components[] =
|
|||
"@mozilla.org/gfx/screenmanager;1",
|
||||
nsScreenManagerGtkConstructor },
|
||||
#ifdef NATIVE_THEME_SUPPORT
|
||||
/* XXX temporary! Disable native theme support on linux until roc
|
||||
* lands new native theme stuff
|
||||
*/
|
||||
#ifndef MOZ_CAIRO_GFX
|
||||
{ "Native Theme Renderer",
|
||||
NS_THEMERENDERER_CID,
|
||||
"@mozilla.org/chrome/chrome-native-theme;1",
|
||||
nsNativeThemeGTKConstructor },
|
||||
#endif
|
||||
#endif
|
||||
{ "PrintSettings Service",
|
||||
NS_PRINTSETTINGSSERVICE_CID,
|
||||
|
|
Загрузка…
Ссылка в новой задаче