diff options
author | Carl Worth <cworth@cworth.org> | 2006-02-13 16:47:32 -0800 |
---|---|---|
committer | Carl Worth <cworth@cworth.org> | 2006-02-13 16:47:32 -0800 |
commit | 01dd527ef3cb3205e33ffec90a11fda11f0e281a (patch) | |
tree | 39c7e267083fde15b6cbc249b34d597d4d6522ff | |
parent | 6aff9afc22eb6c5c814992c5ca4b3bd437935d3a (diff) | |
parent | 0e40baa9a7bd08abd15bfdc666c6e2d21d791e82 (diff) |
Remove pixman from SNAPSHOT_0_5_2SNAPSHOT_0_5_2
58 files changed, 5582 insertions, 1926 deletions
@@ -1,3 +1,10 @@ +cairo-xlib-surface needs to do the Xlib register extension hack so +that it can listen for a Display close event. When it gets that, it +needs to run through its caches and eliminate anything associated with +that display. + +-- + cairo_show_surface fails when given a non-default CTM, see the show_surface.cairo snippet in: diff --git a/CODING_STYLE b/CODING_STYLE index 04fc53c44..143723d4f 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -57,8 +57,8 @@ but some of the code uses an alternate style: } and that seems just fine. We won't lay down any strict rule on this -point, (though there should be some local). If you came here hoping to -find some guidance, then use the first form above. +point, (though there should be some local consistency). If you came +here hoping to find some guidance, then use the first form above. If all of the substatements of an if statement are single statements, the optional braces should not usually appear: @@ -1,3 +1,586 @@ +2005-07-18 Carl Worth <cworth@cworth.org> + + * src/cairo-quartz-surface.c: + (_cairo_quartz_surface_acquire_source_image), + (_cairo_quartz_surface_set_clip_region): Fix the quartz backend so + that it at least compiles again. This change is slipped in before + 0.5.2 was pushed out and tagged. + +2005-07-18 Carl Worth <cworth@cworth.org> + + * NEWS: Added notes for snapshot 0.5.2 + + * configure.in: Increment CAIRO_VERSION to 0.5.2 + +2005-07-18 Carl Worth <cworth@cworth.org> + + * test/composite-integer-translate-over-repeat.c (draw): Fix leak + of pattern. + +2005-07-18 Carl Worth <cworth@cworth.org> + + * ROADMAP: Note some progress. + + * test/cairo-test.h: + * test/cairo-test.c: (cairo_test_create_surface_from_png), + (cairo_test_create_pattern_from_png): New helper function to help + create an image surface from a PNG file, while taking the srcdir + environment variable into consideration. Rename the pattern + creating helper function to match. + + * test/composite-integer-translate-over.c: (draw): + * test/composite-integer-translate-source.c: (draw): Use new + cairo_test_create_surface_from_png so that non-srcdir builds work. + + * test/mask.c: (set_image_pattern): + * test/trap-clip.c: (set_image_pattern): Track change in + cairo_test_create_pattern_from_png. + +2005-07-18 Carl Worth <cworth@cworth.org> + + * BUGS: Add note that Xlib caches need to be cleand up on close + of display. + +2005-07-15 Carl Worth <cworth@cworth.org> + + * src/cairo-font.c: + * src/cairo-surface.c: Remove cairo_private qualifier from .c + files, (we only need it in the .h files). + +2005-07-15 Vladimir Vukicevic <vladimir@pobox.com> + + * src/cairo-xlib-surface.c: (_cairo_xlib_surface_composite, + _recategorize_composite_repeat): Use XCopyArea when + possible, for optimization and bug workaround. + + * test/composite-integer-translate-{source,over,over-repeat}.c: + Exercise XCopyArea, XRenderComposite, and XSetTile/XFillRectangle + paths for _cairo_xlib_surface_composite + +2005-07-15 Carl Worth <cworth@cworth.org> + + * test/Makefile.am: Add new check-valgrind target for running the + test suite under the influence of valgrind. + + * test/buffer-diff.c: (image_diff): Fix memory leak when reference + image is not found. + +2005-07-15 Carl Worth <cworth@cworth.org> + + * test/mask.c: (set_gradient_pattern), (set_image_pattern), + (draw): Fix a few memory leaks (missing cairo_pattern_destroy in 3 + places). + +2005-07-15 Carl Worth <cworth@cworth.org> + + * src/cairo-gstate.c: (_cairo_gstate_mask): Fix memory leak, + (missing _cairo_pattern_fini). + +2005-07-15 Carl Worth <cworth@cworth.org> + + * test/cairo-test.c (cairo_test_for_target): Remove errant line of + code that was inadvertently committed recently. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/surface-finish-twice.c: (draw): Fix leak of surface. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairo-pdf-surface.c: (_cairo_pdf_surface_finish), + (_cairo_pdf_document_finish): Call _cairo_array_fini on the + several array objects to patch memory leaks. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairo-xlib-surface.c: (_cairo_xlib_surface_composite), + (_cairo_xlib_surface_composite_trapezoids), + (_cairo_xlib_surface_show_glyphs): Eek. Yet _more_ fixups for + _cairo_pattern_release_surface missed earlier. + +2005-08-14 Stuart Parmenter <pavlov@pavlov.net> + + * src/cairo-win32-surface.c: (_cairo_win32_surface_fill_rectangles): + blue and green values were swapped in the DO_SOURCE case. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/cairo-test.c: (cairo_test_create_png_pattern): Rewrite to + use cairo_image_surface_create_from_png rather than custom + read_png_argb32. In addition to being simpler, this eliminates the + leak of the image data buffer. + + * test/trap-clip.c: (set_gradient_pattern), (set_image_pattern): + Add calls to cairo_pattern_destroy to close two memory leaks. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairo-pattern.c: (_cairo_pattern_acquire_surfaces): Fix up + one more call to _cairo_pattern_release_surface missed in the + previous commit. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairoint.h: + * src/cairo-pattern.c: (_cairo_pattern_release_surface): Fix to + accept a cairo_pattern_t rather than a cairo_surface_t as the + primary argument. + + * src/cairo-image-surface.c: (_cairo_image_surface_composite), + (_cairo_image_surface_composite_trapezoids): Track change in + _cairo_pattern_release_surface and also pass the appropriate + pattern for each acquired surface. The previous backend mismatch + was causing memory leaks. + + * src/cairo-xlib-surface.c: (_get_image_surface): Remove stale + comment. + + * test/xlib-surface.c: (main): Add missing fclose to keep valgrind + happy about memory leaks. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/cairo-test.c: (cairo_test_expecting), + (cairo_test_expect_failure), (cairo_test): Make the per-backend + test results print XFAIL rather than FAIL for expected failures. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/cairo-test.c: (cairo_test_real): Fix comment describing + test result. It's not enough that all tested backends are + successful. We also require that at least one backend is actually + tested. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/cairo-test.c: (cairo_test_for_target), (cairo_test_real): + * test/cairo-test.h: Don't consider a test to fail if it can't + create a surface at all (eg. no X server is available). Instead + mark this backend as untested and only consider the overall test a + success if all tested backend are successful. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairo-ps-surface.c: (pattern_is_translucent): Add missing + (putatively unreachable) return value. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairo-image-surface.c: + (_cairo_image_surface_set_clip_region): Remove the copying of the + region. This was a workaround for a missing copy bug in libpixman + that has since been fixed. So now it was just a memory leak. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * src/cairo-png.c (read_png): Patch memory leak of png_info + object. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/.cvsignore: + * test/Makefile.am: + * test/create-for-png-ref.png: + * test/create-for-png.c: + * test/create-from-png.c: (draw): Rename create-for-png test to + create-from-png to match the naming of the + cairo_image_surface_create_from_png function. + +2005-07-14 Carl Worth <cworth@cworth.org> + + * test/cairo-test.c: (cleanup_xcb), (cleanup_xlib), + (cairo_test_for_target), (cairo_test_real): Patch a few memory + leaks. + +2005-07-13 Kristian Høgsberg <krh@redhat.com> + + * src/cairo-meta-surface.c: (_cairo_meta_surface_replay): Use + the _cairo_surface_*() functions when replaying. + + * src/cairo-ps-surface.c: Fold the "locate fallbacks" pass into + the postscript output pass, and add a simple, first implementation + of image fallbacks. + +2005-07-13 Carl Worth <cworth@cworth.org> + + * src/cairoint.h: + * src/cairo-font.c: (_cairo_simple_font_face_create_font): Rename + CAIRO_FONT_BACKEND_DEFAULT to the more accurate + CAIRO_SCALED_FONT_BACKEND_DEFAULT. + +2005-07-13 Carl Worth <cworth@cworth.org> + + * src/cairo-ft-private.h: + * src/cairo-ft-font.c: (_cairo_unscaled_font_is_ft), + (_cairo_scaled_font_is_ft): New predicates to allow checking for + cairo_ft derivates of generic font type. + + * src/cairo-ps-surface.c: (_cairo_ps_surface_get_font), + (_cairo_ps_surface_show_glyphs), (_ps_output_show_glyphs): + * src/cairo-pdf-surface.c: (_cairo_pdf_document_get_font), + (_cairo_pdf_surface_show_glyphs): + * src/cairo-font-subset.c: (_cairo_font_subset_create): + Add explicit checks for cairo_ft derivatives of generic fonts + rather than just blindly assuming that's what we get. + +2005-07-12 Carl Worth <cworth@cworth.org> + + * src/cairo-hash-private.h: + * src/cairo-hash.c: + (_cairo_hash_table_create): Remove destroy notifier. This + simplifies the implementation a bit, and no anticipated use of + cairo_hash_table_t in cairo needs the destroy notifier. Most uses + will be hash-backed object create/destroy functions. + + (_cairo_hash_table_destroy): Document that it is now a fatal + error to call _cairo_hash_table_destroy on a non-empty hash table. + + (_cairo_hash_table_insert): Document that it is now a fatal + error to insert an entry with a key that matches an existing + entry. + + (_cairo_hash_table_random_entry): Add predicate function so that + the user can select a random entry satisying the given predicate. + + (_cairo_hash_table_remove): Change return type to void since + failure is really not possible here. + +2005-07-11 Carl Worth <cworth@cworth.org> + + * ROADMAP: Add cache lock deadlock problem to roadmap for 0.5.2. + Fix typo (cairo_ft_options_t -> cairo_font_options_t). + Add note that glyph measurement performance needs to improve. + + * src/cairoint.h: Disable mutex locks, (making the caches + non-thread-safe again, just like they were in the last snapshot + and before). + +2005-07-11 Carl Worth <cworth@cworth.org> + + * ROADMAP: Note that the cairo_content_t work is done. + Note progress on cairo_meta_surface_t, ARGB text, and group + support. + Add recent API change proposals (cairo_ft_options_t, + cairo_xlib_surface_create needs to be screen aware, and + cairo_xlib_surface_set_drawable). + + * src/cairo-arc.c (_arc_segments_needed): Note that this function + is computing an incorrect result. + +2005-07-08 Carl Worth <cworth@cworth.org> + + * src/cairo.h: Give enum tags an underscore prefix to match the + style of the struct tags. Add new cairo_content_t and change + cairo_surface_create_similar to accept a cairo_content_t rather + than a cairo_format_t. + + * src/cairoint.h: + * src/cairo-glitz-surface.c: (_cairo_glitz_surface_create_similar): + * src/cairo-image-surface.c: (_cairo_image_surface_create_similar): + * src/cairo-meta-surface.c: (_cairo_meta_surface_create_similar): + * src/cairo-pdf-surface.c: (_cairo_pdf_surface_create_similar): + * src/cairo-ps-surface.c: (_cairo_ps_surface_create_similar): + * src/cairo-quartz-surface.c: (_cairo_quartz_surface_create_similar): + * src/cairo-surface.c: (_cairo_surface_create_similar_scratch), + (cairo_surface_create_similar), + (_cairo_surface_create_similar_solid): + * src/cairo-win32-surface.c: (_cairo_win32_surface_create_similar), + (_cairo_win32_surface_get_subimage): + * src/cairo-xcb-surface.c: (_cairo_xcb_surface_create_similar), + (_cairo_xcb_surface_clone_similar): + * src/cairo-xlib-surface.c: (_cairo_xlib_surface_create_similar), + (_cairo_xlib_surface_clone_similar): Change surface backend + create_similar call to accept a cairo_content_t rather than a + cairo_format_t. + + * src/cairo-glitz-surface.c: (_glitz_format_from_content), + (_cairo_glitz_surface_clone_similar), + (_cairo_glitz_pattern_acquire_surface), + (_cairo_glitz_surface_fill_rectangles), + (_cairo_glitz_surface_composite_trapezoids): + * src/cairo-gstate.c: (_cairo_gstate_mask), + (_composite_traps_intermediate_surface), + (_cairo_gstate_intersect_clip_mask), (_cairo_gstate_show_glyphs): + * src/cairo-image-surface.c: (cairo_image_surface_create), + (cairo_image_surface_create_for_data), + (_cairo_format_from_content), (_cairo_content_from_format): + * src/cairo-pattern.c: (_cairo_pattern_acquire_surface_for_solid): + * src/cairo-ps-surface.c: (emit_image): + * test/mask.c: (mask_polygon), (draw): + * test/pixman-rotate.c: (draw): + * test/source-clip.c: (draw): Fix all calls into create_similar to + pass a cairo_content_t rather than a cairo_format_t. + +2005-07-07 Carl Worth <cworth@cworth.org> + + * CODING_STYLE: Add a missing word. + + * ROADMAP: Note that the BadMatch bug has been resolved. + +2005-07-06 Carl Worth <cworth@cworth.org> + + * src/cairo.h: + * src/cairo.c: (cairo_status_to_string): Remove + CAIRO_STATUS_NO_TARGET_SURFAC and add CAIRO_STATUS_INVALID_STATUS. + + * src/cairo-gstate.c: + (_cairo_gstate_clip_and_composite_trapezoids), + (_cairo_gstate_copy_page), (_cairo_gstate_show_page): Don't check + for gstate->target == NULL anymore as the API now guarantees it + never occurs. + + * src/cairo.c: (cairo_append_path): Check that path->status is a + valid status value and cause an INVALID_STATUS error otherwise. + + * test/path-data.c: (main): Test the new + CAIRO_STATUS_INVALID_STATUS error case in cairo_append_path. + +2005-07-06 Carl Worth <cworth@cworth.org> + + * configure.in: Require libpixman >= 0.1.5, (since 0.1.4 crashes + on some X servers). + +2005-07-05 Carl Worth <cworth@cworth.org> + + * src/cairo-ps-surface.c: Disable the compilation of the + _ps_locate_fallbacks code which, inaddition to being broken, + appears to not currently be used. + +2005-07-04 David Reveman <davidr@novell.com> + + * configure.in: Require glitz >= 0.4.4. + + * src/cairo-glitz-surface.c: Use frame buffer objects instead of + pbuffers for accelerated offscreen drawing. + (_cairo_glitz_pattern_acquire_surface): Minor improvement to gradient + pattern creation. + +2005-07-01 Kristian Høgsberg <krh@redhat.com> + + * src/cairo-ps-surface.c: Rewrite postscript backend to generate + more interesting output than the current big-image implementation, + using meta surfaces for font subsetting and image fallbacks. + + * src/cairo-meta-surface.c: Remove obsolete comment. + + * src/cairoint.h: + * src/cairo-output-stream.c: Make a couple of stylistic changes + and add _cairo_output_stream_write_hex_string. + + * src/cairo-surface.c: Add _cairo_surface_intersect_clip_path so + we can replay path clipping. + +2005-07-01 Kristian Høgsberg <krh@redhat.com> + + * src/cairo-meta-surface-private.h: + * src/cairo-meta-surface.c: Add meta surface implementation. + + * src/Makefile.am (libcairo_la_SOURCES): Add cairo-meta-surface.c + and cairo-meta-surface-private.h + +2005-06-29 Carl Worth <cworth@cworth.org> + + * src/cairo-hash.c (_cairo_hash_table_resize): Remove debugging + printfs. + +2005-06-29 Carl Worth <cworth@cworth.org> + + * src/cairo-hash-private.h: + * src/cairo-hash.c (_cairo_hash_table_random_entry): Add + _cairo_hash_table_random_entry. + + * src/cairo-hash.c: (_destroy_entry): Fix to update live_entries. + + * src/cairo-hash.c: (_cairo_hash_table_lookup_internal): style + changes. + + * src/cairo-hash.c (_cairo_hash_table_resize): Add code to shrink + table as well as to grow it. + + * src/cairo-hash.c (_cairo_hash_table_insert), + (_cairo_hash_table_remove): Call new version of resize so that + table will grow or shrink as needed on insert and remove. + +2005-06-29 Carl Worth <cworth@cworth.org> + + * src/cairo-hash-private.h: + * src/cairo-hash.c: (_cairo_hash_table_create), (_destroy_entry), + (_cairo_hash_table_destroy), (_cairo_hash_table_lookup_internal), + (_cairo_hash_table_resize), (_cairo_hash_table_lookup), + (_cairo_hash_table_insert), (_cairo_hash_table_remove), + (_cairo_hash_table_foreach): Rewrite hash table to use a single + cairo_hash_entry_t* rather than void *key and void *value. This is + slightly more painful to use, but lends itself to a more + memory-efficient implementation. Add documentation. + +2005-06-29 Carl Worth <cworth@cworth.org> + + * src/cairo-hash-private.h: + * src/cairo-hash.c: (_cairo_hash_table_create), + (_cairo_hash_table_destroy_entry), (_cairo_hash_table_destroy), + (_cairo_hash_table_lookup_internal), (_cairo_hash_table_resize), + (_cairo_hash_table_lookup), (_cairo_hash_table_insert), + (_cairo_hash_table_remove), (_cairo_hash_table_foreach): Rework + the cache code as a hast table with a much simpler interface, (no + object derviation is required to use it). + + * src/cairoint.h: Remove extraneous prototype for non-existent + _cairo_cache_reference. + +2005-06-28 Kristian Høgsberg <krh@redhat.com> + + * src/cairo-pattern.c (cairo_pattern_create_rgb) + (cairo_pattern_create_rgba): New functions to create a + cairo_pattern_t corresponding to a solid color and a translucent + color respectively. Document a few pattern functions. + +2005-06-28 T Rowley <tim.rowley@gmail.com> + + reviewed by: otaylor + + * src/cairo-win32-font.c (_cairo_win32_scaled_font_glyph_path): + Implement. + +2005-06-28 Owen Taylor <otaylor@redhat.com> + + * src/cairo-xlib-surface.c (_cairo_xlib_surface_create_internal): Also + flag older XFree86 servers as buggy-repeat. + +2005-06-28 T Rowley <tim.rowley@gmail.com> + + * src/cairo-atsui-font.c (_cairo_atsui_font_text_to_glyphs): + Prevent crash on empty string. + +2005-06-25 Keith Packard <keithp@keithp.com> + + reviewed by: cworth + + * configure.in: + * src/cairo-cache.c: (_cairo_cache_shrink_to), + (_cairo_cache_lookup): + * src/cairo-font.c: (_lock_global_simple_cache), + (_unlock_global_simple_cache), (_lock_global_font_cache), + (_unlock_global_font_cache), + (_cairo_lock_global_image_glyph_cache), + (_cairo_unlock_global_image_glyph_cache), + (_cairo_get_global_image_glyph_cache): + * src/cairo-ft-font.c: (_lock_global_ft_cache), + (_unlock_global_ft_cache): + * src/cairo-xlib-surface.c: (_xlib_glyphset_cache_create_entry), + (_xlib_glyphset_cache_destroy_entry), (_lock_xlib_glyphset_caches), + (_unlock_xlib_glyphset_caches), (_get_glyphset_cache), + (_cairo_xlib_surface_show_glyphs): + * src/cairoint.h: + Provide locking macros, implement with pthreads. + + Add _cairo_cache_shrink_to which reduces cache memory usage + to a specified level. + + Change global glyph and xlib glyphset caches behaviour to only + shrink cache on unlock. This is done by telling the cache code to + never shrink (max_memory == 0), and then manually shrinking using + _cairo_cache_shrink_to from the unlock function. + + Fix Carl's variable renaming mixing (cache = cache). + +2005-06-24 Owen Taylor <otaylor@redhat.com> + + * src/cairo-ft-font.c (_transform_glyph_bitmap): Add mostly + useless (other than perhaps 90-degree rotation) code for + transforming bitmap glyphs. + +2005-06-25 Carl Worth <cworth@cworth.org> + + From: J. Ali Harlow" <ali@avrc.city.ac.uk> + Reviewed by: Vladimir Vukicevic <vladimirv@gmail.com> + + * test/Makefile.am: + * src/cairo-win32-surface.c: (_cairo_win32_surface_create_for_dc), + (_cairo_win32_surface_create_similar), + (_cairo_win32_surface_create_dib), + (_cairo_win32_surface_composite), (categorize_solid_dest_operator), + (_cairo_win32_surface_set_clip_region): Update win32 backend to + match current API, so it actually builds once again. + +2005-06-25 Carl Worth <cworth@cworth.org> + + * src/cairo-win32-surface.c: (_cairo_win32_surface_create_for_dc), + (_cairo_win32_surface_create_similar), + (_cairo_win32_surface_create_dib), + (_cairo_win32_surface_composite), (categorize_solid_dest_operator), + (_cairo_win32_surface_set_clip_region): + * test/Makefile.am: + +2005-06-24 Carl Worth <cworth@cworth.org> + + * src/cairo-ft-font.c: (_cairo_ft_unscaled_font_create_glyph): + Remove stray character. + +2005-06-24 Carl Worth <cworth@cworth.org> + + * src/cairo-xlib-surface.c: (_xlib_glyphset_cache_create_entry), + (_glyphset_cache_entry_reference), + (_xlib_glyphset_cache_destroy_entry), (_get_glyphset_cache), + (_cairo_xlib_surface_show_glyphs32), + (_cairo_xlib_surface_show_glyphs16), + (_cairo_xlib_surface_show_glyphs8), + (_cairo_xlib_surface_show_glyphs): Prefer descriptive variable + names over single-character names. Remove unneeded XGlpyhInfo + field from glyphset_cache_entry_t. + +2005-06-24 Owen Taylor <otaylor@redhat.com> + + * src/cairo-ft-font.c (_render_glyph_bitmap): Handle rendering + bitmap glyphslots as well as outline glyphslots. + + * src/cairo-ft-font.c (_ft_unscaled_font_set_scale): When setting + the scale for a non-scalable font, use the nearest available + size (FreeType won't set the glyph metrics otherwise.) + +2005-06-23 Carl Worth <cworth@cworth.org> + + * ROADMAP: Add ARGB text to 1.0 roadmap. + +2005-05-17 Owen Taylor <otaylor@redhat.com> + + * src/cairo-png.c (write_png): Only unpremultiply ARGB32 data, + do a simpler conversion for RGB24 data (fixes #2297) + Call png_write_info() *before* we set up the write conversion... + it doesn't work after. + +2005-06-23 Carl Worth <cworth@cworth.org> + + * ROADMAP: Add workaround for Render's overlapping source/dest bug + to the 0.5.2 roadmap. + +2005-06-22 Carl Worth <cworth@cworth.org> + + * ROADMAP: Update ROADMAP with some comments on 0.5.2. + Add link to BadMatch bug. + + * src/cairo.c: Add documentation for cairo_set_source_surface and + fix some typos in other documentation blocks. + +2005-06-22 Øyvind Kolås <pippin@freedesktop.org> + + * doc/public/language-bindings.xml: spelling and whitespace fix. + +2005-06-21 Kristian Høgsberg <krh@redhat.com> + + * src/cairo-pdf-surface.c: Split out font subsetting code from here, + + * src/cairo-font-subset-private.h: + * src/cairo-font-subset.c: and put it here. + +2005-06-21 T Rowley <tim.rowley@gmail.com> + + * src/cairo-atsui-font.c: allow building against < 10.3 SDK. + +2005-06-20 Carl Worth <cworth@cworth.org> + + * configure.in: Add -head to CAIRO_VERSION after tagging with + SNAPSHOT_0_5_1. + 2005-06-20 Carl Worth <cworth@cworth.org> * Makefile.am: Force distcheck to enable gtk-doc. @@ -1,3 +1,77 @@ +Snapshot 0.5.2 (2005-07-18 Carl Worth <cworth@cworth.org>) +========================================================== +API changes +----------- +* New functions for creating patterns of a single color: + + cairo_pattern_create_rgb + cairo_pattern_create_rgba + +* Change cairo_surface_create_similar to accept a new type of + cairo_content_t rather than cairo_format_t: + + typedef enum _cairo_content { + CAIRO_CONTENT_COLOR = 0x1000, + CAIRO_CONTENT_ALPHA = 0x2000, + CAIRO_CONTENT_COLOR_ALPHA = 0x3000 + } cairo_content_t; + +* Add new CAIRO_FORMAT_VALID and CAIRO_CONTENT_VALID macros. + +* Remove unused status value: + + CAIRO_STATUS_NO_TARGET_SURFACE + +* Add new status values: + + CAIRO_STATUS_INVALID_STATUS + +* Require libpixman >= 0.1.5 (for necessary bug fixes) + +Bug fixes +--------- +* Fix cairo_surface_write_to_png for RGB24 images. + +* Fix broken metrics and rendering for bitmap fonts. Add mostly + useless bitmap glyph transformation. + +* Fix glyph caches to not eject entries that might be immediately + needed, (fixing intermittent crashes when rendering text). + +* Fix all memory leaks found by running "make check-valigrind". + +ATSUI backend changes +--------------------- +* Allow building against < 10.3 SDK. + +* Prevent crash on empty strings. + +Glitz backend changes +--------------------- +* Require glitz >= 0.4.4. + +* Use frame buffer objects instead of pbuffers for accelerated + offscreen drawing. + +* Minor improvement to gradient pattern creation. + +PostScript backend fixes +------------------------ +* Rewrite of the PS backend to generate more interesting output that + the old big-image implementation. + +Win32 backend fixes +------------------- +* Implement glyph path support. + +* Fix swap of blue and green values in the fill_rectangles path. + +Xlib backend fixes +------------------ +* Add optimization to use XCopyArea rather than XRenderComposite when + transforming only with an integer translation, and using SOURCE + operator or OVER with a source pattern without alpha. + Snapshot 0.5.1 (2005-06-20 Carl Worth <cworth@cworth.org>) ========================================================== API changes @@ -1,8 +1,26 @@ -Here are some quick notes as far as a cairo roadmap goes. - -See also the TODO file for a different view, (less organized, but -often in more detail, particularly for the API Shakeup work in -progress). +cairo 0.5.2 +=========== +✓ Get tor to sign off that the win32 stuff is up to snuff + ✓ glyph path patch reviewed and committed + +✓ Fix the BadMatch error introduced between 0.5.0 and 0.5.1: + https://bugs.freedesktop.org/show_bug.cgi?id=3604 + + (It turns out this was just exposing an old bug in libpixman + 0.1.4 which is already fixed in libpixman 0.1.5) + + Add a workaround for Render's overlapping source/dest bug + ✓ Use XCopyArea when possible (integer translation) + Otherwise make a copy of the source + + Fix the cache lock deadlocking problems. + Difficulty: Hard + Status: The cache code was ugly enough that I ended up doing a + major rewrite rather than just reviewing the + locking. The upside is that the rewrite should also + add the missing metrics caches which will fix some + performance problems with text measurement. Almost + done now. cairo 1.0 release requirements ============================== @@ -15,11 +33,15 @@ Implementation work I2. Real PostScript/PDF fallbacks (cairo_meta_surface_t) Difficulty: hard - Status: otaylor has drafted a plan or two on the list + Status: krh has committed cairo_meta_surface_t and a preliminary + version of cairo_ps_surface_t that uses it. + + I3. Add support for sub-pixel (ARGB) rendering of text. + Status: keithp walked cworth through this. Patch sent to the + list is almost complete. API Issues (more detail in TODO file) ------------------------------------- - ✓A1. Add cairo_paint Difficulty: moderate or moderate to minor Dependencies: much easier after [I1]. needs some cleanups from [A4] @@ -33,8 +55,10 @@ API Issues (more detail in TODO file) Difficulty: easy to hard (depending on how sophisticated an implementation is acceptable, and whether the cairo_meta_surface_t mentioned in [I2] is done) - Status: cworth sent API proposal to the list - (still some unresolved API issues) + + Status: cworth has a posted a preliminary patch, and keithp, + krh, and otaylor answered all the tough questions it + raised. There's not much work left to finish this one. ✓A4. Make set_source consistent Difficulty: easy @@ -63,11 +87,11 @@ API Issues (more detail in TODO file) based on it Status: cworth has sent API proposal to list - A8. cairo_format_t: +✓A8. cairo_content_t: Difficulty: moderate. It's just going through and examining each use of cairo_format_t, but there are a lot of them. - Status: not started, there is a rough plan in TODO + Status: Done. A9. consistent error handling for all objects Difficulty: Easy to implement to get the API right. Hard to test. @@ -75,6 +99,21 @@ API Issues (more detail in TODO file) Still need to do cairo_font_face_t, cairo_scaled_font_t, and cairo_surface_t. + A10. cairo_font_options_t + Difficulty: Moderate + Status: Owen has done all the hard thinking, and we've got + consensus on the API now. Owen's working on a patch. + + A11. cairo_xlib_surface_create needs to be screen-aware + Difficulty: Easy + Status: Keith has cooked up a patch with an APi that should be + sufficient. It still needs testing on multi-screen X + server. + + A12. cairo_xlib_surface_set_drawable + Difficulty: Easy + Status: Keith has a patch sitting ready on the list. + Performance work ---------------- ✓P1. Make pixel-aligned rectangle compositing fast @@ -84,3 +123,6 @@ Performance work P2. Generate better trapezoids to go easier on the rasterizer Difficulty: moderate to hard Status: cworth drafted a plan to the list + + P3. Glyph measurement needs to be sped up. + Status: Now planned as part of "cache lock deadlock" above. diff --git a/configure.in b/configure.in index c2305de50..7d6aa47d5 100644 --- a/configure.in +++ b/configure.in @@ -5,7 +5,7 @@ AC_INIT(src/cairo.h) dnl =========================================================================== # Package version number, (as distinct from shared library version) -CAIRO_VERSION=0.5.1 +CAIRO_VERSION=0.5.2 # libtool shared library version @@ -215,7 +215,7 @@ AC_ARG_ENABLE(glitz, [use_glitz=$enableval], [use_glitz=yes]) if test "x$use_glitz" = "xyes"; then - PKG_CHECK_MODULES(GLITZ, glitz >= 0.4.0, [ + PKG_CHECK_MODULES(GLITZ, glitz >= 0.4.4, [ GLITZ_REQUIRES=glitz use_glitz=yes], [use_glitz="no (requires glitz http://freedesktop.org/Software/glitz)"]) fi @@ -233,7 +233,7 @@ AC_SUBST(GLITZ_REQUIRES) dnl =========================================================================== -PKG_CHECK_MODULES(PIXMAN, libpixman >= 0.1.4) +PKG_CHECK_MODULES(PIXMAN, libpixman >= 0.1.5) CAIRO_CFLAGS="$CAIRO_CFLAGS $PIXMAN_CFLAGS" CAIRO_LIBS="$CAIRO_LIBS $PIXMAN_LIBS" @@ -307,6 +307,14 @@ AC_SUBST(FT_FONT_FEATURE) dnl =========================================================================== +# +# The FreeType backend uses pthread locking when avaialble +# + +AC_CHECK_HEADERS([pthread.h]) + +dnl =========================================================================== + AC_ARG_ENABLE(pdf, [ --disable-pdf Disable cairo's PDF backend], [use_pdf=$enableval], [use_pdf=yes]) diff --git a/doc/public/language-bindings.xml b/doc/public/language-bindings.xml index 1b999d82f..b0ee8e879 100644 --- a/doc/public/language-bindings.xml +++ b/doc/public/language-bindings.xml @@ -418,7 +418,7 @@ public void writeToPNG (OutputStream stream) throws IOException; </para></listitem> <listitem><para> Constructors, rather than - returning<constant>NULL</constant> on out-of-memory failure, + returning <constant>NULL</constant> on out-of-memory failure, return a special singleton object on which all operations do nothing. Retrieving the status of the singleton object returns <constant>CAIRO_STATUS_NO_MEMORY</constant> @@ -534,7 +534,7 @@ cairo_pattern_t Like patterns, surfaces, which use only the <link linkend="cairo-surface-t"><type>cairo_surface_t</type></link> - type in the C API should be broken up into a heirarchy of types + type in the C API should be broken up into a hierarchy of types in a language binding. </para> <programlisting> diff --git a/src/Makefile.am b/src/Makefile.am index 8c624f247..888105503 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,11 +2,13 @@ if CAIRO_HAS_PS_SURFACE libcairo_ps_headers = cairo-ps.h libcairo_ps_sources = cairo-ps-surface.c +libcairo_font_subset_sources = cairo-font-subset.c cairo-font-subset-private.h endif if CAIRO_HAS_PDF_SURFACE libcairo_pdf_headers = cairo-pdf.h libcairo_pdf_sources = cairo-pdf-surface.c +libcairo_font_subset_sources = cairo-font-subset.c cairo-font-subset-private.h endif if CAIRO_HAS_PNG_FUNCTIONS @@ -75,50 +77,53 @@ cairoinclude_HEADERS = \ lib_LTLIBRARIES = libcairo.la -libcairo_la_SOURCES = \ - cairo.c \ - cairo.h \ - cairo-private.h \ - cairo-arc.c \ - cairo-arc-private.h \ - cairo-array.c \ - cairo-cache.c \ - cairo-color.c \ - cairo-fixed.c \ - cairo-font.c \ - cairo-gstate.c \ - cairo-gstate-private.h \ - cairo-hull.c \ - cairo-image-surface.c \ - cairo-matrix.c \ - cairo-path.c \ - cairo-path-bounds.c \ - cairo-path-data.c \ - cairo-path-data-private.h \ - cairo-path-fill.c \ - cairo-path-fixed-private.h \ - cairo-path-stroke.c \ - cairo-pen.c \ - cairo-polygon.c \ - cairo-slope.c \ - cairo-spline.c \ - cairo-surface.c \ - cairo-traps.c \ - cairo-pattern.c \ - cairo-unicode.c \ - cairo-output-stream.c \ - cairo-wideint.c \ - cairo-wideint.h \ - $(libcairo_atsui_sources)\ - $(libcairo_ft_sources)\ - $(libcairo_ps_sources) \ - $(libcairo_pdf_sources) \ - $(libcairo_png_sources) \ - $(libcairo_xlib_sources)\ - $(libcairo_quartz_sources)\ - $(libcairo_xcb_sources) \ - $(libcairo_glitz_sources)\ - $(libcairo_win32_sources)\ +libcairo_la_SOURCES = \ + cairo.c \ + cairo.h \ + cairo-private.h \ + cairo-arc.c \ + cairo-arc-private.h \ + cairo-array.c \ + cairo-cache.c \ + cairo-color.c \ + cairo-fixed.c \ + cairo-font.c \ + cairo-gstate.c \ + cairo-gstate-private.h \ + cairo-hull.c \ + cairo-image-surface.c \ + cairo-matrix.c \ + cairo-path.c \ + cairo-path-bounds.c \ + cairo-path-data.c \ + cairo-path-data-private.h \ + cairo-path-fill.c \ + cairo-path-fixed-private.h \ + cairo-path-stroke.c \ + cairo-pen.c \ + cairo-polygon.c \ + cairo-slope.c \ + cairo-spline.c \ + cairo-surface.c \ + cairo-traps.c \ + cairo-pattern.c \ + cairo-unicode.c \ + cairo-output-stream.c \ + cairo-wideint.c \ + cairo-wideint.h \ + cairo-meta-surface.c \ + cairo-meta-surface-private.h \ + $(libcairo_atsui_sources) \ + $(libcairo_ft_sources) \ + $(libcairo_ps_sources) \ + $(libcairo_pdf_sources) \ + $(libcairo_font_subset_sources) \ + $(libcairo_png_sources) \ + $(libcairo_xlib_sources) \ + $(libcairo_quartz_sources) \ + $(libcairo_xcb_sources) \ + $(libcairo_glitz_sources) \ + $(libcairo_win32_sources) \ cairoint.h libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined diff --git a/src/cairo-arc.c b/src/cairo-arc.c index d3302cdd5..e653fcda5 100644 --- a/src/cairo-arc.c +++ b/src/cairo-arc.c @@ -102,6 +102,8 @@ _arc_max_angle_for_tolerance_normalized (double tolerance) return angle; } +/* XXX: The computation here if bogus. Correct math (with proof!) is + * available in _cairo_pen_vertices_needed. */ static int _arc_segments_needed (double angle, double radius, diff --git a/src/cairo-atsui-font.c b/src/cairo-atsui-font.c index 5c7ddf54a..bcb079150 100644 --- a/src/cairo-atsui-font.c +++ b/src/cairo-atsui-font.c @@ -38,7 +38,19 @@ #include "cairo-atsui.h" #include "cairoint.h" #include "cairo.h" +#if 0 #include <iconv.h> +#endif + +/* + * FixedToFloat/FloatToFixed are 10.3+ SDK items - include definitions + * here so we can use older SDKs. + */ +#ifndef FixedToFloat +#define fixed1 ((Fixed) 0x00010000L) +#define FixedToFloat(a) ((float)(a) / fixed1) +#define FloatToFixed(a) ((Fixed)((float)(a) * fixed1)) +#endif typedef struct { cairo_scaled_font_t base; @@ -240,29 +252,32 @@ _cairo_atsui_font_text_to_glyphs(void *abstract_font, ItemCount glyphCount, charCount; UniChar *theText; - err = ATSUCreateTextLayout(&textLayout); - -#if 1 + // liberal estimate of size charCount = strlen(utf8); + if (charCount == 0) { + *glyphs = NULL; + *num_glyphs = 0; + return CAIRO_STATUS_SUCCESS; + } + // Set the text in the text layout object, so we can measure it theText = (UniChar *) malloc(charCount * sizeof(UniChar)); +#if 1 for (i = 0; i < charCount; i++) { theText[i] = utf8[i]; } #endif #if 0 - // Set the text in the text layout object, so we can measure it - charCount = strlen(utf8); - theText = (UniChar *) malloc(charCount * sizeof(UniChar)); - size_t inBytes = charCount, outBytes = charCount; iconv_t converter = iconv_open("UTF-8", "UTF-16"); charCount = iconv(converter, utf8, &inBytes, theText, &outBytes); #endif + err = ATSUCreateTextLayout(&textLayout); + err = ATSUSetTextPointerLocation(textLayout, theText, 0, charCount, charCount); diff --git a/src/cairo-cache.c b/src/cairo-cache.c index e7547bc29..a0c202223 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -383,6 +383,19 @@ _cairo_cache_destroy (cairo_cache_t *cache) cache->backend->destroy_cache (cache); } +void +_cairo_cache_shrink_to (cairo_cache_t *cache, + unsigned long max_memory) +{ + unsigned long idx; + /* Make some entries die if we're under memory pressure. */ + while (cache->live_entries > 0 && cache->used_memory > max_memory) { + idx = _random_entry (cache, NULL) - cache->entries; + assert (idx < cache->arrangement->size); + _entry_destroy (cache, idx); + } +} + cairo_status_t _cairo_cache_lookup (cairo_cache_t *cache, void *key, @@ -390,7 +403,6 @@ _cairo_cache_lookup (cairo_cache_t *cache, int *created_entry) { - unsigned long idx; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_cache_entry_base_t **slot = NULL, *new_entry; @@ -440,14 +452,8 @@ _cairo_cache_lookup (cairo_cache_t *cache, /* Store the hash value in case the backend forgot. */ new_entry->hashcode = cache->backend->hash (cache, key); - /* Make some entries die if we're under memory pressure. */ - while (cache->live_entries > 0 && - cache->max_memory > 0 && - ((cache->max_memory - cache->used_memory) < new_entry->memory)) { - idx = _random_entry (cache, NULL) - cache->entries; - assert (idx < cache->arrangement->size); - _entry_destroy (cache, idx); - } + if (cache->live_entries && cache->max_memory) + _cairo_cache_shrink_to (cache, cache->max_memory); /* Can't assert this; new_entry->memory may be larger than max_memory */ /* assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); */ diff --git a/src/cairo-font-subset-private.h b/src/cairo-font-subset-private.h new file mode 100644 index 000000000..5b43f52db --- /dev/null +++ b/src/cairo-font-subset-private.h @@ -0,0 +1,68 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg <krh@redhat.com> + */ + +#include "cairoint.h" + +#ifndef CAIRO_FONT_SUBSET_PRIVATE_H +#define CAIRO_FONT_SUBSET_PRIVATE_H + +typedef struct cairo_font_subset_backend cairo_font_subset_backend_t; +typedef struct cairo_font_subset cairo_font_subset_t; +struct cairo_font_subset { + cairo_font_subset_backend_t *backend; + cairo_unscaled_font_t *unscaled_font; + unsigned int font_id; + char *base_font; + int num_glyphs; + int *widths; + long x_min, y_min, x_max, y_max; + long ascent, descent; +}; + + +cairo_private int +_cairo_font_subset_use_glyph (cairo_font_subset_t *font, int glyph); + +cairo_private cairo_status_t +_cairo_font_subset_generate (cairo_font_subset_t *font, + const char **data, unsigned long *length); + +cairo_private void +_cairo_font_subset_destroy (cairo_font_subset_t *font); + +cairo_private cairo_font_subset_t * +_cairo_font_subset_create (cairo_unscaled_font_t *unscaled_font); + +#endif /* CAIRO_FONT_SUBSET_PRIVATE_H */ diff --git a/src/cairo-font-subset.c b/src/cairo-font-subset.c new file mode 100644 index 000000000..e3a2784cf --- /dev/null +++ b/src/cairo-font-subset.c @@ -0,0 +1,649 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg <krh@redhat.com> + */ + +#include "cairoint.h" +#include "cairo-pdf.h" +/* XXX: Eventually, we need to handle other font backends */ +#include "cairo-font-subset-private.h" +#include "cairo-ft-private.h" + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_TABLES_H + +typedef struct ft_subset_glyph ft_subset_glyph_t; +struct ft_subset_glyph { + int parent_index; + unsigned long location; +}; + +struct cairo_font_subset_backend { + int (*use_glyph) (void *abstract_font, + int glyph); + cairo_status_t (*generate) (void *abstract_font, + const char **data, + unsigned long *length); + void (*destroy) (void *abstract_font); +}; + +typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t; +struct cairo_pdf_ft_font { + cairo_font_subset_t base; + ft_subset_glyph_t *glyphs; + FT_Face face; + int checksum_index; + cairo_array_t output; + int *parent_to_subset; + cairo_status_t status; +}; + +#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) + +#define SFNT_VERSION 0x00010000 + +#ifdef WORDS_BIGENDIAN + +#define cpu_to_be16(v) (v) +#define be16_to_cpu(v) (v) +#define cpu_to_be32(v) (v) +#define be32_to_cpu(v) (v) + +#else + +static inline unsigned short +cpu_to_be16(unsigned short v) +{ + return (v << 8) | (v >> 8); +} + +static inline unsigned short +be16_to_cpu(unsigned short v) +{ + return cpu_to_be16 (v); +} + +static inline unsigned long +cpu_to_be32(unsigned long v) +{ + return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); +} + +static inline unsigned long +be32_to_cpu(unsigned long v) +{ + return cpu_to_be32 (v); +} + +#endif + +static cairo_font_subset_backend_t cairo_pdf_ft_font_backend; + +int +_cairo_font_subset_use_glyph (cairo_font_subset_t *font, int glyph) +{ + return font->backend->use_glyph (font, glyph); +} + +cairo_status_t +_cairo_font_subset_generate (cairo_font_subset_t *font, + const char **data, unsigned long *length) +{ + return font->backend->generate (font, data, length); +} + +void +_cairo_font_subset_destroy (cairo_font_subset_t *font) +{ + font->backend->destroy (font); +} + +cairo_font_subset_t * +_cairo_font_subset_create (cairo_unscaled_font_t *unscaled_font) +{ + FT_Face face; + cairo_pdf_ft_font_t *font; + unsigned long size; + int i, j; + + /* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */ + if (! _cairo_unscaled_font_is_ft (unscaled_font)) + return NULL; + + face = _cairo_ft_unscaled_font_lock_face (unscaled_font); + + /* We currently only support freetype truetype fonts. */ + size = 0; + if (!FT_IS_SFNT (face) || + FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0) + return NULL; + + font = malloc (sizeof (cairo_pdf_ft_font_t)); + if (font == NULL) + return NULL; + + font->base.unscaled_font = unscaled_font; + _cairo_unscaled_font_reference (unscaled_font); + font->base.backend = &cairo_pdf_ft_font_backend; + + _cairo_array_init (&font->output, sizeof (char)); + if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) + goto fail1; + + font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); + if (font->glyphs == NULL) + goto fail2; + + font->parent_to_subset = calloc (face->num_glyphs, sizeof (int)); + if (font->parent_to_subset == NULL) + goto fail3; + + font->base.num_glyphs = 1; + font->base.x_min = face->bbox.xMin; + font->base.y_min = face->bbox.yMin; + font->base.x_max = face->bbox.xMax; + font->base.y_max = face->bbox.yMax; + font->base.ascent = face->ascender; + font->base.descent = face->descender; + font->base.base_font = strdup (face->family_name); + if (font->base.base_font == NULL) + goto fail4; + + for (i = 0, j = 0; font->base.base_font[j]; j++) { + if (font->base.base_font[j] == ' ') + continue; + font->base.base_font[i++] = font->base.base_font[j]; + } + font->base.base_font[i] = '\0'; + + font->base.widths = calloc (face->num_glyphs, sizeof (int)); + if (font->base.widths == NULL) + goto fail5; + + _cairo_ft_unscaled_font_unlock_face (unscaled_font); + + font->status = CAIRO_STATUS_SUCCESS; + + return &font->base; + + fail5: + free (font->base.base_font); + fail4: + free (font->parent_to_subset); + fail3: + free (font->glyphs); + fail2: + _cairo_array_fini (&font->output); + fail1: + free (font); + return NULL; +} + +static void +cairo_pdf_ft_font_destroy (void *abstract_font) +{ + cairo_pdf_ft_font_t *font = abstract_font; + + _cairo_unscaled_font_destroy (font->base.unscaled_font); + free (font->base.base_font); + free (font->parent_to_subset); + free (font->glyphs); + _cairo_array_fini (&font->output); + free (font); +} + +static void * +cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font, + const void *data, size_t length) +{ + void *p; + + p = _cairo_array_append (&font->output, data, length); + if (p == NULL) + font->status = CAIRO_STATUS_NO_MEMORY; + + return p; +} + +static void +cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font, + unsigned short value) +{ + unsigned short be16_value; + + be16_value = cpu_to_be16 (value); + cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value); +} + +static void +cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value) +{ + unsigned long be32_value; + + be32_value = cpu_to_be32 (value); + cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value); +} + +static unsigned long +cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font) +{ + int length, aligned; + static const char pad[4]; + + length = _cairo_array_num_elements (&font->output); + aligned = (length + 3) & ~3; + cairo_pdf_ft_font_write (font, pad, aligned - length); + + return aligned; +} + +static int +cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag) +{ + int i; + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); + + cairo_pdf_ft_font_write_be16 (font, 1); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be32 (font, 12); + + /* Output a format 6 encoding table. */ + + cairo_pdf_ft_font_write_be16 (font, 6); + cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1)); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */ + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1); + for (i = 1; i < font->base.num_glyphs; i++) + cairo_pdf_ft_font_write_be16 (font, i); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned char *buffer; + unsigned long size; + + size = 0; + FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size); + buffer = cairo_pdf_ft_font_write (font, NULL, size); + FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size); + + return 0; +} + +static int +cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned long start_offset, index, size; + TT_Header *header; + unsigned long begin, end; + unsigned char *buffer; + int i; + union { + unsigned char *bytes; + unsigned short *short_offsets; + unsigned long *long_offsets; + } u; + + header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + if (header->Index_To_Loc_Format == 0) + size = sizeof (short) * (font->face->num_glyphs + 1); + else + size = sizeof (long) * (font->face->num_glyphs + 1); + + u.bytes = malloc (size); + if (u.bytes == NULL) { + font->status = CAIRO_STATUS_NO_MEMORY; + return font->status; + } + FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size); + + start_offset = _cairo_array_num_elements (&font->output); + for (i = 0; i < font->base.num_glyphs; i++) { + index = font->glyphs[i].parent_index; + if (header->Index_To_Loc_Format == 0) { + begin = be16_to_cpu (u.short_offsets[index]) * 2; + end = be16_to_cpu (u.short_offsets[index + 1]) * 2; + } + else { + begin = be32_to_cpu (u.long_offsets[index]); + end = be32_to_cpu (u.long_offsets[index + 1]); + } + + size = end - begin; + + font->glyphs[i].location = + cairo_pdf_ft_font_align_output (font) - start_offset; + buffer = cairo_pdf_ft_font_write (font, NULL, size); + if (buffer == NULL) + break; + FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size); + /* FIXME: remap composite glyphs */ + } + + font->glyphs[i].location = + cairo_pdf_ft_font_align_output (font) - start_offset; + + free (u.bytes); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + TT_Header *head; + + head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + + cairo_pdf_ft_font_write_be32 (font, head->Table_Version); + cairo_pdf_ft_font_write_be32 (font, head->Font_Revision); + + font->checksum_index = _cairo_array_num_elements (&font->output); + cairo_pdf_ft_font_write_be32 (font, 0); + cairo_pdf_ft_font_write_be32 (font, head->Magic_Number); + + cairo_pdf_ft_font_write_be16 (font, head->Flags); + cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM); + + cairo_pdf_ft_font_write_be32 (font, head->Created[0]); + cairo_pdf_ft_font_write_be32 (font, head->Created[1]); + cairo_pdf_ft_font_write_be32 (font, head->Modified[0]); + cairo_pdf_ft_font_write_be32 (font, head->Modified[1]); + + cairo_pdf_ft_font_write_be16 (font, head->xMin); + cairo_pdf_ft_font_write_be16 (font, head->yMin); + cairo_pdf_ft_font_write_be16 (font, head->xMax); + cairo_pdf_ft_font_write_be16 (font, head->yMax); + + cairo_pdf_ft_font_write_be16 (font, head->Mac_Style); + cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM); + + cairo_pdf_ft_font_write_be16 (font, head->Font_Direction); + cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format); + cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format); + + return font->status; +} + +static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag) +{ + TT_HoriHeader *hhea; + + hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea); + + cairo_pdf_ft_font_write_be32 (font, hhea->Version); + cairo_pdf_ft_font_write_be16 (font, hhea->Ascender); + cairo_pdf_ft_font_write_be16 (font, hhea->Descender); + cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap); + + cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max); + + cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing); + cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing); + cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset); + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + + cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format); + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned long entry_size; + short *p; + int i; + + for (i = 0; i < font->base.num_glyphs; i++) { + entry_size = 2 * sizeof (short); + p = cairo_pdf_ft_font_write (font, NULL, entry_size); + FT_Load_Sfnt_Table (font->face, TTAG_hmtx, + font->glyphs[i].parent_index * entry_size, + (FT_Byte *) p, &entry_size); + font->base.widths[i] = be16_to_cpu (p[0]); + } + + return font->status; +} + +static int +cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + int i; + TT_Header *header; + + header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + + if (header->Index_To_Loc_Format == 0) { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2); + } + else { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location); + } + + return font->status; +} + +static int +cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + TT_MaxProfile *maxp; + + maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp); + + cairo_pdf_ft_font_write_be32 (font, maxp->version); + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxContours); + cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours); + cairo_pdf_ft_font_write_be16 (font, maxp->maxZones); + cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage); + cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements); + cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions); + cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements); + cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth); + + return font->status; +} + +typedef struct table table_t; +struct table { + unsigned long tag; + int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag); +}; + +static const table_t truetype_tables[] = { + { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table }, + { TTAG_cvt, cairo_pdf_ft_font_write_generic_table }, + { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table }, + { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table }, + { TTAG_head, cairo_pdf_ft_font_write_head_table }, + { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table }, + { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table }, + { TTAG_loca, cairo_pdf_ft_font_write_loca_table }, + { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table }, + { TTAG_name, cairo_pdf_ft_font_write_generic_table }, + { TTAG_prep, cairo_pdf_ft_font_write_generic_table }, +}; + +static cairo_status_t +cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font) +{ + unsigned short search_range, entry_selector, range_shift; + int num_tables; + + num_tables = ARRAY_LENGTH (truetype_tables); + search_range = 1; + entry_selector = 0; + while (search_range * 2 <= num_tables) { + search_range *= 2; + entry_selector++; + } + search_range *= 16; + range_shift = num_tables * 16 - search_range; + + cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION); + cairo_pdf_ft_font_write_be16 (font, num_tables); + cairo_pdf_ft_font_write_be16 (font, search_range); + cairo_pdf_ft_font_write_be16 (font, entry_selector); + cairo_pdf_ft_font_write_be16 (font, range_shift); + + cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16); + + return font->status; +} + +static unsigned long +cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font, + unsigned long start, unsigned long end) +{ + unsigned long *padded_end; + unsigned long *p; + unsigned long checksum; + char *data; + + checksum = 0; + data = _cairo_array_index (&font->output, 0); + p = (unsigned long *) (data + start); + padded_end = (unsigned long *) (data + ((end + 3) & ~3)); + while (p < padded_end) + checksum += *p++; + + return checksum; +} + +static void +cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag, + unsigned long start, unsigned long end) +{ + unsigned long *entry; + + entry = _cairo_array_index (&font->output, 12 + 16 * index); + entry[0] = cpu_to_be32 (tag); + entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end)); + entry[2] = cpu_to_be32 (start); + entry[3] = cpu_to_be32 (end - start); +} + +static cairo_status_t +cairo_pdf_ft_font_generate (void *abstract_font, + const char **data, unsigned long *length) +{ + cairo_pdf_ft_font_t *font = abstract_font; + unsigned long start, end, next, checksum, *checksum_location; + int i; + + font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font); + + if (cairo_pdf_ft_font_write_offset_table (font)) + goto fail; + + start = cairo_pdf_ft_font_align_output (font); + end = start; + + end = 0; + for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { + if (truetype_tables[i].write (font, truetype_tables[i].tag)) + goto fail; + + end = _cairo_array_num_elements (&font->output); + next = cairo_pdf_ft_font_align_output (font); + cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag, + start, end); + start = next; + } + + checksum = + 0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end); + checksum_location = _cairo_array_index (&font->output, font->checksum_index); + *checksum_location = cpu_to_be32 (checksum); + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + fail: + _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font); + font->face = NULL; + + return font->status; +} + +static int +cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph) +{ + cairo_pdf_ft_font_t *font = abstract_font; + + if (font->parent_to_subset[glyph] == 0) { + font->parent_to_subset[glyph] = font->base.num_glyphs; + font->glyphs[font->base.num_glyphs].parent_index = glyph; + font->base.num_glyphs++; + } + + return font->parent_to_subset[glyph]; +} + +static cairo_font_subset_backend_t cairo_pdf_ft_font_backend = { + cairo_pdf_ft_font_use_glyph, + cairo_pdf_ft_font_generate, + cairo_pdf_ft_font_destroy +}; diff --git a/src/cairo-font.c b/src/cairo-font.c index 0aeb00b61..a74cac453 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -183,16 +183,18 @@ typedef struct { static const cairo_cache_backend_t _cairo_simple_font_cache_backend; +CAIRO_MUTEX_DECLARE(_global_simple_cache_mutex); + static void _lock_global_simple_cache (void) { - /* FIXME: Perform locking here. */ + CAIRO_MUTEX_LOCK (_global_simple_cache_mutex); } static void _unlock_global_simple_cache (void) { - /* FIXME: Perform locking here. */ + CAIRO_MUTEX_UNLOCK (_global_simple_cache_mutex); } static cairo_cache_t * @@ -358,7 +360,8 @@ _cairo_simple_font_face_create_font (void *abstract_face, const cairo_matrix_t *ctm, cairo_scaled_font_t **scaled_font) { - const cairo_scaled_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; + const cairo_scaled_font_backend_t * backend = CAIRO_SCALED_FONT_BACKEND_DEFAULT; + cairo_simple_font_face_t *simple_face = abstract_face; return backend->create (simple_face->family, simple_face->slant, simple_face->weight, @@ -383,7 +386,7 @@ static const cairo_font_face_backend_t _cairo_simple_font_face_backend = { * Return value: a newly created #cairo_font_face_t, destroy with * cairo_font_face_destroy() **/ -cairo_private cairo_font_face_t * +cairo_font_face_t * _cairo_simple_font_face_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) @@ -451,16 +454,18 @@ typedef struct { static const cairo_cache_backend_t _cairo_outer_font_cache_backend; static const cairo_cache_backend_t _cairo_inner_font_cache_backend; +CAIRO_MUTEX_DECLARE(_global_font_cache_mutex); + static void _lock_global_font_cache (void) { - /* FIXME: Perform locking here. */ + CAIRO_MUTEX_LOCK (_global_font_cache_mutex); } static void _unlock_global_font_cache (void) { - /* FIXME: Perform locking here. */ + CAIRO_MUTEX_UNLOCK (_global_font_cache_mutex); } static cairo_cache_t * @@ -1177,21 +1182,25 @@ static const cairo_cache_backend_t cairo_image_cache_backend = { _image_glyph_cache_destroy_cache }; +CAIRO_MUTEX_DECLARE(_global_image_glyph_cache_mutex); + +static cairo_cache_t * +_global_image_glyph_cache = NULL; + void _cairo_lock_global_image_glyph_cache() { - /* FIXME: implement locking. */ + CAIRO_MUTEX_LOCK (_global_image_glyph_cache_mutex); } void _cairo_unlock_global_image_glyph_cache() { - /* FIXME: implement locking. */ + _cairo_cache_shrink_to (_global_image_glyph_cache, + CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT); + CAIRO_MUTEX_UNLOCK (_global_image_glyph_cache_mutex); } -static cairo_cache_t * -_global_image_glyph_cache = NULL; - cairo_cache_t * _cairo_get_global_image_glyph_cache () { @@ -1203,7 +1212,7 @@ _cairo_get_global_image_glyph_cache () if (_cairo_cache_init (_global_image_glyph_cache, &cairo_image_cache_backend, - CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT)) + 0)) goto FAIL; } diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 63f51a51c..99776764b 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -34,6 +34,8 @@ * Owen Taylor <otaylor@redhat.com> */ +#include <float.h> + #include "cairo-ft-private.h" #include <fontconfig/fontconfig.h> @@ -84,10 +86,11 @@ typedef struct { int id; /* We temporarily scale the unscaled font as neede */ - int have_scale; + cairo_bool_t have_scale; cairo_matrix_t current_scale; double x_scale; /* Extracted X scale factor */ double y_scale; /* Extracted Y scale factor */ + cairo_bool_t have_shape; /* true if the current scale has a non-scale component*/ int lock; /* count of how many times this font has been locked */ @@ -126,6 +129,12 @@ _ft_unscaled_font_create_from_face (FT_Face face) return unscaled; } +cairo_bool_t +_cairo_unscaled_font_is_ft (cairo_unscaled_font_t *unscaled_font) +{ + return unscaled_font->backend == &cairo_ft_unscaled_font_backend; +} + static ft_unscaled_font_t * _ft_unscaled_font_create_from_filename (const char *filename, int id) @@ -271,16 +280,18 @@ static const cairo_cache_backend_t _ft_font_cache_backend = { static ft_cache_t *_global_ft_cache = NULL; +CAIRO_MUTEX_DECLARE(_global_ft_cache_mutex); + static void _lock_global_ft_cache (void) { - /* FIXME: Perform locking here. */ + CAIRO_MUTEX_LOCK(_global_ft_cache_mutex); } static void _unlock_global_ft_cache (void) { - /* FIXME: Perform locking here. */ + CAIRO_MUTEX_UNLOCK(_global_ft_cache_mutex); } static cairo_cache_t * @@ -448,6 +459,7 @@ _ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, { ft_font_transform_t sf; FT_Matrix mat; + FT_UInt pixel_width, pixel_height; assert (unscaled->face != NULL); @@ -471,11 +483,35 @@ _ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); - FT_Set_Transform(unscaled->face, &mat, NULL); + unscaled->have_shape = (mat.xx != 0x10000 || + mat.yx != 0x00000 || + mat.xy != 0x00000 || + mat.yy != 0x10000); - FT_Set_Pixel_Sizes(unscaled->face, - (FT_UInt) sf.x_scale, - (FT_UInt) sf.y_scale); + FT_Set_Transform(unscaled->face, &mat, NULL); + + if ((unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) { + pixel_width = sf.x_scale; + pixel_height = sf.y_scale; + } else { + double min_distance = DBL_MAX; + int i; + + pixel_width = pixel_height = 0; + + for (i = 0; i < unscaled->face->num_fixed_sizes; i++) { + double size = unscaled->face->available_sizes[i].y_ppem / 64.; + double distance = fabs (size - sf.y_scale); + + if (distance <= min_distance) { + min_distance = distance; + pixel_width = unscaled->face->available_sizes[i].x_ppem >> 6; + pixel_height = unscaled->face->available_sizes[i].y_ppem >> 6; + } + } + } + + FT_Set_Pixel_Sizes (unscaled->face, pixel_width, pixel_height); } static void @@ -515,59 +551,32 @@ _cairo_ft_unscaled_font_destroy (void *abstract_font) } } -static cairo_status_t -_cairo_ft_unscaled_font_create_glyph (void *abstract_font, - cairo_image_glyph_cache_entry_t *val) +/* Converts an outline FT_GlyphSlot into an image + * + * This could go through _render_glyph_bitmap as well, letting + * FreeType convert the outline to a bitmap, but doing it ourselves + * has two minor advantages: first, we save a copy of the bitmap + * buffer: we can directly use the buffer that FreeType renders + * into. + * + * Second, it may help when we add support for subpixel + * rendering: the Xft code does it this way. (Keith thinks that + * it may also be possible to get the subpixel rendering with + * FT_Render_Glyph: something worth looking into in more detail + * when we add subpixel support. If so, we may want to eliminate + * this version of the code path entirely. + */ +static cairo_status_t +_render_glyph_outline (FT_Face face, + cairo_image_glyph_cache_entry_t *val) { - ft_unscaled_font_t *unscaled = abstract_font; - FT_GlyphSlot glyphslot; + FT_GlyphSlot glyphslot = face->glyph; + FT_Outline *outline = &glyphslot->outline; unsigned int width, height, stride; - FT_Face face; - FT_Outline *outline; - FT_BBox cbox; FT_Bitmap bitmap; - FT_Glyph_Metrics *metrics; + FT_BBox cbox; cairo_status_t status = CAIRO_STATUS_SUCCESS; - face = _ft_unscaled_font_lock_face (unscaled); - if (!face) - return CAIRO_STATUS_NO_MEMORY; - - glyphslot = face->glyph; - metrics = &glyphslot->metrics; - - _ft_unscaled_font_set_scale (unscaled, &val->key.scale); - - if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinates of the bearing and advance need to be negated. - * - * Scale metrics back to glyph space from the scaled glyph space returned - * by FreeType - */ - - val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; - - val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; - val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; - - /* - * use untransformed advance values - * XXX uses horizontal advance only at present; - should provide FT_LOAD_VERTICAL_LAYOUT - */ - - val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; - val->extents.y_advance = 0 / unscaled->y_scale; - - outline = &glyphslot->outline; - FT_Outline_Get_CBox (outline, &cbox); cbox.xMin &= -64; @@ -583,7 +592,7 @@ _cairo_ft_unscaled_font_create_glyph (void *abstract_ val->image = NULL; } else { - bitmap.pixel_mode = ft_pixel_mode_grays; + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; bitmap.num_grays = 256; bitmap.width = width; bitmap.rows = height; @@ -591,16 +600,14 @@ _cairo_ft_unscaled_font_create_glyph (void *abstract_ bitmap.buffer = calloc (1, stride * height); if (bitmap.buffer == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; + return CAIRO_STATUS_NO_MEMORY; } FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { free (bitmap.buffer); - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; + return CAIRO_STATUS_NO_MEMORY; } val->image = (cairo_image_surface_t *) @@ -609,8 +616,7 @@ _cairo_ft_unscaled_font_create_glyph (void *abstract_ width, height, stride); if (val->image == NULL) { free (bitmap.buffer); - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; + return CAIRO_STATUS_NO_MEMORY; } _cairo_image_surface_assume_ownership_of_data (val->image); @@ -625,10 +631,295 @@ _cairo_ft_unscaled_font_create_glyph (void *abstract_ val->size.height = (unsigned short) height; val->size.x = (short) (cbox.xMin >> 6); val->size.y = - (short) (cbox.yMax >> 6); + + return status; +} + +/* Converts a bitmap (or other) FT_GlyphSlot into an image + * + * This could go through _render_glyph_bitmap as well, letting + * FreeType convert the outline to a bitmap, but doing it ourselves + * has two minor advantages: first, we save a copy of the bitmap + * buffer: we can directly use the buffer that FreeType renders + * into. + * + * Second, it may help when we add support for subpixel + * rendering: the Xft code does it this way. (Keith thinks that + * it may also be possible to get the subpixel rendering with + * FT_Render_Glyph: something worth looking into in more detail + * when we add subpixel support. If so, we may want to eliminate + * this version of the code path entirely. + */ +static cairo_status_t +_render_glyph_bitmap (FT_Face face, + cairo_image_glyph_cache_entry_t *val) +{ + FT_GlyphSlot glyphslot = face->glyph; + FT_Bitmap *bitmap; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int width, height, stride; + unsigned char *data; + FT_Error error; + int i, j; + + /* According to the FreeType docs, glyphslot->format could be + * something other than FT_GLYPH_FORMAT_OUTLINE or + * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType + * the opportunity to convert such to + * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since + * we avoid the FT_LOAD_NO_RECURSE flag. + */ + error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); + if (error) + return CAIRO_STATUS_NO_MEMORY; + + bitmap = &glyphslot->bitmap; + + width = bitmap->width; + height = bitmap->rows; + + if (width * height == 0) { + val->image = NULL; + } else { + switch (bitmap->pixel_mode) { + case FT_PIXEL_MODE_MONO: + stride = (width + 3) & ~3; + data = calloc (stride * height, 1); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + for (j = 0; j < height; j++) { + const unsigned char *p = bitmap->buffer + j * bitmap->pitch; + unsigned char *q = data + j * stride; + for (i = 0; i < width; i++) { + /* FreeType bitmaps are always stored MSB */ + unsigned char byte = p[i >> 3]; + unsigned char bit = 1 << (7 - (i % 8)); + + if (byte & bit) + q[i] = 0xff; + } + } + break; + case FT_PIXEL_MODE_GRAY: + stride = bitmap->pitch; + data = malloc (stride * height); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + memcpy (data, bitmap->buffer, stride * height); + break; + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + /* These could be triggered by very rare types of TrueType fonts */ + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + /* These should never be triggered unless we ask for them */ + default: + return CAIRO_STATUS_NO_MEMORY; + } + + val->image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_A8, + width, height, stride); + if (val->image == NULL) { + free (data); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_image_surface_assume_ownership_of_data (val->image); + } + + val->size.width = width; + val->size.height = height; + val->size.x = - glyphslot->bitmap_left; + val->size.y = - glyphslot->bitmap_top; + + return status; +} + +static cairo_status_t +_transform_glyph_bitmap (cairo_image_glyph_cache_entry_t *val) +{ + ft_font_transform_t sf; + cairo_matrix_t original_to_transformed; + cairo_matrix_t transformed_to_original; + cairo_image_surface_t *old_image; + cairo_surface_t *image; + double x[4], y[4]; + double origin_x, origin_y; + int i; + int x_min, y_min, x_max, y_max; + int width, height; + cairo_status_t status; + cairo_surface_pattern_t pattern; + + /* We want to compute a transform that takes the origin + * (val->size.x, val->size.y) to 0,0, then applies the "shape" + * portion of the font transform + */ + _compute_transform (&sf, &val->key.scale); + + cairo_matrix_init (&original_to_transformed, + sf.shape[0][0], sf.shape[0][1], + sf.shape[1][0], sf.shape[1][1], + 0, 0); + + cairo_matrix_translate (&original_to_transformed, + val->size.x, val->size.y); + + /* Find the bounding box of the original bitmap under that + * transform + */ + x[0] = 0; y[0] = 0; + x[1] = val->size.width; y[1] = 0; + x[2] = val->size.width; y[2] = val->size.height; + x[3] = 0; y[3] = val->size.height; + + for (i = 0; i < 4; i++) + cairo_matrix_transform_point (&original_to_transformed, + &x[i], &y[i]); + + x_min = floor (x[0]); y_min = floor (y[0]); + x_max = ceil (x[0]); y_max = ceil (y[0]); + + for (i = 1; i < 4; i++) { + if (x[i] < x_min) + x_min = floor (x[i]); + if (x[i] > x_max) + x_max = ceil (x[i]); + if (y[i] < y_min) + y_min = floor (y[i]); + if (y[i] > y_max) + y_max = ceil (y[i]); + } + + /* Adjust the transform so that the bounding box starts at 0,0 ... + * this gives our final transform from original bitmap to transformed + * bitmap. + */ + original_to_transformed.x0 -= x_min; + original_to_transformed.y0 -= y_min; + + /* Create the transformed bitmap + */ + width = x_max - x_min; + height = y_max - y_min; + + transformed_to_original = original_to_transformed; + status = cairo_matrix_invert (&transformed_to_original); + if (status) + return status; + + /* We need to pad out the width to 32-bit intervals for cairo-xlib-surface.c */ + width = (width + 3) & ~3; + image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + if (!image) + return CAIRO_STATUS_NO_MEMORY; + + /* Initialize it to empty + */ + _cairo_surface_fill_rectangle (image, CAIRO_OPERATOR_SOURCE, + CAIRO_COLOR_TRANSPARENT, + 0, 0, + width, height); + + /* Draw the original bitmap transformed into the new bitmap + */ + _cairo_pattern_init_for_surface (&pattern, &val->image->base); + cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); + + _cairo_surface_composite (CAIRO_OPERATOR_OVER, + &pattern.base, NULL, image, + 0, 0, 0, 0, 0, 0, + width, + height); + + _cairo_pattern_fini (&pattern.base); + + /* Now update the cache entry for the new bitmap, recomputing + * the origin based on the final transform. + */ + origin_x = - val->size.x; + origin_y = - val->size.y; + cairo_matrix_transform_point (&original_to_transformed, + &origin_x, &origin_y); + + old_image = val->image; + val->image = (cairo_image_surface_t *)image; + cairo_surface_destroy (&old_image->base); + + val->size.width = width; + val->size.height = height; + val->size.x = - floor (origin_x + 0.5); + val->size.y = - floor (origin_y + 0.5); + return status; +} + +static cairo_status_t +_cairo_ft_unscaled_font_create_glyph (void *abstract_font, + cairo_image_glyph_cache_entry_t *val) +{ + ft_unscaled_font_t *unscaled = abstract_font; + FT_GlyphSlot glyphslot; + FT_Face face; + FT_Glyph_Metrics *metrics; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + face = _ft_unscaled_font_lock_face (unscaled); + if (!face) + return CAIRO_STATUS_NO_MEMORY; + + glyphslot = face->glyph; + metrics = &glyphslot->metrics; + + _ft_unscaled_font_set_scale (unscaled, &val->key.scale); + + if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinates of the bearing and advance need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + */ + + val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; + val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; + + val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; + val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; + + /* + * use untransformed advance values + * XXX uses horizontal advance only at present; + should provide FT_LOAD_VERTICAL_LAYOUT + */ + + val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; + val->extents.y_advance = 0 / unscaled->y_scale; + + if (glyphslot->format == FT_GLYPH_FORMAT_OUTLINE) + status = _render_glyph_outline (face, val); + else + status = _render_glyph_bitmap (face, val); + + if (unscaled->have_shape && + (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) + status = _transform_glyph_bitmap (val); + FAIL: + if (status && val->image) { + cairo_surface_destroy (&val->image->base); + val->image = NULL; + } + _ft_unscaled_font_unlock_face (unscaled); - + return status; } @@ -733,6 +1024,12 @@ _ft_scaled_font_create (ft_unscaled_font_t *unscaled, return (cairo_scaled_font_t *)f; } +cairo_bool_t +_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend == &cairo_ft_scaled_font_backend; +} + static cairo_status_t _cairo_ft_scaled_font_create (const char *family, cairo_font_slant_t slant, diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h index 494d8369a..e92048b20 100644 --- a/src/cairo-ft-private.h +++ b/src/cairo-ft-private.h @@ -44,6 +44,12 @@ CAIRO_BEGIN_DECLS +cairo_bool_t +_cairo_unscaled_font_is_ft (cairo_unscaled_font_t *unscaled_font); + +cairo_bool_t +_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font); + /* These functions are needed by the PDF backend, which needs to keep track of the * the different fonts-on-disk used by a document, so it can embed them */ diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c index ce9da259b..4b9d619e6 100644 --- a/src/cairo-glitz-surface.c +++ b/src/cairo-glitz-surface.c @@ -52,26 +52,26 @@ _cairo_glitz_surface_finish (void *abstract_surface) } static glitz_format_name_t -_glitz_format (cairo_format_t format) +_glitz_format_from_content (cairo_content_t content) { - switch (format) { - default: - case CAIRO_FORMAT_ARGB32: - return GLITZ_STANDARD_ARGB32; - case CAIRO_FORMAT_RGB24: + switch (content) { + case CAIRO_CONTENT_COLOR: return GLITZ_STANDARD_RGB24; - case CAIRO_FORMAT_A8: + case CAIRO_CONTENT_ALPHA: return GLITZ_STANDARD_A8; - case CAIRO_FORMAT_A1: - return GLITZ_STANDARD_A1; + case CAIRO_CONTENT_COLOR_ALPHA: + return GLITZ_STANDARD_ARGB32; } + + ASSERT_NOT_REACHED; + return GLITZ_STANDARD_ARGB32; } static cairo_surface_t * _cairo_glitz_surface_create_similar (void *abstract_src, - cairo_format_t format, - int width, - int height) + cairo_content_t content, + int width, + int height) { cairo_glitz_surface_t *src = abstract_src; cairo_surface_t *crsurface; @@ -81,7 +81,8 @@ _cairo_glitz_surface_create_similar (void *abstract_src, drawable = glitz_surface_get_drawable (src->surface); - gformat = glitz_find_standard_format (drawable, _glitz_format (format)); + gformat = glitz_find_standard_format (drawable, + _glitz_format_from_content (content)); if (!gformat) return NULL; @@ -350,9 +351,10 @@ _cairo_glitz_surface_clone_similar (void *abstract_surface, else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; + cairo_content_t content = _cairo_content_from_format (image_src->format); clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (surface, image_src->format, + _cairo_glitz_surface_create_similar (surface, content, image_src->width, image_src->height); if (!clone) @@ -440,73 +442,18 @@ _glitz_operator (cairo_operator_t op) return CAIRO_OPERATOR_XOR; } +#define CAIRO_GLITZ_FEATURE_OK(surface, name) \ + (glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \ + (GLITZ_FEATURE_ ## name ## _MASK)) + static glitz_status_t _glitz_ensure_target (glitz_surface_t *surface) { - glitz_drawable_t *drawable; - - drawable = glitz_surface_get_attached_drawable (surface); - if (!drawable) { - glitz_drawable_format_t *dformat; - glitz_drawable_format_t templ; - glitz_format_t *format; - glitz_drawable_t *pbuffer; - unsigned long mask; - int i; - - format = glitz_surface_get_format (surface); - if (format->type != GLITZ_FORMAT_TYPE_COLOR) - return CAIRO_INT_STATUS_UNSUPPORTED; - - drawable = glitz_surface_get_drawable (surface); - dformat = glitz_drawable_get_format (drawable); - - templ.types.pbuffer = 1; - mask = GLITZ_FORMAT_PBUFFER_MASK; - - templ.samples = dformat->samples; - mask |= GLITZ_FORMAT_SAMPLES_MASK; - - i = 0; - do { - dformat = glitz_find_similar_drawable_format (drawable, - mask, &templ, i++); - - if (dformat) { - int sufficient = 1; - - if (format->color.red_size) { - if (dformat->color.red_size < format->color.red_size) - sufficient = 0; - } - if (format->color.alpha_size) { - if (dformat->color.alpha_size < format->color.alpha_size) - sufficient = 0; - } - - if (sufficient) - break; - } - } while (dformat); - - if (!dformat) - return CAIRO_INT_STATUS_UNSUPPORTED; - - pbuffer = - glitz_create_pbuffer_drawable (drawable, dformat, - glitz_surface_get_width (surface), - glitz_surface_get_height (surface)); - if (!pbuffer) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glitz_surface_attach (surface, pbuffer, - GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, - 0, 0); - - glitz_drawable_destroy (pbuffer); - } + if (glitz_surface_get_attached_drawable (surface) || + CAIRO_GLITZ_FEATURE_OK (surface, FRAMEBUFFER_OBJECT)) + return CAIRO_STATUS_SUCCESS; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } typedef struct _cairo_glitz_surface_attributes { @@ -536,13 +483,26 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, switch (pattern->type) { case CAIRO_PATTERN_LINEAR: case CAIRO_PATTERN_RADIAL: { - cairo_gradient_pattern_t *gradient = + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; - glitz_drawable_t *drawable; - glitz_fixed16_16_t *params; - int n_params; - int i; - unsigned short alpha; + char *data; + glitz_fixed16_16_t *params; + int n_params; + unsigned int *pixels; + int i; + unsigned char alpha; + glitz_buffer_t *buffer; + static glitz_pixel_format_t format = { + { + 32, + 0xff000000, + 0x00ff0000, + 0x0000ff00, + 0x000000ff + }, + 0, 0, 0, + GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP + }; /* XXX: the current color gradient acceleration provided by glitz is * experimental, it's been proven inappropriate in a number of ways, @@ -565,22 +525,20 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, break; } - drawable = glitz_surface_get_drawable (dst->surface); - if (!(glitz_drawable_get_features (drawable) & - GLITZ_FEATURE_FRAGMENT_PROGRAM_MASK)) + if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM)) break; - + if (pattern->filter != CAIRO_FILTER_BILINEAR && pattern->filter != CAIRO_FILTER_GOOD && pattern->filter != CAIRO_FILTER_BEST) break; - alpha = (gradient->stops[0].color.alpha) * 0xffff; + alpha = gradient->stops[0].color.alpha * 0xff; for (i = 1; i < gradient->n_stops; i++) { - unsigned short a; + unsigned char a; - a = (gradient->stops[i].color.alpha) * 0xffff; + a = gradient->stops[i].color.alpha * 0xff; if (a != alpha) break; } @@ -592,35 +550,51 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, n_params = gradient->n_stops * 3 + 4; - params = malloc (sizeof (glitz_fixed16_16_t) * n_params); - if (!params) + data = malloc (sizeof (glitz_fixed16_16_t) * n_params + + sizeof (unsigned int) * gradient->n_stops); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + + params = (glitz_fixed16_16_t *) data; + pixels = (unsigned int *) + (data + sizeof (glitz_fixed16_16_t) * n_params); + + buffer = glitz_buffer_create_for_data (pixels); + if (!buffer) + { + free (data); return CAIRO_STATUS_NO_MEMORY; + } src = (cairo_glitz_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, - CAIRO_FORMAT_ARGB32, + CAIRO_CONTENT_COLOR_ALPHA, gradient->n_stops, 1); if (!src) { - free (params); + glitz_buffer_destroy (buffer); + free (data); return CAIRO_STATUS_NO_MEMORY; } - for (i = 0; i < gradient->n_stops; i++) { - glitz_color_t color; - - color.red = gradient->stops[i].color.red * alpha; - color.green = gradient->stops[i].color.green * alpha; - color.blue = gradient->stops[i].color.blue * alpha; - color.alpha = alpha; - - glitz_set_rectangle (src->surface, &color, i, 0, 1, 1); - + for (i = 0; i < gradient->n_stops; i++) + { + pixels[i] = + (((int) alpha) << 24) | + (((int) gradient->stops[i].color.red * alpha) << 16) | + (((int) gradient->stops[i].color.green * alpha) << 8) | + (((int) gradient->stops[i].color.blue * alpha)); + params[4 + 3 * i] = gradient->stops[i].offset; params[5 + 3 * i] = i << 16; params[6 + 3 * i] = 0; } + glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1, + &format, buffer); + + glitz_buffer_destroy (buffer); + if (pattern->type == CAIRO_PATTERN_LINEAR) { cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; @@ -910,10 +884,6 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, glitz_color.blue = color->blue_short; glitz_color.alpha = color->alpha_short; - if (glitz_surface_get_width (dst->surface) != 1 || - glitz_surface_get_height (dst->surface) != 1) - _glitz_ensure_target (dst->surface); - glitz_set_rectangles (dst->surface, &glitz_color, (glitz_rectangle_t *) rects, n_rects); } @@ -929,7 +899,8 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, src = (cairo_glitz_surface_t *) _cairo_surface_create_similar_solid (&dst->base, - CAIRO_FORMAT_ARGB32, 1, 1, + CAIRO_CONTENT_COLOR_ALPHA, + 1, 1, (cairo_color_t *) color); if (!src) return CAIRO_STATUS_NO_MEMORY; @@ -1031,7 +1002,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, mask = (cairo_glitz_surface_t *) _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, 2, 1); if (!mask) { @@ -1131,7 +1102,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, mask = (cairo_glitz_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, width, height); if (!mask) { @@ -1452,7 +1423,7 @@ _cairo_glitz_area_find (cairo_glitz_area_t *area, width, height, kick_out, closure); if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } } break; case CAIRO_GLITZ_AREA_DIVIDED: { diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 94770f329..ed6210cfb 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -903,7 +903,7 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_surface_t *intermediate; intermediate = cairo_surface_create_similar (gstate->clip.surface, - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, extents.width, extents.height); if (intermediate == NULL) @@ -952,6 +952,7 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, if (gstate->clip.surface) _cairo_pattern_fini (&intermediate_pattern.base); + _cairo_pattern_fini (&pattern.base); return status; } @@ -1242,7 +1243,7 @@ _composite_traps_intermediate_surface (cairo_gstate_t *gstate, translate_traps (traps, -extents->x, -extents->y); intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, extents->width, extents->height, CAIRO_COLOR_TRANSPARENT); @@ -1376,9 +1377,6 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; - if (gstate->target == NULL) - return CAIRO_STATUS_NO_TARGET_SURFACE; - status = _cairo_traps_extract_region (traps, &trap_region); if (status) return status; @@ -1515,18 +1513,12 @@ BAIL: cairo_status_t _cairo_gstate_copy_page (cairo_gstate_t *gstate) { - if (gstate->target == NULL) - return CAIRO_STATUS_NO_TARGET_SURFACE; - return _cairo_surface_copy_page (gstate->target); } cairo_status_t _cairo_gstate_show_page (cairo_gstate_t *gstate) { - if (gstate->target == NULL) - return CAIRO_STATUS_NO_TARGET_SURFACE; - return _cairo_surface_show_page (gstate->target); } @@ -1727,7 +1719,7 @@ _cairo_gstate_intersect_clip_mask (cairo_gstate_t *gstate, _cairo_rectangle_intersect (&surface_rect, &gstate->clip.surface_rect); surface = _cairo_surface_create_similar_solid (gstate->target, - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, surface_rect.width, surface_rect.height, CAIRO_COLOR_WHITE); @@ -2138,9 +2130,9 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, status = CAIRO_STATUS_SUCCESS; goto BAIL1; } - + intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, extents.width, extents.height, CAIRO_COLOR_TRANSPARENT); diff --git a/src/cairo-hash-private.h b/src/cairo-hash-private.h new file mode 100644 index 000000000..6dc9c9073 --- /dev/null +++ b/src/cairo-hash-private.h @@ -0,0 +1,125 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc. + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Keith Packard <keithp@keithp.com> + * Graydon Hoare <graydon@redhat.com> + * Carl Worth <cworth@cworth.org> + */ + +#ifndef CAIRO_HASH_PRIVATE_H +#define CAIRO_HASH_PRIVATE_H + +/* XXX: I'd like this file to be self-contained in terms of + * includeability, but that's not really possible with the current + * monolithic cairoint.h. So, for now, just include cairoint.h instead + * if you want to include this file. */ + +typedef struct _cairo_hash_table cairo_hash_table_t; + +/** + * cairo_hash_entry_t: + * + * A #cairo_hash_entry_t contains both a key and a value for + * cairo_hash_table_t. User-derived types for cairo_hash_entry_t must + * be type-compatible with this structure (eg. they must have an + * unsigned long as the first parameter. The easiest way to get this + * is to use: + * + * typedef _my_entry { + * cairo_hash_entry_t base; + * ... Remainder of key and value fields here .. + * } my_entry_t; + * + * which then allows a pointer to my_entry_t to be passed to any of + * the cairo_hash_table functions as follows without requiring a cast: + * + * _cairo_hash_table_insert (hash_table, &my_entry->base); + * + * IMPORTANT: The caller is reponsible for initializing + * my_entry->base.hash with a hash code derived from the key. The + * essential property of the hash code is that keys_equal must never + * return TRUE for two keys that have different hashes. The best hash + * code will reduce the frequency of two keys with the same code for + * which keys_equal returns FALSE. + * + * Which parts of the entry make up the "key" and which part make up + * the value are entirely up to the caller, (as determined by the + * computation going into base.hash as well as the keys_equal + * function). A few of the cairo_hash_table functions accept an entry + * which will be used exclusively as a "key", (indicated by a + * parameter name of key). In these cases, the value-related fields of + * the entry need not be initialized if so desired. + **/ +typedef struct _cairo_hash_entry { + unsigned long hash; +} cairo_hash_entry_t; + +typedef cairo_bool_t +(*cairo_hash_keys_equal_func_t) (void *key_a, void *key_b); + +typedef cairo_bool_t +(*cairo_hash_predicate_func_t) (void *entry); + +typedef void +(*cairo_hash_callback_func_t) (void *entry, + void *closure); + +cairo_private cairo_hash_table_t * +_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal); + +cairo_private void +_cairo_hash_table_destroy (cairo_hash_table_t *hash_table); + +cairo_private cairo_bool_t +_cairo_hash_table_lookup (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key, + cairo_hash_entry_t **entry_return); + +cairo_private void * +_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, + cairo_hash_predicate_func_t predicate); + +cairo_private cairo_status_t +_cairo_hash_table_insert (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *entry); + +cairo_private void +_cairo_hash_table_remove (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key); + +cairo_private void +_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, + cairo_hash_callback_func_t hash_callback, + void *closure); + +#endif diff --git a/src/cairo-hash.c b/src/cairo-hash.c index e7547bc29..e44ab3025 100644 --- a/src/cairo-hash.c +++ b/src/cairo-hash.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * - * This file is Copyright © 2004 Red Hat, Inc. + * Copyright © 2004 Red Hat, Inc. + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -30,61 +31,35 @@ * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): - * Keith Packard + * Keith Packard <keithp@keithp.com> * Graydon Hoare <graydon@redhat.com> + * Carl Worth <cworth@cworth.org> */ #include "cairoint.h" -/* - * This structure, and accompanying table, is borrowed/modified from the - * file xserver/render/glyph.c in the freedesktop.org x server, with - * permission (and suggested modification of doubling sizes) by Keith - * Packard. +/* + * An entry can be in one of three states: + * + * FREE: Entry has never been used, terminates all searches. + * Appears in the table as a NULL pointer. + * + * DEAD: Entry had been live in the past. A dead entry can be reused + * but does not terminate a search for an exact entry. + * Appears in the table as a pointer to DEAD_ENTRY. + * + * LIVE: Entry is currently being used. + * Appears in the table as any non-NULL, non-DEAD_ENTRY pointer. */ -static const cairo_cache_arrangement_t cache_arrangements [] = { - { 16, 43, 41 }, - { 32, 73, 71 }, - { 64, 151, 149 }, - { 128, 283, 281 }, - { 256, 571, 569 }, - { 512, 1153, 1151 }, - { 1024, 2269, 2267 }, - { 2048, 4519, 4517 }, - { 4096, 9013, 9011 }, - { 8192, 18043, 18041 }, - { 16384, 36109, 36107 }, - { 32768, 72091, 72089 }, - { 65536, 144409, 144407 }, - { 131072, 288361, 288359 }, - { 262144, 576883, 576881 }, - { 524288, 1153459, 1153457 }, - { 1048576, 2307163, 2307161 }, - { 2097152, 4613893, 4613891 }, - { 4194304, 9227641, 9227639 }, - { 8388608, 18455029, 18455027 }, - { 16777216, 36911011, 36911009 }, - { 33554432, 73819861, 73819859 }, - { 67108864, 147639589, 147639587 }, - { 134217728, 295279081, 295279079 }, - { 268435456, 590559793, 590559791 } -}; +static cairo_hash_entry_t dead_entry = { 0 }; +#define DEAD_ENTRY (&dead_entry) -#define N_CACHE_SIZES (sizeof(cache_arrangements)/sizeof(cache_arrangements[0])) +#define ENTRY_IS_FREE(entry) ((entry) == NULL) +#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) +#define ENTRY_IS_LIVE(entry) ((entry) && ! ENTRY_IS_DEAD(entry)) -/* - * Entries 'e' are poiners, in one of 3 states: - * - * e == NULL: The entry has never had anything put in it - * e != DEAD_ENTRY: The entry has an active value in it currently - * e == DEAD_ENTRY: The entry *had* a value in it at some point, but the - * entry has been killed. Lookups requesting free space can - * reuse these entries; lookups requesting a precise match - * should neither return these entries nor stop searching when - * seeing these entries. - * - * We expect keys will not be destroyed frequently, so our table does not +/* We expect keys will not be destroyed frequently, so our table does not * contain any explicit shrinking code nor any chain-coalescing code for * entries randomly deleted by memory pressure (except during rehashing, of * course). These assumptions are potentially bad, but they make the @@ -93,118 +68,196 @@ static const cairo_cache_arrangement_t cache_arrangements [] = { * Revisit later if evidence appears that we're using excessive memory from * a mostly-dead table. * - * Generally you do not need to worry about freeing cache entries; the - * cache will expire entries randomly as it experiences memory pressure. - * If max_memory is set, entries are not expired, and must be explicitely - * removed. - * * This table is open-addressed with double hashing. Each table size is a * prime chosen to be a little more than double the high water mark for a * given arrangement, so the tables should remain < 50% full. The table * size makes for the "first" hash modulus; a second prime (2 less than the * first prime) serves as the "second" hash modulus, which is co-prime and * thus guarantees a complete permutation of table indices. - * + * + * This structure, and accompanying table, is borrowed/modified from the + * file xserver/render/glyph.c in the freedesktop.org x server, with + * permission (and suggested modification of doubling sizes) by Keith + * Packard. */ -#define DEAD_ENTRY ((cairo_cache_entry_base_t *) 1) -#define NULL_ENTRY_P(cache, i) ((cache)->entries[i] == NULL) -#define DEAD_ENTRY_P(cache, i) ((cache)->entries[i] == DEAD_ENTRY) -#define LIVE_ENTRY_P(cache, i) \ - (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) - -#ifdef NDEBUG -#define _cache_sane_state(c) -#else -static void -_cache_sane_state (cairo_cache_t *cache) -{ - assert (cache != NULL); - assert (cache->entries != NULL); - assert (cache->backend != NULL); - assert (cache->arrangement != NULL); - /* Cannot check this, a single object may larger */ - /* assert (cache->used_memory <= cache->max_memory); */ - assert (cache->live_entries <= cache->arrangement->size); +typedef struct _cairo_hash_table_arrangement { + unsigned long high_water_mark; + unsigned long size; + unsigned long rehash; +} cairo_hash_table_arrangement_t; + +static const cairo_hash_table_arrangement_t hash_table_arrangements [] = { + { 16, 43, 41 }, + { 32, 73, 71 }, + { 64, 151, 149 }, + { 128, 283, 281 }, + { 256, 571, 569 }, + { 512, 1153, 1151 }, + { 1024, 2269, 2267 }, + { 2048, 4519, 4517 }, + { 4096, 9013, 9011 }, + { 8192, 18043, 18041 }, + { 16384, 36109, 36107 }, + { 32768, 72091, 72089 }, + { 65536, 144409, 144407 }, + { 131072, 288361, 288359 }, + { 262144, 576883, 576881 }, + { 524288, 1153459, 1153457 }, + { 1048576, 2307163, 2307161 }, + { 2097152, 4613893, 4613891 }, + { 4194304, 9227641, 9227639 }, + { 8388608, 18455029, 18455027 }, + { 16777216, 36911011, 36911009 }, + { 33554432, 73819861, 73819859 }, + { 67108864, 147639589, 147639587 }, + { 134217728, 295279081, 295279079 }, + { 268435456, 590559793, 590559791 } +}; + +#define NUM_HASH_TABLE_ARRANGEMENTS (sizeof(hash_table_arrangements)/sizeof(hash_table_arrangements[0])) + +struct _cairo_hash_table { + cairo_hash_keys_equal_func_t keys_equal; + + const cairo_hash_table_arrangement_t *arrangement; + cairo_hash_entry_t **entries; + + unsigned long live_entries; +}; + +/** + * _cairo_hash_table_create: + * @keys_equal: a function to return TRUE if two keys are equal + * + * Creates a new hash table which will use the keys_equal() function + * to compare hash keys. Data is provided to the hash table in the + * form of user-derived versions of cairo_hash_entry_t. A hash entry + * must be able to hold both a key (including a hash code) and a + * value. Sometimes only the key will be necessary, (as in + * _cairo_hash_table_remove), and other times both a key and a value + * will be necessary, (as in _cairo_hash_table_insert). + * + * See #cairo_hash_entry_t for more details. + * + * Return value: the new hash table or NULL if out of memory. + **/ +cairo_hash_table_t * +_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) +{ + cairo_hash_table_t *hash_table; + + hash_table = malloc (sizeof (cairo_hash_table_t)); + if (hash_table == NULL) + return NULL; + + hash_table->keys_equal = keys_equal; + + hash_table->arrangement = &hash_table_arrangements[0]; + + hash_table->entries = calloc (hash_table->arrangement->size, + sizeof(cairo_hash_entry_t *)); + if (hash_table->entries == NULL) { + free (hash_table); + return NULL; + } + + hash_table->live_entries = 0; + + return hash_table; } -#endif -static void -_entry_destroy (cairo_cache_t *cache, unsigned long i) +/** + * _cairo_hash_table_destroy: + * @hash_table: an empty hash table to destroy + * + * Immediately destroys the given hash table, freeing all resources + * associated with it. + * + * WARNING: The hash_table must have no live entries in it before + * _cairo_hash_table_destroy is called. It is a fatal error otherwise, + * and this function will halt. The rationale for this behavior is to + * avoid memory leaks and to avoid needless complication of the API + * with destroy notifiy callbacks. + **/ +void +_cairo_hash_table_destroy (cairo_hash_table_t *hash_table) { - _cache_sane_state (cache); + if (hash_table == NULL) + return; - if (LIVE_ENTRY_P(cache, i)) - { - cairo_cache_entry_base_t *entry = cache->entries[i]; - assert(cache->live_entries > 0); - assert(cache->used_memory >= entry->memory); - - cache->live_entries--; - cache->used_memory -= entry->memory; - cache->backend->destroy_entry (cache, entry); - cache->entries[i] = DEAD_ENTRY; - } + /* The hash table must be empty. Otherwise, halt. */ + assert (hash_table->live_entries == 0); + + free (hash_table->entries); + hash_table->entries = NULL; + + free (hash_table); } -static cairo_cache_entry_base_t ** -_cache_lookup (cairo_cache_t *cache, - void *key, - int (*predicate)(void*,void*,void*)) +/** + * _cairo_hash_table_lookup_internal: + * + * @hash_table: a #cairo_hash_table_t to search + * @key: the key to search on + * @hash_code: the hash_code for @key + * @key_unique: If TRUE, then caller asserts that no key already + * exists that will compare equal to #key, so search can be + * optimized. If unsure, set to FALSE and the code will always work. + * + * Search the hashtable for a live entry for which + * hash_table->keys_equal returns true. If no such entry exists then + * return the first available (free or dead entry). + * + * If the key_unique flag is set, then the search will never call + * hash_table->keys_equal and will act as if it always returned + * false. This is useful as a performance optimization in special + * circumstances where the caller knows that there is no existing + * entry in the hash table with a matching key. + * + * Return value: The matching entry in the hash table (if + * any). Otherwise, the first available entry. The caller should check + * entry->state to check whether a match was found or not. + **/ +static cairo_hash_entry_t ** +_cairo_hash_table_lookup_internal (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key, + cairo_bool_t key_is_unique) { - - cairo_cache_entry_base_t **probe; - unsigned long hash; + cairo_hash_entry_t **entry, **first_available = NULL; unsigned long table_size, i, idx, step; - _cache_sane_state (cache); - assert (key != NULL); + table_size = hash_table->arrangement->size; - table_size = cache->arrangement->size; - hash = cache->backend->hash (cache, key); - idx = hash % table_size; + idx = key->hash % table_size; step = 0; for (i = 0; i < table_size; ++i) { -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->probes++; -#endif - assert(idx < table_size); - probe = cache->entries + idx; - - /* - * There are two lookup modes: searching for a free slot and searching - * for an exact entry. - */ - - if (predicate != NULL) + entry = &hash_table->entries[idx]; + + if (ENTRY_IS_FREE(*entry)) { - /* We are looking up an exact entry. */ - if (*probe == NULL) - { - /* Found an empty spot, there can't be a match */ - break; - } - else if (*probe != DEAD_ENTRY - && (*probe)->hashcode == hash - && predicate (cache, key, *probe)) - { - return probe; - } + return entry; } - else + else if (ENTRY_IS_DEAD(*entry)) { - /* We are just looking for a free slot. */ - if (*probe == NULL - || *probe == DEAD_ENTRY) - { - return probe; + if (key_is_unique) { + return entry; + } else { + if (! first_available) + first_available = entry; } } + else /* ENTRY_IS_LIVE(*entry) */ + { + if (! key_is_unique) + if (hash_table->keys_equal (key, *entry)) + return entry; + } if (step == 0) { - step = hash % cache->arrangement->rehash; + step = key->hash % hash_table->arrangement->rehash; if (step == 0) step = 1; } @@ -218,110 +271,153 @@ _cache_lookup (cairo_cache_t *cache, * The table should not have permitted you to get here if you were just * looking for a free slot: there should have been room. */ - assert(predicate != NULL); - return NULL; -} - -static cairo_cache_entry_base_t ** -_find_available_entry_for (cairo_cache_t *cache, - void *key) -{ - return _cache_lookup (cache, key, NULL); -} + assert (key_is_unique == 0); -static cairo_cache_entry_base_t ** -_find_exact_live_entry_for (cairo_cache_t *cache, - void *key) -{ - return _cache_lookup (cache, key, cache->backend->keys_equal); -} - -static const cairo_cache_arrangement_t * -_find_cache_arrangement (unsigned long proposed_size) -{ - unsigned long idx; - - for (idx = 0; idx < N_CACHE_SIZES; ++idx) - if (cache_arrangements[idx].high_water_mark >= proposed_size) - return &cache_arrangements[idx]; - return NULL; + return first_available; } +/** + * _cairo_hash_table_resize: + * @hash_table: a hash table + * + * Resize the hash table if the number of entries has gotten much + * bigger or smaller than the ideal number of entries for the current + * size. + * + * Return value: CAIRO_STATUS_SUCCESS if successful or + * CAIRO_STATUS_NO_MEMORY if out of memory. + **/ static cairo_status_t -_resize_cache (cairo_cache_t *cache, unsigned long proposed_size) +_cairo_hash_table_resize (cairo_hash_table_t *hash_table) { - cairo_cache_t tmp; - cairo_cache_entry_base_t **e; + cairo_hash_table_t tmp; + cairo_hash_entry_t **entry; unsigned long new_size, i; - tmp = *cache; - tmp.arrangement = _find_cache_arrangement (proposed_size); - assert(tmp.arrangement != NULL); - if (tmp.arrangement == cache->arrangement) + /* This keeps the hash table between 25% and 50% full. */ + unsigned long high = hash_table->arrangement->high_water_mark; + unsigned long low = high >> 2; + + if (hash_table->live_entries >= low && hash_table->live_entries <= high) return CAIRO_STATUS_SUCCESS; + tmp = *hash_table; + + if (hash_table->live_entries > high) + { + tmp.arrangement = hash_table->arrangement + 1; + /* This code is being abused if we can't make a table big enough. */ + assert (tmp.arrangement - hash_table_arrangements < + NUM_HASH_TABLE_ARRANGEMENTS); + } + else /* hash_table->live_entries < low */ + { + /* Can't shrink if we're at the smallest size */ + if (hash_table->arrangement == &hash_table_arrangements[0]) + return CAIRO_STATUS_SUCCESS; + tmp.arrangement = hash_table->arrangement - 1; + } + new_size = tmp.arrangement->size; - tmp.entries = calloc (new_size, sizeof (cairo_cache_entry_base_t *)); + tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*)); if (tmp.entries == NULL) return CAIRO_STATUS_NO_MEMORY; - for (i = 0; i < cache->arrangement->size; ++i) { - if (LIVE_ENTRY_P(cache, i)) { - e = _find_available_entry_for (&tmp, cache->entries[i]); - assert (e != NULL); - *e = cache->entries[i]; + for (i = 0; i < hash_table->arrangement->size; ++i) { + if (ENTRY_IS_LIVE (hash_table->entries[i])) { + entry = _cairo_hash_table_lookup_internal (&tmp, + hash_table->entries[i], + TRUE); + assert (ENTRY_IS_FREE(*entry)); + *entry = hash_table->entries[i]; } } - free (cache->entries); - cache->entries = tmp.entries; - cache->arrangement = tmp.arrangement; + + free (hash_table->entries); + hash_table->entries = tmp.entries; + hash_table->arrangement = tmp.arrangement; + return CAIRO_STATUS_SUCCESS; } - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE -static double -_load_factor (cairo_cache_t *cache) +/** + * _cairo_hash_table_lookup: + * @hash_table: a hash table + * @key: the key of interest + * @entry_return: pointer for return value. + * + * Performs a lookup in @hash_table looking for an entry which has a + * key that matches @key, (as determined by the keys_equal() function + * passed to _cairo_hash_table_create). + * + * Return value: TRUE if there is an entry in the hash table that + * matches the given key, (which will now be in *entry_return). FALSE + * otherwise, (in which case *entry_return will be NULL). + **/ +cairo_bool_t +_cairo_hash_table_lookup (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key, + cairo_hash_entry_t **entry_return) { - return ((double) cache->live_entries) - / ((double) cache->arrangement->size); + cairo_hash_entry_t **entry; + + /* See if we have an entry in the table already. */ + entry = _cairo_hash_table_lookup_internal (hash_table, key, FALSE); + if (ENTRY_IS_LIVE(*entry)) { + *entry_return = *entry; + return TRUE; + } + + *entry_return = NULL; + return FALSE; } -#endif - -/* Find a random in the cache matching the given predicate. We use the - * same algorithm as the probing algorithm to walk over the entries in - * the hash table in a pseudo-random order. Walking linearly would - * favor entries following gaps in the hash table. We could also - * call rand() repeatedly, which works well for almost-full tables, - * but degrades when the table is almost empty, or predicate - * returns false for most entries. - */ -static cairo_cache_entry_base_t ** -_random_entry (cairo_cache_t *cache, - int (*predicate)(void*)) -{ - cairo_cache_entry_base_t **probe; + +/** + * _cairo_hash_table_random_entry: + * @hash_table: a hash table + * @predicate: a predicate function, or NULL for any entry. + * + * Find a random entry in the hash table satisfying the given + * @predicate. A NULL @predicate is taken as equivalent to a function + * which always returns TRUE, (eg. any entry in the table will do). + * + * We use the same algorithm as the lookup algorithm to walk over the + * entries in the hash table in a pseudo-random order. Walking + * linearly would favor entries following gaps in the hash table. We + * could also call rand() repeatedly, which works well for almost-full + * tables, but degrades when the table is almost empty, or predicate + * returns TRUE for most entries. + * + * Return value: a random live entry or NULL if there are no entries + * that match the given predicate. In particular, if predicate is + * NULL, a NULL return value indicates that the table is empty. + **/ +void * +_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, + cairo_hash_predicate_func_t predicate) +{ + cairo_hash_entry_t **entry; unsigned long hash; unsigned long table_size, i, idx, step; - - _cache_sane_state (cache); - table_size = cache->arrangement->size; + table_size = hash_table->arrangement->size; + hash = rand (); idx = hash % table_size; step = 0; for (i = 0; i < table_size; ++i) { - assert(idx < table_size); - probe = cache->entries + idx; + entry = &hash_table->entries[idx]; - if (LIVE_ENTRY_P(cache, idx) - && (!predicate || predicate (*probe))) - return probe; + if (ENTRY_IS_LIVE (*entry) && + (predicate == NULL || predicate (*entry))) + { + return *entry; + } if (step == 0) { - step = hash % cache->arrangement->rehash; + step = hash % hash_table->arrangement->rehash; if (step == 0) step = 1; } @@ -334,180 +430,104 @@ _random_entry (cairo_cache_t *cache, return NULL; } -/* public API follows */ - -cairo_status_t -_cairo_cache_init (cairo_cache_t *cache, - const cairo_cache_backend_t *backend, - unsigned long max_memory) -{ - assert (backend != NULL); - - if (cache != NULL){ - cache->arrangement = &cache_arrangements[0]; - cache->max_memory = max_memory; - cache->used_memory = 0; - cache->live_entries = 0; - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->hits = 0; - cache->misses = 0; - cache->probes = 0; -#endif - - cache->backend = backend; - cache->entries = calloc (cache->arrangement->size, - sizeof(cairo_cache_entry_base_t *)); - - if (cache->entries == NULL) - return CAIRO_STATUS_NO_MEMORY; - } - _cache_sane_state (cache); - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_cache_destroy (cairo_cache_t *cache) -{ - unsigned long i; - if (cache == NULL) - return; - - _cache_sane_state (cache); - - for (i = 0; i < cache->arrangement->size; ++i) - _entry_destroy (cache, i); - - free (cache->entries); - cache->entries = NULL; - cache->backend->destroy_cache (cache); -} - +/** + * _cairo_hash_table_insert: + * @hash_table: a hash table + * @key_and_value: an entry to be inserted + * + * Insert the entry #key_and_value into the hash table. + * + * WARNING: It is a fatal error if an entry exists in the hash table + * with a matching key, (this function will halt). + * + * Instead of using insert to replace an entry, consider just editing + * the entry obtained with _cairo_hash_table_lookup. Or if absolutely + * necessary, use _cairo_hash_table_remove first. + * + * Return value: CAIRO_STATUS_SUCCESS if successful or + * CAIRO_STATUS_NO_MEMORY if insufficient memory is available. + **/ cairo_status_t -_cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return, - int *created_entry) +_cairo_hash_table_insert (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key_and_value) { - - unsigned long idx; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_cache_entry_base_t **slot = NULL, *new_entry; - - _cache_sane_state (cache); - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - if ((cache->hits + cache->misses) % 0xffff == 0) - printf("cache %p stats: size %ld, live %ld, load %.2f\n" - " mem %ld/%ld, hit %ld, miss %ld\n" - " probe %ld, %.2f probe/access\n", - cache, - cache->arrangement->size, - cache->live_entries, - _load_factor (cache), - cache->used_memory, - cache->max_memory, - cache->hits, - cache->misses, - cache->probes, - ((double) cache->probes) - / ((double) (cache->hits + - cache->misses + 1))); -#endif + cairo_status_t status; + cairo_hash_entry_t **entry; - /* See if we have an entry in the table already. */ - slot = _find_exact_live_entry_for (cache, key); - if (slot != NULL) { -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->hits++; -#endif - *entry_return = *slot; - if (created_entry) - *created_entry = 0; - return status; - } - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->misses++; -#endif - - /* Build the new entry. */ - status = cache->backend->create_entry (cache, key, - (void **)&new_entry); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - /* Store the hash value in case the backend forgot. */ - new_entry->hashcode = cache->backend->hash (cache, key); - - /* Make some entries die if we're under memory pressure. */ - while (cache->live_entries > 0 && - cache->max_memory > 0 && - ((cache->max_memory - cache->used_memory) < new_entry->memory)) { - idx = _random_entry (cache, NULL) - cache->entries; - assert (idx < cache->arrangement->size); - _entry_destroy (cache, idx); - } + entry = _cairo_hash_table_lookup_internal (hash_table, + key_and_value, FALSE); - /* Can't assert this; new_entry->memory may be larger than max_memory */ - /* assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); */ - - /* Make room in the table for a new slot. */ - status = _resize_cache (cache, cache->live_entries + 1); - if (status != CAIRO_STATUS_SUCCESS) { - cache->backend->destroy_entry (cache, new_entry); - return status; + if (ENTRY_IS_LIVE(*entry)) + { + /* User is being bad, let's crash. */ + ASSERT_NOT_REACHED; } - slot = _find_available_entry_for (cache, key); - assert(slot != NULL); - - /* Store entry in slot and increment statistics. */ - *slot = new_entry; - cache->live_entries++; - cache->used_memory += new_entry->memory; + *entry = key_and_value; + hash_table->live_entries++; - _cache_sane_state (cache); + status = _cairo_hash_table_resize (hash_table); + if (status) + return status; - *entry_return = new_entry; - if (created_entry) - *created_entry = 1; - - return status; + return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_cache_remove (cairo_cache_t *cache, - void *key) +/** + * _cairo_hash_table_remove: + * @hash_table: a hash table + * @key: key of entry to be removed + * + * Remove an entry from the hash table which has a key that matches + * @key, if any (as determined by the keys_equal() function passed to + * _cairo_hash_table_create). + * + * Return value: CAIRO_STATUS_SUCCESS if successful or + * CAIRO_STATUS_NO_MEMORY if out of memory. + **/ +void +_cairo_hash_table_remove (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) { - cairo_cache_entry_base_t **slot; + cairo_hash_entry_t **entry; - _cache_sane_state (cache); + entry = _cairo_hash_table_lookup_internal (hash_table, key, FALSE); + if (! ENTRY_IS_LIVE(*entry)) + return; - /* See if we have an entry in the table already. */ - slot = _find_exact_live_entry_for (cache, key); - if (slot != NULL) - _entry_destroy (cache, slot - cache->entries); + *entry = DEAD_ENTRY; + hash_table->live_entries--; - return CAIRO_STATUS_SUCCESS; + /* This call _can_ fail, but only in failing to allocate new + * memory to shrink the hash table. It does leave the table in a + * consistent state, and we've already succeeded in removing the + * entry, so we don't examine the failure status of this call. */ + _cairo_hash_table_resize (hash_table); } -void * -_cairo_cache_random_entry (cairo_cache_t *cache, - int (*predicate)(void*)) +/** + * _cairo_hash_table_foreach: + * @hash_table: a hash table + * @hash_callback: function to be called for each live entry + * @closure: additional argument to be passed to @hash_callback + * + * Call @hash_callback for each live entry in the hash table, in a + * non-specified order. + **/ +void +_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, + cairo_hash_callback_func_t hash_callback, + void *closure) { - cairo_cache_entry_base_t **slot = _random_entry (cache, predicate); - - return slot ? *slot : NULL; -} + unsigned long i; + cairo_hash_entry_t *entry; -unsigned long -_cairo_hash_string (const char *c) -{ - /* This is the djb2 hash. */ - unsigned long hash = 5381; - while (*c) - hash = ((hash << 5) + hash) + *c++; - return hash; + if (hash_table == NULL) + return; + + for (i = 0; i < hash_table->arrangement->size; i++) { + entry = hash_table->entries[i]; + if (ENTRY_IS_LIVE(entry)) + hash_callback (entry, closure); + } } - diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 255cd0c43..aff35ce3c 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -156,6 +156,10 @@ cairo_image_surface_create (cairo_format_t format, pixman_format_t *pixman_format; pixman_image_t *pixman_image; + /* XXX: Really need to make this kind of thing pass through _cairo_error. */ + if (! CAIRO_FORMAT_VALID (format)) + return NULL; + pixman_format = _create_pixman_format (format); if (pixman_format == NULL) return NULL; @@ -205,6 +209,10 @@ cairo_image_surface_create_for_data (unsigned char *data, pixman_format_t *pixman_format; pixman_image_t *pixman_image; + /* XXX: Really need to make this kind of thing pass through _cairo_error. */ + if (! CAIRO_FORMAT_VALID (format)) + return NULL; + pixman_format = _create_pixman_format (format); if (pixman_format == NULL) return NULL; @@ -256,13 +264,51 @@ cairo_image_surface_get_height (cairo_surface_t *surface) return image_surface->height; } +cairo_format_t +_cairo_format_from_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_COLOR: + return CAIRO_FORMAT_RGB24; + case CAIRO_CONTENT_ALPHA: + return CAIRO_FORMAT_A8; + case CAIRO_CONTENT_COLOR_ALPHA: + return CAIRO_FORMAT_ARGB32; + } + + ASSERT_NOT_REACHED; + return CAIRO_FORMAT_ARGB32; +} + +cairo_content_t +_cairo_content_from_format (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + return CAIRO_CONTENT_COLOR_ALPHA; + case CAIRO_FORMAT_RGB24: + return CAIRO_CONTENT_COLOR; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + return CAIRO_CONTENT_ALPHA; + } + + ASSERT_NOT_REACHED; + return CAIRO_CONTENT_COLOR_ALPHA; +} + static cairo_surface_t * -_cairo_image_surface_create_similar (void *abstract_src, - cairo_format_t format, +_cairo_image_surface_create_similar (void *abstract_src, + cairo_content_t content, int width, int height) { - return cairo_image_surface_create (format, width, height); + /* XXX: Really need to make this kind of thing pass through _cairo_error. */ + if (! CAIRO_CONTENT_VALID (content)) + return NULL; + + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); } static cairo_status_t @@ -549,9 +595,9 @@ _cairo_image_surface_composite (cairo_operator_t operator, } if (mask) - _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); - _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); + _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); return status; } @@ -628,7 +674,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, render_src_y + attributes.y_offset, (pixman_trapezoid_t *) traps, num_traps); - _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); + _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; } @@ -646,20 +692,7 @@ cairo_int_status_t _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, pixman_region16_t *region) { - if (region) { - pixman_region16_t *rcopy; - - rcopy = pixman_region_create(); - /* pixman_image_set_clip_region expects to take ownership of the - * passed-in region, so we create a copy to give it. */ - /* XXX: I think that's probably a bug in pixman. But its - * memory management issues need auditing anyway, so a - * workaround like this is fine for now. */ - pixman_region_copy (rcopy, region); - pixman_image_set_clip_region (surface->pixman_image, rcopy); - } else { - pixman_image_set_clip_region (surface->pixman_image, region); - } + pixman_image_set_clip_region (surface->pixman_image, region); return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo-meta-surface-private.h b/src/cairo-meta-surface-private.h new file mode 100644 index 000000000..234c6ccfc --- /dev/null +++ b/src/cairo-meta-surface-private.h @@ -0,0 +1,152 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg <krh@redhat.com> + */ + +#ifndef CAIRO_META_SURFACE_H +#define CAIRO_META_SURFACE_H + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" + +typedef enum { + CAIRO_COMMAND_COMPOSITE, + CAIRO_COMMAND_FILL_RECTANGLES, + CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS, + CAIRO_COMMAND_SET_CLIP_REGION, + CAIRO_COMMAND_INTERSECT_CLIP_PATH, + CAIRO_COMMAND_SHOW_GLYPHS, + CAIRO_COMMAND_FILL_PATH +} cairo_command_type_t; + +typedef struct _cairo_command_composite { + cairo_command_type_t type; + cairo_operator_t operator; + cairo_pattern_union_t src_pattern; + cairo_pattern_union_t mask_pattern; + cairo_pattern_t *mask_pattern_pointer; + int src_x; + int src_y; + int mask_x; + int mask_y; + int dst_x; + int dst_y; + unsigned int width; + unsigned int height; +} cairo_command_composite_t; + +typedef struct _cairo_command_fill_rectangles { + cairo_command_type_t type; + cairo_operator_t operator; + cairo_color_t color; + cairo_rectangle_t *rects; + int num_rects; +} cairo_command_fill_rectangles_t; + +typedef struct _cairo_command_composite_trapezoids { + cairo_command_type_t type; + cairo_operator_t operator; + cairo_pattern_union_t pattern; + int x_src; + int y_src; + int x_dst; + int y_dst; + unsigned int width; + unsigned int height; + cairo_trapezoid_t *traps; + int num_traps; +} cairo_command_composite_trapezoids_t; + +typedef struct _cairo_command_set_clip_region { + cairo_command_type_t type; + pixman_region16_t *region; + unsigned int serial; +} cairo_command_set_clip_region_t; + +typedef struct _cairo_command_intersect_clip_path { + cairo_command_type_t type; + cairo_path_fixed_t *path_pointer; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; +} cairo_command_intersect_clip_path_t; + +typedef struct _cairo_command_show_glyphs { + cairo_command_type_t type; + cairo_scaled_font_t *scaled_font; + cairo_operator_t operator; + cairo_pattern_union_t pattern; + int source_x; + int source_y; + int dest_x; + int dest_y; + unsigned int width; + unsigned int height; + cairo_glyph_t *glyphs; + int num_glyphs; +} cairo_command_show_glyphs_t; + +typedef struct _cairo_command_fill_path { + cairo_command_type_t type; + cairo_operator_t operator; + cairo_pattern_union_t pattern; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; +} cairo_command_fill_path_t; + +typedef union _cairo_command { + cairo_command_type_t type; + cairo_command_composite_t composite; + cairo_command_fill_rectangles_t fill_rectangles; + cairo_command_composite_trapezoids_t composite_trapezoids; + cairo_command_set_clip_region_t set_clip_region; + cairo_command_intersect_clip_path_t intersect_clip_path; + cairo_command_show_glyphs_t show_glyphs; + cairo_command_fill_path_t fill_path; +} cairo_command_t; + +typedef struct _cairo_meta_surface { + cairo_surface_t base; + double width, height; + cairo_array_t commands; +} cairo_meta_surface_t; + +cairo_surface_t * +_cairo_meta_surface_create (double width, double height); + +cairo_int_status_t +_cairo_meta_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target); + +#endif /* CAIRO_META_SURFACE_H */ diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c new file mode 100644 index 000000000..ae869f0ed --- /dev/null +++ b/src/cairo-meta-surface.c @@ -0,0 +1,594 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg <krh@redhat.com> + */ + +#include "cairoint.h" +#include "cairo-meta-surface-private.h" + +/* + * Notes: + * + * Can't use cairo_surface_* calls since we often don't want + * fallbacks. For example, when determining the font subsets or the + * fallback areas. Hmm... but maybe those passes could be integrated + * into the delegation wrappers and the ps output pass, respectively. + */ + +static const cairo_surface_backend_t cairo_meta_surface_backend; + +cairo_surface_t * +_cairo_meta_surface_create (double width, double height) +{ + cairo_meta_surface_t *meta; + + meta = malloc (sizeof (cairo_meta_surface_t)); + if (meta == NULL) + return NULL; + + meta->width = width; + meta->height = height; + _cairo_surface_init (&meta->base, &cairo_meta_surface_backend); + _cairo_array_init (&meta->commands, sizeof (cairo_command_t *)); + + return &meta->base; +} + +static cairo_surface_t * +_cairo_meta_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + return _cairo_meta_surface_create (width, height); +} + +static cairo_status_t +_cairo_meta_surface_finish (void *abstract_surface) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_t *command; + cairo_command_t **elements; + int i, num_elements; + + num_elements = meta->commands.num_elements; + elements = (cairo_command_t **) meta->commands.elements; + for (i = 0; i < num_elements; i++) { + command = elements[i]; + switch (command->type) { + case CAIRO_COMMAND_COMPOSITE: + _cairo_pattern_fini (&command->composite.src_pattern.base); + if (command->composite.mask_pattern_pointer) + _cairo_pattern_fini (command->composite.mask_pattern_pointer); + free (command); + break; + + case CAIRO_COMMAND_FILL_RECTANGLES: + free (command->fill_rectangles.rects); + free (command); + break; + + case CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS: + _cairo_pattern_fini (&command->composite_trapezoids.pattern.base); + free (command->composite_trapezoids.traps); + free (command); + break; + + case CAIRO_COMMAND_SET_CLIP_REGION: + free (command); + break; + + case CAIRO_COMMAND_INTERSECT_CLIP_PATH: + if (command->intersect_clip_path.path_pointer) + _cairo_path_fixed_fini (&command->intersect_clip_path.path); + free (command); + break; + + case CAIRO_COMMAND_SHOW_GLYPHS: + cairo_scaled_font_destroy (command->show_glyphs.scaled_font); + _cairo_pattern_fini (&command->show_glyphs.pattern.base); + free (command->show_glyphs.glyphs); + free (command); + break; + + case CAIRO_COMMAND_FILL_PATH: + _cairo_pattern_fini (&command->fill_path.pattern.base); + _cairo_path_fixed_fini (&command->fill_path.path); + free (command); + break; + + default: + ASSERT_NOT_REACHED; + } + } + + _cairo_array_fini (&meta->commands); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_composite (cairo_operator_t operator, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_surface, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_composite_t *command; + + command = malloc (sizeof (cairo_command_composite_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_COMPOSITE; + command->operator = operator; + _cairo_pattern_init_copy (&command->src_pattern.base, src_pattern); + if (mask_pattern) { + _cairo_pattern_init_copy (&command->mask_pattern.base, mask_pattern); + command->mask_pattern_pointer = &command->mask_pattern.base; + } else { + command->mask_pattern_pointer = NULL; + } + + command->src_x = src_x; + command->src_y = src_y; + command->mask_x = mask_x; + command->mask_y = mask_y; + command->dst_x = dst_x; + command->dst_y = dst_y; + command->width = width; + command->height = height; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + _cairo_pattern_fini (&command->src_pattern.base); + _cairo_pattern_fini (command->mask_pattern_pointer); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_fill_rectangles_t *command; + + command = malloc (sizeof (cairo_command_fill_rectangles_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_FILL_RECTANGLES; + command->operator = operator; + command->color = *color; + + command->rects = malloc (sizeof (cairo_rectangle_t) * num_rects); + if (command->rects == NULL) { + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + memcpy (command->rects, rects, sizeof (cairo_rectangle_t) * num_rects); + + command->num_rects = num_rects; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + free (command->rects); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + int x_src, + int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_composite_trapezoids_t *command; + + command = malloc (sizeof (cairo_command_composite_trapezoids_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS; + command->operator = operator; + _cairo_pattern_init_copy (&command->pattern.base, pattern); + command->x_src = x_src; + command->y_src = y_src; + command->x_dst = x_dst; + command->y_dst = y_dst; + command->width = width; + command->height = height; + + command->traps = malloc (sizeof (cairo_trapezoid_t) * num_traps); + if (command->traps == NULL) { + _cairo_pattern_fini (&command->pattern.base); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + memcpy (command->traps, traps, sizeof (cairo_trapezoid_t) * num_traps); + + command->num_traps = num_traps; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + _cairo_pattern_fini (&command->pattern.base); + free (command->traps); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_set_clip_region_t *command; + + command = malloc (sizeof (cairo_command_set_clip_region_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_SET_CLIP_REGION; + + if (region) { + command->region = pixman_region_create (); + pixman_region_copy (command->region, region); + } else { + command->region = NULL; + } + + command->serial = meta->base.current_clip_serial; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + if (command->region) + pixman_region_destroy (command->region); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_intersect_clip_path (void *dst, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + cairo_meta_surface_t *meta = dst; + cairo_command_intersect_clip_path_t *command; + cairo_status_t status; + + command = malloc (sizeof (cairo_command_intersect_clip_path_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_INTERSECT_CLIP_PATH; + + if (path) { + status = _cairo_path_fixed_init_copy (&command->path, path); + if (status) { + free (command); + return status; + } + command->path_pointer = &command->path; + } else { + command->path_pointer = NULL; + } + command->fill_rule = fill_rule; + command->tolerance = tolerance; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + if (path) + _cairo_path_fixed_fini (&command->path); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + cairo_meta_surface_t *meta = abstract_surface; + + /* Currently this is used for getting the extents of the surface + * before calling cairo_paint(). This is the only this that + * requires the meta surface to have an explicit size. If paint + * was just a backend function, this would not be necessary. */ + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = meta->width; + rectangle->height = meta->height; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_show_glyphs_t *command; + + command = malloc (sizeof (cairo_command_show_glyphs_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_SHOW_GLYPHS; + command->scaled_font = scaled_font; + cairo_scaled_font_reference (scaled_font); + command->operator = operator; + _cairo_pattern_init_copy (&command->pattern.base, pattern); + command->source_x = source_x; + command->source_y = source_y; + command->dest_x = dest_x; + command->dest_y = dest_y; + command->width = width; + command->height = height; + + command->glyphs = malloc (sizeof (cairo_glyph_t) * num_glyphs); + if (command->glyphs == NULL) { + _cairo_pattern_fini (&command->pattern.base); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + memcpy (command->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + + command->num_glyphs = num_glyphs; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + _cairo_pattern_fini (&command->pattern.base); + free (command->glyphs); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_meta_surface_fill_path (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + cairo_meta_surface_t *meta = abstract_surface; + cairo_command_fill_path_t *command; + cairo_status_t status; + + command = malloc (sizeof (cairo_command_fill_path_t)); + if (command == NULL) + return CAIRO_STATUS_NO_MEMORY; + + command->type = CAIRO_COMMAND_FILL_PATH; + command->operator = operator; + _cairo_pattern_init_copy (&command->pattern.base, pattern); + status = _cairo_path_fixed_init_copy (&command->path, path); + if (status) { + _cairo_pattern_fini (&command->pattern.base); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + command->fill_rule = fill_rule; + command->tolerance = tolerance; + + if (_cairo_array_append (&meta->commands, &command, 1) == NULL) { + _cairo_path_fixed_fini (&command->path); + _cairo_pattern_fini (&command->pattern.base); + free (command); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_meta_surface_backend = { + _cairo_meta_surface_create_similar, + _cairo_meta_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + _cairo_meta_surface_composite, + _cairo_meta_surface_fill_rectangles, + _cairo_meta_surface_composite_trapezoids, + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_meta_surface_set_clip_region, + _cairo_meta_surface_intersect_clip_path, + _cairo_meta_surface_get_extents, + _cairo_meta_surface_show_glyphs, + _cairo_meta_surface_fill_path, +}; + +cairo_int_status_t +_cairo_meta_surface_replay (cairo_surface_t *surface, + cairo_surface_t *target) +{ + cairo_meta_surface_t *meta; + cairo_command_t *command, **elements; + int i, num_elements; + cairo_int_status_t status; + + meta = (cairo_meta_surface_t *) surface; + status = CAIRO_STATUS_SUCCESS; + + num_elements = meta->commands.num_elements; + elements = (cairo_command_t **) meta->commands.elements; + for (i = 0; i < num_elements; i++) { + command = elements[i]; + switch (command->type) { + case CAIRO_COMMAND_COMPOSITE: + status = _cairo_surface_composite + (command->composite.operator, + &command->composite.src_pattern.base, + command->composite.mask_pattern_pointer, + target, + command->composite.src_x, + command->composite.src_y, + command->composite.mask_x, + command->composite.mask_y, + command->composite.dst_x, + command->composite.dst_y, + command->composite.width, + command->composite.height); + break; + + case CAIRO_COMMAND_FILL_RECTANGLES: + status = _cairo_surface_fill_rectangles + (target, + command->fill_rectangles.operator, + &command->fill_rectangles.color, + command->fill_rectangles.rects, + command->fill_rectangles.num_rects); + break; + + case CAIRO_COMMAND_COMPOSITE_TRAPEZOIDS: + status = _cairo_surface_composite_trapezoids + (command->composite_trapezoids.operator, + &command->composite_trapezoids.pattern.base, + target, + command->composite_trapezoids.x_src, + command->composite_trapezoids.y_src, + command->composite_trapezoids.x_dst, + command->composite_trapezoids.y_dst, + command->composite_trapezoids.width, + command->composite_trapezoids.height, + command->composite_trapezoids.traps, + command->composite_trapezoids.num_traps); + break; + + case CAIRO_COMMAND_SET_CLIP_REGION: + status = _cairo_surface_set_clip_region + (target, + command->set_clip_region.region, + command->set_clip_region.serial); + break; + + case CAIRO_COMMAND_INTERSECT_CLIP_PATH: + /* XXX Meta surface clipping is broken and requires some + * cairo-gstate.c rewriting. Work around it for now. */ + if (target->backend->intersect_clip_path == NULL) + break; + + status = _cairo_surface_intersect_clip_path + (target, + command->intersect_clip_path.path_pointer, + command->intersect_clip_path.fill_rule, + command->intersect_clip_path.tolerance); + break; + + case CAIRO_COMMAND_SHOW_GLYPHS: + status = _cairo_surface_show_glyphs + (command->show_glyphs.scaled_font, + command->show_glyphs.operator, + &command->show_glyphs.pattern.base, + target, + command->show_glyphs.source_x, + command->show_glyphs.source_y, + command->show_glyphs.dest_x, + command->show_glyphs.dest_y, + command->show_glyphs.width, + command->show_glyphs.height, + command->show_glyphs.glyphs, + command->show_glyphs.num_glyphs); + break; + + case CAIRO_COMMAND_FILL_PATH: + /* XXX Meta surface fill_path is broken and requires some + * cairo-gstate.c rewriting. Work around it for now. */ + if (target->backend->fill_path == NULL) + break; + + status = _cairo_surface_fill_path + (command->fill_path.operator, + &command->fill_path.pattern.base, + target, + &command->fill_path.path, + command->fill_path.fill_rule, + command->fill_path.tolerance); + break; + + default: + ASSERT_NOT_REACHED; + } + + if (status) + break; + } + + return status; +} diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index 14b4486a6..17ef7e2cb 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -90,6 +90,26 @@ _cairo_output_stream_write (cairo_output_stream_t *stream, return stream->status; } +void +_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, + const char *data, + size_t length) +{ + const char hex_chars[] = "0123456789abcdef"; + char buffer[2]; + int i, column; + + for (i = 0, column = 0; i < length; i++, column++) { + if (column == 38) { + _cairo_output_stream_write (stream, "\n", 1); + column = 0; + } + buffer[0] = hex_chars[(data[i] >> 4) & 0x0f]; + buffer[1] = hex_chars[data[i] & 0x0f]; + _cairo_output_stream_write (stream, buffer, 2); + } +} + /* Format a double in a locale independent way and trim trailing * zeros. Based on code from Alex Larson <alexl@redhat.com>. * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html @@ -187,8 +207,8 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, switch (*f | length_modifier) { case '%': - p[0] = *f; - p[1] = 0; + buffer[0] = *f; + buffer[1] = 0; break; case 'd': snprintf (buffer, sizeof buffer, "%d", va_arg (ap, int)); @@ -211,6 +231,10 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, case 'f': dtostr (buffer, sizeof buffer, va_arg (ap, double)); break; + case 'c': + buffer[0] = va_arg (ap, int); + buffer[1] = 0; + break; default: ASSERT_NOT_REACHED; } diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index cd099fdd2..b309e8e38 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -294,6 +294,92 @@ _cairo_pattern_create_in_error (cairo_status_t status) return &pattern->base; } +/** + * cairo_pattern_create_rgb: + * @red: red component of the color + * @green: green component of the color + * @blue: blue component of the color + * + * Create a new cairo_pattern_t corresponding to a opaque color. The + * color components are floating point numbers in the range 0 to 1. + * If the values passed in are outside that range, they will be + * clamped. + * + * Return value: the newly created #cairo_pattern_t if succesful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_rgb (double red, double green, double blue) +{ + cairo_color_t color; + + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + + _cairo_color_init_rgb (&color, red, green, blue); + + return _cairo_pattern_create_solid (&color); +} + +/** + * cairo_pattern_create_rgba: + * @red: red component of the color + * @green: green component of the color + * @blue: blue component of the color + * @alpha: alpha component of the color + * + * Create a new cairo_pattern_t corresponding to a translucent color. + * The color components are floating point numbers in the range 0 to + * 1. If the values passed in are outside that range, they will be + * clamped. + * + * Return value: the newly created #cairo_pattern_t if succesful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ +cairo_pattern_t * +cairo_pattern_create_rgba (double red, double green, double blue, + double alpha) +{ + cairo_color_t color; + + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + _cairo_restrict_value (&alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + + return _cairo_pattern_create_solid (&color); +} + +/** + * cairo_pattern_create_for_surface: + * @surface: the surface + * + * Create a new cairo_pattern_t for the given surface. + * + * Return value: the newly created #cairo_pattern_t if succesful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface) { @@ -308,6 +394,28 @@ cairo_pattern_create_for_surface (cairo_surface_t *surface) return &pattern->base; } +/** + * cairo_pattern_create_linear: + * @x0: x coordinate of the start point + * @y0: y coordinate of the start point + * @x1: x coordinate of the end point + * @y1: y coordinate of the end point + * + * Create a new linear gradient cairo_pattern_t along the line defined + * by (x0, y0) and (x1, y1). Before using the gradient pattern, a + * number of color stops should be defined using + * cairo_pattern_add_color_stop_rgb() or + * cairo_pattern_add_color_stop_rgba(). + * + * Return value: the newly created #cairo_pattern_t if succesful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ cairo_pattern_t * cairo_pattern_create_linear (double x0, double y0, double x1, double y1) { @@ -322,6 +430,30 @@ cairo_pattern_create_linear (double x0, double y0, double x1, double y1) return &pattern->base.base; } +/** + * cairo_pattern_create_radial: + * @cx0: x coordinate for the center of the start circle + * @cy0: y coordinate for the center of the start circle + * @radius0: radius of the start cirle + * @cx1: x coordinate for the center of the end circle + * @cy1: y coordinate for the center of the end circle + * @radius1: radius of the end cirle + * + * Create a new radial gradient cairo_pattern_t between the two + * circles defined by (x0, y0, c0) and (x1, y1, c0). Before using the + * gradient pattern, a number of color stops should be defined using + * cairo_pattern_add_color_stop_rgb() or + * cairo_pattern_add_color_stop_rgba(). + * + * Return value: the newly created #cairo_pattern_t if succesful, or + * an error pattern in case of no memory. The caller owns the + * returned object and should call cairo_pattern_destroy() when + * finished with it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect + * the status of a pattern use cairo_pattern_status(). + **/ cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1) @@ -337,6 +469,14 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0, return &pattern->base.base; } +/** + * cairo_pattern_reference: + * @pattern: a #cairo_pattern_t + * + * Increases the reference count on @pattern by one. This prevents + * @pattern from being destroyed until a matching call to + * cairo_pattern_destroy() is made. + **/ void cairo_pattern_reference (cairo_pattern_t *pattern) { @@ -364,6 +504,14 @@ cairo_pattern_status (cairo_pattern_t *pattern) return pattern->status; } +/** + * cairo_pattern_destroy: + * @pattern: a #cairo_pattern_t + * + * Decreases the reference count on @pattern by one. If the result is + * zero, then @pattern and all associated resources are freed. See + * cairo_pattern_reference(). + **/ void cairo_pattern_destroy (cairo_pattern_t *pattern) { @@ -1077,7 +1225,7 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, cairo_surface_attributes_t *attribs) { *out = _cairo_surface_create_similar_solid (dst, - CAIRO_FORMAT_ARGB32, + CAIRO_CONTENT_COLOR_ALPHA, 1, 1, &pattern->color); @@ -1261,19 +1409,24 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, /** * _cairo_pattern_release_surface: * @pattern: a #cairo_pattern_t - * @info: pointer to #cairo_surface_attributes_t filled in by - * _cairo_pattern_acquire_surface + * @surface: a surface obtained by _cairo_pattern_acquire_surface + * @attributes: attributes obtained by _cairo_pattern_acquire_surface * * Releases resources obtained by _cairo_pattern_acquire_surface. **/ void -_cairo_pattern_release_surface (cairo_surface_t *dst, +_cairo_pattern_release_surface (cairo_pattern_t *pattern, cairo_surface_t *surface, cairo_surface_attributes_t *attributes) { if (attributes->acquired) { - _cairo_surface_release_source_image (dst, + cairo_surface_pattern_t *surface_pattern; + + assert (pattern->type == CAIRO_PATTERN_SURFACE); + surface_pattern = (cairo_surface_pattern_t *) pattern; + + _cairo_surface_release_source_image (surface_pattern->surface, (cairo_image_surface_t *) surface, attributes->extra); } @@ -1299,7 +1452,7 @@ _cairo_pattern_acquire_surfaces (cairo_pattern_t *src, cairo_surface_attributes_t *mask_attributes) { cairo_int_status_t status; - cairo_pattern_union_t tmp; + cairo_pattern_union_t src_tmp, mask_tmp; if (src->status) return src->status; @@ -1322,46 +1475,44 @@ _cairo_pattern_acquire_surfaces (cairo_pattern_t *src, combined = src_solid->color; _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - _cairo_pattern_init_solid (&tmp.solid, &combined); + _cairo_pattern_init_solid (&src_tmp.solid, &combined); mask = NULL; } else { - _cairo_pattern_init_copy (&tmp.base, src); + _cairo_pattern_init_copy (&src_tmp.base, src); } - status = _cairo_pattern_acquire_surface (&tmp.base, dst, + status = _cairo_pattern_acquire_surface (&src_tmp.base, dst, src_x, src_y, width, height, src_out, src_attributes); - - _cairo_pattern_fini (&tmp.base); - - if (status) + if (status) { + _cairo_pattern_fini (&src_tmp.base); return status; + } - if (mask) + if (mask == NULL) { - _cairo_pattern_init_copy (&tmp.base, mask); + _cairo_pattern_fini (&src_tmp.base); + *mask_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_pattern_init_copy (&mask_tmp.base, mask); - status = _cairo_pattern_acquire_surface (&tmp.base, dst, - mask_x, mask_y, - width, height, - mask_out, mask_attributes); + status = _cairo_pattern_acquire_surface (&mask_tmp.base, dst, + mask_x, mask_y, + width, height, + mask_out, mask_attributes); - _cairo_pattern_fini (&tmp.base); + if (status) + _cairo_pattern_release_surface (&src_tmp.base, + *src_out, src_attributes); - if (status) - { - _cairo_pattern_release_surface (dst, *src_out, src_attributes); - return status; - } - } - else - { - *mask_out = NULL; - } + _cairo_pattern_fini (&src_tmp.base); + _cairo_pattern_fini (&mask_tmp.base); - return CAIRO_STATUS_SUCCESS; + return status; } diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 3d65aa75e..1c5026ce3 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -36,15 +36,9 @@ #include "cairoint.h" #include "cairo-pdf.h" -/* XXX: Eventually, we need to handle other font backends */ +#include "cairo-font-subset-private.h" #include "cairo-ft-private.h" -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_TRUETYPE_TAGS_H -#include FT_TRUETYPE_TABLES_H - #include <time.h> #include <zlib.h> @@ -93,45 +87,6 @@ * instead of outputting the cm operator in every page. */ -typedef struct ft_subset_glyph ft_subset_glyph_t; -struct ft_subset_glyph { - int parent_index; - unsigned long location; -}; - -typedef struct cairo_pdf_font_backend cairo_pdf_font_backend_t; -struct cairo_pdf_font_backend { - int (*use_glyph) (void *abstract_font, - int glyph); - cairo_status_t (*generate) (void *abstract_font, - const char **data, - unsigned long *length); - void (*destroy) (void *abstract_font); -}; - -typedef struct cairo_pdf_font cairo_pdf_font_t; -struct cairo_pdf_font { - cairo_pdf_font_backend_t *backend; - cairo_unscaled_font_t *unscaled_font; - unsigned int font_id; - char *base_font; - int num_glyphs; - int *widths; - long x_min, y_min, x_max, y_max; - long ascent, descent; -}; - -typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t; -struct cairo_pdf_ft_font { - cairo_pdf_font_t base; - ft_subset_glyph_t *glyphs; - FT_Face face; - int checksum_index; - cairo_array_t output; - int *parent_to_subset; - cairo_status_t status; -}; - typedef struct cairo_pdf_object cairo_pdf_object_t; typedef struct cairo_pdf_resource cairo_pdf_resource_t; typedef struct cairo_pdf_stream cairo_pdf_stream_t; @@ -221,6 +176,9 @@ static cairo_pdf_stream_t * _cairo_pdf_document_open_stream (cairo_pdf_document_t *document, const char *fmt, ...); +static void +_cairo_pdf_document_close_stream (cairo_pdf_document_t *document); + static cairo_surface_t * _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, double width, @@ -233,590 +191,6 @@ _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface); static const cairo_surface_backend_t cairo_pdf_surface_backend; -/* Truetype font subsetting code */ - -#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) - -#define SFNT_VERSION 0x00010000 - -#ifdef WORDS_BIGENDIAN - -#define cpu_to_be16(v) (v) -#define be16_to_cpu(v) (v) -#define cpu_to_be32(v) (v) -#define be32_to_cpu(v) (v) - -#else - -static inline unsigned short -cpu_to_be16(unsigned short v) -{ - return (v << 8) | (v >> 8); -} - -static inline unsigned short -be16_to_cpu(unsigned short v) -{ - return cpu_to_be16 (v); -} - -static inline unsigned long -cpu_to_be32(unsigned long v) -{ - return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); -} - -static inline unsigned long -be32_to_cpu(unsigned long v) -{ - return cpu_to_be32 (v); -} - -#endif - -static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend; - -static int -cairo_pdf_font_use_glyph (cairo_pdf_font_t *font, int glyph) -{ - return font->backend->use_glyph (font, glyph); -} - -static cairo_status_t -cairo_pdf_font_generate (cairo_pdf_font_t *font, - const char **data, unsigned long *length) -{ - return font->backend->generate (font, data, length); -} - -static void -cairo_pdf_font_destroy (cairo_pdf_font_t *font) -{ - if (font == NULL) - return; - - font->backend->destroy (font); -} - -static cairo_pdf_font_t * -cairo_pdf_ft_font_create (cairo_pdf_document_t *document, - cairo_unscaled_font_t *unscaled_font) -{ - FT_Face face; - cairo_pdf_ft_font_t *font; - unsigned long size; - int i, j; - - face = _cairo_ft_unscaled_font_lock_face (unscaled_font); - - /* We currently only support freetype truetype fonts. */ - size = 0; - if (!FT_IS_SFNT (face) || - FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0) - return NULL; - - font = malloc (sizeof (cairo_pdf_ft_font_t)); - if (font == NULL) - return NULL; - - font->base.unscaled_font = unscaled_font; - _cairo_unscaled_font_reference (unscaled_font); - font->base.backend = &cairo_pdf_ft_font_backend; - font->base.font_id = _cairo_pdf_document_new_object (document); - - _cairo_array_init (&font->output, sizeof (char)); - if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) - goto fail1; - - font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); - if (font->glyphs == NULL) - goto fail2; - - font->parent_to_subset = calloc (face->num_glyphs, sizeof (int)); - if (font->parent_to_subset == NULL) - goto fail3; - - font->base.num_glyphs = 1; - font->base.x_min = face->bbox.xMin; - font->base.y_min = face->bbox.yMin; - font->base.x_max = face->bbox.xMax; - font->base.y_max = face->bbox.yMax; - font->base.ascent = face->ascender; - font->base.descent = face->descender; - font->base.base_font = strdup (face->family_name); - if (font->base.base_font == NULL) - goto fail4; - - for (i = 0, j = 0; font->base.base_font[j]; j++) { - if (font->base.base_font[j] == ' ') - continue; - font->base.base_font[i++] = font->base.base_font[j]; - } - font->base.base_font[i] = '\0'; - - font->base.widths = calloc (face->num_glyphs, sizeof (int)); - if (font->base.widths == NULL) - goto fail5; - - _cairo_ft_unscaled_font_unlock_face (unscaled_font); - - font->status = CAIRO_STATUS_SUCCESS; - - return &font->base; - - fail5: - free (font->base.base_font); - fail4: - free (font->parent_to_subset); - fail3: - free (font->glyphs); - fail2: - _cairo_array_fini (&font->output); - fail1: - free (font); - return NULL; -} - -static void -cairo_pdf_ft_font_destroy (void *abstract_font) -{ - cairo_pdf_ft_font_t *font = abstract_font; - - if (font == NULL) - return; - - _cairo_unscaled_font_destroy (font->base.unscaled_font); - free (font->base.base_font); - free (font->parent_to_subset); - free (font->glyphs); - _cairo_array_fini (&font->output); - free (font); -} - -static void * -cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font, - const void *data, size_t length) -{ - void *p; - - p = _cairo_array_append (&font->output, data, length); - if (p == NULL) - font->status = CAIRO_STATUS_NO_MEMORY; - - return p; -} - -static void -cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font, - unsigned short value) -{ - unsigned short be16_value; - - be16_value = cpu_to_be16 (value); - cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value); -} - -static void -cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value) -{ - unsigned long be32_value; - - be32_value = cpu_to_be32 (value); - cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value); -} - -static unsigned long -cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font) -{ - int length, aligned; - static const char pad[4]; - - length = _cairo_array_num_elements (&font->output); - aligned = (length + 3) & ~3; - cairo_pdf_ft_font_write (font, pad, aligned - length); - - return aligned; -} - -static int -cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag) -{ - int i; - - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 1); - - cairo_pdf_ft_font_write_be16 (font, 1); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be32 (font, 12); - - /* Output a format 6 encoding table. */ - - cairo_pdf_ft_font_write_be16 (font, 6); - cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1)); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */ - cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1); - for (i = 1; i < font->base.num_glyphs; i++) - cairo_pdf_ft_font_write_be16 (font, i); - - return font->status; -} - -static int -cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - unsigned char *buffer; - unsigned long size; - - size = 0; - FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size); - buffer = cairo_pdf_ft_font_write (font, NULL, size); - FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size); - - return 0; -} - -static int -cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - unsigned long start_offset, index, size; - TT_Header *header; - unsigned long begin, end; - unsigned char *buffer; - int i; - union { - unsigned char *bytes; - unsigned short *short_offsets; - unsigned long *long_offsets; - } u; - - header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); - if (header->Index_To_Loc_Format == 0) - size = sizeof (short) * (font->face->num_glyphs + 1); - else - size = sizeof (long) * (font->face->num_glyphs + 1); - - u.bytes = malloc (size); - if (u.bytes == NULL) { - font->status = CAIRO_STATUS_NO_MEMORY; - return font->status; - } - FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size); - - start_offset = _cairo_array_num_elements (&font->output); - for (i = 0; i < font->base.num_glyphs; i++) { - index = font->glyphs[i].parent_index; - if (header->Index_To_Loc_Format == 0) { - begin = be16_to_cpu (u.short_offsets[index]) * 2; - end = be16_to_cpu (u.short_offsets[index + 1]) * 2; - } else { - begin = be32_to_cpu (u.long_offsets[index]); - end = be32_to_cpu (u.long_offsets[index + 1]); - } - - size = end - begin; - - font->glyphs[i].location = - cairo_pdf_ft_font_align_output (font) - start_offset; - buffer = cairo_pdf_ft_font_write (font, NULL, size); - if (buffer == NULL) - break; - FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size); - /* FIXME: remap composite glyphs */ - } - - font->glyphs[i].location = - cairo_pdf_ft_font_align_output (font) - start_offset; - - free (u.bytes); - - return font->status; -} - -static int -cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - TT_Header *head; - - head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); - - cairo_pdf_ft_font_write_be32 (font, head->Table_Version); - cairo_pdf_ft_font_write_be32 (font, head->Font_Revision); - - font->checksum_index = _cairo_array_num_elements (&font->output); - cairo_pdf_ft_font_write_be32 (font, 0); - cairo_pdf_ft_font_write_be32 (font, head->Magic_Number); - - cairo_pdf_ft_font_write_be16 (font, head->Flags); - cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM); - - cairo_pdf_ft_font_write_be32 (font, head->Created[0]); - cairo_pdf_ft_font_write_be32 (font, head->Created[1]); - cairo_pdf_ft_font_write_be32 (font, head->Modified[0]); - cairo_pdf_ft_font_write_be32 (font, head->Modified[1]); - - cairo_pdf_ft_font_write_be16 (font, head->xMin); - cairo_pdf_ft_font_write_be16 (font, head->yMin); - cairo_pdf_ft_font_write_be16 (font, head->xMax); - cairo_pdf_ft_font_write_be16 (font, head->yMax); - - cairo_pdf_ft_font_write_be16 (font, head->Mac_Style); - cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM); - - cairo_pdf_ft_font_write_be16 (font, head->Font_Direction); - cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format); - cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format); - - return font->status; -} - -static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag) -{ - TT_HoriHeader *hhea; - - hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea); - - cairo_pdf_ft_font_write_be32 (font, hhea->Version); - cairo_pdf_ft_font_write_be16 (font, hhea->Ascender); - cairo_pdf_ft_font_write_be16 (font, hhea->Descender); - cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap); - - cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max); - - cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing); - cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing); - cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent); - cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise); - cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run); - cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset); - - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 0); - - cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format); - cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); - - return font->status; -} - -static int -cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - unsigned long entry_size; - short *p; - int i; - - for (i = 0; i < font->base.num_glyphs; i++) { - entry_size = 2 * sizeof (short); - p = cairo_pdf_ft_font_write (font, NULL, entry_size); - FT_Load_Sfnt_Table (font->face, TTAG_hmtx, - font->glyphs[i].parent_index * entry_size, - (FT_Byte *) p, &entry_size); - font->base.widths[i] = be16_to_cpu (p[0]); - } - - return font->status; -} - -static int -cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - int i; - TT_Header *header; - - header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); - - if (header->Index_To_Loc_Format == 0) { - for (i = 0; i < font->base.num_glyphs + 1; i++) - cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2); - } else { - for (i = 0; i < font->base.num_glyphs + 1; i++) - cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location); - } - - return font->status; -} - -static int -cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - TT_MaxProfile *maxp; - - maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp); - - cairo_pdf_ft_font_write_be32 (font, maxp->version); - cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); - cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints); - cairo_pdf_ft_font_write_be16 (font, maxp->maxContours); - cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints); - cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours); - cairo_pdf_ft_font_write_be16 (font, maxp->maxZones); - cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints); - cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage); - cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs); - cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs); - cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements); - cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions); - cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements); - cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth); - - return font->status; -} - -typedef struct table table_t; -struct table { - unsigned long tag; - int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag); -}; - -static const table_t truetype_tables[] = { - { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table }, - { TTAG_cvt, cairo_pdf_ft_font_write_generic_table }, - { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table }, - { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table }, - { TTAG_head, cairo_pdf_ft_font_write_head_table }, - { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table }, - { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table }, - { TTAG_loca, cairo_pdf_ft_font_write_loca_table }, - { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table }, - { TTAG_name, cairo_pdf_ft_font_write_generic_table }, - { TTAG_prep, cairo_pdf_ft_font_write_generic_table }, -}; - -static cairo_status_t -cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font) -{ - unsigned short search_range, entry_selector, range_shift; - int num_tables; - - num_tables = ARRAY_LENGTH (truetype_tables); - search_range = 1; - entry_selector = 0; - while (search_range * 2 <= num_tables) { - search_range *= 2; - entry_selector++; - } - search_range *= 16; - range_shift = num_tables * 16 - search_range; - - cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION); - cairo_pdf_ft_font_write_be16 (font, num_tables); - cairo_pdf_ft_font_write_be16 (font, search_range); - cairo_pdf_ft_font_write_be16 (font, entry_selector); - cairo_pdf_ft_font_write_be16 (font, range_shift); - - cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16); - - return font->status; -} - -static unsigned long -cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font, - unsigned long start, unsigned long end) -{ - unsigned long *padded_end; - unsigned long *p; - unsigned long checksum; - char *data; - - checksum = 0; - data = _cairo_array_index (&font->output, 0); - p = (unsigned long *) (data + start); - padded_end = (unsigned long *) (data + ((end + 3) & ~3)); - while (p < padded_end) - checksum += *p++; - - return checksum; -} - -static void -cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag, - unsigned long start, unsigned long end) -{ - unsigned long *entry; - - entry = _cairo_array_index (&font->output, 12 + 16 * index); - entry[0] = cpu_to_be32 (tag); - entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end)); - entry[2] = cpu_to_be32 (start); - entry[3] = cpu_to_be32 (end - start); -} - -static cairo_status_t -cairo_pdf_ft_font_generate (void *abstract_font, - const char **data, unsigned long *length) -{ - cairo_pdf_ft_font_t *font = abstract_font; - unsigned long start, end, next, checksum; - unsigned long *checksum_location; - int i; - - font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font); - - if (cairo_pdf_ft_font_write_offset_table (font)) - goto fail; - - start = cairo_pdf_ft_font_align_output (font); - end = start; - - end = 0; - for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { - if (truetype_tables[i].write (font, truetype_tables[i].tag)) - goto fail; - - end = _cairo_array_num_elements (&font->output); - next = cairo_pdf_ft_font_align_output (font); - cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag, - start, end); - start = next; - } - - checksum = - 0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end); - checksum_location = _cairo_array_index (&font->output, font->checksum_index); - *checksum_location = cpu_to_be32 (checksum); - - *data = _cairo_array_index (&font->output, 0); - *length = _cairo_array_num_elements (&font->output); - - fail: - _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font); - font->face = NULL; - - return font->status; -} - -static int -cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph) -{ - cairo_pdf_ft_font_t *font = abstract_font; - - if (font->parent_to_subset[glyph] == 0) { - font->parent_to_subset[glyph] = font->base.num_glyphs; - font->glyphs[font->base.num_glyphs].parent_index = glyph; - font->base.num_glyphs++; - } - - return font->parent_to_subset[glyph]; -} - -static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend = { - cairo_pdf_ft_font_use_glyph, - cairo_pdf_ft_font_generate, - cairo_pdf_ft_font_destroy -}; - -/* PDF Generation */ - static unsigned int _cairo_pdf_document_new_object (cairo_pdf_document_t *document) { @@ -1015,8 +389,8 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) } static cairo_surface_t * -_cairo_pdf_surface_create_similar (void *abstract_src, - cairo_format_t format, +_cairo_pdf_surface_create_similar (void *abstract_src, + cairo_content_t content, int width, int height) { @@ -1111,6 +485,12 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_pdf_document_destroy (document); + _cairo_array_fini (&surface->streams); + _cairo_array_fini (&surface->patterns); + _cairo_array_fini (&surface->xobjects); + _cairo_array_fini (&surface->alphas); + _cairo_array_fini (&surface->fonts); + return status; } @@ -1851,14 +1231,19 @@ _cairo_pdf_surface_get_extents (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static cairo_pdf_font_t * +static cairo_font_subset_t * _cairo_pdf_document_get_font (cairo_pdf_document_t *document, cairo_scaled_font_t *scaled_font) { cairo_unscaled_font_t *unscaled_font; - cairo_pdf_font_t *pdf_font; + cairo_font_subset_t *pdf_font; unsigned int num_fonts, i; + /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ + if (! _cairo_scaled_font_is_ft (scaled_font)) + return NULL; + + /* XXX Why is this an ft specific function? */ unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font); num_fonts = _cairo_array_num_elements (&document->fonts); @@ -1870,12 +1255,14 @@ _cairo_pdf_document_get_font (cairo_pdf_document_t *document, /* FIXME: Figure out here which font backend is in use and call * the appropriate constructor. */ - pdf_font = cairo_pdf_ft_font_create (document, unscaled_font); + pdf_font = _cairo_font_subset_create (unscaled_font); if (pdf_font == NULL) return NULL; + pdf_font->font_id = _cairo_pdf_document_new_object (document); + if (_cairo_array_append (&document->fonts, &pdf_font, 1) == NULL) { - cairo_pdf_font_destroy (pdf_font); + _cairo_font_subset_destroy (pdf_font); return NULL; } @@ -1899,9 +1286,13 @@ _cairo_pdf_surface_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_document_t *document = surface->document; cairo_output_stream_t *output = document->output_stream; - cairo_pdf_font_t *pdf_font; + cairo_font_subset_t *pdf_font; int i, index; + /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ + if (! _cairo_scaled_font_is_ft (scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + pdf_font = _cairo_pdf_document_get_font (document, scaled_font); if (pdf_font == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -1912,7 +1303,7 @@ _cairo_pdf_surface_show_glyphs (cairo_scaled_font_t *scaled_font, "BT /res%u 1 Tf", pdf_font->font_id); for (i = 0; i < num_glyphs; i++) { - index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); + index = _cairo_font_subset_use_glyph (pdf_font, glyphs[i].index); _cairo_output_stream_printf (output, " %f %f %f %f %f %f Tm (\\%o) Tj", @@ -2036,7 +1427,7 @@ _cairo_pdf_document_create (cairo_output_stream_t *output_stream, document->pages_id = _cairo_pdf_document_new_object (document); - _cairo_array_init (&document->fonts, sizeof (cairo_pdf_font_t *)); + _cairo_array_init (&document->fonts, sizeof (cairo_font_subset_t *)); /* Document header */ _cairo_output_stream_printf (output_stream, @@ -2100,7 +1491,7 @@ static cairo_status_t _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) { cairo_output_stream_t *output = document->output_stream; - cairo_pdf_font_t *font; + cairo_font_subset_t *font; int num_fonts, i, j; const char *data; char *compressed; @@ -2112,7 +1503,7 @@ _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) for (i = 0; i < num_fonts; i++) { _cairo_array_copy_element (&document->fonts, i, &font); - status = cairo_pdf_font_generate (font, &data, &data_size); + status = _cairo_font_subset_generate (font, &data, &data_size); if (status) goto fail; @@ -2195,7 +1586,7 @@ _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) "endobj\r\n"); fail: - cairo_pdf_ft_font_destroy (font); + _cairo_font_subset_destroy (font); } return status; @@ -2303,6 +1694,10 @@ _cairo_pdf_document_finish (cairo_pdf_document_t *document) status = _cairo_output_stream_get_status (output); _cairo_output_stream_destroy (output); + _cairo_array_fini (&document->objects); + _cairo_array_fini (&document->pages); + _cairo_array_fini (&document->fonts); + document->finished = TRUE; return status; diff --git a/src/cairo-png.c b/src/cairo-png.c index d7297848d..532054e51 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -38,6 +38,7 @@ #include <png.h> #include "cairoint.h" +/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ static void unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) { @@ -53,14 +54,33 @@ unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) if (alpha == 0) { b[0] = b[1] = b[2] = b[3] = 0; } else { - b[0] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; - b[2] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; b[3] = alpha; } } } +/* Converts native endian xRGB => RGBx bytes */ +static void +convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) +{ + int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + + memcpy (&pixel, b, sizeof (uint32_t)); + + b[0] = (pixel & 0xff0000) >> 16; + b[1] = (pixel & 0x00ff00) >> 8; + b[2] = (pixel & 0x0000ff) >> 0; + b[3] = 0; + } +} + static cairo_status_t write_png (cairo_surface_t *surface, png_rw_ptr write_func, @@ -153,14 +173,19 @@ write_png (cairo_surface_t *surface, png_convert_from_time_t (&pt, time (NULL)); png_set_tIME (png, info, &pt); - png_set_write_user_transform_fn (png, unpremultiply_data); - if (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_RGB24) - png_set_bgr (png); + /* We have to call png_write_info() before setting up the write + * transformation, since it stores data internally in 'png' + * that is needed for the write transformation functions to work. + */ + png_write_info (png, info); + + if (image->format == CAIRO_FORMAT_ARGB32) + png_set_write_user_transform_fn (png, unpremultiply_data); + else if (image->format == CAIRO_FORMAT_RGB24) + png_set_write_user_transform_fn (png, convert_data_to_bytes); if (image->format == CAIRO_FORMAT_RGB24) png_set_filler (png, 0, PNG_FILLER_AFTER); - - png_write_info (png, info); + png_write_image (png, rows); png_write_end (png, info); @@ -262,6 +287,7 @@ cairo_surface_write_to_png_stream (cairo_surface_t *surface, return write_png (surface, stream_write_func, &png_closure); } +/* Premultiplies data and converts RGBA bytes => native endian */ static void premultiply_data (png_structp png, png_row_infop row_info, @@ -271,9 +297,9 @@ premultiply_data (png_structp png, for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *base = &data[i]; - uint8_t blue = base[0]; + uint8_t red = base[0]; uint8_t green = base[1]; - uint8_t red = base[2]; + uint8_t blue = base[2]; uint8_t alpha = base[3]; uint32_t p; @@ -352,7 +378,6 @@ read_png (png_rw_ptr read_func, if (interlace != PNG_INTERLACE_NONE) png_set_interlace_handling (png); - png_set_bgr (png); png_set_filler (png, 0xff, PNG_FILLER_AFTER); png_set_read_user_transform_fn (png, premultiply_data); @@ -383,7 +408,7 @@ read_png (png_rw_ptr read_func, BAIL: free (row_pointers); free (data); - png_destroy_read_struct (&png, NULL, NULL); + png_destroy_read_struct (&png, &info, NULL); return surface; } diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index dd12c2ca5..0464b5a0a 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California + * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -32,14 +33,27 @@ * * Contributor(s): * Carl D. Worth <cworth@cworth.org> + * Kristian Høgsberg <krh@redhat.com> */ #include "cairoint.h" #include "cairo-ps.h" +#include "cairo-font-subset-private.h" +#include "cairo-meta-surface-private.h" +#include "cairo-ft-private.h" #include <time.h> #include <zlib.h> +/* TODO: + * + * - Add document structure convention comments where appropriate. + * + * - Fix image compression. + * + * - Create a set of procs to use... specifically a trapezoid proc. + */ + static const cairo_surface_backend_t cairo_ps_surface_backend; typedef struct cairo_ps_surface { @@ -48,29 +62,31 @@ typedef struct cairo_ps_surface { /* PS-specific fields */ cairo_output_stream_t *stream; - double width_in_points; - double height_in_points; + double width; + double height; double x_dpi; double y_dpi; - int pages; - - cairo_image_surface_t *image; + cairo_surface_t *current_page; + cairo_array_t pages; + cairo_array_t fonts; } cairo_ps_surface_t; #define PS_SURFACE_DPI_DEFAULT 300.0 -static void -_cairo_ps_surface_erase (cairo_ps_surface_t *surface); +static cairo_int_status_t +_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface); + +static cairo_int_status_t +_cairo_ps_surface_render_page (cairo_ps_surface_t *surface, + cairo_surface_t *page, int page_number); static cairo_surface_t * _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, - double width_in_points, - double height_in_points) + double width, + double height) { cairo_ps_surface_t *surface; - int width_in_pixels, height_in_pixels; - time_t now = time (0); surface = malloc (sizeof (cairo_ps_surface_t)); if (surface == NULL) @@ -80,45 +96,20 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->stream = stream; - surface->width_in_points = width_in_points; - surface->height_in_points = height_in_points; + surface->width = width; + surface->height = height; surface->x_dpi = PS_SURFACE_DPI_DEFAULT; surface->y_dpi = PS_SURFACE_DPI_DEFAULT; - surface->pages = 0; - - width_in_pixels = (int) (surface->x_dpi * width_in_points / 72.0 + 1.0); - height_in_pixels = (int) (surface->y_dpi * height_in_points / 72.0 + 1.0); - - surface->image = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - width_in_pixels, height_in_pixels); - if (surface->image == NULL) { + surface->current_page = _cairo_meta_surface_create (width, + height); + if (surface->current_page == NULL) { free (surface); return NULL; } - _cairo_ps_surface_erase (surface); - - /* Document header */ - _cairo_output_stream_printf (surface->stream, - "%%!PS-Adobe-3.0\n" - "%%%%Creator: Cairo (http://cairographics.org)\n"); - _cairo_output_stream_printf (surface->stream, - "%%%%CreationDate: %s", - ctime (&now)); - _cairo_output_stream_printf (surface->stream, - "%%%%BoundingBox: %f %f %f %f\n", - 0.0, 0.0, - surface->width_in_points, - surface->height_in_points); - /* The "/FlateDecode filter" currently used is a feature of LanguageLevel 3 */ - _cairo_output_stream_printf (surface->stream, - "%%%%DocumentData: Clean7Bit\n" - "%%%%LanguageLevel: 3\n"); - _cairo_output_stream_printf (surface->stream, - "%%%%Orientation: Portrait\n" - "%%%%EndComments\n"); + _cairo_array_init (&surface->pages, sizeof (cairo_surface_t *)); + _cairo_array_init (&surface->fonts, sizeof (cairo_font_subset_t *)); return &surface->base; } @@ -157,10 +148,10 @@ cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, } static cairo_surface_t * -_cairo_ps_surface_create_similar (void *abstract_src, - cairo_format_t format, - int width, - int height) +_cairo_ps_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) { return NULL; } @@ -169,210 +160,1160 @@ static cairo_status_t _cairo_ps_surface_finish (void *abstract_surface) { cairo_ps_surface_t *surface = abstract_surface; + cairo_surface_t *page; + cairo_font_subset_t *subset; + cairo_status_t status; + int i; + time_t now; + + now = time (0); + + /* Document header */ + _cairo_output_stream_printf (surface->stream, + "%%!PS-Adobe-3.0\n" + "%%%%Creator: Cairo (http://cairographics.org)\n" + "%%%%CreationDate: %s" + "%%%%Title: Some clever title\n" + "%%%%Pages: %d\n" + "%%%%BoundingBox: %f %f %f %f\n", + ctime (&now), + surface->pages.num_elements, + 0.0, 0.0, + surface->width, + surface->height); + + /* The "/FlateDecode filter" currently used is a feature of + * LanguageLevel 3 */ + _cairo_output_stream_printf (surface->stream, + "%%%%DocumentData: Clean7Bit\n" + "%%%%LanguageLevel: 3\n" + "%%%%Orientation: Portrait\n" + "%%%%EndComments\n"); + + _cairo_ps_surface_write_font_subsets (surface); + + status = CAIRO_STATUS_SUCCESS; + for (i = 0; i < surface->pages.num_elements; i++) { + _cairo_array_copy_element (&surface->pages, i, &page); + + status = _cairo_ps_surface_render_page (surface, page, i + 1); + if (status) + break; + } /* Document footer */ _cairo_output_stream_printf (surface->stream, + "%%%%Trailer\n" "%%%%EOF\n"); - cairo_surface_destroy (&surface->image->base); + for (i = 0; i < surface->pages.num_elements; i++) { + _cairo_array_copy_element (&surface->pages, i, &page); + cairo_surface_destroy (page); + } + _cairo_array_fini (&surface->pages); - return CAIRO_STATUS_SUCCESS; + for (i = 0; i < surface->fonts.num_elements; i++) { + _cairo_array_copy_element (&surface->fonts, i, &subset); + _cairo_font_subset_destroy (subset); + } + _cairo_array_fini (&surface->fonts); + + _cairo_output_stream_destroy (surface->stream); + cairo_surface_destroy (surface->current_page); + + return status; } -static void -_cairo_ps_surface_erase (cairo_ps_surface_t *surface) +static cairo_int_status_t +_cairo_ps_surface_composite (cairo_operator_t operator, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { - _cairo_surface_fill_rectangle (&surface->image->base, - CAIRO_OPERATOR_SOURCE, - CAIRO_COLOR_TRANSPARENT, - 0, 0, - surface->image->width, - surface->image->height); + cairo_ps_surface_t *surface = abstract_dst; + + return _cairo_surface_composite (operator, + src_pattern, + mask_pattern, + surface->current_page, + src_x, + src_y, + mask_x, + mask_y, + dst_x, + dst_y, + width, + height); } -static cairo_status_t -_cairo_ps_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) +static cairo_int_status_t +_cairo_ps_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) { cairo_ps_surface_t *surface = abstract_surface; - - *image_out = surface->image; + + return _cairo_surface_fill_rectangles (surface->current_page, + operator, + color, + rects, + num_rects); +} + +static cairo_int_status_t +_cairo_ps_surface_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + int x_src, + int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) +{ + cairo_ps_surface_t *surface = abstract_dst; + + return _cairo_surface_composite_trapezoids (operator, + pattern, + surface->current_page, + x_src, + y_src, + x_dst, + y_dst, + width, + height, + traps, + num_traps); +} + +static cairo_int_status_t +_cairo_ps_surface_copy_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + /* FIXME: We need to copy the meta surface contents here */ + + return _cairo_surface_show_page (&surface->base); +} + +static cairo_int_status_t +_cairo_ps_surface_show_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + _cairo_array_append (&surface->pages, &surface->current_page, 1); + surface->current_page = _cairo_meta_surface_create (surface->width, + surface->height); + if (surface->current_page == NULL) + return CAIRO_STATUS_NO_MEMORY; return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ps_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) +static cairo_int_status_t +_cairo_ps_surface_intersect_clip_path (void *dst, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + cairo_ps_surface_t *surface = dst; + + return _cairo_surface_intersect_clip_path (surface->current_page, + path, + fill_rule, + tolerance); +} + +static cairo_int_status_t +_cairo_ps_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) { cairo_ps_surface_t *surface = abstract_surface; - - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->image->width; - image_rect->height = surface->image->height; - - *image_out = surface->image; + + rectangle->x = 0; + rectangle->y = 0; + + /* XXX: The conversion to integers here is pretty bogus, (not to + * mention the aribitray limitation of width to a short(!). We + * may need to come up with a better interface for get_size. + */ + rectangle->width = surface->width; + rectangle->height = surface->height; return CAIRO_STATUS_SUCCESS; } +static cairo_font_subset_t * +_cairo_ps_surface_get_font (cairo_ps_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_unscaled_font_t *unscaled_font; + cairo_font_subset_t *subset; + unsigned int num_fonts, i; + + /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ + if (! _cairo_scaled_font_is_ft (scaled_font)) + return NULL; + + /* XXX Why is this an ft specific function? */ + unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font); + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &subset); + if (subset->unscaled_font == unscaled_font) + return subset; + } + + subset = _cairo_font_subset_create (unscaled_font); + if (subset == NULL) + return NULL; + + subset->font_id = surface->fonts.num_elements; + if (_cairo_array_append (&surface->fonts, &subset, 1) == NULL) { + _cairo_font_subset_destroy (subset); + return NULL; + } + + return subset; +} + static cairo_int_status_t -_cairo_ps_surface_copy_page (void *abstract_surface) +_cairo_ps_surface_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_ps_surface_t *surface = abstract_surface; - int width = surface->image->width; - int height = surface->image->height; - cairo_output_stream_t *stream = surface->stream; + cairo_font_subset_t *subset; + int i; + + /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ + if (! _cairo_scaled_font_is_ft (scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Collect font subset info as we go. */ + subset = _cairo_ps_surface_get_font (surface, scaled_font); + if (subset == NULL) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < num_glyphs; i++) + _cairo_font_subset_use_glyph (subset, glyphs[i].index); + + return _cairo_surface_show_glyphs (scaled_font, + operator, + pattern, + surface->current_page, + source_x, + source_y, + dest_x, + dest_y, + width, + height, + glyphs, + num_glyphs); +} - int i, x, y; +static cairo_int_status_t +_cairo_ps_surface_fill_path (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + cairo_ps_surface_t *surface = abstract_dst; + + return _cairo_surface_fill_path (operator, + pattern, + surface->current_page, + path, + fill_rule, + tolerance); +} - cairo_solid_pattern_t white_pattern; - unsigned char *rgb, *compressed; - unsigned long rgb_size, compressed_size; +static const cairo_surface_backend_t cairo_ps_surface_backend = { + _cairo_ps_surface_create_similar, + _cairo_ps_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + _cairo_ps_surface_composite, + _cairo_ps_surface_fill_rectangles, + _cairo_ps_surface_composite_trapezoids, + _cairo_ps_surface_copy_page, + _cairo_ps_surface_show_page, + NULL, /* set_clip_region */ + _cairo_ps_surface_intersect_clip_path, + _cairo_ps_surface_get_extents, + _cairo_ps_surface_show_glyphs, + _cairo_ps_surface_fill_path +}; - rgb_size = 3 * width * height; - rgb = malloc (rgb_size); - if (rgb == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; +static cairo_int_status_t +_cairo_ps_surface_write_type42_dict (cairo_ps_surface_t *surface, + cairo_font_subset_t *subset) +{ + const char *data; + unsigned long data_size; + cairo_status_t status; + int i; + + status = CAIRO_STATUS_SUCCESS; + + /* FIXME: Figure out document structure convention for fonts */ + + _cairo_output_stream_printf (surface->stream, + "11 dict begin\n" + "/FontType 42 def\n" + "/FontName /f%d def\n" + "/PaintType 0 def\n" + "/FontMatrix [ 1 0 0 1 0 0 ] def\n" + "/FontBBox [ 0 0 0 0 ] def\n" + "/Encoding 256 array def\n" + "0 1 255 { Encoding exch /.notdef put } for\n", + subset->font_id); + + /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ + + for (i = 1; i < subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->stream, + "Encoding %d /g%d put\n", i, i); + + _cairo_output_stream_printf (surface->stream, + "/CharStrings %d dict dup begin\n" + "/.notdef 0 def\n", + subset->num_glyphs); + + for (i = 1; i < subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->stream, + "/g%d %d def\n", i, i); + + _cairo_output_stream_printf (surface->stream, + "end readonly def\n"); + + status = _cairo_font_subset_generate (subset, &data, &data_size); + + /* FIXME: We need to break up fonts bigger than 64k so we don't + * exceed string size limitation. At glyph boundaries. Stupid + * postscript. */ + _cairo_output_stream_printf (surface->stream, + "/sfnts [<"); + + _cairo_output_stream_write_hex_string (surface->stream, data, data_size); + + _cairo_output_stream_printf (surface->stream, + ">] def\n" + "FontName currentdict end definefont pop\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface) +{ + cairo_font_subset_t *subset; + int i; + + for (i = 0; i < surface->fonts.num_elements; i++) { + _cairo_array_copy_element (&surface->fonts, i, &subset); + _cairo_ps_surface_write_type42_dict (surface, subset); } - compressed_size = (int) (1.0 + 1.1 * rgb_size); - compressed = malloc (compressed_size); - if (compressed == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _cairo_ps_fallback_area cairo_ps_fallback_area_t; +struct _cairo_ps_fallback_area { + int x, y; + unsigned int width, height; + cairo_ps_fallback_area_t *next; +}; + +typedef struct _ps_output_surface { + cairo_surface_t base; + cairo_ps_surface_t *parent; + cairo_ps_fallback_area_t *fallback_areas; +} ps_output_surface_t; + +static cairo_int_status_t +_ps_output_add_fallback_area (ps_output_surface_t *surface, + int x, int y, + unsigned int width, + unsigned int height) +{ + cairo_ps_fallback_area_t *area; + + /* FIXME: Do a better job here. Ideally, we would use a 32 bit + * region type, but probably just computing bounding boxes would + * also work fine. */ + + area = malloc (sizeof (cairo_ps_fallback_area_t)); + if (area == NULL) + return CAIRO_STATUS_NO_MEMORY; + + area->x = x; + area->y = y; + area->width = width; + area->height = height; + area->next = surface->fallback_areas; + + surface->fallback_areas = area; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_ps_output_finish (void *abstract_surface) +{ + ps_output_surface_t *surface = abstract_surface; + cairo_ps_fallback_area_t *area, *next; + + for (area = surface->fallback_areas; area != NULL; area = next) { + next = area->next; + free (area); } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +color_is_gray (cairo_color_t *color) +{ + const double epsilon = 0.00001; + + return (fabs (color->red - color->green) < epsilon && + fabs (color->red - color->blue) < epsilon); +} + +static cairo_bool_t +pattern_is_translucent (cairo_pattern_t *abstract_pattern) +{ + cairo_pattern_union_t *pattern; + + pattern = (cairo_pattern_union_t *) abstract_pattern; + switch (pattern->base.type) { + case CAIRO_PATTERN_SOLID: + return pattern->solid.color.alpha < 0.9; + case CAIRO_PATTERN_SURFACE: + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +/* PS Output - this section handles output of the parts of the meta + * surface we can render natively in PS. */ + +static void * +compress_dup (const void *data, unsigned long data_size, + unsigned long *compressed_size) +{ + void *compressed; + + /* Bound calculation taken from zlib. */ + *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11; + compressed = malloc (*compressed_size); + if (compressed == NULL) + return NULL; + + compress (compressed, compressed_size, data, data_size); + + return compressed; +} + +static cairo_status_t +emit_image (cairo_ps_surface_t *surface, + cairo_image_surface_t *image, + cairo_matrix_t *matrix) +{ + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + unsigned char *rgb, *compressed; + unsigned long rgb_size, compressed_size; + cairo_surface_t *opaque; + cairo_pattern_union_t pattern; + cairo_matrix_t d2i; + int x, y, i; + /* PostScript can not represent the alpha channel, so we blend the current image over a white RGB surface to eliminate it. */ - _cairo_pattern_init_solid (&white_pattern, CAIRO_COLOR_WHITE); + opaque = _cairo_surface_create_similar_solid (&image->base, + CAIRO_CONTENT_COLOR, + image->width, + image->height, + CAIRO_COLOR_WHITE); + if (opaque == NULL) + goto bail0; + + _cairo_pattern_init_for_surface (&pattern.surface, &image->base); _cairo_surface_composite (CAIRO_OPERATOR_DEST_OVER, - &white_pattern.base, + &pattern.base, NULL, - &surface->image->base, + opaque, 0, 0, 0, 0, 0, 0, - width, height); - - _cairo_pattern_fini (&white_pattern.base); + image->width, + image->height); + + _cairo_pattern_fini (&pattern.base); + + rgb_size = 3 * image->width * image->height; + rgb = malloc (rgb_size); + if (rgb == NULL) + goto bail1; i = 0; - for (y = 0; y < height; y++) { - pixman_bits_t *pixel = (pixman_bits_t *) (surface->image->data + y * surface->image->stride); - for (x = 0; x < width; x++, pixel++) { + for (y = 0; y < image->height; y++) { + pixman_bits_t *pixel = (pixman_bits_t *) (image->data + y * image->stride); + for (x = 0; x < image->width; x++, pixel++) { rgb[i++] = (*pixel & 0x00ff0000) >> 16; rgb[i++] = (*pixel & 0x0000ff00) >> 8; rgb[i++] = (*pixel & 0x000000ff) >> 0; } } - compress (compressed, &compressed_size, rgb, rgb_size); + compressed = compress_dup (rgb, rgb_size, &compressed_size); + if (compressed == NULL) + goto bail2; - /* Page header */ - _cairo_output_stream_printf (stream, "%%%%Page: %d\n", ++surface->pages); + /* matrix transforms from user space to image space. We need to + * transform from device space to image space to compensate for + * postscripts coordinate system. */ + cairo_matrix_init (&d2i, 1, 0, 0, -1, 0, surface->height); + cairo_matrix_multiply (&d2i, &d2i, matrix); - _cairo_output_stream_printf (stream, "gsave\n"); - - /* Image header goop */ - _cairo_output_stream_printf (stream, "%f %f translate\n", - 0.0, surface->height_in_points); - _cairo_output_stream_printf (stream, "/DeviceRGB setcolorspace\n"); - _cairo_output_stream_printf (stream, "<<\n"); - _cairo_output_stream_printf (stream, " /ImageType 1\n"); - _cairo_output_stream_printf (stream, " /Width %d\n", width); - _cairo_output_stream_printf (stream, " /Height %d\n", height); - _cairo_output_stream_printf (stream, " /BitsPerComponent 8\n"); - _cairo_output_stream_printf (stream, " /Decode [ 0 1 0 1 0 1 ]\n"); - _cairo_output_stream_printf (stream, " /DataSource currentfile /FlateDecode filter\n"); - _cairo_output_stream_printf (stream, " /ImageMatrix [ 1 0 0 -1 0 1 ]\n"); - _cairo_output_stream_printf (stream, ">>\n"); - _cairo_output_stream_printf (stream, "image\n"); + _cairo_output_stream_printf (surface->stream, + "/DeviceRGB setcolorspace\n" + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [ 0 1 0 1 0 1 ]\n" + " /DataSource currentfile\n" + " /ImageMatrix [ %f %f %f %f %f %f ]\n" + ">>\n" + "image\n", + image->width, + image->height, + d2i.xx, d2i.yx, + d2i.xy, d2i.yy, + d2i.x0, d2i.y0); /* Compressed image data */ - _cairo_output_stream_write (stream, compressed, compressed_size); - - _cairo_output_stream_printf (stream, "showpage\n"); + _cairo_output_stream_write (surface->stream, rgb, rgb_size); + status = CAIRO_STATUS_SUCCESS; - _cairo_output_stream_printf (stream, "grestore\n"); - - /* Page footer */ - _cairo_output_stream_printf (stream, "%%%%EndPage\n"); + _cairo_output_stream_printf (surface->stream, + "\n"); free (compressed); - BAIL1: + bail2: free (rgb); - BAIL0: + bail1: + cairo_surface_destroy (opaque); + bail0: return status; } +static void +emit_solid_pattern (cairo_ps_surface_t *surface, + cairo_solid_pattern_t *pattern) +{ + if (color_is_gray (&pattern->color)) + _cairo_output_stream_printf (surface->stream, + "%f setgray\n", + pattern->color.red); + else + _cairo_output_stream_printf (surface->stream, + "%f %f %f setrgbcolor\n", + pattern->color.red, + pattern->color.green, + pattern->color.blue); +} + +static void +emit_surface_pattern (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ +} + +static void +emit_linear_pattern (cairo_ps_surface_t *surface, + cairo_linear_pattern_t *pattern) +{ +} + +static void +emit_radial_pattern (cairo_ps_surface_t *surface, + cairo_radial_pattern_t *pattern) +{ +} + +static void +emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern) +{ + /* FIXME: We should keep track of what pattern is currently set in + * the postscript file and only emit code if we're setting a + * different pattern. */ + + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: + emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_SURFACE: + emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_LINEAR: + emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_RADIAL: + emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); + break; + } +} + + static cairo_int_status_t -_cairo_ps_surface_show_page (void *abstract_surface) +_ps_output_composite (cairo_operator_t operator, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { - cairo_int_status_t status; - cairo_ps_surface_t *surface = abstract_surface; + ps_output_surface_t *surface = abstract_dst; + cairo_output_stream_t *stream = surface->parent->stream; + cairo_surface_pattern_t *surface_pattern; + cairo_status_t status; + cairo_image_surface_t *image; + void *image_extra; + + if (mask_pattern) { + /* FIXME: Investigate how this can be done... we'll probably + * need pixmap fallbacks for this, though. */ + _cairo_output_stream_printf (stream, + "%% _ps_output_composite: with mask\n"); + return CAIRO_STATUS_SUCCESS; + } - status = _cairo_ps_surface_copy_page (surface); - if (status) - return status; + status = CAIRO_STATUS_SUCCESS; + switch (src_pattern->type) { + case CAIRO_PATTERN_SOLID: + _cairo_output_stream_printf (stream, + "%% _ps_output_composite: solid\n"); + break; + + case CAIRO_PATTERN_SURFACE: + surface_pattern = (cairo_surface_pattern_t *) src_pattern; + + if (src_pattern->extend != CAIRO_EXTEND_NONE) { + _cairo_output_stream_printf (stream, + "%% _ps_output_composite: repeating image\n"); + break; + } + + + status = _cairo_surface_acquire_source_image (surface_pattern->surface, + &image, + &image_extra); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_output_stream_printf (stream, + "%% _ps_output_composite: src_pattern not available as image\n"); + break; + } else if (status) { + break; + } + status = emit_image (surface->parent, image, &src_pattern->matrix); + _cairo_surface_release_source_image (surface_pattern->surface, + image, image_extra); + break; + + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: + _cairo_output_stream_printf (stream, + "%% _ps_output_composite: gradient\n"); + break; + } - _cairo_ps_surface_erase (surface); + return status; +} + +static cairo_int_status_t +_ps_output_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + ps_output_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->parent->stream; + cairo_solid_pattern_t solid; + int i; + + _cairo_output_stream_printf (stream, + "%% _ps_output_fill_rectangles\n"); + + _cairo_pattern_init_solid (&solid, color); + emit_pattern (surface->parent, &solid.base); + _cairo_pattern_fini (&solid.base); + + _cairo_output_stream_printf (stream, "["); + for (i = 0; i < num_rects; i++) { + _cairo_output_stream_printf (stream, + " %d %d %d %d", + rects[i].x, + surface->parent->height - rects[i].y - rects[i].height, + rects[i].width, rects[i].height); + } + + _cairo_output_stream_printf (stream, " ] rectfill\n"); return CAIRO_STATUS_SUCCESS; } +static double +intersect (cairo_line_t *line, cairo_fixed_t y) +{ + return _cairo_fixed_to_double (line->p1.x) + + _cairo_fixed_to_double (line->p2.x - line->p1.x) * + _cairo_fixed_to_double (y - line->p1.y) / + _cairo_fixed_to_double (line->p2.y - line->p1.y); +} + static cairo_int_status_t -_cairo_ps_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) +_ps_output_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + int x_src, + int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) { - cairo_ps_surface_t *surface = abstract_surface; + ps_output_surface_t *surface = abstract_dst; + cairo_output_stream_t *stream = surface->parent->stream; + int i; + + if (pattern_is_translucent (pattern)) + return _ps_output_add_fallback_area (surface, x_dst, y_dst, width, height); + + _cairo_output_stream_printf (stream, + "%% _ps_output_composite_trapezoids\n"); + + emit_pattern (surface->parent, pattern); + + for (i = 0; i < num_traps; i++) { + double left_x1, left_x2, right_x1, right_x2, top, bottom; + + left_x1 = intersect (&traps[i].left, traps[i].top); + left_x2 = intersect (&traps[i].left, traps[i].bottom); + right_x1 = intersect (&traps[i].right, traps[i].top); + right_x2 = intersect (&traps[i].right, traps[i].bottom); + top = surface->parent->height - _cairo_fixed_to_double (traps[i].top); + bottom = surface->parent->height - _cairo_fixed_to_double (traps[i].bottom); + + _cairo_output_stream_printf + (stream, + "%f %f moveto %f %f lineto %f %f lineto %f %f lineto " + "closepath\n", + left_x1, top, + left_x2, bottom, + right_x2, bottom, + right_x1, top); + } + + _cairo_output_stream_printf (stream, + "fill\n"); - return _cairo_image_surface_set_clip_region (surface->image, region); + return CAIRO_STATUS_SUCCESS; +} + +typedef struct +{ + double height; + cairo_output_stream_t *output_stream; + cairo_bool_t has_current_point; +} ps_output_path_info_t; + +static cairo_status_t +_ps_output_path_move_to (void *closure, cairo_point_t *point) +{ + ps_output_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "%f %f moveto ", + _cairo_fixed_to_double (point->x), + info->height - _cairo_fixed_to_double (point->y)); + info->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_ps_output_path_line_to (void *closure, cairo_point_t *point) +{ + ps_output_path_info_t *info = closure; + const char *ps_operator; + + if (info->has_current_point) + ps_operator = "lineto"; + else + ps_operator = "moveto"; + + _cairo_output_stream_printf (info->output_stream, + "%f %f %s ", + _cairo_fixed_to_double (point->x), + info->height - _cairo_fixed_to_double (point->y), + ps_operator); + info->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_ps_output_path_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) +{ + ps_output_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "%f %f %f %f %f %f curveto ", + _cairo_fixed_to_double (b->x), + info->height - _cairo_fixed_to_double (b->y), + _cairo_fixed_to_double (c->x), + info->height - _cairo_fixed_to_double (c->y), + _cairo_fixed_to_double (d->x), + info->height - _cairo_fixed_to_double (d->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_ps_output_path_close_path (void *closure) +{ + ps_output_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "closepath\n"); + info->has_current_point = FALSE; + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_ps_surface_get_extents (void *abstract_surface, - cairo_rectangle_t *rectangle) +_ps_output_intersect_clip_path (void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) { - cairo_ps_surface_t *surface = abstract_surface; + ps_output_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->parent->stream; + cairo_status_t status; + ps_output_path_info_t info; + const char *ps_operator; + + _cairo_output_stream_printf (stream, + "%% _ps_output_intersect_clip_path\n"); + + if (path == NULL) { + _cairo_output_stream_printf (stream, "initclip\n"); + return CAIRO_STATUS_SUCCESS; + } - rectangle->x = 0; - rectangle->y = 0; + info.output_stream = stream; + info.has_current_point = FALSE; + info.height = surface->parent->height; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _ps_output_path_move_to, + _ps_output_path_line_to, + _ps_output_path_curve_to, + _ps_output_path_close_path, + &info); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + ps_operator = "clip"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + ps_operator = "eoclip"; + break; + default: + ASSERT_NOT_REACHED; + } + + _cairo_output_stream_printf (stream, + "%s newpath\n", + ps_operator); + + return status; +} - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the aribitray limitation of width to a short(!). We - * may need to come up with a better interface for get_size. - */ - rectangle->width = (surface->width_in_points + 0.5); - rectangle->height = (surface->height_in_points + 0.5); + +static cairo_int_status_t +_ps_output_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + ps_output_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->parent->stream; + cairo_font_subset_t *subset; + int i, subset_index; + + /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ + if (! _cairo_scaled_font_is_ft (scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern_is_translucent (pattern)) + return _ps_output_add_fallback_area (surface, dest_x, dest_y, width, height); + + _cairo_output_stream_printf (stream, + "%% _ps_output_show_glyphs\n"); + + emit_pattern (surface->parent, pattern); + + /* FIXME: Need to optimize this so we only do this sequence if the + * font isn't already set. */ + + subset = _cairo_ps_surface_get_font (surface->parent, scaled_font); + _cairo_output_stream_printf (stream, + "/f%d findfont\n" + "[ %f %f %f %f 0 0 ] makefont\n" + "setfont\n", + subset->font_id, + scaled_font->scale.xx, + scaled_font->scale.yx, + scaled_font->scale.xy, + scaled_font->scale.yy); + + /* FIXME: Need to optimize per glyph code. Should detect when + * glyphs share the same baseline and when the spacing corresponds + * to the glyph widths. */ + + for (i = 0; i < num_glyphs; i++) { + subset_index = _cairo_font_subset_use_glyph (subset, glyphs[i].index); + _cairo_output_stream_printf (stream, + "%f %f moveto (\\%o) show\n", + glyphs[i].x, + surface->parent->height - glyphs[i].y, + subset_index); + + } return CAIRO_STATUS_SUCCESS; } -static const cairo_surface_backend_t cairo_ps_surface_backend = { - _cairo_ps_surface_create_similar, - _cairo_ps_surface_finish, - _cairo_ps_surface_acquire_source_image, +static cairo_int_status_t +_ps_output_fill_path (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + ps_output_surface_t *surface = abstract_dst; + cairo_output_stream_t *stream = surface->parent->stream; + cairo_int_status_t status; + ps_output_path_info_t info; + const char *ps_operator; + + _cairo_output_stream_printf (stream, + "%% _ps_output_fill_path\n"); + + emit_pattern (surface->parent, pattern); + + info.output_stream = stream; + info.has_current_point = FALSE; + info.height = surface->parent->height; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _ps_output_path_move_to, + _ps_output_path_line_to, + _ps_output_path_curve_to, + _ps_output_path_close_path, + &info); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + ps_operator = "fill"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + ps_operator = "eofill"; + break; + default: + ASSERT_NOT_REACHED; + } + + _cairo_output_stream_printf (stream, + "%s\n", ps_operator); + + return status; +} + +static const cairo_surface_backend_t ps_output_backend = { + NULL, /* create_similar */ + _ps_output_finish, + NULL, /* acquire_source_image */ NULL, /* release_source_image */ - _cairo_ps_surface_acquire_dest_image, + NULL, /* acquire_dest_image */ NULL, /* release_dest_image */ NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - _cairo_ps_surface_copy_page, - _cairo_ps_surface_show_page, - _cairo_ps_surface_set_clip_region, - NULL, /* intersect_clip_path */ - _cairo_ps_surface_get_extents, - NULL /* show_glyphs */ + _ps_output_composite, + _ps_output_fill_rectangles, + _ps_output_composite_trapezoids, + NULL, /* copy_page */ + NULL, /* show_page */ + NULL, /* set_clip_region */ + _ps_output_intersect_clip_path, + NULL, /* get_extents */ + _ps_output_show_glyphs, + _ps_output_fill_path }; + +static cairo_int_status_t +_ps_output_render_fallbacks (cairo_surface_t *surface, + cairo_surface_t *page) +{ + ps_output_surface_t *ps_output; + cairo_surface_t *image; + cairo_int_status_t status; + cairo_matrix_t matrix; + int width, height; + + ps_output = (ps_output_surface_t *) surface; + if (ps_output->fallback_areas == NULL) + return CAIRO_STATUS_SUCCESS; + + width = ps_output->parent->width * ps_output->parent->x_dpi / 72; + height = ps_output->parent->height * ps_output->parent->y_dpi / 72; + + image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + if (image == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_surface_fill_rectangle (image, + CAIRO_OPERATOR_SOURCE, + CAIRO_COLOR_WHITE, + 0, 0, width, height); + if (status) + goto bail; + + status = _cairo_meta_surface_replay (page, image); + if (status) + goto bail; + + matrix.xx = 1; + matrix.xy = 0; + matrix.yx = 0; + matrix.yy = 1; + matrix.x0 = 0; + matrix.y0 = 0; + + status = emit_image (ps_output->parent, + (cairo_image_surface_t *) image, &matrix); + + bail: + cairo_surface_destroy (image); + + return status; +} + +static cairo_surface_t * +_ps_output_surface_create (cairo_ps_surface_t *parent) +{ + ps_output_surface_t *ps_output; + + ps_output = malloc (sizeof (ps_output_surface_t)); + if (ps_output == NULL) + return NULL; + + _cairo_surface_init (&ps_output->base, &ps_output_backend); + ps_output->parent = parent; + ps_output->fallback_areas = NULL; + + return &ps_output->base; +} + +static cairo_int_status_t +_cairo_ps_surface_render_page (cairo_ps_surface_t *surface, + cairo_surface_t *page, int page_number) +{ + cairo_surface_t *ps_output; + cairo_int_status_t status; + + _cairo_output_stream_printf (surface->stream, + "%%%%Page: %d\n" + "gsave\n", + page_number); + + ps_output = _ps_output_surface_create (surface); + if (ps_output == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_meta_surface_replay (page, ps_output); + + _ps_output_render_fallbacks (ps_output, page); + + cairo_surface_destroy (ps_output); + + _cairo_output_stream_printf (surface->stream, + "showpage\n" + "grestore\n" + "%%%%EndPage\n"); + + return status; +} diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 566842515..17d51303b 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -58,12 +58,11 @@ ImageDataReleaseFunc(void *info, const void *data, size_t size) } } -static cairo_surface_t *_cairo_quartz_surface_create_similar(void - *abstract_src, - cairo_format_t - format, - int width, - int height) +static cairo_surface_t * +_cairo_quartz_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) { return NULL; } @@ -135,12 +134,6 @@ _cairo_quartz_surface_acquire_source_image(void *abstract_surface, surface->width, surface->height, rowBytes); - - // Set the image surface Cairo state to match our own. - _cairo_image_surface_set_repeat(surface->image, surface->base.repeat); - _cairo_image_surface_set_matrix(surface->image, - &(surface->base.matrix)); - *image_out = surface->image; *image_extra = NULL; @@ -194,8 +187,11 @@ _cairo_quartz_surface_set_clip_region(void *abstract_surface, pixman_region16_t * region) { cairo_quartz_surface_t *surface = abstract_surface; + unsigned int serial; - return _cairo_surface_set_clip_region(&surface->image->base, region); + serial = _cairo_surface_allocate_clip_serial (&surface->image->base); + return _cairo_surface_set_clip_region(&surface->image->base, + region, serial); } static cairo_int_status_t diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 318ee87d9..75f887c1f 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -59,42 +59,62 @@ _cairo_surface_init (cairo_surface_t *surface, } cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_format_t format, +_cairo_surface_create_similar_scratch (cairo_surface_t *other, + cairo_content_t content, int width, int height) { if (other == NULL) return NULL; - return other->backend->create_similar (other, format, width, height); + return other->backend->create_similar (other, content, width, height); } +/** + * cairo_surface_create_similar: + * @other: an existing surface used to select the backend of the new surface + * @content: the content for the new surface + * @width: width of the new surface, (in device-space units) + * @height: height of the new surface (in device-space units) + * + * Create a new surface that is as compatible as possible with an + * existing surface. The new surface will use the same backend as + * @other unless that is not possible for some reason. + * + * Return value: a pointer to the newly allocated surface, or NULL in + * the case of errors. The caller owns the surface and should call + * cairo_surface_destroy when done with it. + **/ cairo_surface_t * -cairo_surface_create_similar (cairo_surface_t *other, - cairo_format_t format, +cairo_surface_create_similar (cairo_surface_t *other, + cairo_content_t content, int width, int height) { if (other == NULL) return NULL; - return _cairo_surface_create_similar_solid (other, format, + /* XXX: Really need to make this kind of thing pass through _cairo_error. */ + if (! CAIRO_CONTENT_VALID (content)) + return NULL; + + return _cairo_surface_create_similar_solid (other, content, width, height, CAIRO_COLOR_TRANSPARENT); } cairo_surface_t * _cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_format_t format, + cairo_content_t content, int width, int height, const cairo_color_t *color) { cairo_status_t status; cairo_surface_t *surface; + cairo_format_t format = _cairo_format_from_content (content); - surface = _cairo_surface_create_similar_scratch (other, format, + surface = _cairo_surface_create_similar_scratch (other, content, width, height); if (surface == NULL) @@ -280,7 +300,7 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified * surface. Or %CAIRO_STATUS_NO_MEMORY. **/ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, void **image_extra) @@ -297,7 +317,7 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, * * Releases any resources obtained with _cairo_surface_acquire_source_image() **/ -cairo_private void +void _cairo_surface_release_source_image (cairo_surface_t *surface, cairo_image_surface_t *image, void *image_extra) @@ -650,7 +670,7 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); } -cairo_private cairo_int_status_t +cairo_int_status_t _cairo_surface_fill_path (cairo_operator_t operator, cairo_pattern_t *pattern, cairo_surface_t *dst, @@ -804,7 +824,7 @@ _cairo_surface_show_page (cairo_surface_t *surface) * verify that the correct clip is set in the surface before * invoking any surface drawing function */ -cairo_private unsigned int +unsigned int _cairo_surface_get_current_clip_serial (cairo_surface_t *surface) { return surface->current_clip_serial; @@ -819,7 +839,7 @@ _cairo_surface_get_current_clip_serial (cairo_surface_t *surface) * As zero is reserved for the special no-clipping case, * this function will not return that. */ -cairo_private unsigned int +unsigned int _cairo_surface_allocate_clip_serial (cairo_surface_t *surface) { unsigned int serial; @@ -838,7 +858,7 @@ _cairo_surface_allocate_clip_serial (cairo_surface_t *surface) * unclipped. It also sets the clip serial number * to zero. */ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_reset_clip (cairo_surface_t *surface) { cairo_status_t status; @@ -875,7 +895,7 @@ _cairo_surface_reset_clip (cairo_surface_t *surface) * the specified region and sets the surface clipping * serial number to the associated serial number. */ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region, unsigned int serial) @@ -889,6 +909,23 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface, return surface->backend->set_clip_region (surface, region); } +cairo_int_status_t +_cairo_surface_intersect_clip_path (cairo_surface_t *surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + assert (surface->backend->intersect_clip_path != NULL); + + return surface->backend->intersect_clip_path (surface, + path, + fill_rule, + tolerance); +} + static cairo_status_t _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, cairo_clip_path_t *clip_path) @@ -920,7 +957,7 @@ _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, * Sets the clipping path to be the intersection of the current * clipping path of the surface and the given path. **/ -cairo_private cairo_status_t +cairo_status_t _cairo_surface_set_clip_path (cairo_surface_t *surface, cairo_clip_path_t *clip_path, unsigned int serial) diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c index eef2b4e72..385297ee2 100644 --- a/src/cairo-win32-font.c +++ b/src/cairo-win32-font.c @@ -1101,13 +1101,136 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, } } +static cairo_fixed_t +_cairo_fixed_from_FIXED (FIXED f) +{ + return *((cairo_fixed_t *)&f); +} + static cairo_status_t -_cairo_win32_scaled_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path) +_cairo_win32_scaled_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) { - return CAIRO_STATUS_SUCCESS; + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } }; + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_status_t status; + GLYPHMETRICS metrics; + HDC hdc; + int i; + + hdc = _get_global_font_dc (); + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + for (i = 0; i < num_glyphs; i++) + { + DWORD bytesGlyph; + unsigned char *buffer, *ptr; + + cairo_fixed_t x = _cairo_fixed_from_double (glyphs[i].x); + cairo_fixed_t y = _cairo_fixed_from_double (glyphs[i].y); + + bytesGlyph = GetGlyphOutlineW (hdc, glyphs[i].index, + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (bytesGlyph == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto FAIL; + } + + ptr = buffer = malloc (bytesGlyph); + + if (!buffer) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + if (GetGlyphOutlineW (hdc, glyphs[i].index, + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + free (buffer); + goto FAIL; + } + + while (ptr < buffer + bytesGlyph) { + TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr; + unsigned char *endPoly = ptr + header->cb; + + ptr += sizeof (TTPOLYGONHEADER); + + _cairo_path_fixed_move_to (path, + _cairo_fixed_from_FIXED (header->pfxStart.x) + x, + _cairo_fixed_from_FIXED (header->pfxStart.y) + y); + + while (ptr < endPoly) { + TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr; + POINTFX *points = curve->apfx; + int i; + switch (curve->wType) { + case TT_PRIM_LINE: + for (i = 0; i < curve->cpfx; i++) { + _cairo_path_fixed_line_to (path, + _cairo_fixed_from_FIXED (points[i].x) + x, + _cairo_fixed_from_FIXED (points[i].y) + y); + } + break; + case TT_PRIM_QSPLINE: + for (i = 0; i < curve->cpfx - 1; i++) { + cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y; + _cairo_path_fixed_get_current_point (path, &p1x, &p1y); + cx = _cairo_fixed_from_FIXED (points[i].x) + x; + cy = _cairo_fixed_from_FIXED (points[i].y) + y; + + if (i + 1 == curve->cpfx - 1) { + p2x = _cairo_fixed_from_FIXED (points[i + 1].x) + x; + p2y = _cairo_fixed_from_FIXED (points[i + 1].y) + y; + } else { + /* records with more than one curve use interpolation for + control points, per http://support.microsoft.com/kb/q87115/ */ + p2x = (cx + _cairo_fixed_from_FIXED (points[i + 1].x) + x) / 2; + p2y = (cy + _cairo_fixed_from_FIXED (points[i + 1].y) + y) / 2; + } + + c1x = 2 * cx / 3 + p1x / 3; + c1y = 2 * cy / 3 + p1y / 3; + c2x = 2 * cx / 3 + p2x / 3; + c2y = 2 * cy / 3 + p2y / 3; + + _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y); + } + break; + case TT_PRIM_CSPLINE: + for (i = 0; i < curve->cpfx - 2; i += 2) { + _cairo_path_fixed_curve_to (path, + _cairo_fixed_from_FIXED (points[i].x) + x, + _cairo_fixed_from_FIXED (points[i].y) + y, + _cairo_fixed_from_FIXED (points[i + 1].x) + x, + _cairo_fixed_from_FIXED (points[i + 1].y) + y, + _cairo_fixed_from_FIXED (points[i + 2].x) + x, + _cairo_fixed_from_FIXED (points[i + 2].y) + y); + } + break; + } + ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1); + } + _cairo_path_fixed_close_path (path); + } + free(buffer); + } + +FAIL: + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return status; } const cairo_scaled_font_backend_t cairo_win32_scaled_font_backend = { diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c index 8d63adbf3..148d22bcd 100644 --- a/src/cairo-win32-surface.c +++ b/src/cairo-win32-surface.c @@ -240,7 +240,6 @@ _create_dc_and_bitmap (cairo_win32_surface_t *surface, static cairo_surface_t * _cairo_win32_surface_create_for_dc (HDC original_dc, cairo_format_t format, - int drawable, int width, int height) { @@ -291,14 +290,14 @@ _cairo_win32_surface_create_for_dc (HDC original_dc, static cairo_surface_t * _cairo_win32_surface_create_similar (void *abstract_src, - cairo_format_t format, + cairo_content_t content, int width, int height) { cairo_win32_surface_t *src = abstract_src; + cairo_format_t format = _cairo_format_from_content (content); - return _cairo_win32_surface_create_for_dc (src->dc, format, drawable, - width, height); + return _cairo_win32_surface_create_for_dc (src->dc, format, width, height); } /** @@ -319,8 +318,7 @@ _cairo_win32_surface_create_dib (cairo_format_t format, int width, int height) { - return _cairo_win32_surface_create_for_dc (NULL, format, TRUE, - width, height); + return _cairo_win32_surface_create_for_dc (NULL, format, width, height); } static cairo_status_t @@ -354,11 +352,13 @@ _cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, { cairo_win32_surface_t *local; cairo_status_t status; + cairo_content_t content = _cairo_content_from_format (surface->format); local = (cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface, - surface->format, - width, height); + content, + width, + height); if (!local) return CAIRO_STATUS_NO_MEMORY; @@ -577,7 +577,6 @@ _cairo_win32_surface_composite (cairo_operator_t operator, } else if (integer_transform && (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) && dst->format == CAIRO_FORMAT_RGB24 && - !src->base.repeat && operator == CAIRO_OPERATOR_OVER) { BLENDFUNCTION blend_function; @@ -675,6 +674,9 @@ categorize_solid_dest_operator (cairo_operator_t operator, return DO_UNSUPPORTED; break; } + + ASSERT_NOT_REACHED; + return DO_UNSUPPORTED; } static cairo_int_status_t @@ -704,7 +706,7 @@ _cairo_win32_surface_fill_rectangles (void *abstract_surface, new_color = RGB (0, 0, 0); break; case DO_SOURCE: - new_color = RGB (color->red_short >> 8, color->blue_short >> 8, color->green_short >> 8); + new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); break; case DO_NOTHING: return CAIRO_STATUS_SUCCESS; @@ -751,8 +753,12 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, /* If we are in-memory, then we set the clip on the image surface * as well as on the underlying GDI surface. */ - if (surface->image) - _cairo_surface_set_clip_region (surface->image, region); + if (surface->image) { + unsigned int serial; + + serial = _cairo_surface_allocate_clip_serial (surface->image); + _cairo_surface_set_clip_region (surface->image, region, serial); + } /* The semantics we want is that any clip set by cairo combines * is intersected with the clip on device context that the diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index 4e3204452..1b0e2aaee 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -228,7 +228,7 @@ _CAIRO_FORMAT_DEPTH (cairo_format_t format) static cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_src, - cairo_format_t format, + cairo_content_t content, int width, int height) { @@ -236,6 +236,7 @@ _cairo_xcb_surface_create_similar (void *abstract_src, XCBConnection *dpy = src->dpy; XCBDRAWABLE d; cairo_xcb_surface_t *surface; + cairo_format_t format = _cairo_format_from_content (content); XCBRenderPICTFORMINFO xrender_format = _format_from_cairo (dpy, format); /* As a good first approximation, if the display doesn't have COMPOSITE, @@ -635,9 +636,10 @@ _cairo_xcb_surface_clone_similar (void *abstract_surface, } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + cairo_content_t content = _cairo_content_from_format (image_src->format); clone = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_similar (surface, image_src->format, + _cairo_xcb_surface_create_similar (surface, content, image_src->width, image_src->height); if (clone == NULL) return CAIRO_STATUS_NO_MEMORY; diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index f06d409e2..caec2b9dd 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -179,8 +179,8 @@ _CAIRO_FORMAT_XRENDER_FORMAT(Display *dpy, cairo_format_t format) } static cairo_surface_t * -_cairo_xlib_surface_create_similar (void *abstract_src, - cairo_format_t format, +_cairo_xlib_surface_create_similar (void *abstract_src, + cairo_content_t content, int width, int height) { @@ -189,6 +189,7 @@ _cairo_xlib_surface_create_similar (void *abstract_src, int scr; Pixmap pix; cairo_xlib_surface_t *surface; + cairo_format_t format = _cairo_format_from_content (content); int depth = _CAIRO_FORMAT_DEPTH (format); XRenderPictFormat *xrender_format = _CAIRO_FORMAT_XRENDER_FORMAT (dpy, format); @@ -449,7 +450,6 @@ _get_image_surface (cairo_xlib_surface_t *surface, } /* Let the surface take ownership of the data */ - /* XXX: Can probably come up with a cleaner API here. */ _cairo_image_surface_assume_ownership_of_data (image); ximage->data = NULL; XDestroyImage (ximage); @@ -639,9 +639,10 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + cairo_content_t content = _cairo_content_from_format (image_src->format); clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (surface, image_src->format, + _cairo_xlib_surface_create_similar (surface, content, image_src->width, image_src->height); if (clone == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -828,17 +829,39 @@ _surface_has_alpha (cairo_xlib_surface_t *surface) } } +/* Returns true if the given operator and source-alpha combination + * requires alpha compositing to complete. + */ +static cairo_bool_t +_operator_needs_alpha_composite (cairo_operator_t operator, + cairo_bool_t surface_has_alpha) +{ + if (operator == CAIRO_OPERATOR_SOURCE || + (!surface_has_alpha && + (operator == CAIRO_OPERATOR_OVER || + operator == CAIRO_OPERATOR_ATOP || + operator == CAIRO_OPERATOR_IN))) + return FALSE; + + return TRUE; +} + /* There is a bug in most older X servers with compositing using a repeating * source pattern when the source is in off-screen video memory. When that * bug could be triggered, we need a fallback: in the common case where we have no * transformation and the source and destination have the same format/visual, * we can do the operation using the core protocol, otherwise, we need * a software fallback. + * + * We can also often optimize a compositing operation by calling XCopyArea + * for some common cases where there is no alpha compositing to be done. + * We figure that out here as well. */ typedef enum { - DO_RENDER, /* use render */ - DO_XLIB, /* core protocol fallback */ - DO_UNSUPPORTED /* software fallback */ + DO_RENDER, /* use render */ + DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ + DO_XTILE, /* core protocol XSetTile optimization/fallback */ + DO_UNSUPPORTED /* software fallback */ } composite_operation_t; /* Initial check for the bug; we need to recheck after we turn @@ -849,10 +872,10 @@ typedef enum { * hit the bug and won't be able to use a core protocol fallback. */ static composite_operation_t -_categorize_composite_repeat (cairo_xlib_surface_t *dst, - cairo_operator_t operator, - cairo_pattern_t *src_pattern, - cairo_bool_t have_mask) +_categorize_composite_operation (cairo_xlib_surface_t *dst, + cairo_operator_t operator, + cairo_pattern_t *src_pattern, + cairo_bool_t have_mask) { if (!dst->buggy_repeat) @@ -891,27 +914,43 @@ _categorize_composite_repeat (cairo_xlib_surface_t *dst, * If we end up returning DO_UNSUPPORTED here, we're throwing away work we * did to turn gradients into a pattern, but most of the time we can handle * that case with core protocol fallback. + * + * Also check here if we can just use XCopyArea, instead of going through + * Render. */ static composite_operation_t -_recategorize_composite_repeat (cairo_xlib_surface_t *dst, - cairo_operator_t operator, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *src_attr, - cairo_bool_t have_mask) +_recategorize_composite_operation (cairo_xlib_surface_t *dst, + cairo_operator_t operator, + cairo_xlib_surface_t *src, + cairo_surface_attributes_t *src_attr, + cairo_bool_t have_mask) { + cairo_bool_t is_integer_translation = + _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL); + cairo_bool_t needs_alpha_composite = + _operator_needs_alpha_composite (operator, _surface_has_alpha (src)); + + if (!have_mask && + is_integer_translation && + src_attr->extend == CAIRO_EXTEND_NONE && + !needs_alpha_composite && + _surfaces_compatible(src, dst)) + { + return DO_XCOPYAREA; + } + if (!dst->buggy_repeat) return DO_RENDER; - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && + if (is_integer_translation && src_attr->extend == CAIRO_EXTEND_REPEAT && (src->width != 1 || src->height != 1)) { if (!have_mask && - (operator == CAIRO_OPERATOR_SOURCE || - (operator == CAIRO_OPERATOR_OVER && !_surface_has_alpha (src)))) + !needs_alpha_composite && + _surfaces_compatible (dst, src)) { - if (_surfaces_compatible (dst, src)) - return DO_XLIB; + return DO_XTILE; } return DO_UNSUPPORTED; @@ -980,12 +1019,13 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, cairo_xlib_surface_t *mask; cairo_int_status_t status; composite_operation_t operation; + int itx, ity; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - operation = _categorize_composite_repeat (dst, operator, src_pattern, - mask_pattern != NULL); + operation = _categorize_composite_operation (dst, operator, src_pattern, + mask_pattern != NULL); if (operation == DO_UNSUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1000,8 +1040,8 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, if (status) return status; - operation = _recategorize_composite_repeat (dst, operator, src, &src_attr, - mask_pattern != NULL); + operation = _recategorize_composite_operation (dst, operator, src, &src_attr, + mask_pattern != NULL); if (operation == DO_UNSUPPORTED) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; @@ -1011,8 +1051,9 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, if (status) goto FAIL; - if (operation == DO_RENDER) + switch (operation) { + case DO_RENDER: _cairo_xlib_surface_ensure_dst_picture (dst); if (mask) { status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); @@ -1040,17 +1081,28 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, dst_x, dst_y, width, height); } - } - else /* DO_XLIB */ - { + break; + + case DO_XCOPYAREA: + _cairo_xlib_surface_ensure_gc (dst); + XCopyArea (dst->dpy, + src->drawable, + dst->drawable, + dst->gc, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + width, height, + dst_x, dst_y); + break; + + case DO_XTILE: /* This case is only used for bug fallbacks, though it is theoretically * applicable to the case where we don't have the RENDER extension as * well. * * We've checked that we have a repeating unscaled source in - * _recategorize_composite_repeat. + * _recategorize_composite_operation. */ - int itx, ity; _cairo_xlib_surface_ensure_gc (dst); _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); @@ -1062,14 +1114,18 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, XFillRectangle (dst->dpy, dst->drawable, dst->gc, dst_x, dst_y, width, height); + break; + + default: + ASSERT_NOT_REACHED; } FAIL: if (mask) - _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); - _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); + _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); return status; } @@ -1126,7 +1182,7 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - operation = _categorize_composite_repeat (dst, operator, pattern, TRUE); + operation = _categorize_composite_operation (dst, operator, pattern, TRUE); if (operation == DO_UNSUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1137,7 +1193,7 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, if (status) return status; - operation = _recategorize_composite_repeat (dst, operator, src, &attributes, TRUE); + operation = _recategorize_composite_operation (dst, operator, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; @@ -1167,7 +1223,7 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, (XTrapezoid *) traps, num_traps); FAIL: - _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); + _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; } @@ -1355,6 +1411,9 @@ _cairo_xlib_surface_create_internal (Display *dpy, if (strcmp (ServerVendor (dpy), "The X.Org Foundation") == 0) { if (VendorRelease (dpy) <= 60802000) surface->buggy_repeat = TRUE; + } else if (strcmp (ServerVendor (dpy), "The XFree86 Project, Inc") == 0) { + if (VendorRelease (dpy) <= 40400000) + surface->buggy_repeat = TRUE; } surface->dst_picture = None; @@ -1510,8 +1569,6 @@ typedef struct glyphset_cache { typedef struct { cairo_glyph_cache_key_t key; Glyph glyph; - XGlyphInfo info; - int refcount; } glyphset_cache_entry_t; static Glyph @@ -1520,26 +1577,26 @@ _next_xlib_glyph (glyphset_cache_t *cache) return ++(cache->counter); } - static cairo_status_t -_xlib_glyphset_cache_create_entry (void *cache, - void *key, +_xlib_glyphset_cache_create_entry (void *abstract_cache, + void *abstract_key, void **return_entry) { - glyphset_cache_t *g = (glyphset_cache_t *) cache; - cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *)key; - glyphset_cache_entry_t *v; + glyphset_cache_t *cache = abstract_cache; + cairo_glyph_cache_key_t *key = abstract_key; + glyphset_cache_entry_t *entry; + XGlyphInfo glyph_info; cairo_status_t status; cairo_cache_t *im_cache; cairo_image_glyph_cache_entry_t *im; - v = malloc (sizeof (glyphset_cache_entry_t)); + entry = malloc (sizeof (glyphset_cache_entry_t)); _cairo_lock_global_image_glyph_cache (); im_cache = _cairo_get_global_image_glyph_cache (); - if (g == NULL || v == NULL || im_cache == NULL) { + if (cache == NULL || entry == NULL || im_cache == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } @@ -1550,14 +1607,13 @@ _xlib_glyphset_cache_create_entry (void *cache, return CAIRO_STATUS_NO_MEMORY; } - v->refcount = 1; - v->key = *k; - _cairo_unscaled_font_reference (v->key.unscaled); + entry->key = *key; + _cairo_unscaled_font_reference (entry->key.unscaled); - v->glyph = _next_xlib_glyph (g); + entry->glyph = _next_xlib_glyph (cache); - v->info.width = im->image ? im->image->stride : im->size.width; - v->info.height = im->size.height; + glyph_info.width = im->image ? im->image->stride : im->size.width; + glyph_info.height = im->size.height; /* * Most of the font rendering system thinks of glyph tiles as having @@ -1595,28 +1651,22 @@ _xlib_glyphset_cache_create_entry (void *cache, * sitting around for x and y. */ - v->info.x = -im->size.x; - v->info.y = -im->size.y; - v->info.xOff = 0; - v->info.yOff = 0; + glyph_info.x = -im->size.x; + glyph_info.y = -im->size.y; + glyph_info.xOff = 0; + glyph_info.yOff = 0; - XRenderAddGlyphs (g->display, g->glyphset, - &(v->glyph), &(v->info), 1, + XRenderAddGlyphs (cache->display, cache->glyphset, + &(entry->glyph), &(glyph_info), 1, im->image ? (char *) im->image->data : NULL, - im->image ? v->info.height * v->info.width : 0); + im->image ? glyph_info.height * glyph_info.width : 0); - v->key.base.memory = im->image ? im->image->width * im->image->stride : 0; - *return_entry = v; + entry->key.base.memory = im->image ? im->image->width * im->image->stride : 0; + *return_entry = entry; _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_SUCCESS; } -static void -_glyphset_cache_entry_reference (glyphset_cache_entry_t *e) -{ - e->refcount++; -} - static void _xlib_glyphset_cache_destroy_cache (void *cache) { @@ -1624,20 +1674,15 @@ _xlib_glyphset_cache_destroy_cache (void *cache) } static void -_xlib_glyphset_cache_destroy_entry (void *cache, void *entry) +_xlib_glyphset_cache_destroy_entry (void *abstract_cache, + void *abstract_entry) { - glyphset_cache_t *g; - glyphset_cache_entry_t *v; + glyphset_cache_t *cache = abstract_cache; + glyphset_cache_entry_t *entry = abstract_entry; - g = (glyphset_cache_t *) cache; - v = (glyphset_cache_entry_t *) entry; - - if (--v->refcount > 0) - return; - - _cairo_unscaled_font_destroy (v->key.unscaled); - XRenderFreeGlyphs (g->display, g->glyphset, &(v->glyph), 1); - free (v); + _cairo_unscaled_font_destroy (entry->key.unscaled); + XRenderFreeGlyphs (cache->display, cache->glyphset, &(entry->glyph), 1); + free (entry); } static const cairo_cache_backend_t _xlib_glyphset_cache_backend = { @@ -1648,6 +1693,7 @@ static const cairo_cache_backend_t _xlib_glyphset_cache_backend = { _xlib_glyphset_cache_destroy_cache }; +CAIRO_MUTEX_DECLARE(_xlib_glyphset_caches_mutex); static glyphset_cache_t * _xlib_glyphset_caches = NULL; @@ -1655,13 +1701,15 @@ _xlib_glyphset_caches = NULL; static void _lock_xlib_glyphset_caches (void) { - /* FIXME: implement locking */ + CAIRO_MUTEX_LOCK(_xlib_glyphset_caches_mutex); } static void -_unlock_xlib_glyphset_caches (void) +_unlock_xlib_glyphset_caches (glyphset_cache_t *cache) { - /* FIXME: implement locking */ + _cairo_cache_shrink_to (&cache->base, + CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT); + CAIRO_MUTEX_UNLOCK(_xlib_glyphset_caches_mutex); } static glyphset_cache_t * @@ -1672,35 +1720,34 @@ _get_glyphset_cache (Display *d) * displays. So we just do a linear scan. */ - glyphset_cache_t *g; + glyphset_cache_t *cache; - for (g = _xlib_glyphset_caches; g != NULL; g = g->next) { - if (g->display == d) - return g; + for (cache = _xlib_glyphset_caches; cache != NULL; cache = cache->next) { + if (cache->display == d) + return cache; } - g = malloc (sizeof (glyphset_cache_t)); - if (g == NULL) + cache = malloc (sizeof (glyphset_cache_t)); + if (cache == NULL) goto ERR; - g->counter = 0; - g->display = d; - g->a8_pict_format = XRenderFindStandardFormat (d, PictStandardA8); - if (g->a8_pict_format == NULL) - goto ERR; - - if (_cairo_cache_init (&g->base, - &_xlib_glyphset_cache_backend, - CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT)) + if (_cairo_cache_init (&cache->base, + &_xlib_glyphset_cache_backend, 0)) goto FREE_GLYPHSET_CACHE; + + cache->display = d; + cache->counter = 0; + + cache->a8_pict_format = XRenderFindStandardFormat (d, PictStandardA8); + cache->glyphset = XRenderCreateGlyphSet (d, cache->a8_pict_format); - g->glyphset = XRenderCreateGlyphSet (d, g->a8_pict_format); - g->next = _xlib_glyphset_caches; - _xlib_glyphset_caches = g; - return g; + cache->next = _xlib_glyphset_caches; + _xlib_glyphset_caches = cache; + + return cache; FREE_GLYPHSET_CACHE: - free (g); + free (cache); ERR: return NULL; @@ -1711,7 +1758,7 @@ _get_glyphset_cache (Display *d) static cairo_status_t _cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, - glyphset_cache_t *g, + glyphset_cache_t *cache, cairo_glyph_cache_key_t *key, cairo_xlib_surface_t *src, cairo_xlib_surface_t *self, @@ -1751,7 +1798,7 @@ _cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, chars[i] = entries[i]->glyph; elts[i].chars = &(chars[i]); elts[i].nchars = 1; - elts[i].glyphset = g->glyphset; + elts[i].glyphset = cache->glyphset; thisX = (int) floor (glyphs[i].x + 0.5); thisY = (int) floor (glyphs[i].y + 0.5); elts[i].xOff = thisX - lastX; @@ -1764,7 +1811,7 @@ _cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, _render_operator (operator), src->src_picture, self->dst_picture, - g->a8_pict_format, + cache->a8_pict_format, source_x, source_y, 0, 0, elts, num_glyphs); @@ -1788,7 +1835,7 @@ _cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, static cairo_status_t _cairo_xlib_surface_show_glyphs16 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, - glyphset_cache_t *g, + glyphset_cache_t *cache, cairo_glyph_cache_key_t *key, cairo_xlib_surface_t *src, cairo_xlib_surface_t *self, @@ -1828,7 +1875,7 @@ _cairo_xlib_surface_show_glyphs16 (cairo_scaled_font_t *scaled_font, chars[i] = entries[i]->glyph; elts[i].chars = &(chars[i]); elts[i].nchars = 1; - elts[i].glyphset = g->glyphset; + elts[i].glyphset = cache->glyphset; thisX = (int) floor (glyphs[i].x + 0.5); thisY = (int) floor (glyphs[i].y + 0.5); elts[i].xOff = thisX - lastX; @@ -1841,7 +1888,7 @@ _cairo_xlib_surface_show_glyphs16 (cairo_scaled_font_t *scaled_font, _render_operator (operator), src->src_picture, self->dst_picture, - g->a8_pict_format, + cache->a8_pict_format, source_x, source_y, 0, 0, elts, num_glyphs); @@ -1864,7 +1911,7 @@ _cairo_xlib_surface_show_glyphs16 (cairo_scaled_font_t *scaled_font, static cairo_status_t _cairo_xlib_surface_show_glyphs8 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, - glyphset_cache_t *g, + glyphset_cache_t *cache, cairo_glyph_cache_key_t *key, cairo_xlib_surface_t *src, cairo_xlib_surface_t *self, @@ -1904,7 +1951,7 @@ _cairo_xlib_surface_show_glyphs8 (cairo_scaled_font_t *scaled_font, chars[i] = entries[i]->glyph; elts[i].chars = &(chars[i]); elts[i].nchars = 1; - elts[i].glyphset = g->glyphset; + elts[i].glyphset = cache->glyphset; thisX = (int) floor (glyphs[i].x + 0.5); thisY = (int) floor (glyphs[i].y + 0.5); elts[i].xOff = thisX - lastX; @@ -1917,7 +1964,7 @@ _cairo_xlib_surface_show_glyphs8 (cairo_scaled_font_t *scaled_font, _render_operator (operator), src->src_picture, self->dst_picture, - g->a8_pict_format, + cache->a8_pict_format, source_x, source_y, 0, 0, elts, num_glyphs); @@ -1957,7 +2004,7 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, unsigned int elt_size; cairo_xlib_surface_t *self = abstract_surface; cairo_xlib_surface_t *src; - glyphset_cache_t *g; + glyphset_cache_t *cache; cairo_glyph_cache_key_t key; glyphset_cache_entry_t **entries; glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; @@ -1967,7 +2014,7 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (self) || !self->format) return CAIRO_INT_STATUS_UNSUPPORTED; - operation = _categorize_composite_repeat (self, operator, pattern, TRUE); + operation = _categorize_composite_operation (self, operator, pattern, TRUE); if (operation == DO_UNSUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1978,7 +2025,7 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, if (status) return status; - operation = _recategorize_composite_repeat (self, operator, src, &attributes, TRUE); + operation = _recategorize_composite_operation (self, operator, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; @@ -1999,8 +2046,8 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, } _lock_xlib_glyphset_caches (); - g = _get_glyphset_cache (self->dpy); - if (g == NULL) + cache = _get_glyphset_cache (self->dpy); + if (cache == NULL) goto UNLOCK; /* Work out the index size to use. */ @@ -2009,19 +2056,10 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, for (i = 0; i < num_glyphs; ++i) { key.index = glyphs[i].index; - status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i]), NULL); + status = _cairo_cache_lookup (&cache->base, &key, (void **) (&entries[i]), NULL); if (status != CAIRO_STATUS_SUCCESS || entries[i] == NULL) goto UNLOCK; - /* Referencing the glyph entries we use prevents them from - * being freed if lookup of later entries causes them to - * be ejected from the cache. It would be more efficient - * (though more complex) to prevent them from being ejected - * from the cache at all, so they could get reused later - * in the same string. - */ - _glyphset_cache_entry_reference (entries[i]); - if (elt_size == 8 && entries[i]->glyph > 0xff) elt_size = 16; if (elt_size == 16 && entries[i]->glyph > 0xffff) { @@ -2035,37 +2073,34 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, _cairo_xlib_surface_ensure_dst_picture (self); if (elt_size == 8) { - status = _cairo_xlib_surface_show_glyphs8 (scaled_font, operator, g, &key, src, self, + status = _cairo_xlib_surface_show_glyphs8 (scaled_font, operator, cache, &key, src, self, source_x + attributes.x_offset, source_y + attributes.y_offset, glyphs, entries, num_glyphs); } else if (elt_size == 16) { - status = _cairo_xlib_surface_show_glyphs16 (scaled_font, operator, g, &key, src, self, + status = _cairo_xlib_surface_show_glyphs16 (scaled_font, operator, cache, &key, src, self, source_x + attributes.x_offset, source_y + attributes.y_offset, glyphs, entries, num_glyphs); } else { - status = _cairo_xlib_surface_show_glyphs32 (scaled_font, operator, g, &key, src, self, + status = _cairo_xlib_surface_show_glyphs32 (scaled_font, operator, cache, &key, src, self, source_x + attributes.x_offset, source_y + attributes.y_offset, glyphs, entries, num_glyphs); } - for (i = 0; i < num_glyphs; ++i) - _xlib_glyphset_cache_destroy_entry (g, entries[i]); - UNLOCK: - _unlock_xlib_glyphset_caches (); + _unlock_xlib_glyphset_caches (cache); if (num_glyphs >= N_STACK_BUF) free (entries); FAIL: - _cairo_pattern_release_surface (&self->base, &src->base, &attributes); + _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; } diff --git a/src/cairo.c b/src/cairo.c index 73dfc2cdd..56da06eaa 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -62,7 +62,7 @@ static const cairo_t cairo_nil = { * a bit of a pain, but it should be easy to always catch as long as * one adds a new test case to test a trigger of the new status value. */ -#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_SURFACE_TYPE_MISMATCH +#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_PATTERN_TYPE_MISMATCH /** * _cairo_error: @@ -320,18 +320,6 @@ cairo_set_operator (cairo_t *cr, cairo_operator_t op) _cairo_error (cr, cr->status); } -static void -_cairo_set_source_solid (cairo_t *cr, const cairo_color_t *color) -{ - cairo_pattern_t *source; - - source = _cairo_pattern_create_solid (color); - - cairo_set_source (cr, source); - - cairo_pattern_destroy (source); -} - /** * cairo_set_source_rgb * @cr: a cairo context @@ -350,20 +338,16 @@ _cairo_set_source_solid (cairo_t *cr, const cairo_color_t *color) void cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) { - cairo_color_t color; + cairo_pattern_t *pattern; if (cr->status) { _cairo_error (cr, cr->status); return; } - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); - - _cairo_color_init_rgb (&color, red, green, blue); - - _cairo_set_source_solid (cr, &color); + pattern = cairo_pattern_create_rgb (red, green, blue); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); } /** @@ -387,23 +371,41 @@ cairo_set_source_rgba (cairo_t *cr, double red, double green, double blue, double alpha) { - cairo_color_t color; + cairo_pattern_t *pattern; if (cr->status) { _cairo_error (cr, cr->status); return; } - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); - _cairo_restrict_value (&alpha, 0.0, 1.0); - - _cairo_color_init_rgba (&color, red, green, blue, alpha); - - _cairo_set_source_solid (cr, &color); + pattern = cairo_pattern_create_rgba (red, green, blue, alpha); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); } +/** + * cairo_set_source_surface: + * @cr: a cairo context + * @surface: a surface to be used to set the source pattern + * @x: User-space X coordinate for surface origin + * @y: User-space Y coordinate for surface origin + * + * This is a convenience function for creating a pattern from @surface + * and setting it as the source in @cr with cairo_set_source(). + * + * The @x and @y parameters give the user-space coordinate at which + * the surface origin should appear. (The surface origin is its + * upper-left corner before any transformation has been applied.) The + * @x and @y patterns are negated and then set as translation values + * in the pattern matrix. + * + * Other than the initial translation pattern matrix, as described + * above, all other pattern attributes, (such as its extend mode), are + * set to the default values as in cairo_pattern_create_for_surface. + * The resulting pattern can be queried with cairo_get_source() so + * that these attributes can be modified if desired, (eg. to create a + * repeating pattern with cairo_pattern_set_extend()). + **/ void cairo_set_source_surface (cairo_t *cr, cairo_surface_t *surface, @@ -655,7 +657,7 @@ cairo_set_miter_limit (cairo_t *cr, double limit) * @tx: amount to translate in the X direction * @ty: amount to translate in the Y direction * - * Modifies the current transformation matrix (CTM) by tanslating the + * Modifies the current transformation matrix (CTM) by translating the * user-space origin by (@tx, @ty). This offset is interpreted as a * user-space coordinate according to the CTM in place before the new * call to cairo_translate. In other words, the translation of the @@ -1538,7 +1540,7 @@ cairo_fill_extents (cairo_t *cr, * * Calling cairo_clip() can only make the clip region smaller, never * larger. But the current clip is part of the graphics state, so a - * tempoarary restriction of the clip region can be achieved by + * temporary restriction of the clip region can be achieved by * calling cairo_clip() within a cairo_save()/cairo_restore() * pair. The only other means of increasing the size of the clip * region is cairo_reset_clip(). @@ -1568,7 +1570,7 @@ cairo_clip (cairo_t *cr) * * Calling cairo_clip() can only make the clip region smaller, never * larger. But the current clip is part of the graphics state, so a - * tempoarary restriction of the clip region can be achieved by + * temporary restriction of the clip region can be achieved by * calling cairo_clip() within a cairo_save()/cairo_restore() * pair. The only other means of increasing the size of the clip * region is cairo_reset_clip(). @@ -2266,13 +2268,21 @@ cairo_append_path (cairo_t *cr, return; } - if (path == NULL || path->data == NULL) { + if (path == NULL) { _cairo_error (cr, CAIRO_STATUS_NULL_POINTER); return; } if (path->status) { - _cairo_error (cr, path->status); + if (path->status <= CAIRO_STATUS_LAST_STATUS) + _cairo_error (cr, path->status); + else + _cairo_error (cr, CAIRO_STATUS_INVALID_STATUS); + return; + } + + if (path->data == NULL) { + _cairo_error (cr, CAIRO_STATUS_NULL_POINTER); return; } @@ -2303,8 +2313,8 @@ cairo_status_to_string (cairo_status_t status) return "no current point defined"; case CAIRO_STATUS_INVALID_MATRIX: return "invalid matrix (not invertible)"; - case CAIRO_STATUS_NO_TARGET_SURFACE: - return "no target surface has been set"; + case CAIRO_STATUS_INVALID_STATUS: + return " invalid value for an input cairo_status_t"; case CAIRO_STATUS_NULL_POINTER: return "NULL pointer"; case CAIRO_STATUS_INVALID_STRING: diff --git a/src/cairo.h b/src/cairo.h index 0fcd85602..41ae83ce7 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -127,33 +127,34 @@ typedef struct _cairo_user_data_key { /** * cairo_status_t * @CAIRO_STATUS_SUCCESS: no error has occurred - * @CAIRO_STATUS_NO_MEMORY: - * @CAIRO_STATUS_INVALID_RESTORE: + * @CAIRO_STATUS_NO_MEMORY: out of memory + * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore without matching cairo_save * @CAIRO_STATUS_INVALID_POP_GROUP: - * @CAIRO_STATUS_NO_CURRENT_POINT: - * @CAIRO_STATUS_INVALID_MATRIX: - * @CAIRO_STATUS_NO_TARGET_SURFACE: - * @CAIRO_STATUS_NULL_POINTER: - * @CAIRO_STATUS_INVALID_STRING: - * @CAIRO_STATUS_INVALID_PATH_DATA: - * @CAIRO_STATUS_READ_ERROR: - * @CAIRO_STATUS_WRITE_ERROR: - * @CAIRO_STATUS_SURFACE_FINISHED: - * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined + * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) + * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input cairo_status_t + * @CAIRO_STATUS_NULL_POINTER: NULL pointer + * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 + * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid + * @CAIRO_STATUS_READ_ERROR: error while reading from input stream + * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream + * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished + * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation + * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation * * #cairo_status_t is used to indicate errors that can occur when * using Cairo. In some cases it is returned directly by functions. * but when using #cairo_t, the last error, if any, is stored in * the context and can be retrieved with cairo_status(). **/ -typedef enum cairo_status { +typedef enum _cairo_status { CAIRO_STATUS_SUCCESS = 0, CAIRO_STATUS_NO_MEMORY, CAIRO_STATUS_INVALID_RESTORE, CAIRO_STATUS_INVALID_POP_GROUP, CAIRO_STATUS_NO_CURRENT_POINT, CAIRO_STATUS_INVALID_MATRIX, - CAIRO_STATUS_NO_TARGET_SURFACE, + CAIRO_STATUS_INVALID_STATUS, CAIRO_STATUS_NULL_POINTER, CAIRO_STATUS_INVALID_STRING, CAIRO_STATUS_INVALID_PATH_DATA, @@ -220,7 +221,7 @@ cairo_pop_group (cairo_t *cr); /* Modify state */ -typedef enum cairo_operator { +typedef enum _cairo_operator { CAIRO_OPERATOR_CLEAR, CAIRO_OPERATOR_SOURCE, @@ -284,7 +285,7 @@ cairo_set_tolerance (cairo_t *cr, double tolerance); * (Note that filling is not actually implemented in this way. This * is just a description of the rule that is applied.) **/ -typedef enum cairo_fill_rule { +typedef enum _cairo_fill_rule { CAIRO_FILL_RULE_WINDING, CAIRO_FILL_RULE_EVEN_ODD } cairo_fill_rule_t; @@ -304,7 +305,7 @@ cairo_set_line_width (cairo_t *cr, double width); * * enumeration for style of line-endings **/ -typedef enum cairo_line_cap { +typedef enum _cairo_line_cap { CAIRO_LINE_CAP_BUTT, CAIRO_LINE_CAP_ROUND, CAIRO_LINE_CAP_SQUARE @@ -313,7 +314,7 @@ typedef enum cairo_line_cap { void cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap); -typedef enum cairo_line_join { +typedef enum _cairo_line_join { CAIRO_LINE_JOIN_MITER, CAIRO_LINE_JOIN_ROUND, CAIRO_LINE_JOIN_BEVEL @@ -624,13 +625,13 @@ typedef struct { double max_y_advance; } cairo_font_extents_t; -typedef enum cairo_font_slant { +typedef enum _cairo_font_slant { CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_SLANT_OBLIQUE } cairo_font_slant_t; -typedef enum cairo_font_weight { +typedef enum _cairo_font_weight { CAIRO_FONT_WEIGHT_NORMAL, CAIRO_FONT_WEIGHT_BOLD } cairo_font_weight_t; @@ -824,7 +825,7 @@ cairo_get_target (cairo_t *cr); * cairo_path_destroy (path); * </programlisting></informalexample> */ -typedef enum cairo_path_data_type { +typedef enum _cairo_path_data_type { CAIRO_PATH_MOVE_TO, CAIRO_PATH_LINE_TO, CAIRO_PATH_CURVE_TO, @@ -887,39 +888,32 @@ cairo_status_to_string (cairo_status_t status); /* Surface manipulation */ /** - * cairo_format_t - * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with - * alpha in the upper 8 bits, then red, then green, then blue. - * The 32-bit quantities are stored native-endian. Pre-multiplied - * alpha is used. (That is, 50% transparent red is 0x80800000, - * not 0x80ff0000.) - * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with - * the upper 8 bits unused. Red, Green, and Blue are stored - * in the remaining 24 bits in that order. - * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding - * an alpha value. - * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding - * an alpha value. Pixels are packed together into 32-bit - * quantities. The ordering of the bits matches the - * endianess of the platform. On a big-endian machine, the - * first pixel is in the uppermost bit, on a little-endian - * machine the first pixel is in the least-significant bit. + * cairo_content_t + * @CAIRO_CONTENT_COLOR: The surface will hold color content only. + * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. + * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. * - * #cairo_format_t is used to identify the memory format of - * image data. + * @cairo_content_t is used to describe the content that a surface will + * contain, whether color information, alpha information (translucence + * vs. opacity), or both. + * + * Note: The large values here are designed to keep cairo_content_t + * values distinct from cairo_format_t values so that the + * implementation can detect the error if users confuse the two types. */ -typedef enum cairo_format { - CAIRO_FORMAT_ARGB32, - CAIRO_FORMAT_RGB24, - CAIRO_FORMAT_A8, - CAIRO_FORMAT_A1 -} cairo_format_t; +typedef enum _cairo_content { + CAIRO_CONTENT_COLOR = 0x1000, + CAIRO_CONTENT_ALPHA = 0x2000, + CAIRO_CONTENT_COLOR_ALPHA = 0x3000 +} cairo_content_t; + +#define CAIRO_CONTENT_VALID(content) (((content) & ~(CAIRO_CONTENT_COLOR | \ + CAIRO_CONTENT_ALPHA | \ + CAIRO_CONTENT_COLOR_ALPHA)) == 0) -/* XXX: I want to remove this function, (replace with - cairo_begin_group and friends). */ cairo_surface_t * -cairo_surface_create_similar (cairo_surface_t *other, - cairo_format_t format, +cairo_surface_create_similar (cairo_surface_t *other, + cairo_content_t content, int width, int height); @@ -962,6 +956,38 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, /* Image-surface functions */ +/** + * cairo_format_t + * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with + * alpha in the upper 8 bits, then red, then green, then blue. + * The 32-bit quantities are stored native-endian. Pre-multiplied + * alpha is used. (That is, 50% transparent red is 0x80800000, + * not 0x80ff0000.) + * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with + * the upper 8 bits unused. Red, Green, and Blue are stored + * in the remaining 24 bits in that order. + * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding + * an alpha value. + * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding + * an alpha value. Pixels are packed together into 32-bit + * quantities. The ordering of the bits matches the + * endianess of the platform. On a big-endian machine, the + * first pixel is in the uppermost bit, on a little-endian + * machine the first pixel is in the least-significant bit. + * + * #cairo_format_t is used to identify the memory format of + * image data. + */ +typedef enum _cairo_format { + CAIRO_FORMAT_ARGB32, + CAIRO_FORMAT_RGB24, + CAIRO_FORMAT_A8, + CAIRO_FORMAT_A1 +} cairo_format_t; + +#define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ + (format) <= CAIRO_FORMAT_A1) + cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, @@ -992,6 +1018,14 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, #endif /* Pattern creation functions */ + +cairo_pattern_t * +cairo_pattern_create_rgb (double red, double green, double blue); + +cairo_pattern_t * +cairo_pattern_create_rgba (double red, double green, double blue, + double alpha); + cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface); @@ -1031,7 +1065,7 @@ void cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix); -typedef enum { +typedef enum _cairo_extend { CAIRO_EXTEND_NONE, CAIRO_EXTEND_REPEAT, CAIRO_EXTEND_REFLECT @@ -1043,7 +1077,7 @@ cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); cairo_extend_t cairo_pattern_get_extend (cairo_pattern_t *pattern); -typedef enum { +typedef enum _cairo_filter { CAIRO_FILTER_FAST, CAIRO_FILTER_GOOD, CAIRO_FILTER_BEST, diff --git a/src/cairoint.h b/src/cairoint.h index 1b21228d7..bd8072c5d 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -55,6 +55,10 @@ #include <string.h> #include <stdarg.h> +#if HAVE_PTHREAD_H +#include <pthread.h> +#endif + #ifdef _MSC_VER #define _USE_MATH_DEFINES #endif @@ -116,6 +120,45 @@ #define __attribute__(x) #endif +/* XXX: There's a bad bug in the cache locking code that attempts to + * recursively lock a mutex, (which we shouldn't actually need to ever + * do). This leads to deadlocks in even single-threaded applications, + * (if they link with -lpthread). + * + * For now, we're removing all mutex locking, which leaves things at + * the same level of non-thread-safeness that we've had in every + * snapshot since the cache code first landed. + * + * I'm rewriting the cache code now and plan to have thread-safe, + * locking caches working before the next snapshot. CDW. + */ + +#if CAIRO_CACHE_CODE_IS_FIXED_TO_NOT_DEADLOCK_SINGLE_THREADED_APPLICATIONS + +# if HAVE_PTHREAD_H +# define CAIRO_MUTEX_DECLARE(name) static pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER +# define CAIRO_MUTEX_DECLARE_GLOBAL(name) pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER +# define CAIRO_MUTEX_LOCK(name) pthread_mutex_lock (&name) +# define CAIRO_MUTEX_UNLOCK(name) pthread_mutex_unlock (&name) +# endif + +# ifndef CAIRO_MUTEX_DECLARE +# warning "No mutex declarations, assuming single-threaded code" +# define CAIRO_MUTEX_DECLARE(name) +# define CAIRO_MUTEX_DECLARE_GLOBAL(name) +# define CAIRO_MUTEX_LOCK(name) +# define CAIRO_MUTEX_UNLOCK(name) +# endif + +#else + +# define CAIRO_MUTEX_DECLARE(name) +# define CAIRO_MUTEX_DECLARE_GLOBAL(name) +# define CAIRO_MUTEX_LOCK(name) +# define CAIRO_MUTEX_UNLOCK(name) + +#endif + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -377,10 +420,11 @@ _cairo_cache_init (cairo_cache_t *cache, unsigned long max_memory); cairo_private void -_cairo_cache_reference (cairo_cache_t *cache); +_cairo_cache_destroy (cairo_cache_t *cache); cairo_private void -_cairo_cache_destroy (cairo_cache_t *cache); +_cairo_cache_shrink_to (cairo_cache_t *cache, + unsigned long max_memory); cairo_private cairo_status_t _cairo_cache_lookup (cairo_cache_t *cache, @@ -566,7 +610,7 @@ extern const cairo_private struct _cairo_scaled_font_backend cairo_atsui_scaled_ typedef struct _cairo_surface_backend { cairo_surface_t * (*create_similar) (void *surface, - cairo_format_t format, + cairo_content_t content, int width, int height); @@ -905,17 +949,17 @@ typedef struct _cairo_traps { #if CAIRO_HAS_WIN32_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_win32_scaled_font_backend +#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &cairo_win32_scaled_font_backend #elif CAIRO_HAS_ATSUI_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_ATSUI_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_atsui_scaled_font_backend +#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &cairo_atsui_scaled_font_backend #elif CAIRO_HAS_FT_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_ft_scaled_font_backend +#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &cairo_ft_scaled_font_backend #endif @@ -1404,14 +1448,14 @@ _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, /* cairo-surface.c */ cairo_private cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_format_t format, +_cairo_surface_create_similar_scratch (cairo_surface_t *other, + cairo_content_t content, int width, int height); cairo_private cairo_surface_t * _cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_format_t format, + cairo_content_t content, int width, int height, const cairo_color_t *color); @@ -1523,6 +1567,12 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region, unsigned int serial); +cairo_private cairo_int_status_t +_cairo_surface_intersect_clip_path (cairo_surface_t *surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance); + typedef struct _cairo_clip_path cairo_clip_path_t; cairo_private cairo_status_t @@ -1550,6 +1600,12 @@ _cairo_surface_show_glyphs (cairo_scaled_font_t *scaled_font, /* cairo_image_surface.c */ +cairo_private cairo_format_t +_cairo_format_from_content (cairo_content_t content); + +cairo_private cairo_content_t +_cairo_content_from_format (cairo_format_t format); + cairo_private cairo_image_surface_t * _cairo_image_surface_create_with_masks (unsigned char *data, cairo_format_masks_t *format, @@ -1761,7 +1817,7 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, cairo_surface_attributes_t *attributes); cairo_private void -_cairo_pattern_release_surface (cairo_surface_t *dst, +_cairo_pattern_release_surface (cairo_pattern_t *pattern, cairo_surface_t *surface, cairo_surface_attributes_t *attributes); @@ -1810,6 +1866,11 @@ cairo_private cairo_status_t _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length); +cairo_private void +_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, + const char *data, + size_t length); + cairo_private cairo_status_t _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *fmt, va_list ap); diff --git a/test/.cvsignore b/test/.cvsignore index 0b4c1531c..00ed2feca 100644 --- a/test/.cvsignore +++ b/test/.cvsignore @@ -4,8 +4,11 @@ Makefile Makefile.in clip-nesting clip-twice +composite-integer-translate-source +composite-integer-translate-over +composite-integer-translate-over-repeat coverage -create-for-png +create-from-png fill-and-stroke fill-rule filter-nearest-offset diff --git a/test/Makefile.am b/test/Makefile.am index 48204192d..ee8d1b98d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,7 +2,10 @@ TESTS = \ clip-nesting \ clip-twice \ -create-for-png \ +composite-integer-translate-source \ +composite-integer-translate-over \ +composite-integer-translate-over-repeat \ +create-from-png \ fill-and-stroke \ fill-rule \ filter-nearest-offset \ @@ -52,7 +55,10 @@ endif EXTRA_DIST = \ clip-nesting-ref.png \ clip-twice-ref.png \ -create-for-png-ref.png \ +composite-integer-translate-source-ref.png \ +composite-integer-translate-over-ref.png \ +composite-integer-translate-over-repeat-ref.png \ +create-from-png-ref.png \ fill-and-stroke-ref.png \ fill-rule-ref.png \ filter-nearest-offset-ref.png \ @@ -109,8 +115,8 @@ INCLUDES = \ -D_GNU_SOURCE \ $(CAIRO_CFLAGS) \ -I$(srcdir) \ - -I$(top_srcdir)/src \ - -I$(top_builddir)/src + -I$(top_builddir)/src \ + -I$(top_srcdir)/src noinst_LTLIBRARIES = libcairotest.la @@ -133,7 +139,10 @@ LDADDS = libcairotest.la $(top_builddir)/src/libcairo.la # from autogen.sh. My, but this is painful... clip_nesting_LDADD = $(LDADDS) clip_twice_LDADD = $(LDADDS) -create_for_png_LDADD = $(LDADDS) +composite_integer_translate_source_LDADD = $(LDADDS) +composite_integer_translate_over_LDADD = $(LDADDS) +composite_integer_translate_over_repeat_LDADD = $(LDADDS) +create_from_png_LDADD = $(LDADDS) fill_and_stroke_LDADD = $(LDADDS) fill_rule_LDADD = $(LDADDS) filter_nearest_offset_LDADD = $(LDADDS) @@ -179,3 +188,6 @@ CLEANFILES = \ ps-surface.ps \ pdf-surface.pdf \ pdf-clip.pdf + +check-valgrind: + TESTS_ENVIRONMENT="libtool --mode=execute valgrind --tool=memcheck --leak-check=yes --show-reachable=yes" $(MAKE) check diff --git a/test/buffer-diff.c b/test/buffer-diff.c index e540d2f9b..e5ba513ac 100644 --- a/test/buffer-diff.c +++ b/test/buffer-diff.c @@ -112,8 +112,10 @@ image_diff (const char *filename_a, return -1; status = read_png_argb32 (filename_b, &buf_b, &width_b, &height_b, &stride_b); - if (status) + if (status) { + free (buf_a); return -1; + } if (width_a != width_b || height_a != height_b || diff --git a/test/cairo-test.c b/test/cairo-test.c index a17cd6f8b..77cf48fd0 100644 --- a/test/cairo-test.c +++ b/test/cairo-test.c @@ -338,6 +338,7 @@ cleanup_xcb (void *closure) XCBFreePixmap (xtc->c, xtc->drawable.pixmap); XCBDisconnect (xtc->c); + free (xtc); } #endif @@ -388,6 +389,7 @@ cleanup_xlib (void *closure) XFreePixmap (xtc->dpy, xtc->pixmap); XCloseDisplay (xtc->dpy); + free (xtc); } #endif @@ -401,7 +403,6 @@ cairo_test_for_target (cairo_test_t *test, cairo_t *cr; char *png_name, *ref_name, *diff_name; char *srcdir; - int pixels_changed; cairo_test_status_t ret; /* Get the strings ready that we'll need. */ @@ -420,7 +421,8 @@ cairo_test_for_target (cairo_test_t *test, &target->closure); if (surface == NULL) { cairo_test_log ("Error: Failed to set %s target\n", target->name); - return CAIRO_TEST_FAILURE; + ret = CAIRO_TEST_UNTESTED; + goto UNWIND_STRINGS; } cr = cairo_create (surface); @@ -436,40 +438,39 @@ cairo_test_for_target (cairo_test_t *test, /* Then, check all the different ways it could fail. */ if (status) { cairo_test_log ("Error: Function under test failed\n"); - return status; + ret = status; + goto UNWIND_CAIRO; } if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) { cairo_test_log ("Error: Function under test left cairo status in an error state: %s\n", cairo_status_to_string (cairo_status (cr))); - return CAIRO_TEST_FAILURE; + ret = CAIRO_TEST_FAILURE; + goto UNWIND_CAIRO; } /* Skip image check for tests with no image (width,height == 0,0) */ - if (test->width == 0 || test->height == 0) { - cairo_destroy (cr); - return CAIRO_TEST_SUCCESS; + if (test->width != 0 && test->height != 0) { + int pixels_changed; + cairo_surface_write_to_png (surface, png_name); + pixels_changed = image_diff (png_name, ref_name, diff_name); + if (pixels_changed) { + if (pixels_changed > 0) + cairo_test_log ("Error: %d pixels differ from reference image %s\n", + pixels_changed, ref_name); + ret = CAIRO_TEST_FAILURE; + goto UNWIND_CAIRO; + } } - cairo_surface_write_to_png (surface, png_name); + ret = CAIRO_TEST_SUCCESS; +UNWIND_CAIRO: cairo_destroy (cr); - cairo_surface_destroy (surface); - target->cleanup_target (target->closure); - pixels_changed = image_diff (png_name, ref_name, diff_name); - - if (pixels_changed) { - ret = CAIRO_TEST_FAILURE; - if (pixels_changed > 0) - cairo_test_log ("Error: %d pixels differ from reference image %s\n", - pixels_changed, ref_name); - } else { - ret = CAIRO_TEST_SUCCESS; - } - +UNWIND_STRINGS: free (png_name); free (ref_name); free (diff_name); @@ -478,7 +479,8 @@ cairo_test_for_target (cairo_test_t *test, } static cairo_test_status_t -cairo_test_real (cairo_test_t *test, cairo_test_draw_function_t draw) +cairo_test_expecting (cairo_test_t *test, cairo_test_draw_function_t draw, + cairo_test_status_t expectation) { int i; cairo_test_status_t status, ret; @@ -511,20 +513,46 @@ cairo_test_real (cairo_test_t *test, cairo_test_draw_function_t draw) fprintf (stderr, "Error opening log file: %s\n", log_name); cairo_test_log_file = stderr; } - - ret = CAIRO_TEST_SUCCESS; + free (log_name); + + /* The intended logic here is that we return overall SUCCESS + * iff. there is at least one tested backend and that all tested + * backends return SUCCESS. In other words: + * + * if any backend FAILURE + * -> FAILURE + * else if all backends UNTESTED + * -> FAILURE + * else (== some backend SUCCESS) + * -> SUCCESS + */ + ret = CAIRO_TEST_UNTESTED; for (i=0; i < sizeof(targets)/sizeof(targets[0]); i++) { cairo_test_target_t *target = &targets[i]; cairo_test_log ("Testing %s with %s target\n", test->name, target->name); printf ("%s-%s:\t", test->name, target->name); status = cairo_test_for_target (test, draw, target); - if (status) { - printf ("FAIL\n"); - ret = status; - } else { + switch (status) { + case CAIRO_TEST_SUCCESS: printf ("PASS\n"); + if (ret == CAIRO_TEST_UNTESTED) + ret = CAIRO_TEST_SUCCESS; + break; + case CAIRO_TEST_UNTESTED: + printf ("UNTESTED\n"); + break; + default: + case CAIRO_TEST_FAILURE: + if (expectation == CAIRO_TEST_FAILURE) + printf ("XFAIL\n"); + else + printf ("FAIL\n"); + ret = status; + break; } } + if (ret == CAIRO_TEST_UNTESTED) + ret = CAIRO_TEST_FAILURE; fclose (cairo_test_log_file); @@ -537,43 +565,50 @@ cairo_test_expect_failure (cairo_test_t *test, const char *because) { printf ("\n%s is expected to fail:\n\t%s\n", test->name, because); - return cairo_test_real (test, draw); + return cairo_test_expecting (test, draw, CAIRO_TEST_FAILURE); } cairo_test_status_t cairo_test (cairo_test_t *test, cairo_test_draw_function_t draw) { printf ("\n"); - return cairo_test_real (test, draw); + return cairo_test_expecting (test, draw, CAIRO_TEST_SUCCESS); } -cairo_pattern_t * -cairo_test_create_png_pattern (cairo_t *cr, const char *filename) +cairo_surface_t * +cairo_test_create_surface_from_png (const char *filename) { cairo_surface_t *image; - cairo_pattern_t *pattern; - unsigned char *buffer; - unsigned int w, h, stride; - read_png_status_t status; char *srcdir = getenv ("srcdir"); - status = read_png_argb32 (filename, &buffer, &w,&h, &stride); - if (status != READ_PNG_SUCCESS) { + image = cairo_image_surface_create_from_png (filename); + if (image == NULL) { if (srcdir) { char *srcdir_filename; xasprintf (&srcdir_filename, "%s/%s", srcdir, filename); - status = read_png_argb32 (srcdir_filename, &buffer, &w,&h, &stride); + image = cairo_image_surface_create_from_png (srcdir_filename); free (srcdir_filename); } + if (image == NULL) + return NULL; } - if (status != READ_PNG_SUCCESS) - return NULL; - image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_ARGB32, - w, h, stride); + return image; +} + +cairo_pattern_t * +cairo_test_create_pattern_from_png (const char *filename) +{ + cairo_surface_t *image; + cairo_pattern_t *pattern; + + image = cairo_test_create_surface_from_png (filename); pattern = cairo_pattern_create_for_surface (image); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + cairo_surface_destroy (image); + return pattern; } diff --git a/test/cairo-test.h b/test/cairo-test.h index 6260daed1..beabc6a5e 100644 --- a/test/cairo-test.h +++ b/test/cairo-test.h @@ -31,7 +31,8 @@ typedef enum cairo_test_status { CAIRO_TEST_SUCCESS = 0, - CAIRO_TEST_FAILURE + CAIRO_TEST_FAILURE, + CAIRO_TEST_UNTESTED } cairo_test_status_t; typedef struct cairo_test { @@ -52,8 +53,11 @@ cairo_test_expect_failure (cairo_test_t *test, cairo_test_draw_function_t draw, const char *reason); +cairo_surface_t * +cairo_test_create_surface_from_png (const char *filename); + cairo_pattern_t * -cairo_test_create_png_pattern (cairo_t *cr, const char *filename); +cairo_test_create_pattern_from_png (const char *filename); void cairo_test_log (const char *fmt, ...); @@ -62,4 +66,3 @@ void xasprintf (char **strp, const char *fmt, ...); #endif - diff --git a/test/composite-integer-translate-over-ref.png b/test/composite-integer-translate-over-ref.png Binary files differnew file mode 100644 index 000000000..1fbaaaa3b --- /dev/null +++ b/test/composite-integer-translate-over-ref.png diff --git a/test/composite-integer-translate-over-repeat-ref.png b/test/composite-integer-translate-over-repeat-ref.png Binary files differnew file mode 100644 index 000000000..c04db2631 --- /dev/null +++ b/test/composite-integer-translate-over-repeat-ref.png diff --git a/test/composite-integer-translate-over-repeat.c b/test/composite-integer-translate-over-repeat.c new file mode 100644 index 000000000..ed55f63a6 --- /dev/null +++ b/test/composite-integer-translate-over-repeat.c @@ -0,0 +1,62 @@ +#include <math.h> +#include "cairo-test.h" +#include <stdio.h> + +#define SIZE 100 +#define SIZE2 20 +#define OFFSET 10 + +cairo_test_t test = { + "composite-integer-translate-over-repeat", + "Test simple compositing: integer-translation 32->32 OVER, with repeat", + SIZE, SIZE +}; + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_surface_t *image; + cairo_pattern_t *pat; + cairo_t *cr2; + + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, SIZE2, SIZE2); + + cr2 = cairo_create (image); + cairo_set_source_rgba (cr2, 1, 0, 0, 1); + cairo_rectangle (cr2, 0, 0, SIZE2/2, SIZE2/2); + cairo_fill (cr2); + cairo_set_source_rgba (cr2, 0, 1, 0, 1); + cairo_rectangle (cr2, SIZE2/2, 0, SIZE2/2, SIZE2/2); + cairo_fill (cr2); + cairo_set_source_rgba (cr2, 0, 0, 1, 1); + cairo_rectangle (cr2, 0, SIZE2/2, SIZE2/2, SIZE2/2); + cairo_fill (cr2); + cairo_set_source_rgba (cr2, 1, 1, 0, 1); + cairo_rectangle (cr2, SIZE2/2, SIZE2/2, SIZE2/2, SIZE2/2); + cairo_fill (cr2); + cairo_destroy (cr2); + + pat = cairo_pattern_create_for_surface (image); + cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT); + + cairo_set_source_rgba (cr, 0, 0, 0, 1); + cairo_rectangle (cr, 0, 0, SIZE, SIZE); + cairo_fill (cr); + + cairo_translate (cr, OFFSET, OFFSET); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source (cr, pat); + cairo_rectangle (cr, 0, 0, SIZE - OFFSET, SIZE - OFFSET); + cairo_fill (cr); + + cairo_surface_destroy (image); + cairo_pattern_destroy (pat); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test, draw); +} diff --git a/test/composite-integer-translate-over.c b/test/composite-integer-translate-over.c new file mode 100644 index 000000000..52b4a6c47 --- /dev/null +++ b/test/composite-integer-translate-over.c @@ -0,0 +1,42 @@ +#include <math.h> +#include "cairo-test.h" +#include <stdio.h> + +#define SIZE 100 +#define OFFSET 5.5 +#define SCALE 1.5 +const char png_filename[] = "romedalen.png"; + +cairo_test_t test = { + "composite-integer-translate-over", + "Test simple compositing: integer-translation 32->32 OVER", + SIZE, SIZE +}; + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_surface_t *image; + + image = cairo_test_create_surface_from_png (png_filename); + + cairo_set_source_rgba (cr, 0, 0, 0, 1); + cairo_rectangle (cr, 0, 0, SIZE, SIZE); + cairo_fill (cr); + + cairo_translate (cr, OFFSET, OFFSET); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface (cr, image, 0, 0); + cairo_rectangle (cr, 0, 0, (SIZE-OFFSET), (SIZE-OFFSET)); + cairo_fill (cr); + + cairo_surface_destroy (image); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test, draw); +} diff --git a/test/composite-integer-translate-source-ref.png b/test/composite-integer-translate-source-ref.png Binary files differnew file mode 100644 index 000000000..da9a3986a --- /dev/null +++ b/test/composite-integer-translate-source-ref.png diff --git a/test/composite-integer-translate-source.c b/test/composite-integer-translate-source.c new file mode 100644 index 000000000..d2f39a351 --- /dev/null +++ b/test/composite-integer-translate-source.c @@ -0,0 +1,42 @@ +#include <math.h> +#include "cairo-test.h" +#include <stdio.h> + +#define SIZE 100 +#define OFFSET 10 + +const char png_filename[] = "romedalen.png"; + +cairo_test_t test = { + "composite-integer-translate-source", + "Test simple compositing: integer-translation 32->32 SOURCE", + SIZE, SIZE +}; + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_surface_t *image; + + image = cairo_test_create_surface_from_png (png_filename); + + cairo_set_source_rgba (cr, 0, 0, 0, 1); + cairo_rectangle (cr, 0, 0, SIZE, SIZE); + cairo_fill (cr); + + cairo_translate (cr, OFFSET, OFFSET); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface (cr, image, 0, 0); + cairo_rectangle (cr, 0, 0, SIZE - OFFSET, SIZE - OFFSET); + cairo_fill (cr); + + cairo_surface_destroy (image); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test, draw); +} diff --git a/test/create-for-png-ref.png b/test/create-for-png-ref.png Binary files differdeleted file mode 100644 index 765adc4a4..000000000 --- a/test/create-for-png-ref.png +++ /dev/null diff --git a/test/create-for-png.c b/test/create-for-png.c deleted file mode 100644 index ef3b6f62e..000000000 --- a/test/create-for-png.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2005 Red Hat, Inc. - * - * Permission to use, copy, modify, distribute, and sell this software - * and its documentation for any purpose is hereby granted without - * fee, provided that the above copyright notice appear in all copies - * and that both that copyright notice and this permission notice - * appear in supporting documentation, and that the name of - * Red Hat, Inc. not be used in advertising or publicity pertaining to - * distribution of the software without specific, written prior - * permission. Red Hat, Inc. makes no representations about the - * suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author: Carl Worth <cworth@cworth.org> - */ - -#include "cairo-test.h" - -#include <stdlib.h> - -#define WIDTH 2 -#define HEIGHT 2 - -cairo_test_t test = { - "create-for-png", - "Tests the creation of an image surface from a PNG file", - WIDTH, HEIGHT -}; - -static cairo_test_status_t -draw (cairo_t *cr, int width, int height) -{ - char *srcdir = getenv ("srcdir"); - char *filename; - cairo_surface_t *surface; - - xasprintf (&filename, "%s/%s", srcdir ? srcdir : ".", - "create-for-png-ref.png"); - - surface = cairo_image_surface_create_from_png (filename); - free (filename); - - if (surface == NULL) { - cairo_test_log ("Error: failed to open file %s\n", filename); - return CAIRO_TEST_FAILURE; - } - - cairo_set_source_surface (cr, surface, 0, 0); - cairo_paint (cr); - - cairo_surface_destroy (surface); - - return CAIRO_TEST_SUCCESS; -} - -int -main (void) -{ - return cairo_test (&test, draw); -} diff --git a/test/create-from-png.c b/test/create-from-png.c index ef3b6f62e..f4fd1b934 100644 --- a/test/create-from-png.c +++ b/test/create-from-png.c @@ -31,7 +31,7 @@ #define HEIGHT 2 cairo_test_t test = { - "create-for-png", + "create-from-png", "Tests the creation of an image surface from a PNG file", WIDTH, HEIGHT }; @@ -44,7 +44,7 @@ draw (cairo_t *cr, int width, int height) cairo_surface_t *surface; xasprintf (&filename, "%s/%s", srcdir ? srcdir : ".", - "create-for-png-ref.png"); + "create-from-png-ref.png"); surface = cairo_image_surface_create_from_png (filename); free (filename); diff --git a/test/mask.c b/test/mask.c index ec0322da0..a9414e046 100644 --- a/test/mask.c +++ b/test/mask.c @@ -56,6 +56,7 @@ set_gradient_pattern (cairo_t *cr, int x, int y) cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 1, 1, 1); cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 0.4, 1); cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); } static void @@ -63,8 +64,9 @@ set_image_pattern (cairo_t *cr, int x, int y) { cairo_pattern_t *pattern; - pattern = cairo_test_create_png_pattern (cr, png_filename); + pattern = cairo_test_create_pattern_from_png (png_filename); cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); } static void @@ -74,7 +76,7 @@ mask_polygon (cairo_t *cr, int x, int y) cairo_t *cr2; mask_surface = cairo_surface_create_similar (cairo_get_target (cr), - CAIRO_FORMAT_A8, + CAIRO_CONTENT_ALPHA, WIDTH, HEIGHT); cr2 = cairo_create (mask_surface); @@ -196,12 +198,14 @@ draw (cairo_t *cr, int width, int height) * a temporary surface and copy over. */ tmp_surface = cairo_surface_create_similar (cairo_get_target (cr), - CAIRO_FORMAT_ARGB32, + CAIRO_CONTENT_COLOR_ALPHA, IMAGE_WIDTH, IMAGE_HEIGHT); cr2 = cairo_create (tmp_surface); tmp_pattern = cairo_pattern_create_for_surface (tmp_surface); cairo_set_source (cr, tmp_pattern); + cairo_pattern_destroy (tmp_pattern); + cairo_surface_destroy (tmp_surface); for (k = 0; k < ARRAY_SIZE (clip_funcs); k++) { for (j = 0; j < ARRAY_SIZE (mask_funcs); j++) { @@ -234,7 +238,6 @@ draw (cairo_t *cr, int width, int height) } cairo_destroy (cr2); - cairo_surface_destroy (tmp_surface); return CAIRO_TEST_SUCCESS; } diff --git a/test/path-data.c b/test/path-data.c index b2d7edbf5..b02ce4333 100644 --- a/test/path-data.c +++ b/test/path-data.c @@ -152,8 +152,23 @@ main (void) cairo_destroy (cr); cr = cairo_create (surface); + path.status = -1; + cairo_append_path (cr, &path); + if (cairo_status (cr) != CAIRO_STATUS_INVALID_STATUS) + return 1; + cairo_destroy (cr); + + cr = cairo_create (surface); + path.status = CAIRO_STATUS_NO_MEMORY; + cairo_append_path (cr, &path); + if (cairo_status (cr) != CAIRO_STATUS_NO_MEMORY) + return 1; + cairo_destroy (cr); + + cr = cairo_create (surface); path.data = NULL; path.num_data = 0; + path.status = CAIRO_STATUS_SUCCESS; cairo_append_path (cr, &path); if (cairo_status (cr) != CAIRO_STATUS_NULL_POINTER) return 1; diff --git a/test/pixman-rotate.c b/test/pixman-rotate.c index 5c6fe4140..93ed42b30 100644 --- a/test/pixman-rotate.c +++ b/test/pixman-rotate.c @@ -28,7 +28,7 @@ draw (cairo_t *cr, int width, int height) cairo_t *cr2; stamp = cairo_surface_create_similar (cairo_get_target (cr), - CAIRO_FORMAT_ARGB32, + CAIRO_CONTENT_COLOR_ALPHA, WIDTH, HEIGHT); cr2 = cairo_create (stamp); { diff --git a/test/source-clip.c b/test/source-clip.c index 73be6b432..f479e485a 100644 --- a/test/source-clip.c +++ b/test/source-clip.c @@ -43,7 +43,7 @@ draw (cairo_t *cr, int width, int height) cairo_t *cr2; source_surface = cairo_surface_create_similar (cairo_get_target (cr), - CAIRO_FORMAT_ARGB32, + CAIRO_CONTENT_COLOR_ALPHA, SIZE, SIZE); cr2 = cairo_create (source_surface); diff --git a/test/surface-finish-twice.c b/test/surface-finish-twice.c index 3f24e391c..68d49c928 100644 --- a/test/surface-finish-twice.c +++ b/test/surface-finish-twice.c @@ -63,6 +63,8 @@ draw (cairo_t *cr, int width, int height) if (status != CAIRO_STATUS_SURFACE_FINISHED) return CAIRO_TEST_FAILURE; + cairo_surface_destroy (surface); + return CAIRO_TEST_SUCCESS; } diff --git a/test/trap-clip.c b/test/trap-clip.c index 2ad43ea5f..75b06a83d 100644 --- a/test/trap-clip.c +++ b/test/trap-clip.c @@ -55,6 +55,7 @@ set_gradient_pattern (cairo_t *cr, int x, int y) cairo_pattern_add_color_stop_rgba (pattern, 0, 1, 1, 1, 1); cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 0.4, 1); cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); } static void @@ -62,8 +63,9 @@ set_image_pattern (cairo_t *cr, int x, int y) { cairo_pattern_t *pattern; - pattern = cairo_test_create_png_pattern (cr, png_filename); + pattern = cairo_test_create_pattern_from_png (png_filename); cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); } static void diff --git a/test/xlib-surface.c b/test/xlib-surface.c index d8c241c8e..2921eb852 100644 --- a/test/xlib-surface.c +++ b/test/xlib-surface.c @@ -221,11 +221,13 @@ main (void) dpy = XOpenDisplay (NULL); if (!dpy) { fprintf (log_file, "xlib-surface: Cannot open display, skipping\n"); + fclose (log_file); return 0; } if (!check_visual (dpy)) { fprintf (log_file, "xlib-surface: default visual is not RGB24 or BGR24, skipping\n"); + fclose (log_file); return 0; } @@ -264,6 +266,8 @@ main (void) free (diff_data); XCloseDisplay (dpy); + + fclose (log_file); return result; } |