povray/source/base/image/image.cpp
2013-11-06 13:07:19 -05:00

4098 lines
126 KiB
C++

/*******************************************************************************
* image.cpp
*
* ---------------------------------------------------------------------------
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
*
* POV-Ray is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* POV-Ray is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------------
* POV-Ray is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
* ---------------------------------------------------------------------------
* $File: //depot/public/povray/3.x/source/base/image/image.cpp $
* $Revision: #1 $
* $Change: 6069 $
* $DateTime: 2013/11/06 11:59:40 $
* $Author: chrisc $
*******************************************************************************/
#include <vector>
#include <algorithm>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// configbase.h must always be the first POV file included within base *.cpp files
#include "base/configbase.h"
#include "base/image/image.h"
#include "base/image/targa.h"
#include "base/image/gif.h"
#include "base/image/png_pov.h"
#include "base/image/jpeg_pov.h"
#include "base/image/iff.h"
#include "base/image/pgm.h"
#include "base/image/ppm.h"
#include "base/image/tiff_pov.h"
#include "base/image/bmp.h"
#include "base/image/openexr.h"
#include "base/image/hdr.h"
#include "base/platformbase.h"
#include "base/safemath.h"
#include "base/povmsgid.h"
#ifdef USE_SYSPROTO
#include "syspovprotobase.h"
#endif
#define CHECK_BOUNDS(x,y) assert(((x) < width) && ((y) < height))
#define ALPHA_OPAQUE (1.0f)
#define ALPHA_OPAQUE_INT(MAX) (MAX)
#define FT_OPAQUE (0.0f)
#define FT_OPAQUE_INT(MAX) (0)
#define IS_NONZERO_RGB(r,g,b) ((r)*(g)*(b) != 0.0f) // TODO FIXME - [CLi] this tests whether *all* channels are nonzero - is this desired?
#define IS_NONZERO_RGB_INT(r,g,b) ((r)*(g)*(b) != 0) // TODO FIXME - [CLi] this tests whether *all* channels are nonzero - is this desired?
// this must be the last file included
#include "base/povdebug.h"
namespace pov_base
{
template<class Allocator = allocator<bool> >
class BitMapImage : public Image
{
public:
BitMapImage(unsigned int w, unsigned int h) :
Image(w, h, Bit_Map) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
BitMapImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, Bit_Map, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
BitMapImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, Bit_Map, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
BitMapImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, Bit_Map, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
~BitMapImage() { }
bool IsOpaque() const
{
return true;
}
bool IsGrayscale() const
{
return true;
}
bool IsColour() const
{
return false;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return false;
}
bool HasAlphaChannel() const
{
return false;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return 1;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return pixels[x + y * size_t(width)];
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
if(pixels[x + y * size_t(width)] == true)
return 1.0f;
else
return 0.0f;
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
gray = GetGrayValue(x, y);
alpha = ALPHA_OPAQUE;
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
red = green = blue = GetGrayValue(x, y);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
red = green = blue = GetGrayValue(x, y);
alpha = ALPHA_OPAQUE;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
red = green = blue = GetGrayValue(x, y);
filter = transm = FT_OPAQUE;
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = bit;
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = (gray != 0.0f);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = (gray != 0);
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = (gray != 0.0f);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = (gray != 0);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = IS_NONZERO_RGB(red, green, blue);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = IS_NONZERO_RGB_INT(red, green, blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
SetRGBValue(x, y, red, green, blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float, float)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
SetRGBValue(x, y, col.red(), col.green(), col.blue());
}
void FillBitValue(bool bit)
{
fill(pixels.begin(), pixels.end(), bit);
}
void FillGrayValue(float gray)
{
FillBitValue(gray != 0.0f);
}
void FillGrayValue(unsigned int gray)
{
FillBitValue(gray != 0);
}
void FillGrayAValue(float gray, float)
{
FillBitValue(gray != 0.0f);
}
void FillGrayAValue(unsigned int gray, unsigned int)
{
FillBitValue(gray != 0);
}
void FillRGBValue(float red, float green, float blue)
{
FillBitValue(IS_NONZERO_RGB(red, green, blue));
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillBitValue(IS_NONZERO_RGB_INT(red, green, blue));
}
void FillRGBAValue(float red, float green, float blue, float)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
FillRGBValue(red, green, blue);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
FillRGBValue(red, green, blue);
}
void FillRGBFTValue(float red, float green, float blue, float, float)
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
FillRGBValue(red, green, blue);
}
private:
vector<bool, Allocator> pixels;
};
typedef BitMapImage<> MemoryBitMapImage;
template<class Allocator = allocator<unsigned char> >
class ColourMapImage : public Image
{
public:
ColourMapImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, Colour_Map, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
ColourMapImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, Colour_Map, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
ColourMapImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, Colour_Map, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
~ColourMapImage() { }
bool IsOpaque() const
{
if((colormaptype != RGBAColourMap) && (colormaptype != RGBFTColourMap))
return true;
bool transp = false;
for(size_t i = 0; i < colormap.size(); i++)
{
if(colormaptype == RGBAColourMap)
transp = (colormap[i].filter < 1.0); // with RGBAColourMap, .filter is actually alpha
else if(colormaptype == RGBFTColourMap)
transp = (colormap[i].transm > 0.0);
if(transp == true)
break;
}
if(transp == false)
return true;
if(colormaptype == RGBAColourMap)
{
for(typename vector<unsigned char, Allocator>::const_iterator i(pixels.begin()); i != pixels.end(); i++)
{
if(colormap[*i].filter < 1.0) // with RGBAColourMap, .filter is actually alpha
return false;
}
}
else
{
for(typename vector<unsigned char, Allocator>::const_iterator i(pixels.begin()); i != pixels.end(); i++)
{
if(colormap[*i].transm > 0.0)
return false;
}
}
return true;
}
bool IsGrayscale() const
{
return false;
}
bool IsColour() const
{
return true;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return true;
}
bool IsGammaEncoded() const
{
return false;
}
bool HasAlphaChannel() const
{
return (colormaptype == RGBAColourMap);
}
bool HasFilterTransmit() const
{
return (colormaptype == RGBFTColourMap);
}
unsigned int GetMaxIntValue() const
{
return 255;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
float red, green, blue, filter, transm, alpha;
switch(colormaptype)
{
case RGBColourMap:
GetRGBValue(x, y, red, green, blue);
return IS_NONZERO_RGB(red, green, blue);
case RGBAColourMap:
// TODO FIXME - [CLi] This takes into account opacity information; other bit-based code doesn't.
GetRGBAValue(x, y, red, green, blue, alpha);
return IS_NONZERO_RGB(red, green, blue) && (alpha == ALPHA_OPAQUE);
case RGBFTColourMap:
// TODO FIXME - [CLi] This takes into account opacity information; other bit-based code doesn't.
GetRGBFTValue(x, y, red, green, blue, filter, transm);
return IS_NONZERO_RGB(red, green, blue) && (filter == FT_OPAQUE) && (transm == FT_OPAQUE);
default:
return false;
}
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue, filter, transm, alpha;
switch(colormaptype)
{
case RGBColourMap:
GetRGBValue(x, y, red, green, blue);
return RGB2Gray(red, green, blue);
case RGBAColourMap:
GetRGBAValue(x, y, red, green, blue, alpha);
return RGB2Gray(red, green, blue);
case RGBFTColourMap:
GetRGBFTValue(x, y, red, green, blue, filter, transm);
return RGB2Gray(red, green, blue);
default:
return 0.0f;
}
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
float red, green, blue, filter, transm;
alpha = ALPHA_OPAQUE; // (unless noted otherwise)
switch(colormaptype)
{
case RGBColourMap:
GetRGBValue(x, y, red, green, blue);
gray = RGB2Gray(red, green, blue);
return;
case RGBAColourMap:
GetRGBAValue(x, y, red, green, blue, alpha);
gray = RGB2Gray(red, green, blue);
return;
case RGBFTColourMap:
GetRGBFTValue(x, y, red, green, blue, filter, transm);
gray = RGB2Gray(red, green, blue);
alpha = Colour::FTtoA(filter, transm);
return;
default:
gray = 0.0f;
return;
}
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
CHECK_BOUNDS(x, y);
MapEntry e(colormap[pixels[x + y * size_t(width)]]);
red = e.red;
green = e.green;
blue = e.blue;
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
CHECK_BOUNDS(x, y);
MapEntry e(colormap[pixels[x + y * size_t(width)]]);
red = e.red;
green = e.green;
blue = e.blue;
alpha = ALPHA_OPAQUE; // (unless noted otherwise)
switch(colormaptype)
{
case RGBAColourMap:
alpha = e.filter; // with RGBAColourMap, .filter is actually alpha
return;
case RGBFTColourMap:
alpha = Colour::FTtoA(e.filter, e.transm);
return;
default:
return;
}
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
CHECK_BOUNDS(x, y);
MapEntry e(colormap[pixels[x + y * size_t(width)]]);
red = e.red;
green = e.green;
blue = e.blue;
filter = FT_OPAQUE; // (unless noted otherwise)
transm = FT_OPAQUE; // (unless noted otherwise)
switch(colormaptype)
{
case RGBAColourMap:
Colour::AtoFT(e.filter, filter, transm); // with RGBAColourMap, .filter is actually alpha
return;
case RGBFTColourMap:
filter = e.filter;
transm = e.transm;
return;
default:
return;
}
}
unsigned char GetIndexedValue(unsigned int x, unsigned int y)
{
CHECK_BOUNDS(x, y);
return pixels[x + y * size_t(width)];
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = Bit2Map(bit);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = Gray2Map(gray);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = Gray2Map(float(gray) / 255.0);
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = GrayA2Map(gray, alpha);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = GrayA2Map(float(gray) / 255.0, float(alpha) / 255.0);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = RGB2Map(red, green, blue);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = RGB2Map(float(red) / 255.0, float(green) / 255.0, float(blue) / 255.0);
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = RGBA2Map(red, green, blue, alpha);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = RGBA2Map(float(red) / 255.0, float(green) / 255.0, float(blue) / 255.0, float(alpha) / 255.0);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
CHECK_BOUNDS(x, y);
// [CLi 2009-09] this was dividing by 255 - which I presume to have been a bug.
pixels[x + y * size_t(width)] = RGBFT2Map(red, green, blue, filter, transm);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
CHECK_BOUNDS(x, y);
// [CLi 2009-09] this was dividing by 255 - which I presume to have been a bug.
pixels[x + y * size_t(width)] = RGBFT2Map(col.red(), col.green(), col.blue(), col.filter(), col.transm());
}
void SetIndexedValue(unsigned int x, unsigned int y, unsigned char index)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = index;
}
void FillBitValue(bool bit)
{
fill(pixels.begin(), pixels.end(), Bit2Map(bit));
}
void FillGrayValue(float gray)
{
fill(pixels.begin(), pixels.end(), Gray2Map(gray));
}
void FillGrayValue(unsigned int gray)
{
fill(pixels.begin(), pixels.end(), Gray2Map(float(gray) / 255.0));
}
void FillGrayAValue(float gray, float alpha)
{
fill(pixels.begin(), pixels.end(), GrayA2Map(gray, alpha));
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
fill(pixels.begin(), pixels.end(), GrayA2Map(float(gray) / 255.0, float(alpha) / 255.0));
}
void FillRGBValue(float red, float green, float blue)
{
fill(pixels.begin(), pixels.end(), RGB2Map(red, green, blue));
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
fill(pixels.begin(), pixels.end(), RGB2Map(float(red) / 255.0, float(green) / 255.0, float(blue) / 255.0));
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
fill(pixels.begin(), pixels.end(), RGBA2Map(red, green, blue, alpha));
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
fill(pixels.begin(), pixels.end(), RGBA2Map(float(red) / 255.0, float(green) / 255.0, float(blue) / 255.0, float(alpha) / 255.0));
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
// [CLi 2009-09] this was dividing by 255 - which I presume to have been a bug.
fill(pixels.begin(), pixels.end(), RGBFT2Map(red, green, blue, filter, transm));
}
void FillIndexedValue(unsigned char index)
{
fill(pixels.begin(), pixels.end(), index);
}
private:
vector<unsigned char, Allocator> pixels;
unsigned char Bit2Map(bool bit) const
{
if(bit == true)
return Gray2Map(1.0f);
else
return Gray2Map(0.0f);
}
unsigned char Gray2Map(float gray) const
{
switch(colormaptype)
{
case RGBColourMap:
return FindBestRGB(gray, gray, gray);
case RGBAColourMap:
return FindBestRGBA(gray, gray, gray, ALPHA_OPAQUE);
case RGBFTColourMap:
return FindBestRGBFT(gray, gray, gray, FT_OPAQUE, FT_OPAQUE);
default:
return 0;
}
}
unsigned char GrayA2Map(float gray, float alpha) const
{
float filter, transm;
switch(colormaptype)
{
case RGBColourMap:
return FindBestRGB(gray, gray, gray);
case RGBAColourMap:
return FindBestRGBA(gray, gray, gray, alpha);
case RGBFTColourMap:
Colour::AtoFT(alpha, filter, transm);
return FindBestRGBFT(gray, gray, gray, filter, transm);
default:
return 0;
}
}
unsigned char RGB2Map(float red, float green, float blue) const
{
switch(colormaptype)
{
case RGBColourMap:
return FindBestRGB(red, green, blue);
case RGBAColourMap:
return FindBestRGBA(red, green, blue, ALPHA_OPAQUE);
case RGBFTColourMap:
return FindBestRGBFT(red, green, blue, FT_OPAQUE, FT_OPAQUE);
default:
return 0;
}
}
unsigned char RGBA2Map(float red, float green, float blue, float alpha) const
{
float filter, transm;
switch(colormaptype)
{
case RGBColourMap:
return FindBestRGB(red, green, blue);
case RGBAColourMap:
return FindBestRGBA(red, green, blue, alpha);
case RGBFTColourMap:
Colour::AtoFT(alpha, filter, transm);
return FindBestRGBFT(red, green, blue, filter, transm);
default:
return 0;
}
}
unsigned char RGBFT2Map(float red, float green, float blue, float filter, float transm) const
{
switch(colormaptype)
{
case RGBColourMap:
return FindBestRGB(red, green, blue);
case RGBAColourMap:
return FindBestRGBA(red, green, blue, Colour::FTtoA(filter, transm));
case RGBFTColourMap:
return FindBestRGBFT(red, green, blue, filter, transm);
default:
return 0;
}
}
unsigned char FindBestRGB(float red, float green, float blue) const
{
unsigned char best = 0;
float diff = 3.0f;
for(size_t i = 0; i < colormap.size(); i++)
{
float d(RGB2Gray(fabs(colormap[i].red - red), fabs(colormap[i].green - green), fabs(colormap[i].red - blue)));
if(d < diff)
{
d = diff;
best = (unsigned char)i;
}
}
return best;
}
unsigned char FindBestRGBA(float red, float green, float blue, float alpha) const
{
unsigned char best = 0;
float diff = 3.0f;
for(size_t i = 0; i < colormap.size(); i++)
{
float d((RGB2Gray(fabs(colormap[i].red - red), fabs(colormap[i].green - green), fabs(colormap[i].red - blue)) * 3.0f +
fabs(colormap[i].filter - alpha)) / 4.0f); // with RGBAColourMap, .filter is actually alpha
if(d < diff)
{
d = diff;
best = (unsigned char)i;
}
}
return best;
}
unsigned char FindBestRGBFT(float red, float green, float blue, float filter, float transm) const
{
unsigned char best = 0;
float diff = 3.0f;
for(size_t i = 0; i < colormap.size(); i++)
{
float d((RGB2Gray(fabs(colormap[i].red - red), fabs(colormap[i].green - green), fabs(colormap[i].red - blue)) * 3.0f +
fabs(colormap[i].filter - filter) + fabs(colormap[i].transm - transm)) / 5.0f);
if(d < diff)
{
d = diff;
best = (unsigned char)i;
}
}
return best;
}
};
typedef ColourMapImage<> MemoryColourMapImage;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class GrayImage : public Image
{
public:
GrayImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
GrayImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
GrayImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
GrayImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
~GrayImage() { }
bool IsOpaque() const
{
return true;
}
bool IsGrayscale() const
{
return true;
}
bool IsColour() const
{
return false;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsGammaEncoded() const
{
return false;
}
bool IsIndexed() const
{
return false;
}
bool HasAlphaChannel() const
{
return false;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return (pixels[x + y * size_t(width)] != 0);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return float(pixels[x + y * size_t(width)]) / float(TMAX);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
CHECK_BOUNDS(x, y);
gray = float(pixels[x + y * size_t(width)]) / float(TMAX);
alpha = ALPHA_OPAQUE;
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
red = green = blue = GetGrayValue(x, y);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
red = green = blue = GetGrayValue(x, y);
alpha = ALPHA_OPAQUE;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
red = green = blue = GetGrayValue(x, y);
filter = transm = FT_OPAQUE;
}
unsigned char GetIndexedValue(unsigned int x, unsigned int y)
{
CHECK_BOUNDS(x, y);
return (unsigned char)(int(pixels[x + y * size_t(width)]) / ((TMAX + 1) >> 8));
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, TMAX);
else
SetGrayValue(x, y, (unsigned int)0);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = T(gray * float(TMAX));
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = T(gray);
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = T(gray * float(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = T(gray);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetGrayValue(x, y, RGB2Gray(red, green, blue));
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetGrayValue(x, y, RGB2Gray(float(red) / float(TMAX), float(green) / float(TMAX), float(blue) / float(TMAX)));
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
SetRGBValue(x, y, col.red(), col.green(), col.blue());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillGrayValue((unsigned int)(gray * float(TMAX)));
}
void FillGrayValue(unsigned int gray)
{
fill(pixels.begin(), pixels.end(), T(gray));
}
void FillGrayAValue(float gray, float)
{
FillGrayValue(gray);
}
void FillGrayAValue(unsigned int gray, unsigned int)
{
FillGrayValue(gray);
}
void FillRGBValue(float red, float green, float blue)
{
FillGrayValue(RGB2Gray(red, green, blue));
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillGrayValue(RGB2Gray(float(red) / float(TMAX), float(green) / float(TMAX), float(blue) / float(TMAX)));
}
void FillRGBAValue(float red, float green, float blue, float)
{
FillRGBValue(red, green, blue);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
FillRGBValue(red, green, blue);
}
void FillRGBFTValue(float red, float green, float blue, float, float)
{
FillRGBValue(red, green, blue);
}
private:
vector<T, Allocator> pixels;
};
typedef GrayImage<unsigned char, 255, Image::Gray_Int8> MemoryGray8Image;
typedef GrayImage<unsigned short, 65535, Image::Gray_Int16> MemoryGray16Image;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class GrayAImage : public Image
{
public:
GrayAImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
GrayAImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
GrayAImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
GrayAImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
~GrayAImage() { }
bool IsOpaque() const
{
for(typename vector<T, Allocator>::const_iterator i(pixels.begin()); i != pixels.end(); i += 2)
{
if(i[1] < TMAX)
return false;
}
return true;
}
bool IsGrayscale() const
{
return true;
}
bool IsColour() const
{
return false;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return false;
}
bool HasAlphaChannel() const
{
return true;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
CHECK_BOUNDS(x, y);
return (pixels[(x + y * size_t(width)) * 2] != 0);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return float(pixels[(x + y * size_t(width)) * 2]) / float(TMAX);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
CHECK_BOUNDS(x, y);
gray = float(pixels[(x + y * size_t(width)) * 2]) / float(TMAX);
alpha = float(pixels[(x + y * size_t(width)) * 2 + 1]) / float(TMAX);
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
red = green = blue = GetGrayValue(x, y);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
GetGrayAValue(x, y, red, alpha);
green = blue = red;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
float alpha;
GetGrayAValue(x, y, red, alpha);
green = blue = red;
Colour::AtoFT(alpha, filter, transm);
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayAValue(x, y, TMAX, ALPHA_OPAQUE_INT(TMAX));
else
SetGrayAValue(x, y, (unsigned int)0, ALPHA_OPAQUE_INT(TMAX));
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetGrayAValue(x, y, gray, ALPHA_OPAQUE);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
SetGrayAValue(x, y, gray, ALPHA_OPAQUE_INT(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 2] = T(gray * float(TMAX));
pixels[(x + y * size_t(width)) * 2 + 1] = T(alpha * float(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 2] = gray;
pixels[(x + y * size_t(width)) * 2 + 1] = alpha;
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetGrayValue(x, y, RGB2Gray(red, green, blue));
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetGrayValue(x, y, RGB2Gray(float(red) / float(TMAX), float(green) / float(TMAX), float(blue) / float(TMAX)));
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
SetGrayAValue(x, y, RGB2Gray(red, green, blue), alpha);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
// TODO FIXME - this unnecessarily converts alpha from int to float, requiring it to be converted back to int
SetGrayAValue(x, y, RGB2Gray(float(red) / float(TMAX), float(green) / float(TMAX), float(blue) / float(TMAX)), float(alpha) / float(TMAX));
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
SetGrayAValue(x, y, RGB2Gray(red, green, blue), Colour::FTtoA(filter, transm));
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
SetGrayAValue(x, y, RGB2Gray(col.red(), col.green(), col.blue()), col.FTtoA());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillGrayValue((unsigned int)(gray * float(TMAX)));
}
void FillGrayValue(unsigned int gray)
{
FillGrayAValue(gray, ALPHA_OPAQUE_INT(TMAX));
}
void FillGrayAValue(float gray, float alpha)
{
// [CLi 2009-09] this was dividing by float(TMAX) - which I presume to have been a bug.
T g(gray * float(TMAX)), a(alpha * float(TMAX));
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = g;
i++;
*i = a;
}
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = T(gray);
i++;
*i = T(alpha);
}
}
void FillRGBValue(float red, float green, float blue)
{
FillGrayValue(RGB2Gray(red, green, blue));
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillGrayValue(RGB2Gray(float(red) / float(TMAX), float(green) / float(TMAX), float(blue) / float(TMAX)));
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
FillGrayAValue(RGB2Gray(red, green, blue), alpha);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
// TODO FIXME - this unnecessarily converts alpha from int to float, requiring it to be converted back to int
FillGrayAValue(RGB2Gray(float(red) / float(TMAX), float(green) / float(TMAX), float(blue) / float(TMAX)), float(alpha) / float(TMAX));
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
FillGrayAValue(RGB2Gray(red, green, blue), Colour::FTtoA(filter, transm));
}
private:
vector<T, Allocator> pixels;
};
typedef GrayAImage<unsigned char, 255, Image::GrayA_Int8> MemoryGrayA8Image;
typedef GrayAImage<unsigned short, 65535, Image::GrayA_Int16> MemoryGrayA16Image;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class RGBImage : public Image
{
public:
RGBImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
RGBImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
RGBImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
RGBImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
~RGBImage() { }
bool IsOpaque() const
{
return true;
}
bool IsGrayscale() const
{
return false;
}
bool IsColour() const
{
return true;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return false;
}
bool HasAlphaChannel() const
{
return false;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
float red, green, blue;
GetRGBValue(x, y, red, green, blue);
return IS_NONZERO_RGB(red, green, blue);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue;
GetRGBValue(x, y, red, green, blue);
return RGB2Gray(red, green, blue);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
gray = GetGrayValue(x, y);
alpha = ALPHA_OPAQUE;
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
CHECK_BOUNDS(x, y);
red = float(pixels[(x + y * size_t(width)) * 3]) / float(TMAX);
green = float(pixels[(x + y * size_t(width)) * 3 + 1]) / float(TMAX);
blue = float(pixels[(x + y * size_t(width)) * 3 + 2]) / float(TMAX);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
GetRGBValue(x, y, red, green, blue);
alpha = ALPHA_OPAQUE;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
GetRGBValue(x, y, red, green, blue);
filter = transm = FT_OPAQUE;
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, TMAX);
else
SetGrayValue(x, y, (unsigned int)0);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetRGBValue(x, y, gray, gray, gray);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width) * 3] =
pixels[x + y * size_t(width) * 3 + 1] =
pixels[x + y * size_t(width) * 3 + 2] = gray;
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width) * 3] =
pixels[x + y * size_t(width) * 3 + 1] =
pixels[x + y * size_t(width) * 3 + 2] = T(gray * float(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width) * 3] =
pixels[x + y * size_t(width) * 3 + 1] =
pixels[x + y * size_t(width) * 3 + 2] = gray;
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 3] = T(red * float(TMAX));
pixels[(x + y * size_t(width)) * 3 + 1] = T(green * float(TMAX));
pixels[(x + y * size_t(width)) * 3 + 2] = T(blue * float(TMAX));
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 3] = T(red);
pixels[(x + y * size_t(width)) * 3 + 1] = T(green);
pixels[(x + y * size_t(width)) * 3 + 2] = T(blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
SetRGBValue(x, y, col.red(), col.green(), col.blue());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillGrayValue((unsigned int)(gray * float(TMAX)));
}
void FillGrayValue(unsigned int gray)
{
fill(pixels.begin(), pixels.end(), gray);
}
void FillGrayAValue(float gray, float)
{
FillRGBValue(gray, gray, gray);
}
void FillGrayAValue(unsigned int gray, unsigned int)
{
FillRGBValue(gray, gray, gray);
}
void FillRGBValue(float red, float green, float blue)
{
// [CLi 2009-09] this was dividing by float(TMAX) - which I presume to have been a bug.
T r(red * float(TMAX)), g(green * float(TMAX)), b(blue * float(TMAX));
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = r;
i++;
*i = g;
i++;
*i = b;
}
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = T(red);
i++;
*i = T(green);
i++;
*i = T(blue);
}
}
void FillRGBAValue(float red, float green, float blue, float)
{
FillRGBValue(red, green, blue);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
FillRGBValue(red, green, blue);
}
void FillRGBFTValue(float red, float green, float blue, float, float)
{
FillRGBValue(red, green, blue);
}
private:
vector<T, Allocator> pixels;
};
typedef RGBImage<unsigned char, 255, Image::RGB_Int8> MemoryRGB8Image;
typedef RGBImage<unsigned short, 65535, Image::RGB_Int16> MemoryRGB16Image;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class RGBAImage : public Image
{
public:
RGBAImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
RGBAImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
RGBAImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
RGBAImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
~RGBAImage() { }
bool IsOpaque() const
{
for(typename vector<T, Allocator>::const_iterator i(pixels.begin()); i != pixels.end(); i += 4)
{
if(i[3] < TMAX)
return false;
}
return true;
}
bool IsGrayscale() const
{
return false;
}
bool IsColour() const
{
return true;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return false;
}
bool HasAlphaChannel() const
{
return true;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
float red, green, blue, alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
return IS_NONZERO_RGB(red, green, blue);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue, alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
return RGB2Gray(red, green, blue);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
float red, green, blue;
GetRGBAValue(x, y, red, green, blue, alpha);
gray = RGB2Gray(red, green, blue);
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
float alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
CHECK_BOUNDS(x, y);
red = float(pixels[(x + y * size_t(width)) * 4]) / float(TMAX);
green = float(pixels[(x + y * size_t(width)) * 4 + 1]) / float(TMAX);
blue = float(pixels[(x + y * size_t(width)) * 4 + 2]) / float(TMAX);
alpha = float(pixels[(x + y * size_t(width)) * 4 + 3]) / float(TMAX);
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
float alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
Colour::AtoFT(alpha, filter, transm);
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, TMAX);
else
SetGrayValue(x, y, (unsigned int)0);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetRGBAValue(x, y, gray, gray, gray, ALPHA_OPAQUE);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
SetRGBAValue(x, y, gray, gray, gray, ALPHA_OPAQUE_INT(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
SetRGBAValue(x, y, gray, gray, gray, alpha);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
SetRGBAValue(x, y, gray, gray, gray, alpha);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetRGBAValue(x, y, red, green, blue, ALPHA_OPAQUE);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetRGBAValue(x, y, red, green, blue, ALPHA_OPAQUE_INT(TMAX));
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 4] = T(red * float(TMAX));
pixels[(x + y * size_t(width)) * 4 + 1] = T(green * float(TMAX));
pixels[(x + y * size_t(width)) * 4 + 2] = T(blue * float(TMAX));
pixels[(x + y * size_t(width)) * 4 + 3] = T(alpha * float(TMAX));
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 4] = T(red);
pixels[(x + y * size_t(width)) * 4 + 1] = T(green);
pixels[(x + y * size_t(width)) * 4 + 2] = T(blue);
pixels[(x + y * size_t(width)) * 4 + 3] = T(alpha);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
SetRGBAValue(x, y, red, green, blue, Colour::FTtoA(filter, transm));
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 4] = T(col.red());
pixels[(x + y * size_t(width)) * 4 + 1] = T(col.green());
pixels[(x + y * size_t(width)) * 4 + 2] = T(col.blue());
pixels[(x + y * size_t(width)) * 4 + 3] = T(col.FTtoA());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillRGBAValue(gray, gray, gray, ALPHA_OPAQUE);
}
void FillGrayValue(unsigned int gray)
{
FillRGBAValue(gray, gray, gray, ALPHA_OPAQUE_INT(TMAX));
}
void FillGrayAValue(float gray, float alpha)
{
FillRGBAValue(gray, gray, gray, alpha);
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
FillRGBAValue(gray, gray, gray, alpha);
}
void FillRGBValue(float red, float green, float blue)
{
FillRGBAValue(red, green, blue, ALPHA_OPAQUE);
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillRGBAValue(red, green, blue, ALPHA_OPAQUE_INT(TMAX));
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
// [CLi 2009-09] this was dividing by float(TMAX) - which I presume to have been a bug.
T r(red * float(TMAX)), g(green * float(TMAX)), b(blue * float(TMAX)), a(alpha * float(TMAX));
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = r;
i++;
*i = g;
i++;
*i = b;
i++;
*i = a;
}
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = T(red);
i++;
*i = T(green);
i++;
*i = T(blue);
i++;
*i = T(alpha);
}
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
FillRGBAValue(red, green, blue, Colour::FTtoA(filter, transm));
}
private:
vector<T, Allocator> pixels;
};
typedef RGBAImage<unsigned char, 255, Image::RGBA_Int8> MemoryRGBA8Image;
typedef RGBAImage<unsigned short, 65535, Image::RGBA_Int16> MemoryRGBA16Image;
template<class PixelContainer = vector<float, allocator<float> > >
class RGBFTImage : public Image
{
public:
RGBFTImage(unsigned int w, unsigned int h) :
Image(w, h, RGBFT_Float) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 5u)); FillBitValue(false); }
RGBFTImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, RGBFT_Float, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 5u)); FillBitValue(false); }
RGBFTImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, RGBFT_Float, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 5u)); FillBitValue(false); }
RGBFTImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, RGBFT_Float, m) { pixels.resize(SafeUnsignedProduct<size_t>(w, h, 5u)); FillBitValue(false); }
~RGBFTImage() { }
bool IsOpaque() const
{
for(typename PixelContainer::const_iterator i(pixels.begin()); i != pixels.end(); i += 5)
{
if(i[4] > 0.0f) // TODO FIXME - this ignores filter
return false;
}
return true;
}
bool IsGrayscale() const
{
return false;
}
bool IsColour() const
{
return true;
}
bool IsFloat() const
{
return true;
}
bool IsInt() const
{
return false;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return false;
}
bool HasAlphaChannel() const
{
return false;
}
bool HasFilterTransmit() const
{
return true;
}
unsigned int GetMaxIntValue() const
{
return 255;
}
bool TryDeferDecoding(GammaCurvePtr&, unsigned int)
{
return false;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
float red, green, blue, filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
return IS_NONZERO_RGB(red, green, blue);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue, filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
return RGB2Gray(red, green, blue);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
float red, green, blue, filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
gray = RGB2Gray(red, green, blue);
alpha = Colour::FTtoA(filter, transm);
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
float filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
float filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
alpha = Colour::FTtoA(filter, transm);
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
CHECK_BOUNDS(x, y);
red = pixels[(x + y * size_t(width)) * 5];
green = pixels[(x + y * size_t(width)) * 5 + 1];
blue = pixels[(x + y * size_t(width)) * 5 + 2];
filter = pixels[(x + y * size_t(width)) * 5 + 3];
transm = pixels[(x + y * size_t(width)) * 5 + 4];
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, 1.0f);
else
SetGrayValue(x, y, 0.0f);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetRGBFTValue(x, y, gray, gray, gray, FT_OPAQUE, FT_OPAQUE);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
SetGrayValue(x, y, float(gray) / 255.0f);
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
SetRGBFTValue(x, y, gray, gray, gray, filter, transm);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
float c = float(gray) / 255.0f;
float filter, transm;
Colour::AtoFT(float(alpha) / 255.0f, filter, transm);
SetRGBFTValue(x, y, c, c, c, filter, transm);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetRGBFTValue(x, y, red, green, blue, FT_OPAQUE, FT_OPAQUE);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetRGBFTValue(x, y, float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, FT_OPAQUE, FT_OPAQUE);
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
SetRGBFTValue(x, y, red, green, blue, filter, transm);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
float filter, transm;
Colour::AtoFT(float(alpha) / 255.0f, filter, transm);
SetRGBFTValue(x, y, float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, filter, transm);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 5] = red;
pixels[(x + y * size_t(width)) * 5 + 1] = green;
pixels[(x + y * size_t(width)) * 5 + 2] = blue;
pixels[(x + y * size_t(width)) * 5 + 3] = filter;
pixels[(x + y * size_t(width)) * 5 + 4] = transm;
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 5] = col.red();
pixels[(x + y * size_t(width)) * 5 + 1] = col.green();
pixels[(x + y * size_t(width)) * 5 + 2] = col.blue();
pixels[(x + y * size_t(width)) * 5 + 3] = col.filter();
pixels[(x + y * size_t(width)) * 5 + 4] = col.transm();
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(1.0f);
else
FillGrayValue(0.0f);
}
void FillGrayValue(float gray)
{
FillRGBFTValue(gray, gray, gray, FT_OPAQUE, FT_OPAQUE);
}
void FillGrayValue(unsigned int gray)
{
FillGrayValue(float(gray) / 255.0f);
}
void FillGrayAValue(float gray, float alpha)
{
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
FillRGBFTValue(gray, gray, gray, filter, transm);
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
FillGrayAValue(float(gray) / 255.0f, float(alpha) / 255.0f);
}
void FillRGBValue(float red, float green, float blue)
{
FillRGBFTValue(red, green, blue, FT_OPAQUE, FT_OPAQUE);
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillRGBFTValue(float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, FT_OPAQUE, FT_OPAQUE);
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
FillRGBFTValue(red, green, blue, filter, transm);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
float filter, transm;
Colour::AtoFT(float(alpha) / 255.0f, filter, transm);
FillRGBFTValue(float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, filter, transm);
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
for(typename PixelContainer::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = red;
i++;
*i = green;
i++;
*i = blue;
i++;
*i = filter;
i++;
*i = transm;
}
}
private:
PixelContainer pixels;
};
typedef RGBFTImage<> MemoryRGBFTImage;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class NonlinearGrayImage : public Image
{
public:
NonlinearGrayImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
NonlinearGrayImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
NonlinearGrayImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
NonlinearGrayImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h)); FillBitValue(false); }
~NonlinearGrayImage() { }
bool IsOpaque() const
{
return true;
}
bool IsGrayscale() const
{
return true;
}
bool IsColour() const
{
return false;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return true;
}
bool HasAlphaChannel() const
{
return false;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr& g, unsigned int max)
{
if (max != TMAX) return false;
if (!GammaCurve::IsNeutral(gamma)) return !g;
gamma.swap(g);
gammaLUT = gamma->GetLookupTable(TMAX);
return true;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return (pixels[x + y * size_t(width)] != 0);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return gammaLUT[pixels[x + y * size_t(width)]];
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
CHECK_BOUNDS(x, y);
gray = gammaLUT[pixels[x + y * size_t(width)]];
alpha = ALPHA_OPAQUE;
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
red = green = blue = GetGrayValue(x, y);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
red = green = blue = GetGrayValue(x, y);
alpha = ALPHA_OPAQUE;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
red = green = blue = GetGrayValue(x, y);
filter = transm = FT_OPAQUE;
}
unsigned char GetIndexedValue(unsigned int x, unsigned int y)
{
CHECK_BOUNDS(x, y);
return (unsigned char)(int(pixels[x + y * size_t(width)]) / ((TMAX + 1) >> 8));
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, TMAX);
else
SetGrayValue(x, y, (unsigned int)0);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = IntEncode(gamma, gray, TMAX);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = T(gray);
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = IntEncode(gamma, gray, TMAX);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width)] = T(gray);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetGrayValue(x, y, RGB2Gray(red, green, blue));
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetGrayValue(x, y, RGB2Gray(gammaLUT[red], gammaLUT[green], gammaLUT[blue]));
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
SetRGBValue(x, y, col.red(), col.green(), col.blue());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillGrayValue(IntEncode(gamma, gray, TMAX));
}
void FillGrayValue(unsigned int gray)
{
fill(pixels.begin(), pixels.end(), T(gray));
}
void FillGrayAValue(float gray, float)
{
FillGrayValue(gray);
}
void FillGrayAValue(unsigned int gray, unsigned int)
{
FillGrayValue(gray);
}
void FillRGBValue(float red, float green, float blue)
{
FillGrayValue(RGB2Gray(red, green, blue));
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillGrayValue(RGB2Gray(gammaLUT[red], gammaLUT[green], gammaLUT[blue]));
}
void FillRGBAValue(float red, float green, float blue, float)
{
FillRGBValue(red, green, blue);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
FillRGBValue(red, green, blue);
}
void FillRGBFTValue(float red, float green, float blue, float, float)
{
FillRGBValue(red, green, blue);
}
private:
vector<T, Allocator> pixels;
GammaCurvePtr gamma;
const float* gammaLUT;
};
typedef NonlinearGrayImage<unsigned char, 255, Image::Gray_Gamma8> MemoryNonlinearGray8Image;
typedef NonlinearGrayImage<unsigned short, 65535, Image::Gray_Gamma16> MemoryNonlinearGray16Image;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class NonlinearGrayAImage : public Image
{
public:
NonlinearGrayAImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
NonlinearGrayAImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
NonlinearGrayAImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
NonlinearGrayAImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 2u)); FillBitValue(false); }
~NonlinearGrayAImage() { }
bool IsOpaque() const
{
for(typename vector<T, Allocator>::const_iterator i(pixels.begin()); i != pixels.end(); i += 2)
{
if(i[1] < TMAX)
return false;
}
return true;
}
bool IsGrayscale() const
{
return true;
}
bool IsColour() const
{
return false;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return true;
}
bool HasAlphaChannel() const
{
return true;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr& g, unsigned int max)
{
if (max != TMAX) return false;
if (!GammaCurve::IsNeutral(gamma)) return !g;
gamma.swap(g);
gammaLUT = gamma->GetLookupTable(TMAX);
return true;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
CHECK_BOUNDS(x, y);
return (pixels[(x + y * size_t(width)) * 2] != 0);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
CHECK_BOUNDS(x, y);
return gammaLUT[pixels[(x + y * size_t(width)) * 2]];
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
CHECK_BOUNDS(x, y);
gray = gammaLUT[pixels[(x + y * size_t(width)) * 2]];
alpha = pixels[(x + y * size_t(width)) * 2 + 1] / float(TMAX);
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
red = green = blue = GetGrayValue(x, y);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
GetGrayAValue(x, y, red, alpha);
green = blue = red;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
float alpha;
GetGrayAValue(x, y, red, alpha);
green = blue = red;
Colour::AtoFT(alpha, filter, transm);
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayAValue(x, y, TMAX, ALPHA_OPAQUE_INT(TMAX));
else
SetGrayAValue(x, y, (unsigned int)0, ALPHA_OPAQUE_INT(TMAX));
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetGrayAValue(x, y, gray, ALPHA_OPAQUE);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
SetGrayAValue(x, y, gray, ALPHA_OPAQUE_INT(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 2] = IntEncode(gamma, gray, TMAX);
pixels[(x + y * size_t(width)) * 2 + 1] = T(alpha * float(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 2] = gray;
pixels[(x + y * size_t(width)) * 2 + 1] = alpha;
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetGrayValue(x, y, RGB2Gray(red, green, blue));
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
// not really pretty here, but we're doing color math, so we need to decode and re-encode
SetGrayValue(x, y, RGB2Gray(gammaLUT[red], gammaLUT[green], gammaLUT[blue]));
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
SetGrayAValue(x, y, RGB2Gray(red, green, blue), alpha);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
// not really pretty here, but we're doing color math, so we need to decode and re-encode
// TODO FIXME - this unnecessarily converts alpha from int to float, requiring it to be converted back to int
SetGrayAValue(x, y, RGB2Gray(gammaLUT[red], gammaLUT[green], gammaLUT[blue]), float(alpha) / float(TMAX));
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
SetGrayAValue(x, y, RGB2Gray(red, green, blue), Colour::FTtoA(filter, transm));
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
SetGrayAValue(x, y, RGB2Gray(col.red(), col.green(), col.blue()), col.FTtoA());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillGrayValue(IntEncode(gamma, gray, TMAX));
}
void FillGrayValue(unsigned int gray)
{
FillGrayAValue(gray, ALPHA_OPAQUE_INT(TMAX));
}
void FillGrayAValue(float gray, float alpha)
{
// [CLi 2009-09] this was dividing by float(TMAX) - which I presume to have been a bug.
T g(IntEncode(gamma, gray, TMAX)), a(IntEncode(alpha, TMAX));
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = g;
i++;
*i = a;
}
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = T(gray);
i++;
*i = T(alpha);
}
}
void FillRGBValue(float red, float green, float blue)
{
FillGrayValue(RGB2Gray(red, green, blue));
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
// not really pretty here, but we're doing color math, so we need to decode and re-encode
FillGrayValue(RGB2Gray(gammaLUT[red], gammaLUT[green], gammaLUT[blue]));
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
FillGrayAValue(RGB2Gray(red, green, blue), alpha);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
// not really pretty here, but we're doing color math, so we need to decode and re-encode
// TODO FIXME - this unnecessarily converts alpha from int to float, requiring it to be converted back to int
FillGrayAValue(RGB2Gray(gammaLUT[red], gammaLUT[green], gammaLUT[blue]), float(alpha) / float(TMAX));
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
FillGrayAValue(RGB2Gray(red, green, blue), Colour::FTtoA(filter, transm));
}
private:
vector<T, Allocator> pixels;
GammaCurvePtr gamma;
const float* gammaLUT;
};
typedef NonlinearGrayAImage<unsigned char, 255, Image::GrayA_Gamma8> MemoryNonlinearGrayA8Image;
typedef NonlinearGrayAImage<unsigned short, 65535, Image::GrayA_Gamma16> MemoryNonlinearGrayA16Image;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class NonlinearRGBImage : public Image
{
public:
NonlinearRGBImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
NonlinearRGBImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
NonlinearRGBImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
NonlinearRGBImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 3u)); FillBitValue(false); }
~NonlinearRGBImage() { }
bool IsOpaque() const
{
return true;
}
bool IsGrayscale() const
{
return false;
}
bool IsColour() const
{
return true;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return true;
}
bool HasAlphaChannel() const
{
return false;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr& g, unsigned int max)
{
if (max != TMAX) return false;
if (!GammaCurve::IsNeutral(gamma)) return !g;
gamma.swap(g);
gammaLUT = gamma->GetLookupTable(TMAX);
return true;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
float red, green, blue;
GetRGBValue(x, y, red, green, blue);
return IS_NONZERO_RGB(red, green, blue);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue;
GetRGBValue(x, y, red, green, blue);
return RGB2Gray(red, green, blue);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
gray = GetGrayValue(x, y);
alpha = ALPHA_OPAQUE;
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
CHECK_BOUNDS(x, y);
red = gammaLUT[pixels[(x + y * size_t(width)) * 3]];
green = gammaLUT[pixels[(x + y * size_t(width)) * 3 + 1]];
blue = gammaLUT[pixels[(x + y * size_t(width)) * 3 + 2]];
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
GetRGBValue(x, y, red, green, blue);
alpha = ALPHA_OPAQUE;
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
GetRGBValue(x, y, red, green, blue);
filter = transm = FT_OPAQUE;
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, TMAX);
else
SetGrayValue(x, y, (unsigned int)0);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetRGBValue(x, y, gray, gray, gray);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width) * 3] =
pixels[x + y * size_t(width) * 3 + 1] =
pixels[x + y * size_t(width) * 3 + 2] = gray;
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width) * 3] =
pixels[x + y * size_t(width) * 3 + 1] =
pixels[x + y * size_t(width) * 3 + 2] = IntEncode(gamma, gray, TMAX);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int)
{
CHECK_BOUNDS(x, y);
pixels[x + y * size_t(width) * 3] =
pixels[x + y * size_t(width) * 3 + 1] =
pixels[x + y * size_t(width) * 3 + 2] = gray;
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 3] = IntEncode(gamma, red, TMAX);
pixels[(x + y * size_t(width)) * 3 + 1] = IntEncode(gamma, green, TMAX);
pixels[(x + y * size_t(width)) * 3 + 2] = IntEncode(gamma, blue, TMAX);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 3] = T(red);
pixels[(x + y * size_t(width)) * 3 + 1] = T(green);
pixels[(x + y * size_t(width)) * 3 + 2] = T(blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float, float)
{
SetRGBValue(x, y, red, green, blue);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
SetRGBValue(x, y, col.red(), col.green(), col.blue());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillGrayValue((unsigned int)(IntEncode(gamma, gray, TMAX)));
}
void FillGrayValue(unsigned int gray)
{
fill(pixels.begin(), pixels.end(), gray);
}
void FillGrayAValue(float gray, float)
{
FillRGBValue(gray, gray, gray);
}
void FillGrayAValue(unsigned int gray, unsigned int)
{
FillRGBValue(gray, gray, gray);
}
void FillRGBValue(float red, float green, float blue)
{
// [CLi 2009-09] this was dividing by float(TMAX) - which I presume to have been a bug.
T r(IntEncode(gamma, red, TMAX)), g(IntEncode(gamma, green, TMAX)), b(IntEncode(gamma, blue, TMAX));
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = r;
i++;
*i = g;
i++;
*i = b;
}
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = T(red);
i++;
*i = T(green);
i++;
*i = T(blue);
}
}
void FillRGBAValue(float red, float green, float blue, float)
{
FillRGBValue(red, green, blue);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int)
{
FillRGBValue(red, green, blue);
}
void FillRGBFTValue(float red, float green, float blue, float, float)
{
FillRGBValue(red, green, blue);
}
private:
vector<T, Allocator> pixels;
GammaCurvePtr gamma;
const float* gammaLUT;
};
typedef NonlinearRGBImage<unsigned char, 255, Image::RGB_Gamma8> MemoryNonlinearRGB8Image;
typedef NonlinearRGBImage<unsigned short, 65535, Image::RGB_Gamma16> MemoryNonlinearRGB16Image;
template<typename T, unsigned int TMAX, int IDT, class Allocator = allocator<T> >
class NonlinearRGBAImage : public Image
{
public:
NonlinearRGBAImage(unsigned int w, unsigned int h) :
Image(w, h, ImageDataType(IDT)), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
NonlinearRGBAImage(unsigned int w, unsigned int h, const vector<RGBMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
NonlinearRGBAImage(unsigned int w, unsigned int h, const vector<RGBAMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
NonlinearRGBAImage(unsigned int w, unsigned int h, const vector<RGBFTMapEntry>& m) :
Image(w, h, ImageDataType(IDT), m), gamma(NeutralGammaCurve::Get()) { gammaLUT = gamma->GetLookupTable(TMAX); pixels.resize(SafeUnsignedProduct<size_t>(w, h, 4u)); FillBitValue(false); }
~NonlinearRGBAImage() { }
bool IsOpaque() const
{
for(typename vector<T, Allocator>::const_iterator i(pixels.begin()); i != pixels.end(); i += 4)
{
if(i[3] < TMAX)
return false;
}
return true;
}
bool IsGrayscale() const
{
return false;
}
bool IsColour() const
{
return true;
}
bool IsFloat() const
{
return false;
}
bool IsInt() const
{
return true;
}
bool IsIndexed() const
{
return false;
}
bool IsGammaEncoded() const
{
return true;
}
bool HasAlphaChannel() const
{
return true;
}
bool HasFilterTransmit() const
{
return false;
}
unsigned int GetMaxIntValue() const
{
return TMAX;
}
bool TryDeferDecoding(GammaCurvePtr& g, unsigned int max)
{
if (max != TMAX) return false;
if (!GammaCurve::IsNeutral(gamma)) return !g;
gamma.swap(g);
gammaLUT = gamma->GetLookupTable(TMAX);
return true;
}
bool GetBitValue(unsigned int x, unsigned int y) const
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
float red, green, blue, alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
return IS_NONZERO_RGB(red, green, blue);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue, alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
return RGB2Gray(red, green, blue);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
float red, green, blue;
GetRGBAValue(x, y, red, green, blue, alpha);
gray = RGB2Gray(red, green, blue);
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
float alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
CHECK_BOUNDS(x, y);
red = gammaLUT[pixels[(x + y * size_t(width)) * 4]];
green = gammaLUT[pixels[(x + y * size_t(width)) * 4 + 1]];
blue = gammaLUT[pixels[(x + y * size_t(width)) * 4 + 2]];
alpha = float(pixels[(x + y * size_t(width)) * 4 + 3]) / float(TMAX);
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
float alpha;
GetRGBAValue(x, y, red, green, blue, alpha);
Colour::AtoFT(alpha, filter, transm);
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, TMAX);
else
SetGrayValue(x, y, (unsigned int)0);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetRGBAValue(x, y, gray, gray, gray, ALPHA_OPAQUE);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
SetRGBAValue(x, y, gray, gray, gray, ALPHA_OPAQUE_INT(TMAX));
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
SetRGBAValue(x, y, gray, gray, gray, alpha);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
SetRGBAValue(x, y, gray, gray, gray, alpha);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetRGBAValue(x, y, red, green, blue, ALPHA_OPAQUE);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetRGBAValue(x, y, red, green, blue, ALPHA_OPAQUE_INT(TMAX));
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 4] = T(IntEncode(gamma, red, TMAX));
pixels[(x + y * size_t(width)) * 4 + 1] = T(IntEncode(gamma, green, TMAX));
pixels[(x + y * size_t(width)) * 4 + 2] = T(IntEncode(gamma, blue, TMAX));
pixels[(x + y * size_t(width)) * 4 + 3] = T(IntEncode( alpha, TMAX));
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 4] = T(red);
pixels[(x + y * size_t(width)) * 4 + 1] = T(green);
pixels[(x + y * size_t(width)) * 4 + 2] = T(blue);
pixels[(x + y * size_t(width)) * 4 + 3] = T(alpha);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
SetRGBAValue(x, y, red, green, blue, Colour::FTtoA(filter, transm));
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
CHECK_BOUNDS(x, y);
pixels[(x + y * size_t(width)) * 4] = T(col.red());
pixels[(x + y * size_t(width)) * 4 + 1] = T(col.green());
pixels[(x + y * size_t(width)) * 4 + 2] = T(col.blue());
pixels[(x + y * size_t(width)) * 4 + 3] = T(col.FTtoA());
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(TMAX);
else
FillGrayValue((unsigned int)0);
}
void FillGrayValue(float gray)
{
FillRGBAValue(gray, gray, gray, ALPHA_OPAQUE);
}
void FillGrayValue(unsigned int gray)
{
FillRGBAValue(gray, gray, gray, ALPHA_OPAQUE_INT(TMAX));
}
void FillGrayAValue(float gray, float alpha)
{
FillRGBAValue(gray, gray, gray, alpha);
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
FillRGBAValue(gray, gray, gray, alpha);
}
void FillRGBValue(float red, float green, float blue)
{
FillRGBAValue(red, green, blue, ALPHA_OPAQUE);
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillRGBAValue(red, green, blue, ALPHA_OPAQUE_INT(TMAX));
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
// [CLi 2009-09] this was dividing by float(TMAX) - which I presume to have been a bug.
T r(IntEncode(gamma, red, TMAX)), g(IntEncode(gamma, green, TMAX)), b(IntEncode(gamma, blue, TMAX)), a(IntEncode(alpha, TMAX));
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = r;
i++;
*i = g;
i++;
*i = b;
i++;
*i = a;
}
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
for(typename vector<T, Allocator>::iterator i(pixels.begin()); i != pixels.end(); i++)
{
*i = T(red);
i++;
*i = T(green);
i++;
*i = T(blue);
i++;
*i = T(alpha);
}
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
FillRGBAValue(red, green, blue, Colour::FTtoA(filter, transm));
}
private:
vector<T, Allocator> pixels;
GammaCurvePtr gamma;
const float* gammaLUT;
};
typedef NonlinearRGBAImage<unsigned char, 255, Image::RGBA_Gamma8> MemoryNonlinearRGBA8Image;
typedef NonlinearRGBAImage<unsigned short, 65535, Image::RGBA_Gamma16> MemoryNonlinearRGBA16Image;
// sample basic file-based pixel container. not very efficient.
// it is expected that for performance reasons, platforms will provide their own specific
// implementation of this; hence, this should be considered only a fall-back default.
// [JG] That code is hostile to the system, as the file seek are just passed directly.
// (and there is a lot, out of sequence!)
// On the render (1st write), the cache will always miss, but as post-process might access more than once to
// this container, it is mandatory to still read the file every time.
// Measurement about the small read cache (compared to the alternative) show no significant delta in performance
// to read the contained data into a PNG.
// (it's a linear read, the system is able to anticipate it, so we are fine!)
class FileBackedPixelContainer
{
public:
typedef long size_type;
enum
{
RED = 0,
GREEN = 1,
BLUE = 2,
FILTER = 3,
TRANSM = 4
};
class pixel_type
{
public:
pixel_type()
{
elements[RED] = 0.0;
elements[GREEN] = 0.0;
elements[BLUE] = 0.0;
elements[FILTER] = 0.0;
elements[TRANSM] = 0.0;
}
pixel_type(const COLC *vals)
{
memcpy(elements, vals, sizeof(elements));
}
pixel_type(float r, float g, float b, float f, float t)
{
elements[RED] = r;
elements[GREEN] = g;
elements[BLUE] = b;
elements[FILTER] = f;
elements[TRANSM] = t;
}
~pixel_type() {}
operator COLC *() { return elements; }
operator const COLC *() const { return elements; }
bool operator==(float val) const { return elements[RED] == val && elements[GREEN] == val && elements[BLUE] == val && elements[FILTER] == val && elements[TRANSM] == val; }
bool operator!=(float val) const { return !(*this == val); }
protected:
COLC elements[5];
};
FileBackedPixelContainer(size_type width, size_type height, size_type bs):
m_File(-1), m_Width(width), m_Height(height), m_xPos(0), m_yPos(0), m_Dirty(false), m_Path(POV_PLATFORM_BASE.CreateTemporaryFile())
{
if ((m_File = open(UCS2toASCIIString(m_Path).c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) == -1)
throw POV_EXCEPTION(kCannotOpenFileErr, "Cannot open backing file for intermediate image storage.");
m_Blocksize = bs;
m_Buffer.resize(m_Blocksize);
// write extra data to create the big file and help 3rd party reader
POV_LONG pos;
// NB: The following use of SafeUnsignedProduct also safeguards later coputations of
// pixel positions within the file, as long as x and y coordinates are sane
pos = SafeUnsignedProduct<POV_LONG>(m_Width, m_Height);
if ( pos% m_Blocksize)
{ /* issue: the block would overlap the end of file */
pos /= m_Blocksize;
pos++;
pos = SafeUnsignedProduct<POV_LONG>(pos, m_Blocksize);
}
/* else fine case: the boundary of block match the boundary of pixels in file */
pos = SafeUnsignedProduct<POV_LONG>(pos, sizeof(pixel_type));
size_type meta[3];
meta[0] = sizeof(pixel_type);
meta[1] = m_Width;
meta[2] = m_Height;
if (lseek64(m_File, pos, SEEK_SET) != pos)
throw POV_EXCEPTION(kFileDataErr, "Intermediate image storage backing file write/seek failed at creation.");
if (write(m_File, &meta[0], (int) sizeof(size_type)*3) != (sizeof(size_type)*3))
throw POV_EXCEPTION(kFileDataErr, "Intermediate image storage backing file write failed at creation.");
// m_Committed.resize(width * height / m_Blocksize);
}
virtual ~FileBackedPixelContainer()
{
if (m_File != -1)
{
Flush();
close(m_File);
}
if (m_Path.empty() == false)
{
// if shutdown has been delayed, by the time we reach here, the platform base
// may no longer be valid (see crashdump #77 for an example of this). we need
// to take the address of the reference to see if it's now NULL before we use it.
PlatformBase *pb(&POV_PLATFORM_BASE);
if (pb != NULL)
pb->DeleteTemporaryFile(m_Path);
}
}
void Flush(void)
{
WriteCurrentBlock();
}
void SetPixel(size_type x, size_type y, const pixel_type& pixel)
{
WritePixel(x, y, pixel);
NextPixel();
}
void SetPixel(size_type x, size_type y, float red, float green, float blue, float filter, float transm)
{
pixel_type pixel;
pixel[RED] = red;
pixel[GREEN] = green;
pixel[BLUE] = blue;
pixel[FILTER] = filter;
pixel[TRANSM] = transm;
WritePixel(x, y, pixel);
}
void SetPixel(float red, float green, float blue, float filter, float transm)
{
SetPixel(m_xPos, m_yPos, red, green, blue, filter, transm);
NextPixel();
}
void GetPixel(pixel_type& pixel)
{
ReadPixel(m_xPos, m_yPos, pixel);
NextPixel();
}
void GetPixel(float& red, float& green, float& blue, float& filter, float& transm)
{
pixel_type pixel;
GetPixel(pixel); // advances NextPixel
red = pixel[RED];
green = pixel[GREEN];
blue = pixel[BLUE];
filter = pixel[FILTER];
transm = pixel[TRANSM];
}
void GetPixel(size_type x, size_type y, pixel_type& pixel)
{
SetPos(x, y);
ReadPixel(x, y, pixel);
}
void GetPixel(size_type x, size_type y, float& red, float& green, float& blue, float& filter, float& transm)
{
pixel_type pixel;
GetPixel(x, y, pixel); // sets Position
red = pixel[RED];
green = pixel[GREEN];
blue = pixel[BLUE];
filter = pixel[FILTER];
transm = pixel[TRANSM];
}
/* void ClearCache(const pixel_type& pixel = pixel_type())
{
for (int i = 0; i < m_Width; i++)
memcpy(&m_Buffer[i], &pixel, sizeof(pixel));
} */
void FillLine(size_type y, const pixel_type& pixel)
{
// bool notBlank(pixel != 0.0);
for (size_type x = 0; x < m_Width; x++)
// if (notBlank)
WritePixel(x, y, pixel);
}
void Fill(const pixel_type& pixel)
{
for (size_type y = 0; y < m_Height; y++)
FillLine(y, pixel);
}
void Fill(float red, float green, float blue, float filter, float transm)
{
pixel_type pixel(red, green, blue, filter, transm);
Fill(pixel);
}
protected:
int m_File;
bool m_Dirty;
size_type m_Blocksize;
POV_LONG m_CurrentBlock;
size_type m_Width;
size_type m_Height;
size_type m_xPos;
size_type m_yPos;
UCS2String m_Path;
//vector<bool> m_Committed;
vector<pixel_type> m_Buffer;
void SetPos(size_type x, size_type y, bool cache = true)
{
if (x < 0 || x >= m_Width || y < 0 || y >= m_Height)
throw POV_EXCEPTION(kFileDataErr, "Invalid coordinates in intermediate image file seek.");
if (y == m_yPos)
{
m_xPos = x;
return;
}
m_xPos = x;
m_yPos = y;
}
void NextPixel(void)
{
if (m_xPos == m_Width - 1)
{
if (m_yPos == m_Height - 1)
return;
}
else
m_xPos++;
}
void ReadPixel(size_type x, size_type y, pixel_type& pixel)
{
POV_LONG pos, block = (y * (POV_LONG)(m_Width) + x) / m_Blocksize;
if (block != m_CurrentBlock) {
WriteCurrentBlock();
#if 0
if (m_Committed[block] == false) {
COLC pixel[5] = {0.0, 0.0, 0.0, 0.0, 0.0};
for (size_type i = 0; i < m_Blocksize; i++)
memcpy(&m_Buffer[i], pixel, sizeof(pixel));
m_CurrentBlock = block;
return;
}
#endif
pos = block * sizeof(pixel_type) * m_Blocksize;
int chunk = sizeof(pixel_type) * m_Blocksize;
if (lseek64(m_File, pos, SEEK_SET) != pos)
throw POV_EXCEPTION(kFileDataErr, "Intermediate image storage backing file read/seek failed.");
int bytes = read(m_File, &m_Buffer[0], chunk);
if (bytes != (sizeof(pixel_type) * m_Blocksize))
throw POV_EXCEPTION(kFileDataErr, "Intermediate image storage backing file read failed.");
m_CurrentBlock = block;
}
memcpy(&pixel, m_Buffer[(y * (POV_LONG)(m_Width) + x) % m_Blocksize], sizeof(pixel));
}
#if 0
bool BlockCommitted(size_type x, size_type y)
{
POV_LONG block = (y * POV_LONG(m_Width) + x) / m_Blocksize;
return(m_Committed[block]);
}
#endif
void WriteCurrentBlock()
{
POV_LONG pos;
if (m_Dirty) {
pos = m_CurrentBlock * sizeof(pixel_type) * m_Blocksize;
if (lseek64(m_File, pos, SEEK_SET) != pos)
throw POV_EXCEPTION(kFileDataErr, "Intermediate image storage backing file write/seek failed.");
if (write(m_File, &m_Buffer[0], (int) sizeof(pixel_type) * m_Blocksize) != (sizeof(pixel_type) * m_Blocksize))
throw POV_EXCEPTION(kFileDataErr, "Intermediate image storage backing file write failed.");
// m_Committed[m_CurrentBlock] = true;
m_Dirty = false;
}
}
void WritePixel(size_type x, size_type y, const pixel_type& pixel)
{
pixel_type dummy;
ReadPixel(x, y, dummy);
memcpy(m_Buffer[(y * (POV_LONG)(m_Width) + x) % m_Blocksize], &pixel, sizeof(pixel));
m_Dirty = true;
}
private:
// not available
FileBackedPixelContainer(void) {}
};
class FileRGBFTImage : public Image
{
public:
typedef FileBackedPixelContainer::pixel_type pixel_type;
FileRGBFTImage(unsigned int w, unsigned int h, unsigned int bs): Image(w, h, RGBFT_Float), pixels(width, height, bs) { }
~FileRGBFTImage() { }
bool IsGrayscale() const { return false; }
bool IsColour() const { return true; }
bool IsFloat() const { return true; }
bool IsInt() const { return false; }
bool IsIndexed() const { return false; }
bool IsGammaEncoded() const { return false; }
bool HasAlphaChannel() const { return false; }
bool HasFilterTransmit() const { return true; }
unsigned int GetMaxIntValue() const { return 255; }
void SetEncodingGamma(GammaCurvePtr gamma) { ; }
bool TryDeferDecoding(GammaCurvePtr&, unsigned int) { return false; }
bool IsOpaque() const { throw POV_EXCEPTION(kUncategorizedError, "Internal error: IsOpaque() not supported in FileRGBFTImage"); }
bool GetBitValue(unsigned int x, unsigned int y) const
{
// TODO FIXME - [CLi] This ignores opacity information; other bit-based code doesn't.
float red, green, blue, filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
return IS_NONZERO_RGB(red, green, blue);
}
float GetGrayValue(unsigned int x, unsigned int y) const
{
float red, green, blue, filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
return RGB2Gray(red, green, blue);
}
void GetGrayAValue(unsigned int x, unsigned int y, float& gray, float& alpha) const
{
float red, green, blue, filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
gray = RGB2Gray(red, green, blue);
alpha = Colour::FTtoA(filter, transm);
}
void GetRGBValue(unsigned int x, unsigned int y, float& red, float& green, float& blue) const
{
float filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
}
void GetRGBAValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& alpha) const
{
float filter, transm;
GetRGBFTValue(x, y, red, green, blue, filter, transm);
alpha = Colour::FTtoA(filter, transm);
}
void GetRGBFTValue(unsigned int x, unsigned int y, float& red, float& green, float& blue, float& filter, float& transm) const
{
CHECK_BOUNDS(x, y);
pixels.GetPixel(x, y, red, green, blue, filter, transm);
}
void SetBitValue(unsigned int x, unsigned int y, bool bit)
{
if(bit == true)
SetGrayValue(x, y, 1.0f);
else
SetGrayValue(x, y, 0.0f);
}
void SetGrayValue(unsigned int x, unsigned int y, float gray)
{
SetRGBFTValue(x, y, gray, gray, gray, FT_OPAQUE, FT_OPAQUE);
}
void SetGrayValue(unsigned int x, unsigned int y, unsigned int gray)
{
SetGrayValue(x, y, float(gray) / 255.0f);
}
void SetGrayAValue(unsigned int x, unsigned int y, float gray, float alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
SetRGBFTValue(x, y, gray, gray, gray, filter, transm);
}
void SetGrayAValue(unsigned int x, unsigned int y, unsigned int gray, unsigned int alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float c = float(gray) / 255.0f;
float filter, transm;
Colour::AtoFT(float(alpha) / 255.0f, filter, transm);
SetRGBFTValue(x, y, c, c, c, filter, transm);
}
void SetRGBValue(unsigned int x, unsigned int y, float red, float green, float blue)
{
SetRGBFTValue(x, y, red, green, blue, FT_OPAQUE, FT_OPAQUE);
}
void SetRGBValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue)
{
SetRGBFTValue(x, y, float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, FT_OPAQUE, FT_OPAQUE);
}
void SetRGBAValue(unsigned int x, unsigned int y, float red, float green, float blue, float alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
SetRGBFTValue(x, y, red, green, blue, filter, transm);
}
void SetRGBAValue(unsigned int x, unsigned int y, unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float filter, transm;
Colour::AtoFT(float(alpha) / 255.0f, filter, transm);
SetRGBFTValue(x, y, float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, filter, transm);
}
void SetRGBFTValue(unsigned int x, unsigned int y, float red, float green, float blue, float filter, float transm)
{
CHECK_BOUNDS(x, y);
pixels.SetPixel(x, y, red, green, blue, filter, transm);
}
void SetRGBFTValue(unsigned int x, unsigned int y, const Colour& col)
{
CHECK_BOUNDS(x, y);
pixels.SetPixel(x, y, *col);
}
void FillBitValue(bool bit)
{
if(bit == true)
FillGrayValue(1.0f);
else
FillGrayValue(0.0f);
}
void FillGrayValue(float gray)
{
FillRGBFTValue(gray, gray, gray, FT_OPAQUE, FT_OPAQUE);
}
void FillGrayValue(unsigned int gray)
{
FillGrayValue(float(gray) / 255.0f);
}
void FillGrayAValue(float gray, float alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
FillRGBFTValue(gray, gray, gray, filter, transm);
}
void FillGrayAValue(unsigned int gray, unsigned int alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
FillGrayAValue(float(gray) / 255.0f, float(alpha) / 255.0f);
}
void FillRGBValue(float red, float green, float blue)
{
FillRGBFTValue(red, green, blue, FT_OPAQUE, FT_OPAQUE);
}
void FillRGBValue(unsigned int red, unsigned int green, unsigned int blue)
{
FillRGBFTValue(float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, FT_OPAQUE, FT_OPAQUE);
}
void FillRGBAValue(float red, float green, float blue, float alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float filter, transm;
Colour::AtoFT(alpha, filter, transm);
FillRGBFTValue(red, green, blue, filter, transm);
}
void FillRGBAValue(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha)
{
// TODO - should alpha be converted to filter and transm? [trf]
float filter, transm;
Colour::AtoFT(float(alpha) / 255.0f, filter, transm);
FillRGBFTValue(float(red) / 255.0f, float(green) / 255.0f, float(blue) / 255.0f, filter, transm);
}
void FillRGBFTValue(float red, float green, float blue, float filter, float transm)
{
pixels.Fill(red, green, blue, filter, transm);
}
protected:
mutable FileBackedPixelContainer pixels;
};
void RGBMap2RGBAMap(const vector<Image::RGBMapEntry>& m, vector<Image::RGBAMapEntry>& n);
void RGBMap2RGBFTMap(const vector<Image::RGBMapEntry>& m, vector<Image::RGBFTMapEntry>& n);
void RGBAMap2RGBMap(const vector<Image::RGBAMapEntry>& m, vector<Image::RGBMapEntry>& n);
void RGBAMap2RGBFTMap(const vector<Image::RGBAMapEntry>& m, vector<Image::RGBFTMapEntry>& n);
void RGBFTMap2RGBMap(const vector<Image::RGBFTMapEntry>& m, vector<Image::RGBMapEntry>& n);
void RGBFTMap2RGBAMap(const vector<Image::RGBFTMapEntry>& m, vector<Image::RGBAMapEntry>& n);
void RGBMap2RGBAMap(const vector<Image::RGBMapEntry>& m, vector<Image::RGBAMapEntry>& n)
{
n.clear();
n.reserve(m.size());
for(vector<Image::RGBMapEntry>::const_iterator i(m.begin()); i != m.end(); i++)
n.push_back(Image::RGBAMapEntry(i->red, i->green, i->blue, ALPHA_OPAQUE));
}
void RGBMap2RGBFTMap(const vector<Image::RGBMapEntry>& m, vector<Image::RGBFTMapEntry>& n)
{
n.clear();
n.reserve(m.size());
for(vector<Image::RGBMapEntry>::const_iterator i(m.begin()); i != m.end(); i++)
n.push_back(Image::RGBFTMapEntry(i->red, i->green, i->blue, FT_OPAQUE, FT_OPAQUE));
}
void RGBAMap2RGBMap(const vector<Image::RGBAMapEntry>& m, vector<Image::RGBMapEntry>& n)
{
n.clear();
n.reserve(m.size());
for(vector<Image::RGBAMapEntry>::const_iterator i(m.begin()); i != m.end(); i++)
n.push_back(Image::RGBMapEntry(i->red, i->green, i->blue));
}
void RGBAMap2RGBFTMap(const vector<Image::RGBAMapEntry>& m, vector<Image::RGBFTMapEntry>& n)
{
n.clear();
n.reserve(m.size());
for(vector<Image::RGBAMapEntry>::const_iterator i(m.begin()); i != m.end(); i++)
{
float filter, transm;
Colour::AtoFT(i->alpha, filter, transm);
n.push_back(Image::RGBFTMapEntry(i->red, i->green, i->blue, filter, transm));
}
}
void RGBFTMap2RGBMap(const vector<Image::RGBFTMapEntry>& m, vector<Image::RGBMapEntry>& n)
{
n.clear();
n.reserve(m.size());
for(vector<Image::RGBFTMapEntry>::const_iterator i(m.begin()); i != m.end(); i++)
n.push_back(Image::RGBMapEntry(i->red, i->green, i->blue));
}
void RGBFTMap2RGBAMap(const vector<Image::RGBFTMapEntry>& m, vector<Image::RGBAMapEntry>& n)
{
n.clear();
n.reserve(m.size());
for(vector<Image::RGBFTMapEntry>::const_iterator i(m.begin()); i != m.end(); i++)
n.push_back(Image::RGBAMapEntry(i->red, i->green, i->blue, Colour::FTtoA(i->filter, i->transm)));
}
Image *Image::Create(unsigned int w, unsigned int h, ImageDataType t, unsigned int maxRAMmbHint, unsigned int pixelsPerBlockHint)
{
try
{
switch(t)
{
case Bit_Map:
return new MemoryBitMapImage(w, h);
case Gray_Int8:
return new MemoryGray8Image(w, h);
case Gray_Int16:
return new MemoryGray16Image(w, h);
case GrayA_Int8:
return new MemoryGrayA8Image(w, h);
case GrayA_Int16:
return new MemoryGrayA16Image(w, h);
case RGB_Int8:
return new MemoryRGB8Image(w, h);
case RGB_Int16:
return new MemoryRGB16Image(w, h);
case RGBA_Int8:
return new MemoryRGBA8Image (w, h);
case RGBA_Int16:
return new MemoryRGBA16Image(w, h);
case RGBFT_Float:
if (maxRAMmbHint > 0)
if (SafeUnsignedProduct<POV_ULONG>(w, h, sizeof(FileRGBFTImage::pixel_type)) / 1048576 > maxRAMmbHint)
return new FileRGBFTImage(w, h, pixelsPerBlockHint);
return new MemoryRGBFTImage(w, h);
case RGB_Gamma8:
return new MemoryNonlinearRGB8Image(w, h);
case RGB_Gamma16:
return new MemoryNonlinearRGB16Image(w, h);
case RGBA_Gamma8:
return new MemoryNonlinearRGBA8Image (w, h);
case RGBA_Gamma16:
return new MemoryNonlinearRGBA16Image(w, h);
case Gray_Gamma8:
return new MemoryNonlinearGray8Image(w, h);
case Gray_Gamma16:
return new MemoryNonlinearGray16Image(w, h);
case GrayA_Gamma8:
return new MemoryNonlinearGrayA8Image(w, h);
case GrayA_Gamma16:
return new MemoryNonlinearGrayA16Image(w, h);
default:
throw POV_EXCEPTION_STRING("Undefined image format in Image::Create");
}
}
catch(std::bad_alloc&)
{
throw POV_EXCEPTION(kOutOfMemoryErr, "Insufficient memory to allocate intermediate image storage.");
}
}
Image *Image::Create(unsigned int w, unsigned int h, ImageDataType t, bool allowFileBacking)
{
try
{
switch(t)
{
case Bit_Map:
return new MemoryBitMapImage(w, h);
case Gray_Int8:
return new MemoryGray8Image(w, h);
case Gray_Int16:
return new MemoryGray16Image(w, h);
case GrayA_Int8:
return new MemoryGrayA8Image(w, h);
case GrayA_Int16:
return new MemoryGrayA16Image(w, h);
case RGB_Int8:
return new MemoryRGB8Image(w, h);
case RGB_Int16:
return new MemoryRGB16Image(w, h);
case RGBA_Int8:
return new MemoryRGBA8Image (w, h);
case RGBA_Int16:
return new MemoryRGBA16Image(w, h);
case RGBFT_Float:
#ifdef FILE_MAPPED_IMAGE_ALLOCATOR
if (allowFileBacking)
return new RGBFTImage<FILE_MAPPED_IMAGE_ALLOCATOR<float> >(w, h);
#endif
return new MemoryRGBFTImage(w, h);
case RGB_Gamma8:
return new MemoryNonlinearRGB8Image(w, h);
case RGB_Gamma16:
return new MemoryNonlinearRGB16Image(w, h);
case RGBA_Gamma8:
return new MemoryNonlinearRGBA8Image (w, h);
case RGBA_Gamma16:
return new MemoryNonlinearRGBA16Image(w, h);
case Gray_Gamma8:
return new MemoryNonlinearGray8Image(w, h);
case Gray_Gamma16:
return new MemoryNonlinearGray16Image(w, h);
case GrayA_Gamma8:
return new MemoryNonlinearGrayA8Image(w, h);
case GrayA_Gamma16:
return new MemoryNonlinearGrayA16Image(w, h);
default:
throw POV_EXCEPTION_STRING("Undefined image format in Image::Create");
}
}
catch(std::bad_alloc&)
{
throw POV_EXCEPTION(kOutOfMemoryErr, "Insufficient memory to allocate intermediate image storage.");
}
}
Image *Image::Create(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBMapEntry>& m, bool allowFileBacking)
{
try
{
switch(t)
{
case Bit_Map:
return new MemoryBitMapImage(w, h, m);
case Colour_Map:
return new MemoryColourMapImage(w, h, m);
case Gray_Int8:
return new MemoryGray8Image(w, h, m);
case Gray_Int16:
return new MemoryGray16Image(w, h, m);
case GrayA_Int8:
return new MemoryGrayA8Image(w, h, m);
case GrayA_Int16:
return new MemoryGrayA16Image(w, h, m);
case RGB_Int8:
return new MemoryRGB8Image(w, h, m);
case RGB_Int16:
return new MemoryRGB16Image(w, h, m);
case RGBA_Int8:
return new MemoryRGBA8Image (w, h, m);
case RGBA_Int16:
return new MemoryRGBA16Image(w, h, m);
case RGBFT_Float:
#ifdef FILE_MAPPED_IMAGE_ALLOCATOR
if (allowFileBacking)
return new RGBFTImage<FILE_MAPPED_IMAGE_ALLOCATOR<float> >(w, h, m);
#endif
return new MemoryRGBFTImage(w, h, m);
case RGB_Gamma8:
return new MemoryNonlinearRGB8Image(w, h, m);
case RGB_Gamma16:
return new MemoryNonlinearRGB16Image(w, h, m);
case RGBA_Gamma8:
return new MemoryNonlinearRGBA8Image (w, h, m);
case RGBA_Gamma16:
return new MemoryNonlinearRGBA16Image(w, h, m);
case Gray_Gamma8:
return new MemoryNonlinearGray8Image(w, h, m);
case Gray_Gamma16:
return new MemoryNonlinearGray16Image(w, h, m);
case GrayA_Gamma8:
return new MemoryNonlinearGrayA8Image(w, h, m);
case GrayA_Gamma16:
return new MemoryNonlinearGrayA16Image(w, h, m);
default:
throw POV_EXCEPTION_STRING("Image::Create Exception TODO"); // TODO FIXME WIP
}
}
catch(std::bad_alloc&)
{
throw POV_EXCEPTION(kOutOfMemoryErr, "Insufficient memory to allocate intermediate image storage.");
}
}
Image *Image::Create(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBAMapEntry>& m, bool allowFileBacking)
{
try
{
switch(t)
{
case Bit_Map:
return new MemoryBitMapImage(w, h, m);
case Colour_Map:
return new MemoryColourMapImage(w, h, m);
case Gray_Int8:
return new MemoryGray8Image(w, h, m);
case Gray_Int16:
return new MemoryGray16Image(w, h, m);
case GrayA_Int8:
return new MemoryGrayA8Image(w, h, m);
case GrayA_Int16:
return new MemoryGrayA16Image(w, h, m);
case RGB_Int8:
return new MemoryRGB8Image(w, h, m);
case RGB_Int16:
return new MemoryRGB16Image(w, h, m);
case RGBA_Int8:
return new MemoryRGBA8Image (w, h, m);
case RGBA_Int16:
return new MemoryRGBA16Image(w, h, m);
case RGBFT_Float:
#ifdef FILE_MAPPED_RGBFT_IMAGE_ALLOCATOR
if (allowFileBacking)
return new RGBFTImage<FILE_MAPPED_RGBFT_IMAGE_ALLOCATOR<float> >(w, h, m);
#endif
return new MemoryRGBFTImage(w, h, m);
case RGB_Gamma8:
return new MemoryNonlinearRGB8Image(w, h, m);
case RGB_Gamma16:
return new MemoryNonlinearRGB16Image(w, h, m);
case RGBA_Gamma8:
return new MemoryNonlinearRGBA8Image (w, h, m);
case RGBA_Gamma16:
return new MemoryNonlinearRGBA16Image(w, h, m);
case Gray_Gamma8:
return new MemoryNonlinearGray8Image(w, h, m);
case Gray_Gamma16:
return new MemoryNonlinearGray16Image(w, h, m);
case GrayA_Gamma8:
return new MemoryNonlinearGrayA8Image(w, h, m);
case GrayA_Gamma16:
return new MemoryNonlinearGrayA16Image(w, h, m);
default:
throw POV_EXCEPTION_STRING("Image::Create Exception TODO"); // TODO FIXME WIP
}
}
catch(std::bad_alloc&)
{
throw POV_EXCEPTION(kOutOfMemoryErr, "Insufficient memory to allocate intermediate image storage.");
}
}
Image *Image::Create(unsigned int w, unsigned int h, ImageDataType t, const vector<RGBFTMapEntry>& m, bool allowFileBacking)
{
try
{
switch(t)
{
case Bit_Map:
return new MemoryBitMapImage(w, h, m);
case Colour_Map:
return new MemoryColourMapImage(w, h, m);
case Gray_Int8:
return new MemoryGray8Image(w, h, m);
case Gray_Int16:
return new MemoryGray16Image(w, h, m);
case GrayA_Int8:
return new MemoryGrayA8Image(w, h, m);
case GrayA_Int16:
return new MemoryGrayA16Image(w, h, m);
case RGB_Int8:
return new MemoryRGB8Image(w, h, m);
case RGB_Int16:
return new MemoryRGB16Image(w, h, m);
case RGBA_Int8:
return new MemoryRGBA8Image (w, h, m);
case RGBA_Int16:
return new MemoryRGBA16Image(w, h, m);
case RGBFT_Float:
#ifdef FILE_MAPPED_IMAGE_ALLOCATOR
if (allowFileBacking)
return new RGBFTImage<FILE_MAPPED_IMAGE_ALLOCATOR<float> >(w, h, m);
#endif
return new MemoryRGBFTImage(w, h, m);
case RGB_Gamma8:
return new MemoryNonlinearRGB8Image(w, h, m);
case RGB_Gamma16:
return new MemoryNonlinearRGB16Image(w, h, m);
case RGBA_Gamma8:
return new MemoryNonlinearRGBA8Image (w, h, m);
case RGBA_Gamma16:
return new MemoryNonlinearRGBA16Image(w, h, m);
case Gray_Gamma8:
return new MemoryNonlinearGray8Image(w, h, m);
case Gray_Gamma16:
return new MemoryNonlinearGray16Image(w, h, m);
case GrayA_Gamma8:
return new MemoryNonlinearGrayA8Image(w, h, m);
case GrayA_Gamma16:
return new MemoryNonlinearGrayA16Image(w, h, m);
default:
throw POV_EXCEPTION_STRING("Image::Create Exception TODO"); // TODO FIXME WIP
}
}
catch(std::bad_alloc&)
{
throw POV_EXCEPTION(kOutOfMemoryErr, "Insufficient memory to allocate intermediate image storage.");
}
}
Image *Image::Read(ImageFileType type, IStream *file, const ReadOptions& options)
{
#ifdef SYS_TO_STANDARD
if (type == SYS)
type = SYS_TO_STANDARD ;
#endif
switch (type)
{
case HDR:
return (HDR::Read(file, options));
case EXR:
#ifndef OPENEXR_MISSING
return (OpenEXR::Read(file, options));
#else
throw POV_EXCEPTION(kCannotOpenFileErr,
"This unofficial POV-Ray binary was built without support for the OpenEXR \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the OpenEXR library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: HDR.");
return NULL;
#endif
case PNG:
#ifndef LIBPNG_MISSING
return (Png::Read(file, options));
#else
throw POV_EXCEPTION(kCannotOpenFileErr,
"This unofficial POV-Ray binary was built without support for the PNG \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the libPNG library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: GIF, TGA, IFF, PGM, PPM, BMP.");
return NULL;
#endif
case GIF:
return (Gif::Read(file, options, false));
case POT:
return (Gif::Read(file, options, true));
case TGA:
return (Targa::Read(file, options));
case JPEG:
#ifndef LIBJPEG_MISSING
return (Jpeg::Read(file, options));
#else
throw POV_EXCEPTION(kCannotOpenFileErr,
"This unofficial POV-Ray binary was built without support for the JPEG \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the libJPEG library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: GIF, TGA, IFF, PGM, PPM, BMP.");
return NULL;
#endif
case IFF:
return (Iff::Read(file, options));
case PGM:
return (Pgm::Read(file, options));
case PPM:
return (Ppm::Read(file, options));
case BMP:
return (Bmp::Read(file, options));
case TIFF:
#ifndef LIBTIFF_MISSING
return (Tiff::Read(file, options));
#else
throw POV_EXCEPTION(kCannotOpenFileErr,
"This unofficial POV-Ray binary was built without support for the TIFF \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the libTIFF library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: GIF, TGA, IFF, PGM, PPM, BMP.");
return NULL;
#endif
case SYS:
throw POV_EXCEPTION(kCannotOpenFileErr, "This platform has not defined a SYS file type");
return (NULL);
default :
throw POV_EXCEPTION(kParamErr, "Invalid file type");
return (NULL);
}
}
void Image::Write(ImageFileType type, OStream *file, const Image *image, const WriteOptions& options)
{
if (image->GetWidth() == 0 || image->GetHeight() == 0)
throw POV_EXCEPTION(kParamErr, "Invalid image size for output");
if (file == NULL)
throw POV_EXCEPTION(kCannotOpenFileErr, "Invalid image file");
#ifdef SYS_TO_STANDARD
if (type == SYS)
type = SYS_TO_STANDARD ;
#endif
switch (type)
{
case GIF:
case IFF:
case PGM:
case TIFF:
case POT:
throw POV_EXCEPTION(kParamErr, "Unsupported file type for output");
break;
case SYS:
throw POV_EXCEPTION(kCannotOpenFileErr, "This platform has not defined a SYS file type");
break;
case HDR:
HDR::Write(file, image, options);
break;
case EXR:
#ifndef OPENEXR_MISSING
OpenEXR::Write(file, image, options);
#else
throw POV_EXCEPTION(kParamErr,
"This unofficial POV-Ray binary was built without support for the OpenEXR \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the OpenEXR library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: HDR.");
#endif
break;
case PNG:
#ifndef LIBPNG_MISSING
Png::Write(file, image, options);
#else
throw POV_EXCEPTION(kParamErr,
"This unofficial POV-Ray binary was built without support for the PNG \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the libPNG library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: TGA, PPM, BMP.");
#endif
break;
case TGA:
Targa::Write(file, image, options);
break;
case PPM:
Ppm::Write(file, image, options);
break;
case BMP:
Bmp::Write(file, image, options);
break;
case JPEG:
#ifndef LIBJPEG_MISSING
Jpeg::Write(file, image, options);
#else
throw POV_EXCEPTION(kParamErr,
"This unofficial POV-Ray binary was built without support for the JPEG \
file format. You must either use an official POV-Ray binary or recompile \
the POV-Ray sources on a system providing you with the libJPEG library \
to make use of this facility. Alternatively, you may use any of the \
following built-in formats: TGA, PPM, BMP.");
#endif
break;
default :
throw POV_EXCEPTION(kParamErr, "Invalid file type");
break;
}
}
void Image::GetRGBIndexedValue(unsigned char index, float& red, float& green, float& blue) const
{
switch(colormaptype)
{
case NoColourMap:
red = 0.0f;
green = 0.0f;
blue = 0.0f;
break;
case RGBColourMap:
case RGBAColourMap:
case RGBFTColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
break;
}
}
void Image::GetRGBAIndexedValue(unsigned char index, float& red, float& green, float& blue, float& alpha) const
{
switch(colormaptype)
{
case NoColourMap:
red = 0.0f;
green = 0.0f;
blue = 0.0f;
alpha = ALPHA_OPAQUE;
break;
case RGBColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
alpha = ALPHA_OPAQUE;
break;
case RGBAColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
alpha = colormap[index].filter; // with RGBAColourMap, .filter is actually alpha
break;
case RGBFTColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
alpha = Colour::FTtoA(colormap[index].filter, colormap[index].transm);
break;
}
}
void Image::GetRGBFTIndexedValue(unsigned char index, float& red, float& green, float& blue, float& filter, float& transm) const
{
switch(colormaptype)
{
case NoColourMap:
red = 0.0f;
green = 0.0f;
blue = 0.0f;
filter = FT_OPAQUE;
transm = FT_OPAQUE;
break;
case RGBColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
filter = transm = FT_OPAQUE;
break;
case RGBAColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
Colour::AtoFT(colormap[index].filter, filter, transm); // with RGBAColourMap, .filter is actually alpha
break;
case RGBFTColourMap:
red = colormap[index].red;
green = colormap[index].green;
blue = colormap[index].blue;
filter = colormap[index].filter;
transm = colormap[index].transm;
break;
}
}
void Image::SetRGBIndexedValue(unsigned char index, float red, float green, float blue)
{
switch(colormaptype)
{
case NoColourMap:
break;
case RGBColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = 0.0f; // not used with RGBColourMap
colormap[index].transm = 0.0f; // not used with RGBColourMap
break;
case RGBAColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = ALPHA_OPAQUE; // with RGBAColourMap, .filter is actually alpha
colormap[index].transm = 0.0f; // not used with RGBAColourMap
break;
case RGBFTColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = FT_OPAQUE;
colormap[index].transm = FT_OPAQUE;
break;
}
}
void Image::SetRGBAIndexedValue(unsigned char index, float red, float green, float blue, float alpha)
{
switch(colormaptype)
{
case NoColourMap:
break;
case RGBColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = 0.0f; // not used with RGBColourMap
colormap[index].transm = 0.0f; // not used with RGBColourMap
break;
case RGBAColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = alpha; // with RGBAColourMap, .filter is actually alpha
colormap[index].transm = 0.0f; // not used with RGBAColourMap
break;
case RGBFTColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
Colour::AtoFT(alpha, colormap[index].filter, colormap[index].transm);
break;
}
}
void Image::SetRGBFTIndexedValue(unsigned char index, float red, float green, float blue, float filter, float transm)
{
switch(colormaptype)
{
case NoColourMap:
break;
case RGBColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = 0.0f; // not used with RGBColourMap
colormap[index].transm = 0.0f; // not used with RGBColourMap
break;
case RGBAColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = Colour::FTtoA(filter, transm); // note: filter is alpha in RGBA maps
colormap[index].transm = 0.0f; // not used with RGBAColourMap
break;
case RGBFTColourMap:
colormap[index].red = red;
colormap[index].green = green;
colormap[index].blue = blue;
colormap[index].filter = filter;
colormap[index].transm = transm;
break;
}
}
void Image::SetRGBFTIndexedValue(unsigned char index, const Colour& col)
{
switch(colormaptype)
{
case NoColourMap:
break;
case RGBColourMap:
colormap[index].red = col.red();
colormap[index].green = col.green();
colormap[index].blue = col.blue();
colormap[index].filter = 0.0f; // not used with RGBColourMap
colormap[index].transm = 0.0f; // not used with RGBColourMap
break;
case RGBAColourMap:
colormap[index].red = col.red();
colormap[index].green = col.green();
colormap[index].blue = col.blue();
colormap[index].filter = col.FTtoA(); // note: filter is alpha in RGBA maps
colormap[index].transm = 0.0f; // not used with RGBAColourMap
break;
case RGBFTColourMap:
colormap[index].red = col.red();
colormap[index].green = col.green();
colormap[index].blue = col.blue();
colormap[index].filter = col.filter();
colormap[index].transm = col.transm();
break;
}
}
unsigned char Image::GetIndexedValue(unsigned int, unsigned int)
{
return 0;
}
void Image::SetIndexedValue(unsigned int x, unsigned int y, unsigned char index)
{
CHECK_BOUNDS(x, y);
switch(colormaptype)
{
case NoColourMap:
SetBitValue(x,y, false);
break;
case RGBColourMap:
SetRGBValue(x, y, colormap[index].red, colormap[index].green, colormap[index].blue);
break;
case RGBAColourMap:
SetRGBAValue(x, y, colormap[index].red, colormap[index].green, colormap[index].blue, colormap[index].filter); // with RGBAColourMap, .filter is actually alpha
break;
case RGBFTColourMap:
SetRGBFTValue(x, y, colormap[index].red, colormap[index].green, colormap[index].blue, colormap[index].filter, colormap[index].transm);
break;
}
}
void Image::FillIndexedValue(unsigned char index)
{
switch(colormaptype)
{
case NoColourMap:
FillBitValue(false);
break;
case RGBColourMap:
FillRGBValue(colormap[index].red, colormap[index].green, colormap[index].blue);
break;
case RGBAColourMap:
FillRGBAValue(colormap[index].red, colormap[index].green, colormap[index].blue, colormap[index].filter); // with RGBAColourMap, .filter is actually alpha
break;
case RGBFTColourMap:
FillRGBFTValue(colormap[index].red, colormap[index].green, colormap[index].blue, colormap[index].filter, colormap[index].transm);
break;
}
}
void Image::GetRGBValue(unsigned int x, unsigned int y, RGBColour& colour, bool premul) const
{
if (premul && !premultiplied && HasTransparency())
{
// data is non-premultiplied, but caller expects premultiplied data
float alpha;
GetRGBAValue(x, y, colour.red(), colour.green(), colour.blue(), alpha);
colour *= alpha;
}
if (!premul && premultiplied && HasTransparency())
{
// data is premultiplied, but caller expects non-premultiplied data
float alpha;
GetRGBAValue(x, y, colour.red(), colour.green(), colour.blue(), alpha);
if (alpha != 0.0) // TODO maybe use some epsilon?!
colour /= alpha;
}
else
{
GetRGBValue(x, y, colour.red(), colour.green(), colour.blue());
}
}
void Image::GetRGBFTValue(unsigned int x, unsigned int y, Colour& colour, bool premul) const
{
GetRGBFTValue(x, y, colour.red(), colour.green(), colour.blue(), colour.filter(), colour.transm());
if (premul && !premultiplied && HasTransparency())
{
// data is non-premultiplied, but caller expects premultiplied data
float alpha = colour.FTtoA();
colour.red() *= alpha;
colour.green() *= alpha;
colour.blue() *= alpha;
}
else if (!premul && premultiplied && HasTransparency())
{
// data is premultiplied, but caller expects non-premultiplied data
float alpha = colour.FTtoA();
if (alpha != 0.0) // TODO maybe use some epsilon?!
{
colour.red() /= alpha;
colour.green() /= alpha;
colour.blue() /= alpha;
}
}
}
unsigned int Image::GetColourMapSize() const
{
return colormap.size();
}
void Image::GetColourMap(vector<RGBMapEntry>& m) const
{
m.resize(colormap.size());
for(size_t i = 0; i < colormap.size(); i++)
GetRGBIndexedValue((unsigned char)(i), m[i].red, m[i].green, m[i].blue);
}
void Image::GetColourMap(vector<RGBAMapEntry>& m) const
{
m.resize(colormap.size());
for(size_t i = 0; i < colormap.size(); i++)
GetRGBAIndexedValue((unsigned char)(i), m[i].red, m[i].green, m[i].blue, m[i].alpha);
}
void Image::GetColourMap(vector<RGBFTMapEntry>& m) const
{
m.resize(colormap.size());
for(size_t i = 0; i < colormap.size(); i++)
GetRGBFTIndexedValue((unsigned char)(i), m[i].red, m[i].green, m[i].blue, m[i].filter, m[i].transm);
}
void Image::SetColourMap(const vector<RGBMapEntry>& m)
{
colormap.resize(max(m.size(), sizeof(unsigned char) * 256));
colormaptype = RGBColourMap;
colormap.assign(m.begin(), m.end());
}
void Image::SetColourMap(const vector<RGBAMapEntry>& m)
{
colormap.resize(max(m.size(), sizeof(unsigned char) * 256));
colormaptype = RGBAColourMap;
colormap.assign(m.begin(), m.end());
}
void Image::SetColourMap(const vector<RGBFTMapEntry>& m)
{
colormap.resize(max(m.size(), sizeof(unsigned char) * 256));
colormaptype = RGBFTColourMap;
colormap.assign(m.begin(), m.end());
}
}