#include "G3D/ImageFormat.h" #include "G3D/Color1unorm8.h" #include "G3D/Color3unorm8.h" #include "G3D/Color4unorm8.h" #include "G3D/Color1.h" #include "G3D/Color3.h" #include "G3D/Color4.h" namespace G3D { // this is the signature for all conversion routines (same parameters as ImageFormat::convert) typedef void (*ConvertFunc)(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg); // this defines the conversion routines for converting between compatible formats static const int NUM_CONVERT_IMAGE_FORMATS = 5; struct ConvertAttributes { ConvertFunc m_converter; ImageFormat::Code m_sourceFormats[NUM_CONVERT_IMAGE_FORMATS]; ImageFormat::Code m_destFormats[NUM_CONVERT_IMAGE_FORMATS]; bool m_handlesSourcePadding; bool m_handlesDestPadding; bool m_handleInvertY; }; // forward declare the converters we can use them below #define DECLARE_CONVERT_FUNC(name) static void name(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg); DECLARE_CONVERT_FUNC(l8_to_rgb8); DECLARE_CONVERT_FUNC(l32f_to_rgb8); DECLARE_CONVERT_FUNC(rgb8_to_rgba8); DECLARE_CONVERT_FUNC(rgb8_to_bgr8); DECLARE_CONVERT_FUNC(rgb8_to_rgba32f); DECLARE_CONVERT_FUNC(bgr8_to_rgb8); DECLARE_CONVERT_FUNC(bgr8_to_rgba8); DECLARE_CONVERT_FUNC(bgr8_to_rgba32f); DECLARE_CONVERT_FUNC(rgba8_to_rgb8); DECLARE_CONVERT_FUNC(rgba8_to_bgr8); DECLARE_CONVERT_FUNC(rgba8_to_rgba32f); DECLARE_CONVERT_FUNC(rgb32f_to_rgba32f); DECLARE_CONVERT_FUNC(rgba32f_to_rgb8); DECLARE_CONVERT_FUNC(rgba32f_to_rgba8); DECLARE_CONVERT_FUNC(rgba32f_to_bgr8); DECLARE_CONVERT_FUNC(rgba32f_to_rgb32f); DECLARE_CONVERT_FUNC(rgba32f_to_bayer_rggb8); DECLARE_CONVERT_FUNC(rgba32f_to_bayer_gbrg8); DECLARE_CONVERT_FUNC(rgba32f_to_bayer_grbg8); DECLARE_CONVERT_FUNC(rgba32f_to_bayer_bggr8); DECLARE_CONVERT_FUNC(bayer_rggb8_to_rgba32f); DECLARE_CONVERT_FUNC(bayer_gbrg8_to_rgba32f); DECLARE_CONVERT_FUNC(bayer_grbg8_to_rgba32f); DECLARE_CONVERT_FUNC(bayer_bggr8_to_rgba32f); DECLARE_CONVERT_FUNC(rgb8_to_yuv420p); DECLARE_CONVERT_FUNC(rgb8_to_yuv422); DECLARE_CONVERT_FUNC(rgb8_to_yuv444); DECLARE_CONVERT_FUNC(yuv420p_to_rgb8); DECLARE_CONVERT_FUNC(yuv422_to_rgb8); DECLARE_CONVERT_FUNC(yuv444_to_rgb8); // this is the list of mappings between formats and the routines to perform them static const ConvertAttributes sConvertMappings[] = { // RGB -> RGB color space // L8 -> {l8_to_rgb8, {ImageFormat::CODE_L8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, // L32F -> {l32f_to_rgb8, {ImageFormat::CODE_L32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, // RGB8 -> {rgb8_to_rgba8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true}, {rgb8_to_bgr8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true}, {rgb8_to_rgba32f, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, // BGR8 -> {bgr8_to_rgb8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, {bgr8_to_rgba8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true}, {bgr8_to_rgba32f, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, // RGBA8 -> {rgba8_to_rgb8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true}, {rgba8_to_bgr8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true}, {rgba8_to_rgba32f, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, // RGB32F -> {rgb32f_to_rgba32f, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true}, // RGBA32F -> {rgba32f_to_rgb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, true, true}, {rgba32f_to_rgba8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, true, true}, {rgba32f_to_bgr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, true, true}, {rgba32f_to_rgb32f, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, false, true, true}, // RGB -> BAYER color space {rgba32f_to_bayer_rggb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, false, true, true}, {rgba32f_to_bayer_gbrg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, false, true, true}, {rgba32f_to_bayer_grbg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, false, true, true}, {rgba32f_to_bayer_bggr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, false, true, true}, // BAYER -> RGB color space {bayer_rggb8_to_rgba32f, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, {bayer_gbrg8_to_rgba32f, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, {bayer_grbg8_to_rgba32f, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, {bayer_bggr8_to_rgba32f, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true}, // RGB <-> YUV color space {rgb8_to_yuv420p, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, false, false, false}, {rgb8_to_yuv422, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, false, false, false}, {rgb8_to_yuv444, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, false, false, false}, {yuv420p_to_rgb8, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false}, {yuv422_to_rgb8, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false}, {yuv444_to_rgb8, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false}, }; static ConvertFunc findConverter(TextureFormat::Code sourceCode, TextureFormat::Code destCode, bool needsSourcePadding, bool needsDestPadding, bool needsInvertY) { int numRoutines = sizeof(sConvertMappings) / sizeof(ConvertAttributes); for (int routineIndex = 0; routineIndex < numRoutines; ++routineIndex) { int sourceIndex = 0; ConvertAttributes routine = sConvertMappings[routineIndex]; while (routine.m_sourceFormats[sourceIndex] != ImageFormat::CODE_NONE) { // check for matching source if (routine.m_sourceFormats[sourceIndex] == sourceCode) { int destIndex = 0; // now check for matching dest to see if the routine fits while (routine.m_destFormats[destIndex] != ImageFormat::CODE_NONE) { // check if dest format matches and padding + invert rules match if ((routine.m_destFormats[destIndex] == destCode) && (!needsSourcePadding || (routine.m_handlesSourcePadding == needsSourcePadding)) && (!needsDestPadding || (routine.m_handlesDestPadding == needsDestPadding)) && (!needsInvertY || (routine.m_handleInvertY == needsInvertY))) { // found compatible converter return routine.m_converter; } ++destIndex; } } ++sourceIndex; } } return NULL; } bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false) { bool conversionAvailable = false; // check if a conversion is available if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) { conversionAvailable = true; } else { ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY); conversionAvailable = (directConverter != NULL); } return conversionAvailable; } bool ImageFormat::convert(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, BayerAlgorithm bayerAlg) { bool conversionAvailable = false; // Handle direct copy of image to same format if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) { System::memcpy(dstBytes[0], srcBytes[0], iCeil(((srcWidth * srcFormat->cpuBitsPerPixel + srcRowPadBits) * srcHeight) / 8.0f)); conversionAvailable = true; } else { // if no direct conversion routine exists, // then look for conversion to intermediate // and then from intermediate to dest. // intermediate format is RGBA32F ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY); // if we have a direct converter, use it, otherwise find intermdiate path if (directConverter) { directConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg); conversionAvailable = true; } else { ConvertFunc toInterConverter = findConverter(srcFormat->code, ImageFormat::CODE_RGBA32F, srcRowPadBits > 0, false, false);; ConvertFunc fromInterConverter = findConverter(ImageFormat::CODE_RGBA32F, dstFormat->code, false, dstRowPadBits > 0, invertY);; if (toInterConverter && fromInterConverter) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * ImageFormat::RGBA32F()->cpuBitsPerPixel * 8)); toInterConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, tmp, ImageFormat::RGBA32F(), 0, false, bayerAlg); fromInterConverter(reinterpret_cast&>(tmp), srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg); System::free(tmp[0]); conversionAvailable = true; } } } return conversionAvailable; } // ******************* // RGB -> RGB color space conversions // ******************* // L8 -> static void l8_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { (void)bayerAlg; (void)dstRowPadBits; unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; dst[i3 + 0] = src[i]; dst[i3 + 1] = src[i]; dst[i3 + 2] = src[i]; } } } // L32F -> static void l32f_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { int srcIndex = 0; int dstByteOffset = 0; unorm8* dst = static_cast(dstBytes[0]); const float* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { srcIndex = srcWidth * (srcHeight - y - 1); } for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) { Color3unorm8& d = *reinterpret_cast(dst + dstByteOffset); const float s = src[srcIndex]; const unorm8 c(s); d = Color3unorm8(c, c, c); } } } // RGB8 -> static void rgb8_to_rgba8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; int i4 = i3 + i; dst[i4 + 0] = src[i3 + 0]; dst[i4 + 1] = src[i3 + 1]; dst[i4 + 2] = src[i3 + 2]; dst[i4 + 3] = unorm8::one(); } } } static void rgb8_to_bgr8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; dst[i3 + 0] = src[i3 + 2]; dst[i3 + 1] = src[i3 + 1]; dst[i3 + 2] = src[i3 + 0]; } } } static void rgb8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); int dstIndex = 0; int srcByteOffset = 0; int srcRowPadBytes = srcRowPadBits / 8; Color4* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { dstIndex = srcWidth * (srcHeight - 1 - y); } for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) { const Color3unorm8& s = *reinterpret_cast(src + srcByteOffset); dst[dstIndex] = Color4(Color3(s), 1.0f); } srcByteOffset += srcRowPadBytes; } } // BGR8 -> static void bgr8_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; dst[i3 + 0] = src[i3 + 2]; dst[i3 + 1] = src[i3 + 1]; dst[i3 + 2] = src[i3 + 0]; } } } static void bgr8_to_rgba8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; int i4 = i3 + i; dst[i4 + 0] = src[i3 + 2]; dst[i4 + 1] = src[i3 + 1]; dst[i4 + 2] = src[i3 + 0]; dst[i4 + 3] = unorm8::one(); } } } static void bgr8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); int dstIndex = 0; int srcByteOffset = 0; int srcRowPadBytes = srcRowPadBits / 8; Color4* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { dstIndex = srcWidth * (srcHeight - 1 - y); } for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) { const Color3unorm8& s = *reinterpret_cast(src + srcByteOffset); dst[dstIndex] = Color4(Color3(s).bgr(), 1.0f); } srcByteOffset += srcRowPadBytes; } } // RGBA8 -> static void rgba8_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; int i4 = i3 + i; dst[i3 + 0] = src[i4 + 0]; dst[i3 + 1] = src[i4 + 1]; dst[i3 + 2] = src[i4 + 2]; } } } static void rgba8_to_bgr8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { unorm8* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x); int i3 = i * 3; int i4 = i3 + i; dst[i3 + 0] = src[i4 + 2]; dst[i3 + 1] = src[i4 + 1]; dst[i3 + 2] = src[i4 + 0]; } } } static void rgba8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); int dstIndex = 0; int srcByteOffset = 0; int srcRowPadBytes = srcRowPadBits / 8; Color4* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { dstIndex = srcWidth * (srcHeight - 1 - y); } for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 4) { const Color4unorm8& s = *reinterpret_cast(src + srcByteOffset); dst[dstIndex] = Color4(s); } srcByteOffset += srcRowPadBytes; } } // RGB32F -> static void rgb32f_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format"); int dstIndex = 0; int srcByteOffset = 0; int srcRowPadBytes = srcRowPadBits / 8; Color4* dst = static_cast(dstBytes[0]); const unorm8* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { dstIndex = srcWidth * (srcHeight - 1 - y); } for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3 * sizeof(float)) { const Color3& s = *reinterpret_cast(src + srcByteOffset); dst[dstIndex] = Color4(Color3(s), 1.0f); } srcByteOffset += srcRowPadBytes; } } // RGBA32F -> static void rgba32f_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); int srcIndex = 0; int dstByteOffset = 0; int dstRowPadBytes = dstRowPadBits / 8; unorm8* dst = static_cast(dstBytes[0]); const Color4* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { srcIndex = srcWidth * (srcHeight - y - 1); } for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) { Color3unorm8& d = *reinterpret_cast(dst + dstByteOffset); const Color4& s = src[srcIndex]; d = Color3unorm8(s.rgb()); } dstByteOffset += dstRowPadBytes; } } static void rgba32f_to_rgba8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); int srcIndex = 0; int dstByteOffset = 0; int dstRowPadBytes = dstRowPadBits / 8; unorm8* dst = static_cast(dstBytes[0]); const Color4* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { srcIndex = srcWidth * (srcHeight - 1 - y); } for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 4) { Color4unorm8& d = *reinterpret_cast(dst + dstByteOffset); const Color4& s = src[srcIndex]; d = Color4unorm8(s); } dstByteOffset += dstRowPadBytes; } } static void rgba32f_to_bgr8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); int srcIndex = 0; int dstByteOffset = 0; int dstRowPadBytes = dstRowPadBits / 8; unorm8* dst = static_cast(dstBytes[0]); const Color4* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { srcIndex = srcWidth * (srcHeight - y - 1); } for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) { Color3unorm8& d = *reinterpret_cast(dst + dstByteOffset); const Color4& s = src[srcIndex]; d = Color3unorm8(s.rgb()).bgr(); } dstByteOffset += dstRowPadBytes; } } static void rgba32f_to_rgb32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format"); int srcIndex = 0; int dstByteOffset = 0; int dstRowPadBytes = dstRowPadBits / 8; unorm8* dst = static_cast(dstBytes[0]); const Color4* src = static_cast(srcBytes[0]); for (int y = 0; y < srcHeight; ++y) { if (invertY) { srcIndex = srcWidth * (srcHeight - 1 - y); } for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3 * sizeof(float)) { Color3& d = *reinterpret_cast(dst + dstByteOffset); const Color4& s = src[srcIndex]; d = s.rgb(); } dstByteOffset += dstRowPadBytes; } } // ******************* // RGB <-> YUV color space conversions // ******************* static uint32 blendPixels(uint32 pixel1, uint32 pixel2) { static const uint32 rbMask = 0x00FF00FF; static const uint32 agMask = 0xFF00FF00; // Compute two color channels at a time. Use >> 1 for fast division by two // Using alternating color channels prevents overflow const uint32 rb = ((pixel1 & rbMask) + (pixel2 & rbMask)) >> 1; // Shift first to avoid overflow in alpha channel const uint32 ag = (((pixel1 & agMask) >> 1) + ((pixel2 & agMask) >> 1)); return ((rb & rbMask) | (ag & agMask)); } #define PIXEL_RGB8_TO_YUV_Y(r, g, b) unorm8::fromBits(iClamp(((66 * r.bits() + 129 * g.bits() + 25 * b.bits() + 128) >> 8) + 16, 0, 255)) #define PIXEL_RGB8_TO_YUV_U(r, g, b) unorm8::fromBits(iClamp(((-38 * r.bits() - 74 * g.bits() + 112 * b.bits() + 128) >> 8) + 128, 0, 255)) #define PIXEL_RGB8_TO_YUV_V(r, g, b) unorm8::fromBits(iClamp(((112 * r.bits() - 94 * g.bits() - 18 * b.bits() + 128) >> 8) + 128, 0, 255)) static void rgb8_to_yuv420p(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two"); const Color3unorm8* src = static_cast(srcBytes[0]); unorm8* dstY = static_cast(dstBytes[0]); unorm8* dstU = static_cast(dstBytes[1]); unorm8* dstV = static_cast(dstBytes[2]); for (int y = 0; y < srcHeight; y += 2) { for (int x = 0; x < srcWidth; x += 2) { // convert 4-pixel block at a time int srcPixelOffset0 = y * srcWidth + x; int srcPixelOffset1 = srcPixelOffset0 + 1; int srcPixelOffset2 = srcPixelOffset0 + srcWidth; int srcPixelOffset3 = srcPixelOffset2 + 1; int yIndex = y * srcWidth + x; dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset0].r, src[srcPixelOffset0].g, src[srcPixelOffset0].b); dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset1].r, src[srcPixelOffset1].g, src[srcPixelOffset1].b); yIndex += srcWidth; dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset2].r, src[srcPixelOffset2].g, src[srcPixelOffset2].b); dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset3].r, src[srcPixelOffset3].g, src[srcPixelOffset3].b); uint32 blendedPixel = blendPixels(src[srcPixelOffset0].asUInt32(), src[srcPixelOffset2].asUInt32()); Color3unorm8 uvSrcColor = Color3unorm8::fromARGB(blendedPixel); int uvIndex = y / 2 * srcWidth / 2 + x / 2; dstU[uvIndex] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); dstV[uvIndex] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); } } } static void rgb8_to_yuv422(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two"); const Color3unorm8* src = static_cast(srcBytes[0]); unorm8* dst = static_cast(dstBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; x += 2) { // convert 2-pixel horizontal block at a time int srcIndex = y * srcWidth + x; int dstIndex = srcIndex * 2; uint32 blendedPixel = blendPixels(src[srcIndex].asUInt32(), src[srcIndex + 1].asUInt32()); Color3unorm8 uvSrcColor = Color3unorm8::fromARGB(blendedPixel); dst[dstIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex].r, src[srcIndex].g, src[srcIndex].b); dst[dstIndex + 1] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); dst[dstIndex + 2] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex + 1].r, src[srcIndex + 1].g, src[srcIndex + 1].b); dst[dstIndex + 3] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b); } } } static void rgb8_to_yuv444(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); const Color3unorm8* src = static_cast(srcBytes[0]); Color3unorm8* dst = static_cast(dstBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { // convert 1-pixels at a time int index = y * srcWidth + x; unorm8 y = PIXEL_RGB8_TO_YUV_Y(src[index].r, src[index].g, src[index].b); unorm8 u = PIXEL_RGB8_TO_YUV_U(src[index].r, src[index].g, src[index].b); unorm8 v = PIXEL_RGB8_TO_YUV_V(src[index].r, src[index].g, src[index].b); dst[index].r = y; dst[index].g = u; dst[index].b = v; } } } #define PIXEL_YUV_TO_RGB8_R(y, u, v) unorm8::fromBits(iClamp((298 * (y.bits() - 16) + 409 * (v.bits() - 128) + 128) >> 8, 0, 255)) #define PIXEL_YUV_TO_RGB8_G(y, u, v) unorm8::fromBits(iClamp((298 * (y.bits() - 16) - 100 * (u.bits() - 128) - 208 * (v.bits() - 128) + 128) >> 8, 0, 255)) #define PIXEL_YUV_TO_RGB8_B(y, u, v) unorm8::fromBits(iClamp((298 * (y.bits() - 16) + 516 * (u.bits() - 128) + 128) >> 8, 0, 255)) static void yuv420p_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two"); const unorm8* srcY = static_cast(srcBytes[0]); const unorm8* srcU = static_cast(srcBytes[1]); const unorm8* srcV = static_cast(srcBytes[2]); Color3unorm8* dst = static_cast(dstBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; x += 2) { // convert to two rgb pixels in a row Color3unorm8* rgb = &dst[y * srcWidth + x]; int yOffset = y * srcWidth + x; int uvOffset = y / 2 * srcWidth / 2 + x / 2; rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]); rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]); rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]); rgb += 1; rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]); rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]); rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]); } } } static void yuv422_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two"); const unorm8* src = static_cast(srcBytes[0]); Color3unorm8* dst = static_cast(dstBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; x += 2) { // convert to two rgb pixels in a row Color3unorm8* rgb = &dst[y * srcWidth + x]; const int srcIndex = (y * srcWidth + x) * 2; const unorm8 y = src[srcIndex]; const unorm8 u = src[srcIndex + 1]; const unorm8 y2 = src[srcIndex + 2]; const unorm8 v = src[srcIndex + 3]; rgb->r = PIXEL_YUV_TO_RGB8_R(y, u, v); rgb->g = PIXEL_YUV_TO_RGB8_G(y, u, v); rgb->b = PIXEL_YUV_TO_RGB8_B(y, u, v); rgb += 1; rgb->r = PIXEL_YUV_TO_RGB8_R(y2, u, v); rgb->g = PIXEL_YUV_TO_RGB8_G(y2, u, v); rgb->b = PIXEL_YUV_TO_RGB8_B(y2, u, v); } } } static void yuv444_to_rgb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format"); const Color3unorm8* src = static_cast(srcBytes[0]); Color3unorm8* dst = static_cast(dstBytes[0]); for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { // convert to one rgb pixels at a time const int index = y * srcWidth + x; const Color3unorm8 s = src[index]; Color3unorm8& rgb = dst[index]; rgb.r = PIXEL_YUV_TO_RGB8_R(s.r, s.g, s.b); rgb.g = PIXEL_YUV_TO_RGB8_G(s.r, s.g, s.b); rgb.b = PIXEL_YUV_TO_RGB8_B(s.r, s.g, s.b); } } } //////////////////////////////////////////////////////////////////////////////////////////////// // // Bayer conversions // // There are two kinds of rows (GR and BG). // In each row, there are two kinds of pixels (G/R, B/G). // We express the four kinds of INPUT pixels as: // GRG, GRG, BGB, BGG // // There are three kinds of OUTPUT pixels: R, G, B. // Thus there are nominally 12 different I/O combinations, // but several are impulses because needed output at that // location *is* the input (e.g., G_GRG and G_BGG). // // The following 5x5 row-major filters are named as output_input. // Green static const float G_GRR[5][5] = {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; static const float G_BGB[5][5] = {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; // Red //(the caption in the paper is wrong for this case: // "R row B column really means R row G column" static const float R_GRG[5][5] = {{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, { -1.0f, 4.0f, 5.0f, 4.0f, -1.0f}, { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}}; static const float R_BGG[5][5] = {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, { 0.5f, 0.0f, 5.0f, 0.0f, 0.5f}, { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; static const float R_BGB[5][5] = {{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}, { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, {-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f}, { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, { 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}}; // Blue //(the caption in the paper is wrong for this case: // "B row R column really means B row G column") #define B_BGG R_GRG #define B_GRG R_BGG #define B_GRR R_BGB // ===================================================================== // Helper methods // ===================================================================== /** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */ static unorm8 applyFilter(const unorm8* I, int x, int y, int w, int h, const float filter[5][5]) { debugAssert(isEven(w)); debugAssert(isEven(h)); float sum = 0.0f; float denom = 0.0f; for (int dy = 0; dy < 5; ++dy) { int offset = ((y + dy + h - 2) % h) * w; for (int dx = 0; dx < 5; ++dx) { float f = filter[dy][dx]; sum += f * (float)I[((x + dx + w - 2) % w) + offset]; denom += f; } } return unorm8(sum / denom); } /** Helper method for Bayer grbg and bggr --> rgb8 */ static void swapRedAndBlue(int N, Color3unorm8* out) { for (int i = N - 1; i >= 0; --i) { unorm8 tmp = out[i].r; out[i].r = out[i].b; out[i].b = tmp; } } // RGB -> BAYER color space // ===================================================================== // rgb8 --> bayer helpers // ===================================================================== static void rgb8_to_bayer_rggb8(const int w, const int h, const unorm8* src, unorm8* dst) { Color3unorm8* srcColor = (Color3unorm8*)src; Color1unorm8* dstColor = (Color1unorm8*)dst; // Top row pixels for (int y = 0; y < h - 1; y += 2) { int offset = y * w; // Top left pixels for(int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].r); } // Top right pixels for(int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } } // Bottom row pixels for (int y = 1; y < h - 1; y += 2) { int offset = y * w; // Bottom left pixels for (int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } // Bottom right pixels for (int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].b); } } } static void rgb8_to_bayer_grbg8(const int w, const int h, const unorm8* src, unorm8* dst) { Color3unorm8* srcColor = (Color3unorm8*)src; Color1unorm8* dstColor = (Color1unorm8*)dst; // Top row pixels for (int y = 0; y < h - 1; y += 2) { int offset = y * w; // Top left pixels for (int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } // Top right pixels for (int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].r); } } // Bottom row pixels for (int y = 1; y < h - 1; y += 2) { int offset = y * w; // Bottom left pixels for (int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].b); } // Bottom right pixels for (int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } } } static void rgb8_to_bayer_bggr8(const int w, const int h, const unorm8* src, unorm8* dst) { Color3unorm8* srcColor = (Color3unorm8*)src; Color1unorm8* dstColor = (Color1unorm8*)dst; // Top row pixels for (int y = 0; y < h - 1; y += 2) { int offset = y * w; // Top left pixels for (int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].b); } // Top right pixels for (int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } } // Bottom row pixels for (int y = 1; y < h - 1; y += 2) { int offset = y * w; // Bottom left pixels for(int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } // Bottom right pixels for(int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].r); } } } static void rgb8_to_bayer_gbrg8(const int w, const int h, const unorm8* src, unorm8* dst) { Color3unorm8* srcColor = (Color3unorm8*)src; Color1unorm8* dstColor = (Color1unorm8*)dst; // Top row pixels for(int y = 0; y < h - 1; y += 2) { int offset = y * w; // Top left pixels for(int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } // Top right pixels for(int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].b); } } // Bottom row pixels for(int y = 1; y < h - 1; y += 2) { int offset = y * w; // Bottom left pixels for(int x = 0; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].r); } // Bottom right pixels for(int x = 1; x < w - 1; x += 2) { dstColor[x + offset] = Color1unorm8(srcColor[x + offset].g); } } } // ===================================================================== // rgba32f (-->rgb8) --> bayer converter implementations // ===================================================================== static void rgba32f_to_bayer_rggb8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); rgb8_to_bayer_rggb8(srcWidth, srcHeight, static_cast(tmp[0]), static_cast(dstBytes[0])); System::free(tmp[0]); } static void rgba32f_to_bayer_gbrg8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); rgb8_to_bayer_grbg8(srcWidth, srcHeight, static_cast(tmp[0]), static_cast(dstBytes[0])); System::free(tmp[0]); } static void rgba32f_to_bayer_grbg8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); rgb8_to_bayer_gbrg8(srcWidth, srcHeight, static_cast(tmp[0]), static_cast(dstBytes[0])); System::free(tmp[0]); } static void rgba32f_to_bayer_bggr8(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg); rgb8_to_bayer_bggr8(srcWidth, srcHeight, static_cast(tmp[0]), static_cast(dstBytes[0])); System::free(tmp[0]); } // BAYER -> RGB color space // ===================================================================== // bayer --> rgb8 helpers // ===================================================================== static void bayer_rggb8_to_rgb8_mhc(int w, int h, const unorm8* in, unorm8* _out) { debugAssert(in != _out); Color3unorm8* out = (Color3unorm8*)_out; for (int y = 0; y < h; ++y) { // Row beginning in the input array. int offset = y * w; // RG row for (int x = 0; x < w; ++x, ++out) { // R pixel { out->r = in[x + offset]; out->g = applyFilter(in, x, y, w, h, G_GRR); out->b = applyFilter(in, x, y, w, h, B_GRR); } ++x; ++out; // G pixel { out->r = applyFilter(in, x, y, w, h, R_GRG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_GRG); } } ++y; offset += w; // GB row for (int x = 0; x < w; ++x, ++out) { // G pixel { out->r = applyFilter(in, x, y, w, h, R_BGG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_BGG); } ++x; ++out; // B pixel { out->r = applyFilter(in, x, y, w, h, R_BGB); out->g = applyFilter(in, x, y, w, h, G_BGB); out->b = in[x + offset]; } } } } static void bayer_gbrg8_to_rgb8_mhc(int w, int h, const unorm8* in, unorm8* _out) { debugAssert(in != _out); Color3unorm8* out = (Color3unorm8*)_out; for (int y = 0; y < h; ++y) { // Row beginning in the input array. int offset = y * w; // GB row for (int x = 0; x < w; ++x, ++out) { // G pixel { out->r = applyFilter(in, x, y, w, h, R_BGG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_BGG); } ++x; ++out; // B pixel { out->r = applyFilter(in, x, y, w, h, R_BGB); out->g = applyFilter(in, x, y, w, h, G_BGB); out->b = in[x + offset]; } } } } static void bayer_grbg8_to_rgb8_mhc(int w, int h, const unorm8* in, unorm8* _out) { // Run the equivalent function for red bayer_gbrg8_to_rgb8_mhc(w, h, in, _out); // Now swap red and blue swapRedAndBlue(w * h, (Color3unorm8*)_out); } static void bayer_bggr8_to_rgb8_mhc(int w, int h, const unorm8* in, unorm8* _out) { // Run the equivalent function for red bayer_rggb8_to_rgb8_mhc(w, h, in, _out); // Now swap red and blue swapRedAndBlue(w * h, (Color3unorm8*)_out); } // ===================================================================== // bayer (--> rgb8) --> rgba32f converter implementations // ===================================================================== static void bayer_rggb8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); bayer_rggb8_to_rgb8_mhc(srcWidth, srcHeight, static_cast(srcBytes[0]), static_cast(tmp[0])); rgb8_to_rgba32f(reinterpret_cast&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); System::free(tmp[0]); } static void bayer_gbrg8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); bayer_grbg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast(srcBytes[0]), static_cast(tmp[0])); rgb8_to_rgba32f(reinterpret_cast&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); System::free(tmp[0]); } static void bayer_grbg8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); bayer_gbrg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast(srcBytes[0]), static_cast(tmp[0])); rgb8_to_rgba32f(reinterpret_cast&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); System::free(tmp[0]); } static void bayer_bggr8_to_rgba32f(const Array& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) { Array tmp; tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3unorm8))); bayer_bggr8_to_rgb8_mhc(srcWidth, srcHeight, static_cast(srcBytes[0]), static_cast(tmp[0])); rgb8_to_rgba32f(reinterpret_cast&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg); System::free(tmp[0]); } // TODO: The following region is commented out because so far // those conversions are not used anywhere else. Until it is // decided that such conversions are not needed, this region // remains commented out. // // ===================================================================== // // bayer --> bgr8 // // ===================================================================== // static void bayer_rggb8_to_bgr8_mhc(int w, int h, // const unorm8* in, unorm8* _out) { // debugAssert(in != _out); // Color3unorm8* out = (Color3unorm8*)_out; // for (int y = 0; y < h; ++y) { // // Row beginning in the input array. // int offset = y * w; // // RG row // for (int x = 0; x < w; ++x, ++out) { // // R pixel // { // out->b = in[x + offset]; // out->g = applyFilter(in, x, y, w, h, G_GRR); // out->r = applyFilter(in, x, y, w, h, B_GRR); // } // ++x; ++out; // // G pixel // { // out->b = applyFilter(in, x, y, w, h, R_GRG); // out->g = in[x + offset]; // out->r = applyFilter(in, x, y, w, h, B_GRG); // } // } // ++y; // offset += w; // // GB row // for (int x = 0; x < w; ++x, ++out) { // // G pixel // { // out->b = applyFilter(in, x, y, w, h, R_BGG); // out->g = in[x + offset]; // out->r = applyFilter(in, x, y, w, h, B_BGG); // } // ++x; ++out; // // B pixel // { // out->b = applyFilter(in, x, y, w, h, R_BGB); // out->g = applyFilter(in, x, y, w, h, G_BGB); // out->r = in[x + offset]; // } // } // } // } // static void bayer_gbrg8_to_bgr8_mhc(int w, int h, // const unorm8* in, unorm8* _out) { // debugAssert(in != _out); // Color3unorm8* out = (Color3unorm8*)_out; // for (int y = 0; y < h; ++y) { // // Row beginning in the input array. // int offset = y * w; // // GB row // for (int x = 0; x < srcWidth; ++x, ++out) { // // G pixel // { // out->b = applyFilter(in, x, y, w, h, R_BGG); // out->g = in[x + offset]; // out->r = applyFilter(in, x, y, w, h, B_BGG); // } // ++x; ++out; // // B pixel // { // out->b = applyFilter(in, x, y, w, h, R_BGB); // out->g = applyFilter(in, x, y, w, h, G_BGB); // out->r = in[x + offset]; // } // } // } // } // static void bayer_grbg8_to_bgr8_mhc(int w, int h, // const unorm8* in, unorm8* _out) { // // Run the equivalent function for red // bayer_gbrg8_to_bgr8_mhc(w, h, in, _out); // // Now swap red and blue // swapRedAndBlue(srcWidth * h, (Color3unorm8*)_out); // } // static void bayer_bggr8_to_bgr8_mhc(int w, int h, // const unorm8* in, unorm8* _out) { // // Run the equivalent function for red // bayer_rggb8_to_bgr8_mhc(w, h, in, _out); // // Now swap red and blue // swapRedAndBlue(srcWidth * h, (Color3unorm8*)_out); // } /////////////////////////////////////////////////// } // namespace G3D