MercuryTextureManager.cpp

Go to the documentation of this file.
00001 #include "global.h"
00002 #include "MercuryTextureManager.h"
00003 #include "MercuryDisplay.h"
00004 #include "MercuryLog.h"
00005 #include "MercuryUtil.h"
00006 #include "MercuryFiles.h"
00007 
00008 #define     MC_TEXTURESTATS     1
00009 #define     MC_LOADTEXTURE      2
00010 #define     MC_UNREGISTER       3
00011 #define     MC_LOADTEXTURERAW   4
00012 
00013 
00014 TextureID::~TextureID()
00015 {
00016     unsigned int num = m_textures.size();
00017     for (unsigned int i = 0; i < num; ++i)
00018         m_textures[i] = NULL;
00019     DISPLAY->DeleteTextures(1, &m_ID);
00020 }
00021 
00022 TextureID::TextureID(const MString& path, bool loadPending)
00023 {
00024     m_ID = 0;
00025     m_path = path;
00026     m_loadPending = loadPending;
00027 }
00028 
00029 bool TextureID::RemoveTexturePtr(const MercuryTexture* texture)
00030 {
00031     unsigned int i;
00032     for( i = 0; i < m_textures.size(); i++ )
00033         if( m_textures[i] == texture )
00034         {
00035             m_textures[i] = NULL;
00036             m_textures.remove(i);
00037             return true;
00038         }
00039     return false;
00040 }
00041 
00042 void TextureID::UpdateTextureData(MercuryTexture* texture)
00043 {
00044     DISPLAY->UpdateTextureData(texture, m_ID);
00045 }
00046 
00047 MercuryTextureManager::MercuryTextureManager()
00048 {
00049     MercuryMessageHandler::MercuryMessageHandler();
00050     m_textureCount = 0;
00051 
00052     MercuryMessageHandler::RegisterMessage( MC_TEXTURESTATS, "RenderStats" );
00053     MercuryMessageHandler::RegisterMessage( MC_LOADTEXTURE, "LoadTexture" );
00054     MercuryMessageHandler::RegisterMessage( MC_UNREGISTER, "UnregisterTexture" );
00055     MercuryMessageHandler::RegisterMessage( MC_LOADTEXTURERAW, "LoadTextureRaw" );
00056 }
00057 
00058 MercuryTextureManager::~MercuryTextureManager()
00059 {
00060     MLockPtr< map<MString, TextureID*> > textureMap(m_textureMap, m_mtxMap);
00061     for(map<MString, TextureID*>::iterator it = textureMap->begin();
00062         it != textureMap->end(); ++it)
00063             SAFE_DELETE(it->second);
00064 }
00065 
00066 void MercuryTextureManager::Message( int Message, PStack & data, const MString & name )
00067 {
00068     switch ( Message ) 
00069     {
00070     case MC_TEXTURESTATS:
00071         {
00072             PStack stats;
00073             stats.PushItemBack( PSElement ((int)(UniqueTextureCount())) );
00074             stats.PushItemBack( PSElement ((int)(TextureCount())) );
00075             MESSAGEMAN->PostSystemMessage( "TextureStats", stats, 0 );
00076         }
00077         break;
00078     case MC_LOADTEXTURE:
00079         {
00080             MString path = data.PeekItem(0).GetValueS();
00081             MercuryTexture* t = (MercuryTexture*)data.PeekItem(1).GetValueV();
00082 
00083             if (!path.empty())
00084             {
00085                 ThreadLoadData* tld = new ThreadLoadData(path,t);
00086                 if (!TryFromExisting(tld))
00087                     MLockPtr< MDeque<ThreadLoadData*> >(m_loadQueue, m_mtxLoadQueue)->push_front(tld);
00088             }
00089         }
00090         break;
00091     case MC_UNREGISTER:
00092         {
00093             MString path = data.PeekItem(0).GetValueS();
00094             MercuryTexture* t = (MercuryTexture*)data.PeekItem(1).GetValueV();
00095             Remove(path, t);
00096         }
00097         break;
00098     case MC_LOADTEXTURERAW:
00099         {
00100             MString path = data.PeekItem(0).GetValueS();
00101             MercuryTexture* t = (MercuryTexture*)data.PeekItem(1).GetValueV();
00102             RawImageData* imagedata = (RawImageData*)data.PeekItem(2).GetValueV();
00103 
00104             if (!path.empty())
00105             {
00106                 ThreadLoadData* tld = new ThreadLoadData(path,t,imagedata);
00107                 if (!TryFromExisting(tld))
00108                     MLockPtr< MDeque<ThreadLoadData*> >(m_loadQueue, m_mtxLoadQueue)->push_front(tld);
00109             }
00110         }
00111         break;
00112     }
00113 }
00114 
00115 bool MercuryTextureManager::TryFromExisting(ThreadLoadData* tld)
00116 {
00117     TextureID* tid = FindTexture(tld->path);
00118     if (tid)
00119     {
00120         tid->m_textures.push_back(tld->texture);
00121         ++m_textureCount;
00122 
00123         PStack data;
00124         data.PushItemBack(PSElement((void*)tld->texture));
00125         data.PushItemBack(PSElement(&(tid->m_attrs)));
00126         MESSAGEMAN->PostSystemMessage("GetTextureID", data);
00127 
00128         SAFE_DELETE(tld);
00129         return true;
00130     }
00131     return false;
00132 }
00133 
00134 void* TheadFunctCaller(void* x)
00135 {
00136     MercuryTextureManager* y = (MercuryTextureManager*)x;
00137     y->ThreadLoop();
00138     return 0;
00139 }
00140 
00141 void MercuryTextureManager::CreateThread()
00142 {
00143 #if !defined( NO_THREADS )
00144     m_thread.Create(TheadFunctCaller, this);
00145 #endif
00146 }
00147 
00148 void MercuryTextureManager::Update( const float dTime )
00149 {
00150 #if defined( NO_THREADS )
00151     ProcessLoadQueue();
00152 #endif
00153     ProcessFinalizeQueue();
00154 }
00155 
00156 RawImageData * MercuryTextureManager::LoadDyn( const MString & sFullName, MString & sShortName )
00157 {
00158     if( sFullName.length() < 7 )
00159         return NULL;
00160     MVector< MString > vSplit;
00161     SplitStrings( sFullName.substr( 7 ), vSplit, ",", " ", 1, 1 );
00162     if( vSplit.size() != 4 )
00163     {
00164         LOG.Warn( "ERROR: Dynamic Textures MUST be reated in the following form: (Name),(Width),(Height),(bitmode=0|1)" );
00165         return NULL;
00166     }
00167     RawImageData * k = new RawImageData;
00168     k->attrs.m_ColorByteType = atoi(vSplit[3].c_str())?RGBA_FLOAT:RGBA;
00169     k->attrs.m_height = k->attrs.m_height_original = atoi( vSplit[2].c_str() );
00170     k->attrs.m_width = k->attrs.m_width_original = atoi( vSplit[1].c_str() );
00171 //  k->attrs.m_isDynamic = true; //XXX FIX ME
00172     k->attrs.m_ID = 0;
00173 
00174     //don't even try to fill the data
00175     k->data = 0;
00176     sShortName = vSplit[0];
00177     return k;
00178 }
00179 
00180 RawImageData * MercuryTextureManager::LoadDynShadow( const MString & sFullName, MString & sShortName )
00181 {
00182     //ERROR feature not implemented....this should never be called
00183     //Crash the program, so that anyone with a debugger can find this location
00184     *((unsigned int*)(NULL)) = 1;
00185 
00186 
00187     if( sFullName.length() < 13 )
00188         return NULL;
00189     MVector< MString > vSplit;
00190     SplitStrings( sFullName.substr( 13 ), vSplit, ",", " ", 1, 1 );
00191     if( vSplit.size() != 4 )
00192     {
00193         LOG.Warn( "ERROR: Dynamic Textures MUST be reated in the following form: (Name),(Width),(Height),(bitmode=0|1)" );
00194         return NULL;
00195     }
00196     RawImageData * k = new RawImageData;
00197     k->attrs.m_ColorByteType = DEPTH_COMPONENT24;
00198     k->attrs.m_height = k->attrs.m_height_original = atoi( vSplit[2].c_str() );
00199     k->attrs.m_width = k->attrs.m_width_original = atoi( vSplit[1].c_str() );
00200 //  k->attrs.m_isDynamicShadow = true; //XXX FIX ME
00201     k->attrs.m_ID = 0;
00202 
00203     //don't even try to fill the data
00204     k->data = 0;
00205     sShortName = vSplit[0];
00206     return k;
00207 }
00208 
00209 RawImageData * MercuryTextureManager::LoadDynCube( const MString & sFullName, MString & sShortName )
00210 {
00211     if( sFullName.length() < 8 )
00212         return NULL;
00213     MVector< MString > vSplit;
00214     SplitStrings( sFullName.substr( 8 ), vSplit, ",", " ", 1, 1 );
00215     if( vSplit.size() != 4 )
00216     {
00217         LOG.Warn( "ERROR: Dynamic Textures MUST be reated in the following form: (Name),(Width),(Height),(bitmode=0|1)" );
00218         return NULL;
00219     }
00220     RawImageData * k = new RawImageData;
00221     k->attrs.m_ColorByteType = atoi(vSplit[3].c_str())?RGBA_FLOAT:RGBA;
00222     k->attrs.m_height = k->attrs.m_height_original = atoi( vSplit[2].c_str() );
00223     k->attrs.m_width = k->attrs.m_width_original = atoi( vSplit[1].c_str() );
00224 //  k->attrs.m_isDynamic = true; //XXX FIX ME
00225     k->attrs.m_ID = 0;
00226 //  k->attrs.m_mapping = CUBE; //XXX FIX ME
00227 
00228     //don't even try to fill the data
00229     k->data = 0;
00230     sShortName = vSplit[0];
00231     return k;
00232 }
00233 
00234 //The cobe map raw image data has the data of each of 6 raw faces stuck together onto 1 data array.....
00235 RawImageData *MercuryTextureManager::LoadCube( const MString & sFullName, MString & sShortName )
00236 {
00237     unsigned int strLen = sFullName.size();
00238     unsigned int i = 0, j = 0, c = 0;
00239 
00240     MString cubeStr[6];
00241 
00242     RawImageData *pCubeRaw[6];
00243 
00244     //appendedRaw has the width and height of a regular face...but the data is really that of 6 raw faces stuck together
00245     RawImageData *pFinalRaw;
00246 
00247     //skip until we find a CUBETEX:
00248     while (c < strLen && sFullName[c] != ':')
00249         c++;
00250     c++;
00251 
00252     //for each of the six cube faces
00253     for (i = 0; i < 6 && c < strLen; i++)
00254     {
00255         j = 0;
00256 
00257         //all of the stuff until the next "," is a path for a cube face
00258         while (c < strLen && sFullName[c] != ',')
00259         {
00260             cubeStr[i] += sFullName[c++];
00261             j++;
00262         }
00263 
00264         LOG.Log(cubeStr[i]);
00265         //getch();
00266 
00267         //skip the actual comma
00268         c++;
00269     }
00270 
00271     //if there weren't 6 faces after all, then something is wrong....
00272     if (i < 6)
00273     {
00274         LOG.Warn( "ERROR: Cube Textures MUST be treated in the following form: minusXpath, plusXpath, minusYpath, plusYpath, minusZpath, plusZpath" );
00275         return NULL;
00276     }
00277 
00278     //now we need the data.......
00279     for (i = 0; i < 6; i++)
00280         pCubeRaw[i] = ImageLoader(cubeStr[i]);
00281 
00282     //if any data is missing, then barf
00283     for (i = 0; i < 6; i++)
00284     {
00285         if (pCubeRaw[i]->data == 0)
00286         {
00287             //kill the faces
00288             for (j = 0; j < 6; j++)
00289             {
00290                 SAFE_DELETE (pCubeRaw[i]->data);
00291                 SAFE_DELETE (pCubeRaw[i]);
00292             }
00293 
00294             //barf
00295             LOG.Warn( (MString)("ERROR: cube face \"") + cubeStr[i] + "\" is not loading properly, cube map aborted ()\n" );
00296             return NULL;
00297         }
00298     }
00299     
00300     //if the attrs do not match up, then barf
00301     for (i = 2; i < 6; i++)
00302     {
00303         if (pCubeRaw[i]->attrs.m_width  != pCubeRaw[i-1]->attrs.m_width ||
00304             pCubeRaw[i]->attrs.m_height != pCubeRaw[i-1]->attrs.m_height||
00305             pCubeRaw[i]->attrs.m_ColorByteType != pCubeRaw[i]->attrs.m_ColorByteType)
00306         {
00307             //kill the faces
00308             for (j = 0; j < 6; j++)
00309             {
00310                 SAFE_DELETE (pCubeRaw[i]->data);
00311                 SAFE_DELETE (pCubeRaw[i]);
00312             }
00313 
00314             //barf
00315             LOG.Warn( (MString)("ERROR: cube faces have inconsistent data\n") );
00316             return NULL;
00317         }
00318     }
00319 
00320     //holy hell, if we got this far, then we must have a damn good cube map
00321     //load up the actual data
00322     pFinalRaw = new RawImageData();
00323     pFinalRaw->attrs = pCubeRaw[0]->attrs;
00324 //  pFinalRaw->attrs.m_mapping = CUBE; //XXX FIX ME
00325     pFinalRaw->attrs.m_ID = 0;
00326     unsigned int faceLength  = pFinalRaw->attrs.m_width * pFinalRaw->attrs.m_height * ColorBytesToSize(pFinalRaw->attrs.m_ColorByteType);
00327     unsigned int totalLength = 6*faceLength;
00328     pFinalRaw->data = new unsigned char [totalLength];
00329 
00330 printf ("pData = %lu\nfaceLength = %lu\ntotalLength = %lu\nfinished newing CubeMap data\n", pFinalRaw->data, faceLength, totalLength);
00331 
00332     c = 0;
00333     for (i = 0; i < 6; i++)
00334     {
00335         for (j = 0; j < faceLength; j++)
00336         {
00337             pFinalRaw->data[c++] = pCubeRaw[i]->data[j];
00338         }
00339     }
00340 
00341     //kill the faces
00342     for (i = 0; i < 6; i++)
00343     {
00344         SAFE_DELETE (pCubeRaw[i]->data);
00345         SAFE_DELETE (pCubeRaw[i]);
00346     }
00347 
00348 LOG.Log("done with cube map raw image loading");
00349 //getch();
00350 
00351     return pFinalRaw;
00352 }
00353 
00354 MercuryTexture* MercuryTextureManager::CreateTexture(const MString& path)
00355 {
00356     MercuryTexture* texture = new MercuryTexture;
00357     texture->Init();
00358     texture->SetPath(path);
00359     texture->SendLoadMessage();
00360     return texture;
00361 }
00362 
00363 TextureID* MercuryTextureManager::RegisterNewTexture(const MercuryTexture* t, RawImageData* d, const MString& path)
00364 {
00365     //Keep this around, we may actually need to lock the entire time if we operate on
00366     //data contained in the map. But it should be safe since the only other thread that
00367     //does anything only adds new TextureIDs to the map.
00368 //  MLockPtr< map<MString, TextureID*> > tMap(m_textureMap, m_mtxMap);
00369 
00370     //If we find a TextureID, assume we can use it (threaded load can add them).
00371     //Otherwise make a new one.
00372     TextureID* tid = FindTexture(path);
00373     if (!tid)
00374     {
00375         tid = new TextureID(path);
00376         MLockPtr< map<MString, TextureID*> >(m_textureMap, m_mtxMap)->operator[](tid->m_path) = tid;  //Map insertion
00377     }
00378 
00379     tid = FindTexture(path);
00380     ASSERT(tid);
00381     tid->m_loadPending = false;
00382     d->CorrectSize();
00383     DISPLAY->CreateCache(d, tid->m_ID);
00384     d->attrs.m_ID = tid->m_ID;
00385     tid->m_attrs = d->attrs;
00386 
00387     SAFE_DELETE(d);
00388 
00389     return tid;
00390 }
00391 
00392 void MercuryTextureManager::ThreadLoop()
00393 {
00394     while(true)
00395     {
00396         ProcessLoadQueue();
00397         Sleep(10);
00398     }
00399 }
00400 
00401 void MercuryTextureManager::ProcessLoadQueue()
00402 {
00403     /*  This is run from a seperate thread than Update() so accessed
00404         data needs to be protected. */
00405 
00406     MDeque<ThreadLoadData*> loadQueue;
00407     MLockPtr< MDeque<ThreadLoadData*> >* loadQueueTmp = new MLockPtr< MDeque<ThreadLoadData*> >(m_loadQueue, m_mtxLoadQueue);
00408 
00409     //make a copy of the m_loadQueue and empty it so its
00410     //not locked for the duration of this function
00411     if ((*loadQueueTmp)->empty())
00412     {
00413         SAFE_DELETE(loadQueueTmp);
00414         return;
00415     }
00416     else
00417     {
00418         while (!(*loadQueueTmp)->empty())
00419         {
00420             loadQueue.push_back((*loadQueueTmp)->front());
00421             (*loadQueueTmp)->pop_front();
00422         }
00423         SAFE_DELETE(loadQueueTmp);
00424     }
00425 
00426     while (!loadQueue.empty())
00427     {
00428         ThreadLoadData* tld = loadQueue.front();
00429         loadQueue.pop_front();
00430 
00431         const TextureID* tid = FindTexture(tld->path);
00432         if (!tid)
00433         {
00434             //Load some data then queue it
00435             LOG.Log("Loading texture data from " + tld->path + " (loadQueue).");
00436 
00437             if (!tld->d)
00438             {
00439                 RawImageData* d = ImageLoader(tld->path);
00440                 if (!d)
00441                     return; //Don't do anything
00442 
00443                 tld->d = d;
00444             }
00445 
00446             /*  create a new TextureID and put it in the map so that
00447                 we don't try to load the same image data in the future */
00448             TextureID* tid = new TextureID(tld->path, true);
00449             MLockPtr< map<MString, TextureID*> >(m_textureMap, m_mtxMap)->operator[](tid->m_path) = tid;  //Map insertion
00450         }
00451         else
00452         {
00453             //If the texture is found we don't want to follow the pointer later
00454             tld->d = NULL; 
00455         }
00456         //Queue in order what we have (from FindTexture or newly loaded data)
00457         MLockPtr< MDeque<ThreadLoadData*> >(m_finalizeLoadQueue, m_mtxFinalize)->push_back(tld);
00458     }
00459 }
00460 
00461 void MercuryTextureManager::ProcessFinalizeQueue()
00462 {
00463     TextureID* tid = NULL;
00464 
00465     MLockPtr< MDeque<ThreadLoadData*> > finalizeQueue(m_finalizeLoadQueue, m_mtxFinalize);
00466 
00467     while(!finalizeQueue->empty())
00468     {
00469         tid = NULL;
00470 
00471         //Process things in order (very important)
00472         ThreadLoadData* tld = finalizeQueue->front();
00473         finalizeQueue->pop_front();
00474 
00475         if (!tld->d)
00476         {
00477             //Texture exists somewhere, find it and use it
00478             tid = FindTexture(tld->path);
00479 //          TryFromExisting(tld);
00480 //          return;
00481         }
00482         else
00483         {
00484             LOG.Log("Texture cache for " + tld->path + " not found, generating cache (finalizeQueue).");
00485             tid = RegisterNewTexture(tld->texture, tld->d, tld->path);
00486         }
00487 
00488         tid->m_textures.push_back(tld->texture);
00489         ++m_textureCount;
00490 
00491         PStack data;
00492         data.PushItemBack(PSElement((void*)tld->texture));
00493         data.PushItemBack(PSElement(&(tid->m_attrs)));
00494         MESSAGEMAN->PostSystemMessage("GetTextureID", data);
00495 
00496         SAFE_DELETE(tld);
00497     }
00498 }
00499 
00500 
00501 void MercuryTextureManager::Remove(MercuryTexture* texture)
00502 {
00503     if (texture->GetID() <= 0)
00504         return;
00505 
00506     MDequeIterator<MercuryTexture*> i;
00507 
00508     TextureID* tmp = FindTexture(texture);
00509     if (tmp)
00510     {
00511         if(tmp->RemoveTexturePtr(texture))
00512             --m_textureCount;
00513     
00514         if (tmp->m_textures.empty())
00515         {
00516             MLockPtr< map<MString, TextureID*> >  textureMap(m_textureMap, m_mtxMap);
00517             textureMap->erase(texture->GetName());
00518         }
00519     }
00520 }
00521 
00522 void MercuryTextureManager::Remove(const MString& path, const MercuryTexture* texture)
00523 {
00524 //  if (texture->GetID() <= 0)
00525 //      return;
00526 
00527     MDequeIterator<MercuryTexture*> i;
00528 
00529     TextureID* tmp = FindTexture(path);
00530     if (tmp)
00531     {
00532         if(tmp->RemoveTexturePtr(texture))
00533             --m_textureCount;
00534     
00535         if (tmp->m_textures.empty())
00536         {
00537             MLockPtr< map<MString, TextureID*> >  textureMap(m_textureMap, m_mtxMap);
00538             SAFE_DELETE(tmp);
00539             textureMap->erase(path);
00540         }
00541     }
00542 }
00543 
00544 TextureID* MercuryTextureManager::FindTexture(const MString& path) const
00545 {
00546     MString name = path;
00547 
00548     TextureID* tID = NULL;
00549     MLockPtr< map<MString, TextureID*> >  textureMap(m_textureMap, m_mtxMap);
00550     map<MString, TextureID*>::const_iterator i = textureMap->find(name);
00551 
00552     if (i != textureMap->end())
00553         tID = (i->second);
00554 
00555     return tID;
00556 }
00557 
00558 void MercuryTextureManager::UpdateTexture(MercuryTexture* texture)
00559 {
00560     TextureID* tmp = FindTexture(texture);
00561     if (!tmp)
00562     {
00563         LOG.Log("Texture " + texture->GetPath() + " not found, can not update.");
00564         return;
00565     }
00566     tmp->UpdateTextureData(texture);
00567 }
00568 
00569 bool MercuryTextureManager::IsTexture(const MercuryTexture* texture)
00570 {
00571     if (!FindTexture(texture))
00572         return false;
00573     return true;
00574 }
00575 
00576 RawImageData* MercuryTextureManager::ImageLoader(const MString& path)
00577 {
00578 LOG.Log("about to load texture: " + path);
00579 
00580     if(path.length() > 7 && path.substr( 0, 7 ) == "DYNTEX:" )
00581     {
00582         MString sDiscardName;
00583         return LoadDyn( path, sDiscardName );
00584     }
00585     else if(path.length() > 8 && path.substr( 0, 8 ) == "CUBETEX:" )
00586     {
00587 LOG.Log("WE've GOT A CUBE TEX");
00588 
00589         MString sDiscardName;
00590         return LoadCube( path, sDiscardName );
00591 
00592     }
00593     else if(path.length() > 8 && path.substr(0, 8) == "DYNCUBE:" )
00594     {
00595         MString sDiscardName;
00596         return LoadDynCube( path, sDiscardName );
00597     }
00598     else if(path.length() > 13 && path.substr(0, 13) == "DYNTEXSHADOW:" )
00599     {
00600 LOG.Log("WE've GOT A DYN TEX SHADOW");
00601         MString sDiscardName;
00602         return LoadDynShadow( path, sDiscardName);
00603     }
00604 
00605     if (path.empty())
00606         return NULL;
00607 
00608     MercuryFile * f = FILEMAN.Open( path );
00609     if (!f)
00610     {
00611         LOG.Warn("Could not open " + path);
00612         return false;
00613     }
00614 
00615     RawImageData* raw = IMAGEREADERREGISTER.Decode( f );
00616 
00617     SAFE_DELETE(f);
00618 
00619     if( !raw )
00620         return NULL;
00621 
00622     return raw;
00623 }
00624 
00625 void MercuryImageReaderRegister::CheckInit()
00626 {
00627     if( !bInit )
00628     {
00629         bInit = true;
00630         m_hDecoders = new MHash< ImageDecoder >;
00631     }
00632 }
00633 
00634 void MercuryImageReaderRegister::AddDecoder( ImageDecoder p, const char * sFingerPrint )
00635 {
00636     CheckInit();
00637     MString sFP( sFingerPrint, 3 );
00638     (*m_hDecoders)[sFP] = p;
00639 }
00640 
00641 RawImageData * MercuryImageReaderRegister::Decode( MercuryFile * f )
00642 {
00643     if( !m_hDecoders )
00644     {
00645         FAIL( "ERROR: There are no image loaders dynamically included.  Cannot operate." );
00646         return NULL;
00647     }
00648     char fingerprint[4];
00649     f->Read( fingerprint, 3 );
00650     MString sFP( fingerprint, 3 );
00651 
00652     f->Seek( 0 );
00653     if( m_hDecoders->get( sFP ) )
00654     {
00655         return (*m_hDecoders)[sFP](f);
00656     }
00657     else
00658         return NULL;
00659 }
00660 
00661 MercuryImageReaderRegister IMAGEREADERREGISTER;
00662 
00663 /* 
00664  * Copyright (c) 2005-2006, Joshua Allen
00665  * All rights reserved.
00666  *
00667  * Redistribution and use in source and binary forms, with or
00668  * without modification, are permitted provided that the following
00669  * conditions are met:
00670  *  -   Redistributions of source code must retain the above
00671  *      copyright notice, this list of conditions and the following disclaimer.
00672  *  -   Redistributions in binary form must reproduce the above copyright
00673  *      notice, this list of conditions and the following disclaimer in
00674  *      the documentation and/or other materials provided with the distribution.
00675  *  -   Neither the name of the Mercury Engine nor the names of its
00676  *      contributors may be used to endorse or promote products derived from
00677  *      this software without specific prior written permission.
00678  *
00679  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00680  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00681  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00682  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
00683  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00684  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00685  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00686  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00687  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00688  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00689  */

Hosted by SourceForge.net Logo