1 /* Copyright (C) 1995, 1996, 1998 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevcgml.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */
20 /* CGM-writing library */
21 #include "memory_.h"
22 #include "stdio_.h"
23 #include "gdevcgmx.h"
24 
25 /* Forward references to command-writing procedures */
26 private void begin_command(P2(cgm_state *, cgm_op_index));
27 
28 #define OP(op) begin_command(st, op)
29 private cgm_result end_command(P1(cgm_state *));
30 
31 #define END_OP (void)end_command(st)
32 #define DONE return end_command(st)
33 /* Parameters */
34 private void put_int(P3(cgm_state *, cgm_int, int));
35 
36 #define CI(ci) put_int(st, ci, st->metafile.color_index_precision)
37 #define I(i) put_int(st, i, st->metafile.integer_precision)
38 #define IX(ix) put_int(st, ix, st->metafile.index_precision)
39 #define E(e) put_int(st, (int)(e), 16)
40 private void put_real(P3(cgm_state *, cgm_real, const cgm_precision *));
41 
42 #define R(r) put_real(st, r, &st->metafile.real_precision)
43 private void put_vdc(P2(cgm_state *, const cgm_vdc *));
44 
45 #define VDC(vdc) put_vdc(st, vdc)
46 #define VDC2(vdc1, vdc2) VDC(vdc1); VDC(vdc2)
47 #define VDC4(vdc1, vdc2, vdc3, vdc4) VDC2(vdc1, vdc2); VDC2(vdc3, vdc4)
48 private void put_vdc_r(P3(cgm_state *, const cgm_line_marker_extent *, cgm_line_marker_specification_mode));
49 
50 #define VDC_R(vdcr, mode) put_vdc_r(st, vdcr, mode)
51 private void put_point(P2(cgm_state *, const cgm_point *));
52 
53 #define P(p) put_point(st, p)
54 private void put_points(P3(cgm_state *, const cgm_point *, int));
55 
56 #define nP(p, n) put_points(st, p, n)
57 private void put_string(P3(cgm_state *, const char *, uint));
58 
59 #define S(s, l) put_string(st, s, l)
60 private void put_color(P2(cgm_state *, const cgm_color *));
61 
62 #define CO(co) put_color(st, co)
63 private void put_rgb(P2(cgm_state *, const cgm_rgb *));
64 
65 #define CD(cd) put_rgb(st, cd)
66 /* Other data types */
67 #define put_byte(st, b)\
68   if ( st->command_count == command_max_count ) write_command(st, false);\
69   st->command[st->command_count++] = (byte)(b)
70 private void put_bytes(P3(cgm_state *, const byte *, uint));
71 private void write_command(P2(cgm_state *, bool));
72 private void put_real_precision(P2(cgm_state *, const cgm_precision *));
73 
74 /* ================ Public routines ================ */
75 
76 /* ---------------- Initialize/terminate ---------------- */
77 
78 /* Initialize a CGM writer. */
79 cgm_state *
cgm_initialize(FILE * file,const cgm_allocator * cal)80 cgm_initialize(FILE * file, const cgm_allocator * cal)
81 {
82     cgm_state *st = (*cal->alloc) (cal->private_data, sizeof(cgm_state));
83 
84     if (st == 0)
85 	return 0;
86     st->file = file;
87     st->allocator = *cal;
88     /* Initialize metafile elements. */
89     st->metafile.vdc_type = cgm_vdc_integer;
90     st->metafile.integer_precision = 16;
91     st->metafile.real_precision.representation = cgm_representation_fixed;
92     st->metafile.real_precision.exponent_or_whole_width = 16;
93     st->metafile.real_precision.fraction_width = 16;
94     st->metafile.index_precision = 16;
95     st->metafile.color_precision = 8;
96     st->metafile.color_index_precision = 8;
97     st->metafile.maximum_color_index = 63;
98     /* color_value_extent */
99     /*st->metafile.character_coding_announcer = 0; */
100     /* Initialize picture elements. */
101     st->picture.scaling_mode = cgm_scaling_abstract;
102     st->picture.color_selection_mode = cgm_color_selection_indexed;
103     st->picture.line_width_specification_mode = cgm_line_marker_absolute;
104     st->picture.marker_size_specification_mode = cgm_line_marker_absolute;
105     st->picture.edge_width_specification_mode = cgm_line_marker_absolute;
106     /* vdc_extent */
107     /* background_color */
108     /* Initialize control elements. */
109     st->vdc_integer_precision = st->metafile.integer_precision;
110     st->vdc_real_precision = st->metafile.real_precision;
111     st->transparency = cgm_transparency_on;
112     /* clip_rectangle */
113     st->clip_indicator = cgm_clip_on;
114     /* Initialize other state elements. */
115     st->line_bundle_index = 1;
116     st->line_type = cgm_line_solid;
117     /* line_width */
118     /* line_color */
119     st->marker_bundle_index = 1;
120     st->marker_type = cgm_marker_asterisk;
121     /* marker_size */
122     /* marker_color */
123     st->text_bundle_index = 1;
124     st->text_font_index = 1;
125     st->text_precision = cgm_text_precision_string;
126     st->character_expansion_factor = 1.0;
127     st->character_spacing = 0.0;
128     /* text_color */
129     /* character_height */
130     /* character_orientation */
131     st->text_path = cgm_text_path_right;
132     /* text_alignment */
133     st->character_set_index = 1;
134     st->alternate_character_set_index = 1;
135     st->fill_bundle_index = 1;
136     st->interior_style = cgm_interior_style_hollow;
137     st->hatch_index = cgm_hatch_horizontal;
138     st->pattern_index = 1;
139     st->edge_bundle_index = 1;
140     st->edge_type = cgm_edge_solid;
141     /* edge_width */
142     st->edge_visibility = false;
143     /* fill_reference_point */
144     /* pattern_table */
145     /* pattern_size */
146     /* color_table */
147     memset(st->source_flags, (byte) cgm_aspect_source_individual,
148 	   sizeof(st->source_flags));
149     return st;
150 }
151 
152 /* Terminate a CGM writer. */
153 cgm_result
cgm_terminate(cgm_state * st)154 cgm_terminate(cgm_state * st)
155 {
156     (*st->allocator.free) (st->allocator.private_data, st);
157     return cgm_result_ok;
158 }
159 
160 /* ---------------- Metafile elements ---------------- */
161 
162 cgm_result
cgm_BEGIN_METAFILE(cgm_state * st,const char * str,uint len)163 cgm_BEGIN_METAFILE(cgm_state * st, const char *str, uint len)
164 {
165     OP(BEGIN_METAFILE);
166     S(str, len);
167     DONE;
168 }
169 
170 cgm_result
cgm_set_metafile_elements(cgm_state * st,const cgm_metafile_elements * meta,long mask)171 cgm_set_metafile_elements(cgm_state * st, const cgm_metafile_elements * meta, long mask)
172 {
173     if ((mask & cgm_set_METAFILE_VERSION)) {
174 	OP(METAFILE_VERSION);
175 	I(meta->metafile_version);
176 	END_OP;
177 	st->metafile.metafile_version = meta->metafile_version;
178     }
179     if ((mask & cgm_set_METAFILE_DESCRIPTION)) {
180 	OP(METAFILE_DESCRIPTION);
181 	S(meta->metafile_description.chars, meta->metafile_description.length);
182 	END_OP;
183 	st->metafile.metafile_description = meta->metafile_description;
184     }
185     if ((mask & cgm_set_VDC_TYPE)) {
186 	OP(VDC_TYPE);
187 	E(meta->vdc_type);
188 	END_OP;
189 	st->metafile.vdc_type = meta->vdc_type;
190     }
191     if ((mask & cgm_set_INTEGER_PRECISION)) {
192 	OP(INTEGER_PRECISION);
193 	I(meta->integer_precision);
194 	END_OP;
195 	st->metafile.integer_precision = meta->integer_precision;
196     }
197     if ((mask & cgm_set_REAL_PRECISION)) {
198 	OP(REAL_PRECISION);
199 	put_real_precision(st, &meta->real_precision);
200 	END_OP;
201 	st->metafile.real_precision = meta->real_precision;
202     }
203     if ((mask & cgm_set_INDEX_PRECISION)) {
204 	OP(INDEX_PRECISION);
205 	I(meta->index_precision);
206 	END_OP;
207 	st->metafile.index_precision = meta->index_precision;
208     }
209     if ((mask & cgm_set_COLOR_PRECISION)) {
210 	OP(COLOR_PRECISION);
211 	I(meta->color_precision);
212 	END_OP;
213 	st->metafile.color_index_precision = meta->color_index_precision;
214     }
215     if ((mask & cgm_set_COLOR_INDEX_PRECISION)) {
216 	OP(COLOR_INDEX_PRECISION);
217 	I(meta->color_index_precision);
218 	END_OP;
219 	st->metafile.color_index_precision = meta->color_index_precision;
220     }
221     if ((mask & cgm_set_MAXIMUM_COLOR_INDEX)) {
222 	OP(MAXIMUM_COLOR_INDEX);
223 	CI(meta->maximum_color_index);
224 	END_OP;
225 	st->metafile.maximum_color_index = meta->maximum_color_index;
226     }
227     if ((mask & cgm_set_METAFILE_ELEMENT_LIST)) {
228 	int i;
229 	const int *p;
230 
231 	OP(METAFILE_ELEMENT_LIST);
232 	for (i = 0, p = meta->metafile_element_list;
233 	     i < meta->metafile_element_list_count;
234 	     i++, p += 2
235 	    ) {
236 	    I(p[0]);
237 	    I(p[1]);
238 	}
239 	END_OP;
240 	st->metafile.metafile_element_list =
241 	    meta->metafile_element_list;
242 	st->metafile.metafile_element_list_count =
243 	    meta->metafile_element_list_count;
244     }
245     /* element list */
246     if ((mask & cgm_set_FONT_LIST)) {
247 	int i;
248 
249 	OP(FONT_LIST);
250 	for (i = 0; i < meta->font_list_count; ++i)
251 	    S(meta->font_list[i].chars, meta->font_list[i].length);
252 	END_OP;
253 	st->metafile.font_list = meta->font_list;
254 	st->metafile.font_list_count = meta->font_list_count;
255     }
256     /* character set list */
257     /* character coding announcer */
258     return st->result;
259 }
260 
261 cgm_result
cgm_END_METAFILE(cgm_state * st)262 cgm_END_METAFILE(cgm_state * st)
263 {
264     OP(END_METAFILE);
265     DONE;
266 }
267 
268 /* ---------------- Picture elements ---------------- */
269 
270 cgm_result
cgm_BEGIN_PICTURE(cgm_state * st,const char * str,uint len)271 cgm_BEGIN_PICTURE(cgm_state * st, const char *str, uint len)
272 {
273     OP(BEGIN_PICTURE);
274     S(str, len);
275     DONE;
276 }
277 
278 cgm_result
cgm_set_picture_elements(cgm_state * st,const cgm_picture_elements * pic,long mask)279 cgm_set_picture_elements(cgm_state * st, const cgm_picture_elements * pic, long mask)
280 {
281     if ((mask & cgm_set_SCALING_MODE)) {
282 	OP(SCALING_MODE);
283 	E(pic->scaling_mode);
284 	R(pic->scale_factor);
285 	st->picture.scaling_mode = pic->scaling_mode;
286 	st->picture.scale_factor = pic->scale_factor;
287 	END_OP;
288     }
289     if ((mask & cgm_set_COLOR_SELECTION_MODE)) {
290 	OP(COLOR_SELECTION_MODE);
291 	E(pic->color_selection_mode);
292 	END_OP;
293 	st->picture.color_selection_mode = pic->color_selection_mode;
294     }
295     if ((mask & cgm_set_LINE_WIDTH_SPECIFICATION_MODE)) {
296 	OP(LINE_WIDTH_SPECIFICATION_MODE);
297 	E(pic->line_width_specification_mode);
298 	END_OP;
299 	st->picture.line_width_specification_mode = pic->line_width_specification_mode;
300     }
301     if ((mask & cgm_set_MARKER_SIZE_SPECIFICATION_MODE)) {
302 	OP(MARKER_SIZE_SPECIFICATION_MODE);
303 	E(pic->marker_size_specification_mode);
304 	END_OP;
305 	st->picture.marker_size_specification_mode = pic->marker_size_specification_mode;
306     }
307     if ((mask & cgm_set_EDGE_WIDTH_SPECIFICATION_MODE)) {
308 	OP(EDGE_WIDTH_SPECIFICATION_MODE);
309 	E(pic->edge_width_specification_mode);
310 	END_OP;
311 	st->picture.edge_width_specification_mode = pic->edge_width_specification_mode;
312     }
313     if ((mask & cgm_set_VDC_EXTENT)) {
314 	OP(VDC_EXTENT);
315 	P(&pic->vdc_extent[0]);
316 	P(&pic->vdc_extent[1]);
317 	END_OP;
318 	st->picture.vdc_extent[0] = pic->vdc_extent[0];
319 	st->picture.vdc_extent[1] = pic->vdc_extent[1];
320     }
321     if ((mask & cgm_set_BACKGROUND_COLOR)) {
322 	OP(BACKGROUND_COLOR);
323 	CD(&pic->background_color.rgb);
324 	DONE;
325 	st->picture.background_color = pic->background_color;
326     }
327     return st->result;
328 }
329 
330 cgm_result
cgm_BEGIN_PICTURE_BODY(cgm_state * st)331 cgm_BEGIN_PICTURE_BODY(cgm_state * st)
332 {
333     OP(BEGIN_PICTURE_BODY);
334     DONE;
335 }
336 
337 cgm_result
cgm_END_PICTURE(cgm_state * st)338 cgm_END_PICTURE(cgm_state * st)
339 {
340     OP(END_PICTURE);
341     DONE;
342 }
343 
344 /* ---------------- Control elements ---------------- */
345 
346 cgm_result
cgm_VDC_INTEGER_PRECISION(cgm_state * st,int precision)347 cgm_VDC_INTEGER_PRECISION(cgm_state * st, int precision)
348 {
349     if (st->vdc_integer_precision != precision) {
350 	OP(VDC_INTEGER_PRECISION);
351 	I(precision);
352 	st->vdc_integer_precision = precision;
353 	DONE;
354     } else
355 	return cgm_result_ok;
356 }
357 
358 cgm_result
cgm_VDC_REAL_PRECISION(cgm_state * st,const cgm_precision * precision)359 cgm_VDC_REAL_PRECISION(cgm_state * st, const cgm_precision * precision)
360 {
361     OP(VDC_REAL_PRECISION);
362     put_real_precision(st, precision);
363     st->vdc_real_precision = *precision;
364     DONE;
365 }
366 
367 cgm_result
cgm_AUXILIARY_COLOR(cgm_state * st,const cgm_color * color)368 cgm_AUXILIARY_COLOR(cgm_state * st, const cgm_color * color)
369 {
370     OP(AUXILIARY_COLOR);
371     CO(color);
372     st->auxiliary_color = *color;
373     DONE;
374 }
375 
376 cgm_result
cgm_TRANSPARENCY(cgm_state * st,cgm_transparency transparency)377 cgm_TRANSPARENCY(cgm_state * st, cgm_transparency transparency)
378 {
379     OP(TRANSPARENCY);
380     E(transparency);
381     st->transparency = transparency;
382     DONE;
383 }
384 
385 cgm_result
cgm_CLIP_RECTANGLE(cgm_state * st,const cgm_point rectangle[2])386 cgm_CLIP_RECTANGLE(cgm_state * st, const cgm_point rectangle[2])
387 {
388     OP(CLIP_RECTANGLE);
389     P(&rectangle[0]);
390     st->clip_rectangle[0] = rectangle[0];
391     P(&rectangle[1]);
392     st->clip_rectangle[1] = rectangle[1];
393     DONE;
394 }
395 
396 cgm_result
cgm_CLIP_INDICATOR(cgm_state * st,cgm_clip_indicator clip)397 cgm_CLIP_INDICATOR(cgm_state * st, cgm_clip_indicator clip)
398 {
399     OP(CLIP_INDICATOR);
400     E(clip);
401     st->clip_indicator = clip;
402     DONE;
403 }
404 
405 /* ---------------- Graphical primitive elements ---------------- */
406 
407 cgm_result
cgm_POLYLINE(cgm_state * st,const cgm_point * vertices,int count)408 cgm_POLYLINE(cgm_state * st, const cgm_point * vertices, int count)
409 {
410     OP(POLYLINE);
411     nP(vertices, count);
412     DONE;
413 }
414 
415 cgm_result
cgm_DISJOINT_POLYLINE(cgm_state * st,const cgm_point * endpoints,int count)416 cgm_DISJOINT_POLYLINE(cgm_state * st, const cgm_point * endpoints, int count)
417 {
418     OP(DISJOINT_POLYLINE);
419     nP(endpoints, count);
420     DONE;
421 }
422 
423 cgm_result
cgm_POLYMARKER(cgm_state * st,const cgm_point * positions,int count)424 cgm_POLYMARKER(cgm_state * st, const cgm_point * positions, int count)
425 {
426     OP(POLYMARKER);
427     nP(positions, count);
428     DONE;
429 }
430 
431 cgm_result
cgm_TEXT(cgm_state * st,const cgm_point * position,bool final,const char * str,uint len)432 cgm_TEXT(cgm_state * st, const cgm_point * position, bool final, const char *str, uint len)
433 {
434     OP(TEXT);
435     P(position);
436     E(final);
437     S(str, len);
438     DONE;
439 }
440 
441 cgm_result
cgm_RESTRICTED_TEXT(cgm_state * st,const cgm_vdc * delta_width,const cgm_vdc * delta_height,const cgm_point * position,bool final,const char * str,uint len)442 cgm_RESTRICTED_TEXT(cgm_state * st, const cgm_vdc * delta_width, const cgm_vdc * delta_height, const cgm_point * position, bool final, const char *str, uint len)
443 {
444     OP(RESTRICTED_TEXT);
445     VDC2(delta_width, delta_height);
446     P(position);
447     E(final);
448     S(str, len);
449     DONE;
450 }
451 
452 cgm_result
cgm_APPEND_TEXT(cgm_state * st,bool final,const char * str,uint len)453 cgm_APPEND_TEXT(cgm_state * st, bool final, const char *str, uint len)
454 {
455     OP(APPEND_TEXT);
456     E(final);
457     S(str, len);
458     DONE;
459 }
460 
461 cgm_result
cgm_POLYGON(cgm_state * st,const cgm_point * vertices,int count)462 cgm_POLYGON(cgm_state * st, const cgm_point * vertices, int count)
463 {
464     OP(POLYGON);
465     nP(vertices, count);
466     DONE;
467 }
468 
469 cgm_result
cgm_POLYGON_SET(cgm_state * st,const cgm_polygon_edge * vertices,int count)470 cgm_POLYGON_SET(cgm_state * st, const cgm_polygon_edge * vertices, int count)
471 {
472     int i;
473 
474     OP(POLYGON);
475     for (i = 0; i < count; ++i) {
476 	P(&vertices[i].vertex);
477 	E(vertices[i].edge_out);
478     }
479     DONE;
480 }
481 
482 cgm_result
cgm_CELL_ARRAY(cgm_state * st,const cgm_point * pqr,cgm_int nx,cgm_int ny,cgm_int local_color_precision,cgm_cell_representation_mode mode,const byte * values,uint source_bit,uint raster)483 cgm_CELL_ARRAY(cgm_state * st, const cgm_point * pqr /*[3] */ , cgm_int nx, cgm_int ny, cgm_int local_color_precision, cgm_cell_representation_mode mode, const byte * values, uint source_bit, uint raster)
484 {
485     int precision = local_color_precision;
486     int bits_per_pixel;
487     uint row_bytes;
488     const byte *row = values + (source_bit >> 3);
489     int bit = source_bit & 7;
490     int y;
491 
492     /* Currently we ignore the cell representation_mode, and always */
493     /* produce cell arrays in 'packed' format. */
494     mode = cgm_cell_mode_packed;
495     OP(CELL_ARRAY);
496     nP(pqr, 3);
497     I(nx);
498     I(ny);
499     I(local_color_precision);
500     E(mode);
501     if (precision == 0)
502 	precision = (st->picture.color_selection_mode ==
503 		     cgm_color_selection_indexed ?
504 		     st->metafile.color_index_precision :
505 		     st->metafile.color_precision);
506     bits_per_pixel =
507 	(st->picture.color_selection_mode == cgm_color_selection_indexed ?
508 	 precision : precision * 3);
509     row_bytes = (bits_per_pixel * nx + 7) >> 3;
510     for (y = 0; y < ny; y++, row += raster) {
511 	if (bit == 0)
512 	    put_bytes(st, row, row_bytes);
513 	else {
514 	    uint i;
515 
516 	    for (i = 0; i < row_bytes; i++) {
517 		byte b = (row[i] << bit) +
518 		(row[i + 1] >> (8 - bit));
519 
520 		put_byte(st, b);
521 	    }
522 	}
523 	if ((row_bytes & 1)) {
524 	    put_byte(st, 0);
525 	}
526     }
527     DONE;
528 }
529 
530 cgm_result
cgm_RECTANGLE(cgm_state * st,const cgm_point * corner1,const cgm_point * corner2)531 cgm_RECTANGLE(cgm_state * st, const cgm_point * corner1, const cgm_point * corner2)
532 {
533     OP(RECTANGLE);
534     P(corner1);
535     P(corner2);
536     DONE;
537 }
538 
539 cgm_result
cgm_CIRCLE(cgm_state * st,const cgm_point * center,const cgm_vdc * radius)540 cgm_CIRCLE(cgm_state * st, const cgm_point * center, const cgm_vdc * radius)
541 {
542     OP(CIRCLE);
543     P(center);
544     VDC(radius);
545     DONE;
546 }
547 
548 cgm_result
cgm_CIRCULAR_ARC_3_POINT(cgm_state * st,const cgm_point * start,const cgm_point * intermediate,const cgm_point * end)549 cgm_CIRCULAR_ARC_3_POINT(cgm_state * st, const cgm_point * start, const cgm_point * intermediate, const cgm_point * end)
550 {
551     OP(CIRCULAR_ARC_3_POINT);
552     P(start);
553     P(intermediate);
554     P(end);
555     DONE;
556 }
557 
558 cgm_result
cgm_CIRCULAR_ARC_3_POINT_CLOSE(cgm_state * st,const cgm_point * start,const cgm_point * intermediate,const cgm_point * end,cgm_arc_closure closure)559 cgm_CIRCULAR_ARC_3_POINT_CLOSE(cgm_state * st, const cgm_point * start, const cgm_point * intermediate, const cgm_point * end, cgm_arc_closure closure)
560 {
561     OP(CIRCULAR_ARC_3_POINT_CLOSE);
562     P(start);
563     P(intermediate);
564     P(end);
565     E(closure);
566     DONE;
567 }
568 
569 cgm_result
cgm_CIRCULAR_ARC_CENTER(cgm_state * st,const cgm_point * center,const cgm_vdc * dx_start,const cgm_vdc * dy_start,const cgm_vdc * dx_end,const cgm_vdc * dy_end,const cgm_vdc * radius)570 cgm_CIRCULAR_ARC_CENTER(cgm_state * st, const cgm_point * center, const cgm_vdc * dx_start, const cgm_vdc * dy_start, const cgm_vdc * dx_end, const cgm_vdc * dy_end, const cgm_vdc * radius)
571 {
572     OP(CIRCULAR_ARC_CENTER);
573     P(center);
574     VDC4(dx_start, dy_start, dx_end, dy_end);
575     VDC(radius);
576     DONE;
577 }
578 
579 cgm_result
cgm_CIRCULAR_ARC_CENTER_CLOSE(cgm_state * st,const cgm_point * center,const cgm_vdc * dx_start,const cgm_vdc * dy_start,const cgm_vdc * dx_end,const cgm_vdc * dy_end,const cgm_vdc * radius,cgm_arc_closure closure)580 cgm_CIRCULAR_ARC_CENTER_CLOSE(cgm_state * st, const cgm_point * center, const cgm_vdc * dx_start, const cgm_vdc * dy_start, const cgm_vdc * dx_end, const cgm_vdc * dy_end, const cgm_vdc * radius, cgm_arc_closure closure)
581 {
582     OP(CIRCULAR_ARC_CENTER_CLOSE);
583     P(center);
584     VDC4(dx_start, dy_start, dx_end, dy_end);
585     VDC(radius);
586     E(closure);
587     DONE;
588 }
589 
590 cgm_result
cgm_ELLIPSE(cgm_state * st,const cgm_point * center,const cgm_point * cd1_end,const cgm_point * cd2_end)591 cgm_ELLIPSE(cgm_state * st, const cgm_point * center, const cgm_point * cd1_end, const cgm_point * cd2_end)
592 {
593     OP(ELLIPSE);
594     P(center);
595     P(cd1_end);
596     P(cd2_end);
597     DONE;
598 }
599 
600 cgm_result
cgm_ELLIPTICAL_ARC(cgm_state * st,const cgm_point * center,const cgm_point * cd1_end,const cgm_point * cd2_end,const cgm_vdc * dx_start,const cgm_vdc * dy_start,const cgm_vdc * dx_end,const cgm_vdc * dy_end)601 cgm_ELLIPTICAL_ARC(cgm_state * st, const cgm_point * center, const cgm_point * cd1_end, const cgm_point * cd2_end, const cgm_vdc * dx_start, const cgm_vdc * dy_start, const cgm_vdc * dx_end, const cgm_vdc * dy_end)
602 {
603     OP(ELLIPTICAL_ARC);
604     P(center);
605     P(cd1_end);
606     P(cd2_end);
607     VDC4(dx_start, dy_start, dx_end, dy_end);
608     DONE;
609 }
610 
611 cgm_result
cgm_ELLIPTICAL_ARC_CLOSE(cgm_state * st,const cgm_point * center,const cgm_point * cd1_end,const cgm_point * cd2_end,const cgm_vdc * dx_start,const cgm_vdc * dy_start,const cgm_vdc * dx_end,const cgm_vdc * dy_end,cgm_arc_closure closure)612 cgm_ELLIPTICAL_ARC_CLOSE(cgm_state * st, const cgm_point * center, const cgm_point * cd1_end, const cgm_point * cd2_end, const cgm_vdc * dx_start, const cgm_vdc * dy_start, const cgm_vdc * dx_end, const cgm_vdc * dy_end, cgm_arc_closure closure)
613 {
614     OP(ELLIPTICAL_ARC_CLOSE);
615     P(center);
616     P(cd1_end);
617     P(cd2_end);
618     VDC4(dx_start, dy_start, dx_end, dy_end);
619     E(closure);
620     DONE;
621 }
622 
623 /* ---------------- Attribute elements ---------------- */
624 
625 cgm_result
cgm_LINE_BUNDLE_INDEX(cgm_state * st,cgm_int index)626 cgm_LINE_BUNDLE_INDEX(cgm_state * st, cgm_int index)
627 {
628     OP(LINE_BUNDLE_INDEX);
629     IX(index);
630     st->line_bundle_index = index;
631     DONE;
632 }
633 
634 cgm_result
cgm_LINE_TYPE(cgm_state * st,cgm_line_type line_type)635 cgm_LINE_TYPE(cgm_state * st, cgm_line_type line_type)
636 {
637     OP(LINE_TYPE);
638     IX((int)line_type);
639     st->line_type = line_type;
640     DONE;
641 }
642 
643 cgm_result
cgm_LINE_WIDTH(cgm_state * st,const cgm_line_width * line_width)644 cgm_LINE_WIDTH(cgm_state * st, const cgm_line_width * line_width)
645 {
646     OP(LINE_WIDTH);
647     VDC_R(line_width, st->picture.line_width_specification_mode);
648     st->line_width = *line_width;
649     DONE;
650 }
651 
652 cgm_result
cgm_LINE_COLOR(cgm_state * st,const cgm_color * color)653 cgm_LINE_COLOR(cgm_state * st, const cgm_color * color)
654 {
655     OP(LINE_COLOR);
656     CO(color);
657     st->line_color = *color;
658     DONE;
659 }
660 
661 cgm_result
cgm_MARKER_BUNDLE_INDEX(cgm_state * st,cgm_int index)662 cgm_MARKER_BUNDLE_INDEX(cgm_state * st, cgm_int index)
663 {
664     OP(MARKER_BUNDLE_INDEX);
665     IX(index);
666     st->marker_bundle_index = index;
667     DONE;
668 }
669 
670 cgm_result
cgm_MARKER_TYPE(cgm_state * st,cgm_marker_type marker_type)671 cgm_MARKER_TYPE(cgm_state * st, cgm_marker_type marker_type)
672 {
673     OP(MARKER_TYPE);
674     IX((int)marker_type);
675     st->marker_type = marker_type;
676     DONE;
677 }
678 
679 cgm_result
cgm_MARKER_SIZE(cgm_state * st,const cgm_marker_size * marker_size)680 cgm_MARKER_SIZE(cgm_state * st, const cgm_marker_size * marker_size)
681 {
682     OP(MARKER_SIZE);
683     VDC_R(marker_size, st->picture.marker_size_specification_mode);
684     st->marker_size = *marker_size;
685     DONE;
686 }
687 
688 cgm_result
cgm_MARKER_COLOR(cgm_state * st,const cgm_color * color)689 cgm_MARKER_COLOR(cgm_state * st, const cgm_color * color)
690 {
691     OP(MARKER_COLOR);
692     CO(color);
693     st->marker_color = *color;
694     DONE;
695 }
696 
697 cgm_result
cgm_TEXT_BUNDLE_INDEX(cgm_state * st,cgm_int index)698 cgm_TEXT_BUNDLE_INDEX(cgm_state * st, cgm_int index)
699 {
700     OP(TEXT_BUNDLE_INDEX);
701     IX(index);
702     st->text_bundle_index = index;
703     DONE;
704 }
705 
706 cgm_result
cgm_TEXT_FONT_INDEX(cgm_state * st,cgm_int index)707 cgm_TEXT_FONT_INDEX(cgm_state * st, cgm_int index)
708 {
709     OP(TEXT_FONT_INDEX);
710     IX(index);
711     st->text_font_index = index;
712     DONE;
713 }
714 
715 cgm_result
cgm_TEXT_PRECISION(cgm_state * st,cgm_text_precision precision)716 cgm_TEXT_PRECISION(cgm_state * st, cgm_text_precision precision)
717 {
718     OP(TEXT_PRECISION);
719     E(precision);
720     st->text_precision = precision;
721     DONE;
722 }
723 
724 cgm_result
cgm_CHARACTER_EXPANSION_FACTOR(cgm_state * st,cgm_real factor)725 cgm_CHARACTER_EXPANSION_FACTOR(cgm_state * st, cgm_real factor)
726 {
727     OP(CHARACTER_EXPANSION_FACTOR);
728     R(factor);
729     st->character_expansion_factor = factor;
730     DONE;
731 }
732 
733 cgm_result
cgm_CHARACTER_SPACING(cgm_state * st,cgm_real spacing)734 cgm_CHARACTER_SPACING(cgm_state * st, cgm_real spacing)
735 {
736     OP(CHARACTER_SPACING);
737     R(spacing);
738     st->character_spacing = spacing;
739     DONE;
740 }
741 
742 cgm_result
cgm_TEXT_COLOR(cgm_state * st,const cgm_color * color)743 cgm_TEXT_COLOR(cgm_state * st, const cgm_color * color)
744 {
745     OP(TEXT_COLOR);
746     CO(color);
747     st->text_color = *color;
748     DONE;
749 }
750 
751 cgm_result
cgm_CHARACTER_HEIGHT(cgm_state * st,const cgm_vdc * height)752 cgm_CHARACTER_HEIGHT(cgm_state * st, const cgm_vdc * height)
753 {
754     OP(CHARACTER_HEIGHT);
755     VDC(height);
756     st->character_height = *height;
757     DONE;
758 }
759 
760 cgm_result
cgm_CHARACTER_ORIENTATION(cgm_state * st,const cgm_vdc * x_up,const cgm_vdc * y_up,const cgm_vdc * x_base,const cgm_vdc * y_base)761 cgm_CHARACTER_ORIENTATION(cgm_state * st, const cgm_vdc * x_up, const cgm_vdc * y_up, const cgm_vdc * x_base, const cgm_vdc * y_base)
762 {
763     OP(CHARACTER_ORIENTATION);
764     VDC4(x_up, y_up, x_base, y_base);
765     st->character_orientation[0] = *x_up;
766     st->character_orientation[1] = *y_up;
767     st->character_orientation[2] = *x_base;
768     st->character_orientation[3] = *y_base;
769     DONE;
770 }
771 
772 cgm_result
cgm_TEXT_PATH(cgm_state * st,cgm_text_path text_path)773 cgm_TEXT_PATH(cgm_state * st, cgm_text_path text_path)
774 {
775     OP(TEXT_PATH);
776     E(text_path);
777     st->text_path = text_path;
778     DONE;
779 }
780 
781 cgm_result
cgm_TEXT_ALIGNMENT(cgm_state * st,cgm_text_alignment_horizontal align_h,cgm_text_alignment_vertical align_v,cgm_real align_cont_h,cgm_real align_cont_v)782 cgm_TEXT_ALIGNMENT(cgm_state * st, cgm_text_alignment_horizontal align_h, cgm_text_alignment_vertical align_v, cgm_real align_cont_h, cgm_real align_cont_v)
783 {
784     OP(TEXT_ALIGNMENT);
785     E(align_h);
786     E(align_v);
787     R(align_cont_h);
788     R(align_cont_v);
789     DONE;
790 }
791 
792 cgm_result
cgm_CHARACTER_SET_INDEX(cgm_state * st,cgm_int index)793 cgm_CHARACTER_SET_INDEX(cgm_state * st, cgm_int index)
794 {
795     OP(CHARACTER_SET_INDEX);
796     IX(index);
797     st->character_set_index = index;
798     DONE;
799 }
800 
801 /* See gdevcgml.c for why this isn't named cgm_ALTERNATE_.... */
802 cgm_result
cgm_ALT_CHARACTER_SET_INDEX(cgm_state * st,cgm_int index)803 cgm_ALT_CHARACTER_SET_INDEX(cgm_state * st, cgm_int index)
804 {
805     OP(ALTERNATE_CHARACTER_SET_INDEX);
806     IX(index);
807     st->alternate_character_set_index = index;
808     DONE;
809 }
810 
811 cgm_result
cgm_FILL_BUNDLE_INDEX(cgm_state * st,cgm_int index)812 cgm_FILL_BUNDLE_INDEX(cgm_state * st, cgm_int index)
813 {
814     OP(FILL_BUNDLE_INDEX);
815     IX(index);
816     st->fill_bundle_index = index;
817     DONE;
818 }
819 
820 cgm_result
cgm_INTERIOR_STYLE(cgm_state * st,cgm_interior_style interior_style)821 cgm_INTERIOR_STYLE(cgm_state * st, cgm_interior_style interior_style)
822 {
823     OP(INTERIOR_STYLE);
824     E(interior_style);
825     st->interior_style = interior_style;
826     DONE;
827 }
828 
829 cgm_result
cgm_FILL_COLOR(cgm_state * st,const cgm_color * color)830 cgm_FILL_COLOR(cgm_state * st, const cgm_color * color)
831 {
832     OP(FILL_COLOR);
833     CO(color);
834     st->fill_color = *color;
835     DONE;
836 }
837 
838 cgm_result
cgm_HATCH_INDEX(cgm_state * st,cgm_hatch_index hatch_index)839 cgm_HATCH_INDEX(cgm_state * st, cgm_hatch_index hatch_index)
840 {
841     OP(HATCH_INDEX);
842     IX((int)hatch_index);
843     st->hatch_index = hatch_index;
844     DONE;
845 }
846 
847 cgm_result
cgm_PATTERN_INDEX(cgm_state * st,cgm_int index)848 cgm_PATTERN_INDEX(cgm_state * st, cgm_int index)
849 {
850     OP(PATTERN_INDEX);
851     IX(index);
852     st->pattern_index = index;
853     DONE;
854 }
855 
856 cgm_result
cgm_EDGE_BUNDLE_INDEX(cgm_state * st,cgm_int index)857 cgm_EDGE_BUNDLE_INDEX(cgm_state * st, cgm_int index)
858 {
859     OP(EDGE_BUNDLE_INDEX);
860     IX(index);
861     st->edge_bundle_index = index;
862     DONE;
863 }
864 
865 cgm_result
cgm_EDGE_TYPE(cgm_state * st,cgm_edge_type edge_type)866 cgm_EDGE_TYPE(cgm_state * st, cgm_edge_type edge_type)
867 {
868     OP(EDGE_TYPE);
869     IX((int)edge_type);
870     st->edge_type = edge_type;
871     DONE;
872 }
873 
874 cgm_result
cgm_EDGE_WIDTH(cgm_state * st,const cgm_edge_width * edge_width)875 cgm_EDGE_WIDTH(cgm_state * st, const cgm_edge_width * edge_width)
876 {
877     OP(EDGE_WIDTH);
878     VDC_R(edge_width, st->picture.edge_width_specification_mode);
879     st->edge_width = *edge_width;
880     DONE;
881 }
882 
883 cgm_result
cgm_EDGE_COLOR(cgm_state * st,const cgm_color * color)884 cgm_EDGE_COLOR(cgm_state * st, const cgm_color * color)
885 {
886     OP(EDGE_COLOR);
887     CO(color);
888     DONE;
889 }
890 
891 cgm_result
cgm_EDGE_VISIBILITY(cgm_state * st,bool visibility)892 cgm_EDGE_VISIBILITY(cgm_state * st, bool visibility)
893 {
894     OP(EDGE_VISIBILITY);
895     E(visibility);
896     st->edge_visibility = visibility;
897     DONE;
898 }
899 
900 cgm_result
cgm_FILL_REFERENCE_POINT(cgm_state * st,const cgm_point * reference_point)901 cgm_FILL_REFERENCE_POINT(cgm_state * st, const cgm_point * reference_point)
902 {
903     OP(FILL_REFERENCE_POINT);
904     P(reference_point);
905     st->fill_reference_point = *reference_point;
906     DONE;
907 }
908 
909 /* PATTERN_TABLE */
910 
911 cgm_result
cgm_PATTERN_SIZE(cgm_state * st,const cgm_vdc * x_height,const cgm_vdc * y_height,const cgm_vdc * x_width,const cgm_vdc * y_width)912 cgm_PATTERN_SIZE(cgm_state * st, const cgm_vdc * x_height, const cgm_vdc * y_height, const cgm_vdc * x_width, const cgm_vdc * y_width)
913 {
914     OP(PATTERN_SIZE);
915     VDC4(x_height, y_height, x_width, y_width);
916     st->pattern_size[0] = *x_height;
917     st->pattern_size[1] = *y_height;
918     st->pattern_size[2] = *x_width;
919     st->pattern_size[3] = *y_width;
920     DONE;
921 }
922 
923 cgm_result
cgm_COLOR_TABLE(cgm_state * st,cgm_int index,const cgm_color * values,int count)924 cgm_COLOR_TABLE(cgm_state * st, cgm_int index, const cgm_color * values, int count)
925 {
926     int i;
927 
928     OP(COLOR_TABLE);
929     CI(index);
930     for (i = 0; i < count; ++i)
931 	CD(&values[i].rgb);
932     DONE;
933 }
934 
935 cgm_result
cgm_ASPECT_SOURCE_FLAGS(cgm_state * st,const cgm_aspect_source_flag * flags,int count)936 cgm_ASPECT_SOURCE_FLAGS(cgm_state * st, const cgm_aspect_source_flag * flags, int count)
937 {
938     int i;
939 
940     OP(ASPECT_SOURCE_FLAGS);
941     for (i = 0; i < count; ++i) {
942 	E(flags[i].type);
943 	E(flags[i].source);
944 	st->source_flags[flags[i].type] = (byte) flags[i].source;
945     }
946     DONE;
947 }
948 
949 /* ================ Internal routines ================ */
950 
951 /* Begin a command. */
952 private void
begin_command(cgm_state * st,cgm_op_index op)953 begin_command(cgm_state * st, cgm_op_index op)
954 {
955     uint op_word = (uint) op << cgm_op_id_shift;
956 
957     st->command[0] = (byte) (op_word >> 8);
958     st->command[1] = (byte) (op_word);
959     st->command_count = 4;	/* leave room for extension */
960     st->command_first = true;
961     st->result = cgm_result_ok;
962 }
963 
964 /* Write the buffer for a partial command. */
965 /* Note that we always write an even number of bytes. */
966 private void
write_command(cgm_state * st,bool last)967 write_command(cgm_state * st, bool last)
968 {
969     byte *command = st->command;
970     int count = st->command_count;
971 
972     if (st->command_first) {
973 	if (count <= 34) {
974 	    command[2] = command[0];
975 	    command[3] = command[1] + count - 4;
976 	    command += 2, count -= 2;
977 	} else {
978 	    int pcount = count - 4;
979 
980 	    command[1] |= 31;
981 	    command[2] = (byte) (pcount >> 8);
982 	    if (!last)
983 		command[2] |= 0x80;
984 	    command[3] = (byte) pcount;
985 	}
986 	st->command_first = false;
987     } else {
988 	int pcount = count - 2;
989 
990 	command[0] = (byte) (pcount >> 8);
991 	if (!last)
992 	    command[0] |= 0x80;
993 	command[1] = (byte) pcount;
994     }
995     fwrite(command, sizeof(byte), count + (count & 1), st->file);
996     st->command_count = 2;	/* leave room for extension header */
997     if (ferror(st->file))
998 	st->result = cgm_result_io_error;
999 }
1000 
1001 /* End a command. */
1002 private cgm_result
end_command(cgm_state * st)1003 end_command(cgm_state * st)
1004 {
1005     write_command(st, true);
1006     return st->result;
1007 }
1008 
1009 /* Put an integer value. */
1010 private void
put_int(cgm_state * st,cgm_int value,int precision)1011 put_int(cgm_state * st, cgm_int value, int precision)
1012 {
1013     switch (precision) {
1014 	case 32:
1015 	    put_byte(st, value >> 24);
1016 	case 24:
1017 	    put_byte(st, value >> 16);
1018 	case 16:
1019 	    put_byte(st, value >> 8);
1020 	case 8:
1021 	    put_byte(st, value);
1022     }
1023 }
1024 
1025 /* Put a real value. */
1026 private void
put_real(cgm_state * st,cgm_real value,const cgm_precision * pr)1027 put_real(cgm_state * st, cgm_real value, const cgm_precision * pr)
1028 {
1029     if (pr->representation == cgm_representation_floating) {
1030     } else {			/* Casting to integer simply discards the fraction, */
1031 	/* so we need to be careful with negative values. */
1032 	long whole = (long)value;
1033 	double fpart;
1034 
1035 	if (value < whole)
1036 	    --whole;
1037 	fpart = value - whole;
1038 	put_int(st, whole, pr->exponent_or_whole_width);
1039 	if (pr->fraction_width == 16) {
1040 	    uint fraction = (uint) (fpart * (1.0 * 0x10000));
1041 
1042 	    put_int(st, fraction, 16);
1043 	} else {		/* pr->fraction_width == 32 */
1044 	    ulong fraction =
1045 	    (ulong) (fpart * (1.0 * 0x10000 * 0x10000));
1046 
1047 	    put_int(st, fraction, 32);
1048 	}
1049     }
1050 }
1051 
1052 /* Put a real precision. */
1053 private void
put_real_precision(cgm_state * st,const cgm_precision * precision)1054 put_real_precision(cgm_state * st, const cgm_precision * precision)
1055 {
1056     I((int)precision->representation);
1057     I(precision->exponent_or_whole_width);
1058     I(precision->fraction_width);
1059 }
1060 
1061 /* Put a VDC. */
1062 private void
put_vdc(cgm_state * st,const cgm_vdc * pvdc)1063 put_vdc(cgm_state * st, const cgm_vdc * pvdc)
1064 {
1065     if (st->metafile.vdc_type == cgm_vdc_integer)
1066 	put_int(st, pvdc->integer, st->vdc_integer_precision);
1067     else
1068 	put_real(st, pvdc->real, &st->vdc_real_precision);
1069 }
1070 
1071 /* Put a VDC or a real. */
1072 private void
put_vdc_r(cgm_state * st,const cgm_line_marker_extent * extent,cgm_line_marker_specification_mode mode)1073 put_vdc_r(cgm_state * st, const cgm_line_marker_extent * extent,
1074 	  cgm_line_marker_specification_mode mode)
1075 {
1076     if (mode == cgm_line_marker_absolute)
1077 	VDC(&extent->absolute);
1078     else
1079 	R(extent->scaled);
1080 }
1081 
1082 /* Put a point (pair of VDCs). */
1083 private void
put_point(cgm_state * st,const cgm_point * ppt)1084 put_point(cgm_state * st, const cgm_point * ppt)
1085 {
1086     if (st->metafile.vdc_type == cgm_vdc_integer) {
1087 	put_int(st, ppt->integer.x, st->vdc_integer_precision);
1088 	put_int(st, ppt->integer.y, st->vdc_integer_precision);
1089     } else {
1090 	put_real(st, ppt->real.x, &st->vdc_real_precision);
1091 	put_real(st, ppt->real.y, &st->vdc_real_precision);
1092     }
1093 }
1094 
1095 /* Put a list of points. */
1096 private void
put_points(cgm_state * st,const cgm_point * ppt,int count)1097 put_points(cgm_state * st, const cgm_point * ppt, int count)
1098 {
1099     int i;
1100 
1101     for (i = 0; i < count; i++)
1102 	P(ppt + i);
1103 }
1104 
1105 /* Put bytes. */
1106 private void
put_bytes(cgm_state * st,const byte * data,uint length)1107 put_bytes(cgm_state * st, const byte * data, uint length)
1108 {
1109     int count;
1110 
1111     while (length > (count = command_max_count - st->command_count)) {
1112 	memcpy(st->command + st->command_count, data, count);
1113 	st->command_count += count;
1114 	write_command(st, false);
1115 	data += count;
1116 	length -= count;
1117     }
1118     memcpy(st->command + st->command_count, data, length);
1119     st->command_count += length;
1120 }
1121 
1122 /* Put a string. */
1123 private void
put_string(cgm_state * st,const char * data,uint length)1124 put_string(cgm_state * st, const char *data, uint length)
1125 {				/* The CGM specification seems to imply that the continuation */
1126     /* mechanism for commands and the mechanism for strings */
1127     /* are orthogonal; we take this interpretation. */
1128     if (length >= 255) {
1129 	put_byte(st, 255);
1130 	while (length > 32767) {
1131 	    put_int(st, 65535, 2);
1132 	    put_bytes(st, (const byte *)data, 32767);
1133 	    data += 32767;
1134 	    length -= 32767;
1135 	}
1136     }
1137     put_byte(st, length);
1138     put_bytes(st, (const byte *)data, length);
1139 }
1140 
1141 /* Put a color. */
1142 private void
put_color(cgm_state * st,const cgm_color * color)1143 put_color(cgm_state * st, const cgm_color * color)
1144 {
1145     if (st->picture.color_selection_mode == cgm_color_selection_indexed)
1146 	CI(color->index);
1147     else
1148 	CD(&color->rgb);
1149 }
1150 
1151 /* Put an RGB value. */
1152 private void
put_rgb(cgm_state * st,const cgm_rgb * rgb)1153 put_rgb(cgm_state * st, const cgm_rgb * rgb)
1154 {
1155     put_int(st, rgb->r, st->metafile.color_precision);
1156     put_int(st, rgb->g, st->metafile.color_precision);
1157     put_int(st, rgb->b, st->metafile.color_precision);
1158 }
1159