summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/psfile.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/psfile.cc')
-rw-r--r--Build/source/utils/asymptote/psfile.cc721
1 files changed, 721 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/psfile.cc b/Build/source/utils/asymptote/psfile.cc
new file mode 100644
index 00000000000..73fe53eb743
--- /dev/null
+++ b/Build/source/utils/asymptote/psfile.cc
@@ -0,0 +1,721 @@
+/*****
+ * psfile.cc
+ * Andy Hammerlindl 2002/06/10
+ *
+ * Encapsulates the writing of commands to a PostScript file.
+ * Allows identification and removal of redundant commands.
+ *****/
+
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#include <zlib.h>
+
+#include "psfile.h"
+#include "settings.h"
+#include "errormsg.h"
+#include "array.h"
+#include "stack.h"
+
+using std::ofstream;
+using std::setw;
+using vm::array;
+using vm::read;
+using vm::stack;
+using vm::callable;
+using vm::pop;
+
+namespace camp {
+
+void checkColorSpace(ColorSpace colorspace)
+{
+ switch(colorspace) {
+ case DEFCOLOR:
+ case INVISIBLE:
+ reportError("Cannot shade with invisible pen");
+ case PATTERN:
+ reportError("Cannot shade with pattern");
+ break;
+ default:
+ break;
+ }
+}
+
+psfile::psfile(const string& filename, bool pdfformat)
+ : filename(filename), pdfformat(pdfformat), pdf(false),
+ buffer(NULL), out(NULL)
+{
+ if(filename.empty()) out=&cout;
+ else out=new ofstream(filename.c_str());
+ out->setf(std::ios::boolalpha);
+ if(!out || !*out)
+ reportError("Cannot write to "+filename);
+}
+
+static const char *inconsistent="inconsistent colorspaces";
+static const char *rectangular="matrix is not rectangular";
+
+void psfile::writefromRGB(unsigned char r, unsigned char g, unsigned char b,
+ ColorSpace colorspace, size_t ncomponents)
+{
+ static const double factor=1.0/255.0;
+ pen p(r*factor,g*factor,b*factor);
+ p.convert();
+ if(!p.promote(colorspace))
+ reportError(inconsistent);
+ write(&p,ncomponents);
+}
+
+inline unsigned char average(unsigned char *a, size_t dx, size_t dy)
+{
+ return ((unsigned) a[0]+(unsigned) a[dx]+(unsigned) a[dy]+
+ (unsigned) a[dx+dy])/4;
+}
+
+void psfile::dealias(unsigned char *a, size_t width, size_t height, size_t n,
+ bool convertrgb, ColorSpace colorspace)
+{
+ // Dealias all but the last row and column of pixels.
+ size_t istop=width-1;
+ size_t jstop=height-1;
+ if(convertrgb) {
+ size_t nwidth=3*width;
+ for(size_t j=0; j < height; ++j) {
+ unsigned char *aj=a+nwidth*j;
+ for(size_t i=0; i < width; ++i) {
+ unsigned char *ai=aj+3*i;
+ if(i < istop && j < jstop)
+ writefromRGB(average(ai,3,nwidth),
+ average(ai+1,3,nwidth),
+ average(ai+2,3,nwidth),colorspace,n);
+ else
+ writefromRGB(ai[0],ai[1],ai[2],colorspace,n);
+ }
+ }
+ } else {
+ size_t nwidth=n*width;
+ for(size_t j=0; j < jstop; ++j) {
+ unsigned char *aj=a+nwidth*j;
+ for(size_t i=0; i < istop; ++i) {
+ unsigned char *ai=aj+n*i;
+ for(size_t k=0; k < n; ++k)
+ ai[k]=average(ai+k,n,nwidth);
+ }
+ }
+ }
+}
+
+void psfile::writeCompressed(const unsigned char *a, size_t size)
+{
+ uLongf compressedSize=compressBound(size);
+ Bytef *compressed=new Bytef[compressedSize];
+
+ if(compress(compressed,&compressedSize,a,size) != Z_OK)
+ reportError("image compression failed");
+
+ encode85 e(out);
+ for(size_t i=0; i < compressedSize; ++i)
+ e.put(compressed[i]);
+}
+
+void psfile::close()
+{
+ if(out) {
+ out->flush();
+ if(!filename.empty()) {
+#ifdef __MSDOS__
+ chmod(filename.c_str(),~settings::mask & 0777);
+#endif
+ if(!out->good())
+ // Don't call reportError since this may be called on handled_error.
+ reportFatal("Cannot write to "+filename);
+ delete out;
+ out=NULL;
+ }
+ }
+}
+
+psfile::~psfile()
+{
+ close();
+}
+
+void psfile::header(bool eps)
+{
+ Int level=settings::getSetting<Int>("level");
+ *out << "%!PS-Adobe-" << level << ".0";
+ if(eps)
+ *out << " EPSF-" << level << ".0";
+ *out << newl;
+}
+
+void psfile::prologue(const bbox& box)
+{
+ header(true);
+ BoundingBox(box);
+ *out << "%%Creator: " << settings::PROGRAM << " " << settings::VERSION
+ << REVISION << newl;
+
+ time_t t; time(&t);
+ struct tm *tt = localtime(&t);
+ char prev = out->fill('0');
+ *out << "%%CreationDate: " << tt->tm_year + 1900 << "."
+ << setw(2) << tt->tm_mon+1 << "." << setw(2) << tt->tm_mday << " "
+ << setw(2) << tt->tm_hour << ":" << setw(2) << tt->tm_min << ":"
+ << setw(2) << tt->tm_sec << newl;
+ out->fill(prev);
+
+ *out << "%%Pages: 1" << newl;
+ *out << "%%Page: 1 1" << newl;
+
+ if(!pdfformat)
+ *out
+ << "/Setlinewidth {0 exch dtransform dup abs 1 lt {pop 0}{round} ifelse"
+ << newl
+ << "idtransform setlinewidth pop} bind def" << newl;
+}
+
+void psfile::epilogue()
+{
+ *out << "showpage" << newl;
+ *out << "%%EOF" << newl;
+}
+
+void psfile::setcolor(const pen& p, const string& begin="",
+ const string& end="")
+{
+ if(p.cmyk() && (!lastpen.cmyk() ||
+ (p.cyan() != lastpen.cyan() ||
+ p.magenta() != lastpen.magenta() ||
+ p.yellow() != lastpen.yellow() ||
+ p.black() != lastpen.black()))) {
+ *out << begin << p.cyan() << " " << p.magenta() << " " << p.yellow() << " "
+ << p.black() << (pdf ? " k" : " setcmykcolor") << end << newl;
+ } else if(p.rgb() && (!lastpen.rgb() ||
+ (p.red() != lastpen.red() ||
+ p.green() != lastpen.green() ||
+ p.blue() != lastpen.blue()))) {
+ *out << begin << p.red() << " " << p.green() << " " << p.blue()
+ << (pdf ? " rg" : " setrgbcolor") << end << newl;
+ } else if(p.grayscale() && (!lastpen.grayscale() ||
+ p.gray() != lastpen.gray())) {
+ *out << begin << p.gray() << (pdf ? " g" : " setgray") << end << newl;
+ }
+}
+
+void psfile::setopacity(const pen& p)
+{
+ if(p.blend() != lastpen.blend()) {
+ *out << "/" << p.blend() << " .setblendmode" << newl;
+ }
+
+ string outputformat=settings::getSetting<string>("outformat");
+ if(p.opacity() != lastpen.opacity() &&
+ (pdf || outputformat == "pdf" || outputformat == "html" ||
+ outputformat == "svg")) {
+ *out << p.opacity() << " .setfillconstantalpha" << newl
+ << p.opacity() << " .setstrokeconstantalpha" << newl;
+ }
+
+ lastpen.settransparency(p);
+}
+
+
+void psfile::setpen(pen p)
+{
+ p.convert();
+
+ setopacity(p);
+
+ if(!p.fillpattern().empty() && p.fillpattern() != lastpen.fillpattern())
+ *out << p.fillpattern() << " setpattern" << newl;
+ else setcolor(p);
+
+ // Defer dynamic linewidth until stroke time in case currentmatrix changes.
+ if(p.width() != lastpen.width())
+ *out << p.width() << (pdfformat ? " setlinewidth" : " Setlinewidth")
+ << newl;
+
+ if(p.cap() != lastpen.cap())
+ *out << p.cap() << " setlinecap" << newl;
+
+ if(p.join() != lastpen.join())
+ *out << p.join() << " setlinejoin" << newl;
+
+ if(p.miter() != lastpen.miter())
+ *out << p.miter() << " setmiterlimit" << newl;
+
+ const LineType *linetype=p.linetype();
+ const LineType *lastlinetype=lastpen.linetype();
+
+ if(!(linetype->pattern == lastlinetype->pattern) ||
+ linetype->offset != lastlinetype->offset) {
+ out->setf(std::ios::fixed);
+ *out << linetype->pattern << " " << linetype->offset << " setdash" << newl;
+ out->unsetf(std::ios::fixed);
+ }
+
+ lastpen=p;
+}
+
+void psfile::write(const pen& p)
+{
+ if(p.cmyk())
+ *out << p.cyan() << " " << p.magenta() << " " << p.yellow() << " "
+ << p.black();
+ else if(p.rgb())
+ *out << p.red() << " " << p.green() << " " << p.blue();
+ else if(p.grayscale())
+ *out << p.gray();
+}
+
+void psfile::write(path p, bool newPath)
+{
+ Int n = p.size();
+ assert(n != 0);
+
+ if(newPath) newpath();
+
+ pair z0=p.point((Int) 0);
+
+ // Draw points
+ moveto(z0);
+
+ for(Int i = 1; i < n; i++) {
+ if(p.straight(i-1)) lineto(p.point(i));
+ else curveto(p.postcontrol(i-1),p.precontrol(i),p.point(i));
+ }
+
+ if(p.cyclic()) {
+ if(p.straight(n-1)) lineto(z0);
+ else curveto(p.postcontrol(n-1),p.precontrol((Int) 0),z0);
+ closepath();
+ } else {
+ if(n == 1) lineto(z0);
+ }
+}
+
+void psfile::latticeshade(const vm::array& a, const transform& t)
+{
+ checkLevel();
+ size_t n=a.size();
+ if(n == 0) return;
+
+ array *a0=read<array *>(a,0);
+ size_t m=a0->size();
+ setfirstopacity(*a0);
+
+ ColorSpace colorspace=maxcolorspace2(a);
+ checkColorSpace(colorspace);
+
+ size_t ncomponents=ColorComponents[colorspace];
+
+ *out << "<< /ShadingType 1" << newl
+ << "/Matrix ";
+ write(t);
+ *out << newl;
+ *out << "/ColorSpace /Device" << ColorDeviceSuffix[colorspace] << newl
+ << "/Function" << newl
+ << "<< /FunctionType 0" << newl
+ << "/Order 1" << newl
+ << "/Domain [0 1 0 1]" << newl
+ << "/Range [";
+ for(size_t i=0; i < ncomponents; ++i)
+ *out << "0 1 ";
+ *out << "]" << newl
+ << "/Decode [";
+ for(size_t i=0; i < ncomponents; ++i)
+ *out << "0 1 ";
+ *out << "]" << newl;
+ *out << "/BitsPerSample 8" << newl;
+ *out << "/Size [" << m << " " << n << "]" << newl
+ << "/DataSource <" << newl;
+
+ for(size_t i=n; i > 0;) {
+ array *ai=read<array *>(a,--i);
+ checkArray(ai);
+ size_t aisize=ai->size();
+ if(aisize != m) reportError(rectangular);
+ for(size_t j=0; j < m; j++) {
+ pen *p=read<pen *>(ai,j);
+ p->convert();
+ if(!p->promote(colorspace))
+ reportError(inconsistent);
+ *out << p->hex() << newl;
+ }
+ }
+
+ *out << ">" << newl
+ << ">>" << newl
+ << ">>" << newl
+ << "shfill" << newl;
+}
+
+// Axial and radial shading
+void psfile::gradientshade(bool axial, ColorSpace colorspace,
+ const pen& pena, const pair& a, double ra,
+ bool extenda, const pen& penb, const pair& b,
+ double rb, bool extendb)
+{
+ checkLevel();
+ endclip(pena);
+
+ setopacity(pena);
+ checkColorSpace(colorspace);
+
+ *out << "<< /ShadingType " << (axial ? "2" : "3") << newl
+ << "/ColorSpace /Device" << ColorDeviceSuffix[colorspace] << newl
+ << "/Coords [";
+ write(a);
+ if(!axial) write(ra);
+ write(b);
+ if(!axial) write(rb);
+ *out << "]" << newl
+ << "/Extend [" << extenda << " " << extendb << "]" << newl
+ << "/Function" << newl
+ << "<< /FunctionType 2" << newl
+ << "/Domain [0 1]" << newl
+ << "/C0 [";
+ write(pena);
+ *out << "]" << newl
+ << "/C1 [";
+ write(penb);
+ *out << "]" << newl
+ << "/N 1" << newl
+ << ">>" << newl
+ << ">>" << newl
+ << "shfill" << newl;
+}
+
+void psfile::gouraudshade(const pen& pentype,
+ const array& pens, const array& vertices,
+ const array& edges)
+{
+ checkLevel();
+ endclip(pentype);
+
+ size_t size=pens.size();
+ if(size == 0) return;
+
+ setfirstopacity(pens);
+ ColorSpace colorspace=maxcolorspace(pens);
+
+ *out << "<< /ShadingType 4" << newl
+ << "/ColorSpace /Device" << ColorDeviceSuffix[colorspace] << newl
+ << "/DataSource [" << newl;
+ for(size_t i=0; i < size; i++) {
+ write(read<Int>(edges,i));
+ write(read<pair>(vertices,i));
+ pen *p=read<pen *>(pens,i);
+ p->convert();
+ if(!p->promote(colorspace))
+ reportError(inconsistent);
+ *out << " ";
+ write(*p);
+ *out << newl;
+ }
+ *out << "]" << newl
+ << ">>" << newl
+ << "shfill" << newl;
+}
+
+void psfile::vertexpen(array *pi, int j, ColorSpace colorspace)
+{
+ pen *p=read<pen *>(pi,j);
+ p->convert();
+ if(!p->promote(colorspace))
+ reportError(inconsistent);
+ *out << " ";
+ write(*p);
+}
+
+// Tensor-product patch shading
+void psfile::tensorshade(const pen& pentype, const array& pens,
+ const array& boundaries, const array& z)
+{
+ checkLevel();
+ endclip(pentype);
+
+ size_t size=pens.size();
+ if(size == 0) return;
+ size_t nz=z.size();
+
+ array *p0=read<array *>(pens,0);
+ if(checkArray(p0) != 4)
+ reportError("4 pens required");
+ setfirstopacity(*p0);
+
+ ColorSpace colorspace=maxcolorspace2(pens);
+ checkColorSpace(colorspace);
+
+ *out << "<< /ShadingType 7" << newl
+ << "/ColorSpace /Device" << ColorDeviceSuffix[colorspace] << newl
+ << "/DataSource [" << newl;
+
+ for(size_t i=0; i < size; i++) {
+ // Only edge flag 0 (new patch) is implemented since the 32% data
+ // compression (for RGB) afforded by other edge flags really isn't worth
+ // the trouble or confusion for the user.
+ write(0);
+ path g=read<path>(boundaries,i);
+ if(!(g.cyclic() && g.size() == 4))
+ reportError("specify cyclic path of length 4");
+ for(Int j=4; j > 0; --j) {
+ write(g.point(j));
+ write(g.precontrol(j));
+ write(g.postcontrol(j-1));
+ }
+ if(nz == 0) { // Coons patch
+ static double nineth=1.0/9.0;
+ for(Int j=0; j < 4; ++j) {
+ write(nineth*(-4.0*g.point(j)+6.0*(g.precontrol(j)+g.postcontrol(j))
+ -2.0*(g.point(j-1)+g.point(j+1))
+ +3.0*(g.precontrol(j-1)+g.postcontrol(j+1))
+ -g.point(j+2)));
+ }
+ } else {
+ array *zi=read<array *>(z,i);
+ if(checkArray(zi) != 4)
+ reportError("specify 4 internal control points for each path");
+ write(read<pair>(zi,0));
+ write(read<pair>(zi,3));
+ write(read<pair>(zi,2));
+ write(read<pair>(zi,1));
+ }
+
+ array *pi=read<array *>(pens,i);
+ if(checkArray(pi) != 4)
+ reportError("specify 4 pens for each path");
+ vertexpen(pi,0,colorspace);
+ vertexpen(pi,3,colorspace);
+ vertexpen(pi,2,colorspace);
+ vertexpen(pi,1,colorspace);
+ *out << newl;
+ }
+
+ *out << "]" << newl
+ << ">>" << newl
+ << "shfill" << newl;
+}
+
+void psfile::write(pen *p, size_t ncomponents)
+{
+ switch(ncomponents) {
+ case 0:
+ break;
+ case 1:
+ writeByte(byte(p->gray()));
+ break;
+ case 3:
+ writeByte(byte(p->red()));
+ writeByte(byte(p->green()));
+ writeByte(byte(p->blue()));
+ break;
+ case 4:
+ writeByte(byte(p->cyan()));
+ writeByte(byte(p->magenta()));
+ writeByte(byte(p->yellow()));
+ writeByte(byte(p->black()));
+ default:
+ break;
+ }
+}
+
+string filter()
+{
+ return settings::getSetting<Int>("level") >= 3 ?
+ "1 (~>) /SubFileDecode filter /ASCII85Decode filter\n/FlateDecode" :
+ "1 (~>) /SubFileDecode filter /ASCII85Decode";
+}
+
+void psfile::imageheader(size_t width, size_t height, ColorSpace colorspace)
+{
+ size_t ncomponents=ColorComponents[colorspace];
+ *out << "/Device" << ColorDeviceSuffix[colorspace] << " setcolorspace"
+ << newl
+ << "<<" << newl
+ << "/ImageType 1" << newl
+ << "/Width " << width << newl
+ << "/Height " << height << newl
+ << "/BitsPerComponent 8" << newl
+ << "/Decode [";
+
+ for(size_t i=0; i < ncomponents; ++i)
+ *out << "0 1 ";
+
+ *out << "]" << newl
+ << "/ImageMatrix [" << width << " 0 0 " << height << " 0 0]" << newl
+ << "/DataSource currentfile " << filter() << " filter" << newl
+ << ">>" << newl
+ << "image" << newl;
+}
+
+void psfile::image(const array& a, const array& P, bool antialias)
+{
+ size_t asize=a.size();
+ size_t Psize=P.size();
+ if(asize == 0 || Psize == 0) return;
+
+ array *a0=read<array *>(a,0);
+ size_t a0size=a0->size();
+ if(a0size == 0) return;
+
+ setfirstopacity(P);
+
+ ColorSpace colorspace=maxcolorspace(P);
+ checkColorSpace(colorspace);
+
+ size_t ncomponents=ColorComponents[colorspace];
+
+ imageheader(a0size,asize,colorspace);
+
+ double min=read<double>(a0,0);
+ double max=min;
+ for(size_t i=0; i < asize; i++) {
+ array *ai=read<array *>(a,i);
+ size_t size=ai->size();
+ if(size != a0size)
+ reportError(rectangular);
+ for(size_t j=0; j < size; j++) {
+ double val=read<double>(ai,j);
+ if(val > max) max=val;
+ else if(val < min) min=val;
+ }
+ }
+
+ double step=(max == min) ? 0.0 : (Psize-1)/(max-min);
+
+ beginImage(ncomponents*a0size*asize);
+ for(size_t i=0; i < asize; i++) {
+ array *ai=read<array *>(a,i);
+ for(size_t j=0; j < a0size; j++) {
+ double val=read<double>(ai,j);
+ size_t index=(size_t) ((val-min)*step+0.5);
+ pen *p=read<pen *>(P,index < Psize ? index : Psize-1);
+ p->convert();
+ if(!p->promote(colorspace))
+ reportError(inconsistent);
+ write(p,ncomponents);
+ }
+ }
+ endImage(antialias,a0size,asize,ncomponents);
+}
+
+void psfile::image(const array& a, bool antialias)
+{
+ size_t asize=a.size();
+ if(asize == 0) return;
+
+ array *a0=read<array *>(a,0);
+ size_t a0size=a0->size();
+ if(a0size == 0) return;
+
+ setfirstopacity(*a0);
+
+ ColorSpace colorspace=maxcolorspace2(a);
+ checkColorSpace(colorspace);
+
+ size_t ncomponents=ColorComponents[colorspace];
+
+ imageheader(a0size,asize,colorspace);
+
+ beginImage(ncomponents*a0size*asize);
+ for(size_t i=0; i < asize; i++) {
+ array *ai=read<array *>(a,i);
+ size_t size=ai->size();
+ if(size != a0size)
+ reportError(rectangular);
+ for(size_t j=0; j < size; j++) {
+ pen *p=read<pen *>(ai,j);
+ p->convert();
+ if(!p->promote(colorspace))
+ reportError(inconsistent);
+ write(p,ncomponents);
+ }
+ }
+ endImage(antialias,a0size,asize,ncomponents);
+}
+
+void psfile::image(stack *Stack, callable *f, Int width, Int height,
+ bool antialias)
+{
+ if(width <= 0 || height <= 0) return;
+
+ Stack->push(0);
+ Stack->push(0);
+ f->call(Stack);
+ pen p=pop<pen>(Stack);
+
+ setopacity(p);
+
+ ColorSpace colorspace=p.colorspace();
+ checkColorSpace(colorspace);
+
+ size_t ncomponents=ColorComponents[colorspace];
+
+ imageheader(width,height,colorspace);
+
+ beginImage(ncomponents*width*height);
+ for(Int j=0; j < height; j++) {
+ for(Int i=0; i < width; i++) {
+ Stack->push(j);
+ Stack->push(i);
+ f->call(Stack);
+ pen p=pop<pen>(Stack);
+ p.convert();
+ if(!p.promote(colorspace))
+ reportError(inconsistent);
+ write(&p,ncomponents);
+ }
+ }
+ endImage(antialias,width,height,ncomponents);
+}
+
+void psfile::outImage(bool antialias, size_t width, size_t height,
+ size_t ncomponents)
+{
+ if(antialias) dealias(buffer,width,height,ncomponents);
+ if(settings::getSetting<Int>("level") >= 3)
+ writeCompressed(buffer,count);
+ else {
+ encode85 e(out);
+ for(size_t i=0; i < count; ++i)
+ e.put(buffer[i]);
+ }
+}
+
+void psfile::rawimage(unsigned char *a, size_t width, size_t height,
+ bool antialias)
+{
+ pen p(0.0,0.0,0.0);
+ p.convert();
+ ColorSpace colorspace=p.colorspace();
+ checkColorSpace(colorspace);
+
+ size_t ncomponents=ColorComponents[colorspace];
+
+ imageheader(width,height,colorspace);
+
+ count=ncomponents*width*height;
+ if(colorspace == RGB) {
+ buffer=a;
+ outImage(antialias,width,height,ncomponents);
+ } else {
+ beginImage(count);
+ if(antialias)
+ dealias(a,width,height,ncomponents,true,colorspace);
+ else {
+ size_t height3=3*height;
+ for(size_t i=0; i < width; ++i) {
+ unsigned char *ai=a+height3*i;
+ for(size_t j=0; j < height; ++j) {
+ unsigned char *aij=ai+3*j;
+ writefromRGB(aij[0],aij[1],aij[2],colorspace,ncomponents);
+ }
+ }
+ }
+ endImage(false,width,height,ncomponents);
+ }
+}
+
+} //namespace camp