#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 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_GRAPHICS_SOURCE 1 #include "Nuclex/Graphics/Rasterization/Texture2.h" #include #include // Who keeps redefining min and max here? Grr... #undef min #undef max namespace Nuclex { namespace Graphics { namespace Rasterization { // ------------------------------------------------------------------------------------------- // Texture2::Texture2( std::size_t width, std::size_t height, PixelFormat::Enum pixelFormat, Usage::Enum usage /* = Usage::Immutable */ ) : Texture(CountRequiredBytes(width * height, pixelFormat), pixelFormat, usage), width(width), height(height) {} // ------------------------------------------------------------------------------------------- // Texture2::Texture2(const Texture2 &other) : Texture(other), width(other.width), height(other.height) {} // ------------------------------------------------------------------------------------------- // Texture2::~Texture2() { OnDestroying(); } // ------------------------------------------------------------------------------------------- // void Texture2::Clear() { std::uint8_t fillValue = 0; std::fill(this->Pixels, this->Pixels + GetSizeInBytes(), fillValue); OnCleared(); } // ------------------------------------------------------------------------------------------- // std::size_t Texture2::GetLength(std::size_t dimensionIndex) const { switch(dimensionIndex) { case 0: { return this->width; } case 1: { return this->height; } default: { throw std::out_of_range("Invalid dimension specified"); } } } // ------------------------------------------------------------------------------------------- // void Texture2::Read( const Rectangle ®ion, void *target, std::size_t targetStride ) { verifyRegion(region); // Calculate the run lengths and offsets required for the copy std::uint8_t *sourceBytes = this->Pixels; std::size_t lineCount, bytesPerLine, targetBytesToSkip, sourceBytesToSkip; { std::size_t bitsPerPixel = CountBitsPerPixel(this->PixelFormat); { std::size_t linesPerBlock = CountLinesPerBlock(this->PixelFormat); // Compressed pixel formats may encode rectangular blocks of pixels in a block of bits sourceBytes += region.Top / linesPerBlock; sourceBytes += (bitsPerPixel * region.Left / 8); lineCount = (region.Bottom - region.Top) / linesPerBlock; } { std::size_t regionWidth = region.Right - region.Left; bytesPerLine = (bitsPerPixel * regionWidth / 8); } targetBytesToSkip = targetStride - bytesPerLine; sourceBytesToSkip = (this->width * bitsPerPixel / 8) - bytesPerLine; } // If the region can be copied in a single run, do so std::uint8_t *targetBytes = reinterpret_cast(target); if((targetBytesToSkip == 0) && (sourceBytesToSkip)) { std::copy_n(sourceBytes, lineCount * bytesPerLine, targetBytes); } else { // Otherwise we need to copy line by line while(lineCount > 0) { std::copy_n(sourceBytes, bytesPerLine, targetBytes); sourceBytes += sourceBytesToSkip; targetBytes += targetBytesToSkip; --lineCount; } } } // ------------------------------------------------------------------------------------------- // void Texture2::Write( const Rectangle ®ion, const void *source, std::size_t sourceStride ) { verifyRegion(region); // Calculate the run lengths and offsets required for the copy std::uint8_t *targetBytes = this->Pixels; std::size_t lineCount, bytesPerLine, targetBytesToSkip, sourceBytesToSkip; { std::size_t bitsPerPixel = CountBitsPerPixel(this->PixelFormat); { std::size_t linesPerBlock = CountLinesPerBlock(this->PixelFormat); // Compressed pixel formats may encode rectangular blocks of pixels in a block of bits targetBytes += region.Top / linesPerBlock; targetBytes += (bitsPerPixel * region.Left / 8); lineCount = (region.Bottom - region.Top) / linesPerBlock; } { std::size_t regionWidth = region.Right - region.Left; bytesPerLine = (bitsPerPixel * regionWidth / 8); } sourceBytesToSkip = sourceStride - bytesPerLine; targetBytesToSkip = (this->width * bitsPerPixel / 8) - bytesPerLine; } // If the region can be copied in a single run, do so const std::uint8_t *sourceBytes = reinterpret_cast(source); if((targetBytesToSkip == 0) && (sourceBytesToSkip == 0)) { std::copy_n(sourceBytes, lineCount * bytesPerLine, targetBytes); } else { // Otherwise we need to copy line by line while(lineCount > 0) { std::copy_n(sourceBytes, bytesPerLine, targetBytes); sourceBytes += sourceBytesToSkip; targetBytes += targetBytesToSkip; --lineCount; } } } // ------------------------------------------------------------------------------------------- // void Texture2::verifyRegion(const Rectangle ®ion) const { bool isLeavingTexture = (region.Left >= this->width) || (region.Right > this->width) || (region.Top >= this->height) || (region.Bottom > this->height); if(isLeavingTexture) { throw std::runtime_error("Region is leaving the texture's area"); } // The left and right side of the region must be aligned to blocks. BC3 (DXT5) uses // 16 byte long blocks, where each block contains a color palette at the beginning // followed by an array of 4x4 pixels. If the region would result in cutting such // a block apart, the data would be unusable, thus we trigger an exception in that case. { std::size_t bitsPerPixel = CountBitsPerPixel(this->PixelFormat); std::size_t bitsPerBlock = CountBitsPerBlock(this->PixelFormat); std::size_t leftBits = region.Left * bitsPerPixel; if((leftBits % bitsPerBlock) > 0) { throw std::runtime_error( "The region's left border is not aligned to the pixel format's block size" ); } std::size_t rightBits = region.Right * bitsPerPixel; if((rightBits % bitsPerBlock) > 0) { throw std::runtime_error( "The region's right border is not aligned to the pixel format's block size" ); } } // See comment above. Some compressed formats store pixels in blocks of several // pixels, both horizontally and vertically. We couldn't return a line starting // in the middle of one of these blocks even if we tried. { std::size_t linesPerBlock = CountLinesPerBlock(this->PixelFormat); if((region.Top % linesPerBlock) > 0) { throw std::runtime_error( "The region's top border is not aligned to the pixel format's block size" ); } if((region.Bottom % linesPerBlock) > 0) { throw std::runtime_error( "The region's bottom border is not aligned to the pixel format's block size" ); } } } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Rasterization