/* image.hpp -- classes for sampled (indexed, gray and rgb) images
* by
[email protected] 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<char*>(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 <stdio.h> */
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