/src/ogre/OgreMain/src/OgreImage.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | #include "OgreImage.h" |
30 | | #include "OgreImageCodec.h" |
31 | | #include "OgreImageResampler.h" |
32 | | |
33 | | namespace Ogre { |
34 | | //----------------------------------------------------------------------------- |
35 | | Image::Image(PixelFormat format, uint32 width, uint32 height, uint32 depth, uchar* buffer, bool autoDelete) |
36 | 2.67k | : mWidth(0), |
37 | 2.67k | mHeight(0), |
38 | 2.67k | mDepth(0), |
39 | 2.67k | mNumMipmaps(0), |
40 | 2.67k | mBufSize(0), |
41 | 2.67k | mFlags(0), |
42 | 2.67k | mFormat(format), |
43 | | mBuffer( NULL ), |
44 | 2.67k | mAutoDelete( true ) |
45 | 2.67k | { |
46 | 2.67k | if (format == PF_UNKNOWN) |
47 | 2.67k | return; |
48 | | |
49 | 0 | size_t size = calculateSize(0, 1, width, height, depth, mFormat); |
50 | |
|
51 | 0 | if (size == 0) |
52 | 0 | return; |
53 | | |
54 | 0 | if (!buffer) |
55 | 0 | buffer = (uchar*)malloc(size); |
56 | 0 | loadDynamicImage(buffer, width, height, depth, format, autoDelete); |
57 | 0 | } |
58 | | |
59 | | void Image::create(PixelFormat format, uint32 width, uint32 height, uint32 depth, uint32 numFaces, |
60 | | uint32 numMipMaps) |
61 | 0 | { |
62 | 0 | size_t size = calculateSize(numMipMaps, numFaces, width, height, depth, format); |
63 | 0 | if (!mAutoDelete || !mBuffer || mBufSize != size) |
64 | 0 | { |
65 | 0 | freeMemory(); |
66 | 0 | mBuffer = (uchar*)malloc(size); // allocate |
67 | 0 | } |
68 | | |
69 | | // make sure freeMemory() does nothing, we set this true immediately after |
70 | 0 | mAutoDelete = false; |
71 | 0 | loadDynamicImage(mBuffer, width, height, depth, format, true, numFaces, numMipMaps); |
72 | 0 | } |
73 | | |
74 | | //----------------------------------------------------------------------------- |
75 | | Image::Image( const Image &img ) |
76 | | : mBuffer( NULL ), |
77 | 0 | mAutoDelete( true ) |
78 | 0 | { |
79 | | // call assignment operator |
80 | 0 | *this = img; |
81 | 0 | } |
82 | | |
83 | | //----------------------------------------------------------------------------- |
84 | | Image::~Image() |
85 | 2.67k | { |
86 | 2.67k | freeMemory(); |
87 | 2.67k | } |
88 | | //--------------------------------------------------------------------- |
89 | | void Image::freeMemory() |
90 | 6.38k | { |
91 | | //Only delete if this was not a dynamic image (meaning app holds & destroys buffer) |
92 | 6.38k | if( mBuffer && mAutoDelete ) |
93 | 1.03k | { |
94 | 1.03k | free(mBuffer); |
95 | 1.03k | mBuffer = NULL; |
96 | 1.03k | } |
97 | | |
98 | 6.38k | } |
99 | | |
100 | | //----------------------------------------------------------------------------- |
101 | | Image& Image::operator=(const Image& img) |
102 | 0 | { |
103 | 0 | if (this == &img) |
104 | 0 | return *this; |
105 | | |
106 | | // Only create & copy when other data was owning |
107 | 0 | if (img.mBuffer && img.mAutoDelete) |
108 | 0 | { |
109 | 0 | create(img.mFormat, img.mWidth, img.mHeight, img.mDepth, img.getNumFaces(), img.mNumMipmaps); |
110 | 0 | memcpy(mBuffer, img.mBuffer, mBufSize); |
111 | 0 | } |
112 | 0 | else |
113 | 0 | { |
114 | 0 | loadDynamicImage(img.mBuffer, img.mWidth, img.mHeight, img.mDepth, img.mFormat, false, |
115 | 0 | img.getNumFaces(), img.mNumMipmaps); |
116 | 0 | } |
117 | |
|
118 | 0 | return *this; |
119 | 0 | } |
120 | | |
121 | | void Image::setTo(const ColourValue& col) |
122 | 0 | { |
123 | 0 | OgreAssert(mBuffer, "No image data loaded"); |
124 | 0 | if(col == ColourValue::ZERO) |
125 | 0 | { |
126 | 0 | memset(mBuffer, 0, getSize()); |
127 | 0 | return; |
128 | 0 | } |
129 | | |
130 | 0 | uchar rawCol[4 * sizeof(float)]; // max packed size currently is 4*float |
131 | 0 | PixelUtil::packColour(col, mFormat, rawCol); |
132 | 0 | for(size_t p = 0; p < mBufSize; p += mPixelSize) |
133 | 0 | { |
134 | 0 | memcpy(mBuffer + p, rawCol, mPixelSize); |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | //----------------------------------------------------------------------------- |
139 | | Image & Image::flipAroundY() |
140 | 0 | { |
141 | 0 | OgreAssert(mBuffer, "No image data loaded"); |
142 | 0 | mNumMipmaps = 0; // Image operations lose precomputed mipmaps |
143 | |
|
144 | 0 | uint32 y; |
145 | 0 | switch (mPixelSize) |
146 | 0 | { |
147 | 0 | case 1: |
148 | 0 | for (y = 0; y < mHeight; y++) |
149 | 0 | { |
150 | 0 | std::reverse(mBuffer + mWidth * y, mBuffer + mWidth * (y + 1)); |
151 | 0 | } |
152 | 0 | break; |
153 | | |
154 | 0 | case 2: |
155 | 0 | for (y = 0; y < mHeight; y++) |
156 | 0 | { |
157 | 0 | std::reverse((ushort*)mBuffer + mWidth * y, (ushort*)mBuffer + mWidth * (y + 1)); |
158 | 0 | } |
159 | 0 | break; |
160 | | |
161 | 0 | case 3: |
162 | 0 | typedef uchar uchar3[3]; |
163 | 0 | for (y = 0; y < mHeight; y++) |
164 | 0 | { |
165 | 0 | std::reverse((uchar3*)mBuffer + mWidth * y, (uchar3*)mBuffer + mWidth * (y + 1)); |
166 | 0 | } |
167 | 0 | break; |
168 | | |
169 | 0 | case 4: |
170 | 0 | for (y = 0; y < mHeight; y++) |
171 | 0 | { |
172 | 0 | std::reverse((uint*)mBuffer + mWidth * y, (uint*)mBuffer + mWidth * (y + 1)); |
173 | 0 | } |
174 | 0 | break; |
175 | | |
176 | 0 | default: |
177 | 0 | OGRE_EXCEPT( |
178 | 0 | Exception::ERR_INTERNAL_ERROR, |
179 | 0 | "Unknown pixel depth", |
180 | 0 | "Image::flipAroundY" ); |
181 | 0 | break; |
182 | 0 | } |
183 | | |
184 | 0 | return *this; |
185 | |
|
186 | 0 | } |
187 | | |
188 | | //----------------------------------------------------------------------------- |
189 | | Image & Image::flipAroundX() |
190 | 0 | { |
191 | 0 | OgreAssert(mBuffer, "No image data loaded"); |
192 | | |
193 | 0 | mNumMipmaps = 0; // Image operations lose precomputed mipmaps |
194 | 0 | PixelUtil::bulkPixelVerticalFlip(getPixelBox()); |
195 | |
|
196 | 0 | return *this; |
197 | 0 | } |
198 | | |
199 | | //----------------------------------------------------------------------------- |
200 | | Image& Image::loadDynamicImage(uchar* pData, uint32 uWidth, uint32 uHeight, uint32 depth, |
201 | | PixelFormat eFormat, bool autoDelete, uint32 numFaces, uint32 numMipMaps) |
202 | 1.03k | { |
203 | | |
204 | 1.03k | freeMemory(); |
205 | | // Set image metadata |
206 | 1.03k | mWidth = uWidth; |
207 | 1.03k | mHeight = uHeight; |
208 | 1.03k | mDepth = depth; |
209 | 1.03k | mFormat = eFormat; |
210 | 1.03k | mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat )); |
211 | 1.03k | mNumMipmaps = numMipMaps; |
212 | 1.03k | mFlags = 0; |
213 | | // Set flags |
214 | 1.03k | if (PixelUtil::isCompressed(eFormat)) |
215 | 0 | mFlags |= IF_COMPRESSED; |
216 | 1.03k | if (mDepth != 1) |
217 | 0 | mFlags |= IF_3D_TEXTURE; |
218 | 1.03k | if(numFaces == 6) |
219 | 0 | mFlags |= IF_CUBEMAP; |
220 | 1.03k | OgreAssert(numFaces == 6 || numFaces == 1, "Invalid number of faces"); |
221 | | |
222 | 1.03k | mBufSize = calculateSize(numMipMaps, numFaces, uWidth, uHeight, depth, eFormat); |
223 | 1.03k | mBuffer = pData; |
224 | 1.03k | mAutoDelete = autoDelete; |
225 | | |
226 | 1.03k | return *this; |
227 | 1.03k | } |
228 | | |
229 | | //----------------------------------------------------------------------------- |
230 | | Image& Image::loadRawData(const DataStreamPtr& stream, uint32 uWidth, uint32 uHeight, uint32 uDepth, |
231 | | PixelFormat eFormat, uint32 numFaces, uint32 numMipMaps) |
232 | 0 | { |
233 | |
|
234 | 0 | size_t size = calculateSize(numMipMaps, numFaces, uWidth, uHeight, uDepth, eFormat); |
235 | 0 | OgreAssert(size == stream->size(), "Wrong stream size"); |
236 | | |
237 | 0 | uchar *buffer = OGRE_ALLOC_T(uchar, size, MEMCATEGORY_GENERAL); |
238 | 0 | stream->read(buffer, size); |
239 | |
|
240 | 0 | return loadDynamicImage(buffer, |
241 | 0 | uWidth, uHeight, uDepth, |
242 | 0 | eFormat, true, numFaces, numMipMaps); |
243 | |
|
244 | 0 | } |
245 | | //----------------------------------------------------------------------------- |
246 | | Image & Image::load(const String& strFileName, const String& group) |
247 | 0 | { |
248 | |
|
249 | 0 | String strExt; |
250 | |
|
251 | 0 | size_t pos = strFileName.find_last_of('.'); |
252 | 0 | if( pos != String::npos && pos < (strFileName.length() - 1)) |
253 | 0 | { |
254 | 0 | strExt = strFileName.substr(pos+1); |
255 | 0 | } |
256 | |
|
257 | 0 | DataStreamPtr encoded = ResourceGroupManager::getSingleton().openResource(strFileName, group); |
258 | 0 | return load(encoded, strExt); |
259 | |
|
260 | 0 | } |
261 | | //----------------------------------------------------------------------------- |
262 | | void Image::save(const String& filename) |
263 | 1.03k | { |
264 | 1.03k | OgreAssert(mBuffer, "No image data loaded"); |
265 | | |
266 | 1.03k | String base, ext; |
267 | 1.03k | StringUtil::splitBaseFilename(filename, base, ext); |
268 | | |
269 | | // getCodec throws when no codec is found |
270 | 1.03k | Codec::getCodec(ext)->encodeToFile(this, filename); |
271 | 1.03k | } |
272 | | //--------------------------------------------------------------------- |
273 | | DataStreamPtr Image::encode(const String& formatextension) |
274 | 0 | { |
275 | 0 | OgreAssert(mBuffer, "No image data loaded"); |
276 | | // getCodec throws when no codec is found |
277 | 0 | return Codec::getCodec(formatextension)->encode(this); |
278 | 0 | } |
279 | | //----------------------------------------------------------------------------- |
280 | | Image & Image::load(const DataStreamPtr& stream, String type ) |
281 | 2.67k | { |
282 | 2.67k | freeMemory(); |
283 | | |
284 | 2.67k | if (type.empty()) |
285 | 0 | { |
286 | 0 | String base, ext; |
287 | 0 | StringUtil::splitBaseFilename(stream->getName(), base, ext); |
288 | 0 | if (!ext.empty()) |
289 | 0 | type = ext; |
290 | 0 | } |
291 | | |
292 | 2.67k | Codec * pCodec = 0; |
293 | 2.67k | if (!type.empty()) |
294 | 2.67k | { |
295 | | // use named codec |
296 | 2.67k | pCodec = Codec::getCodec(type); |
297 | 2.67k | } |
298 | 0 | else |
299 | 0 | { |
300 | | // derive from magic number |
301 | | // read the first 32 bytes or file size, if less |
302 | 0 | size_t magicLen = std::min(stream->size(), (size_t)32); |
303 | 0 | char magicBuf[32]; |
304 | 0 | stream->read(magicBuf, magicLen); |
305 | | // return to start |
306 | 0 | stream->seek(0); |
307 | 0 | pCodec = Codec::getCodec(magicBuf, magicLen); |
308 | |
|
309 | 0 | if (!pCodec) |
310 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
311 | 0 | "Unable to load image: Image format is unknown. Unable to identify codec. " |
312 | 0 | "Check it or specify format explicitly."); |
313 | 0 | } |
314 | | |
315 | 2.67k | pCodec->decode(stream, this); |
316 | | |
317 | | // compute the pixel size |
318 | 2.67k | mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat )); |
319 | | // make sure we delete |
320 | 2.67k | mAutoDelete = true; |
321 | | |
322 | 2.67k | return *this; |
323 | 2.67k | } |
324 | | //--------------------------------------------------------------------- |
325 | | String Image::getFileExtFromMagic(const DataStreamPtr stream) |
326 | 0 | { |
327 | | // read the first 32 bytes or file size, if less |
328 | 0 | size_t magicLen = std::min(stream->size(), (size_t)32); |
329 | 0 | char magicBuf[32]; |
330 | 0 | stream->read(magicBuf, magicLen); |
331 | | // return to start |
332 | 0 | stream->seek(0); |
333 | 0 | Codec* pCodec = Codec::getCodec(magicBuf, magicLen); |
334 | |
|
335 | 0 | if(pCodec) |
336 | 0 | return pCodec->getType(); |
337 | 0 | else |
338 | 0 | return BLANKSTRING; |
339 | |
|
340 | 0 | } |
341 | | |
342 | | //----------------------------------------------------------------------------- |
343 | | bool Image::getHasAlpha(void) const |
344 | 0 | { |
345 | 0 | return PixelUtil::getFlags(mFormat) & PFF_HASALPHA; |
346 | 0 | } |
347 | | //----------------------------------------------------------------------------- |
348 | | void Image::applyGamma( uchar *buffer, Real gamma, size_t size, uchar bpp ) |
349 | 0 | { |
350 | 0 | if( gamma == 1.0f ) |
351 | 0 | return; |
352 | | |
353 | 0 | OgreAssert( bpp == 24 || bpp == 32, ""); |
354 | | |
355 | 0 | uint stride = bpp >> 3; |
356 | | |
357 | 0 | uchar gammaramp[256]; |
358 | 0 | const Real exponent = 1.0f / gamma; |
359 | 0 | for(int i = 0; i < 256; i++) { |
360 | 0 | gammaramp[i] = static_cast<uchar>(Math::Pow(i/255.0f, exponent)*255+0.5f); |
361 | 0 | } |
362 | |
|
363 | 0 | for( size_t i = 0, j = size / stride; i < j; i++, buffer += stride ) |
364 | 0 | { |
365 | 0 | buffer[0] = gammaramp[buffer[0]]; |
366 | 0 | buffer[1] = gammaramp[buffer[1]]; |
367 | 0 | buffer[2] = gammaramp[buffer[2]]; |
368 | 0 | } |
369 | 0 | } |
370 | | //----------------------------------------------------------------------------- |
371 | | void Image::resize(ushort width, ushort height, Filter filter) |
372 | 0 | { |
373 | 0 | OgreAssert(mAutoDelete, "resizing dynamic images is not supported"); |
374 | 0 | OgreAssert(mDepth == 1, "only 2D formats supported"); |
375 | | |
376 | | // reassign buffer to temp image, make sure auto-delete is true |
377 | 0 | Image temp(mFormat, mWidth, mHeight, 1, mBuffer, true); |
378 | | |
379 | | // do not delete[] mBuffer! temp will destroy it |
380 | 0 | mBuffer = 0; |
381 | | |
382 | | // set new dimensions, allocate new buffer |
383 | 0 | create(mFormat, width, height); // Loses precomputed mipmaps |
384 | | |
385 | | // scale the image from temp into our resized buffer |
386 | 0 | Image::scale(temp.getPixelBox(), getPixelBox(), filter); |
387 | 0 | } |
388 | | //----------------------------------------------------------------------- |
389 | | void Image::scale(const PixelBox &src, const PixelBox &scaled, Filter filter) |
390 | 0 | { |
391 | 0 | assert(PixelUtil::isAccessible(src.format)); |
392 | 0 | assert(PixelUtil::isAccessible(scaled.format)); |
393 | 0 | Image buf; // For auto-delete |
394 | | // Assume no intermediate buffer needed |
395 | 0 | PixelBox temp = scaled; |
396 | 0 | switch (filter) |
397 | 0 | { |
398 | 0 | default: |
399 | 0 | case FILTER_NEAREST: |
400 | 0 | if(src.format != scaled.format) |
401 | 0 | { |
402 | | // Allocate temporary buffer of destination size in source format |
403 | 0 | buf.create(src.format, scaled.getWidth(), scaled.getHeight(), scaled.getDepth()); |
404 | 0 | temp = buf.getPixelBox(); |
405 | 0 | } |
406 | | // super-optimized: no conversion |
407 | 0 | switch (PixelUtil::getNumElemBytes(src.format)) |
408 | 0 | { |
409 | 0 | case 1: NearestResampler<1>::scale(src, temp); break; |
410 | 0 | case 2: NearestResampler<2>::scale(src, temp); break; |
411 | 0 | case 3: NearestResampler<3>::scale(src, temp); break; |
412 | 0 | case 4: NearestResampler<4>::scale(src, temp); break; |
413 | 0 | case 6: NearestResampler<6>::scale(src, temp); break; |
414 | 0 | case 8: NearestResampler<8>::scale(src, temp); break; |
415 | 0 | case 12: NearestResampler<12>::scale(src, temp); break; |
416 | 0 | case 16: NearestResampler<16>::scale(src, temp); break; |
417 | 0 | default: |
418 | | // never reached |
419 | 0 | assert(false); |
420 | 0 | } |
421 | 0 | if(temp.data != scaled.data) |
422 | 0 | { |
423 | | // Blit temp buffer |
424 | 0 | PixelUtil::bulkPixelConversion(temp, scaled); |
425 | 0 | } |
426 | 0 | break; |
427 | | |
428 | 0 | case FILTER_BILINEAR: |
429 | 0 | switch (src.format) |
430 | 0 | { |
431 | 0 | case PF_L8: case PF_R8: case PF_A8: case PF_BYTE_LA: |
432 | 0 | case PF_R8G8B8: case PF_B8G8R8: |
433 | 0 | case PF_R8G8B8A8: case PF_B8G8R8A8: |
434 | 0 | case PF_A8B8G8R8: case PF_A8R8G8B8: |
435 | 0 | case PF_X8B8G8R8: case PF_X8R8G8B8: |
436 | 0 | if(src.format != scaled.format) |
437 | 0 | { |
438 | | // Allocate temp buffer of destination size in source format |
439 | 0 | buf.create(src.format, scaled.getWidth(), scaled.getHeight(), scaled.getDepth()); |
440 | 0 | temp = buf.getPixelBox(); |
441 | 0 | } |
442 | | // super-optimized: byte-oriented math, no conversion |
443 | 0 | switch (PixelUtil::getNumElemBytes(src.format)) |
444 | 0 | { |
445 | 0 | case 1: LinearResampler_Byte<1>::scale(src, temp); break; |
446 | 0 | case 2: LinearResampler_Byte<2>::scale(src, temp); break; |
447 | 0 | case 3: LinearResampler_Byte<3>::scale(src, temp); break; |
448 | 0 | case 4: LinearResampler_Byte<4>::scale(src, temp); break; |
449 | 0 | default: |
450 | | // never reached |
451 | 0 | assert(false); |
452 | 0 | } |
453 | 0 | if(temp.data != scaled.data) |
454 | 0 | { |
455 | | // Blit temp buffer |
456 | 0 | PixelUtil::bulkPixelConversion(temp, scaled); |
457 | 0 | } |
458 | 0 | break; |
459 | 0 | case PF_FLOAT32_RGB: |
460 | 0 | case PF_FLOAT32_RGBA: |
461 | 0 | if (scaled.format == PF_FLOAT32_RGB || scaled.format == PF_FLOAT32_RGBA) |
462 | 0 | { |
463 | | // float32 to float32, avoid unpack/repack overhead |
464 | 0 | LinearResampler_Float32::scale(src, scaled); |
465 | 0 | break; |
466 | 0 | } |
467 | | // else, fall through |
468 | 0 | default: |
469 | | // non-optimized: floating-point math, performs conversion but always works |
470 | 0 | LinearResampler::scale(src, scaled); |
471 | 0 | } |
472 | 0 | break; |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | //----------------------------------------------------------------------------- |
477 | | |
478 | | ColourValue Image::getColourAt(uint32 x, uint32 y, uint32 z) const |
479 | 0 | { |
480 | 0 | ColourValue rval; |
481 | 0 | PixelUtil::unpackColour(&rval, mFormat, getData(x, y, z)); |
482 | 0 | return rval; |
483 | 0 | } |
484 | | |
485 | | //----------------------------------------------------------------------------- |
486 | | |
487 | | void Image::setColourAt(ColourValue const &cv, uint32 x, uint32 y, uint32 z) |
488 | 0 | { |
489 | 0 | PixelUtil::packColour(cv, mFormat, getData(x, y, z)); |
490 | 0 | } |
491 | | |
492 | | //----------------------------------------------------------------------------- |
493 | | |
494 | | PixelBox Image::getPixelBox(uint32 face, uint32 mipmap) const |
495 | 0 | { |
496 | | // Image data is arranged as: |
497 | | // face 0, top level (mip 0) |
498 | | // face 0, mip 1 |
499 | | // face 0, mip 2 |
500 | | // face 1, top level (mip 0) |
501 | | // face 1, mip 1 |
502 | | // face 1, mip 2 |
503 | | // etc |
504 | 0 | OgreAssert(mipmap <= getNumMipmaps(), "out of range"); |
505 | 0 | OgreAssert(face < getNumFaces(), "out of range"); |
506 | | // Calculate mipmap offset and size |
507 | 0 | uint8 *offset = mBuffer; |
508 | | // Base offset is number of full faces |
509 | 0 | uint32 width = getWidth(), height=getHeight(), depth=getDepth(); |
510 | 0 | uint32 numMips = getNumMipmaps(); |
511 | | |
512 | | // Figure out the offsets |
513 | 0 | size_t fullFaceSize = 0; |
514 | 0 | size_t finalFaceSize = 0; |
515 | 0 | uint32 finalWidth = 0, finalHeight = 0, finalDepth = 0; |
516 | 0 | for(uint32 mip=0; mip <= numMips; ++mip) |
517 | 0 | { |
518 | 0 | if (mip == mipmap) |
519 | 0 | { |
520 | 0 | finalFaceSize = fullFaceSize; |
521 | 0 | finalWidth = width; |
522 | 0 | finalHeight = height; |
523 | 0 | finalDepth = depth; |
524 | 0 | } |
525 | 0 | fullFaceSize += PixelUtil::getMemorySize(width, height, depth, getFormat()); |
526 | | |
527 | | /// Half size in each dimension |
528 | 0 | if(width!=1) width /= 2; |
529 | 0 | if(height!=1) height /= 2; |
530 | 0 | if(depth!=1) depth /= 2; |
531 | 0 | } |
532 | | // Advance pointer by number of full faces, plus mip offset into |
533 | 0 | offset += face * fullFaceSize; |
534 | 0 | offset += finalFaceSize; |
535 | | // Return subface as pixelbox |
536 | 0 | PixelBox src(finalWidth, finalHeight, finalDepth, getFormat(), offset); |
537 | 0 | return src; |
538 | 0 | } |
539 | | //----------------------------------------------------------------------------- |
540 | | size_t Image::calculateSize(uint32 mipmaps, uint32 faces, uint32 width, uint32 height, uint32 depth, |
541 | | PixelFormat format) |
542 | 1.03k | { |
543 | 1.03k | size_t size = 0; |
544 | 2.06k | for(uint32 mip=0; mip<=mipmaps; ++mip) |
545 | 1.03k | { |
546 | 1.03k | size += PixelUtil::getMemorySize(width, height, depth, format)*faces; |
547 | 1.03k | if(width!=1) width /= 2; |
548 | 1.03k | if(height!=1) height /= 2; |
549 | 1.03k | if(depth!=1) depth /= 2; |
550 | 1.03k | } |
551 | 1.03k | return size; |
552 | 1.03k | } |
553 | | //--------------------------------------------------------------------- |
554 | | Image & Image::loadTwoImagesAsRGBA(const String& rgbFilename, const String& alphaFilename, |
555 | | const String& groupName, PixelFormat fmt) |
556 | 0 | { |
557 | 0 | Image rgb, alpha; |
558 | |
|
559 | 0 | rgb.load(rgbFilename, groupName); |
560 | 0 | alpha.load(alphaFilename, groupName); |
561 | |
|
562 | 0 | return combineTwoImagesAsRGBA(rgb, alpha, fmt); |
563 | |
|
564 | 0 | } |
565 | | //--------------------------------------------------------------------- |
566 | | Image& Image::loadTwoImagesAsRGBA(const DataStreamPtr& rgbStream, |
567 | | const DataStreamPtr& alphaStream, PixelFormat fmt, |
568 | | const String& rgbType, const String& alphaType) |
569 | 0 | { |
570 | 0 | Image rgb, alpha; |
571 | |
|
572 | 0 | rgb.load(rgbStream, rgbType); |
573 | 0 | alpha.load(alphaStream, alphaType); |
574 | |
|
575 | 0 | return combineTwoImagesAsRGBA(rgb, alpha, fmt); |
576 | |
|
577 | 0 | } |
578 | | //--------------------------------------------------------------------- |
579 | | Image & Image::combineTwoImagesAsRGBA(const Image& rgb, const Image& alpha, PixelFormat fmt) |
580 | 0 | { |
581 | | // the images should be the same size, have the same number of mipmaps |
582 | 0 | OgreAssert(rgb.getWidth() == alpha.getWidth() && rgb.getHeight() == alpha.getHeight() && |
583 | 0 | rgb.getDepth() == alpha.getDepth(), |
584 | 0 | "Images must be the same dimensions"); |
585 | 0 | OgreAssert(rgb.getNumMipmaps() == alpha.getNumMipmaps() && rgb.getNumFaces() == alpha.getNumFaces(), |
586 | 0 | "Images must have the same number of surfaces"); |
587 | | |
588 | | // Format check |
589 | 0 | OgreAssert(PixelUtil::getComponentCount(fmt) == 4, "Target format must have 4 components"); |
590 | | |
591 | 0 | OgreAssert(!(PixelUtil::isCompressed(fmt) || PixelUtil::isCompressed(rgb.getFormat()) || |
592 | 0 | PixelUtil::isCompressed(alpha.getFormat())), |
593 | 0 | "Compressed formats are not supported in this method"); |
594 | | |
595 | 0 | uint32 numFaces = rgb.getNumFaces(); |
596 | 0 | create(fmt, rgb.getWidth(), rgb.getHeight(), rgb.getDepth(), numFaces, rgb.getNumMipmaps()); |
597 | |
|
598 | 0 | for (uint32 face = 0; face < numFaces; ++face) |
599 | 0 | { |
600 | 0 | for (uint8 mip = 0; mip <= mNumMipmaps; ++mip) |
601 | 0 | { |
602 | | // convert the RGB first |
603 | 0 | PixelBox srcRGB = rgb.getPixelBox(face, mip); |
604 | 0 | PixelBox dst = getPixelBox(face, mip); |
605 | 0 | PixelUtil::bulkPixelConversion(srcRGB, dst); |
606 | | |
607 | | // now selectively add the alpha |
608 | 0 | PixelBox srcAlpha = alpha.getPixelBox(face, mip); |
609 | 0 | uchar* psrcAlpha = srcAlpha.data; |
610 | 0 | uchar* pdst = dst.data; |
611 | 0 | for (uint32 d = 0; d < mDepth; ++d) |
612 | 0 | { |
613 | 0 | for (uint32 y = 0; y < mHeight; ++y) |
614 | 0 | { |
615 | 0 | for (uint32 x = 0; x < mWidth; ++x) |
616 | 0 | { |
617 | 0 | ColourValue colRGBA, colA; |
618 | | // read RGB back from dest to save having another pointer |
619 | 0 | PixelUtil::unpackColour(&colRGBA, mFormat, pdst); |
620 | 0 | PixelUtil::unpackColour(&colA, alpha.getFormat(), psrcAlpha); |
621 | | |
622 | | // combine RGB from alpha source texture |
623 | 0 | colRGBA.a = (colA.r + colA.g + colA.b) / 3.0f; |
624 | |
|
625 | 0 | PixelUtil::packColour(colRGBA, mFormat, pdst); |
626 | | |
627 | 0 | psrcAlpha += PixelUtil::getNumElemBytes(alpha.getFormat()); |
628 | 0 | pdst += PixelUtil::getNumElemBytes(mFormat); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |
632 | 0 | } |
633 | 0 | } |
634 | |
|
635 | 0 | return *this; |
636 | 0 | } |
637 | | //--------------------------------------------------------------------- |
638 | | |
639 | | |
640 | | } |