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