#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2021 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library 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
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
#ifndef NUCLEX_PIXELS_PIXELFORMATS_CONVERTPIXEL_H
#error This file is intended to be included through ConvertPixel.h
#endif
namespace Nuclex { namespace Pixels { namespace PixelFormats {
// ------------------------------------------------------------------------------------------- //
/// Converts a pixel in an integer format into a floating point format
/// Pixel format used by the input pixel
/// Pixel format used by the output pixel
/// Pixel that will be converted to the output pixel format
/// A pixel in the output pixel format that's equivalent to the input pixel
template<
PixelFormat TSourcePixelFormat, PixelFormat TTargetPixelFormat,
typename std::enable_if_t<
(TSourcePixelFormat != TTargetPixelFormat) &&
(!IsFloatFormat) &&
(IsFloatFormat)
> * = nullptr
>
NUCLEX_PIXELS_ALWAYS_INLINE void ConvertPixel(
const PixelTypeFromFormat *sourcePixel,
PixelTypeFromFormat *targetPixel
) {
(void)sourcePixel; // MSVC fantasizes a constellation where no channels exist
(void)targetPixel; // then warns that these two parameters aren't used...
// TODO: Int->Float converter currently does not support signed integer formats
static_assert(
!IsSignedFormat && u8"Signed pixel formats not implemented yet"
);
if constexpr(NeedConvertChannel1) {
static_assert(
((PixelFormatDescription::Channel1::LowestBitIndex % 8) == 0) &&
u8"Target floating point channel bits start at a byte boundary"
);
typedef ChannelFloatType<
PixelFormatDescription::Channel1::BitCount
> TargetFloatType;
TargetFloatType *target = reinterpret_cast(
reinterpret_cast(targetPixel) +
(PixelFormatDescription::Channel1::LowestBitIndex / 8)
);
const constexpr PixelTypeFromFormat channelBitMask = BitMask<
PixelTypeFromFormat,
PixelFormatDescription::Channel1::LowestBitIndex,
PixelFormatDescription::Channel1::BitCount
>;
#if defined(_MSC_VER) // MSVC invents warnings here that don't apply.
#pragma warning(push)
#pragma warning(disable:4244) // conversion from 'int' to 'float', possible loss of data
#endif
// Floating point accuracy depends on how large the value is. At 1<<12 accuracy
// it reaches an ULP of 0.5 (meaning it can only represent numbers in steps of 0.5),
// at 1<<13 the ULP is 1. We can only use the more efficient method up to here.
if constexpr(channelBitMask < 4194304) { // TODO: Use different value for half float
*target = (
static_cast(*sourcePixel & channelBitMask) /
static_cast(channelBitMask)
);
} else { // Shift right, then divide by channel max if value too large
*target = (
static_cast(
BitShift::Channel1::LowestBitIndex>(
*sourcePixel & channelBitMask
)
) / (
static_cast(
BitMask<
std::uint_fast32_t,
0,
PixelFormatDescription::Channel1::BitCount
>
)
)
);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
}
if constexpr(NeedConvertChannel2) {
static_assert(
((PixelFormatDescription::Channel2::LowestBitIndex % 8) == 0) &&
u8"Target floating point channel bits start at a byte boundary"
);
typedef ChannelFloatType<
PixelFormatDescription::Channel2::BitCount
> TargetFloatType;
TargetFloatType *target = reinterpret_cast(
reinterpret_cast(targetPixel) +
(PixelFormatDescription::Channel2::LowestBitIndex / 8)
);
const constexpr PixelTypeFromFormat channelBitMask = BitMask<
PixelTypeFromFormat,
PixelFormatDescription::Channel2::LowestBitIndex,
PixelFormatDescription::Channel2::BitCount
>;
#if defined(_MSC_VER) // MSVC invents warnings here that don't apply.
#pragma warning(push)
#pragma warning(disable:4244) // conversion from 'int' to 'float', possible loss of data
#endif
// Floating point accuracy depends on how large the value is. At 1<<12 accuracy
// reaches an ULP of 0.5 (meaning it canonly represent numbers in steps of 0.5),
// at 1<<13 the ULP is 1. We can only use the more efficient method up to here.
if constexpr(channelBitMask < 4194304) { // TODO: Use different value for half float
*target = (
static_cast(*sourcePixel & channelBitMask) /
static_cast(channelBitMask)
);
} else { // Shift right, then divide by channel max if value too large
*target = (
static_cast(
BitShift::Channel2::LowestBitIndex>(
*sourcePixel & channelBitMask
)
) / (
static_cast(
BitMask<
std::uint_fast32_t,
0,
PixelFormatDescription::Channel2::BitCount
>
)
)
);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
}
// Optimization idea (hard to implement)
// If some channels have bits above 1<<12, perhaps the higher channels can be shifted
// down to save one bit shift in total. Gain is small or nonexistent, though, instructions
// become dependent and shifting is generally a 1 clock or half-clock instruction.
if constexpr(NeedConvertChannel3) {
static_assert(
((PixelFormatDescription::Channel3::LowestBitIndex % 8) == 0) &&
u8"Target floating point channel bits start at a byte boundary"
);
typedef ChannelFloatType<
PixelFormatDescription::Channel3::BitCount
> TargetFloatType;
TargetFloatType *target = reinterpret_cast(
reinterpret_cast(targetPixel) +
(PixelFormatDescription::Channel3::LowestBitIndex / 8)
);
const constexpr PixelTypeFromFormat channelBitMask = BitMask<
PixelTypeFromFormat,
PixelFormatDescription::Channel3::LowestBitIndex,
PixelFormatDescription::Channel3::BitCount
>;
#if defined(_MSC_VER) // MSVC invents warnings here that don't apply.
#pragma warning(push)
#pragma warning(disable:4244) // conversion from 'int' to 'float', possible loss of data
#endif
// Floating point accuracy depends on how large the value is. At 1<<12 accuracy
// reaches an ULP of 0.5 (meaning it canonly represent numbers in steps of 0.5),
// at 1<<13 the ULP is 1. We can only use the more efficient method up to here.
if constexpr(channelBitMask < 4194304) { // TODO: Use different value for half float
*target = (
static_cast(*sourcePixel & channelBitMask) /
static_cast(channelBitMask)
);
} else { // Shift right, then divide by channel max if value too large
*target = (
static_cast(
BitShift::Channel3::LowestBitIndex>(
*sourcePixel & channelBitMask
)
) / (
static_cast(
BitMask<
std::uint_fast32_t,
0,
PixelFormatDescription::Channel3::BitCount
>
)
)
);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
}
if constexpr(NeedConvertChannel4) {
static_assert(
((PixelFormatDescription::Channel4::LowestBitIndex % 8) == 0) &&
u8"Target floating point channel bits start at a byte boundary"
);
typedef ChannelFloatType<
PixelFormatDescription::Channel4::BitCount
> TargetFloatType;
TargetFloatType *target = reinterpret_cast(
reinterpret_cast(targetPixel) +
(PixelFormatDescription::Channel4::LowestBitIndex / 8)
);
const constexpr PixelTypeFromFormat channelBitMask = BitMask<
PixelTypeFromFormat,
PixelFormatDescription::Channel4::LowestBitIndex,
PixelFormatDescription::Channel4::BitCount
>;
#if defined(_MSC_VER) // MSVC invents warnings here that don't apply.
#pragma warning(push)
#pragma warning(disable:4244) // conversion from 'int' to 'float', possible loss of data
#endif
// Floating point accuracy depends on how large the value is. At 1<<12 accuracy
// reaches an ULP of 0.5 (meaning it canonly represent numbers in steps of 0.5),
// at 1<<13 the ULP is 1. We can only use the more efficient method up to here.
if constexpr(channelBitMask < 4194304) { // TODO: Use different value for half float
*target = (
static_cast(*sourcePixel & channelBitMask) /
static_cast(channelBitMask)
);
} else { // Shift right, then divide by channel max if value too large
*target = (
static_cast(
BitShift::Channel4::LowestBitIndex>(
*sourcePixel & channelBitMask
)
) / (
static_cast(
BitMask<
std::uint_fast32_t,
0,
PixelFormatDescription::Channel4::BitCount
>
)
)
);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
} else if constexpr(HasAlphaChannel) {
typedef ChannelFloatType<
PixelFormatDescription::Channel4::BitCount
> TargetFloatType;
TargetFloatType *target = reinterpret_cast(
reinterpret_cast(targetPixel) +
(PixelFormatDescription::Channel4::LowestBitIndex / 8)
);
*target = static_cast(1);
}
}
// ------------------------------------------------------------------------------------------- //
}}} // namespace Nuclex::Pixels::PixelFormats