]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/mipmap.cxx
Don't compute mipmap for inexistant image color components
[simgear.git] / simgear / scene / material / mipmap.cxx
1 // Copyright (C) 2010  Frederic Bouvier\r
2 //\r
3 // This library is free software; you can redistribute it and/or\r
4 // modify it under the terms of the GNU Library General Public\r
5 // License as published by the Free Software Foundation; either\r
6 // version 2 of the License, or (at your option) any later version.\r
7 //\r
8 // This library is distributed in the hope that it will be useful,\r
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
11 // Library General Public License for more details.\r
12 //\r
13 // You should have received a copy of the GNU General Public License\r
14 // along with this program; if not, write to the Free Software\r
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
16 \r
17 #ifdef HAVE_CONFIG_H\r
18 #  include <simgear_config.h>\r
19 #endif\r
20 \r
21 #include "mipmap.hxx"\r
22 #include "EffectBuilder.hxx"\r
23 \r
24 #include <limits>\r
25 \r
26 #include <osg/Image>\r
27 #include <osg/Vec4>\r
28 \r
29 #include <boost/lexical_cast.hpp>\r
30 #include <boost/tuple/tuple_comparison.hpp>\r
31 \r
32 namespace simgear { namespace effect {\r
33 \r
34 EffectNameValue<MipMapFunction> mipmapFunctionsInit[] =\r
35 {\r
36     {"auto", AUTOMATIC},\r
37     {"average", AVERAGE},\r
38     {"sum", SUM},\r
39     {"product", PRODUCT},\r
40     {"min", MIN},\r
41     {"max", MAX}\r
42 };\r
43 EffectPropertyMap<MipMapFunction> mipmapFunctions(mipmapFunctionsInit);\r
44 \r
45 MipMapTuple makeMipMapTuple(Effect* effect, const SGPropertyNode* props,\r
46                       const SGReaderWriterXMLOptions* options)\r
47 {\r
48     const SGPropertyNode* pMipmapR\r
49         = getEffectPropertyChild(effect, props, "function-r");\r
50     MipMapFunction mipmapR = AUTOMATIC;\r
51     if (pMipmapR)\r
52         findAttr(mipmapFunctions, pMipmapR, mipmapR);\r
53     const SGPropertyNode* pMipmapG\r
54         = getEffectPropertyChild(effect, props, "function-g");\r
55     MipMapFunction mipmapG = AUTOMATIC;\r
56     if (pMipmapG)\r
57         findAttr(mipmapFunctions, pMipmapG, mipmapG);\r
58     const SGPropertyNode* pMipmapB\r
59         = getEffectPropertyChild(effect, props, "function-b");\r
60     MipMapFunction mipmapB = AUTOMATIC;\r
61     if (pMipmapB)\r
62         findAttr(mipmapFunctions, pMipmapB, mipmapB);\r
63     const SGPropertyNode* pMipmapA\r
64         = getEffectPropertyChild(effect, props, "function-a");\r
65     MipMapFunction mipmapA = AUTOMATIC;\r
66     if (pMipmapA)\r
67         findAttr(mipmapFunctions, pMipmapA, mipmapA);\r
68     return MipMapTuple( mipmapR, mipmapG, mipmapB, mipmapA );\r
69 }\r
70 \r
71 \r
72 unsigned char* imageData(unsigned char* ptr, GLenum pixelFormat, GLenum dataType, int width, int height, int packing, int column, int row=0, int image=0)\r
73 {\r
74     if (!ptr) return NULL;\r
75     return ptr +\r
76         ( column * osg::Image::computePixelSizeInBits( pixelFormat, dataType ) ) / 8 +\r
77         row * osg::Image::computeRowWidthInBytes( width, pixelFormat, dataType, packing ) +\r
78         image * height * osg::Image::computeRowWidthInBytes( width, pixelFormat, dataType, packing );\r
79 }\r
80 \r
81 template <typename T>\r
82 void _writeColor(GLenum pixelFormat, T* data, float scale, osg::Vec4 value)\r
83 {\r
84     switch(pixelFormat)\r
85     {\r
86         case(GL_DEPTH_COMPONENT):   //intentionally fall through and execute the code for GL_LUMINANCE\r
87         case(GL_LUMINANCE):         { *data++ = value.r()*scale; break; }\r
88         case(GL_ALPHA):             { *data++ = value.a()*scale; break; }\r
89         case(GL_LUMINANCE_ALPHA):   { *data++ = value.r()*scale; *data++ = value.a()*scale; break; }\r
90         case(GL_RGB):               { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; break; }\r
91         case(GL_RGBA):              { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; *data++ = value.a()*scale; break; }\r
92         case(GL_BGR):               { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; break; }\r
93         case(GL_BGRA):              { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; *data++ = value.a()*scale; break; }\r
94     }\r
95 }\r
96 \r
97 void setColor(unsigned char *ptr, GLenum pixelFormat, GLenum dataType, osg::Vec4 color)\r
98 {\r
99     switch(dataType)\r
100     {\r
101         case(GL_BYTE):              return _writeColor(pixelFormat, (char*)ptr,             128.0f, color);\r
102         case(GL_UNSIGNED_BYTE):     return _writeColor(pixelFormat, (unsigned char*)ptr,    255.0f, color);\r
103         case(GL_SHORT):             return _writeColor(pixelFormat, (short*)ptr,            32768.0f, color);\r
104         case(GL_UNSIGNED_SHORT):    return _writeColor(pixelFormat, (unsigned short*)ptr,   65535.0f, color);\r
105         case(GL_INT):               return _writeColor(pixelFormat, (int*)ptr,              2147483648.0f, color);\r
106         case(GL_UNSIGNED_INT):      return _writeColor(pixelFormat, (unsigned int*)ptr,     4294967295.0f, color);\r
107         case(GL_FLOAT):             return _writeColor(pixelFormat, (float*)ptr,            1.0f, color);\r
108     }\r
109 }\r
110 \r
111 template <typename T>    \r
112 osg::Vec4 _readColor(GLenum pixelFormat, T* data,float scale)\r
113 {\r
114     switch(pixelFormat)\r
115     {\r
116         case(GL_DEPTH_COMPONENT):   //intentionally fall through and execute the code for GL_LUMINANCE\r
117         case(GL_LUMINANCE):         { float l = float(*data++)*scale; return osg::Vec4(l, l, l, 1.0f); }\r
118         case(GL_ALPHA):             { float a = float(*data++)*scale; return osg::Vec4(1.0f, 1.0f, 1.0f, a); }\r
119         case(GL_LUMINANCE_ALPHA):   { float l = float(*data++)*scale; float a = float(*data++)*scale; return osg::Vec4(l,l,l,a); }\r
120         case(GL_RGB):               { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; return osg::Vec4(r,g,b,1.0f); }\r
121         case(GL_RGBA):              { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; float a = float(*data++)*scale; return osg::Vec4(r,g,b,a); }\r
122         case(GL_BGR):               { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; return osg::Vec4(r,g,b,1.0f); }\r
123         case(GL_BGRA):              { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; float a = float(*data++)*scale; return osg::Vec4(r,g,b,a); }\r
124     }\r
125     return osg::Vec4(1.0f,1.0f,1.0f,1.0f);\r
126 }\r
127 \r
128 osg::Vec4 getColor(const unsigned char* ptr, GLenum pixelFormat, GLenum dataType)\r
129 {\r
130     switch(dataType)\r
131     {\r
132         case(GL_BYTE):              return _readColor(pixelFormat, (char*)ptr,             1.0f/128.0f);\r
133         case(GL_UNSIGNED_BYTE):     return _readColor(pixelFormat, (unsigned char*)ptr,    1.0f/255.0f);\r
134         case(GL_SHORT):             return _readColor(pixelFormat, (short*)ptr,            1.0f/32768.0f);\r
135         case(GL_UNSIGNED_SHORT):    return _readColor(pixelFormat, (unsigned short*)ptr,   1.0f/65535.0f);\r
136         case(GL_INT):               return _readColor(pixelFormat, (int*)ptr,              1.0f/2147483648.0f);\r
137         case(GL_UNSIGNED_INT):      return _readColor(pixelFormat, (unsigned int*)ptr,     1.0f/4294967295.0f);\r
138         case(GL_FLOAT):             return _readColor(pixelFormat, (float*)ptr,            1.0f);\r
139     }\r
140     return osg::Vec4(1.0f,1.0f,1.0f,1.0f);\r
141 }\r
142 \r
143 osg::Vec4::value_type computeAverage( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] )\r
144 {\r
145     osg::Vec4::value_type r = 0;\r
146     osg::Vec4::value_type nb = 0;\r
147     for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i )\r
148     {\r
149         if ( colorValid[i][j][k] )\r
150         {\r
151             r += colors[i][j][k][c];\r
152             nb += 1;\r
153         }\r
154     }\r
155     return r / nb;\r
156 }\r
157 \r
158 osg::Vec4::value_type computeSum( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] )\r
159 {\r
160     osg::Vec4::value_type r = 0;\r
161     for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i )\r
162         if ( colorValid[i][j][k] )\r
163         {\r
164             r += colors[i][j][k][c];\r
165         }\r
166     return r;\r
167 }\r
168 \r
169 osg::Vec4::value_type computeProduct( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] )\r
170 {\r
171     osg::Vec4::value_type r = 1;\r
172     for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i )\r
173         if ( colorValid[i][j][k] )\r
174         {\r
175             r *= colors[i][j][k][c];\r
176         }\r
177     return r;\r
178 }\r
179 \r
180 osg::Vec4::value_type computeMin( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] )\r
181 {\r
182     osg::Vec4::value_type r = std::numeric_limits<osg::Vec4::value_type>::max();\r
183     for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i )\r
184         if ( colorValid[i][j][k] )\r
185         {\r
186             r = std::min( r, colors[i][j][k][c] );\r
187         }\r
188     return r;\r
189 }\r
190 \r
191 osg::Vec4::value_type computeMax( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2] )\r
192 {\r
193     osg::Vec4::value_type r = std::numeric_limits<osg::Vec4::value_type>::min();\r
194     for ( int k = 0; k < 2; ++k ) for ( int j = 0; j < 2; ++j ) for ( int i = 0; i < 2; ++i )\r
195         if ( colorValid[i][j][k] )\r
196         {\r
197             r = std::max( r, colors[i][j][k][c] );\r
198         }\r
199     return r;\r
200 }\r
201 \r
202 osg::Vec4::value_type computeComponent( int c, osg::Vec4 colors[2][2][2], bool colorValid[2][2][2], MipMapFunction f )\r
203 {\r
204     switch ( f )\r
205     {\r
206     case AVERAGE: return computeAverage( c, colors, colorValid );\r
207     case SUM: return computeSum( c, colors, colorValid );\r
208     case PRODUCT: return computeProduct( c, colors, colorValid );\r
209     case MIN: return computeMin( c, colors, colorValid );\r
210     case MAX: return computeMax( c, colors, colorValid );\r
211     }\r
212     return 0;\r
213 }\r
214 \r
215 osg::Vec4 computeColor( osg::Vec4 colors[2][2][2], bool colorValid[2][2][2], MipMapTuple attrs, GLenum pixelFormat )\r
216 {\r
217     osg::Vec4 result;\r
218     unsigned int nbComponents = osg::Image::computeNumComponents( pixelFormat );\r
219     result[0] = computeComponent( 0, colors, colorValid, attrs.get<0>() );\r
220     if ( nbComponents >= 2 )\r
221         result[1] = computeComponent( 1, colors, colorValid, attrs.get<1>() );\r
222     if ( nbComponents >= 3 )\r
223         result[2] = computeComponent( 2, colors, colorValid, attrs.get<2>() );\r
224     if ( nbComponents == 4 )\r
225         result[3] = computeComponent( 3, colors, colorValid, attrs.get<3>() );\r
226     return result;\r
227 }\r
228 \r
229 osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )\r
230 {\r
231     bool computeMipmap = false;\r
232     unsigned int nbComponents = osg::Image::computeNumComponents( image->getPixelFormat() );\r
233     if ( attrs.get<0>() != AUTOMATIC &&\r
234         ( attrs.get<1>() != AUTOMATIC || nbComponents < 2 ) &&\r
235         ( attrs.get<2>() != AUTOMATIC || nbComponents < 3 ) &&\r
236         ( attrs.get<3>() != AUTOMATIC || nbComponents < 4 ) )\r
237     {\r
238         computeMipmap = true;\r
239     }\r
240     else if ( attrs.get<0>() != AUTOMATIC ||\r
241         ( attrs.get<1>() != AUTOMATIC && nbComponents >= 2 ) ||\r
242         ( attrs.get<2>() != AUTOMATIC && nbComponents >= 3 ) ||\r
243         ( attrs.get<3>() != AUTOMATIC && nbComponents == 4 ) )\r
244     {\r
245         throw BuilderException("invalid mipmap control function combination");\r
246     }\r
247 \r
248     if ( computeMipmap )\r
249     {\r
250         osg::ref_ptr<osg::Image> mipmaps = new osg::Image();\r
251         int s = image->s(),\r
252             t = image->t(),\r
253             r = image->r();\r
254         int nb = osg::Image::computeNumberOfMipmapLevels(s, t, r);\r
255         osg::Image::MipmapDataType mipmapOffsets;\r
256         unsigned int offset = 0;\r
257         for ( int i = 0; i < nb; ++i )\r
258         {\r
259             offset += t * r * osg::Image::computeRowWidthInBytes(s, image->getPixelFormat(), image->getDataType(), image->getPacking());\r
260             mipmapOffsets.push_back( offset );\r
261             s >>= 1; if ( s == 0 ) s = 1;\r
262             t >>= 1; if ( t == 0 ) t = 1;\r
263             r >>= 1; if ( r == 0 ) r = 1;\r
264         }\r
265         mipmapOffsets.pop_back();\r
266         unsigned char *data = new unsigned char[offset];\r
267         memcpy( data, image->data(), mipmapOffsets.front() );\r
268         s = image->s();\r
269         t = image->t();\r
270         r = image->r();\r
271         for ( int m = 0; m < nb-1; ++m )\r
272         {\r
273             unsigned char *src = data;\r
274             if ( m > 0 )\r
275                 src += mipmapOffsets[m-1];\r
276 \r
277             unsigned char *dest = data + mipmapOffsets[m];\r
278 \r
279             int ns = s >> 1; if ( ns == 0 ) ns = 1;\r
280             int nt = t >> 1; if ( nt == 0 ) nt = 1;\r
281             int nr = r >> 1; if ( nr == 0 ) nr = 1;\r
282 \r
283             for ( int k = 0; k < r; k += 2 )\r
284             {\r
285                 for ( int j = 0; j < t; j += 2 )\r
286                 {\r
287                     for ( int i = 0; i < s; i += 2 )\r
288                     {\r
289                         osg::Vec4 colors[2][2][2];\r
290                         bool colorValid[2][2][2];\r
291                         colorValid[0][0][0] = false; colorValid[0][0][1] = false; colorValid[0][1][0] = false; colorValid[0][1][1] = false;\r
292                         colorValid[1][0][0] = false; colorValid[1][0][1] = false; colorValid[1][1][0] = false; colorValid[1][1][1] = false;\r
293                         if ( true )\r
294                         {\r
295                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j, k );\r
296                             colors[0][0][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
297                             colorValid[0][0][0] = true;\r
298                         }\r
299                         if ( i + 1 < s )\r
300                         {\r
301                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j, k );\r
302                             colors[0][0][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
303                             colorValid[0][0][1] = true;\r
304                         }\r
305                         if ( j + 1 < t )\r
306                         {\r
307                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j + 1, k );\r
308                             colors[0][1][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
309                             colorValid[0][1][0] = true;\r
310                         }\r
311                         if ( i + 1 < s && j + 1 < t )\r
312                         {\r
313                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j + 1, k );\r
314                             colors[0][1][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
315                             colorValid[0][1][1] = true;\r
316                         }\r
317                         if ( k + 1 < r )\r
318                         {\r
319                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j, k + 1 );\r
320                             colors[1][0][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
321                             colorValid[1][0][0] = true;\r
322                         }\r
323                         if ( i + 1 < s && k + 1 < r )\r
324                         {\r
325                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j, k + 1 );\r
326                             colors[1][0][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
327                             colorValid[1][0][1] = true;\r
328                         }\r
329                         if ( j + 1 < t && k + 1 < r )\r
330                         {\r
331                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i, j + 1, k + 1 );\r
332                             colors[1][1][0] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
333                             colorValid[1][1][0] = true;\r
334                         }\r
335                         if ( i + 1 < s && j + 1 < t && k + 1 < r )\r
336                         {\r
337                             unsigned char *ptr = imageData( src, image->getPixelFormat(), image->getDataType(), s, t, image->getPacking(), i + 1, j + 1, k + 1 );\r
338                             colors[1][1][1] = getColor( ptr, image->getPixelFormat(), image->getDataType() );\r
339                             colorValid[1][1][1] = true;\r
340                         }\r
341 \r
342                         unsigned char *ptr = imageData( dest, image->getPixelFormat(), image->getDataType(), ns, nt, image->getPacking(), i/2, j/2, k/2 );\r
343                         osg::Vec4 color = computeColor( colors, colorValid, attrs, image->getPixelFormat() );\r
344                         setColor( ptr, image->getPixelFormat(), image->getDataType(), color );\r
345                     }\r
346                 }\r
347             }\r
348             s = ns;\r
349             t = nt;\r
350             r = nr;\r
351         }\r
352         mipmaps->setImage( image->s(), image->t(), image->r(), \r
353             image->getInternalTextureFormat(), image->getPixelFormat(),\r
354             image->getDataType(), data, osg::Image::USE_NEW_DELETE, image->getPacking() );\r
355         mipmaps->setMipmapLevels( mipmapOffsets );\r
356 \r
357         return mipmaps.release();\r
358     }\r
359     return image;\r
360 }\r
361 } }\r