]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CImage.cpp
fcbeacf337d9f2b69a7d53d47aff03f57b9dfb7e
[irrlicht.git] / source / Irrlicht / CImage.cpp
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
4 \r
5 #include "CImage.h"\r
6 #include "irrString.h"\r
7 #include "CColorConverter.h"\r
8 #include "CBlit.h"\r
9 #include "os.h"\r
10 #include "SoftwareDriver2_helper.h"\r
11 \r
12 namespace irr\r
13 {\r
14 namespace video\r
15 {\r
16 \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
20 {\r
21         if (ownForeignMemory)\r
22         {\r
23                 Data = (u8*)data;\r
24         }\r
25         else\r
26         {\r
27                 const u32 dataSize = getDataSizeFromFormat(Format, Size.Width, Size.Height);\r
28 \r
29                 Data = new u8[align_next(dataSize,16)];\r
30                 memcpy(Data, data, dataSize);\r
31                 DeleteMemory = true;\r
32         }\r
33 }\r
34 \r
35 \r
36 //! Constructor of empty image\r
37 CImage::CImage(ECOLOR_FORMAT format, const core::dimension2d<u32>& size) : IImage(format, size, true)\r
38 {\r
39         Data = new u8[align_next(getDataSizeFromFormat(Format, Size.Width, Size.Height),16)];\r
40         DeleteMemory = true;\r
41 }\r
42 \r
43 \r
44 //! sets a pixel\r
45 void CImage::setPixel(u32 x, u32 y, const SColor &color, bool blend)\r
46 {\r
47         if (x >= Size.Width || y >= Size.Height)\r
48                 return;\r
49 \r
50         switch(Format)\r
51         {\r
52                 case ECF_A1R5G5B5:\r
53                 {\r
54                         u16 * dest = (u16*) (Data + ( y * Pitch ) + ( x << 1 ));\r
55                         *dest = video::A8R8G8B8toA1R5G5B5( color.color );\r
56                 } break;\r
57 \r
58                 case ECF_R5G6B5:\r
59                 {\r
60                         u16 * dest = (u16*) (Data + ( y * Pitch ) + ( x << 1 ));\r
61                         *dest = video::A8R8G8B8toR5G6B5( color.color );\r
62                 } break;\r
63 \r
64                 case ECF_R8G8B8:\r
65                 {\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
70                 } break;\r
71 \r
72                 case ECF_A8R8G8B8:\r
73                 {\r
74                         u32 * dest = (u32*) (Data + ( y * Pitch ) + ( x << 2 ));\r
75                         *dest = blend ? PixelBlend32 ( *dest, color.color ) : color.color;\r
76                 } break;\r
77 \r
78                 IRR_CASE_IIMAGE_COMPRESSED_FORMAT\r
79                         os::Printer::log("IImage::setPixel method doesn't work with compressed images.", ELL_WARNING);\r
80                         return;\r
81 \r
82                 case ECF_UNKNOWN:\r
83                         os::Printer::log("IImage::setPixel unknown format.", ELL_WARNING);\r
84                         return;\r
85 \r
86                 default:\r
87                         break;\r
88         }\r
89 }\r
90 \r
91 \r
92 //! returns a pixel\r
93 SColor CImage::getPixel(u32 x, u32 y) const\r
94 {\r
95         if (x >= Size.Width || y >= Size.Height)\r
96                 return SColor(0);\r
97 \r
98         switch(Format)\r
99         {\r
100         case ECF_A1R5G5B5:\r
101                 return A1R5G5B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);\r
102         case ECF_R5G6B5:\r
103                 return R5G6B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);\r
104         case ECF_A8R8G8B8:\r
105                 return ((u32*)Data)[y*Size.Width + x];\r
106         case ECF_R8G8B8:\r
107                 {\r
108                         u8* p = Data+(y*3)*Size.Width + (x*3);\r
109                         return SColor(255,p[0],p[1],p[2]);\r
110                 }\r
111 \r
112         IRR_CASE_IIMAGE_COMPRESSED_FORMAT\r
113                 os::Printer::log("IImage::getPixel method doesn't work with compressed images.", ELL_WARNING);\r
114                 break;\r
115 \r
116         case ECF_UNKNOWN:\r
117                 os::Printer::log("IImage::getPixel unknown format.", ELL_WARNING);\r
118                 break;\r
119 \r
120         default:\r
121                 break;\r
122         }\r
123 \r
124         return SColor(0);\r
125 }\r
126 \r
127 \r
128 //! copies this surface into another at given position\r
129 void CImage::copyTo(IImage* target, const core::position2d<s32>& pos)\r
130 {\r
131         if (IImage::isCompressedFormat(Format))\r
132         {\r
133                 os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);\r
134                 return;\r
135         }\r
136 \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
140         {\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
144         }\r
145 }\r
146 \r
147 \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
150 {\r
151         if (IImage::isCompressedFormat(Format))\r
152         {\r
153                 os::Printer::log("IImage::copyTo method doesn't work with compressed images.", ELL_WARNING);\r
154                 return;\r
155         }\r
156 \r
157         Blit(BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0);\r
158 }\r
159 \r
160 \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
163 {\r
164         if (IImage::isCompressedFormat(Format))\r
165         {\r
166                 os::Printer::log("IImage::copyToWithAlpha method doesn't work with compressed images.", ELL_WARNING);\r
167                 return;\r
168         }\r
169 \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
173 }\r
174 \r
175 \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
179 {\r
180         if (IImage::isCompressedFormat(Format))\r
181         {\r
182                 os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);\r
183                 return;\r
184         }\r
185 \r
186         if (!target || !width || !height || !Size.Width || !Size.Height)\r
187                 return;\r
188 \r
189         const u32 bpp=getBitsPerPixelFromFormat(format)/8;\r
190         if (0==pitch)\r
191                 pitch = width*bpp;\r
192 \r
193         if (Format==format && Size.Width==width && Size.Height==height)\r
194         {\r
195                 if (pitch==Pitch)\r
196                 {\r
197                         memcpy(target, Data, height*pitch);\r
198                         return;\r
199                 }\r
200                 else\r
201                 {\r
202                         u8* tgtpos = (u8*) target;\r
203                         u8* srcpos = Data;\r
204                         const u32 bwidth = width*bpp;\r
205                         const u32 rest = pitch-bwidth;\r
206                         for (u32 y=0; y<height; ++y)\r
207                         {\r
208                                 // copy scanline\r
209                                 memcpy(tgtpos, srcpos, bwidth);\r
210                                 // clear pitch\r
211                                 memset(tgtpos+bwidth, 0, rest);\r
212                                 tgtpos += pitch;\r
213                                 srcpos += Pitch;\r
214                         }\r
215                         return;\r
216                 }\r
217         }\r
218 \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
224         //     Similar for y.\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
232         else\r
233         {\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
236         }\r
237         if (height % Size.Height == 0)\r
238                 sourceYStep = (f32)(Size.Height) / (f32)(height);\r
239         else\r
240         {\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
243         }\r
244 \r
245         s32 yval=0, syval=0;\r
246         f32 sy = sourceYStart;\r
247         for (u32 y=0; y<height; ++y)\r
248         {\r
249                 f32 sx = sourceXStart;\r
250                 for (u32 x=0; x<width; ++x)\r
251                 {\r
252                         CColorConverter::convert_viaFormat(Data+ syval + ((s32)sx)*BytesPerPixel, Format, 1, ((u8*)target)+ yval + (x*bpp), format);\r
253                         sx+=sourceXStep;\r
254                 }\r
255                 sy+=sourceYStep;\r
256                 syval=(s32)(sy)*Pitch;\r
257                 yval+=pitch;\r
258         }\r
259 }\r
260 \r
261 \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
265 {\r
266         if (IImage::isCompressedFormat(Format))\r
267         {\r
268                 os::Printer::log("IImage::copyToScaling method doesn't work with compressed images.", ELL_WARNING);\r
269                 return;\r
270         }\r
271 \r
272         if (!target)\r
273                 return;\r
274 \r
275         const core::dimension2d<u32>& targetSize = target->getDimension();\r
276 \r
277         if (targetSize==Size)\r
278         {\r
279                 copyTo(target);\r
280                 return;\r
281         }\r
282 \r
283         copyToScaling(target->getData(), targetSize.Width, targetSize.Height, target->getColorFormat());\r
284 }\r
285 \r
286 \r
287 //! copies this surface into another, scaling it to fit it.\r
288 void CImage::copyToScalingBoxFilter(IImage* target, s32 bias, bool blend)\r
289 {\r
290         if (IImage::isCompressedFormat(Format))\r
291         {\r
292                 os::Printer::log("IImage::copyToScalingBoxFilter method doesn't work with compressed images.", ELL_WARNING);\r
293                 return;\r
294         }\r
295 \r
296         const core::dimension2d<u32> destSize = target->getDimension();\r
297 \r
298         const f32 sourceXStep = (f32) Size.Width / (f32) destSize.Width;\r
299         const f32 sourceYStep = (f32) Size.Height / (f32) destSize.Height;\r
300 \r
301         s32 fx = core::ceil32( sourceXStep );\r
302         s32 fy = core::ceil32( sourceYStep );\r
303         f32 sx;\r
304         f32 sy;\r
305 \r
306         sy = 0.f;\r
307         for ( u32 y = 0; y != destSize.Height; ++y )\r
308         {\r
309                 sx = 0.f;\r
310                 for ( u32 x = 0; x != destSize.Width; ++x )\r
311                 {\r
312                         target->setPixel( x, y,\r
313                                 getPixelBox( core::floor32(sx), core::floor32(sy), fx, fy, bias ), blend );\r
314                         sx += sourceXStep;\r
315                 }\r
316                 sy += sourceYStep;\r
317         }\r
318 }\r
319 \r
320 \r
321 //! fills the surface with given color\r
322 void CImage::fill(const SColor &color)\r
323 {\r
324         if (IImage::isCompressedFormat(Format))\r
325         {\r
326                 os::Printer::log("IImage::fill method doesn't work with compressed images.", ELL_WARNING);\r
327                 return;\r
328         }\r
329 \r
330         u32 c;\r
331 \r
332         switch ( Format )\r
333         {\r
334                 case ECF_A1R5G5B5:\r
335                         c = color.toA1R5G5B5();\r
336                         c |= c << 16;\r
337                         break;\r
338                 case ECF_R5G6B5:\r
339                         c = video::A8R8G8B8toR5G6B5( color.color );\r
340                         c |= c << 16;\r
341                         break;\r
342                 case ECF_A8R8G8B8:\r
343                         c = color.color;\r
344                         break;\r
345                 case ECF_R8G8B8:\r
346                 {\r
347                         u8 rgb[3];\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
351                         {\r
352                                 memcpy(Data+i, rgb, 3);\r
353                         }\r
354                         return;\r
355                 }\r
356                 break;\r
357                 default:\r
358                 // TODO: Handle other formats\r
359                         return;\r
360         }\r
361         memset32( Data, c, getImageDataSizeInBytes() );\r
362 }\r
363 \r
364 \r
365 //! get a filtered pixel\r
366 inline SColor CImage::getPixelBox( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) const\r
367 {\r
368         if (IImage::isCompressedFormat(Format))\r
369         {\r
370                 os::Printer::log("IImage::getPixelBox method doesn't work with compressed images.", ELL_WARNING);\r
371                 return SColor(0);\r
372         }\r
373 \r
374         SColor c;\r
375         s32 a = 0, r = 0, g = 0, b = 0;\r
376 \r
377         for ( s32 dx = 0; dx != fx; ++dx )\r
378         {\r
379                 for ( s32 dy = 0; dy != fy; ++dy )\r
380                 {\r
381                         c = getPixel(   core::s32_min ( x + dx, Size.Width - 1 ) ,\r
382                                                         core::s32_min ( y + dy, Size.Height - 1 )\r
383                                                 );\r
384 \r
385                         a += c.getAlpha();\r
386                         r += c.getRed();\r
387                         g += c.getGreen();\r
388                         b += c.getBlue();\r
389                 }\r
390 \r
391         }\r
392 \r
393         s32 sdiv = s32_log2_s32(fx * fy);\r
394 \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
399 \r
400         c.set( a, r, g, b );\r
401         return c;\r
402 }\r
403 \r
404 \r
405 \r
406 } // end namespace video\r
407 } // end namespace irr\r