1 % Copyright
2012 Taco Hoekwater.
3 % This program is free software
: you can redistribute it and
/or modify
4 % it under the terms of the GNU Lesser General Public License as published by
5 % the Free Software Foundation
, either version
3 of the License
, or
6 % (at your option
) any later version.
8 % This program is distributed in the hope that it will be useful
,
9 % but WITHOUT
ANY WARRANTY
; without even the implied warranty of
10 % MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the
11 % GNU Lesser General Public License for more details.
13 % You should have received a copy of the GNU Lesser General Public License
14 % along with this program. If not
, see
<http
://www.gnu.org
/licenses
/>.
16 % TeX is a trademark of the American Mathematical Society.
17 % METAFONT is a trademark of Addison-Wesley Publishing Company.
18 % PostScript is a trademark of Adobe Systems Incorporated.
20 % Here is TeX material that gets inserted after \input webmac
22 \font\tenlogo
=logo10
% font used for the METAFONT logo
24 \def\MF
{{\tenlogo META
}\
-{\tenlogo
FONT}}
25 \def\MP
{{\tenlogo META
}\
-{\tenlogo POST
}}
26 \def\
<#
1>{$\langle#
1\rangle$
}
27 \def\section
{\mathhexbox278
}
28 \def\
[#
1]{} % from pascal web
29 \def\
(#
1){} % this is used to make section names sort themselves better
30 \def\
9#
1{} % this is used for sort keys in the index via @@
:sort key
}{entry@@
>
32 \def\title
{MetaPost PNG output
}
33 \def\topofcontents
{\hsize
5.5in
34 \vglue
-30pt plus
1fil minus
1.5in
35 \def\?##
1]{\hbox to
1in
{\hfil##
1.\
}}
37 \def\botofcontents
{\vskip
0pt plus
1fil minus
1.5in
}
42 @d zero_t
((math_data
*)mp-
>math
)->zero_t
43 @d number_zero
(A
) (((math_data
*)(mp-
>math
))->equal
)(A
,zero_t
)
44 @d number_greater
(A
,B
) (((math_data
*)(mp-
>math
))->greater
)(A
,B
)
45 @d number_positive
(A
) number_greater
(A
, zero_t
)
46 @d number_to_scaled
(A
) (((math_data
*)(mp-
>math
))->to_scaled
)(A
)
47 @d round_unscaled
(A
) (((math_data
*)(mp-
>math
))->round_unscaled
)(A
)
53 @d incr
(A
) (A
)=(A
)+1 /* increase a variable by unity
*/
54 @d decr
(A
) (A
)=(A
)-1 /* decrease a variable by unity
*/
55 @d negate
(A
) (A
)=-(A
) /* change the sign of a variable
*/
58 #include
<w2c
/config.h
>
64 #include
"mplibps.h" /* external header
*/
65 #include
"mplibpng.h" /* external header
*/
66 #include
"mpmp.h" /* internal header
*/
67 #include
"mppsout.h" /* internal header
*/
68 #include
"mppngout.h" /* internal header
*/
69 #include
"mpmath.h" /* internal header
*/
71 @
<Types in the outer block@
>
74 @ There is a small bit of code from the backend that bleads through
75 to the frontend because I do not know how to set up the includes
76 properly. That is |typedef struct pngout_data_struct
* pngout_data|.
82 #define PNG_SKIP_SETJMP_CHECK
1
87 typedef struct pngout_data_struct
{
89 } pngout_data_struct
;
90 @
<Exported function headers@
>
93 @ @
<Exported function headers@
>=
94 void mp_png_backend_initialize
(MP mp
) ;
95 void mp_png_backend_free
(MP mp
) ;
98 void mp_png_backend_initialize
(MP mp
) {
99 mp-
>png
= mp_xmalloc
(mp
,1,sizeof
(pngout_data_struct
));
100 memset
(mp-
>png
,0,sizeof
(pngout_data_struct
));
102 void mp_png_backend_free
(MP mp
) {
107 @ Writing to PNG files
110 cairo_surface_t
*surface
;
113 @ We often need to print a pair of coordinates
, and these need to
114 offset so that all coordinates are positive.
122 static void mp_png_start
(MP mp
,mp_edge_object
*hh
, double hppp
, double vppp
, int colormodel
, int antialias
);
126 void mp_png_start
(MP mp
,mp_edge_object
*hh
, double hppp
, double vppp
, int colormodel
, int antialias
) {
128 if
( hh-
>minx
>hh-
>maxx
) {
134 w
= (ceil
(hh-
>maxx
) - floor
(hh-
>minx
)) / hppp
;
135 h
= (ceil
(hh-
>maxy
) - floor
(hh-
>miny
)) / vppp
;
136 mp-
>png-
>dx
= -floor
(hh-
>minx
);
137 mp-
>png-
>dy
= -floor
(hh-
>miny
);
139 mp-
>png-
>surface
= cairo_image_surface_create
(CAIRO_FORMAT_ARGB32
, w
, h
);
140 mp-
>png-
>cr
= cairo_create
(mp-
>png-
>surface
);
141 /* if there is no alpha channel
, a white background is needed
*/
142 if
(colormodel
== PNG_COLOR_TYPE_RGB ||
143 colormodel
== PNG_COLOR_TYPE_GRAY
) {
144 cairo_save
(mp-
>png-
>cr
);
145 cairo_set_source_rgb
(mp-
>png-
>cr
, 1.0, 1.0, 1.0);
146 cairo_rectangle
(mp-
>png-
>cr
, 0, 0, w
, h
);
147 cairo_fill
(mp-
>png-
>cr
);
148 cairo_restore
(mp-
>png-
>cr
);
150 cairo_scale
(mp-
>png-
>cr
, 1/hppp
, -1/vppp
);
151 cairo_translate
(mp-
>png-
>cr
, 0, -(h
*vppp
));
152 cairo_translate
(mp-
>png-
>cr
, mp-
>png-
>dx
, mp-
>png-
>dy
);
153 cairo_set_antialias
(mp-
>png-
>cr
, antialias
);
156 @ Outputting a color specification.
158 @d set_color_objects
(pq
)
159 object_color_model
= pq-
>color_model
;
160 object_color_a
= pq-
>color.a_val
;
161 object_color_b
= pq-
>color.b_val
;
162 object_color_c
= pq-
>color.c_val
;
163 object_color_d
= pq-
>color.d_val
;
166 static void mp_png_color_out
(MP mp
, mp_graphic_object
*p
) {
167 int object_color_model
;
168 double object_color_a
, object_color_b
, object_color_c
, object_color_d
;
169 if
(gr_type
(p
) == mp_fill_code
) {
170 mp_fill_object
*pq
= (mp_fill_object
*)p
;
171 set_color_objects
(pq
);
172 } else if
(gr_type
(p
) == mp_stroked_code
) {
173 mp_stroked_object
*pq
= (mp_stroked_object
*)p
;
174 set_color_objects
(pq
);
176 mp_text_object
*pq
= (mp_text_object
*)p
;
177 set_color_objects
(pq
);
179 if
( object_color_model
==mp_no_model
) {
180 cairo_set_source_rgb
(mp-
>png-
>cr
, 0, 0, 0);
182 if
( object_color_model
==mp_grey_model
) {
183 object_color_b
= object_color_a
;
184 object_color_c
= object_color_a
;
185 } else if
( object_color_model
==mp_cmyk_model
) {
191 object_color_a
= unity
- (c
+k
>unity ? unity
: c
+k
);
192 object_color_b
= unity
- (m
+k
>unity ? unity
: m
+k
);
193 object_color_c
= unity
- (y
+k
>unity ? unity
: y
+k
);
195 cairo_set_source_rgb
(mp-
>png-
>cr
, object_color_a
, object_color_b
, object_color_c
);
200 static void mp_png_color_out
(MP mp
, mp_graphic_object
*p
);
202 @ This is the information that comes from a pen
205 typedef struct mp_pen_info
{
207 double sx
, rx
, ry
, sy
;
212 @
(Re
)discover the characteristics of an elliptical pen
215 mp_pen_info
*mp_png_pen_info
(MP mp
, mp_gr_knot pp
, mp_gr_knot p
);
217 @ The next two constants come from the original web source.
218 Together with the two helper functions
, they will tell whether
219 the |x| or the |y| direction of the path is the most important
221 @d aspect_bound
(10/65536.0)
225 static double coord_range_x
(mp_gr_knot h
, double dz
) {
227 double zlo
= 0, zhi
= 0;
231 if
(z
< zlo
) zlo
= z
; else if
(z
> zhi
) zhi
= z
;
233 if
(z
< zlo
) zlo
= z
; else if
(z
> zhi
) zhi
= z
;
235 if
(z
< zlo
) zlo
= z
; else if
(z
> zhi
) zhi
= z
;
240 return
(zhi
- zlo
<= dz ? aspect_bound
: aspect_default
);
242 static double coord_range_y
(mp_gr_knot h
, double dz
) {
244 double zlo
= 0, zhi
= 0;
248 if
(z
< zlo
) zlo
= z
; else if
(z
> zhi
) zhi
= z
;
250 if
(z
< zlo
) zlo
= z
; else if
(z
> zhi
) zhi
= z
;
252 if
(z
< zlo
) zlo
= z
; else if
(z
> zhi
) zhi
= z
;
257 return
(zhi
- zlo
<= dz ? aspect_bound
: aspect_default
);
262 mp_pen_info
*mp_png_pen_info
(MP mp
, mp_gr_knot pp
, mp_gr_knot p
) {
263 double wx
, wy
; /* temporary pen widths
, in either direction
*/
264 struct mp_pen_info
*pen
; /* return structure
*/
267 pen
= mp_xmalloc
(mp
, 1, sizeof
(mp_pen_info
));
271 if
((gr_right_x
(p
) == gr_x_coord
(p
))
273 (gr_left_y
(p
) == gr_y_coord
(p
))) {
274 wx
= fabs
(gr_left_x
(p
) - gr_x_coord
(p
));
275 wy
= fabs
(gr_right_y
(p
) - gr_y_coord
(p
));
278 arg1
= gr_left_x
(p
)-gr_x_coord
(p
);
279 arg2
= gr_right_x
(p
)-gr_x_coord
(p
);
280 wx
= sqrt
(arg1
*arg1
+ arg2
*arg2
);
281 arg1
= gr_left_y
(p
)-gr_y_coord
(p
);
282 arg2
= gr_right_y
(p
)-gr_y_coord
(p
);
283 wy
= sqrt
(arg1
*arg1
+ arg2
*arg2
);
285 if
((wy
/coord_range_x
(pp
, wx
)) >= (wx
/coord_range_y
(pp
, wy
)))
289 pen-
>tx
= gr_x_coord
(p
);
290 pen-
>ty
= gr_y_coord
(p
);
291 pen-
>sx
= gr_left_x
(p
) - pen-
>tx
;
292 pen-
>rx
= gr_left_y
(p
) - pen-
>ty
;
293 pen-
>ry
= gr_right_x
(p
) - pen-
>tx
;
294 pen-
>sy
= gr_right_y
(p
) - pen-
>ty
;
295 if
(pen-
>ww
!= unity
) {
300 pen-
>rx
= pen-
>rx
/ pen-
>ww
;
301 pen-
>ry
= pen-
>ry
/ pen-
>ww
;
302 pen-
>sx
= pen-
>sx
/ pen-
>ww
;
303 pen-
>sy
= pen-
>sy
/ pen-
>ww
;
309 @ Two types of straight lines come up often in \MP\ paths
:
310 cubics with zero initial and final velocity as created by |make_path| or
311 |make_envelope|
, and cubics with control points uniformly spaced on a line
312 as created by |make_choices|.
315 static boolean mp_is_curved
(mp_gr_knot p
, mp_gr_knot q
) ;
319 @d bend_tolerance
(131/65536.0) /* allow rounding error of $
2\cdot10^
{-3}$
*/
322 boolean mp_is_curved
(mp_gr_knot p
, mp_gr_knot q
) {
323 double d
; /* a temporary value
*/
324 if
( gr_right_x
(p
)==gr_x_coord
(p
) )
325 if
( gr_right_y
(p
)==gr_y_coord
(p
) )
326 if
( gr_left_x
(q
)==gr_x_coord
(q
) )
327 if
( gr_left_y
(q
)==gr_y_coord
(q
) )
329 d
=gr_left_x
(q
)-gr_right_x
(p
);
330 if
( fabs
(gr_right_x
(p
)-gr_x_coord
(p
)-d
)<=bend_tolerance
)
331 if
( fabs
(gr_x_coord
(q
)-gr_left_x
(q
)-d
)<=bend_tolerance
) {
332 d
=gr_left_y
(q
)-gr_right_y
(p
);
333 if
( fabs
(gr_right_y
(p
)-gr_y_coord
(p
)-d
)<=bend_tolerance
)
334 if
( fabs
(gr_y_coord
(q
)-gr_left_y
(q
)-d
)<=bend_tolerance
)
341 @ Cairo does not want to draw a path that consists of only a moveto
,
342 so make sure there is some kind of line even for single-pair paths.
344 static void mp_png_path_out
(MP mp
, mp_gr_knot h
) {
345 mp_gr_knot p
, q
; /* for scanning the path
*/
347 cairo_move_to
(mp-
>png-
>cr
, gr_x_coord
(h
),gr_y_coord
(h
));
350 if
( gr_right_type
(p
)==mp_endpoint
) {
352 cairo_line_to
(mp-
>png-
>cr
, gr_x_coord
(p
),gr_y_coord
(p
));
357 if
(mp_is_curved
(p
, q
)){
358 cairo_curve_to
(mp-
>png-
>cr
, gr_right_x
(p
),gr_right_y
(p
),
359 gr_left_x
(q
),gr_left_y
(q
),
360 gr_x_coord
(q
),gr_y_coord
(q
));
362 cairo_line_to
(mp-
>png-
>cr
, gr_x_coord
(q
),gr_y_coord
(q
));
367 if
((gr_x_coord
(p
) == gr_x_coord
(h
)) && (gr_y_coord(p) == gr_y_coord(h)) &&
368 gr_right_type
(p
)!=mp_endpoint
) {
369 cairo_close_path
(mp-
>png-
>cr
);
373 @ Now for outputting the actual graphic objects.
376 static double mp_png_choose_scale
(MP mp
, mp_graphic_object
*p
) ;
378 @ @c double mp_png_choose_scale
(MP mp
, mp_graphic_object
*p
) {
379 /* |p| should point to a text node
*/
380 double a
,b
,c
,d
,ad
,bc
; /* temporary values
*/
386 if
( a
<0 ) negate
(a
);
387 if
( b
<0 ) negate
(b
);
388 if
( c
<0 ) negate
(c
);
389 if
( d
<0 ) negate
(d
);
392 ret1
= sqrt
((d
+ad
)*(d
+ad
) + ad
*ad
);
393 ret2
= sqrt
((c
+bc
)*(c
+bc
) + bc
*bc
);
394 return sqrt
(ret1
*ret1
+ ret2
*ret2
);
398 @d xrealloc
(P
,A
,B
) mp_xrealloc
(mp
,P
,(size_t
)A
,B
)
399 @d XREALLOC
(a
,b
,c
) a
= xrealloc
(a
,(b
+1),sizeof
(c
));
402 void mp_reallocate_psfonts
(MP mp
, int l
) {
403 if
(l
>=mp-
>png-
>font_max
) {
405 mp-
>png-
>last_fnum
= mp-
>png-
>font_max
;
406 XREALLOC
(mp-
>png-
>psfonts
, l
, mp_edge_object
*);
407 for
(f
= (mp-
>png-
>last_fnum
+ 1); f
<= l
; f
++) {
408 mp-
>png-
>psfonts
[f
] = NULL;
410 mp-
>png-
>font_max
= l
;
415 void mp_reallocate_psfonts
(MP mp
, int l
);
418 mp_edge_object
**psfonts
;
423 static void mp_png_text_out
(MP mp
, mp_text_object
*p
) ;
426 void mp_png_text_out
(MP mp
, mp_text_object
*p
) {
427 double ds
; /* design size and scale factor for a text node
*/
428 unsigned char
*s
= (unsigned char
*)gr_text_p
(p
);
429 size_t l
= gr_text_l
(p
); /* string length
*/
430 boolean transformed
= (gr_txx_val
(p
)!=unity
)||
(gr_tyy_val
(p
)!=unity
)||
431 (gr_txy_val
(p
)!=0)||
(gr_tyx_val
(p
)!=0);
432 int fn
= gr_font_n
(p
);
435 ds
=(mp-
>font_dsize
[fn
]+8) / (16*65536.0);
436 scf
= mp_png_choose_scale
(mp
,(mp_graphic_object
*)p
);
437 cairo_save
(mp-
>png-
>cr
);
439 cairo_matrix_t matrix
= {0,0,0,0,0,0};
440 cairo_matrix_init
(&matrix, (gr_txx_val(p)/scf), (gr_tyx_val(p)/scf),
441 (gr_txy_val
(p
)/scf
), (gr_tyy_val
(p
)/scf
),
442 gr_tx_val
(p
),gr_ty_val
(p
));
443 cairo_transform
(mp-
>png-
>cr
, &matrix);
444 cairo_move_to
(mp-
>png-
>cr
, 0, 0);
446 cairo_translate
(mp-
>png-
>cr
,gr_tx_val
(p
),gr_ty_val
(p
));
448 cairo_scale
(mp-
>png-
>cr
, ((ds
/1000.0)*scf
), ((ds
/1000.0)*scf
));
449 mp_png_color_out
(mp
,(mp_graphic_object
*)p
);
453 double wd
= 0.0; /* this is in PS design units
*/
454 mp_reallocate_psfonts
(mp
, ((fn
+1) * 256));
455 ch
= mp-
>png-
>psfonts
[(fn
*256)+k
];
457 f
= mp_ps_font_parse
(mp
, fn
);
458 if
(f
== NULL) return
;
459 ch
= mp_ps_font_charstring
(mp
,f
,k
);
460 mp-
>png-
>psfonts
[(fn
*256)+k
] = ch
;
463 mp_graphic_object
*pp
= ch-
>body
;
465 mp_png_path_out
(mp
, gr_path_p
((mp_fill_object
*)pp
));
468 cairo_fill
(mp-
>png-
>cr
);
470 wd
= mp_get_char_dimension
(mp
, mp-
>font_name
[fn
], k
, 'w'
);
471 /* wd
/100 is the size in PS point
, ie wd
=100*real_wd
*/
472 /* but _without_ considering scaling.
*/
473 /* We have a scale factor of
*/
474 /* (ds
/1000.0)*scf
*/
475 /* so to match the scale wd should be
*/
476 /* 1000*real_wd
*scf
/(ds
*scf
) */
479 cairo_translate
(mp-
>png-
>cr
,wd
,0);
481 cairo_restore
(mp-
>png-
>cr
);
484 @ When stroking a path with an elliptical pen
, it is necessary to transform
485 the coordinate system so that a unit circular pen will have the desired shape.
486 To keep this transformation local
, we enclose it in a $$\
&{<g>}\ldots\&{</g>}$$
487 block. Any translation component must be applied to the path being stroked
488 while the rest of the transformation must apply only to the pen.
489 If |fill_also
=true|
, the path is to be filled as well as stroked so we must
490 insert commands to do this after giving the path.
493 static void mp_png_stroke_out
(MP mp
, mp_graphic_object
*h
,
494 mp_pen_info
*pen
, boolean fill_also
) ;
498 void mp_png_stroke_out
(MP mp
, mp_graphic_object
*h
,
499 mp_pen_info
*pen
, boolean fill_also
) {
500 boolean transformed
= false
;
502 cairo_save
(mp-
>png-
>cr
);
503 mp_png_path_out
(mp
, gr_path_p
((mp_stroked_object
*)h
));
504 cairo_close_path
(mp-
>png-
>cr
);
505 cairo_fill
(mp-
>png-
>cr
);
506 cairo_restore
(mp-
>png-
>cr
);
508 cairo_save
(mp-
>png-
>cr
);
511 if
((pen-
>sx
==unity
) &&
521 cairo_set_line_width
(mp-
>png-
>cr
, pen-
>ww
);
523 cairo_set_line_width
(mp-
>png-
>cr
, 0);
525 if
(gr_lcap_val
(h
)!=0) {
526 switch
(gr_lcap_val
(h
)) {
527 case
1: cairo_set_line_cap
(mp-
>png-
>cr
,CAIRO_LINE_CAP_ROUND
); break
;
528 case
2: cairo_set_line_cap
(mp-
>png-
>cr
,CAIRO_LINE_CAP_SQUARE
); break
;
529 default
:cairo_set_line_cap
(mp-
>png-
>cr
,CAIRO_LINE_CAP_BUTT
); break
;
532 if
(gr_type
(h
)!=mp_fill_code
) {
533 mp_dash_object
*hh
= gr_dash_p
(h
);
534 if
(hh
!= NULL && hh->array != NULL) {
536 for
(i
=0; *(hh-
>array
+i
) != -1;i
++)
538 cairo_set_dash
(mp-
>png-
>cr
, hh-
>array
, i
, hh-
>offset
);
541 if
(gr_ljoin_val
((mp_stroked_object
*)h
)!=0) {
542 switch
(gr_ljoin_val
((mp_stroked_object
*)h
)) {
543 case
1: cairo_set_line_join
(mp-
>png-
>cr
,CAIRO_LINE_JOIN_ROUND
); break
;
544 case
2: cairo_set_line_join
(mp-
>png-
>cr
,CAIRO_LINE_JOIN_BEVEL
); break
;
545 default
: cairo_set_line_join
(mp-
>png-
>cr
,CAIRO_LINE_JOIN_MITER
); break
;
548 cairo_set_miter_limit
(mp-
>png-
>cr
,gr_miterlim_val
((mp_stroked_object
*)h
));
550 cairo_matrix_t matrix
= {0,0,0,0,0,0};
551 cairo_matrix_init
(&matrix, pen->sx, pen->rx, pen->ry, pen->sy, pen->tx, pen->ty);
552 cairo_transform
(mp-
>png-
>cr
, &matrix);
554 mp_png_path_out
(mp
, gr_path_p
((mp_stroked_object
*)h
));
555 cairo_stroke
(mp-
>png-
>cr
);
556 cairo_restore
(mp-
>png-
>cr
);
559 @ Here is a simple routine that just fills a cycle.
562 static void mp_png_fill_out
(MP mp
, mp_gr_knot p
, mp_graphic_object
*h
);
565 void mp_png_fill_out
(MP mp
, mp_gr_knot p
, mp_graphic_object
*h
) {
566 cairo_save
(mp-
>png-
>cr
);
567 mp_png_path_out
(mp
, p
);
568 cairo_close_path
(mp-
>png-
>cr
);
569 cairo_fill
(mp-
>png-
>cr
);
570 cairo_restore
(mp-
>png-
>cr
);
573 @ The main output function
575 @d pen_is_elliptical
(A
) ((A
)==gr_next_knot
((A
)))
576 @d gr_has_color
(A
) (gr_type
((A
))<mp_start_clip_code
)
578 @
<Exported function ...@
>=
579 int mp_png_gr_ship_out
(mp_edge_object
*hh
, const char
*options
, int standalone
) ;
581 @ This is a structure to ship data from cairo to our png writer. |width| and
582 |height| could have been stored in our private |mp| instance
, but this is just
587 unsigned char
* data
;
592 @ This is a small structure that is needed so that the png writer callbacks
593 can actually access the |mp| object instance.
601 @ Output a png chunk
: the libpng callbacks
603 static void mp_write_png_data
(png_structp png_ptr
, png_bytep data
, png_size_t length
)
605 mp_png_io
*ioptr
= (mp_png_io
*)png_get_io_ptr
(png_ptr
);
607 (mp-
>write_binary_file
)(mp
,ioptr-
>fp
, (void
*)data
, (size_t
)length
);
609 static void mp_write_png_flush
(png_structp png_ptr
)
615 @ Write |bitmap| to a PNG file specified by |path|
; returns
0 on
616 success
, non-zero on error. The original of this function was
617 borrowed from an internet post
, and extended as needed.
620 int mp_png_save_to_file
(MP mp
, const bitmap_t
* bitmap
, const char
*path
, int colormodel
);
623 int mp_png_save_to_file
(MP mp
, const bitmap_t
* bitmap
, const char
*path
, int colormodel
)
626 png_structp png_ptr
= NULL;
627 png_infop info_ptr
= NULL;
629 png_byte
** row_pointers
= NULL;
634 int ppm_y
; /* pixels per metre
*/
637 io.fp
= (mp-
>open_file
)(mp
, path
, "wb", mp_filetype_bitmap
);
642 png_ptr
= png_create_write_struct
(PNG_LIBPNG_VER_STRING
, NULL, NULL, NULL);
643 if
(png_ptr
== NULL) {
644 goto png_create_write_struct_failed
;
647 info_ptr
= png_create_info_struct
(png_ptr
);
648 if
(info_ptr
== NULL) {
649 goto png_create_info_struct_failed
;
652 /* Set up error handling.
*/
654 if
(setjmp
(png_jmpbuf
(png_ptr
))) {
658 /* Set image attributes.
*/
660 png_set_IHDR
(png_ptr
,
667 PNG_COMPRESSION_TYPE_DEFAULT
,
668 PNG_FILTER_TYPE_DEFAULT
);
670 /* Compression level |
3| appears the best tradeoff between
671 disk size and compression speed
*/
672 png_set_compression_level
(png_ptr
, 3);
673 png_set_filter
(png_ptr
,0,PNG_FILTER_NONE
);
674 /* setup some information
*/
677 char
*a
, *b
, *c
, *d
; /* to get rid of a typecast warning
*/
678 a
= xstrdup
("Title");
680 c
= xstrdup
("Software");
681 d
= xstrdup
("Generated by Metapost version " metapost_version
);
683 text
[0].compression
= PNG_TEXT_COMPRESSION_NONE
;
686 text
[1].compression
= PNG_TEXT_COMPRESSION_NONE
;
689 png_set_text
(png_ptr
, info_ptr
, text
, 2);
697 /* The original plan was to add |hppp| and |vppp| values in here
,
698 but that seems to have negative effects on various bits of
699 software. Better keep the DPI at
72 */
700 ppm_x
= dpi
/ 0.0254;
701 ppm_y
= dpi
/ 0.0254;
702 png_set_pHYs
(png_ptr
, info_ptr
, ppm_x
, ppm_y
, PNG_RESOLUTION_METER
);
704 /* Initialize rows of PNG.
*/
706 row_pointers
= malloc
(bitmap-
>height
* sizeof
(png_byte
*));
707 for
(y
= 0; y
< bitmap-
>height
; ++y
) {
708 if
(colormodel
== PNG_COLOR_TYPE_GRAY
) {
709 row_pointers
[y
] = bitmap-
>data
+ bitmap-
>width
* y
;
710 } else if
(colormodel
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
711 row_pointers
[y
] = bitmap-
>data
+ bitmap-
>width
* 2 * y
;
713 row_pointers
[y
] = bitmap-
>data
+ bitmap-
>width
* 4 * y
;
717 /* Write the image data to |io|
*/
718 png_set_write_fn
(png_ptr
, &io, mp_write_png_data, mp_write_png_flush);
720 png_set_rows
(png_ptr
, info_ptr
, row_pointers
);
721 if
(colormodel
== PNG_COLOR_TYPE_RGB
) {
722 /* Unfortunately
, |png_write_png| does not have enough |PNG_TRANSFORM| options
723 to do this properly
, so we have to modify the bitmap data
*/
725 for
(i
= 0; i
< bitmap-
>width
*bitmap-
>height
*4; i
+=4) {
726 unsigned char b
= bitmap-
>data
[i
];
727 unsigned char g
= bitmap-
>data
[i
+1];
728 bitmap-
>data
[i
] = bitmap-
>data
[i
+3];
729 bitmap-
>data
[i
+1] = bitmap-
>data
[i
+2];
730 bitmap-
>data
[i
+2] = g
;
731 bitmap-
>data
[i
+3] = b
;
733 png_write_png
(png_ptr
, info_ptr
, PNG_TRANSFORM_STRIP_FILLER
, NULL);
734 } else if
(colormodel
== PNG_COLOR_TYPE_RGB_ALPHA
) {
735 png_write_png
(png_ptr
, info_ptr
, PNG_TRANSFORM_BGR
, NULL);
736 } else if
(colormodel
== PNG_COLOR_TYPE_GRAY ||
737 colormodel
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
740 for
(i
= 0; i
< bitmap-
>width
*bitmap-
>height
*4; i
+=4) {
741 unsigned char b
= bitmap-
>data
[i
];
742 unsigned char g
= bitmap-
>data
[i
+1];
743 unsigned char r
= bitmap-
>data
[i
+2];
744 bitmap-
>data
[j
++] = ((r
==g
&& r==b) ? r : 0.2126*r + 0.7152*g + 0.0722*b);
745 if
(colormodel
== PNG_COLOR_TYPE_GRAY_ALPHA
)
746 bitmap-
>data
[j
++] = bitmap-
>data
[i
+3];
748 png_write_png
(png_ptr
, info_ptr
, PNG_TRANSFORM_IDENTITY
, NULL);
755 png_create_info_struct_failed
:
756 png_destroy_write_struct
(&png_ptr, &info_ptr);
757 png_create_write_struct_failed
:
758 (mp-
>close_file
)(mp
, io.fp
);
765 @d number_to_double
(A
) (((math_data
*)(mp-
>math
))->to_double
)(A
)
768 int mp_png_gr_ship_out
(mp_edge_object
*hh
, const char
*options
, int standalone
) {
770 mp_graphic_object
*p
;
771 mp_pen_info
*pen
= NULL;
774 const char
*currentoption
= options
;
775 int colormodel
= PNG_COLOR_TYPE_RGB_ALPHA
;
776 int antialias
= CAIRO_ANTIALIAS_FAST
;
778 while
(currentoption
&& *currentoption) {
779 if
(strncmp
(currentoption
,"format=",7)==0) {
781 if
(strncmp
(currentoption
,"rgba",4)==0) {
782 colormodel
= PNG_COLOR_TYPE_RGB_ALPHA
;
784 } else if
(strncmp
(currentoption
,"rgb",3)==0) {
785 colormodel
= PNG_COLOR_TYPE_RGB
;
787 } else if
(strncmp
(currentoption
,"graya",5)==0) {
788 colormodel
= PNG_COLOR_TYPE_GRAY_ALPHA
;
790 } else if
(strncmp
(currentoption
,"gray",4)==0) {
791 colormodel
= PNG_COLOR_TYPE_GRAY
;
794 } else if
(strncmp
(currentoption
,"antialias=",10)==0) {
796 if
(strncmp
(currentoption
,"none",4)==0) {
797 antialias
= CAIRO_ANTIALIAS_NONE
;
799 } else if
(strncmp
(currentoption
,"fast",4)==0) {
800 antialias
= CAIRO_ANTIALIAS_FAST
;
802 } else if
(strncmp
(currentoption
,"good",4)==0) {
803 antialias
= CAIRO_ANTIALIAS_GOOD
;
805 } else if
(strncmp
(currentoption
,"best",4)==0) {
806 antialias
= CAIRO_ANTIALIAS_BEST
;
810 currentoption
= strchr
(currentoption
,' '
);
812 while
(*currentoption
== ' '
)
816 c
= round_unscaled
(internal_value
(mp_char_code
));
818 mp-
>jump_buf
= malloc
(sizeof
(jmp_buf
));
819 if
(mp-
>jump_buf
== NULL || setjmp
(*(mp-
>jump_buf
)))
822 if
(mp-
>history
>= mp_fatal_error_stop
) return
1;
823 mp_png_start
(mp
, hh
, number_to_double
(internal_value
(mp_hppp
)), number_to_double
(internal_value
(mp_vppp
)),
824 colormodel
, antialias
);
827 if
(gr_has_color
(p
))
828 mp_png_color_out
(mp
, p
);
829 switch
(gr_type
(p
)) {
832 mp_fill_object
*ph
= (mp_fill_object
*)p
;
833 if
( gr_pen_p
(ph
)==NULL ) {
834 mp_png_fill_out
(mp
, gr_path_p
(ph
), p
);
835 } else if
( pen_is_elliptical
(gr_pen_p
(ph
)) ) {
836 pen
= mp_png_pen_info
(mp
, gr_path_p
(ph
), gr_pen_p
(ph
));
837 mp_png_stroke_out
(mp
, p
, pen
, true
);
840 mp_png_fill_out
(mp
, gr_path_p
(ph
), p
);
841 mp_png_fill_out
(mp
, gr_htap_p
(ph
), p
);
845 case mp_stroked_code
:
847 mp_stroked_object
*ph
= (mp_stroked_object
*)p
;
848 if
( pen_is_elliptical
(gr_pen_p
(ph
))) {
849 pen
= mp_png_pen_info
(mp
, gr_path_p
(ph
), gr_pen_p
(ph
));
850 mp_png_stroke_out
(mp
, p
, pen
, false
);
853 mp_png_fill_out
(mp
, gr_path_p
(ph
), p
);
858 if
( (gr_font_n
(p
)!=null_font
) && (gr_text_l(p)>0) ) {
859 mp_png_text_out
(mp
, (mp_text_object
*)p
);
862 case mp_start_clip_code
:
863 cairo_save
(mp-
>png-
>cr
);
864 mp_png_path_out
(mp
, gr_path_p
((mp_clip_object
*)p
));
865 cairo_clip
(mp-
>png-
>cr
);
866 cairo_new_path
(mp-
>png-
>cr
);
868 case mp_stop_clip_code
:
869 cairo_restore
(mp-
>png-
>cr
);
871 case mp_start_bounds_code
:
872 case mp_stop_bounds_code
:
874 case mp_special_code
:
876 } /* all cases are enumerated
*/
879 (void
)mp_set_output_file_name
(mp
, c
);
880 mp_store_true_output_filename
(mp
, c
);
881 ss
= xstrdup
(mp-
>name_of_file
);
882 cairo_surface_flush
(mp-
>png-
>surface
);
883 cairo_destroy
(mp-
>png-
>cr
);
884 bitmap.data
= cairo_image_surface_get_data
(mp-
>png-
>surface
);
885 bitmap.width
= cairo_image_surface_get_width
(mp-
>png-
>surface
);
886 bitmap.height
= cairo_image_surface_get_height
(mp-
>png-
>surface
);
887 mp_png_save_to_file
(mp
, &bitmap, ss, colormodel);
888 cairo_surface_destroy
(mp-
>png-
>surface
);
896 int mp_png_ship_out
(mp_edge_object
*hh
, const char
*options
) ;
900 int mp_png_ship_out
(mp_edge_object
*hh
, const char
*options
) {
901 return mp_png_gr_ship_out
(hh
, options
, (int
)true
);