1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
|
/* image.hpp -- classes for sampled (indexed, gray and rgb) images
* by pts@fazekas.hu Wed Feb 27 09:24:47 CET 2002
*/
/* Imp: find memory leaks */
/* Imp: keep future transparency in toIndexed(...) */
#ifdef __GNUC__
#pragma interface
#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.
*/
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(Sampled *img, 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);
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
*/
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
|