Bug 671428. cairo: Handle bad strides in acquire_source_surface. r=bas
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Tue, 16 Aug 2011 19:07:49 -0400
changeset 75418 be62b6c4392a9eb07d5a570ba9a1450c1aa8d805
parent 75417 0c4897315acce9bc8fc97cd1b9bb810cbd356f64
child 75419 8f315a50dcc9736e0efa47d8110fc14dc3d80415
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersbas
bugs671428
milestone9.0a1
Bug 671428. cairo: Handle bad strides in acquire_source_surface. r=bas Cairo requires image surfaces to have a particular stride alignment. Sometimes the texture we get back from Direct3D doesn't meet these requirements. In that case, copy to a different buffer that meets these requirments.
gfx/cairo/cairo/src/cairo-d2d-surface.cpp
gfx/cairo/cairo/src/cairoint.h
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -2505,21 +2505,38 @@ static cairo_status_t
     // We need to release the device after calling the constructor, since the
     // device destruction may release the D3D/D2D libraries.
     cairo_device_t *device = &d2dsurf->device->base;
     d2dsurf->~cairo_d2d_surface_t();
     cairo_release_device(device);
     return CAIRO_STATUS_SUCCESS;
 }
 
+/* The input types for src and dst don't match because in our particular use case, copying from a texture,
+ * those types don't match. */
+static void
+_copy_data_to_different_stride(unsigned char *dst, int dst_stride, void *src, UINT src_stride, int height)
+{
+
+    unsigned char *src_p = (unsigned char *)src;
+    int min_stride = MIN(dst_stride, src_stride);
+    while (height) {
+        memcpy(dst, src_p, min_stride);
+        height--;
+        dst += dst_stride;
+        src_p += src_stride;
+    }
+}
+
 static cairo_status_t
 _cairo_d2d_acquire_source_image(void                    *abstract_surface,
-				cairo_image_surface_t  **image_out,
+				cairo_image_surface_t  **image_out_ret,
 				void                   **image_extra)
 {
+    cairo_surface_t *image_out;
     cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(abstract_surface);
     _cairo_d2d_flush(d2dsurf);
 
     HRESULT hr;
     D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
 
     RefPtr<ID3D10Texture2D> softTexture;
 
@@ -2547,34 +2564,46 @@ static cairo_status_t
 
     d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface);
 
     D3D10_MAPPED_TEXTURE2D data;
     hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data);
     if (FAILED(hr)) {
 	return _cairo_error(CAIRO_STATUS_NO_DEVICE);
     }
-    *image_out = 
-	(cairo_image_surface_t*)cairo_image_surface_create_for_data((unsigned char*)data.pData,
-										  d2dsurf->format,
-										  size.width,
-										  size.height,
-										  data.RowPitch);
-
-    if (cairo_surface_status(&((*image_out)->base))) {
-	volatile cairo_status_t flambo[10];
-	for (int i=0; i<10; i++) {
-	    flambo[i] = cairo_surface_status(&((*image_out)->base));
-	}
-	volatile int p = 0;
-	p = 5/p;
+
+    if (_cairo_valid_stride_alignment(data.RowPitch)) {
+	image_out = cairo_image_surface_create_for_data((unsigned char*)data.pData,
+						  d2dsurf->format,
+						  size.width,
+						  size.height,
+						  data.RowPitch);
+    } else {
+	/* Slow path used when the stride doesn't match our requirements.
+	 * This is possible on at least the Intel driver 8.15.10.2302.
+	 *
+	 * Create a new image surface and copy our data into it */
+	image_out = cairo_image_surface_create(d2dsurf->format,
+					 size.width,
+					 size.height);
+	_copy_data_to_different_stride(cairo_image_surface_get_data(image_out),
+				       cairo_image_surface_get_stride(image_out),
+				       data.pData,
+				       data.RowPitch,
+				       size.height);
+
     }
+    /* these are the only surface statuses we expect */
+    assert(cairo_surface_status(image_out) == CAIRO_STATUS_SUCCESS ||
+	   cairo_surface_status(image_out) == CAIRO_STATUS_NO_MEMORY);
+
     *image_extra = softTexture.forget();
-
-    return CAIRO_STATUS_SUCCESS;
+    *image_out_ret = (cairo_image_surface_t*)image_out;
+
+    return cairo_surface_status(image_out);
 }
 
 static void
 _cairo_d2d_release_source_image(void                   *abstract_surface,
 				cairo_image_surface_t  *image,
 				void                   *image_extra)
 {
     if (((cairo_surface_t*)abstract_surface)->type != CAIRO_SURFACE_TYPE_D2D) {
--- a/gfx/cairo/cairo/src/cairoint.h
+++ b/gfx/cairo/cairo/src/cairoint.h
@@ -1903,16 +1903,22 @@ cairo_private void
    ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT)
 
 #define CAIRO_CONTENT_VALID(content) ((content) && 			         \
 				      (((content) & ~(CAIRO_CONTENT_COLOR |      \
 						      CAIRO_CONTENT_ALPHA |      \
 						      CAIRO_CONTENT_COLOR_ALPHA))\
 				       == 0))
 
+static inline cairo_bool_t
+_cairo_valid_stride_alignment(int stride)
+{
+    return !(stride & (CAIRO_STRIDE_ALIGNMENT-1));
+}
+
 cairo_private int
 _cairo_format_bits_per_pixel (cairo_format_t format) cairo_const;
 
 cairo_private cairo_format_t
 _cairo_format_from_content (cairo_content_t content) cairo_const;
 
 cairo_private cairo_format_t
 _cairo_format_from_pixman_format (pixman_format_code_t pixman_format);