1 // Copyright (C) 2002-2012 Nikolaus Gebhardt / Thomas Alten
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
6 #include "irrString.h"
\r
7 #include "CColorConverter.h"
\r
10 #include "SoftwareDriver2_helper.h"
\r
17 //! Constructor from raw data
\r
18 CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size, void* data,
\r
19 bool ownForeignMemory, bool deleteMemory) : IImage(format, size, deleteMemory)
\r
21 if (ownForeignMemory)
\r
27 const u32 dataSize = getDataSizeFromFormat(Format, Size.Width, Size.Height);
\r
29 Data = new u8[align_next(dataSize,16)];
\r
30 memcpy(Data, data, dataSize);
\r
31 DeleteMemory = true;
\r
36 //! Constructor of empty image
\r
37 CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size) : IImage(format, size, true)
\r
39 Data = new u8[align_next(getDataSizeFromFormat(Format, Size.Width, Size.Height),16)];
\r
40 DeleteMemory = true;
\r
45 void CImage::setPixel(u32 x, u32 y, const SColor &color, bool blend)
\r
47 if (x >= Size.Width || y >= Size.Height)
\r
54 u16 * dest = (u16*) (Data + ( y * Pitch ) + ( x << 1 ));
\r
55 *dest = video::A8R8G8B8toA1R5G5B5( color.color );
\r
60 u16 * dest = (u16*) (Data + ( y * Pitch ) + ( x << 1 ));
\r
61 *dest = video::A8R8G8B8toR5G6B5( color.color );
\r
66 u8* dest = Data + ( y * Pitch ) + ( x * 3 );
\r
67 dest[0] = (u8)color.getRed();
\r
68 dest[1] = (u8)color.getGreen();
\r
69 dest[2] = (u8)color.getBlue();
\r
74 u32 * dest = (u32*) (Data + ( y * Pitch ) + ( x << 2 ));
\r
75 *dest = blend ? PixelBlend32 ( *dest, color.color ) : color.color;
\r
78 IRR_CASE_IIMAGE_COMPRESSED_FORMAT
\r
79 os::Printer::log("IImage::setPixel method doesn't work with compressed images.", ELL_WARNING);
\r
83 os::Printer::log("IImage::setPixel unknown format.", ELL_WARNING);
\r
93 SColor CImage::getPixel(u32 x, u32 y) const
\r
95 if (x >= Size.Width || y >= Size.Height)
\r
101 return A1R5G5B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);
\r
103 return R5G6B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);
\r
105 return ((u32*)Data)[y*Size.Width + x];
\r
108 u8* p = Data+(y*3)*Size.Width + (x*3);
\r
109 return SColor(255,p[0],p[1],p[2]);
\r
112 IRR_CASE_IIMAGE_COMPRESSED_FORMAT
\r
113 os::Printer::log("IImage::getPixel method doesn't work with compressed images.", ELL_WARNING);
\r
117 os::Printer::log("IImage::getPixel unknown format.", ELL_WARNING);
\r
128 //! copies this surface into another at given position
\r
129 void CImage::copyTo(IImage* target, const core::position2d<s32>& pos)
\r
131 if (IImage::isCompressedFormat(Format))
\r
133 os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);
\r
137 if (!Blit(BLITTER_TEXTURE, target, 0, &pos, this, 0, 0)
\r
138 && target && pos.X == 0 && pos.Y == 0 &&
\r
139 CColorConverter::canConvertFormat(Format, target->getColorFormat()))
\r
141 // No fast blitting, but copyToScaling uses other color conversions and might work
\r
142 irr::core::dimension2du dim(target->getDimension());
\r
143 copyToScaling(target->getData(), dim.Width, dim.Height, target->getColorFormat(), target->getPitch());
\r
148 //! copies this surface partially into another at given position
\r
149 void CImage::copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect)
\r
151 if (IImage::isCompressedFormat(Format))
\r
153 os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);
\r
157 Blit(BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0);
\r
161 //! copies this surface into another, using the alpha mask, a cliprect and a color to add with
\r
162 void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const SColor &color, const core::rect<s32>* clipRect, bool combineAlpha)
\r
164 if (IImage::isCompressedFormat(Format))
\r
166 os::Printer::log("IImage::copyToWithAlpha method doesn't work with compressed images.", ELL_WARNING);
\r
170 eBlitter op = combineAlpha ? BLITTER_TEXTURE_COMBINE_ALPHA :
\r
171 color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND : BLITTER_TEXTURE_ALPHA_COLOR_BLEND;
\r
172 Blit(op,target, clipRect, &pos, this, &sourceRect, color.color);
\r
176 //! copies this surface into another, scaling it to the target image size
\r
177 // note: this is very very slow.
\r
178 void CImage::copyToScaling(void* target, u32 width, u32 height, ECOLOR_FORMAT format, u32 pitch)
\r
180 if (IImage::isCompressedFormat(Format))
\r
182 os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);
\r
186 if (!target || !width || !height || !Size.Width || !Size.Height)
\r
189 const u32 bpp=getBitsPerPixelFromFormat(format)/8;
\r
193 if (Format==format && Size.Width==width && Size.Height==height)
\r
197 memcpy(target, Data, (size_t)height*pitch);
\r
202 u8* tgtpos = (u8*) target;
\r
204 const u32 bwidth = width*bpp;
\r
205 const u32 rest = pitch-bwidth;
\r
206 for (u32 y=0; y<height; ++y)
\r
209 memcpy(tgtpos, srcpos, bwidth);
\r
211 memset(tgtpos+bwidth, 0, rest);
\r
219 // NOTE: Scaling is coded to keep the border pixels intact.
\r
220 // Alternatively we could for example work with first pixel being taken at half step-size.
\r
221 // Then we have one more step here and it would be:
\r
222 // sourceXStep = (f32)(Size.Width-1) / (f32)(width);
\r
223 // And sx would start at 0.5f + sourceXStep / 2.f;
\r
225 // As scaling is done without any antialiasing it doesn't matter too much which outermost pixels we use and keeping
\r
226 // border pixels intact is probably mostly better (with AA the other solution would be more correct).
\r
227 // This is however unnecessary (and unexpected) for scaling to integer multiples, so don't do it there.
\r
228 f32 sourceXStep, sourceYStep;
\r
229 f32 sourceXStart = 0.f, sourceYStart = 0.f;
\r
230 if (width % Size.Width == 0)
\r
231 sourceXStep = (f32)(Size.Width) / (f32)(width);
\r
234 sourceXStep = width > 1 ? (f32)(Size.Width-1) / (f32)(width-1) : 0.f;
\r
235 sourceXStart = 0.5f; // for rounding to nearest pixel
\r
237 if (height % Size.Height == 0)
\r
238 sourceYStep = (f32)(Size.Height) / (f32)(height);
\r
241 sourceYStep = height > 1 ? (f32)(Size.Height-1) / (f32)(height-1) : 0.f;
\r
242 sourceYStart = 0.5f; // for rounding to nearest pixel
\r
245 s32 yval=0, syval=0;
\r
246 f32 sy = sourceYStart;
\r
247 for (u32 y=0; y<height; ++y)
\r
249 f32 sx = sourceXStart;
\r
250 for (u32 x=0; x<width; ++x)
\r
252 CColorConverter::convert_viaFormat(Data+ syval + ((s32)sx)*BytesPerPixel, Format, 1, ((u8*)target)+ yval + (x*bpp), format);
\r
256 syval=(s32)(sy)*Pitch;
\r
262 //! copies this surface into another, scaling it to the target image size
\r
263 // note: this is very very slow.
\r
264 void CImage::copyToScaling(IImage* target)
\r
266 if (IImage::isCompressedFormat(Format))
\r
268 os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);
\r
275 const core::dimension2d<u32>& targetSize = target->getDimension();
\r
277 if (targetSize==Size)
\r
283 copyToScaling(target->getData(), targetSize.Width, targetSize.Height, target->getColorFormat());
\r
287 //! copies this surface into another, scaling it to fit it.
\r
288 void CImage::copyToScalingBoxFilter(IImage* target, s32 bias, bool blend)
\r
290 if (IImage::isCompressedFormat(Format))
\r
292 os::Printer::log("IImage::copyToScalingBoxFilter method doesn't work with compressed images.", ELL_WARNING);
\r
296 const core::dimension2d<u32> destSize = target->getDimension();
\r
298 const f32 sourceXStep = (f32) Size.Width / (f32) destSize.Width;
\r
299 const f32 sourceYStep = (f32) Size.Height / (f32) destSize.Height;
\r
301 s32 fx = core::ceil32( sourceXStep );
\r
302 s32 fy = core::ceil32( sourceYStep );
\r
307 for ( u32 y = 0; y != destSize.Height; ++y )
\r
310 for ( u32 x = 0; x != destSize.Width; ++x )
\r
312 target->setPixel( x, y,
\r
313 getPixelBox( core::floor32(sx), core::floor32(sy), fx, fy, bias ), blend );
\r
321 //! fills the surface with given color
\r
322 void CImage::fill(const SColor &color)
\r
324 if (IImage::isCompressedFormat(Format))
\r
326 os::Printer::log("IImage::fill method doesn't work with compressed images.", ELL_WARNING);
\r
335 c = color.toA1R5G5B5();
\r
339 c = video::A8R8G8B8toR5G6B5( color.color );
\r
348 CColorConverter::convert_A8R8G8B8toR8G8B8(&color, 1, rgb);
\r
349 const u32 size = getImageDataSizeInBytes();
\r
350 for (u32 i=0; i<size; i+=3)
\r
352 memcpy(Data+i, rgb, 3);
\r
358 // TODO: Handle other formats
\r
361 memset32( Data, c, getImageDataSizeInBytes() );
\r
365 //! get a filtered pixel
\r
366 inline SColor CImage::getPixelBox( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) const
\r
368 if (IImage::isCompressedFormat(Format))
\r
370 os::Printer::log("IImage::getPixelBox method doesn't work with compressed images.", ELL_WARNING);
\r
375 s32 a = 0, r = 0, g = 0, b = 0;
\r
377 for ( s32 dx = 0; dx != fx; ++dx )
\r
379 for ( s32 dy = 0; dy != fy; ++dy )
\r
381 c = getPixel( core::s32_min ( x + dx, Size.Width - 1 ) ,
\r
382 core::s32_min ( y + dy, Size.Height - 1 )
\r
393 s32 sdiv = s32_log2_s32(fx * fy);
\r
395 a = core::s32_clamp( ( a >> sdiv ) + bias, 0, 255 );
\r
396 r = core::s32_clamp( ( r >> sdiv ) + bias, 0, 255 );
\r
397 g = core::s32_clamp( ( g >> sdiv ) + bias, 0, 255 );
\r
398 b = core::s32_clamp( ( b >> sdiv ) + bias, 0, 255 );
\r
400 c.set( a, r, g, b );
\r
406 } // end namespace video
\r
407 } // end namespace irr
\r