1 /*
2  * image.cpp (formerly sampled.cpp)
3  * by pts@fazekas.hu at Wed Feb 27 09:26:05 CET 2002
4  */
5 
6 #ifdef __GNUC__
7 #ifndef __clang__
8 #pragma implementation
9 #endif
10 #endif
11 
12 #include "image.hpp"
13 #include "error.hpp"
14 #include <string.h> /* strlen() */
15 #include "gensio.hpp"
16 
17 /* --- 4-byte hashing */
18 
19 #if SIZEOF_INT>=4
20 typedef unsigned int  u32_t;
21 typedef   signed int  s32_t;
22 #else
23 typedef unsigned long u32_t;
24 typedef   signed long s32_t;
25 #endif
26 
27 /* vvv Dat: moved these out of Hash46 to pacify VC6.0 */
28   const unsigned M=1409;
29   /** Number of _value_ data bytes (they are not hashed) */
30   const unsigned D=2;
31   /** Size of each tuple in the array `t' */
32   const unsigned HD=4+D;
33   /** A tuple is considered free iff its first byte equals FREE */
34   const unsigned char FREE=255;
35 
36 
37 /**
38  * M=1409
39  * h(K)=K%1409
40  * h(i,K)=-i*(1+(K%1408)) (i in 0..1408)
41  * K=[a,r,g,b]=(a<<24)+(r<<16)+(g<<8)+b (4 bytes)
42  * h(K)=(253*a+722*r+(g<<8)+b)%1409
43  * h(i,K)=-i*(1+(896*a+768*r+(g<<8)+b)%1408)
44  *
45  * -- No dynamic growing or re-hashing. Useful for hashing colormap palettes
46  *    with a maximum size of 256.
47  * -- Deleting not supported.
48  *
49  * Implementation: Use 32 bit integers for calculation.
50  *
51  * Imp: remove unused attr `size'
52  */
53 class Hash46 {
54  public:
55   /** Creates an empty hash. */
56   Hash46();
getSize() const57   inline unsigned getSize() const { return size; }
getLength() const58   inline unsigned getLength() const { return size; }
getMaxSize() const59   inline unsigned getMaxSize() const { return M; }
isFull() const60   inline bool isFull() const { return size==M; }
61   /** @return NULLP or the pointer to a tuple */
lookup(unsigned char k[4])62   inline unsigned char* lookup(unsigned char k[4]) {
63     unsigned char *ret=walk(k);
64     return ret==NULLP || *ret==FREE ? (unsigned char*)NULLP : ret;
65   }
66   /** Can be called only if !isFull()
67    * @return NULL if isFull() and not found; otherwise: pointer to the tuple
68    *         found or the place to which the insert can take place.
69    */
70   unsigned char* walk(unsigned char k[4]);
71  protected:
72   /** Number of non-free tuples in the hash. */
73   unsigned size;
74   unsigned char t[M*HD];
75 };
76 
Hash46()77 Hash46::Hash46(): size(0) {
78   memset(t, FREE, sizeof(t));
79 }
80 
walk(unsigned char k[4])81 unsigned char *Hash46::walk(unsigned char k[4]) {
82   u32_t hk, hik;
83   hk=HD*(((((u32_t)1<<24)%M)*k[0]+(((u32_t)1<<16)%M)*k[1]+
84           (((u32_t)1<< 8)%M)*k[2]+k[3])%M);
85   hik=HD*(1+((((u32_t)1<<24)%(M-1))*k[0]+(((u32_t)1<<16)%(M-1))*k[1]+
86              (((u32_t)1<< 8)%(M-1))*k[2]+k[3])%(M-1));
87   /* fprintf(stderr, "hk=%u hik=%u\n", hk, hik); */
88   register unsigned char *p=t+hk;
89   unsigned i=M;
90   /* fprintf(stderr, "?? %02x %02x %02x %02x\n", k[0], k[1], k[2], k[3]); */
91   do {
92     /* fprintf(stderr, "examining %02x %02x %02x %02x %d\n", p[0], p[1], p[2], p[3], (k[0]=p[0] && k[1]==p[1] && k[2]==p[2])); */
93     if (*p==FREE || (k[0]==p[0] && k[1]==p[1] && k[2]==p[2] && k[3]==p[3])) return p;
94     /* ^^^ huge == BUGFIX at Sun Apr 14 00:16:59 CEST 2002 */
95     if (hk>=hik) { hk-=hik; p-=hik; }
96             else { hk+=M*HD-hik; p+=M*HD-hik; }
97   } while (--i!=0);
98   /* fprintf(stderr, "full\n"); */
99   return (unsigned char*)NULLP;
100 }
101 
102 /* --- */
103 
104 const unsigned char Image::Sampled::cs2cpp[6]= { 0, 1, 3, 3, 4, 4 };
cs2devcs(unsigned char cs)105 char const *Image::Sampled::cs2devcs(unsigned char cs) {
106   static const char *names[]={ (char*)NULLP, "Gray", "RGB", "RGB", "CMYK", "CMYK" };
107   return cs>=1 && cs<=5 ? names[cs] : (char*)NULLP;
108 }
109 
fatal_image_too_large()110 static void fatal_image_too_large() {
111   Error::sev(Error::EERROR) << "Image: Image too large." << (Error*)0;
112 }
113 
multiply_check(slen_t a,slen_t b)114 static slen_t multiply_check(slen_t a, slen_t b) {
115   const slen_t result = a * b;
116   /* Check for overflow. Works only if everything is unsigned. */
117   if (result / a != b) fatal_image_too_large();
118   return result;
119 }
120 
multiply_check(slen_t a,slen_t b,slen_t c)121 static slen_t multiply_check(slen_t a, slen_t b, slen_t c) {
122   return multiply_check(multiply_check(a, b), c);
123 }
124 
add_check(slen_t a,slen_t b)125 static slen_t add_check(slen_t a, slen_t b) {
126   /* Check for overflow. Works only if everything is unsigned. */
127   if (b > (slen_t)-1 - a) fatal_image_too_large();
128   return a + b;
129 }
130 
131 #if 0
132 static slen_t add_check(slen_t a, slen_t b, slen_t c) {
133   return add_check(add_check(a, b), c);
134 }
135 #endif
136 
add_check(slen_t a,slen_t b,slen_t c,slen_t d)137 static slen_t add_check(slen_t a, slen_t b, slen_t c, slen_t d) {
138   return add_check(add_check(a, b), add_check(c, d));
139 }
140 
init(slen_t l_comment,slen_t l_header,dimen_t wd_,dimen_t ht_,unsigned char bpc_,unsigned char ty_,unsigned char cpp_)141 void Image::Sampled::init(slen_t l_comment, slen_t l_header, dimen_t wd_, dimen_t ht_,
142   /* ^^^ 24 is required for /Transparent in out_tiff_work */
143   unsigned char bpc_, unsigned char ty_, unsigned char cpp_) {
144   /* Even if we continue from here, most probably we'll reach
145    * ``sam2p.yes: Error: applyProfile: invalid combination, no applicable OutputRule''.
146    * So more work is needed to support output images of size 0.
147    */
148   if (wd_ <= 0 || ht_ <= 0) Error::sev(Error::EERROR) << "Image: Image of size 0." << (Error*)0;
149   bpc=bpc_;
150   ty=ty_;
151   wd=wd_;
152   ht=ht_;
153   cpp=cpp_;
154   // pred=1;
155   transpc=0x1000000UL; /* Dat: this means: no transparent color */
156   const slen_t rlens = add_check(multiply_check(bpc_, cpp_, wd_), 7) >> 3;
157   rlen = rlens;
158   if (rlen != rlens) fatal_image_too_large();
159   beg=new char[len=add_check(l_comment, l_header, multiply_check(rlen, ht_), bpc)];
160   rowbeg=(headp=const_cast<char*>(beg)+l_comment)+l_header;
161   trail=const_cast<char*>(beg)+len-bpc;
162   memset(trail, 0, bpc);
163 }
164 
toGray0(unsigned char bpc_)165 Image::Gray*    Image::Sampled::toGray0(unsigned char bpc_) {
166   unsigned char *crow=new unsigned char[wd*3+7*3], *p, *pend;
167   Image::Gray *img=new Image::Gray(wd, ht, bpc_);
168   unsigned char *outp=(unsigned char*)img->getRowbeg();
169   dimen_t htc;
170   memset(crow+wd*3, '\0', 7*3); /* *3 BUGFIX at Tue Jan 18 17:04:15 CET 2005 */
171   unsigned i;
172   /* Dat: not optimising for minimal rounding error since caller should ensure
173    *      that there is no such error at all.
174    */
175   if (bpc_==1) {
176     assert(img->getBpc()==1);
177     for (htc=0;htc<ht;htc++) {
178       copyRGBRow((char*)crow, htc);
179       for (p=crow-3,pend=p+wd*3; p<pend; ) {
180         i =(*(p+=3)!=0)<<7; i|=(*(p+=3)!=0)<<6; i|=(*(p+=3)!=0)<<5; i|=(*(p+=3)!=0)<<4;
181         i|=(*(p+=3)!=0)<<3; i|=(*(p+=3)!=0)<<2; i|=(*(p+=3)!=0)<<1; i|=(*(p+=3)!=0);
182         *outp++=i;
183       }
184     }
185   } else if (bpc_==2) {
186     for (htc=0;htc<ht;htc++) {
187       copyRGBRow((char*)crow, htc);
188       for (p=crow-3,pend=p+wd*3; p<pend; ) {
189         i =(*(p+=3)/85)<<6; i|=(*(p+=3)/85)<<4; i|=(*(p+=3)/85)<<2; i|=(*(p+=3)/85);
190         *outp++=i;
191       }
192     }
193   } else if (bpc_==4) {
194     for (htc=0;htc<ht;htc++) {
195       copyRGBRow((char*)crow, htc);
196       for (p=crow-3,pend=p+wd*3; p<pend; ) {
197         i =(*(p+=3)/17)<<4; i|=(*(p+=3)/17);
198         *outp++=i;
199       }
200     }
201   } else if (bpc_==8) {
202     for (htc=0;htc<ht;htc++) {
203       copyRGBRow((char*)crow, htc);
204       for (p=crow-3,pend=p+wd*3; p!=pend; ) {
205         *outp++=*(p+=3);
206       }
207     }
208   } else assert(0);
209   delete [] crow;
210   return img;
211 }
toRGB0(unsigned char bpc_)212 Image::RGB*     Image::Sampled::toRGB0(unsigned char bpc_) {
213   unsigned char *crow=new unsigned char[wd*3+7], *p, *pend;
214   Image::RGB *img=new Image::RGB(wd, ht, bpc_);
215   unsigned char *outp=(unsigned char*)img->getRowbeg();
216   dimen_t htc;
217   memset(crow+wd*3, '\0', 7);
218   unsigned i;
219   /* Dat: not optimising for minimal rounding error since caller should ensure
220    *      that there is no such error at all.
221    */
222   if (bpc_==1) {
223     for (htc=0;htc<ht;htc++) {
224       copyRGBRow((char*)crow, htc);
225       for (p=crow,pend=crow+wd*3; p<pend; ) {
226         i =(*p++!=0)<<7; i|=(*p++!=0)<<6; i|=(*p++!=0)<<5; i|=(*p++!=0)<<4;
227         i|=(*p++!=0)<<3; i|=(*p++!=0)<<2; i|=(*p++!=0)<<1; i|=(*p++!=0);
228         *outp++=i;
229       }
230     }
231   } else if (bpc_==2) {
232     for (htc=0;htc<ht;htc++) {
233       copyRGBRow((char*)crow, htc);
234       for (p=crow,pend=crow+wd*3; p<pend; ) {
235         i =(*p++/85)<<6; i|=(*p++/85)<<4; i|=(*p++/85)<<2; i|=(*p++/85);
236         *outp++=i;
237       }
238     }
239   } else if (bpc_==4) {
240     for (htc=0;htc<ht;htc++) {
241       copyRGBRow((char*)crow, htc);
242       for (p=crow,pend=crow+wd*3; p<pend; ) {
243         i =(*p++/17)<<4; i|=(*p++/17);
244         *outp++=i;
245       }
246     }
247   } else if (bpc_==8) {
248     for (htc=0;htc<ht;htc++) {
249       copyRGBRow((char*)outp, htc);
250       outp+=wd*3;
251     }
252   } else assert(0);
253   delete [] crow;
254   return img;
255 }
256 
toIndexed0()257 Image::Indexed* Image::Sampled::toIndexed0()/* const*/ {
258   unsigned char *crow=new unsigned char[wd*3], k[6], *p, *pend, *w;
259   Image::Indexed *img=new Image::Indexed(wd, ht, 256, 8);
260   dimen_t htc;
261   unsigned char *pal=(unsigned char*)img->getHeadp(), *outp=(unsigned char*)img->getRowbeg();
262   unsigned ncols=0;
263   Hash46 h;
264   k[0]=0;
265   for (htc=0;htc<ht;htc++) {
266     copyRGBRow((char*)crow, htc);
267     for (p=crow,pend=crow+wd*3; p!=pend; p+=3) {
268       memcpy(k+1, p, 3);
269       w=h.walk(k);
270       assert(w!=NULL); /* Hash cannot be full since h.M>=256. */
271       /* fprintf(stderr, "w=%p\n", w); */
272       if (*w==/*h.*/FREE) {
273         if (ncols==256) { delete img; delete [] crow; return (Image::Indexed*)NULLP; }
274         /* ^^^ too many colors; cannot convert image to indexed */
275         memcpy(w,k,4);
276         memcpy(pal,k+1,3);
277         /* fprintf(stderr,"newcol=%02x #%02x%02x%02x\n", k[0], k[1], k[2], k[3]); */
278         /* fprintf(stderr,"newcol=pal #%02x%02x%02x\n", pal[0], pal[1], pal[2]); */
279         pal+=3;
280         *outp++=w[4]=ncols++;
281       } else { /* a color that we have already seen */
282         *outp++=w[4];
283       }
284     }
285   }
286   img->setNcolsMove(ncols);
287   delete [] crow;
288 
289   /* Now img is ready. The user should call packPal() to make it even tighter. */
290   return img;
291 }
292 
to8mul()293 void Image::Sampled::to8mul() {
294   if (bpc==8) return;
295   if (wd==0 || ht==0) { bpc=8; return; }
296 
297   unsigned oldBpc=bpc;
298   slen_t wdcpp=wd*cpp;
299   const char *oldBeg=beg;
300   unsigned char *p=(unsigned char*)rowbeg;
301 
302   bpc=8;
303   rlen=wd;
304   beg=new char[len=rowbeg-oldBeg+rlen*ht+bpc];
305   headp= const_cast<char*>(beg)+(headp-oldBeg);
306   rowbeg=const_cast<char*>(beg)+(rowbeg-oldBeg);
307   trail= const_cast<char*>(beg)+len-bpc;
308   memcpy(const_cast<char*>(beg), oldBeg, rowbeg-beg);
309 
310   unsigned char *to=(unsigned char*)rowbeg, *toend;
311   unsigned int i, j;
312   Image::Sampled::dimen_t htc;
313   if (oldBpc==1) {
314     htc=ht; while (htc--!=0) {
315       toend=to+((wdcpp)&~7);
316       while (to!=toend) {
317         i=*p++;
318         *to++=(i>>7)*255;
319         *to++=((i>>6)&1)*255;
320         *to++=((i>>5)&1)*255;
321         *to++=((i>>4)&1)*255;
322         *to++=((i>>3)&1)*255;
323         *to++=((i>>2)&1)*255;
324         *to++=((i>>1)&1)*255;
325         *to++=(     i&1)*255;
326       }
327       if (0!=(j=(wdcpp)&7)) {
328         i=*p; /* No mem overrun, even if (wd&7)==0 */
329         while (j--!=0) { *to++=(i>>7)*255; i<<=1; }
330       }
331     }
332   } else if (oldBpc==2) {
333     htc=ht; while (htc--!=0) {
334       toend=to+((wdcpp)&~3);
335       while (to!=toend) {
336         i=*p++;
337         *to++=(i>>6)*85;
338         *to++=((i>>4)&3)*85;
339         *to++=((i>>2)&3)*85;
340         *to++=(     i&3)*85;
341       }
342       if (0!=(j=(wdcpp)&3)) {
343         i=*p; /* No mem overrun, even if (wd&7)==0 */
344         while (j--!=0) { *to++=(i>>6)*85; i<<=2; }
345       }
346     }
347   } else if (oldBpc==4) {
348     htc=ht; while (htc--!=0) {
349       toend=to+((wdcpp)&~1);
350       while (to!=toend) {
351         i=*p++;
352         *to++=(i>>4)*17;
353         *to++=(     i&15)*17;
354       }
355       if (0!=((wdcpp)&1)) *to++=(*p++>>4)*17;
356     }
357   } else assert(0 && "invalid bpc");
358 
359   delete [] const_cast<char*>(oldBeg);
360 }
361 
to8nomul()362 void Image::Sampled::to8nomul() {
363   if (bpc==8) return;
364   if (wd==0 || ht==0) { bpc=8; return; }
365 
366   unsigned oldBpc=bpc;
367   slen_t wdcpp=wd*cpp;
368   const char *oldBeg=beg;
369   unsigned char *p=(unsigned char*)rowbeg;
370 
371   bpc=8;
372   rlen=wd;
373   beg=new char[len=rowbeg-oldBeg+rlen*ht+bpc];
374   headp= const_cast<char*>(beg)+(headp-oldBeg);
375   rowbeg=const_cast<char*>(beg)+(rowbeg-oldBeg);
376   trail= const_cast<char*>(beg)+len-bpc;
377   memcpy(const_cast<char*>(beg), oldBeg, rowbeg-beg);
378 
379   unsigned char *to=(unsigned char*)rowbeg, *toend;
380   unsigned int i, j;
381   Image::Sampled::dimen_t htc;
382   if (oldBpc==1) {
383     htc=ht; while (htc--!=0) {
384       toend=to+((wdcpp)&~7);
385       while (to!=toend) {
386         i=*p++;
387         *to++=(i>>7);
388         *to++=((i>>6)&1);
389         *to++=((i>>5)&1);
390         *to++=((i>>4)&1);
391         *to++=((i>>3)&1);
392         *to++=((i>>2)&1);
393         *to++=((i>>1)&1);
394         *to++=(     i&1);
395       }
396       if (0!=(j=(wdcpp)&7)) {
397         i=*p++; /* No mem overrun, even if (wd&7)==0 */
398         while (j--!=0) { *to++=(i>>7); i<<=1; }
399       }
400     }
401   } else if (oldBpc==2) {
402     // assert(0);
403     htc=ht; while (htc--!=0) {
404       toend=to+((wdcpp)&~3);
405       while (to!=toend) {
406         i=*p++;
407         *to++=(i>>6);
408         *to++=((i>>4)&3);
409         *to++=((i>>2)&3);
410         *to++=(     i&3);
411       }
412       if (0!=(j=(wdcpp)&3)) {
413         i=*p++;
414         // fprintf(stderr,"j=%d\n",j);
415         while (j--!=0) { *to++=(i>>6); i<<=2; }
416       }
417     }
418     assert((slen_t)((char*)to-rowbeg)==(slen_t)wd*cpp*ht);
419   } else if (oldBpc==4) {
420     htc=ht; while (htc--!=0) {
421       toend=to+((wdcpp)&~1);
422       while (to!=toend) {
423         i=*p++;
424         *to++=(i>>4);
425         *to++=(     i&15);
426       }
427       if (0!=((wdcpp)&1)) *to++=(*p++>>4);
428     }
429   } else assert(0 && "invalid bpc");
430 
431   delete [] const_cast<char*>(oldBeg);
432 }
433 
minRGBBpc() const434 unsigned char Image::Sampled::minRGBBpc() const {
435   unsigned char *crow=new unsigned char[wd*3], *p, *pend=crow+wd*3;
436   register unsigned minbpb=0;
437   dimen_t htc;
438   for (htc=0;htc<ht;htc++) {
439     copyRGBRow((char*)crow, htc);
440     for (p=crow; p!=pend; p++) {
441       if ((*p&15)*17!=*p) { delete [] crow; return 8; } /* 4 bits are not enough */
442       else if ((*p&3)*85!=*p) minbpb=3; /* 2 bits are not enough */
443       else if ((*p&1)*255!=*p) minbpb|=1; /* 1 bit is not enough */
444     }
445   }
446   delete [] crow;
447   return 1+minbpb;
448 }
449 
hasPixelRGB(Image::Sampled::rgb_t rgb) const450 bool Image::Sampled::hasPixelRGB(Image::Sampled::rgb_t rgb) const {
451   /* by pts@fazekas.hu at Sat Jan  8 13:24:19 CET 2005 */
452   /* Dat: this dumb implementation will be overridden */
453   if (rgb>0xffffffUL) return false;
454   unsigned char *crow=new unsigned char[wd*3], *p, *pend=crow+wd*3, t[3];
455   dimen_t htc;
456   t[0]=(rgb>>16)&255; t[1]=(rgb>>8)&255; t[2]=rgb&255;
457   for (htc=0;htc<ht;htc++) {
458     copyRGBRow((char*)crow, htc);
459     for (p=crow; p!=pend; p+=3) {
460       if (t[0]==p[0] && t[1]==p[1] && t[2]==p[2]) { delete [] crow; return true; }
461     }
462   }
463   delete [] crow;
464   return false;
465 }
466 
hasPixelRGB(Image::Sampled::rgb_t rgb) const467 bool Image::Gray::hasPixelRGB(Image::Sampled::rgb_t rgb) const {
468   /* by pts@fazekas.hu at Sat Jan  8 13:24:19 CET 2005 */
469   /* Dat: faster than Image::Sampled::hasPixelRGB */
470   if (rgb>0xffffffUL) return false;
471   unsigned char t[3];
472   t[0]=(rgb>>16)&255; t[1]=(rgb>>8)&255; t[2]=rgb&255;
473   if (t[0]!=t[1] || t[0]!=t[2]) return false;
474   if (bpc==8) {
475     unsigned char *p=(unsigned char*)rowbeg, *pend=p+wd*ht;
476     /* Imp: use memchr() if available */
477     while (p!=pend && t[0]!=p[0]) p++;
478     return p!=pend;
479   }
480   unsigned char *crow=new unsigned char[wd*3], *p, *pend=crow+wd*3;
481   dimen_t htc;
482   for (htc=0;htc<ht;htc++) {
483     copyRGBRow((char*)crow, htc); /* Imp: avoid this if bpp==8 */
484     p=crow; while (p!=pend && t[0]!=p[0]) p+=3;
485     if (p!=pend) { delete [] crow; return true; }
486   }
487   delete [] crow;
488   return false;
489 }
490 
addAlpha0(Image::Indexed * iimg,Image::Gray * al)491 Image::Indexed* Image::Sampled::addAlpha0(Image::Indexed *iimg, Image::Gray *al) {
492   if (iimg==NULLP) Error::sev(Error::EERROR) << "addAlpha: too many colors, transparency impossible" << (Error*)0;
493   iimg->to8();
494   unsigned ncols;
495   if ((ncols=iimg->getNcols()) == 256) {
496     iimg->packPal();  // TODO: Do a more lightweight palette packing if there are only 2 colors (such as PNG import through PNM).
497     if ((ncols=iimg->getNcols())==256) Error::sev(Error::EERROR) << "addAlpha: too many colors, transparency impossible" << (Error*)0;
498   }
499   iimg->setNcolsMove(ncols+1);
500   /* fprintf(stderr,"old ncols=%u\n", ncols); */
501   iimg->setPal(ncols,0); /* black */
502   iimg->setTransp(ncols);
503   assert(iimg->getRlen()==iimg->getWd());
504   assert(iimg->getWd()==al->getWd());
505   char *p=iimg->getRowbeg(), *pend=p+iimg->getRlen()*iimg->getHt(), *alq=al->getRowbeg();
506   while (p!=pend) {
507     if ((unsigned char)*alq++!=255) *p=ncols; /* make it transparent */
508     p++;
509   }
510   return iimg;
511 }
512 
513 /* --- */
514 
Indexed(Image::Sampled::dimen_t wd_,Image::Sampled::dimen_t ht_,unsigned short ncols_,unsigned char bpc_)515 Image::Indexed::Indexed(Image::Sampled::dimen_t wd_, Image::Sampled::dimen_t ht_, unsigned short ncols_, unsigned char bpc_) {
516   param_assert(ncols_<=256);
517   /* vvv Dat: `3' is here for an extra palette entry */
518   init(3,3*ncols_,wd_,ht_,bpc_,TY_INDEXED,1);
519   transp=-1;
520   cs=CS_Indexed_RGB;
521 }
setNcols(unsigned short ncols_)522 void Image::Indexed::setNcols(unsigned short ncols_) {
523   headp=rowbeg-ncols_*3;
524 }
setNcolsMove(unsigned short ncols_)525 void Image::Indexed::setNcolsMove(unsigned short ncols_) {
526   param_assert(ncols_<=256);
527   unsigned ncols=getNcols();
528   if (ncols_==ncols) return;
529   if (ncols_<ncols || (slen_t)(headp-beg)>=(ncols_-ncols)*3) {
530     memmove(rowbeg-ncols_*3, headp, (ncols_<ncols ? ncols_ : ncols)*3);
531     /* ^^^ *3 BUGFIX at Sun Apr 14 00:50:34 CEST 2002 */
532   } else { /* Imp: test this routine */
533     /* Tue Jun 11 16:22:52 CEST 2002 */
534     assert(ncols_>ncols);
535     const char *oldBeg=beg, *oldHeadp=headp, *oldRowbeg=rowbeg, *oldEnd=beg+len;
536     slen_t delta=(ncols_-ncols)*3;
537     // substr_grow(headp-oldBeg, ncols*3, ncols_*3); /* no such method */
538     beg=new char[len+delta];
539     headp= const_cast<char*>(beg)+(headp-oldBeg);
540     rowbeg=const_cast<char*>(beg)+(rowbeg-oldBeg)+delta;
541     trail= const_cast<char*>(beg)+(trail-oldBeg)+delta;
542     assert(beg+(headp-oldBeg)==rowbeg-ncols_*3);
543     /* Dat: this->xoffs is left unchanged */
544     memcpy(headp,  oldHeadp,  oldRowbeg-oldHeadp);
545     memcpy(rowbeg, oldRowbeg, oldEnd-oldRowbeg);
546     delete [] const_cast<char*>(oldBeg);
547   }
548   headp=rowbeg-ncols_*3;
549 }
setPal(unsigned char color,Image::Sampled::rgb_t rgb)550 void Image::Indexed::setPal(unsigned char color, Image::Sampled::rgb_t rgb) {
551   assert(color<(rowbeg-headp)/3);
552   unsigned char *p=(unsigned char*)headp+3*color;
553   *p++=rgb>>16;
554   *p++=rgb>>8;
555   *p=rgb;
556 }
setTransp(unsigned char color)557 void Image::Indexed::setTransp(unsigned char color) {
558   // param_assert(color>=0); /* always */
559   assert(transp==-1);
560   transp=color;
561   unsigned char *p=(unsigned char*)headp+3*color;
562   transpc=((Image::Sampled::rgb_t)p[0]<<16)+(p[1]<<8)+p[2];
563 }
564 
setTranspc(rgb_t color)565 bool Image::Indexed::setTranspc(rgb_t color) {
566   if (color!=0x1000000UL && color!=transpc) {
567     char t[3];
568     t[0]=color>>16; t[1]=color>>8; t[2]=color;
569     char *p=headp, *pend=rowbeg;
570     while (p!=pend) { /* Examine the palette. */
571       if (p[0]==t[0] && p[1]==t[1] && p[2]==t[2]) {
572         transpc=color;
573         transp=(p-headp)/3; /* destroy old transparency */
574       }
575       p+=3;
576     }
577   }
578   return transp!=-1;
579 }
580 
wouldSetTranspc(rgb_t color) const581 bool Image::Indexed::wouldSetTranspc(rgb_t color) const {
582   if (transp!=-1) return true;
583   if (color!=0x1000000UL && color!=transpc) {
584     char t[3];
585     t[0]=color>>16; t[1]=color>>8; t[2]=color;
586     char *p=headp, *pend=rowbeg;
587     while (p!=pend) { /* Examine the palette. */
588       if (p[0]==t[0] && p[1]==t[1] && p[2]==t[2]) return true;
589       p+=3;
590     }
591   }
592   return false;
593 }
594 
setTranspcAndRepack(rgb_t color)595 void Image::Indexed::setTranspcAndRepack(rgb_t color) {
596   if (!(color!=0x1000000UL && color!=transpc)) return;
597   char t[3];
598   t[0]=color>>16; t[1]=color>>8; t[2]=color;
599   char *p=headp, *pend=rowbeg;
600   bool need_repack = false;
601   while (p!=pend) { /* Examine the palette. */
602     if (p[0]==t[0] && p[1]==t[1] && p[2]==t[2]) {
603       transpc=color;
604       transp=(p-headp)/3; /* destroy old transparency */
605       need_repack = true;
606     }
607     p+=3;
608   }
609   if (need_repack) {
610     const unsigned char old_bpc = bpc;
611     packPal();  /* May change bpc. */
612     setBpc(old_bpc);
613   }
614 }
615 
to8()616 void Image::Indexed::to8() { to8nomul(); }
toIndexed()617 Image::Indexed* Image::Indexed::toIndexed()/* const*/ { return this; }
toRGB(unsigned char bpc_)618 Image::RGB*     Image::Indexed::toRGB(unsigned char bpc_)/* const*/ { return toRGB0(bpc_); }
toGray(unsigned char bpc_)619 Image::Gray*    Image::Indexed::toGray(unsigned char bpc_)/* const*/ { return toGray0(bpc_); }
canGray() const620 bool Image::Indexed::canGray() const {
621   char *p=headp, *pend=rowbeg, *tp=p+transp*3;
622   /* ignore transparent color at Sat Jun 15 15:18:24 CEST 2002 */
623   if (transp!=-1 && tp!=pend-3) {
624     while (p!=pend) { /* Examine the palette. */
625       if (p!=tp && (p[0]!=p[1] || p[1]!=p[2])) return false; /* Found a non-gray color. */
626       p+=3;
627     }
628   } else {
629     if (transp!=-1 && tp==pend-3) pend-=3; /* both conditions are important */
630     while (p!=pend) { /* Examine the palette. */
631       if (p[0]!=p[1] || p[1]!=p[2]) return false; /* Found a non-gray color. */
632       p+=3;
633     }
634   }
635   return true;
636 }
minRGBBpc() const637 unsigned char Image::Indexed::minRGBBpc() const {
638   unsigned char *p=(unsigned char*)headp, *pend=(unsigned char*)rowbeg;
639   unsigned char *tp=p+transp*3;
640   /* ignore transparent color at Sat Jun 15 15:18:24 CEST 2002 */
641   register unsigned minbpb=0;
642   while (p!=pend) { /* Examine the palette. */
643     if (p==tp) { p+=3; continue; } /* ignore transparent color */
644     if ((*p&15)*17!=*p) return 8; /* 4 bits are not enough */
645     else if ((*p&3)*85!=*p) minbpb=3; /* 2 bits are not enough */
646     else if ((*p&1)*255!=*p) minbpb|=1; /* 1 bit is not enough */
647     p++;
648   }
649   return 1+minbpb;
650 }
copyRGBRow(char * to,Image::Sampled::dimen_t whichrow) const651 void Image::Indexed::copyRGBRow(char *to, Image::Sampled::dimen_t whichrow) const {
652   param_assert(whichrow<ht);
653   if (wd==0) return;
654   unsigned char *p=(unsigned char*)rowbeg+rlen*whichrow;
655   char *r, *toend=to+3*wd;
656   unsigned int i, j;
657 
658   if (bpc==1) {
659     toend-=3*(wd&7);
660     while (to!=toend) {
661       i=*p++;
662       r=headp+3*(i>>7);     *to++=*r++; *to++=*r++; *to++=*r++;
663       r=headp+3*((i>>6)&1); *to++=*r++; *to++=*r++; *to++=*r++;
664       r=headp+3*((i>>5)&1); *to++=*r++; *to++=*r++; *to++=*r++;
665       r=headp+3*((i>>4)&1); *to++=*r++; *to++=*r++; *to++=*r++;
666       r=headp+3*((i>>3)&1); *to++=*r++; *to++=*r++; *to++=*r++;
667       r=headp+3*((i>>2)&1); *to++=*r++; *to++=*r++; *to++=*r++;
668       r=headp+3*((i>>1)&1); *to++=*r++; *to++=*r++; *to++=*r++;
669       r=headp+3*(     i&1); *to++=*r++; *to++=*r++; *to++=*r++;
670     }
671     i=*p; /* No mem overrun, even if (wd&7)==0 */
672     j=wd&7;
673     while (j--!=0) { r=headp+3*(i>>7); *to++=*r++; *to++=*r++; *to++=*r++; i<<=1; }
674   } else if (bpc==2) {
675     toend-=3*(wd&3);
676     while (to!=toend) {
677       i=*p++;
678       r=headp+3*(i>>6);     *to++=*r++; *to++=*r++; *to++=*r++;
679       r=headp+3*((i>>4)&3); *to++=*r++; *to++=*r++; *to++=*r++;
680       r=headp+3*((i>>2)&3); *to++=*r++; *to++=*r++; *to++=*r++;
681       r=headp+3*(     i&3); *to++=*r++; *to++=*r++; *to++=*r++;
682     }
683     i=*p; /* No mem overrun, even if (wd&7)==0 */
684     j=wd&3;
685     while (j--!=0) { r=headp+3*(i>>6); *to++=*r++; *to++=*r++; *to++=*r++; i<<=2; }
686   } else if (bpc==4) {
687     toend-=3*(wd&1);
688     while (to!=toend) {
689       i=*p++;
690       r=headp+3*(i>>4);      *to++=*r++; *to++=*r++; *to++=*r++;
691       r=headp+3*(     i&15); *to++=*r++; *to++=*r++; *to++=*r++;
692     }
693     if (0!=(wd&1)) { r=headp+3*(*p>>4); *to++=*r++; *to++=*r++; *to++=*r++; }
694   } else if (bpc==8) {
695     // fprintf(stderr, "p=%u pp=%u ppp=%u\n", p[0], p[1], p[2]);
696     while (to!=toend) {
697       r=headp+3**p++; *to++=*r++; *to++=*r++; *to++=*r++;
698     }
699   } else assert(0 && "invalid bpc");
700 }
packPal()701 void Image::Indexed::packPal() {
702   /* Convert samples, make bpc=8. */
703   to8();
704 
705   unsigned oldNcols=getNcols();
706   unsigned char *p, *pend;
707   assert((rowbeg-headp)%3==0);
708   assert(transp>=-1);
709   assert(transp<(int)oldNcols);
710   if (oldNcols<=1) return; /* Cannot optimize further. */
711 
712   /* Find unused colors. old2new[c]=(is c used at least once)?1:0 */
713   unsigned char old2new[256], newpal[768];
714   memset(old2new, 0, sizeof(old2new));
715   for (p=(unsigned char*)rowbeg, pend=p+wd*ht; p!=pend; p++) old2new[*p]=1;
716 
717   /* Find and eliminate duplicate colors. Build the new palette in the
718    * beginning of newpal. Use a Hash46 for a quick lookup of colors already
719    * seen. Use the previously computed old2new, but also overwrite it.
720    */
721   Hash46 h;
722   int newTransp=-1;
723   unsigned char *op=old2new, *opend=op+oldNcols, *w, k[6],
724                 *ptransp=(unsigned char*)headp+3*transp; /* ==p-3 if no transparent color */
725   /* ^^^ headp BUGFIX at Fri Mar 22 18:02:18 CET 2002 */
726   p=(unsigned char*)headp;
727   // fprintf(stderr, "oldNcols=%d\n", (int)oldNcols);
728   unsigned newNcols=0;
729   while (op!=opend) {
730     // fprintf(stderr, "color=%d %d\n", (int)(op-old2new), p-ptransp);
731     if (0!=*op) { /* Map the color only if it is used in the image. */
732       // fprintf(stderr, "used=%d\n", (int)(op-old2new));
733       if (p==ptransp) { k[0]=1; k[1]=k[2]=k[3]=0; newTransp=newNcols; }
734                  else { k[0]=0; memcpy(k+1,p,3); }
735       w=h.walk(k);
736       assert(w!=NULL); /* Hash cannot be full since h.M>=256. */
737       if (*w==/*h.*/FREE) {
738         memcpy(newpal+3*newNcols, p /* k+1 */, 3);
739         /* ^^^ side effect: make the transparent color black */
740         memcpy(w,k,4); w[4]=newNcols; *op=newNcols++;
741       } else *op=w[4];
742     }
743     p+=3; op++;
744   }
745   // fprintf(stderr,"newTransp=%d transp=%d\n", newTransp, transp);
746 
747   // assert((newTransp==-1) == (transp==-1));
748   assert(newTransp==-1 || transp!=-1);
749   /* ^^^ BUGFIX: == not true, because image may have transparency, but no
750    *     transparent pixels.
751    */
752   assert((char*)p==headp+oldNcols*3);
753   if (newNcols==oldNcols && transp==newTransp) {
754     /* Could not change # colors. */
755     if (transp==-1) goto done;
756     if ((unsigned)transp==oldNcols-1) { setPal(transp, 0); goto done; }
757   }
758 
759   /* Make the transparent color last. */
760   if (newTransp!=-1 && newTransp!=(int)newNcols-1) {
761     assert(transp!=-1);
762     unsigned newLast=newNcols-1;
763     memcpy(newpal+3*newTransp, newpal+3*newLast, 3);
764     memset(newpal+3*newLast, 0, 3); transpc=0; /* make it black */
765     for (op=old2new; op!=opend; op++) if (*op==newLast) *op=newTransp;
766     old2new[transp]=newLast;
767     transp=newTransp=newLast;
768     p=newpal+newTransp*3;
769   }
770 
771   /* Update the image. */
772   for (p=(unsigned char*)rowbeg, pend=p+wd*ht; p!=pend; p++) {
773     assert(*p<oldNcols);
774     *p=old2new[*p];
775   }
776 
777   /* Update the palette. */
778   headp=rowbeg-3*newNcols;  /* public method getNcols() uses rowbeg-headp */
779   memcpy(headp, newpal, 3*newNcols);
780   transp=newTransp;
781   /* vvv BUGFIX at Tue May 21 13:10:30 CEST 2002 */
782   if (newTransp==-1) transpc=0x1000000UL; /* Dat: this means: no transparent color */
783                 else { transp=-1; setTransp(newTransp); }
784 
785  done:
786   sortPal();
787 }
sortPal()788 void Image::Indexed::sortPal() {
789   /* Run packPal() first (but it's recursive!) if transp is in the middle. */
790   unsigned ncols = getNcols(), i;
791   assert(transp == -1 || transp + 0U == ncols - 1);
792   assert(ncols <= 256);
793   if (ncols == 0) return;  /* Safe if ncols == 0 and transp == -1. */
794   if (transp + 0U == ncols - 1) --ncols;
795   if (ncols <= 1) return;
796   #if SIZEOF_SHORT>=4
797     typedef unsigned short d_t;
798   #elif SIZEOF_INT>=4
799     typedef unsigned d_t;
800   #else
801     typedef unsigned long d_t;
802   #endif
803   d_t d[256];
804   unsigned char *p, *pend;
805   for (i = 0, p = (unsigned char*)headp; i < ncols; ++i, p += 3) {
806     d[i] = (d_t)p[0] << 24 | (d_t)p[1] << 16 | (d_t)p[2] << 8 | i;
807     /*printf("c[%d]=0x%08x\n", i, d[i]);*/
808   }
809   for (i = 1; i < ncols; ++i) {
810     if (d[i] < d[i - 1]) break;
811   }
812   if (i >= ncols) return;  /* Palette already sorted. */
813 
814   /* Heap sort (unstable). Based on Knuth's TAOCP 5.2.3.H .
815    * Although heap sort is unstable, sortPal implements a stable sort, because
816    * the color index (i) is included in the sorted number (d[i]).
817    */
818   { unsigned k;
819     d_t tmp, *i, *j, *l=d+(ncols>>1), *r=d+ncols-1;
820     while (1) { /* h2: */
821       k=l-d;
822       if (k!=0) {
823         tmp=*--l;
824       } else {
825         tmp=*r; *r=d[0];
826         if (--r==d) { d[0]=tmp; break; }
827         k++;
828       }
829       i=j=l;
830       while ((j+=k)<=r) { /* h4: */
831         k<<=1;
832         if (j<r && j[0]<j[1]) {
833           if (!(tmp<*++j)) break;
834           k++;
835         } else if (!(tmp<*j)) break;
836         *i=*j; i=j;
837       }
838       /* h8: */
839       *i=tmp;
840     }
841   }
842 
843   unsigned char old2new[256];
844   for (i = 0, p = (unsigned char*)headp; i < ncols; ++i) {
845     d_t di = d[i];
846     /*printf("d[%d]=0x%08x\n", i, di);*/
847     assert((i == 0 || di >= d[i - 1]) && "bug in sorting palette");
848     old2new[di & 255] = i;
849     *p++ = di >> 24;  *p++ = di >> 16;  *p++ = di >> 8;
850   }
851 
852   /* Update the image. */
853   for (p=(unsigned char*)rowbeg, pend=p+wd*ht; p!=pend; p++) {
854     *p=old2new[*p];
855   }
856 }
857 
delete_separated(register Indexed ** p)858 void Image::Indexed::delete_separated(register Indexed **p) {
859   while (*p!=NULLP) delete *p++;
860 }
separate()861 Image::Indexed **Image::Indexed::separate() {
862   assert(getNcols()>=1);
863   unsigned char ncols1=getNcols()-1;
864   signed nncols=getNcols()-(transp==-1 ? 0 : 1);
865   register unsigned char curcol;
866   Indexed **ret=new Indexed*[nncols+1], **curimg=ret;
867   Image::Sampled::dimen_t htc;
868   assert(cpp==1);
869   slen_t wdcpp=wd/* *cpp*/;
870   register unsigned char *p;
871   char *to, *toend;
872   register unsigned int i;
873 
874   ret[nncols]=(Indexed*)NULLP;
875   to8();
876   for (curcol=0; curcol<=ncols1; curcol++) {
877     if (transp==(signed int)curcol) continue;
878     curimg[0]=new Indexed(wd, ht, /*ncols:*/2, /*bpc:*/1);
879     memcpy(curimg[0]->headp, headp+3*curcol, 3); /* copy the color value */
880     curimg[0]->setTransp(1);
881     to=curimg[0]->rowbeg; p=(unsigned char*)rowbeg;
882     htc=ht; while (htc--!=0) {
883       toend=to+((wdcpp+7)>>3);
884       while (to!=toend) {
885         i =(*p++!=curcol)<<7; i|=(*p++!=curcol)<<6;
886         i|=(*p++!=curcol)<<5; i|=(*p++!=curcol)<<4;
887         i|=(*p++!=curcol)<<3; i|=(*p++!=curcol)<<2;
888         i|=(*p++!=curcol)<<1; i|=(*p++!=curcol);
889         *to++=i;
890       }
891       if (0!=(wdcpp&7)) p+=(wdcpp&7)-8; /* negative */
892     }
893     curimg++;
894   }
895   assert(curimg==ret+nncols);
896   return ret;
897 }
898 
calcAlpha()899 Image::Indexed *Image::Indexed::calcAlpha() {
900   /* by pts@fazekas.hu at Tue Jun  4 21:27:29 CEST 2002 */
901   assert(getNcols()>=1);
902   packPal(); /* removes transparency if no transparent pixel */
903   if (transp==-1) return (Image::Indexed*)NULLP;
904   to8();
905   Indexed *ret=new Indexed(wd, ht, /*ncols:*/2, /*bpc:*/1);
906   Image::Sampled::dimen_t htc;
907   assert(cpp==1);
908   slen_t wdcpp=wd/* *cpp*/;
909   register unsigned char *p;
910   char *to, *toend;
911   register unsigned int i, i8, i7;
912   unsigned char transpx=transp;
913 
914   ret->headp[0]=ret->headp[1]=ret->headp[2]='\xFF'; /* white */
915   ret->headp[3]=ret->headp[4]=ret->headp[5]='\x00'; /* black, transparent */
916   ret->setTransp(1);
917   to=ret->rowbeg; p=(unsigned char*)rowbeg;
918   assert(transpx!=0);
919   #if 0
920     printf("tx=%u\n", transpx);
921     printf("%u %u %u\n", headp[0], headp[1], headp[2]);
922   #endif
923   htc=ht; while (htc--!=0) {
924     // putchar('.'); printf("mod=%d\n",(to-ret->rowbeg)%ret->rlen);
925     // assert((to-ret->rowbeg)%ret->rlen==0);
926     toend=to+(wdcpp>>3); /* ((wdcpp+7)>>3)-1; */
927     /* ^^^ BUGFIX at Tue Sep 17 11:08:46 CEST 2002 */
928     assert(toend>=to);
929     while (to!=toend) {
930       #if 1 /* add ->pal[0] funcitonality at Sat Jun 15 14:24:25 CEST 2002 */
931         i=0; i8=256;
932         /* vvv p[-1]=0 BUGFIX at Sun Dec  8 23:21:47 CET 2002 */
933         while ((i8>>=1)!=0) if (*p++==transpx) { p[-1]=0; i|=i8; }
934       #else
935         i =(*p++==transpx)<<7; i|=(*p++==transpx)<<6;
936         i|=(*p++==transpx)<<5; i|=(*p++==transpx)<<4;
937         i|=(*p++==transpx)<<3; i|=(*p++==transpx)<<2;
938         i|=(*p++==transpx)<<1; i|=(*p++==transpx);
939       #endif
940       *to++=i;
941     }
942     #if 1 /* This works even when p gets modified; this puts fixed 0 pads at EOLs */
943       if ((wdcpp&7)!=0) {
944         i7=1<<(7-(wdcpp&7)); i8=256; i=0;
945         /* vvv p[-1]=0 BUGFIX at Sun Dec  8 23:21:47 CET 2002 */
946         while ((i8>>=1)!=i7) if (*p++==transpx) { p[-1]=0; i|=i8; }
947         *to++=i;
948       }
949     #else
950       if (0!=(wdcpp&7)) p+=(wdcpp&7)-8; /* negative */
951     #endif
952   }
953   assert(ret->rlen==((wd+7)>>3));
954   /* printf("rlen=%d %d\n", ret->rlen, to-ret->rowbeg); */
955   assert(to==ret->rowbeg+ht*ret->rlen);
956   return ret;
957 }
958 
setBpc(unsigned char bpc_)959 void Image::Indexed::setBpc(unsigned char bpc_) {
960   unsigned ncols=getNcols();
961   if (bpc_==0) {
962     if (ncols<=2) bpc_=1;
963     else if (ncols<=4) bpc_=2;
964     else if (ncols<=16) bpc_=4;
965     else bpc_=8;
966   } else {
967     if (bpc_==1) assert(ncols<=2);
968     else if (bpc_==2) assert(ncols<=4);
969     else if (bpc_==4) assert(ncols<=16);
970     else if (bpc_!=8) param_assert(0 && "invalid bpc_");
971   }
972   // fprintf(stderr, "bpc: %u -> %u\n", bpc, bpc_);
973   if (bpc==bpc_) return;
974   to8(); /* Imp: make the transition without the intermediate 8-bits... */
975   if (bpc_==8) return;
976   if (ht==0 || wd==0) { bpc=bpc_; return; }
977 
978   const char *oldBeg=beg;
979   unsigned char *p=(unsigned char*)rowbeg;
980   assert(cpp==1);
981   slen_t wdcpp=wd/* *cpp*/;
982   bpc=bpc_;
983   rlen=(((rlen_t)bpc_)*wd+7)>>3;
984   beg=new char[len=rowbeg-oldBeg+rlen*ht+bpc];
985   headp= const_cast<char*>(beg)+(headp-oldBeg);
986   rowbeg=const_cast<char*>(beg)+(rowbeg-oldBeg);
987   trail= const_cast<char*>(beg)+len-bpc;
988   memcpy(const_cast<char*>(beg), oldBeg, rowbeg-beg);
989 
990   unsigned char *to=(unsigned char*)rowbeg, *toend;
991   unsigned int i;
992   Image::Sampled::dimen_t htc;
993   if (bpc_==1) {
994     // This reads bytes from trail.
995     htc=ht; while (htc--!=0) {
996       toend=to+((wdcpp+7)>>3);
997       while (to!=toend) {
998         i =*p++<<7; i|=*p++<<6; i|=*p++<<5; i|=*p++<<4;
999         i|=*p++<<3; i|=*p++<<2; i|=*p++<<1; i|=*p++;
1000         *to++=i;
1001       }
1002       if (0!=(wdcpp&7)) p+=(wdcpp&7)-8; /* negative */
1003     }
1004   } else if (bpc_==2) {
1005     // This reads bytes from trail.
1006     htc=ht; while (htc--!=0) {
1007       toend=to+((wdcpp+3)>>2);
1008       while (to!=toend) {
1009         i =*p++<<6; i|=*p++<<4; i|=*p++<<2; i|=*p++;
1010         *to++=i;
1011       }
1012       if (0!=(wdcpp&3)) p+=(wdcpp&3)-4;
1013     }
1014   } else if (bpc_==4) {
1015     // This reads bytes from trail.
1016     htc=ht; while (htc--!=0) {
1017       toend=to+((wdcpp+1)>>1);
1018       while (to!=toend) {
1019         i =*p++<<4; i|=*p++;
1020         *to++=i;
1021       }
1022       if (0!=(wdcpp&1)) p--;
1023     }
1024   } else assert(0 && "invalid bpc");
1025   delete [] const_cast<char*>(oldBeg);
1026 }
getPal(unsigned char color) const1027 Image::Sampled::rgb_t Image::Indexed::getPal(unsigned char color) const {
1028   unsigned char *p=(unsigned char*)headp+3*color;
1029   return ((Image::Sampled::rgb_t)p[0]<<16)+(p[1]<<8)+p[2];
1030 }
1031 
dumpDebug(GenBuffer::Writable & gw)1032 void Image::Indexed::dumpDebug(GenBuffer::Writable& gw) {
1033   gw <<"% ncols=" << getNcols()
1034      << " rlen=" << rlen
1035      << " ht=" << ht
1036      << " wd=" << wd
1037      << " cpp=" << (unsigned)cpp
1038      << " bpc=" << (unsigned)bpc
1039      << " transp=" << transp
1040      << " transpc=" << (transp==-1?"none":rgb2webhash(getPal(transp)))
1041      << " ty=" << (unsigned)ty
1042      //<< " pred=" << (unsigned)pred
1043      << '\n';
1044   unsigned char *p=(unsigned char*)headp;
1045   while (p!=(unsigned char*)rowbeg) {
1046     gw << rgb2webhash(((Image::Sampled::rgb_t)p[0]<<16)+(p[1]<<8)+p[2]) << '\n';
1047     p+=3;
1048   }
1049   gw << '\n';
1050   gw.vi_write(rowbeg,rlen*ht);
1051 }
1052 
addAlpha(Image::Gray * al)1053 Image::Sampled* Image::Indexed::addAlpha(Image::Gray *al) {
1054   // Error::sev(Error::WARNING) << "Indexed: alpha channel ignored" << (Error*)0; return this;
1055   if (al->getHt()!=ht || al->getWd()!=wd) Error::sev(Error::EERROR) << "addAlpha: image dimension mismatch" << (Error*)0;
1056   bool ign_mid=false;
1057   unsigned char lightest, darkest;
1058   al->to8();
1059   al->calcExtrema(lightest, darkest);
1060   if (darkest==255) return this; /* no transparent pixels at all */
1061   to8();
1062   if (transp>=0) { /* Already have a transparent index. Join. */
1063     register char *p=rowbeg;
1064     register unsigned char transp_=transp;
1065     char *pend=rowbeg+rlen*ht, *alq=al->getRowbeg();
1066     /* Imp: choose an image color instead of black... */
1067     /* Dat: 0..254: transparent, 255: opaque */
1068     while (p!=pend) {
1069       if ((unsigned char)(*alq+1)>1) ign_mid=true;
1070       /* fprintf(stderr,"alq=%u\n", (unsigned char)*alq); */
1071       if ((unsigned char)*alq++!=255) p[0]=transp_; /* black out transparent-wannabe pixels */
1072       p++;
1073     }
1074   } else { /* No transparent color yet. */
1075     packPal();
1076     unsigned ncols=getNcols();
1077     char *p=rowbeg, *pend=rowbeg+rlen*ht, *alq=al->getRowbeg();
1078     /* Imp: choose an image color instead of black... */
1079     /* Dat: 0..254: transparent, 255: opaque */
1080     while (p!=pend) {
1081       if ((unsigned char)(*alq+1)>1) ign_mid=true;
1082       /* fprintf(stderr,"alq=%u\n", (unsigned char)*alq); */
1083       if ((unsigned char)*alq++!=255) p[0]=ncols; /* may set to 0 if ncols==256 */
1084       p++;
1085     }
1086     if (ncols==256) { /* Try again, probably now we have fewer colors */
1087       packPal();
1088       if ((ncols=getNcols())==256) Error::sev(Error::EERROR) << "Indexed::addAlpha: too many colors, transparency impossible" << (Error*)0;
1089       for (p=rowbeg,alq=al->getRowbeg(); p!=pend; p++)
1090         if ((unsigned char)*alq++!=255) *p=ncols;
1091     }
1092     setNcolsMove(ncols+1);
1093     setPal(ncols,0); /* black */
1094     setTransp(ncols);
1095   }
1096   if (ign_mid) Error::sev(Error::WARNING) << "addAlpha: half-transparent pixels made transparent" << (Error*)0;
1097   return this;
1098 }
makeTranspZero()1099 void Image::Indexed::makeTranspZero() {
1100   if (transp<1) return; /* no transparency or already 0 */
1101   unsigned char oldBpc=bpc;
1102   register unsigned char transpch=transp;
1103   /* Imp: make this faster by not converting to 8 bits */
1104   if (oldBpc!=8) to8();
1105 
1106   /* Update the image. */
1107   register unsigned char *p; unsigned char *pend;
1108   for (p=(unsigned char*)rowbeg, pend=p+wd*ht; p!=pend; p++) {
1109     if (*p==0) *p=transp;
1110     else if (*p==transpch) *p=0;
1111   }
1112   rgb_t rzero=getPal(0), rtransp=getPal(transp);
1113   setPal(transp, rzero); setPal(0, rtransp);
1114   transp=0;
1115   if (oldBpc!=8) setBpc(oldBpc);
1116 }
1117 
1118 /* --- */
1119 
Gray(Image::Sampled::dimen_t wd_,Image::Sampled::dimen_t ht_,unsigned char bpc_)1120 Image::Gray::Gray(Image::Sampled::dimen_t wd_, Image::Sampled::dimen_t ht_, unsigned char bpc_) {
1121   init(0,0,wd_,ht_,bpc_,TY_GRAY,1);
1122   cs=CS_GRAYSCALE;
1123 }
to8()1124 void Image::Gray::to8() { to8mul(); }
toRGB(unsigned char bpc_)1125 Image::RGB    * Image::Gray::toRGB(unsigned char bpc_)/* const*/ { return toRGB0(bpc_); }
toGray(unsigned char bpc_)1126 Image::Gray*    Image::Gray::toGray(unsigned char bpc_)/* const*/ { return bpc==bpc_ ? this : toGray0(bpc_); }
toIndexed()1127 Image::Indexed* Image::Gray::toIndexed()/* const*/ {
1128   Image::Indexed *img=new Image::Indexed(wd, ht, (1<<bpc), bpc);
1129   unsigned i;
1130   rgb_t rgb;
1131   if (bpc==1) {
1132     img->setPal(0,0);
1133     img->setPal(1,0xffffffL);
1134   } else if (bpc==2) {
1135     img->setPal(0,0);
1136     img->setPal(1,0x555555L);
1137     img->setPal(2,0xaaaaaaL);
1138     img->setPal(3,0xffffffL);
1139   } else if (bpc==4) {
1140     for (i=0,rgb=0;i<16;i++,rgb+=(rgb_t)0x111111L) img->setPal(i,rgb);
1141   } else if (bpc==8) {
1142     for (i=0,rgb=0;i<256;i++,rgb+=(rgb_t)0x010101L) img->setPal(i,rgb);
1143   }
1144   memcpy(img->getRowbeg(), rowbeg, rlen*ht);
1145   return img;
1146 }
canGray() const1147 bool Image::Gray::canGray() const { return true; }
copyRGBRow(char * to,Image::Sampled::dimen_t whichrow) const1148 void Image::Gray::copyRGBRow(char *to, Image::Sampled::dimen_t whichrow) const {
1149   param_assert(whichrow<ht);
1150   if (wd==0) return;
1151   unsigned char *p=(unsigned char*)rowbeg+rlen*whichrow;
1152   char *toend=to+3*wd;
1153   unsigned int i, j, k;
1154 
1155   if (bpc==1) {
1156     toend-=3*(wd&7);
1157     while (to!=toend) {
1158       i=*p++;
1159       k=(i>>7)*255;     *to++=k; *to++=k; *to++=k;
1160       k=((i>>6)&1)*255; *to++=k; *to++=k; *to++=k;
1161       k=((i>>5)&1)*255; *to++=k; *to++=k; *to++=k;
1162       k=((i>>4)&1)*255; *to++=k; *to++=k; *to++=k;
1163       k=((i>>3)&1)*255; *to++=k; *to++=k; *to++=k;
1164       k=((i>>2)&1)*255; *to++=k; *to++=k; *to++=k;
1165       k=((i>>1)&1)*255; *to++=k; *to++=k; *to++=k;
1166       k=(     i&1)*255; *to++=k; *to++=k; *to++=k;
1167     }
1168     i=*p; /* No mem overrun, even if (wd&7)==0 */
1169     j=wd&7;
1170     while (j--!=0) { k=(i>>7)*255; *to++=k; *to++=k; *to++=k; i<<=1; }
1171   } else if (bpc==2) {
1172     toend-=3*(wd&3);
1173     while (to!=toend) {
1174       i=*p++;
1175       k=(i>>6)*85;     *to++=k; *to++=k; *to++=k;
1176       k=((i>>4)&3)*85; *to++=k; *to++=k; *to++=k;
1177       k=((i>>2)&3)*85; *to++=k; *to++=k; *to++=k;
1178       k=(     i&3)*85; *to++=k; *to++=k; *to++=k;
1179     }
1180     i=*p; /* No mem overrun, even if (wd&7)==0 */
1181     j=wd&3;
1182     while (j--!=0) { k=(i>>6)*85; *to++=k; *to++=k; *to++=k; i<<=2; }
1183   } else if (bpc==4) {
1184     toend-=3*(wd&1);
1185     while (to!=toend) {
1186       i=*p++;
1187       k=(i>>4)*17;      *to++=k; *to++=k; *to++=k;
1188       k=(     i&15)*17; *to++=k; *to++=k; *to++=k;
1189     }
1190     if (0!=(wd&1)) { k=(*p>>4)*17; *to++=k; *to++=k; *to++=k; }
1191   } else if (bpc==8) {
1192     while (to!=toend) { *to++=*p; *to++=*p; *to++=*p++; }
1193   } else assert(0 && "invalid bpc");
1194 }
1195 #if 0
1196 void Image::Gray::setBpc(unsigned char bpc_) {
1197   (void)bpc_;
1198   assert(0 && "unimplemented"); /* unimplemented */
1199 }
1200 #endif
1201 
addAlpha(Image::Gray * al)1202 Image::Sampled* Image::Gray::addAlpha(Image::Gray *al) {
1203   // Error::sev(Error::WARNING) << "Gray: alpha channel ignored" << (Error*)0; return this;
1204   if (al->getHt()!=ht || al->getWd()!=wd) Error::sev(Error::EERROR) << "addAlpha: image dimension mismatch" << (Error*)0;
1205   bool ign_mid=false;
1206   unsigned char lightest, darkest;
1207   al->to8();
1208   al->calcExtrema(lightest, darkest);
1209   if (darkest==255) return this; /* no transparent pixels at all */
1210   char *p=rowbeg, *pend=rowbeg+rlen*ht, *alq=al->getRowbeg();
1211   /* Imp: choose an image color instead of black... */
1212   /* Dat: 0..254: transparent, 255: opaque */
1213   while (p!=pend) {
1214     if ((unsigned char)(*alq+1)>1) ign_mid=true;
1215     /* fprintf(stderr,"alq=%u\n", (unsigned char)*alq); */
1216     if ((unsigned char)*alq++!=255) *p=0; /* black out transparent-wannabe pixels */
1217     p++;
1218   }
1219   if (ign_mid) Error::sev(Error::WARNING) << "addAlpha: half-transparent pixels made transparent" << (Error*)0;
1220   return addAlpha0(toIndexed(), al);
1221 }
1222 
calcExtrema(unsigned char & lightest,unsigned char & darkest)1223 void Image::Gray::calcExtrema(unsigned char &lightest, unsigned char &darkest) {
1224   to8();
1225   unsigned l=0, d=255, val;
1226   char *p=rowbeg, *pend=rowbeg+ht*wd;
1227   while (p!=pend) {
1228     val=*(unsigned char*)p++;
1229     if (val>l) l=val;
1230     if (val<d) d=val;
1231   }
1232   lightest=l; darkest=d;
1233 }
1234 
1235 
1236 /* --- */
1237 
RGB(Image::Sampled::dimen_t wd_,Image::Sampled::dimen_t ht_,unsigned char bpc_)1238 Image::RGB::RGB(Image::Sampled::dimen_t wd_, Image::Sampled::dimen_t ht_, unsigned char bpc_) {
1239   init(0,0,wd_,ht_,bpc_,TY_RGB,3);
1240   cs=CS_RGB;
1241 }
to8()1242 void Image::RGB::to8() { to8mul(); }
toGray(unsigned char bpc_)1243 Image::Gray*    Image::RGB::toGray(unsigned char bpc_)/* const*/ { return toGray0(bpc_); }
toRGB(unsigned char bpc_)1244 Image::RGB*     Image::RGB::toRGB(unsigned char bpc_)/* const*/ { return bpc==bpc_ ? this : toRGB0(bpc_); }
toIndexed()1245 Image::Indexed* Image::RGB::toIndexed()/* const*/ { return toIndexed0(); }
canGray() const1246 bool Image::RGB::canGray() const { return false; }
copyRGBRow(char * to,Image::Sampled::dimen_t whichrow) const1247 void Image::RGB::copyRGBRow(char *to, Image::Sampled::dimen_t whichrow) const {
1248   param_assert(whichrow<ht);
1249   if (wd==0) return;
1250   unsigned char *p=(unsigned char*)rowbeg+rlen*whichrow;
1251   char *toend=to+3*wd;
1252   unsigned int i, j, k;
1253 
1254   if (bpc==1) {
1255     toend-=(wd*3)&7;
1256     while (to!=toend) {
1257       i=*p++;
1258       k=(i>>7)*255;     *to++=k;
1259       k=((i>>6)&1)*255; *to++=k;
1260       k=((i>>5)&1)*255; *to++=k;
1261       k=((i>>4)&1)*255; *to++=k;
1262       k=((i>>3)&1)*255; *to++=k;
1263       k=((i>>2)&1)*255; *to++=k;
1264       k=((i>>1)&1)*255; *to++=k;
1265       k=(     i&1)*255; *to++=k;
1266     }
1267     i=*p; /* No mem overrun, even if (wd&7)==0 */
1268     j=(wd*3)&7;
1269     while (j--!=0) { k=(i>>7)*255; *to++=k; i<<=1; }
1270   } else if (bpc==2) {
1271     toend-=(wd*3)&3;
1272     while (to!=toend) {
1273       i=*p++;
1274       k=(i>>6)*85;     *to++=k;
1275       k=((i>>4)&3)*85; *to++=k;
1276       k=((i>>2)&3)*85; *to++=k;
1277       k=(     i&3)*85; *to++=k;
1278     }
1279     i=*p; /* No mem overrun, even if (wd&7)==0 */
1280     j=(wd*3)&3;
1281     while (j--!=0) { k=(i>>6)*85; *to++=k; i<<=2; }
1282   } else if (bpc==4) {
1283     toend-=(wd*3)&7;
1284     while (to!=toend) {
1285       i=*p++;
1286       k=(i>>4)*17;      *to++=k;
1287       k=(     i&15)*17; *to++=k;
1288     }
1289     if (0!=((wd*3)&1)) *to++=(*p>>4)*17;
1290   } else if (bpc==8) {
1291     memcpy(to, p, 3*wd);
1292   } else assert(0 && "invalid bpc");
1293 }
1294 #if 0
1295 void Image::RGB::setBpc(unsigned char bpc_) {
1296   (void)bpc_;
1297   assert(0 && "unimplemented"); /* unimplemented */
1298 }
1299 #endif
1300 
addAlpha(Image::Gray * al)1301 Image::Sampled* Image::RGB::addAlpha(Image::Gray *al) {
1302   if (al->getHt()!=ht || al->getWd()!=wd) Error::sev(Error::EERROR) << "addAlpha: image dimension mismatch" << (Error*)0;
1303   bool ign_mid=false;
1304   unsigned char lightest, darkest;
1305   al->to8();
1306   al->calcExtrema(lightest, darkest);
1307   if (darkest==255) return this; /* no transparent pixels at all */
1308   char *p=rowbeg, *pend=rowbeg+rlen*ht, *alq=al->getRowbeg();
1309   /* Imp: choose an image color instead of black... */
1310   /* Dat: 0..254: transparent, 255: opaque */
1311   while (p!=pend) {
1312     if ((unsigned char)(*alq+1)>1) ign_mid=true;
1313     /* fprintf(stderr,"alq=%u\n", (unsigned char)*alq); */
1314     if ((unsigned char)*alq++!=255) p[0]=p[1]=p[2]=0; /* black out transparent-wannabe pixels */
1315     p+=3;
1316   }
1317   if (ign_mid) Error::sev(Error::WARNING) << "addAlpha: half-transparent pixels made transparent" << (Error*)0;
1318   return addAlpha0(toIndexed(), al);
1319 }
1320 
1321 /* --- */
1322 
rgb2webhash(rgb_t rgb)1323 char *Image::Sampled::rgb2webhash(rgb_t rgb) {
1324   static char tmp[8];
1325   char *p=tmp;
1326   *p='#';
1327   *++p='0'+(rgb>>20);      if (*p>'9') *p+='a'-'0'-10;
1328   *++p='0'+((rgb>>16)&15); if (*p>'9') *p+='a'-'0'-10;
1329   *++p='0'+((rgb>>12)&15); if (*p>'9') *p+='a'-'0'-10;
1330   *++p='0'+((rgb>>8)&15);  if (*p>'9') *p+='a'-'0'-10;
1331   *++p='0'+((rgb>>4)&15);  if (*p>'9') *p+='a'-'0'-10;
1332   *++p='0'+((rgb   )&15);  if (*p>'9') *p+='a'-'0'-10;
1333   *++p='\0';
1334   return tmp;
1335 }
1336 
operator <<(GenBuffer::Writable & gw,Image::Sampled const & img)1337 GenBuffer::Writable& operator<<(GenBuffer::Writable& gw, Image::Sampled const& img) {
1338   slen_t buflen=img.getWd()*3;
1339   char *buf=new char[buflen];
1340   Image::Sampled::dimen_t y, ht=img.getHt();
1341   /* vvv in the xv program: image file must be >=30 bytes long to be treated as image */
1342   gw << "P6\n###############\n" << img.getWd() << ' ' << ht;
1343   if (img.getTranspc()>=0x1000000UL) gw << "\n#Opaque";
1344                                 else gw << "\n#T" << img.rgb2webhash(img.getTranspc());
1345   gw << "\n255\n";
1346   for (y=0; y<ht; y++) {
1347     img.copyRGBRow(buf, y);
1348     gw.vi_write(buf, buflen);
1349   }
1350   delete [] buf;
1351   return gw;
1352 }
1353 
1354 static Image::Loader *first_image_loader=(Image::Loader*)NULLP;
1355 
register0(Image::Loader * anew)1356 void Image::register0(Image::Loader *anew) {
1357   param_assert(anew!=NULLP);
1358   anew->next=first_image_loader;
1359   first_image_loader=anew;
1360 }
1361 
1362 #if 0 /* removed by code refactoring */
1363 Image::Sampled* Image::load(char const* format, filep_t f_, SimBuffer::Flat const& loadHints) {}  /* Removed. */
1364 #endif
1365 
1366 // #include <unistd.h> /* sleep() */
1367 
1368 #if 0
1369 Rule::Sampled *Rule::load(char const* filename) {
1370   static char buf[2*Applier::MAGIC_LEN];
1371   FILE *f=fopen(filename, "rb");
1372   unsigned got=0;
1373   if (f==NULLP) Error::sev(Error::EERROR) << "Cannot open/read image file: " << FNQ(filename) << (Error*)0;
1374   slen_t ret=fread(buf, 1, Applier::MAGIC_LEN, f);
1375   /* vvv Imp: clarify error message: may be a read error */
1376   if (ret==0) Error::sev(Error::EERROR) << "Zero-length image file: " << FNQ(filename) << (Error*)0;
1377   if (ret<Applier::MAGIC_LEN) memset(buf+ret, '\0', Applier::MAGIC_LEN-ret);
1378 #if 0
1379   unsigned long pos=fseek(f, 0, SEEK_END);
1380   pos=(pos<=Applier::MAGIC_LEN)?0:pos-Applier::MAGIC_LEN;
1381   if (0!=fseek(f, pos, SEEK_SET)
1382    || (got=fread(buf+Applier::MAGIC_LEN, 1, Applier::MAGIC_LEN, f))==0
1383 #else
1384   if (0
1385 #endif
1386    || (rewind(f), 0)
1387    || ferror(f))
1388     Error::sev(Error::EERROR) << "I/O error in image file: " << FNQ(filename) << (Error*)0;
1389   if (got!=0 && got!=Applier::MAGIC_LEN) memmove(buf+2*Applier::MAGIC_LEN-got, buf+Applier::MAGIC_LEN, got);
1390   Applier *p=first_image_loader;
1391   Applier::reader_t reader;
1392   while (p!=NULLP) {
1393     if (NULLP!=(reader=p->checker(buf,buf+Applier::MAGIC_LEN))) { return reader(f); }
1394     p=p->next;
1395   }
1396   Error::sev(Error::EERROR) << "Unknown image format: " << FNQ(filename) << (Error*)0;
1397   // Error::sev(Error::WARNING) << "Zero-length image1." << (Error*)0;
1398   // Error::sev(Error::WARNING) << "Zero-length image2." << (Error*)0;
1399   return 0; /*notreached*/
1400 }
1401 #endif
1402 
1403 
load(Image::Loader::UFD * ufd0,SimBuffer::Flat const & loadHints,char const * format)1404 Image::Sampled *Image::load(Image::Loader::UFD* ufd0, SimBuffer::Flat const& loadHints, char const* format) {
1405   Filter::UngetFILED &ufd=*(Filter::UngetFILED*)ufd0;
1406   /* Dat: format arg used in in_pnm.cpp */
1407   static char buf[Loader::MAGIC_LEN+1];
1408   slen_t ret=ufd.vi_read(buf, Loader::MAGIC_LEN);
1409   /* vvv Imp: clarify error message: may be a read error */
1410   if (ufd.hadError()) Error::sev(Error::EERROR) << "I/O error pre in image file: " << FNQ(ufd.getFilenameDefault("-")) << (Error*)0;
1411   if (ret==0) Error::sev(Error::EERROR) << "Zero-length image file: " << FNQ(ufd.getFilenameDefault("-")) << (Error*)0;
1412   if (ret<Loader::MAGIC_LEN) memset(buf+ret, '\0', Loader::MAGIC_LEN-ret);
1413   buf[Loader::MAGIC_LEN]='\0';
1414   /* Dat: do not read the trailer onto buf+Loader::MAGIC_LEN, because no ->checker() uses it yet. */
1415   Loader *p=first_image_loader;
1416   Loader::reader_t reader;
1417   ufd.unread(buf, ret); /* tries to seek back, on failure calls ufd.getUnget().vi_write() */
1418   // ^^^ rewind(f); /* checker might have read */
1419   /* ^^^ do this early for the checkers */
1420   while (p!=NULLP) {
1421     /* vvv each checker() must rewind ufd for itself */
1422     if ((format==(char const*)NULLP || 0==strcmp(p->format, format))
1423      && (Loader::checker_t)0!=p->checker
1424      && (Loader::reader_t)0!=(reader=p->checker(buf,buf+Loader::MAGIC_LEN, loadHints, ufd0))
1425        ) {
1426       // fprintf(stderr, "%p %p\n", ufd0, &ufd);
1427       return reader(ufd0, loadHints);
1428     }
1429     p=p->next;
1430   }
1431   // sleep(1000);
1432   Error::sev(Error::EERROR) << "Unknown input image format: " << FNQ(ufd.getFilenameDefault("-")) << (Error*)0;
1433   return 0; /*notreached*/
1434 }
1435 
1436 #if 0 /* not used anywhere, except in test_main. */
1437 Image::Sampled *Image::load(char const* filename, SimBuffer::Flat const& loadHints, filep_t stdin_f, char const* format) {  /* Commented out. */
1438   Filter::UngetFILED ufd(filename, stdin_f==NULLP ? stdin : (FILE*)stdin_f,
1439     Filter::UngetFILED::CM_closep|Filter::UngetFILED::CM_keep_stdinp);
1440   return load((Image::Loader::UFD*)&ufd, loadHints, format);
1441   // Imp: better error message, something like: if (f==NULLP) Error::sev(Error::EERROR) << "Cannot open/read image file: " << FNQ(filename) << (Error*)0;
1442 }
1443 #endif
1444 
1445 #if 0 /* before Sat Apr 19 13:42:04 CEST 2003 */
1446 Image::Sampled *Image::load(char const* filename, SimBuffer::Flat const& loadHints, filep_t stdin_f, char const* format) {
1447   /* Dat: format arg used in in_pnm.cpp */
1448   static char buf[2*Loader::MAGIC_LEN];
1449   bool stdin_p=filename[0]=='-' && filename[1]=='\0';
1450   FILE *f=!stdin_p ? fopen(filename, "rb") : stdin_f!=NULLP ? (FILE*)stdin_f : stdin;
1451   unsigned got=0;
1452   if (f==NULLP) Error::sev(Error::EERROR) << "Cannot open/read image file: " << FNQ(filename) << (Error*)0;
1453   slen_t ret=fread(buf, 1, Loader::MAGIC_LEN, f);
1454   /* vvv Imp: clarify error message: may be a read error */
1455   if (ret==0) Error::sev(Error::EERROR) << "Zero-length image file: " << FNQ(filename) << (Error*)0;
1456   if (ret<Loader::MAGIC_LEN) memset(buf+ret, '\0', Loader::MAGIC_LEN-ret);
1457 #if 0
1458   /* Dat: do not read the trailer, because no ->checker() uses it yet. */
1459   unsigned long pos=fseek(f, 0, SEEK_END);
1460   pos=(pos<=Loader::MAGIC_LEN)?0:pos-Loader::MAGIC_LEN;
1461   if (0!=fseek(f, pos, SEEK_SET)
1462    || (got=fread(buf+Loader::MAGIC_LEN, 1, Loader::MAGIC_LEN, f))==0
1463 #else
1464   if (0
1465 #endif
1466    || (rewind(f), 0)
1467    || ferror(f))
1468     Error::sev(Error::EERROR) << "I/O error pre in image file: " << FNQ(filename) << (Error*)0;
1469   if (got!=0 && got!=Loader::MAGIC_LEN) memmove(buf+2*Loader::MAGIC_LEN-got, buf+Loader::MAGIC_LEN, got);
1470   Loader *p=first_image_loader;
1471   Loader::reader_t reader;
1472   while (p!=NULLP) {
1473     if ((format==(char const*)NULLP || 0==strcmp(p->format, format))
1474      && (Loader::checker_t)NULLP!=p->checker
1475      && (Loader::reader_t)NULLP!=(reader=p->checker(buf,buf+Loader::MAGIC_LEN, loadHints, f))
1476        ) {
1477       rewind(f); /* checker might have read */
1478       Image::Sampled *ret=reader(f, loadHints);
1479       if (ferror(f) || (!stdin_p && 0!=fclose(f))) /* don't close stdin */
1480         Error::sev(Error::EERROR) << "I/O error post in image file: " << FNQ(filename) << (Error*)0;
1481       return ret;
1482     }
1483     p=p->next;
1484   }
1485   Error::sev(Error::EERROR) << "Unknown input image format: " << FNQ(filename) << (Error*)0;
1486   // Error::sev(Error::WARNING) << "Zero-length image1." << (Error*)0;
1487   // Error::sev(Error::WARNING) << "Zero-length image2." << (Error*)0;
1488   return 0; /*notreached*/
1489 }
1490 #endif
1491 
printLoaders(GenBuffer::Writable & out)1492 unsigned Image::printLoaders(GenBuffer::Writable &out) {
1493   unsigned num=0;
1494   Loader *p=first_image_loader;
1495   while (p!=NULLP) {
1496     if (p->checker!=(Loader::checker_t)0 && p->format!=(char const*)NULLP) { num++; out << ' ' << p->format; }
1497     p=p->next;
1498   }
1499   return num;
1500 }
1501 
1502 /* --- */
1503 
SampledInfo(Sampled * img_)1504 Image::SampledInfo::SampledInfo(Sampled *img_)
1505  :hasTransp(false)
1506  ,nncols(257)
1507  ,canGray(false)
1508  ,minRGBBpc(8)
1509  ,img(img_)
1510  ,imgs((Indexed**)NULLP) {
1511   param_assert(img_!=NULLP);
1512   Sampled *bak=img;
1513   if ((img=img->toIndexed())==NULLP) {
1514     img=bak;
1515   } else {
1516     if (bak!=img) delete bak;
1517     assert(img->getTy()==img->TY_INDEXED);
1518     Indexed *iimg=PTS_dynamic_cast(Image::Indexed*,img);
1519     /* This packPal() contains a call to sortPal(), which converts the indexed
1520      * image to canonical form.
1521      */
1522     iimg->packPal();
1523     nncols=iimg->getNcols();
1524     if (true==(hasTransp=iimg->hasTransp())) nncols--;
1525   }
1526   minRGBBpc=img->minRGBBpc();
1527   if ((canGray=img->canGray())==true && nncols==257) nncols=256;
1528   sf=(sf_t)((img->getTy()==img->TY_BLACKBOX) ? 0+SF_Asis : 0+SF_None);
1529   /* Dat: 0+: pacify gcc-3.1 */
1530 }
1531 
~SampledInfo()1532 Image::SampledInfo::~SampledInfo() {
1533   delete img;
1534   if (imgs!=NULLP) { Image::Indexed::delete_separated(imgs); delete imgs; }
1535 }
1536 
separate()1537 void Image::SampledInfo::separate() {
1538   // bool ok;
1539   // ASSERT_SIDE(ok=(sf!=SF_Transparent2 && sf!=SF_Transparent4 && sf!=SF_Transparent8));
1540   // if (!ok) return;
1541   if (sf!=SF_Transparent2 && sf!=SF_Transparent4 && sf!=SF_Transparent8) return;
1542   Indexed *iimg=PTS_dynamic_cast(Indexed*,img);
1543   imgs=iimg->separate();
1544 }
1545 
setSampleFormat(sf_t sf_,bool WarningOK,bool TryOnly,Sampled::rgb_t Transparent)1546 bool Image::SampledInfo::setSampleFormat(sf_t sf_, bool WarningOK, bool TryOnly, Sampled::rgb_t Transparent) {
1547   /* at Sat Jun 15 11:48:51 CEST 2002: added transparency warnings */
1548   /* fprintf(stderr, "sf=%u sf_=%u transparent=0x%lx\n", sf, sf_, Transparent+0UL); */
1549   // assert(sf_==SF_Asis);
1550   Indexed *iimg;
1551   Sampled *bak=img;
1552   param_assert(sf_!=SF_None);
1553   param_assert(sf==SF_None || sf==SF_Asis || sf==sf_);
1554   if (sf_==sf) return true; /* already converted */
1555   bool zero=img->getWd()==0 || img->getHt()==0;
1556   if (sf==SF_Asis && sf_!=SF_Asis && sf_!=SF_Bbox) {
1557     Error::sev(Error::WARNING) << "SampleFormat: can't convert image loaded as Asis to other" << (Error*)0;
1558     return false;
1559   }
1560   switch (sf_) {
1561    case SF_Bbox:
1562     sf=SF_Bbox; return true;
1563    case SF_Opaque:
1564     if (!hasTransp && nncols==1 && PTS_dynamic_cast(Indexed*,img)->wouldSetTranspc(Transparent)) return false;
1565     if (hasTransp || nncols!=1) return false;
1566     assert(img->getTy()==img->TY_INDEXED);
1567     /* The color can be calculated: PTS_dynamic_cast(Indexed*>(img)->getPal(0); */
1568     /* Conversion is not necessary. */
1569     sf=SF_Opaque; return true;
1570    case SF_Transparent:
1571     if (!hasTransp && nncols==1 && PTS_dynamic_cast(Indexed*,img)->setTranspc(Transparent)) {
1572       /* Must return true eventually, because setTranspc has modified the image. */
1573       hasTransp=true; nncols=0;
1574     } else if (!hasTransp || nncols!=0) {
1575       return false;
1576     }
1577     assert(img->getTy()==img->TY_INDEXED);
1578     /* Conversion is not necessary. */
1579     sf=SF_Transparent; return true;
1580    case SF_Gray1:
1581     /* vvv strict hasTransp added at Mon Sep  9 22:53:24 CEST 2002 */
1582     if (nncols>2 || !canGray || minRGBBpc>1 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1583     /* ^^^ Imp: !! make the hasPixelRFB() check a lot faster (cache results) */
1584     if (TryOnly) return WarningOK || (nncols>=2 && !hasTransp);
1585     if (hasTransp) {
1586       if (!WarningOK) return false;
1587       /* Dat: nncols may be 1 or 2 ! (both transparent and opaque black) */
1588       Error::sev(Error::WARNING) <<
1589         (nncols<=1 ? "SampleFormat: Mask would be better than "
1590                    : "SampleFormat: Transparent2 would be better than ")
1591         << "Gray1" << (Error*)0;
1592     }
1593     if (nncols<2) {
1594       if (!WarningOK) return false;
1595       Error::sev(Error::WARNING) << "SampleFormat: Opaque would be better than Gray1" << (Error*)0;
1596     }
1597     img=img->toGray(1); if (bak!=img) delete bak;
1598     assert(img!=NULLP);
1599     assert(img->getBpc()==1);
1600     sf=SF_Gray1; return true;
1601    case SF_Indexed1:
1602     if (nncols>2 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1603     if (TryOnly) return WarningOK || nncols>=2;
1604     if (nncols<2) {
1605       if (!WarningOK) return false;
1606       Error::sev(Error::WARNING) << "SampleFormat: Opaque would be better than Indexed1" << (Error*)0;
1607     }
1608     if (canGray && minRGBBpc==1) {
1609       Error::sev(Error::NOTICE) << "SampleFormat: Gray1 would be better than Indexed1" << (Error*)0;
1610     }
1611     assert(img->getTy()==img->TY_INDEXED);
1612     // img=img->toIndexed(); /* should be a no-op */
1613     // assert(img!=NULLP); if (bak!=img) delete bak;
1614     { iimg=PTS_dynamic_cast(Indexed*,img);
1615       iimg->setBpc(1);
1616       if (iimg->wouldSetTranspc(Transparent)) return false; /* Dat: false if must be changed to become transparent. */
1617       iimg->setTranspcAndRepack(Transparent);
1618     }
1619     sf=SF_Indexed1; return true;
1620    case SF_Mask:
1621     if (!hasTransp && nncols==2 && PTS_dynamic_cast(Indexed*,img)->wouldSetTranspc(Transparent)) { hasTransp=true; --nncols; }
1622     if (nncols>1 || zero) return false;
1623     if (TryOnly) return WarningOK || nncols+(hasTransp?1:0)==2;
1624     if (nncols==1 && !hasTransp) {
1625       if (!WarningOK) return false;
1626       Error::sev(Error::WARNING) << "SampleFormat: Opaque would be better than Mask" << (Error*)0;
1627     }
1628     if (nncols==0) {
1629       assert(hasTransp);
1630       Error::sev(Error::WARNING) << "SampleFormat: Transparent would be better than Mask" << (Error*)0;
1631     }
1632     assert(img->getTy()==img->TY_INDEXED);
1633     { iimg=PTS_dynamic_cast(Indexed*,img);
1634       iimg->setBpc(1);
1635       if (!iimg->wouldSetTranspc(Transparent)) return false;
1636       iimg->setTranspcAndRepack(Transparent);
1637     }
1638     /* printf("gett=%d\n", PTS_dynamic_cast(Indexed*,img)->getTransp()); */
1639     /* vvv BUGFIX: <1U -> <2U */
1640     assert(PTS_dynamic_cast(Indexed*,img)->getTransp()==-1 || PTS_dynamic_cast(Indexed*,img)->getTransp()+0U<2U);
1641     /* ^^^ color 0 is opaque, color 1 is transparent, thanks to
1642      * img->packPal() called in SampleInfo() -- but setTranspc may have changed this
1643      */
1644     sf=SF_Mask; return true;
1645    case SF_Transparent2:
1646     if (!hasTransp && nncols==4 && PTS_dynamic_cast(Indexed*,img)->wouldSetTranspc(Transparent)) { hasTransp=true; --nncols; }
1647     if (nncols>3 || zero) return false;
1648     if (TryOnly) return WarningOK || (hasTransp && nncols>=2);
1649     Error::sev(Error::NOTICE) << "SampleFormat: Transparent2 separates colors" << (Error*)0;
1650     if (!hasTransp) {
1651       if (!WarningOK) return false;
1652       Error::sev(Error::WARNING) << "SampleFormat: Indexed2 would be better than Transparent2" << (Error*)0;
1653     }
1654     if (nncols<2) {
1655       if (!WarningOK) return false;
1656       Error::sev(Error::WARNING) << "SampleFormat: Mask would be better than Transparent2" << (Error*)0;
1657     }
1658     assert(img->getTy()==img->TY_INDEXED);
1659     { iimg=PTS_dynamic_cast(Indexed*,img);
1660       iimg->setBpc(2); /* BUGFIX at Sat Jun 15 13:55:25 CEST 2002 */
1661       iimg->setTranspcAndRepack(Transparent);
1662       // imgs=iimg->separate(); /* postponed because of GIF89a output */
1663     }
1664     sf=SF_Transparent2; return true;
1665    case SF_Gray2:
1666     if (nncols>4 || !canGray || minRGBBpc>2 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1667     if (TryOnly) return WarningOK || (nncols>2 && !hasTransp);
1668     if (hasTransp) {
1669       if (!WarningOK) return false;
1670       Error::sev(Error::WARNING) <<
1671         (nncols<=3 ? "SampleFormat: Transparent2 would be better than "
1672                    : "SampleFormat: Transparent4 would be better than ")
1673         << "Gray2" << (Error*)0;
1674     }
1675     if (nncols<=2) {
1676       if (!WarningOK) return false;
1677       Error::sev(Error::WARNING) << "SampleFormat: Gray1 would be better than Gray2" << (Error*)0;
1678     }
1679     img=img->toGray(2); if (bak!=img) delete bak;
1680     assert(img!=NULLP);
1681     sf=SF_Gray2; return true;
1682    case SF_Indexed2:
1683     if (nncols>4 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1684     if (TryOnly) return WarningOK || nncols>2;
1685     if (nncols<=2) {
1686       if (!WarningOK) return false;
1687       Error::sev(Error::WARNING) << "SampleFormat: Indexed1 would be better than Indexed2" << (Error*)0;
1688     }
1689     if (canGray && minRGBBpc<=2) {
1690       Error::sev(Error::NOTICE) << "SampleFormat: Gray2 would be better than Indexed2" << (Error*)0;
1691     }
1692     assert(img->getTy()==img->TY_INDEXED);
1693     { iimg=PTS_dynamic_cast(Indexed*,img);
1694       iimg->setBpc(2);
1695       if (iimg->wouldSetTranspc(Transparent)) return false;
1696       iimg->setTranspcAndRepack(Transparent);
1697     }
1698     sf=SF_Indexed2; return true;
1699    case SF_Transparent4:
1700     if (!hasTransp && nncols==16 && PTS_dynamic_cast(Indexed*,img)->wouldSetTranspc(Transparent)) { hasTransp=true; --nncols; }
1701     if (nncols>15 || zero) return false;
1702     if (TryOnly) return WarningOK || (hasTransp && nncols>=4);
1703     Error::sev(Error::NOTICE) << "SampleFormat: Transparent4 separates colors" << (Error*)0;
1704     if (!hasTransp) {
1705       if (!WarningOK) return false;
1706       Error::sev(Error::WARNING) << "SampleFormat: Indexed4 would be better than Transparent4" << (Error*)0;
1707     }
1708     if (nncols<4) {
1709       if (!WarningOK) return false;
1710       Error::sev(Error::WARNING) << "SampleFormat: Transparent2 would be better than Transparent4" << (Error*)0;
1711     }
1712     assert(img->getTy()==img->TY_INDEXED);
1713     { iimg=PTS_dynamic_cast(Indexed*,img);
1714       iimg->setBpc(4);
1715       iimg->setTranspcAndRepack(Transparent);
1716       // imgs=iimg->separate(); /* postponed because of GIF89a output */
1717     }
1718     sf=SF_Transparent4; return true;
1719    case SF_Rgb1:
1720     if (nncols>8 || minRGBBpc>1 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1721     if (TryOnly) return WarningOK || (nncols>4 && !canGray && !hasTransp);
1722     if (hasTransp) {
1723       if (!WarningOK) return false;
1724       Error::sev(Error::WARNING) << "SampleFormat: Transparent4 would be better than " << "Rgb1" << (Error*)0;
1725     }
1726     if (canGray) {
1727       if (!WarningOK) return false;
1728       Error::sev(Error::WARNING) << "SampleFormat: Gray1 would be better than Rgb1" << (Error*)0;
1729     }
1730     if (nncols<=4) {
1731       if (!WarningOK) return false;
1732       Error::sev(Error::WARNING) << "SampleFormat: "
1733         << (hasTransp ? "Transparent2 may" : "Indexed2 would")
1734         << " be better than Rgb1" << (Error*)0;
1735     }
1736     img=img->toRGB(1); if (bak!=img) delete bak;
1737     assert(img!=NULLP);
1738     sf=SF_Rgb1; return true;
1739    case SF_Gray4:
1740     if (nncols>16 || !canGray || minRGBBpc>4 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1741     /* ^^^ BUGFIX at Sat Jun  1 18:27:10 CEST 2002 */
1742     if (TryOnly) return WarningOK || (nncols>4 && !hasTransp);
1743     if (hasTransp) {
1744       if (!WarningOK) return false;
1745       Error::sev(Error::WARNING) <<
1746         (nncols<=15 ? "SampleFormat: Transparent4 would be better than "
1747                     : "SampleFormat: Transparent8 may be better than ")
1748         << "Gray4" << (Error*)0;
1749     }
1750     if (nncols<=4) {
1751       if (!WarningOK) return false;
1752       Error::sev(Error::WARNING) << "SampleFormat: Gray2 would be better than Gray4" << (Error*)0;
1753     }
1754     img=img->toGray(4); if (bak!=img) delete bak;
1755     assert(img!=NULLP);
1756     sf=SF_Gray4; return true;
1757    case SF_Indexed4:
1758     if (nncols>16 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1759     if (TryOnly) return WarningOK || (nncols>4 && minRGBBpc>=4);
1760     if (nncols<=4) {
1761       if (!WarningOK) return false;
1762       Error::sev(Error::WARNING) << "SampleFormat: Indexed2 would be better than Indexed4" << (Error*)0;
1763     }
1764     if (minRGBBpc<=1) {
1765       if (!WarningOK) return false;
1766       Error::sev(Error::WARNING) << "SampleFormat: RGB1 would be better than Indexed4" << (Error*)0;
1767     }
1768     if (canGray && minRGBBpc<=4) {
1769       if (!WarningOK) return false;
1770       Error::sev(Error::NOTICE) << "SampleFormat: Gray4 would be better than Indexed4" << (Error*)0;
1771     }
1772     assert(img->getTy()==img->TY_INDEXED);
1773     { iimg=PTS_dynamic_cast(Indexed*,img);
1774       iimg->setBpc(4);
1775       if (iimg->wouldSetTranspc(Transparent)) return false;
1776       iimg->setTranspcAndRepack(Transparent);
1777     }
1778     sf=SF_Indexed4; return true;
1779    case SF_Transparent8:
1780     if (!hasTransp && nncols==256 && PTS_dynamic_cast(Indexed*,img)->wouldSetTranspc(Transparent)) { hasTransp=true; --nncols; }
1781     if (nncols>255 || zero) return false;
1782     if (!WarningOK) return false;
1783     if (TryOnly) return true;
1784     Error::sev(Error::WARNING) << "SampleFormat: Transparent8 separates too many colors" << (Error*)0;
1785     if (!hasTransp) {
1786       Error::sev(Error::WARNING) << "SampleFormat: Indexed8 would be much better than Transparent8" << (Error*)0;
1787     }
1788     if (nncols<16) {
1789       Error::sev(Error::WARNING) << "SampleFormat: Transparent4 would be better than Transparent8" << (Error*)0;
1790     }
1791     assert(img->getTy()==img->TY_INDEXED);
1792     { iimg=PTS_dynamic_cast(Indexed*,img);
1793       iimg->setBpc(8); /* should be a no-op */
1794       iimg->setTranspcAndRepack(Transparent);
1795       // imgs=iimg->separate(); /* postponed because of GIF89a output */
1796     }
1797     sf=SF_Transparent8; return true;
1798    case SF_Rgb2:
1799     if (nncols>64 || minRGBBpc>2 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1800     if (TryOnly) return WarningOK || (nncols>16 && !canGray && !hasTransp);
1801     if (hasTransp) {
1802       if (!WarningOK) return false;
1803       Error::sev(Error::WARNING) << "SampleFormat: Transparent8 would be better than " << "Rgb2" << (Error*)0;
1804     }
1805     if (canGray) {
1806       if (!WarningOK) return false;
1807       Error::sev(Error::WARNING) << "SampleFormat: Gray2 would be better than Rgb2" << (Error*)0;
1808     }
1809     if (nncols<=16) {
1810       if (!WarningOK) return false;
1811       Error::sev(Error::WARNING) << "SampleFormat: "
1812         << (hasTransp ? "Transparent4 may" : "Indexed4 would")
1813         << " be better than Rgb2" << (Error*)0;
1814     }
1815     img=img->toRGB(2); if (bak!=img) delete bak;
1816     assert(img!=NULLP);
1817     sf=SF_Rgb2; return true;
1818    case SF_Gray8:
1819     if (nncols>256 || !canGray || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1820     if (TryOnly) return WarningOK || (nncols>16 && !hasTransp);
1821     if (hasTransp) {
1822       if (!WarningOK) return false;
1823       Error::sev(Error::WARNING) <<
1824         (nncols<=255 ? "SampleFormat: Transparent8 may be better than "
1825                      : "SampleFormat: ignoring transparency for ")
1826         << "Gray8" << (Error*)0;
1827     }
1828     if (minRGBBpc<=4) {
1829       /* BUGFIX at Wed Jul  3 01:07:44 CEST 2002 */
1830       if (!WarningOK) return false;
1831       Error::sev(Error::WARNING) << "SampleFormat: Gray4 would be better than Gray8" << (Error*)0;
1832     }
1833     img=img->toGray(8); if (bak!=img) delete bak;
1834     assert(img!=NULLP);
1835     sf=SF_Gray8; return true;
1836    case SF_Indexed8:
1837     // fprintf(stderr, "nncols=%u hasTransp=%u zero=%u\n", nncols, hasTransp, zero);
1838     if (nncols>256 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1839     // assert(0);
1840     if (TryOnly) return WarningOK || (nncols>16 && minRGBBpc>=8);
1841     if (nncols<=16) {
1842       if (!WarningOK) return false;
1843       Error::sev(Error::WARNING) << "SampleFormat: Indexed4 would be better than Indexed8" << (Error*)0;
1844     }
1845     if (minRGBBpc<=2) {
1846       if (!WarningOK) return false;
1847       Error::sev(Error::WARNING) << "SampleFormat: RGB2 would be better than Indexed8" << (Error*)0;
1848     }
1849     if (canGray) {
1850       if (!WarningOK) return false;
1851       Error::sev(Error::NOTICE) << "SampleFormat: Gray8 would be better than Indexed8" << (Error*)0;
1852     }
1853     assert(img->getTy()==img->TY_INDEXED);
1854     { iimg=PTS_dynamic_cast(Indexed*,img);
1855       iimg->setBpc(8); /* should be a no-op */
1856       if (iimg->wouldSetTranspc(Transparent)) return false;
1857       iimg->setTranspcAndRepack(Transparent);
1858     }
1859     sf=SF_Indexed8; return true;
1860    case SF_Rgb4:
1861     // fprintf(stderr, "minrgbbpc=%d to=%d\n", minRGBBpc, TryOnly);
1862     if (minRGBBpc>4 || zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1863     if (TryOnly) return WarningOK || (nncols>256 && !canGray && !hasTransp);
1864     if (hasTransp) {
1865       if (!WarningOK) return false;
1866       Error::sev(Error::WARNING) <<
1867         (nncols<=255 ? "SampleFormat: Transparent8 may be better than "
1868                      : "SampleFormat: ignoring transparency for ")
1869         << "Rgb4" << (Error*)0;
1870     }
1871     if (canGray) {
1872       if (!WarningOK) return false;
1873       Error::sev(Error::WARNING) << "SampleFormat: Gray4 would be better than Rgb4" << (Error*)0;
1874     }
1875     if (nncols<=256) {
1876       if (!WarningOK) return false;
1877       Error::sev(Error::WARNING) << "SampleFormat: "
1878         << (hasTransp && nncols<=255 ? "Transparent8 may" : "Indexed8 would")
1879         << " be better than Rgb4" << (Error*)0;
1880     }
1881     img=img->toRGB(4); if (bak!=img) delete bak;
1882     assert(img!=NULLP);
1883     sf=SF_Rgb4; return true;
1884    case SF_Rgb8:
1885     if (zero || hasTransp || img->hasPixelRGB(Transparent)) return false;
1886     if (TryOnly) return WarningOK || (nncols>256 && !canGray && minRGBBpc>=8 && !hasTransp);
1887     if (hasTransp) {
1888       if (!WarningOK) return false;
1889       Error::sev(Error::WARNING) <<
1890         (nncols<=255 ? "SampleFormat: Transparent8 may be better than "
1891                      : "SampleFormat: ignoring transparency for ")
1892         << "Rgb8" << (Error*)0;
1893     }
1894     if (canGray) {
1895       if (!WarningOK) return false;
1896       Error::sev(Error::WARNING) << "SampleFormat: Gray8 would be better than Rgb8" << (Error*)0;
1897     }
1898     if (minRGBBpc<=4) {
1899       if (!WarningOK) return false;
1900       Error::sev(Error::WARNING) << "SampleFormat: Rgb4 would be better than Rgb8" << (Error*)0;
1901     }
1902     if (nncols<=256) {
1903       if (!WarningOK) return false;
1904       Error::sev(Error::WARNING) << "SampleFormat: "
1905         << (hasTransp && nncols<=255 ? "Transparent8 may" : "Indexed8 would")
1906         << " be better than Rgb8" << (Error*)0;
1907     }
1908     img=img->toRGB(8); if (bak!=img) delete bak;
1909     assert(img!=NULLP);
1910     sf=SF_Rgb8; return true;
1911    case SF_Asis:
1912     if (img->getTy()!=img->TY_BLACKBOX) {
1913       Error::sev(Error::WARNING) << "SampleFormat: cannot convert image to /Asis" << (Error*)0;
1914       return false;
1915     }
1916     sf=SF_Asis; return true;
1917   }
1918   assert(0 && "unknown SampleFormat requested");
1919   return false; /* NOTREACHED */
1920 }
1921 
1922 /* __END__ */
1923