mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 20:49:27 +00:00
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:
parent
44ee4169cd
commit
7c86e32ec8
@ -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
|
||||
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user