diff options
Diffstat (limited to 'dep/src/g3dlite/GImage.cpp')
-rw-r--r-- | dep/src/g3dlite/GImage.cpp | 1166 |
1 files changed, 1166 insertions, 0 deletions
diff --git a/dep/src/g3dlite/GImage.cpp b/dep/src/g3dlite/GImage.cpp new file mode 100644 index 00000000000..9527e96cf67 --- /dev/null +++ b/dep/src/g3dlite/GImage.cpp @@ -0,0 +1,1166 @@ +/** + \file GImage.cpp + \author Morgan McGuire, http://graphics.cs.williams.edu + Copyright 2002-2010, Morgan McGuire + + \created 2002-05-27 + \edited 2010-01-04 + */ +#include "G3D/platform.h" +#include "G3D/GImage.h" +#include "G3D/debug.h" +#include "G3D/stringutils.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Log.h" +#include "G3D/fileutils.h" + +#ifdef G3D_LINUX +# include <png.h> +#else +# include "png.h" +#endif + +#include <sys/stat.h> +#include <assert.h> +#include <sys/types.h> + +////////////////////////////////////////////////////////////////////////////////////////////// + +namespace G3D { + +void GImage::LtoRGBA( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int v = in[i]; + int i4 = i * 4; + + out[i4 + 0] = v; + out[i4 + 1] = v; + out[i4 + 2] = v; + out[i4 + 3] = 255; + } +} + + +void GImage::LtoRGB( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int v = in[i]; + int i3 = i * 3; + + out[i3 + 0] = v; + out[i3 + 1] = v; + out[i3 + 2] = v; + } +} + + +void GImage::RGBtoRGBA( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 0] = in[i3 + 0]; + out[i4 + 1] = in[i3 + 1]; + out[i4 + 2] = in[i3 + 2]; + out[i4 + 3] = 255; + } +} + + +void GImage::RGBAtoRGB( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i3 + 0] = in[i4 + 0]; + out[i3 + 1] = in[i4 + 1]; + out[i3 + 2] = in[i4 + 2]; + } +} + + +void GImage::RGBtoBGRA( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 2] = in[i3 + 0]; + out[i4 + 1] = in[i3 + 1]; + out[i4 + 0] = in[i3 + 2]; + out[i4 + 3] = 255; + } +} + + + +void GImage::RGBtoBGR( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + + int r = in[i3 + 0]; + int g = in[i3 + 1]; + int b = in[i3 + 2]; + + out[i3 + 2] = r; + out[i3 + 1] = g; + out[i3 + 0] = b; + } +} + + +void GImage::RGBxRGBtoRGBA( + const uint8* colorRGB, + const uint8* alphaRGB, + uint8* out, + int numPixels) { + + for (int i = numPixels - 1; i >= 0; --i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 0] = colorRGB[i3 + 0]; + out[i4 + 1] = colorRGB[i3 + 1]; + out[i4 + 2] = colorRGB[i3 + 2]; + out[i4 + 3] = alphaRGB[i3 + 0]; + } +} + + +void GImage::RGBtoARGB( + const uint8* in, + uint8* out, + int numPixels) { + + for (int i = 0; i < numPixels; ++i) { + int i3 = i * 3; + int i4 = i3 + i; + + out[i4 + 0] = 255; + out[i4 + 1] = in[i3 + 0]; + out[i4 + 2] = in[i3 + 1]; + out[i4 + 3] = in[i3 + 2]; + } +} + + +void GImage::flipRGBVertical( + const uint8* in, + uint8* out, + int width, + int height) { + + + // Allocate a temp row so the operation + // is still safe if in == out + uint8* temp = (uint8*)System::malloc(width * 3); + alwaysAssertM(temp != NULL, "Out of memory"); + + int oneRow = width * 3; + int N = height / 2; + + // if height is an odd value, don't swap odd middle row + for (int i = 0; i < N; ++i) { + int topOff = i * oneRow; + int botOff = (height - i - 1) * oneRow; + System::memcpy(temp, in + topOff, oneRow); + System::memcpy(out + topOff, in + botOff, oneRow); + System::memcpy(out + botOff, temp, oneRow); + } + + System::free(temp); +} + + +void GImage::flipRGBAVertical( + const uint8* in, + uint8* out, + int width, + int height) { + + + // Allocate a temp row so the operation + // is still safe if in == out + uint8* temp = (uint8*)System::malloc(width * 4); + alwaysAssertM(temp != NULL, "Out of memory"); + + int oneRow = width * 4; + + // if height is an odd value, don't swap odd middle row + for (int i = 0; i < height / 2; ++i) { + int topOff = i * oneRow; + int botOff = (height - i - 1) * oneRow; + System::memcpy(temp, in + topOff, oneRow); + System::memcpy(out + topOff, in + botOff, oneRow); + System::memcpy(out + botOff, temp, oneRow); + } + + System::free(temp); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void GImage::decode( + BinaryInput& input, + Format format) { + + switch (format) { + case PPM_ASCII: + decodePPMASCII(input); + break; + + case PPM_BINARY: + decodePPM(input); + break; + + case PNG: + decodePNG(input); + break; + + case JPEG: + decodeJPEG(input); + break; + + case TGA: + decodeTGA(input); + break; + + case BMP: + decodeBMP(input); + break; + + case ICO: + decodeICO(input); + break; + + case PCX: + decodePCX(input); + break; + + default: + debugAssert(false); + } + + debugAssert(m_width >= 0); + debugAssert(m_height >= 0); + debugAssert(m_channels == 1 || m_channels == 3 || m_channels == 4); + debugAssert(m_byte != NULL); +} + + +void GImage::decodePCX( + BinaryInput& input) { + + uint8 manufacturer = input.readUInt8(); + uint8 version = input.readUInt8(); + uint8 encoding = input.readUInt8(); + uint8 bitsPerPixel = input.readUInt8(); + + uint16 xmin = input.readUInt16(); + uint16 ymin = input.readUInt16(); + uint16 xmax = input.readUInt16(); + uint16 ymax = input.readUInt16(); + + uint16 horizDPI = input.readUInt16(); + uint16 vertDPI = input.readUInt16(); + + Color3uint8 colorMap[16]; + input.readBytes(colorMap, 48); + + input.skip(1); + + uint8 planes = input.readUInt8(); + uint16 bytesPerLine = input.readUInt16(); + uint16 paletteType = input.readUInt16(); + input.skip(4 + 54); + + (void)bytesPerLine; + + m_width = xmax - xmin + 1; + m_height = ymax - ymin + 1; + m_channels = 3; + + if ((manufacturer != 0x0A) || (encoding != 0x01)) { + throw GImage::Error("PCX file is corrupted", input.getFilename()); + } + + (void)version; + (void)vertDPI; + (void)horizDPI; + + if ((bitsPerPixel != 8) || ((planes != 1) && (planes != 3))) { + throw GImage::Error("Only 8-bit paletted and 24-bit PCX files supported.", input.getFilename()); + } + + // Prepare the pointer object for the pixel data + m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3); + + if ((paletteType == 1) && (planes == 3)) { + + Color3uint8* pixel = pixel3(); + + // Iterate over each scan line + for (int row = 0; row < m_height; ++row) { + // Read each scan line once per plane + for (int plane = 0; plane < planes; ++plane) { + int p = row * m_width; + int p1 = p + m_width; + while (p < p1) { + uint8 value = input.readUInt8(); + int length = 1; + + if (value >= 192) { + // This is the length, not the value. Mask off + // the two high bits and read the true index. + length = value & 0x3F; + value = input.readUInt8(); + } + + // Set the whole run + for (int i = length - 1; i >= 0; --i, ++p) { + debugAssert(p < m_width * m_height); + pixel[p][plane] = value; + } + } + } + } + + } else if (planes == 1) { + + Color3uint8 palette[256]; + + int imageBeginning = input.getPosition(); + int paletteBeginning = input.getLength() - 769; + + input.setPosition(paletteBeginning); + + uint8 dummy = input.readUInt8(); + + if (dummy != 12) { + Log::common()->println("\n*********************"); + Log::common()->printf("Warning: Corrupted PCX file (palette marker byte was missing) \"%s\"\nLoading anyway\n\n", input.getFilename().c_str()); + } + + input.readBytes(palette, sizeof(palette)); + input.setPosition(imageBeginning); + + Color3uint8* pixel = pixel3(); + + // The palette indices are run length encoded. + int p = 0; + while (p < m_width * m_height) { + uint8 index = input.readUInt8(); + uint8 length = 1; + + if (index >= 192) { + // This is the length, not the index. Mask off + // the two high bits and read the true index. + length = index & 0x3F; + index = input.readUInt8(); + } + + Color3uint8 color = palette[index]; + + // Set the whole run + for (int i = length - 1; i >= 0; --i, ++p) { + if (p > m_width * m_height) { + break; + } + pixel[p] = color; + } + + } + + } else { + throw GImage::Error("Unsupported PCX file type.", input.getFilename()); + } +} + + +GImage::Format GImage::resolveFormat(const std::string& filename) { + BinaryInput b(filename, G3D_LITTLE_ENDIAN); + if (b.size() <= 0) { + throw Error("File not found.", filename); + } + + return resolveFormat(filename, b.getCArray(), b.size(), AUTODETECT); +} + + +GImage::Format GImage::resolveFormat( + const std::string& filename, + const uint8* data, + int dataLen, + Format maybeFormat) { + + // Return the provided format if it is specified. + if (maybeFormat != AUTODETECT) { + return maybeFormat; + } + + std::string extension = toUpper(filenameExt(filename)); + + if ((extension == "PPM") || (extension == "PGM") || (extension == "PBM")) { + // There are two PPM formats (binary and ASCII); we handle them differently + if (dataLen > 3) { + if (!memcmp(data, "P6", 2) || !memcmp(data, "P5", 2)) { + return PPM_BINARY; + } else { + return PPM_ASCII; + } + } + } + + Format tmp = stringToFormat(extension); + if ((tmp != AUTODETECT) && (tmp != UNKNOWN)) { + return tmp; + } + + // Try and autodetect from the file itself by looking at the first + // character. + + // We can't look at the character if it is null. + debugAssert(data != NULL); + + if ((dataLen > 3) && (! memcmp(data, "P3", 2) || (! memcmp(data, "P2", 2)) || (! memcmp(data, "P1", 2)))) { + return PPM_ASCII; + } + + if ((dataLen > 3) && (!memcmp(data, "P6", 2) ||!memcmp(data, "P5", 2))) { + return PPM_BINARY; + } + + if (dataLen > 8) { + if (!png_sig_cmp((png_bytep)data, 0, 8)) { + return PNG; + } + } + + if ((dataLen > 0) && (data[0] == 'B')) { + return BMP; + } + + if (dataLen > 10) { + if ((dataLen > 11) && (data[0] == 0xFF) && + (memcmp(&data[6], "JFIF", 4) == 0)) { + return JPEG; + } + } + + if (dataLen > 40) { + if (memcmp(&data[dataLen - 18], "TRUEVISION-XFILE", 16) == 0) { + return TGA; + } + } + + if ((dataLen > 4) && (data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 1)) { + return ICO; + } + + if ((dataLen > 0) && (data[0] == 10)) { + return PCX; + } + + return UNKNOWN; +} + + +GImage::GImage( + const std::string& filename, + Format format, + const MemoryManager::Ref& m) : + m_memMan(m), + m_byte(NULL), + m_channels(0), + m_width(0), + m_height(0) { + + load(filename, format); +} + + +void GImage::load( + const std::string& filename, + Format format) { + + clear(); + + try { + BinaryInput b(filename, G3D_LITTLE_ENDIAN); + if (b.size() <= 0) { + throw Error("File not found.", filename); + } + + alwaysAssertM(this != NULL, "Corrupt GImage"); + decode(b, resolveFormat(filename, b.getCArray(), b.size(), format)); + } catch (const std::string& error) { + throw Error(error, filename); + } +} + + +GImage::GImage( + const uint8* data, + int length, + Format format, + const MemoryManager::Ref& m) : + m_memMan(m), + m_byte(NULL), + m_channels(0), + m_width(0), + m_height(0) { + + BinaryInput b(data, length, G3D_LITTLE_ENDIAN); + // It is safe to cast away the const because we + // know we don't corrupt the data. + + decode(b, resolveFormat("", data, length, format)); +} + + +GImage::GImage( + int width, + int height, + int channels, + const MemoryManager::Ref& mem) : + m_memMan(mem), + m_byte(0), + m_channels(0), + m_width(0), + m_height(0) { + + resize(width, height, channels); +} + + +void GImage::resize( + int width, + int height, + int channels, + bool zero) { + + debugAssert(width >= 0); + debugAssert(height >= 0); + debugAssert(channels >= 1); + + clear(); + + m_width = width; + m_height = height; + m_channels = channels; + size_t sz = width * height * channels; + + if (sz > 0) { + m_byte = (uint8*)m_memMan->alloc(sz); + if (zero) { + System::memset(m_byte, 0, sz); + } + debugAssert(isValidHeapPointer(m_byte)); + } +} + + +void GImage::_copy( + const GImage& other) { + + clear(); + + m_width = other.m_width; + m_height = other.m_height; + m_channels = other.m_channels; + int s = m_width * m_height * m_channels * sizeof(uint8); + m_byte = (uint8*)m_memMan->alloc(s); + debugAssert(isValidHeapPointer(m_byte)); + memcpy(m_byte, other.m_byte, s); +} + + +void GImage::flipHorizontal() { + uint8 temp[4]; + int rowBytes = m_width * m_channels; + for (int y = 0; y < m_height; ++y) { + uint8* row = m_byte + y * rowBytes; + for (int x = 0; x < m_width / 2; ++x) { + System::memcpy(temp, row + x * m_channels, m_channels); + System::memcpy(row + x * m_channels, row + (m_width - x - 1) * m_channels, m_channels); + System::memcpy(row + (m_width - x - 1) * m_channels, temp, m_channels); + } + } +} + + +void GImage::flipVertical() { + uint8* old = m_byte; + m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels); + + // We could do this with only a single-row temp buffer, but then + // we'd have to copy twice as much data. + int rowBytes = m_width * m_channels; + for (int y = 0; y < m_height; ++y) { + System::memcpy(m_byte + y * rowBytes, old + (m_height - y - 1) * rowBytes, rowBytes); + } + + m_memMan->free(old); +} + + +void GImage::rotate90CW(int numTimes) { + + uint8* old = NULL; + numTimes = iWrap(numTimes, 4); + if (numTimes > 0) { + (uint8*)m_memMan->alloc(m_width * m_height * m_channels); + } + for (int j = 0; j < numTimes; ++j) { + { + uint8* temp = old; + uint8* old = m_byte; + m_byte = temp; + } + + { + int temp = m_width; + m_width = m_height; + m_height = temp; + } + + int rowBytes = m_width * m_channels; + for (int y = 0; y < m_height; ++y) { + for (int x = 0; x < m_width; ++x) { + uint8* dst = m_byte + x + y * rowBytes; + uint8* src = old + y + (m_height - x - 1) * rowBytes; + System::memcpy(dst, src, m_channels); + } + } + } + m_memMan->free(old); +} + + + +GImage::GImage( + const GImage& other, + const MemoryManager::Ref& m) : m_memMan(m), m_byte(NULL) { + + _copy(other); +} + + +GImage::~GImage() { + clear(); +} + + +void GImage::clear() { + m_width = 0; + m_height = 0; + m_memMan->free(m_byte); + m_byte = NULL; +} + + +GImage& GImage::operator=(const GImage& other) { + _copy(other); + return *this; +} + + +bool GImage::copySubImage( + GImage & dest, const GImage & src, + int srcX, int srcY, int srcWidth, int srcHeight) { + if ((src.m_width < srcX + srcWidth) || + (src.m_height < srcY + srcHeight) || + (srcY < 0) || + (srcX < 0)) { + + return false; + } + + dest.resize(srcWidth, srcHeight, src.m_channels); + + bool ret; + ret = pasteSubImage(dest, src, 0, 0, srcX, srcY, srcWidth, srcHeight); + debugAssert(ret); + + return true; +} + + +bool GImage::pasteSubImage( + GImage & dest, const GImage & src, + int destX, int destY, + int srcX, int srcY, int srcWidth, int srcHeight) { + + if ((src.m_width < srcX + srcWidth) || + (src.m_height < srcY + srcHeight) || + (dest.m_width < destX + srcWidth) || + (dest.m_height < destY + srcHeight) || + (srcY < 0) || + (srcX < 0) || + (destY < 0) || + (destX < 0) || + (src.channels() != dest.channels())) { + + return false; + } + + for (int i = 0; i < srcHeight; i++) { + const uint8* srcRow = src.byte() + + ((i + srcY) * src.m_width + srcX) * src.channels(); + uint8* destRow = dest.byte() + + ((i + destY) * dest.width() + destX) * dest.channels(); + memcpy(destRow, srcRow, srcWidth * src.m_channels); + } + + return true; +} + + +bool GImage::supportedFormat( + const std::string& format) { + + return (stringToFormat(format) != UNKNOWN); +} + + +GImage::Format GImage::stringToFormat( + const std::string& format) { + + std::string extension = toUpper(format); + + if ((extension == "JPG") || (extension == "JPEG")) { + return JPEG; + } else if (extension == "TGA") { + return TGA; + } else if (extension == "BMP") { + return BMP; + } else if (extension == "PCX") { + return PCX; + } else if (extension == "ICO") { + return ICO; + } else if (extension == "PNG") { + return PNG; + } else { + return UNKNOWN; + } + // Don't put PPM here, since it has two versions +} + + +void GImage::save( + const std::string& filename, + Format format) const { + + BinaryOutput b(filename, G3D_LITTLE_ENDIAN); + encode(resolveFormat(filename, NULL, 0, format), b); + b.commit(false); +} + + +void GImage::encode( + Format format, + uint8*& outData, + int& outLength) const { + + BinaryOutput out; + + encode(format, out); + + outData = (uint8*)System::malloc(out.size()); + debugAssert(outData); + outLength = out.size(); + + out.commit(outData); +} + + +void GImage::encode( + Format format, + BinaryOutput& out) const { + + switch (format) { + case PPM_ASCII: + encodePPMASCII(out); + break; + + case PPM_BINARY: + encodePPM(out); + break; + + case PNG: + encodePNG(out); + break; + + case JPEG: + encodeJPEG(out); + break; + + case BMP: + encodeBMP(out); + break; + + case TGA: + encodeTGA(out); + break; + + default: + debugAssert(false); + } +} + + +void GImage::insertRedAsAlpha(const GImage& alpha, GImage& output) const { + debugAssert(alpha.width() == width()); + debugAssert(alpha.height() == height()); + + // make sure output GImage is valid + if (output.width() != width() || output.height() != height() || output.channels() != 4) { + output.resize(width(), height(), 4); + } + + int N = m_width * m_height; + for (int i = 0; i < N; ++i) { + output.byte()[i * 4 + 0] = byte()[i * m_channels + 0]; + output.byte()[i * 4 + 1] = byte()[i * m_channels + 1]; + output.byte()[i * 4 + 2] = byte()[i * m_channels + 2]; + output.byte()[i * 4 + 3] = alpha.byte()[i * alpha.m_channels]; + } +} + + +void GImage::stripAlpha(GImage& output) const { + + if (output.m_width != m_width || output.m_height != m_height || output.m_channels != 3) { + output.resize(m_width, m_height, 3); + } + + int N = m_width * m_height; + for (int i = 0; i < N; ++i) { + output.byte()[i * 3 + 0] = byte()[i * m_channels + 0]; + output.byte()[i * 3 + 1] = byte()[i * m_channels + 1]; + output.byte()[i * 3 + 2] = byte()[i * m_channels + 2]; + } +} + + +int GImage::sizeInMemory() const { + return sizeof(GImage) + m_width * m_height * m_channels; +} + + +void GImage::computeNormalMap( + const GImage& bump, + GImage& normal, + const BumpMapPreprocess& preprocess) { + computeNormalMap(bump.m_width, bump.m_height, bump.m_channels, + bump.byte(), normal, preprocess); +} + +void GImage::computeNormalMap( + int width, + int height, + int channels, + const uint8* src, + GImage& normal, + const BumpMapPreprocess& preprocess) { + + float whiteHeightInPixels = preprocess.zExtentPixels; + bool lowPassBump = preprocess.lowPassFilter; + bool scaleHeightByNz = preprocess.scaleZByNz; + + if (whiteHeightInPixels < 0.0f) { + // Default setting scales so that a gradient ramp + // over the whole image becomes a 45-degree angle + + // Account for potentially non-square aspect ratios + whiteHeightInPixels = max(width, height) * -whiteHeightInPixels; + } + + debugAssert(whiteHeightInPixels >= 0); + + const int w = width; + const int h = height; + const int stride = channels; + + normal.resize(w, h, 4); + + const uint8* const B = src; + Color4uint8* const N = normal.pixel4(); + + // 1/s for the scale factor that each ELEVATION should be multiplied by. + // We avoid actually multiplying by this and instead just divide it out of z. + float elevationInvScale = 255.0f / whiteHeightInPixels; + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + // Index into normal map pixel + int i = x + y * w; + + // Index into bump map *byte* + int j = stride * i; + + Vector3 delta; + + // Get a value from B (with wrapping lookup) relative to (x, y) + // and divide by 255 + #define ELEVATION(DX, DY) ((int)B[(((DX + x + w) % w) + \ + ((DY + y + h) % h) * w) * stride]) + + + // Sobel filter to compute the normal. + // + // Y Filter (X filter is the transpose) + // [ -1 -2 -1 ] + // [ 0 0 0 ] + // [ 1 2 1 ] + + // Write the Y value directly into the x-component so we don't have + // to explicitly compute a cross product at the end. Does not + // go out of bounds because the above is computed mod (width, height) + delta.y = -( ELEVATION(-1, -1) * 1 + ELEVATION( 0, -1) * 2 + ELEVATION( 1, -1) * 1 + + -ELEVATION(-1, 1) * 1 + -ELEVATION( 0, 1) * 2 + -ELEVATION( 1, 1) * 1); + + delta.x = -(-ELEVATION(-1, -1) * 1 + ELEVATION( 1, -1) * 1 + + -ELEVATION(-1, 0) * 2 + ELEVATION( 1, 0) * 2 + + -ELEVATION(-1, 1) * 1 + ELEVATION( 1, 1) * 1); + + // The scale of each filter row is 4, the filter width is two pixels, + // and the "normal" range is 0-255. + delta.z = 4 * 2 * elevationInvScale; + + // Delta is now scaled in pixels; normalize + delta = delta.direction(); + + // Copy over the bump value into the alpha channel. + float H = B[j] / 255.0f; + + if (lowPassBump) { + H = (ELEVATION(-1, -1) + ELEVATION( 0, -1) + ELEVATION(1, -1) + + ELEVATION(-1, 0) + ELEVATION( 0, 0) + ELEVATION(1, 0) + + ELEVATION(-1, 1) + ELEVATION( 0, 1) + ELEVATION(1, 1)) / (255.0f * 9.0f); + } +# undef ELEVATION + + if (scaleHeightByNz) { + // delta.z can't possibly be negative, so we avoid actually + // computing the absolute value. + H *= delta.z; + } + + N[i].a = iRound(H * 255.0f); + + // Pack into byte range + delta = delta * 127.5f + Vector3(127.5f, 127.5f, 127.5f); + N[i].r = iClamp(iRound(delta.x), 0, 255); + N[i].g = iClamp(iRound(delta.y), 0, 255); + N[i].b = iClamp(iRound(delta.z), 0, 255); + } + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void GImage::convertToL8() { + switch (m_channels) { + case 1: + return; + + case 3: + { + // Average + Color3uint8* src = (Color3uint8*)m_byte; + m_byte = NULL; + resize(m_width, m_height, 1); + for (int i = m_width * m_height - 1; i >= 0; --i) { + const Color3uint8 s = src[i]; + uint8& d = m_byte[i]; + d = ((int)s.r + (int)s.g + (int)s.b) / 3; + } + m_memMan->free(src); + } + break; + + case 4: + { + // Average + Color4uint8* src = (Color4uint8*)m_byte; + m_byte = NULL; + resize(m_width, m_height, 1); + for (int i = m_width * m_height - 1; i >= 0; --i) { + const Color4uint8 s = src[i]; + uint8& d = m_byte[i]; + d = ((int)s.r + (int)s.g + (int)s.b) / 3; + } + m_memMan->free(src); + } + return; + + default: + alwaysAssertM(false, "Bad number of channels in input image"); + } +} + + +void GImage::convertToRGBA() { + switch (m_channels) { + case 1: + { + // Spread + uint8* old = m_byte; + m_byte = NULL; + resize(m_width, m_height, 4); + for (int i = m_width * m_height - 1; i >= 0; --i) { + const uint8 s = old[i]; + Color4uint8& d = ((Color4uint8*)m_byte)[i]; + d.r = d.g = d.b = s; + d.a = 255; + } + m_memMan->free(m_byte); + } + break; + + case 3: + { + // Add alpha + Color3uint8* old = (Color3uint8*)m_byte; + m_byte = NULL; + resize(m_width, m_height, 4); + for (int i = m_width * m_height - 1; i >= 0; --i) { + const Color3uint8 s = old[i]; + Color4uint8& d = ((Color4uint8*)m_byte)[i]; + d.r = s.r; + d.g = s.g; + d.b = s.b; + d.a = 255; + } + m_memMan->free(old); + } + break; + + case 4: + // Already RGBA + return; + + default: + alwaysAssertM(false, "Bad number of channels in input image"); + } +} + + +void GImage::convertToRGB() { + switch (m_channels) { + case 1: + { + // Spread + uint8* old = m_byte; + m_byte = NULL; + resize(m_width, m_height, 3); + for (int i = m_width * m_height - 1; i >= 0; --i) { + const uint8 s = old[i]; + Color3uint8& d = ((Color3uint8*)m_byte)[i]; + d.r = d.g = d.b = s; + } + m_memMan->free(old); + } + break; + + case 3: + return; + + case 4: + // Strip alpha + { + Color4uint8* old = (Color4uint8*)m_byte; + m_byte = NULL; + resize(m_width, m_height, 3); + for (int i = m_width * m_height - 1; i >= 0; --i) { + const Color4uint8 s = old[i]; + Color3uint8& d = ((Color3uint8*)m_byte)[i]; + d.r = s.r; + d.g = s.g; + d.b = s.b; + } + m_memMan->free(old); + } + break; + + default: + alwaysAssertM(false, "Bad number of channels in input image"); + } +} + + +void GImage::R8G8B8_to_Y8U8V8(int width, int height, const uint8* _in, uint8* _out) { + const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in); + Color3uint8* out = reinterpret_cast<Color3uint8*>(_out); + + Color3uint8 p; + for (int i = width * height - 1; i >= 0; --i) { + p.r = iClamp(iRound(in->r * 0.229 + in->g * 0.587 + in->b * 0.114), 0, 255); + p.g = iClamp(iRound(in->r * -0.147 + in->g * -0.289 + in->b * 0.436) + 127, 0, 255); + p.b = iClamp(iRound(in->r * 0.615 + in->g * -0.515 + in->b * -0.100) + 127, 0, 255); + *out = p; + ++in; + ++out; + } +} + + + +void GImage::Y8U8V8_to_R8G8B8(int width, int height, const uint8* _in, uint8* _out) { + const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in); + Color3uint8* out = reinterpret_cast<Color3uint8*>(_out); + + Color3uint8 p; + for (int i = width * height - 1; i >= 0; --i) { + p.r = iClamp(iRound(in->r * 1.0753 + (in->b - 127) * 1.2256), 0, 255); + p.g = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * -0.3946 + (in->b - 127) * -0.4947), 0, 255); + p.b = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * 2.0320 + (in->b - 127) * 0.0853), 0, 255); + *out = p; + ++in; + ++out; + } +} + + +void GImage::makeCheckerboard(GImage& im, int checkerSize, const Color4uint8& A, const Color4uint8& B) { + for (int y = 0; y < im.m_height; ++y) { + for (int x = 0; x < im.m_width; ++x) { + bool checker = isOdd((x / checkerSize) + (y / checkerSize)); + const Color4uint8& color = checker ? A : B; + for (int c = 0; c < im.m_channels; ++c) { + uint8* v = im.byte() + (x + y * im.m_width) * im.m_channels + c; + *v = color[c]; + } + } + } +} + +} + |