/* image.hpp -- classes for sampled (indexed, gray and rgb) images * by pts@fazekas.hu Wed Feb 27 09:24:47 CET 2002 */ /* Imp: keep future transparency in toIndexed(...) */ #ifdef __GNUC__ #ifndef __clang__ #pragma interface #endif #endif #ifndef SAMPLED_HPP #define SAMPLED_HPP 1 #include "config2.h" #include "gensi.hpp" class Image { public: class RGB; class Gray; class Indexed; /** Generic, sampled, rectangular image data. Abstract class. * Each sample is 1, 2, 4 or 8 bits. Regions: * beg..head-1: comment, ignored (e.g unused part of the indexed palette) * headp..rowbeg-1: header: not predicted or compressed (e.g the indexed palette) * rowbeg+0*rlen..rowbeg+0*rlen+rlen-1: sample data of the 0th row, compressed and predicted * rowbeg+1*rlen..rowbeg+1*rlen+rlen-1: sample data of the 1st row * rowbeg+2*rlen..rowbeg+2*rlen+rlen-1: sample data of the 1st row * ... * rowbeg+(h-1)*rlen..rowbeg+h*rlen-1: sample data of the last row * trail..beg+len: trailer, ignored. Its length must be >=bpc. * TODO: Remove trail, and see what breaks (e.g. setBpc). */ class Sampled: public SimBuffer::Flat { public: /** Can hold 1 component of a sample of a single pixel. */ typedef unsigned char sample_t; /** Can hold a height or depth of the image */ typedef unsigned int dimen_t; /** Can hold a row length (in byte), which isn't greater than 3*(image width). */ typedef unsigned int rlen_t; /** RGB = (red<<16)+(green<<8)+(blue). red, green and blue are 0..255 */ #if SIZEOF_LONG>4 && SIZEOF_INT>=4 typedef unsigned int rgb_t; #else typedef unsigned long rgb_t; #endif BEGIN_STATIC_ENUM1(unsigned char) TY_INDEXED=1, TY_GRAY=2, TY_RGB=3, TY_OTHER=4, TY_BLACKBOX=5 END_STATIC_ENUM() BEGIN_STATIC_ENUM1(unsigned char) // static const unsigned char CS_UNKNOWN=0, /* error/unspecified */ CS_GRAYSCALE=1, /* monochrome */ CS_RGB=2, /* red/green/blue */ CS_YCbCr=3, /* Y/Cb/Cr (also known as YUV) */ CS_CMYK=4, /* C/M/Y/K */ CS_YCCK=5, /* Y/Cb/Cr/K */ CS_Indexed_RGB=12 END_STATIC_ENUM() static const unsigned char cs2cpp[6]; /** @return NULLP on error */ static char const *cs2devcs(unsigned char cs); protected: char *headp; char *rowbeg; char *trail; /** Extra offset */ slen_t xoffs; /** Length of one row, in bytes. Each row must begin on a byte boundary, so * extra bits are appended after the rightmost pixel. These bits are * arbitrary, and are ignored by the PostScript interpreter. */ rlen_t rlen; /** Image height, in pixels. */ dimen_t ht; /** Image width, in pixels. */ dimen_t wd; /** Color space. */ unsigned char cs; /** Components per pixel. (number of planes, image depth). 1 for indexed, * 1 for gray, 3 for RGB */ unsigned char cpp; /** BitsPerComponent: 1, 2, 4 or 8. PostScript allows 12 too. */ unsigned char bpc; /** Transparent color value. Imp: ... */ rgb_t transpc; /** Image type, TY_... */ unsigned char ty; /** Initializes various fields, allocates memory. Called from descendants' * constructors. */ void init(slen_t l_comment, slen_t l_header, dimen_t wd_, dimen_t ht_, unsigned char bpc_, unsigned char ty_, unsigned char cpp_); /** Convert samples, make bpc=8, multiplication. */ void to8mul(); /** Convert samples, make bpc=8, no multiplication. */ void to8nomul(); /** Calls copyRGBRow. * @return an Image::Indexed version of (this) iff the number of * colors<=256. Otherwise, returns NULLP. */ Indexed* toIndexed0()/* const*/; /** No averaging is done, only the red component is extracted */ Gray* toGray0(unsigned char bpc_); RGB * toRGB0(unsigned char bpc_); /** @return if any pixels are not gray: false. otherwise: true or false. */ public: inline bool hasTransp() const { return transpc!=0x1000000UL; } virtual bool canGray() const =0; /** @return an RGB BitsPerComponent number (1,2,4 or 8) to which the image * could be converted without any loss. The default implementation calls * copyRGBRow(). */ virtual unsigned char minRGBBpc() const; inline virtual ~Sampled() { delete [] const_cast(beg); } /** Copies whichrow as wd*3 bytes (R0,G0,B0,R1,G1,B1...) to `to' */ virtual void copyRGBRow(char *to, dimen_t whichrow) const =0; virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const; inline char *getRowbeg() const { return rowbeg; } inline dimen_t getWd() const { return wd; } inline dimen_t getHt() const { return ht; } inline unsigned char getTy() const { return ty; } inline unsigned char getBpc() const { return bpc; } inline unsigned char getCpp() const { return cpp; } inline unsigned char getCs() const { return cs; } inline slen_t getXoffs() const { return xoffs; } inline rlen_t getRlen() const { return rlen; } inline rgb_t getTranspc() const { return transpc; } inline char *getHeadp() const { return headp; } /** Convert samples, make bpc=8. */ virtual void to8() =0; /** @return NULLP if too many colors for indexed; otherwise a new Image::Indexed. * The caller should `delete' (this) if toIndexed()==NULLP. */ virtual /*Image::*/Indexed* toIndexed() =0; virtual /*Image::*/RGB* toRGB(unsigned char bpc_) =0; virtual /*Image::*/Gray* toGray(unsigned char bpc_) =0; // virtual void setBpc(unsigned char bpc_) =0; friend GenBuffer::Writable& operator<<(GenBuffer::Writable&, /*Image::*/Sampled const&); /** @return address of static buffer: "#RRGGBB" */ static char *rgb2webhash(rgb_t); /** @return (this) or an image containing (this) composed with alpha * channel `al' */ virtual Sampled* addAlpha(/*Image::*/Gray *al) =0; /** assert(al.bpp=8) etc. Imp: document this */ static Indexed* addAlpha0(Indexed *iimg, Gray *al); }; class Indexed: public Sampled { public: /** @param ncols_ must be >= the colors used */ Indexed(dimen_t wd_, dimen_t ht_, unsigned short ncols_, unsigned char bpc_); /** This includes the transparent color as well. */ inline unsigned short getNcols() const { return (rowbeg-headp)/3; } /** Destroys the color table, and creates one with ncols_ colors. * @param ncols_ must be <= the ncols_ specified in the constructor */ void setNcols(unsigned short ncols_); /** Decreases the size of the palette (forgets last colors) to the * specified amount. */ void setNcolsMove(unsigned short ncols_); void setPal(unsigned char coloridx, rgb_t rgb); rgb_t getPal(unsigned char coloridx) const; /** @param coloridx must be >=0, transp must be -1 */ void setTransp(unsigned char coloridx); /** @return new hasTransp */ bool setTranspc(rgb_t color); /** Returns like setTranspc, but doesn't change the image. * @return new hasTransp */ bool wouldSetTranspc(rgb_t color) const; /** Like setTranspc, but if it makes any changes, then it calls * packPal() and changes back bpc to its old value. */ void setTranspcAndRepack(rgb_t color); virtual void copyRGBRow(char *to, dimen_t whichrow) const; /* virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const; */ /** Packs (compresses) the palette so that it will be continuous in * 0..ncols-1, and each color will be used exactly once. The * transparent color (if present) will become black. As a side-effect, * packPal() may set (this)->bpc=8. */ void packPal(); virtual void to8(); virtual /*Image::*/Indexed* toIndexed(); virtual /*Image::*/RGB* toRGB(unsigned char bpc_); virtual /*Image::*/Gray* toGray(unsigned char bpc_); virtual bool canGray() const; inline signed short getTransp() const { return transp; } inline signed short getClearTransp() { signed short ret=transp; transp=-1; return ret; } /** if (transp>0) transp=0;, converts image data. Does not change bpc. */ void makeTranspZero(); virtual unsigned char minRGBBpc() const; /** Separates the current image into Indexed1 images. The caller is * recommended to call packPal() first to reduce the number of required * images. * As a side-effect, * separate() may set (this)->bpc=8. * @return the array of images after full color separation: that is * a dynamically allocated array of `getNcols()-(getTransp()!=-1)' * Indexed images: each image is Indexed1, color 0 is opaque (with the * color obtained from (this)), color 1 is transparent. The returned * array is NULLP-terminated. */ Indexed **separate(); /** Also calls packPal(). As a side effect, changes all transparent * pixels to color index 0. * @return NULLP if no transparent pixels. */ Indexed *calcAlpha(); /** Deletes all elements of p, but not p itself. * @param p a NULLP-terminated list of (Indexed*)s. */ static void delete_separated(Indexed **p); /** Reorganizes the image so it will have the specified bpc. Issues a * runtime error if the specified bpc cannot be achieved. * @param bpc_ the desired bpc, or 0: the best achievable. */ virtual void setBpc(unsigned char bpc_); void dumpDebug(GenBuffer::Writable& gw); protected: /* Index of the transparent color, or -1. */ signed short transp; virtual /*Image::*/Sampled* addAlpha(/*Image::*/Gray *al); /** Sorts the palette colors in lexicographic, stable order. * Called from packPal() to get a consistent palette. */ void sortPal(); }; class Gray: public Sampled { public: Gray(dimen_t wd_, dimen_t ht_, unsigned char bpc_); virtual void copyRGBRow(char *to, dimen_t whichrow) const; virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const; virtual void to8(); virtual /*Image::*/Indexed* toIndexed(); virtual bool canGray() const; // virtual void setBpc(unsigned char bpc_); virtual /*Image::*/RGB * toRGB(unsigned char bpc_); virtual /*Image::*/Gray * toGray(unsigned char bpc_); virtual /*Image::*/Sampled* addAlpha(/*Image::*/Gray *al); /** Calls to8(). */ void calcExtrema(unsigned char &lightest, unsigned char &darkest); }; class RGB: public Sampled { public: RGB(dimen_t wd_, dimen_t ht_, unsigned char bpc_); virtual void copyRGBRow(char *to, dimen_t whichrow) const; /* virtual bool hasPixelRGB(Image::Sampled::rgb_t rgb) const; */ virtual void to8(); virtual /*Image::*/Indexed* toIndexed(); virtual bool canGray() const; // virtual void setBpc(unsigned char bpc_); virtual /*Image::*/RGB * toRGB(unsigned char bpc_); virtual /*Image::*/Gray * toGray(unsigned char bpc_); virtual /*Image::*/Sampled* addAlpha(/*Image::*/Gray *al); }; /** Avoid including */ typedef void *filep_t; /** Describes a driver that can load a specific image file format. */ struct Loader { /** Filter::UngetFILED */ class UFD; /** A function that can (allocate and) load a sampled image. Never * returns NULL. On error, it calls Error::.... The filep_t argument * should be really cast back to FILE*. The reader must fclose the FILE*. */ // typedef Sampled*(*reader_t)(filep_t, SimBuffer::Flat const& loadHints); typedef Sampled*(*reader_t)(UFD* ufd, SimBuffer::Flat const& loadHints); BEGIN_STATIC_ENUM1(unsigned) MAGIC_LEN=64 END_STATIC_ENUM() /** A function that checks the magic numbers at the beginning of a file * (already read into buf), and returns NULL if it cannot load an image * of that type, or a reader_t that will load the image. If (and only if!) * file is shorter than 64 bytes, the buf is padded with '\000' bytes. * @param f may read from freely if necessary (MAGIC_LEN is short), but * has to call rewind(f) before reading */ typedef reader_t(*checker_t)(char buf[MAGIC_LEN], char bufend[MAGIC_LEN], SimBuffer::Flat const& loadHints, UFD* ufd); /** A null-terminated, compact string describing (not defining!) the image * file format. * Examples: "GIF", "XPM", "PNM" */ char const*format; checker_t checker; /** Null or next loader. */ Loader *next; }; /** Registers a new type of image Loader, i.e a new image file format. The * new image format will be put in front of all others, and will be checked * first */ static void register0(Loader *); /** Loads the image contained in te file `filename'. * @param format NULLP is unknown (load any format) * or an Image::Loader::format already registered */ static Sampled* load(Loader::UFD* ufd, SimBuffer::Flat const& loadHints, char const* format); static Sampled* load(char const *filename, SimBuffer::Flat const& loadHints, filep_t stdin_f=(filep_t*)NULLP, char const* format=(char const*)NULLP); /* Prints the list of available Loaders (->format), separated by spaces. * Returns the number of available Loaders. Prepends a space if >= loaders. */ static unsigned printLoaders(GenBuffer::Writable &); /** SampleFormat constants */ BEGIN_STATIC_ENUM(unsigned, sf_t) SF_None=0, /* no specific sample format */ SF_Opaque=1, SF_Transparent=2, SF_Gray1=3, SF_Indexed1=4, SF_Mask=5, SF_Transparent2=6, SF_Gray2=7, SF_Indexed2=8, SF_Transparent4=9, SF_Rgb1=10, SF_Gray4=11, SF_Indexed4=12, SF_Transparent8=13, SF_Rgb2=14, SF_Gray8=15, SF_Indexed8=16, SF_Rgb4=17, SF_Rgb8=18, SF_Asis=19, SF_Bbox=20, SF_max=31 END_STATIC_ENUM() /** Contains (and memory-manages) an image, and optimization information * as a cache. * * A SampledInfo contains an image in a canonical format. That is, if two * images have the same RGB8 (identical width, height and pixels) or * blackbox (identical bytes) representation, and SampledInfo{} are * created for both of them, it is guaranteed that the two SampledInfo{}s * contain the same image data (width, height, canGray, minRGBBpc, * SampleFormat (except for bpc), pixel data, palette (same size, colors * and color order)). */ class SampledInfo { public: /** This constructor call takes ownership of the `img_' pointer: it either * reuses the original image (and will delete it in ~SampledInfo), or it * immediately deletes the image, and uses another image. */ SampledInfo(Sampled *img_); ~SampledInfo(); inline Sampled* getImage() const { return img; } /** * Source image, may be modified even if TryOnly==false. If * modified, the original will be freed. * @param sf desired sample format, see Image::SF_* constants * @param WarningOK if false: make the conversion fail if it would produces * a Warning * @param TryOnly if true: don't do the real conversion (but may do auxilary, * idempontent, helper conversion), assume it has succeeded * @param Transparent: as part of the conversion, try to make this RGB color * transparent * @return true iff the conversion succeeded. Note that img may be the same * pointer even when true is returned. If false is returned, keeps * the image unchanged. */ bool setSampleFormat(sf_t sf, bool WarningOK, bool TryOnly, Sampled::rgb_t Transparent); inline Indexed **getImgs() const { return imgs; } inline Sampled *getImg() const { return img; } inline unsigned getNncols() const { return nncols; } void separate(); inline bool canGrayy() const { return canGray; } inline unsigned char minRGBBpcc() const { return minRGBBpc; } inline bool hasTranspp() const { return hasTransp; } inline unsigned char minGrayBpcc() const { return canGray ? minRGBBpc : 0; } inline void clearTransp() { hasTransp=false; } protected: bool hasTransp; /** Number of non-transparent colors, or 257 if >=257. */ unsigned nncols; /** Has only gray colors. */ bool canGray; unsigned char minRGBBpc; Sampled *img; /** The array of images after full color separation. May be NULLP (default), * or a dynamically allocated array of `nncols' Indexed images: each * image is Indexed1, color 0 is opaque (with any value), color 1 * is transparent. */ Indexed **imgs; sf_t sf; }; }; /** Dumps this Image as a rawbits PPM file (plus a comment indicating transparency) * @return the Writable. */ GenBuffer::Writable& operator<<(GenBuffer::Writable&, Image::Sampled const&); #endif