#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
// If the library is compiled as a DLL, this ensures symbols are exported
#define NUCLEX_PIXELS_SOURCE 1
#include "Nuclex/Pixels/PixelFormats/PixelFormatConverter.h"
#include "ConvertPixel.h"
#include "OnPixelFormat.h"
namespace {
// ------------------------------------------------------------------------------------------- //
/// Converts one row of pixels from one pixel format to another
/// Pixel format of the source pixels
///
/// Pixel format the pixels will be converted to
///
/// Starting address to read source pixels from
///
/// Starting address at which converted pixels will be written
///
/// Number of pixels that will be converted
template<
Nuclex::Pixels::PixelFormat TSourcePixelFormat,
Nuclex::Pixels::PixelFormat TTargetPixelFormat
>
void ConvertRow(
const std::uint8_t *sourcePixels, std::uint8_t *targetPixels, std::size_t pixelCount
) {
typedef Nuclex::Pixels::PixelFormats::PixelTypeFromFormat SourceType;
typedef Nuclex::Pixels::PixelFormats::PixelTypeFromFormat TargetType;
constexpr std::size_t bytesPerSourcePixel = (
Nuclex::Pixels::CountBitsPerPixel(TSourcePixelFormat) / 8
);
constexpr std::size_t bytesPerTargetPixel = (
Nuclex::Pixels::CountBitsPerPixel(TTargetPixelFormat) / 8
);
while(pixelCount > 0) {
Nuclex::Pixels::PixelFormats::ConvertPixel(
reinterpret_cast(sourcePixels),
reinterpret_cast(targetPixels)
);
sourcePixels += bytesPerSourcePixel;
targetPixels += bytesPerTargetPixel;
--pixelCount;
}
}
// ------------------------------------------------------------------------------------------- //
///
/// Retrieves a full row pixel format conversion function from the specified source
/// pixel format (to a target specified at runtime via parameter)
///
/// Pixel format of the source pixels
template
class GetConvertRowFunction {
///
/// Retrieves a full row pixel format conversion function from the source pixel
/// format (specified to the outer template class) to the specified target pixel format
///
///
/// Pixel format the returned conversion function will convert to
///
template
class GetConvertRowFunctionWithKnownSourcePixelFormat {
///
/// Returns a function that converts from the source to the target pixel format
///
/// The pixel format conversion function
public: Nuclex::Pixels::PixelFormats::PixelFormatConverter::ConvertRowFunction *operator()(
) const {
return &ConvertRow;
}
};
///
/// Returns a function that converts from the source to the specified target pixel format
///
///
/// Pixel format to which the returned function will be converting
///
/// The pixel format conversion function
public: Nuclex::Pixels::PixelFormats::PixelFormatConverter::ConvertRowFunction *operator()(
Nuclex::Pixels::PixelFormat targetPixelFormat
) const {
return Nuclex::Pixels::PixelFormats::OnPixelFormat<
GetConvertRowFunctionWithKnownSourcePixelFormat,
Nuclex::Pixels::PixelFormats::PixelFormatConverter::ConvertRowFunction *
>(targetPixelFormat);
}
};
// ------------------------------------------------------------------------------------------- //
} // anonymous namespace
namespace Nuclex { namespace Pixels { namespace PixelFormats {
// ------------------------------------------------------------------------------------------- //
PixelFormatConverter::ConvertRowFunction *PixelFormatConverter::GetRowConverter(
PixelFormat sourcePixelFormat, PixelFormat targetPixelFormat
) {
return Nuclex::Pixels::PixelFormats::OnPixelFormat<
GetConvertRowFunction, PixelFormatConverter::ConvertRowFunction *
>(sourcePixelFormat, targetPixelFormat);
}
// ------------------------------------------------------------------------------------------- //
Bitmap PixelFormatConverter::Convert(
const Bitmap &source, PixelFormat newPixelFormat
) {
const BitmapMemory &sourceMemory = source.Access();
// Create a new bitmap with the same dimension as the source bitmap but
// using the new pixel format requested by the caller
Bitmap target(sourceMemory.Width, sourceMemory.Height, newPixelFormat);
const BitmapMemory &targetMemory = target.Access();
// Get row converter from source to requested target pixel format
ConvertRowFunction *convertRow = GetRowConverter(sourceMemory.PixelFormat, newPixelFormat);
// Perform the conversion row by row, writing the converted pixels into
// the target bitmap while respecting both bitmap's 'stride' value.
{
const std::uint8_t *sourceRowStart = (
reinterpret_cast(sourceMemory.Pixels)
);
std::uint8_t *targetRowStart = (
reinterpret_cast(targetMemory.Pixels)
);
for(std::size_t rowIndex = 0; rowIndex < sourceMemory.Height; ++rowIndex) {
convertRow(sourceRowStart, targetRowStart, sourceMemory.Width);
sourceRowStart += sourceMemory.Stride;
targetRowStart += targetMemory.Stride;
}
}
return target;
}
// ------------------------------------------------------------------------------------------- //
void PixelFormatConverter::Convert(const Bitmap &source, Bitmap &target) {
const BitmapMemory &sourceMemory = source.Access();
const BitmapMemory &targetMemory = target.Access();
bool hasDifferentDimensions = (
(sourceMemory.Width != targetMemory.Width) ||
(sourceMemory.Height != targetMemory.Height)
);
if(hasDifferentDimensions) {
throw std::invalid_argument(
u8"Provided bitmaps have different dimensions, "
u8"the pixel format converter supports conversions between same-sized bitmaps."
);
}
// Get row converter from source to requested target pixel format
ConvertRowFunction *convertRow = GetRowConverter(
sourceMemory.PixelFormat, targetMemory.PixelFormat
);
// Perform the conversion row by row, writing the converted pixels into
// the target bitmap while respecting both bitmap's 'stride' value.
{
const std::uint8_t *sourceRowStart = (
reinterpret_cast(sourceMemory.Pixels)
);
std::uint8_t *targetRowStart = (
reinterpret_cast(targetMemory.Pixels)
);
for(std::size_t rowIndex = 0; rowIndex < sourceMemory.Height; ++rowIndex) {
convertRow(sourceRowStart, targetRowStart, sourceMemory.Width);
sourceRowStart += sourceMemory.Stride;
targetRowStart += targetMemory.Stride;
}
}
}
// ------------------------------------------------------------------------------------------- //
}}} // namespace Nuclex::Pixels::PixelFormats