2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 // - Rotation problem in gradient-tests.swf.
23 // - Would be nice to have a header/implementation separation.
24 // - Document workings of Cairo and this renderer.
25 // - Test bitmap implementation correctness.
26 // - Figure out what extend types should be used and when.
30 // Already implemented:
32 // - fills: solid, linear, radial, focal and bitmap
36 // - video (from old Cairo renderer)
38 #include "Renderer_cairo.h"
41 #include <cairo/cairo.h>
42 #include <boost/scoped_array.hpp>
43 #include <boost/scoped_ptr.hpp>
44 #include <boost/bind.hpp>
46 #include "smart_ptr.h"
48 #include "GnashImage.h"
49 #include "PathParser.h"
50 #include "swf/ShapeRecord.h"
52 #include "FillStyle.h"
53 #include "Transform.h"
54 #include "ImageIterators.h"
55 #include "CachedBitmap.h"
60 void pattern_add_color_stops(const GradientFill
& f
,
61 cairo_pattern_t
* pattern
, const SWFCxForm
& cx
);
62 void init_cairo_matrix(cairo_matrix_t
* cairo_matrix
,
63 const SWFMatrix
& gnash_matrix
);
68 // Converts from RGB image to 32-bit pixels in CAIRO_FORMAT_RGB24 format
70 rgb_to_cairo_rgb24(boost::uint8_t* dst
, const image::GnashImage
* im
)
72 boost::uint32_t* dst32
= reinterpret_cast<boost::uint32_t*>(dst
);
73 for (size_t y
= 0; y
< im
->height(); y
++)
75 const boost::uint8_t* src
= scanline(*im
, y
);
76 for (size_t x
= 0; x
< im
->width(); x
++, src
+= 3) {
77 *dst32
++ = (src
[0] << 16) | (src
[1] << 8) | src
[2];
82 // Converts from RGBA image to 32-bit pixels in CAIRO_FORMAT_ARGB32 format
84 rgba_to_cairo_argb(boost::uint8_t* dst
, const image::GnashImage
* im
)
86 boost::uint32_t* dst32
= reinterpret_cast<boost::uint32_t*>(dst
);
87 for (size_t y
= 0; y
< im
->height(); y
++)
89 const boost::uint8_t* src
= scanline(*im
, y
);
90 for (size_t x
= 0; x
< im
->width(); x
++, src
+= 4)
92 const boost::uint8_t& r
= src
[0],
98 *dst32
++ = (a
<< 24) | (r
<< 16) | (g
<< 8) | b
;
106 class bitmap_info_cairo
: public CachedBitmap
, boost::noncopyable
109 bitmap_info_cairo(boost::uint8_t* data
, int width
, int height
,
110 size_t bpp
, cairo_format_t format
)
115 _bytes_per_pixel(bpp
),
117 _surface(cairo_image_surface_create_for_data(_data
.get(),
118 format
, width
, height
, width
* bpp
)),
119 _pattern(cairo_pattern_create_for_surface (_surface
))
122 assert(cairo_surface_status(_surface
) == CAIRO_STATUS_SUCCESS
);
123 assert(cairo_pattern_status(_pattern
) == CAIRO_STATUS_SUCCESS
);
126 virtual void dispose() {
131 virtual bool disposed() const {
136 image::GnashImage
& image() {
137 if (_image
.get()) return *_image
;
140 case CAIRO_FORMAT_RGB24
:
141 _image
.reset(new image::ImageRGB(_width
, _height
));
144 case CAIRO_FORMAT_ARGB32
:
145 _image
.reset(new image::ImageRGBA(_width
, _height
));
152 // We assume that cairo uses machine-endian order, as that's what
153 // the existing conversion functions do.
154 boost::uint32_t* start
=
155 reinterpret_cast<boost::uint32_t*>(_data
.get());
156 const size_t sz
= _width
* _height
;
157 std::copy(start
, start
+ sz
, image::begin
<image::ARGB
>(*_image
));
161 void update() const {
162 if (!_image
.get()) return;
164 case CAIRO_FORMAT_RGB24
:
165 rgb_to_cairo_rgb24(_data
.get(), _image
.get());
167 case CAIRO_FORMAT_ARGB32
:
168 rgba_to_cairo_argb(_data
.get(), _image
.get());
178 cairo_surface_destroy(_surface
);
179 cairo_pattern_destroy(_pattern
);
182 cairo_pattern_t
* apply(const cairo_matrix_t
* mat
, int /*fill_type*/) const
187 cairo_pattern_set_matrix(_pattern
, mat
);
189 cairo_extend_t extend
= CAIRO_EXTEND_REPEAT
;
192 // The extend type should probably depend on certain factors, but which?
194 case SWF::FILL_CLIPPED_BITMAP
:
196 case SWF::FILL_CLIPPED_BITMAP_HARD
:
197 case SWF::FILL_TILED_BITMAP_HARD
:
199 case SWF::FILL_TILED_BITMAP
:
204 cairo_pattern_set_extend(_pattern
, extend
);
210 mutable boost::scoped_ptr
<image::GnashImage
> _image
;
211 boost::scoped_array
<boost::uint8_t> _data
;
214 size_t _bytes_per_pixel
;
215 cairo_format_t _format
;
216 cairo_surface_t
* _surface
;
217 cairo_pattern_t
* _pattern
;
223 /// Transfer FillStyles to agg styles.
224 struct StyleHandler
: boost::static_visitor
<cairo_pattern_t
*>
226 StyleHandler(const SWFCxForm
& c
)
231 cairo_pattern_t
* operator()(const GradientFill
& f
) const {
232 const SWFMatrix m
= f
.matrix();
234 case GradientFill::LINEAR
:
237 init_cairo_matrix(&mat
, m
);
239 cairo_pattern_t
* pattern
=
240 cairo_pattern_create_linear(0, 0, 256.0, 0);
241 cairo_pattern_set_matrix (pattern
, &mat
);
243 pattern_add_color_stops(f
, pattern
, _cx
);
246 case GradientFill::RADIAL
:
249 // Undo the translation our parser applied.
250 gnash::SWFMatrix transl
;
251 transl
.concatenate(m
);
254 init_cairo_matrix(&mat
, transl
);
256 /// This is 0 for radial gradients.
257 const double focal_pos
= 32.0f
* f
.focalPoint();
259 cairo_pattern_t
* pattern
=
260 cairo_pattern_create_radial(focal_pos
, 0.0, 0.0,
263 cairo_pattern_set_matrix (pattern
, &mat
);
265 pattern_add_color_stops(f
, pattern
, _cx
);
269 // We should never get here.
273 cairo_pattern_t
* operator()(const SolidFill
& f
) const {
274 rgba c
= _cx
.transform(f
.color());
275 cairo_pattern_t
* pattern
=
276 cairo_pattern_create_rgba(c
.m_r
/ 255.0, c
.m_g
/ 255.0,
277 c
.m_b
/ 255.0, c
.m_a
/ 255.0);
281 cairo_pattern_t
* operator()(const BitmapFill
& f
) const {
282 SWFMatrix m
= f
.matrix();
284 const bitmap_info_cairo
* binfo
=
285 dynamic_cast<const bitmap_info_cairo
*>(f
.bitmap());
287 if (!binfo
) return 0;
290 init_cairo_matrix(&mat
, m
);
292 // TODO: the second argument should be fill_type but is unused.
293 cairo_pattern_t
* pattern
= binfo
->apply(&mat
, 0);
298 const SWFCxForm
& _cx
;
305 snap_to_half_pixel(cairo_t
* cr
, double& x
, double& y
)
307 cairo_user_to_device(cr
, &x
, &y
);
309 x
= std::floor(x
+ 0.5) + 0.5;
310 y
= std::floor(y
+ 0.5) + 0.5;
312 cairo_device_to_user(cr
, &x
, &y
);
315 static cairo_pattern_t
*
316 get_cairo_pattern(const FillStyle
& style
, const SWFCxForm
& cx
)
319 cairo_pattern_t
* pattern
= boost::apply_visitor(st
, style
.fill
);
325 class CairoPathRunner
: public PathParser
328 CairoPathRunner(Renderer_cairo
& renderer
,
329 const std::vector
<Path
>& paths
,
330 const std::vector
<FillStyle
>& FillStyles
, cairo_t
* context
)
331 : PathParser(paths
, FillStyles
.size()),
335 _FillStyles(FillStyles
)
339 virtual void prepareFill(int fill_index
, const SWFCxForm
& cx
)
342 _pattern
= get_cairo_pattern(_FillStyles
[fill_index
-1], cx
);
345 virtual void terminateFill(int FillStyle
)
354 cairo_set_source(_cr
, _pattern
);
358 // Surfaces are owned by const bitmap_info_cairo
359 if (cairo_pattern_get_type(_pattern
) != CAIRO_PATTERN_TYPE_SURFACE
) {
360 cairo_pattern_destroy(_pattern
);
365 void moveTo(const point
& ap
)
367 double x
= ap
.x
, y
= ap
.y
;
368 snap_to_half_pixel(_cr
, x
, y
);
370 cairo_move_to(_cr
, x
, y
);
373 virtual void curveTo(const Edge
& cur_edge
)
375 const float two_thirds
= 2.0/3.0;
376 const float one_third
= 1 - two_thirds
;
379 cairo_get_current_point(_cr
, &x
, &y
);
381 double x1
= x
+ two_thirds
* (cur_edge
.cp
.x
- x
);
382 double y1
= y
+ two_thirds
* (cur_edge
.cp
.y
- y
);
384 double x2
= cur_edge
.cp
.x
+ one_third
* (cur_edge
.ap
.x
- cur_edge
.cp
.x
);
385 double y2
= cur_edge
.cp
.y
+ one_third
* (cur_edge
.ap
.y
- cur_edge
.cp
.y
);
390 snap_to_half_pixel(_cr
, x1
, y1
);
391 snap_to_half_pixel(_cr
, x2
, y2
);
392 snap_to_half_pixel(_cr
, x
, y
);
394 cairo_curve_to(_cr
, x1
, y1
, x2
, y2
, x
, y
);
398 void lineTo(const point
& ap
)
402 snap_to_half_pixel(_cr
, x
, y
);
404 cairo_line_to(_cr
, x
, y
);
408 Renderer_cairo
& _renderer
;
410 cairo_pattern_t
* _pattern
;
411 const std::vector
<FillStyle
>& _FillStyles
;
417 /// Transforms the current Cairo SWFMatrix using the given SWFMatrix.
418 /// When it goes out of scope, the SWFMatrix will be reset to what it
419 /// was before the new SWFMatrix was applied.
420 class CairoScopeMatrix
: public boost::noncopyable
423 CairoScopeMatrix(cairo_t
* cr
, const SWFMatrix
& new_mat
)
426 cairo_get_matrix(_cr
, &old_mat
);
429 init_cairo_matrix(&tmp
, new_mat
);
430 cairo_transform(_cr
, &tmp
);
435 cairo_set_matrix(_cr
, &old_mat
);
440 cairo_matrix_t old_mat
;
444 /// Implementation of Renderer_cairo class
445 Renderer_cairo::Renderer_cairo()
449 _cr
= cairo_create(NULL
);
450 cairo_matrix_init_scale(&_stage_mat
, 1/20.0f
, 1/20.0f
);
453 Renderer_cairo::~Renderer_cairo()
459 Renderer_cairo::createCachedBitmap(std::auto_ptr
<image::GnashImage
> im
)
461 int buf_size
= im
->width() * im
->height() * 4;
462 boost::uint8_t* buffer
= new boost::uint8_t[buf_size
];
466 case image::TYPE_RGB
:
468 rgb_to_cairo_rgb24(buffer
, im
.get());
470 return new bitmap_info_cairo(buffer
, im
->width(), im
->height(), 4,
474 case image::TYPE_RGBA
:
476 rgba_to_cairo_argb(buffer
, im
.get());
478 return new bitmap_info_cairo(buffer
, im
->width(), im
->height(), 4,
479 CAIRO_FORMAT_ARGB32
);
488 Renderer_cairo::drawVideoFrame(image::GnashImage
* baseframe
, const Transform
& xform
,
489 const SWFRect
* bounds
, bool /*smooth*/)
492 if (baseframe
->type() == image::TYPE_RGBA
)
494 LOG_ONCE(log_error(_("Can't render videos with alpha")));
498 image::ImageRGB
* frame
= dynamic_cast<image::ImageRGB
*>(baseframe
);
502 // Extract frame attributes
503 int w
= frame
->width();
504 int h
= frame
->height();
506 // Compute bounding rectangle size relative to video object
507 double w_scale
= bounds
->width() / w
;
508 double h_scale
= bounds
->height() / h
;
510 // Fit video to bounding rectangle
512 cairo_matrix_init_scale(&mat
, w_scale
, h_scale
);
513 cairo_matrix_translate(&mat
, bounds
->get_x_min(), bounds
->get_y_min());
515 // Now apply transformation to video
516 cairo_matrix_t frame_mat
;
517 init_cairo_matrix(&frame_mat
, xform
.matrix
);
519 cairo_matrix_multiply(&mat
, &mat
, &frame_mat
);
521 // Inverse the SWFMatrix for pattern space
522 cairo_matrix_invert(&mat
);
524 // Convert RGB frame to cairo format
525 size_t buf_size
= w
* h
* 4;
527 if (_video_bufsize
< buf_size
) {
528 _video_buffer
.reset(new boost::uint8_t[buf_size
]);
529 _video_bufsize
= buf_size
;
532 rgb_to_cairo_rgb24(_video_buffer
.get(), frame
);
534 // Create a pattern from the the RGB frame
535 cairo_surface_t
* surface
= cairo_image_surface_create_for_data(
536 _video_buffer
.get(), CAIRO_FORMAT_RGB24
, w
, h
, w
* 4);
537 cairo_pattern_t
* pattern
= cairo_pattern_create_for_surface(surface
);
538 cairo_pattern_set_extend(pattern
, CAIRO_EXTEND_NONE
);
539 cairo_pattern_set_matrix(pattern
, &mat
);
541 // Draw the frame now
543 cairo_set_source(_cr
, pattern
);
545 geometry::Range2d
<boost::int32_t> range
= bounds
->getRange();
546 xform
.matrix
.transform(range
);
548 cairo_rectangle(_cr
, range
.getMinX(), range
.getMinY(), range
.width(),
555 cairo_pattern_destroy(pattern
);
556 cairo_surface_destroy(surface
);
560 geometry::Range2d
<int>
561 Renderer_cairo::world_to_pixel(const SWFRect
& worldbounds
)
563 double xmin
= worldbounds
.get_x_min(),
564 ymin
= worldbounds
.get_y_min(),
565 xmax
= worldbounds
.get_x_max(),
566 ymax
= worldbounds
.get_y_max();
568 cairo_matrix_transform_point(&_stage_mat
, &xmin
, &ymin
);
569 cairo_matrix_transform_point(&_stage_mat
, &xmax
, &ymax
);
571 geometry::Range2d
<int> ret(xmin
, ymin
, xmax
, ymax
);
577 Renderer_cairo::pixel_to_world(int x
, int y
)
579 cairo_matrix_t inv_stage
= _stage_mat
;
580 cairo_matrix_invert(&inv_stage
);
585 cairo_matrix_transform_point(&inv_stage
, &xconv
, &yconv
);
586 return point(xconv
, yconv
);
590 Renderer_cairo::set_color(const rgba
& c
)
592 cairo_set_source_rgba (_cr
, c
.m_r
/ 255.0, c
.m_g
/ 255.0,
593 c
.m_b
/ 255.0, c
.m_a
/ 255.0);
597 Renderer_cairo::set_invalidated_regions(const InvalidatedRanges
& ranges
)
599 _invalidated_ranges
= ranges
;
603 Renderer_cairo::begin_display(const rgba
& bg_color
,
604 int /*viewport_width*/, int /*viewport_height*/,
605 float /*x0*/, float /*x1*/, float /*y0*/, float /*y1*/)
607 cairo_identity_matrix(_cr
);
616 for (size_t rno
=0; rno
< _invalidated_ranges
.size(); rno
++) {
617 const geometry::Range2d
<boost::int32_t>& range
=
618 _invalidated_ranges
.getRange(rno
);
619 if (range
.isNull()) {
622 if (range
.isWorld()) {
624 // reset any rectangles that might have been added to the path...
626 cairo_set_matrix(_cr
, &_stage_mat
);
630 double x
= range
.getMinX(),
632 maxx
= range
.getMaxX(),
633 maxy
= range
.getMaxY();
635 // Transform to pixels.
636 cairo_matrix_transform_point(&_stage_mat
, &x
, &y
);
637 cairo_matrix_transform_point(&_stage_mat
, &maxx
, &maxy
);
639 cairo_rectangle(_cr
, rint(x
), rint(y
), rint(maxx
- x
), rint(maxy
- y
));
644 // Paint the background color over the clipped region(s).
647 cairo_set_matrix(_cr
, &_stage_mat
);
651 Renderer_cairo::end_display()
657 Renderer_cairo::set_scale(float xscale
, float yscale
)
659 _stage_mat
.xx
= xscale
/ 20;
660 _stage_mat
.yy
= yscale
/ 20;
664 Renderer_cairo::set_translation(float xoff
, float yoff
)
666 _stage_mat
.x0
= xoff
;
667 _stage_mat
.y0
= yoff
;
671 Renderer_cairo::drawLine(const std::vector
<point
>& coords
,
672 const rgba
& color
, const SWFMatrix
& mat
)
674 if (coords
.empty()) return;
676 CairoScopeMatrix
mat_transformer(_cr
, mat
);
678 std::vector
<point
>::const_iterator i
= coords
.begin();
680 double x
= i
->x
, y
= i
->y
;
681 snap_to_half_pixel(_cr
, x
, y
);
683 cairo_move_to(_cr
, x
, y
);
685 for (std::vector
<point
>::const_iterator e
= coords
.end(); i
!= e
; ++i
) {
686 double x
= i
->x
, y
= i
->y
;
687 snap_to_half_pixel(_cr
, x
, y
);
688 cairo_line_to(_cr
, x
, y
);
692 cairo_set_line_cap(_cr
, CAIRO_LINE_CAP_ROUND
);
693 cairo_set_line_join(_cr
, CAIRO_LINE_JOIN_ROUND
);
697 cairo_device_to_user_distance(_cr
, &hwidth
, &hwidth
);
698 cairo_set_line_width(_cr
, hwidth
);
704 Renderer_cairo::draw_poly(const point
* corners
, size_t corner_count
,
705 const rgba
& fill
, const rgba
& outline
,
706 const SWFMatrix
& mat
, bool /*masked*/)
708 CairoScopeMatrix
mat_transformer(_cr
, mat
);
709 cairo_transform(_cr
, &_stage_mat
);
711 if (corner_count
< 1) {
715 cairo_move_to(_cr
, corners
[0].x
, corners
[0].y
);
717 for (size_t i
= 0; i
< corner_count
; ++i
) {
718 cairo_line_to(_cr
, corners
[i
].x
, corners
[i
].y
);
721 cairo_close_path(_cr
);
725 cairo_fill_preserve(_cr
);
731 // FIXME: coordinate alignment (for single-pixel lines should be in
732 // between two pixels for sharp hair line.
734 cairo_set_line_width(_cr
, 1.0);
735 cairo_stroke_preserve(_cr
);
738 // Clear the current path which was _preserve()d.
743 Renderer_cairo::set_antialiased(bool /*enable*/)
745 log_unimpl("set_antialiased");
749 Renderer_cairo::begin_submit_mask()
752 _masks
.push_back(mask
);
754 _drawing_mask
= true;
758 Renderer_cairo::end_submit_mask()
760 _drawing_mask
= false;
762 // Load the mask paths into the cairo context.
763 add_paths(_masks
.back());
765 // Save the context so we can return to the former clip later.
768 // Clip the fills defined by the current paths.
771 // Remove the current path since we have no further use for it (and may
772 // confuse us later).
777 Renderer_cairo::disable_mask()
779 // Restore the previous clip.
786 Renderer_cairo::add_path(cairo_t
* cr
, const Path
& cur_path
)
788 double x
= cur_path
.ap
.x
;
789 double y
= cur_path
.ap
.y
;
791 snap_to_half_pixel(cr
, x
, y
);
792 cairo_move_to(cr
, x
, y
);
794 for (std::vector
<Edge
>::const_iterator it
= cur_path
.m_edges
.begin(),
795 end
= cur_path
.m_edges
.end(); it
!= end
; ++it
) {
796 const Edge
& cur_edge
= *it
;
798 if (cur_edge
.straight()) {
801 snap_to_half_pixel(cr
, x
, y
);
802 cairo_line_to(cr
, x
, y
);
804 // Cairo expects a cubic Bezier curve, while Flash gives us a
805 // quadratic one. We must apply a conversion:
807 const float two_thirds
= 2.0/3.0;
808 const float one_third
= 1 - two_thirds
;
810 double x1
= x
+ two_thirds
* (cur_edge
.cp
.x
- x
);
811 double y1
= y
+ two_thirds
* (cur_edge
.cp
.y
- y
);
813 double x2
= cur_edge
.cp
.x
814 + one_third
* (cur_edge
.ap
.x
- cur_edge
.cp
.x
);
815 double y2
= cur_edge
.cp
.y
816 + one_third
* (cur_edge
.ap
.y
- cur_edge
.cp
.y
);
821 snap_to_half_pixel(cr
, x1
, y1
);
822 snap_to_half_pixel(cr
, x2
, y2
);
823 snap_to_half_pixel(cr
, x
, y
);
825 cairo_curve_to(cr
, x1
, y1
, x2
, y2
, x
, y
);
831 Renderer_cairo::apply_line_style(const LineStyle
& style
, const SWFCxForm
& cx
,
832 const SWFMatrix
& /*mat*/)
834 cairo_line_join_t join_style
= CAIRO_LINE_JOIN_MITER
;
835 switch (style
.joinStyle()) {
837 join_style
= CAIRO_LINE_JOIN_ROUND
;
840 join_style
= CAIRO_LINE_JOIN_BEVEL
;
845 log_unimpl("join style");
847 cairo_set_line_join(_cr
, join_style
);
849 if (style
.startCapStyle() != style
.endCapStyle()) {
850 log_unimpl("differing start and end cap styles");
853 cairo_line_cap_t cap_style
= CAIRO_LINE_CAP_ROUND
;
854 switch(style
.startCapStyle()) {
858 cap_style
= CAIRO_LINE_CAP_BUTT
;
861 cap_style
= CAIRO_LINE_CAP_SQUARE
;
864 log_unimpl("cap style");
867 cairo_set_line_cap(_cr
, cap_style
);
869 // TODO: test that this is correct.
870 cairo_set_miter_limit(_cr
, style
.miterLimitFactor());
872 float width
= style
.getThickness();
874 if ( width
== 0.0 ) {
876 cairo_matrix_t inv_stage
= _stage_mat
;
877 cairo_matrix_invert(&inv_stage
);
882 cairo_matrix_transform_distance(&inv_stage
, &xconv
, &yconv
);
884 cairo_set_line_width(_cr
, xconv
);
886 // TODO: this is correct for !style.scaleThicknessVertically()
887 // and !style.scaleThicknessHorizontally().
888 // If that's not the case, we should scale the thickness
889 // togheter with the shapes.
890 if (style
.scaleThicknessVertically() ||
891 style
.scaleThicknessHorizontally()) {
892 LOG_ONCE( log_unimpl(_("Scaled strokes in Cairo renderer")) );
895 cairo_set_line_width(_cr
, width
);
898 rgba color
= cx
.transform(style
.get_color());
903 Renderer_cairo::draw_outlines(const PathVec
& path_vec
,
904 const std::vector
<LineStyle
>& line_styles
,
906 const SWFMatrix
& mat
)
908 for (PathVec::const_iterator it
= path_vec
.begin(), end
= path_vec
.end();
910 const Path
& cur_path
= *it
;
912 if (!cur_path
.m_line
) {
916 apply_line_style(line_styles
[cur_path
.m_line
-1], cx
, mat
);
917 add_path(_cr
, cur_path
);
923 Renderer_cairo::draw_subshape(const PathVec
& path_vec
, const SWFMatrix
& mat
,
925 const std::vector
<FillStyle
>& FillStyles
,
926 const std::vector
<LineStyle
>& line_styles
)
928 CairoPathRunner
runner(*this, path_vec
, FillStyles
, _cr
);
931 draw_outlines(path_vec
, line_styles
, cx
, mat
);
935 std::vector
<PathVec::const_iterator
>
936 Renderer_cairo::find_subshapes(const PathVec
& path_vec
)
938 std::vector
<PathVec::const_iterator
> subshapes
;
940 PathVec::const_iterator it
= path_vec
.begin();
941 PathVec::const_iterator end
= path_vec
.end();
943 subshapes
.push_back(it
);
946 for (;it
!= end
; ++it
) {
947 const Path
& cur_path
= *it
;
949 if (cur_path
.m_new_shape
) {
950 subshapes
.push_back(it
);
954 subshapes
.push_back(end
);
960 Renderer_cairo::draw_mask(const PathVec
& path_vec
)
962 for (PathVec::const_iterator it
= path_vec
.begin(), end
= path_vec
.end();
964 const Path
& cur_path
= *it
;
966 if (cur_path
.m_fill0
|| cur_path
.m_fill1
) {
967 _masks
.back().push_back(cur_path
);
973 Renderer_cairo::add_paths(const PathVec
& path_vec
)
975 for (PathVec::const_iterator it
= path_vec
.begin(), end
= path_vec
.end();
977 const Path
& cur_path
= *it
;
979 add_path(_cr
, cur_path
);
983 /// Takes a path and translates it using the given SWFMatrix.
985 Renderer_cairo::apply_matrix_to_paths(std::vector
<Path
>& paths
,
986 const SWFMatrix
& mat
)
988 std::for_each(paths
.begin(), paths
.end(),
989 boost::bind(&Path::transform
, _1
, boost::ref(mat
)));
993 Renderer_cairo::drawShape(const SWF::ShapeRecord
& shape
, const Transform
& xform
)
995 const PathVec
& path_vec
= shape
.paths();
997 if (!path_vec
.size()) {
1001 cairo_set_fill_rule(_cr
, CAIRO_FILL_RULE_EVEN_ODD
); // TODO: Move to init
1003 if (_drawing_mask
) {
1004 PathVec scaled_path_vec
= path_vec
;
1006 apply_matrix_to_paths(scaled_path_vec
, xform
.matrix
);
1007 draw_mask(scaled_path_vec
);
1011 CairoScopeMatrix
mat_transformer(_cr
, xform
.matrix
);
1013 std::vector
<PathVec::const_iterator
> subshapes
= find_subshapes(path_vec
);
1015 const std::vector
<FillStyle
>& FillStyles
= shape
.fillStyles();
1016 const std::vector
<LineStyle
>& line_styles
= shape
.lineStyles();
1018 for (size_t i
= 0; i
< subshapes
.size()-1; ++i
) {
1019 PathVec subshape_paths
;
1021 if (subshapes
[i
] != subshapes
[i
+1]) {
1022 subshape_paths
= PathVec(subshapes
[i
], subshapes
[i
+1]);
1024 subshape_paths
.push_back(*subshapes
[i
]);
1027 draw_subshape(subshape_paths
, xform
.matrix
, xform
.colorTransform
,
1028 FillStyles
, line_styles
);
1033 Renderer_cairo::drawGlyph(const SWF::ShapeRecord
& rec
, const rgba
& color
,
1034 const SWFMatrix
& mat
)
1037 std::vector
<FillStyle
> glyph_fs
;
1039 FillStyle coloring
= FillStyle(SolidFill(color
));
1041 glyph_fs
.push_back(coloring
);
1043 const PathVec
& path_vec
= rec
.paths();
1045 std::vector
<LineStyle
> dummy_ls
;
1047 CairoScopeMatrix
mat_transformer(_cr
, mat
);
1049 draw_subshape(path_vec
, mat
, dummy_cx
, glyph_fs
, dummy_ls
);
1053 Renderer_cairo::set_context(cairo_t
* context
)
1055 if (context
== _cr
) {
1064 Renderer_cairo::initTestBuffer(unsigned width
, unsigned height
)
1066 cairo_surface_t
* test_surface
=
1067 cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
1069 if (cairo_surface_status(test_surface
) != CAIRO_STATUS_SUCCESS
) {
1073 cairo_t
* context
= cairo_create(test_surface
);
1074 if (cairo_status(context
) == CAIRO_STATUS_NO_MEMORY
) {
1078 cairo_surface_destroy(test_surface
);
1079 set_context(context
);
1085 Renderer_cairo::getBitsPerPixel() const
1087 cairo_surface_t
* surface
= cairo_get_target (_cr
);
1088 cairo_format_t format
= cairo_image_surface_get_format (surface
);
1091 case CAIRO_FORMAT_ARGB32
:
1093 case CAIRO_FORMAT_RGB24
:
1094 // In practice this is 32 with 8 bits unused...
1096 case CAIRO_FORMAT_A8
:
1098 case CAIRO_FORMAT_A1
:
1106 Renderer_cairo::getPixel(rgba
& color_return
, int x
, int y
) const
1108 if (x
< 0 || y
< 0) {
1112 cairo_surface_t
* surface
= cairo_get_target (_cr
);
1114 assert(cairo_image_surface_get_format (surface
) == CAIRO_FORMAT_ARGB32
);
1116 unsigned char* data
= cairo_image_surface_get_data (surface
);
1117 int width
= cairo_image_surface_get_width(surface
);
1118 int height
= cairo_image_surface_get_height(surface
);
1119 int stride
= cairo_image_surface_get_stride(surface
); // in bytes
1121 if (x
>= width
|| y
>= height
) {
1125 unsigned char* ptr
= data
+ y
* stride
+ x
* 4;
1127 color_return
.m_a
= ptr
[3];
1128 color_return
.m_r
= ptr
[2];
1129 color_return
.m_g
= ptr
[1];
1130 color_return
.m_b
= ptr
[0];
1138 pattern_add_color_stops(const GradientFill
& f
, cairo_pattern_t
* pattern
,
1139 const SWFCxForm
& cx
)
1141 for (size_t index
= 0; index
< f
.recordCount(); ++index
) {
1142 const GradientRecord
& grad
= f
.record(index
);
1144 rgba c
= cx
.transform(grad
.color
);
1146 cairo_pattern_add_color_stop_rgba (pattern
,
1147 grad
.ratio
/ 255.0, c
.m_r
/ 255.0, c
.m_g
/ 255.0,
1148 c
.m_b
/ 255.0, c
.m_a
/ 255.0);
1153 init_cairo_matrix(cairo_matrix_t
* cairo_matrix
, const SWFMatrix
& gnash_matrix
)
1155 cairo_matrix_init(cairo_matrix
,
1156 gnash_matrix
.sx
/65536.0, gnash_matrix
.shx
/65536.0,
1157 gnash_matrix
.shy
/65536.0, gnash_matrix
.sy
/65536.0,
1158 gnash_matrix
.tx
, gnash_matrix
.ty
);
1165 namespace renderer
{
1172 return new Renderer_cairo();
1176 set_context(Renderer
* handler
, cairo_t
* context
)
1178 Renderer_cairo
* cairo_handler
= static_cast<Renderer_cairo
*>(handler
);
1179 cairo_handler
->set_context(context
);
1183 } // namespace cairo
1184 } // namespace renderer
1185 } // namespace gnash