diff --git a/rtengine/image16.cc b/rtengine/image16.cc index 68dd4bb40..65d9fc5a7 100644 --- a/rtengine/image16.cc +++ b/rtengine/image16.cc @@ -60,7 +60,7 @@ Image16::~Image16 () { } -void Image16::getScanline (int row, unsigned char* buffer, int bps) +void Image16::getScanline (int row, unsigned char* buffer, int bps, bool isFloat) { if (data == nullptr) { @@ -74,20 +74,13 @@ void Image16::getScanline (int row, unsigned char* buffer, int bps) } } -/* - * void Image16::setScanline (int row, unsigned char* buffer, int bps, int minValue[3], int maxValue[3]); - * has not been implemented yet, because as of now, this method is called for IIOSF_FLOATxx sample format only - */ -void Image16::setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples, float *minValue, float *maxValue) +void Image16::setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples) { if (data == nullptr) { return; } - // For optimization purpose, we're assuming that this class never has to provide min/max bounds - assert(!minValue); - switch (sampleFormat) { case (IIOSF_UNSIGNED_CHAR): { int ix = 0; diff --git a/rtengine/image16.h b/rtengine/image16.h index 33e74f146..9764785a4 100644 --- a/rtengine/image16.h +++ b/rtengine/image16.h @@ -55,8 +55,8 @@ public: { return 8 * sizeof(unsigned short); } - virtual void getScanline (int row, unsigned char* buffer, int bps); - virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples, float *minValue = nullptr, float *maxValue = nullptr); + virtual void getScanline (int row, unsigned char* buffer, int bps, bool isFloat = false); + virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples); // functions inherited from IImage16: virtual MyMutex& getMutex () diff --git a/rtengine/image8.cc b/rtengine/image8.cc index 69066f2dd..ab7393100 100644 --- a/rtengine/image8.cc +++ b/rtengine/image8.cc @@ -37,7 +37,7 @@ Image8::~Image8 () { } -void Image8::getScanline (int row, unsigned char* buffer, int bps) +void Image8::getScanline (int row, unsigned char* buffer, int bps, bool isFloat) { if (data == nullptr) { @@ -55,16 +55,13 @@ void Image8::getScanline (int row, unsigned char* buffer, int bps) } } -void Image8::setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples, float *minValue, float *maxValue) +void Image8::setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples) { if (data == nullptr) { return; } - // For optimization purpose, we're assuming that this class never have to provide min/max bound - assert(!minValue); - switch (sampleFormat) { case (IIOSF_UNSIGNED_CHAR): if(numSamples == 1) { diff --git a/rtengine/image8.h b/rtengine/image8.h index 6444499d9..59d13c298 100644 --- a/rtengine/image8.h +++ b/rtengine/image8.h @@ -50,8 +50,8 @@ public: { return 8 * sizeof(unsigned char); } - virtual void getScanline (int row, unsigned char* buffer, int bps); - virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples, float *minValue = nullptr, float *maxValue = nullptr); + virtual void getScanline (int row, unsigned char* buffer, int bps, bool isFloat = false); + virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples); // functions inherited from IImage*: virtual MyMutex& getMutex () diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index e79678194..9ef6cc70b 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -44,7 +44,7 @@ Imagefloat::~Imagefloat () } // Call this method to handle floating points input values of different size -void Imagefloat::setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples, float *minValue, float *maxValue) +void Imagefloat::setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples) { if (data == nullptr) { @@ -55,45 +55,27 @@ void Imagefloat::setScanline (int row, unsigned char* buffer, int bps, unsigned // DNG_HalfToFloat and DNG_FP24ToFloat from dcraw.cc can be used to manually convert // from 16 and 24 bits to 32 bits float respectively switch (sampleFormat) { - case (IIOSF_FLOAT16): - case (IIOSF_FLOAT24): + case (IIOSF_FLOAT16): { + int ix = 0; + uint16_t* sbuffer = (uint16_t*) buffer; + + for (int i = 0; i < width; i++) { + r(row, i) = 65535.f * DNG_HalfToFloat(sbuffer[ix++]); + g(row, i) = 65535.f * DNG_HalfToFloat(sbuffer[ix++]); + b(row, i) = 65535.f * DNG_HalfToFloat(sbuffer[ix++]); + } + + break; + } + //case (IIOSF_FLOAT24): case (IIOSF_FLOAT32): { int ix = 0; float* sbuffer = (float*) buffer; for (int i = 0; i < width; i++) { - r(row, i) = 65535.f * sbuffer[ix]; - - if (minValue) { - if (sbuffer[ix] < minValue[0]) { - minValue[0] = sbuffer[ix]; - } else if (sbuffer[ix] > maxValue[0]) { - maxValue[0] = sbuffer[ix]; - } - } - ++ix; - - g(row, i) = 65535.f * sbuffer[ix]; - - if (minValue) { - if (sbuffer[ix] < minValue[1]) { - minValue[1] = sbuffer[ix]; - } else if (sbuffer[ix] > maxValue[1]) { - maxValue[1] = sbuffer[ix]; - } - } - ++ix; - - b(row, i) = 65535.f * sbuffer[ix]; - - if (minValue) { - if (sbuffer[ix] < minValue[2]) { - minValue[2] = sbuffer[ix]; - } else if (sbuffer[ix] > maxValue[2]) { - maxValue[2] = sbuffer[ix]; - } - } - ++ix; + r(row, i) = 65535.f * sbuffer[ix++]; + g(row, i) = 65535.f * sbuffer[ix++]; + b(row, i) = 65535.f * sbuffer[ix++]; } break; @@ -112,34 +94,8 @@ void Imagefloat::setScanline (int row, unsigned char* buffer, int bps, unsigned // TODO: we may have to handle other color space than sRGB! Color::xyz2srgb(xyzvalues[0], xyzvalues[1], xyzvalues[2], rgbvalues[0], rgbvalues[1], rgbvalues[2]); r(row, i) = rgbvalues[0]; - - if (minValue) { - if (rgbvalues[0] < minValue[0]) { - minValue[0] = rgbvalues[0]; - } else if (rgbvalues[0] > maxValue[0]) { - maxValue[0] = rgbvalues[0]; - } - } - g(row, i) = rgbvalues[1]; - - if (minValue) { - if (rgbvalues[1] < minValue[1]) { - minValue[1] = rgbvalues[1]; - } else if (rgbvalues[1] > maxValue[1]) { - maxValue[1] = rgbvalues[1]; - } - } - b(row, i) = rgbvalues[2]; - - if (minValue) { - if (rgbvalues[2] < minValue[2]) { - minValue[2] = rgbvalues[2]; - } else if (rgbvalues[2] > maxValue[2]) { - maxValue[2] = rgbvalues[2]; - } - } } break; @@ -154,22 +110,32 @@ void Imagefloat::setScanline (int row, unsigned char* buffer, int bps, unsigned namespace rtengine { extern void filmlike_clip(float *r, float *g, float *b); } -void Imagefloat::getScanline (int row, unsigned char* buffer, int bps) +void Imagefloat::getScanline (int row, unsigned char* buffer, int bps, bool isFloat) { if (data == nullptr) { return; } - if (bps == 32) { - int ix = 0; - float* sbuffer = (float*) buffer; - - // agriggio -- assume the image is normalized to [0, 65535] - for (int i = 0; i < width; i++) { - sbuffer[ix++] = r(row, i) / 65535.f; - sbuffer[ix++] = g(row, i) / 65535.f; - sbuffer[ix++] = b(row, i) / 65535.f; + if (isFloat) { + if (bps == 32) { + int ix = 0; + float* sbuffer = (float*) buffer; + // agriggio -- assume the image is normalized to [0, 65535] + for (int i = 0; i < width; i++) { + sbuffer[ix++] = r(row, i) / 65535.f; + sbuffer[ix++] = g(row, i) / 65535.f; + sbuffer[ix++] = b(row, i) / 65535.f; + } + } else if (bps == 16) { + int ix = 0; + uint16_t* sbuffer = (uint16_t*) buffer; + // agriggio -- assume the image is normalized to [0, 65535] + for (int i = 0; i < width; i++) { + sbuffer[ix++] = DNG_FloatToHalf(r(row, i) / 65535.f); + sbuffer[ix++] = DNG_FloatToHalf(g(row, i) / 65535.f); + sbuffer[ix++] = DNG_FloatToHalf(b(row, i) / 65535.f); + } } } else { unsigned short *sbuffer = (unsigned short *)buffer; diff --git a/rtengine/imagefloat.h b/rtengine/imagefloat.h index abb74bc8e..a705a45e7 100644 --- a/rtengine/imagefloat.h +++ b/rtengine/imagefloat.h @@ -59,8 +59,8 @@ public: { return 8 * sizeof(float); } - virtual void getScanline (int row, unsigned char* buffer, int bps); - virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples, float *minValue = nullptr, float *maxValue = nullptr); + virtual void getScanline (int row, unsigned char* buffer, int bps, bool isFloat = false); + virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples); // functions inherited from IImagefloat: virtual MyMutex& getMutex () @@ -100,6 +100,120 @@ public: delete this; } + inline uint16_t DNG_FloatToHalf(float f) + { + union { + float f; + uint32_t i; + } tmp; + + tmp.f = f; + int32_t sign = (tmp.i >> 16) & 0x00008000; + int32_t exponent = ((tmp.i >> 23) & 0x000000ff) - (127 - 15); + int32_t mantissa = tmp.i & 0x007fffff; + if (exponent <= 0) { + if (exponent < -10) { + return (uint16_t)sign; + } + mantissa = (mantissa | 0x00800000) >> (1 - exponent); + if (mantissa & 0x00001000) + mantissa += 0x00002000; + return (uint16_t)(sign | (mantissa >> 13)); + } else if (exponent == 0xff - (127 - 15)) { + if (mantissa == 0) { + return (uint16_t)(sign | 0x7c00); + } else { + return (uint16_t)(sign | 0x7c00 | (mantissa >> 13)); + } + } + if (mantissa & 0x00001000) { + mantissa += 0x00002000; + if (mantissa & 0x00800000) { + mantissa = 0; // overflow in significand, + exponent += 1; // adjust exponent + } + } + if (exponent > 30) { + return (uint16_t)(sign | 0x7c00); // infinity with the same sign as f. + } + return (uint16_t)(sign | (exponent << 10) | (mantissa >> 13)); + } + + // From DNG SDK dng_utils.h + inline float DNG_HalfToFloat(uint16_t halfValue) + { + union { + float f; + uint32_t i; + } tmp; + + int32_t sign = (halfValue >> 15) & 0x00000001; + int32_t exponent = (halfValue >> 10) & 0x0000001f; + int32_t mantissa = halfValue & 0x000003ff; + if (exponent == 0) { + if (mantissa == 0) { + // Plus or minus zero + return (uint32_t) (sign << 31); + } else { + // Denormalized number -- renormalize it + while (!(mantissa & 0x00000400)) { + mantissa <<= 1; + exponent -= 1; + } + exponent += 1; + mantissa &= ~0x00000400; + } + } else if (exponent == 31) { + if (mantissa == 0) { + // Positive or negative infinity, convert to maximum (16 bit) values. + return (uint32_t) ((sign << 31) | ((0x1eL + 127 - 15) << 23) | (0x3ffL << 13)); + } else { + // Nan -- Just set to zero. + return 0; + } + } + // Normalized number + exponent += (127 - 15); + mantissa <<= 13; + // Assemble sign, exponent and mantissa. + tmp.i = (uint32_t) ((sign << 31) | (exponent << 23) | mantissa); + return tmp.f; + } + + inline uint32_t DNG_FP24ToFloat(const uint8_t * input) + { + int32_t sign = (input [0] >> 7) & 0x01; + int32_t exponent = (input [0] ) & 0x7F; + int32_t mantissa = (((int32_t) input [1]) << 8) | input[2]; + if (exponent == 0) { + if (mantissa == 0) { + // Plus or minus zero + return (uint32_t) (sign << 31); + } else { + // Denormalized number -- renormalize it + while (!(mantissa & 0x00010000)) { + mantissa <<= 1; + exponent -= 1; + } + exponent += 1; + mantissa &= ~0x00010000; + } + } else if (exponent == 127) { + if (mantissa == 0) { + // Positive or negative infinity, convert to maximum (24 bit) values. + return (uint32_t) ((sign << 31) | ((0x7eL + 128 - 64) << 23) | (0xffffL << 7)); + } else { + // Nan -- Just set to zero. + return 0; + } + } + // Normalized number + exponent += (128 - 64); + mantissa <<= 7; + // Assemble sign, exponent and mantissa. + return (uint32_t) ((sign << 31) | (exponent << 23) | mantissa); + } + virtual void normalizeFloat(float srcMinVal, float srcMaxVal); void normalizeFloatTo1(); void normalizeFloatTo65535(); diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index d3e815027..8bfe81504 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -1468,9 +1468,9 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, float isFloat, bool uncompr } for (int row = 0; row < height; row++) { - getScanline (row, linebuffer, bps); + getScanline (row, linebuffer, bps, isFloat); - /*if (bps == 16) { + if (bps == 16) { if(needsReverse && !uncompressed) { for(int i = 0; i < lineWidth; i += 2) { char temp = linebuffer[i]; @@ -1478,7 +1478,7 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, float isFloat, bool uncompr linebuffer[i + 1] = temp; } } - } else */ if (bps == 32) { + } else if (bps == 32) { if(needsReverse && !uncompressed) { for(int i = 0; i < lineWidth; i += 4) { char temp = linebuffer[i]; diff --git a/rtengine/imageio.h b/rtengine/imageio.h index 135927bbf..cbf245291 100644 --- a/rtengine/imageio.h +++ b/rtengine/imageio.h @@ -112,8 +112,8 @@ public: } virtual int getBPS () = 0; - virtual void getScanline (int row, unsigned char* buffer, int bps) {} - virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples = 3, float minValue[3] = nullptr, float maxValue[3] = nullptr) {} + virtual void getScanline (int row, unsigned char* buffer, int bps, bool isFloat = false) {} + virtual void setScanline (int row, unsigned char* buffer, int bps, unsigned int numSamples = 3) {} virtual bool readImage (Glib::ustring &fname, FILE *fh) {