1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
|
/* This file is part of the GNU plotutils package. Copyright (C) 1995,
1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
The GNU plotutils package is free software. You may redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software foundation; either version 2, or (at your
option) any later version.
The GNU plotutils package is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with the GNU plotutils package; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
Boston, MA 02110-1301, USA. */
/* This file contains low-level functions used by CGMPlotters. E.g.,
_cgm_emit_command_header and _cgm_emit_command_terminator, which begin
and end a CGM command. A CGM output file, in either the binary or clear
text encoding, is simply a sequence of CGM commands.
Commands are usually written to the CGMPlotter's output buffer, in which
the current page of graphics (i.e. "picture", in CGM jargon) is stored.
An output buffer (a plOutbuf) is manipulated by the routines in
g_outbuf.c. It includes an array of char, which can grow.
This file also contains _cgm_emit_integer, _cgm_emit_unsigned_integer,
_cgm_emit_point, _cgm_emit_points, _cgm_emit_index, _cgm_emit_enum,
_cgm_emit_color_component, and _cgm_emit_string, etc., routines, which
write the parameters of the command (i.e., its `data') to the output
buffer. The caller invokes zero or more of these routines between a
_cgm_emit_command_header .. _cgm_emit_command_terminator pair.
There is support for specifying a non-default output buffer, i.e., one
not associated with the CGMPlotter in the usual way. That is useful for
preparing the output file's header and trailer, and per-page headers.
See c_defplot.c.
If the binary CGM encoding is used, CGM's data partitioning scheme is
used. As a command and its arguments are emitted, variables that play a
role in implementing the data partitioning scheme are updated via
pointers. These include the number of data bytes written, and the total
number of bytes written as part of the command. The caller should
initialize these variables to zero at the beginning of the CGM command.
There is support for turning off data partitioning. _cgm_emit_integer()
and the other commands for emitting command parameters support a
`no_partitioning' flag argument. This is useful because some CGM
commands take a `structured data record' argument. An SDR is
essentially a string [a sequence of octets], which may be emitted by
calling _cgm_emit_string(), like an ordinary string. However, an SDR
must first be formed by calling a sequence of zero or more such commands
as _cgm_emit_integer() etc., with output to a plOutbuf (with data
partitioning turned off, if the binary encoding is used). */
/* Note: in the binary encoding is used, we go to extremes to make the
written-out CGM file portable. E.g., we hand-craft a big-endian
2's-complement representation (the CGM standard) for each integer or
unsigned integer, and write each octet individually to the output buffer
as an unsigned char or char. We don't assume the system represents
integers using 2's complement. We do assume that casting an unsigned
char to a char doesn't change the bit pattern.
The number of octets used in the CGM representation of an integer or
unsigned integer, CGM_BINARY_BYTES_PER_INTEGER, is set in extern.h. It
should NOT be greater than the number of octets used in the system
representation of an unsigned int; see comment below. On nearly all
systems that GNU supports, this maximum value for
CGM_BINARY_BYTES_PER_INTEGER is 4 (it is never 2).
Many CGM files use CGM_BINARY_BYTES_PER_INTEGER = 2. In some old,
noncompliant CGM parsers this value is hard-coded, even though it
shouldn't be. So use higher values (e.g., 3 and 4) with caution. The
"CGM Handbook" says the use of 3, rather than 2 or 4, is very rare. */
#include "sys-defines.h"
#include "extern.h"
/* In the binary encoding, if the data section, i.e., the list of
parameters for the command, contains more than 30 bytes, it is written
in partitioned format. This is the maximum number of data bytes we
place in each block of the partition. Could be as large as 32767, but
we keep it small to avoid a buffer overrun (see comment in g_outbuf.c). */
#define CGM_BINARY_DATA_BYTES_PER_PARTITION 3000
/* How to recognize the beginning of a new block of the partition
(*data_byte_count is the running count of emitted data bytes,
initialized by the caller to zero, and updated throughout the CGM
command). */
#define CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count) \
(((data_len) > 30) && ((*(data_byte_count)) % CGM_BINARY_DATA_BYTES_PER_PARTITION == 0))
/* forward references */
static void cgm_emit_partition_control_word (plOutbuf *outbuf, int data_len, const int *data_byte_count, int *byte_count);
static void double_to_ieee_single_precision (double d, unsigned char output[4]);
static void int_to_cgm_int (int n, unsigned char *cgm_int, int octets_per_cgm_int);
static void unsigned_int_to_cgm_unsigned_int (unsigned int n, unsigned char *cgm_unsigned_int, int octets_per_cgm_unsigned_int);
/* Write the header of a CGM command.
In the clear text encoding, a string (the `op code') is written.
In the binary encoding, a 2-byte word is written: it specifies the CGM
element class and element ID, and `data_len': the number of data bytes
that the caller will write, by subsequently calling the functions that
emit command arguments.
`data_len' includes CGM_BINARY_BYTES_PER_INTEGER bytes for an integer,
and twice that for a point; two bytes for an index or enumerative, and
four bytes for a real. For a string, the number of data bytes can be
computed from the CGM_BINARY_BYTES_PER_STRING() macro. The caller
should initialize *byte_count to zero, and also *data_byte_count (the
latter is updated by the argument-emitting functions). */
void
_cgm_emit_command_header (plOutbuf *outbuf, int cgm_encoding, int element_class, int id, int data_len, int *byte_count, const char *op_code)
{
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
{
int temp;
if (data_len > 30)
data_len = 31; /* set all 5 bits; will partition the data */
temp = (element_class & 017) << 4; /* 4 bits, shifted up by 4 */
temp |= (id >> 3) & 017; /* top 4 of 7 bits, shifted down by 3 */
outbuf->point[0] = (char)(unsigned char)temp;
temp = (id & 0177) << 5; /* lower 3 of 7 bits, shifted up by 5 */
temp |= (data_len & 037); /* 5 bits, not shifted */
outbuf->point[1] = (char)(unsigned char)temp;
_update_buffer_by_added_bytes (outbuf, 2);
(*byte_count) += 2;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, "%s", op_code);
_update_buffer (outbuf);
break;
}
}
/* In the binary encoding, this is called automatically at the beginning of
each data partition, if a partitioned parameter list is used. It writes
a 2-byte big-endian control word that specifies how many data bytes the
partition will contain. It may set a continuation flag in the control
word, to indicate that another data partition will follow. */
static void
cgm_emit_partition_control_word (plOutbuf *outbuf, int data_len, const int *data_byte_count, int *byte_count)
{
int bytes_remaining = data_len - (*data_byte_count);
int bytes_in_partition;
unsigned int control_word;
if (bytes_remaining > CGM_BINARY_DATA_BYTES_PER_PARTITION)
{
bytes_in_partition = CGM_BINARY_DATA_BYTES_PER_PARTITION;
control_word = 1 << 15; /* set continuation flag */
}
else
{
bytes_in_partition = bytes_remaining;
control_word = 0;
}
control_word |= (unsigned int)bytes_in_partition;
/* write control word, big-endian */
outbuf->point[0] = (char)(unsigned char)((control_word >> 8) & 0377);
outbuf->point[1] = (char)(unsigned char)(control_word & 0377);
_update_buffer_by_added_bytes (outbuf, 2);
(*byte_count) += 2;
}
/* Encode a (signed) integer in binary CGM format. This is a big-endian
2's complement format, with k=8*octets_per_cgm_int bits per integer.
The signed integer is clamped to the range -(2^(k-1) - 1) .. (2^(k-1)-1)
and split into octets, with attention paid to the sign bit.
We do not assume the system representation of integers is a 2's
complement format. We do assume that the system uses at least k octets
per unsigned int.
The octets are returned in an array of unsigned chars. Since any of our
output buffers contains an array of char, we'll be assuming that the bit
pattern of chars and unsigned chars is the same, so that we can cast
unsigned chars to chars with impunity. */
static void
int_to_cgm_int (int n, unsigned char *cgm_int, int octets_per_cgm_int)
{
int max_int, i;
unsigned int u;
bool negative = false;
/* clamp integer; we assume here that the system uses at least
octest_per_cgm_int octets per unsigned int, i.e. that the system
precision is at least as great as the CGM precision */
max_int = 0;
for (i = 0; i < (8 * octets_per_cgm_int - 1); i++)
max_int += (1 << i);
if (n > max_int)
n = max_int;
else if (n < -max_int)
n = -max_int;
if (n < 0)
{
int temp;
negative = true;
temp = -(n + 1);
u = (unsigned int)(max_int - temp); /* compute 2's complement */
}
else
u = (unsigned int)n;
for (i = 0; i < octets_per_cgm_int; i++)
{
unsigned char v;
v = 0xff & (u >> (8 * ((octets_per_cgm_int - 1) - i)));
if (i == 0 && negative)
v |= 0x80;
cgm_int[i] = v;
}
}
/* similar to the preceding, but for unsigned ints rather than signed ints */
static void
unsigned_int_to_cgm_unsigned_int (unsigned int n, unsigned char *cgm_unsigned_int, int octets_per_cgm_unsigned_int)
{
unsigned int max_unsigned_int;
int i;
/* clamp unsigned integer; we assume here that the system uses at least
octets_per_cgm_unsigned_int octets per unsigned int, i.e. that the
system precision is at least as great as the CGM precision */
max_unsigned_int = 0;
for (i = 0; i < (8 * octets_per_cgm_unsigned_int); i++)
max_unsigned_int += (1 << i);
if (n > max_unsigned_int)
n = max_unsigned_int;
for (i = 0; i < octets_per_cgm_unsigned_int; i++)
{
unsigned char v;
v = 0xff & (n >> (8 * ((octets_per_cgm_unsigned_int - 1) - i)));
cgm_unsigned_int[i] = v;
}
}
/* Write a (signed) integer in CGM format. In the binary encoding,
CGM_BINARY_BYTES_PER_INTEGER bytes are written. In CGM files the
default value for that parameter (defined in extern.h) is 2, but it can
be increased. */
void
_cgm_emit_integer (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, int x, int data_len, int *data_byte_count, int *byte_count)
{
int i;
unsigned char cgm_int[CGM_BINARY_BYTES_PER_INTEGER];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
int_to_cgm_int (x, cgm_int, CGM_BINARY_BYTES_PER_INTEGER);
for (i = 0; i < CGM_BINARY_BYTES_PER_INTEGER; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %d", x);
_update_buffer (outbuf);
break;
}
}
/* similar to the preceding, but writes an unsigned integer rather than a
signed integer. */
void
_cgm_emit_unsigned_integer (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, unsigned int x, int data_len, int *data_byte_count, int *byte_count)
{
int i;
unsigned char cgm_unsigned_int[CGM_BINARY_BYTES_PER_INTEGER];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
unsigned_int_to_cgm_unsigned_int (x, cgm_unsigned_int, CGM_BINARY_BYTES_PER_INTEGER);
for (i = 0; i < CGM_BINARY_BYTES_PER_INTEGER; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_unsigned_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %u", x);
_update_buffer (outbuf);
break;
}
}
/* similar to the preceding, but writes an `8-bit' unsigned integer (an
unsigned integer in the range 0.255) as a single byte. */
void
_cgm_emit_unsigned_integer_8bit (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, unsigned int x, int data_len, int *data_byte_count, int *byte_count)
{
/* clamp to 0..255 */
if (x > (unsigned int)255)
x = (unsigned int)255;
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(unsigned char)x;
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %u", x);
_update_buffer (outbuf);
break;
}
}
/* Write a point, i.e. a pair of (signed) integers, in CGM format. In the
binary encoding, 2 * CGM_BINARY_BYTES_PER_INTEGER bytes are written. */
void
_cgm_emit_point (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, int x, int y, int data_len, int *data_byte_count, int *byte_count)
{
int i;
unsigned char cgm_int[CGM_BINARY_BYTES_PER_INTEGER];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
int_to_cgm_int (x, cgm_int, CGM_BINARY_BYTES_PER_INTEGER);
for (i = 0; i < CGM_BINARY_BYTES_PER_INTEGER; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
int_to_cgm_int (y, cgm_int, CGM_BINARY_BYTES_PER_INTEGER);
for (i = 0; i < CGM_BINARY_BYTES_PER_INTEGER; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " (%d, %d)", x, y);
_update_buffer (outbuf);
break;
}
}
/* Write a list of points, i.e. a list of pairs of (signed) integers, in
CGM format. */
void
_cgm_emit_points (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, const int *x, const int *y, int npoints, int data_len, int *data_byte_count, int *byte_count)
{
int i, j;
unsigned char cgm_int[CGM_BINARY_BYTES_PER_INTEGER];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
for (j = 0; j < npoints; j++)
{
int_to_cgm_int (x[j], cgm_int, CGM_BINARY_BYTES_PER_INTEGER);
for (i = 0; i < CGM_BINARY_BYTES_PER_INTEGER; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
int_to_cgm_int (y[j], cgm_int, CGM_BINARY_BYTES_PER_INTEGER);
for (i = 0; i < CGM_BINARY_BYTES_PER_INTEGER; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
for (i = 0; i < npoints; i++)
{
sprintf (outbuf->point, " (%d, %d)", x[i], y[i]);
_update_buffer (outbuf);
}
break;
}
}
/* Write an `enumerative', in CGM format. In the binary encoding, 2 bytes
are written. This is just like _cgm_emit_integer, except that the
precision is fixed at 16 bits. In the clear text encoding, a text
string is written. */
void
_cgm_emit_enum (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, int x, int data_len, int *data_byte_count, int *byte_count, const char *text_string)
{
int i;
unsigned char cgm_int[2];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
int_to_cgm_int (x, cgm_int, 2);
for (i = 0; i < 2; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %s", text_string);
_update_buffer (outbuf);
break;
}
}
/* Write an `index' in CGM format. In the binary encoding, 2 bytes are
written. This is just like _cgm_emit_integer, except that we fix the
precision at 16 bits (this could be changed, but according to the "CGM
Handbook", using any other index precision is very rare).
In c_defplot.c, we use this routine also for writing 2-byte integers or
VDC integers (necessary before we reset the integer and VDC integer
precisions). */
void
_cgm_emit_index (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, int x, int data_len, int *data_byte_count, int *byte_count)
{
int i;
unsigned char cgm_int[2];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
int_to_cgm_int (x, cgm_int, 2);
for (i = 0; i < 2; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %d", x);
_update_buffer (outbuf);
break;
}
}
/* Write a `color component' in CGM format. In the binary encoding,
CGM_BINARY_BYTES_PER_COLOR_COMPONENT bytes are written. Valid values
for that parameter (set in extern.h) are 1, 2, 3, 4, but our code in
c_color.c supports only 1 or 2, i.e. 24-bit color or 48-bit color. */
void
_cgm_emit_color_component (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, unsigned int x, int data_len, int *data_byte_count, int *byte_count)
{
int i;
unsigned char cgm_unsigned_int[CGM_BINARY_BYTES_PER_COLOR_COMPONENT];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
unsigned_int_to_cgm_unsigned_int (x, cgm_unsigned_int,
CGM_BINARY_BYTES_PER_COLOR_COMPONENT);
for (i = 0; i < CGM_BINARY_BYTES_PER_COLOR_COMPONENT; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_unsigned_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %u", x);
_update_buffer (outbuf);
break;
}
}
/* Write a real quantity. In the binary encoding, the default CGM
fixed-point format is used. That is 32 bits, with 16 bits for integer
part [including sign bit] and 16 for added fraction in range [0,1);
numbers from -32767.0 to 32768.0- may be represented. In the clear text
encoding, a conventional representation is used. */
void
_cgm_emit_real_fixed_point (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, double x, int data_len, int *data_byte_count, int *byte_count)
{
int x_floor;
unsigned int x_frac;
int i;
unsigned char cgm_int[2], cgm_unsigned_int[2];
/* clamp to range [-32767.0,32767.0] */
if (x < -32767.0)
x = -32767.0;
else if (x > 32767.0)
x = 32767.0;
x_floor = (x >= 0.0 ? (int)x : -1 - ((int)(-x)));
x_frac = (unsigned int)(65536 * (x - x_floor));
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
int_to_cgm_int (x_floor, cgm_int, 2);
for (i = 0; i < 2; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
unsigned_int_to_cgm_unsigned_int (x_frac, cgm_unsigned_int, 2);
for (i = 0; i < 2; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cgm_unsigned_int[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
if (x != 0.0)
sprintf (outbuf->point, " %.8f", x);
else
sprintf (outbuf->point, " 0.0");
_update_buffer (outbuf);
break;
}
}
/* Express a real number (a C `double') in IEEE single precision format:
32 bits, including 1 sign bit, 8 exponent bits, and 23 mantissa bits,
split into 4 octets, i.e. bytes, in big-endian order.
The octets are returned in an array of unsigned chars. Since any of our
output buffers contains an array of char, we'll be assuming that the bit
pattern of chars and unsigned chars is the same, so that we can cast
unsigned chars to chars with impunity. */
static void
double_to_ieee_single_precision (double d, unsigned char output[4])
{
double min_magnitude, max_magnitude, tmp_power, max_power;
bool got_a_bit;
int i, j;
int sign_bit;
int mantissa_bits[23]; /* leading `1' omitted */
int exponent_bits[8];
int biased_exponent = 0; /* usually 1..254, meaning 1-127..254-127 */
int bits[256]; /* as indices, 1..254 are meaningful */
int output_bits[32];
/* compute min, max magnitudes we'll produce */
/* minimum = 2^(1-127) = 2^(-126). This is the minimum non-subnormalized
IEEE single-precision floating point number. */
min_magnitude = 1.0;
for (i = 0; i < 127-1; i++)
min_magnitude /= 2;
/* maximum = 2^(255-127) [1.0 - 2^(-24)] = 2^128 - 2^104
= 1.11111111111111111111111 * 2^(254-127)
= 1.11111111111111111111111 * 2^127
This is the maximum IEEE single-precision floating point number. */
tmp_power = 1.0;
max_magnitude = 0.0;
for (i = 0; i <= 254-127; i++)
{
if (i >= 104)
max_magnitude += tmp_power;
tmp_power *= 2;
}
/* replace NaN by maximum positive value */
if (d != d)
d = max_magnitude;
/* extract sign bit */
if (d < 0.0)
{
sign_bit = 1;
d = -d;
}
else
sign_bit = 0;
/* if nonzero, clamp to allowed range */
if (d != 0.0 && d < min_magnitude)
d = min_magnitude;
else if (d > max_magnitude)
d = max_magnitude;
/* compute max power of two that can occur in binary expansion,
i.e. 2^(254-127) = 2^127 */
max_power = 1.0;
for (i = 0; i < 254-127; i++)
max_power *= 2;
/* compute bits array; location of first `1' will be biased exponent */
for (i = 0; i < 256; i++)
bits[i] = 0;
got_a_bit = false;
for (i = 254, tmp_power = max_power; i >= 1; i--, tmp_power /= 2)
if (d >= tmp_power)
{
if (got_a_bit == false)
{
biased_exponent = i; /* will be in range 1..254, if set */
got_a_bit = true;
}
bits[i] = 1;
d -= tmp_power;
}
if (got_a_bit == false)
/* d = 0.0, use bogus value for biased exponent */
biased_exponent = 0;
/* extract mantissa bits: in bits array, they start after first `1' */
for (j = 0; j < 23; j++)
mantissa_bits[j] = 0;
if (got_a_bit == true)
for (i = biased_exponent - 1, j = 0; i >= 1 && j < 23; i--, j++)
mantissa_bits[j] = bits[i];
/* extract exponent bits; exponent is in range 0..254 */
for (j = 7; j >= 0; j--)
{
exponent_bits[j] = biased_exponent % 2;
biased_exponent /= 2;
}
/* construct output array of 32 bits */
output_bits[0] = sign_bit;
for (j = 0; j < 8; j++)
output_bits[j + 1] = exponent_bits[j];
for (j = 0; j < 23; j++)
output_bits[j + 9] = mantissa_bits[j];
for (j = 0; j < 4; j++)
output[j] = (unsigned char)0;
for (j = 0; j < 32; j++)
if (output_bits[j] == 1)
output[j / 8] |= (1 << ((31 - j) % 8));
}
/* Write a real quantity. Like the _cgm_emit_real_fixed_point, but in the
binary encoding, rather than a fixed-point format, a floating-point
format is used. In particular, IEEE single-precision format, occupying
32 bits; split into octets in big-endian order.
A CGMPlotter calls this function only to write a mandatory `scaling
factor' that is probably bogus. See c_defplot.c. */
void
_cgm_emit_real_floating_point (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, double x, int data_len, int *data_byte_count, int *byte_count)
{
int i;
unsigned char cp[4];
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
double_to_ieee_single_precision (x, cp);
for (i = 0; i < 4; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = (char)(cp[i]);
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
sprintf (outbuf->point, " %.8f", x);
_update_buffer (outbuf);
break;
}
}
/* Write a string, in CGM format.
In the binary encoding, string encoding depends on string length. (1)
If length <= 254 bytes, the length is prepended to the string, as a
single byte. (2) If length >= 255 bytes, the encoding begins with a
byte equal to 255. Then there is a sixteen-bit word containing a length
(up to 32767) and a continuation flag, followed by data bytes; the two
of them constitute a `string partition', which may be repeated
arbitrarily many times. We use at most CGM_STRING_PARTITION_SIZE data
bytes in a partition, rather than 32767, to avoid buffer overrun; see
comment above. The total byte length of the encoded string, if
string_length=original length, equals
CGM_BINARY_BYTES_PER_STRING(string_length). This macro is defined in
extern.h.
In the clear text encoding, we surround the string by quotes, and escape
any quote that it contains by doubling it. We use single quotes unless
the `use_double_quotes' flag is set. */
void
_cgm_emit_string (plOutbuf *outbuf, bool no_partitioning, int cgm_encoding, const char *s, int string_length, bool use_double_quotes, int data_len, int *data_byte_count, int *byte_count)
{
int i, encoded_string_length;
const char *sp = s;
char *t, *tp, c;
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
{
#if 0
fprintf (stderr, "cgm_emit_string(), length=%d\n", string_length);
for (i = 0; i < string_length; i++)
putc (s[i], stderr);
putc ('\n', stderr);
#endif
/* first, encode the string */
encoded_string_length = CGM_BINARY_BYTES_PER_STRING(string_length);
tp = t = (char *)_pl_xmalloc (encoded_string_length * sizeof(char));
if (string_length <= 254)
{
/* begin with `count' byte, follow by original string */
*tp++ = (char)(unsigned char)string_length;
for (i = 0; i < string_length; i++)
*tp++ = *sp++;
}
else
{
/* first byte is `255' */
*tp++ = (char)255;
/* copy data bytes, with string partition headers interpolated
as needed; `i' counts data bytes copied */
for (i = 0; i < string_length; i++, sp++)
{
if (i % CGM_STRING_PARTITION_SIZE == 0)
/* write two-byte string partition header */
{
int bytes_remaining = string_length - i;
int string_header_word;
if (bytes_remaining <= CGM_STRING_PARTITION_SIZE)
string_header_word = bytes_remaining;
else
/* must continue; set continuation flag */
{
string_header_word = (1 << 15);
string_header_word |= CGM_STRING_PARTITION_SIZE;
}
/* write string partition header word, big-endian */
*tp++ = (char)((string_header_word >> 8) & 0377);
*tp++ = (char)(string_header_word & 0377);
}
/* copy byte */
*tp++ = *sp;
}
}
/* copy encoded string to output buffer; it may require more than
one data partition */
for (i = 0; i < encoded_string_length; i++)
{
if (no_partitioning == false
&& CGM_BINARY_DATA_PARTITION_BEGINS(data_len, data_byte_count))
cgm_emit_partition_control_word (outbuf, data_len, data_byte_count, byte_count);
*(outbuf->point) = t[i];
_update_buffer_by_added_bytes (outbuf, 1);
(*data_byte_count)++;
(*byte_count)++;
}
/* free encoded string */
free (t);
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
{
/* allocate space for encoded string, including initial and final
quotes, a space for readability, and a final NULL */
encoded_string_length = 2 * string_length + 3;
tp = t = (char *)_pl_xmalloc ((encoded_string_length + 1) * sizeof(char));
/* begin with a space for readability, and a quote */
*tp++ = ' ';
*tp++ = (use_double_quotes ? '"' : '\'');
while ((c = *sp++) != '\0')
{
/* escape all quotes by doubling them */
if (((use_double_quotes == true) && c == '"')
|| ((use_double_quotes == false) && c == '\''))
*tp++ = c;
*tp++ = c;
}
/* end with a quote */
*tp++ = (use_double_quotes ? '"' : '\'');
*tp++ = '\0';
strcpy (outbuf->point, t);
_update_buffer (outbuf);
free (t);
}
break;
}
}
/* Write the terminator of a CGM command. In the binary encoding this
writes a single null if and only if the number of bytes previously
written (kept track of via the `byte_count' pointer) is odd; otherwise
it does nothing. In the clear text encoding it writes ";\n". */
void
_cgm_emit_command_terminator (plOutbuf *outbuf, int cgm_encoding, int *byte_count)
{
switch (cgm_encoding)
{
case CGM_ENCODING_BINARY:
default:
if ((*byte_count) % 2 == 1)
{
*(outbuf->point) = '\0';
_update_buffer_by_added_bytes (outbuf, 1);
(*byte_count)++;
}
break;
case CGM_ENCODING_CHARACTER: /* not supported */
break;
case CGM_ENCODING_CLEAR_TEXT:
strcpy (outbuf->point, ";\n");
_update_buffer (outbuf);
break;
}
}
|