b=580440 use native-rendering temp surfaces only as large as necessary r=roc a2.0=beltzner

--HG--
extra : rebase_source : 53788fe162ea7ef0c8b4190a08801cbd276e702b
This commit is contained in:
Karl Tomlinson 2010-07-23 10:25:19 +12:00
parent 44ee4169cd
commit 7c86e32ec8
4 changed files with 178 additions and 153 deletions

View File

@ -82,89 +82,61 @@ _convert_coord_to_int (double coord, PRInt32 *v)
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;
}
static cairo_bool_t
static PRBool
_get_rectangular_clip (cairo_t *cr,
const nsIntRect& bounds,
cairo_bool_t *need_clip,
PRBool *need_clip,
nsIntRect *rectangles, int max_rectangles,
int *num_rectangles)
{
cairo_rectangle_list_t *cliplist;
cairo_rectangle_t *clips;
int i;
double b_x = bounds.x;
double b_y = bounds.y;
double b_x_most = bounds.XMost();
double b_y_most = bounds.YMost();
int rect_count = 0;
cairo_bool_t retval = True;
PRBool retval = PR_TRUE;
cliplist = cairo_copy_clip_rectangle_list (cr);
if (cliplist->status != CAIRO_STATUS_SUCCESS) {
retval = False;
goto FINISH;
}
if (cliplist->num_rectangles == 0) {
*num_rectangles = 0;
*need_clip = True;
retval = PR_FALSE;
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-rectangular clip\n");
goto FINISH;
}
/* the clip is always in surface backend coordinates (i.e. native backend coords) */
clips = cliplist->rectangles;
for (i = 0; i < cliplist->num_rectangles; ++i) {
double intersect_x, intersect_y, intersect_x_most, intersect_y_most;
/* the clip is always in surface backend coordinates (i.e. native backend coords) */
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;
nsIntRect rect;
if (!_convert_coord_to_int (clips[i].x, &rect.x) ||
!_convert_coord_to_int (clips[i].y, &rect.y) ||
!_convert_coord_to_int (clips[i].width, &rect.width) ||
!_convert_coord_to_int (clips[i].height, &rect.height))
{
retval = PR_FALSE;
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-integer clip\n");
goto FINISH;
}
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)) {
nsIntRect *rect = &rectangles[rect_count];
if (rect_count >= max_rectangles) {
retval = False;
goto FINISH;
}
if (rect == bounds) {
/* the bounds are entirely inside the clip region so we don't need to clip. */
*need_clip = PR_FALSE;
goto FINISH;
}
if (!_convert_coord_to_int (intersect_x, &rect->x) ||
!_convert_coord_to_int (intersect_y, &rect->y) ||
!_convert_coord_to_int (intersect_x_most - intersect_x, &rect->width) ||
!_convert_coord_to_int (intersect_y_most - intersect_y, &rect->height))
{
retval = False;
goto FINISH;
}
NS_ASSERTION(bounds.Contains(rect),
"Was expecting to be clipped to bounds");
++rect_count;
if (i >= max_rectangles) {
retval = PR_FALSE;
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: unsupported clip rectangle count\n");
goto FINISH;
}
rectangles[i] = rect;
}
*need_clip = True;
*num_rectangles = rect_count;
*need_clip = PR_TRUE;
*num_rectangles = cliplist->num_rectangles;
FINISH:
cairo_rectangle_list_destroy (cliplist);
@ -179,79 +151,20 @@ FINISH:
* @return True if we took the direct path
*/
PRBool
gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize bounds,
gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize size,
PRUint32 flags,
Screen *screen, Visual *visual)
{
cairo_surface_t *target;
cairo_matrix_t matrix;
cairo_bool_t needs_clip;
nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
int rect_count;
double device_offset_x, device_offset_y;
int max_rectangles;
cairo_bool_t have_rectangular_clip;
cairo_t *cr = ctx->GetCairo();
target = cairo_get_group_target (cr);
cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
cairo_get_matrix (cr, &matrix);
/* 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) {
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: matrix not a pure translation\n");
return PR_FALSE;
}
/* Check that the matrix translation offsets (adjusted for
device offset) are integers */
nsIntPoint offset;
if (!_convert_coord_to_int (matrix.x0 + device_offset_x, &offset.x) ||
!_convert_coord_to_int (matrix.y0 + device_offset_y, &offset.y)) {
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-integer offset\n");
return PR_FALSE;
}
max_rectangles = 0;
if (flags & DRAW_SUPPORTS_CLIP_RECT) {
max_rectangles = 1;
}
if (flags & DRAW_SUPPORTS_CLIP_LIST) {
max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
}
/* Check that the clip is rectangular and aligned on unit boundaries. */
/* Temporarily set the matrix for _get_rectangular_clip. It's basically
the identity matrix, but we must adjust for the fact that our
offset-rect is in device coordinates. */
cairo_identity_matrix (cr);
cairo_translate (cr, -device_offset_x, -device_offset_y);
have_rectangular_clip =
_get_rectangular_clip (cr,
nsIntRect(offset, bounds),
&needs_clip,
rectangles, max_rectangles, &rect_count);
cairo_set_matrix (cr, &matrix);
if (!have_rectangular_clip) {
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: unsupported clip\n");
return PR_FALSE;
}
/* Stop now if everything is clipped out */
if (needs_clip && rect_count == 0) {
NATIVE_DRAWING_NOTE("TAKING FAST PATH: all clipped\n");
return PR_TRUE;
}
/* Check that the operator is OVER */
if (cairo_get_operator (cr) != CAIRO_OPERATOR_OVER) {
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-OVER operator\n");
return PR_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. */
/* Check that the target surface is an xlib surface. */
cairo_surface_t *target = cairo_get_group_target (cr);
if (cairo_surface_get_type (target) != CAIRO_SURFACE_TYPE_XLIB) {
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: non-X surface\n");
return PR_FALSE;
@ -290,6 +203,57 @@ gfxXlibNativeRenderer::DrawDirect(gfxContext *ctx, nsIntSize bounds,
}
}
cairo_matrix_t matrix;
cairo_get_matrix (cr, &matrix);
double device_offset_x, device_offset_y;
cairo_surface_get_device_offset (target, &device_offset_x, &device_offset_y);
/* Draw() checked that the matrix contained only a very-close-to-integer
translation. Here (and in several other places and thebes) device
offsets are assumed to be integer. */
NS_ASSERTION(PRUint32(device_offset_x) == device_offset_x &&
PRUint32(device_offset_y) == device_offset_y,
"Expected integer device offsets");
nsIntPoint offset(NS_lroundf(matrix.x0 + device_offset_x),
NS_lroundf(matrix.y0 + device_offset_y));
int max_rectangles = 0;
if (flags & DRAW_SUPPORTS_CLIP_RECT) {
max_rectangles = 1;
}
if (flags & DRAW_SUPPORTS_CLIP_LIST) {
max_rectangles = MAX_STATIC_CLIP_RECTANGLES;
}
/* The client won't draw outside the surface so consider this when
analysing clip rectangles. */
nsIntRect bounds(offset, size);
bounds.IntersectRect(bounds,
nsIntRect(0, 0,
cairo_xlib_surface_get_width(target),
cairo_xlib_surface_get_height(target)));
PRBool needs_clip;
nsIntRect rectangles[MAX_STATIC_CLIP_RECTANGLES];
int rect_count;
/* Check that the clip is rectangular and aligned on unit boundaries. */
/* Temporarily set the matrix for _get_rectangular_clip. It's basically
the identity matrix, but we must adjust for the fact that our
offset-rect is in device coordinates. */
cairo_identity_matrix (cr);
cairo_translate (cr, -device_offset_x, -device_offset_y);
PRBool have_rectangular_clip =
_get_rectangular_clip (cr, bounds, &needs_clip,
rectangles, max_rectangles, &rect_count);
cairo_set_matrix (cr, &matrix);
if (!have_rectangular_clip)
return PR_FALSE;
/* Draw only calls this function when the clip region is not empty. */
NS_ASSERTION(!needs_clip || rect_count != 0,
"Where did the clip region go?");
/* we're good to go! */
NATIVE_DRAWING_NOTE("TAKING FAST PATH\n");
cairo_surface_flush (target);
@ -360,21 +324,13 @@ _create_temp_xlib_surface (cairo_t *cr, nsIntSize size,
PRBool
gfxXlibNativeRenderer::DrawOntoTempSurface(gfxXlibSurface *tempXlibSurface,
double background_gray_value)
nsIntPoint offset)
{
cairo_surface_t *temp_xlib_surface = tempXlibSurface->CairoSurface();
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 */
nsresult rv = DrawWithXlib(tempXlibSurface, nsIntPoint(0, 0), NULL, 0);
nsresult rv = DrawWithXlib(tempXlibSurface, offset, NULL, 0);
cairo_surface_mark_dirty (temp_xlib_surface);
return NS_SUCCEEDED(rv);
}
@ -501,29 +457,77 @@ gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size,
result->mUniformColor = PR_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 (size.width == 0 || size.height == 0)
return;
PRBool matrixIsIntegerTranslation =
!ctx->CurrentMatrix().HasNonIntegerTranslation();
if (DrawDirect(ctx, size, flags, screen, visual))
return;
// The padding of 0.5 for non-pixel-exact transformations used here is
// the same as what _cairo_pattern_analyze_filter uses.
const gfxFloat filterRadius = 0.5;
gfxRect affectedRect(0.0, 0.0, size.width, size.height);
if (!matrixIsIntegerTranslation) {
// The filter footprint means that the affected rectangle is a
// little larger than the drawingRect;
affectedRect.Outset(filterRadius);
NATIVE_DRAWING_NOTE("TAKING SLOW PATH: matrix not integer translation\n");
}
// Clipping to the region affected by drawing allows us to consider only
// the portions of the clip region that will be affected by drawing.
gfxRect clipExtents;
{
gfxContextAutoSaveRestore autoSR(ctx);
ctx->Clip(affectedRect);
clipExtents = ctx->GetClipExtents();
if (clipExtents.IsEmpty())
return; // nothing to do
if (matrixIsIntegerTranslation &&
DrawDirect(ctx, size, flags, screen, visual))
return;
}
nsIntRect drawingRect(nsIntPoint(0, 0), size);
PRBool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0;
if (drawIsOpaque || !result) {
// Drawing need only be performed within the clip extents
// (and padding for the filter).
if (!matrixIsIntegerTranslation) {
// The source surface may need to be a little larger than the clip
// extents due to the filter footprint.
clipExtents.Outset(filterRadius);
}
clipExtents.RoundOut();
nsIntRect intExtents(PRInt32(clipExtents.X()),
PRInt32(clipExtents.Y()),
PRInt32(clipExtents.Width()),
PRInt32(clipExtents.Height()));
drawingRect.IntersectRect(drawingRect, intExtents);
}
gfxPoint offset(drawingRect.x, drawingRect.y);
cairo_t *cr = ctx->GetCairo();
nsRefPtr<gfxXlibSurface> tempXlibSurface =
_create_temp_xlib_surface (cr, size, flags, screen, visual);
_create_temp_xlib_surface (cr, drawingRect.Size(),
flags, screen, visual);
if (tempXlibSurface == NULL)
return;
if (!DrawOntoTempSurface(tempXlibSurface, 0.0)) {
nsRefPtr<gfxContext> tmpCtx;
if (!drawIsOpaque) {
tmpCtx = new gfxContext(tempXlibSurface);
tmpCtx->SetOperator(gfxContext::OPERATOR_CLEAR);
tmpCtx->Paint();
}
if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) {
return;
}
if (flags & DRAW_IS_OPAQUE) {
cairo_set_source_surface (cr, tempXlibSurface->CairoSurface(),
0.0, 0.0);
cairo_paint (cr);
if (drawIsOpaque) {
ctx->SetSource(tempXlibSurface, offset);
ctx->Paint();
if (result) {
result->mSurface = tempXlibSurface;
/* fill in the result with what we know, which is really just what our
@ -534,13 +538,16 @@ gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size,
return;
}
int width = size.width;
int height = size.height;
int width = drawingRect.width;
int height = drawingRect.height;
black_image_surface =
_copy_xlib_surface_to_image (tempXlibSurface, CAIRO_FORMAT_ARGB32,
width, height, &black_data);
DrawOntoTempSurface(tempXlibSurface, 1.0);
tmpCtx->SetDeviceColor(gfxRGBA(1.0, 1.0, 1.0));
tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
tmpCtx->Paint();
DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft());
white_image_surface =
_copy_xlib_surface_to_image (tempXlibSurface, CAIRO_FORMAT_RGB24,
width, height, &white_data);
@ -554,7 +561,7 @@ gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size,
_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);
cairo_set_source_surface (cr, black_image_surface, offset.x, offset.y);
/* 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

View File

@ -57,11 +57,17 @@ class THEBES_API gfxXlibNativeRenderer {
public:
/**
* Perform the native drawing.
* @param surface the drawable for drawing.
* The extents of this surface do not necessarily cover the
* entire rectangle with size provided to Draw().
* @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 clipRects an array of rectangles; clip to the union.
* Any rectangles provided will be contained by the
* rectangle with size provided to Draw and by the
* surface extents.
* @param numClipRects the number of rects in the array, or zero if
* no clipping is required
* no clipping is required.
*/
virtual nsresult DrawWithXlib(gfxXlibSurface* surface,
nsIntPoint offset,
@ -116,7 +122,7 @@ private:
PRUint32 flags, Screen *screen, Visual *visual);
PRBool DrawOntoTempSurface(gfxXlibSurface *tempXlibSurface,
double background_gray_value);
nsIntPoint offset);
};

View File

@ -5496,16 +5496,22 @@ nsPluginInstanceOwner::Renderer::DrawWithXlib(gfxXlibSurface* xsurface,
clipRect.y = clipRects[0].y;
clipRect.width = clipRects[0].width;
clipRect.height = clipRects[0].height;
// NPRect members are unsigned, but clip rectangles should be contained by
// the surface.
NS_ASSERTION(clipRect.x >= 0 && clipRect.y >= 0,
"Clip rectangle offsets are negative!");
}
else {
// NPRect members are unsigned, but
// we should have been given a clip if an offset is -ve.
NS_ASSERTION(offset.x >= 0 && offset.y >= 0,
"Clip rectangle offsets are negative!");
clipRect.x = offset.x;
clipRect.y = offset.y;
clipRect.width = mWindow->width;
clipRect.height = mWindow->height;
// Don't ask the plugin to draw outside the drawable.
// This also ensures that the unsigned clip rectangle offsets won't be -ve.
gfxIntSize surfaceSize = xsurface->GetSize();
clipRect.IntersectRect(clipRect,
nsIntRect(0, 0,
surfaceSize.width, surfaceSize.height));
}
NPRect newClipRect;

View File

@ -655,6 +655,12 @@ ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX,
GdkRectangle gdk_clip = mGDKClip;
gdk_clip.x += offsetX;
gdk_clip.y += offsetY;
GdkRectangle surfaceRect;
surfaceRect.x = 0;
surfaceRect.y = 0;
gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height);
gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip);
NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!");
moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip,