diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/sam2p/appliers.cpp |
Initial commit
Diffstat (limited to 'graphics/sam2p/appliers.cpp')
-rw-r--r-- | graphics/sam2p/appliers.cpp | 2255 |
1 files changed, 2255 insertions, 0 deletions
diff --git a/graphics/sam2p/appliers.cpp b/graphics/sam2p/appliers.cpp new file mode 100644 index 0000000000..2614ef4b45 --- /dev/null +++ b/graphics/sam2p/appliers.cpp @@ -0,0 +1,2255 @@ +/* + * appliers.cpp -- specific OutputRule implementations + * by pts@fazekas.hu at Sat Mar 16 14:45:02 CET 2002 + * Blanca JAI additions at Tue May 21 13:18:26 CEST 2002 + * TIFF output at Tue Jun 4 19:50:45 CEST 2002 + */ + +#ifdef __GNUC__ +#ifndef __clang__ +#pragma implementation +#endif +#endif + +#include "rule.hpp" +#include "error.hpp" +#include "encoder.hpp" +#include "in_jai.hpp" +#include "crc32.h" /* crc32() used by out_png_work() */ +#include <string.h> + +/** Appends 4 bytes in MSB first (network) byte order */ +static inline void mf32(char *&p, slen_t u32) { + p[0]=(u32>>24)&255; + p[1]=(u32>>16)&255; + p[2]=(u32>> 8)&255; + p[3]=(u32 )&255; + p+=4; +} +/** Appends 4 bytes in LSB first (PC) byte order */ +static inline void lf32(char *&p, slen_t u32) { + p[0]=(u32 )&255; + p[1]=(u32>> 8)&255; + p[2]=(u32>>16)&255; + p[3]=(u32>>24)&255; + p+=4; +} +/** Appends 2 bytes in LSB first (PC) byte order */ +static inline void lf16(char *&p, unsigned u16) { + p[0]=(u16 )&255; + p[1]=(u16>> 8)&255; + p+=2; +} + + +/* --- at Sat Mar 23 15:42:17 CET 2002, removed obsolete + * PostScript Level2 FlateEncode or LZWEncode filter, predictors supported + * written at Mar 16 14:48:27 CET 2002 + */ + +/* --- Sun Mar 17 15:43:20 CET 2002 */ + +#if 0 +/* l1fa85g.tte was a special .tte that is not built in to bts.ttt, but I've + * integrated it into bts2.ttt at Sun Sep 22 00:48:21 CEST 2002. I've also + * integrated _l1fa85g_ to _l1c_ that day. + */ +static char *l1fa85g_tte= +#include "l1fa85g.tth" + +/** PostScript Level1 FlateEncode, A85, without Predictor */ +Rule::Applier::cons_t out_l1fa85g_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (!cache->isPS() + || cache->Compression!=Rule::Cache::CO_ZIP + || cache->hasPredictor() + || !cache->isGray() + || cache->TransferEncoding!=cache->TE_A85 + || (!cache->WarningOK && !cache->isPSL3()) + ) return Rule::Applier::DONT_KNOW; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_l1fa85g_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + /* by pts@fazekas.hu at Sun Mar 17 15:52:48 CET 2002 */ + // Imp: two other TransferEncodings [no more, since #if 0] + if (out_l1fa85g_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + if (!or_->cache.isPSL3()) + Error::sev(Error::WARNING_DEFER) << "l1fa85g: /ZIP without /PSL3 will be slow" << (Error*)0; + or_->doSampleFormat(sf); + PSEncoder *bp=PSEncoder::newASCII85Encode(out, or_->cacheHints.TransferCPL); + PSEncoder *cp=PSEncoder::newFlateEncode(*bp, or_->cacheHints.Effort); + Rule::writeTTE(out, out, *cp, l1fa85g_tte, or_, sf, Rule::writeData); + delete bp; + delete cp; + return Rule::Applier::OK; +} + +Rule::Applier out_l1fa85g_applier = { "l1fa85g", out_l1fa85g_check_rule, out_l1fa85g_work, 0 }; +#endif + +/* l2jbin --- Sun Mar 17 21:45:22 CET 2002 + * p0jbin (integrated to p0jbin) --- Mon Apr 15 23:29:13 CEST 2002 + */ + +//static char *l2jbin_tte= +//#include "l2jbin.tth" + +/** PostScript Level2 DCTEncode (Baseline JPEG), Binary */ +Rule::Applier::cons_t out_l2jbin_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if ((!cache->isPS() && !cache->isPDF()) + || cache->Compression!=Rule::Cache::CO_JAI + ) return Rule::Applier::DONT_KNOW; + /* Dat: not unrequired anymore: cache->TransferEncoding!=cache->TE_Binary */ + bool badp=false; + if (cache->isPS() && !cache->isPSL2()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PSL* /Compression/JAI requires /PSL2+" << (Error*)0; + badp=true; + } + if (cache->SampleFormat!=Image::SF_Asis) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PSL*|PDF* /Compression/JAI requires /SampleFormat/Asis" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PSL*|PDF* /Compression/JAI requires /Predictor 1" << (Error*)0; + badp=true; + } + return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK); + /* ^^^ 0+: pacify g++-3.1 */ +} +Rule::Applier::cons_t out_l2jbin_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + char const*strings[]={ (char const*)NULLP/*LanguageLevel, PDF-1.`0'*/, ""/*colorSpace*/ }; + //, " F closefile T closefile"/*closes*/ }; + // Error::sev(Error::WARNING_DEFER) << "l2jbin: /ZIP without /PSL3 will be slow" << (Error*)0; + if (out_l2jbin_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + assert(sf->getImg()->getBpc()==8); + or_->doSampleFormat(sf); + // PSEncoder *bp=PSEncoder::newASCII85Encode(out, or_->cacheHints.TransferCPL); + Rule::Cache *cache=&or_->cache; + Filter::VerbatimE outve(out); /* required since template /p0jbin is TTM */ + GenBuffer::Writable *op=cache->isPDF() ? &outve : &out, *tp=op; + strings[0]=const_cast<char*>(cache->isPDF() ? (char const*)"0" : (char const*)"2"); + if (cache->TransferEncoding==cache->TE_A85) tp=PSEncoder::newASCII85Encode (*op, or_->cacheHints.TransferCPL); + else if (cache->TransferEncoding==cache->TE_Hex) tp=PSEncoder::newASCIIHexEncode(*op, or_->cacheHints.TransferCPL); + // else strings[2]=" F closefile"; + Rule::writeTTT(*op, *tp, *tp, !cache->isPDF()?"l2jbin":cache->isPDFB()?"p0jbb":"p0jbin", or_, sf, Rule::writePalData, strings); + if (tp!=op) delete tp; + /* Dat: outve is deleted by C++ scope */ + // if (tp!=op) delete tp; /* BUGFIX at Thu Jan 20 15:04:47 CET 2005 */ + return Rule::Applier::OK; +} + +Rule::Applier out_l2jbin_applier = { "PSL2+PDF-JAI", out_l2jbin_check_rule, out_l2jbin_work, 0 }; + +/* --- */ + +#if 0 /* integrated to _l2jbin_ at Sun Sep 22 14:29:13 CEST 2002 */ +//static char *p0jbin_ttm= +//#include "p0jbin.tth" + +/** PostScript Level2 DCTEncode (Baseline JPEG), Binary */ +Rule::Applier::cons_t out_p0jbin_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (!cache->isPDF() + || cache->Compression!=Rule::Cache::CO_JAI + ) return Rule::Applier::DONT_KNOW; + /* Dat: not unrequired anymore: cache->TransferEncoding!=cache->TE_Binary */ + bool badp=false; + if (cache->SampleFormat!=Image::SF_Asis) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PDF* /Compression/JAI requires /SampleFormat/Asis" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/PDF* /Compression/JAI requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_p0jbin_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + char *strings[]={ "0"/*PDF-1.`0'*/, ""/*colorSpace*/ }; + // " F closefile"/*closes*/ }; + if (out_p0jbin_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + assert(sf->getImg()->getBpc()==8); + or_->doSampleFormat(sf); + Rule::Cache *cache=&or_->cache; + + Filter::VerbatimE outve(out); /* required since template /p0jbin is TTM */ + GenBuffer::Writable *tp=&outve; + if (cache->TransferEncoding==cache->TE_A85) tp=PSEncoder::newASCII85Encode (outve, or_->cacheHints.TransferCPL); + else if (cache->TransferEncoding==cache->TE_Hex) tp=PSEncoder::newASCIIHexEncode(outve, or_->cacheHints.TransferCPL); + else strings[2]=""; + +#if 0 /* old */ + MiniPS::VALUE ttm; + { Filter::FlatR flatD(p0jbin_ttm); + MiniPS::Parser p(&flatD); + ttm=p.parse1(); + if (p.parse1(p.EOF_ALLOWED)!=MiniPS::Qundef || MiniPS::getType(ttm)!=MiniPS::T_ARRAY) + Error::sev(Error::EERROR) << "TTM: the TTM file should contain a single array" << (Error*)0; + /* ^^^ Dat: the result of the second p.parse1() doesn't get delete0(...)d */ + } + Filter::VerbatimE outve(out); + Rule::writeTTM(outve, outve, outve, MiniPS::RARRAY(ttm), or_, sf, Rule::writePalData, strings); + MiniPS::delete0(ttm); +#else /* new */ + Rule::writeTTT(outve, *tp, *tp, cache->isPDFB()?"p0jbb":"p0jbin", or_, sf, Rule::writePalData, strings); +#endif + if (tp!=&outve)delete tp; + return Rule::Applier::OK; +} + +Rule::Applier out_p0jbin_applier = { "PDF-JAI", out_p0jbin_check_rule, out_p0jbin_work, 0 }; +#endif + +/* --- Fri Mar 22 11:52:53 CET 2002 */ +/* PDF added at Sat Apr 20 20:07:34 CEST 2002 */ +/* Integrated l23ind1 at Sat Apr 20 20:24:21 CEST 2002 */ + +//static char *l23_tte= +//#include "l23.tth" + +/** PostScript Level2 and PDF generic non-transparent */ +Rule::Applier::cons_t out_l23_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + unsigned char sf=cache->SampleFormat; + if (cache->hasPredictor() && cache->Compression!=Rule::Cache::CO_ZIP && cache->Compression!=Rule::Cache::CO_LZW) { + Error::sev(Error::WARNING_DEFER) << "check_rule: real /Predictor requires /ZIP or /LZW" << (Error*)0; + return Rule::Applier::BAD; + } + + // assert(cache->isPSL2() && (sf==Image::SF_Transparent2 || sf==Image::SF_Transparent4 || sf==Image::SF_Transparent8)); + + if ((!cache->isPSL2() && !cache->isPDF()) + || (sf!=Image::SF_Gray1 && sf!=Image::SF_Gray2 && sf!=Image::SF_Gray4 && sf!=Image::SF_Gray8 + && sf!=Image::SF_Indexed1 && sf!=Image::SF_Indexed2 && sf!=Image::SF_Indexed4 && sf!=Image::SF_Indexed8 + && sf!=Image::SF_Rgb1 && sf!=Image::SF_Rgb2 && sf!=Image::SF_Rgb4 && sf!=Image::SF_Rgb8 + && sf!=Image::SF_Transparent2 && sf!=Image::SF_Transparent4 && sf!=Image::SF_Transparent8 + && sf!=Image::SF_Mask) + || cache->TransferEncoding==cache->TE_ASCII + || !cache->isZIPOK() + ) return Rule::Applier::DONT_KNOW; + #if !HAVE_LZW + if (cache->Compression==Rule::Cache::CO_LZW) return Rule::Applier::DONT_KNOW; + #endif + + // if (cache->isDCTE() && !cache->isRGB() && !cache->isGray()) { + bool badp=false; + if (cache->isDCTE() && cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Indexed8) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /DCTEncode requires /Rgb8 or /Gray8 (or /Indexed8)" << (Error*)0; + badp=true; + } + if (sf==Image::SF_Transparent2 || sf==Image::SF_Transparent4 || sf==Image::SF_Transparent8) { + if (cache->isPDF()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: unsupported /Transparent+ for /PDF*" << (Error*)0; + badp=true; + } else assert(cache->isPSL2()); + } + return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK); +} +Rule::Applier::cons_t out_l23_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + Rule::stream_writer_t writeXData=Rule::writePalData; + char LanguageLevel[2]="2", closes[30]; + SimBuffer::B colorSpace; + char const*strings[]={ LanguageLevel, (char*)NULLP /*colorSpace*/, closes }; + Rule::Cache *cache=&or_->cache; + // assert(0); + if (out_l23_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf, true); + if (cache->Compression==Rule::Cache::CO_ZIP) { + if (!cache->isPDF()) LanguageLevel[0]='3'; + } else if (cache->isPDF()) LanguageLevel[0]='0'; + if (cache->isIndexed() || cache->isTransparentM()) { + unsigned ncols=PTS_dynamic_cast(Image::Indexed*,sf->getImg())->getNcols(); + colorSpace << "[/Indexed/DeviceRGB " << ncols-1; + if (ncols==0) { /* Avoid writing zero bytes to the filters */ + /* Imp: verify this in Acrobat Reader */ + colorSpace << "()]"; + } else if (!cache->isPDF()) { /* Insert an in-line hexdump. No better way :-( */ + /*sprintf(colorSpace, "[/Indexed/DeviceRGB %u T %u string readstring pop]", ncols-1, ncols*3); */ + colorSpace << " T " << ncols*3 << " string readstring pop]"; + } else { /* Insert an in-line hexdump. No shorter way for PDF */ +#if 0 + colorSpace="/DeviceGray "; +#else + /* SUXX: gs5.50 & PDF & /Indexed doesn't work */ + colorSpace << "\n<"; + GenBuffer::Writable *hp=PSEncoder::newASCIIHexEncode(colorSpace, or_->cacheHints.TransferCPL); + hp->vi_write(sf->getImg()->getHeadp(), ncols*3); + hp->vi_write(0,0); + colorSpace << ']'; /* Dat: '>' is appended by ASCIIHexEncode */ +#endif + writeXData=Rule::writeData; /* The palette has already been written. */ + } + } else if (cache->isGray()) colorSpace="/DeviceGray "; + else { assert(cache->isRGB()); colorSpace="/DeviceRGB "; } + if (cache->SampleFormat==Image::SF_Mask || cache->SampleFormat==Image::SF_Indexed1) writeXData=Rule::writeData; + + GenBuffer::Writable *vp=&out; + if (cache->isPDF()) vp=new Filter::VerbatimE(out); /* required since template /p02* is TTM */ + + GenBuffer::Writable *tp=vp; + if (cache->TransferEncoding==cache->TE_A85) tp=PSEncoder::newASCII85Encode (*vp, or_->cacheHints.TransferCPL); + else if (cache->TransferEncoding==cache->TE_Hex) tp=PSEncoder::newASCIIHexEncode(*vp, or_->cacheHints.TransferCPL); + + GenBuffer::Writable *cp=tp; + switch (cache->Compression) { + case Rule::Cache::CO_None: break; + case Rule::Cache::CO_ZIP: cp=PSEncoder::newFlateEncode(*tp, or_->cacheHints.Effort); break; + case Rule::Cache::CO_LZW: cp=PSEncoder::newLZWEncode(*tp); break; + case Rule::Cache::CO_RLE: cp=PSEncoder::newRunLengthEncode(*tp, or_->cacheHints.RecordSize); break; + case Rule::Cache::CO_Fax: cp=PSEncoder::newCCITTFaxEncode(*tp, or_->cacheHints.K, or_->cacheHints.EncoderBPL, /*EndOfLine:*/ or_->cacheHints.K>0); break; + /* ^^^ getBpp() BUGFIX at Wed Jul 3 20:00:30 CEST 2002 */ + /* ^^^ EndOfLine BUGFIX at Wed Jul 3 21:12:54 CEST 2002 + * With EndOfLine==false, `sam2p -c:fax:1', acroread triggers the bug. + * With EndOfLine==false, `sam2p -c:fax:2', acroread and gs trigger the bug. + */ + + case Rule::Cache::CO_IJG: cp=PSEncoder::newDCTIJGEncode(*tp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.Quality); break; + case Rule::Cache::CO_DCT: { SimBuffer::B other_parameters; + or_->cacheHints.DCT->dump(other_parameters, 0, false); + cp=PSEncoder::newDCTEncode(*tp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.ColorTransform, other_parameters); + break; } + default: assert(0); + } + + GenBuffer::Writable *pp=cp; + if (cache->hasPredictor()) pp=PSEncoder::newPredictor(*cp, cache->Predictor, or_->cacheHints.PredictorBPC, or_->cacheHints.PredictorColumns, or_->cacheHints.PredictorColors); + + #if 0 /* Sun Sep 22 20:40:51 CEST 2002 */ + if (cp!=tp) strcpy(closes," F closefile"); else closes[0]='\0'; + if (tp!=vp) strcpy(closes+strlen(closes)," T closefile"); + #endif + + strings[1]=colorSpace.term0()(); + Rule::writeTTT(*vp, *tp, *pp, + !cache->isPDF() ? ( + cache->SampleFormat==Image::SF_Indexed1 ? "l23ind1" : + cache->SampleFormat==Image::SF_Mask ? "l23mask" : + cache->isTransparentM() ? "l23tran2" : + "l23" + ) : cache->isPDFB() ? ( + cache->SampleFormat==Image::SF_Indexed1 ? "p02ind1bb" : + cache->SampleFormat==Image::SF_Mask ? "p02maskbb" : + "p02bb" + ) : ( + cache->SampleFormat==Image::SF_Indexed1 ? "p02ind1" : + cache->SampleFormat==Image::SF_Mask ? "p02mask" : + "p02" + ), or_, sf, writeXData, strings); + if (pp!=cp) delete pp; + if (cp!=tp) delete cp; + if (tp!=vp) delete tp; + if (vp!=&out)delete vp; + return Rule::Applier::OK; +} + +Rule::Applier out_l23_applier = { "PSL23+PDF", out_l23_check_rule, out_l23_work, 0 }; + +/* --- Fri Mar 22 17:22:40 CET 2002 -- Sat Jun 15 16:03:06 CEST 2002 */ + +/* integrated l1tr at Sat Jun 15 16:03:03 CEST 2002 */ +/* added /Bbox at Sat Jun 15 16:03:31 CEST 2002 */ + +//static char *l1tr_tte= +//#include "l1tr.tth" + +/** PostScript Level1 or PDF Fully transparent image */ +Rule::Applier::cons_t out_l1tr_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (!(cache->isPS() || cache->isPDF()) + || (cache->SampleFormat!=Image::SF_Transparent && cache->SampleFormat!=Image::SF_Bbox && cache->SampleFormat!=Image::SF_Opaque) + ) return Rule::Applier::DONT_KNOW; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_l1tr_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + char t[]="...."; + if (out_l1tr_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf); + Rule::Cache *cache=&or_->cache; + Filter::VerbatimE outve(out); /* required since template /p0* is TTM */ + if (cache->isPS()) { t[0]='l'; t[1]='1'; } + else { t[0]='p'; t[1]='0'; } + switch (cache->SampleFormat) { + case Image::SF_Transparent: t[2]='t'; t[3]='r'; break; + case Image::SF_Opaque: t[2]='o'; t[3]='p'; break; + case Image::SF_Bbox: t[2]='b'; t[3]='b'; break; + default: assert(0); + } + Rule::writeTTT(outve, outve, outve, t, or_, sf, 0/*NULLP*/); + return Rule::Applier::OK; +} + +Rule::Applier out_l1tr_applier = { "P-TrOpBb", out_l1tr_check_rule, out_l1tr_work, 0 }; + +/* --- */ + +/* --- l23mask Fri Mar 22 18:11:12 CET 2002 + * --- l23ind1 Fri Mar 22 18:11:12 CET 2002 + * removed (integrated to l23) at Sat Apr 20 20:25:57 CEST 2002 + */ + +/* --- l1mask Fri Mar 22 18:33:01 CET 2002 + * --- l1mashex Sun Apr 14 15:25:25 CEST 2002 + * --- l1in1 Fri Mar 22 18:33:37 CET 2002 + * --- l1in1hex Fri Mar 22 18:33:37 CET 2002 + * removed (integrated to l1c) at Sat Apr 20 20:25:57 CEST 2002 + * --- lcr Sat Jun 1 17:09:57 CEST 2002 + * removed (integrated to l1c) at Sun Jun 2 16:48:31 CEST 2002 + * --- l1gbh + * removed (integrated to l1c) at Sun Jun 2 16:48:31 CEST 2002 + * --- l1fa85g.tte: PostScript Level1 FlateEncode, A85, without Predictor + * removed (integrated to l1c) at Sun Sep 22 00:48:21 CEST 2002 + */ + +/* --- Sun Jun 2 16:48:44 CEST 2002 */ + +//static char *l1mask_tte= +//#include "l1mask.tth" + +static void gen_tkey(char *tkey, GenBuffer::Writable& out, GenBuffer::Writable*&tp, GenBuffer::Writable*& cp, Rule::OutputRule*or_) { + Rule::Cache *cache=&or_->cache; + tp=&out; + if (cache->TransferEncoding==cache->TE_A85) { tkey[3]='8'; tp=PSEncoder::newASCII85Encode (out, or_->cacheHints.TransferCPL); } + else if (cache->TransferEncoding==cache->TE_Hex) { tkey[3]='h'; tp=PSEncoder::newASCIIHexEncode(out, or_->cacheHints.TransferCPL); } + else tkey[3]='b'; + cp=tp; + if (cache->Compression==Rule::Cache::CO_RLE) { tkey[4]='r'; cp=PSEncoder::newRunLengthEncode(*tp, or_->cacheHints.RecordSize); } + else if (cache->Compression==Rule::Cache::CO_ZIP) { tkey[4]='z'; cp=PSEncoder::newFlateEncode(*tp, or_->cacheHints.Effort); } + else if (cache->Compression==Rule::Cache::CO_LZW) { tkey[4]='l'; cp=PSEncoder::newLZWEncode(*tp); } + else tkey[4]='n'; + /* vvv removed 'm' and '1' at Sun Sep 22 17:53:08 CEST 2002 */ + tkey[2]=// cache->SampleFormat==Image::SF_Mask ? 'm' : + // cache->SampleFormat==Image::SF_Indexed1 ? '1' : + cache->SampleFormat==Image::SF_Indexed2 ? '2' : + cache->SampleFormat==Image::SF_Indexed4 ? '4' : + cache->SampleFormat==Image::SF_Indexed8 ? '8' : + cache->SampleFormat==Image::SF_Transparent2 ? 't' : + cache->SampleFormat==Image::SF_Transparent4 ? 't' : + cache->SampleFormat==Image::SF_Transparent8 ? 't' : + 'g'; /* /Gray*, /Rgb*, /Mask, /Indexed1 */ +} + +/** PostScript Level1 uncompressed binary or hex */ +Rule::Applier::cons_t out_l1c_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if ((cache->FileFormat!=cache->FF_PSL1 && cache->FileFormat!=cache->FF_PSLC && cache->FileFormat!=cache->FF_PSL2) + || (cache->FileFormat==cache->FF_PSL2 && cache->Compression!=Rule::Cache::CO_ZIP) + || (cache->TransferEncoding!=cache->TE_Binary && cache->TransferEncoding!=cache->TE_Hex && cache->TransferEncoding!=cache->TE_A85) + || (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_RLE && cache->Compression!=Rule::Cache::CO_ZIP && cache->Compression!=Rule::Cache::CO_LZW) + || cache->hasPredictor() + || !(cache->isTransparentM() || cache->isIndexed() || cache->isGray() || cache->isRGB()) + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + if (cache->FileFormat==cache->FF_PSL1 && cache->SampleFormat!=Image::SF_Indexed1 && cache->isIndexed()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /SampleFormat/Indexed+ doesn't work with /FileFormat/PSL1 (use /PSLC or /PSL2)" << (Error*)0; + badp=true; + } + if (cache->FileFormat==cache->FF_PSL1 && cache->isRGB()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /SampleFormat/RGB* doesn't work with /FileFormat/PSL1 (use /PSLC or /PSL2)" << (Error*)0; + badp=true; + } + char tkey[]="l1..."; /* /l1{2,4,8,t}{8,h}{r,z,l} */ + GenBuffer::Writable *tp0,*cp0,*out=(GenBuffer::Writable*)NULLP; + gen_tkey(tkey, *out, tp0, cp0, or_); + /* fprintf(stderr,"tkey=%s\n", tkey); */ + if (!badp && Rule::Templates->get(tkey, strlen(tkey))==MiniPS::Qundef) return Rule::Applier::DONT_KNOW; + return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK); + /* ^^^ Dat: 0+: pacify gcc-3.1 */ +} +Rule::Applier::cons_t out_l1c_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + if (out_l1c_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + /* Dat: only these have been defined so far: grep '^/l1[248tg][8h][rzl]' bts2.ttt + * /l1thr /l1g8r /l1ghr /l128r /l12hr /l148r /l14hr /l188r /l18hr /l1g8z /l1ghz /l1g8l /l1ghl + */ + or_->doSampleFormat(sf, true); /* Dat: `true': because of /Transparent+ */ + char tkey[]="l1..."; /* /l1{2,4,8,t}{8,h}{r,z,l} */ + GenBuffer::Writable *tp, *cp; + gen_tkey(tkey, out, tp, cp, or_); + // fprintf(stderr, "tkey=(%s)\n", tkey); + Rule::writeTTT(out, *tp, *cp, tkey, or_, sf, + tkey[2]=='2' || tkey[2]=='4' || tkey[2]=='8' || tkey[2]=='t' ? Rule::writePalData : Rule::writeData + ); + // Rule::writeTTT(out, *tp, *cp, tkey, or_, sf, Rule::writePalData); + if (cp!=tp) delete cp; + if (tp!=&out)delete tp; + return Rule::Applier::OK; +} + +Rule::Applier out_l1c_applier = { "PSL1C", out_l1c_check_rule, out_l1c_work, 0 }; + +/* lcrbin (lcrb) --- Sun Apr 14 16:50:14 CEST 2002 + * lcrhex (lcr8, lcrh) --- Sun Apr 14 16:50:22 CEST 2002 + * removed (integrated to _l1c_) at Sat Jun 1 17:09:52 CEST 2002 + */ + +#if 0 +/* --- Sat Jun 1 17:09:57 CEST 2002 */ + +//static char *lcrbin_tte= +//#include "lcrbin.tth" + +/** PostScript Level1+C uncompressed RGB image */ +Rule::Applier::cons_t out_lcr_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (!cache->isPS() + || cache->FileFormat==cache->FF_PSL1 + || (cache->TransferEncoding!=cache->TE_Binary && cache->TransferEncoding!=cache->TE_Hex && cache->TransferEncoding!=cache->TE_A85) + || cache->Compression!=Rule::Cache::CO_None + || cache->hasPredictor() + || !cache->isRGB() + ) return Rule::Applier::DONT_KNOW; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_lcr_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + Rule::Cache *cache=&or_->cache; + if (out_lcr_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf); + char tkey[]="lcr."; + GenBuffer::Writable *tp=&out; + if (cache->TransferEncoding==cache->TE_A85) { tkey[3]='8'; tp=PSEncoder::newASCII85Encode (out, or_->cacheHints.TransferCPL); } + else if (cache->TransferEncoding==cache->TE_Hex) { tkey[3]='h'; tp=PSEncoder::newASCIIHexEncode(out, or_->cacheHints.TransferCPL); } + else tkey[3]='b'; + Rule::writeTTT(out, *tp, *tp, tkey, or_, sf, Rule::writeData); + if (tp!=&out)delete tp; + return Rule::Applier::OK; +} + +Rule::Applier out_lcr_applier = { "PSLC-RGB", out_lcr_check_rule, out_lcr_work, 0 }; +#endif + +/* --- Sat Mar 23 12:37:04 CET 2002; Tue Jul 2 10:23:44 CEST 2002 */ + +Rule::Applier::cons_t out_gif89a_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + bool badp=false; + if (cache->FileFormat!=cache->FF_GIF89a + ) return Rule::Applier::DONT_KNOW; + if (!cache->isIndexed() && !cache->isTransparentM()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a must be /Indexed*, /Mask or /Transparent+" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_Binary) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a requires /Binary" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_LZW) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a requires /LZW" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /GIF89a requires /Predictor 1" << (Error*)0; + badp=true; + } + #if !USE_OUT_GIF + Error::sev(Error::WARNING_DEFER) << "check_rule: please `configure --enable-gif' for /GIF89a" << (Error*)0; + badp=true; + #endif + if (badp) return Rule::Applier::BAD; + or_->cache.WarningOK=true; + return Rule::Applier::OK; +} +#if USE_OUT_GIF + #if OBJDEP + # warning REQUIRES: out_gif.o + #endif + extern void out_gif_write(GenBuffer::Writable& out, Image::Indexed *img); /* out_gif.cpp */ + Rule::Applier::cons_t out_gif89a_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + if (out_gif89a_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->cache.SampleFormat=(Image::sf_t)(or_->cache.isIndexed()? 0+Image::SF_Indexed8: 0+Image::SF_Transparent8); + /* ^^^ Dat: 0+: pacify gcc-3.1; (Image::sf_t): pacify VC6.0 */ + or_->doSampleFormat(sf); + out_gif_write(out, PTS_dynamic_cast(Image::Indexed*,sf->getImg())); + return Rule::Applier::OK; + } +#else +/*# define out_gif89a_check_rule (Rule::Applier::check_rule_t)NULLP*/ +# define out_gif89a_work (Rule::Applier::work_t)0 +#endif + +Rule::Applier out_gif89a_applier = { +#if HAVE_LZW + "GIF89a+LZW" +#else + "GIF89a" +#endif + , out_gif89a_check_rule, out_gif89a_work, 0 }; + +/* --- Tue Jun 4 19:51:03 CEST 2002 */ + +Rule::Applier::cons_t out_xpm_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + bool badp=false; + if (cache->FileFormat!=cache->FF_XPM + ) return Rule::Applier::DONT_KNOW; + if (!cache->isIndexed() && !cache->isTransparentM()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM must be /Indexed*, /Mask or /Transparent+" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_ASCII && cache->TransferEncoding!=cache->TE_Binary) { + /* ^^^ && BUGFIX at Thu Jul 11 21:57:09 CEST 2002 */ + Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM requires /TransferEncoding/ASCII" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM requires /Compression/None" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XPM requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + or_->cache.WarningOK=true; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_xpm_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + if (out_xpm_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->cache.SampleFormat=(Image::sf_t)(or_->cache.isIndexed()? 0+Image::SF_Indexed8: 0+Image::SF_Transparent8); + /* ^^^ force 8-bit; may trigger warnings... */ + /* ^^^ Dat: 0+: pacify gcc-3.1 */ + or_->doSampleFormat(sf); + Image::Indexed *iimg=PTS_dynamic_cast(Image::Indexed*,sf->getImg()); + /* vvv 93 useful ASCII chars (+ '\0'), missing: " and \ ; plus a hextable */ + static char const xpmc=93; + static char const xpms[xpmc+1]="0123456789abcdef !#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`ghijklmnopqrstuvwxyz{|}~"; + + Image::Sampled::dimen_t wd=iimg->getWd(), htc=iimg->getHt(); + bool pal2ch=iimg->getNcols()>xpmc; + // pal2ch=true; + out << "/* XPM */\nstatic char *sam2p_xpm[] = {\n" + "/* columns rows colors chars-per-pixel */\n\"" + << wd << ' ' << htc << ' ' << iimg->getNcols() + << ' ' << (pal2ch?2:1) << "\",\n"; + char const *p=iimg->getHeadp(), *pend=iimg->getRowbeg(), *phend; + char coline3[]="\".. c #ABCDEF\",\n"; + char cotran3[]="\".. c None s None \",\n"; + assert((pend-p)%3==0); + unsigned i=0; + bool transp=false; + if (iimg->getTransp()>=0) { pend-=3; assert((iimg->getTransp())*3+p==pend); transp=true; } + if (pal2ch) { + while (p!=pend) { + coline3[ 1]=xpms[i>>2]; + coline3[ 2]=xpms[i++&3]; + coline3[ 7]=xpms[*(unsigned char const*)p>>4]; + coline3[ 8]=xpms[*(unsigned char const*)p++&15]; + coline3[ 9]=xpms[*(unsigned char const*)p>>4]; + coline3[10]=xpms[*(unsigned char const*)p++&15]; + coline3[11]=xpms[*(unsigned char const*)p>>4]; + coline3[12]=xpms[*(unsigned char const*)p++&15]; + out << coline3; + } + if (transp) { + cotran3[ 1]=xpms[i>>2]; + cotran3[ 2]=xpms[i++&3]; + out << cotran3; + p+=3; + } + pend=iimg->getRowbeg()+wd*htc; + out << "/* Pixels */\n"; + char *obuf=new char[4+2*wd], *op; + obuf[0]='"'; + obuf[2*wd+1]='"'; + obuf[2*wd+2]=','; /* Dat: it's OK to have a comma in the last line */ + obuf[2*wd+3]='\n'; + while (htc--!=0) { + phend=p+wd; + op=obuf; + while (p!=phend) { *++op=xpms[*p>>2]; *++op=xpms[*p++&3]; } + out.vi_write(obuf, 2*wd+4); + } + delete [] obuf; + } else { + coline3[1]='"'; + while (p!=pend) { + coline3[ 2]=xpms[i++]; + coline3[ 7]=xpms[*(unsigned char const*)p>>4]; + coline3[ 8]=xpms[*(unsigned char const*)p++&15]; + coline3[ 9]=xpms[*(unsigned char const*)p>>4]; + coline3[10]=xpms[*(unsigned char const*)p++&15]; + coline3[11]=xpms[*(unsigned char const*)p>>4]; + coline3[12]=xpms[*(unsigned char const*)p++&15]; + out << (coline3+1); + } + if (transp) { + cotran3[1]='"'; + cotran3[ 2]=xpms[i]; + out << (cotran3+1); + p+=3; + } + pend=iimg->getRowbeg()+wd*htc; + out << "/* Pixels */\n"; + char *obuf=new char[4+wd], *op; + obuf[0]='"'; + obuf[wd+1]='"'; + obuf[wd+2]=','; /* Dat: it's OK to have a comma in the last line */ + obuf[wd+3]='\n'; + while (htc--!=0) { + phend=p+wd; + op=obuf; + while (p!=phend) *++op=xpms[0U+*p++]; + out.vi_write(obuf, wd+4); + } + delete [] obuf; + } + assert(p==pend); + out << "};\n"; + return Rule::Applier::OK; +} + +Rule::Applier out_xpm_applier = { "XPM", out_xpm_check_rule, out_xpm_work, 0 }; + +/* --- Sat Mar 23 13:18:07 CET 2002 */ + +Rule::Applier::cons_t out_pnm_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_PNM + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + // if (cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Gray1) { + if (!cache->isGray() && !cache->isRGB() && !cache->isIndexed() && !cache->isTransparentM()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM must be /Rgb8, /Gray8 or /Gray1" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_Binary && cache->TransferEncoding!=cache->TE_ASCII) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM requires /Binary or /ASCII" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM requires /Compression/None" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNM requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + or_->cache.WarningOK=true; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_pnm_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + /*static*/ char head[]={ 'P', 0, '\n', '#', ' ', 'b', 'y', ' ' }; + /*static*/ char tmp[72]; + Image::sf_t sfo=or_->cache.SampleFormat; + Image::Indexed *alphaChannel=(Image::Indexed*)NULLP; + if (out_pnm_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + if (or_->cache.SampleFormat==Image::SF_Gray1 || or_->cache.SampleFormat==Image::SF_Gray8) { + if (or_->cache.TransferEncoding==or_->cache.TE_ASCII) or_->cache.SampleFormat=Image::SF_Gray8; + } else if (or_->cache.SampleFormat==Image::SF_Gray2 || or_->cache.SampleFormat==Image::SF_Gray4) { + or_->cache.SampleFormat=Image::SF_Gray8; + } else if (or_->cache.isRGB()) { + or_->cache.SampleFormat=Image::SF_Rgb8; + } else { + assert(or_->cache.isIndexed() || or_->cache.isTransparentM()); + if (or_->cache.isTransparentM()) { + alphaChannel=PTS_dynamic_cast(Image::Indexed*,sf->getImg())->calcAlpha(); + sf->clearTransp(); /* BUGFIX at Tue Sep 17 10:05:47 CEST 2002 */ + } + or_->cache.SampleFormat=(Image::sf_t)( + !sf->canGrayy() ? 0+Image::SF_Rgb8 + : sf->minRGBBpcc()==1 ? 0+Image::SF_Gray1 : 0+Image::SF_Gray8); + /* ^^^ Dat: 0+: pacify gcc-3.1 */ + } + if (or_->cache.SampleFormat==Image::SF_Gray1 && or_->cache.TransferEncoding==or_->cache.TE_ASCII) + or_->cache.SampleFormat=Image::SF_Gray8; + or_->doSampleFormat(sf); + sfo=or_->cache.SampleFormat; + head[1]=(sfo==Image::SF_Rgb8 ? '3' : sfo==Image::SF_Gray8 ? '2' : '1') + +(or_->cache.TransferEncoding==or_->cache.TE_Binary)*3; + out.vi_write(head, sizeof(head)); + out << Error::banner0; + Image::Sampled *img=sf->getImg(); + out << '\n' << img->getWd() << ' ' << img->getHt(); + out << &(" 255\n"[sfo==Image::SF_Gray1?4:0]); + /* ^^^ SF_Gray1 BUGFIX at Tue Jun 4 21:44:17 CEST 2002 */ + register char *p=img->getRowbeg(), *t=(char*)NULLP; + slen_t len=img->getRlen()*img->getHt(); + // fprintf(stderr, "len=%u\n", len); + register unsigned smallen, i; + switch (head[1]) { + case '1': /* PBM ASCII */ + while (len>=70) { + smallen=70; t=tmp; while (smallen--!=0) *t++=(*p++==0)?'1':'0'; + /* ^^^ note the swapping of '0' and '1' above */ + *t++='\n'; out.vi_write(tmp, 71); + len-=70; + } + while (len--!=0) *t++=(*p++==0)?'0':'1'; + /* Dat: xv requires a whitespace just before EOF */ + *t++='\n'; out.vi_write(tmp, t-tmp); + break; + case '2': case '3': /* PGM ASCII, PPM ASCII */ + while (len!=0) { + if (len>17) { smallen=17; len-=17; } else { smallen=len; len=0; } + t=tmp; while (smallen--!=0) { + if ((i=*(unsigned char*)p++)<10) *t++=i+'0'; + else if (i<100) { *t++=i/10+'0'; *t++=i%10+'0'; } + else if (i<200) { *t++='1'; *t++=(i-100)/10+'0'; *t++=i%10+'0'; } + else { *t++='2'; *t++=(i-200)/10+'0'; *t++=i%10+'0'; } + *t++=' '; + } + /* Dat: xv requires a whitespace just before EOF */ + t[-1]='\n'; out.vi_write(tmp, t-tmp/*-(len==0)*/); + } + break; + case '4': /* PBM RAWBITS */ + /* Invert the image */ + while (len--!=0) *p++^=-1; + p=img->getRowbeg(); + len=img->getRlen()*img->getHt(); + /* fall through */ + default: /* PBM RAWBITS, PGM RAWBITS, PPM RAWBITS */ + /* fwrite(p, 1, len, stdout); */ + out.vi_write(p, len); + } + if (alphaChannel!=NULLP) { + /* OK: don't always output rawbits PBM file */ + assert(alphaChannel->getBpc()==1); + assert(alphaChannel->getWd()==img->getWd()); + assert(alphaChannel->getHt()==img->getHt()); + /* write PBM RAWBITS subfile (alpha channel) */ + if (or_->cache.TransferEncoding==or_->cache.TE_Binary) { + out << "P4 " << img->getWd() << ' ' << img->getHt() << '\n'; + out.vi_write(alphaChannel->getRowbeg(), alphaChannel->getRlen()*alphaChannel->getHt()); + /* ^^^ BUGFIX at Tue Sep 17 10:18:28 CEST 2002 */ + } else { + out << "P1 " << img->getWd() << ' ' << img->getHt() << '\n'; + alphaChannel->to8(); + p=alphaChannel->getRowbeg(); len=alphaChannel->getRlen()*alphaChannel->getHt(); + while (len>=70) { + smallen=70; t=tmp; while (smallen--!=0) *t++=(*p++!=0)?'1':'0'; + /* ^^^ note the non-swapping of '0' and '1' above */ + *t++='\n'; out.vi_write(tmp, 71); + len-=70; + } + while (len--!=0) *t++=(*p++!=0)?'0':'1'; + /* Dat: xv requires a whitespace just before EOF */ + *t++='\n'; out.vi_write(tmp, t-tmp); + } + delete alphaChannel; + } + return Rule::Applier::OK; +} + +Rule::Applier out_pnm_applier = { "PNM", out_pnm_check_rule, out_pnm_work, 0 }; + +/* --- Sat Aug 10 22:18:33 CEST 2002 */ + +Rule::Applier::cons_t out_xwd_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_XWD + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + if (cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Indexed8) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD must be /Rgb8, /Gray8 or /Indexed8" << (Error*)0; + return Rule::Applier::BAD; + } + if (cache->TransferEncoding!=cache->TE_Binary) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD requires /Binary" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD requires /Compression/None" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /XWD requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_xwd_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + static const unsigned + XWD_FILE_VERSION=7, + ZPixmap=2, + MSBFirst=1, + DirectColor=5, + PseudoColor=3, + //GrayScale=1, + StaticGray=0; + Image::sf_t sfo=or_->cache.SampleFormat; + if (out_xwd_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf); + sfo=or_->cache.SampleFormat; + char head[101], *p=head; + Image::Sampled *img=sf->getImg(); + slen_t bits_per_pixel, bitmap_pad, bytes_per_line; + unsigned ncolors; + + memset(head, '\0', 101); + /*header_size*/ mf32(p,101); + /*file_version*/ (p+=4)[-1]=XWD_FILE_VERSION; + /*pixmap_format*/ (p+=4)[-1]=ZPixmap; + /*pixmap_depth*/ (p+=4)[-1]=sfo==Image::SF_Rgb8 ? 24 : 8; + /*pixmap_width */ mf32(p, img->getWd()); + /*pixmap_height*/ mf32(p, img->getHt()); + /*xoffset*/ p+=4; + /*byte_order*/ (p+=4)[-1]=MSBFirst; + /*bitmap_unit*/ (p+=4)[-1]=8; // sfo==Image::SF_Rgb8 ? 32 : 8; + /*bitmap_bit_order*/ (p+=4)[-1]=MSBFirst; + /*bitmap_pad*/ bitmap_pad=(p+=4)[-1]=8; // sfo==Image::SF_Rgb8 ? 32 : 8; + /* ^^^ force no padding at all */ + /*bits_per_pixel*/ bits_per_pixel=(p+=4)[-1]=sfo==Image::SF_Rgb8 ? 24 : 8; + /*bytes_per_line*/ mf32(p, bytes_per_line=((bits_per_pixel*img->getWd()+bitmap_pad-1)&~(bitmap_pad-1))>>3); + /*visual_class*/ (p+=4)[-1]=sfo==Image::SF_Rgb8 ? DirectColor : sfo==Image::SF_Indexed8 ? PseudoColor : StaticGray; + /*red_mask */ (p+=4)[-3]=(char)(sfo==Image::SF_Rgb8 ? 255 : 0); + /*green_mask*/ (p+=4)[-2]=(char)(sfo==Image::SF_Rgb8 ? 255 : 0); + /*blue_mask */ (p+=4)[-1]=(char)(sfo==Image::SF_Rgb8 ? 255 : 0); + /*bits_per_rgb*/ (p+=4)[-1]=sfo==Image::SF_Rgb8 ? 24 : 8; + /*colormap_entries*/ (p+=4)[-2]=1; /*256*/ + /*ncolors*/ mf32(p, ncolors=sfo==Image::SF_Rgb8 ? 0 : sfo==Image::SF_Indexed8 ? ((Image::Indexed*)img)->getNcols() : 256); + /*window_width */ mf32(p, img->getWd()); + /*window_height*/ mf32(p, img->getHt()); + assert(p+13==head+101); + /*window_x*/ /*0*/ + /*window_y*/ /*0*/ + /*window_bdrwidth*/ /*0*/ + /*filename*/ /*""*/ + out.vi_write(head, 101); + + if (sfo!=Image::SF_Rgb8) { + char *pal=new char[ncolors*12], *pp=pal; + unsigned pixel; + if (sfo==Image::SF_Indexed8) { + char const* q=img->getHeadp(); + for (pixel=0;pixel<ncolors;pixel++) { + *pp++=0; *pp++=0; *pp++=0; *pp++=pixel; + *pp++=*q; *pp++=*q++; /* red */ + *pp++=*q; *pp++=*q++; /* green */ + *pp++=*q; *pp++=*q++; /* blue */ + *pp++=7; + *pp++=0; + } + } else { assert(sfo==Image::SF_Gray8); + for (pixel=0;pixel<ncolors;pixel++) { + *pp++=0; *pp++=0; *pp++=0; *pp++=pixel; + *pp++=pixel; *pp++=pixel; /* red */ + *pp++=pixel; *pp++=pixel; /* green */ + *pp++=pixel; *pp++=pixel; /* blue */ + *pp++=7; + *pp++=0; + } + } + out.vi_write(pal, ncolors*12); + delete [] pal; + } + + slen_t rlen=img->getRlen(); + Image::Sampled::dimen_t htc=img->getHt(); + char const* rp=img->getRowbeg(); + unsigned scanline_pad=bytes_per_line-rlen; + // assert(*rp=='\xff'); + if (scanline_pad!=0) { + assert(1<=scanline_pad && scanline_pad<=3); + while (htc--!=0) { + out.vi_write(rp, rlen); + rp+=rlen; + if (scanline_pad!=0) out.vi_write("\0\0", scanline_pad); + } + } else out.vi_write(rp, rlen*htc); + + return Rule::Applier::OK; +} + +Rule::Applier out_xwd_applier = { "XWD", out_xwd_check_rule, out_xwd_work, 0 }; + +Rule::Applier out_x11_applier = { "X11", 0/*out_x11_check_rule*/, 0/*out_x11_work*/, 0 }; + +/* --- Sat Apr 20 11:49:56 CEST 2002 */ + +/** Baseline (lossy) JPEG */ +Rule::Applier::cons_t out_jpeg_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_JPEG + || cache->Compression==Rule::Cache::CO_JAI + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + if (cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /DCTEncode requires /Rgb8 or /Gray8" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_Binary) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /JPEG requires /Binary" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_DCT && cache->Compression!=Rule::Cache::CO_IJG) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /JPEG requires /DCT or /IJG" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /JPEG requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + or_->cache.WarningOK=true; /* ?? */ + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_jpeg_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + Rule::Cache *cache=&or_->cache; + // assert(0); + if (out_jpeg_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf); + // GenBuffer::Writable *tp=&out; /* always binary */ + GenBuffer::Writable *cp=&out; + /* SUXX: cjpeg(1) won't create a color JPEG for a grayscale image */ + if (cache->Compression==Rule::Cache::CO_DCT) { + SimBuffer::B other_parameters; + or_->cacheHints.DCT->dump(other_parameters, 0, false); + cp=PSEncoder::newDCTEncode(out, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.ColorTransform, other_parameters); + } else { + assert(cache->Compression==Rule::Cache::CO_None || cache->Compression==Rule::Cache::CO_IJG); + cp=PSEncoder::newDCTIJGEncode(out, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.Quality); + } + Rule::writePalData(out, *cp, sf); + delete cp; + return Rule::Applier::OK; +} + +Rule::Applier out_jpeg_applier = { "JPEG", out_jpeg_check_rule, out_jpeg_work, 0 }; + +/* --- Wed Apr 17 13:32:46 CEST 2002 */ + +Rule::Applier::cons_t out_jpegjai_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_JPEG + || cache->Compression!=Rule::Cache::CO_JAI + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + if (cache->SampleFormat!=Image::SF_Asis) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/JPEG /Compression/JAI requires /SampleFormat/Asis" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_Binary) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/JPEG /Compression/JAI requires /TransferEncoding/Binary" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/JPEG /Compression/JAI requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_jpegjai_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + /* This is the second simplest Applier I've ever written. */ + if (out_jpegjai_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + Image::Sampled *img=sf->getImg(); + // out.vi_write(img->getHeadp(), img->end_()-img->getHeadp()); + /* ^^^ end_() BUGFIX by pts@fazekas.hu at Sun Jun 2 22:24:32 CEST 2002 */ + /* ^^^ end_() contains 8 extra bytes */ + out.vi_write(img->getHeadp(), img->getRowbeg()-img->getHeadp()); + return Rule::Applier::OK; +} + +Rule::Applier out_jpegjai_applier = { "JPEG-JAI", out_jpegjai_check_rule, out_jpegjai_work, 0 }; + +/* --- Sun Jun 2 22:25:16 CEST 2002 */ + +class TIFFPrinter { + GenBuffer::Writable& out; + /** true iff little endian == LSB first */ + bool le; + SimBuffer::B s, dir; + public: + TIFFPrinter(GenBuffer::Writable &out_, bool le_); + inline SimBuffer::B& getS() { return s; } + inline SimBuffer::B const& getDir() const { return dir; } + void aSHORT(SimBuffer::B &s, unsigned count, unsigned short const*val); + void aLONG (SimBuffer::B &s, unsigned count, slen_t const*val); + void dirSHORT(unsigned short tag, slen_t count, unsigned short const*val); + void dirLONG(unsigned short tag, slen_t count, slen_t const*val); + void dirSL(unsigned short tag, slen_t val); + void dirLONG(unsigned short tag, slen_t val); + void dirRATIONAL(unsigned short tag, slen_t count, slen_t const*val); + void dirUNDEFINED(unsigned short tag, slen_t count, char const*val); + void dirUNDEFINED(unsigned short tag, slen_t count, char const*val, slen_t count2, char const*val2); + void dirClose(); + BEGIN_STATIC_ENUM1(unsigned short) + EXTRASAMPLE_ASSOCALPHA=1, /* !associated alpha data */ + EXTRASAMPLE_UNASSALPHA=2 /* !unassociated alpha data */ + END_STATIC_ENUM() + BEGIN_STATIC_ENUM1(unsigned short) + COMPRESSION_NONE =1, /* dump mode */ + COMPRESSION_CCITTRLE =2, /* CCITT modified Huffman RLE; unused in sam2p */ + COMPRESSION_CCITTFAX3=3, /* CCITT Group 3 fax encoding */ + COMPRESSION_CCITTFAX4=4, /* CCITT Group 4 fax encoding */ + COMPRESSION_LZW =5, /* Lempel-Ziv & Welch */ + COMPRESSION_OJPEG =6, /* !6.0 JPEG; obsolete, unused in sam2p */ + COMPRESSION_JPEG =7, /* %JPEG DCT compression */ + COMPRESSION_CCITTRLEW=32771,/* #1 w/ word alignment; unused in sam2p */ + COMPRESSION_PACKBITS =32773,/* Macintosh RLE */ + COMPRESSION_DEFLATE =32946 /* Deflate compression */ + END_STATIC_ENUM() + BEGIN_STATIC_ENUM1(unsigned short) + PHOTOMETRIC_MINISBLACK=1, + PHOTOMETRIC_RGB=2, + PHOTOMETRIC_PALETTE=3, + PHOTOMETRIC_MASK=4, + PHOTOMETRIC_SEPARATED=5, /* possibly CMYK */ + PHOTOMETRIC_YCBCR=6 + END_STATIC_ENUM() + BEGIN_STATIC_ENUM1(unsigned short) + GROUP3OPT_2DENCODING=0x1, + GROUP3OPT_UNCOMPRESSED=0x2, + GROUP3OPT_FILLBITS=0x4, + GROUP4OPT_UNCOMPRESSED=0x2 + END_STATIC_ENUM() + BEGIN_STATIC_ENUM1(unsigned short) + ImageWidth=256, + ImageLength=257, + BitsPerSample=258, + Compression=259, + Photometric=262, + FillOrder=266, + StripOffsets=273, + SamplesPerPixel=277, + RowsPerStrip=278, + StripByteCounts=279, + XResolution=282, + YResolution=283, + PlanarConfig=284, + Group3Options=292, + Group4Options=293, + ResolutionUnit=296, + Predictor=317, + ColorMap=320, + InkSet=332, + ExtraSamples=338, + JPEGTables=347, + YCbCrSubsampling=530, + ReferenceBlackWhite=532 + END_STATIC_ENUM() +}; + +TIFFPrinter::TIFFPrinter(GenBuffer::Writable &out_, bool le_) :out(out_), le(le_) { + /* Directory now at unknown offset (will be set by .dirClose()) */ + s.vi_write(le_ ? "II*\0\0\0\0\0" : "MM\0*\0\0\0\0", 8); + dir.vi_write("\0",2); /* initial number of directory entries */ +} + +void TIFFPrinter::aSHORT(SimBuffer::B &s, unsigned count, unsigned short const*val) { + if (le) while (count--!=0) { s << (char)(*val) << (char)(*val>>8); val++; } + else while (count--!=0) { s << (char)(*val>>8) << (char)(*val); val++; } +} +void TIFFPrinter::aLONG (SimBuffer::B &s, unsigned count, slen_t const*val) { + /* Imp: is vi_write(..., 4); faster ? */ + if (le) while (count--!=0) { s << (char)(*val) << (char)(*val>>8) << (char)(*val>>16) << (char)(*val>>24); val++; } + else while (count--!=0) { s << (char)(*val>>24) << (char)(*val>>16) << (char)(*val>>8) << (char)(*val); val++; } +} + +void TIFFPrinter::dirSHORT(unsigned short const tag, slen_t const count, unsigned short const*val) { + slen_t offs; + aSHORT(dir, 1U, &tag); + dir.vi_write(&("\0\3"[le?1:0]), 2); + aLONG(dir, 1U, &count); + switch (count) { + case 0: dir.vi_write("\0\0\0", 4); break; + case 1: aSHORT(dir, 1, val); dir.vi_write("\0", 2); break; + case 2: aSHORT(dir, 2, val); break; + default:offs=s.getLength(); aSHORT(s, count, val); aLONG(dir, 1, &offs); + } +} +void TIFFPrinter::dirLONG(unsigned short const tag, slen_t const count, slen_t const*val) { + slen_t offs; + aSHORT(dir, 1U, &tag); + dir.vi_write(&("\0\4"[le?1:0]), 2); + aLONG(dir, 1U, &count); + switch (count) { + case 0: dir.vi_write("\0\0\0", 4); break; + case 1: aLONG(dir, 1, val); break; + default:offs=s.getLength(); aLONG(s, count, val); aLONG(dir, 1, &offs); + } +} +void TIFFPrinter::dirRATIONAL(unsigned short tag, slen_t count, slen_t const*val) { + slen_t offs; + aSHORT(dir, 1U, &tag); + dir.vi_write(&("\0\5"[le?1:0]), 2); + aLONG(dir, 1U, &count); + switch (count) { + case 0: dir.vi_write("\0\0\0", 4); break; + default:offs=s.getLength(); aLONG(s, count*2, val); aLONG(dir, 1, &offs); + } +} +void TIFFPrinter::dirUNDEFINED(unsigned short tag, slen_t count, char const*val) { + slen_t offs; + aSHORT(dir, 1U, &tag); + dir.vi_write(&("\0\7"[le?1:0]), 2); + aLONG(dir, 1U, &count); + if (count<=4) { + dir.vi_write(val, count); + if (count!=4) dir.vi_write("\0\0\0", 4-count); + } else { + offs=s.getLength(); s.vi_write(val, count); aLONG(dir, 1, &offs); + } +} +void TIFFPrinter::dirUNDEFINED(unsigned short tag, slen_t count, char const*val, slen_t count2, char const*val2) { + slen_t offs, countx=count+count2; + aSHORT(dir, 1U, &tag); + dir.vi_write(&("\0\7"[le?1:0]), 2); + aLONG(dir, 1U, &countx); + if (countx<=4) { + dir.vi_write(val, count); + dir.vi_write(val2, count2); + if (countx!=4) dir.vi_write("\0\0\0", 4-countx); + } else { + offs=s.getLength(); + s.vi_write(val, count); + s.vi_write(val2, count2); + aLONG(dir, 1, &offs); + } +} +void TIFFPrinter::dirSL(unsigned short tag, slen_t val) { + unsigned short sh; + if (val<1<<16U) { sh=val; dirSHORT(tag, 1, &sh); } + else dirLONG(tag, 1, &val); +} +void TIFFPrinter::dirLONG(unsigned short tag, slen_t val) { + dirLONG(tag, 1, &val); +} +void TIFFPrinter::dirClose() { + char *ss=s.begin_(); + slen_t len=s.getLength(); + if (le) { ss[4]=len; ss[5]=len>>8; ss[6]=len>>16; ss[7]=len>>24; } + else { ss[4]=len>>24; ss[5]=len>>16; ss[6]=len>>8; ss[7]=len; } + /* ^^^ dir offset: end of file */ + out.vi_write(ss, len); + assert(dir.getLength()%12==2); + unsigned dirc=(dir.getLength()-2)/12; + ss=dir.begin_(); + if (le) { ss[0]=dirc; ss[1]=dirc>>8; } + else { ss[0]=dirc>>8; ss[1]=dirc; } + out.vi_write(dir(), dir.getLength()); + out.vi_write("", 0); +} + +/* tiffjai --- Sun Jun 2 22:25:16 CEST 2002 */ + +Rule::Applier::cons_t out_tiffjai_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_TIFF + || cache->Compression!=Rule::Cache::CO_JAI + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + if (cache->SampleFormat!=Image::SF_Asis) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/JAI requires /SampleFormat/Asis" << (Error*)0; + badp=true; + } + if (!cache->isBinSB()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /TransferEncoding/Binary|/?SBFirst" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/JAI requires /Prediror 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_tiffjai_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + if (out_tiffjai_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + Image::Sampled *img=sf->getImg(); + unsigned char cs=img->getCs(); /* color space */ + if (cs!=Image::Sampled::CS_GRAYSCALE && cs!=Image::Sampled::CS_RGB && cs!=Image::Sampled::CS_YCbCr && cs!=Image::Sampled::CS_CMYK) { + /* Dat: CS_YCCK is supported by JPEG, but unsupported by TIFF-JPEG */ + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/JAI doesn't support this color space" << (Error*)0; + return Rule::Applier::BAD; + } + /* Imp: test all CMYK TIFF tags */ + /* Imp: test with RGB and CMYK images */ + TIFFPrinter tp(out, or_->cache.TransferEncoding==or_->cache.TE_LSBfirst); + /* ^^^ Dat: Binary defaults to MSBfirst, beacuse QuarkXPress 3 can read only + * MSBfirst TIFF files. + */ + char const *databeg=img->getHeadp()+img->getXoffs(); + assert(databeg[-2]=='\xFF' && databeg[-1]=='\xC0'); + slen_t datalen=img->getRowbeg()-databeg; + // printf("LEN=%u gl=%u datalen=%u\n", img->end_()-img->begin_(), img->getLength(), datalen); + tp.getS().vi_write("\xFF\xD8\xFF\xC0", 4); /* fake-SOI SOF0 */ + tp.getS().vi_write(databeg, datalen); /* DataPart */ + // tp.getS().vi_write("\xFF\xD9", 2); /* fake-EOI */ + /* ^^^ there must be an EOI already */ + + unsigned phot=0; bool inks=false, refe=false; + switch (cs) { + case Image::Sampled::CS_GRAYSCALE: phot=tp.PHOTOMETRIC_MINISBLACK; break; + case Image::Sampled::CS_RGB: phot=tp.PHOTOMETRIC_RGB; break; + case Image::Sampled::CS_YCbCr: phot=tp.PHOTOMETRIC_YCBCR; refe=true; break; /* preferred to RGB */ + case Image::Sampled::CS_CMYK: phot=tp.PHOTOMETRIC_SEPARATED; inks=true; break; /* preferred to RGB */ + default: Error::sev(Error::EERROR) << "TIFF6-JAI: color space " << (unsigned)cs << " not supported in TIFF-JPEG" << (Error*)0; + } + + /* Dat: TIFF tags must appear in increasing numerical order */ + tp.dirSL(tp.ImageWidth, img->getWd()); + tp.dirSL(tp.ImageLength, img->getHt()); + unsigned short eights[]={8,8,8,8}; + tp.dirSHORT(tp.BitsPerSample, img->cs2cpp[cs], eights); + tp.dirSL(tp.Compression, tp.COMPRESSION_JPEG); /* SHORT */ + tp.dirSL(tp.Photometric, phot); /* SHORT */ + tp.dirLONG(tp.StripOffsets, 8); + tp.dirSL(tp.SamplesPerPixel, img->cs2cpp[cs]); /* SHORT */ + tp.dirSL(tp.RowsPerStrip, img->getHt()); + tp.dirLONG(tp.StripByteCounts, datalen+6); + slen_t rats[]={1,1, 0,1, 255,1, 128,1, 255,1, 128,1, 255,1}; + tp.dirRATIONAL(tp.XResolution, 1, rats); + tp.dirRATIONAL(tp.YResolution, 1, rats); + tp.dirSL(tp.PlanarConfig, 1); /* SHORT, PLANARCONFIG_CONTIG */ + tp.dirSL(tp.ResolutionUnit, 1); /* SHORT */ + if (inks) tp.dirSL(tp.InkSet, 1); /* SHORT */ + tp.dirUNDEFINED(tp.JPEGTables, databeg-img->getHeadp()-2, img->getHeadp(), 2, "\xFF\xD9"); + const unsigned char hvs=img->end_()[-1]; + if (hvs!=0x22 && cs==Image::Sampled::CS_YCbCr) { + // printf("hvs=0x%02X\n", hvs); + const unsigned short horiz_vert[2]={ (unsigned short)((hvs+0U)>>4), + (unsigned short)(hvs&15U) }; + tp.dirSHORT(tp.YCbCrSubsampling, 2, horiz_vert); + } + if (refe) tp.dirRATIONAL(tp.ReferenceBlackWhite, 6, rats+2); + tp.dirClose(); + #if 0 + { Files::FILEW f(fopen("t.hea","wb")); + f.vi_write(img->getHeadp(), databeg-img->getHeadp()-2); /* HeadPart */ + f.vi_write("\xFF\xD9", 2); + f.close(); + } + { Files::FILEW f(fopen("t.dat","wb")); + f.vi_write("\xFF\xD8\xFF\xC0", 4); /* fake-SOI SOF0 */ + f.vi_write(databeg, datalen); /* DataPart */ + f.close(); + } + #endif + // out.vi_write(img->getHeadp(), img->getRowbeg()-img->getHeadp()); /* HeadPart */ + // out.vi_write(img->getRowbeg(), img->end_()-img->getRowbeg()); /* DataPart */ + return Rule::Applier::OK; + /* SUXX: see compatibility notes in FAQ (README) */ +} + +Rule::Applier out_tiffjai_applier = { "TIFF6-JAI", out_tiffjai_check_rule, out_tiffjai_work, 0 }; + +/* tiff --- Tue Jun 4 16:23:48 CEST 2002 */ + +/** by pts@fazekas.hu at Tue Jun 11 16:56:53 CEST 2002 + * Reads a JPEG Baseline (SOF0) stream on input, and drops everything before + * the first SOF0 marker. Then writes an SOI marker, the SOF0 marker, and + * everything after the SOI marker. The last two bytes read must be an + * EOI marker (/\xFF\xD9/). The first thing to be read must be a SOI marker + * (/\xFF+\xD8/). The getJPEGTables() method can be used to retrieve the + * data read before the SOF0 marker. An SOS marker must be read after SOF0. + * Also retains some information (hvsamples[0]) from the SOF0 marker. + * Suitable for reading the output of cjpeg and Ghostscript /FlateEncode. + * This encoder would be much harder if we were not allowed to read the + * JPEG stream into memory. + */ +class JPEGSOF0Encode: public PSEncoder { + public: + /** @param maxcpl_: maximum # hex digits per line, should be even */ + JPEGSOF0Encode(GenBuffer::Writable &out_); + virtual void vi_write(char const*buf, slen_t len); + SimBuffer::Flat const& getJPEGTables() const { return buf; } + unsigned char getColorSpace() const { return gi.colorspace; } + unsigned char getHVS() const { return gi.hvs; } + protected: + GenBuffer::Writable &out; + SimBuffer::B buf; + struct jai_gfxinfo gi; +}; + +JPEGSOF0Encode::JPEGSOF0Encode(GenBuffer::Writable &out_): out(out_) {} +void JPEGSOF0Encode::vi_write(char const*bufr, slen_t len) { + if (len==0) { + { Filter::FlatD flatd(buf(), buf.getLength()); + jai_parse_jpeg(&gi, &flatd); + } + slen_t len=buf.getLength(); + if (gi.bad==0 && (len<4 || buf[len-2]!='\xFF' || buf[len-1]!='\xD9')) gi.bad=10; + if (gi.bad!=0) + Error::sev(Error::EERROR) << "JPEGS0F0: invalid JPEG stream: " << jai_errors[gi.bad] << (Error*)0; + out.vi_write("\xFF\xD8", 2); /* extra SOI */ + out.vi_write(buf()+gi.SOF_offs-2, len-gi.SOF_offs+2); /* SOF0 and followers */ + out.vi_write(0,0); + assert(buf[gi.SOF_offs-2]=='\xFF'); /* BUGFIX at 2002.12.02 */ + buf[gi.SOF_offs-1]='\xD9'; /* extra EOI */ + buf.keepLeft(gi.SOF_offs); /* keep only headers */ + } else buf.vi_write(bufr, len); +} + + +Rule::Applier::cons_t out_tiff_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_TIFF + || cache->Compression==Rule::Cache::CO_JAI + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + /* Dat: acroread TIFF predictor2 OK. (examples/fishg_lzw2_pdf.job) */ + /* Dat: /ZIP with /Predictor OK */ + if (cache->Predictor!=cache->PR_None) { + if (cache->Predictor!=cache->PR_TIFF2) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF requires /Predictor 1|2" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_ZIP && cache->Compression!=Rule::Cache::CO_LZW) { + Error::sev(Error::WARNING_DEFER) << "check_rule: real /Predictor requires /ZIP or /LZW" << (Error*)0; + badp=true; + } + } + #if !HAVE_LZW + if (cache->Compression==Rule::Cache::CO_LZW) { + Error::sev(Error::WARNING_DEFER) << "check_rule: `configure --enable-lzw' for /Compression/LZW with /FileFormat/TIFF" << (Error*)0; + badp=true; + } + #endif + if (cache->Compression==Rule::Cache::CO_Fax && !cache->isOneBit()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF /Compression/Fax requires a 1-bit /SampleFormat" << (Error*)0; + badp=true; + } + if (!cache->isGray() && !cache->isTransparentM() && !cache->isIndexed() && !cache->isRGB()) { + /* Dat: unsupported SampleFormats: /Opaque, /Transparent and /Asis. */ + Error::sev(Error::WARNING_DEFER) << "check_rule: unsupported /SampleFormat for /FileFormat/TIFF" << (Error*)0; + badp=true; + } + if (!cache->isBinSB()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /FileFormat/TIFF requires /TransferEncoding/Binary|/?SBFirst" << (Error*)0; + badp=true; + } + if (cache->isDCTE() && cache->SampleFormat!=Image::SF_Rgb8 && cache->SampleFormat!=Image::SF_Gray8 && cache->SampleFormat!=Image::SF_Indexed8) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /DCTEncode requires /Rgb8 or /Gray8 (or /Indexed8)" << (Error*)0; + badp=true; + } + if (!badp && cache->isTransparentM()) { + cache->origSampleFormat=cache->SampleFormat; + cache->SampleFormat=Image::SF_Transparent8; + } + return (Rule::Applier::cons_t)(badp ? 0+Rule::Applier::BAD : 0+Rule::Applier::OK); + /* ^^^ Dat: 0+: pacify g++-3.1 */ +} + +Rule::Applier::cons_t out_tiff_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + Rule::Cache *cache=&or_->cache; + Image::sf_t origSampleFormat=cache->origSampleFormat; + if (out_tiff_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + + assert(!sf->hasTranspp() || cache->isTransparentM()); + /** Alpha channel or null. */ + Image::Indexed *alpha=(Image::Indexed*)NULLP; + if (cache->isTransparentM()) { + // fprintf(stderr,"sf=%u osf=%u\n", cache->SampleFormat, origSampleFormat); + alpha=PTS_dynamic_cast(Image::Indexed*,sf->getImg())->calcAlpha(); + PTS_dynamic_cast(Image::Indexed*,sf->getImg())->getClearTransp(); + assert((alpha!=NULLP) == (sf->hasTranspp()==true)); + sf->clearTransp(); + 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}; + 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}; + 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}; + unsigned char minbpc=sf->minRGBBpcc(); + // fprintf(stderr,"minbpc=%u\n",minbpc); + if (minbpc<8 && origSampleFormat==Image::SF_Transparent8) minbpc=8; + else if (minbpc<4 && origSampleFormat==Image::SF_Transparent4) minbpc=4; + else if (minbpc<2 && origSampleFormat==Image::SF_Transparent2) minbpc=2; + // fprintf(stderr,"minbpc=%u\n",minbpc); + // cacheHints.EncoderBPL=(slen_t)img->getWd()*img->getCpp()*img->getBpc(); + /* ^^^ Dat: doSampleFormat will do it correctly */ + // assert(saf!=Image::SF_max); + cache->SampleFormat=(alpha==NULLP ? indexedtab : sf->minGrayBpcc()==0 ? rgbtab : graytab)[minbpc]; + } + + or_->doSampleFormat(sf); /* No separations */ + Image::Sampled *img=sf->getImg(); /* call this _after_ doSampleFormat()! */ + + TIFFPrinter tp(out, or_->cache.TransferEncoding==or_->cache.TE_LSBfirst); + /* ^^^ Dat: Binary defaults to MSBfirst, beacuse QuarkXPress 3 can read only + * MSBfirst TIFF files. + */ + unsigned phot= cache->isRGB() ? 0+tp.PHOTOMETRIC_RGB + // : cache->SampleFormat==Image::SF_Mask ? tp.PHOTOMETRIC_MASK + : cache->isGray() ? 0+tp.PHOTOMETRIC_MINISBLACK + // : cache->isIndexed() ? tp.PHOTOMETRIC_PALETTE + : 0+tp.PHOTOMETRIC_PALETTE /* /Indexed*, /Mask, /Transparent+ */; + unsigned compr=tp.COMPRESSION_NONE; + + Filter::VerbatimCountE vc(tp.getS()); + GenBuffer::Writable *cp=&vc; + JPEGSOF0Encode *jp=(JPEGSOF0Encode*)NULLP; + // Dat: g++-3.4: Rule::Cache::CO_None: `cache cannot appear in a constant expression' + switch (cache->Compression) { + case Rule::Cache::CO_None: break; + case Rule::Cache::CO_ZIP: compr=tp.COMPRESSION_DEFLATE; cp=PSEncoder::newFlateEncode(*cp, or_->cacheHints.Effort); break; + case Rule::Cache::CO_LZW: compr=tp.COMPRESSION_LZW; cp=PSEncoder::newLZWEncode(*cp); break; + /* vvv Dat: RunLengthEncode EOD (char 128) is OK and ignored in TIFF PackBits streams */ + case Rule::Cache::CO_RLE: compr=tp.COMPRESSION_PACKBITS; cp=PSEncoder::newRunLengthEncode(*cp, or_->cacheHints.RecordSize); break; + case Rule::Cache::CO_Fax: + if (or_->cacheHints.K<0) compr=tp.COMPRESSION_CCITTFAX4; + else if (or_->cacheHints.K==0) compr=tp.COMPRESSION_CCITTFAX3; + else { compr=tp.COMPRESSION_CCITTFAX3; or_->cacheHints.K=img->getHt(); } + cp=PSEncoder::newCCITTFaxEncode(*cp, or_->cacheHints.K, or_->cacheHints.EncoderBPL, /*EndOfLine:*/ compr==tp.COMPRESSION_CCITTFAX3, /*BlackIs1: !*/ true); + /* Dat: libtiff enforces EndOfLine==(compr==tp.COMPRESSION_CCITTFAX3) */ + break; + case Rule::Cache::CO_IJG: + compr=tp.COMPRESSION_JPEG; + jp=new JPEGSOF0Encode(vc); + cp=PSEncoder::newDCTIJGEncode(*jp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.Quality); + break; + case Rule::Cache::CO_DCT: compr=tp.COMPRESSION_JPEG; { + SimBuffer::B other_parameters; + or_->cacheHints.DCT->dump(other_parameters, 0, false); + jp=new JPEGSOF0Encode(vc); + cp=PSEncoder::newDCTEncode(*jp, or_->cacheHints.EncoderColumns, or_->cacheHints.EncoderRows, or_->cacheHints.EncoderColors, or_->cacheHints.ColorTransform, other_parameters); + break; } + default: assert(0); + } + + GenBuffer::Writable *pp=cp; + if (cache->hasPredictor()) pp=PSEncoder::newPredictor(*cp, cache->Predictor, or_->cacheHints.PredictorBPC, or_->cacheHints.PredictorColumns, or_->cacheHints.PredictorColors); + + slen_t rlenht=img->getRlen()*img->getHt(); + Image::Sampled::dimen_t wd=img->getWd(); + if (rlenht!=0) { + /* Dat: TIFF inserts extra sample bits... */ + /* TIFF images with transparency are really inefficient. That's because + * libtiff doesn't read or write an indexed image with transparency: + * Sorry, can not handle contiguous data with PhotometricInterpretation=3, + * and Samples/pixel=2. + * So Indexed images are converted to RGB* or Gray* first, and then the + * alpha channel is added. The result is a big and inefficiently + * compressible TIFF file. + */ + + if (alpha!=NULLP) { + unsigned char bpc=img->getBpc(); + slen_t rlen=img->getRlen(); + slen_t writelen; + char *buf=(char*)NULLP; + char const*psave=img->getRowbeg(), *p, *ppend=psave+rlenht; + char const*r=alpha->getRowbeg(); +#ifndef NDEBUG + char const*rend; + Image::Sampled::dimen_t rlena=(wd+7)>>3; +#endif + char *t; + register unsigned u; +#ifndef NDEBUG + assert(rlena==alpha->getRlen()); +#endif + // printf("SF=%u\n", cache->SampleFormat); + if (cache->isGray()) { + /* works at Mon Dec 9 01:25:59 CET 2002 */ + 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}; + static unsigned char const szor2[16]={85,84,81,80,69,68,65,64,21,20,17,16,5,4,1,0}; + // static unsigned char const szor2[8]={0,1,16,17,64,65,80,81}; + buf=new char[(writelen=((slen_t)wd*bpc+3)>>2)+24]; +#ifndef NDEBUG + assert(rlena*8>=rlen); +#endif + while (psave!=ppend) { + t=buf; +#ifndef NDEBUG + rend=r+rlena; +#endif + p=psave; psave+=rlen; + assert(psave<=ppend); + u=(1<<16); + switch (bpc) { + case 8: + while (p!=psave) { + /* Dat: works at Tue Sep 10 22:45:52 CEST 2002 */ + if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8); + *t++=*p++; *t++=-(0==(u&128)); + u<<=1; + } break; + case 4: + while (p!=psave) { + if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8); + *t++=(p[0]&0xF0)|(0==(u&128)?15:0); + *t++=((p[0]&0xF)<<4)|(0==(u&64)?15:0); + p++; u<<=2; + } break; + case 2: + while (p!=psave) { + if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8); + *t++=(p[0]&0xC0 )|((u&128)!=0?0:48)|((p[0]&0x30)>>2)|((u&64)!=0?0:3); + *t++=((p[0]&0xC)<<4)|((u& 32)!=0?0:48)|((p[0]&0x3 )<<2)|((u&16)!=0?0:3); + p++; u<<=4; + } break; + case 1: + while (p!=psave) { + u=*(unsigned char const*)r++; + *t++=szor1[*(unsigned char const*)p>>4]|szor2[u>>4]; + *t++=szor1[*(unsigned char const*)p&15]|szor2[u&15]; + p++; + } break; + } /* SWITCH Gray bpc */ + // assert(p-psave+0U==rlena*8U); + assert(p>=psave); + pp->vi_write(buf, writelen); + } + } else { + /* works at Sun Dec 8 23:30:17 CET 2002 */ + assert(cache->isRGB()); + buf=new char[(writelen=((slen_t)wd*bpc+1)>>1)+24]; + while (psave!=ppend) { + t=buf; + p=psave; psave+=rlen; + assert(psave<=ppend); + u=(1<<16); +#ifndef NDEBUG + rend=r+rlena; /* superfluous */ +#endif + switch (bpc) { + case 8: + while (p<psave) { + if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8); + *t++=*p++; + *t++=*p++; + *t++=*p++; + *t++=-(0==(u&128)); + u<<=1; + } break; + case 4: + while (p<psave) { + if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8); + *t++=p[0]; /* R0 and G0 */ + *t++=(p[1]&0xF0)|((u&128)!=0?0:15); /* B0 and A0 */ + *t++=(p[1]<<4)|((p[2]>>4)&15); /* R1 and G1 */ + *t++=(p[2]<<4)|((u&64)!=0?0:15); /* B1 and A1 */ + p+=3; u<<=2; + } break; + case 2: + while (p<psave) { + if (0!=(u&(1<<16))) u=*(unsigned char const*)r++|(1<<8); + /* Dat: p[0]==R0G0B0R1 p[1]==G1B1R2G2 p[2]==B2R3G3B3 */ + *t++=(p[0]&0xFC)|((u&128)!=0?0:3); /* R0G0B0 and A0 */ + *t++=(p[0]<<6)|((p[1]>>2)&0x3C)|((u&64)!=0?0:3); /* R1 G1B1 A1 */ + *t++=(p[1]<<4)|((p[2]>>4)&0x0C)|((u&32)!=0?0:3); /* R2G1 B1 A2 */ + *t++=(p[2]<<2)|((u&16)!=0?0:3); /* R3G3B3 and A3 */ + p+=3; u<<=4; + } break; + default: // case 1: + while (p<psave) { + u=*(unsigned char const*)r++; + /* Dat: p[0]==RGBRGBRG p[1]==BRGBRGBR p[2]==GBRGBRGB */ + *t++=(p[0]&0xE0)|((u&128)!=0?0:16) + |((p[0]>>1)&0xE)|((u&64)!=0?0:1); + *t++=(p[0]<<6)|((p[1]>>2)&0x80)|((u&32)!=0?0:16) + |((p[1]>>3)&0xE)|((u&16)!=0?0:1); + *t++=((p[1]<<4)&0xE0)|((u&8)!=0?0:16) + |((p[1]<<3)&0x7)|((p[2]>>5)&0x06)|((u&4)!=0?0:1); + *t++=((p[2]<<2)&0xE0)|((u&2)!=0?0:16) + |((p[2]<<1)&0x0E)|((u&1)!=0?0:1); + p+=3; + } + break; + } /* SWITCH RGB bpc */ +#ifndef NDEBUG + assert(r==rend); // r=rend; +#endif + pp->vi_write(buf, writelen); + } + } + delete [] buf; + } else pp->vi_write(img->getRowbeg(), rlenht); + } + pp->vi_write(0,0); /* flush all */ + if (pp!=cp) delete pp; + if (cp!=&vc) delete cp; + + bool refe=false; + if (compr==tp.COMPRESSION_JPEG) switch (jp->getColorSpace()) { + case Image::Sampled::CS_GRAYSCALE: + assert(phot==tp.PHOTOMETRIC_MINISBLACK || phot==tp.PHOTOMETRIC_PALETTE); + /* Dat: we don't change `phot' here, so JPEG compression can be applied + * to the palette indexes of indexed images :-) + */ + break; + case Image::Sampled::CS_RGB: phot=tp.PHOTOMETRIC_RGB; break; + case Image::Sampled::CS_YCbCr: phot=tp.PHOTOMETRIC_YCBCR; refe=true; break; /* preferred to RGB */ + // case Image::Sampled::CS_CMYK: phot=tp.PHOTOMETRIC_SEPARATED; inks=true; break; /* preferred to RGB */ + default: Error::sev(Error::EERROR) << "TIFF6: color space " << (unsigned)jp->getColorSpace() << " not supported in TIFF-JPEG" << (Error*)0; + } + + /* Dat: TIFF tags must appear in increasing numerical order */ + tp.dirSL(tp.ImageWidth, wd); +// tp.dirSL(tp.ImageWidth, img->getWd()/2); + tp.dirSL(tp.ImageLength, img->getHt()); + unsigned short s4[]={img->getBpc(),img->getBpc(),img->getBpc(),img->getBpc()}; + /* ^^^ these values may be different according to the TIFF6 spec, but + * libtiff can read TIFF files only with same BitsPerSamples. + */ + unsigned sppa=img->getCpp()+(alpha!=NULLP ? 1:0); + tp.dirSHORT(tp.BitsPerSample, sppa, s4); + tp.dirSL(tp.Compression, compr); /* SHORT */ + tp.dirSL(tp.Photometric, phot); /* SHORT */ + if (cache->Compression==Rule::Cache::CO_Fax) tp.dirSL(tp.FillOrder, 1); /* byte abcdefgh is (a<<7)+...+h */ + tp.dirLONG(tp.StripOffsets, 8); + if (!cache->isIndexed()) + tp.dirSL(tp.SamplesPerPixel, sppa); /* SHORT */ +// tp.dirSL(tp.SamplesPerPixel, 2); /* SHORT */ + tp.dirSL(tp.RowsPerStrip, img->getHt()); + tp.dirLONG(tp.StripByteCounts, vc.getCount()); + slen_t rats[]={1,1, 0,1, 255,1, 128,1, 255,1, 128,1, 255,1}; + tp.dirRATIONAL(tp.XResolution, 1, rats); + tp.dirRATIONAL(tp.YResolution, 1, rats); + tp.dirSL(tp.PlanarConfig, 1); /* SHORT, PLANARCONFIG_CONTIG */ + if (compr==tp.COMPRESSION_CCITTFAX3) tp.dirLONG(tp.Group3Options, (tp.GROUP3OPT_UNCOMPRESSED)|(or_->cacheHints.K!=0?tp.GROUP3OPT_2DENCODING:0)); + if (compr==tp.COMPRESSION_CCITTFAX4) tp.dirLONG(tp.Group4Options, tp.GROUP4OPT_UNCOMPRESSED); + tp.dirSL(tp.ResolutionUnit, 1); /* SHORT */ + if (cache->Predictor!=cache->PR_None) { + tp.dirSL(tp.Predictor, cache->Predictor); /* 1|2 */ + if (img->getBpc()!=8) + Error::sev(Error::WARNING) << "TIFF6: libtiff supports /Predictor only with bpc=8 and bpc=16" << (Error*)0; + } + + if (cache->isIndexed() || cache->isTransparentM()) { + unsigned colorlen=(1<<img->getBpc()), i=0; + unsigned short *r=new unsigned short[3*colorlen], *g=r+colorlen, *b=g+colorlen; + memset(r, '\0', 3*colorlen); + /** Image palette data */ + char const *p=img->getHeadp(), *pend=img->getRowbeg(); + assert((pend-p)%3==0); + assert(3*colorlen>=(unsigned)(pend-p)); + while (p<pend) { + r[i ]=*(unsigned char const*)p++*257; + g[i ]=*(unsigned char const*)p++*257; + b[i++]=*(unsigned char const*)p++*257; + } + /* ^^^ In a TIFF palette (ColorMap) 0 is black, 65535 is lightest */ + tp.dirSHORT(tp.ColorMap, colorlen*3, r); + delete [] r; + } + if (alpha!=NULLP) { + /* vvv Dat: GIMP 1.0 load fails with tp.EXTRASAMPLE_UNASSALPHA */ + tp.dirSL(tp.ExtraSamples, tp.EXTRASAMPLE_ASSOCALPHA); /* SHORT */ + delete alpha; + } + /* no tp.InkSet, because no CMYK */ + + if (compr==tp.COMPRESSION_JPEG) { + tp.dirUNDEFINED(tp.JPEGTables, jp->getJPEGTables().getLength(), jp->getJPEGTables()() /* , 2, "\xFF\xD9" */); + const unsigned char hvs=jp->getHVS(); + if (hvs!=0x22 && phot==tp.PHOTOMETRIC_YCBCR) { + const unsigned short horiz_vert[2]={ (unsigned short)((hvs+0U)>>4), + (unsigned short)(hvs&15U) }; + tp.dirSHORT(tp.YCbCrSubsampling, 2, horiz_vert); + } + if (refe) tp.dirRATIONAL(tp.ReferenceBlackWhite, 6, rats+2); + delete jp; + } + + tp.dirClose(); + return Rule::Applier::OK; +} + +Rule::Applier out_tiff_applier = { "TIFF6", out_tiff_check_rule, out_tiff_work, 0 }; + + +/* --- Sat Apr 20 11:32:42 CEST 2002 -- Sat Apr 20 16:48:25 CEST 2002 */ + +#if OBJDEP +# warning REQUIRES: crc32.o +# warning REQUIRES: crc32.o +#endif +/** Encodes 32-bit (data.length-minus), data, crc32. */ +class LenCRC32Encode: public PSEncoder { + public: + /** @param maxcpl_: maximum # hex digits per line, should be even */ + LenCRC32Encode(GenBuffer::Writable &out_, unsigned minus_); + virtual void vi_write(char const*buf, slen_t len); + + protected: + GenBuffer::Writable &out; + unsigned minus; + /** The length is not known in advance, so we have to buffer the whole output + * in memory. + */ + SimBuffer::B sofar; +}; + +LenCRC32Encode::LenCRC32Encode(GenBuffer::Writable &out_, unsigned minus_): out(out_), minus(minus_), sofar("1234") {} +void LenCRC32Encode::vi_write(char const*buf, slen_t len) { + if (len==0) { + char *s=const_cast<char*>(sofar()); + slen_t slen=sofar.getLength(); + unsigned PTS_INT32_T crc=crc32(CRC32_INITIAL, s+4, slen-4); + slen-=minus+4; + s[0]=slen>>24; s[1]=slen>>16; s[2]=slen>>8; s[3]=slen; + s=sofar.vi_mkend(4); + s[0]=crc >>24; s[1]=crc>>16; s[2]=crc>>8; s[3]=crc; + out.vi_write(sofar(), sofar.getLength()); + out.vi_write(0,0); + } else sofar.vi_write(buf, len); +} + +/** PNG output (RFC 2083) */ +Rule::Applier::cons_t out_png_check_rule(Rule::OutputRule* or_) { + /* Supported PNG types: /Gray1, /Gray2, /Gray4, /Gray8, /Rgb8, + * /Indexed1, /Indexed2, /Indexed4, /Indexed8. + * Unsupported PNG types: /Gray16, /Rgb16, /GrayA8, /GrayA16, /RgbA8, + * /RgbA16. + * /Indexed* supports transparency (via PNG chunk tRNS). + */ + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_PNG + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + /* Dat: (by policy) we don't support /Transparent or /Opaque here; the + * user should not specify such SampleFormat in the .job file. + */ + if (!cache->isGray() + && !cache->isTransparentM() + && !cache->isIndexed() + && cache->SampleFormat!=Image::SF_Rgb8) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Gray*, /Indexed*, /Mask, /Transparent+ or /Rgb8" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_Binary) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Binary" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_ZIP) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Compression/None or /ZIP" << (Error*)0; + badp=true; + } + if (cache->Predictor==cache->PR_TIFF2) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /PNG requires /Predictor >=10" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + /* Now we are sure about Rule::Applier::OK. */ + or_->cache.WarningOK=true; /* ?? */ + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_png_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + Rule::Cache *cache=&or_->cache; + char tmp[64], colortype; register char*p; + unsigned PTS_INT32_T crc; + + // assert(0); + if (out_png_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf); + Image::Sampled *img=sf->getImg(); + unsigned char bpc=img->getBpc(); /* set _after_ doSampleFormat */ + Image::Sampled::dimen_t wd=img->getWd(), ht=img->getHt(); + + GenBuffer::Writable *rp=new LenCRC32Encode(out,4); + GenBuffer::Writable *cp=PSEncoder::newFlateEncode(*rp, (cache->Compression==Rule::Cache::CO_None) ? 0: or_->cacheHints.Effort); + + /* Dat: PredictorColumns, PredictorBPC and PredictorColors are forced to + * match the image, because PNG requires it. + */ + GenBuffer::Writable *pp=PSEncoder::newPredictor( + *cp, + cache->Predictor==cache->PR_None ? cache->PR_PNGNone : cache->Predictor, + bpc, /*or_->cacheHints.PredictorBPC*/ + wd, /*or_->cacheHints.PredictorColumns*/ + img->getCpp() /*or_->cacheHints.PredictorColors*/ + ); + + /* Critical PNG chunks (capital 1st letter) and ancillary PNG chunks: + * + * -: zero, 1: exactly one, ?: 0 or 1, *: 0 or more, +: 1 or more + * Name Nr sam2p Ordering constraints + * IHDR 1 1 Must be first + * cHRM ? - Before PLTE and IDAT + * gAMA ? - Before PLTE and IDAT + * sBIT ? - Before PLTE and IDAT + * PLTE ? ? Before IDAT + * hIST ? - After PLTE; before IDAT + * tRNS ? ? After PLTE; before IDAT + * bKGD ? ? After PLTE; before IDAT + * pHYs ? - Before IDAT + * tIME ? - None + * tEXt * - None + * zTXt * - None + * IDAT + 1 After IHDR and PLTE, multiple IDATs must be consecutive + * IEND 1 1 Must be last + */ + + /* 0..3: PNG header */ + /* 8..11: IHDR chunk length */ + /* 12..15: IHDR chunk name */ + memcpy(tmp,"\211PNG\r\n\032\n\0\0\0\015IHDR",16); /* ASCII charset is assumed. */ + p=tmp+16; + *p++=wd>>24; *p++=wd>>16; *p++=wd>>8; *p++=wd; /* 16: Width */ + *p++=ht>>24; *p++=ht>>16; *p++=ht>>8; *p++=ht; /* 20: Height */ + *p++=bpc; /* 24: Bit depth */ + colortype=*p++=cache->isGray()?0:cache->isRGB()?2:3; /* 25: Color type */ + *p++=0; /* 26: Compression method: deflate/32K (forced by PNG spec), overcoming with Effort==0 */ + *p++=0; /* 27: Filter type: adaptive filtering with 5 basic PNG filter types */ + *p++=0; /* 28: Non-interlaced */ + crc=crc32(CRC32_INITIAL, tmp+12, 17); + *p++=crc>>24;*p++=crc>>16;*p++=crc>>8;*p++=crc; /* 29: IHDR CRC */ + out.vi_write(tmp, 33); + if (colortype==3) { + bool transp=cache->isTransparentM() && PTS_dynamic_cast(Image::Indexed*,img)->getTransp()>=0; + /* unsigned ncols=PTS_dynamic_cast(Image::Indexed*,img)->getNcols(); */ + unsigned ncols3=img->getRowbeg()-img->getHeadp(); + + p=tmp; + *p++=0; *p++=0; *p++=ncols3>>8; *p++=ncols3; /* 0: PLTE chunk length */ + *p++='P'; *p++='L'; *p++='T'; *p++='E'; /* 4: PLTE chunk name */ + out.vi_write(tmp, 8); + if (transp) { + PTS_dynamic_cast(Image::Indexed*,img)->makeTranspZero(); + p=img->getHeadp(); p[0]=p[1]=p[2]=(char)0; /* force black backround (for consistency) */ + } + out.vi_write(img->getHeadp(), ncols3); + crc=crc32(crc32(CRC32_INITIAL,tmp+4,4), img->getHeadp(), ncols3); + p=tmp; *p++=crc>>24;*p++=crc>>16;*p++=crc>>8;*p++=crc; /* 0: PLTE CRC */ + out.vi_write(tmp, 4); + if (transp) { + out.vi_write("\0\0\0\1tRNS\0@\346\330f", 13); /* color 0 is transparent */ + //fprintf(stderr, "tRNS: 0x%08lx", (unsigned long)crc32(CRC32_INITIAL,"tRNS\0",5)); /* 0x40e6d866 */ + out.vi_write("\0\0\0\1bKGD\0\210\5\35H", 13); /* color 0 is background color */ + //out.vi_write("\0\0\0\1bKGD\2\x66\x0b\x7c\x64", 13); /* color 2 is background color */ + //fprintf(stderr, "bKGD: 0x%08lx", (unsigned long)crc32(CRC32_INITIAL,"bKGD\2",5)); /* 0x88051d48 */ + } + } + + slen_t rlenht=img->getRlen()*img->getHt(); + #if 0 /* Calculates _bad_ CRC (before compression etc.) */ + p=tmp; + *p++=rlenht>>24; *p++=rlenht>>16; *p++=rlenht>>8; *p++=rlenht; /* 0: IDAT chunk length */ + *p++='I'; *p++='D'; *p++='A'; *p++='T'; /* 4: IDAT chunk name */ + out.vi_write(tmp, 8); + if (rlenht!=0) pp->vi_write(img->getRowbeg(), rlenht); + pp->vi_write(0,0); /* flush all, write CRC */ + crc=crc32(crc32(CRC32_INITIAL,tmp+4,4), img->getRowbeg(), rlenht); + p=tmp; *p++=crc>>24;*p++=crc>>16;*p++=crc>>8;*p++=crc; /* 0: IDAT CRC */ + out.vi_write(tmp, 4); + #else + rp->vi_write("IDAT",4); + if (rlenht!=0) pp->vi_write(img->getRowbeg(), rlenht); + pp->vi_write(0,0); /* flush all, write CRC */ + #endif + + out.vi_write("\0\0\0\0IEND\256B`\202",12); /* IEND chunk */ + delete pp; + delete cp; + delete rp; + + return Rule::Applier::OK; +} + +Rule::Applier out_png_applier = { "PNG", out_png_check_rule, out_png_work, 0 }; + +/* --- Sat Jun 15 19:30:41 CEST 2002 */ + +/** BMP RLE compression, type 1. Seems to be optimal. + * @param dst buffer with enough place for compressed data. The caller must + * pre-allocate >=(send-sbeg)+(send-sbeg+128)*2/255. + * @param sbeg first raw data char to compress + * @param send char to finish compression just before + * @return dst+(number of characters occupied by compressed data) + * + * in-memory implementation Sun Jun 23 20:02:41 CEST 2002 + */ + +static char *bmp_compress1_row(char *dst, char const *sbeg, char const *send) { + #if 0 + # define BMP_ASSERT(x) assert(x) + #else + # define BMP_ASSERT(x) + #endif + #undef BUF + #define BUF(x) (*(x)) + #undef PUTCH__ + #define PUTCH__(c) (*dst++=(c)) + char c, c2; + char const *beg, *end, *rend, *q, *r, *best; + signed bestca, rca; /* both must fit into -255..255 */ + slen_t frl, efrl; + int ci; + // bool oddp; + + beg=sbeg; + end=(send-sbeg>255) ? beg+255 : send; + + while (beg!=end) { /* there is still unprocessed data in the buffer */ + c=BUF(beg++); + if (beg==end) { PUTCH__(1); PUTCH__(c); break; } /* last char */ + if (c==BUF(beg)) { /* sure r chunk */ + ci=2; beg++; + rep: + while (beg!=end && c==BUF(beg)) { beg++; ci++; } + PUTCH__(ci); PUTCH__(c); /* r chunk */ + } else { /* possible c chunk */ + rend=end; + BMP_ASSERT(end-beg<=254); + if (end!=send) { /* read an extra char as the terminator of the last run-length of c, buf:0..254 */ + end++; + BMP_ASSERT(end-beg==255); /* buffer is full (255 chars) */ + } + + best=r=beg; + bestca=rca=-1; /* best and current advantage of c over r */ + + while (r!=rend) { /* c chunk should stop only at run-length boundaries */ + BMP_ASSERT(-255<=rca && rca<=255); + BMP_ASSERT(-255<=bestca && bestca<=255); + q=r; r=q+1; ci=1; while (r!=end && BUF(r)==BUF(q)) { r++; ci++; } + if (r==end && end!=rend) break; + if (((r-beg)&1)==0) { /* odd (!) copy length */ + rca+=3-ci; + 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|' */ + if (bestca<rca) { bestca=rca; best=r-1; } /* make c as short as possible */ + rca--; + } else { /* even copy length */ + rca+=2-ci; + 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|' */ + if (bestca<rca) { bestca=rca; best=r; } /* make c as short as possible */ + } + } + BMP_ASSERT(-255<=rca && rca<=255); + BMP_ASSERT(-255<=bestca && bestca<=255); + if (bestca<=0 /* no possible positive advantage */ + || best-beg<=1 /* 1: c is one char, plus 1 char in buf. Imp: ==1?! */ + ) { ci=1; goto rep; } + r=best; /* Imp: get rid of this assignment */ + BMP_ASSERT(beg!=r); + BMP_ASSERT(((r-beg)&1)==1); /* even copy length */ + + if (end==r) { /* no followers, last chunk */ + /* BMP_ASSERT(had_eof); */ + // oddp=(1+(r-beg)&1)==1; + PUTCH__(0); + PUTCH__((r-beg)+1); + PUTCH__(c); + while (beg!=r) { PUTCH__(BUF(beg)); beg++; } /* emit c chunk */ + // if (oddp) PUTCH__(0); /* Imp: padding breaks optimality */ + } else { + BMP_ASSERT(r!=end); + /* BMP_ASSERT(r!=rend); */ /* r==rend is possible here */ + c2=BUF(r); frl=1; q=r+1; + while (q!=end && c2==BUF(q)) { q++; frl++; } /* count follower run length */ + efrl=frl; ci=-2; if (q==end) { /* Imp: get rid of -2 (-2 -> -1) */ + BMP_ASSERT(q==end); + while ((ci=(q==send)?-1:(unsigned char)*q++)!=-1 && (char)ci==c2) efrl++; + } + + /* printf("clen=%u\n", clen); */ + 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 */ + + // oddp=(1+(r-beg)&1)==1; + PUTCH__(0); + PUTCH__(1+(r-beg)); + PUTCH__(c); + while (beg!=r) { PUTCH__(BUF(beg)); beg++; } /* emit c chunk */ + // if (oddp) PUTCH__(0); /* Imp: padding breaks optimality */ + + beg=q; /* remove beginning of the r chunk from the buffer */ + if (ci>=0) { beg--; BMP_ASSERT((unsigned char)BUF(beg)==ci); } + + while (efrl>=255) { PUTCH__('\377'); PUTCH__(c2); efrl-=255; } /* emit full r chunks */ + if (efrl>=2) { /* emit last r chunk */ + PUTCH__(efrl); PUTCH__(c2); + } else if (efrl!=0) { + BMP_ASSERT(efrl==1); + beg--; /* leave a single instance of c2 in beginning of the buffer */ + BMP_ASSERT(BUF(beg)==c2); + } + } /* IF c chunk has followers */ + } /* IF r or c chunk */ + end=(send-beg>255) ? beg+255 : send; + } /* WHILE main loop */ + return dst; + #undef BUF + #undef PUTCH__ +} + + +/** Windows Bitmap BMP output */ +Rule::Applier::cons_t out_bmp_check_rule(Rule::OutputRule* or_) { + /* Supported BMP types: /Rgb8, + * /Indexed1, /Indexed4, /Indexed8. + */ + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_BMP + ) return Rule::Applier::DONT_KNOW; + bool badp=false; + /* Dat: (by policy) we don't support /Transparent or /Opaque here; the + * user should not specify such SampleFormat in the .job file. + */ + if (cache->SampleFormat!=Image::SF_Indexed1 + /* Dat: /Indexed2 is not supported by /BMP */ + && cache->SampleFormat!=Image::SF_Indexed4 + && cache->SampleFormat!=Image::SF_Indexed8 + && cache->SampleFormat!=Image::SF_Rgb8) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Indexed1, /Indexed4, /Indexed8 or /Rgb8" << (Error*)0; + badp=true; + } + if (cache->TransferEncoding!=cache->TE_Binary) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Binary" << (Error*)0; + badp=true; + } + if (cache->Compression!=Rule::Cache::CO_None && cache->Compression!=Rule::Cache::CO_RLE) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Compression/None or /RLE" << (Error*)0; + badp=true; + } + if (cache->Compression==Rule::Cache::CO_RLE && cache->SampleFormat!=Image::SF_Indexed8) { + /* !! Imp: Implement compr==2 for /Indexed4 */ + Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP/RLE requires /Indexed8" << (Error*)0; + badp=true; + } + if (cache->hasPredictor()) { + Error::sev(Error::WARNING_DEFER) << "check_rule: /BMP requires /Predictor 1" << (Error*)0; + badp=true; + } + if (badp) return Rule::Applier::BAD; + /* Now we are sure about Rule::Applier::OK. */ + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_bmp_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + if (out_bmp_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + or_->doSampleFormat(sf); + Image::Sampled *img=sf->getImg(); + Image::Sampled::dimen_t wd=img->getWd(), ht=img->getHt(), htc=ht; + slen_t rlen=img->getRlen(); + char const*pend=img->getRowbeg()+rlen*ht; + char const*palbeg=img->getHeadp(), *palend=img->getRowbeg(); + slen_t ncols4=(palend-palbeg)/3*4; + unsigned biCompr=or_->cache.Compression!=or_->cache.CO_RLE ? 0 + : or_->cache.SampleFormat==Image::SF_Indexed4 ? 2 : 1; + + SimBuffer::B data; + slen_t crowsize=2+ rlen+(rlen+128)*2/255; /* !! Imp: real upper bound? */ + char *crow=new char[crowsize]; + /* !! GIMP compatibility */ + if (or_->cache.Compression==or_->cache.CO_RLE) { + /* Imp: bmp_compress2_row */ + char *crow2; + while (htc--!=0) { /* BMP stores rows from down */ + crow2=bmp_compress1_row(crow, pend-rlen, pend); +#if 0 + crow2=crow; + *crow2++=10; + *crow2++=1; + *crow2++=10; + *crow2++=2; + *crow2++=10; + *crow2++=3; + *crow2++=10; + *crow2++=4; + *crow2++=10; + *crow2++=5; + *crow2++=10; + *crow2++=6; +#endif + + assert((slen_t)(crow2-crow)<=crowsize-2); + pend-=rlen; + *crow2++='\0'; *crow2++='\0'; /* signal end of compressed data row */ + /* fprintf(stderr, "htc=%u cl=%u\n", htc, crow2-crow); */ + data.vi_write(crow, crow2-crow); + } + data.vi_write("\0\1", 2); /* signal end of bitmap */ + } else { + unsigned pad=(4-(rlen&3))&3; /* Dat: pad rows to 32 bits */ + if (or_->cache.SampleFormat==Image::SF_Rgb8) { /* SWAP RGB values */ + /* BUGFIX at Thu Dec 12 21:36:57 CET 2002 */ + char *buf=new char[rlen]; + while (htc--!=0) { /* BMP stores rows from down */ + char const*pxend=pend, *px=pend-=rlen; + char *q=buf; + while (px!=pxend) { + *q++=px[2]; + *q++=px[1]; + *q++=px[3]; + px+=3; + } + data.vi_write(buf, rlen); + if (pad!=0) data.vi_write("\0\0\0", pad); + } + delete [] buf; + } else { + while (htc--!=0) { /* BMP stores rows from down */ + data.vi_write(pend-=rlen, rlen); + if (pad!=0) data.vi_write("\0\0\0", pad); + } + } + } + assert(pend==img->getRowbeg()); + delete [] crow; + /* Now data is ready. */ + + char *bmphead=new char[54+ncols4+data.getLength()], *p=bmphead; + *p++='B'; *p++='M'; /* magic number header */ + lf32(p, 54+ncols4+data.getLength()); /* bfSize */ + lf32(p, 0); /* zzHotX, zzHotY */ + lf32(p, 54+ncols4); /* bfOffs */ + lf32(p, 40); /* biSize==40 => Windows 3.x style BMP file */ + lf32(p, wd); /* biWidth */ + lf32(p, ht); /* biHeight */ + lf16(p, 1); /* biPlanes, must be 1 */ + lf16(p, img->getBpc()*img->getCpp()); /* biBitCnt: bits per pixel (1, 4, 8: /Indexed*; 24: /Rgb8) */ + lf32(p, biCompr); /* biCompr */ + lf32(p, 0); /* biSizeIm */ + lf32(p, 0); /* biXPels */ + lf32(p, 0); /* biYPels */ + lf32(p, (palend-palbeg)/3); /* biClrUsed */ + lf32(p, (palend-palbeg)/3); /* biClrImp */ + /* vvv Now comes the palette (zero length for /Rgb) */ + while (palbeg!=palend) { + *p++=palbeg[2]; *p++=palbeg[1]; *p++=palbeg[0]; *p++='\0'; + palbeg+=3; + } + assert((slen_t)(p-bmphead)==54+ncols4); + + out.vi_write(bmphead, p-bmphead); delete [] bmphead; +#if 0 + out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + 100); + out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + 100); + out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + 100); + out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + 100); + out.vi_write("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + 100); +#endif + out << data; + out.vi_write(0,0); /* signal EOF */ + + return Rule::Applier::OK; +} + +Rule::Applier out_bmp_applier = { "BMP", out_bmp_check_rule, out_bmp_work, 0 }; + +/* --- Sun Mar 24 13:48:57 CET 2002 */ + +/** The Empty applier produces an empty output file. */ +Rule::Applier::cons_t out_empty_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_Empty + ) return Rule::Applier::DONT_KNOW; + return Rule::Applier::OK; +} +Rule::Applier::cons_t out_empty_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + (void)out; + (void)sf; + if (out_empty_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + return Rule::Applier::OK; +} + +Rule::Applier out_empty_applier = { "Empty", out_empty_check_rule, out_empty_work, 0 }; + +/* --- Sun Mar 24 13:48:57 CET 2002 */ + +/** The Meta applier writes some meta-information into the output file */ +Rule::Applier::cons_t out_meta_check_rule(Rule::OutputRule* or_) { + Rule::Cache *cache=&or_->cache; + if (cache->FileFormat!=cache->FF_Meta + ) return Rule::Applier::DONT_KNOW; + // return Rule::Applier::OK; /* Imp: implement the applier */ + return Rule::Applier::BAD; +} +Rule::Applier::cons_t out_meta_work(GenBuffer::Writable& out, Rule::OutputRule*or_, Image::SampledInfo *sf) { + (void)out; + (void)sf; + if (out_meta_check_rule(or_)!=Rule::Applier::OK) return Rule::Applier::DONT_KNOW; + /* Imp: real content here */ + return Rule::Applier::OK; +} + +Rule::Applier out_meta_applier = { "Meta", out_meta_check_rule, out_meta_work, 0 }; + +/* __END__ */ |