1 //****************************************************************************** 2 /// 3 /// @file base/image/image.h 4 /// 5 /// Declarations related to image containers. 6 /// 7 /// @copyright 8 /// @parblock 9 /// 10 /// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8. 11 /// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd. 12 /// 13 /// POV-Ray is free software: you can redistribute it and/or modify 14 /// it under the terms of the GNU Affero General Public License as 15 /// published by the Free Software Foundation, either version 3 of the 16 /// License, or (at your option) any later version. 17 /// 18 /// POV-Ray is distributed in the hope that it will be useful, 19 /// but WITHOUT ANY WARRANTY; without even the implied warranty of 20 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 /// GNU Affero General Public License for more details. 22 /// 23 /// You should have received a copy of the GNU Affero General Public License 24 /// along with this program. If not, see <http://www.gnu.org/licenses/>. 25 /// 26 /// ---------------------------------------------------------------------------- 27 /// 28 /// POV-Ray is based on the popular DKB raytracer version 2.12. 29 /// DKBTrace was originally written by David K. Buck. 30 /// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. 31 /// 32 /// @endparblock 33 /// 34 //****************************************************************************** 35 36 #ifndef POVRAY_BASE_IMAGE_H 37 #define POVRAY_BASE_IMAGE_H 38 39 // Module config header file must be the first file included within POV-Ray unit header files 40 #include "base/configbase.h" 41 42 // POV-Ray base header files 43 #include "base/fileinputoutput.h" 44 #include "base/pov_err.h" 45 #include "base/image/colourspace.h" 46 #include "base/image/encoding.h" 47 48 namespace pov_base 49 { 50 51 class DitherStrategy; 52 using DitherStrategySPtr = shared_ptr<DitherStrategy>; 53 54 //############################################################################## 55 /// 56 /// @defgroup PovBaseImage Image Handling 57 /// @ingroup PovBase 58 /// 59 /// @{ 60 61 /** 62 * Generic image data container. 63 * 64 * @note Except for access functions having a `premul` parameter as well as those named 65 * `GetEncodedSomethingValue` or `SetEncodedSomethingValue`, all other access functions are unaware of 66 * premultiplied vs. non-premultiplied alpha issues, and will access the data in whatever format 67 * it is stored (as far as alpha handling goes). 68 * 69 * @note When backed by a gamma-encoded data container, unsigned int access methods are presumed 70 * to read/write raw encoded data, while float access methods will read/write logical 71 * linear values. 72 * 73 * @note Image coordinates increase from left (x=0) to right (x>0) and from top (y=0) to bottom (y>0). 74 */ 75 class Image 76 { 77 public: 78 struct RGBMapEntry 79 { 80 float red; 81 float green; 82 float blue; 83 RGBMapEntryRGBMapEntry84 RGBMapEntry() : red(0.0f), green(0.0f), blue(0.0f) { } RGBMapEntryRGBMapEntry85 RGBMapEntry(float r, float g, float b) : red(r), green(g), blue(b) { } 86 }; 87 88 struct RGBAMapEntry 89 { 90 float red; 91 float green; 92 float blue; 93 float alpha; 94 RGBAMapEntryRGBAMapEntry95 RGBAMapEntry() : red(0.0f), green(0.0f), blue(0.0f), alpha(0.0f) { } RGBAMapEntryRGBAMapEntry96 RGBAMapEntry(float r, float g, float b, float a) : red(r), green(g), blue(b), alpha(a) { } 97 }; 98 99 struct RGBFTMapEntry 100 { 101 float red; 102 float green; 103 float blue; 104 float filter; 105 float transm; 106 RGBFTMapEntryRGBFTMapEntry107 RGBFTMapEntry() : red(0.0f), green(0.0f), blue(0.0f), filter(0.0f), transm(0.0f) { } RGBFTMapEntryRGBFTMapEntry108 RGBFTMapEntry(float r, float g, float b, float f, float t) : red(r), green(g), blue(b), filter(f), transm(t) { } 109 }; 110 111 enum ColourMapType 112 { 113 NoColourMap, 114 RGBColourMap, 115 RGBAColourMap, 116 RGBFTColourMap 117 }; 118 119 enum ImageChannelDataType 120 { 121 kImageChannelDataType_Int8, 122 kImageChannelDataType_Int16, 123 kImageChannelDataType_Gamma8, 124 kImageChannelDataType_Gamma16, 125 }; 126 127 enum ImageChannelLayout 128 { 129 kImageChannelLayout_Gray, 130 kImageChannelLayout_GrayA, 131 kImageChannelLayout_RGB, 132 kImageChannelLayout_RGBA, 133 }; 134 135 enum ImageDataType 136 { 137 /// Value used to indicate that image decoder is free to pick the most fitting type. 138 Undefined, 139 /// Palette-based image with 2 palette entries. 140 Bit_Map, 141 /// Palette-based image with up to 256 palette entries. 142 Colour_Map, 143 /// Single-channel (grayscale) image using 8-bit linear encoding. 144 Gray_Int8, 145 /// Single-channel (grayscale) image using 16-bit linear encoding. 146 Gray_Int16, 147 /// Dual-channel (grayscale and alpha) image using 8-bit linear encoding. 148 GrayA_Int8, 149 /// Dual-channel (grayscale and alpha) image using 16-bit linear encoding. 150 GrayA_Int16, 151 /// 3-channel (colour) image using 8-bit linear encoding. 152 RGB_Int8, 153 /// 3-channel (colour) image using 16-bit linear encoding. 154 RGB_Int16, 155 /// 4-channel (colour and alpha) image using 8-bit linear encoding. 156 RGBA_Int8, 157 /// 4-channel (colour and alpha) image using 16-bit linear encoding. 158 RGBA_Int16, 159 /// 5-channel (colour, filter and transmit) image using single-precision floating-point encoding. 160 RGBFT_Float, 161 /// 3-channel (colour) image using 8-bit gamma encoding. 162 RGB_Gamma8, 163 /// 3-channel (colour) image using 16-bit gamma encoding. 164 RGB_Gamma16, 165 /// 4-channel (colour and alpha) image using 8-bit gamma colour encoding and 8-bit linear alpha encoding. 166 RGBA_Gamma8, 167 /// 4-channel (colour and alpha) image using 16-bit gamma colour encoding and 16-bit linear alpha encoding. 168 RGBA_Gamma16, 169 /// Single-channel (grayscale) image using 8-bit gamma encoding. 170 Gray_Gamma8, 171 /// Single-channel (grayscale) image using 16-bit gamma encoding. 172 Gray_Gamma16, 173 /// Dual-channel (grayscale and alpha) image using 8-bit gamma greyscale encoding and 8-bit linear alpha encoding. 174 GrayA_Gamma8, 175 /// Dual-channel (grayscale and alpha) image using 16-bit gamma greyscale encoding and 16-bit linear alpha encoding. 176 GrayA_Gamma16 177 }; 178 179 /// Image file type identifier. 180 enum ImageFileType 181 { 182 GIF, 183 POT, 184 SYS, 185 IFF, 186 TGA, 187 PGM, 188 PPM, 189 PNG, 190 JPEG, 191 TIFF, 192 BMP, 193 EXR, 194 HDR 195 }; 196 197 /// The mode to use for alpha handling. 198 enum AlphaMode 199 { 200 kAlphaMode_None, ///< Disable alpha channel. @note Not a valid setting for input files. 201 kAlphaMode_Default, ///< Use auto-detection or file format specific default. 202 kAlphaMode_Premultiplied, ///< Enforce premultiplied mode, aka associated alpha. 203 kAlphaMode_Straight, ///< Enforce straight mode, aka unassociated alpha. 204 }; 205 206 struct ReadOptions 207 { 208 ImageDataType itype; 209 SimpleGammaCurvePtr defaultGamma; // the gamma curve to use by default for converting to linear colour space 210 SimpleGammaCurvePtr workingGamma; // the working colour space gamma 211 bool gammaOverride; // whether to apply defaultGamma even if the file indicates a different gamma 212 bool gammacorrect; // whether to do any gamma correction at all; if false, raw encoded values are used 213 bool premultipliedOverride; // whether to override file-format default for alpha premultiplication 214 bool premultiplied; // whether to expect premultiplied ("associated") alpha or not ("straight alpha") 215 mutable vector<string> warnings; 216 ReadOptionsReadOptions217 ReadOptions() : itype(Undefined), gammaOverride(false), gammacorrect(false), premultipliedOverride(false), premultiplied(false) { } 218 }; 219 220 struct WriteOptions 221 { 222 //------------------------------------------------------------------------------ 223 /// @name Gamma Handling 224 /// @{ 225 226 /// Gamma to encode for. 227 /// Set this to `nullptr` to use the file format specific default. 228 /// @note 229 /// This setting is ignored with file formats that mandate linear encoding or a 230 /// specific encoding gamma. 231 SimpleGammaCurvePtr encodingGamma; 232 233 /// Working colour space gamma to encode from. 234 /// Set to `nullptr` or a neutral gamma curve to indicate linear working colour space. 235 SimpleGammaCurvePtr workingGamma; 236 237 /// @} 238 //------------------------------------------------------------------------------ 239 240 /// Dithering algorithm. 241 /// Leave this at the default to disable dithering. 242 /// @note 243 /// This setting is ignored with file formats that are not prone to colour banding 244 /// artifacts (such as OpenEXR) or do not benefit from dithering (such as JPEG). 245 DitherStrategySPtr ditherStrategy; 246 247 unsigned int offset_x; ///< Currently not actively set. 248 unsigned int offset_y; ///< Currently not actively set. 249 250 /// How to handle image transparency. 251 /// Set this to @ref kAlphaMode_None to disable creation of an alpha channel, 252 /// @ref kAlphaMode_Default to write an alpha channel using a file format specific 253 /// default mode, @ref kAlphaMode_Premultiplied to write an alpha channel using 254 /// premultiplied mode (aka associated alpha), or @ref kAlphaMode_Straight to write an 255 /// alpha channel using straight mode (aka unassociated alpha). 256 /// @note 257 /// This setting is ignored with file formats that do not support transparency, or 258 /// for which transparency support has not been implemented in POV-Ray. 259 AlphaMode alphaMode; 260 261 /// Bits per colour channel. 262 /// Set this to `0` to use the file format specific default. 263 /// @note 264 /// This setting is ignored with file formats that mandate a particular bit depth, 265 /// for which POV-Ray only supports a particular bit depth, or for which bit depth 266 /// is not applicable (such as JPEG). 267 /// @note 268 /// The actual bit depth may differ if the file format or POV-Ray's implementation 269 /// thereof does not support the requested bit depth. In that case, the next higher 270 /// supported bit depth is used if possible, or the highest supported bit depth 271 /// otherwise. 272 unsigned char bitsPerChannel; 273 274 /// Whether to use compression. 275 /// Set this to a negative value to use the file format specific default setting, `0` 276 /// to disable, or any higher value to enable. Depending on the file format, such a 277 /// setting may be interpreted as a format specific compression parameter. 278 /// @note 279 /// Whether a positive value indicates a mode, compression level or quality level 280 /// is specific to the file format. 281 /// @note 282 /// This setting is ignored with file formats that never use compression, always 283 /// use compression, or for which POV-Ray's implementation leaves no choice. 284 signed short compression; 285 286 /// Whether to write a greyscale image. 287 /// @note 288 /// This setting is ignored with file formats that do not support a dedicated 289 /// greyscale mode, or for which support of such a mode has not been implemented 290 /// in POV-Ray. 291 bool grayscale : 1; 292 293 WriteOptions(); 294 AlphaIsEnabledWriteOptions295 inline bool AlphaIsEnabled() const 296 { 297 return (alphaMode != Image::kAlphaMode_None); 298 } 299 AlphaIsPremultipliedWriteOptions300 inline bool AlphaIsPremultiplied(bool defaultToPremultiplied) const 301 { 302 if (defaultToPremultiplied) 303 return (alphaMode != Image::kAlphaMode_Straight); 304 else 305 return (alphaMode == Image::kAlphaMode_Premultiplied); 306 } 307 GetTranscodingGammaCurveWriteOptions308 GammaCurvePtr GetTranscodingGammaCurve(GammaCurvePtr defaultEncodingGamma) const 309 { 310 if (encodingGamma) 311 return TranscodingGammaCurve::Get(workingGamma, encodingGamma); 312 else 313 return TranscodingGammaCurve::Get(workingGamma, defaultEncodingGamma); 314 } 315 }; 316 ~Image()317 virtual ~Image() { } 318 319 static ImageDataType GetImageDataType (ImageChannelDataType channelType, ImageChannelLayout layout); 320 321 static Image *Create(unsigned int w, unsigned int h, ImageDataType t, unsigned int maxRAMmbHint, unsigned int pixelsPerBlockHint); 322 static Image *Create(unsigned int w, unsigned int h, ImageDataType t, bool allowFileBacking = false); 323 static Image *Create(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBMapEntry>& m, bool allowFileBacking = false); 324 static Image *Create(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBAMapEntry>& m, bool allowFileBacking = false); 325 static Image *Create(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBFTMapEntry>& m, bool allowFileBacking = false); 326 327 // ftype = use this image type, if "Undefined" use best match 328 static Image *Read(ImageFileType ftype, IStream *file, const ReadOptions& options = ReadOptions()); 329 330 static void Write(ImageFileType ftype, OStream *file, const Image *image, const WriteOptions& options = WriteOptions()); 331 GetWidth()332 unsigned int GetWidth() const { return width; } GetHeight()333 unsigned int GetHeight() const { return height; } GetImageDataType()334 ImageDataType GetImageDataType() const { return type; } 335 336 /// Returns true if image is fully opaque. 337 virtual bool IsOpaque() const = 0; 338 339 virtual bool IsGrayscale() const = 0; 340 virtual bool IsColour() const = 0; 341 virtual bool IsFloat() const = 0; 342 virtual bool IsInt() const = 0; 343 344 /// Returns true if backed by a palette-based container. 345 virtual bool IsIndexed() const = 0; 346 347 /// Returns true if backed by a gamma-encoded data container. 348 virtual bool IsGammaEncoded() const = 0; 349 350 /// Returns true if container features a genuine alpha channel. 351 virtual bool HasAlphaChannel() const = 0; 352 353 /// Returns true if container features genuine filter & transmit channels. 354 virtual bool HasFilterTransmit() const = 0; 355 356 /// Returns true if container features any way of storing transparency information. HasTransparency()357 virtual bool HasTransparency() const { return (HasAlphaChannel() || HasFilterTransmit()); } 358 359 /// Returns the maximum value supported by int access methods or for palette indices (1, 255 or 65535). 360 virtual unsigned int GetMaxIntValue() const = 0; 361 362 /// Specifies whether container holds color data premultiplied with alpha SetPremultiplied(bool b)363 void SetPremultiplied(bool b) { premultiplied = b; } // TODO - mechanism not fully functional yet for image reading 364 365 /// Returns true if container holds data premultiplied with alpha IsPremultiplied()366 bool IsPremultiplied() const { return premultiplied; } 367 368 /** 369 * Requests the image container to perform deferred decoding of integer values. 370 * In order for the request to be honored, the requested value range must match the container's native 371 * bit depth, and the container must have a neutral encoding gamma curve set at present. 372 * @note If the request is honored, this will also affect subsequent unsigned int read accesses. 373 * @param[in,out] gamma Gamma encoding curve of the encoded material. Set to empty by the function 374 * if the request is accepted. 375 * @param[in] max Maximum encoded value. In order for the request to be honored, this must match 376 * the image container's native bit depth. 377 * @return true it the request is accepted, false otherwise. 378 */ 379 virtual bool TryDeferDecoding(GammaCurvePtr& gamma, unsigned int max) = 0; 380 381 void GetRGBIndexedValue(unsigned char index, float& red, float& green, float& blue) const; 382 void GetRGBAIndexedValue(unsigned char index, float& red, float& green, float& blue, float& alpha) const; 383 void GetRGBTIndexedValue(unsigned char index, float& red, float& green, float& blue, float& transm) const; 384 void GetRGBFTIndexedValue(unsigned char index, float& red, float& green, float& blue, float& filter, float& transm) const; 385 386 void SetRGBIndexedValue(unsigned char index, float red, float green, float blue); 387 void SetRGBAIndexedValue(unsigned char index, float red, float green, float blue, float alpha); 388 void SetRGBTIndexedValue(unsigned char index, float red, float green, float blue, float transm); 389 void SetRGBFTIndexedValue(unsigned char index, float red, float green, float blue, float filter, float transm); 390 void SetRGBFTIndexedValue(unsigned char index, const RGBFTColour& colour); 391 392 virtual bool GetBitValue(unsigned int x, unsigned int y) const = 0; 393 virtual float GetGrayValue(unsigned int x, unsigned int y) const = 0; 394 virtual void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const = 0; 395 virtual void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const = 0; 396 virtual void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const = 0; 397 virtual void GetRGBTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& transm) const = 0; 398 virtual void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const = 0; 399 virtual unsigned char GetIndexedValue(unsigned int x, unsigned int y); 400 401 virtual void SetBitValue(unsigned int x, unsigned int y, bool bit) = 0; 402 virtual void SetGrayValue(unsigned int x, unsigned int y, float gray) = 0; 403 virtual void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray) = 0; 404 virtual void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha) = 0; 405 virtual void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha) = 0; 406 virtual void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue) = 0; 407 virtual void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue) = 0; 408 virtual void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha) = 0; 409 virtual void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha) = 0; 410 virtual void SetRGBTValue(unsigned int x, unsigned int y, float red, float green, float blue, float transm) = 0; 411 virtual void SetRGBTValue(unsigned int x, unsigned int y, const RGBTColour& col) = 0; 412 virtual void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm) = 0; 413 virtual void SetRGBFTValue(unsigned int x, unsigned int y, const RGBFTColour& col) = 0; 414 virtual void SetIndexedValue(unsigned int x, unsigned int y, unsigned char index); 415 416 // convenience functions for image evaluation 417 void GetRGBValue(unsigned int x, unsigned int y, RGBColour& colour, bool premul = false) const; 418 void GetRGBTValue(unsigned int x, unsigned int y, RGBTColour& colour, bool premul = false) const; 419 void GetRGBFTValue(unsigned int x, unsigned int y, RGBFTColour& colour, bool premul = false) const; 420 421 virtual void FillBitValue(bool bit) = 0; 422 virtual void FillGrayValue(float gray) = 0; 423 virtual void FillGrayValue(unsigned int gray) = 0; 424 virtual void FillGrayAValue(float gray, float alpha) = 0; 425 virtual void FillGrayAValue(unsigned int gray, unsigned int alpha) = 0; 426 virtual void FillRGBValue(float red, float green, float blue) = 0; 427 virtual void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue) = 0; 428 virtual void FillRGBAValue(float red, float green, float blue, float alpha) = 0; 429 virtual void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha) = 0; 430 virtual void FillRGBTValue(float red, float green, float blue, float transm) = 0; 431 virtual void FillRGBFTValue(float red, float green, float blue, float filter, float transm) = 0; 432 433 unsigned int GetColourMapSize() const; 434 435 void GetColourMap(vector<RGBMapEntry>& m) const; 436 void GetColourMap(vector<RGBAMapEntry>& m) const; 437 void GetColourMap(vector<RGBFTMapEntry>& m) const; 438 439 void SetColourMap(const vector<RGBMapEntry>& m); 440 void SetColourMap(const vector<RGBAMapEntry>& m); 441 void SetColourMap(const vector<RGBFTMapEntry>& m); 442 /* 443 void CopyTo(unsigned int x, unsigned int y, const Image& srcimage) 444 { 445 // TODO 446 } 447 void CopyToScaled(unsigned int x, unsigned int y, unsigned int w, unsigned int h, const Image& srcimage, bool smooth = false) 448 { 449 // TODO 450 }*/ 451 protected: 452 struct MapEntry 453 { 454 float red; 455 float green; 456 float blue; 457 float filter; // alpha = filter 458 float transm; 459 MapEntryMapEntry460 MapEntry() : red(0.0f), green(0.0f), blue(0.0f), filter(0.0f), transm(0.0f) { } MapEntryMapEntry461 MapEntry(float r, float g, float b, float f, float t) : red(r), green(g), blue(b), filter(f), transm(t) { } MapEntryMapEntry462 MapEntry(const RGBMapEntry& e) : red(e.red), green(e.green), blue(e.blue), filter(0.0f), transm(0.0f) { } MapEntryMapEntry463 MapEntry(const RGBAMapEntry& e) : red(e.red), green(e.green), blue(e.blue), filter(e.alpha), transm(0.0f) { } MapEntryMapEntry464 MapEntry(const RGBFTMapEntry& e) : red(e.red), green(e.green), blue(e.blue), filter(e.filter), transm(e.transm) { } 465 }; 466 467 vector<MapEntry> colormap; 468 ColourMapType colormaptype; 469 unsigned int width; 470 unsigned int height; 471 ImageDataType type; 472 bool premultiplied; 473 Image(unsigned int w,unsigned int h,ImageDataType t)474 Image(unsigned int w, unsigned int h, ImageDataType t) : 475 width(w), height(h), type(t), colormaptype(NoColourMap), premultiplied(false) { } 476 Image(unsigned int w,unsigned int h,ImageDataType t,const vector<RGBMapEntry> & m)477 Image(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBMapEntry>& m) : 478 width(w), height(h), type(t), colormaptype(RGBColourMap), premultiplied(false) { colormap.resize(max(m.size(), sizeof(unsigned char) * 256)); colormap.assign(m.begin(), m.end()); } 479 Image(unsigned int w,unsigned int h,ImageDataType t,const vector<RGBAMapEntry> & m)480 Image(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBAMapEntry>& m) : 481 width(w), height(h), type(t), colormaptype(RGBAColourMap), premultiplied(false) { colormap.resize(max(m.size(), sizeof(unsigned char) * 256)); colormap.assign(m.begin(), m.end()); } 482 Image(unsigned int w,unsigned int h,ImageDataType t,const vector<RGBFTMapEntry> & m)483 Image(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBFTMapEntry>& m) : 484 width(w), height(h), type(t), colormaptype(RGBFTColourMap), premultiplied(false) { colormap.resize(max(m.size(), sizeof(unsigned char) * 256)); colormap.assign(m.begin(), m.end()); } 485 RGB2Gray(float red,float green,float blue)486 float RGB2Gray(float red, float green, float blue) const 487 { 488 return RGBColour(red, green, blue).Greyscale(); 489 } 490 private: 491 /// not available 492 Image(); 493 /// not available 494 Image(const Image&); 495 /// not available 496 Image& operator=(Image&); 497 }; 498 499 /// @} 500 /// 501 //############################################################################## 502 503 } 504 505 #endif // POVRAY_BASE_IMAGE_H 506