// Copyright (C) 2002-2012 Nikolaus Gebhardt / Thomas Alten // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CImage.h" #include "irrString.h" #include "CColorConverter.h" #include "CBlit.h" #include "os.h" #include "SoftwareDriver2_helper.h" namespace irr { namespace video { //! Constructor from raw data CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d& size, void* data, bool ownForeignMemory, bool deleteMemory) : IImage(format, size, deleteMemory) { if (ownForeignMemory) { Data = (u8*)data; } else { const size_t dataSize = getDataSizeFromFormat(Format, Size.Width, Size.Height); Data = new u8[align_next(dataSize,16)]; memcpy(Data, data, dataSize); DeleteMemory = true; } } //! Constructor of empty image CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d& size) : IImage(format, size, true) { const size_t dataSize = getDataSizeFromFormat(Format, Size.Width, Size.Height); Data = new u8[align_next(dataSize,16)]; DeleteMemory = true; } //! sets a pixel void CImage::setPixel(u32 x, u32 y, const SColor &color, bool blend) { if (x >= Size.Width || y >= Size.Height) return; switch(Format) { case ECF_A1R5G5B5: { u16 * dest = (u16*) (Data + ( y * Pitch ) + ( x << 1 )); *dest = video::A8R8G8B8toA1R5G5B5( color.color ); } break; case ECF_R5G6B5: { u16 * dest = (u16*) (Data + ( y * Pitch ) + ( x << 1 )); *dest = video::A8R8G8B8toR5G6B5( color.color ); } break; case ECF_R8G8B8: { u8* dest = Data + ( y * Pitch ) + ( x * 3 ); dest[0] = (u8)color.getRed(); dest[1] = (u8)color.getGreen(); dest[2] = (u8)color.getBlue(); } break; case ECF_A8R8G8B8: { u32 * dest = (u32*) (Data + ( y * Pitch ) + ( x << 2 )); *dest = blend ? PixelBlend32 ( *dest, color.color ) : color.color; } break; IRR_CASE_IIMAGE_COMPRESSED_FORMAT os::Printer::log("IImage::setPixel method doesn't work with compressed images.", ELL_WARNING); return; case ECF_UNKNOWN: os::Printer::log("IImage::setPixel unknown format.", ELL_WARNING); return; default: break; } } //! returns a pixel SColor CImage::getPixel(u32 x, u32 y) const { if (x >= Size.Width || y >= Size.Height) return SColor(0); switch(Format) { case ECF_A1R5G5B5: return A1R5G5B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]); case ECF_R5G6B5: return R5G6B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]); case ECF_A8R8G8B8: return ((u32*)Data)[y*Size.Width + x]; case ECF_R8G8B8: { u8* p = Data+(y*3)*Size.Width + (x*3); return SColor(255,p[0],p[1],p[2]); } IRR_CASE_IIMAGE_COMPRESSED_FORMAT os::Printer::log("IImage::getPixel method doesn't work with compressed images.", ELL_WARNING); break; case ECF_UNKNOWN: os::Printer::log("IImage::getPixel unknown format.", ELL_WARNING); break; default: break; } return SColor(0); } //! copies this surface into another at given position void CImage::copyTo(IImage* target, const core::position2d& pos) { if (IImage::isCompressedFormat(Format)) { os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING); return; } if (!Blit(BLITTER_TEXTURE, target, 0, &pos, this, 0, 0) && target && pos.X == 0 && pos.Y == 0 && CColorConverter::canConvertFormat(Format, target->getColorFormat())) { // No fast blitting, but copyToScaling uses other color conversions and might work irr::core::dimension2du dim(target->getDimension()); copyToScaling(target->getData(), dim.Width, dim.Height, target->getColorFormat(), target->getPitch()); } } //! copies this surface partially into another at given position void CImage::copyTo(IImage* target, const core::position2d& pos, const core::rect& sourceRect, const core::rect* clipRect) { if (IImage::isCompressedFormat(Format)) { os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING); return; } Blit(BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0); } //! copies this surface into another, using the alpha mask, a cliprect and a color to add with void CImage::copyToWithAlpha(IImage* target, const core::position2d& pos, const core::rect& sourceRect, const SColor &color, const core::rect* clipRect, bool combineAlpha) { if (IImage::isCompressedFormat(Format)) { os::Printer::log("IImage::copyToWithAlpha method doesn't work with compressed images.", ELL_WARNING); return; } eBlitter op = combineAlpha ? BLITTER_TEXTURE_COMBINE_ALPHA : color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND : BLITTER_TEXTURE_ALPHA_COLOR_BLEND; Blit(op,target, clipRect, &pos, this, &sourceRect, color.color); } //! copies this surface into another, scaling it to the target image size // note: this is very very slow. void CImage::copyToScaling(void* target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch) { if (IImage::isCompressedFormat(Format)) { os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING); return; } if (!target || !width || !height) return; const u32 bpp=getBitsPerPixelFromFormat(format)/8; if (0==pitch) pitch = width*bpp; if (Format==format && Size.Width==width && Size.Height==height) { if (pitch==Pitch) { memcpy(target, Data, height*pitch); return; } else { u8* tgtpos = (u8*) target; u8* srcpos = Data; const u32 bwidth = width*bpp; const u32 rest = pitch-bwidth; for (u32 y=0; y 1 ? (f32)(Size.Width-1) / (f32)(width-1) : 0.f; const f32 sourceYStep = height > 1 ? (f32)(Size.Height-1) / (f32)(height-1) : 0.f; s32 yval=0, syval=0; f32 sy = 0.5f; // for rounding to nearest pixel for (u32 y=0; y& targetSize = target->getDimension(); if (targetSize==Size) { copyTo(target); return; } copyToScaling(target->getData(), targetSize.Width, targetSize.Height, target->getColorFormat()); } //! copies this surface into another, scaling it to fit it. void CImage::copyToScalingBoxFilter(IImage* target, s32 bias, bool blend) { if (IImage::isCompressedFormat(Format)) { os::Printer::log("IImage::copyToScalingBoxFilter method doesn't work with compressed images.", ELL_WARNING); return; } const core::dimension2d destSize = target->getDimension(); const f32 sourceXStep = (f32) Size.Width / (f32) destSize.Width; const f32 sourceYStep = (f32) Size.Height / (f32) destSize.Height; target->getData(); const s32 fx = core::ceil32( sourceXStep ); const s32 fy = core::ceil32( sourceYStep ); f32 sx; f32 sy; sy = 0.f; for ( u32 y = 0; y != destSize.Height; ++y ) { sx = 0.f; for ( u32 x = 0; x != destSize.Width; ++x ) { target->setPixel( x, y, getPixelBox( core::floor32(sx), core::floor32(sy), fx, fy, bias ), blend ); sx += sourceXStep; } sy += sourceYStep; } } //! fills the surface with given color void CImage::fill(const SColor &color) { if (IImage::isCompressedFormat(Format)) { os::Printer::log("IImage::fill method doesn't work with compressed images.", ELL_WARNING); return; } u32 c; switch ( Format ) { case ECF_A1R5G5B5: c = color.toA1R5G5B5(); c |= c << 16; break; case ECF_R5G6B5: c = video::A8R8G8B8toR5G6B5( color.color ); c |= c << 16; break; case ECF_A8R8G8B8: c = color.color; break; case ECF_R8G8B8: { u8 rgb[3]; CColorConverter::convert_A8R8G8B8toR8G8B8(&color, 1, rgb); const size_t size = getImageDataSizeInBytes(); for (size_t i=0; i