]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/lib/font/Type1/spaces.c
Removed Xserver/ directory, it does nothing useful ATM.
[rdpsrv] / Xserver / lib / font / Type1 / spaces.c
diff --git a/Xserver/lib/font/Type1/spaces.c b/Xserver/lib/font/Type1/spaces.c
deleted file mode 100644 (file)
index f026420..0000000
+++ /dev/null
@@ -1,1000 +0,0 @@
-/* $XConsortium: spaces.c,v 1.8 95/06/08 23:20:39 gildea Exp $ */
-/* Copyright International Business Machines, Corp. 1991
- * All Rights Reserved
- * Copyright Lexmark International, Inc. 1991
- * All Rights Reserved
- *
- * License to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted,
- * provided that the above copyright notice appear in all copies and that
- * both that copyright notice and this permission notice appear in
- * supporting documentation, and that the name of IBM or Lexmark not be
- * used in advertising or publicity pertaining to distribution of the
- * software without specific, written prior permission.
- *
- * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
- * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
- * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
- * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
- * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
- * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
- * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
- * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- */
- /* SPACES   CWEB         V0021 ********                             */
-/*
-:h1 id=spaces.SPACES Module - Handles Coordinate Spaces
-This module is responsible for handling the TYPE1IMAGER "XYspace" object.
-&author. Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
-:h3.Include Files
-*/
-#include "objects.h"
-#include "spaces.h"
-#include "paths.h"
-#include "pictures.h"
-#include "fonts.h"
-#include "arith.h"
-#include "trig.h"
-
-static void FindFfcn();
-static void FindIfcn();
-/*
-:h3.Entry Points Provided to the TYPE1IMAGER User
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-/*
-:h3.Entry Points Provided to Other Modules
-*/
-/*
-In addition, other modules call the SPACES module through function
-vectors in the "XYspace" structure.  The entry points accessed that
-way are "FConvert()", "IConvert()", and "ForceFloat()".
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-/*
-:h3.Macros and Typedefs Provided to Other Modules
-:h4.Duplicating and Killing Spaces
-Destroying XYspaces is so simple we can do it with a
-macro:
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-/*
-On the other hand, duplicating XYspaces is slightly more difficult
-because of the need to keep a unique ID in the space, see
-:hdref refid=dupspace..
-:h4.Fixed Point Pel Representation
-We represent pel positions with fixed point numbers.  This does NOT
-mean integer, but truly means fixed point, with a certain number
-of binary digits (FRACTBITS) representing the fractional part of the
-pel.
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-/*
-:h2.Data Structures for Coordinate Spaces and Points
-*/
-/*
-:h3 id=matrix.Matrices
-TYPE1IMAGER uses 2x2 transformation matrices.  We'll use C notation for
-such a matrix (M[2][2]), the first index being rows, the second columns.
-*/
-/*
-:h3.The "doublematrix" Structure
-We frequently find it desirable to store both a matrix and its
-inverse.  We store these in a "doublematrix" structure.
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-/*
-:h3.The "XYspace" Structure
-The XYspace structure represents the XYspace object.
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-#define    RESERVED  10      /* 'n' IDs are reserved for invalid & immortal spaces */
-/*
-*/
-#define    NEXTID    ((SpaceID < RESERVED) ? (SpaceID = RESERVED) : ++SpaceID)
-static unsigned int SpaceID = 1;
-struct XYspace *CopySpace(S)
-       register struct XYspace *S;
-{
-       S = (struct XYspace *)Allocate(sizeof(struct XYspace), S, 0);
-       S->ID = NEXTID;
-       return(S);
-}
-/*
-:h3.The "fractpoint" Structure
-A fractional point is just a "fractpel" x and y:
-*/
-/*SHARED LINE(S) ORIGINATED HERE*/
-/*
-:h3.Lazy Evaluation of Matrix Inverses
-Calculating the inverse of a matrix is somewhat involved, and we usually
-do not need them.  So, we flag whether or not the space has the inverse
-already calculated:
-*/
-#define    HASINVERSE(flag)   ((flag)&0x80)
-/*
-The following macro forces a space to have an inverse:
-*/
-#define    CoerceInverse(S)   if (!HASINVERSE((S)->flag)) { \
-    MatrixInvert((S)->tofract.normal, (S)->tofract.inverse); (S)->flag |= HASINVERSE(ON); }
-/*
-:h3.IDENTITY Space
-IDENTITY space is (logically) the space corresponding to the identity
-transformation matrix.  However, since all our transformation matrices
-have a common FRACTFLOAT scale factor to convert to 'fractpel's, that
-is actually what we store in 'tofract' matrix of IDENTITY:
-*/
-static struct XYspace identity = { SPACETYPE, ISPERMANENT(ON) + ISIMMORTAL(ON)
-                        + HASINVERSE(ON), 2, /* added 3-26-91 PNM */
-                        NULL, NULL,
-                        NULL, NULL, NULL, NULL,
-                        INVALIDID + 1, 0,
-                        FRACTFLOAT, 0.0, 0.0, FRACTFLOAT,
-                        1.0/FRACTFLOAT, 0.0, 0.0, 1.0/FRACTFLOAT,
-                        0, 0, 0, 0 };
-struct XYspace *IDENTITY = &identity;
-/*
-*/
-#define  MAXCONTEXTS   16
-static struct doublematrix contexts[MAXCONTEXTS];
-#ifdef notdef
-
-static int nextcontext = 1;
-/*SHARED LINE(S) ORIGINATED HERE*/
-
-#ifdef __STDC__
-#define   pointer          void *
-#else
-#define   pointer          char *
-#endif
-/*
-:h3.FindDeviceContext() - Find the Context Given a Device
-This routine, given a device, returns the index of the device's
-transformation matrix in the context array.  If it cannot find it,
-it will allocate a new array entry and fill it out.
-*/
-static int FindDeviceContext(device)
-       pointer device;       /* device token                                 */
-{
-       double M[2][2];       /* temporary matrix                             */
-       float Xres,Yres;      /* device  resolution                           */
-       int orient = -1;      /* device orientation                           */
-       int rc = -1;          /* return code for QueryDeviceState             */
-       if (rc != 0)          /* we only bother with this check once          */
-               abort("Context:  QueryDeviceState didn't work");
-       M[0][0] = M[1][0] = M[0][1] = M[1][1] = 0.0;
-       switch (orient) {
-           case 0:
-               M[0][0] = Xres;  M[1][1] = -Yres;
-               break;
-           case 1:
-               M[1][0] = Yres;  M[0][1] = Xres;
-               break;
-           case 2:
-               M[0][0] = -Xres;  M[1][1] = Yres;
-               break;
-           case 3:
-               M[1][0] = -Yres;  M[0][1] = -Xres;
-               break;
-           default:
-               abort("QueryDeviceState returned invalid orientation");
-       }
-       return(FindContext(M));
-}
-/*
-:h3.FindContext() - Find the Context Given a Matrix
-This routine, given a matrix, returns the index of that matrix matrix in
-the context array.  If it cannot find it, it will allocate a new array
-entry and fill it out.
-*/
-int FindContext(M)
-       double M[2][2];       /* array to search for                          */
-{
-       register int i;       /* loop variable for search                     */
-       for (i=0; i < nextcontext; i++)
-               if (M[0][0] == contexts[i].normal[0][0] && M[1][0] == contexts[i].normal[1][0]
-                   && M[0][1] == contexts[i].normal[0][1] && M[1][1] == contexts[i].normal[1][1])
-                       break;
-       if (i >= nextcontext) {
-               if (i >= MAXCONTEXTS)
-                       abort("Context:  out of them");
-               LONGCOPY(contexts[i].normal, M, sizeof(contexts[i].normal));
-               MatrixInvert(M, contexts[i].inverse);
-               nextcontext++;
-       }
-       return(i);
-}
-/*
-:h3.Context() - Create a Coordinate Space for a Device
-This user operator is implemented by first finding the device context
-array index, then transforming IDENTITY space to create an appropriate
-cooridnate space.
-*/
-struct XYspace *Context(device, units)
-       pointer device;       /* device token                                 */
-       double units;         /* multiples of one inch                        */
-{
-       double M[2][2];       /* device transformation matrix                 */
-       register int n;       /* will hold device context number              */
-       register struct XYspace *S;  /* XYspace constructed                   */
-       IfTrace2((MustTraceCalls),"Context(%x, %f)\n", device, &units);
-       ARGCHECK((device == NULL), "Context of NULLDEVICE not allowed",
-                    NULL, IDENTITY, (0), struct XYspace *);
-       ARGCHECK((units == 0.0), "Context: bad units", NULL, IDENTITY, (0), struct XYspace *);
-       n = FindDeviceContext(device);
-       LONGCOPY(M, contexts[n].normal, sizeof(M));
-       M[0][0] *= units;
-       M[0][1] *= units;
-       M[1][0] *= units;
-       M[1][1] *= units;
-       S = (struct XYspace *)Xform(IDENTITY, M);
-       S->context = n;
-       return(S);
-}
-#endif
-/*
-:h3.ConsiderContext() - Adjust a Matrix to Take Out Device Transform
-Remember, we have :f/x times U times D/ and :f/M/ and and we want :f/x
-times U times M times D/.  An easy way to do this is to calculate
-:f/D sup <-1> times M times D/, because:
-:formula.
-x times U times D times D sup <-1> times M times D = x times U times M times D
-:formula.
-So this subroutine, given an :f/M/and an object, finds the :f/D/ for that
-object and modifies :f/M/ so it is :f/D sup <-1> times M times D/.
-*/
-static void ConsiderContext(obj, M)
-       register struct xobject *obj;  /* object to be transformed            */
-       register double M[2][2];    /* matrix (may be changed)                */
-{
-       register int context; /* index in contexts array                      */
-       if (obj == NULL) return;
-       if (ISPATHTYPE(obj->type)) {
-               struct segment *path = (struct segment *) obj;
-               context = path->context;
-       }
-       else if (obj->type == SPACETYPE) {
-               struct XYspace *S = (struct XYspace *) obj;
-               context = S->context;
-       }
-       else if (obj->type == PICTURETYPE) {
-
-       }
-       else
-               context = NULLCONTEXT;
-       if (context != NULLCONTEXT) {
-               MatrixMultiply(contexts[context].inverse, M, M);
-               MatrixMultiply(M, contexts[context].normal, M);
-       }
-}
-/*
-:h2.Conversion from User's X,Y to "fractpel" X,Y
-When the user is building paths (lines, moves, curves, etc.) he passes
-the control points (x,y) for the paths together with an XYspace.  We
-must convert from the user's (x,y) to our internal representation
-which is in pels (fractpels, actually).  This involves transforming
-the user's (x,y) under the coordinate space transformation.  It is
-important that we do this quickly.  So, we store pointers to different
-conversion functions right in the XYspace structure.  This allows us
-to have simpler special case functions for the more commonly
-encountered types of transformations.
-:h3.Convert(), IConvert(), and ForceFloat() - Called Through "XYspace" Structure
-These are functions that fit in the "convert" and "iconvert" function
-pointers in the XYspace structure.  They call the "xconvert", "yconvert",
-"ixconvert", and "iyconvert" as appropriate to actually do the work.
-These secondary routines come in many flavors to handle different
-special cases as quickly as possible.
-*/
-static void FXYConvert(pt, S, x, y)
-       register struct fractpoint *pt;  /* point to set                      */
-       register struct XYspace *S;  /* relevant coordinate space             */
-       register double x,y;  /* user's coordinates of point                  */
-{
-       pt->x = (*S->xconvert)(S->tofract.normal[0][0], S->tofract.normal[1][0], x, y);
-       pt->y = (*S->yconvert)(S->tofract.normal[0][1], S->tofract.normal[1][1], x, y);
-}
-static void IXYConvert(pt, S, x, y)
-       register struct fractpoint *pt;  /* point to set                      */
-       register struct XYspace *S;  /* relevant coordinate space             */
-       register long x,y;    /* user's coordinates of point                  */
-{
-       pt->x = (*S->ixconvert)(S->itofract[0][0], S->itofract[1][0], x, y);
-       pt->y = (*S->iyconvert)(S->itofract[0][1], S->itofract[1][1], x, y);
-}
-/*
-ForceFloat is a substitute for IConvert(), when we just do not have
-enough significant digits in the coefficients to get high enough
-precision in the answer with fixed point arithmetic.  So, we force the
-integers to floats, and do the arithmetic all with floats:
-*/
-static void ForceFloat(pt, S, x, y)
-       register struct fractpoint *pt;  /* point to set                      */
-       register struct XYspace *S;  /* relevant coordinate space             */
-       register long x,y;    /* user's coordinates of point                  */
-{
-       (*S->convert)(pt, S, (double) x, (double) y);
-}
-/*
-:h3.FXYboth(), FXonly(), FYonly() - Floating Point Conversion
-These are the routines we use when the user has given us floating
-point numbers for x and y. FXYboth() is the general purpose routine;
-FXonly() and FYonly() are special cases when one of the coefficients
-is 0.0.
-*/
-static fractpel FXYboth(cx, cy, x, y)
-       register double cx,cy;  /* x and y coefficients                       */
-       register double x,y;  /* user x,y                                     */
-{
-       register double r;    /* temporary float                              */
-       r = x * cx + y * cy;
-       return((fractpel) r);
-}
-/*ARGSUSED*/
-static fractpel FXonly(cx, cy, x, y)
-       register double cx,cy;  /* x and y coefficients                       */
-       register double x,y;  /* user x,y                                     */
-{
-       register double r;    /* temporary float                              */
-       r = x * cx;
-       return((fractpel) r);
-}
-/*ARGSUSED*/
-static fractpel FYonly(cx, cy, x, y)
-       register double cx,cy;  /* x and y coefficients                       */
-       register double x,y;  /* user x,y                                     */
-{
-       register double r;    /* temporary float                              */
-       r = y * cy;
-       return((fractpel) r);
-}
-/*
-:h3.IXYboth(), IXonly(), IYonly() - Simple Integer Conversion
-These are the routines we use when the user has given us integers for
-x and y, and the coefficients have enough significant digits to
-provide precise answers with only "long" (32 bit?) multiplication.
-IXYboth() is the general purpose routine; IXonly() and IYonly() are
-special cases when one of the coefficients is 0.
-*/
-static fractpel IXYboth(cx, cy, x, y)
-       register fractpel cx,cy;  /* x and y coefficients                     */
-       register long x,y;    /* user x,y                                     */
-{
-       return(x * cx + y * cy);
-}
-/*ARGSUSED*/
-static fractpel IXonly(cx, cy, x, y)
-       register fractpel cx,cy;  /* x and y coefficients                     */
-       register long x,y;    /* user x,y                                     */
-{
-       return(x * cx);
-}
-/*ARGSUSED*/
-static fractpel IYonly(cx, cy, x, y)
-       register fractpel cx,cy;  /* x and y coefficients                     */
-       register long x,y;    /* user x,y                                     */
-{
-       return(y * cy);
-}
-/*
-:h3.FPXYboth(), FPXonly(), FPYonly() - More Involved Integer Conversion
-These are the routines we use when the user has given us integers for
-x and y, but the coefficients do not have enough significant digits to
-provide precise answers with only "long" (32 bit?)  multiplication.
-We have increased the number of significant bits in the coefficients
-by FRACTBITS; therefore we must use "double long" (64 bit?)
-multiplication by calling FPmult().  FPXYboth() is the general purpose
-routine; FPXonly() and FPYonly() are special cases when one of the
-coefficients is 0.
-Note that it is perfectly possible for us to calculate X with the
-"FP" method and Y with the "I" method, or vice versa.  It all depends
-on how the functions in the XYspace structure are filled out.
-*/
-static fractpel FPXYboth(cx, cy, x, y)
-       register fractpel cx,cy;  /* x and y coefficients                     */
-       register long x,y;    /* user x,y                                     */
-{
-       return( FPmult(x, cx) + FPmult(y, cy) );
-}
-/*ARGSUSED*/
-static fractpel FPXonly(cx, cy, x, y)
-       register fractpel cx,cy;  /* x and y coefficients                     */
-       register long x,y;    /* user x,y                                     */
-{
-       return( FPmult(x, cx) );
-}
-/*ARGSUSED*/
-static fractpel FPYonly(cx, cy, x, y)
-       register fractpel cx,cy;  /* x and y coefficients                     */
-       register long x,y;    /* user x,y                                     */
-{
-       return( FPmult(y, cy) );
-}
-/*
-:h3.FillOutFcns() - Determine the Appropriate Functions to Use for Conversion
-This function fills out the "convert" and "iconvert" function pointers
-in an XYspace structure, and also fills the "helper"
-functions that actually do the work.
-*/
-static void FillOutFcns(S)
-       register struct XYspace *S;  /* functions will be set in this structure */
-{
-       S->convert = FXYConvert;
-       S->iconvert = IXYConvert;
-       FindFfcn(S->tofract.normal[0][0], S->tofract.normal[1][0], &S->xconvert);
-       FindFfcn(S->tofract.normal[0][1], S->tofract.normal[1][1], &S->yconvert);
-       FindIfcn(S->tofract.normal[0][0], S->tofract.normal[1][0],
-                &S->itofract[0][0], &S->itofract[1][0], &S->ixconvert);
-       FindIfcn(S->tofract.normal[0][1], S->tofract.normal[1][1],
-                &S->itofract[0][1], &S->itofract[1][1], &S->iyconvert);
-       if (S->ixconvert == NULL || S->iyconvert == NULL)
-                S->iconvert = ForceFloat;
-}
-/*
-:h4.FindFfcn() - Subroutine of FillOutFcns() to Fill Out Floating Functions
-This function tests for the special case of one of the coefficients
-being zero:
-*/
-static void FindFfcn(cx, cy, fcnP)
-       register double cx,cy;  /* x and y coefficients                       */
-       register fractpel (**fcnP)();  /* pointer to function to set          */
-{
-       if (cx == 0.0)
-               *fcnP = FYonly;
-       else if (cy == 0.0)
-               *fcnP = FXonly;
-       else
-               *fcnP = FXYboth;
-}
-/*
-:h4.FindIfcn() - Subroutine of FillOutFcns() to Fill Out Integer Functions
-There are two types of integer functions, the 'I' type and the 'FP' type.
-We use the I type functions when we are satisfied with simple integer
-arithmetic.  We used the FP functions when we feel we need higher
-precision (but still fixed point) arithmetic.  If all else fails,
-we store a NULL indicating that this we should do the conversion in
-floating point.
-*/
-static void FindIfcn(cx, cy, icxP, icyP, fcnP)
-       register double cx,cy;  /* x and y coefficients                       */
-       register fractpel *icxP,*icyP;  /* fixed point coefficients to set    */
-       register fractpel (**fcnP)();  /* pointer to function to set          */
-{
-       register fractpel imax;  /* maximum of cx and cy                      */
-       *icxP = cx;
-       *icyP = cy;
-       if (cx != (float) (*icxP) || cy != (float) (*icyP)) {
-/*
-At this point we know our integer approximations of the coefficients
-are not exact.  However, we will still use them if the maximum
-coefficient will not fit in a 'fractpel'.   Of course, we have little
-choice at that point, but we haven't lost that much precision by
-staying with integer arithmetic.  We have enough significant digits
-so that
-any error we introduce is less than one part in 2:sup/16/.
-*/
-               imax = MAX(ABS(*icxP), ABS(*icyP));
-               if (imax < (fractpel) (1<<(FRACTBITS-1)) ) {
-/*
-At this point we know our integer approximations just do not have
-enough significant digits for accuracy.  We will add FRACTBITS
-significant digits to the coefficients (by multiplying them by
-1<<FRACTBITS) and go to the "FP" form of the functions.  First, we
-check to see if we have ANY significant digits at all (that is, if
-imax == 0).  If we don't, we suspect that adding FRACTBITS digits
-won't help, so we punt the whole thing.
-*/
-                       if (imax == 0) {
-                               *fcnP = NULL;
-                               return;
-                       }
-                       cx *= FRACTFLOAT;
-                       cy *= FRACTFLOAT;
-                       *icxP = cx;
-                       *icyP = cy;
-                       *fcnP = FPXYboth;
-               }
-               else
-                       *fcnP = IXYboth;
-       }
-       else
-               *fcnP = IXYboth;
-/*
-Now we check for special cases where one coefficient is zero (after
-integer conversion):
-*/
-       if (*icxP == 0)
-               *fcnP = (*fcnP == FPXYboth) ? FPYonly : IYonly;
-       else if (*icyP == 0)
-               *fcnP = (*fcnP == FPXYboth) ? FPXonly : IXonly;
-}
-/*
-:h3.UnConvert() - Find User Coordinates From FractPoints
-The interesting thing with this routine is that we avoid calculating
-the matrix inverse of the device transformation until we really need
-it, which is to say, until this routine is called for the first time
-with a given coordinate space.
-We also only calculate it only once.  If the inverted matrix is valid,
-we don't calculate it; if not, we do.  We never expect matrices with
-zero determinants, so by convention, we mark the matrix is invalid by
-marking both X terms zero.
-*/
-void UnConvert(S, pt, xp, yp)
-       register struct XYspace *S;  /* relevant coordinate space             */
-       register struct fractpoint *pt;  /* device coordinates                */
-       double *xp,*yp;       /* where to store resulting x,y                 */
-{
-       double x,y;
-       CoerceInverse(S);
-       x = pt->x;
-       y = pt->y;
-       *xp = S->tofract.inverse[0][0] * x + S->tofract.inverse[1][0] * y;
-       *yp = S->tofract.inverse[0][1] * x + S->tofract.inverse[1][1] * y;
-}
-/*
-:h2.Transformations
-*/
-/*
-:h3 id=xform.Xform() - Transform Object in X and Y
-TYPE1IMAGER wants transformations of objects like paths to be identical
-to transformations of spaces.  For example, if you scale a line(1,1)
-by 10 it should yield the same result as generating the line(1,1) in
-a coordinate space that has been scaled by 10.
-We handle fonts by storing the accumulated transform, for example, SR
-(accumulating on the right).  Then when we map the font through space TD,
-for example, we multiply the accumulated font transform on the left by
-the space transform on the right, yielding SRTD in this case.  We will
-get the same result if we did S, then R, then T on the space and mapping
-an unmodified font through that space.
-*/
-struct xobject *t1_Xform(obj, M)
-       register struct xobject *obj;  /* object to transform                 */
-       register double M[2][2];    /* transformation matrix                  */
-{
-       if (obj == NULL)
-               return(NULL);
-       if (obj->type == FONTTYPE) {
-               register struct font *F = (struct font *) obj;
-               F = UniqueFont(F);
-               return((struct xobject*)F);
-       }
-       if (obj->type == PICTURETYPE) {
-/*
-In the case of a picture, we choose both to update the picture's
-transformation matrix and keep the handles up to date.
-*/
-               register struct picture *P = (struct picture *) obj;
-               register struct segment *handles;  /* temporary path to transform handles */
-               P = UniquePicture(P);
-               handles = PathSegment(LINETYPE, P->origin.x, P->origin.y);
-               handles = Join(handles,
-                              PathSegment(LINETYPE, P->ending.x, P->ending.y) );
-               handles = (struct segment *)Xform((struct xobject *) handles, M);
-               P->origin = handles->dest;
-               P->ending = handles->link->dest;
-               KillPath(handles);
-               return((struct xobject *)P);
-       }
-       if (ISPATHTYPE(obj->type)) {
-               struct XYspace pseudo;  /* local temporary space              */
-               PseudoSpace(&pseudo, M);
-               return((struct xobject *) PathTransform(obj, &pseudo));
-       }
-       if (obj->type == SPACETYPE) {
-               register struct XYspace *S = (struct XYspace *) obj;
-/* replaced ISPERMANENT(S->flag) with S->references > 1 3-26-91 PNM */
-               if (S->references > 1)
-                       S = CopySpace(S);
-               else
-                       S->ID = NEXTID;
-               MatrixMultiply(S->tofract.normal, M, S->tofract.normal);
-               /*
-               * mark inverted matrix invalid:
-               */
-               S->flag &= ~HASINVERSE(ON);
-               FillOutFcns(S);
-               return((struct xobject *) S);
-       }
-       return(ArgErr("Untransformable object", obj, obj));
-}
-/*
-:h3.Transform() - Transform an Object
-This is the external user's entry point.
-*/
-struct xobject *t1_Transform(obj, cxx, cyx, cxy, cyy)
-       struct xobject *obj;
-       double cxx,cyx,cxy,cyy;  /* 2x2 transform matrix elements in row order */
-{
-       double M[2][2];
-       IfTrace1((MustTraceCalls),"Transform(%z,", obj);
-       IfTrace4((MustTraceCalls)," %f %f %f %f)\n", &cxx, &cyx, &cxy, &cyy);
-       M[0][0] = cxx;
-       M[0][1] = cyx;
-       M[1][0] = cxy;
-       M[1][1] = cyy;
-       ConsiderContext(obj, M);
-       return(Xform(obj, M));
-}
-/*
-:h3.Scale() - Special Case of Transform()
-This is a user operator.
-*/
-struct xobject *t1_Scale(obj, sx, sy)
-       struct xobject *obj;  /* object to scale                              */
-       double sx,sy;         /* scale factors in x and y                     */
-{
-       double M[2][2];
-       IfTrace3((MustTraceCalls),"Scale(%z, %f, %f)\n", obj, &sx, &sy);
-       M[0][0] = sx;
-       M[1][1] = sy;
-       M[1][0] = M[0][1] = 0.0;
-       ConsiderContext(obj, M);
-       return(Xform(obj, M));
-}
-/*
-:h3 id=rotate.Rotate() - Special Case of Transform()
-We special-case different settings of 'degrees' for performance
-and accuracy within the DegreeSin() and DegreeCos() routines themselves.
-*/
-#ifdef notdef
-struct xobject *xiRotate(obj, degrees)
-       struct xobject *obj;  /* object to be transformed                     */
-       double degrees;       /* degrees of COUNTER-clockwise rotation        */
-{
-       double M[2][2];
-       IfTrace2((MustTraceCalls),"Rotate(%z, %f)\n", obj, &degrees);
-       M[0][0] = M[1][1] = DegreeCos(degrees);
-       M[1][0] = - (M[0][1] = DegreeSin(degrees));
-       ConsiderContext(obj, M);
-       return(Xform(obj, M));
-}
-#endif
-/*
-:h3.PseudoSpace() - Build a Coordinate Space from a Matrix
-Since we have built all this optimized code that, given an (x,y) and
-a coordinate space, yield transformed (x,y), it seems a shame not to
-use the same logic when we need to multiply an (x,y) by an arbitrary
-matrix that is not (initially) part of a coordinate space.  This
-subroutine takes the arbitrary matrix and builds a coordinate
-space, with all its nifty function pointers.
-*/
-void PseudoSpace(S, M)
-       struct XYspace *S;    /* coordinate space structure to fill out       */
-       double M[2][2];       /* matrix that will become 'tofract.normal'     */
-{
-       S->type = SPACETYPE;
-       S->flag = ISPERMANENT(ON) + ISIMMORTAL(ON);
-       S->references = 2;   /* 3-26-91 added PNM  */
-       S->tofract.normal[0][0] = M[0][0];
-       S->tofract.normal[1][0] = M[1][0];
-       S->tofract.normal[0][1] = M[0][1];
-       S->tofract.normal[1][1] = M[1][1];
-       FillOutFcns(S);
-}
-/*
-:h2 id=matrixa.Matrix Arithmetic
-Following the convention in Newman and Sproull, :hp1/Interactive
-Computer Graphics/,
-matrices are organized:
-:xmp.
-       | cxx   cyx |
-       | cxy   cyy |
-:exmp.
-A point is horizontal, for example:
-:xmp.
-       [ x y ]
-:exmp.
-This means that:
-:formula/x prime = cxx times x + cxy times y/
-:formula/y prime = cyx times x + cyy times y/
-I've seen the other convention, where transform matrices are
-transposed, equally often in the literature.
-*/
-/*
-:h3.MatrixMultiply() - Implements Multiplication of Two Matrices
-Implements matrix multiplication, A * B = C.
-To remind myself, matrix multiplication goes rows of A times columns
-of B.
-The output matrix may be the same as one of the input matrices.
-*/
-void MatrixMultiply(A, B, C)
-       register double A[2][2],B[2][2];  /* input matrices                   */
-       register double C[2][2];    /* output matrix                          */
-{
-       register double txx,txy,tyx,tyy;
-       txx = A[0][0] * B[0][0] + A[0][1] * B[1][0];
-       txy = A[1][0] * B[0][0] + A[1][1] * B[1][0];
-       tyx = A[0][0] * B[0][1] + A[0][1] * B[1][1];
-       tyy = A[1][0] * B[0][1] + A[1][1] * B[1][1];
-       C[0][0] = txx;
-       C[1][0] = txy;
-       C[0][1] = tyx;
-       C[1][1] = tyy;
-}
-/*
-:h3.MatrixInvert() - Invert a Matrix
-My reference for matrix inversion was :hp1/Elementary Linear Algebra/
-by Paul C. Shields, Worth Publishers, Inc., 1968.
-*/
-void MatrixInvert(M, Mprime)
-       double M[2][2];       /* input matrix                                 */
-       double Mprime[2][2];    /* output inverted matrix                     */
-{
-       register double D;    /* determinant of matrix M                      */
-       register double txx,txy,tyx,tyy;
-       txx = M[0][0];
-       txy = M[1][0];
-       tyx = M[0][1];
-       tyy = M[1][1];
-       D = M[1][1] * M[0][0] - M[1][0] * M[0][1];
-       if (D == 0.0)
-               abort("MatrixInvert:  can't");
-       Mprime[0][0] = tyy / D;
-       Mprime[1][0] = -txy / D;
-       Mprime[0][1] = -tyx / D;
-       Mprime[1][1] = txx / D;
-}
-/*
-:h2.Initialization, Queries, and Debug
-*/
-/*
-:h3.InitSpaces() - Initialize Constant Spaces
-For compatibility, we initialize a coordinate space called USER which
-maps 72nds of an inch to pels on the default device.
-*/
-struct XYspace *USER = &identity;
-void InitSpaces()
-{
-       extern char *DEFAULTDEVICE;
-       IDENTITY->type = SPACETYPE;
-       FillOutFcns(IDENTITY);
-       contexts[NULLCONTEXT].normal[1][0]
-             = contexts[NULLCONTEXT].normal[0][1]
-             = contexts[NULLCONTEXT].inverse[1][0]
-             = contexts[NULLCONTEXT].inverse[0][1] = 0.0;
-       contexts[NULLCONTEXT].normal[0][0]
-             = contexts[NULLCONTEXT].normal[1][1]
-             = contexts[NULLCONTEXT].inverse[0][0]
-             = contexts[NULLCONTEXT].inverse[1][1] = 1.0;
-       USER->flag |= ISIMMORTAL(ON);
-       CoerceInverse(USER);
-}
-/*
-:h3.QuerySpace() - Returns the Transformation Matrix of a Space
-Since the tofract matrix of an XYspace includes the scale factor
-necessary to produce fractpel results (i.e., FRACTFLOAT), this
-must be taken out before we return the matrix to the user.  Fortunately,
-this is simple:  just multiply by the inverse of IDENTITY!
-*/
-void QuerySpace(S, cxxP, cyxP, cxyP, cyyP)
-       register struct XYspace *S;  /* space asked about                     */
-       register double *cxxP,*cyxP,*cxyP,*cyyP;  /* where to put answer      */
-{
-       double M[2][2];       /* temp matrix to build user's answer           */
-       if (S->type != SPACETYPE) {
-               ArgErr("QuerySpace: not a space", S, NULL);
-               return;
-       }
-       MatrixMultiply(S->tofract.normal, IDENTITY->tofract.inverse, M);
-       *cxxP = M[0][0];
-       *cxyP = M[1][0];
-       *cyxP = M[0][1];
-       *cyyP = M[1][1];
-}
-/*
-:h3.FormatFP() - Format a Fixed Point Pel
-We format the pel as "dddd.XXXX", where XX's are hexidecimal digits,
-and the dd's are decimal digits.  This might be a little confusing
-mixing hexidecimal and decimal like that, but it is convenient
-to use for debug.
-We make sure we have N (FRACTBITS/4) digits past the decimal point.
-*/
-#define  FRACTMASK   ((1<<FRACTBITS)-1)  /* mask for fractional part         */
-void FormatFP(string, fpel)
-       register char *string;  /* output string                              */
-       register fractpel fpel; /* fractional pel input                       */
-{
-       char temp[8];
-       register char *s;
-       register char *sign;
-       if (fpel < 0) {
-               sign = "-";
-               fpel = -fpel;
-       }
-       else
-               sign = "";
-       sprintf(temp, "000%x", fpel & FRACTMASK);
-       s = temp + strlen(temp) - (FRACTBITS/4);
-       sprintf(string, "%s%d.%sx", sign, fpel >> FRACTBITS, s);
-}
-/*
-:h3.DumpSpace() - Display a Coordinate Space
-*/
-/*ARGSUSED*/
-void DumpSpace(S)
-       register struct XYspace *S;
-{
-       IfTrace4(TRUE,"--Coordinate space at %x,ID=%d,convert=%x,iconvert=%x\n",
-                   S, S->ID, S->convert, S->iconvert);
-       IfTrace2(TRUE,"             |  %12.3f  %12.3f  |",
-                   &S->tofract.normal[0][0], &S->tofract.normal[0][1]);
-       IfTrace2(TRUE,"   [  %p  %p ]\n", S->itofract[0][0], S->itofract[0][1]);
-       IfTrace2(TRUE,"             |  %12.3f  %12.3f  |",
-                   &S->tofract.normal[1][0], &S->tofract.normal[1][1]);
-       IfTrace2(TRUE,"   [  %p  %p ]\n", S->itofract[1][0], S->itofract[1][1]);
-}