pixel_format.C

00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2006 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
00018  */
00019 
00020 #include <map>
00021 
00022 #include <archon/util/text.H>
00023 #include <archon/util/pixel_format.H>
00024 
00025 
00026 namespace Archon
00027 {
00028   namespace Utilities
00029   {
00030     namespace
00031     {
00032       struct WordTypeDescriptor
00033       {
00034         PixelFormat::WordType type;
00035         string tag;
00036         int width; 
00037         bool isFloat;
00038 
00039         WordTypeDescriptor() {}
00040 
00041         WordTypeDescriptor(PixelFormat::WordType type, string tag, int width,
00042                            bool isFloat):
00043           type(type), tag(tag), width(width), isFloat(isFloat) {}
00044       };
00045 
00046       struct WordTypeRegistry
00047       {
00048         map<PixelFormat::WordType, WordTypeDescriptor> typeMap;
00049         map<string, WordTypeDescriptor> tagMap;
00050         map<int,    WordTypeDescriptor> intWidthMap;   
00051         map<int,    WordTypeDescriptor> floatWidthMap; 
00052 
00053         template<typename T> void addDescriptor(PixelFormat::WordType type, string tag)
00054         {
00055           int width = sizeof(T) * numeric_limits<unsigned char>::digits;
00056           bool isFloat = !numeric_limits<T>::is_integer;
00057           WordTypeDescriptor d(type, tag, width, isFloat);
00058           typeMap[type] = tagMap[tag] = d;
00059           if(isFloat) floatWidthMap[width] = d;
00060           else intWidthMap[width] = d;
00061         }
00062 
00063         WordTypeRegistry()
00064         {
00065           addDescriptor<unsigned char>       (PixelFormat::std_char,        "char");
00066           addDescriptor<unsigned short>      (PixelFormat::std_short,       "short");
00067           addDescriptor<unsigned int>        (PixelFormat::std_int,         "int");
00068           addDescriptor<unsigned long>       (PixelFormat::std_long,        "long");
00069           addDescriptor<PixelFormat::MaxInt> (PixelFormat::std_max_int,     "max_int");
00070           addDescriptor<float>               (PixelFormat::std_float,       "float");
00071           addDescriptor<double>              (PixelFormat::std_double,      "double");
00072           addDescriptor<long double>         (PixelFormat::std_long_double, "long_double");
00073         }
00074       };
00075 
00076       const WordTypeRegistry &getWordTypeRegistry()
00077       {
00078         static WordTypeRegistry registry;
00079         return registry;
00080       }
00081 
00082       const WordTypeDescriptor &getWordTypeDescriptor(PixelFormat::WordType type)
00083       {
00084         return getWordTypeRegistry().typeMap.find(type)->second;
00085       }
00086     }
00087 
00095     PixelFormat PixelFormat::getExpandedFormat()
00096       const throw(UnsupportedWordTypeException, InconsistencyException)
00097     {
00098 cerr << "PixelFormat::getExpandedFormat: ---------------" << endl;
00099       PixelFormat complete;
00100       complete.formatType = formatType;
00101       complete.mostSignificantBitsFirst = mostSignificantBitsFirst;
00102 
00103 
00104       // Determine word type
00105       WordTypeDescriptor wordTypeDescriptor;
00106       if(wordType == custom_int)
00107       {
00108         map<int, WordTypeDescriptor>::const_iterator i =
00109           getWordTypeRegistry().intWidthMap.find(bitsPerWord);
00110         if(i == getWordTypeRegistry().intWidthMap.end())
00111           ARCHON_THROW(UnsupportedWordTypeException);
00112         wordTypeDescriptor = i->second;
00113       }
00114       else if(wordType == custom_float)
00115       {
00116         map<int, WordTypeDescriptor>::const_iterator i =
00117           getWordTypeRegistry().floatWidthMap.find(bitsPerWord);
00118         if(i == getWordTypeRegistry().floatWidthMap.end())
00119           ARCHON_THROW(UnsupportedWordTypeException);
00120         wordTypeDescriptor = i->second;
00121       }
00122       else
00123       {
00124         wordTypeDescriptor = getWordTypeDescriptor(wordType);
00125         if(bitsPerWord && wordTypeDescriptor.width != bitsPerWord)
00126           ARCHON_THROW1(InconsistencyException, "Conflicting bits per word ("+
00127                         Text::toString(bitsPerWord)+") specified for standard "
00128                         "word type ("+wordTypeDescriptor.tag+")");
00129       }
00130       complete.wordType = wordTypeDescriptor.type;
00131       complete.bitsPerWord = wordTypeDescriptor.width;
00132       if(wordTypeDescriptor.isFloat && formatType != direct)
00133         ARCHON_THROW1(InconsistencyException, "Only direct formats can be "
00134                       "used with floating point word types");
00135 cerr << "PixelFormat::getExpandedFormat: wordType: " << wordTypeDescriptor.tag << endl;
00136 cerr << "PixelFormat::getExpandedFormat: bitsPerWord: " << complete.bitsPerWord << endl;
00137 
00138       // Determine the number of channels
00139       int numberOfChannels;
00140       switch(colorScheme)
00141       {
00142       case luminance:
00143         numberOfChannels = hasAlphaChannel ? 2 : 1;
00144         break;
00145 
00146       case rgb:
00147       case hsv:
00148         numberOfChannels = hasAlphaChannel ? 4 : 3;
00149         break;
00150 
00151       default:
00152         numberOfChannels = 0;
00153       }
00154       if(numberOfChannels)
00155       {
00156         if(channelLayout.size() &&
00157            numberOfChannels != static_cast<int>(channelLayout.size()))
00158           ARCHON_THROW1(InconsistencyException, "Mismatching number of "
00159                         "channels in channel layout and color scheme");
00160       }
00161       else numberOfChannels =
00162         channelLayout.size() ? channelLayout.size() :
00163         pixelSize ? pixelSize : hasAlphaChannel ? 2 : 1;
00164 
00165 
00166       // Determine number of bits/words per pixel
00167       if(pixelSize) complete.pixelSize = pixelSize;
00168       else switch(formatType)
00169       {
00170       case direct:
00171         // One word per channel
00172         complete.pixelSize = numberOfChannels;
00173         break;
00174 
00175       case packed:
00176       case tight:
00177         int bitsPerPixel;
00178         if(channelLayout.empty()) bitsPerPixel = numberOfChannels; // Assume one bit per channel
00179         else
00180         {
00181           int maxOffsetIndex = -1;
00182           int minWidth = 0;
00183           for(int i=0; i<static_cast<int>(channelLayout.size()); ++i)
00184           {
00185             const Channel &c = channelLayout[i];
00186             if(maxOffsetIndex < 0 || channelLayout[maxOffsetIndex].offset < c.offset) maxOffsetIndex = i;
00187             if(!minWidth || c.width < minWidth) minWidth = c.width;
00188           }
00189           const Channel &c = channelLayout[maxOffsetIndex];
00190           bitsPerPixel = c.offset + (c.width ? c.width : minWidth ? minWidth : 1);
00191         }
00192         complete.pixelSize = formatType == packed ?
00193           (numberOfChannels - 1) / complete.bitsPerWord + 1 : numberOfChannels;
00194         break;
00195       }
00196 cerr << "PixelFormat::getExpandedFormat: pixelSize: " << complete.pixelSize << " " << (complete.formatType == tight ? "bits" : "words") << endl;
00197 
00198 
00199       // Determine channel layout
00200       if(channelLayout.empty())
00201       {
00202         complete.channelLayout.resize(numberOfChannels);
00203         switch(formatType)
00204         {
00205         case direct:
00206           if(complete.pixelSize < numberOfChannels)
00207             ARCHON_THROW1(InconsistencyException, "Words per pixel less than number of channels for direct format");
00208           for(int i=0; i<numberOfChannels; ++i)
00209           {
00210             Channel &c = complete.channelLayout[i];
00211             c.offset = i;
00212             c.width  = complete.bitsPerWord;
00213           }
00214           break;
00215 
00216         case packed:
00217           if(complete.pixelSize*complete.bitsPerWord < numberOfChannels)
00218             ARCHON_THROW1(InconsistencyException, "Bits per pixel less than number of channels for packed format");
00219           {
00220             int o = 0;
00221             int w = complete.pixelSize*complete.bitsPerWord / numberOfChannels;
00222             for(int i=0; i<numberOfChannels; ++i)
00223             {
00224               Channel &c = complete.channelLayout[i];
00225               c.offset = o;
00226               c.width  = w;
00227               o += w;
00228             }
00229           }
00230           break;
00231 
00232         case tight:
00233           if(complete.pixelSize < numberOfChannels)
00234             ARCHON_THROW1(InconsistencyException, "Bits per pixel less than number of channels for tight format");
00235           {
00236             int o = 0;
00237             int w = complete.pixelSize / numberOfChannels;
00238             for(int i=0; i<numberOfChannels; ++i)
00239             {
00240               Channel &c = complete.channelLayout[i];
00241               c.offset = o;
00242               c.width  = w;
00243               o += w;
00244             }
00245           }
00246           break;
00247         }
00248       }
00249       else
00250       {
00251         vector<int> v(formatType == packed ? complete.pixelSize*complete.bitsPerWord : complete.pixelSize, -1);
00252         for(int i=0; i<numberOfChannels; ++i)
00253         {
00254           int o = channelLayout[i].offset;
00255           if(o < 0 || static_cast<int>(v.size()) <= o) ARCHON_THROW1(InconsistencyException, "Channel "+Text::toString(i)+" offset escapes pixel boundary");
00256           int &w = v[o];
00257           if(w != -1) ARCHON_THROW1(InconsistencyException, "Multiple channels at offset "+Text::toString(o));
00258           w = i;
00259         }
00260         complete.channelLayout = channelLayout;
00261         int maxIntBits = sizeof(MaxInt)*numeric_limits<unsigned char>::digits;
00262         for(int i=0; i<numberOfChannels; ++i)
00263         {
00264           Channel &c = complete.channelLayout[i];
00265           int &w = c.width;
00266           if(w < 0) ARCHON_THROW1(InconsistencyException, "Channel "+Text::toString(i)+" width negative");
00267           if(maxIntBits < w) ARCHON_THROW1(InconsistencyException, "Channel "+Text::toString(i)+" too wide for your CPU architecture");
00268           if(w && wordTypeDescriptor.isFloat && w != bitsPerWord) ARCHON_THROW1(InconsistencyException, "Channel "+Text::toString(i)+" width differs from word width for floating point channel");
00269           switch(formatType)
00270           {
00271           case direct:
00272             // Allow one word per channel
00273             if(!w) w = complete.bitsPerWord;
00274             else if(complete.bitsPerWord < w) ARCHON_THROW1(InconsistencyException, "Channel "+Text::toString(i)+" wider than word type for direct format");
00275             break;
00276 
00277           case packed:
00278           case tight:
00279             // Allow all bits up to next channel
00280             int j = c.offset+1;
00281             while(j < static_cast<int>(v.size()) && v[j] == -1) ++j;
00282             int maxWidth = j - c.offset;
00283             if(maxIntBits < maxWidth) maxWidth = maxIntBits;
00284             if(!w) w = maxWidth;
00285             else if(maxWidth <= w)
00286             {
00287               if(j == static_cast<int>(v.size()))
00288                 ARCHON_THROW1(InconsistencyException, "Channel "+Text::toString(i)+" too wide - extends beyond pixel boundary");
00289               else ARCHON_THROW1(InconsistencyException, "Channel "+
00290                                  Text::toString(i)+" too wide - overlaps "
00291                                  "next channel ("+Text::toString(v[j])+")");
00292             }
00293             break;
00294           }
00295         }
00296       }
00297 cerr << "PixelFormat::getExpandedFormat: channelLayout:";
00298 for(int i=0; i<static_cast<int>(complete.channelLayout.size()); ++i)
00299 {
00300   cerr << " (" << complete.channelLayout[i].offset << ", " << complete.channelLayout[i].width << ")";
00301 }
00302 cerr << endl;
00303 
00304 
00305       // Determine color scheme
00306       if(colorScheme == implied)
00307       {
00308         switch(numberOfChannels)
00309         {
00310         case 1:
00311           complete.colorScheme     = luminance;
00312           complete.hasAlphaChannel = false;
00313           break;
00314 
00315         case 2:
00316           complete.colorScheme     = luminance;
00317           complete.hasAlphaChannel = true;
00318           break;
00319 
00320         case 3:
00321           complete.colorScheme     = rgb;
00322           complete.hasAlphaChannel = false;
00323           break;
00324 
00325         case 4:
00326           complete.colorScheme     = rgb;
00327           complete.hasAlphaChannel = true;
00328           break;
00329 
00330         default:
00331           complete.colorScheme     = custom;
00332           complete.hasAlphaChannel = false;
00333           break;
00334         }
00335       }
00336       else
00337       {
00338         complete.colorScheme     = colorScheme;
00339         complete.hasAlphaChannel = hasAlphaChannel;
00340       }
00341 cerr << "PixelFormat::getExpandedFormat: colorScheme: " << (complete.colorScheme == luminance ? "luminance" :
00342                                                             complete.colorScheme == rgb ? "rgb" :
00343                                                             complete.colorScheme == hsv ? "hsv" :
00344                                                             complete.colorScheme == custom ? "custom" : "implied") << endl;
00345 cerr << "PixelFormat::getExpandedFormat: hasAlphaChannel: " << complete.hasAlphaChannel << endl;
00346 
00347 
00348       return complete;
00349     }
00350 
00351 
00352     PixelFormat PixelFormat::getReducedFormat() const
00353       throw(UnsupportedWordTypeException, InconsistencyException)
00354     {
00355       PixelFormat expanded = getExpandedFormat();
00356       PixelFormat reduced = expanded;
00357 
00358       // Remove channel widths  if equal to inferred
00359       for(int i=0; i<static_cast<int>(reduced.channelLayout.size()); ++i)
00360       {
00361         PixelFormat f = reduced;
00362         f.channelLayout[i].width = 0;
00363         if(f.getExpandedFormat() == expanded) reduced = f;
00364       }
00365 
00366       // Remove channel layout if equal to inferred
00367       {
00368         PixelFormat f = reduced;
00369         f.channelLayout.clear();
00370         if(f.getExpandedFormat() == expanded) reduced = f;
00371       }
00372 
00373       // Remove color-schema if equal to inferred
00374       {
00375         PixelFormat f = reduced;
00376         f.colorScheme = implied;
00377         f.hasAlphaChannel = false;
00378         if(f.getExpandedFormat() == expanded) reduced = f;
00379       }
00380 
00381       // Remove pixel size if equal to inferred
00382       {
00383         PixelFormat f = reduced;
00384         f.pixelSize = 0;
00385         if(f.getExpandedFormat() == expanded) reduced = f;
00386       }
00387 
00388       // Translate to custom word type for portability
00389       reduced.wordType = getWordTypeDescriptor(expanded.wordType).isFloat ?
00390         custom_float : custom_int;
00391 
00392       return reduced;
00393     }
00394 
00395 
00396     string PixelFormat::toString() const
00397     {
00398       PixelFormat expanded = getExpandedFormat();
00399       PixelFormat reduced = expanded.getReducedFormat();
00400       string s = expanded.formatType == packed ? "packed" :
00401         expanded.formatType == tight ? "tight" : "direct";
00402 
00403       s += "_";
00404       s += reduced.wordType == custom_int ? "int" +
00405         Text::toString(expanded.bitsPerWord) :
00406         reduced.wordType == custom_float ? "float" +
00407         Text::toString(expanded.bitsPerWord) :
00408         getWordTypeDescriptor(reduced.wordType).tag;
00409 
00410       s += "_";
00411       if(reduced.channelLayout.empty() && reduced.colorScheme == custom)
00412       {
00413         // For straight layouts of custom channels
00414         s += Text::toString(reduced.pixelSize);
00415         if(expanded.hasAlphaChannel) s += "a";
00416       }
00417       else
00418       {
00419         vector<int> channelMap(expanded.formatType == packed ?
00420                                expanded.pixelSize*expanded.bitsPerWord :
00421                                expanded.pixelSize, -1);
00422         for(int i=0; i<static_cast<int>(expanded.channelLayout.size()); ++i)
00423           channelMap[expanded.channelLayout[i].offset] = i;
00424 
00425         vector<string> colorMap;
00426         switch(expanded.colorScheme)
00427         {
00428         case implied: // Never
00429           break;
00430 
00431         case custom:
00432           {
00433             int n = expanded.channelLayout.size();
00434             if(expanded.hasAlphaChannel) --n;
00435             for(int i=0; i<n; ++i) colorMap.push_back(Text::toString(i+1)+"c");
00436           }
00437           break;
00438 
00439         case luminance:
00440           colorMap.push_back("l");
00441           break;
00442 
00443         case rgb:
00444           colorMap.push_back("r");
00445           colorMap.push_back("g");
00446           colorMap.push_back("b");
00447           break;
00448 
00449         case hsv:
00450           colorMap.push_back("h");
00451           colorMap.push_back("s");
00452           colorMap.push_back("v");
00453           break;
00454         }
00455         colorMap.push_back("a");
00456 
00457 
00458         // Can we use the condensed channel layout
00459         bool condensedLayout = expanded.colorScheme != custom;
00460         if(condensedLayout)
00461         {
00462           // Detect precense of unused fields
00463           if(expanded.formatType == direct)
00464           {
00465             for(int i=0; i<static_cast<int>(channelMap.size()); ++i) if(channelMap[i] < 0)
00466             {
00467               condensedLayout = false;
00468               break;
00469             }
00470           }
00471           else // Packed and tight formats
00472           {
00473             int i=0;
00474             while(i < static_cast<int>(channelMap.size()))
00475             {
00476               int gapSize = 0;
00477               while(i+gapSize < static_cast<int>(channelMap.size()) && channelMap[i+gapSize] < 0) ++gapSize;
00478               if(gapSize)
00479               {
00480                 // Do not output a small explicit final gaps for packed formats since pixels word-align anyway
00481                 if(i == static_cast<int>(channelMap.size()) && gapSize < expanded.bitsPerWord && expanded.formatType == packed) break;
00482 
00483                 condensedLayout = false;
00484                 break;
00485               }
00486 
00487               i += expanded.channelLayout[channelMap[i]].width;;
00488             }
00489           }
00490         }
00491 
00492 
00493         // Generate channel layout
00494         if(expanded.formatType == direct)
00495         {
00496           for(int i=0; i<static_cast<int>(channelMap.size()); ++i)
00497           {
00498             if(i && !condensedLayout) s += "_";
00499             int j = channelMap[i];
00500             s += j < 0 ? "z" : colorMap[j];
00501             if(reduced.channelLayout.size())
00502             {
00503               int w = reduced.channelLayout[j].width;
00504               if(w) s+= Text::toString(w);
00505             }
00506           }
00507         }
00508         else // Packed and tight formats
00509         {
00510           int i=0;
00511           while(i < static_cast<int>(channelMap.size()))
00512           {
00513             // Is there a gap of unused bit here
00514             int w = 0;
00515             while(i+w < static_cast<int>(channelMap.size()) && channelMap[i+w] < 0) ++w;
00516             if(w)
00517             {
00518               // Do not output a small explicit final gaps for packed formats since pixels word-align anyway
00519               if(i == static_cast<int>(channelMap.size()) && w < expanded.bitsPerWord && expanded.formatType == packed) break;
00520 
00521               if(i && !condensedLayout) s += "_";
00522               s += Text::toString(w);
00523             }
00524             else // No gap this time
00525             {
00526               if(i && !condensedLayout) s += "_";
00527               int j = channelMap[i];
00528               w = expanded.channelLayout[j].width;
00529               s += colorMap[j] + Text::toString(w);
00530             }
00531             i += w;
00532           }
00533         }
00534       }
00535 
00536       if(mostSignificantBitsFirst) s += "_msb";
00537       return s;
00538     }
00539   }
00540 }

Generated on Sun Jul 30 22:55:45 2006 for Archon by  doxygen 1.4.4