1 /* image.hpp -- classes for sampled (indexed, gray and rgb) images
2  * by pts@fazekas.hu Wed Feb 27 09:24:47 CET 2002
3  */
4 /* Imp: keep future transparency in toIndexed(...) */
5 
6 #ifdef __GNUC__
7 #ifndef __clang__
8 #pragma interface
9 #endif
10 #endif
11 
12 #ifndef SAMPLED_HPP
13 #define SAMPLED_HPP 1
14 
15 #include "config2.h"
16 #include "gensi.hpp"
17 
18 class Image {
19  public:
20   class RGB;
21   class Gray;
22   class Indexed;
23   /** Generic, sampled, rectangular image data. Abstract class.
24    * Each sample is 1, 2, 4 or 8 bits. Regions:
25    * beg..head-1: comment, ignored (e.g unused part of the indexed palette)
26    * headp..rowbeg-1: header: not predicted or compressed (e.g the indexed palette)
27    * rowbeg+0*rlen..rowbeg+0*rlen+rlen-1: sample data of the 0th row, compressed and predicted
28    * rowbeg+1*rlen..rowbeg+1*rlen+rlen-1: sample data of the 1st row
29    * rowbeg+2*rlen..rowbeg+2*rlen+rlen-1: sample data of the 1st row
30    * ...
31    * rowbeg+(h-1)*rlen..rowbeg+h*rlen-1: sample data of the last row
32    * trail..beg+len: trailer, ignored. Its length must be >=bpc.
33    * TODO: Remove trail, and see what breaks (e.g. setBpc).
34    */
35   class Sampled: public SimBuffer::Flat {
36    public:
37     /** Can hold 1 component of a sample of a single pixel. */
38     typedef unsigned char sample_t;
39     /** Can hold a height or depth of the image */
40     typedef unsigned int dimen_t;
41     /** Can hold a row length (in byte), which isn't greater than 3*(image width). */
42     typedef unsigned int rlen_t;
43     /** RGB = (red<<16)+(green<<8)+(blue). red, green and blue are 0..255 */
44     #if SIZEOF_LONG>4 && SIZEOF_INT>=4
45       typedef unsigned int rgb_t;
46     #else
47       typedef unsigned long rgb_t;
48     #endif
49     BEGIN_STATIC_ENUM1(unsigned char)
50       TY_INDEXED=1, TY_GRAY=2, TY_RGB=3, TY_OTHER=4, TY_BLACKBOX=5
51     END_STATIC_ENUM()
52     BEGIN_STATIC_ENUM1(unsigned char) // static const unsigned char
53         CS_UNKNOWN=0,           /* error/unspecified */
54         CS_GRAYSCALE=1,         /* monochrome */
55         CS_RGB=2,               /* red/green/blue */
56         CS_YCbCr=3,             /* Y/Cb/Cr (also known as YUV) */
57         CS_CMYK=4,              /* C/M/Y/K */
58         CS_YCCK=5,              /* Y/Cb/Cr/K */
59         CS_Indexed_RGB=12
60     END_STATIC_ENUM()
61     static const unsigned char cs2cpp[6];
62     /** @return NULLP on error */
63     static char const *cs2devcs(unsigned char cs);
64    protected:
65     char *headp;
66     char *rowbeg;
67     char *trail;
68     /** Extra offset */
69     slen_t xoffs;
70 
71     /** Length of one row, in bytes. Each row must begin on a byte boundary, so
72      * extra bits are appended after the rightmost pixel. These bits are
73      * arbitrary, and are ignored by the PostScript interpreter.
74      */
75     rlen_t rlen;
76     /** Image height, in pixels. */
77     dimen_t ht;
78     /** Image width, in pixels. */
79     dimen_t wd;
80     /** Color space. */
81     unsigned char cs;
82     /** Components per pixel. (number of planes, image depth). 1 for indexed,
83      * 1 for gray, 3 for RGB
84      */
85     unsigned char cpp;
86     /** BitsPerComponent: 1, 2, 4 or 8. PostScript allows 12 too. */
87     unsigned char bpc;
88     /** Transparent color value. Imp: ... */
89     rgb_t transpc;
90     /** Image type, TY_... */
91     unsigned char ty;
92     /** Initializes various fields, allocates memory. Called from descendants'
93      * constructors.
94      */
95     void init(slen_t l_comment, slen_t l_header, dimen_t wd_, dimen_t ht_,
96       unsigned char bpc_, unsigned char ty_, unsigned char cpp_);
97     /** Convert samples, make bpc=8, multiplication. */
98     void to8mul();
99     /** Convert samples, make bpc=8, no multiplication. */
100     void to8nomul();
101     /** Calls copyRGBRow.
102      * @return an Image::Indexed version of (this) iff the number of
103      *         colors<=256. Otherwise, returns NULLP.
104      */
105     Indexed* toIndexed0()/* const*/;
106     /** No averaging is done, only the red component is extracted */
107     Gray* toGray0(unsigned char bpc_);
108     RGB * toRGB0(unsigned char bpc_);
109     /** @return if any pixels are not gray: false. otherwise: true or false. */
110    public:
hasTransp() const111     inline bool hasTransp() const { return transpc!=0x1000000UL; }
112     virtual bool canGray() const =0;
113     /** @return an RGB BitsPerComponent number (1,2,4 or 8) to which the image
114      * could be converted without any loss. The default implementation calls
115      * copyRGBRow().
116      */
117     virtual unsigned char minRGBBpc() const;
~Sampled()118     inline virtual ~Sampled() { delete [] const_cast<char*>(beg); }
119     /** Copies whichrow as wd*3 bytes (R0,G0,B0,R1,G1,B1...) to `to' */
120     virtual void copyRGBRow(char *to, dimen_t whichrow) const =0;
121     virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const;
getRowbeg() const122     inline char *getRowbeg() const { return rowbeg; }
getWd() const123     inline dimen_t getWd() const { return wd; }
getHt() const124     inline dimen_t getHt() const { return ht; }
getTy() const125     inline unsigned char getTy() const { return ty; }
getBpc() const126     inline unsigned char getBpc() const { return bpc; }
getCpp() const127     inline unsigned char getCpp() const { return cpp; }
getCs() const128     inline unsigned char getCs() const { return cs; }
getXoffs() const129     inline slen_t getXoffs() const { return xoffs; }
getRlen() const130     inline rlen_t getRlen() const { return rlen; }
getTranspc() const131     inline rgb_t getTranspc() const { return transpc; }
getHeadp() const132     inline char *getHeadp() const { return headp; }
133     /** Convert samples, make bpc=8. */
134     virtual void to8() =0;
135     /** @return NULLP if too many colors for indexed; otherwise a new Image::Indexed.
136      * The caller should `delete' (this) if toIndexed()==NULLP.
137      */
138     virtual /*Image::*/Indexed* toIndexed() =0;
139     virtual /*Image::*/RGB*  toRGB(unsigned char bpc_) =0;
140     virtual /*Image::*/Gray* toGray(unsigned char bpc_) =0;
141     // virtual void setBpc(unsigned char bpc_) =0;
142     friend GenBuffer::Writable& operator<<(GenBuffer::Writable&, /*Image::*/Sampled const&);
143     /** @return address of static buffer: "#RRGGBB" */
144     static char *rgb2webhash(rgb_t);
145     /** @return (this) or an image containing (this) composed with alpha
146      * channel `al'
147      */
148     virtual Sampled* addAlpha(/*Image::*/Gray *al) =0;
149     /** assert(al.bpp=8) etc. Imp: document this */
150     static Indexed* addAlpha0(Indexed *iimg, Gray *al);
151   };
152 
153   class Indexed: public Sampled {
154    public:
155     /** @param ncols_ must be >= the colors used */
156     Indexed(dimen_t wd_, dimen_t ht_, unsigned short ncols_, unsigned char bpc_);
157     /** This includes the transparent color as well. */
getNcols() const158     inline unsigned short getNcols() const { return (rowbeg-headp)/3; }
159     /** Destroys the color table, and creates one with ncols_ colors.
160      * @param ncols_ must be <= the ncols_ specified in the constructor
161      */
162     void setNcols(unsigned short ncols_);
163     /** Decreases the size of the palette (forgets last colors) to the
164      * specified amount.
165      */
166     void setNcolsMove(unsigned short ncols_);
167     void setPal(unsigned char coloridx, rgb_t rgb);
168     rgb_t getPal(unsigned char coloridx) const;
169     /** @param coloridx must be >=0, transp must be -1 */
170     void setTransp(unsigned char coloridx);
171     /** @return new hasTransp */
172     bool setTranspc(rgb_t color);
173     /** Returns like setTranspc, but doesn't change the image.
174      * @return new hasTransp
175      */
176     bool wouldSetTranspc(rgb_t color) const;
177     /** Like setTranspc, but if it makes any changes, then it calls
178      * packPal() and changes back bpc to its old value.
179      */
180     void setTranspcAndRepack(rgb_t color);
181     virtual void copyRGBRow(char *to, dimen_t whichrow) const;
182     /* virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const; */
183     /** Packs (compresses) the palette so that it will be continuous in
184      * 0..ncols-1, and each color will be used exactly once. The
185      * transparent color (if present) will become black. As a side-effect,
186      * packPal() may set (this)->bpc=8.
187      */
188     void packPal();
189     virtual void to8();
190     virtual /*Image::*/Indexed* toIndexed();
191     virtual /*Image::*/RGB*  toRGB(unsigned char bpc_);
192     virtual /*Image::*/Gray* toGray(unsigned char bpc_);
193     virtual bool canGray() const;
getTransp() const194     inline signed short getTransp() const { return transp; }
getClearTransp()195     inline signed short getClearTransp() { signed short ret=transp; transp=-1; return ret; }
196     /** if (transp>0) transp=0;, converts image data. Does not change bpc. */
197     void makeTranspZero();
198     virtual unsigned char minRGBBpc() const;
199     /** Separates the current image into Indexed1 images. The caller is
200      * recommended to call packPal() first to reduce the number of required
201      * images.
202      * As a side-effect,
203      * separate() may set (this)->bpc=8.
204      * @return the array of images after full color separation: that is
205      *   a dynamically allocated array of `getNcols()-(getTransp()!=-1)'
206      *   Indexed images: each image is Indexed1, color 0 is opaque (with the
207      *   color obtained from (this)), color 1 is transparent. The returned
208      *   array is NULLP-terminated.
209      */
210     Indexed **separate();
211     /** Also calls packPal(). As a side effect, changes all transparent
212      * pixels to color index 0.
213      * @return NULLP if no transparent pixels.
214      */
215     Indexed *calcAlpha();
216     /** Deletes all elements of p, but not p itself.
217      * @param p a NULLP-terminated list of (Indexed*)s.
218      */
219     static void delete_separated(Indexed **p);
220     /** Reorganizes the image so it will have the specified bpc. Issues a
221      * runtime error if the specified bpc cannot be achieved.
222      * @param bpc_ the desired bpc, or 0: the best achievable.
223      */
224     virtual void setBpc(unsigned char bpc_);
225     void dumpDebug(GenBuffer::Writable& gw);
226    protected:
227     /* Index of the transparent color, or -1. */
228     signed short transp;
229     virtual /*Image::*/Sampled* addAlpha(/*Image::*/Gray *al);
230     /** Sorts the palette colors in lexicographic, stable order.
231      * Called from packPal() to get a consistent palette.
232      */
233     void sortPal();
234   };
235   class Gray: public Sampled {
236    public:
237     Gray(dimen_t wd_, dimen_t ht_, unsigned char bpc_);
238     virtual void copyRGBRow(char *to, dimen_t whichrow) const;
239     virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const;
240     virtual void to8();
241     virtual /*Image::*/Indexed* toIndexed();
242     virtual bool canGray() const;
243     // virtual void setBpc(unsigned char bpc_);
244     virtual /*Image::*/RGB    * toRGB(unsigned char bpc_);
245     virtual /*Image::*/Gray   * toGray(unsigned char bpc_);
246     virtual /*Image::*/Sampled* addAlpha(/*Image::*/Gray *al);
247     /** Calls to8(). */
248     void calcExtrema(unsigned char &lightest, unsigned char &darkest);
249   };
250   class RGB: public Sampled {
251    public:
252     RGB(dimen_t wd_, dimen_t ht_, unsigned char bpc_);
253     virtual void copyRGBRow(char *to, dimen_t whichrow) const;
254     /* virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const; */
255     virtual void to8();
256     virtual /*Image::*/Indexed* toIndexed();
257     virtual bool canGray() const;
258     // virtual void setBpc(unsigned char bpc_);
259     virtual /*Image::*/RGB    * toRGB(unsigned char bpc_);
260     virtual /*Image::*/Gray   * toGray(unsigned char bpc_);
261     virtual /*Image::*/Sampled* addAlpha(/*Image::*/Gray *al);
262   };
263 
264   /** Avoid including <stdio.h> */
265   typedef void *filep_t;
266 
267   /** Describes a driver that can load a specific image file format. */
268   struct Loader {
269     /** Filter::UngetFILED */
270     class UFD;
271     /** A function that can (allocate and) load a sampled image. Never
272      * returns NULL. On error, it calls Error::.... The filep_t argument
273      * should be really cast back to FILE*. The reader must fclose the FILE*.
274      */
275     // typedef Sampled*(*reader_t)(filep_t, SimBuffer::Flat const& loadHints);
276     typedef Sampled*(*reader_t)(UFD* ufd, SimBuffer::Flat const& loadHints);
277     BEGIN_STATIC_ENUM1(unsigned) MAGIC_LEN=64 END_STATIC_ENUM()
278     /** A function that checks the magic numbers at the beginning of a file
279      * (already read into buf), and returns NULL if it cannot load an image
280      * of that type, or a reader_t that will load the image. If (and only if!)
281      * file is shorter than 64 bytes, the buf is padded with '\000' bytes.
282      * @param f may read from freely if necessary (MAGIC_LEN is short), but
283      *   has to call rewind(f) before reading
284      */
285     typedef reader_t(*checker_t)(char buf[MAGIC_LEN], char bufend[MAGIC_LEN], SimBuffer::Flat const& loadHints, UFD* ufd);
286     /** A null-terminated, compact string describing (not defining!) the image
287      * file format.
288      * Examples: "GIF", "XPM", "PNM"
289      */
290     char const*format;
291     checker_t checker;
292     /** Null or next loader. */
293     Loader *next;
294   };
295 
296   /** Registers a new type of image Loader, i.e a new image file format. The
297    * new image format will be put in front of all others, and will be checked
298    * first
299    */
300   static void register0(Loader *);
301   /** Loads the image contained in te file `filename'.
302    * @param format NULLP is unknown (load any format)
303    *   or an Image::Loader::format already registered
304    */
305   static Sampled* load(Loader::UFD* ufd, SimBuffer::Flat const& loadHints, char const* format);
306   static Sampled* load(char const *filename, SimBuffer::Flat const& loadHints, filep_t stdin_f=(filep_t*)NULLP, char const* format=(char const*)NULLP);
307   /* Prints the list of available Loaders (->format), separated by spaces.
308    * Returns the number of available Loaders. Prepends a space if >= loaders.
309    */
310   static unsigned printLoaders(GenBuffer::Writable &);
311 
312   /** SampleFormat constants */
313   BEGIN_STATIC_ENUM(unsigned, sf_t)
314     SF_None=0, /* no specific sample format */
315     SF_Opaque=1,
316     SF_Transparent=2,
317     SF_Gray1=3,
318     SF_Indexed1=4,
319     SF_Mask=5,
320     SF_Transparent2=6,
321     SF_Gray2=7,
322     SF_Indexed2=8,
323     SF_Transparent4=9,
324     SF_Rgb1=10,
325     SF_Gray4=11,
326     SF_Indexed4=12,
327     SF_Transparent8=13,
328     SF_Rgb2=14,
329     SF_Gray8=15,
330     SF_Indexed8=16,
331     SF_Rgb4=17,
332     SF_Rgb8=18,
333     SF_Asis=19,
334     SF_Bbox=20,
335     SF_max=31
336   END_STATIC_ENUM()
337 
338   /** Contains (and memory-manages) an image, and optimization information
339    * as a cache.
340    *
341    * A SampledInfo contains an image in a canonical format. That is, if two
342    * images have the same RGB8 (identical width, height and pixels) or
343    * blackbox (identical bytes) representation, and SampledInfo{} are
344    * created for both of them, it is guaranteed that the two SampledInfo{}s
345    * contain the same image data (width, height, canGray, minRGBBpc,
346    * SampleFormat (except for bpc), pixel data, palette (same size, colors
347    * and color order)).
348    */
349   class SampledInfo {
350    public:
351     /** This constructor call takes ownership of the `img_' pointer: it either
352      * reuses the original image (and will delete it in ~SampledInfo), or it
353      * immediately deletes the image, and uses another image.
354      */
355     SampledInfo(Sampled *img_);
356     ~SampledInfo();
getImage() const357     inline Sampled* getImage() const { return img; }
358     /**
359      * Source image, may be modified even if TryOnly==false. If
360      * modified, the original will be freed.
361      * @param sf desired sample format, see Image::SF_* constants
362      * @param WarningOK if false: make the conversion fail if it would produces
363      *        a Warning
364      * @param TryOnly if true: don't do the real conversion (but may do auxilary,
365      *        idempontent, helper conversion), assume it has succeeded
366      * @param Transparent: as part of the conversion, try to make this RGB color
367      *        transparent
368      * @return true iff the conversion succeeded. Note that img may be the same
369      *         pointer even when true is returned. If false is returned, keeps
370      *         the image unchanged.
371      */
372     bool setSampleFormat(sf_t sf, bool WarningOK, bool TryOnly, Sampled::rgb_t Transparent);
getImgs() const373     inline Indexed **getImgs() const { return imgs; }
getImg() const374     inline Sampled *getImg() const { return img; }
getNncols() const375     inline unsigned getNncols() const { return nncols; }
376     void separate();
canGrayy() const377     inline bool canGrayy() const { return canGray; }
minRGBBpcc() const378     inline unsigned char minRGBBpcc() const { return minRGBBpc; }
hasTranspp() const379     inline bool hasTranspp() const { return hasTransp; }
minGrayBpcc() const380     inline unsigned char minGrayBpcc() const { return canGray ? minRGBBpc : 0; }
clearTransp()381     inline void clearTransp() { hasTransp=false; }
382    protected:
383     bool hasTransp;
384     /** Number of non-transparent colors, or 257 if >=257. */
385     unsigned nncols;
386     /** Has only gray colors. */
387     bool canGray;
388     unsigned char minRGBBpc;
389     Sampled *img;
390     /** The array of images after full color separation. May be NULLP (default),
391      * or a dynamically allocated array of `nncols' Indexed images: each
392      * image is Indexed1, color 0 is opaque (with any value), color 1
393      * is transparent.
394      */
395     Indexed **imgs;
396     sf_t sf;
397   };
398 };
399 
400 /** Dumps this Image as a rawbits PPM file (plus a comment indicating transparency)
401  * @return the Writable.
402  */
403 GenBuffer::Writable& operator<<(GenBuffer::Writable&, Image::Sampled const&);
404 
405 #endif
406