pdf: fix mime-unique-id jpeg attached to recording test

- Restructure the emit_surface code so that mime types are checked first.

- Add a test parameter to emit_surface to test if the surface will be emitted
  as an image or recording instead checking the surface type as the attached
  mime may override this.

- Mark surface as not clear when mime is attached to avoid optimizing away
  "clear" surfaces that have mime attached.

- Include entire surface in analysis if mime attached (also fixes bug with
  calculating the extents CONTENT_COLOR surfaces)
This commit is contained in:
Adrian Johnson 2017-11-09 20:52:36 +10:30
parent bff47b43c4
commit d5cb45013b
5 changed files with 287 additions and 145 deletions

View file

@ -137,81 +137,6 @@ detach_proxy (cairo_surface_t *proxy)
cairo_surface_destroy (proxy);
}
static cairo_int_status_t
_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents)
{
const cairo_surface_pattern_t *surface_pattern;
cairo_analysis_surface_t *tmp;
cairo_surface_t *source, *proxy;
cairo_matrix_t p2d;
cairo_status_t status, analysis_status;
cairo_bool_t surface_is_unbounded;
cairo_bool_t unused;
assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
surface_pattern = (const cairo_surface_pattern_t *) pattern;
assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
source = surface_pattern->surface;
proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
if (proxy != NULL) {
/* nothing untoward found so far */
return CAIRO_STATUS_SUCCESS;
}
tmp = (cairo_analysis_surface_t *)
_cairo_analysis_surface_create (surface->target);
if (unlikely (tmp->base.status))
return tmp->base.status;
proxy = attach_proxy (source, &tmp->base);
p2d = pattern->matrix;
status = cairo_matrix_invert (&p2d);
assert (status == CAIRO_STATUS_SUCCESS);
_cairo_analysis_surface_set_ctm (&tmp->base, &p2d);
source = _cairo_surface_get_source (source, NULL);
surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT
|| pattern->extend == CAIRO_EXTEND_REFLECT);
status = _cairo_recording_surface_replay_and_create_regions (source,
&pattern->matrix,
&tmp->base,
surface_is_unbounded);
if (tmp->has_supported) {
surface->has_supported = TRUE;
unused = cairo_region_union (&surface->supported_region, &tmp->supported_region);
}
if (tmp->has_unsupported) {
surface->has_unsupported = TRUE;
unused = cairo_region_union (&surface->fallback_region, &tmp->fallback_region);
}
analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
if (pattern->extend != CAIRO_EXTEND_NONE) {
_cairo_unbounded_rectangle_init (extents);
} else if (source->content & CAIRO_CONTENT_ALPHA) {
status = cairo_matrix_invert (&tmp->ctm);
_cairo_matrix_transform_bounding_box_fixed (&tmp->ctm,
&tmp->page_bbox, NULL);
_cairo_box_round_to_rectangle (&tmp->page_bbox, extents);
} else {
/* black background fills entire extents */
_cairo_surface_get_extents (source, extents);
}
detach_proxy (proxy);
cairo_surface_destroy (&tmp->base);
if (unlikely (status))
return status;
return analysis_status;
}
static cairo_int_status_t
_add_operation (cairo_analysis_surface_t *surface,
cairo_rectangle_int_t *rect,
@ -329,6 +254,103 @@ _add_operation (cairo_analysis_surface_t *surface,
return status;
}
static cairo_int_status_t
_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents)
{
const cairo_surface_pattern_t *surface_pattern;
cairo_analysis_surface_t *tmp;
cairo_surface_t *source, *proxy;
cairo_matrix_t p2d;
cairo_int_status_t status, analysis_status;
cairo_bool_t surface_is_unbounded;
cairo_bool_t unused;
assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
surface_pattern = (const cairo_surface_pattern_t *) pattern;
assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
source = surface_pattern->surface;
proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
if (proxy != NULL) {
/* nothing untoward found so far */
return CAIRO_STATUS_SUCCESS;
}
tmp = (cairo_analysis_surface_t *)
_cairo_analysis_surface_create (surface->target);
if (unlikely (tmp->base.status)) {
status =tmp->base.status;
goto cleanup1;
}
proxy = attach_proxy (source, &tmp->base);
p2d = pattern->matrix;
status = cairo_matrix_invert (&p2d);
assert (status == CAIRO_INT_STATUS_SUCCESS);
_cairo_analysis_surface_set_ctm (&tmp->base, &p2d);
source = _cairo_surface_get_source (source, NULL);
surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT
|| pattern->extend == CAIRO_EXTEND_REFLECT);
status = _cairo_recording_surface_replay_and_create_regions (source,
&pattern->matrix,
&tmp->base,
surface_is_unbounded);
if (unlikely (status))
goto cleanup2;
/* black background or mime data fills entire extents */
if (!(source->content & CAIRO_CONTENT_ALPHA) || _cairo_surface_has_mime_image (source)) {
cairo_rectangle_int_t rect;
if (_cairo_surface_get_extents (source, &rect)) {
cairo_box_t bbox;
_cairo_box_from_rectangle (&bbox, &rect);
_cairo_matrix_transform_bounding_box_fixed (&p2d, &bbox, NULL);
_cairo_box_round_to_rectangle (&bbox, &rect);
status = _add_operation (tmp, &rect, CAIRO_INT_STATUS_SUCCESS);
if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
status = CAIRO_INT_STATUS_SUCCESS;
if (unlikely (status))
goto cleanup2;
}
}
if (tmp->has_supported) {
surface->has_supported = TRUE;
unused = cairo_region_union (&surface->supported_region, &tmp->supported_region);
}
if (tmp->has_unsupported) {
surface->has_unsupported = TRUE;
unused = cairo_region_union (&surface->fallback_region, &tmp->fallback_region);
}
analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
if (pattern->extend != CAIRO_EXTEND_NONE) {
_cairo_unbounded_rectangle_init (extents);
} else {
status = cairo_matrix_invert (&tmp->ctm);
_cairo_matrix_transform_bounding_box_fixed (&tmp->ctm,
&tmp->page_bbox, NULL);
_cairo_box_round_to_rectangle (&tmp->page_bbox, extents);
}
cleanup2:
detach_proxy (proxy);
cleanup1:
cairo_surface_destroy (&tmp->base);
if (unlikely (status))
return status;
return analysis_status;
}
static cairo_status_t
_cairo_analysis_surface_finish (void *abstract_surface)
{

View file

@ -267,6 +267,12 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
static cairo_int_status_t
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface);
static cairo_int_status_t
_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *source,
cairo_bool_t test,
cairo_bool_t *is_image);
static cairo_int_status_t
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
@ -1646,6 +1652,11 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
goto fail3;
}
/* Test if surface will be emitted as image or recording */
status = _cairo_pdf_surface_emit_surface (surface, &src_surface, TRUE, &surface_entry->emit_image);
if (unlikely (status))
goto fail3;
if (surface_entry->bounded) {
status = _cairo_array_append (&surface->page_surfaces, &src_surface);
if (unlikely (status))
@ -2890,7 +2901,8 @@ _cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t *surface,
static cairo_int_status_t
_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t *surface,
cairo_surface_t *source,
cairo_pdf_source_surface_entry_t *surface_entry)
cairo_pdf_source_surface_entry_t *surface_entry,
cairo_bool_t test)
{
cairo_int_status_t status;
const unsigned char *mime_data;
@ -2913,6 +2925,10 @@ _cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t *surface,
if (status)
return status;
/* At this point we know emitting jbig2 will succeed. */
if (test)
return CAIRO_STATUS_SUCCESS;
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
&global_id, &global_id_length);
if (global_id && global_id_length > 0) {
@ -2998,7 +3014,8 @@ _cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t *surface,
static cairo_int_status_t
_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface,
cairo_surface_t *source,
cairo_pdf_source_surface_entry_t *surface_entry)
cairo_pdf_source_surface_entry_t *surface_entry,
cairo_bool_t test)
{
cairo_int_status_t status;
const unsigned char *mime_data;
@ -3029,6 +3046,10 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface,
else
smask_buf[0] = 0;
/* At this point we know emitting jpx will succeed. */
if (test)
return CAIRO_STATUS_SUCCESS;
if (surface_entry->stencil_mask) {
status = _cairo_pdf_surface_open_stream (surface,
&surface_entry->surface_res,
@ -3073,7 +3094,8 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface,
static cairo_int_status_t
_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface,
cairo_surface_t *source,
cairo_pdf_source_surface_entry_t *surface_entry)
cairo_pdf_source_surface_entry_t *surface_entry,
cairo_bool_t test)
{
cairo_int_status_t status;
const unsigned char *mime_data;
@ -3113,6 +3135,10 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface,
return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* At this point we know emitting jpeg will succeed. */
if (test)
return CAIRO_STATUS_SUCCESS;
if (surface_entry->smask_res.id)
snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id);
else
@ -3168,7 +3194,8 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface,
static cairo_int_status_t
_cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface,
cairo_surface_t *source,
cairo_pdf_source_surface_entry_t *surface_entry)
cairo_pdf_source_surface_entry_t *surface_entry,
cairo_bool_t test)
{
cairo_status_t status;
const unsigned char *ccitt_data;
@ -3203,6 +3230,10 @@ _cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface,
free (params);
/* At this point we know emitting jbig2 will succeed. */
if (test)
return CAIRO_STATUS_SUCCESS;
p = buf;
*p = 0;
end = buf + sizeof(buf) - 1;
@ -3272,54 +3303,6 @@ _cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface,
return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *source)
{
cairo_image_surface_t *image;
void *image_extra;
cairo_int_status_t status;
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
status = _cairo_pdf_surface_emit_jbig2_image (surface, source->surface, source->hash_entry);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
status = _cairo_pdf_surface_emit_jpx_image (surface, source->surface, source->hash_entry);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
status = _cairo_pdf_surface_emit_jpeg_image (surface, source->surface, source->hash_entry);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
status = _cairo_pdf_surface_emit_ccitt_image (surface, source->surface, source->hash_entry);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra);
} else {
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source->raster_pattern,
&image, &image_extra);
}
if (unlikely (status))
return status;
status = _cairo_pdf_surface_emit_image (surface,
image,
source->hash_entry);
if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
_cairo_surface_release_source_image (source->surface, image, image_extra);
else
_cairo_pdf_surface_release_source_image_from_pattern (surface, source->raster_pattern,
image, image_extra);
return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *pdf_source)
@ -3441,15 +3424,107 @@ err:
return status;
}
/**
* _cairo_pdf_surface_emit_surface:
* @surface: [in] the pdf surface
* @source: [in] #cairo_pdf_source_surface_t containing the surface to write
* @test: [in] if true, test what type of surface will be emitted.
* @is_image: [out] if @test is true, returns TRUE if the surface will be emitted
* as an Image XObject.
*
* If @test is FALSE, emit @src_surface as an XObject.
* If @test is TRUE, don't emit anything. Set @is_image based on the output that would be emitted.
**/
static cairo_int_status_t
_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *src_surface)
cairo_pdf_source_surface_t *source,
cairo_bool_t test,
cairo_bool_t *is_image)
{
if (src_surface->type == CAIRO_PATTERN_TYPE_SURFACE &&
src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
return _cairo_pdf_surface_emit_recording_surface (surface, src_surface);
cairo_image_surface_t *image;
void *image_extra;
cairo_int_status_t status;
return _cairo_pdf_surface_emit_image_surface (surface, src_surface);
/* Try all the supported mime types and recording type, falling through
* each option if unsupported */
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
status = _cairo_pdf_surface_emit_jbig2_image (surface,
source->surface,
source->hash_entry,
test);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*is_image = TRUE;
return status;
}
status = _cairo_pdf_surface_emit_jpx_image (surface,
source->surface,
source->hash_entry,
test);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*is_image = TRUE;
return status;
}
status = _cairo_pdf_surface_emit_jpeg_image (surface,
source->surface,
source->hash_entry,
test);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*is_image = TRUE;
return status;
}
status = _cairo_pdf_surface_emit_ccitt_image (surface,
source->surface,
source->hash_entry,
test);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*is_image = TRUE;
return status;
}
if (source->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (test) {
*is_image = FALSE;
return CAIRO_INT_STATUS_SUCCESS;
} else {
return _cairo_pdf_surface_emit_recording_surface (surface, source);
}
}
}
/* The only option left is to emit as an image surface */
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra);
} else {
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface,
source->raster_pattern,
&image,
&image_extra);
}
if (unlikely (status))
return status;
if (test) {
*is_image = TRUE;
} else {
status = _cairo_pdf_surface_emit_image (surface,
image,
source->hash_entry);
}
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
_cairo_surface_release_source_image (source->surface, image, image_extra);
} else {
_cairo_pdf_surface_release_source_image_from_pattern (surface,
source->raster_pattern,
image,
image_extra);
}
return status;
}
static cairo_int_status_t
@ -3596,8 +3671,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &mat);
cairo_matrix_translate (&pdf_p2d, x_offset, y_offset);
if (((cairo_surface_pattern_t *)pattern)->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
{
if (pdf_source->emit_image) {
cairo_matrix_translate (&pdf_p2d, 0.0, pdf_source->extents.height);
cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
}
@ -3625,18 +3699,18 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
if (unlikely (status))
return status;
if (((cairo_surface_pattern_t *) pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
snprintf(draw_surface,
sizeof (draw_surface),
"/x%d Do",
pdf_source->surface_res.id);
} else {
if (pdf_source->emit_image) {
snprintf(draw_surface,
sizeof (draw_surface),
"q %d 0 0 %d 0 0 cm /x%d Do Q",
pdf_source->extents.width,
pdf_source->extents.height,
pdf_source->surface_res.id);
} else {
snprintf(draw_surface,
sizeof (draw_surface),
"/x%d Do",
pdf_source->surface_res.id);
}
if (extend == CAIRO_EXTEND_REFLECT) {
@ -6729,6 +6803,7 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface
cairo_pdf_source_surface_t src_surface;
unsigned int pattern_index, group_index, surface_index, doc_surface_index;
cairo_int_status_t status;
cairo_bool_t is_image;
/* Writing out PDF_MASK groups will cause additional smask groups
* to be appended to surface->smask_groups. Additional patterns
@ -6762,7 +6837,7 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface
for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) {
_cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface);
status = _cairo_pdf_surface_emit_surface (surface, &src_surface);
status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image);
if (unlikely (status))
return status;
}
@ -6770,7 +6845,7 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface
if (finish) {
for (; doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces); doc_surface_index++) {
_cairo_array_copy_element (&surface->doc_surfaces, doc_surface_index, &src_surface);
status = _cairo_pdf_surface_emit_surface (surface, &src_surface);
status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image);
if (unlikely (status))
return status;
}
@ -6915,7 +6990,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface)
static cairo_int_status_t
_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface,
cairo_surface_pattern_t *pattern)
cairo_surface_pattern_t *pattern)
{
cairo_image_surface_t *image;
void *image_extra;

View file

@ -1224,6 +1224,44 @@ _cairo_mime_data_destroy (void *ptr)
free (mime_data);
}
static const char *_cairo_surface_image_mime_types[] = {
CAIRO_MIME_TYPE_JPEG,
CAIRO_MIME_TYPE_PNG,
CAIRO_MIME_TYPE_JP2,
CAIRO_MIME_TYPE_JBIG2,
CAIRO_MIME_TYPE_CCITT_FAX,
};
cairo_bool_t
_cairo_surface_has_mime_image (cairo_surface_t *surface)
{
cairo_user_data_slot_t *slots;
int i, j, num_slots;
/* Prevent reads of the array during teardown */
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return FALSE;
/* The number of mime-types attached to a surface is usually small,
* typically zero. Therefore it is quicker to do a strcmp() against
* each key than it is to intern the string (i.e. compute a hash,
* search the hash table, and do a final strcmp).
*/
num_slots = surface->mime_data.num_elements;
slots = _cairo_array_index (&surface->mime_data, 0);
for (i = 0; i < num_slots; i++) {
if (slots[i].key != NULL) {
for (j = 0; j < ARRAY_LENGTH (_cairo_surface_image_mime_types); j++) {
if (strcmp ((char *) slots[i].key, _cairo_surface_image_mime_types[j]) == 0)
return TRUE;
}
}
}
return FALSE;
}
/**
* CAIRO_MIME_TYPE_CCITT_FAX:
*
@ -1405,6 +1443,8 @@ cairo_surface_set_mime_data (cairo_surface_t *surface,
return _cairo_surface_set_error (surface, status);
}
surface->is_clear = FALSE;
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def (cairo_surface_set_mime_data);
@ -1479,6 +1519,8 @@ _cairo_surface_copy_mime_data (cairo_surface_t *dst,
_cairo_mime_data_reference,
NULL);
dst->is_clear = FALSE;
return CAIRO_STATUS_SUCCESS;
}

View file

@ -1336,6 +1336,9 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
/* cairo-surface.c */
cairo_private cairo_bool_t
_cairo_surface_has_mime_image (cairo_surface_t *surface);
cairo_private cairo_status_t
_cairo_surface_copy_mime_data (cairo_surface_t *dst,
cairo_surface_t *src);

View file

@ -72,7 +72,7 @@
* file size due to changes to the PS/PDF surfaces while being small
* enough to catch any attempt to embed the surface more than
* once. The compressed size of each surface embedded in PDF is:
* - image: 111,952
* - image: 108,774
* - jpeg: 11,400
* - recording: 17,518
*
@ -81,7 +81,7 @@
*/
#define PS2_EXPECTED_SIZE 315362
#define PS3_EXPECTED_SIZE 315362
#define PDF_EXPECTED_SIZE 142968
#define PDF_EXPECTED_SIZE 347182
#define SIZE_TOLERANCE 5000
static const char *png_filename = "romedalen.png";