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:
roc+%cs.cmu.edu 2006-02-23 01:01:29 +00:00
Родитель ad5f1c79ec
Коммит 3650a89de7
9 изменённых файлов: 1152 добавлений и 142 удалений

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

@ -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,