summaryrefslogtreecommitdiff
path: root/pcl/pcpatxfm.c
blob: 58c538c9068da04d69c7051de52c1d5a40bff735 (plain)
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
/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises.  All rights
   reserved.  Unauthorized use, copying, and/or distribution
   prohibited.  */

/* pcpatxfm.c - code to set the pattern transformation */

#include "gx.h"
#include "math_.h"
#include "pcpatrn.h"
#include "pcfont.h"
#include "pcpatxfm.h"


/*
 * The four rotation matrices used by PCL. Note that rotations in PCL are
 * always multiples of 90 degrees, and map the positive x-axis to the negative
 * y-axis (the opposite or the rotation sense used by PostScript).
 *
 * All of the matrices are pure rotations; they have no scaling or translation
 * components.
 */
private const gs_matrix rot_mtx[4] = {
    {  1.0,  0.0,  0.0,  1.0,  0.0,  0.0 },     /*   0 degrees */
    {  0.0, -1.0,  1.0,  0.0,  0.0,  0.0 },     /*  90 degrees */
    { -1.0,  0.0,  0.0, -1.0,  0.0,  0.0 },     /* 180 degrees */
    {  0.0,  1.0, -1.0,  0.0,  0.0,  0.0 }      /* 270 degrees */
};


/*
 * Inverst a diagonal 2-dimensional affine transformation. This is much simpler
 * than inverting a general 2-dimensional affine transformation, hence a 
 * separate routine is provided for this purpose.
 *
 * Note that both operands may point to the same matrix.
 *
 * NB: This routine ASSUMES that *pmtx1 is non singular. This is always the
 *     case for PCL, but obviously may not be in a more general setting.
 */
  void
pcl_invert_mtx(
    const gs_matrix *   pmtx1,
    gs_matrix *         pmtx2
)
{
    float               xx = pmtx1->xx;
    float               tx = pmtx1->tx;
    float               ty = pmtx1->ty;

    if (xx == 0.0) {
        float   xy = pmtx1->xy;
        float   yx = pmtx1->yx;

        pmtx2->xx = 0.0;
        pmtx2->xy = 1.0 / yx;
        pmtx2->yx = 1.0 / xy;
        pmtx2->yy = 0.0;
        pmtx2->tx = -ty / xy;
        pmtx2->ty = -tx / yx;
        
    } else {
        float   yy = pmtx1->yy;

        pmtx2->xx = 1.0 / xx;
        pmtx2->xy = 0.0;
        pmtx2->yx = 0.0;
        pmtx2->yy = 1.0 / yy;
        pmtx2->tx = -tx / xx;
        pmtx2->ty = -ty / yy;
    }
}

/*
 * Transform an aligned rectangle. Because all transformations in PCL are
 * diagonal, both the source and destination rectangles are aligned with the
 * coordinate axes, and hence may be represented by a pair of points.
 *
 * prect1 and prect2 may point to the same rectangle.
 */
  void
pcl_transform_rect(
    const gs_rect *     prect1,
    gs_rect *           prect2,
    const gs_matrix *   pmtx
)
{
    gs_point_transform(prect1->p.x, prect1->p.y, pmtx, &(prect2->p));
    gs_point_transform(prect1->q.x, prect1->q.y, pmtx, &(prect2->q));
    if (prect2->p.x > prect2->q.x) {
        double  ftmp = prect2->p.x;

        prect2->p.x = prect2->q.x;
        prect2->q.x = ftmp;
    }
    if (prect2->p.y > prect2->q.y) {
        double  ftmp = prect2->p.y;

        prect2->p.y = prect2->q.y;
        prect2->q.y = ftmp;
    }
}

/*
 * Create the transformation matrix corresponding to a PCL rotation.
 *
 * The rotation operand is an integer in the range 0..3; the desired angle of
 * rotation is 90 * rot.
 *
 * PCL rotations are not pure rotations, and thus cannot be uniquely identified
 * by an angle. A PCL coordinate system is always associated with a rectangular
 * (and aligned) subset of the page, which lies in the possitive quadrant of
 * the coordinate system. The origin of the coordinate system is always one
 * corner of this rectangular region. A rotation in PCL both alters the
 * orientation of the coordinate system and moves its origin to another corner,
 * so as to keep the rectangluar subregion in the positive quadrant.
 *
 * To implement such a transformation, it is necessary to know both the
 * desired change of orientation, and the dimensions of the rectangular
 * region (in the original coordinate system).
 */
  void
pcl_make_rotation(
    int           rot,
    floatp        width,
    floatp        height,
    gs_matrix *   pmtx
)
{
    *pmtx = rot_mtx[rot & 0x3];
    if (pmtx->xx + pmtx->yx < 0.0)
        pmtx->tx = width;
    if (pmtx->xy + pmtx->yy < 0.0)
        pmtx->ty = height;
}

/*
 * Pattern reference point manipulation.
 *
 * Logically, the pattern reference point transforms in the same manner as the
 * logical page: it is reset by changing the logical page orientation (not
 * documented, but verified empirically), it moves with the left and top offset
 * commands, but it does not change with changes in print direction.
 *
 * Ergo, the logical space in which to maintain the pattern reference point
 * is the logical page transformation. This is also the logical space in which
 * to keep the current addressable (cursor) position, but the PCL code is
 * illogical in this regard (see the code for pcl_set_print_direction in
 * pcdraw.c if you are not convinced of this); the current addressable
 * position is instead maintained in a curious "semi-print-direction" space,
 * which incorporates the orientation but not the translation component of
 * the "print direction" coordinate space.
 *
 * Hence, this code must convert the current addressable position back into
 * logical page space before making use of it. In other portions of this code
 * such a change is by using the current transformation directly the graphic
 * state. This is a bit unfortunate, as transformation must be set twice,
 * which can play havoc with transformation-sensitive caches.
 *
 * The current code instead instead directly access the matrix machinery,
 * using the graphic state only to gather the default transformation (which
 * maps the physical page to the device, with centipoints as the source unit).
 *
 * When setting up the transformation matrix to be used by a pattern, the code
 * constructs the resulting matrix in three steps:
 *
 *     Get the default transformation matrix and modify it to transform
 *         logical page space to device space. This involves incorporating
 *         the left and top offsets and the logical page orientation.
 *
 *     Translate the resulting matrix by the pattern reference point.
 *
 *     If the pattern is to be rotated by the print direction, apply the
 *        print direction rotation
 */

/*
 * Convert a floating point number that is nearly an integer to an integer.
 */
  private floatp
adjust_param(
    floatp  val
)
{
    floatp  fval = floor(val);
    floatp  cval = ceil(val);

    return (val - fval < .001 ? fval : (cval - val < .001 ? cval : val));
}

/*
 * Form the transformation matrix required to render a pattern.
 */
  void
pcl_xfm_get_pat_xfm(
    const pcl_state_t *     pcs,
    pcl_pattern_t *         pptrn,
    gs_matrix *             pmat
)
{
    const pcl_xfm_state_t * pxfmst = &(pcs->xfm_state);
    int                     rot = (pcs->pat_orient - pxfmst->lp_orient) & 0x3;

    *pmat = pxfmst->lp2dev_mtx;
    pmat->tx = pcs->pat_ref_pt.x;
    pmat->ty = pcs->pat_ref_pt.y;

    /* record the referenc point used in the rendering structure */
    pptrn->ref_pt = pcs->pat_ref_pt;

    /* rotate as necessar */
    if (rot != 0)
        gs_matrix_multiply(&(rot_mtx[rot]), pmat, pmat);

    /* scale to the appropriate resolution (before print direction rotation) */
    gs_matrix_scale( pmat,
                     inch2coord(1.0 / (floatp)pptrn->ppat_data->xres),
                     inch2coord(1.0 / (floatp)pptrn->ppat_data->yres),
                     pmat
                     );

    /* avoid parameters that are slightly different from integers */
    pmat->xx = adjust_param(pmat->xx);
    pmat->xy = adjust_param(pmat->xy);
    pmat->yx = adjust_param(pmat->yx);
    pmat->yy = adjust_param(pmat->yy);

    /* record the rotation used for rendering */
    pptrn->orient = pcs->pat_orient;
}

/*
 * Reset the pattern reference point. This should be done each time the logical
 * page parameters change. (This would be better done via a reset, but
 * unfortunately there was no reset created for this category of even.)
 */
  void
pcl_xfm_reset_pcl_pat_ref_pt(
    pcl_state_t *   pcs
)
{
    pcs->pcl_pat_ref_pt.x = 0.0;
    pcs->pcl_pat_ref_pt.y = 0.0;
    pcs->rotate_patterns = true;
}

/*
 * Set up the pattern orientation and reference point for PCL. Note that PCL's
 * pattern reference point is kept in logical page space.
 */
  void
pcl_xfm_pcl_set_pat_ref_pt(
    pcl_state_t *       pcs
)
{
    pcl_xfm_state_t *   pxfmst = &(pcs->xfm_state);

    gs_point_transform( pcs->pcl_pat_ref_pt.x,
                        pcs->pcl_pat_ref_pt.y,
                        &(pxfmst->lp2dev_mtx),
                        &(pcs->pat_ref_pt)
                        );
    pcs->pat_ref_pt.x = floor(pcs->pat_ref_pt.x + 0.5);
    pcs->pat_ref_pt.y = floor(pcs->pat_ref_pt.y + 0.5);
    pcs->pat_orient = (pxfmst->lp_orient 
                       + (pcs->rotate_patterns ? pxfmst->print_dir : 0)) & 0x3;
}

/*
 * Set up the pattern orientatin and reference point for GL. The anchor point
 * is taken as being in the current GL space, and the current transform is
 * assumed properly set. The orientation is that of the logical page.
 */
  void
pcl_xfm_gl_set_pat_ref_pt(
    pcl_state_t *   pcs
)
{
    gs_transform( pcs->pgs,
                  pcs->g.anchor_corner.x,
                  pcs->g.anchor_corner.y,
                  &(pcs->pat_ref_pt)
                  );
    pcs->pat_ref_pt.x = floor(pcs->pat_ref_pt.x + 0.5);
    pcs->pat_ref_pt.y = floor(pcs->pat_ref_pt.y + 0.5);
    pcs->pat_orient = (pcs->xfm_state.lp_orient + (pcs->g.rotation / 90)) & 0x3;
}


/*
 * ESC * p # R
 *
 * Set pattern reference point.
 *
 */
  private int
set_pat_ref_pt(
    pcl_args_t *    pargs,
    pcl_state_t *   pcs
)
{
    uint            rotate = uint_arg(pargs);

    if (rotate <= 1) {
        pcl_break_underline(pcs);
        gs_point_transform( (floatp)pcs->cap.x,
                            (floatp)pcs->cap.y,
                            &(pcs->xfm_state.pd2lp_mtx),
                            &(pcs->pcl_pat_ref_pt)
                            );
        pcs->rotate_patterns = (rotate == 0);
    }
    return 0;
}


/*
 * Initializaton and reset routines. There is currently no copy routine, as the
 * desired transformation is reset for each object printed. No special reset
 * routine is required, as a reset of the logical page will reset the pattern
 * reference point as well.
 */
  private int
xfm_do_registration(
    pcl_parser_state_t *pcl_parser_state,
    gs_memory_t *   pmem
)
{
    DEFINE_CLASS('*')
    {
        'p', 'R',
        PCL_COMMAND( "Pattern Reference Point",
                     set_pat_ref_pt,
                     pca_neg_ok | pca_big_ignore
                     )
    },
    END_CLASS
    return 0;
}

const pcl_init_t    pcl_xfm_init = { xfm_do_registration, 0, 0 };