~ubuntu-branches/ubuntu/utopic/texlive-bin/utopic

« back to all changes in this revision

Viewing changes to libs/t1lib/t1lib-5.1.2/lib/type1/spaces.c

  • Committer: Package Import Robot
  • Author(s): Norbert Preining
  • Date: 2012-04-10 10:16:01 UTC
  • mfrom: (1.2.3)
  • Revision ID: package-import@ubuntu.com-20120410101601-7mt8nyn280xrgza6
Tags: 2011.20120410-1
* new upstream checkout:
  - remove decls of popen and pclose (Closes: #64524) (!yow, 5 digit bug!)
  - do not declare getopt in C++, fixes FTBFS with g++ >= 4.7 
    (Closes: #667392)
* add patches (maybe to be included upstream) that allows inclusion of
  one config file in another for (x)dvipdfmx. This will be
  used by the paper code.
* fix description of libptexenc-dev package (Closes: #667694)
* remove xdvik patch, included upstream
* remove conflict with ptex-bin, we are building a transitional package now
* build with internal t1lib, as t1lib is going to disappear in
  wheezy (Closes: #667912) (no, dropping xdvi is not an option!)
  (add a lintian override otherwise this gives a lintian error)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $XConsortium: spaces.c,v 1.4 91/10/10 11:19:16 rws Exp $ */
 
2
/* Copyright International Business Machines, Corp. 1991
 
3
 * All Rights Reserved
 
4
 * Copyright Lexmark International, Inc. 1991
 
5
 * All Rights Reserved
 
6
 *
 
7
 * License to use, copy, modify, and distribute this software and its
 
8
 * documentation for any purpose and without fee is hereby granted,
 
9
 * provided that the above copyright notice appear in all copies and that
 
10
 * both that copyright notice and this permission notice appear in
 
11
 * supporting documentation, and that the name of IBM or Lexmark not be
 
12
 * used in advertising or publicity pertaining to distribution of the
 
13
 * software without specific, written prior permission.
 
14
 *
 
15
 * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
 
16
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
 
17
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 
18
 * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
 
19
 * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
 
20
 * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
 
21
 * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
 
22
 * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
 
23
 * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 
24
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 
25
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 
26
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 
27
 * THIS SOFTWARE.
 
28
 */
 
29
 /* SPACES   CWEB         V0021 ********                             */
 
30
/*
 
31
:h1 id=spaces.SPACES Module - Handles Coordinate Spaces
 
32
 
 
33
This module is responsible for handling the TYPE1IMAGER "XYspace" object.
 
34
 
 
35
&author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
 
36
 
 
37
 
 
38
:h3.Include Files
 
39
*/
 
40
 
 
41
#include <stdio.h>
 
42
#include <stdlib.h>
 
43
#include <string.h>
 
44
 
 
45
#include "types.h"
 
46
#include "objects.h"
 
47
#include "spaces.h"
 
48
#include "paths.h"
 
49
#include "pictures.h"
 
50
#include "fonts.h"
 
51
#include "arith.h"
 
52
#include "trig.h"
 
53
 
 
54
static int FindFfcn();
 
55
static int FindIfcn();
 
56
/*
 
57
:h3.Entry Points Provided to the TYPE1IMAGER User
 
58
*/
 
59
 
 
60
/*SHARED LINE(S) ORIGINATED HERE*/
 
61
 
 
62
/*
 
63
:h3.Entry Points Provided to Other Modules
 
64
*/
 
65
 
 
66
/*
 
67
In addition, other modules call the SPACES module through function
 
68
vectors in the "XYspace" structure.  The entry points accessed that
 
69
way are "FConvert()", "IConvert()", and "ForceFloat()".
 
70
*/
 
71
 
 
72
/*SHARED LINE(S) ORIGINATED HERE*/
 
73
/*
 
74
:h3.Macros and Typedefs Provided to Other Modules
 
75
 
 
76
:h4.Duplicating and Killing Spaces
 
77
 
 
78
Destroying XYspaces is so simple we can do it with a
 
79
macro:
 
80
*/
 
81
 
 
82
/*SHARED LINE(S) ORIGINATED HERE*/
 
83
/*
 
84
On the other hand, duplicating XYspaces is slightly more difficult
 
85
because of the need to keep a unique ID in the space, see
 
86
:hdref refid=dupspace..
 
87
 
 
88
:h4.Fixed Point Pel Representation
 
89
 
 
90
We represent pel positions with fixed point numbers.  This does NOT
 
91
mean integer, but truly means fixed point, with a certain number
 
92
of binary digits (FRACTBITS) representing the fractional part of the
 
93
pel.
 
94
*/
 
95
 
 
96
/*SHARED LINE(S) ORIGINATED HERE*/
 
97
/*
 
98
:h2.Data Structures for Coordinate Spaces and Points
 
99
*/
 
100
/*
 
101
:h3 id=matrix.Matrices
 
102
 
 
103
TYPE1IMAGER uses 2x2 transformation matrices.  We'll use C notation for
 
104
such a matrix (M[2][2]), the first index being rows, the second columns.
 
105
*/
 
106
 
 
107
/*
 
108
:h3.The "doublematrix" Structure
 
109
 
 
110
We frequently find it desirable to store both a matrix and its
 
111
inverse.  We store these in a "doublematrix" structure.
 
112
*/
 
113
 
 
114
/*SHARED LINE(S) ORIGINATED HERE*/
 
115
 
 
116
/*
 
117
:h3.The "XYspace" Structure
 
118
 
 
119
The XYspace structure represents the XYspace object.
 
120
*/
 
121
 
 
122
/*SHARED LINE(S) ORIGINATED HERE*/
 
123
#define    RESERVED  10      /* 'n' IDs are reserved for invalid & immortal spaces */
 
124
/*
 
125
*/
 
126
#define    NEXTID    ((SpaceID < RESERVED) ? (SpaceID = RESERVED) : ++SpaceID)
 
127
 
 
128
static unsigned int SpaceID = 1;
 
129
 
 
130
struct XYspace *CopySpace(S)
 
131
       register struct XYspace *S;
 
132
{
 
133
       S = (struct XYspace *)Allocate(sizeof(struct XYspace), S, 0);
 
134
       S->ID = NEXTID;
 
135
       return(S);
 
136
}
 
137
/*
 
138
:h3.The "fractpoint" Structure
 
139
 
 
140
A fractional point is just a "fractpel" x and y:
 
141
*/
 
142
 
 
143
/*SHARED LINE(S) ORIGINATED HERE*/
 
144
 
 
145
/*
 
146
:h3.Lazy Evaluation of Matrix Inverses
 
147
 
 
148
Calculating the inverse of a matrix is somewhat involved, and we usually
 
149
do not need them.  So, we flag whether or not the space has the inverse
 
150
already calculated:
 
151
*/
 
152
 
 
153
#define    HASINVERSE(flag)   ((flag)&0x80)
 
154
 
 
155
/*
 
156
The following macro forces a space to have an inverse:
 
157
*/
 
158
 
 
159
#define    CoerceInverse(S)   if (!HASINVERSE((S)->flag)) { \
 
160
    MatrixInvert((S)->tofract.normal, (S)->tofract.inverse); (S)->flag |= HASINVERSE(ON); }
 
161
/*
 
162
:h3.IDENTITY Space
 
163
 
 
164
IDENTITY space is (logically) the space corresponding to the identity
 
165
transformation matrix.  However, since all our transformation matrices
 
166
have a common FRACTFLOAT scale factor to convert to 'fractpel's, that
 
167
is actually what we store in 'tofract' matrix of IDENTITY:
 
168
*/
 
169
 
 
170
static struct XYspace identity = { SPACETYPE, ISPERMANENT(ON) + ISIMMORTAL(ON)
 
171
                        + HASINVERSE(ON), 2, /* added 3-26-91 PNM */
 
172
                        NULL, NULL,
 
173
                        NULL, NULL, NULL, NULL,
 
174
                        INVALIDID + 1, 0,
 
175
                        {{{FRACTFLOAT, 0.0}, {0.0, FRACTFLOAT}},
 
176
                        {{1.0/FRACTFLOAT, 0.0}, {0.0, 1.0/FRACTFLOAT}}},
 
177
                        {{0, 0}, {0, 0}} };
 
178
struct XYspace *IDENTITY = &identity;
 
179
 
 
180
/*
 
181
*/
 
182
#define  MAXCONTEXTS   16
 
183
 
 
184
static struct doublematrix contexts[MAXCONTEXTS];
 
185
 
 
186
#ifdef notdef
 
187
 
 
188
static int nextcontext = 1;
 
189
 
 
190
/*SHARED LINE(S) ORIGINATED HERE*/
 
191
 
 
192
#if __STDC__
 
193
#define   pointer          void *
 
194
#else
 
195
#define   pointer          char *
 
196
#endif
 
197
 
 
198
/*
 
199
:h3.FindDeviceContext() - Find the Context Given a Device
 
200
 
 
201
This routine, given a device, returns the index of the device's
 
202
transformation matrix in the context array.  If it cannot find it,
 
203
it will allocate a new array entry and fill it out.
 
204
*/
 
205
 
 
206
static int FindDeviceContext(device)
 
207
       pointer device;       /* device token                                 */
 
208
{
 
209
       DOUBLE M[2][2];       /* temporary matrix                             */
 
210
       float Xres,Yres;      /* device  resolution                           */
 
211
       int orient = -1;      /* device orientation                           */
 
212
       int rc = -1;          /* return code for QueryDeviceState             */
 
213
 
 
214
       if (rc != 0)          /* we only bother with this check once          */
 
215
               abort("Context:  QueryDeviceState didn't work", 44);
 
216
 
 
217
       M[0][0] = M[1][0] = M[0][1] = M[1][1] = 0.0;
 
218
 
 
219
       switch (orient) {
 
220
           case 0:
 
221
               M[0][0] = Xres;  M[1][1] = -Yres;
 
222
               break;
 
223
           case 1:
 
224
               M[1][0] = Yres;  M[0][1] = Xres;
 
225
               break;
 
226
           case 2:
 
227
               M[0][0] = -Xres;  M[1][1] = Yres;
 
228
               break;
 
229
           case 3:
 
230
               M[1][0] = -Yres;  M[0][1] = -Xres;
 
231
               break;
 
232
           default:
 
233
               abort("QueryDeviceState returned invalid orientation", 45);
 
234
       }
 
235
       return(FindContext(M));
 
236
}
 
237
 
 
238
/*
 
239
:h3.FindContext() - Find the Context Given a Matrix
 
240
 
 
241
This routine, given a matrix, returns the index of that matrix matrix in
 
242
the context array.  If it cannot find it, it will allocate a new array
 
243
entry and fill it out.
 
244
*/
 
245
 
 
246
int FindContext(M)
 
247
       DOUBLE M[2][2];       /* array to search for                          */
 
248
{
 
249
       register int i;       /* loop variable for search                     */
 
250
       for (i=0; i < nextcontext; i++)
 
251
               if (M[0][0] == contexts[i].normal[0][0] && M[1][0] == contexts[i].normal[1][0]
 
252
                   && M[0][1] == contexts[i].normal[0][1] && M[1][1] == contexts[i].normal[1][1])
 
253
                       break;
 
254
 
 
255
       if (i >= nextcontext) {
 
256
               if (i >= MAXCONTEXTS)
 
257
                       abort("Context:  out of them", 46);
 
258
               LONGCOPY(contexts[i].normal, M, sizeof(contexts[i].normal));
 
259
               MatrixInvert(M, contexts[i].inverse);
 
260
               nextcontext++;
 
261
       }
 
262
 
 
263
       return(i);
 
264
}
 
265
 
 
266
/*
 
267
:h3.Context() - Create a Coordinate Space for a Device
 
268
 
 
269
This user operator is implemented by first finding the device context
 
270
array index, then transforming IDENTITY space to create an appropriate
 
271
cooridnate space.
 
272
*/
 
273
 
 
274
struct XYspace *Context(device, units)
 
275
       pointer device;       /* device token                                 */
 
276
       DOUBLE units;         /* multiples of one inch                        */
 
277
{
 
278
       DOUBLE M[2][2];       /* device transformation matrix                 */
 
279
       register int n;       /* will hold device context number              */
 
280
       register struct XYspace *S;  /* XYspace constructed                   */
 
281
 
 
282
       IfTrace2((MustTraceCalls),"Context(%x, %f)\n", device, &units);
 
283
 
 
284
       ARGCHECK((device == NULL), "Context of NULLDEVICE not allowed",
 
285
                    NULL, IDENTITY, (0), struct XYspace *);
 
286
       ARGCHECK((units == 0.0), "Context: bad units", NULL, IDENTITY, (0), struct XYspace *);
 
287
 
 
288
       n = FindDeviceContext(device);
 
289
 
 
290
       LONGCOPY(M, contexts[n].normal, sizeof(M));
 
291
 
 
292
       M[0][0] *= units;
 
293
       M[0][1] *= units;
 
294
       M[1][0] *= units;
 
295
       M[1][1] *= units;
 
296
 
 
297
       S = (struct XYspace *)Xform(IDENTITY, M);
 
298
 
 
299
       S->context = n;
 
300
       return(S);
 
301
}
 
302
#endif
 
303
 
 
304
/*
 
305
:h3.ConsiderContext() - Adjust a Matrix to Take Out Device Transform
 
306
 
 
307
Remember, we have :f/x times U times D/ and :f/M/ and and we want :f/x
 
308
times U times M times D/.  An easy way to do this is to calculate
 
309
:f/D sup <-1> times M times D/, because:
 
310
:formula.
 
311
x times U times D times D sup <-1> times M times D = x times U times M times D
 
312
:formula.
 
313
So this subroutine, given an :f/M/and an object, finds the :f/D/ for that
 
314
object and modifies :f/M/ so it is :f/D sup <-1> times M times D/.
 
315
*/
 
316
 
 
317
static void ConsiderContext(obj, M)
 
318
       register struct xobject *obj;  /* object to be transformed            */
 
319
       register DOUBLE M[2][2];    /* matrix (may be changed)                */
 
320
{
 
321
       register int context=0; /* index in contexts array                      */
 
322
 
 
323
       if (obj == NULL) return;
 
324
 
 
325
       if (ISPATHTYPE(obj->type)) {
 
326
               struct segment *path = (struct segment *) obj;
 
327
 
 
328
               context = path->context;
 
329
       }
 
330
       else if (obj->type == SPACETYPE) {
 
331
               struct XYspace *S = (struct XYspace *) obj;
 
332
 
 
333
               context = S->context;
 
334
       }
 
335
       else if (obj->type == PICTURETYPE) {
 
336
 
 
337
       }
 
338
       else
 
339
               context = NULLCONTEXT;
 
340
 
 
341
       if (context != NULLCONTEXT) {
 
342
               MatrixMultiply(contexts[context].inverse, M, M);
 
343
               MatrixMultiply(M, contexts[context].normal, M);
 
344
       }
 
345
}
 
346
 
 
347
/*
 
348
:h2.Conversion from User's X,Y to "fractpel" X,Y
 
349
 
 
350
When the user is building paths (lines, moves, curves, etc.) he passes
 
351
the control points (x,y) for the paths together with an XYspace.  We
 
352
must convert from the user's (x,y) to our internal representation
 
353
which is in pels (fractpels, actually).  This involves transforming
 
354
the user's (x,y) under the coordinate space transformation.  It is
 
355
important that we do this quickly.  So, we store pointers to different
 
356
conversion functions right in the XYspace structure.  This allows us
 
357
to have simpler special case functions for the more commonly
 
358
encountered types of transformations.
 
359
 
 
360
:h3.Convert(), IConvert(), and ForceFloat() - Called Through "XYspace" Structure
 
361
 
 
362
These are functions that fit in the "convert" and "iconvert" function
 
363
pointers in the XYspace structure.  They call the "xconvert", "yconvert",
 
364
"ixconvert", and "iyconvert" as appropriate to actually do the work.
 
365
These secondary routines come in many flavors to handle different
 
366
special cases as quickly as possible.
 
367
*/
 
368
 
 
369
int FXYConvert(pt, S, x, y)
 
370
       register struct fractpoint *pt;  /* point to set                      */
 
371
       register struct XYspace *S;  /* relevant coordinate space             */
 
372
       register DOUBLE x,y;  /* user's coordinates of point                  */
 
373
{
 
374
       pt->x = (*S->xconvert)(S->tofract.normal[0][0], S->tofract.normal[1][0], x, y);
 
375
       pt->y = (*S->yconvert)(S->tofract.normal[0][1], S->tofract.normal[1][1], x, y);
 
376
       return(0);
 
377
       
 
378
}
 
379
 
 
380
int IXYConvert(pt, S, x, y)
 
381
       register struct fractpoint *pt;  /* point to set                      */
 
382
       register struct XYspace *S;  /* relevant coordinate space             */
 
383
       register LONG x,y;    /* user's coordinates of point                  */
 
384
{
 
385
       pt->x = (*S->ixconvert)(S->itofract[0][0], S->itofract[1][0], x, y);
 
386
       pt->y = (*S->iyconvert)(S->itofract[0][1], S->itofract[1][1], x, y);
 
387
       return(0);
 
388
       
 
389
}
 
390
 
 
391
/*
 
392
ForceFloat is a substitute for IConvert(), when we just do not have
 
393
enough significant digits in the coefficients to get high enough
 
394
precision in the answer with fixed point arithmetic.  So, we force the
 
395
integers to floats, and do the arithmetic all with floats:
 
396
*/
 
397
 
 
398
int ForceFloat(pt, S, x, y)
 
399
       register struct fractpoint *pt;  /* point to set                      */
 
400
       register struct XYspace *S;  /* relevant coordinate space             */
 
401
       register LONG x,y;    /* user's coordinates of point                  */
 
402
{
 
403
       (*S->convert)(pt, S, (DOUBLE) x, (DOUBLE) y);
 
404
       return(0);
 
405
       
 
406
}
 
407
 
 
408
/*
 
409
:h3.FXYboth(), FXonly(), FYonly() - Floating Point Conversion
 
410
 
 
411
These are the routines we use when the user has given us floating
 
412
point numbers for x and y. FXYboth() is the general purpose routine;
 
413
FXonly() and FYonly() are special cases when one of the coefficients
 
414
is 0.0.
 
415
*/
 
416
 
 
417
fractpel FXYboth(cx, cy, x, y)
 
418
       register DOUBLE cx,cy;  /* x and y coefficients                       */
 
419
       register DOUBLE x,y;  /* user x,y                                     */
 
420
{
 
421
       register DOUBLE r;    /* temporary float                              */
 
422
 
 
423
       r = x * cx + y * cy;
 
424
       return((fractpel) r);
 
425
}
 
426
 
 
427
/*ARGSUSED*/
 
428
fractpel FXonly(cx, cy, x, y)
 
429
       register DOUBLE cx,cy;  /* x and y coefficients                       */
 
430
       register DOUBLE x,y;  /* user x,y                                     */
 
431
{
 
432
       register DOUBLE r;    /* temporary float                              */
 
433
 
 
434
       r = x * cx;
 
435
       return((fractpel) r);
 
436
}
 
437
 
 
438
/*ARGSUSED*/
 
439
fractpel FYonly(cx, cy, x, y)
 
440
       register DOUBLE cx,cy;  /* x and y coefficients                       */
 
441
       register DOUBLE x,y;  /* user x,y                                     */
 
442
{
 
443
       register DOUBLE r;    /* temporary float                              */
 
444
 
 
445
       r = y * cy;
 
446
       return((fractpel) r);
 
447
}
 
448
 
 
449
/*
 
450
:h3.IXYboth(), IXonly(), IYonly() - Simple Integer Conversion
 
451
 
 
452
These are the routines we use when the user has given us integers for
 
453
x and y, and the coefficients have enough significant digits to
 
454
provide precise answers with only "long" (32 bit?) multiplication.
 
455
IXYboth() is the general purpose routine; IXonly() and IYonly() are
 
456
special cases when one of the coefficients is 0.
 
457
*/
 
458
 
 
459
fractpel IXYboth(cx, cy, x, y)
 
460
       register fractpel cx,cy;  /* x and y coefficients                     */
 
461
       register LONG x,y;    /* user x,y                                     */
 
462
{
 
463
       return(x * cx + y * cy);
 
464
}
 
465
 
 
466
/*ARGSUSED*/
 
467
fractpel IXonly(cx, cy, x, y)
 
468
       register fractpel cx,cy;  /* x and y coefficients                     */
 
469
       register LONG x,y;    /* user x,y                                     */
 
470
{
 
471
       return(x * cx);
 
472
}
 
473
 
 
474
/*ARGSUSED*/
 
475
fractpel IYonly(cx, cy, x, y)
 
476
       register fractpel cx,cy;  /* x and y coefficients                     */
 
477
       register LONG x,y;    /* user x,y                                     */
 
478
{
 
479
       return(y * cy);
 
480
}
 
481
 
 
482
 
 
483
/*
 
484
:h3.FPXYboth(), FPXonly(), FPYonly() - More Involved Integer Conversion
 
485
 
 
486
These are the routines we use when the user has given us integers for
 
487
x and y, but the coefficients do not have enough significant digits to
 
488
provide precise answers with only "long" (32 bit?)  multiplication.
 
489
We have increased the number of significant bits in the coefficients
 
490
by FRACTBITS; therefore we must use "double long" (64 bit?)
 
491
multiplication by calling FPmult().  FPXYboth() is the general purpose
 
492
routine; FPXonly() and FPYonly() are special cases when one of the
 
493
coefficients is 0.
 
494
 
 
495
Note that it is perfectly possible for us to calculate X with the
 
496
"FP" method and Y with the "I" method, or vice versa.  It all depends
 
497
on how the functions in the XYspace structure are filled out.
 
498
*/
 
499
 
 
500
fractpel FPXYboth(cx, cy, x, y)
 
501
       register fractpel cx,cy;  /* x and y coefficients                     */
 
502
       register LONG x,y;    /* user x,y                                     */
 
503
{
 
504
       return( FPmult(x, cx) + FPmult(y, cy) );
 
505
}
 
506
 
 
507
/*ARGSUSED*/
 
508
fractpel FPXonly(cx, cy, x, y)
 
509
       register fractpel cx,cy;  /* x and y coefficients                     */
 
510
       register LONG x,y;    /* user x,y                                     */
 
511
{
 
512
       return( FPmult(x, cx) );
 
513
}
 
514
 
 
515
/*ARGSUSED*/
 
516
fractpel FPYonly(cx, cy, x, y)
 
517
       register fractpel cx,cy;  /* x and y coefficients                     */
 
518
       register LONG x,y;    /* user x,y                                     */
 
519
{
 
520
       return( FPmult(y, cy) );
 
521
}
 
522
 
 
523
 
 
524
 
 
525
/*
 
526
:h3.FillOutFcns() - Determine the Appropriate Functions to Use for Conversion
 
527
 
 
528
This function fills out the "convert" and "iconvert" function pointers
 
529
in an XYspace structure, and also fills the "helper"
 
530
functions that actually do the work.
 
531
*/
 
532
 
 
533
static void FillOutFcns(S)
 
534
       register struct XYspace *S;  /* functions will be set in this structure */
 
535
{
 
536
       S->convert = FXYConvert;
 
537
       S->iconvert = IXYConvert;
 
538
 
 
539
       FindFfcn(S->tofract.normal[0][0], S->tofract.normal[1][0], &S->xconvert);
 
540
       FindFfcn(S->tofract.normal[0][1], S->tofract.normal[1][1], &S->yconvert);
 
541
       FindIfcn(S->tofract.normal[0][0], S->tofract.normal[1][0],
 
542
                &S->itofract[0][0], &S->itofract[1][0], &S->ixconvert);
 
543
       FindIfcn(S->tofract.normal[0][1], S->tofract.normal[1][1],
 
544
                &S->itofract[0][1], &S->itofract[1][1], &S->iyconvert);
 
545
 
 
546
       if (S->ixconvert == NULL || S->iyconvert == NULL)
 
547
                S->iconvert = ForceFloat;
 
548
}
 
549
 
 
550
/*
 
551
:h4.FindFfcn() - Subroutine of FillOutFcns() to Fill Out Floating Functions
 
552
 
 
553
This function tests for the special case of one of the coefficients
 
554
being zero:
 
555
*/
 
556
 
 
557
static int FindFfcn(cx, cy, fcnP)
 
558
       register DOUBLE cx,cy;  /* x and y coefficients                       */
 
559
       register fractpel (**fcnP)();  /* pointer to function to set          */
 
560
{
 
561
       if (cx == 0.0)
 
562
               *fcnP = FYonly;
 
563
       else if (cy == 0.0)
 
564
               *fcnP = FXonly;
 
565
       else
 
566
               *fcnP = FXYboth;
 
567
       return(0);
 
568
       
 
569
}
 
570
 
 
571
/*
 
572
:h4.FindIfcn() - Subroutine of FillOutFcns() to Fill Out Integer Functions
 
573
 
 
574
There are two types of integer functions, the 'I' type and the 'FP' type.
 
575
We use the I type functions when we are satisfied with simple integer
 
576
arithmetic.  We used the FP functions when we feel we need higher
 
577
precision (but still fixed point) arithmetic.  If all else fails,
 
578
we store a NULL indicating that this we should do the conversion in
 
579
floating point.
 
580
*/
 
581
 
 
582
static int FindIfcn(cx, cy, icxP, icyP, fcnP)
 
583
       register DOUBLE cx,cy;  /* x and y coefficients                       */
 
584
       register fractpel *icxP,*icyP;  /* fixed point coefficients to set    */
 
585
       register fractpel (**fcnP)();  /* pointer to function to set          */
 
586
{
 
587
       register fractpel imax;  /* maximum of cx and cy                      */
 
588
 
 
589
       *icxP = cx;
 
590
       *icyP = cy;
 
591
 
 
592
       if (cx != (float) (*icxP) || cy != (float) (*icyP)) {
 
593
/*
 
594
At this point we know our integer approximations of the coefficients
 
595
are not exact.  However, we will still use them if the maximum
 
596
coefficient will not fit in a 'fractpel'.   Of course, we have little
 
597
choice at that point, but we haven't lost that much precision by
 
598
staying with integer arithmetic.  We have enough significant digits
 
599
so that
 
600
any error we introduce is less than one part in 2:sup/16/.
 
601
*/
 
602
 
 
603
               imax = TYPE1_MAX(TYPE1_ABS(*icxP), TYPE1_ABS(*icyP));
 
604
               if (imax < (fractpel) (1<<(FRACTBITS-1)) ) {
 
605
/*
 
606
At this point we know our integer approximations just do not have
 
607
enough significant digits for accuracy.  We will add FRACTBITS
 
608
significant digits to the coefficients (by multiplying them by
 
609
1<<FRACTBITS) and go to the "FP" form of the functions.  First, we
 
610
check to see if we have ANY significant digits at all (that is, if
 
611
imax == 0).  If we don't, we suspect that adding FRACTBITS digits
 
612
won't help, so we punt the whole thing.
 
613
*/
 
614
                       if (imax == 0) {
 
615
                               *fcnP = NULL;
 
616
                               return(0);
 
617
                       }
 
618
                       cx *= FRACTFLOAT;
 
619
                       cy *= FRACTFLOAT;
 
620
                       *icxP = cx;
 
621
                       *icyP = cy;
 
622
                       *fcnP = FPXYboth;
 
623
               }
 
624
               else
 
625
                       *fcnP = IXYboth;
 
626
       }
 
627
       else
 
628
               *fcnP = IXYboth;
 
629
/*
 
630
Now we check for special cases where one coefficient is zero (after
 
631
integer conversion):
 
632
*/
 
633
       if (*icxP == 0)
 
634
               *fcnP = (*fcnP == FPXYboth) ? FPYonly : IYonly;
 
635
       else if (*icyP == 0)
 
636
               *fcnP = (*fcnP == FPXYboth) ? FPXonly : IXonly;
 
637
       return(0);
 
638
       
 
639
}
 
640
/*
 
641
:h3.UnConvert() - Find User Coordinates From FractPoints
 
642
 
 
643
The interesting thing with this routine is that we avoid calculating
 
644
the matrix inverse of the device transformation until we really need
 
645
it, which is to say, until this routine is called for the first time
 
646
with a given coordinate space.
 
647
 
 
648
We also only calculate it only once.  If the inverted matrix is valid,
 
649
we don't calculate it; if not, we do.  We never expect matrices with
 
650
zero determinants, so by convention, we mark the matrix is invalid by
 
651
marking both X terms zero.
 
652
*/
 
653
 
 
654
void UnConvert(S, pt, xp, yp)
 
655
       register struct XYspace *S;  /* relevant coordinate space             */
 
656
       register struct fractpoint *pt;  /* device coordinates                */
 
657
       DOUBLE *xp,*yp;       /* where to store resulting x,y                 */
 
658
{
 
659
       DOUBLE x,y;
 
660
 
 
661
       CoerceInverse(S);
 
662
       x = pt->x;
 
663
       y = pt->y;
 
664
       *xp = S->tofract.inverse[0][0] * x + S->tofract.inverse[1][0] * y;
 
665
       *yp = S->tofract.inverse[0][1] * x + S->tofract.inverse[1][1] * y;
 
666
}
 
667
 
 
668
/*
 
669
:h2.Transformations
 
670
*/
 
671
/*
 
672
:h3 id=xform.Xform() - Transform Object in X and Y
 
673
 
 
674
TYPE1IMAGER wants transformations of objects like paths to be identical
 
675
to transformations of spaces.  For example, if you scale a line(1,1)
 
676
by 10 it should yield the same result as generating the line(1,1) in
 
677
a coordinate space that has been scaled by 10.
 
678
 
 
679
We handle fonts by storing the accumulated transform, for example, SR
 
680
(accumulating on the right).  Then when we map the font through space TD,
 
681
for example, we multiply the accumulated font transform on the left by
 
682
the space transform on the right, yielding SRTD in this case.  We will
 
683
get the same result if we did S, then R, then T on the space and mapping
 
684
an unmodified font through that space.
 
685
*/
 
686
 
 
687
struct xobject *t1_Xform(obj, M)
 
688
       register struct xobject *obj;  /* object to transform                 */
 
689
       register DOUBLE M[2][2];    /* transformation matrix                  */
 
690
{
 
691
       if (obj == NULL)
 
692
               return(NULL);
 
693
 
 
694
       if (obj->type == FONTTYPE) {
 
695
               register struct font *F = (struct font *) obj;
 
696
 
 
697
               F = UniqueFont(F);
 
698
               return((struct xobject*)F);
 
699
       }
 
700
       if (obj->type == PICTURETYPE) {
 
701
/*
 
702
In the case of a picture, we choose both to update the picture's
 
703
transformation matrix and keep the handles up to date.
 
704
*/
 
705
               register struct picture *P = (struct picture *) obj;
 
706
               register struct segment *handles;  /* temporary path to transform handles */
 
707
 
 
708
               P = UniquePicture(P);
 
709
               handles = PathSegment(LINETYPE, P->origin.x, P->origin.y);
 
710
               handles = Join(handles,
 
711
                              PathSegment(LINETYPE, P->ending.x, P->ending.y) );
 
712
               handles = (struct segment *)Xform((struct xobject *) handles, M);
 
713
               P->origin = handles->dest;
 
714
               P->ending = handles->link->dest;
 
715
               KillPath(handles);
 
716
               return((struct xobject *)P);
 
717
       }
 
718
 
 
719
       if (ISPATHTYPE(obj->type)) {
 
720
               struct XYspace pseudo;  /* local temporary space              */
 
721
               PseudoSpace(&pseudo, M);
 
722
               return((struct xobject *) PathTransform(obj, &pseudo));
 
723
       }
 
724
 
 
725
 
 
726
       if (obj->type == SPACETYPE) {
 
727
               register struct XYspace *S = (struct XYspace *) obj;
 
728
 
 
729
/* replaced ISPERMANENT(S->flag) with S->references > 1 3-26-91 PNM */
 
730
               if (S->references > 1)
 
731
                       S = CopySpace(S);
 
732
               else
 
733
                       S->ID = NEXTID;
 
734
 
 
735
               MatrixMultiply(S->tofract.normal, M, S->tofract.normal);
 
736
               /*
 
737
               * mark inverted matrix invalid:
 
738
               */
 
739
               S->flag &= ~HASINVERSE(ON);
 
740
 
 
741
               FillOutFcns(S);
 
742
               return((struct xobject *) S);
 
743
       }
 
744
 
 
745
       return(ArgErr("Untransformable object", obj, obj));
 
746
}
 
747
 
 
748
/*
 
749
:h3.Transform() - Transform an Object
 
750
 
 
751
This is the external user's entry point.
 
752
*/
 
753
struct xobject *t1_Transform(obj, cxx, cyx, cxy, cyy)
 
754
       struct xobject *obj;
 
755
       DOUBLE cxx,cyx,cxy,cyy;  /* 2x2 transform matrix elements in row order */
 
756
{
 
757
       DOUBLE M[2][2];
 
758
 
 
759
       IfTrace1((MustTraceCalls),"Transform(%p,", obj);
 
760
       IfTrace4((MustTraceCalls)," %f %f %f %f)\n", cxx, cyx, cxy, cyy);
 
761
 
 
762
       M[0][0] = cxx;
 
763
       M[0][1] = cyx;
 
764
       M[1][0] = cxy;
 
765
       M[1][1] = cyy;
 
766
       ConsiderContext(obj, M);
 
767
       return(Xform(obj, M));
 
768
}
 
769
/*
 
770
:h3.Scale() - Special Case of Transform()
 
771
 
 
772
This is a user operator.
 
773
*/
 
774
 
 
775
struct xobject *t1_Scale(obj, sx, sy)
 
776
       struct xobject *obj;  /* object to scale                              */
 
777
       DOUBLE sx,sy;         /* scale factors in x and y                     */
 
778
{
 
779
       DOUBLE M[2][2];
 
780
       IfTrace3((MustTraceCalls),"Scale(%p, %f, %f)\n", obj, sx, sy);
 
781
       M[0][0] = sx;
 
782
       M[1][1] = sy;
 
783
       M[1][0] = M[0][1] = 0.0;
 
784
       ConsiderContext(obj, M);
 
785
       return(Xform(obj, M));
 
786
}
 
787
 
 
788
/*
 
789
:h3 id=rotate.Rotate() - Special Case of Transform()
 
790
 
 
791
We special-case different settings of 'degrees' for performance
 
792
and accuracy within the DegreeSin() and DegreeCos() routines themselves.
 
793
*/
 
794
 
 
795
#ifdef notdef
 
796
struct xobject *xiRotate(obj, degrees)
 
797
       struct xobject *obj;  /* object to be transformed                     */
 
798
       DOUBLE degrees;       /* degrees of COUNTER-clockwise rotation        */
 
799
{
 
800
       DOUBLE M[2][2];
 
801
 
 
802
 
 
803
       IfTrace2((MustTraceCalls),"Rotate(%z, %f)\n", obj, &degrees);
 
804
 
 
805
       M[0][0] = M[1][1] = DegreeCos(degrees);
 
806
       M[1][0] = - (M[0][1] = DegreeSin(degrees));
 
807
       ConsiderContext(obj, M);
 
808
       return(Xform(obj, M));
 
809
}
 
810
#endif
 
811
 
 
812
/*
 
813
:h3.PseudoSpace() - Build a Coordinate Space from a Matrix
 
814
 
 
815
Since we have built all this optimized code that, given an (x,y) and
 
816
a coordinate space, yield transformed (x,y), it seems a shame not to
 
817
use the same logic when we need to multiply an (x,y) by an arbitrary
 
818
matrix that is not (initially) part of a coordinate space.  This
 
819
subroutine takes the arbitrary matrix and builds a coordinate
 
820
space, with all its nifty function pointers.
 
821
*/
 
822
 
 
823
void PseudoSpace(S, M)
 
824
       struct XYspace *S;    /* coordinate space structure to fill out       */
 
825
       DOUBLE M[2][2];       /* matrix that will become 'tofract.normal'     */
 
826
{
 
827
       S->type = SPACETYPE;
 
828
       S->flag = ISPERMANENT(ON) + ISIMMORTAL(ON);
 
829
       S->references = 2;   /* 3-26-91 added PNM  */
 
830
       S->tofract.normal[0][0] = M[0][0];
 
831
       S->tofract.normal[1][0] = M[1][0];
 
832
       S->tofract.normal[0][1] = M[0][1];
 
833
       S->tofract.normal[1][1] = M[1][1];
 
834
 
 
835
       FillOutFcns(S);
 
836
}
 
837
 
 
838
/*
 
839
:h2 id=matrixa.Matrix Arithmetic
 
840
 
 
841
Following the convention in Newman and Sproull, :hp1/Interactive
 
842
Computer Graphics/,
 
843
matrices are organized:
 
844
:xmp.
 
845
       | cxx   cyx |
 
846
       | cxy   cyy |
 
847
:exmp.
 
848
A point is horizontal, for example:
 
849
:xmp.
 
850
       [ x y ]
 
851
:exmp.
 
852
This means that:
 
853
:formula/x prime = cxx times x + cxy times y/
 
854
:formula/y prime = cyx times x + cyy times y/
 
855
I've seen the other convention, where transform matrices are
 
856
transposed, equally often in the literature.
 
857
*/
 
858
 
 
859
/*
 
860
:h3.MatrixMultiply() - Implements Multiplication of Two Matrices
 
861
 
 
862
Implements matrix multiplication, A * B = C.
 
863
 
 
864
To remind myself, matrix multiplication goes rows of A times columns
 
865
of B.
 
866
The output matrix may be the same as one of the input matrices.
 
867
*/
 
868
void MatrixMultiply(A, B, C)
 
869
       register DOUBLE A[2][2],B[2][2];  /* input matrices                   */
 
870
       register DOUBLE C[2][2];    /* output matrix                          */
 
871
{
 
872
       register DOUBLE txx,txy,tyx,tyy;
 
873
 
 
874
       txx = A[0][0] * B[0][0] + A[0][1] * B[1][0];
 
875
       txy = A[1][0] * B[0][0] + A[1][1] * B[1][0];
 
876
       tyx = A[0][0] * B[0][1] + A[0][1] * B[1][1];
 
877
       tyy = A[1][0] * B[0][1] + A[1][1] * B[1][1];
 
878
 
 
879
       C[0][0] = txx;
 
880
       C[1][0] = txy;
 
881
       C[0][1] = tyx;
 
882
       C[1][1] = tyy;
 
883
}
 
884
/*
 
885
:h3.MatrixInvert() - Invert a Matrix
 
886
 
 
887
My reference for matrix inversion was :hp1/Elementary Linear Algebra/
 
888
by Paul C. Shields, Worth Publishers, Inc., 1968.
 
889
*/
 
890
void MatrixInvert(M, Mprime)
 
891
       DOUBLE M[2][2];       /* input matrix                                 */
 
892
       DOUBLE Mprime[2][2];    /* output inverted matrix                     */
 
893
{
 
894
       register DOUBLE D;    /* determinant of matrix M                      */
 
895
       register DOUBLE txx,txy,tyx,tyy;
 
896
 
 
897
       txx = M[0][0];
 
898
       txy = M[1][0];
 
899
       tyx = M[0][1];
 
900
       tyy = M[1][1];
 
901
 
 
902
       D = M[1][1] * M[0][0] - M[1][0] * M[0][1];
 
903
       if (D == 0.0)
 
904
               abort("MatrixInvert:  can't", 47);
 
905
 
 
906
       Mprime[0][0] = tyy / D;
 
907
       Mprime[1][0] = -txy / D;
 
908
       Mprime[0][1] = -tyx / D;
 
909
       Mprime[1][1] = txx / D;
 
910
}
 
911
/*
 
912
:h2.Initialization, Queries, and Debug
 
913
*/
 
914
/*
 
915
:h3.InitSpaces() - Initialize Constant Spaces
 
916
 
 
917
For compatibility, we initialize a coordinate space called USER which
 
918
maps 72nds of an inch to pels on the default device.
 
919
*/
 
920
 
 
921
struct XYspace *USER = &identity;
 
922
 
 
923
void InitSpaces()
 
924
{
 
925
  /* extern char *DEFAULTDEVICE; */
 
926
 
 
927
       IDENTITY->type = SPACETYPE;
 
928
       FillOutFcns(IDENTITY);
 
929
 
 
930
       contexts[NULLCONTEXT].normal[1][0]
 
931
             = contexts[NULLCONTEXT].normal[0][1]
 
932
             = contexts[NULLCONTEXT].inverse[1][0]
 
933
             = contexts[NULLCONTEXT].inverse[0][1] = 0.0;
 
934
       contexts[NULLCONTEXT].normal[0][0]
 
935
             = contexts[NULLCONTEXT].normal[1][1]
 
936
             = contexts[NULLCONTEXT].inverse[0][0]
 
937
             = contexts[NULLCONTEXT].inverse[1][1] = 1.0;
 
938
 
 
939
       USER->flag |= ISIMMORTAL(ON);
 
940
       CoerceInverse(USER);
 
941
}
 
942
/*
 
943
:h3.QuerySpace() - Returns the Transformation Matrix of a Space
 
944
 
 
945
Since the tofract matrix of an XYspace includes the scale factor
 
946
necessary to produce fractpel results (i.e., FRACTFLOAT), this
 
947
must be taken out before we return the matrix to the user.  Fortunately,
 
948
this is simple:  just multiply by the inverse of IDENTITY!
 
949
*/
 
950
 
 
951
void QuerySpace(S, cxxP, cyxP, cxyP, cyyP)
 
952
       register struct XYspace *S;  /* space asked about                     */
 
953
       register DOUBLE *cxxP,*cyxP,*cxyP,*cyyP;  /* where to put answer      */
 
954
{
 
955
       DOUBLE M[2][2];       /* temp matrix to build user's answer           */
 
956
 
 
957
       if (S->type != SPACETYPE) {
 
958
               ArgErr("QuerySpace: not a space", S, NULL);
 
959
               return;
 
960
       }
 
961
       MatrixMultiply(S->tofract.normal, IDENTITY->tofract.inverse, M);
 
962
       *cxxP = M[0][0];
 
963
       *cxyP = M[1][0];
 
964
       *cyxP = M[0][1];
 
965
       *cyyP = M[1][1];
 
966
}
 
967
 
 
968
/*
 
969
:h3.FormatFP() - Format a Fixed Point Pel
 
970
 
 
971
We format the pel as "dddd.XXXX", where XX's are hexidecimal digits,
 
972
and the dd's are decimal digits.  This might be a little confusing
 
973
mixing hexidecimal and decimal like that, but it is convenient
 
974
to use for debug.
 
975
 
 
976
We make sure we have N (FRACTBITS/4) digits past the decimal point.
 
977
*/
 
978
#define  FRACTMASK   ((1<<FRACTBITS)-1)  /* mask for fractional part         */
 
979
 
 
980
void FormatFP(string, fpel)
 
981
       register char *string;  /* output string                              */
 
982
       register fractpel fpel; /* fractional pel input                       */
 
983
{
 
984
       char temp[8];
 
985
       register char *s;
 
986
       register char *sign;
 
987
 
 
988
       if (fpel < 0) {
 
989
               sign = "-";
 
990
               fpel = -fpel;
 
991
       }
 
992
       else
 
993
               sign = "";
 
994
 
 
995
       sprintf(temp, "000%x", fpel & FRACTMASK);
 
996
       s = temp + strlen(temp) - (FRACTBITS/4);
 
997
 
 
998
       sprintf(string, "%s%d.%sx", sign, fpel >> FRACTBITS, s);
 
999
}
 
1000
 
 
1001
/*
 
1002
:h3.DumpSpace() - Display a Coordinate Space
 
1003
*/
 
1004
/*ARGSUSED*/
 
1005
void DumpSpace(S)
 
1006
       register struct XYspace *S;
 
1007
{
 
1008
       IfTrace4(TRUE,"--Coordinate space at %p,ID=%d,convert=%p,iconvert=%p\n",
 
1009
                   S, S->ID, S->convert, S->iconvert);
 
1010
       IfTrace2(TRUE,"             |  %12.3f  %12.3f  |",
 
1011
                   S->tofract.normal[0][0], S->tofract.normal[0][1]);
 
1012
       IfTrace2(TRUE,"   [  %d  %d ]\n", S->itofract[0][0], S->itofract[0][1]);
 
1013
       IfTrace2(TRUE,"             |  %12.3f  %12.3f  |",
 
1014
                   S->tofract.normal[1][0], S->tofract.normal[1][1]);
 
1015
       IfTrace2(TRUE,"   [  %d  %d ]\n", S->itofract[1][0], S->itofract[1][1]);
 
1016
}