1 /*
2 **
3 ** This library module contains the pamdraw routines.
4 **
5 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
6 **
7 ** Modified from ppm to pam by Willem van Schaik, Feb 2011
8 **
9 ** Permission to use, copy, modify, and distribute this software and its
10 ** documentation for any purpose and without fee is hereby granted, provided
11 ** that the above copyright notice appear in all copies and that both that
12 ** copyright notice and this permission notice appear in supporting
13 ** documentation.  This software is provided "as is" without express or
14 ** implied warranty.
15 **
16 ** The character drawing routines are by John Walker
17 ** Copyright (C) 1994 by John Walker, kelvin@fourmilab.ch
18 */
19 
20 #include <assert.h>
21 #include <stdlib.h>
22 
23 #include "netpbm/pm_config.h"
24 #include "netpbm/pm_c_util.h"
25 #include "netpbm/mallocvar.h"
26 
27 #include "pam.h"
28 #include "ppmdfont.h"
29 
30 #include "pamdraw.h"
31 
32 
33 struct penpos {
34     int x;
35     int y;
36 };
37 
38 struct rectangle {
39     /* ((0,0),(0,0)) means empty. */
40     /* 'lr' is guaranteed not to be left of or above 'ul' */
41     struct penpos ul;
42     struct penpos lr;
43 };
44 
45 static struct rectangle const emptyRectangle = {
46     {0, 0},
47     {0, 0},
48 };
49 
50 
51 
52 static pamd_point
makePoint(int const x,int const y)53 makePoint(int const x,
54           int const y) {
55 
56     return pamd_makePoint(x, y);
57 }
58 
59 
60 
61 static pamd_point
middlePoint(pamd_point const a,pamd_point const b)62 middlePoint(pamd_point const a,
63             pamd_point const b) {
64 
65     pamd_point retval;
66 
67     retval.x = (a.x + b.x) / 2;
68     retval.y = (a.y + b.y) / 2;
69 
70     return retval;
71 }
72 
73 
74 
75 static bool
pointsEqual(pamd_point const a,pamd_point const b)76 pointsEqual(pamd_point const a,
77             pamd_point const b) {
78 
79     return a.x == b.x && a.y == b.y;
80 }
81 
82 
83 
84 static bool
pointIsWithinBounds(pamd_point const p,unsigned int const cols,unsigned int const rows)85 pointIsWithinBounds(pamd_point   const p,
86                     unsigned int const cols,
87                     unsigned int const rows) {
88 
89     return (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows);
90 }
91 
92 
93 
94 static pamd_point
vectorSum(pamd_point const a,pamd_point const b)95 vectorSum(pamd_point const a,
96           pamd_point const b) {
97 
98     return makePoint(a.x + b.x, a.y + b.y);
99 }
100 
101 
102 
103 static long int const DDA_SCALE = 8192;
104 
105 #define PAMD_MAXCOORD 32767
106 /*
107   Several factors govern the limit of x, y coordination values.
108 
109   The limit must be representable as (signed) int for coordinates to
110   be carried in struct penpos (immediately above).
111 
112   The following calculation, done with long ints, must not overflow:
113   cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0);
114 
115   The following must not overflow when DDA_SCALE is set to 8092:
116   dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0);
117 
118   Overflow conditions for pamd_text are rather complicated, for commands
119   come from an external PPMD font file.  See comments below.
120 */
121 
122 
123 
124 void
pamd_validateCoord(int const c)125 pamd_validateCoord(int const c) {
126 
127     if (c < -PAMD_MAXCOORD || c > PAMD_MAXCOORD)
128         pm_error("Coordinate out of bounds: %d", c);
129 }
130 
131 
132 
133 void
pamd_validatePoint(pamd_point const p)134 pamd_validatePoint(pamd_point const p) {
135 
136     if (p.x < -PAMD_MAXCOORD || p.x > PAMD_MAXCOORD)
137         pm_error("x coordinate of (%d, %d) out of bounds", p.x, p.y);
138 
139     if (p.y < -PAMD_MAXCOORD || p.y > PAMD_MAXCOORD)
140         pm_error("y coordinate of (%d, %d) out of bounds", p.x, p.y);
141 }
142 
143 
144 
145 static void
drawPoint(pamd_drawproc drawproc,const void * const clientdata,tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,pamd_point const p)146 drawPoint(pamd_drawproc       drawproc,
147           const void *  const clientdata,
148           tuple **      const tuples,
149           int           const cols,
150           int           const rows,
151           int           const depth,
152           sample        const maxval,
153           pamd_point    const p) {
154 /*----------------------------------------------------------------------------
155    Draw a single point, assuming that it is within the bounds of the
156    image.
157 -----------------------------------------------------------------------------*/
158     int i;
159 
160     if (drawproc == PAMD_NULLDRAWPROC) {
161         assert(p.x >= 0); assert(p.x < cols);
162         assert(p.y >= 0); assert(p.y < rows);
163 
164         for (i = 0; i < depth; i++)
165             tuples[p.y][p.x][i] = (sample) *((tuple *) clientdata + i);
166     } else {
167         drawproc(tuples, cols, rows, depth, maxval, p, clientdata);
168     }
169 }
170 
171 
172 
173 void
pamd_point_drawproc(tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,pamd_point const p,const void * const clientdata)174 pamd_point_drawproc(tuple **     const tuples,
175                     unsigned int const cols,
176                     unsigned int const rows,
177                     unsigned int const depth,
178                     sample       const maxval,
179                     pamd_point   const p,
180                     const void * const clientdata) {
181 
182     unsigned int i;
183 
184     if ((p.x >= 0) && (p.x < cols) && (p.y >= 0) && (p.y < rows))
185         for (i = 0; i < depth; ++i)
186             tuples[p.y][p.x][i] = (sample) *((tuple *) clientdata + i);
187 }
188 
189 
190 
191 static void
findRectangleIntersection(struct rectangle const rect1,struct rectangle const rect2,struct rectangle * const intersectionP)192 findRectangleIntersection(struct rectangle   const rect1,
193                           struct rectangle   const rect2,
194                           struct rectangle * const intersectionP) {
195 /*----------------------------------------------------------------------------
196    Find the intersection between rectangles 'rect1' and 'rect2'.
197    Return it as *intersectionP.
198 -----------------------------------------------------------------------------*/
199     struct penpos tentativeUl, tentativeLr;
200 
201     tentativeUl.x = MAX(rect1.ul.x, rect2.ul.x);
202     tentativeUl.y = MAX(rect1.ul.y, rect2.ul.y);
203     tentativeLr.x = MIN(rect1.lr.x, rect2.lr.x);
204     tentativeLr.y = MIN(rect1.lr.y, rect2.lr.y);
205 
206     if (tentativeLr.x <= tentativeUl.x ||
207         tentativeLr.y <= tentativeUl.y) {
208         /* No intersection */
209         *intersectionP = emptyRectangle;
210     } else {
211         intersectionP->ul = tentativeUl;
212         intersectionP->lr = tentativeLr;
213     }
214 }
215 
216 
217 
218 void
pamd_filledrectangle(tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,int const left,int const top,int const width,int const height,pamd_drawproc drawProc,const void * const clientdata)219 pamd_filledrectangle(tuple **      const tuples,
220                      int           const cols,
221                      int           const rows,
222                      int           const depth,
223                      sample        const maxval,
224                      int           const left,
225                      int           const top,
226                      int           const width,
227                      int           const height,
228                      pamd_drawproc       drawProc,
229                      const void *  const clientdata) {
230 
231     struct rectangle image, request, intersection;
232     unsigned int row;
233 
234     if (width < 0)
235         pm_error("negative width %d passed to pamd_filledrectanglep", width);
236     if (height < 0)
237         pm_error("negative height %d passed to pamd_filledrectanglep", height);
238     if (cols < 0)
239         pm_error("negative image width %d passed to pamd_filledrectanglep",
240                  cols);
241     if (rows < 0)
242         pm_error("negative image height %d passed to pamd_filledrectanglep",
243                  rows);
244 
245     request.ul.x = left;
246     request.ul.y = top;
247     request.lr.x = left + width;
248     request.lr.y = top + height;
249 
250     image.ul.x = 0;
251     image.ul.y = 0;
252     image.lr.x = cols;
253     image.lr.y = rows;
254 
255     findRectangleIntersection(image, request, &intersection);
256 
257     /* Draw. */
258     for (row = intersection.ul.y; row < intersection.lr.y; ++row) {
259         unsigned int col;
260         for (col = intersection.ul.x; col < intersection.lr.x; ++col)
261             drawPoint(drawProc, clientdata,
262                       tuples, cols, rows, depth, maxval, makePoint(col, row));
263     }
264 }
265 
266 
267 
268 /* Outline drawing stuff. */
269 
270 static int linetype = PAMD_LINETYPE_NORMAL;
271 
272 
273 
274 int
pamd_setlinetype(int const type)275 pamd_setlinetype(int const type) {
276 
277     int old;
278 
279     old = linetype;
280     linetype = type;
281     return old;
282 }
283 
284 
285 static bool lineclip = TRUE;
286 
287 
288 
289 int
pamd_setlineclip(int const newSetting)290 pamd_setlineclip(int const newSetting) {
291 
292     bool previousSetting;
293 
294     previousSetting = lineclip;
295 
296     lineclip = newSetting;
297 
298     return previousSetting;
299 }
300 
301 
302 
303 static void
clipEnd0(pamd_point const p0,pamd_point const p1,int const cols,int const rows,pamd_point * const c0P,bool * const noLineP)304 clipEnd0(pamd_point   const p0,
305          pamd_point   const p1,
306          int          const cols,
307          int          const rows,
308          pamd_point * const c0P,
309          bool *       const noLineP) {
310 /*----------------------------------------------------------------------------
311    Given a line that goes from p0 to p1, where any of these coordinates may be
312    anywhere in space -- not just in the frame, clip the p0 end to bring it
313    into the frame.  Return the clipped-to location as *c0P.
314 
315    Iff this is not possible because the entire line described is
316    outside the frame, return *nolineP == true.
317 
318    The frame is 'cols' columns starting at 0, by 'rows' rows starting
319    at 0.
320 -----------------------------------------------------------------------------*/
321     pamd_point c0;
322     bool noLine;
323 
324     c0 = p0;         /* initial value */
325     noLine = FALSE;  /* initial value */
326 
327     /* Clip End 0 of the line horizontally */
328     if (c0.x < 0) {
329         if (p1.x < 0)
330             noLine = TRUE;
331         else {
332             c0.y = c0.y + (p1.y - c0.y) * (-c0.x) / (p1.x - c0.x);
333             c0.x = 0;
334         }
335     } else if (c0.x >= cols) {
336         if (p1.x >= cols)
337             noLine = TRUE;
338         else {
339             c0.y = c0.y + (p1.y - c0.y) * (cols - 1 - c0.x) / (p1.x - c0.x);
340             c0.x = cols - 1;
341         }
342     }
343 
344     /* Clip End 0 of the line vertically */
345     if (c0.y < 0) {
346         if (p1.y < 0)
347             noLine = TRUE;
348         else {
349             c0.x = c0.x + (p1.x - c0.x) * (-c0.y) / (p1.y - c0.y);
350             c0.y = 0;
351         }
352     } else if (c0.y >= rows) {
353         if (p1.y >= rows)
354             noLine = TRUE;
355         else {
356             c0.x = c0.x + (p1.x - c0.x) * (rows - 1 - c0.y) / (p1.y - c0.y);
357             c0.y = rows - 1;
358         }
359     }
360 
361     /* Clipping vertically may have moved the endpoint out of frame
362        horizontally.  If so, we know the other endpoint is also out of
363        frame horizontally and the line misses the frame entirely.
364     */
365     if (c0.x < 0 || c0.x >= cols) {
366         assert(p1.x < 0 || p1.x >= cols);
367         noLine = TRUE;
368     }
369     *c0P = c0;
370     *noLineP = noLine;
371 }
372 
373 
374 
375 static void
clipEnd1(pamd_point const p0,pamd_point const p1,int const cols,int const rows,pamd_point * const c1P)376 clipEnd1(pamd_point   const p0,
377          pamd_point   const p1,
378          int          const cols,
379          int          const rows,
380          pamd_point * const c1P) {
381 /*----------------------------------------------------------------------------
382    Given a line that goes from p0 to p1, where p0 is within the frame, but p1
383    can be anywhere in space, clip the p1 end to bring it into the frame.
384    Return the clipped-to location as *c1P.
385 
386    This is guaranteed to be possible, since we already know at least one point
387    (i.e. p0) is in the frame.
388 
389    The frame is 'cols' columns starting at 0, by 'rows' rows starting
390    at 0.
391 -----------------------------------------------------------------------------*/
392     pamd_point c1;
393         /* The current clipped location of p1; we clip it multiple times
394            to get the final location.
395         */
396     /* p0 is in the frame: */
397     assert(p0.x >= 0 && p0.x < cols);
398     assert(p0.y >= 0 && p0.y < rows);
399 
400     /* Clip End 1 of the line horizontally */
401     c1 = p1;  /* initial value */
402 
403     if (c1.x < 0) {
404         /* We know the line isn't vertical, since End 0 is in the frame
405            and End 1 is left of frame.
406         */
407         c1.y = c1.y + (p0.y - c1.y) * (-c1.x) / (p0.x - c1.x);
408         c1.x = 0;
409     } else if (c1.x >= cols) {
410         /* We know the line isn't vertical, since End 0 is in the frame
411            and End 1 is right of frame.
412         */
413         c1.y = c1.y + (p0.y - c1.y) * (cols - 1 - c1.x) / (p0.x - c1.x);
414         c1.x = cols - 1;
415     }
416 
417     /* Clip End 1 of the line vertically */
418     if (c1.y < 0) {
419         /* We know the line isn't horizontal, since End 0 is in the frame
420            and End 1 is above frame.
421         */
422         c1.x = c1.x + (p0.x - c1.x) * (-c1.y) / (p0.y - c1.y);
423         c1.y = 0;
424     } else if (c1.y >= rows) {
425         /* We know the line isn't horizontal, since End 0 is in the frame
426            and End 1 is below frame.
427         */
428         c1.x = c1.x + (p0.x - c1.x) * (rows - 1 - c1.y) / (p0.y - c1.y);
429         c1.y = rows - 1;
430     }
431 
432     *c1P = c1;
433 }
434 
435 
436 
437 static void
clipLine(pamd_point const p0,pamd_point const p1,int const cols,int const rows,pamd_point * const c0P,pamd_point * const c1P,bool * const noLineP)438 clipLine(pamd_point   const p0,
439          pamd_point   const p1,
440          int          const cols,
441          int          const rows,
442          pamd_point * const c0P,
443          pamd_point * const c1P,
444          bool *       const noLineP) {
445 /*----------------------------------------------------------------------------
446    Clip the line that goes from p0 to p1 so that none of it is outside the
447    boundaries of the raster with width 'cols' and height 'rows'
448 
449    The clipped line goes from *c0P to *c1P.
450 
451    But if the entire line is outside the boundaries (i.e. we clip the
452    entire line), return *noLineP true and the other values undefined.
453 -----------------------------------------------------------------------------*/
454     pamd_point c0, c1;
455         /* The line we successively modify.  Starts out as the input
456            line and ends up as the output line.
457         */
458     bool noLine;
459 
460     clipEnd0(p0, p1, cols, rows, &c0, &noLine);
461 
462     if (!noLine) {
463         /* p0 is in the frame: */
464         assert(c0.x >= 0 && c0.x < cols);
465         assert(c0.y >= 0 && c0.y < rows);
466 
467         clipEnd1(c0, p1, cols, rows, &c1);
468     }
469 
470     *c0P = c0;
471     *c1P = c1;
472     *noLineP = noLine;
473 }
474 
475 
476 
477 static void
drawShallowLine(pamd_drawproc drawProc,const void * const clientdata,tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,pamd_point const p0,pamd_point const p1)478 drawShallowLine(pamd_drawproc       drawProc,
479                 const void *  const clientdata,
480                 tuple **      const tuples,
481                 int           const cols,
482                 int           const rows,
483                 int           const depth,
484                 sample        const maxval,
485                 pamd_point    const p0,
486                 pamd_point    const p1) {
487 /*----------------------------------------------------------------------------
488    Draw a line that is more horizontal than vertical.
489 
490    Don't clip.
491 
492    Assume the line has distinct start and end points (i.e. it's at least
493    two points).
494 -----------------------------------------------------------------------------*/
495     /* Loop over X domain. */
496     long dy, srow;
497     int dx, col, row, prevrow;
498 
499     if (p1.x > p0.x)
500         dx = 1;
501     else
502         dx = -1;
503     dy = (p1.y - p0.y) * DDA_SCALE / abs(p1.x - p0.x);
504     prevrow = row = p0.y;
505     srow = row * DDA_SCALE + DDA_SCALE / 2;
506     col = p0.x;
507     for ( ; ; ) {
508         if (linetype == PAMD_LINETYPE_NODIAGS && row != prevrow) {
509             drawPoint(drawProc, clientdata,
510                       tuples, cols, rows, depth, maxval,
511                       makePoint(col, prevrow));
512             prevrow = row;
513         }
514         drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval,
515                   makePoint(col, row));
516         if (col == p1.x)
517             break;
518         srow += dy;
519         row = srow / DDA_SCALE;
520         col += dx;
521     }
522 }
523 
524 
525 
526 static void
drawSteepLine(pamd_drawproc drawProc,const void * const clientdata,tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,pamd_point const p0,pamd_point const p1)527 drawSteepLine(pamd_drawproc       drawProc,
528               const void *  const clientdata,
529               tuple **      const tuples,
530               int           const cols,
531               int           const rows,
532               int           const depth,
533               sample        const maxval,
534               pamd_point    const p0,
535               pamd_point    const p1) {
536 /*----------------------------------------------------------------------------
537    Draw a line that is more vertical than horizontal.
538 
539    Don't clip.
540 
541    Assume the line has distinct start and end points (i.e. it's at least
542    two points).
543 -----------------------------------------------------------------------------*/
544     /* Loop over Y domain. */
545 
546     long dx, scol;
547     int dy, col, row, prevcol;
548 
549     if (p1.y > p0.y)
550         dy = 1;
551     else
552         dy = -1;
553     dx = (p1.x - p0.x) * DDA_SCALE / abs(p1.y - p0.y);
554     row = p0.y;
555     prevcol = col = p0.x;
556     scol = col * DDA_SCALE + DDA_SCALE / 2;
557     for ( ; ; ) {
558         if (linetype == PAMD_LINETYPE_NODIAGS && col != prevcol) {
559             drawPoint(drawProc, clientdata,
560                       tuples, cols, rows, depth, maxval,
561                       makePoint(prevcol, row));
562             prevcol = col;
563         }
564         drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval,
565                   makePoint(col, row));
566         if (row == p1.y)
567             break;
568         row += dy;
569         scol += dx;
570         col = scol / DDA_SCALE;
571     }
572 }
573 
574 
575 
576 void
pamd_line(tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,pamd_point const p0,pamd_point const p1,pamd_drawproc drawProc,const void * const clientdata)577 pamd_line(tuple **      const tuples,
578           int           const cols,
579           int           const rows,
580           int           const depth,
581           sample        const maxval,
582           pamd_point    const p0,
583           pamd_point    const p1,
584           pamd_drawproc       drawProc,
585           const void *  const clientdata) {
586 
587     pamd_point c0, c1;
588     bool noLine;  /* There's no line left after clipping */
589 
590     pamd_validateCoord(cols);
591     pamd_validateCoord(rows);
592     pamd_validatePoint(p0);
593     pamd_validatePoint(p1);
594 
595     if (lineclip) {
596         clipLine(p0, p1, cols, rows, &c0, &c1, &noLine);
597     } else {
598         c0 = p0;
599         c1 = p1;
600         noLine = FALSE;
601     }
602 
603     if (noLine) {
604         /* Nothing to draw */
605     } else if (pointsEqual(c0, c1)) {
606         /* This line is just a point.  Because there aren't two
607            distinct endpoints, we have a special case.
608         */
609         drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, c0);
610     } else {
611         /* Draw, using a simple DDA. */
612         if (abs(c1.x - c0.x) > abs(c1.y - c0.y))
613             drawShallowLine(drawProc, clientdata, tuples, cols, rows,
614                             depth, maxval, c0, c1);
615         else
616             drawSteepLine(drawProc, clientdata, tuples, cols, rows,
617                           depth, maxval, c0, c1);
618     }
619 }
620 
621 
622 
623 static unsigned int
distanceFromLine(pamd_point const p,pamd_point const l0,pamd_point const l1)624 distanceFromLine(pamd_point const p,
625                  pamd_point const l0,
626                  pamd_point const l1) {
627 /*----------------------------------------------------------------------------
628   Compute, sort of, the distance between point 'p' and the line through
629   'l0' and 'l1'.
630 
631   I don't really know the signficance of this measurement.
632 -----------------------------------------------------------------------------*/
633 
634     pamd_point const middle = middlePoint(l0, l1);
635 
636     return (abs(p.x - middle.x) + abs(p.y - middle.y));
637 }
638 
639 
640 
641 void
pamd_spline3(tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,pamd_point const p0,pamd_point const ctl,pamd_point const p1,pamd_drawproc drawProc,const void * const clientdata)642 pamd_spline3(tuple **      const tuples,
643              int           const cols,
644              int           const rows,
645              int           const depth,
646              sample        const maxval,
647              pamd_point    const p0,
648              pamd_point    const ctl,
649              pamd_point    const p1,
650              pamd_drawproc       drawProc,
651              const void *  const clientdata) {
652 
653     static unsigned int const splineThresh = 3;
654         /* The limit of recursion */
655 
656     if (distanceFromLine(ctl, p0, p1) <= splineThresh) {
657         /* The control point is pretty close to the straight line that
658            joins the endpoints, so we'll just draw a straight line.
659         */
660         pamd_line(
661             tuples, cols, rows, depth, maxval, p0, p1, drawProc, clientdata);
662     } else {
663         /* We want some curvature, so pick a point (b) sort of between the
664            two endpoints and the control point and then draw a spline
665            between each of the endpoints and (b):
666         */
667         pamd_point const a = middlePoint(p0, ctl);
668         pamd_point const c = middlePoint(ctl, p1);
669         pamd_point const b = middlePoint(a, c);
670 
671         pamd_spline3(
672             tuples, cols, rows, depth, maxval, p0, a, b, drawProc, clientdata);
673 
674         pamd_spline3(
675             tuples, cols, rows, depth, maxval, b, c, p1, drawProc, clientdata);
676     }
677 }
678 
679 
680 
681 void
pamd_polyspline(tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,pamd_point const p0,unsigned int const nc,pamd_point * const c,pamd_point const p1,pamd_drawproc drawProc,const void * const clientdata)682 pamd_polyspline(tuple **      const tuples,
683                 unsigned int  const cols,
684                 unsigned int  const rows,
685                 unsigned int  const depth,
686                 sample        const maxval,
687                 pamd_point    const p0,
688                 unsigned int  const nc,
689                 pamd_point *  const c,
690                 pamd_point    const p1,
691                 pamd_drawproc       drawProc,
692                 const void *  const clientdata) {
693 
694     pamd_point p;
695 
696     unsigned int i;
697 
698     assert(nc > 0);
699 
700     p = p0;
701     for (i = 0; i < nc - 1; ++i) {
702         pamd_point const n = middlePoint(c[i], c[i+1]);
703         pamd_spline3(
704             tuples, cols, rows, depth, maxval, p, c[i], n,
705             drawProc, clientdata);
706         p = n;
707     }
708     pamd_spline3(
709         tuples, cols, rows, depth, maxval, p, c[nc - 1], p1,
710         drawProc, clientdata);
711 }
712 
713 
714 
715 void
pamd_spline4(tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,pamd_point const endPt0,pamd_point const endPt1,pamd_point const ctlPt0,pamd_point const ctlPt1,pamd_drawproc drawproc,const void * const clientdata)716 pamd_spline4(tuple **      const tuples,
717              unsigned int  const cols,
718              unsigned int  const rows,
719              unsigned int  const depth,
720              sample        const maxval,
721              pamd_point    const endPt0,
722              pamd_point    const endPt1,
723              pamd_point    const ctlPt0,
724              pamd_point    const ctlPt1,
725              pamd_drawproc       drawproc,
726              const void *  const clientdata) {
727 /*----------------------------------------------------------------------------
728    Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and
729    'ctlPt1' as control points in the classic way: a line through
730    'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the
731    length of that line controls "enthusiasm," whatever that is.
732    Same for 'endPt1' and 'ctlPt1'.
733 -----------------------------------------------------------------------------*/
734 
735     pm_error("pamd_spline4() has not been written yet!");
736 
737 }
738 
739 
740 
741 void
pamd_circle(tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,pamd_point const center,unsigned int const radius,pamd_drawproc drawProc,const void * const clientData)742 pamd_circle(tuple **      const tuples,
743             unsigned int  const cols,
744             unsigned int  const rows,
745             unsigned int  const depth,
746             sample        const maxval,
747             pamd_point    const center,
748             unsigned int  const radius,
749             pamd_drawproc       drawProc,
750             const void *  const clientData) {
751 /*----------------------------------------------------------------------------
752   If lineclip mode is on, draw only points within the image.
753   If lineclip is off, "draw" all points (by designated drawproc).  Note
754   that the drawproc can't actually draw a point outside the image, but
755   it might maintain state that is affected by imaginary points outside
756   the image.
757 
758   Initial point is 3 o'clock.
759 -----------------------------------------------------------------------------*/
760     if (radius >= DDA_SCALE)
761         pm_error("Error drawing circle.  Radius %d is too large.", radius);
762 
763     pamd_validateCoord(center.x + radius);
764     pamd_validateCoord(center.y + radius);
765     pamd_validateCoord(center.x - radius);
766     pamd_validateCoord(center.y - radius);
767 
768     if (radius > 0) {
769         long const e = DDA_SCALE / radius;
770 
771         pamd_point const p0 = makePoint(radius, 0);  /* 3 o'clock */
772             /* The starting point around the circle, assuming (0, 0) center */
773         pamd_point p;
774             /* Current drawing position in the circle, assuming (0,0) center */
775         bool onFirstPoint;
776         bool prevPointExists;
777         pamd_point prevPoint;
778             /* Previous drawing position, assuming (0, 0) center*/
779         long sx, sy;  /* 'p', scaled by DDA_SCALE */
780 
781         p = p0;
782 
783         sx = p.x * DDA_SCALE + DDA_SCALE / 2;
784         sy = p.y * DDA_SCALE + DDA_SCALE / 2;
785 
786         onFirstPoint = TRUE;
787         prevPointExists = FALSE;
788 
789         while (onFirstPoint || !pointsEqual(p, p0)) {
790             if (prevPointExists && pointsEqual(p, prevPoint)) {
791                 /* We're on the same point we were on last time (we moved less
792                    than a point's worth).  Just keep moving.
793                 */
794             } else {
795                 pamd_point const imagePoint = vectorSum(center,p);
796                 if (!lineclip || pointIsWithinBounds(imagePoint, cols, rows))
797                     drawPoint(drawProc, clientData,
798                               tuples, cols, rows, depth, maxval, imagePoint);
799 
800                 prevPoint = p;
801                 prevPointExists = TRUE;
802             }
803 
804             if (!pointsEqual(p, p0))
805                 onFirstPoint = FALSE;
806 
807             sx += e * sy / DDA_SCALE;
808             sy -= e * sx / DDA_SCALE;
809             p = makePoint(sx / DDA_SCALE, sy / DDA_SCALE);
810         }
811     }
812 }
813 
814 
815 
816 /* Arbitrary fill stuff. */
817 
818 typedef struct {
819     pamd_point point;
820     int edge;
821 } coord;
822 
823 typedef struct fillState {
824     int n;
825         /* Number of elements in 'coords' */
826     int size;
827     int curedge;
828     int segstart;
829     int ydir;
830     int startydir;
831     coord * coords;
832 } fillState;
833 
834 typedef struct fillobj {
835 
836     /* The only reason we have a struct fillState separate from
837        struct fillobj is that the drawproc interface is defined to
838        have drawing not modify the fillobj, i.e. it passed
839        const fillobj * to the drawing program.
840     */
841     struct fillState * stateP;
842 } fillobj;
843 
844 #define SOME 1000
845 
846 static int oldclip;
847 
848 
849 
850 struct fillobj *
pamd_fill_create(void)851 pamd_fill_create(void) {
852 
853     fillobj * fillObjP;
854     struct fillState * stateP;
855 
856     MALLOCVAR(fillObjP);
857     if (fillObjP == NULL)
858         pm_error("out of memory allocating a fillhandle");
859 
860     MALLOCVAR(stateP);
861     if (stateP == NULL)
862         pm_error("out of memory allocating a fillhandle");
863 
864     stateP->n = 0;
865     stateP->size = SOME;
866     MALLOCARRAY(stateP->coords, stateP->size);
867     if (stateP->coords == NULL)
868         pm_error("out of memory allocating a fillhandle");
869     stateP->curedge = 0;
870 
871     fillObjP->stateP = stateP;
872 
873     /* Turn off line clipping. */
874     /* UGGH! We must eliminate this global variable */
875     oldclip = pamd_setlineclip(0);
876 
877     return fillObjP;
878 }
879 
880 
881 
882 void
pamd_fill_destroy(struct fillobj * const fillObjP)883 pamd_fill_destroy(struct fillobj * const fillObjP) {
884 
885     free(fillObjP->stateP->coords);
886     free(fillObjP->stateP);
887     free(fillObjP);
888 }
889 
890 
891 
892 static void
addCoord(struct fillState * const stateP,pamd_point const point)893 addCoord(struct fillState *  const stateP,
894          pamd_point const point) {
895 
896     stateP->coords[stateP->n].point = point;
897     stateP->coords[stateP->n].edge = stateP->curedge;
898 
899     ++stateP->n;
900 }
901 
902 
903 
904 static void
startNewSegment(struct fillState * const stateP)905 startNewSegment(struct fillState * const stateP) {
906 /*----------------------------------------------------------------------------
907    Close off the segment we're currently building and start a new one.
908 -----------------------------------------------------------------------------*/
909     if (stateP->startydir != 0 && stateP->ydir != 0) {
910         /* There's stuff in the current segment.  */
911         if (stateP->startydir == stateP->ydir) {
912             /* Oops, first edge and last edge of current segment are the same.
913                Change all points in the first edge to be in the last.
914             */
915             int const firstEdge = stateP->coords[stateP->segstart].edge;
916             int const lastEdge  = stateP->coords[stateP->n - 1].edge;
917             coord * const segStartCoordP = &stateP->coords[stateP->segstart];
918             coord * const segEndCoordP   = &stateP->coords[stateP->n];
919 
920             coord * fcP;
921 
922             for (fcP = segStartCoordP;
923                  fcP < segEndCoordP && fcP->edge == firstEdge;
924                  ++fcP)
925                 fcP->edge = lastEdge;
926         }
927     }
928     /* And start new segment. */
929     ++stateP->curedge;
930     stateP->segstart  = stateP->n;
931     stateP->ydir      = 0;
932     stateP->startydir = 0;
933 }
934 
935 
936 
937 static void
continueSegment(struct fillState * const stateP,int const dy)938 continueSegment(struct fillState * const stateP,
939                 int                const dy) {
940 /*----------------------------------------------------------------------------
941    'dy' is how much the current point is above the previous one.
942 -----------------------------------------------------------------------------*/
943     if (dy != 0) {
944         if (stateP->ydir != 0 && stateP->ydir != dy) {
945             /* Direction changed.  Insert a fake coord, old
946                position but new edge number.
947             */
948             ++stateP->curedge;
949             addCoord(stateP, stateP->coords[stateP->n - 1].point);
950         }
951         stateP->ydir = dy;
952         if (stateP->startydir == 0)
953             stateP->startydir = dy;
954     }
955 }
956 
957 
958 
959 /* pamd_fill_drawproc() is a drawproc that turns an outline drawing function
960    into a filled shape function.  This is a somewhat off-label application of
961    a drawproc:  A drawproc is intended just to draw a point.  So e.g. you
962    might draw a circle with a fat brush by calling pamd_circle with a drawproc
963    that draws a point as a 10-tuple disk.
964 
965    But pamd_fill_drawproc() just draws a point the trivial way: as one tuple.
966    However, it tracks every point that is drawn in a form that a subsequent
967    pamd_fill() call can use to to fill in the shape drawn, assuming it turns
968    out to be a closed shape.
969 */
970 
971 void
pamd_fill_drawproc(tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,pamd_point const p,const void * const clientdata)972 pamd_fill_drawproc(tuple **     const tuples,
973                    unsigned int const cols,
974                    unsigned int const rows,
975                    unsigned int const depth,
976                    sample       const maxval,
977                    pamd_point   const p,
978                    const void * const clientdata) {
979 
980     const fillobj *    const fillObjP = clientdata;
981     struct fillState * const stateP   = fillObjP->stateP;
982 
983     /* Make room for two more coords, the max we might add. */
984     if (stateP->n + 2 > stateP->size) {
985         stateP->size += SOME;
986         REALLOCARRAY(stateP->coords, stateP->size);
987         if (stateP->coords == NULL)
988             pm_error("out of memory enlarging a fillhandle");
989     }
990 
991     if (stateP->n == 0) {
992         /* Start first segment. */
993         stateP->segstart = stateP->n;
994         stateP->ydir = 0;
995         stateP->startydir = 0;
996         addCoord(stateP, p);
997     } else {
998         pamd_point const prevPoint = stateP->coords[stateP->n - 1].point;
999         int const dx = p.x - prevPoint.x;
1000         int const dy = p.y - prevPoint.y;
1001 
1002         if (dx == 0 && dy == 0) {
1003             /* These are the same coords we had last time; don't bother */
1004         } else {
1005             if (abs(dx) > 1 || abs(dy) > 1)
1006                 startNewSegment(stateP);
1007             else
1008                 continueSegment(stateP, dy);
1009 
1010             addCoord(stateP, p);
1011         }
1012     }
1013 }
1014 
1015 
1016 
1017 #ifndef LITERAL_FN_DEF_MATCH
1018 static qsort_comparison_fn yxCompare;
1019 #endif
1020 
1021 static int
yxCompare(const void * const c1Arg,const void * const c2Arg)1022 yxCompare(const void * const c1Arg,
1023           const void * const c2Arg) {
1024 
1025     const coord * const c1P = c1Arg;
1026     const coord * const c2P = c2Arg;
1027 
1028     pamd_point const p1 = c1P->point;
1029     pamd_point const p2 = c2P->point;
1030 
1031     int retval;
1032 
1033     if (p1.y > p2.y)
1034         retval = 1;
1035     else if (p1.y < p2.y)
1036         retval = -1;
1037     else if (p1.x > p2.x)
1038         retval = 1;
1039     else if (p1.x < p2.x)
1040         retval = -1;
1041     else
1042         retval = 0;
1043 
1044     return retval;
1045 }
1046 
1047 
1048 
1049 void
pamd_fill(tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,struct fillobj * const fillObjP,pamd_drawproc drawProc,const void * const clientdata)1050 pamd_fill(tuple **         const tuples,
1051           int              const cols,
1052           int              const rows,
1053           int              const depth,
1054           sample           const maxval,
1055           struct fillobj * const fillObjP,
1056           pamd_drawproc          drawProc,
1057           const void *     const clientdata) {
1058 
1059     struct fillState * const fh = fillObjP->stateP;
1060 
1061     int pedge;
1062     int i, edge, lx, rx, py;
1063     coord * cp;
1064     bool eq;
1065     bool leftside;
1066 
1067     /* Close off final segment. */
1068     if (fh->n > 0 && fh->startydir != 0 && fh->ydir != 0) {
1069         if (fh->startydir == fh->ydir) {
1070             /* Oops, first edge and last edge are the same. */
1071             coord * fcp;
1072             const coord * const fcpLast = & (fh->coords[fh->n - 1]);
1073             int lastedge, oldedge;
1074 
1075             lastedge = fh->coords[fh->n - 1].edge;
1076             fcp = &(fh->coords[fh->segstart]);
1077             oldedge = fcp->edge;
1078             for ( ; fcp<=fcpLast && fcp->edge == oldedge; ++fcp )
1079                 fcp->edge = lastedge;
1080         }
1081     }
1082     /* Restore clipping now. */
1083     pamd_setlineclip(oldclip);
1084 
1085     /* Sort the coords by Y, secondarily by X. */
1086     qsort((char*) fh->coords, fh->n, sizeof(coord), yxCompare);
1087 
1088     /* Find equal coords with different edge numbers, and swap if necessary. */
1089     edge = -1;
1090     for (i = 0; i < fh->n; ++i) {
1091         cp = &fh->coords[i];
1092         if (i > 1 && eq && cp->edge != edge && cp->edge == pedge) {
1093             /* Swap .-1 and .-2. */
1094             coord t;
1095 
1096             t = fh->coords[i-1];
1097             fh->coords[i-1] = fh->coords[i-2];
1098             fh->coords[i-2] = t;
1099         }
1100         if (i > 0) {
1101             if (cp->point.x == lx && cp->point.y == py) {
1102                 eq = TRUE;
1103                 if (cp->edge != edge && cp->edge == pedge) {
1104                     /* Swap . and .-1. */
1105                     coord t;
1106 
1107                     t = *cp;
1108                     *cp = fh->coords[i-1];
1109                     fh->coords[i-1] = t;
1110                 }
1111             } else
1112                 eq = FALSE;
1113         }
1114         lx    = cp->point.x;
1115         py    = cp->point.y;
1116         pedge = edge;
1117         edge  = cp->edge;
1118     }
1119 
1120     /* Ok, now run through the coords filling spans. */
1121     for (i = 0; i < fh->n; ++i) {
1122         cp = &fh->coords[i];
1123         if (i == 0) {
1124             lx       = rx = cp->point.x;
1125             py       = cp->point.y;
1126             edge     = cp->edge;
1127             leftside = TRUE;
1128         } else {
1129             if (cp->point.y != py) {
1130                 /* Row changed.  Emit old span and start a new one. */
1131                 pamd_filledrectangle(
1132                     tuples, cols, rows, depth, maxval, lx, py, rx - lx + 1, 1,
1133                     drawProc, clientdata);
1134                 lx       = rx = cp->point.x;
1135                 py       = cp->point.y;
1136                 edge     = cp->edge;
1137                 leftside = TRUE;
1138             } else {
1139                 if (cp->edge == edge) {
1140                     /* Continuation of side. */
1141                     rx = cp->point.x;
1142                 } else {
1143                     /* Edge changed.  Is it a span? */
1144                     if (leftside) {
1145                         rx       = cp->point.x;
1146                         leftside = FALSE;
1147                     } else {
1148                         /* Got a span to fill. */
1149                         pamd_filledrectangle(
1150                             tuples, cols, rows, depth, maxval,
1151                             lx, py, rx - lx + 1, 1, drawProc, clientdata);
1152                         lx       = rx = cp->point.x;
1153                         leftside = TRUE;
1154                     }
1155                     edge = cp->edge;
1156                 }
1157             }
1158         }
1159     }
1160 }
1161 
1162 
1163 
1164 /* Table used to look up sine of angles from 0 through 90 degrees.
1165    The value returned is the sine * 65536.  Symmetry is used to
1166    obtain sine and cosine for arbitrary angles using this table. */
1167 
1168 static long sintab[] = {
1169     0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, 9120, 10252, 11380,
1170     12504, 13625, 14742, 15854, 16961, 18064, 19160, 20251, 21336,
1171     22414, 23486, 24550, 25606, 26655, 27696, 28729, 29752, 30767,
1172     31772, 32768, 33753, 34728, 35693, 36647, 37589, 38521, 39440,
1173     40347, 41243, 42125, 42995, 43852, 44695, 45525, 46340, 47142,
1174     47929, 48702, 49460, 50203, 50931, 51643, 52339, 53019, 53683,
1175     54331, 54963, 55577, 56175, 56755, 57319, 57864, 58393, 58903,
1176     59395, 59870, 60326, 60763, 61183, 61583, 61965, 62328, 62672,
1177     62997, 63302, 63589, 63856, 64103, 64331, 64540, 64729, 64898,
1178     65047, 65176, 65286, 65376, 65446, 65496, 65526, 65536
1179 };
1180 
1181 static int extleft, exttop, extright, extbottom;  /* To accumulate extents */
1182 
1183 
1184 
1185 /* LINTLIBRARY */
1186 
1187 static long
isin(int const argDeg)1188 isin(int const argDeg) {
1189 /*----------------------------------------------------------------------------
1190     Return sine of an angle in integral degrees.  The value returned is 65536
1191     times the sine.
1192 -----------------------------------------------------------------------------*/
1193 
1194     int deg360;
1195         /* The argument reduced to the range 0-360 degrees */
1196 
1197     if (argDeg < 0) {
1198         deg360 = (360 - ((- argDeg) % 360)) % 360;
1199     } else if (argDeg >= 360) {
1200         deg360 = argDeg % 360;
1201     } else
1202         deg360 = argDeg;
1203 
1204     /* Now look up from table according to quadrant. */
1205 
1206     if (deg360 <= 90) {
1207         return sintab[deg360];
1208     } else if (deg360 <= 180) {
1209         return sintab[180 - deg360];
1210     } else if (deg360 <= 270) {
1211         return -sintab[deg360 - 180];
1212     }
1213     return -sintab[360 - deg360];
1214 }
1215 
1216 
1217 
1218 static long
icos(int const deg)1219 icos(int const deg) {
1220 /*----------------------------------------------------------------------------
1221   Return cosine of an angle in integral degrees.  The value returned is 65536
1222   times the cosine
1223 -----------------------------------------------------------------------------*/
1224     return isin(deg + 90);
1225 }
1226 
1227 
1228 
1229 static int
twosCompByteValue(unsigned char const c)1230 twosCompByteValue(unsigned char const c) {
1231 /*----------------------------------------------------------------------------
1232    E.g. if 'c' is 0x5, return 5.  If 'c' is 0xF0, return -16.
1233 -----------------------------------------------------------------------------*/
1234     return (char)c;
1235 }
1236 
1237 
1238 
1239 static int
glyphSkipBefore(const struct ppmd_glyph * const glyphP)1240 glyphSkipBefore(const struct ppmd_glyph * const glyphP) {
1241 
1242     return twosCompByteValue(glyphP->header.skipBefore);
1243 }
1244 
1245 
1246 
1247 static int
glyphWidth(const struct ppmd_glyph * const glyphP)1248 glyphWidth(const struct ppmd_glyph * const glyphP) {
1249 
1250     return twosCompByteValue(glyphP->header.skipAfter) -
1251         twosCompByteValue(glyphP->header.skipBefore);
1252 }
1253 
1254 
1255 static pamd_point
commandPoint(const struct ppmd_glyphCommand * const commandP)1256 commandPoint(const struct ppmd_glyphCommand * const commandP) {
1257 /*----------------------------------------------------------------------------
1258    Return the point which is the argument of glyph drawing command
1259    *commandP.  The origin of the coordinate system for this point
1260    is the center of the glyph cell and the scale is the scale of the
1261    font, so (-10, -10) means the upper left corner of the glyph cell.
1262 -----------------------------------------------------------------------------*/
1263     return makePoint(twosCompByteValue(commandP->x),
1264                      twosCompByteValue(commandP->y));
1265 }
1266 
1267 #define Scalef 21       /* Font design size */
1268 #define Descend 9       /* Descender offset */
1269 
1270 
1271 static pamd_point
textPosFromFontPos(pamd_point const fontPos,pamd_point const textBoxOrigin,pamd_point const center,pamd_point const glyphOrigin,unsigned int const height,long const rotcos,long const rotsin)1272 textPosFromFontPos(pamd_point   const fontPos,
1273                    pamd_point   const textBoxOrigin,
1274                    pamd_point   const center,
1275                    pamd_point   const glyphOrigin,
1276                    unsigned int const height,
1277                    long         const rotcos,
1278                    long         const rotsin) {
1279 /*----------------------------------------------------------------------------
1280    'fontPos' is a position within a glyph as told by the font definition.
1281    It is relative to the center of the glyph, in units of font tuples
1282    (1/21 of a glyph cell).
1283 
1284    We return the position on the canvas of that point.
1285 
1286    That takes into account where in the text box we are, where the text box
1287    is on the canvas, the size of the characters, and the rotation of the
1288    text box.
1289 -----------------------------------------------------------------------------*/
1290     pamd_point const ptl = vectorSum(center, fontPos);
1291         /* Position relative to the top left of the standard glyph cell */
1292 
1293     pamd_point const pl = vectorSum(glyphOrigin, ptl);
1294         /* Position relative to the top left of the whole text box,
1295            assuming the text box is horizontal and has font scale.
1296         */
1297 
1298     pamd_point const ps = makePoint((pl.x * (int)height) / Scalef,
1299                                     (pl.y * (int)height) / Scalef);
1300          /* Same as above, but with the text box its actual size */
1301 
1302     pamd_point const retval =
1303         makePoint(textBoxOrigin.x +
1304                   (ps.x * rotcos - (ps.y-(int)height) * rotsin) / 65536,
1305                   textBoxOrigin.y +
1306                   (ps.x * rotsin + (ps.y-(int)height) * rotcos) / 65536);
1307 
1308     pamd_validatePoint(retval);
1309 
1310     return retval;
1311 }
1312 
1313 
1314 
1315 static void
drawGlyph(const struct ppmd_glyph * const glyphP,pamd_point const glyphOrigin,tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,int const height,pamd_point const textBoxOrigin,long const rotcos,long const rotsin,pamd_drawproc drawProc,const void * const clientdata,unsigned int * const cursorAdvanceP)1316 drawGlyph(const struct ppmd_glyph * const glyphP,
1317           pamd_point                const glyphOrigin,
1318           tuple **                  const tuples,
1319           unsigned int              const cols,
1320           unsigned int              const rows,
1321           unsigned int              const depth,
1322           sample                    const maxval,
1323           int                       const height,
1324           pamd_point                const textBoxOrigin,
1325           long                      const rotcos,
1326           long                      const rotsin,
1327           pamd_drawproc                   drawProc,
1328           const void *              const clientdata,
1329           unsigned int *            const cursorAdvanceP
1330     ) {
1331 /*----------------------------------------------------------------------------
1332   'glyphOrigin' is the position relative to the upper left corner of the text
1333   box of the upper left corner of this glyph cell.  It is in units of font
1334   tuples (so you have to scale it by the font size to actual distance on
1335   the canvas).
1336 
1337   We return as *cursorAdvanceP the amount to the right of this glyph cell
1338   the next glyph cell on the line (if any) should be.
1339 
1340   The actual glyph cell may be a little to the left of the nominal position
1341   because of kerning.  The font says how much to shift the cell left.
1342 
1343   'textBoxOrigin' is the left end of the baseline of the top line in the
1344   text box, in the coordinate system of the canvas.  'rotcos' and 'rotsin'
1345   tell how that text box is rotated with respect to the horizontal on the
1346   canvas.
1347 
1348   'height' is the height in canvas tuples of a glyph.  This is a scale factor
1349   to convert font coordinates to canvas coordinates.
1350 -----------------------------------------------------------------------------*/
1351     pamd_point const center = makePoint(-glyphSkipBefore(glyphP), Scalef/2);
1352         /* This is what you have to add to the coordinates in a glyph
1353            command, which are relative to the center of the glyph, to get
1354            coordinates relative to the upper left corner of the glyph
1355         */
1356     pamd_point p;
1357         /* Current drawing position within the glyph.  Origin is the top
1358            left of the glyph cell.  Units are font tuples.
1359         */
1360     unsigned int commandNum;
1361 
1362     p = textPosFromFontPos(makePoint(0, 0),
1363                            textBoxOrigin,
1364                            center,
1365                            glyphOrigin,
1366                            height,
1367                            rotcos, rotsin);   /* initial value */
1368 
1369     for (commandNum = 0;
1370          commandNum < glyphP->header.commandCount;
1371          ++commandNum) {
1372 
1373         const struct ppmd_glyphCommand * const commandP =
1374             &glyphP->commandList[commandNum];
1375 
1376         switch (commandP->verb) {
1377         case CMD_NOOP:
1378             break;
1379         case CMD_DRAWLINE:
1380         {
1381             pamd_point const n = textPosFromFontPos(commandPoint(commandP),
1382                                                     textBoxOrigin,
1383                                                     center,
1384                                                     glyphOrigin,
1385                                                     height,
1386                                                     rotcos, rotsin);
1387 
1388             pamd_line(tuples, cols, rows, depth, maxval, p, n,
1389                       drawProc, clientdata);
1390 
1391             p = n;
1392         }
1393         break;
1394         case CMD_MOVEPEN:
1395             p = textPosFromFontPos(commandPoint(commandP),
1396                                    textBoxOrigin,
1397                                    center,
1398                                    glyphOrigin,
1399                                    height,
1400                                    rotcos, rotsin);
1401             break;
1402         }
1403     }
1404     *cursorAdvanceP = glyphWidth(glyphP);
1405 }
1406 
1407 
1408 
1409 void
pamd_text(tuple ** const tuples,int const cols,int const rows,int const depth,sample const maxval,pamd_point const pos,int const height,int const angle,const char * const sArg,pamd_drawproc drawProc,const void * const clientdata)1410 pamd_text(tuple**       const tuples,
1411           int           const cols,
1412           int           const rows,
1413           int           const depth,
1414           sample        const maxval,
1415           pamd_point    const pos,
1416           int           const height,
1417           int           const angle,
1418           const char *  const sArg,
1419           pamd_drawproc       drawProc,
1420           const void *  const clientdata) {
1421 /*----------------------------------------------------------------------------
1422    Draw the zero-terminated string 'sArg', with its baseline starting at point
1423    'pos', inclined by 'angle' degrees to the X axis, with letters 'height'
1424    tuples high (descenders will extend below the baseline).  We pass the
1425    supplied drawproc and clientdata to pamd_linep, which performs the actual
1426    drawing.
1427 
1428    There may be multiple lines of text.  The baseline of the topmost line
1429    starts at 'pos'.
1430 -----------------------------------------------------------------------------*/
1431     const struct ppmd_font * const fontP = ppmd_get_font();
1432 
1433     long rotsin, rotcos;
1434     pamd_point p;
1435     const char * s;
1436 
1437     pamd_validatePoint(pos);
1438 
1439     p = makePoint(0, 0);
1440     rotsin = isin(-angle);
1441     rotcos = icos(-angle);
1442 
1443     for (s = &sArg[0]; *s; ) {
1444         unsigned char const ch = *s++;
1445 
1446         if (ch >= fontP->header.firstCodePoint &&
1447             ch < fontP->header.firstCodePoint + fontP->header.characterCount) {
1448 
1449             const struct ppmd_glyph * const glyphP =
1450                 &fontP->glyphTable[ch - fontP->header.firstCodePoint];
1451 
1452             unsigned int cursorAdvance;
1453 
1454             pamd_validatePoint(p);
1455 
1456             drawGlyph(glyphP, p, tuples, cols, rows, depth, maxval,
1457                       height, pos, rotcos, rotsin,
1458                       drawProc, clientdata, &cursorAdvance);
1459             p.x += cursorAdvance;
1460         } else if (ch == '\n') {
1461             /* Move to the left edge of the next line down */
1462             p.y += Scalef + Descend;
1463             p.x = 0;
1464         }
1465     }
1466 }
1467 
1468 
1469 
1470 #ifndef LITERAL_FN_DEF_MATCH
1471 static pamd_drawproc extetnsDrawproc;
1472 #endif
1473 
1474 static void
extentsDrawproc(tuple ** const tuples,unsigned int const cols,unsigned int const rows,unsigned int const depth,sample const maxval,pamd_point const p,const void * const clientdata)1475 extentsDrawproc(tuple**      const tuples,
1476                 unsigned int const cols,
1477                 unsigned int const rows,
1478                 unsigned int const depth,
1479                 sample       const maxval,
1480                 pamd_point   const p,
1481                 const void * const clientdata) {
1482 /*----------------------------------------------------------------------------
1483     Drawproc which just accumulates the extents rectangle bounding the
1484     text.
1485 -----------------------------------------------------------------------------*/
1486     extleft   = MIN(extleft,   p.x);
1487     exttop    = MIN(exttop,    p.y);
1488     extright  = MAX(extright,  p.x);
1489     extbottom = MAX(extbottom, p.y);
1490 }
1491 
1492 
1493 
1494 void
pamd_text_box(int const height,int const angle,const char * const s,int * const leftP,int * const topP,int * const rightP,int * const bottomP)1495 pamd_text_box(int          const height,
1496               int          const angle,
1497               const char * const s,
1498               int *        const leftP,
1499               int *        const topP,
1500               int *        const rightP,
1501               int *        const bottomP) {
1502 /*----------------------------------------------------------------------------
1503   Calculate extents rectangle for a given piece of text.  For most
1504   applications where extents are needed, angle should be zero to obtain the
1505   unrotated extents.  If you need the extents box for post-rotation text,
1506   however, you can set angle nonzero and it will be calculated correctly.
1507 -----------------------------------------------------------------------------*/
1508     extleft   =  32767;
1509     exttop    =  32767;
1510     extright  = -32767;
1511     extbottom = -32767;
1512 
1513     pamd_text(NULL, 32767, 32767, 255, 255, makePoint(1000, 1000),
1514               height, angle, s,
1515               extentsDrawproc, NULL);
1516 
1517     *leftP   = extleft   - 1000;
1518     *topP    = exttop    - 1000;
1519     *rightP  = extright  - 1000;
1520     *bottomP = extbottom - 1000;
1521 }
1522 
1523 
1524 
1525