Image Interpolation with Geometric Contour Stencils
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pen.c
Go to the documentation of this file.
1 
15 #include <stdio.h>
16 #include "pen.h"
17 #include "svd2x2.h"
18 
19 
21 #define FILE_BUFFER_CAPACITY (1024*4)
22 
23 
25 const pentrans IdentityPenTrans = {1, 0, 0, 1, 0, 0};
26 
28 static const unsigned long Pow10[] = {1,10,100,1000,10000,100000,
29  1000000,10000000,100000000};
30 
31 
33 static int StubDrawLine(ATTRIBUTE_UNUSED pen *Pen,
34  ATTRIBUTE_UNUSED float x1, ATTRIBUTE_UNUSED float y1,
35  ATTRIBUTE_UNUSED float x2, ATTRIBUTE_UNUSED float y2)
36 {
37  ErrorMessage("Pen::DrawLine callback has not been defined.\n");
38  return 0;
39 }
40 
41 
43 static int StubDrawQBezier(pen *Pen, float x1, float y1,
44  float x2, float y2, float x3, float y3)
45 {
46  float L, t, xlast, ylast, xcur, ycur;
47  int i, NumLines;
48 
49  /* Check whether DrawLine is available */
50  if(!Pen || !(Pen->DrawLine) || Pen->DrawLine == StubDrawLine)
51  {
52  ErrorMessage("Pen::DrawQBezier callback has not been defined.\n");
53  return 0;
54  }
55 
56  /* Approximate the quadratic Bezier curve using lines */
57  L = EstArcLenQBezier(x1, y1, x2, y2, x3, y3, Pen->Trans);
58  NumLines = (int)(L*0.04);
59  NumLines = (NumLines < 4) ? 4 : NumLines;
60  xlast = x1;
61  ylast = y1;
62 
63  for(i = 1; i <= NumLines; i++)
64  {
65  t = ((float)i)/NumLines;
66  xcur = (1 - t)*((1 - t)*x1 + 2*t*x2) + t*t*x3;
67  ycur = (1 - t)*((1 - t)*y1 + 2*t*y2) + t*t*y3;
68  Pen->DrawLine(Pen, xlast, ylast, xcur, ycur);
69  xlast = xcur;
70  ylast = ycur;
71  }
72 
73  return 1;
74 }
75 
76 
78 static int StubDrawEllipse(pen *Pen,
79  float x, float y, float rx, float ry, float Theta)
80 {
81  const float CosTheta = (float)cos(Theta), SinTheta = (float)sin(Theta);
82  float C, t, xlast, ylast, xcur, ycur;
83  int i, NumLines;
84 
85  /* Check whether DrawLine is available */
86  if(!Pen || !(Pen->DrawLine) || Pen->DrawLine == StubDrawLine)
87  {
88  ErrorMessage("Pen::DrawEllipse callback has not been defined.\n");
89  return 0;
90  }
91 
92  /* Approximate the ellipse using lines */
93  C = EstArcLenEllipse(x, y, rx, ry, Theta, Pen->Trans);
94  NumLines = (int)(C*0.07);
95  NumLines = (NumLines < 8) ? 8 : NumLines;
96 
97  xlast = x + CosTheta*rx;
98  ylast = y - SinTheta*rx;
99 
100  for(i = 1; i <= NumLines; i++)
101  {
102  t = (i < NumLines) ? ((float)i)*M_2PI/NumLines : 0;
103  xcur = x + CosTheta*rx*cos(t) + SinTheta*ry*sin(t);
104  ycur = y - SinTheta*rx*cos(t) + CosTheta*ry*sin(t);
105  Pen->DrawLine(Pen, xlast, ylast, xcur, ycur);
106  xlast = xcur;
107  ylast = ycur;
108  }
109 
110  return 0;
111 }
112 
113 
122 {
123  static const pen InitPen = {NULL, NULL, 0, 0, PEN_DEFAULT_NUMDIGITS,
124  {1, 0, 0, 1, 0, 0}, NULL, 0, 0, {0, 0, 0}, -1,
125  NULL, StubDrawLine, StubDrawQBezier, StubDrawEllipse};
126  pen *Pen;
127 
128  if((Pen = (pen *)Malloc(sizeof(pen))))
129  *Pen = InitPen;
130 
131  return Pen;
132 }
133 
134 
139 void FreePen(pen *Pen)
140 {
141  if(Pen)
142  {
143  Free(Pen->Palette);
144  Free(Pen);
145  }
146 }
147 
148 
153 FILE *PenGetFile(pen *Pen)
154 {
155  return (Pen) ? Pen->File : NULL;
156 }
157 
158 
167 int PenDrawLine(pen *Pen, float x1, float y1, float x2, float y2)
168 {
169  return (Pen && Pen->DrawLine(Pen, x1, y1, x2, y2));
170 }
171 
172 
179 int PenDrawRectangle(pen *Pen, float x1, float y1, float x2, float y2)
180 {
181  return (Pen
182  && Pen->DrawLine(Pen, x1, y1, x2, y1)
183  && Pen->DrawLine(Pen, x2, y1, x2, y2)
184  && Pen->DrawLine(Pen, x2, y2, x1, y2)
185  && Pen->DrawLine(Pen, x1, y2, x1, y1));
186 }
187 
188 
197 int PenDrawQBezier(pen *Pen, float x1, float y1,
198  float x2, float y2, float x3, float y3)
199 {
200  return (Pen && Pen->DrawQBezier(Pen, x1, y1, x2, y2, x3, y3));
201 }
202 
203 
213 int PenDrawCircle(pen *Pen, float x, float y, float r)
214 {
215  return (Pen && Pen->DrawEllipse(Pen, x, y, r, r, 0));
216 }
217 
218 
230  float x, float y, float rx, float ry, float Theta)
231 {
232  return (Pen && Pen->DrawEllipse(Pen, x, y, rx, ry, Theta));
233 }
234 
235 
245 void PenWriteDouble(FILE *File, double Value, int NumDigits)
246 {
247  int i;
248  unsigned long Digit, Whole, Frac;
249 
250  /* Write sign and negate if Value is negative */
251  if(Value < 0)
252  {
253  putc('-', File);
254  Value = -Value;
255  }
256 
257  if(NumDigits > 0)
258  {
259  if(NumDigits > PEN_MAXDIGITS)
260  NumDigits = PEN_MAXDIGITS;
261 
262  /* Write the whole part */
263  Whole = (unsigned long)Value;
264  /* Represent fractional part as an integer */
265  Frac = (unsigned long)(Pow10[NumDigits]*(Value - Whole) + 0.5f);
266 
267  /* Carry the rounding +0.5/Pow10[NumDigits] if necessary */
268  if(Frac >= Pow10[NumDigits])
269  {
270  Whole++;
271  Frac -= Pow10[NumDigits];
272  }
273 
274  fprintf(File, "%lu", Whole);
275 
276  /* Write fractional part without trailing zeros */
277  if(Frac > 0)
278  {
279  putc('.', File);
280 
281  for(i = NumDigits - 1; Frac > 0 && i >= 0; i--)
282  {
283  Digit = Frac / Pow10[i];
284  Frac %= Pow10[i];
285  putc('0' + (char)Digit, File);
286  }
287  }
288  }
289  else
290  fprintf(File, "%lu", (unsigned long)(Value + 0.5f));
291 }
292 
293 
302 {
303  return (Pen) ? Pen->NumDigits : PEN_DEFAULT_NUMDIGITS;
304 }
305 
306 
314 void PenSetNumDigits(pen *Pen, int NumDigits)
315 {
316  if(Pen)
317  Pen->NumDigits = (NumDigits < PEN_MAXDIGITS)
318  ? NumDigits : PEN_MAXDIGITS;
319 }
320 
321 
331 {
332  return (Pen) ? Pen->Trans : IdentityPenTrans;
333 }
334 
335 
344 void PenSetTrans(pen *Pen, pentrans Trans)
345 {
346  if(Pen)
347  Pen->Trans = Trans;
348 }
349 
350 
356 void PenTransformCanvas(pen *Pen, double a, double b,
357  double c, double d, double e, double f)
358 {
359  if(Pen)
360  {
361  pentrans NewTrans;
362 
363  NewTrans.a = Pen->Trans.a*a
364  + Pen->Trans.c*b;
365  NewTrans.b = Pen->Trans.b*a
366  + Pen->Trans.d*b;
367  NewTrans.c = Pen->Trans.a*c
368  + Pen->Trans.c*d;
369  NewTrans.d = Pen->Trans.b*c
370  + Pen->Trans.d*d;
371 
372  NewTrans.e = Pen->Trans.e
373  + Pen->Trans.a*e
374  + Pen->Trans.c*f;
375  NewTrans.f = Pen->Trans.f
376  + Pen->Trans.b*e
377  + Pen->Trans.d*f;
378 
379  Pen->Trans = NewTrans;
380  }
381 }
382 
383 
389 void PenTranslateCanvas(pen *Pen, double tx, double ty)
390 {
391  if(Pen)
392  PenTransformCanvas(Pen, 1, 0, 0, 1, tx, ty);
393 }
394 
395 
401 void PenScaleCanvas(pen *Pen, double XScale, double YScale)
402 {
403  if(Pen)
404  PenTransformCanvas(Pen, XScale, 0, 0, YScale, 0, 0);
405 }
406 
407 
413 void PenRotateCanvas(pen *Pen, double Theta)
414 {
415  if(Pen)
416  {
417  const double CosTheta = cos(Theta);
418  const double SinTheta = sin(Theta);
419  PenTransformCanvas(Pen, CosTheta, -SinTheta, SinTheta, CosTheta, 0, 0);
420  }
421 }
422 
423 
429 void PenXSkewCanvas(pen *Pen, double Skew)
430 {
431  if(Pen)
432  PenTransformCanvas(Pen, 1, 0, Skew, 1, 0, 0);
433 }
434 
435 
441 void PenYSkewCanvas(pen *Pen, double Skew)
442 {
443  if(Pen)
444  PenTransformCanvas(Pen, 1, Skew, 0, 1, 0, 0);
445 }
446 
447 
454 int PenColorToIndex(pen *Pen, const float *Color)
455 {
456  int i;
457 
458  if(!Pen || !(Pen->Palette))
459  return -1;
460 
461  for(i = 0; i < Pen->NumPalette; i++)
462  if(Pen->Palette[i][0] == Color[0]
463  && Pen->Palette[i][1] == Color[1]
464  && Pen->Palette[i][2] == Color[2])
465  return i;
466 
467  return -1;
468 }
469 
470 
480 int PenDefineColor(pen *Pen, const float *Color)
481 {
482  const int NumPalette = Pen->NumPalette;
483  int i;
484 
485  if(!Pen || !Color)
486  return 0;
487 
488  /* Check if Color is already in Pen->Palette */
489  if(PenColorToIndex(Pen, Color) >= 0)
490  return 1;
491 
492  /* Allocate space for the new color if necessary */
493  if(NumPalette >= Pen->CapacityPalette)
494  {
495  float (*NewPalette)[3];
496  int NewCapacity = NumPalette + 1 + (NumPalette + 5)/10;
497 
498  if(!(NewPalette = (float (*)[3])Realloc(Pen->Palette,
499  sizeof(float)*3*NewCapacity)))
500  return 0;
501 
502  Pen->Palette = NewPalette;
503  Pen->CapacityPalette = NewCapacity;
504  }
505 
506  for(i = 0; i < 3; i++)
507  Pen->Palette[NumPalette][i] = Color[i];
508 
509  (Pen->NumPalette)++;
510  return 1;
511 }
512 
513 
520 int PenSetColor(pen *Pen, const float *Color)
521 {
522  if(Pen && Color)
523  {
524  Pen->Color[0] = Color[0];
525  Pen->Color[1] = Color[1];
526  Pen->Color[2] = Color[2];
527 
528  if(Pen->SetColor)
529  Pen->SetColor(Pen, Color);
530  }
531 
532  return (Pen->ColorIndex = PenColorToIndex(Pen, Color));
533 }
534 
535 
545 void TransformEllipse(float *x, float *y, float *rx, float *ry, float *Theta,
546  pentrans Trans)
547 {
548  const double CosTheta = cos(*Theta), SinTheta = sin(*Theta);
549  double XTrans, YTrans, A[2][2], ThetaTrans, RxTrans, RyTrans, Phi;
550  int Sign1, Sign2;
551 
552  A[0][0] = (Trans.a*CosTheta - Trans.c*SinTheta)*(*rx);
553  A[1][0] = (Trans.b*CosTheta - Trans.d*SinTheta)*(*rx);
554  A[0][1] = (Trans.a*SinTheta + Trans.c*CosTheta)*(*ry);
555  A[1][1] = (Trans.b*SinTheta + Trans.d*CosTheta)*(*ry);
556 
557  /* Compute singular value decomposition of A */
558  Svd2x2(&ThetaTrans, &RxTrans, &RyTrans, &Phi, &Sign1, &Sign2, A);
559 
560  /* If ThetaTrans = +/- pi/2, swap x and y so that the rotation is zero. */
561  if(fabs(fabs(ThetaTrans) - M_PI/2) < 1e-12)
562  {
563  *rx = (float)RyTrans;
564  *ry = (float)RxTrans;
565  *Theta = 0;
566  }
567  else
568  {
569  *rx = (float)RxTrans;
570  *ry = (float)RyTrans;
571  *Theta = (float)ThetaTrans;
572  }
573 
574  XTrans = Trans.a*(*x) + Trans.c*(*y) + Trans.e;
575  YTrans = Trans.b*(*x) + Trans.d*(*y) + Trans.f;
576  *x = (float)XTrans;
577  *y = (float)YTrans;
578 }
579 
580 
589 float EstArcLenQBezier(float x1, float y1, float x2, float y2,
590  float x3, float y3, pentrans Trans)
591 {
592  float Temp;
593 
594  Temp = Trans.a*x1 + Trans.c*y1 + Trans.e;
595  y1 = Trans.b*x1 + Trans.d*y1 + Trans.f;
596  x1 = Temp;
597  Temp = Trans.a*x2 + Trans.c*y2 + Trans.e;
598  y2 = Trans.b*x2 + Trans.d*y2 + Trans.f;
599  x2 = Temp;
600  Temp = Trans.a*x3 + Trans.c*y3 + Trans.e;
601  y3 = Trans.b*x3 + Trans.d*y3 + Trans.f;
602  x3 = Temp;
603 
604  /* Crude overestimate of the arc length */
605  return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2))
606  + sqrt((x2 - x3)*(x2 - x3) + (y2 - y3)*(y2 - y3));
607 }
608 
609 
616 float EstArcLenEllipse(float x, float y, float rx, float ry, float Theta,
617  pentrans Trans)
618 {
619  float Temp;
620 
621  TransformEllipse(&x, &y, &rx, &ry, &Theta, Trans);
622  Temp = (rx - ry)/(rx + ry);
623  Temp *= Temp;
624  return M_PI*(rx + ry)*(1 + 3*Temp/(10 + sqrt(4 - 3*Temp)));
625 }
626 
627 
628 
629 /*****************************************************************************
630  * Raster-specific code *
631  *****************************************************************************/
632 
633 
635 #define SWAP(A,B) \
636  Temp = A; \
637  A = B; \
638  B = Temp;
639 
640 
641 static float fpart(float x)
642 {
643  return (float)(x - floor(x));
644 }
645 
646 
647 static void PlotPixel(float *Image, int Width, int Height, int x, int y,
648  float Alpha, const float *Color)
649 {
650  if(0 <= x && x < Width && 0 <= y && y < Height)
651  {
652  const float CAlpha = 1 - Alpha;
653 
654  Image += 3*(x + Width*y);
655  Image[0] = CAlpha*Image[0] + Alpha*Color[0];
656  Image[1] = CAlpha*Image[1] + Alpha*Color[1];
657  Image[2] = CAlpha*Image[2] + Alpha*Color[2];
658  }
659 }
660 
661 
668 static int RasterDrawLine(pen *Pen, float x1, float y1, float x2, float y2)
669 {
670  float *Image, *Color;
671  float yend, Gap, dx, dy, Gradient, y, Temp;
672  int Width, Height, xend, ix, iy, ix1, iy1, ix2, iy2, Swapped = 0;
673 
674 
675  if(!Pen || !(Image = Pen->Image))
676  return 0;
677 
678  Width = Pen->ImageWidth;
679  Height = Pen->ImageHeight;
680  Color = Pen->Color;
681 
682  /* Transform endpoint coordinates */
683  Temp = Pen->Trans.a*x1 + Pen->Trans.c*y1 + Pen->Trans.e;
684  y1 = Pen->Trans.b*x1 + Pen->Trans.d*y1 + Pen->Trans.f;
685  x1 = Temp;
686  Temp = Pen->Trans.a*x2 + Pen->Trans.c*y2 + Pen->Trans.e;
687  y2 = Pen->Trans.b*x2 + Pen->Trans.d*y2 + Pen->Trans.f;
688  x2 = Temp;
689 
690  dx = x2 - x1;
691  dy = y2 - y1;
692 
693  if(fabs(dx) < fabs(dy))
694  {
695  SWAP(x1,y1)
696  SWAP(x2,y2)
697  SWAP(dx,dy)
698  Swapped = 1;
699  }
700 
701  if(x2 < x1)
702  {
703  SWAP(x1,x2)
704  SWAP(y1,y2)
705  }
706 
707  if(dx == 0)
708  return 1;
709 
710  Gradient = dy/dx;
711 
712  xend = (int)floor(x1 + 0.5f);
713  yend = y1 + Gradient * (xend - x1);
714  y = yend + Gradient;
715  Gap = 1 - fpart(x1 + 0.5f);
716  ix1 = xend;
717  iy1 = (int)floor(yend);
718 
719  if(!Swapped)
720  {
721  PlotPixel(Image, Width, Height, ix1, iy1, (1 - fpart(yend)) * Gap, Color);
722  PlotPixel(Image, Width, Height, ix1, iy1 + 1, fpart(yend) * Gap, Color);
723  }
724  else
725  {
726  PlotPixel(Image, Width, Height, iy1, ix1, (1 - fpart(yend)) * Gap, Color);
727  PlotPixel(Image, Width, Height, iy1 + 1, ix1, fpart(yend) * Gap, Color);
728  }
729 
730  xend = (int)floor(x2 + 0.5f);
731  yend = y2 + Gradient * (xend - x2);
732  Gap = fpart(x2 + 0.5f);
733  ix2 = xend;
734  iy2 = (int)floor(yend);
735 
736  if(!Swapped)
737  {
738  PlotPixel(Image, Width, Height, ix2, iy2, (1 - fpart(yend)) * Gap, Color);
739  PlotPixel(Image, Width, Height, ix2, iy2 + 1, fpart(yend) * Gap, Color);
740 
741  for(ix = ix1 + 1; ix < ix2; ix++)
742  {
743  iy = (int)y;
744  PlotPixel(Image, Width, Height, ix, iy, 1 - fpart(y), Color);
745  PlotPixel(Image, Width, Height, ix, iy + 1, fpart(y), Color);
746  y += Gradient;
747  }
748  }
749  else
750  {
751  PlotPixel(Image, Width, Height, iy2, ix2, (1 - fpart(yend)) * Gap, Color);
752  PlotPixel(Image, Width, Height, iy2 + 1, ix2, fpart(yend) * Gap, Color);
753 
754  for(ix = ix1 + 1; ix < ix2; ix++)
755  {
756  iy = (int)y;
757  PlotPixel(Image, Width, Height, iy, ix, 1 - fpart(y), Color);
758  PlotPixel(Image, Width, Height, iy + 1, ix, fpart(y), Color);
759  y += Gradient;
760  }
761  }
762 
763  return 1;
764 }
765 
766 
774 int PenRenderToImage(pen *Pen, float *Image, int Width, int Height)
775 {
776  if(!Pen || !Image || Width <= 0 || Height <= 0)
777  return 0;
778 
779  Pen->Image = Image;
780  Pen->SetColor = NULL;
781  Pen->ImageWidth = Width;
782  Pen->ImageHeight = Height;
783  Pen->DrawLine = RasterDrawLine;
784  return 1;
785 }
786 
787 
788 
789 /*****************************************************************************
790  * EPS-specific code *
791  *****************************************************************************/
792 
798 void EpsSetColor(pen *Pen, const float *Color)
799 {
800  FILE *File;
801 
802  if(Pen && (File = Pen->File))
803  {
804  PenWriteDouble(File, Color[0], Pen->NumDigits);
805  putc(' ', File);
806  PenWriteDouble(File, Color[1], Pen->NumDigits);
807  putc(' ', File);
808  PenWriteDouble(File, Color[2], Pen->NumDigits);
809  fputs(" setrgbcolor\n", File);
810  }
811 }
812 
813 
820 static int EpsDrawLine(pen *Pen, float x1, float y1, float x2, float y2)
821 {
822  FILE *File;
823  double xt1, yt1, xt2, yt2;
824  int NumDigits;
825 
826  if(!Pen || !(File = Pen->File))
827  return 0;
828 
829  NumDigits = Pen->NumDigits;
830 
831  xt1 = Pen->Trans.a*x1 + Pen->Trans.c*y1 + Pen->Trans.e;
832  yt1 = Pen->Trans.b*x1 + Pen->Trans.d*y1 + Pen->Trans.f;
833  xt2 = Pen->Trans.a*x2 + Pen->Trans.c*y2 + Pen->Trans.e;
834  yt2 = Pen->Trans.b*x2 + Pen->Trans.d*y2 + Pen->Trans.f;
835 
836  PenWriteDouble(File, xt2 - xt1, NumDigits);
837  putc(' ', File);
838  PenWriteDouble(File, yt2 - yt1, NumDigits);
839  putc(' ', File);
840  PenWriteDouble(File, xt1, NumDigits);
841  putc(' ', File);
842  PenWriteDouble(File, yt1, NumDigits);
843  fputs(" m1\n", File);
844  return 1;
845 }
846 
847 
854 static int EpsDrawQBezier(pen *Pen, float x1, float y1,
855  float x2, float y2, float x3, float y3)
856 {
857  FILE *File;
858  double xt1, yt1, xt2, yt2, xt3, yt3;
859  int NumDigits;
860 
861  if(!Pen || !(File = Pen->File))
862  return 0;
863 
864  NumDigits = Pen->NumDigits;
865  xt1 = Pen->Trans.a*x1 + Pen->Trans.c*y1 + Pen->Trans.e;
866  yt1 = Pen->Trans.b*x1 + Pen->Trans.d*y1 + Pen->Trans.f;
867  xt2 = Pen->Trans.a*x2 + Pen->Trans.c*y2 + Pen->Trans.e;
868  yt2 = Pen->Trans.b*x2 + Pen->Trans.d*y2 + Pen->Trans.f;
869  xt3 = Pen->Trans.a*x3 + Pen->Trans.c*y3 + Pen->Trans.e;
870  yt3 = Pen->Trans.b*x3 + Pen->Trans.d*y3 + Pen->Trans.f;
871 
872  PenWriteDouble(File, (2.0/3.0)*(xt2 - xt1), NumDigits);
873  putc(' ', File);
874  PenWriteDouble(File, (2.0/3.0)*(yt2 - yt1), NumDigits);
875  putc(' ', File);
876  PenWriteDouble(File, (xt3 - xt1) + (2.0/3.0)*(xt2 - xt3), NumDigits);
877  putc(' ', File);
878  PenWriteDouble(File, (yt3 - yt1) + (2.0/3.0)*(yt2 - yt3), NumDigits);
879  putc(' ', File);
880  PenWriteDouble(File, xt3 - xt1, NumDigits);
881  putc(' ', File);
882  PenWriteDouble(File, yt3 - yt1, NumDigits);
883  putc(' ', File);
884  PenWriteDouble(File, xt1, NumDigits);
885  putc(' ', File);
886  PenWriteDouble(File, yt1, NumDigits);
887  fputs(" m2\n", File);
888  return 1;
889 }
890 
891 
900 static int EpsDrawEllipse(pen *Pen, float x, float y,
901  float rx, float ry, float Theta)
902 {
903  FILE *File;
904  int NumDigits;
905 
906  if(!Pen || !(File = Pen->File))
907  return 0;
908 
909  NumDigits = Pen->NumDigits;
910  TransformEllipse(&x, &y, &rx, &ry, &Theta, Pen->Trans);
911 
912  PenWriteDouble(File, rx, NumDigits);
913  putc(' ', File);
914  PenWriteDouble(File, ry, NumDigits);
915  putc(' ', File);
916  PenWriteDouble(File, Theta*(180/M_PI), NumDigits);
917  putc(' ', File);
918  PenWriteDouble(File, x, NumDigits);
919  putc(' ', File);
920  PenWriteDouble(File, y, NumDigits);
921  fputs(" m3\n", File);
922  return 1;
923 }
924 
925 
933 int EpsOpen(pen *Pen, const char *FileName, float Width, float Height)
934 {
935  FILE *File = NULL;
936 
937  if(!(File = fopen(FileName, "wb")))
938  {
939  ErrorMessage("Failed to open for writing \"%s\".\n", FileName);
940  return 0;
941  }
942 
943  /* Tell File to use buffering */
944  setvbuf(File, 0, _IOFBF, FILE_BUFFER_CAPACITY);
945 
946  fputs("%%!PS-Adobe-2.0\n%%%%BoundingBox: 0 0 ", File);
947  PenWriteDouble(File, Width, Pen->NumDigits);
948  putc(' ', File);
949  PenWriteDouble(File, Height, Pen->NumDigits);
950  putc('\n', File);
951 
952  /* Set PDF distiller info */
953  fputs("<< /PageSize [", File);
954  PenWriteDouble(File, Width, Pen->NumDigits);
955  putc(' ', File);
956  PenWriteDouble(File, Height, Pen->NumDigits);
957  fputs("] >> setpagedevice\n", File);
958  fputs("systemdict /setdistillerparams known {\n"
959  "<< /AutoFilterGrayImages false /GrayImageFilter /FlateEncode "
960  "/AutoFilterColorImages false /ColorImageFilter /FlateEncode "
961  ">> setdistillerparams\n"
962  "} if\n", File);
963  /* Set PS macros and line width */
964  fputs("/bdef {bind def} bind def\n"
965  "/m1 {moveto rlineto stroke} bdef\n"
966  "/m2 {moveto rcurveto stroke} bdef\n"
967  "/m3 {/savematrix matrix currentmatrix def\n"
968  "translate rotate scale 0 0 1 0 360 arc\n"
969  "savematrix setmatrix stroke} bdef\n"
970  "0.2 setlinewidth\n", File);
971 
972  if(ferror(File))
973  {
974  ErrorMessage("Failed to write \"%s\".\n", FileName);
975  goto Catch;
976  }
977 
978  Pen->File = File;
979  Pen->SetColor = EpsSetColor;
980  Pen->DrawLine = EpsDrawLine;
981  Pen->DrawQBezier = EpsDrawQBezier;
982  Pen->DrawEllipse = EpsDrawEllipse;
983  return 1;
984 Catch:
985  if(File)
986  fclose(File);
987  return 0;
988 }
989 
990 
996 int EpsClose(pen *Pen)
997 {
998  if(!Pen || !(Pen->File)
999  || fputs("showpage\n", Pen->File) == EOF
1000  || ferror(Pen->File)
1001  || fclose(Pen->File))
1002  return 0;
1003 
1004  return 1;
1005 }
1006 
1007 
1015 int WriteASCII85(FILE *File, const uint8_t *Data, int NumBytes)
1016 {
1017  unsigned long Tuple, Plain[4];
1018  unsigned int Encoded[5];
1019  int i, k, LineCount, Padding;
1020 
1021 
1022  /* Write ASCII85-encoded data */
1023  for(i = 0, LineCount = 0; i < NumBytes; i += 4)
1024  {
1025  for(k = 0; k < 4; k++) /* Get four bytes */
1026  Plain[k] = Data[i + k];
1027 
1028  Tuple = (Plain[0] << 24) | (Plain[1] << 16)
1029  | (Plain[2] << 8) | Plain[3];
1030 
1031  for(k = 4; k >= 0; k--) /* Convert to radix 85 */
1032  {
1033  Encoded[k] = Tuple % 85;
1034  Tuple /= 85;
1035  }
1036 
1037  for(k = 0; k < 5; k++) /* Write ASCII85 tuple */
1038  fputc(33 + Encoded[k], File);
1039 
1040  /* Periodically emit newlines */
1041  if(++LineCount >= 15)
1042  {
1043  LineCount = 0;
1044 
1045  if(fprintf(File, "\n") < 0)
1046  return 0;
1047  }
1048  }
1049 
1050  /* Write final tuple */
1051  if(i < NumBytes)
1052  {
1053  for(k = 0; i + k < NumBytes; k++)
1054  Plain[k] = Data[i + k];
1055 
1056  for(Padding = 0; k < 4; k++, Padding++)
1057  Plain[k] = 0;
1058 
1059  Tuple = (Plain[0] << 24) | (Plain[1] << 16)
1060  | (Plain[2] << 8) | Plain[3];
1061 
1062  for(k = 4; k >= 0; k--) /* Convert to radix 85 */
1063  {
1064  Encoded[k] = Tuple % 85;
1065  Tuple /= 85;
1066  }
1067 
1068  for(k = 0; k < 5 - Padding; k++) /* Write ASCII85 tuple */
1069  fputc(33 + Encoded[k], File);
1070 
1071  if(++LineCount >= 15
1072  && fprintf(File, "\n") < 0)
1073  return 0;
1074  }
1075 
1076  if(fprintf(File, "~>\n") < 0 || ferror(File))
1077  return 0;
1078 
1079  return 1;
1080 }
1081 
1082 
1100 int EpsWriteGrayImage(FILE *File,
1101  const uint8_t *Image, int Width, int Height)
1102 {
1103  /* Specify ASCII85 sRGB 8-bit color image data */
1104  if(!File || !Image || fprintf(File,
1105  "gsave\n"
1106  "/DeviceGray setcolorspace\n"
1107  "0 %d translate %d %d scale\n"
1108  "<< /ImageType 1\n"
1109  " /Width %d\n"
1110  " /Height %d\n"
1111  " /ImageMatrix [%d 0 0 -%d 0 0]\n"
1112  " /BitsPerComponent 8\n"
1113  " /Decode [0 1]\n"
1114  " /DataSource currentfile /ASCII85Decode filter\n"
1115  " /Interpolate false\n"
1116  ">> image\n",
1117  Height, Width, Height,
1118  Width, Height, Width, Height) < 0
1119  || !WriteASCII85(File, Image, Width*Height)
1120  || fprintf(File, "grestore\n") < 0)
1121  return 0;
1122  else
1123  return 1;
1124 }
1125 
1126 
1144 int EpsWriteColorImage(FILE *File,
1145  const uint8_t *Image, int Width, int Height)
1146 {
1147  /* Specify ASCII85 sRGB 8-bit color image data */
1148  if(!File || !Image || fprintf(File,
1149  "gsave\n"
1150  "/DeviceRGB setcolorspace\n"
1151  "0 %d translate %d %d scale\n"
1152  "<< /ImageType 1\n"
1153  " /Width %d\n"
1154  " /Height %d\n"
1155  " /ImageMatrix [%d 0 0 -%d 0 0]\n"
1156  " /BitsPerComponent 8\n"
1157  " /Decode [0 1 0 1 0 1]\n"
1158  " /DataSource currentfile /ASCII85Decode filter\n"
1159  " /Interpolate false\n"
1160  ">> image\n",
1161  Height, Width, Height,
1162  Width, Height, Width, Height) < 0
1163  || !WriteASCII85(File, Image, 3*Width*Height)
1164  || fprintf(File, "grestore\n") < 0)
1165  return 0;
1166  else
1167  return 1;
1168 }
1169 
1170 
1171 
1172 /*****************************************************************************
1173  * SVG-specific code *
1174  *****************************************************************************/
1175 
1177 static void WriteHexColor(FILE *File, const float *Color)
1178 {
1179  fprintf(File, "#%02X%02X%02X",
1180  (int)(255*Color[0] + 0.5f),
1181  (int)(255*Color[1] + 0.5f),
1182  (int)(255*Color[2] + 0.5f));
1183 }
1184 
1185 
1192 static int SvgDrawLine(pen *Pen, float x1, float y1, float x2, float y2)
1193 {
1194  FILE *File;
1195  double xt, yt;
1196  int NumDigits;
1197 
1198  if(!Pen || !(File = Pen->File))
1199  return 0;
1200 
1201  NumDigits = Pen->NumDigits;
1202  fputs("<line ", File);
1203 
1204  if(Pen->ColorIndex >= 0)
1205  fprintf(File, "class=\"%c\" ", 'a' + Pen->ColorIndex);
1206  else
1207  {
1208  fputs("style=\"stroke:", File);
1209  WriteHexColor(File, Pen->Color);
1210  fputs("\" ", File);
1211  }
1212 
1213  xt = Pen->Trans.a*x1 + Pen->Trans.c*y1 + Pen->Trans.e;
1214  yt = Pen->Trans.b*x1 + Pen->Trans.d*y1 + Pen->Trans.f;
1215  fputs("x1=\"", File);
1216  PenWriteDouble(File, xt, NumDigits);
1217  fputs("\" y1=\"", File);
1218  PenWriteDouble(File, yt, NumDigits);
1219 
1220  xt = Pen->Trans.a*x2 + Pen->Trans.c*y2 + Pen->Trans.e;
1221  yt = Pen->Trans.b*x2 + Pen->Trans.d*y2 + Pen->Trans.f;
1222  fputs("\" x2=\"", File);
1223  PenWriteDouble(File, xt, NumDigits);
1224  fputs("\" y2=\"", File);
1225  PenWriteDouble(File, yt, NumDigits);
1226  fputs("\" />\n", File);
1227  return 1;
1228 }
1229 
1230 
1237 static int SvgDrawQBezier(pen *Pen,
1238  float x1, float y1, float x2, float y2, float x3, float y3)
1239 {
1240  FILE *File;
1241  double xt0, yt0, xt, yt;
1242  int NumDigits;
1243 
1244  if(!Pen || !(File = Pen->File))
1245  return 0;
1246 
1247  NumDigits = Pen->NumDigits;
1248  fputs("<path ", File);
1249 
1250  if(Pen->ColorIndex >= 0)
1251  fprintf(File, "class=\"%c\" ", 'a' + Pen->ColorIndex);
1252  else
1253  {
1254  fputs("style=\"stroke:", File);
1255  WriteHexColor(File, Pen->Color);
1256  fputs("; fill:none\" ", File);
1257  }
1258 
1259  xt0 = Pen->Trans.a*x1 + Pen->Trans.c*y1 + Pen->Trans.e;
1260  yt0 = Pen->Trans.b*x1 + Pen->Trans.d*y1 + Pen->Trans.f;
1261  fputs("d=\"M", File);
1262  PenWriteDouble(File, xt0, NumDigits);
1263  putc(',', File);
1264  PenWriteDouble(File, yt0, NumDigits);
1265  fputs(" q", File);
1266 
1267  xt = Pen->Trans.a*x2 + Pen->Trans.c*y2 + Pen->Trans.e;
1268  yt = Pen->Trans.b*x2 + Pen->Trans.d*y2 + Pen->Trans.f;
1269  PenWriteDouble(File, xt - xt0, NumDigits);
1270  putc(',', File);
1271  PenWriteDouble(File, yt - yt0, NumDigits);
1272  putc(' ', File);
1273 
1274  xt = Pen->Trans.a*x3 + Pen->Trans.c*y3 + Pen->Trans.e;
1275  yt = Pen->Trans.b*x3 + Pen->Trans.d*y3 + Pen->Trans.f;
1276  PenWriteDouble(File, xt - xt0, NumDigits);
1277  putc(',', File);
1278  PenWriteDouble(File, yt - yt0, NumDigits);
1279  fputs("\" />\n", File);
1280  return 1;
1281 }
1282 
1283 
1292 static int SvgDrawEllipse(pen *Pen,
1293  float x, float y, float rx, float ry, float Theta)
1294 {
1295  FILE *File;
1296  int NumDigits, IsCircle;
1297 
1298  if(!Pen || !(File = Pen->File))
1299  return 0;
1300 
1301  NumDigits = Pen->NumDigits;
1302  TransformEllipse(&x, &y, &rx, &ry, &Theta, Pen->Trans);
1303  IsCircle = (fabs(rx - ry) < 1e-2);
1304 
1305  if(IsCircle)
1306  fputs("<circle ", File);
1307  else
1308  fputs("<ellipse ", File);
1309 
1310  if(Pen->ColorIndex >= 0)
1311  fprintf(File, "class=\"%c\" ", 'a' + Pen->ColorIndex);
1312  else
1313  {
1314  fputs("style=\"stroke:", File);
1315  WriteHexColor(File, Pen->Color);
1316  fputs("\" ", File);
1317  }
1318 
1319  Theta *= 180/M_PI;
1320 
1321  if(!IsCircle && fabs(Theta) >= 1e-2 && abs(Theta - 180) >= 1e-2)
1322  {
1323  fputs("transform=\"rotate(", File);
1324  PenWriteDouble(File, Theta, NumDigits);
1325  putc(' ', File);
1326  PenWriteDouble(File, x, NumDigits);
1327  putc(' ', File);
1328  PenWriteDouble(File, y, NumDigits);
1329  fputs(")\" ", File);
1330  }
1331 
1332  fputs("cx=\"", File);
1333  PenWriteDouble(File, x, NumDigits);
1334  fputs("\" cy=\"", File);
1335  PenWriteDouble(File, y, NumDigits);
1336 
1337  if(IsCircle)
1338  fputs("\" r=\"", File);
1339  else
1340  {
1341  fputs("\" rx=\"", File);
1342  PenWriteDouble(File, rx, NumDigits);
1343  fputs("\" ry=\"", File);
1344  }
1345 
1346  PenWriteDouble(File, ry, NumDigits);
1347  fputs("\" />\n", File);
1348  return 1;
1349 }
1350 
1351 
1359 int SvgOpen(pen *Pen, const char *FileName, float Width, float Height)
1360 {
1361  FILE *File = NULL;
1362  int i;
1363 
1364  if(!FileName || !(File = fopen(FileName, "wb")))
1365  {
1366  ErrorMessage("Failed to open for writing \"%s\".\n", FileName);
1367  goto Catch;
1368  }
1369 
1370  /* Tell File to use buffering */
1371  setvbuf(File, 0, _IOFBF, FILE_BUFFER_CAPACITY);
1372 
1373  /* Write the XML and DOCTYPE header */
1374  fputs( "<?xml version=\"1.0\" standalone=\"no\"?>\n"
1375  "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"
1376  " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", File);
1377 
1378  /* Write SVG header */
1379  fputs("<svg width=\"", File);
1380  PenWriteDouble(File, Width, Pen->NumDigits);
1381  fputs("\" height=\"", File);
1382  PenWriteDouble(File, Height, Pen->NumDigits);
1383  fputs("\" version=\"1.1\"\n"
1384  " xmlns=\"http://www.w3.org/2000/svg\""
1385  " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", File);
1386 
1387  /* Write colors in Pen->Palette in an internal stylesheet */
1388  if(Pen->NumPalette > 0)
1389  {
1390  fputs("<defs>\n<style type=\"text/css\"><![CDATA[\n", File);
1391 
1392  for(i = 0; i < Pen->NumPalette; i++)
1393  {
1394  fprintf(File, ".%c {stroke:", 'a' + i);
1395  WriteHexColor(File, Pen->Palette[i]);
1396  fputs("; stroke-width:2; fill:none}\n", File);
1397  }
1398 
1399  fputs("]]></style>\n</defs>\n", File);
1400  }
1401 
1402  if(ferror(File))
1403  {
1404  ErrorMessage("Failed to write \"%s\".\n", FileName);
1405  goto Catch;
1406  }
1407 
1408  Pen->File = File;
1409  Pen->SetColor = NULL;
1410  Pen->DrawLine = SvgDrawLine;
1411  Pen->DrawQBezier = SvgDrawQBezier;
1412  Pen->DrawEllipse = SvgDrawEllipse;
1413  return 1;
1414 Catch:
1415  if(File)
1416  fclose(File);
1417  return 0;
1418 }
1419 
1420 
1426 int SvgClose(pen *Pen)
1427 {
1428  if(!Pen || !(Pen->File)
1429  || fputs("</svg>\n", Pen->File) == EOF
1430  || ferror(Pen->File)
1431  || fclose(Pen->File))
1432  return 0;
1433 
1434  return 1;
1435 }
1436 
1437 
1438 
1439