/src/ogre/OgreMain/src/OgrePixelFormat.cpp
Line | Count | Source |
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 "OgrePixelFormat.h" |
30 | | #include "OgrePixelFormatDescriptions.h" |
31 | | |
32 | | namespace { |
33 | | #include "OgrePixelConversions.h" |
34 | | } |
35 | | |
36 | | namespace Ogre { |
37 | | |
38 | | //----------------------------------------------------------------------- |
39 | | size_t PixelBox::getConsecutiveSize() const |
40 | 0 | { |
41 | 0 | return PixelUtil::getMemorySize(getWidth(), getHeight(), getDepth(), format); |
42 | 0 | } |
43 | | PixelBox PixelBox::getSubVolume(const Box &def, bool resetOrigin /* = true */) const |
44 | 0 | { |
45 | 0 | OgreAssert(contains(def), ""); |
46 | | |
47 | 0 | if(PixelUtil::isCompressed(format) && (def.left != left || def.top != top || def.right != right || def.bottom != bottom)) |
48 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Cannot return subvolume of compressed PixelBuffer with less than slice granularity", "PixelBox::getSubVolume"); |
49 | | |
50 | | // Calculate new pixelbox and optionally reset origin. |
51 | 0 | PixelBox rval(def, format, data); |
52 | 0 | rval.rowPitch = rowPitch; |
53 | 0 | rval.slicePitch = slicePitch; |
54 | |
|
55 | 0 | if(resetOrigin) |
56 | 0 | { |
57 | 0 | if(PixelUtil::isCompressed(format)) |
58 | 0 | { |
59 | 0 | if(rval.front > 0) |
60 | 0 | { |
61 | 0 | rval.data = (uint8*)rval.data + rval.front * PixelUtil::getMemorySize(getWidth(), getHeight(), 1, format); |
62 | 0 | rval.back -= rval.front; |
63 | 0 | rval.front = 0; |
64 | 0 | } |
65 | 0 | } |
66 | 0 | else |
67 | 0 | { |
68 | 0 | rval.data = rval.getTopLeftFrontPixelPtr(); |
69 | 0 | rval.right -= rval.left; |
70 | 0 | rval.bottom -= rval.top; |
71 | 0 | rval.back -= rval.front; |
72 | 0 | rval.front = rval.top = rval.left = 0; |
73 | 0 | } |
74 | 0 | } |
75 | |
|
76 | 0 | return rval; |
77 | 0 | } |
78 | | uchar* PixelBox::getTopLeftFrontPixelPtr() const |
79 | 0 | { |
80 | 0 | return data + (left + top * rowPitch + front * slicePitch) * PixelUtil::getNumElemBytes(format); |
81 | 0 | } |
82 | | //----------------------------------------------------------------------- |
83 | | /** |
84 | | * Directly get the description record for provided pixel format. For debug builds, |
85 | | * this checks the bounds of fmt with an assertion. |
86 | | */ |
87 | | static inline const PixelFormatDescription &getDescriptionFor(const PixelFormat fmt) |
88 | 0 | { |
89 | 0 | const int ord = (int)fmt; |
90 | 0 | assert(ord>=0 && ord<PF_COUNT); |
91 | |
|
92 | 0 | return _pixelFormats[ord]; |
93 | 0 | } |
94 | | //----------------------------------------------------------------------- |
95 | | uint8 PixelUtil::getNumElemBytes( PixelFormat format ) |
96 | 0 | { |
97 | 0 | return getDescriptionFor(format).elemBytes; |
98 | 0 | } |
99 | | //----------------------------------------------------------------------- |
100 | | static size_t astc_slice_size(uint32 width, uint32 height, uint32 blockWidth, uint32 blockHeight) |
101 | 0 | { |
102 | 0 | return ((width + blockWidth - 1) / blockWidth) * |
103 | 0 | ((height + blockHeight - 1) / blockHeight) * 16; |
104 | 0 | } |
105 | | size_t PixelUtil::getMemorySize(uint32 width, uint32 height, uint32 depth, PixelFormat format) |
106 | 0 | { |
107 | 0 | if(isCompressed(format)) |
108 | 0 | { |
109 | 0 | switch(format) |
110 | 0 | { |
111 | | // DXT formats work by dividing the image into 4x4 blocks, then encoding each |
112 | | // 4x4 block with a certain number of bytes. |
113 | 0 | case PF_DXT1: |
114 | 0 | return ((width+3)/4)*((height+3)/4)*8 * depth; |
115 | 0 | case PF_DXT2: |
116 | 0 | case PF_DXT3: |
117 | 0 | case PF_DXT4: |
118 | 0 | case PF_DXT5: |
119 | 0 | return ((width+3)/4)*((height+3)/4)*16 * depth; |
120 | 0 | case PF_BC4_SNORM: |
121 | 0 | case PF_BC4_UNORM: |
122 | 0 | return ((width+3)/4)*((height+3)/4)*8 * depth; |
123 | 0 | case PF_BC5_SNORM: |
124 | 0 | case PF_BC5_UNORM: |
125 | 0 | case PF_BC6H_SF16: |
126 | 0 | case PF_BC6H_UF16: |
127 | 0 | case PF_BC7_UNORM: |
128 | 0 | return ((width+3)/4)*((height+3)/4)*16 * depth; |
129 | | |
130 | | // Size calculations from the PVRTC OpenGL extension spec |
131 | | // http://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt |
132 | | // Basically, 32 bytes is the minimum texture size. Smaller textures are padded up to 32 bytes |
133 | 0 | case PF_PVRTC_RGB2: |
134 | 0 | case PF_PVRTC_RGBA2: |
135 | 0 | case PF_PVRTC2_2BPP: |
136 | 0 | return (std::max((int)width, 16) * std::max((int)height, 8) * 2 + 7) / 8; |
137 | 0 | case PF_PVRTC_RGB4: |
138 | 0 | case PF_PVRTC_RGBA4: |
139 | 0 | case PF_PVRTC2_4BPP: |
140 | 0 | return (std::max((int)width, 8) * std::max((int)height, 8) * 4 + 7) / 8; |
141 | | |
142 | | // see https://registry.khronos.org/OpenGL-Refpages/es3/html/glCompressedTexImage2D.xhtml |
143 | 0 | case PF_ETC1_RGB8: |
144 | 0 | case PF_ETC2_RGB8: |
145 | 0 | case PF_ETC2_RGB8A1: |
146 | 0 | case PF_ATC_RGB: |
147 | 0 | return ((width + 3) / 4) * ((height + 3) / 4) * 8; |
148 | 0 | case PF_ETC2_RGBA8: |
149 | 0 | case PF_ATC_RGBA_EXPLICIT_ALPHA: |
150 | 0 | case PF_ATC_RGBA_INTERPOLATED_ALPHA: |
151 | 0 | return ((width + 3) / 4) * ((height + 3) / 4) * 16; |
152 | | |
153 | 0 | case PF_ASTC_RGBA_4X4_LDR: |
154 | 0 | return astc_slice_size(width, height, 4, 4) * depth; |
155 | 0 | case PF_ASTC_RGBA_5X4_LDR: |
156 | 0 | return astc_slice_size(width, height, 5, 4) * depth; |
157 | 0 | case PF_ASTC_RGBA_5X5_LDR: |
158 | 0 | return astc_slice_size(width, height, 5, 5) * depth; |
159 | 0 | case PF_ASTC_RGBA_6X5_LDR: |
160 | 0 | return astc_slice_size(width, height, 6, 5) * depth; |
161 | 0 | case PF_ASTC_RGBA_6X6_LDR: |
162 | 0 | return astc_slice_size(width, height, 6, 6) * depth; |
163 | 0 | case PF_ASTC_RGBA_8X5_LDR: |
164 | 0 | return astc_slice_size(width, height, 8, 5) * depth; |
165 | 0 | case PF_ASTC_RGBA_8X6_LDR: |
166 | 0 | return astc_slice_size(width, height, 8, 6) * depth; |
167 | 0 | case PF_ASTC_RGBA_8X8_LDR: |
168 | 0 | return astc_slice_size(width, height, 8, 8) * depth; |
169 | 0 | case PF_ASTC_RGBA_10X5_LDR: |
170 | 0 | return astc_slice_size(width, height, 10, 5) * depth; |
171 | 0 | case PF_ASTC_RGBA_10X6_LDR: |
172 | 0 | return astc_slice_size(width, height, 10, 6) * depth; |
173 | 0 | case PF_ASTC_RGBA_10X8_LDR: |
174 | 0 | return astc_slice_size(width, height, 10, 8) * depth; |
175 | 0 | case PF_ASTC_RGBA_10X10_LDR: |
176 | 0 | return astc_slice_size(width, height, 10, 10) * depth; |
177 | 0 | case PF_ASTC_RGBA_12X10_LDR: |
178 | 0 | return astc_slice_size(width, height, 12, 10) * depth; |
179 | 0 | case PF_ASTC_RGBA_12X12_LDR: |
180 | 0 | return astc_slice_size(width, height, 12, 12) * depth; |
181 | 0 | default: |
182 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Invalid compressed pixel format", |
183 | 0 | "PixelUtil::getMemorySize"); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | else |
187 | 0 | { |
188 | 0 | return width*height*depth*getNumElemBytes(format); |
189 | 0 | } |
190 | 0 | } |
191 | | //----------------------------------------------------------------------- |
192 | | uint8 PixelUtil::getNumElemBits( PixelFormat format ) |
193 | 0 | { |
194 | 0 | return getDescriptionFor(format).elemBytes * 8; |
195 | 0 | } |
196 | | //----------------------------------------------------------------------- |
197 | | unsigned int PixelUtil::getFlags( PixelFormat format ) |
198 | 0 | { |
199 | 0 | return getDescriptionFor(format).flags; |
200 | 0 | } |
201 | | //----------------------------------------------------------------------- |
202 | | bool PixelUtil::hasAlpha(PixelFormat format) |
203 | 0 | { |
204 | 0 | return (PixelUtil::getFlags(format) & PFF_HASALPHA) > 0; |
205 | 0 | } |
206 | | //----------------------------------------------------------------------- |
207 | | bool PixelUtil::isFloatingPoint(PixelFormat format) |
208 | 0 | { |
209 | 0 | return (PixelUtil::getFlags(format) & PFF_FLOAT) > 0; |
210 | 0 | } |
211 | | //----------------------------------------------------------------------- |
212 | | bool PixelUtil::isInteger(PixelFormat format) |
213 | 0 | { |
214 | 0 | return (PixelUtil::getFlags(format) & PFF_INTEGER) > 0; |
215 | 0 | } |
216 | | //----------------------------------------------------------------------- |
217 | | bool PixelUtil::isCompressed(PixelFormat format) |
218 | 0 | { |
219 | 0 | return (PixelUtil::getFlags(format) & PFF_COMPRESSED) > 0; |
220 | 0 | } |
221 | | //----------------------------------------------------------------------- |
222 | | bool PixelUtil::isDepth(PixelFormat format) |
223 | 0 | { |
224 | 0 | return (PixelUtil::getFlags(format) & PFF_DEPTH) > 0; |
225 | 0 | } |
226 | | //----------------------------------------------------------------------- |
227 | | bool PixelUtil::isNativeEndian(PixelFormat format) |
228 | 0 | { |
229 | 0 | return (PixelUtil::getFlags(format) & PFF_NATIVEENDIAN) > 0; |
230 | 0 | } |
231 | | //----------------------------------------------------------------------- |
232 | | bool PixelUtil::isLuminance(PixelFormat format) |
233 | 0 | { |
234 | 0 | return (PixelUtil::getFlags(format) & PFF_LUMINANCE) > 0; |
235 | 0 | } |
236 | | //----------------------------------------------------------------------- |
237 | | void PixelUtil::getBitDepths(PixelFormat format, int rgba[4]) |
238 | 0 | { |
239 | 0 | const PixelFormatDescription &des = getDescriptionFor(format); |
240 | 0 | rgba[0] = des.rbits; |
241 | 0 | rgba[1] = des.gbits; |
242 | 0 | rgba[2] = des.bbits; |
243 | 0 | rgba[3] = des.abits; |
244 | 0 | } |
245 | | //----------------------------------------------------------------------- |
246 | | void PixelUtil::getBitMasks(PixelFormat format, uint64 rgba[4]) |
247 | 0 | { |
248 | 0 | const PixelFormatDescription &des = getDescriptionFor(format); |
249 | 0 | rgba[0] = des.rmask; |
250 | 0 | rgba[1] = des.gmask; |
251 | 0 | rgba[2] = des.bmask; |
252 | 0 | rgba[3] = des.amask; |
253 | 0 | } |
254 | | //--------------------------------------------------------------------- |
255 | | void PixelUtil::getBitShifts(PixelFormat format, unsigned char rgba[4]) |
256 | 0 | { |
257 | 0 | const PixelFormatDescription &des = getDescriptionFor(format); |
258 | 0 | rgba[0] = des.rshift; |
259 | 0 | rgba[1] = des.gshift; |
260 | 0 | rgba[2] = des.bshift; |
261 | 0 | rgba[3] = des.ashift; |
262 | 0 | } |
263 | | //----------------------------------------------------------------------- |
264 | | const String& PixelUtil::getFormatName(PixelFormat srcformat) |
265 | 0 | { |
266 | 0 | return getDescriptionFor(srcformat).name; |
267 | 0 | } |
268 | | //----------------------------------------------------------------------- |
269 | | bool PixelUtil::isAccessible(PixelFormat srcformat) |
270 | 0 | { |
271 | 0 | return (srcformat != PF_UNKNOWN) && !isCompressed(srcformat); |
272 | 0 | } |
273 | | //----------------------------------------------------------------------- |
274 | | PixelComponentType PixelUtil::getComponentType(PixelFormat fmt) |
275 | 0 | { |
276 | 0 | const PixelFormatDescription &des = getDescriptionFor(fmt); |
277 | 0 | return des.componentType; |
278 | 0 | } |
279 | | //----------------------------------------------------------------------- |
280 | | uint8 PixelUtil::getComponentCount(PixelFormat fmt) |
281 | 0 | { |
282 | 0 | const PixelFormatDescription &des = getDescriptionFor(fmt); |
283 | 0 | return des.componentCount; |
284 | 0 | } |
285 | | //----------------------------------------------------------------------- |
286 | | PixelFormat PixelUtil::getFormatFromName(const String& name, bool accessibleOnly, bool caseSensitive) |
287 | 0 | { |
288 | 0 | String tmp = name; |
289 | 0 | if (!caseSensitive) |
290 | 0 | { |
291 | | // We are stored upper-case format names. |
292 | 0 | StringUtil::toUpperCase(tmp); |
293 | 0 | } |
294 | |
|
295 | 0 | for (int i = 0; i < PF_COUNT; ++i) |
296 | 0 | { |
297 | 0 | PixelFormat pf = static_cast<PixelFormat>(i); |
298 | 0 | if (!accessibleOnly || isAccessible(pf)) |
299 | 0 | { |
300 | 0 | if (tmp == getFormatName(pf)) |
301 | 0 | return pf; |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | // allow look-up by alias name |
306 | 0 | if(tmp == "PF_BYTE_RGB") |
307 | 0 | return PF_BYTE_RGB; |
308 | 0 | if(tmp == "PF_BYTE_RGBA") |
309 | 0 | return PF_BYTE_RGBA; |
310 | 0 | if(tmp == "PF_BYTE_BGR") |
311 | 0 | return PF_BYTE_BGR; |
312 | 0 | if(tmp == "PF_BYTE_BGRA") |
313 | 0 | return PF_BYTE_BGRA; |
314 | | |
315 | 0 | return PF_UNKNOWN; |
316 | 0 | } |
317 | | //----------------------------------------------------------------------- |
318 | | PixelFormat PixelUtil::getFormatForBitDepths(PixelFormat fmt, ushort integerBits, ushort floatBits) |
319 | 0 | { |
320 | 0 | switch (integerBits) |
321 | 0 | { |
322 | 0 | case 16: |
323 | 0 | switch (fmt) |
324 | 0 | { |
325 | 0 | case PF_R8G8B8: |
326 | 0 | case PF_X8R8G8B8: |
327 | 0 | return PF_R5G6B5; |
328 | | |
329 | 0 | case PF_B8G8R8: |
330 | 0 | case PF_X8B8G8R8: |
331 | 0 | return PF_B5G6R5; |
332 | | |
333 | 0 | case PF_A8R8G8B8: |
334 | 0 | case PF_R8G8B8A8: |
335 | 0 | case PF_A8B8G8R8: |
336 | 0 | case PF_B8G8R8A8: |
337 | 0 | return PF_A4R4G4B4; |
338 | | |
339 | 0 | case PF_A2R10G10B10: |
340 | 0 | case PF_A2B10G10R10: |
341 | 0 | return PF_A1R5G5B5; |
342 | | |
343 | 0 | default: |
344 | | // use original image format |
345 | 0 | break; |
346 | 0 | } |
347 | 0 | break; |
348 | | |
349 | 0 | case 32: |
350 | 0 | switch (fmt) |
351 | 0 | { |
352 | 0 | case PF_R5G6B5: |
353 | 0 | return PF_X8R8G8B8; |
354 | | |
355 | 0 | case PF_B5G6R5: |
356 | 0 | return PF_X8B8G8R8; |
357 | | |
358 | 0 | case PF_A4R4G4B4: |
359 | 0 | return PF_A8R8G8B8; |
360 | | |
361 | 0 | case PF_A1R5G5B5: |
362 | 0 | return PF_A2R10G10B10; |
363 | | |
364 | 0 | default: |
365 | | // use original image format |
366 | 0 | break; |
367 | 0 | } |
368 | 0 | break; |
369 | | |
370 | 0 | default: |
371 | | // use original image format |
372 | 0 | break; |
373 | 0 | } |
374 | | |
375 | 0 | switch (floatBits) |
376 | 0 | { |
377 | 0 | case 16: |
378 | 0 | switch (fmt) |
379 | 0 | { |
380 | 0 | case PF_FLOAT32_R: |
381 | 0 | return PF_FLOAT16_R; |
382 | | |
383 | 0 | case PF_FLOAT32_RGB: |
384 | 0 | return PF_FLOAT16_RGB; |
385 | | |
386 | 0 | case PF_FLOAT32_RGBA: |
387 | 0 | return PF_FLOAT16_RGBA; |
388 | | |
389 | 0 | default: |
390 | | // use original image format |
391 | 0 | break; |
392 | 0 | } |
393 | 0 | break; |
394 | | |
395 | 0 | case 32: |
396 | 0 | switch (fmt) |
397 | 0 | { |
398 | 0 | case PF_FLOAT16_R: |
399 | 0 | return PF_FLOAT32_R; |
400 | | |
401 | 0 | case PF_FLOAT16_RGB: |
402 | 0 | return PF_FLOAT32_RGB; |
403 | | |
404 | 0 | case PF_FLOAT16_RGBA: |
405 | 0 | return PF_FLOAT32_RGBA; |
406 | | |
407 | 0 | default: |
408 | | // use original image format |
409 | 0 | break; |
410 | 0 | } |
411 | 0 | break; |
412 | | |
413 | 0 | default: |
414 | | // use original image format |
415 | 0 | break; |
416 | 0 | } |
417 | | |
418 | 0 | return fmt; |
419 | 0 | } |
420 | | //----------------------------------------------------------------------- |
421 | | /************************************************************************* |
422 | | * Pixel packing/unpacking utilities |
423 | | */ |
424 | | void PixelUtil::packColour(const uint8 r, const uint8 g, const uint8 b, const uint8 a, const PixelFormat pf, void* dest) |
425 | 0 | { |
426 | 0 | const PixelFormatDescription &des = getDescriptionFor(pf); |
427 | 0 | if(des.flags & PFF_NATIVEENDIAN) { |
428 | | // Shortcut for integer formats packing |
429 | 0 | unsigned int value = ((Bitwise::fixedToFixed(r, 8, des.rbits)<<des.rshift) & des.rmask) | |
430 | 0 | ((Bitwise::fixedToFixed(g, 8, des.gbits)<<des.gshift) & des.gmask) | |
431 | 0 | ((Bitwise::fixedToFixed(b, 8, des.bbits)<<des.bshift) & des.bmask) | |
432 | 0 | ((Bitwise::fixedToFixed(a, 8, des.abits)<<des.ashift) & des.amask); |
433 | | // And write to memory |
434 | 0 | Bitwise::intWrite(dest, des.elemBytes, value); |
435 | 0 | } else { |
436 | | // Convert to float |
437 | 0 | packColour((float)r/255.0f,(float)g/255.0f,(float)b/255.0f,(float)a/255.0f, pf, dest); |
438 | 0 | } |
439 | 0 | } |
440 | | //----------------------------------------------------------------------- |
441 | | void PixelUtil::packColour(const float r, const float g, const float b, const float a, const PixelFormat pf, void* dest) |
442 | 0 | { |
443 | | // Catch-it-all here |
444 | 0 | const PixelFormatDescription &des = getDescriptionFor(pf); |
445 | 0 | if(des.flags & PFF_NATIVEENDIAN) { |
446 | | // Do the packing |
447 | | //std::cerr << dest << " " << r << " " << g << " " << b << " " << a << std::endl; |
448 | 0 | const unsigned int value = ((Bitwise::floatToFixed(r, des.rbits)<<des.rshift) & des.rmask) | |
449 | 0 | ((Bitwise::floatToFixed(g, des.gbits)<<des.gshift) & des.gmask) | |
450 | 0 | ((Bitwise::floatToFixed(b, des.bbits)<<des.bshift) & des.bmask) | |
451 | 0 | ((Bitwise::floatToFixed(a, des.abits)<<des.ashift) & des.amask); |
452 | | // And write to memory |
453 | 0 | Bitwise::intWrite(dest, des.elemBytes, value); |
454 | 0 | } else { |
455 | 0 | switch(pf) |
456 | 0 | { |
457 | 0 | case PF_FLOAT32_R: |
458 | 0 | ((float*)dest)[0] = r; |
459 | 0 | break; |
460 | 0 | case PF_FLOAT32_GR: |
461 | 0 | ((float*)dest)[0] = g; |
462 | 0 | ((float*)dest)[1] = r; |
463 | 0 | break; |
464 | 0 | case PF_FLOAT32_RGB: |
465 | 0 | ((float*)dest)[0] = r; |
466 | 0 | ((float*)dest)[1] = g; |
467 | 0 | ((float*)dest)[2] = b; |
468 | 0 | break; |
469 | 0 | case PF_FLOAT32_RGBA: |
470 | 0 | ((float*)dest)[0] = r; |
471 | 0 | ((float*)dest)[1] = g; |
472 | 0 | ((float*)dest)[2] = b; |
473 | 0 | ((float*)dest)[3] = a; |
474 | 0 | break; |
475 | 0 | case PF_DEPTH16: |
476 | 0 | case PF_FLOAT16_R: |
477 | 0 | ((uint16*)dest)[0] = Bitwise::floatToHalf(r); |
478 | 0 | break; |
479 | 0 | case PF_FLOAT16_GR: |
480 | 0 | ((uint16*)dest)[0] = Bitwise::floatToHalf(g); |
481 | 0 | ((uint16*)dest)[1] = Bitwise::floatToHalf(r); |
482 | 0 | break; |
483 | 0 | case PF_FLOAT16_RGB: |
484 | 0 | ((uint16*)dest)[0] = Bitwise::floatToHalf(r); |
485 | 0 | ((uint16*)dest)[1] = Bitwise::floatToHalf(g); |
486 | 0 | ((uint16*)dest)[2] = Bitwise::floatToHalf(b); |
487 | 0 | break; |
488 | 0 | case PF_FLOAT16_RGBA: |
489 | 0 | ((uint16*)dest)[0] = Bitwise::floatToHalf(r); |
490 | 0 | ((uint16*)dest)[1] = Bitwise::floatToHalf(g); |
491 | 0 | ((uint16*)dest)[2] = Bitwise::floatToHalf(b); |
492 | 0 | ((uint16*)dest)[3] = Bitwise::floatToHalf(a); |
493 | 0 | break; |
494 | 0 | case PF_SHORT_RGB: |
495 | 0 | ((uint16*)dest)[0] = (uint16)Bitwise::floatToFixed(r, 16); |
496 | 0 | ((uint16*)dest)[1] = (uint16)Bitwise::floatToFixed(g, 16); |
497 | 0 | ((uint16*)dest)[2] = (uint16)Bitwise::floatToFixed(b, 16); |
498 | 0 | break; |
499 | 0 | case PF_SHORT_RGBA: |
500 | 0 | ((uint16*)dest)[0] = (uint16)Bitwise::floatToFixed(r, 16); |
501 | 0 | ((uint16*)dest)[1] = (uint16)Bitwise::floatToFixed(g, 16); |
502 | 0 | ((uint16*)dest)[2] = (uint16)Bitwise::floatToFixed(b, 16); |
503 | 0 | ((uint16*)dest)[3] = (uint16)Bitwise::floatToFixed(a, 16); |
504 | 0 | break; |
505 | 0 | case PF_BYTE_LA: |
506 | 0 | ((uint8*)dest)[0] = (uint8)Bitwise::floatToFixed(r, 8); |
507 | 0 | ((uint8*)dest)[1] = (uint8)Bitwise::floatToFixed(a, 8); |
508 | 0 | break; |
509 | 0 | case PF_A8: |
510 | 0 | ((uint8*)dest)[0] = (uint8)Bitwise::floatToFixed(r, 8); |
511 | 0 | break; |
512 | 0 | default: |
513 | | // Not yet supported |
514 | 0 | OGRE_EXCEPT( |
515 | 0 | Exception::ERR_NOT_IMPLEMENTED, |
516 | 0 | "pack to "+getFormatName(pf)+" not implemented", |
517 | 0 | "PixelUtil::packColour"); |
518 | 0 | break; |
519 | 0 | } |
520 | 0 | } |
521 | 0 | } |
522 | | //----------------------------------------------------------------------- |
523 | | void PixelUtil::unpackColour(uint8 *r, uint8 *g, uint8 *b, uint8 *a, PixelFormat pf, const void* src) |
524 | 0 | { |
525 | 0 | const PixelFormatDescription &des = getDescriptionFor(pf); |
526 | 0 | if(des.flags & PFF_NATIVEENDIAN) { |
527 | | // Shortcut for integer formats unpacking |
528 | 0 | const unsigned int value = Bitwise::intRead(src, des.elemBytes); |
529 | 0 | if(des.flags & PFF_LUMINANCE) |
530 | 0 | { |
531 | | // Luminance format -- only rbits used |
532 | 0 | *r = *g = *b = (uint8)Bitwise::fixedToFixed( |
533 | 0 | (value & des.rmask)>>des.rshift, des.rbits, 8); |
534 | 0 | } |
535 | 0 | else |
536 | 0 | { |
537 | 0 | *r = (uint8)Bitwise::fixedToFixed((value & des.rmask)>>des.rshift, des.rbits, 8); |
538 | 0 | *g = (uint8)Bitwise::fixedToFixed((value & des.gmask)>>des.gshift, des.gbits, 8); |
539 | 0 | *b = (uint8)Bitwise::fixedToFixed((value & des.bmask)>>des.bshift, des.bbits, 8); |
540 | 0 | } |
541 | 0 | if(des.flags & PFF_HASALPHA) |
542 | 0 | { |
543 | 0 | *a = (uint8)Bitwise::fixedToFixed((value & des.amask)>>des.ashift, des.abits, 8); |
544 | 0 | } |
545 | 0 | else |
546 | 0 | { |
547 | 0 | *a = 255; // No alpha, default a component to full |
548 | 0 | } |
549 | 0 | } else { |
550 | | // Do the operation with the more generic floating point |
551 | 0 | float rr = 0, gg = 0, bb = 0, aa = 0; |
552 | 0 | unpackColour(&rr,&gg,&bb,&aa, pf, src); |
553 | 0 | *r = (uint8)Bitwise::floatToFixed(rr, 8); |
554 | 0 | *g = (uint8)Bitwise::floatToFixed(gg, 8); |
555 | 0 | *b = (uint8)Bitwise::floatToFixed(bb, 8); |
556 | 0 | *a = (uint8)Bitwise::floatToFixed(aa, 8); |
557 | 0 | } |
558 | 0 | } |
559 | | //----------------------------------------------------------------------- |
560 | | void PixelUtil::unpackColour(float *r, float *g, float *b, float *a, |
561 | | PixelFormat pf, const void* src) |
562 | 0 | { |
563 | 0 | const PixelFormatDescription &des = getDescriptionFor(pf); |
564 | 0 | if(des.flags & PFF_NATIVEENDIAN) { |
565 | | // Shortcut for integer formats unpacking |
566 | 0 | const unsigned int value = Bitwise::intRead(src, des.elemBytes); |
567 | 0 | if(des.flags & PFF_LUMINANCE) |
568 | 0 | { |
569 | | // Luminance format -- only rbits used |
570 | 0 | *r = *g = *b = Bitwise::fixedToFloat( |
571 | 0 | (value & des.rmask)>>des.rshift, des.rbits); |
572 | 0 | } |
573 | 0 | else |
574 | 0 | { |
575 | 0 | *r = Bitwise::fixedToFloat((value & des.rmask)>>des.rshift, des.rbits); |
576 | 0 | *g = Bitwise::fixedToFloat((value & des.gmask)>>des.gshift, des.gbits); |
577 | 0 | *b = Bitwise::fixedToFloat((value & des.bmask)>>des.bshift, des.bbits); |
578 | 0 | } |
579 | 0 | if(des.flags & PFF_HASALPHA) |
580 | 0 | { |
581 | 0 | *a = Bitwise::fixedToFloat((value & des.amask)>>des.ashift, des.abits); |
582 | 0 | } |
583 | 0 | else |
584 | 0 | { |
585 | 0 | *a = 1.0f; // No alpha, default a component to full |
586 | 0 | } |
587 | 0 | } else { |
588 | 0 | switch(pf) |
589 | 0 | { |
590 | 0 | case PF_FLOAT32_R: |
591 | 0 | *r = *g = *b = ((const float*)src)[0]; |
592 | 0 | *a = 1.0f; |
593 | 0 | break; |
594 | 0 | case PF_FLOAT32_GR: |
595 | 0 | *g = ((const float*)src)[0]; |
596 | 0 | *r = *b = ((const float*)src)[1]; |
597 | 0 | *a = 1.0f; |
598 | 0 | break; |
599 | 0 | case PF_FLOAT32_RGB: |
600 | 0 | *r = ((const float*)src)[0]; |
601 | 0 | *g = ((const float*)src)[1]; |
602 | 0 | *b = ((const float*)src)[2]; |
603 | 0 | *a = 1.0f; |
604 | 0 | break; |
605 | 0 | case PF_FLOAT32_RGBA: |
606 | 0 | *r = ((const float*)src)[0]; |
607 | 0 | *g = ((const float*)src)[1]; |
608 | 0 | *b = ((const float*)src)[2]; |
609 | 0 | *a = ((const float*)src)[3]; |
610 | 0 | break; |
611 | 0 | case PF_FLOAT16_R: |
612 | 0 | *r = *g = *b = Bitwise::halfToFloat(((const uint16*)src)[0]); |
613 | 0 | *a = 1.0f; |
614 | 0 | break; |
615 | 0 | case PF_FLOAT16_GR: |
616 | 0 | *g = Bitwise::halfToFloat(((const uint16*)src)[0]); |
617 | 0 | *r = *b = Bitwise::halfToFloat(((const uint16*)src)[1]); |
618 | 0 | *a = 1.0f; |
619 | 0 | break; |
620 | 0 | case PF_FLOAT16_RGB: |
621 | 0 | *r = Bitwise::halfToFloat(((const uint16*)src)[0]); |
622 | 0 | *g = Bitwise::halfToFloat(((const uint16*)src)[1]); |
623 | 0 | *b = Bitwise::halfToFloat(((const uint16*)src)[2]); |
624 | 0 | *a = 1.0f; |
625 | 0 | break; |
626 | 0 | case PF_FLOAT16_RGBA: |
627 | 0 | *r = Bitwise::halfToFloat(((const uint16*)src)[0]); |
628 | 0 | *g = Bitwise::halfToFloat(((const uint16*)src)[1]); |
629 | 0 | *b = Bitwise::halfToFloat(((const uint16*)src)[2]); |
630 | 0 | *a = Bitwise::halfToFloat(((const uint16*)src)[3]); |
631 | 0 | break; |
632 | 0 | case PF_SHORT_RGB: |
633 | 0 | *r = Bitwise::fixedToFloat(((const uint16*)src)[0], 16); |
634 | 0 | *g = Bitwise::fixedToFloat(((const uint16*)src)[1], 16); |
635 | 0 | *b = Bitwise::fixedToFloat(((const uint16*)src)[2], 16); |
636 | 0 | *a = 1.0f; |
637 | 0 | break; |
638 | 0 | case PF_SHORT_RGBA: |
639 | 0 | *r = Bitwise::fixedToFloat(((const uint16*)src)[0], 16); |
640 | 0 | *g = Bitwise::fixedToFloat(((const uint16*)src)[1], 16); |
641 | 0 | *b = Bitwise::fixedToFloat(((const uint16*)src)[2], 16); |
642 | 0 | *a = Bitwise::fixedToFloat(((const uint16*)src)[3], 16); |
643 | 0 | break; |
644 | 0 | case PF_BYTE_LA: |
645 | 0 | *r = *g = *b = Bitwise::fixedToFloat(((const uint8*)src)[0], 8); |
646 | 0 | *a = Bitwise::fixedToFloat(((const uint8*)src)[1], 8); |
647 | 0 | break; |
648 | 0 | default: |
649 | | // Not yet supported |
650 | 0 | OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, |
651 | 0 | "unpack from "+getFormatName(pf)+" not implemented", |
652 | 0 | "PixelUtil::unpackColour"); |
653 | 0 | break; |
654 | 0 | } |
655 | 0 | } |
656 | 0 | } |
657 | | //----------------------------------------------------------------------- |
658 | | /* Convert pixels from one format to another */ |
659 | | void PixelUtil::bulkPixelConversion(const PixelBox &src, const PixelBox &dst) |
660 | 0 | { |
661 | 0 | OgreAssert(src.getSize() == dst.getSize(), ""); |
662 | | |
663 | | // Check for compressed formats, we don't support decompression, compression or recoding |
664 | 0 | if(PixelUtil::isCompressed(src.format) || PixelUtil::isCompressed(dst.format)) |
665 | 0 | { |
666 | 0 | OgreAssert(src.format == dst.format && src.isConsecutive() && dst.isConsecutive(), |
667 | 0 | "This method can not be used to compress or decompress images"); |
668 | | // we can copy with slice granularity, useful for Tex2DArray handling |
669 | 0 | size_t bytesPerSlice = getMemorySize(src.getWidth(), src.getHeight(), 1, src.format); |
670 | 0 | memcpy(dst.data + bytesPerSlice * dst.front, src.data + bytesPerSlice * src.front, |
671 | 0 | bytesPerSlice * src.getDepth()); |
672 | 0 | return; |
673 | 0 | } |
674 | | |
675 | | // The easy case |
676 | 0 | if(src.format == dst.format) { |
677 | 0 | uint8 *srcptr = src.getTopLeftFrontPixelPtr(); |
678 | 0 | uint8 *dstptr = dst.getTopLeftFrontPixelPtr(); |
679 | | |
680 | | // Everything consecutive? |
681 | 0 | if(src.isConsecutive() && dst.isConsecutive()) |
682 | 0 | { |
683 | 0 | memcpy(dstptr, srcptr, src.getConsecutiveSize()); |
684 | 0 | return; |
685 | 0 | } |
686 | | |
687 | 0 | const size_t srcPixelSize = PixelUtil::getNumElemBytes(src.format); |
688 | 0 | const size_t dstPixelSize = PixelUtil::getNumElemBytes(dst.format); |
689 | | |
690 | | // Calculate pitches+skips in bytes |
691 | 0 | const size_t srcRowPitchBytes = src.rowPitch*srcPixelSize; |
692 | | //const size_t srcRowSkipBytes = src.getRowSkip()*srcPixelSize; |
693 | 0 | const size_t srcSliceSkipBytes = src.getSliceSkip()*srcPixelSize; |
694 | |
|
695 | 0 | const size_t dstRowPitchBytes = dst.rowPitch*dstPixelSize; |
696 | | //const size_t dstRowSkipBytes = dst.getRowSkip()*dstPixelSize; |
697 | 0 | const size_t dstSliceSkipBytes = dst.getSliceSkip()*dstPixelSize; |
698 | | |
699 | | // Otherwise, copy per row |
700 | 0 | const size_t rowSize = src.getWidth()*srcPixelSize; |
701 | 0 | for(size_t z=src.front; z<src.back; z++) |
702 | 0 | { |
703 | 0 | for(size_t y=src.top; y<src.bottom; y++) |
704 | 0 | { |
705 | 0 | memcpy(dstptr, srcptr, rowSize); |
706 | 0 | srcptr += srcRowPitchBytes; |
707 | 0 | dstptr += dstRowPitchBytes; |
708 | 0 | } |
709 | 0 | srcptr += srcSliceSkipBytes; |
710 | 0 | dstptr += dstSliceSkipBytes; |
711 | 0 | } |
712 | 0 | return; |
713 | 0 | } |
714 | | // Converting to PF_X8R8G8B8 is exactly the same as converting to |
715 | | // PF_A8R8G8B8. (same with PF_X8B8G8R8 and PF_A8B8G8R8) |
716 | 0 | if(dst.format == PF_X8R8G8B8 || dst.format == PF_X8B8G8R8) |
717 | 0 | { |
718 | | // Do the same conversion, with PF_A8R8G8B8, which has a lot of |
719 | | // optimized conversions |
720 | 0 | PixelBox tempdst = dst; |
721 | 0 | tempdst.format = dst.format==PF_X8R8G8B8?PF_A8R8G8B8:PF_A8B8G8R8; |
722 | 0 | bulkPixelConversion(src, tempdst); |
723 | 0 | return; |
724 | 0 | } |
725 | | // Converting from PF_X8R8G8B8 is exactly the same as converting from |
726 | | // PF_A8R8G8B8, given that the destination format does not have alpha. |
727 | 0 | if((src.format == PF_X8R8G8B8||src.format == PF_X8B8G8R8) && !hasAlpha(dst.format)) |
728 | 0 | { |
729 | | // Do the same conversion, with PF_A8R8G8B8, which has a lot of |
730 | | // optimized conversions |
731 | 0 | PixelBox tempsrc = src; |
732 | 0 | tempsrc.format = src.format==PF_X8R8G8B8?PF_A8R8G8B8:PF_A8B8G8R8; |
733 | 0 | bulkPixelConversion(tempsrc, dst); |
734 | 0 | return; |
735 | 0 | } |
736 | | |
737 | | // NB VC6 can't handle the templates required for optimised conversion, tough |
738 | 0 | #if OGRE_COMPILER != OGRE_COMPILER_MSVC || OGRE_COMP_VER >= 1300 |
739 | | // Is there a specialized, inlined, conversion? |
740 | 0 | if(doOptimizedConversion(src, dst)) |
741 | 0 | { |
742 | | // If so, good |
743 | 0 | return; |
744 | 0 | } |
745 | 0 | #endif |
746 | | |
747 | 0 | const size_t srcPixelSize = PixelUtil::getNumElemBytes(src.format); |
748 | 0 | const size_t dstPixelSize = PixelUtil::getNumElemBytes(dst.format); |
749 | 0 | uint8* srcptr = src.getTopLeftFrontPixelPtr(); |
750 | 0 | uint8* dstptr = dst.getTopLeftFrontPixelPtr(); |
751 | | |
752 | | // Old way, not taking into account box dimensions |
753 | | //uint8 *srcptr = static_cast<uint8*>(src.data), *dstptr = static_cast<uint8*>(dst.data); |
754 | | |
755 | | // Calculate pitches+skips in bytes |
756 | 0 | const size_t srcRowSkipBytes = src.getRowSkip()*srcPixelSize; |
757 | 0 | const size_t srcSliceSkipBytes = src.getSliceSkip()*srcPixelSize; |
758 | 0 | const size_t dstRowSkipBytes = dst.getRowSkip()*dstPixelSize; |
759 | 0 | const size_t dstSliceSkipBytes = dst.getSliceSkip()*dstPixelSize; |
760 | | |
761 | | // The brute force fallback |
762 | 0 | float r = 0, g = 0, b = 0, a = 1; |
763 | 0 | for(size_t z=src.front; z<src.back; z++) |
764 | 0 | { |
765 | 0 | for(size_t y=src.top; y<src.bottom; y++) |
766 | 0 | { |
767 | 0 | for(size_t x=src.left; x<src.right; x++) |
768 | 0 | { |
769 | 0 | unpackColour(&r, &g, &b, &a, src.format, srcptr); |
770 | 0 | packColour(r, g, b, a, dst.format, dstptr); |
771 | 0 | srcptr += srcPixelSize; |
772 | 0 | dstptr += dstPixelSize; |
773 | 0 | } |
774 | 0 | srcptr += srcRowSkipBytes; |
775 | 0 | dstptr += dstRowSkipBytes; |
776 | 0 | } |
777 | 0 | srcptr += srcSliceSkipBytes; |
778 | 0 | dstptr += dstSliceSkipBytes; |
779 | 0 | } |
780 | 0 | } |
781 | | //----------------------------------------------------------------------- |
782 | | void PixelUtil::bulkPixelVerticalFlip(const PixelBox &box) |
783 | 0 | { |
784 | | // Check for compressed formats, we don't support decompression, compression or recoding |
785 | 0 | OgreAssert(!PixelUtil::isCompressed(box.format), "This method can not be used for compressed formats"); |
786 | | |
787 | 0 | const size_t pixelSize = PixelUtil::getNumElemBytes(box.format); |
788 | 0 | const size_t copySize = box.getWidth() * pixelSize; |
789 | | |
790 | | // Calculate pitches in bytes |
791 | 0 | const size_t rowPitchBytes = box.rowPitch * pixelSize; |
792 | 0 | const size_t slicePitchBytes = box.slicePitch * pixelSize; |
793 | |
|
794 | 0 | uint8 *basesrcptr = box.getTopLeftFrontPixelPtr(); |
795 | 0 | uint8 *basedstptr = basesrcptr + (box.bottom - box.top - 1) * rowPitchBytes; |
796 | 0 | uint8* tmpptr = (uint8*)OGRE_MALLOC_SIMD(copySize, MEMCATEGORY_GENERAL); |
797 | | |
798 | | // swap rows |
799 | 0 | const size_t halfRowCount = (box.bottom - box.top) >> 1; |
800 | 0 | for(size_t z = box.front; z < box.back; z++) |
801 | 0 | { |
802 | 0 | uint8* srcptr = basesrcptr; |
803 | 0 | uint8* dstptr = basedstptr; |
804 | 0 | for(size_t y = 0; y < halfRowCount; y++) |
805 | 0 | { |
806 | | // swap rows |
807 | 0 | memcpy(tmpptr, dstptr, copySize); |
808 | 0 | memcpy(dstptr, srcptr, copySize); |
809 | 0 | memcpy(srcptr, tmpptr, copySize); |
810 | 0 | srcptr += rowPitchBytes; |
811 | 0 | dstptr -= rowPitchBytes; |
812 | 0 | } |
813 | 0 | basesrcptr += slicePitchBytes; |
814 | 0 | basedstptr += slicePitchBytes; |
815 | 0 | } |
816 | | |
817 | 0 | OGRE_FREE_SIMD(tmpptr, MEMCATEGORY_GENERAL); |
818 | 0 | } |
819 | | |
820 | | ColourValue PixelBox::getColourAt(size_t x, size_t y, size_t z) const |
821 | 0 | { |
822 | 0 | ColourValue cv; |
823 | |
|
824 | 0 | size_t pixelSize = PixelUtil::getNumElemBytes(format); |
825 | 0 | size_t pixelOffset = pixelSize * (z * slicePitch + y * rowPitch + x); |
826 | 0 | PixelUtil::unpackColour(&cv, format, (unsigned char *)data + pixelOffset); |
827 | |
|
828 | 0 | return cv; |
829 | 0 | } |
830 | | |
831 | | void PixelBox::setColourAt(ColourValue const &cv, size_t x, size_t y, size_t z) |
832 | 0 | { |
833 | 0 | size_t pixelSize = PixelUtil::getNumElemBytes(format); |
834 | 0 | size_t pixelOffset = pixelSize * (z * slicePitch + y * rowPitch + x); |
835 | 0 | PixelUtil::packColour(cv, format, (unsigned char *)data + pixelOffset); |
836 | 0 | } |
837 | | |
838 | | } |