1 /*
2  * appliers.cpp -- specific OutputRule implementations
3  * by pts@fazekas.hu at Sat Mar 16 14:45:02 CET 2002
4  * Blanca JAI additions at Tue May 21 13:18:26 CEST 2002
5  * TIFF output at Tue Jun  4 19:50:45 CEST 2002
6  */
7 
8 #ifdef __GNUC__
9 #ifndef __clang__
10 #pragma implementation
11 #endif
12 #endif
13 
14 #include "rule.hpp"
15 #include "error.hpp"
16 #include "encoder.hpp"
17 #include "in_jai.hpp"
18 #include "crc32.h" /* crc32() used by out_png_work() */
19 #include <string.h>
20 
21 /** Appends 4 bytes in MSB first (network) byte order */
mf32(char * & p,slen_t u32)22 static inline void mf32(char *&p, slen_t u32) {
23   p[0]=(u32>>24)&255;
24   p[1]=(u32>>16)&255;
25   p[2]=(u32>> 8)&255;
26   p[3]=(u32    )&255;
27   p+=4;
28 }
29 /** Appends 4 bytes in LSB first (PC) byte order */
lf32(char * & p,slen_t u32)30 static inline void lf32(char *&p, slen_t u32) {
31   p[0]=(u32    )&255;
32   p[1]=(u32>> 8)&255;
33   p[2]=(u32>>16)&255;
34   p[3]=(u32>>24)&255;
35   p+=4;
36 }
37 /** Appends 2 bytes in LSB first (PC) byte order */
lf16(char * & p,unsigned u16)38 static inline void lf16(char *&p, unsigned u16) {
39   p[0]=(u16    )&255;
40   p[1]=(u16>> 8)&255;
41   p+=2;
42 }
43 
44 
45 /* --- at Sat Mar 23 15:42:17 CET 2002, removed obsolete
46  * PostScript Level2 FlateEncode or LZWEncode filter, predictors supported
47  * written at Mar 16 14:48:27 CET 2002
48  */
49 
50 /* --- Sun Mar 17 15:43:20 CET 2002 */
51 
52 #if 0
53 /* l1fa85g.tte was a special .tte that is not built in to bts.ttt, but I've
54  * integrated it into bts2.ttt at Sun Sep 22 00:48:21 CEST 2002. I've also
55  * integrated _l1fa85g_ to _l1c_ that day.
56  */
57 static char *l1fa85g_tte=
58 #include "l1fa85g.tth"
59 
60 /** PostScript Level1 FlateEncode, A85, without Predictor */
61 Rule::Applier::cons_t out_l1fa85g_check_rule(Rule::OutputRule* or_) {
62   Rule::Cache *cache=&or_->cache;
63   if (!cache->isPS()
64    || cache->Compression!=Rule::Cache::CO_ZIP
65    || cache->hasPredictor()
66    || !cache->isGray()
67    || cache->TransferEncoding!=cache->TE_A85
68    || (!cache->WarningOK && !cache->isPSL3())
69      ) return Rule::Applier::DONT_KNOW;
70   return Rule::Applier::OK;
71 }
72 Rule::Applier::cons_t out_l1fa85g_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
73   /* by pts@fazekas.hu at Sun Mar 17 15:52:48 CET 2002 */
74   // Imp: two other TransferEncodings [no more, since #if 0]
75   if (out_l1fa85g_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
76   if (!or_->cache.isPSL3())
77     Error::sev(Error::WARNING_DEFER) << "l1fa85g: /ZIP without /PSL3 will be slow" << (Error*)0;
78   or_->doSampleFormat(sf);
79   PSEncoder *bp=PSEncoder::newASCII85Encode(out, or_->cacheHints.TransferCPL);
80   PSEncoder *cp=PSEncoder::newFlateEncode(*bp, or_->cacheHints.Effort);
81   Rule::writeTTE(out, out, *cp, l1fa85g_tte, or_, sf, Rule::writeData);
82   delete bp;
83   delete cp;
84   return Rule::Applier::OK;
85 }
86 
87 Rule::Applier out_l1fa85g_applier = { "l1fa85g", out_l1fa85g_check_rule, out_l1fa85g_work, 0 };
88 #endif
89 
90 /* l2jbin --- Sun Mar 17 21:45:22 CET 2002
91  * p0jbin (integrated to p0jbin) --- Mon Apr 15 23:29:13 CEST 2002
92  */
93 
94 //static char *l2jbin_tte=
95 //#include "l2jbin.tth"
96 
97 /** PostScript Level2 DCTEncode (Baseline JPEG), Binary */
out_l2jbin_check_rule(Rule::OutputRule * or_)98 Rule::Applier::cons_t out_l2jbin_check_rule(Rule::OutputRule* or_) {
99   Rule::Cache *cache=&or_->cache;
100   if ((!cache->isPS() && !cache->isPDF())
101    || cache->Compression!=Rule::Cache::CO_JAI
102      ) return Rule::Applier::DONT_KNOW;
103   /* Dat: not unrequired anymore: cache->TransferEncoding!=cache->TE_Binary  */
104   bool badp=false;
105   if (cache->isPS() && !cache->isPSL2()) {
106     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PSL* /Compression/JAI requires /PSL2+" << (Error*)0;
107     badp=true;
108   }
109   if (cache->SampleFormat!=Image::SF_Asis) {
110     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PSL*|PDF* /Compression/JAI requires /SampleFormat/Asis" << (Error*)0;
111     badp=true;
112   }
113   if (cache->hasPredictor()) {
114     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PSL*|PDF* /Compression/JAI requires /Predictor 1" << (Error*)0;
115     badp=true;
116   }
117   return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK);
118   /* ^^^ 0+: pacify g++-3.1 */
119 }
out_l2jbin_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)120 Rule::Applier::cons_t out_l2jbin_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
121   char const*strings[]={ (char const*)NULLP/*LanguageLevel, PDF-1.`0'*/, ""/*colorSpace*/ };
122   //, " F closefile T closefile"/*closes*/ };
123   //  Error::sev(Error::WARNING_DEFER) << "l2jbin: /ZIP without /PSL3 will be slow" << (Error*)0;
124   if (out_l2jbin_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
125   assert(sf->getImg()->getBpc()==8);
126   or_->doSampleFormat(sf);
127   // PSEncoder *bp=PSEncoder::newASCII85Encode(out, or_->cacheHints.TransferCPL);
128   Rule::Cache *cache=&or_->cache;
129   Filter::VerbatimE outve(out); /* required since template /p0jbin is TTM */
130   GenBuffer::Writable *op=cache->isPDF() ? &outve : &out, *tp=op;
131   strings[0]=const_cast<char*>(cache->isPDF() ? (char const*)"0" : (char const*)"2");
132        if (cache->TransferEncoding==cache->TE_A85) tp=PSEncoder::newASCII85Encode (*op, or_->cacheHints.TransferCPL);
133   else if (cache->TransferEncoding==cache->TE_Hex) tp=PSEncoder::newASCIIHexEncode(*op, or_->cacheHints.TransferCPL);
134   // else strings[2]=" F closefile";
135   Rule::writeTTT(*op, *tp, *tp, !cache->isPDF()?"l2jbin":cache->isPDFB()?"p0jbb":"p0jbin", or_, sf, Rule::writePalData, strings);
136   if (tp!=op) delete tp;
137   /* Dat: outve is deleted by C++ scope */
138   // if (tp!=op) delete tp; /* BUGFIX at Thu Jan 20 15:04:47 CET 2005 */
139   return Rule::Applier::OK;
140 }
141 
142 Rule::Applier out_l2jbin_applier = { "PSL2+PDF-JAI", out_l2jbin_check_rule, out_l2jbin_work, 0 };
143 
144 /* ---  */
145 
146 #if 0 /* integrated to _l2jbin_ at Sun Sep 22 14:29:13 CEST 2002 */
147 //static char *p0jbin_ttm=
148 //#include "p0jbin.tth"
149 
150 /** PostScript Level2 DCTEncode (Baseline JPEG), Binary */
151 Rule::Applier::cons_t out_p0jbin_check_rule(Rule::OutputRule* or_) {
152   Rule::Cache *cache=&or_->cache;
153   if (!cache->isPDF()
154    || cache->Compression!=Rule::Cache::CO_JAI
155      ) return Rule::Applier::DONT_KNOW;
156   /* Dat: not unrequired anymore: cache->TransferEncoding!=cache->TE_Binary  */
157   bool badp=false;
158   if (cache->SampleFormat!=Image::SF_Asis) {
159     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PDF* /Compression/JAI requires /SampleFormat/Asis" << (Error*)0;
160     badp=true;
161   }
162   if (cache->hasPredictor()) {
163     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PDF* /Compression/JAI requires /Predictor 1" << (Error*)0;
164     badp=true;
165   }
166   if (badp) return Rule::Applier::BAD;
167   return Rule::Applier::OK;
168 }
169 Rule::Applier::cons_t out_p0jbin_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
170   char *strings[]={ "0"/*PDF-1.`0'*/, ""/*colorSpace*/ };
171   // " F closefile"/*closes*/ };
172   if (out_p0jbin_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
173   assert(sf->getImg()->getBpc()==8);
174   or_->doSampleFormat(sf);
175   Rule::Cache *cache=&or_->cache;
176 
177   Filter::VerbatimE outve(out); /* required since template /p0jbin is TTM */
178   GenBuffer::Writable *tp=&outve;
179        if (cache->TransferEncoding==cache->TE_A85) tp=PSEncoder::newASCII85Encode (outve, or_->cacheHints.TransferCPL);
180   else if (cache->TransferEncoding==cache->TE_Hex) tp=PSEncoder::newASCIIHexEncode(outve, or_->cacheHints.TransferCPL);
181   else strings[2]="";
182 
183 #if 0 /* old */
184   MiniPS::VALUE ttm;
185   { Filter::FlatR flatD(p0jbin_ttm);
186     MiniPS::Parser p(&flatD);
187     ttm=p.parse1();
188     if (p.parse1(p.EOF_ALLOWED)!=MiniPS::Qundef || MiniPS::getType(ttm)!=MiniPS::T_ARRAY)
189       Error::sev(Error::EERROR) << "TTM: the TTM file should contain a single array" << (Error*)0;
190     /* ^^^ Dat: the result of the second p.parse1() doesn't get delete0(...)d */
191   }
192   Filter::VerbatimE outve(out);
193   Rule::writeTTM(outve, outve, outve, MiniPS::RARRAY(ttm), or_, sf, Rule::writePalData, strings);
194   MiniPS::delete0(ttm);
195 #else /* new */
196   Rule::writeTTT(outve, *tp, *tp, cache->isPDFB()?"p0jbb":"p0jbin", or_, sf, Rule::writePalData, strings);
197 #endif
198   if (tp!=&outve)delete tp;
199   return Rule::Applier::OK;
200 }
201 
202 Rule::Applier out_p0jbin_applier = { "PDF-JAI", out_p0jbin_check_rule, out_p0jbin_work, 0 };
203 #endif
204 
205 /* --- Fri Mar 22 11:52:53 CET 2002 */
206 /* PDF added at Sat Apr 20 20:07:34 CEST 2002 */
207 /* Integrated l23ind1 at Sat Apr 20 20:24:21 CEST 2002 */
208 
209 //static char *l23_tte=
210 //#include "l23.tth"
211 
212 /** PostScript Level2 and PDF generic non-transparent */
out_l23_check_rule(Rule::OutputRule * or_)213 Rule::Applier::cons_t out_l23_check_rule(Rule::OutputRule* or_) {
214   Rule::Cache *cache=&or_->cache;
215   unsigned char sf=cache->SampleFormat;
216   if (cache->hasPredictor() && cache->Compression!=Rule::Cache::CO_ZIP && cache->Compression!=Rule::Cache::CO_LZW) {
217     Error::sev(Error::WARNING_DEFER) << "check_rule: real /Predictor requires /ZIP or /LZW" << (Error*)0;
218     return Rule::Applier::BAD;
219   }
220 
221   // assert(cache->isPSL2() && (sf==Image::SF_Transparent2 || sf==Image::SF_Transparent4 || sf==Image::SF_Transparent8));
222 
223   if ((!cache->isPSL2() && !cache->isPDF())
224    || (sf!=Image::SF_Gray1    && sf!=Image::SF_Gray2        && sf!=Image::SF_Gray4        && sf!=Image::SF_Gray8
225     && sf!=Image::SF_Indexed1 && sf!=Image::SF_Indexed2     && sf!=Image::SF_Indexed4     && sf!=Image::SF_Indexed8
226     && sf!=Image::SF_Rgb1     && sf!=Image::SF_Rgb2         && sf!=Image::SF_Rgb4         && sf!=Image::SF_Rgb8
227                               && sf!=Image::SF_Transparent2 && sf!=Image::SF_Transparent4 && sf!=Image::SF_Transparent8
228     && sf!=Image::SF_Mask)
229    || cache->TransferEncoding==cache->TE_ASCII
230    || !cache->isZIPOK()
231      ) return Rule::Applier::DONT_KNOW;
232   #if !HAVE_LZW
233     if (cache->Compression==Rule::Cache::CO_LZW) return Rule::Applier::DONT_KNOW;
234   #endif
235 
236   // if (cache->isDCTE() && !cache->isRGB() && !cache->isGray()) {
237   bool badp=false;
238   if (cache->isDCTE() && cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Indexed8) {
239     Error::sev(Error::WARNING_DEFER) << "check_rule: /DCTEncode requires /Rgb8 or /Gray8 (or /Indexed8)" << (Error*)0;
240     badp=true;
241   }
242   if (sf==Image::SF_Transparent2 || sf==Image::SF_Transparent4 || sf==Image::SF_Transparent8) {
243     if (cache->isPDF()) {
244       Error::sev(Error::WARNING_DEFER) << "check_rule: unsupported /Transparent+ for /PDF*" << (Error*)0;
245       badp=true;
246     } else assert(cache->isPSL2());
247   }
248   return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK);
249 }
out_l23_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)250 Rule::Applier::cons_t out_l23_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
251   Rule::stream_writer_t writeXData=Rule::writePalData;
252   char LanguageLevel[2]="2", closes[30];
253   SimBuffer::B colorSpace;
254   char const*strings[]={ LanguageLevel, (char*)NULLP /*colorSpace*/, closes };
255   Rule::Cache *cache=&or_->cache;
256   // assert(0);
257   if (out_l23_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
258   or_->doSampleFormat(sf, true);
259   if (cache->Compression==Rule::Cache::CO_ZIP) {
260     if (!cache->isPDF()) LanguageLevel[0]='3';
261   } else if (cache->isPDF()) LanguageLevel[0]='0';
262   if (cache->isIndexed() || cache->isTransparentM()) {
263     unsigned ncols=PTS_dynamic_cast(Image::Indexed*,sf->getImg())->getNcols();
264     colorSpace << "[/Indexed/DeviceRGB " << ncols-1;
265     if (ncols==0) { /* Avoid writing zero bytes to the filters */
266       /* Imp: verify this in Acrobat Reader */
267       colorSpace << "()]";
268     } else if (!cache->isPDF()) { /* Insert an in-line hexdump. No better way :-( */
269       /*sprintf(colorSpace, "[/Indexed/DeviceRGB %u T %u string readstring pop]", ncols-1, ncols*3); */
270       colorSpace << " T " << ncols*3 << " string readstring pop]";
271     } else {  /* Insert an in-line hexdump. No shorter way for PDF */
272 #if 0
273       colorSpace="/DeviceGray ";
274 #else
275       /* SUXX: gs5.50 & PDF & /Indexed doesn't work */
276       colorSpace << "\n<";
277       GenBuffer::Writable *hp=PSEncoder::newASCIIHexEncode(colorSpace, or_->cacheHints.TransferCPL);
278       hp->vi_write(sf->getImg()->getHeadp(), ncols*3);
279       hp->vi_write(0,0);
280       colorSpace << ']'; /* Dat: '>' is appended by ASCIIHexEncode */
281 #endif
282       writeXData=Rule::writeData; /* The palette has already been written. */
283     }
284   } else if (cache->isGray()) colorSpace="/DeviceGray ";
285   else { assert(cache->isRGB()); colorSpace="/DeviceRGB "; }
286   if (cache->SampleFormat==Image::SF_Mask || cache->SampleFormat==Image::SF_Indexed1) writeXData=Rule::writeData;
287 
288   GenBuffer::Writable *vp=&out;
289   if (cache->isPDF()) vp=new Filter::VerbatimE(out); /* required since template /p02* is TTM */
290 
291   GenBuffer::Writable *tp=vp;
292        if (cache->TransferEncoding==cache->TE_A85) tp=PSEncoder::newASCII85Encode (*vp, or_->cacheHints.TransferCPL);
293   else if (cache->TransferEncoding==cache->TE_Hex) tp=PSEncoder::newASCIIHexEncode(*vp, or_->cacheHints.TransferCPL);
294 
295   GenBuffer::Writable *cp=tp;
296   switch (cache->Compression) {
297    case Rule::Cache::CO_None: break;
298    case Rule::Cache::CO_ZIP: cp=PSEncoder::newFlateEncode(*tp, or_->cacheHints.Effort); break;
299    case Rule::Cache::CO_LZW: cp=PSEncoder::newLZWEncode(*tp); break;
300    case Rule::Cache::CO_RLE: cp=PSEncoder::newRunLengthEncode(*tp, or_->cacheHints.RecordSize); break;
301    case Rule::Cache::CO_Fax: cp=PSEncoder::newCCITTFaxEncode(*tp, or_->cacheHints.K, or_->cacheHints.EncoderBPL, /*EndOfLine:*/ or_->cacheHints.K>0); break;
302    /* ^^^ getBpp() BUGFIX at Wed Jul  3 20:00:30 CEST 2002 */
303    /* ^^^ EndOfLine BUGFIX at Wed Jul  3 21:12:54 CEST 2002
304     * With EndOfLine==false, `sam2p -c:fax:1', acroread triggers the bug.
305     * With EndOfLine==false, `sam2p -c:fax:2', acroread and gs trigger the bug.
306     */
307 
308    case Rule::Cache::CO_IJG: cp=PSEncoder::newDCTIJGEncode(*tp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.Quality); break;
309    case Rule::Cache::CO_DCT: { SimBuffer::B other_parameters;
310     or_->cacheHints.DCT->dump(other_parameters, 0, false);
311     cp=PSEncoder::newDCTEncode(*tp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.ColorTransform, other_parameters);
312     break; }
313    default: assert(0);
314   }
315 
316   GenBuffer::Writable *pp=cp;
317   if (cache->hasPredictor()) pp=PSEncoder::newPredictor(*cp, cache->Predictor, or_->cacheHints.PredictorBPC, or_->cacheHints.PredictorColumns, or_->cacheHints.PredictorColors);
318 
319   #if 0 /* Sun Sep 22 20:40:51 CEST 2002 */
320     if (cp!=tp) strcpy(closes," F closefile"); else closes[0]='\0';
321     if (tp!=vp) strcpy(closes+strlen(closes)," T closefile");
322   #endif
323 
324   strings[1]=colorSpace.term0()();
325   Rule::writeTTT(*vp, *tp, *pp,
326     !cache->isPDF() ? (
327       cache->SampleFormat==Image::SF_Indexed1 ? "l23ind1" :
328       cache->SampleFormat==Image::SF_Mask ? "l23mask" :
329       cache->isTransparentM() ? "l23tran2" :
330       "l23"
331     ) : cache->isPDFB() ? (
332       cache->SampleFormat==Image::SF_Indexed1 ? "p02ind1bb" :
333       cache->SampleFormat==Image::SF_Mask ? "p02maskbb" :
334       "p02bb"
335     ) : (
336       cache->SampleFormat==Image::SF_Indexed1 ? "p02ind1" :
337       cache->SampleFormat==Image::SF_Mask ? "p02mask" :
338       "p02"
339     ), or_, sf, writeXData, strings);
340   if (pp!=cp)  delete pp;
341   if (cp!=tp)  delete cp;
342   if (tp!=vp)  delete tp;
343   if (vp!=&out)delete vp;
344   return Rule::Applier::OK;
345 }
346 
347 Rule::Applier out_l23_applier = { "PSL23+PDF", out_l23_check_rule, out_l23_work, 0 };
348 
349 /* --- Fri Mar 22 17:22:40 CET 2002 -- Sat Jun 15 16:03:06 CEST 2002 */
350 
351 /* integrated l1tr at Sat Jun 15 16:03:03 CEST 2002 */
352 /* added /Bbox at Sat Jun 15 16:03:31 CEST 2002 */
353 
354 //static char *l1tr_tte=
355 //#include "l1tr.tth"
356 
357 /** PostScript Level1 or PDF Fully transparent image */
out_l1tr_check_rule(Rule::OutputRule * or_)358 Rule::Applier::cons_t out_l1tr_check_rule(Rule::OutputRule* or_) {
359   Rule::Cache *cache=&or_->cache;
360   if (!(cache->isPS() || cache->isPDF())
361    || (cache->SampleFormat!=Image::SF_Transparent && cache->SampleFormat!=Image::SF_Bbox && cache->SampleFormat!=Image::SF_Opaque)
362      ) return Rule::Applier::DONT_KNOW;
363   return Rule::Applier::OK;
364 }
out_l1tr_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)365 Rule::Applier::cons_t out_l1tr_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
366   char t[]="....";
367   if (out_l1tr_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
368   or_->doSampleFormat(sf);
369   Rule::Cache *cache=&or_->cache;
370   Filter::VerbatimE outve(out); /* required since template /p0* is TTM */
371   if (cache->isPS()) { t[0]='l'; t[1]='1'; }
372                 else { t[0]='p'; t[1]='0'; }
373   switch (cache->SampleFormat) {
374     case Image::SF_Transparent: t[2]='t'; t[3]='r'; break;
375     case Image::SF_Opaque:      t[2]='o'; t[3]='p'; break;
376     case Image::SF_Bbox:        t[2]='b'; t[3]='b'; break;
377     default: assert(0);
378   }
379   Rule::writeTTT(outve, outve, outve, t, or_, sf, 0/*NULLP*/);
380   return Rule::Applier::OK;
381 }
382 
383 Rule::Applier out_l1tr_applier = { "P-TrOpBb", out_l1tr_check_rule, out_l1tr_work, 0 };
384 
385 /* --- */
386 
387 /* --- l23mask Fri Mar 22 18:11:12 CET 2002
388  * --- l23ind1 Fri Mar 22 18:11:12 CET 2002
389  * removed (integrated to l23) at Sat Apr 20 20:25:57 CEST 2002
390  */
391 
392 /* --- l1mask Fri Mar 22 18:33:01 CET 2002
393  * --- l1mashex Sun Apr 14 15:25:25 CEST 2002
394  * --- l1in1 Fri Mar 22 18:33:37 CET 2002
395  * --- l1in1hex Fri Mar 22 18:33:37 CET 2002
396  * removed (integrated to l1c) at Sat Apr 20 20:25:57 CEST 2002
397  * --- lcr Sat Jun  1 17:09:57 CEST 2002
398  * removed (integrated to l1c) at Sun Jun  2 16:48:31 CEST 2002
399  * --- l1gbh
400  * removed (integrated to l1c) at Sun Jun  2 16:48:31 CEST 2002
401  * --- l1fa85g.tte: PostScript Level1 FlateEncode, A85, without Predictor
402  * removed (integrated to l1c) at Sun Sep 22 00:48:21 CEST 2002
403  */
404 
405 /* --- Sun Jun  2 16:48:44 CEST 2002 */
406 
407 //static char *l1mask_tte=
408 //#include "l1mask.tth"
409 
gen_tkey(char * tkey,GenBuffer::Writable & out,GenBuffer::Writable * & tp,GenBuffer::Writable * & cp,Rule::OutputRule * or_)410 static void gen_tkey(char *tkey, GenBuffer::Writable& out, GenBuffer::Writable*&tp, GenBuffer::Writable*& cp, Rule::OutputRule*or_) {
411   Rule::Cache *cache=&or_->cache;
412   tp=&out;
413        if (cache->TransferEncoding==cache->TE_A85) { tkey[3]='8'; tp=PSEncoder::newASCII85Encode (out, or_->cacheHints.TransferCPL); }
414   else if (cache->TransferEncoding==cache->TE_Hex) { tkey[3]='h'; tp=PSEncoder::newASCIIHexEncode(out, or_->cacheHints.TransferCPL); }
415   else tkey[3]='b';
416   cp=tp;
417        if (cache->Compression==Rule::Cache::CO_RLE) { tkey[4]='r'; cp=PSEncoder::newRunLengthEncode(*tp, or_->cacheHints.RecordSize); }
418   else if (cache->Compression==Rule::Cache::CO_ZIP) { tkey[4]='z'; cp=PSEncoder::newFlateEncode(*tp, or_->cacheHints.Effort); }
419   else if (cache->Compression==Rule::Cache::CO_LZW) { tkey[4]='l'; cp=PSEncoder::newLZWEncode(*tp); }
420   else tkey[4]='n';
421   /* vvv removed 'm' and '1' at Sun Sep 22 17:53:08 CEST 2002 */
422   tkey[2]=// cache->SampleFormat==Image::SF_Mask     ? 'm' :
423           // cache->SampleFormat==Image::SF_Indexed1 ? '1' :
424              cache->SampleFormat==Image::SF_Indexed2 ? '2' :
425              cache->SampleFormat==Image::SF_Indexed4 ? '4' :
426              cache->SampleFormat==Image::SF_Indexed8 ? '8' :
427              cache->SampleFormat==Image::SF_Transparent2 ? 't' :
428              cache->SampleFormat==Image::SF_Transparent4 ? 't' :
429              cache->SampleFormat==Image::SF_Transparent8 ? 't' :
430              'g'; /* /Gray*, /Rgb*, /Mask, /Indexed1 */
431 }
432 
433 /** PostScript Level1 uncompressed binary or hex */
out_l1c_check_rule(Rule::OutputRule * or_)434 Rule::Applier::cons_t out_l1c_check_rule(Rule::OutputRule* or_) {
435   Rule::Cache *cache=&or_->cache;
436   if ((cache->FileFormat!=cache->FF_PSL1 && cache->FileFormat!=cache->FF_PSLC && cache->FileFormat!=cache->FF_PSL2)
437    || (cache->FileFormat==cache->FF_PSL2 && cache->Compression!=Rule::Cache::CO_ZIP)
438    || (cache->TransferEncoding!=cache->TE_Binary && cache->TransferEncoding!=cache->TE_Hex && cache->TransferEncoding!=cache->TE_A85)
439    || (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_RLE && cache->Compression!=Rule::Cache::CO_ZIP && cache->Compression!=Rule::Cache::CO_LZW)
440    || cache->hasPredictor()
441    || !(cache->isTransparentM() || cache->isIndexed() || cache->isGray() || cache->isRGB())
442      ) return Rule::Applier::DONT_KNOW;
443   bool badp=false;
444   if (cache->FileFormat==cache->FF_PSL1 && cache->SampleFormat!=Image::SF_Indexed1 && cache->isIndexed()) {
445     Error::sev(Error::WARNING_DEFER) << "check_rule: /SampleFormat/Indexed+ doesn't work with /FileFormat/PSL1 (use /PSLC or /PSL2)" << (Error*)0;
446     badp=true;
447   }
448   if (cache->FileFormat==cache->FF_PSL1 && cache->isRGB()) {
449     Error::sev(Error::WARNING_DEFER) << "check_rule: /SampleFormat/RGB* doesn't work with /FileFormat/PSL1 (use /PSLC or /PSL2)" << (Error*)0;
450     badp=true;
451   }
452   char tkey[]="l1..."; /* /l1{2,4,8,t}{8,h}{r,z,l} */
453   GenBuffer::Writable *tp0,*cp0,*out=(GenBuffer::Writable*)NULLP;
454   gen_tkey(tkey, *out, tp0, cp0, or_);
455   /* fprintf(stderr,"tkey=%s\n", tkey); */
456   if (!badp && Rule::Templates->get(tkey, strlen(tkey))==MiniPS::Qundef) return Rule::Applier::DONT_KNOW;
457   return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK);
458   /* ^^^ Dat: 0+: pacify gcc-3.1 */
459 }
out_l1c_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)460 Rule::Applier::cons_t out_l1c_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
461   if (out_l1c_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
462   /* Dat: only these have been defined so far:  grep '^/l1[248tg][8h][rzl]' bts2.ttt
463    * /l1thr /l1g8r /l1ghr /l128r /l12hr /l148r /l14hr /l188r /l18hr /l1g8z /l1ghz /l1g8l /l1ghl
464    */
465   or_->doSampleFormat(sf, true); /* Dat: `true': because of /Transparent+ */
466   char tkey[]="l1..."; /* /l1{2,4,8,t}{8,h}{r,z,l} */
467   GenBuffer::Writable *tp, *cp;
468   gen_tkey(tkey, out, tp, cp, or_);
469   // fprintf(stderr, "tkey=(%s)\n", tkey);
470   Rule::writeTTT(out, *tp, *cp, tkey, or_, sf,
471     tkey[2]=='2' || tkey[2]=='4' || tkey[2]=='8' || tkey[2]=='t' ? Rule::writePalData : Rule::writeData
472   );
473   // Rule::writeTTT(out, *tp, *cp, tkey, or_, sf, Rule::writePalData);
474   if (cp!=tp)  delete cp;
475   if (tp!=&out)delete tp;
476   return Rule::Applier::OK;
477 }
478 
479 Rule::Applier out_l1c_applier = { "PSL1C", out_l1c_check_rule, out_l1c_work, 0 };
480 
481 /* lcrbin (lcrb) --- Sun Apr 14 16:50:14 CEST 2002
482  * lcrhex (lcr8, lcrh) --- Sun Apr 14 16:50:22 CEST 2002
483  * removed (integrated to _l1c_) at Sat Jun  1 17:09:52 CEST 2002
484  */
485 
486 #if 0
487 /* --- Sat Jun  1 17:09:57 CEST 2002 */
488 
489 //static char *lcrbin_tte=
490 //#include "lcrbin.tth"
491 
492 /** PostScript Level1+C uncompressed RGB image */
493 Rule::Applier::cons_t out_lcr_check_rule(Rule::OutputRule* or_) {
494   Rule::Cache *cache=&or_->cache;
495   if (!cache->isPS()
496    || cache->FileFormat==cache->FF_PSL1
497    || (cache->TransferEncoding!=cache->TE_Binary && cache->TransferEncoding!=cache->TE_Hex && cache->TransferEncoding!=cache->TE_A85)
498    || cache->Compression!=Rule::Cache::CO_None
499    || cache->hasPredictor()
500    || !cache->isRGB()
501      ) return Rule::Applier::DONT_KNOW;
502   return Rule::Applier::OK;
503 }
504 Rule::Applier::cons_t out_lcr_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
505   Rule::Cache *cache=&or_->cache;
506   if (out_lcr_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
507   or_->doSampleFormat(sf);
508   char tkey[]="lcr.";
509   GenBuffer::Writable *tp=&out;
510        if (cache->TransferEncoding==cache->TE_A85) { tkey[3]='8'; tp=PSEncoder::newASCII85Encode (out, or_->cacheHints.TransferCPL); }
511   else if (cache->TransferEncoding==cache->TE_Hex) { tkey[3]='h'; tp=PSEncoder::newASCIIHexEncode(out, or_->cacheHints.TransferCPL); }
512   else tkey[3]='b';
513   Rule::writeTTT(out, *tp, *tp, tkey, or_, sf, Rule::writeData);
514   if (tp!=&out)delete tp;
515   return Rule::Applier::OK;
516 }
517 
518 Rule::Applier out_lcr_applier = { "PSLC-RGB", out_lcr_check_rule, out_lcr_work, 0 };
519 #endif
520 
521 /* --- Sat Mar 23 12:37:04 CET 2002; Tue Jul  2 10:23:44 CEST 2002 */
522 
out_gif89a_check_rule(Rule::OutputRule * or_)523 Rule::Applier::cons_t out_gif89a_check_rule(Rule::OutputRule* or_) {
524   Rule::Cache *cache=&or_->cache;
525   bool badp=false;
526   if (cache->FileFormat!=cache->FF_GIF89a
527      ) return Rule::Applier::DONT_KNOW;
528   if (!cache->isIndexed() && !cache->isTransparentM()) {
529     Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a must be /Indexed*, /Mask or /Transparent+" << (Error*)0;
530     badp=true;
531   }
532   if (cache->TransferEncoding!=cache->TE_Binary) {
533     Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a requires /Binary" << (Error*)0;
534     badp=true;
535   }
536   if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_LZW) {
537     Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a requires /LZW" << (Error*)0;
538     badp=true;
539   }
540   if (cache->hasPredictor()) {
541     Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a requires /Predictor 1" << (Error*)0;
542     badp=true;
543   }
544   #if !USE_OUT_GIF
545     Error::sev(Error::WARNING_DEFER) << "check_rule: please `configure --enable-gif' for /GIF89a" << (Error*)0;
546     badp=true;
547   #endif
548   if (badp) return Rule::Applier::BAD;
549   or_->cache.WarningOK=true;
550   return Rule::Applier::OK;
551 }
552 #if USE_OUT_GIF
553   #if OBJDEP
554   #  warning REQUIRES: out_gif.o
555   #endif
556   extern void out_gif_write(GenBuffer::Writable& out, Image::Indexed *img); /* out_gif.cpp */
out_gif89a_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)557   Rule::Applier::cons_t out_gif89a_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
558     if (out_gif89a_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
559     or_->cache.SampleFormat=(Image::sf_t)(or_->cache.isIndexed()? 0+Image::SF_Indexed8: 0+Image::SF_Transparent8);
560     /* ^^^ Dat: 0+: pacify gcc-3.1; (Image::sf_t): pacify VC6.0 */
561     or_->doSampleFormat(sf);
562     out_gif_write(out, PTS_dynamic_cast(Image::Indexed*,sf->getImg()));
563     return Rule::Applier::OK;
564   }
565 #else
566 /*#  define out_gif89a_check_rule (Rule::Applier::check_rule_t)NULLP*/
567 #  define out_gif89a_work       (Rule::Applier::work_t)0
568 #endif
569 
570 Rule::Applier out_gif89a_applier = {
571 #if HAVE_LZW
572   "GIF89a+LZW"
573 #else
574   "GIF89a"
575 #endif
576   , out_gif89a_check_rule, out_gif89a_work, 0 };
577 
578 /* --- Tue Jun  4 19:51:03 CEST 2002 */
579 
out_xpm_check_rule(Rule::OutputRule * or_)580 Rule::Applier::cons_t out_xpm_check_rule(Rule::OutputRule* or_) {
581   Rule::Cache *cache=&or_->cache;
582   bool badp=false;
583   if (cache->FileFormat!=cache->FF_XPM
584      ) return Rule::Applier::DONT_KNOW;
585   if (!cache->isIndexed() && !cache->isTransparentM()) {
586     Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM must be /Indexed*, /Mask or /Transparent+" << (Error*)0;
587     badp=true;
588   }
589   if (cache->TransferEncoding!=cache->TE_ASCII && cache->TransferEncoding!=cache->TE_Binary) {
590     /* ^^^ && BUGFIX at Thu Jul 11 21:57:09 CEST 2002 */
591     Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM requires /TransferEncoding/ASCII" << (Error*)0;
592     badp=true;
593   }
594   if (cache->Compression!=Rule::Cache::CO_None) {
595     Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM requires /Compression/None" << (Error*)0;
596     badp=true;
597   }
598   if (cache->hasPredictor()) {
599     Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM requires /Predictor 1" << (Error*)0;
600     badp=true;
601   }
602   if (badp) return Rule::Applier::BAD;
603   or_->cache.WarningOK=true;
604   return Rule::Applier::OK;
605 }
out_xpm_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)606 Rule::Applier::cons_t out_xpm_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
607   if (out_xpm_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
608   or_->cache.SampleFormat=(Image::sf_t)(or_->cache.isIndexed()? 0+Image::SF_Indexed8: 0+Image::SF_Transparent8);
609   /* ^^^ force 8-bit; may trigger warnings... */
610   /* ^^^ Dat: 0+: pacify gcc-3.1 */
611   or_->doSampleFormat(sf);
612   Image::Indexed *iimg=PTS_dynamic_cast(Image::Indexed*,sf->getImg());
613   /* vvv 93 useful ASCII chars (+ '\0'), missing: " and \ ; plus a hextable */
614   static char const xpmc=93;
615   static char const xpms[xpmc+1]="0123456789abcdef !#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`ghijklmnopqrstuvwxyz{|}~";
616 
617   Image::Sampled::dimen_t wd=iimg->getWd(), htc=iimg->getHt();
618   bool pal2ch=iimg->getNcols()>xpmc;
619   // pal2ch=true;
620   out << "/* XPM */\nstatic char *sam2p_xpm[] = {\n"
621          "/* columns rows colors chars-per-pixel */\n\""
622       << wd << ' ' << htc << ' ' << iimg->getNcols()
623       << ' ' << (pal2ch?2:1) << "\",\n";
624   char const *p=iimg->getHeadp(), *pend=iimg->getRowbeg(), *phend;
625   char coline3[]="\".. c #ABCDEF\",\n";
626   char cotran3[]="\".. c None s None \",\n";
627   assert((pend-p)%3==0);
628   unsigned i=0;
629   bool transp=false;
630   if (iimg->getTransp()>=0) { pend-=3; assert((iimg->getTransp())*3+p==pend); transp=true; }
631   if (pal2ch) {
632     while (p!=pend) {
633       coline3[ 1]=xpms[i>>2];
634       coline3[ 2]=xpms[i++&3];
635       coline3[ 7]=xpms[*(unsigned char const*)p>>4];
636       coline3[ 8]=xpms[*(unsigned char const*)p++&15];
637       coline3[ 9]=xpms[*(unsigned char const*)p>>4];
638       coline3[10]=xpms[*(unsigned char const*)p++&15];
639       coline3[11]=xpms[*(unsigned char const*)p>>4];
640       coline3[12]=xpms[*(unsigned char const*)p++&15];
641       out << coline3;
642     }
643     if (transp) {
644       cotran3[ 1]=xpms[i>>2];
645       cotran3[ 2]=xpms[i++&3];
646       out << cotran3;
647       p+=3;
648     }
649     pend=iimg->getRowbeg()+wd*htc;
650     out << "/* Pixels */\n";
651     char *obuf=new char[4+2*wd], *op;
652     obuf[0]='"';
653     obuf[2*wd+1]='"';
654     obuf[2*wd+2]=','; /* Dat: it's OK to have a comma in the last line */
655     obuf[2*wd+3]='\n';
656     while (htc--!=0) {
657       phend=p+wd;
658       op=obuf;
659       while (p!=phend) { *++op=xpms[*p>>2]; *++op=xpms[*p++&3]; }
660       out.vi_write(obuf, 2*wd+4);
661     }
662     delete [] obuf;
663   } else {
664     coline3[1]='"';
665     while (p!=pend) {
666       coline3[ 2]=xpms[i++];
667       coline3[ 7]=xpms[*(unsigned char const*)p>>4];
668       coline3[ 8]=xpms[*(unsigned char const*)p++&15];
669       coline3[ 9]=xpms[*(unsigned char const*)p>>4];
670       coline3[10]=xpms[*(unsigned char const*)p++&15];
671       coline3[11]=xpms[*(unsigned char const*)p>>4];
672       coline3[12]=xpms[*(unsigned char const*)p++&15];
673       out << (coline3+1);
674     }
675     if (transp) {
676       cotran3[1]='"';
677       cotran3[ 2]=xpms[i];
678       out << (cotran3+1);
679       p+=3;
680     }
681     pend=iimg->getRowbeg()+wd*htc;
682     out << "/* Pixels */\n";
683     char *obuf=new char[4+wd], *op;
684     obuf[0]='"';
685     obuf[wd+1]='"';
686     obuf[wd+2]=','; /* Dat: it's OK to have a comma in the last line */
687     obuf[wd+3]='\n';
688     while (htc--!=0) {
689       phend=p+wd;
690       op=obuf;
691       while (p!=phend) *++op=xpms[0U+*p++];
692       out.vi_write(obuf, wd+4);
693     }
694     delete [] obuf;
695   }
696   assert(p==pend);
697   out << "};\n";
698   return Rule::Applier::OK;
699 }
700 
701 Rule::Applier out_xpm_applier = { "XPM", out_xpm_check_rule, out_xpm_work, 0 };
702 
703 /* --- Sat Mar 23 13:18:07 CET 2002 */
704 
out_pnm_check_rule(Rule::OutputRule * or_)705 Rule::Applier::cons_t out_pnm_check_rule(Rule::OutputRule* or_) {
706   Rule::Cache *cache=&or_->cache;
707   if (cache->FileFormat!=cache->FF_PNM
708      ) return Rule::Applier::DONT_KNOW;
709   bool badp=false;
710   // if (cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Gray1) {
711   if (!cache->isGray() && !cache->isRGB() && !cache->isIndexed() && !cache->isTransparentM()) {
712     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM must be /Rgb8, /Gray8 or /Gray1" << (Error*)0;
713     badp=true;
714   }
715   if (cache->TransferEncoding!=cache->TE_Binary && cache->TransferEncoding!=cache->TE_ASCII) {
716     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM requires /Binary or /ASCII" << (Error*)0;
717     badp=true;
718   }
719   if (cache->Compression!=Rule::Cache::CO_None) {
720     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM requires /Compression/None" << (Error*)0;
721     badp=true;
722   }
723   if (cache->hasPredictor()) {
724     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM requires /Predictor 1" << (Error*)0;
725     badp=true;
726   }
727   if (badp) return Rule::Applier::BAD;
728   or_->cache.WarningOK=true;
729   return Rule::Applier::OK;
730 }
out_pnm_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)731 Rule::Applier::cons_t out_pnm_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
732   /*static*/ char head[]={ 'P', 0, '\n', '#', ' ', 'b', 'y', ' ' };
733   /*static*/ char tmp[72];
734   Image::sf_t sfo=or_->cache.SampleFormat;
735   Image::Indexed *alphaChannel=(Image::Indexed*)NULLP;
736   if (out_pnm_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
737   if (or_->cache.SampleFormat==Image::SF_Gray1 || or_->cache.SampleFormat==Image::SF_Gray8) {
738     if (or_->cache.TransferEncoding==or_->cache.TE_ASCII) or_->cache.SampleFormat=Image::SF_Gray8;
739   } else if (or_->cache.SampleFormat==Image::SF_Gray2 || or_->cache.SampleFormat==Image::SF_Gray4) {
740     or_->cache.SampleFormat=Image::SF_Gray8;
741   } else if (or_->cache.isRGB()) {
742     or_->cache.SampleFormat=Image::SF_Rgb8;
743   } else {
744     assert(or_->cache.isIndexed() || or_->cache.isTransparentM());
745     if (or_->cache.isTransparentM()) {
746       alphaChannel=PTS_dynamic_cast(Image::Indexed*,sf->getImg())->calcAlpha();
747       sf->clearTransp(); /* BUGFIX at Tue Sep 17 10:05:47 CEST 2002 */
748     }
749     or_->cache.SampleFormat=(Image::sf_t)(
750                             !sf->canGrayy() ? 0+Image::SF_Rgb8
751                           : sf->minRGBBpcc()==1 ? 0+Image::SF_Gray1 : 0+Image::SF_Gray8);
752     /* ^^^ Dat: 0+: pacify gcc-3.1 */
753   }
754   if (or_->cache.SampleFormat==Image::SF_Gray1 && or_->cache.TransferEncoding==or_->cache.TE_ASCII)
755     or_->cache.SampleFormat=Image::SF_Gray8;
756   or_->doSampleFormat(sf);
757   sfo=or_->cache.SampleFormat;
758   head[1]=(sfo==Image::SF_Rgb8 ? '3' : sfo==Image::SF_Gray8 ? '2' : '1')
759           +(or_->cache.TransferEncoding==or_->cache.TE_Binary)*3;
760   out.vi_write(head, sizeof(head));
761   out << Error::banner0;
762   Image::Sampled *img=sf->getImg();
763   out << '\n' << img->getWd() << ' ' << img->getHt();
764   out << &(" 255\n"[sfo==Image::SF_Gray1?4:0]);
765   /* ^^^ SF_Gray1 BUGFIX at Tue Jun  4 21:44:17 CEST 2002 */
766   register char *p=img->getRowbeg(), *t=(char*)NULLP;
767   slen_t len=img->getRlen()*img->getHt();
768   // fprintf(stderr, "len=%u\n", len);
769   register unsigned smallen, i;
770   switch (head[1]) {
771    case '1': /* PBM ASCII */
772     while (len>=70) {
773       smallen=70; t=tmp; while (smallen--!=0) *t++=(*p++==0)?'1':'0';
774       /* ^^^ note the swapping of '0' and '1' above */
775       *t++='\n'; out.vi_write(tmp, 71);
776       len-=70;
777     }
778     while (len--!=0) *t++=(*p++==0)?'0':'1';
779     /* Dat: xv requires a whitespace just before EOF */
780     *t++='\n'; out.vi_write(tmp, t-tmp);
781     break;
782    case '2': case '3': /* PGM ASCII, PPM ASCII */
783     while (len!=0) {
784       if (len>17) { smallen=17; len-=17; } else { smallen=len; len=0; }
785       t=tmp; while (smallen--!=0) {
786         if ((i=*(unsigned char*)p++)<10) *t++=i+'0';
787         else if (i<100) {           *t++=i/10+'0'; *t++=i%10+'0'; }
788         else if (i<200) { *t++='1'; *t++=(i-100)/10+'0'; *t++=i%10+'0'; }
789                    else { *t++='2'; *t++=(i-200)/10+'0'; *t++=i%10+'0'; }
790         *t++=' ';
791       }
792       /* Dat: xv requires a whitespace just before EOF */
793       t[-1]='\n'; out.vi_write(tmp, t-tmp/*-(len==0)*/);
794     }
795     break;
796    case '4': /* PBM RAWBITS */
797     /* Invert the image */
798     while (len--!=0) *p++^=-1;
799     p=img->getRowbeg();
800     len=img->getRlen()*img->getHt();
801     /* fall through */
802    default: /* PBM RAWBITS, PGM RAWBITS, PPM RAWBITS */
803     /* fwrite(p, 1, len, stdout); */
804     out.vi_write(p, len);
805   }
806   if (alphaChannel!=NULLP) {
807     /* OK: don't always output rawbits PBM file */
808     assert(alphaChannel->getBpc()==1);
809     assert(alphaChannel->getWd()==img->getWd());
810     assert(alphaChannel->getHt()==img->getHt());
811     /* write PBM RAWBITS subfile (alpha channel) */
812     if (or_->cache.TransferEncoding==or_->cache.TE_Binary) {
813       out << "P4 " << img->getWd() << ' ' << img->getHt() << '\n';
814       out.vi_write(alphaChannel->getRowbeg(), alphaChannel->getRlen()*alphaChannel->getHt());
815       /* ^^^ BUGFIX at Tue Sep 17 10:18:28 CEST 2002 */
816     } else {
817       out << "P1 " << img->getWd() << ' ' << img->getHt() << '\n';
818       alphaChannel->to8();
819       p=alphaChannel->getRowbeg(); len=alphaChannel->getRlen()*alphaChannel->getHt();
820       while (len>=70) {
821         smallen=70; t=tmp; while (smallen--!=0) *t++=(*p++!=0)?'1':'0';
822         /* ^^^ note the non-swapping of '0' and '1' above */
823         *t++='\n'; out.vi_write(tmp, 71);
824         len-=70;
825       }
826       while (len--!=0) *t++=(*p++!=0)?'0':'1';
827       /* Dat: xv requires a whitespace just before EOF */
828       *t++='\n'; out.vi_write(tmp, t-tmp);
829     }
830     delete alphaChannel;
831   }
832   return Rule::Applier::OK;
833 }
834 
835 Rule::Applier out_pnm_applier = { "PNM", out_pnm_check_rule, out_pnm_work, 0 };
836 
837 /* --- Sat Aug 10 22:18:33 CEST 2002 */
838 
out_xwd_check_rule(Rule::OutputRule * or_)839 Rule::Applier::cons_t out_xwd_check_rule(Rule::OutputRule* or_) {
840   Rule::Cache *cache=&or_->cache;
841   if (cache->FileFormat!=cache->FF_XWD
842      ) return Rule::Applier::DONT_KNOW;
843   bool badp=false;
844   if (cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Indexed8) {
845     Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD must be /Rgb8, /Gray8 or /Indexed8" << (Error*)0;
846     return Rule::Applier::BAD;
847   }
848   if (cache->TransferEncoding!=cache->TE_Binary) {
849     Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD requires /Binary" << (Error*)0;
850     badp=true;
851   }
852   if (cache->Compression!=Rule::Cache::CO_None) {
853     Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD requires /Compression/None" << (Error*)0;
854     badp=true;
855   }
856   if (cache->hasPredictor()) {
857     Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD requires /Predictor 1" << (Error*)0;
858     badp=true;
859   }
860   if (badp) return Rule::Applier::BAD;
861   return Rule::Applier::OK;
862 }
out_xwd_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)863 Rule::Applier::cons_t out_xwd_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
864   static const unsigned
865     XWD_FILE_VERSION=7,
866     ZPixmap=2,
867     MSBFirst=1,
868     DirectColor=5,
869     PseudoColor=3,
870     //GrayScale=1,
871     StaticGray=0;
872   Image::sf_t sfo=or_->cache.SampleFormat;
873   if (out_xwd_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
874   or_->doSampleFormat(sf);
875   sfo=or_->cache.SampleFormat;
876   char head[101], *p=head;
877   Image::Sampled *img=sf->getImg();
878   slen_t bits_per_pixel, bitmap_pad, bytes_per_line;
879   unsigned ncolors;
880 
881   memset(head, '\0', 101);
882   /*header_size*/ mf32(p,101);
883   /*file_version*/ (p+=4)[-1]=XWD_FILE_VERSION;
884   /*pixmap_format*/ (p+=4)[-1]=ZPixmap;
885   /*pixmap_depth*/ (p+=4)[-1]=sfo==Image::SF_Rgb8 ? 24 : 8;
886   /*pixmap_width */ mf32(p, img->getWd());
887   /*pixmap_height*/ mf32(p, img->getHt());
888   /*xoffset*/ p+=4;
889   /*byte_order*/ (p+=4)[-1]=MSBFirst;
890   /*bitmap_unit*/ (p+=4)[-1]=8; // sfo==Image::SF_Rgb8 ? 32 : 8;
891   /*bitmap_bit_order*/ (p+=4)[-1]=MSBFirst;
892   /*bitmap_pad*/ bitmap_pad=(p+=4)[-1]=8; // sfo==Image::SF_Rgb8 ? 32 : 8;
893   /* ^^^ force no padding at all */
894   /*bits_per_pixel*/ bits_per_pixel=(p+=4)[-1]=sfo==Image::SF_Rgb8 ? 24 : 8;
895   /*bytes_per_line*/ mf32(p, bytes_per_line=((bits_per_pixel*img->getWd()+bitmap_pad-1)&~(bitmap_pad-1))>>3);
896   /*visual_class*/ (p+=4)[-1]=sfo==Image::SF_Rgb8 ? DirectColor : sfo==Image::SF_Indexed8 ? PseudoColor : StaticGray;
897   /*red_mask  */ (p+=4)[-3]=(char)(sfo==Image::SF_Rgb8 ? 255 : 0);
898   /*green_mask*/ (p+=4)[-2]=(char)(sfo==Image::SF_Rgb8 ? 255 : 0);
899   /*blue_mask */ (p+=4)[-1]=(char)(sfo==Image::SF_Rgb8 ? 255 : 0);
900   /*bits_per_rgb*/ (p+=4)[-1]=sfo==Image::SF_Rgb8 ? 24 : 8;
901   /*colormap_entries*/ (p+=4)[-2]=1; /*256*/
902   /*ncolors*/ mf32(p, ncolors=sfo==Image::SF_Rgb8 ? 0 : sfo==Image::SF_Indexed8 ? ((Image::Indexed*)img)->getNcols() : 256);
903   /*window_width */ mf32(p, img->getWd());
904   /*window_height*/ mf32(p, img->getHt());
905   assert(p+13==head+101);
906   /*window_x*/ /*0*/
907   /*window_y*/ /*0*/
908   /*window_bdrwidth*/ /*0*/
909   /*filename*/ /*""*/
910   out.vi_write(head, 101);
911 
912   if (sfo!=Image::SF_Rgb8) {
913     char *pal=new char[ncolors*12], *pp=pal;
914     unsigned pixel;
915     if (sfo==Image::SF_Indexed8) {
916       char const* q=img->getHeadp();
917       for (pixel=0;pixel<ncolors;pixel++) {
918         *pp++=0; *pp++=0; *pp++=0; *pp++=pixel;
919         *pp++=*q; *pp++=*q++; /* red */
920         *pp++=*q; *pp++=*q++; /* green */
921         *pp++=*q; *pp++=*q++; /* blue */
922         *pp++=7;
923         *pp++=0;
924       }
925     } else { assert(sfo==Image::SF_Gray8);
926       for (pixel=0;pixel<ncolors;pixel++) {
927         *pp++=0; *pp++=0; *pp++=0; *pp++=pixel;
928         *pp++=pixel; *pp++=pixel; /* red */
929         *pp++=pixel; *pp++=pixel; /* green */
930         *pp++=pixel; *pp++=pixel; /* blue */
931         *pp++=7;
932         *pp++=0;
933       }
934     }
935     out.vi_write(pal, ncolors*12);
936     delete [] pal;
937   }
938 
939   slen_t rlen=img->getRlen();
940   Image::Sampled::dimen_t htc=img->getHt();
941   char const* rp=img->getRowbeg();
942   unsigned scanline_pad=bytes_per_line-rlen;
943   // assert(*rp=='\xff');
944   if (scanline_pad!=0) {
945     assert(1<=scanline_pad && scanline_pad<=3);
946     while (htc--!=0) {
947       out.vi_write(rp, rlen);
948       rp+=rlen;
949       if (scanline_pad!=0) out.vi_write("\0\0", scanline_pad);
950     }
951   } else out.vi_write(rp, rlen*htc);
952 
953   return Rule::Applier::OK;
954 }
955 
956 Rule::Applier out_xwd_applier = { "XWD", out_xwd_check_rule, out_xwd_work, 0 };
957 
958 Rule::Applier out_x11_applier = { "X11", 0/*out_x11_check_rule*/, 0/*out_x11_work*/, 0 };
959 
960 /* --- Sat Apr 20 11:49:56 CEST 2002 */
961 
962 /** Baseline (lossy) JPEG */
out_jpeg_check_rule(Rule::OutputRule * or_)963 Rule::Applier::cons_t out_jpeg_check_rule(Rule::OutputRule* or_) {
964   Rule::Cache *cache=&or_->cache;
965   if (cache->FileFormat!=cache->FF_JPEG
966    || cache->Compression==Rule::Cache::CO_JAI
967      ) return Rule::Applier::DONT_KNOW;
968   bool badp=false;
969   if (cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8) {
970     Error::sev(Error::WARNING_DEFER) << "check_rule: /DCTEncode requires /Rgb8 or /Gray8" << (Error*)0;
971     badp=true;
972   }
973   if (cache->TransferEncoding!=cache->TE_Binary) {
974     Error::sev(Error::WARNING_DEFER) << "check_rule: /JPEG requires /Binary" << (Error*)0;
975     badp=true;
976   }
977   if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_DCT && cache->Compression!=Rule::Cache::CO_IJG) {
978     Error::sev(Error::WARNING_DEFER) << "check_rule: /JPEG requires /DCT or /IJG" << (Error*)0;
979     badp=true;
980   }
981   if (cache->hasPredictor()) {
982     Error::sev(Error::WARNING_DEFER) << "check_rule: /JPEG requires /Predictor 1" << (Error*)0;
983     badp=true;
984   }
985   if (badp) return Rule::Applier::BAD;
986   or_->cache.WarningOK=true; /* ?? */
987   return Rule::Applier::OK;
988 }
out_jpeg_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)989 Rule::Applier::cons_t out_jpeg_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
990   Rule::Cache *cache=&or_->cache;
991   // assert(0);
992   if (out_jpeg_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
993   or_->doSampleFormat(sf);
994   // GenBuffer::Writable *tp=&out; /* always binary */
995   GenBuffer::Writable *cp=&out;
996   /* SUXX: cjpeg(1) won't create a color JPEG for a grayscale image */
997   if (cache->Compression==Rule::Cache::CO_DCT) {
998     SimBuffer::B other_parameters;
999     or_->cacheHints.DCT->dump(other_parameters, 0, false);
1000     cp=PSEncoder::newDCTEncode(out, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.ColorTransform, other_parameters);
1001   } else {
1002     assert(cache->Compression==Rule::Cache::CO_None || cache->Compression==Rule::Cache::CO_IJG);
1003     cp=PSEncoder::newDCTIJGEncode(out, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.Quality);
1004   }
1005   Rule::writePalData(out, *cp, sf);
1006   delete cp;
1007   return Rule::Applier::OK;
1008 }
1009 
1010 Rule::Applier out_jpeg_applier = { "JPEG", out_jpeg_check_rule, out_jpeg_work, 0 };
1011 
1012 /* --- Wed Apr 17 13:32:46 CEST 2002 */
1013 
out_jpegjai_check_rule(Rule::OutputRule * or_)1014 Rule::Applier::cons_t out_jpegjai_check_rule(Rule::OutputRule* or_) {
1015   Rule::Cache *cache=&or_->cache;
1016   if (cache->FileFormat!=cache->FF_JPEG
1017    || cache->Compression!=Rule::Cache::CO_JAI
1018      ) return Rule::Applier::DONT_KNOW;
1019   bool badp=false;
1020   if (cache->SampleFormat!=Image::SF_Asis) {
1021     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/JPEG /Compression/JAI requires /SampleFormat/Asis" << (Error*)0;
1022     badp=true;
1023   }
1024   if (cache->TransferEncoding!=cache->TE_Binary) {
1025     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/JPEG /Compression/JAI requires /TransferEncoding/Binary" << (Error*)0;
1026     badp=true;
1027   }
1028   if (cache->hasPredictor()) {
1029     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/JPEG /Compression/JAI requires /Predictor 1" << (Error*)0;
1030     badp=true;
1031   }
1032   if (badp) return Rule::Applier::BAD;
1033   return Rule::Applier::OK;
1034 }
out_jpegjai_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)1035 Rule::Applier::cons_t out_jpegjai_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
1036   /* This is the second simplest Applier I've ever written. */
1037   if (out_jpegjai_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
1038   Image::Sampled *img=sf->getImg();
1039   // out.vi_write(img->getHeadp(), img->end_()-img->getHeadp());
1040   /* ^^^ end_() BUGFIX by pts@fazekas.hu at Sun Jun  2 22:24:32 CEST 2002 */
1041   /* ^^^ end_() contains 8 extra bytes */
1042   out.vi_write(img->getHeadp(), img->getRowbeg()-img->getHeadp());
1043   return Rule::Applier::OK;
1044 }
1045 
1046 Rule::Applier out_jpegjai_applier = { "JPEG-JAI", out_jpegjai_check_rule, out_jpegjai_work, 0 };
1047 
1048 /* --- Sun Jun  2 22:25:16 CEST 2002 */
1049 
1050 class TIFFPrinter {
1051   GenBuffer::Writable& out;
1052   /** true iff little endian == LSB first */
1053   bool le;
1054   SimBuffer::B s, dir;
1055  public:
1056   TIFFPrinter(GenBuffer::Writable &out_, bool le_);
getS()1057   inline SimBuffer::B& getS() { return s; }
getDir() const1058   inline SimBuffer::B const& getDir() const { return dir; }
1059   void aSHORT(SimBuffer::B &s, unsigned count, unsigned short const*val);
1060   void aLONG (SimBuffer::B &s, unsigned count, slen_t const*val);
1061   void dirSHORT(unsigned short tag, slen_t count, unsigned short const*val);
1062   void dirLONG(unsigned short tag, slen_t count, slen_t const*val);
1063   void dirSL(unsigned short tag, slen_t val);
1064   void dirLONG(unsigned short tag, slen_t val);
1065   void dirRATIONAL(unsigned short tag, slen_t count, slen_t const*val);
1066   void dirUNDEFINED(unsigned short tag, slen_t count, char const*val);
1067   void dirUNDEFINED(unsigned short tag, slen_t count, char const*val, slen_t count2, char const*val2);
1068   void dirClose();
1069   BEGIN_STATIC_ENUM1(unsigned short)
1070     EXTRASAMPLE_ASSOCALPHA=1, /* !associated alpha data */
1071     EXTRASAMPLE_UNASSALPHA=2  /* !unassociated alpha data */
1072   END_STATIC_ENUM()
1073   BEGIN_STATIC_ENUM1(unsigned short)
1074     COMPRESSION_NONE	 =1,	/* dump mode */
1075     COMPRESSION_CCITTRLE =2,	/* CCITT modified Huffman RLE; unused in sam2p */
1076     COMPRESSION_CCITTFAX3=3,	/* CCITT Group 3 fax encoding */
1077     COMPRESSION_CCITTFAX4=4,	/* CCITT Group 4 fax encoding */
1078     COMPRESSION_LZW	 =5,	/* Lempel-Ziv  & Welch */
1079     COMPRESSION_OJPEG	 =6,	/* !6.0 JPEG; obsolete, unused in sam2p */
1080     COMPRESSION_JPEG	 =7,	/* %JPEG DCT compression */
1081     COMPRESSION_CCITTRLEW=32771,/* #1 w/ word alignment; unused in sam2p */
1082     COMPRESSION_PACKBITS =32773,/* Macintosh RLE */
1083     COMPRESSION_DEFLATE  =32946 /* Deflate compression */
1084   END_STATIC_ENUM()
1085   BEGIN_STATIC_ENUM1(unsigned short)
1086     PHOTOMETRIC_MINISBLACK=1,
1087     PHOTOMETRIC_RGB=2,
1088     PHOTOMETRIC_PALETTE=3,
1089     PHOTOMETRIC_MASK=4,
1090     PHOTOMETRIC_SEPARATED=5, /* possibly CMYK */
1091     PHOTOMETRIC_YCBCR=6
1092   END_STATIC_ENUM()
1093   BEGIN_STATIC_ENUM1(unsigned short)
1094     GROUP3OPT_2DENCODING=0x1,
1095     GROUP3OPT_UNCOMPRESSED=0x2,
1096     GROUP3OPT_FILLBITS=0x4,
1097     GROUP4OPT_UNCOMPRESSED=0x2
1098   END_STATIC_ENUM()
1099   BEGIN_STATIC_ENUM1(unsigned short)
1100     ImageWidth=256,
1101     ImageLength=257,
1102     BitsPerSample=258,
1103     Compression=259,
1104     Photometric=262,
1105     FillOrder=266,
1106     StripOffsets=273,
1107     SamplesPerPixel=277,
1108     RowsPerStrip=278,
1109     StripByteCounts=279,
1110     XResolution=282,
1111     YResolution=283,
1112     PlanarConfig=284,
1113     Group3Options=292,
1114     Group4Options=293,
1115     ResolutionUnit=296,
1116     Predictor=317,
1117     ColorMap=320,
1118     InkSet=332,
1119     ExtraSamples=338,
1120     JPEGTables=347,
1121     YCbCrSubsampling=530,
1122     ReferenceBlackWhite=532
1123   END_STATIC_ENUM()
1124 };
1125 
TIFFPrinter(GenBuffer::Writable & out_,bool le_)1126 TIFFPrinter::TIFFPrinter(GenBuffer::Writable &out_, bool le_) :out(out_), le(le_) {
1127   /* Directory now at unknown offset (will be set by .dirClose()) */
1128   s.vi_write(le_ ? "II*\0\0\0\0\0" : "MM\0*\0\0\0\0", 8);
1129   dir.vi_write("\0",2); /* initial number of directory entries */
1130 }
1131 
aSHORT(SimBuffer::B & s,unsigned count,unsigned short const * val)1132 void TIFFPrinter::aSHORT(SimBuffer::B &s, unsigned count, unsigned short const*val) {
1133   if (le) while (count--!=0) { s << (char)(*val) << (char)(*val>>8); val++; }
1134      else while (count--!=0) { s << (char)(*val>>8) << (char)(*val); val++; }
1135 }
aLONG(SimBuffer::B & s,unsigned count,slen_t const * val)1136 void TIFFPrinter::aLONG (SimBuffer::B &s, unsigned count, slen_t const*val) {
1137   /* Imp: is vi_write(..., 4); faster ? */
1138   if (le) while (count--!=0) { s << (char)(*val) << (char)(*val>>8) << (char)(*val>>16) << (char)(*val>>24); val++; }
1139      else while (count--!=0) { s << (char)(*val>>24) << (char)(*val>>16) << (char)(*val>>8) << (char)(*val); val++; }
1140 }
1141 
dirSHORT(unsigned short const tag,slen_t const count,unsigned short const * val)1142 void TIFFPrinter::dirSHORT(unsigned short const tag, slen_t const count, unsigned short const*val) {
1143   slen_t offs;
1144   aSHORT(dir, 1U, &tag);
1145   dir.vi_write(&("\0\3"[le?1:0]), 2);
1146   aLONG(dir, 1U, &count);
1147   switch (count) {
1148     case 0: dir.vi_write("\0\0\0", 4); break;
1149     case 1: aSHORT(dir, 1, val); dir.vi_write("\0", 2); break;
1150     case 2: aSHORT(dir, 2, val); break;
1151     default:offs=s.getLength(); aSHORT(s, count, val); aLONG(dir, 1, &offs);
1152   }
1153 }
dirLONG(unsigned short const tag,slen_t const count,slen_t const * val)1154 void TIFFPrinter::dirLONG(unsigned short const tag, slen_t const count, slen_t const*val) {
1155   slen_t offs;
1156   aSHORT(dir, 1U, &tag);
1157   dir.vi_write(&("\0\4"[le?1:0]), 2);
1158   aLONG(dir, 1U, &count);
1159   switch (count) {
1160     case 0: dir.vi_write("\0\0\0", 4); break;
1161     case 1: aLONG(dir, 1, val); break;
1162     default:offs=s.getLength(); aLONG(s, count, val); aLONG(dir, 1, &offs);
1163   }
1164 }
dirRATIONAL(unsigned short tag,slen_t count,slen_t const * val)1165 void TIFFPrinter::dirRATIONAL(unsigned short tag, slen_t count, slen_t const*val) {
1166   slen_t offs;
1167   aSHORT(dir, 1U, &tag);
1168   dir.vi_write(&("\0\5"[le?1:0]), 2);
1169   aLONG(dir, 1U, &count);
1170   switch (count) {
1171     case 0: dir.vi_write("\0\0\0", 4); break;
1172     default:offs=s.getLength(); aLONG(s, count*2, val); aLONG(dir, 1, &offs);
1173   }
1174 }
dirUNDEFINED(unsigned short tag,slen_t count,char const * val)1175 void TIFFPrinter::dirUNDEFINED(unsigned short tag, slen_t count, char const*val) {
1176   slen_t offs;
1177   aSHORT(dir, 1U, &tag);
1178   dir.vi_write(&("\0\7"[le?1:0]), 2);
1179   aLONG(dir, 1U, &count);
1180   if (count<=4) {
1181     dir.vi_write(val, count);
1182     if (count!=4) dir.vi_write("\0\0\0", 4-count);
1183   } else {
1184     offs=s.getLength(); s.vi_write(val, count); aLONG(dir, 1, &offs);
1185   }
1186 }
dirUNDEFINED(unsigned short tag,slen_t count,char const * val,slen_t count2,char const * val2)1187 void TIFFPrinter::dirUNDEFINED(unsigned short tag, slen_t count, char const*val, slen_t count2, char const*val2) {
1188   slen_t offs, countx=count+count2;
1189   aSHORT(dir, 1U, &tag);
1190   dir.vi_write(&("\0\7"[le?1:0]), 2);
1191   aLONG(dir, 1U, &countx);
1192   if (countx<=4) {
1193     dir.vi_write(val, count);
1194     dir.vi_write(val2, count2);
1195     if (countx!=4) dir.vi_write("\0\0\0", 4-countx);
1196   } else {
1197     offs=s.getLength();
1198     s.vi_write(val, count);
1199     s.vi_write(val2, count2);
1200     aLONG(dir, 1, &offs);
1201   }
1202 }
dirSL(unsigned short tag,slen_t val)1203 void TIFFPrinter::dirSL(unsigned short tag, slen_t val) {
1204   unsigned short sh;
1205   if (val<1<<16U) { sh=val; dirSHORT(tag, 1, &sh); }
1206              else dirLONG(tag, 1, &val);
1207 }
dirLONG(unsigned short tag,slen_t val)1208 void TIFFPrinter::dirLONG(unsigned short tag, slen_t val) {
1209   dirLONG(tag, 1, &val);
1210 }
dirClose()1211 void TIFFPrinter::dirClose() {
1212   char *ss=s.begin_();
1213   slen_t len=s.getLength();
1214   if (le) { ss[4]=len; ss[5]=len>>8; ss[6]=len>>16; ss[7]=len>>24; }
1215      else { ss[4]=len>>24; ss[5]=len>>16; ss[6]=len>>8; ss[7]=len; }
1216   /* ^^^ dir offset: end of file */
1217   out.vi_write(ss, len);
1218   assert(dir.getLength()%12==2);
1219   unsigned dirc=(dir.getLength()-2)/12;
1220   ss=dir.begin_();
1221   if (le) { ss[0]=dirc; ss[1]=dirc>>8; }
1222      else { ss[0]=dirc>>8; ss[1]=dirc; }
1223   out.vi_write(dir(), dir.getLength());
1224   out.vi_write("", 0);
1225 }
1226 
1227 /* tiffjai --- Sun Jun  2 22:25:16 CEST 2002 */
1228 
out_tiffjai_check_rule(Rule::OutputRule * or_)1229 Rule::Applier::cons_t out_tiffjai_check_rule(Rule::OutputRule* or_) {
1230   Rule::Cache *cache=&or_->cache;
1231   if (cache->FileFormat!=cache->FF_TIFF
1232    || cache->Compression!=Rule::Cache::CO_JAI
1233      ) return Rule::Applier::DONT_KNOW;
1234   bool badp=false;
1235   if (cache->SampleFormat!=Image::SF_Asis) {
1236     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/JAI requires /SampleFormat/Asis" << (Error*)0;
1237     badp=true;
1238   }
1239   if (!cache->isBinSB()) {
1240     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /TransferEncoding/Binary|/?SBFirst" << (Error*)0;
1241     badp=true;
1242   }
1243   if (cache->hasPredictor()) {
1244     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/JAI requires /Prediror 1" << (Error*)0;
1245     badp=true;
1246   }
1247   if (badp) return Rule::Applier::BAD;
1248   return Rule::Applier::OK;
1249 }
out_tiffjai_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)1250 Rule::Applier::cons_t out_tiffjai_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
1251   if (out_tiffjai_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
1252   Image::Sampled *img=sf->getImg();
1253   unsigned char cs=img->getCs(); /* color space */
1254   if (cs!=Image::Sampled::CS_GRAYSCALE && cs!=Image::Sampled::CS_RGB && cs!=Image::Sampled::CS_YCbCr && cs!=Image::Sampled::CS_CMYK) {
1255     /* Dat: CS_YCCK is supported by JPEG, but unsupported by TIFF-JPEG */
1256     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/JAI doesn't support this color space" << (Error*)0;
1257     return Rule::Applier::BAD;
1258   }
1259   /* Imp: test all CMYK TIFF tags */
1260   /* Imp: test with RGB and CMYK images */
1261   TIFFPrinter tp(out, or_->cache.TransferEncoding==or_->cache.TE_LSBfirst);
1262   /* ^^^ Dat: Binary defaults to MSBfirst, beacuse QuarkXPress 3 can read only
1263    * MSBfirst TIFF files.
1264    */
1265   char const *databeg=img->getHeadp()+img->getXoffs();
1266   assert(databeg[-2]=='\xFF' && databeg[-1]=='\xC0');
1267   slen_t datalen=img->getRowbeg()-databeg;
1268   // printf("LEN=%u gl=%u datalen=%u\n", img->end_()-img->begin_(), img->getLength(), datalen);
1269   tp.getS().vi_write("\xFF\xD8\xFF\xC0", 4); /* fake-SOI SOF0 */
1270   tp.getS().vi_write(databeg, datalen); /* DataPart */
1271   // tp.getS().vi_write("\xFF\xD9", 2); /* fake-EOI */
1272   /* ^^^ there must be an EOI already */
1273 
1274   unsigned phot=0;  bool inks=false, refe=false;
1275   switch (cs) {
1276     case Image::Sampled::CS_GRAYSCALE: phot=tp.PHOTOMETRIC_MINISBLACK; break;
1277     case Image::Sampled::CS_RGB: phot=tp.PHOTOMETRIC_RGB; break;
1278     case Image::Sampled::CS_YCbCr: phot=tp.PHOTOMETRIC_YCBCR; refe=true; break; /* preferred to RGB */
1279     case Image::Sampled::CS_CMYK: phot=tp.PHOTOMETRIC_SEPARATED; inks=true; break; /* preferred to RGB */
1280     default: Error::sev(Error::EERROR) << "TIFF6-JAI: color space " << (unsigned)cs << " not supported in TIFF-JPEG" << (Error*)0;
1281   }
1282 
1283   /* Dat: TIFF tags must appear in increasing numerical order */
1284   tp.dirSL(tp.ImageWidth, img->getWd());
1285   tp.dirSL(tp.ImageLength, img->getHt());
1286   unsigned short eights[]={8,8,8,8};
1287   tp.dirSHORT(tp.BitsPerSample, img->cs2cpp[cs], eights);
1288   tp.dirSL(tp.Compression, tp.COMPRESSION_JPEG); /* SHORT */
1289   tp.dirSL(tp.Photometric, phot); /* SHORT */
1290   tp.dirLONG(tp.StripOffsets, 8);
1291   tp.dirSL(tp.SamplesPerPixel, img->cs2cpp[cs]); /* SHORT */
1292   tp.dirSL(tp.RowsPerStrip, img->getHt());
1293   tp.dirLONG(tp.StripByteCounts, datalen+6);
1294   slen_t rats[]={1,1, 0,1, 255,1, 128,1, 255,1, 128,1, 255,1};
1295   tp.dirRATIONAL(tp.XResolution, 1, rats);
1296   tp.dirRATIONAL(tp.YResolution, 1, rats);
1297   tp.dirSL(tp.PlanarConfig, 1); /* SHORT, PLANARCONFIG_CONTIG */
1298   tp.dirSL(tp.ResolutionUnit, 1); /* SHORT */
1299   if (inks) tp.dirSL(tp.InkSet, 1); /* SHORT */
1300   tp.dirUNDEFINED(tp.JPEGTables, databeg-img->getHeadp()-2, img->getHeadp(), 2, "\xFF\xD9");
1301   const unsigned char hvs=img->end_()[-1];
1302   if (hvs!=0x22 && cs==Image::Sampled::CS_YCbCr) {
1303     // printf("hvs=0x%02X\n", hvs);
1304     const unsigned short horiz_vert[2]={ (unsigned short)((hvs+0U)>>4),
1305                                          (unsigned short)(hvs&15U) };
1306     tp.dirSHORT(tp.YCbCrSubsampling, 2, horiz_vert);
1307   }
1308   if (refe) tp.dirRATIONAL(tp.ReferenceBlackWhite, 6, rats+2);
1309   tp.dirClose();
1310   #if 0
1311     { Files::FILEW f(fopen("t.hea","wb"));
1312       f.vi_write(img->getHeadp(), databeg-img->getHeadp()-2); /* HeadPart */
1313       f.vi_write("\xFF\xD9", 2);
1314       f.close();
1315     }
1316     { Files::FILEW f(fopen("t.dat","wb"));
1317       f.vi_write("\xFF\xD8\xFF\xC0", 4); /* fake-SOI SOF0 */
1318       f.vi_write(databeg, datalen); /* DataPart */
1319       f.close();
1320     }
1321   #endif
1322   // out.vi_write(img->getHeadp(), img->getRowbeg()-img->getHeadp()); /* HeadPart */
1323   // out.vi_write(img->getRowbeg(), img->end_()-img->getRowbeg()); /* DataPart */
1324   return Rule::Applier::OK;
1325   /* SUXX: see compatibility notes in FAQ (README) */
1326 }
1327 
1328 Rule::Applier out_tiffjai_applier = { "TIFF6-JAI", out_tiffjai_check_rule, out_tiffjai_work, 0 };
1329 
1330 /* tiff --- Tue Jun  4 16:23:48 CEST 2002 */
1331 
1332 /** by pts@fazekas.hu at Tue Jun 11 16:56:53 CEST 2002
1333  * Reads a JPEG Baseline (SOF0) stream on input, and drops everything before
1334  * the first SOF0 marker. Then writes an SOI marker, the SOF0 marker, and
1335  * everything after the SOI marker. The last two bytes read must be an
1336  * EOI marker (/\xFF\xD9/). The first thing to be read must be a SOI marker
1337  * (/\xFF+\xD8/). The getJPEGTables() method can be used to retrieve the
1338  * data read before the SOF0 marker. An SOS marker must be read after SOF0.
1339  * Also retains some information (hvsamples[0]) from the SOF0 marker.
1340  *   Suitable for reading the output of cjpeg and Ghostscript /FlateEncode.
1341  *   This encoder would be much harder if we were not allowed to read the
1342  * JPEG stream into memory.
1343  */
1344 class JPEGSOF0Encode: public PSEncoder {
1345  public:
1346   /** @param maxcpl_: maximum # hex digits per line, should be even */
1347   JPEGSOF0Encode(GenBuffer::Writable &out_);
1348   virtual void vi_write(char const*buf, slen_t len);
getJPEGTables() const1349   SimBuffer::Flat const& getJPEGTables() const { return buf; }
getColorSpace() const1350   unsigned char getColorSpace() const { return gi.colorspace; }
getHVS() const1351   unsigned char getHVS() const { return gi.hvs; }
1352  protected:
1353   GenBuffer::Writable &out;
1354   SimBuffer::B buf;
1355   struct jai_gfxinfo gi;
1356 };
1357 
JPEGSOF0Encode(GenBuffer::Writable & out_)1358 JPEGSOF0Encode::JPEGSOF0Encode(GenBuffer::Writable &out_): out(out_) {}
vi_write(char const * bufr,slen_t len)1359 void JPEGSOF0Encode::vi_write(char const*bufr, slen_t len) {
1360   if (len==0) {
1361     { Filter::FlatD flatd(buf(), buf.getLength());
1362       jai_parse_jpeg(&gi, &flatd);
1363     }
1364     slen_t len=buf.getLength();
1365     if (gi.bad==0 && (len<4 || buf[len-2]!='\xFF' || buf[len-1]!='\xD9')) gi.bad=10;
1366     if (gi.bad!=0)
1367       Error::sev(Error::EERROR) << "JPEGS0F0: invalid JPEG stream: " << jai_errors[gi.bad] << (Error*)0;
1368     out.vi_write("\xFF\xD8", 2); /* extra SOI */
1369     out.vi_write(buf()+gi.SOF_offs-2, len-gi.SOF_offs+2); /* SOF0 and followers */
1370     out.vi_write(0,0);
1371     assert(buf[gi.SOF_offs-2]=='\xFF'); /* BUGFIX at 2002.12.02 */
1372     buf[gi.SOF_offs-1]='\xD9'; /* extra EOI */
1373     buf.keepLeft(gi.SOF_offs); /* keep only headers */
1374   } else buf.vi_write(bufr, len);
1375 }
1376 
1377 
out_tiff_check_rule(Rule::OutputRule * or_)1378 Rule::Applier::cons_t out_tiff_check_rule(Rule::OutputRule* or_) {
1379   Rule::Cache *cache=&or_->cache;
1380   if (cache->FileFormat!=cache->FF_TIFF
1381    || cache->Compression==Rule::Cache::CO_JAI
1382      ) return Rule::Applier::DONT_KNOW;
1383   bool badp=false;
1384   /* Dat: acroread TIFF predictor2 OK. (examples/fishg_lzw2_pdf.job) */
1385   /* Dat: /ZIP with /Predictor OK */
1386   if (cache->Predictor!=cache->PR_None) {
1387     if (cache->Predictor!=cache->PR_TIFF2) {
1388       Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF requires /Predictor 1|2" << (Error*)0;
1389       badp=true;
1390     }
1391     if (cache->Compression!=Rule::Cache::CO_ZIP && cache->Compression!=Rule::Cache::CO_LZW) {
1392       Error::sev(Error::WARNING_DEFER) << "check_rule: real /Predictor requires /ZIP or /LZW" << (Error*)0;
1393       badp=true;
1394     }
1395   }
1396   #if !HAVE_LZW
1397     if (cache->Compression==Rule::Cache::CO_LZW) {
1398       Error::sev(Error::WARNING_DEFER) << "check_rule: `configure --enable-lzw' for /Compression/LZW with /FileFormat/TIFF" << (Error*)0;
1399       badp=true;
1400     }
1401   #endif
1402   if (cache->Compression==Rule::Cache::CO_Fax && !cache->isOneBit()) {
1403     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/Fax requires a 1-bit /SampleFormat" << (Error*)0;
1404     badp=true;
1405   }
1406   if (!cache->isGray() && !cache->isTransparentM() && !cache->isIndexed() && !cache->isRGB()) {
1407     /* Dat: unsupported SampleFormats: /Opaque, /Transparent and /Asis. */
1408     Error::sev(Error::WARNING_DEFER) << "check_rule: unsupported /SampleFormat for /FileFormat/TIFF" << (Error*)0;
1409     badp=true;
1410   }
1411   if (!cache->isBinSB()) {
1412     Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF requires /TransferEncoding/Binary|/?SBFirst" << (Error*)0;
1413     badp=true;
1414   }
1415   if (cache->isDCTE() && cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Indexed8) {
1416     Error::sev(Error::WARNING_DEFER) << "check_rule: /DCTEncode requires /Rgb8 or /Gray8 (or /Indexed8)" << (Error*)0;
1417     badp=true;
1418   }
1419   if (!badp && cache->isTransparentM()) {
1420     cache->origSampleFormat=cache->SampleFormat;
1421     cache->SampleFormat=Image::SF_Transparent8;
1422   }
1423   return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK);
1424   /* ^^^ Dat: 0+: pacify g++-3.1 */
1425 }
1426 
out_tiff_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)1427 Rule::Applier::cons_t out_tiff_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
1428   Rule::Cache *cache=&or_->cache;
1429   Image::sf_t origSampleFormat=cache->origSampleFormat;
1430   if (out_tiff_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
1431 
1432   assert(!sf->hasTranspp() || cache->isTransparentM());
1433   /** Alpha channel or null. */
1434   Image::Indexed *alpha=(Image::Indexed*)NULLP;
1435   if (cache->isTransparentM()) {
1436     // fprintf(stderr,"sf=%u osf=%u\n", cache->SampleFormat, origSampleFormat);
1437     alpha=PTS_dynamic_cast(Image::Indexed*,sf->getImg())->calcAlpha();
1438     PTS_dynamic_cast(Image::Indexed*,sf->getImg())->getClearTransp();
1439     assert((alpha!=NULLP) == (sf->hasTranspp()==true));
1440     sf->clearTransp();
1441     static Image::sf_t const graytab[9]={Image::SF_None,Image::SF_Gray1,Image::SF_Gray2,Image::SF_None,Image::SF_Gray4,Image::SF_None,Image::SF_None,Image::SF_None,Image::SF_Gray8};
1442     static Image::sf_t const rgbtab[9]={Image::SF_None,Image::SF_Rgb1,Image::SF_Rgb2,Image::SF_None,Image::SF_Rgb4,Image::SF_None,Image::SF_None,Image::SF_None,Image::SF_Rgb8};
1443     static Image::sf_t const indexedtab[9]={Image::SF_None,Image::SF_Indexed1,Image::SF_Indexed2,Image::SF_None,Image::SF_Indexed4,Image::SF_None,Image::SF_None,Image::SF_None,Image::SF_Indexed8};
1444     unsigned char minbpc=sf->minRGBBpcc();
1445     // fprintf(stderr,"minbpc=%u\n",minbpc);
1446          if (minbpc<8 && origSampleFormat==Image::SF_Transparent8) minbpc=8;
1447     else if (minbpc<4 && origSampleFormat==Image::SF_Transparent4) minbpc=4;
1448     else if (minbpc<2 && origSampleFormat==Image::SF_Transparent2) minbpc=2;
1449     // fprintf(stderr,"minbpc=%u\n",minbpc);
1450     // cacheHints.EncoderBPL=(slen_t)img->getWd()*img->getCpp()*img->getBpc();
1451     /* ^^^ Dat: doSampleFormat will do it correctly */
1452     // assert(saf!=Image::SF_max);
1453     cache->SampleFormat=(alpha==NULLP ? indexedtab : sf->minGrayBpcc()==0 ? rgbtab : graytab)[minbpc];
1454   }
1455 
1456   or_->doSampleFormat(sf); /* No separations */
1457   Image::Sampled *img=sf->getImg(); /* call this _after_ doSampleFormat()! */
1458 
1459   TIFFPrinter tp(out, or_->cache.TransferEncoding==or_->cache.TE_LSBfirst);
1460   /* ^^^ Dat: Binary defaults to MSBfirst, beacuse QuarkXPress 3 can read only
1461    * MSBfirst TIFF files.
1462    */
1463   unsigned phot= cache->isRGB() ? 0+tp.PHOTOMETRIC_RGB
1464              // : cache->SampleFormat==Image::SF_Mask ? tp.PHOTOMETRIC_MASK
1465              : cache->isGray() ? 0+tp.PHOTOMETRIC_MINISBLACK
1466              // : cache->isIndexed() ? tp.PHOTOMETRIC_PALETTE
1467              : 0+tp.PHOTOMETRIC_PALETTE /* /Indexed*, /Mask, /Transparent+ */;
1468   unsigned compr=tp.COMPRESSION_NONE;
1469 
1470   Filter::VerbatimCountE vc(tp.getS());
1471   GenBuffer::Writable *cp=&vc;
1472   JPEGSOF0Encode *jp=(JPEGSOF0Encode*)NULLP;
1473   // Dat: g++-3.4: Rule::Cache::CO_None: `cache cannot appear in a constant expression'
1474   switch (cache->Compression) {
1475    case Rule::Cache::CO_None: break;
1476    case Rule::Cache::CO_ZIP: compr=tp.COMPRESSION_DEFLATE; cp=PSEncoder::newFlateEncode(*cp, or_->cacheHints.Effort); break;
1477    case Rule::Cache::CO_LZW: compr=tp.COMPRESSION_LZW; cp=PSEncoder::newLZWEncode(*cp); break;
1478    /* vvv Dat: RunLengthEncode EOD (char 128) is OK and ignored in TIFF PackBits streams */
1479    case Rule::Cache::CO_RLE: compr=tp.COMPRESSION_PACKBITS; cp=PSEncoder::newRunLengthEncode(*cp, or_->cacheHints.RecordSize); break;
1480    case Rule::Cache::CO_Fax:
1481     if (or_->cacheHints.K<0) compr=tp.COMPRESSION_CCITTFAX4;
1482     else if (or_->cacheHints.K==0) compr=tp.COMPRESSION_CCITTFAX3;
1483     else { compr=tp.COMPRESSION_CCITTFAX3; or_->cacheHints.K=img->getHt(); }
1484     cp=PSEncoder::newCCITTFaxEncode(*cp, or_->cacheHints.K, or_->cacheHints.EncoderBPL, /*EndOfLine:*/ compr==tp.COMPRESSION_CCITTFAX3, /*BlackIs1: !*/ true);
1485     /* Dat: libtiff enforces EndOfLine==(compr==tp.COMPRESSION_CCITTFAX3) */
1486     break;
1487    case Rule::Cache::CO_IJG:
1488     compr=tp.COMPRESSION_JPEG;
1489     jp=new JPEGSOF0Encode(vc);
1490     cp=PSEncoder::newDCTIJGEncode(*jp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.Quality);
1491     break;
1492    case Rule::Cache::CO_DCT: compr=tp.COMPRESSION_JPEG; {
1493     SimBuffer::B other_parameters;
1494     or_->cacheHints.DCT->dump(other_parameters, 0, false);
1495     jp=new JPEGSOF0Encode(vc);
1496     cp=PSEncoder::newDCTEncode(*jp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.ColorTransform, other_parameters);
1497     break; }
1498    default: assert(0);
1499   }
1500 
1501   GenBuffer::Writable *pp=cp;
1502   if (cache->hasPredictor()) pp=PSEncoder::newPredictor(*cp, cache->Predictor, or_->cacheHints.PredictorBPC, or_->cacheHints.PredictorColumns, or_->cacheHints.PredictorColors);
1503 
1504   slen_t rlenht=img->getRlen()*img->getHt();
1505   Image::Sampled::dimen_t wd=img->getWd();
1506   if (rlenht!=0) {
1507     /* Dat: TIFF inserts extra sample bits... */
1508     /* TIFF images with transparency are really inefficient. That's because
1509      * libtiff doesn't read or write an indexed image with transparency:
1510      * Sorry, can not handle contiguous data with PhotometricInterpretation=3,
1511      * and Samples/pixel=2.
1512      *   So Indexed images are converted to RGB* or Gray* first, and then the
1513      * alpha channel is added. The result is a big and inefficiently
1514      * compressible TIFF file.
1515      */
1516 
1517     if (alpha!=NULLP) {
1518       unsigned char bpc=img->getBpc();
1519       slen_t rlen=img->getRlen();
1520       slen_t writelen;
1521       char *buf=(char*)NULLP;
1522       char const*psave=img->getRowbeg(), *p, *ppend=psave+rlenht;
1523       char const*r=alpha->getRowbeg();
1524 #ifndef NDEBUG
1525       char const*rend;
1526       Image::Sampled::dimen_t rlena=(wd+7)>>3;
1527 #endif
1528       char *t;
1529       register unsigned u;
1530 #ifndef NDEBUG
1531       assert(rlena==alpha->getRlen());
1532 #endif
1533       // printf("SF=%u\n", cache->SampleFormat);
1534       if (cache->isGray()) {
1535         /* works at Mon Dec  9 01:25:59 CET 2002 */
1536         static unsigned char const szor1[16]={2*0,2*1,2*4,2*5,2*16,2*17,2*20,2*21,2*64,2*65,2*68,2*69,2*80,2*81,2*84,2*85};
1537         static unsigned char const szor2[16]={85,84,81,80,69,68,65,64,21,20,17,16,5,4,1,0};
1538         // static unsigned char const szor2[8]={0,1,16,17,64,65,80,81};
1539         buf=new char[(writelen=((slen_t)wd*bpc+3)>>2)+24];
1540 #ifndef NDEBUG
1541         assert(rlena*8>=rlen);
1542 #endif
1543         while (psave!=ppend) {
1544           t=buf;
1545 #ifndef NDEBUG
1546           rend=r+rlena;
1547 #endif
1548           p=psave; psave+=rlen;
1549           assert(psave<=ppend);
1550           u=(1<<16);
1551           switch (bpc) {
1552            case 8:
1553             while (p!=psave) {
1554               /* Dat: works at Tue Sep 10 22:45:52 CEST 2002 */
1555               if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8);
1556               *t++=*p++; *t++=-(0==(u&128));
1557               u<<=1;
1558             } break;
1559            case 4:
1560             while (p!=psave) {
1561               if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8);
1562               *t++=(p[0]&0xF0)|(0==(u&128)?15:0);
1563               *t++=((p[0]&0xF)<<4)|(0==(u&64)?15:0);
1564               p++; u<<=2;
1565             } break;
1566            case 2:
1567             while (p!=psave) {
1568               if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8);
1569               *t++=(p[0]&0xC0    )|((u&128)!=0?0:48)|((p[0]&0x30)>>2)|((u&64)!=0?0:3);
1570               *t++=((p[0]&0xC)<<4)|((u& 32)!=0?0:48)|((p[0]&0x3 )<<2)|((u&16)!=0?0:3);
1571               p++; u<<=4;
1572             } break;
1573            case 1:
1574             while (p!=psave) {
1575               u=*(unsigned char const*)r++;
1576               *t++=szor1[*(unsigned char const*)p>>4]|szor2[u>>4];
1577               *t++=szor1[*(unsigned char const*)p&15]|szor2[u&15];
1578               p++;
1579             } break;
1580           } /* SWITCH Gray bpc */
1581           // assert(p-psave+0U==rlena*8U);
1582           assert(p>=psave);
1583           pp->vi_write(buf, writelen);
1584         }
1585       } else {
1586         /* works at Sun Dec  8 23:30:17 CET 2002 */
1587         assert(cache->isRGB());
1588         buf=new char[(writelen=((slen_t)wd*bpc+1)>>1)+24];
1589         while (psave!=ppend) {
1590           t=buf;
1591           p=psave; psave+=rlen;
1592           assert(psave<=ppend);
1593           u=(1<<16);
1594 #ifndef NDEBUG
1595           rend=r+rlena; /* superfluous */
1596 #endif
1597           switch (bpc) {
1598            case 8:
1599             while (p<psave) {
1600               if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8);
1601               *t++=*p++;
1602               *t++=*p++;
1603               *t++=*p++;
1604               *t++=-(0==(u&128));
1605               u<<=1;
1606             } break;
1607            case 4:
1608             while (p<psave) {
1609               if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8);
1610               *t++=p[0]; /* R0 and G0 */
1611               *t++=(p[1]&0xF0)|((u&128)!=0?0:15); /* B0 and A0 */
1612               *t++=(p[1]<<4)|((p[2]>>4)&15); /* R1 and G1 */
1613               *t++=(p[2]<<4)|((u&64)!=0?0:15); /* B1 and A1 */
1614               p+=3; u<<=2;
1615             } break;
1616            case 2:
1617             while (p<psave) {
1618               if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8);
1619               /* Dat: p[0]==R0G0B0R1  p[1]==G1B1R2G2  p[2]==B2R3G3B3 */
1620               *t++=(p[0]&0xFC)|((u&128)!=0?0:3); /* R0G0B0 and A0 */
1621               *t++=(p[0]<<6)|((p[1]>>2)&0x3C)|((u&64)!=0?0:3); /* R1 G1B1 A1 */
1622               *t++=(p[1]<<4)|((p[2]>>4)&0x0C)|((u&32)!=0?0:3); /* R2G1 B1 A2 */
1623               *t++=(p[2]<<2)|((u&16)!=0?0:3); /* R3G3B3 and A3 */
1624               p+=3; u<<=4;
1625             } break;
1626            default: // case 1:
1627             while (p<psave) {
1628               u=*(unsigned char const*)r++;
1629               /* Dat: p[0]==RGBRGBRG p[1]==BRGBRGBR  p[2]==GBRGBRGB */
1630               *t++=(p[0]&0xE0)|((u&128)!=0?0:16)
1631                   |((p[0]>>1)&0xE)|((u&64)!=0?0:1);
1632               *t++=(p[0]<<6)|((p[1]>>2)&0x80)|((u&32)!=0?0:16)
1633                   |((p[1]>>3)&0xE)|((u&16)!=0?0:1);
1634               *t++=((p[1]<<4)&0xE0)|((u&8)!=0?0:16)
1635                   |((p[1]<<3)&0x7)|((p[2]>>5)&0x06)|((u&4)!=0?0:1);
1636               *t++=((p[2]<<2)&0xE0)|((u&2)!=0?0:16)
1637                   |((p[2]<<1)&0x0E)|((u&1)!=0?0:1);
1638               p+=3;
1639             }
1640            break;
1641           } /* SWITCH RGB bpc */
1642 #ifndef NDEBUG
1643           assert(r==rend); // r=rend;
1644 #endif
1645           pp->vi_write(buf, writelen);
1646         }
1647       }
1648       delete [] buf;
1649     } else pp->vi_write(img->getRowbeg(), rlenht);
1650   }
1651   pp->vi_write(0,0); /* flush all */
1652   if (pp!=cp)  delete pp;
1653   if (cp!=&vc) delete cp;
1654 
1655   bool refe=false;
1656   if (compr==tp.COMPRESSION_JPEG) switch (jp->getColorSpace()) {
1657     case Image::Sampled::CS_GRAYSCALE:
1658       assert(phot==tp.PHOTOMETRIC_MINISBLACK || phot==tp.PHOTOMETRIC_PALETTE);
1659       /* Dat: we don't change `phot' here, so JPEG compression can be applied
1660        *      to the palette indexes of indexed images :-)
1661        */
1662       break;
1663     case Image::Sampled::CS_RGB: phot=tp.PHOTOMETRIC_RGB; break;
1664     case Image::Sampled::CS_YCbCr: phot=tp.PHOTOMETRIC_YCBCR; refe=true; break; /* preferred to RGB */
1665     // case Image::Sampled::CS_CMYK: phot=tp.PHOTOMETRIC_SEPARATED; inks=true; break; /* preferred to RGB */
1666     default: Error::sev(Error::EERROR) << "TIFF6: color space " << (unsigned)jp->getColorSpace() << " not supported in TIFF-JPEG" << (Error*)0;
1667   }
1668 
1669   /* Dat: TIFF tags must appear in increasing numerical order */
1670   tp.dirSL(tp.ImageWidth, wd);
1671 //  tp.dirSL(tp.ImageWidth, img->getWd()/2);
1672   tp.dirSL(tp.ImageLength, img->getHt());
1673   unsigned short s4[]={img->getBpc(),img->getBpc(),img->getBpc(),img->getBpc()};
1674   /* ^^^ these values may be different according to the TIFF6 spec, but
1675    * libtiff can read TIFF files only with same BitsPerSamples.
1676    */
1677   unsigned sppa=img->getCpp()+(alpha!=NULLP ? 1:0);
1678   tp.dirSHORT(tp.BitsPerSample, sppa, s4);
1679   tp.dirSL(tp.Compression, compr); /* SHORT */
1680   tp.dirSL(tp.Photometric, phot); /* SHORT */
1681   if (cache->Compression==Rule::Cache::CO_Fax) tp.dirSL(tp.FillOrder, 1); /* byte abcdefgh is (a<<7)+...+h */
1682   tp.dirLONG(tp.StripOffsets, 8);
1683   if (!cache->isIndexed())
1684     tp.dirSL(tp.SamplesPerPixel, sppa); /* SHORT */
1685 //    tp.dirSL(tp.SamplesPerPixel, 2); /* SHORT */
1686   tp.dirSL(tp.RowsPerStrip, img->getHt());
1687   tp.dirLONG(tp.StripByteCounts, vc.getCount());
1688   slen_t rats[]={1,1, 0,1, 255,1, 128,1, 255,1, 128,1, 255,1};
1689   tp.dirRATIONAL(tp.XResolution, 1, rats);
1690   tp.dirRATIONAL(tp.YResolution, 1, rats);
1691   tp.dirSL(tp.PlanarConfig, 1); /* SHORT, PLANARCONFIG_CONTIG */
1692   if (compr==tp.COMPRESSION_CCITTFAX3) tp.dirLONG(tp.Group3Options, (tp.GROUP3OPT_UNCOMPRESSED)|(or_->cacheHints.K!=0?tp.GROUP3OPT_2DENCODING:0));
1693   if (compr==tp.COMPRESSION_CCITTFAX4) tp.dirLONG(tp.Group4Options, tp.GROUP4OPT_UNCOMPRESSED);
1694   tp.dirSL(tp.ResolutionUnit, 1); /* SHORT */
1695   if (cache->Predictor!=cache->PR_None) {
1696     tp.dirSL(tp.Predictor, cache->Predictor); /* 1|2 */
1697     if (img->getBpc()!=8)
1698       Error::sev(Error::WARNING) << "TIFF6: libtiff supports /Predictor only with bpc=8 and bpc=16" << (Error*)0;
1699   }
1700 
1701   if (cache->isIndexed() || cache->isTransparentM()) {
1702     unsigned colorlen=(1<<img->getBpc()), i=0;
1703     unsigned short *r=new unsigned short[3*colorlen], *g=r+colorlen, *b=g+colorlen;
1704     memset(r, '\0', 3*colorlen);
1705     /** Image palette data */
1706     char const *p=img->getHeadp(), *pend=img->getRowbeg();
1707     assert((pend-p)%3==0);
1708     assert(3*colorlen>=(unsigned)(pend-p));
1709     while (p<pend) {
1710       r[i  ]=*(unsigned char const*)p++*257;
1711       g[i  ]=*(unsigned char const*)p++*257;
1712       b[i++]=*(unsigned char const*)p++*257;
1713     }
1714     /* ^^^ In a TIFF palette (ColorMap) 0 is black, 65535 is lightest */
1715     tp.dirSHORT(tp.ColorMap, colorlen*3, r);
1716     delete [] r;
1717   }
1718   if (alpha!=NULLP) {
1719     /* vvv Dat: GIMP 1.0 load fails with tp.EXTRASAMPLE_UNASSALPHA */
1720     tp.dirSL(tp.ExtraSamples, tp.EXTRASAMPLE_ASSOCALPHA); /* SHORT */
1721     delete alpha;
1722   }
1723   /* no tp.InkSet, because no CMYK */
1724 
1725   if (compr==tp.COMPRESSION_JPEG) {
1726     tp.dirUNDEFINED(tp.JPEGTables, jp->getJPEGTables().getLength(), jp->getJPEGTables()() /* , 2, "\xFF\xD9" */);
1727     const unsigned char hvs=jp->getHVS();
1728     if (hvs!=0x22 && phot==tp.PHOTOMETRIC_YCBCR) {
1729       const unsigned short horiz_vert[2]={ (unsigned short)((hvs+0U)>>4),
1730                                            (unsigned short)(hvs&15U) };
1731       tp.dirSHORT(tp.YCbCrSubsampling, 2, horiz_vert);
1732     }
1733     if (refe) tp.dirRATIONAL(tp.ReferenceBlackWhite, 6, rats+2);
1734     delete jp;
1735   }
1736 
1737   tp.dirClose();
1738   return Rule::Applier::OK;
1739 }
1740 
1741 Rule::Applier out_tiff_applier = { "TIFF6", out_tiff_check_rule, out_tiff_work, 0 };
1742 
1743 
1744 /* --- Sat Apr 20 11:32:42 CEST 2002 -- Sat Apr 20 16:48:25 CEST 2002 */
1745 
1746 #if OBJDEP
1747 #  warning REQUIRES: crc32.o
1748 #  warning REQUIRES: crc32.o
1749 #endif
1750 /** Encodes 32-bit (data.length-minus), data, crc32. */
1751 class LenCRC32Encode: public PSEncoder {
1752  public:
1753   /** @param maxcpl_: maximum # hex digits per line, should be even */
1754   LenCRC32Encode(GenBuffer::Writable &out_, unsigned minus_);
1755   virtual void vi_write(char const*buf, slen_t len);
1756 
1757  protected:
1758   GenBuffer::Writable &out;
1759   unsigned minus;
1760   /** The length is not known in advance, so we have to buffer the whole output
1761    * in memory.
1762    */
1763   SimBuffer::B sofar;
1764 };
1765 
LenCRC32Encode(GenBuffer::Writable & out_,unsigned minus_)1766 LenCRC32Encode::LenCRC32Encode(GenBuffer::Writable &out_, unsigned minus_): out(out_), minus(minus_), sofar("1234") {}
vi_write(char const * buf,slen_t len)1767 void LenCRC32Encode::vi_write(char const*buf, slen_t len) {
1768   if (len==0) {
1769     char *s=const_cast<char*>(sofar());
1770     slen_t slen=sofar.getLength();
1771     unsigned PTS_INT32_T crc=crc32(CRC32_INITIAL, s+4, slen-4);
1772     slen-=minus+4;
1773     s[0]=slen>>24; s[1]=slen>>16; s[2]=slen>>8; s[3]=slen;
1774     s=sofar.vi_mkend(4);
1775     s[0]=crc >>24; s[1]=crc>>16;  s[2]=crc>>8;  s[3]=crc;
1776     out.vi_write(sofar(), sofar.getLength());
1777     out.vi_write(0,0);
1778   } else sofar.vi_write(buf, len);
1779 }
1780 
1781 /** PNG output (RFC 2083) */
out_png_check_rule(Rule::OutputRule * or_)1782 Rule::Applier::cons_t out_png_check_rule(Rule::OutputRule* or_) {
1783   /* Supported PNG types: /Gray1, /Gray2, /Gray4, /Gray8, /Rgb8,
1784    *   /Indexed1, /Indexed2, /Indexed4, /Indexed8.
1785    * Unsupported PNG types: /Gray16, /Rgb16, /GrayA8, /GrayA16, /RgbA8,
1786    *   /RgbA16.
1787    * /Indexed* supports transparency (via PNG chunk tRNS).
1788    */
1789   Rule::Cache *cache=&or_->cache;
1790   if (cache->FileFormat!=cache->FF_PNG
1791      ) return Rule::Applier::DONT_KNOW;
1792   bool badp=false;
1793   /* Dat: (by policy) we don't support /Transparent or /Opaque here; the
1794    * user should not specify such SampleFormat in the .job file.
1795    */
1796   if (!cache->isGray()
1797    && !cache->isTransparentM()
1798    && !cache->isIndexed()
1799    && cache->SampleFormat!=Image::SF_Rgb8) {
1800     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Gray*, /Indexed*, /Mask, /Transparent+ or /Rgb8" << (Error*)0;
1801     badp=true;
1802   }
1803   if (cache->TransferEncoding!=cache->TE_Binary) {
1804     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Binary" << (Error*)0;
1805     badp=true;
1806   }
1807   if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_ZIP) {
1808     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Compression/None or /ZIP" << (Error*)0;
1809     badp=true;
1810   }
1811   if (cache->Predictor==cache->PR_TIFF2) {
1812     Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Predictor >=10" << (Error*)0;
1813     badp=true;
1814   }
1815   if (badp) return Rule::Applier::BAD;
1816   /* Now we are sure about Rule::Applier::OK. */
1817   or_->cache.WarningOK=true; /* ?? */
1818   return Rule::Applier::OK;
1819 }
out_png_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)1820 Rule::Applier::cons_t out_png_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
1821   Rule::Cache *cache=&or_->cache;
1822   char tmp[64], colortype; register char*p;
1823   unsigned PTS_INT32_T crc;
1824 
1825   // assert(0);
1826   if (out_png_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
1827   or_->doSampleFormat(sf);
1828   Image::Sampled *img=sf->getImg();
1829   unsigned char bpc=img->getBpc(); /* set _after_ doSampleFormat */
1830   Image::Sampled::dimen_t wd=img->getWd(), ht=img->getHt();
1831 
1832   GenBuffer::Writable *rp=new LenCRC32Encode(out,4);
1833   GenBuffer::Writable *cp=PSEncoder::newFlateEncode(*rp, (cache->Compression==Rule::Cache::CO_None) ? 0: or_->cacheHints.Effort);
1834 
1835   /* Dat: PredictorColumns, PredictorBPC and PredictorColors are forced to
1836    *      match the image, because PNG requires it.
1837    */
1838   GenBuffer::Writable *pp=PSEncoder::newPredictor(
1839     *cp,
1840     cache->Predictor==cache->PR_None ? cache->PR_PNGNone : cache->Predictor,
1841     bpc,          /*or_->cacheHints.PredictorBPC*/
1842     wd,           /*or_->cacheHints.PredictorColumns*/
1843     img->getCpp() /*or_->cacheHints.PredictorColors*/
1844   );
1845 
1846   /* Critical PNG chunks (capital 1st letter) and ancillary PNG chunks:
1847    *
1848    * -: zero, 1: exactly one, ?: 0 or 1, *: 0 or more, +: 1 or more
1849    * Name  Nr sam2p Ordering constraints
1850    * IHDR  1  1     Must be first
1851    * cHRM  ?  -     Before PLTE and IDAT
1852    * gAMA  ?  -     Before PLTE and IDAT
1853    * sBIT  ?  -     Before PLTE and IDAT
1854    * PLTE  ?  ?     Before IDAT
1855    * hIST  ?  -     After PLTE; before IDAT
1856    * tRNS  ?  ?     After PLTE; before IDAT
1857    * bKGD  ?  ?     After PLTE; before IDAT
1858    * pHYs  ?  -     Before IDAT
1859    * tIME  ?  -     None
1860    * tEXt  *  -     None
1861    * zTXt  *  -     None
1862    * IDAT  +  1     After IHDR and PLTE, multiple IDATs must be consecutive
1863    * IEND  1  1     Must be last
1864    */
1865 
1866   /* 0..3:   PNG header */
1867   /* 8..11:  IHDR chunk length */
1868   /* 12..15: IHDR chunk name */
1869   memcpy(tmp,"\211PNG\r\n\032\n\0\0\0\015IHDR",16); /* ASCII charset is assumed. */
1870   p=tmp+16;
1871   *p++=wd>>24; *p++=wd>>16; *p++=wd>>8; *p++=wd; /* 16: Width */
1872   *p++=ht>>24; *p++=ht>>16; *p++=ht>>8; *p++=ht; /* 20: Height */
1873   *p++=bpc; /* 24: Bit depth */
1874   colortype=*p++=cache->isGray()?0:cache->isRGB()?2:3; /* 25: Color type */
1875   *p++=0; /* 26: Compression method: deflate/32K (forced by PNG spec), overcoming with Effort==0 */
1876   *p++=0; /* 27: Filter type: adaptive filtering with 5 basic PNG filter types */
1877   *p++=0; /* 28: Non-interlaced */
1878   crc=crc32(CRC32_INITIAL, tmp+12, 17);
1879   *p++=crc>>24;*p++=crc>>16;*p++=crc>>8;*p++=crc; /* 29: IHDR CRC */
1880   out.vi_write(tmp, 33);
1881   if (colortype==3) {
1882     bool transp=cache->isTransparentM() && PTS_dynamic_cast(Image::Indexed*,img)->getTransp()>=0;
1883     /* unsigned ncols=PTS_dynamic_cast(Image::Indexed*,img)->getNcols(); */
1884     unsigned ncols3=img->getRowbeg()-img->getHeadp();
1885 
1886     p=tmp;
1887     *p++=0; *p++=0; *p++=ncols3>>8; *p++=ncols3; /* 0: PLTE chunk length */
1888     *p++='P'; *p++='L'; *p++='T'; *p++='E'; /* 4: PLTE chunk name */
1889     out.vi_write(tmp, 8);
1890     if (transp) {
1891       PTS_dynamic_cast(Image::Indexed*,img)->makeTranspZero();
1892       p=img->getHeadp(); p[0]=p[1]=p[2]=(char)0; /* force black backround (for consistency) */
1893     }
1894     out.vi_write(img->getHeadp(), ncols3);
1895     crc=crc32(crc32(CRC32_INITIAL,tmp+4,4), img->getHeadp(), ncols3);
1896     p=tmp; *p++=crc>>24;*p++=crc>>16;*p++=crc>>8;*p++=crc; /* 0: PLTE CRC */
1897     out.vi_write(tmp, 4);
1898     if (transp) {
1899       out.vi_write("\0\0\0\1tRNS\0@\346\330f", 13); /* color 0 is transparent */
1900       //fprintf(stderr, "tRNS: 0x%08lx", (unsigned long)crc32(CRC32_INITIAL,"tRNS\0",5)); /* 0x40e6d866 */
1901       out.vi_write("\0\0\0\1bKGD\0\210\5\35H", 13); /* color 0 is background color */
1902       //out.vi_write("\0\0\0\1bKGD\2\x66\x0b\x7c\x64", 13); /* color 2 is background color */
1903       //fprintf(stderr, "bKGD: 0x%08lx", (unsigned long)crc32(CRC32_INITIAL,"bKGD\2",5)); /* 0x88051d48 */
1904     }
1905   }
1906 
1907   slen_t rlenht=img->getRlen()*img->getHt();
1908   #if 0 /* Calculates _bad_ CRC (before compression etc.) */
1909     p=tmp;
1910     *p++=rlenht>>24; *p++=rlenht>>16; *p++=rlenht>>8; *p++=rlenht; /* 0: IDAT chunk length */
1911     *p++='I'; *p++='D'; *p++='A'; *p++='T'; /* 4: IDAT chunk name */
1912     out.vi_write(tmp, 8);
1913     if (rlenht!=0) pp->vi_write(img->getRowbeg(), rlenht);
1914     pp->vi_write(0,0); /* flush all, write CRC */
1915     crc=crc32(crc32(CRC32_INITIAL,tmp+4,4), img->getRowbeg(), rlenht);
1916     p=tmp; *p++=crc>>24;*p++=crc>>16;*p++=crc>>8;*p++=crc; /* 0: IDAT CRC */
1917     out.vi_write(tmp, 4);
1918   #else
1919     rp->vi_write("IDAT",4);
1920     if (rlenht!=0) pp->vi_write(img->getRowbeg(), rlenht);
1921     pp->vi_write(0,0); /* flush all, write CRC */
1922   #endif
1923 
1924   out.vi_write("\0\0\0\0IEND\256B`\202",12); /* IEND chunk */
1925   delete pp;
1926   delete cp;
1927   delete rp;
1928 
1929   return Rule::Applier::OK;
1930 }
1931 
1932 Rule::Applier out_png_applier = { "PNG", out_png_check_rule, out_png_work, 0 };
1933 
1934 /* --- Sat Jun 15 19:30:41 CEST 2002 */
1935 
1936 /** BMP RLE compression, type 1. Seems to be optimal.
1937  * @param dst buffer with enough place for compressed data. The caller must
1938  *        pre-allocate >=(send-sbeg)+(send-sbeg+128)*2/255.
1939  * @param sbeg first raw data char to compress
1940  * @param send char to finish compression just before
1941  * @return dst+(number of characters occupied by compressed data)
1942  *
1943  * in-memory implementation Sun Jun 23 20:02:41 CEST 2002
1944  */
1945 
bmp_compress1_row(char * dst,char const * sbeg,char const * send)1946 static char *bmp_compress1_row(char *dst, char const *sbeg, char const *send) {
1947   #if 0
1948   #  define BMP_ASSERT(x) assert(x)
1949   #else
1950   #  define BMP_ASSERT(x)
1951   #endif
1952   #undef  BUF
1953   #define BUF(x) (*(x))
1954   #undef  PUTCH__
1955   #define PUTCH__(c) (*dst++=(c))
1956   char c, c2;
1957   char const *beg, *end, *rend, *q, *r, *best;
1958   signed bestca, rca; /* both must fit into -255..255 */
1959   slen_t frl, efrl;
1960   int ci;
1961   // bool oddp;
1962 
1963   beg=sbeg;
1964   end=(send-sbeg>255) ? beg+255 : send;
1965 
1966   while (beg!=end) { /* there is still unprocessed data in the buffer */
1967     c=BUF(beg++);
1968     if (beg==end) { PUTCH__(1); PUTCH__(c); break; } /* last char */
1969     if (c==BUF(beg)) { /* sure r chunk */
1970       ci=2; beg++;
1971      rep:
1972       while (beg!=end && c==BUF(beg)) { beg++; ci++; }
1973       PUTCH__(ci); PUTCH__(c); /* r chunk */
1974     } else { /* possible c chunk */
1975       rend=end;
1976       BMP_ASSERT(end-beg<=254);
1977       if (end!=send) { /* read an extra char as the terminator of the last run-length of c, buf:0..254 */
1978         end++;
1979         BMP_ASSERT(end-beg==255); /* buffer is full (255 chars) */
1980       }
1981 
1982       best=r=beg;
1983       bestca=rca=-1; /* best and current advantage of c over r */
1984 
1985       while (r!=rend) { /* c chunk should stop only at run-length boundaries */
1986         BMP_ASSERT(-255<=rca && rca<=255);
1987         BMP_ASSERT(-255<=bestca && bestca<=255);
1988         q=r; r=q+1; ci=1; while (r!=end && BUF(r)==BUF(q)) { r++; ci++; }
1989         if (r==end && end!=rend) break;
1990         if (((r-beg)&1)==0) { /* odd (!) copy length */
1991           rca+=3-ci;
1992           if (rca<=bestca-1) { r--; break; } /* fix-5 (instead of rule-4): `xyz|bbbbb|xyz|', `abcdef|gggggggg|abababababab|', `abcdef|ggg|hhh|ggg|hhh|ggg|hhh|ggg|hhh|abababababab|' */
1993           if (bestca<rca) { bestca=rca; best=r-1; } /* make c as short as possible */
1994           rca--;
1995         } else { /* even copy length */
1996           rca+=2-ci;
1997           if (rca<=bestca-2) break; /* fix-5 (instead of rule-4): `xyz|bbbbb|xyz|', `abcdef|gggggggg|abababababab|', `abcdef|ggg|hhh|ggg|hhh|ggg|hhh|ggg|hhh|abababababab|' */
1998           if (bestca<rca) { bestca=rca; best=r; } /* make c as short as possible */
1999         }
2000       }
2001       BMP_ASSERT(-255<=rca && rca<=255);
2002       BMP_ASSERT(-255<=bestca && bestca<=255);
2003       if (bestca<=0 /* no possible positive advantage */
2004        || best-beg<=1  /* 1: c is one char, plus 1 char in buf.  Imp: ==1?! */
2005          ) { ci=1; goto rep; }
2006       r=best; /* Imp: get rid of this assignment */
2007       BMP_ASSERT(beg!=r);
2008       BMP_ASSERT(((r-beg)&1)==1); /* even copy length */
2009 
2010       if (end==r) { /* no followers, last chunk */
2011         /* BMP_ASSERT(had_eof); */
2012         // oddp=(1+(r-beg)&1)==1;
2013         PUTCH__(0);
2014         PUTCH__((r-beg)+1);
2015         PUTCH__(c);
2016         while (beg!=r) { PUTCH__(BUF(beg)); beg++; } /* emit c chunk */
2017         // if (oddp) PUTCH__(0); /* Imp: padding breaks optimality */
2018       } else {
2019         BMP_ASSERT(r!=end);
2020         /* BMP_ASSERT(r!=rend); */ /* r==rend is possible here */
2021         c2=BUF(r); frl=1; q=r+1;
2022         while (q!=end && c2==BUF(q)) { q++; frl++; } /* count follower run length */
2023         efrl=frl; ci=-2; if (q==end) { /* Imp: get rid of -2 (-2 -> -1) */
2024           BMP_ASSERT(q==end);
2025           while ((ci=(q==send)?-1:(unsigned char)*q++)!=-1 && (char)ci==c2) efrl++;
2026         }
2027 
2028         /* printf("clen=%u\n", clen); */
2029         if (1+(r>beg ? r-beg : 256+beg-r)<255 && efrl>=256 && efrl%255==1) { r++; efrl--; } /* make the c chunk one char longer if appropriate */
2030 
2031         // oddp=(1+(r-beg)&1)==1;
2032         PUTCH__(0);
2033         PUTCH__(1+(r-beg));
2034         PUTCH__(c);
2035         while (beg!=r) { PUTCH__(BUF(beg)); beg++; } /* emit c chunk */
2036         // if (oddp) PUTCH__(0); /* Imp: padding breaks optimality */
2037 
2038         beg=q; /* remove beginning of the r chunk from the buffer */
2039         if (ci>=0) { beg--; BMP_ASSERT((unsigned char)BUF(beg)==ci); }
2040 
2041         while (efrl>=255) { PUTCH__('\377'); PUTCH__(c2); efrl-=255; } /* emit full r chunks */
2042         if (efrl>=2) { /* emit last r chunk */
2043           PUTCH__(efrl); PUTCH__(c2);
2044         } else if (efrl!=0) {
2045           BMP_ASSERT(efrl==1);
2046           beg--; /* leave a single instance of c2 in beginning of the buffer */
2047           BMP_ASSERT(BUF(beg)==c2);
2048         }
2049       } /* IF c chunk has followers */
2050     } /* IF r or c chunk */
2051     end=(send-beg>255) ? beg+255 : send;
2052   } /* WHILE main loop */
2053   return dst;
2054   #undef  BUF
2055   #undef  PUTCH__
2056 }
2057 
2058 
2059 /** Windows Bitmap BMP output */
out_bmp_check_rule(Rule::OutputRule * or_)2060 Rule::Applier::cons_t out_bmp_check_rule(Rule::OutputRule* or_) {
2061   /* Supported BMP types: /Rgb8,
2062    *   /Indexed1, /Indexed4, /Indexed8.
2063    */
2064   Rule::Cache *cache=&or_->cache;
2065   if (cache->FileFormat!=cache->FF_BMP
2066      ) return Rule::Applier::DONT_KNOW;
2067   bool badp=false;
2068   /* Dat: (by policy) we don't support /Transparent or /Opaque here; the
2069    * user should not specify such SampleFormat in the .job file.
2070    */
2071   if (cache->SampleFormat!=Image::SF_Indexed1
2072       /* Dat: /Indexed2 is not supported by /BMP */
2073    && cache->SampleFormat!=Image::SF_Indexed4
2074    && cache->SampleFormat!=Image::SF_Indexed8
2075    && cache->SampleFormat!=Image::SF_Rgb8) {
2076     Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Indexed1, /Indexed4, /Indexed8 or /Rgb8" << (Error*)0;
2077     badp=true;
2078   }
2079   if (cache->TransferEncoding!=cache->TE_Binary) {
2080     Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Binary" << (Error*)0;
2081     badp=true;
2082   }
2083   if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_RLE) {
2084     Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Compression/None or /RLE" << (Error*)0;
2085     badp=true;
2086   }
2087   if (cache->Compression==Rule::Cache::CO_RLE && cache->SampleFormat!=Image::SF_Indexed8) {
2088     /* !! Imp: Implement compr==2 for /Indexed4 */
2089     Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP/RLE requires /Indexed8" << (Error*)0;
2090     badp=true;
2091   }
2092   if (cache->hasPredictor()) {
2093     Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Predictor 1" << (Error*)0;
2094     badp=true;
2095   }
2096   if (badp) return Rule::Applier::BAD;
2097   /* Now we are sure about Rule::Applier::OK. */
2098   return Rule::Applier::OK;
2099 }
out_bmp_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)2100 Rule::Applier::cons_t out_bmp_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
2101   if (out_bmp_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
2102   or_->doSampleFormat(sf);
2103   Image::Sampled *img=sf->getImg();
2104   Image::Sampled::dimen_t wd=img->getWd(), ht=img->getHt(), htc=ht;
2105   slen_t rlen=img->getRlen();
2106   char const*pend=img->getRowbeg()+rlen*ht;
2107   char const*palbeg=img->getHeadp(), *palend=img->getRowbeg();
2108   slen_t ncols4=(palend-palbeg)/3*4;
2109   unsigned biCompr=or_->cache.Compression!=or_->cache.CO_RLE ? 0
2110    : or_->cache.SampleFormat==Image::SF_Indexed4 ? 2 : 1;
2111 
2112   SimBuffer::B data;
2113   slen_t crowsize=2+ rlen+(rlen+128)*2/255; /* !! Imp: real upper bound? */
2114   char *crow=new char[crowsize];
2115   /* !! GIMP compatibility */
2116   if (or_->cache.Compression==or_->cache.CO_RLE) {
2117     /* Imp: bmp_compress2_row */
2118     char *crow2;
2119     while (htc--!=0) { /* BMP stores rows from down */
2120       crow2=bmp_compress1_row(crow, pend-rlen, pend);
2121 #if 0
2122       crow2=crow;
2123       *crow2++=10;
2124       *crow2++=1;
2125       *crow2++=10;
2126       *crow2++=2;
2127       *crow2++=10;
2128       *crow2++=3;
2129       *crow2++=10;
2130       *crow2++=4;
2131       *crow2++=10;
2132       *crow2++=5;
2133       *crow2++=10;
2134       *crow2++=6;
2135 #endif
2136 
2137       assert((slen_t)(crow2-crow)<=crowsize-2);
2138       pend-=rlen;
2139       *crow2++='\0'; *crow2++='\0'; /* signal end of compressed data row */
2140       /* fprintf(stderr, "htc=%u cl=%u\n", htc, crow2-crow); */
2141       data.vi_write(crow, crow2-crow);
2142     }
2143     data.vi_write("\0\1", 2); /* signal end of bitmap */
2144   } else {
2145     unsigned pad=(4-(rlen&3))&3; /* Dat: pad rows to 32 bits */
2146     if (or_->cache.SampleFormat==Image::SF_Rgb8) { /* SWAP RGB values */
2147       /* BUGFIX at Thu Dec 12 21:36:57 CET 2002 */
2148       char *buf=new char[rlen];
2149       while (htc--!=0) { /* BMP stores rows from down */
2150         char const*pxend=pend, *px=pend-=rlen;
2151         char *q=buf;
2152         while (px!=pxend) {
2153           *q++=px[2];
2154           *q++=px[1];
2155           *q++=px[3];
2156           px+=3;
2157         }
2158         data.vi_write(buf, rlen);
2159         if (pad!=0) data.vi_write("\0\0\0", pad);
2160       }
2161       delete [] buf;
2162     } else {
2163       while (htc--!=0) { /* BMP stores rows from down */
2164         data.vi_write(pend-=rlen, rlen);
2165         if (pad!=0) data.vi_write("\0\0\0", pad);
2166       }
2167     }
2168   }
2169   assert(pend==img->getRowbeg());
2170   delete [] crow;
2171   /* Now data is ready. */
2172 
2173   char *bmphead=new char[54+ncols4+data.getLength()], *p=bmphead;
2174   *p++='B'; *p++='M'; /* magic number header */
2175   lf32(p, 54+ncols4+data.getLength()); /* bfSize */
2176   lf32(p, 0); /* zzHotX, zzHotY */
2177   lf32(p, 54+ncols4); /* bfOffs */
2178   lf32(p, 40); /* biSize==40 => Windows 3.x style BMP file */
2179   lf32(p, wd); /* biWidth */
2180   lf32(p, ht); /* biHeight */
2181   lf16(p, 1);  /* biPlanes, must be 1 */
2182   lf16(p, img->getBpc()*img->getCpp());  /* biBitCnt: bits per pixel (1, 4, 8: /Indexed*; 24: /Rgb8) */
2183   lf32(p, biCompr); /* biCompr */
2184   lf32(p, 0); /* biSizeIm */
2185   lf32(p, 0); /* biXPels */
2186   lf32(p, 0); /* biYPels */
2187   lf32(p, (palend-palbeg)/3); /* biClrUsed */
2188   lf32(p, (palend-palbeg)/3); /* biClrImp */
2189   /* vvv Now comes the palette (zero length for /Rgb) */
2190   while (palbeg!=palend) {
2191     *p++=palbeg[2];  *p++=palbeg[1];  *p++=palbeg[0];  *p++='\0';
2192     palbeg+=3;
2193   }
2194   assert((slen_t)(p-bmphead)==54+ncols4);
2195 
2196   out.vi_write(bmphead, p-bmphead);  delete [] bmphead;
2197 #if 0
2198   out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
2199     100);
2200   out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
2201     100);
2202   out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
2203     100);
2204   out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
2205     100);
2206   out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
2207     100);
2208 #endif
2209   out << data;
2210   out.vi_write(0,0); /* signal EOF */
2211 
2212   return Rule::Applier::OK;
2213 }
2214 
2215 Rule::Applier out_bmp_applier = { "BMP", out_bmp_check_rule, out_bmp_work, 0 };
2216 
2217 /* --- Sun Mar 24 13:48:57 CET 2002 */
2218 
2219 /** The Empty applier produces an empty output file. */
out_empty_check_rule(Rule::OutputRule * or_)2220 Rule::Applier::cons_t out_empty_check_rule(Rule::OutputRule* or_) {
2221   Rule::Cache *cache=&or_->cache;
2222   if (cache->FileFormat!=cache->FF_Empty
2223      ) return Rule::Applier::DONT_KNOW;
2224   return Rule::Applier::OK;
2225 }
out_empty_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)2226 Rule::Applier::cons_t out_empty_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
2227   (void)out;
2228   (void)sf;
2229   if (out_empty_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
2230   return Rule::Applier::OK;
2231 }
2232 
2233 Rule::Applier out_empty_applier = { "Empty", out_empty_check_rule, out_empty_work, 0 };
2234 
2235 /* --- Sun Mar 24 13:48:57 CET 2002 */
2236 
2237 /** The Meta applier writes some meta-information into the output file */
out_meta_check_rule(Rule::OutputRule * or_)2238 Rule::Applier::cons_t out_meta_check_rule(Rule::OutputRule* or_) {
2239   Rule::Cache *cache=&or_->cache;
2240   if (cache->FileFormat!=cache->FF_Meta
2241      ) return Rule::Applier::DONT_KNOW;
2242   // return Rule::Applier::OK; /* Imp: implement the applier */
2243   return Rule::Applier::BAD;
2244 }
out_meta_work(GenBuffer::Writable & out,Rule::OutputRule * or_,Image::SampledInfo * sf)2245 Rule::Applier::cons_t out_meta_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) {
2246   (void)out;
2247   (void)sf;
2248   if (out_meta_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW;
2249   /* Imp: real content here */
2250   return Rule::Applier::OK;
2251 }
2252 
2253 Rule::Applier out_meta_applier = { "Meta", out_meta_check_rule, out_meta_work, 0 };
2254 
2255 /* __END__ */
2256