зеркало из https://github.com/mozilla/pjs.git
Bug 551350: Part 1: Add proper handling of large image surfaces. r=jrmuizel a=Mossop
This commit is contained in:
Родитель
31363f0b74
Коммит
03de50f60b
|
@ -785,11 +785,13 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
extendMode = D2D1_EXTEND_MODE_CLAMP;
|
||||
}
|
||||
RefPtr<ID2D1Bitmap> sourceBitmap;
|
||||
bool tiled = false;
|
||||
bool partial = false;
|
||||
unsigned int xoffset = 0;
|
||||
unsigned int yoffset = 0;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned char *data = NULL;
|
||||
unsigned int stride = 0;
|
||||
*remaining_runs = 0;
|
||||
if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_D2D) {
|
||||
/**
|
||||
|
@ -814,6 +816,12 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
alpha = D2D1_ALPHA_MODE_IGNORE;
|
||||
}
|
||||
|
||||
data = srcSurf->data;
|
||||
stride = srcSurf->stride;
|
||||
|
||||
/* This is used as a temporary surface for resampling surfaces larget than maxSize. */
|
||||
pixman_image_t *pix_image = NULL;
|
||||
|
||||
DXGI_FORMAT format;
|
||||
unsigned int Bpp;
|
||||
if (srcSurf->format == CAIRO_FORMAT_ARGB32) {
|
||||
|
@ -833,34 +841,98 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2;
|
||||
|
||||
if ((UINT32)srcSurf->width > maxSize || (UINT32)srcSurf->height > maxSize) {
|
||||
tiled = true;
|
||||
UINT32 horiz_tiles = (UINT32)ceil((float)srcSurf->width / maxSize);
|
||||
UINT32 vert_tiles = (UINT32)ceil((float)srcSurf->height / maxSize);
|
||||
UINT32 current_vert_tile = last_run / horiz_tiles;
|
||||
UINT32 current_horiz_tile = last_run % horiz_tiles;
|
||||
xoffset = current_horiz_tile * maxSize;
|
||||
yoffset = current_vert_tile * maxSize;
|
||||
*remaining_runs = horiz_tiles * vert_tiles - last_run - 1;
|
||||
width = min(maxSize, srcSurf->width - maxSize * current_horiz_tile);
|
||||
height = min(maxSize, srcSurf->height - maxSize * current_vert_tile);
|
||||
// Move the image to the right spot.
|
||||
cairo_matrix_translate(&mat, xoffset, yoffset);
|
||||
if (true) {
|
||||
RefPtr<ID2D1RectangleGeometry> clipRect;
|
||||
D2DSurfFactory::Instance()->CreateRectangleGeometry(D2D1::RectF(0, 0, (float)width, (float)height),
|
||||
&clipRect);
|
||||
/* We cannot fit this image directly into a texture, start doing tricks to draw correctly anyway. */
|
||||
partial = true;
|
||||
/* First we check which part of the image is inside the viewable area. */
|
||||
|
||||
/* Transform this surface to image surface space */
|
||||
cairo_matrix_t invMat = mat;
|
||||
if (_cairo_matrix_is_invertible(&mat)) {
|
||||
/* If this is not invertible it will be rank zero, and invMat = mat is fine */
|
||||
cairo_matrix_invert(&invMat);
|
||||
}
|
||||
|
||||
if (!d2dsurf->helperLayer) {
|
||||
d2dsurf->rt->CreateLayer(&d2dsurf->helperLayer);
|
||||
}
|
||||
RefPtr<IDXGISurface> surf;
|
||||
d2dsurf->surface->QueryInterface(&surf);
|
||||
DXGI_SURFACE_DESC desc;
|
||||
surf->GetDesc(&desc);
|
||||
|
||||
double leftMost = 0;
|
||||
double rightMost = desc.Width;
|
||||
double topMost = 0;
|
||||
double bottomMost = desc.Height;
|
||||
|
||||
_cairo_matrix_transform_bounding_box(&invMat, &leftMost, &topMost, &rightMost, &bottomMost, NULL);
|
||||
|
||||
leftMost -= 1;
|
||||
topMost -= 1;
|
||||
rightMost += 1;
|
||||
bottomMost += 1;
|
||||
|
||||
/* Calculate the offsets into the source image and the width of the part required */
|
||||
xoffset = (unsigned int)MAX(0, floor(leftMost));
|
||||
yoffset = (unsigned int)MAX(0, floor(topMost));
|
||||
width = (unsigned int)MIN(MAX(0, ceil(rightMost - xoffset)), srcSurf->width - xoffset);
|
||||
height = (unsigned int)MIN(MAX(0, ceil(bottomMost - yoffset)), srcSurf->height - yoffset);
|
||||
|
||||
cairo_matrix_translate(&mat, xoffset, yoffset);
|
||||
|
||||
if (width > maxSize || height > maxSize) {
|
||||
/*
|
||||
* We cannot upload the required part of the surface directly, we're going to create
|
||||
* a version which is downsampled to a smaller size by pixman and then uploaded.
|
||||
*
|
||||
* We need to size it to at least the diagonal size of this surface, in order to prevent ever
|
||||
* upsampling this again when drawing it to the surface. We want the resized surface
|
||||
* to be as small as possible to limit pixman required fill rate.
|
||||
*
|
||||
* Note this isn't necessarily perfect. Imagine having a 5x5 pixel destination and
|
||||
* a 10x5 image containing a line of blackpixels, white pixels, black pixels, if you rotate
|
||||
* this by 45 degrees and scale it to a size of 5x5 pixels and composite it to the destination,
|
||||
* the composition will require all 10 original columns to do the best possible sampling.
|
||||
*/
|
||||
unsigned int minSize = (unsigned int)ceil(sqrt(pow((float)desc.Width, 2) + pow((float)desc.Height, 2)));
|
||||
|
||||
d2dsurf->rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(),
|
||||
clipRect,
|
||||
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
||||
_cairo_d2d_matrix_from_matrix(&mat)),
|
||||
d2dsurf->helperLayer);
|
||||
*pushed_clip = true;
|
||||
}
|
||||
unsigned int newWidth = MIN(minSize, MIN(width, maxSize));
|
||||
unsigned int newHeight = MIN(minSize, MIN(height, maxSize));
|
||||
double xRatio = (double)width / newWidth;
|
||||
double yRatio = (double)height / newHeight;
|
||||
|
||||
if (newWidth > maxSize || newHeight > maxSize) {
|
||||
/*
|
||||
* Okay, the diagonal of our surface is big enough to require a sampling larger
|
||||
* than the maximum texture size. This is where we give up.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a temporary surface to hold the downsampled image */
|
||||
pix_image = pixman_image_create_bits(srcSurf->pixman_format,
|
||||
newWidth,
|
||||
newHeight,
|
||||
NULL,
|
||||
-1);
|
||||
|
||||
/* Set the transformation to downsample and call pixman_image_composite to downsample */
|
||||
pixman_transform_t transform;
|
||||
pixman_transform_init_scale(&transform, pixman_double_to_fixed(xRatio), pixman_double_to_fixed(yRatio));
|
||||
pixman_transform_translate(&transform, NULL, pixman_int_to_fixed(xoffset), pixman_int_to_fixed(yoffset));
|
||||
|
||||
pixman_image_set_transform(srcSurf->pixman_image, &transform);
|
||||
pixman_image_composite(PIXMAN_OP_SRC, srcSurf->pixman_image, NULL, pix_image, 0, 0, 0, 0, 0, 0, newWidth, newHeight);
|
||||
|
||||
/* Adjust the pattern transform to the used temporary surface */
|
||||
cairo_matrix_scale(&mat, xRatio, yRatio);
|
||||
|
||||
data = (unsigned char*)pixman_image_get_data(pix_image);
|
||||
stride = pixman_image_get_stride(pix_image);
|
||||
|
||||
/* Into this image we actually have no offset */
|
||||
xoffset = 0;
|
||||
yoffset = 0;
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
}
|
||||
} else {
|
||||
width = srcSurf->width;
|
||||
height = srcSurf->height;
|
||||
|
@ -868,7 +940,7 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
|
||||
cached_bitmap *cachebitmap = NULL;
|
||||
|
||||
if (!tiled) {
|
||||
if (!partial) {
|
||||
cachebitmap =
|
||||
(cached_bitmap*)cairo_surface_get_user_data(
|
||||
surfacePattern->surface,
|
||||
|
@ -879,7 +951,7 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
sourceBitmap = cachebitmap->bitmap;
|
||||
if (cachebitmap->dirty) {
|
||||
D2D1_RECT_U rect;
|
||||
/** No need to take tiling into account - tiled surfaces are never cached. */
|
||||
/* No need to take partial uploading into account - partially uploaded surfaces are never cached. */
|
||||
if (pattern->extend == CAIRO_EXTEND_NONE) {
|
||||
rect = D2D1::RectU(1, 1, srcSurf->width + 1, srcSurf->height + 1);
|
||||
} else {
|
||||
|
@ -901,11 +973,10 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
_d2d_snapshot_detached);
|
||||
}
|
||||
} else {
|
||||
cached_bitmap *cachebitmap = new cached_bitmap;
|
||||
if (pattern->extend != CAIRO_EXTEND_NONE) {
|
||||
d2dsurf->rt->CreateBitmap(D2D1::SizeU(width, height),
|
||||
srcSurf->data + yoffset * srcSurf->stride + xoffset,
|
||||
srcSurf->stride,
|
||||
data + yoffset * stride + xoffset * Bpp,
|
||||
stride,
|
||||
D2D1::BitmapProperties(D2D1::PixelFormat(format,
|
||||
alpha)),
|
||||
&sourceBitmap);
|
||||
|
@ -925,7 +996,7 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
for (unsigned int y = 0; y < height; y++) {
|
||||
memcpy(
|
||||
tmp + tmpWidth * Bpp * y + tmpWidth * Bpp + Bpp,
|
||||
srcSurf->data + yoffset * srcSurf->stride + y * srcSurf->stride + xoffset,
|
||||
data + yoffset * stride + y * stride + xoffset * Bpp,
|
||||
width * Bpp);
|
||||
}
|
||||
|
||||
|
@ -938,22 +1009,33 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
|
|||
delete [] tmp;
|
||||
}
|
||||
|
||||
cachebitmap->dirty = false;
|
||||
cachebitmap->bitmap = sourceBitmap;
|
||||
cachebitmap->refs = 2;
|
||||
cairo_surface_set_user_data(surfacePattern->surface,
|
||||
key,
|
||||
cachebitmap,
|
||||
_d2d_release_bitmap);
|
||||
cairo_surface_t *nullSurf =
|
||||
_cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
|
||||
cairo_surface_set_user_data(nullSurf,
|
||||
&bitmap_key_snapshot,
|
||||
cachebitmap,
|
||||
NULL);
|
||||
_cairo_surface_attach_snapshot(surfacePattern->surface,
|
||||
nullSurf,
|
||||
_d2d_snapshot_detached);
|
||||
if (!partial) {
|
||||
cached_bitmap *cachebitmap = new cached_bitmap;
|
||||
/* We can cache it if it isn't a partial bitmap */
|
||||
cachebitmap->dirty = false;
|
||||
cachebitmap->bitmap = sourceBitmap;
|
||||
/*
|
||||
* This will start out with two references, one on the snapshot
|
||||
* and one more in the user data structure.
|
||||
*/
|
||||
cachebitmap->refs = 2;
|
||||
cairo_surface_set_user_data(surfacePattern->surface,
|
||||
key,
|
||||
cachebitmap,
|
||||
_d2d_release_bitmap);
|
||||
cairo_surface_t *nullSurf =
|
||||
_cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
|
||||
cairo_surface_set_user_data(nullSurf,
|
||||
&bitmap_key_snapshot,
|
||||
cachebitmap,
|
||||
NULL);
|
||||
_cairo_surface_attach_snapshot(surfacePattern->surface,
|
||||
nullSurf,
|
||||
_d2d_snapshot_detached);
|
||||
}
|
||||
if (pix_image) {
|
||||
pixman_image_unref(pix_image);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
|
|
Загрузка…
Ссылка в новой задаче