/* * 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 /** 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(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;pixelgetRlen(); 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>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>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>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<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 (pgetJPEGTables().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(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 -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__ */