tex_mgmt.cpp

Go to the documentation of this file.
00001 
00002 //            Copyright (C) 2004-2007 by The Allacrost Project
00003 //                         All Rights Reserved
00004 //
00005 // This code is licensed under the GNU GPL version 2. It is free software
00006 // and you may modify it and/or redistribute it under the terms of this license.
00007 // See http://www.gnu.org/copyleft/gpl.html for details.
00009 
00010 #include "utils.h"
00011 #include "tex_mgmt.h"
00012 #include <cassert>
00013 #include <cstdarg>
00014 #include <set>
00015 #include <math.h>
00016 #include "video.h"
00017 #include "gui.h"
00018 #include <fstream>
00019 
00020 
00021 using namespace hoa_video::private_video;
00022 
00023 
00024 namespace hoa_video
00025 {
00026 
00027 using namespace std;
00028 using namespace hoa_utils;
00029 
00030 
00031 
00032 
00033 //-----------------------------------------------------------------------------
00034 // ConvertImageToGrayscale: Converts an image from color to gray mode
00035 //-----------------------------------------------------------------------------
00036 
00037 void GameVideo::_ConvertImageToGrayscale(const ImageLoadInfo& src, ImageLoadInfo &dst) const
00038 {
00039   if (!dst.width || !dst.height)  // Return if there are no pixels in the image
00040     return;
00041 
00042   // Convert the pixels to grayscale while copying
00043   uint8* src_pix = static_cast<uint8*>(src.pixels);
00044   uint8* src_end = static_cast<uint8*>(src.pixels) + (src.width * src.height * 4);
00045   uint8* dst_pix = static_cast<uint8*>(dst.pixels);
00046   uint8 value;
00047 
00048   for (; src_pix<src_end; src_pix+=4,dst_pix+=4)
00049   {
00050     value = static_cast<uint8>((30 * *(src_pix) + 59 * *(src_pix+1) + 11 * *(src_pix+2))*0.01f);  // Get grayscale value
00051     *dst_pix = *(dst_pix+1) = *(dst_pix+2) = value;   // Assign it
00052     *(dst_pix+3) = *(src_pix+3);          // Assign alpha value
00053   }
00054 }
00055 
00056 
00057 
00058 
00059 //-----------------------------------------------------------------------------
00060 // RGBAToRGB: Converts a buffer from RGBA to RGB
00061 //-----------------------------------------------------------------------------
00062 
00063 void GameVideo::_RGBAToRGB (const private_video::ImageLoadInfo& src, private_video::ImageLoadInfo &dst) const
00064 {
00065   if (!dst.width || !dst.height)  // Return if there are no pixels in the image
00066     return;
00067 
00068   uint8* pSrc = static_cast<uint8*>(src.pixels);
00069   uint8* pDst = static_cast<uint8*>(dst.pixels);
00070 
00071   int32 iSrc;
00072   int32 iDst;
00073   for (int32 i=0; i<src.height*src.width; i++)
00074   {
00075     iSrc = 4 * i;
00076     iDst = 3 * i;
00077     pDst[iDst] = pSrc[iSrc];
00078     pDst[iDst+1] = pSrc[iSrc+1];
00079     pDst[iDst+2] = pSrc[iSrc+2];
00080   }
00081 }
00082 
00083 
00084 //-----------------------------------------------------------------------------
00085 // GetImageInfo: Gets cols, rows and bpp from an image file
00086 //-----------------------------------------------------------------------------
00087 bool GameVideo::GetImageInfo (const std::string& file_name, uint32 &rows, uint32& cols, uint32& bpp)
00088 {
00089   // Isolate the extension
00090   size_t extpos = file_name.rfind('.');
00091 
00092   if(extpos == string::npos)
00093   {
00094     if (VIDEO_DEBUG)
00095       cerr << "VIDEO ERROR: image file extension not specified when calling to GetImageInfo" << endl;
00096     return false;
00097   }
00098 
00099   std::string extension = std::string(file_name, extpos, file_name.length() - extpos);
00100 
00101   if(extension == ".jpeg" || extension == ".jpg")
00102     return _GetImageInfoJpeg(file_name, rows, cols, bpp);
00103   if(extension == ".png")
00104     return _GetImageInfoPng(file_name, rows, cols, bpp);
00105 
00106   if (VIDEO_DEBUG)
00107     cerr << "VIDEO ERROR: image file extension not recognized when calling to GetImageInfo" << endl;
00108 
00109   return false;
00110 }
00111 
00112 
00113 //-----------------------------------------------------------------------------
00114 // GetImageInfoPng: Gets cols, rows and bpp from a PNG image file
00115 //-----------------------------------------------------------------------------
00116 bool GameVideo::_GetImageInfoPng (const std::string& file_name, uint32 &rows, uint32& cols, uint32& bpp)
00117 {
00118   FILE * fp = fopen(file_name.c_str(), "rb");
00119 
00120   if(fp == NULL)
00121     return false;
00122 
00123   uint8 test_buffer[8];
00124 
00125   fread(test_buffer, 1, 8, fp);
00126   if(png_sig_cmp(test_buffer, 0, 8))
00127   {
00128     fclose(fp);
00129     return false;
00130   }
00131 
00132   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00133 
00134   if(!png_ptr)
00135   {
00136     fclose(fp);
00137     return false;
00138   }
00139 
00140   png_infop info_ptr = png_create_info_struct(png_ptr);
00141 
00142   if(!info_ptr)
00143   {
00144     png_destroy_read_struct(&png_ptr, NULL, (png_infopp)NULL);
00145     fclose(fp);
00146     return false;
00147   }
00148 
00149   if(setjmp(png_jmpbuf(png_ptr)))
00150   {
00151     png_destroy_read_struct(&png_ptr, NULL, (png_infopp)NULL);
00152     fclose(fp);
00153     return false;
00154   }
00155 
00156   png_init_io(png_ptr, fp);
00157   png_set_sig_bytes(png_ptr, 8);
00158 
00159   png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
00160 
00161   cols = info_ptr->width;
00162   rows = info_ptr->height;
00163   bpp = info_ptr->channels * 8;
00164 
00165   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00166   fclose (fp);
00167 
00168   return true;
00169 }
00170 
00171 
00172 //-----------------------------------------------------------------------------
00173 // GetImageInfoJpeg: Gets cols, rows and bpp from a JPEG image file
00174 //-----------------------------------------------------------------------------
00175 bool GameVideo::_GetImageInfoJpeg (const std::string& file_name, uint32 &rows, uint32& cols, uint32& bpp)
00176 {
00177   FILE * infile;
00178 
00179   if((infile = fopen(file_name.c_str(), "rb")) == NULL)
00180     return false;
00181 
00182   jpeg_decompress_struct cinfo;
00183   jpeg_error_mgr jerr;
00184 
00185   cinfo.err = jpeg_std_error(&jerr);
00186   jpeg_create_decompress(&cinfo);
00187 
00188   jpeg_stdio_src(&cinfo, infile);
00189   jpeg_read_header(&cinfo, TRUE);
00190 
00191   cols = cinfo.output_width;
00192   rows = cinfo.output_height;
00193   bpp = cinfo.output_components;
00194 
00195   jpeg_destroy_decompress(&cinfo);
00196 
00197   fclose(infile);
00198 
00199   return true;
00200 }
00201 
00202 
00203 //-----------------------------------------------------------------------------
00204 // LoadImage: loads an image (static image or animated image) and returns true
00205 //            on success
00206 //-----------------------------------------------------------------------------
00207 
00208 bool GameVideo::LoadImage(ImageDescriptor &id)
00209 {
00210   if(id._animated)
00211   {
00212     // First load the image
00213     if (!_LoadImage(dynamic_cast<AnimatedImage &>(id)))
00214     {
00215       return false;
00216     }
00217 
00218     // Then turn it grayscale if needed
00219     if (id.IsGrayScale())
00220     {
00221       dynamic_cast<AnimatedImage &>(id).EnableGrayScale();
00222     }
00223   }
00224   else
00225   {
00226     // First load the image
00227     if (!_LoadImage(dynamic_cast<StillImage &>(id)))
00228     {
00229       return false;
00230     }
00231 
00232     // Then turn it grayscale if needed
00233     if (id.IsGrayScale())
00234     {
00235       dynamic_cast<StillImage &>(id).EnableGrayScale();
00236     }
00237   }
00238 
00239   return true;
00240 }
00241 
00242 
00243 
00244 
00245 //-----------------------------------------------------------------------------
00246 // _LoadImage: helper function to load an animated image
00247 //-----------------------------------------------------------------------------
00248 
00249 bool GameVideo::_LoadImage(AnimatedImage &id)
00250 {
00251   uint32 num_frames = static_cast<uint32>(id._frames.size());
00252 
00253   bool success = true;
00254 
00255   // Go through all the frames and load anything that hasn't already been loaded
00256   for(uint32 frame = 0; frame < num_frames; ++frame)
00257   {
00258     // If the API user passes only filenames to AddFrame(), then we need to load
00259     // the images, but if a static image is passed directly, then we can skip
00260     // loading
00261 
00262     bool need_to_load = id._frames[frame].image._elements.empty();
00263 
00264     if(need_to_load)
00265     {
00266       success &= _LoadImage(id._frames[frame].image);
00267     }
00268   }
00269 
00270   return success;
00271 }
00272 
00273 
00274 
00275 
00276 //-----------------------------------------------------------------------------
00277 // _LoadImage: loads an image and returns it in the static image
00278 //             On failure, returns false.
00279 //
00280 //             If isStatic is true, that means this is an image that is probably
00281 //             to remain in memory for the entire game, so place it in a
00282 //             special texture sheet reserved for things that don't change often.
00283 //-----------------------------------------------------------------------------
00284 
00285 bool GameVideo::_LoadImage(StillImage &id)
00286 {
00287   // Delete everything previously stored in here
00288   id._elements.clear();
00289 
00290   // 1. Special case: if filename is empty, load a colored quad
00291   if(id._filename.empty())
00292   {
00293     ImageElement quad(NULL, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, id._width, id._height, id._color);
00294     id._elements.push_back(quad);
00295     return true;
00296   }
00297 
00298   // 2. Check if an image with the same filename has already been loaded
00299   //    If so, point to that
00300   if(_images.find(id._filename) != _images.end())
00301   {
00302     Image *img = _images[id._filename];   // Get the image from the map
00303 
00304     if(!img)
00305     {
00306       if(VIDEO_DEBUG)
00307         cerr << "VIDEO ERROR: got a NULL Image from images map in LoadImage()" << endl;
00308       return false;
00309     }
00310 
00311     if (img->ref_count == 0)
00312     {
00313       // If ref count is zero, it means this image was freed, but
00314       // not removed, so restore it
00315       if(!img->texture_sheet->RestoreImage(img))
00316         return false;
00317     }
00318 
00319     ++(img->ref_count);   // Increment the reference counter of the Image
00320 
00321     if(id._width == 0.0f)
00322       id._width = (float) img->width;
00323     if(id._height == 0.0f)
00324       id._height = (float) img->height;
00325 
00326     ImageElement element(img, 0, 0, 0.0f, 0.0f, 1.0f, 1.0f, id._width, id._height, id._color);
00327     id._elements.push_back(element);
00328 
00329     return true;
00330   }
00331 
00332   // 3. Load the image right away
00333   bool success = _LoadImageHelper(id);
00334 
00335   if(!success)
00336   {
00337     if(VIDEO_DEBUG)
00338       cerr << "VIDEO ERROR: in LoadImage() failed to load " << id._filename << endl;
00339     return false;
00340   }
00341 
00342   return success;
00343 }
00344 
00345 
00346 
00347 
00348 //-----------------------------------------------------------------------------
00349 // LoadMultiImageFromNumberElements: Opens an image file and breaks it in many StillImages
00350 //-----------------------------------------------------------------------------
00351 
00352 bool GameVideo::LoadMultiImageFromNumberElements(std::vector<StillImage> &images, const std::string &file_name, const uint32 rows, const uint32 cols)
00353 {
00354   // Get the size of the image
00355   uint32 image_rows, image_cols, bpp;
00356   GetImageInfo(file_name, image_rows, image_cols, bpp);
00357 
00358   // If all the images are not the same size, then there is an error
00359   if ((image_rows % rows) || (image_cols % cols))
00360   {
00361     if (VIDEO_DEBUG)
00362       cerr << "VIDEO ERROR: loading multi image, the size of the image (file) is not multiple of the size of the images (StillImage)" << endl;
00363     return false;
00364   }
00365 
00366   // If the images vector has not the same size of the required elements, redimension the vector
00367   if (images.size()!= rows*cols)
00368   {
00369     images.resize(rows*cols);
00370   }
00371 
00372   // If the width and height was not specified (0.0f), then assume the size of the image element
00373   float width ((float)image_cols / (float)cols);
00374   float height ((float)image_rows / (float)rows);
00375   for (std::vector<StillImage>::iterator it=images.begin(); it<images.end(); ++it)
00376   {
00377     if (it->_height == 0.0f)
00378       it->_height = height;
00379     if (it->_width == 0.0f)
00380       it->_width = width;
00381   }
00382 
00383   // Load the images
00384   return _LoadMultiImage (images, file_name, rows, cols);
00385 }
00386 
00387 
00388 
00389 //-----------------------------------------------------------------------------
00390 // LoadMultiImageFromElementsSize: Opens an image file and breaks it in many frames of an AnimateImage
00391 //-----------------------------------------------------------------------------
00392 bool GameVideo::LoadMultiImageFromElementsSize(std::vector<StillImage> &images, const std::string &file_name, const uint32 width, const uint32 height)
00393 {
00394   // Get the size of the image
00395   uint32 image_rows, image_cols, bpp;
00396   GetImageInfo(file_name, image_rows, image_cols, bpp);
00397 
00398   // If all the images are not the same size, then there is an error
00399   if ((image_rows % height) || (image_cols % width))
00400   {
00401     if (VIDEO_DEBUG)
00402       cerr << "VIDEO ERROR: loading multi image, the size of the image (file) is not multiple of the size of the images (StillImage)" << endl;
00403     return false;
00404   }
00405 
00406   uint32 rows (image_rows / height);
00407   uint32 cols (image_cols / width);
00408 
00409   // If the images vector has not the same size of the required elements, redimension the vector
00410   // Using this multi image loader probably means tat the images vector is empty
00411   if (images.size() != rows*cols)
00412   {
00413     images.resize(rows*cols);
00414   }
00415 
00416   // If the width and height was not specified (0.0f), then assume the size of the image element
00417   for (std::vector<StillImage>::iterator it=images.begin(); it<images.end(); ++it)
00418   {
00419     if (it->_height == 0.0f)
00420       it->_height = (float)height;
00421     if (it->_width == 0.0f)
00422       it->_width = (float)width;
00423   }
00424 
00425   // Load the images
00426   return _LoadMultiImage (images, file_name, rows, cols);
00427 }
00428 
00429 
00430 //-----------------------------------------------------------------------------
00431 // _LoadMultiImage: Breaks an image in several StillImages
00432 //-----------------------------------------------------------------------------
00433 bool GameVideo::_LoadMultiImage (std::vector <StillImage>& images, const std::string &file_name, const uint32& rows,
00434                  const uint32& cols)
00435 {
00436   std::string tags;
00437   std::string s;
00438   uint32 current_image;
00439   uint32 x, y;
00440 
00441   bool need_load = false;
00442 
00443   // Check if we have loaded all the sub-images
00444   for (x=0; x<rows && !need_load; x++)
00445   {
00446     for (y=0; y<cols && !need_load; y++)
00447     {
00448       tags = "";
00449       DataToString(s,x);
00450       tags += "<X" + s + "_";
00451       DataToString(s,rows);
00452       tags += s + ">";
00453       DataToString(s,y);
00454       tags += "<Y" + s + "_";
00455       DataToString(s,cols);
00456       tags += s + ">";
00457 
00458       // If this image doesn't exist, don't do anything else.
00459       // We will have to load the image file
00460       if(_images.find(file_name+tags) == _images.end())
00461       {
00462         need_load = true;
00463       }
00464     }
00465   }
00466 
00467   // If not all the images are loaded, then load the big image from disk
00468   private_video::MultiImageInfo info;
00469   if (need_load)
00470   {
00471     if(!_LoadRawImage(file_name, info.multi_image))
00472       return false;
00473 
00474     info.image.width = info.multi_image.width / cols;
00475     info.image.height = info.multi_image.height / rows;
00476     info.image.pixels = (uint8 *) malloc(info.image.width*info.image.height*4);
00477   }
00478 
00479   // One by one, get the subimages
00480   for (x=0; x<rows; x++)
00481   {
00482     for (y=0; y<cols; y++)
00483     {
00484       DataToString(s,x);
00485       tags = "<X" + s + "_";
00486       DataToString(s,rows);
00487       tags += s + ">";
00488       DataToString(s,y);
00489       tags += "<Y" + s + "_";
00490       DataToString(s,cols);
00491       tags += s + ">";
00492 
00493       current_image = x*cols + y;
00494 
00495       // If the image exists, take the information from it
00496       if(_images.find(file_name+tags) != _images.end())
00497       {
00498         images.at(current_image)._elements.clear();
00499 
00500         Image *img = _images[file_name+tags];
00501 
00502         if (!img)
00503         {
00504           if(VIDEO_DEBUG)
00505             cerr << "VIDEO ERROR: got a NULL Image from images map in LoadImage()" << endl;
00506 
00507           if (info.multi_image.pixels)
00508             free (info.multi_image.pixels);
00509           if (info.image.pixels)
00510             free (info.image.pixels);
00511           return false;
00512         }
00513 
00514         if (img->ref_count == 0)
00515         {
00516           // If ref count is zero, it means this image was freed, but
00517           // not removed, so restore it
00518           if (!img->texture_sheet->RestoreImage(img))
00519           {
00520             if (info.multi_image.pixels)
00521               free (info.multi_image.pixels);
00522             if (info.image.pixels)
00523               free (info.image.pixels);
00524             return false;
00525           }
00526         }
00527 
00528         ++(img->ref_count);
00529 
00530         ImageElement element(img, 0, 0, 0.0f, 0.0f, 1.0f, 1.0f,
00531           images.at(current_image)._width, images.at(current_image)._height, images.at(current_image)._color);
00532         images.at(current_image)._elements.push_back(element);
00533       }
00534       else  // If the image is not present, take the piece from the loaded image
00535       {
00536         images.at(current_image)._filename = file_name;
00537         images.at(current_image)._animated = false;
00538 
00539         for (int32 i=0; i<info.image.height; i++)
00540         {
00541           memcpy ((uint8*)info.image.pixels+4*info.image.width*i, (uint8*)info.multi_image.pixels+(((x*info.multi_image.height/rows)+i)*info.multi_image.width+y*info.multi_image.width/cols)*4, 4*info.image.width);
00542         }
00543 
00544         // Create an Image structure and store it our std::map of images
00545         Image *new_image = new Image(file_name, tags, info.image.width, info.image.height, false);
00546 
00547         // Try to insert the image in a texture sheet
00548         TexSheet *sheet = _InsertImageInTexSheet(new_image, info.image, images.at(current_image)._is_static);
00549 
00550         if (!sheet)
00551         {
00552           // This should never happen, unless we run out of memory or there
00553           // is a bug in the _InsertImageInTexSheet() function
00554 
00555           if(VIDEO_DEBUG)
00556             cerr << "VIDEO_DEBUG: GameVideo::_InsertImageInTexSheet() returned NULL!" << endl;
00557 
00558           if (info.multi_image.pixels)
00559             free (info.multi_image.pixels);
00560           if (info.image.pixels)
00561             free (info.image.pixels);
00562           return false;
00563         }
00564 
00565         new_image->ref_count = 1;
00566 
00567         // store the image in our std::map
00568         _images[file_name + tags] = new_image;
00569 
00570         // store the new image element
00571         ImageElement element(new_image, 0, 0, 0.0f, 0.0f, 1.0f, 1.0f, images.at(current_image)._width, images.at(current_image)._height, images.at(current_image)._color);
00572         images.at(current_image)._elements.push_back(element);
00573       }
00574 
00575       // If the image is in grayscale, convert it
00576       if (images.at(current_image)._grayscale)
00577       {
00578         images.at(current_image).EnableGrayScale();
00579       }
00580     }
00581   }
00582 
00583   // Free loaded image, in case we used it
00584   if (info.multi_image.pixels)
00585     free (info.multi_image.pixels);
00586   if (info.image.pixels)
00587     free (info.image.pixels);
00588 
00589   return true;
00590 }
00591 
00592 
00593 //-----------------------------------------------------------------------------
00594 // LoadAnimatedImageFromNumberElements: Opens an image file and breaks it in many frames of an AnimateImage
00595 //-----------------------------------------------------------------------------
00596 
00597 bool GameVideo::LoadAnimatedImageFromNumberElements(AnimatedImage &id, const std::string &filename, const uint32 rows, const uint32 cols)
00598 {
00599   // If the number of frames and the number of sub-images doesn't match, return
00600   if (id.GetNumFrames() != rows*cols)
00601   {
00602     if (VIDEO_DEBUG)
00603       cerr << "VIDEO ERROR: The animated image don't have enough frames to hold the data" << endl;
00604     return false;
00605   }
00606 
00607   // Get the vector of images
00608   std::vector <StillImage> v;
00609   for (uint32 frame=0; frame<id.GetNumFrames(); ++frame)
00610   {
00611     v.push_back(id.GetFrame(frame));
00612   }
00613 
00614   // Load the frames via LoadMultiImage
00615   bool success = LoadMultiImageFromNumberElements(v, filename, rows, cols);
00616 
00617   // Put the loaded frame images back into the AnimatedImage
00618   if (success == true) {
00619     for (uint32 i = 0; i < v.size(); i++) {
00620       id._frames[i].image = v[i];
00621     }
00622   }
00623 
00624   return success;
00625 }
00626 
00627 
00628 bool GameVideo::LoadAnimatedImageFromElementsSize(AnimatedImage &image, const std::string &file_name, const uint32 rows, const uint32 cols)
00629 {
00630   return false;
00631 }
00632 
00633 
00634 //-----------------------------------------------------------------------------
00635 // _LoadImageHelper: private function which does the dirty work of actually
00636 //                     loading an image.
00637 //-----------------------------------------------------------------------------
00638 
00639 bool GameVideo::_LoadImageHelper(StillImage &id)
00640 {
00641   bool is_static = id._is_static;
00642 
00643   id._elements.clear();
00644 
00645   private_video::ImageLoadInfo load_info;
00646 
00647   if(!_LoadRawImage(id._filename, load_info))
00648   {
00649     if(VIDEO_DEBUG)
00650       cerr << "VIDEO ERROR: _LoadRawPixelData() failed in _LoadImageHelper()" << endl;
00651     return false;
00652   }
00653 
00654   // Create an Image structure and store it our std::map of images (for the color copy, always present)
00655   Image *new_image = new Image(id._filename, "", load_info.width, load_info.height, false);
00656 
00657   // Try to insert the image in a texture sheet
00658   TexSheet *sheet = _InsertImageInTexSheet(new_image, load_info, is_static);
00659 
00660   if(!sheet)
00661   {
00662     // This should never happen, unless we run out of memory or there
00663     // is a bug in the _InsertImageInTexSheet() function
00664 
00665     if(VIDEO_DEBUG)
00666       cerr << "VIDEO_DEBUG: GameVideo::_InsertImageInTexSheet() returned NULL!" << endl;
00667 
00668     delete new_image;
00669     free (load_info.pixels);
00670     return false;
00671   }
00672 
00673   new_image->ref_count = 1;
00674 
00675   // Store the image in our std::map
00676   _images[id._filename] = new_image;
00677 
00678   // If width or height are zero, that means to use the dimensions of image
00679   if(id._width == 0.0f)
00680     id._width = (float) load_info.width;
00681 
00682   if(id._height == 0.0f)
00683     id._height = (float) load_info.height;
00684 
00685   ImageElement element(new_image, 0, 0, 0.0f, 0.0f, 1.0f, 1.0f, id._width, id._height, id._color);
00686   id._elements.push_back(element);
00687 
00688   // Finally, delete the buffer used to hold the pixel data
00689   if (load_info.pixels)
00690     free (load_info.pixels);
00691 
00692   return true;
00693 }
00694 
00695 
00696 
00697 
00698 //-----------------------------------------------------------------------------
00699 // _LoadRawImage: Determines which image loader to call
00700 //-----------------------------------------------------------------------------
00701 
00702 bool GameVideo::_LoadRawImage(const std::string & filename, private_video::ImageLoadInfo & load_info)
00703 {
00704   // Isolate the extension
00705   size_t extpos = filename.rfind('.');
00706 
00707   if(extpos == string::npos)
00708     return false;
00709 
00710   std::string extension = std::string(filename, extpos, filename.length() - extpos);
00711 
00712   if(extension == ".jpeg" || extension == ".jpg")
00713     return _LoadRawImageJpeg(filename, load_info);
00714   if(extension == ".png")
00715     return _LoadRawImagePng(filename, load_info);
00716 
00717   return false;
00718 }
00719 
00720 
00721 
00722 
00723 //-----------------------------------------------------------------------------
00724 // _LoadRawImagePng: Loads a PNG image to RGBA format
00725 //-----------------------------------------------------------------------------
00726 
00727 bool GameVideo::_LoadRawImagePng(const std::string &filename, hoa_video::private_video::ImageLoadInfo &load_info)
00728 {
00729   FILE * fp = fopen(filename.c_str(), "rb");
00730 
00731   if(fp == NULL)
00732     return false;
00733 
00734   uint8 test_buffer[8];
00735 
00736   fread(test_buffer, 1, 8, fp);
00737   if(png_sig_cmp(test_buffer, 0, 8))
00738   {
00739     fclose(fp);
00740     return false;
00741   }
00742 
00743   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
00744 
00745   if(!png_ptr)
00746   {
00747     fclose(fp);
00748     return false;
00749   }
00750 
00751   png_infop info_ptr = png_create_info_struct(png_ptr);
00752 
00753   if(!info_ptr)
00754   {
00755     png_destroy_read_struct(&png_ptr, NULL, (png_infopp)NULL);
00756     fclose(fp);
00757     return false;
00758   }
00759 
00760   if(setjmp(png_jmpbuf(png_ptr)))
00761   {
00762     png_destroy_read_struct(&png_ptr, NULL, (png_infopp)NULL);
00763     fclose(fp);
00764     return false;
00765   }
00766 
00767   png_init_io(png_ptr, fp);
00768   png_set_sig_bytes(png_ptr, 8);
00769   png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
00770 
00771   uint8** row_pointers = png_get_rows(png_ptr, info_ptr);
00772 
00773   load_info.width = info_ptr->width;
00774   load_info.height = info_ptr->height;
00775   load_info.pixels = malloc (info_ptr->width * info_ptr->height * 4);
00776 
00777   uint32 bpp = info_ptr->channels;
00778 
00779   uint8* img_pixel;
00780   uint8* dst_pixel;
00781 
00782   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
00783   {
00784     png_color c;
00785     for(uint32 y = 0; y < info_ptr->height; y++)
00786     {
00787       for(uint32 x = 0; x < info_ptr->width; x++)
00788       {
00789         img_pixel = row_pointers[y] + (x * bpp);
00790         dst_pixel = ((uint8 *)load_info.pixels) + ((y * info_ptr->width) + x) * 4;
00791         c = info_ptr->palette[img_pixel[0]];
00792 
00793         dst_pixel[0] = c.red;
00794         dst_pixel[1] = c.green;
00795         dst_pixel[2] = c.blue;
00796         dst_pixel[3] = 0xFF;
00797       }
00798     }
00799   }
00800   else if (bpp == 1)
00801   {
00802     for(uint32 y = 0; y < info_ptr->height; y++)
00803     {
00804       for(uint32 x = 0; x < info_ptr->width; x++)
00805       {
00806         img_pixel = row_pointers[y] + (x * bpp);
00807         dst_pixel = ((uint8 *)load_info.pixels) + ((y * info_ptr->width) + x) * 4;
00808         dst_pixel[0] = img_pixel[0];
00809         dst_pixel[1] = img_pixel[0];
00810         dst_pixel[2] = img_pixel[0];
00811         dst_pixel[3] = 0xFF;
00812       }
00813     }
00814   }
00815   else if (bpp == 3)
00816   {
00817     for(uint32 y = 0; y < info_ptr->height; y++)
00818     {
00819       for(uint32 x = 0; x < info_ptr->width; x++)
00820       {
00821         img_pixel = row_pointers[y] + (x * bpp);
00822         dst_pixel = ((uint8 *)load_info.pixels) + ((y * info_ptr->width) + x) * 4;
00823         dst_pixel[0] = img_pixel[0];
00824         dst_pixel[1] = img_pixel[1];
00825         dst_pixel[2] = img_pixel[2];
00826         dst_pixel[3] = 0xFF;
00827       }
00828     }
00829   }
00830   else if (bpp == 4)
00831   {
00832     for(uint32 y = 0; y < info_ptr->height; y++)
00833     {
00834       for(uint32 x = 0; x < info_ptr->width; x++)
00835       {
00836         img_pixel = row_pointers[y] + (x * bpp);
00837         dst_pixel = ((uint8 *)load_info.pixels) + ((y * info_ptr->width) + x) * 4;
00838         dst_pixel[0] = img_pixel[0];
00839         dst_pixel[1] = img_pixel[1];
00840         dst_pixel[2] = img_pixel[2];
00841         dst_pixel[3] = img_pixel[3];
00842       }
00843     }
00844   }
00845   else
00846   {
00847     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00848     fclose (fp);
00849     if (VIDEO_DEBUG)
00850       cerr << "Game Video: Fail in loading Png file (bytes per pixel not supported)" << endl;
00851     return false;
00852   }
00853 
00854   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00855 
00856   fclose(fp);
00857 
00858   return true;
00859 }
00860 
00861 
00862 
00863 
00864 //-----------------------------------------------------------------------------
00865 // _LoadRawImageJpeg: Loads a Jpeg image to RGBA format
00866 //-----------------------------------------------------------------------------
00867 
00868 bool GameVideo::_LoadRawImageJpeg(const std::string &filename, hoa_video::private_video::ImageLoadInfo &load_info)
00869 {
00870   FILE * infile;
00871   uint8** buffer;
00872 
00873   if((infile = fopen(filename.c_str(), "rb")) == NULL)
00874     return false;
00875 
00876   jpeg_decompress_struct cinfo;
00877   jpeg_error_mgr jerr;
00878 
00879   cinfo.err = jpeg_std_error(&jerr);
00880   jpeg_create_decompress(&cinfo);
00881 
00882   jpeg_stdio_src(&cinfo, infile);
00883   jpeg_read_header(&cinfo, TRUE);
00884 
00885   jpeg_start_decompress(&cinfo);
00886 
00887   JDIMENSION row_stride = cinfo.output_width * cinfo.output_components;
00888 
00889   buffer = (*cinfo.mem->alloc_sarray)
00890     ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
00891 
00892   load_info.width = cinfo.output_width;
00893   load_info.height = cinfo.output_height;
00894   load_info.pixels = malloc (cinfo.output_width * cinfo.output_height * 4);
00895 
00896   uint32 bpp = cinfo.output_components;
00897 
00898   uint8* img_pixel;
00899   uint8* dst_pixel;
00900 
00901   if (bpp == 3)
00902   {
00903     for(uint32 y = 0; y < cinfo.output_height; y++)
00904     {
00905       jpeg_read_scanlines(&cinfo, buffer, 1);
00906 
00907       for(uint32 x = 0; x < cinfo.output_width; x++)
00908       {
00909         img_pixel = buffer[0] + (x * bpp);
00910         dst_pixel = ((uint8 *)load_info.pixels) + ((y * cinfo.output_width) + x) * 4;
00911 
00912         dst_pixel[0] = img_pixel[0];
00913         dst_pixel[1] = img_pixel[1];
00914         dst_pixel[2] = img_pixel[2];
00915         dst_pixel[3] = 0xFF;
00916       }
00917     }
00918 
00919   }
00920   else if (bpp == 4)
00921   {
00922     for(uint32 y = 0; y < cinfo.output_height; y++)
00923     {
00924       jpeg_read_scanlines(&cinfo, buffer, 1);
00925 
00926       for(uint32 x = 0; x < cinfo.output_width; x++)
00927       {
00928         img_pixel = buffer[0] + (x * bpp);
00929         dst_pixel = ((uint8 *)load_info.pixels) + ((y * cinfo.output_width) + x) * 4;
00930 
00931         dst_pixel[0] = img_pixel[0];
00932         dst_pixel[1] = img_pixel[1];
00933         dst_pixel[2] = img_pixel[2];
00934         dst_pixel[3] = img_pixel[3];
00935       }
00936     }
00937   }
00938   else
00939   {
00940     jpeg_finish_decompress(&cinfo);
00941     jpeg_destroy_decompress(&cinfo);
00942     fclose(infile);
00943     if (VIDEO_DEBUG)
00944       cerr << "Game Video: Fail in loading Png file (bytes per pixel not supported)" << endl;
00945     return false;
00946   }
00947 
00948   jpeg_finish_decompress(&cinfo);
00949   jpeg_destroy_decompress(&cinfo);
00950 
00951   fclose(infile);
00952 
00953   return true;
00954 }
00955 
00956 
00957 
00958 
00959 //-----------------------------------------------------------------------------
00960 // _SavePng: Stores a buffer in Png format
00961 //-----------------------------------------------------------------------------
00962 
00963 bool GameVideo::_SavePng (const std::string& file_name, hoa_video::private_video::ImageLoadInfo &info) const
00964 {
00965   FILE * fp = fopen(file_name.c_str(), "wb");
00966 
00967   if(fp == NULL)
00968     return false;
00969 
00970   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
00971     (png_voidp)NULL, NULL, NULL);
00972 
00973   if(!png_ptr)
00974   {
00975     fclose(fp);
00976     return false;
00977   }
00978 
00979   png_infop info_ptr = png_create_info_struct(png_ptr);
00980 
00981   if(!info_ptr)
00982   {
00983     png_destroy_write_struct(&png_ptr, NULL);
00984     fclose(fp);
00985     return false;
00986   }
00987 
00988   if(setjmp(png_jmpbuf(png_ptr)))
00989   {
00990     png_destroy_write_struct(&png_ptr, &info_ptr);
00991     fclose(fp);
00992     return false;
00993   }
00994 
00995   png_init_io(png_ptr, fp);
00996 
00997   png_set_IHDR (png_ptr, info_ptr, info.width, info.height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
00998           PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00999 
01000   png_byte** row_pointers = new png_byte* [info.height];
01001   int32 bytes_per_row = info.width * 4;
01002   for (int32 i=0; i<info.height; i++)
01003   {
01004     row_pointers[i] = (png_byte*)info.pixels + bytes_per_row * i;
01005   }
01006   png_set_rows(png_ptr, info_ptr, row_pointers);
01007 
01008   png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
01009 
01010   png_write_image(png_ptr, row_pointers);
01011 
01012   png_write_end(png_ptr, info_ptr);
01013 
01014   png_destroy_write_struct (&png_ptr, &info_ptr);
01015 
01016   delete[] row_pointers;
01017 
01018   return true;
01019 }
01020 
01021 
01022 
01023 
01024 //-----------------------------------------------------------------------------
01025 // _SaveJpeg: Stores a buffer in Jpeg file
01026 //-----------------------------------------------------------------------------
01027 
01028 bool GameVideo::_SaveJpeg (const std::string& file_name, hoa_video::private_video::ImageLoadInfo &info) const
01029 {
01030   FILE * outfile;
01031   if((outfile = fopen(file_name.c_str(), "wb")) == NULL)
01032   {
01033     if (VIDEO_DEBUG)
01034       cerr << "Game Video: could not save '" << file_name.c_str() << "'" << endl;
01035     return false;
01036   }
01037 
01038   jpeg_compress_struct cinfo;
01039   jpeg_error_mgr jerr;
01040 
01041   cinfo.err = jpeg_std_error(&jerr);
01042   jpeg_create_compress(&cinfo);
01043 
01044   cinfo.in_color_space = JCS_RGB;
01045   cinfo.image_width = info.width;
01046   cinfo.image_height = info.height;
01047   cinfo.input_components = 3;
01048   jpeg_set_defaults (&cinfo);
01049 
01050   jpeg_stdio_dest(&cinfo, outfile);
01051 
01052   jpeg_start_compress (&cinfo, TRUE);
01053 
01054   JSAMPROW row_pointer;       // Pointer to a single row
01055   uint32 row_stride = info.width * 3; // Physical row width in buffer
01056 
01057   // Note that the lines have to be stored from top to bottom
01058   while (cinfo.next_scanline < cinfo.image_height)
01059   {
01060     row_pointer = (uint8*)info.pixels + cinfo.next_scanline * row_stride;
01061       jpeg_write_scanlines(&cinfo, &row_pointer, 1);
01062   }
01063 
01064   jpeg_finish_compress(&cinfo);
01065   jpeg_destroy_compress(&cinfo);
01066 
01067   fclose(outfile);
01068 
01069   return true;
01070 }
01071 
01072 
01073 
01074 
01075 //-----------------------------------------------------------------------------
01076 // SaveImage: Saves a vector of images in a single file
01077 //-----------------------------------------------------------------------------
01078 
01079 bool GameVideo::SaveImage (const std::string &file_name, const std::vector<StillImage*> &image,
01080                const uint32 rows, const uint32 columns) const
01081 {
01082   enum eLoadType
01083   {
01084     NONE  = 0,
01085     JPEG  = 1,
01086     PNG   = 2
01087   } type (NONE);
01088 
01089   // Isolate the extension
01090   size_t extpos = file_name.rfind('.');
01091 
01092   if(extpos == string::npos)
01093     return false;
01094 
01095   std::string extension = std::string(file_name, extpos, file_name.length() - extpos);
01096 
01097   if(extension == ".jpeg" || extension == ".jpg")
01098     type = JPEG;
01099   if(extension == ".png")
01100     type = PNG;
01101 
01102   if (type == NONE)
01103   {
01104     if (VIDEO_DEBUG)
01105       cerr << "Game Video: Don't know which format to use for storage of an image" << endl;
01106     return false;
01107   }
01108 
01109   // Check there are elements to store
01110   if (image.empty())
01111   {
01112     if (VIDEO_DEBUG)
01113       cerr << "Game Video: Attempt to store no image" << endl;
01114     return false;
01115   }
01116 
01117   // Check if the number of images is compatible with the number of rows and columns
01118   if (image.size() != rows*columns)
01119   {
01120     if (VIDEO_DEBUG)
01121       cerr << "Game Video: Can't store " << image.size() << " in " << rows << " rows and " << columns << " columns" << endl;
01122     return false;
01123   }
01124 
01125   // Check all the images have just 1 ImageElement
01126   for (uint32 i=0 ; i<image.size(); i++)
01127   {
01128     if (image[i]->_elements.size() != 1)
01129     {
01130       if (VIDEO_DEBUG)
01131         cerr << "Game Video: one of the images didn't have 1 ImageElement" << endl;
01132       return false;
01133     }
01134   }
01135 
01136   // Check all the images are of the same size
01137   int32 width = image[0]->_elements[0].image->width;
01138   int32 height = image[0]->_elements[0].image->height;
01139   for (uint32 i = 0; i < image.size(); i++)
01140   {
01141     if (!image[i]->_elements[0].image || image[i]->_elements[0].image->width != width ||
01142       image[i]->_elements[0].image->height != height)
01143     {
01144       if (VIDEO_DEBUG)
01145         cerr << "Game Video: not all the images where of the same size" << endl;
01146       return false;
01147     }
01148   }
01149 
01150   // Structure for the image buffer to save
01151   hoa_video::private_video::ImageLoadInfo info;
01152   info.height = rows*height;
01153   info.width = columns*width;
01154   info.pixels = malloc (info.width * info.height * 4);
01155 
01156   hoa_video::private_video::Image* img;
01157   GLuint ID;
01158   hoa_video::private_video::ImageLoadInfo texture;
01159 
01160   // Do that for the first image (on later ones, maybe we don't need to get again
01161   // the texture, since it might be the same)
01162   img = const_cast<Image*>(image[0]->_elements[0].image);
01163   ID = img->texture_sheet->tex_ID;
01164   texture.width = img->texture_sheet->width;
01165   texture.height = img->texture_sheet->height;
01166   texture.pixels = malloc (texture.width * texture.height * 4);
01167   VideoManager->_BindTexture(ID);
01168   glGetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.pixels);
01169 
01170   uint32 i=0;
01171   for (uint32 x=0; x<rows; x++)
01172   {
01173     for (uint32 y=0; y<columns; y++)
01174     {
01175       img = const_cast<Image*>(image[i]->_elements[0].image);
01176       if (ID != img->texture_sheet->tex_ID)
01177       {
01178         // Get new texture ID
01179         VideoManager->_BindTexture(img->texture_sheet->tex_ID);
01180         ID = img->texture_sheet->tex_ID;
01181 
01182         // If the new texture is bigger, reallocate memory
01183         if (texture.height * texture.width < img->texture_sheet->height * img->texture_sheet->width * 4)
01184         {
01185           free (texture.pixels);
01186           texture.width = img->texture_sheet->width;
01187           texture.height = img->texture_sheet->height;
01188           texture.pixels = malloc (texture.width * texture.height * 4);
01189         }
01190         glGetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.pixels);
01191       }
01192 
01193       // Copy the part of the texture we are interested in
01194       uint32 copy_bytes = width * 4;
01195       uint32 dst_offset = x*height*width*columns*4 + y*width*4;
01196       uint32 dst_bytes = width*columns*4;
01197       uint32 src_bytes = texture.width * 4;
01198       uint32 src_offset = img->y * texture.width * 4 + img->x * 4;
01199       for (int32 j = 0; j < height; j++)
01200       {
01201         memcpy ((uint8*)info.pixels+j*dst_bytes+dst_offset, (uint8*)texture.pixels+j*src_bytes+src_offset, copy_bytes);
01202       }
01203 
01204       i++;
01205     }
01206   }
01207 
01208   // Store the resultant buffer
01209   if (type == JPEG)
01210   {
01211     _RGBAToRGB (info, info);
01212     _SaveJpeg (file_name, info);
01213   }
01214   else
01215   {
01216     _SavePng (file_name, info);
01217   }
01218 
01219   if (info.pixels)
01220     free (info.pixels);
01221 
01222   if (texture.pixels)
01223     free (texture.pixels);
01224 
01225   return true;
01226 }
01227 
01228 
01229 
01230 
01231 //-----------------------------------------------------------------------------
01232 // SaveImage: Saves an AnimatedImage as a multiimage
01233 //-----------------------------------------------------------------------------
01234 
01235 bool GameVideo::SaveImage (const std::string &file_name, const AnimatedImage &image) const
01236 {
01237   int32 frame_count = dynamic_cast<const AnimatedImage &>(image).GetNumFrames();
01238   std::vector <StillImage*> frames;
01239   frames.reserve (frame_count);
01240 
01241   for (int32 frame=0; frame<frame_count; frame++)
01242   {
01243     frames.push_back(dynamic_cast<const AnimatedImage &>(image).GetFrame(frame));
01244   }
01245 
01246   return SaveImage (file_name, frames, 1, frame_count);
01247 }
01248 
01249 
01250 
01251 
01252 //-----------------------------------------------------------------------------
01253 // SaveImage: Saves an image in a file
01254 //-----------------------------------------------------------------------------
01255 
01256 bool GameVideo::SaveImage (const std::string &file_name, const StillImage &image) const
01257 {
01258   enum eLoadType
01259   {
01260     NONE  = 0,
01261     JPEG  = 1,
01262     PNG   = 2
01263   } type (NONE);
01264 
01265   // Isolate the extension
01266   size_t extpos = file_name.rfind('.');
01267 
01268   if(extpos == string::npos)
01269     return false;
01270 
01271   std::string extension = std::string(file_name, extpos, file_name.length() - extpos);
01272 
01273   if(extension == ".jpeg" || extension == ".jpg")
01274     type = JPEG;
01275   if(extension == ".png")
01276     type = PNG;
01277 
01278   if (type == NONE)
01279   {
01280     if (VIDEO_DEBUG)
01281       cerr << "Game Video: Don't know which format to use for storage of an image" << endl;
01282     return false;
01283   }
01284 
01285   // Check there are elements to store
01286   if (image._elements.empty())
01287   {
01288     if (VIDEO_DEBUG)
01289       cerr << "Game Video: Attempt to store empty image" << endl;
01290     return false;
01291   }
01292 
01293   // Still can't store compound images
01294   if (image._elements.size() > 1)
01295   {
01296     if (VIDEO_DEBUG)
01297       cerr << "Game Video: Compound images can't be stored yet" << endl;
01298     return false;
01299   }
01300 
01301   hoa_video::private_video::ImageLoadInfo buffer;
01302   hoa_video::private_video::Image* img = const_cast<Image*>(image._elements[0].image);
01303 
01304   _GetBufferFromImage (buffer, img);
01305 
01306   if (type == JPEG)
01307   {
01308     _RGBAToRGB (buffer, buffer);
01309     _SaveJpeg (file_name, buffer);
01310   }
01311   else
01312   {
01313     _SavePng (file_name, buffer);
01314   }
01315 
01316   return true;
01317 }
01318 
01319 
01320 
01321 
01322 //----------------------------------------------------------------------------
01323 // Pass a texture to a given buffer
01324 //----------------------------------------------------------------------------
01325 
01326 void GameVideo::_GetBufferFromTexture (hoa_video::private_video::ImageLoadInfo& buffer,
01327                      hoa_video::private_video::TexSheet* texture) const
01328 {
01329   if (buffer.pixels)
01330     free (buffer.pixels);
01331   buffer.pixels = NULL;
01332 
01333   // Get the texture as a buffer
01334   buffer.height = texture->height;
01335   buffer.width = texture->width;
01336   buffer.pixels = malloc (buffer.height * buffer.width * 4);
01337   VideoManager->_BindTexture(texture->tex_ID);
01338   glGetTexImage (GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.pixels);
01339 }
01340 
01341 
01342 
01343 
01344 //----------------------------------------------------------------------------
01345 // Pass an Image to a given buffer
01346 //----------------------------------------------------------------------------
01347 
01348 void GameVideo::_GetBufferFromImage (hoa_video::private_video::ImageLoadInfo& buffer,
01349                    hoa_video::private_video::Image* img) const
01350 {
01351   // Get the texture as a buffer
01352   _GetBufferFromTexture (buffer, img->texture_sheet);
01353 
01354   // In case the image is smaller than the texture (it is just contained there), then copy the image rectangle
01355   if (buffer.height > img->height || buffer.width > img->width)
01356   {
01357     hoa_video::private_video::ImageLoadInfo info;
01358     info.width = img->width;
01359     info.height = img->height;
01360     info.pixels = malloc (img->width * img->height * 4);
01361     uint32 dst_bytes = info.width * 4;
01362     uint32 src_bytes = buffer.width * 4;
01363     uint32 src_offset = img->y * buffer.width * 4 + img->x * 4;
01364     for (int32 i=0; i<info.height; i++)
01365     {
01366       memcpy ((uint8*)info.pixels+i*dst_bytes, (uint8*)buffer.pixels+i*src_bytes+src_offset, dst_bytes);
01367     }
01368 
01369     if (buffer.pixels)
01370       free (buffer.pixels);
01371 
01372     buffer.pixels = info.pixels;
01373     buffer.height = info.height;
01374     buffer.width = info.width;
01375   }
01376 }
01377 
01378 
01379 
01380 
01381 //-----------------------------------------------------------------------------
01382 // TilesToObject: given a vector of tiles, and a 2D vector of indices into
01383 //                those tiles, construct a single image descriptor which
01384 //                stitches those tiles together into one image
01385 //
01386 // NOTE: when calling this function, make sure of the following things:
01387 //     1. All tiles must be the SAME width and height.
01388 //     2. The vectors must be non-empty
01389 //     3. The indices must be within proper bounds
01390 //     4. The indices vector has the same number of columns in every row
01391 //     5. Remember to call DeleteImage() when you're done.
01392 //-----------------------------------------------------------------------------
01393 
01394 StillImage GameVideo::TilesToObject (std::vector<StillImage> &tiles, std::vector< std::vector<uint32> > indices)
01395 {
01396   StillImage id;
01397 
01398   // Figure out the width and height information
01399 
01400   int32 w, h;
01401   w = (int32) indices[0].size();         // How many tiles wide and high
01402   h = (int32) indices.size();
01403 
01404   float tile_width  = tiles[0]._width;   // Width and height of each tile
01405   float tile_height = tiles[0]._height;
01406 
01407   id._width  = (float) w * tile_width;   // Total width/height of compound
01408   id._height = (float) h * tile_height;
01409 
01410   id._is_static = tiles[0]._is_static;
01411 
01412   for(int32 y = 0; y < h; ++y)
01413   {
01414     for(int32 x = 0; x < w; ++x)
01415     {
01416       // Add each tile at the correct offset
01417 
01418       float x_offset = x * tile_width;
01419       float y_offset = y * tile_height;
01420 
01421       if(!id.AddImage(tiles[indices[y][x]], x_offset, y_offset))
01422       {
01423         if(VIDEO_DEBUG)
01424         {
01425           cerr << "VIDEO ERROR: failed to AddImage in TilesToObject()!" << endl;
01426         }
01427       }
01428     }
01429   }
01430 
01431   return id;
01432 }
01433 
01434 
01435 
01436 
01437 //-----------------------------------------------------------------------------
01438 // _InsertImageInTexSheet: takes an image that was loaded, finds
01439 //                        an available texture sheet, copies it to the sheet,
01440 //                        and returns a pointer to the texture sheet. If no
01441 //                        available texture sheet is found, a new one is created.
01442 //
01443 //                        Returns NULL on failure, which should only happen if
01444 //                        we run out of memory or bad argument is passed.
01445 //-----------------------------------------------------------------------------
01446 
01447 TexSheet *GameVideo::_InsertImageInTexSheet(Image *image, private_video::ImageLoadInfo & load_info, bool is_static)
01448 {
01449   // If it's a large image size (>512x512) then we already know it's not going
01450   // to fit in any of our existing texture sheets, so create a new one for it
01451 
01452   if(load_info.width > 512 || load_info.height > 512)
01453   {
01454     int32 round_width = RoundUpPow2(load_info.width);
01455     int32 round_height = RoundUpPow2(load_info.height);
01456     TexSheet *sheet = _CreateTexSheet(round_width, round_height, VIDEO_TEXSHEET_ANY, false);
01457 
01458     // Ran out of memory!
01459     if(!sheet)
01460     {
01461       if(VIDEO_DEBUG)
01462         cerr << "VIDEO ERROR: _CreateTexSheet() returned NULL in _InsertImageInTexSheet()!" << endl;
01463       return NULL;
01464     }
01465 
01466     if(sheet->AddImage(image, load_info))
01467       return sheet;
01468     else
01469     {
01470       if(VIDEO_DEBUG)
01471         cerr << "VIDEO ERROR: AddImage() returned false for inserting large image!" << endl;
01472       return NULL;
01473     }
01474   }
01475 
01476 
01477   // Determine the type of texture sheet that should hold this image
01478 
01479   TexSheetType type;
01480 
01481   if(load_info.width == 32 && load_info.height == 32)
01482     type = VIDEO_TEXSHEET_32x32;
01483   else if(load_info.width == 32 && load_info.height == 64)
01484     type = VIDEO_TEXSHEET_32x64;
01485   else if(load_info.width == 64 && load_info.height == 64)
01486     type = VIDEO_TEXSHEET_64x64;
01487   else
01488     type = VIDEO_TEXSHEET_ANY;
01489 
01490   // Loop through existing texture sheets and see if the image will fit in
01491   // any of the ones which match the type we're looking for
01492 
01493   size_t num_tex_sheets = _tex_sheets.size();
01494 
01495   for(uint32 iSheet = 0; iSheet < num_tex_sheets; ++iSheet)
01496   {
01497     TexSheet *sheet = _tex_sheets[iSheet];
01498     if(!sheet)
01499     {
01500       if(VIDEO_DEBUG)
01501         cerr << "VIDEO ERROR: _texSheets[iSheet] was NULL in _InsertImageInTexSheet()!" << endl;
01502       return NULL;
01503     }
01504 
01505     if(sheet->type == type && sheet->is_static == is_static)
01506     {
01507       if(sheet->AddImage(image, load_info))
01508       {
01509         // Added to a sheet successfully
01510         return sheet;
01511       }
01512     }
01513   }
01514 
01515   // If it doesn't fit in any of them, create a new 512x512 and stuff it in
01516 
01517   TexSheet *sheet = _CreateTexSheet(512, 512, type, is_static);
01518   if(!sheet)
01519   {
01520     // Failed to create texture, ran out of memory probably
01521 
01522     if(VIDEO_DEBUG)
01523     {
01524       cerr << "VIDEO ERROR: Failed to create new texture sheet in _InsertImageInTexSheet!" << endl;
01525     }
01526 
01527     return NULL;
01528   }
01529 
01530   // Now that we have a fresh texture sheet, AddImage() should work without
01531   // any problem
01532   if(sheet->AddImage(image, load_info))
01533   {
01534     return sheet;
01535   }
01536 
01537   return NULL;
01538 }
01539 
01540 
01541 
01542 
01543 //-----------------------------------------------------------------------------
01544 // _CreateTexSheet: creates a new texture sheet with the given parameters,
01545 //                 adds it to our internal vector of texture sheets, and
01546 //                 returns a pointer to it.
01547 //                 Returns NULL on failure, which should only happen if
01548 //                 we run out of memory or bad argument is passed.
01549 //-----------------------------------------------------------------------------
01550 
01551 TexSheet *GameVideo::_CreateTexSheet
01552 (
01553   int32 width,
01554   int32 height,
01555   TexSheetType type,
01556   bool is_static
01557 )
01558 {
01559   // Validate the parameters
01560 
01561   if(!IsPowerOfTwo(width) || !IsPowerOfTwo(height))
01562   {
01563     if(VIDEO_DEBUG)
01564       cerr << "VIDEO ERROR: non pow2 width and/or height passed to _CreateTexSheet!" << endl;
01565 
01566     return NULL;
01567   }
01568 
01569   if(type <= VIDEO_TEXSHEET_INVALID || type >= VIDEO_TEXSHEET_TOTAL)
01570   {
01571     if(VIDEO_DEBUG)
01572       cerr << "VIDEO ERROR: Invalid TexSheetType passed to _CreateTexSheet()!" << endl;
01573 
01574     return NULL;
01575   }
01576 
01577   GLuint tex_ID = _CreateBlankGLTexture(width, height);
01578 
01579   // Now that we have our texture loaded, simply create a new TexSheet
01580 
01581   TexSheet *sheet = new TexSheet(width, height, tex_ID, type, is_static);
01582   _tex_sheets.push_back(sheet);
01583 
01584   return sheet;
01585 }
01586 
01587 
01588 
01589 
01590 //-----------------------------------------------------------------------------
01591 // TexSheet constructor
01592 //-----------------------------------------------------------------------------
01593 
01594 TexSheet::TexSheet(int32 w, int32 h, GLuint tex_ID_, TexSheetType type_, bool is_static_)
01595 {
01596   width = w;
01597   height = h;
01598   tex_ID = tex_ID_;
01599   type = type_;
01600   is_static = is_static_;
01601   loaded = true;
01602 
01603   if(type == VIDEO_TEXSHEET_32x32)
01604     tex_mem_manager = new FixedTexMemMgr(this, 32, 32);
01605   else if(type == VIDEO_TEXSHEET_32x64)
01606     tex_mem_manager = new FixedTexMemMgr(this, 32, 64);
01607   else if(type == VIDEO_TEXSHEET_64x64)
01608     tex_mem_manager = new FixedTexMemMgr(this, 64, 64);
01609   else
01610     tex_mem_manager = new VariableTexMemMgr(this);
01611 }
01612 
01613 
01614 
01615 
01616 //-----------------------------------------------------------------------------
01617 // TexSheet destructor
01618 //-----------------------------------------------------------------------------
01619 
01620 TexSheet::~TexSheet()
01621 {
01622   // Delete texture memory manager
01623   delete tex_mem_manager;
01624 
01625   if(!VideoManager)
01626   {
01627     if(VIDEO_DEBUG)
01628     {
01629       cerr << "VIDEO ERROR: GameVideo::GetReference() returned NULL in TexSheet destructor!" << endl;
01630     }
01631   }
01632 
01633   // Unload actual texture from memory
01634   VideoManager->_DeleteTexture(tex_ID);
01635 }
01636 
01637 
01638 
01639 
01640 //-----------------------------------------------------------------------------
01641 // VariableTexMemMgr constructor
01642 //-----------------------------------------------------------------------------
01643 
01644 VariableTexMemMgr::VariableTexMemMgr(TexSheet *sheet)
01645 {
01646   _tex_sheet    = sheet;
01647   _sheet_width  = sheet->width / 16;
01648   _sheet_height = sheet->height / 16;
01649   _blocks      = new VariableImageNode[_sheet_width*_sheet_height];
01650 }
01651 
01652 
01653 
01654 
01655 //-----------------------------------------------------------------------------
01656 // VariableTexMemMgr destructor
01657 //-----------------------------------------------------------------------------
01658 
01659 VariableTexMemMgr::~VariableTexMemMgr()
01660 {
01661   delete [] _blocks;
01662 }
01663 
01664 
01665 
01666 
01667 bool GameVideo::_DEBUG_ShowTexSheet()
01668 {
01669   // Value of zero means to disable display
01670   if(_current_debug_TexSheet == -1)
01671   {
01672     return true;
01673   }
01674 
01675   // Check if there aren't any texture sheets! (should never happen)
01676   if(_tex_sheets.empty())
01677   {
01678     if(VIDEO_DEBUG)
01679       cerr << "VIDEO_WARNING: Called DEBUG_ShowTexture(), but there were no texture sheets" << endl;
01680     return false;
01681   }
01682 
01683   int32 num_sheets = (int32) _tex_sheets.size();
01684 
01685   // We may go out of bounds say, if we were viewing a texture sheet and then it got
01686   // deleted or something. To recover, just set it to the last texture sheet
01687   if(_current_debug_TexSheet >= num_sheets)
01688   {
01689     _current_debug_TexSheet = num_sheets - 1;
01690   }
01691 
01692   TexSheet *sheet = _tex_sheets[_current_debug_TexSheet];
01693 
01694   if(!sheet)
01695   {
01696     return false;
01697   }
01698 
01699   int32 w = sheet->width;
01700   int32 h = sheet->height;
01701 
01702   Image img(sheet, string(), "<T>", 0, 0, 0.0f, 0.0f, 1.0f, 1.0f, w, h, false);
01703 
01704 
01705   _PushContext();
01706   SetDrawFlags(VIDEO_NO_BLEND, VIDEO_X_LEFT, VIDEO_Y_BOTTOM, 0);
01707   SetCoordSys(0.0f, 1024.0f, 0.0f, 760.0f);
01708 
01709   glPushMatrix();
01710 
01711   Move(0.0f,0.0f);
01712   glScalef(0.5f, 0.5f, 0.5f);
01713 
01714   ImageElement elem(&img, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, (float)w, (float)h);
01715 
01716   StillImage id;
01717   id._elements.push_back(elem);
01718 
01719 
01720   if(!DrawImage(id))
01721   {
01722     glPopMatrix();
01723     _PopContext();
01724     return false;
01725   }
01726 
01727   glPopMatrix();
01728 
01729   if(!SetFont("debug_font"))
01730   {
01731     _PopContext();
01732     return false;
01733   }
01734 
01735   char buf[200];
01736 
01737   Move(20, _coord_sys.GetTop() - 30);
01738   if(!DrawText("Current Texture sheet:"))
01739   {
01740     _PopContext();
01741     return false;
01742   }
01743 
01744   sprintf(buf, "  Sheet #: %d", _current_debug_TexSheet);
01745   MoveRelative(0, -20);
01746   if(!DrawText(buf))
01747   {
01748     _PopContext();
01749     return false;
01750   }
01751 
01752   MoveRelative(0, -20);
01753   sprintf(buf, "  Size:    %dx%d", sheet->width, sheet->height);
01754   if(!DrawText(buf))
01755   {
01756     _PopContext();
01757     return false;
01758   }
01759 
01760   if(sheet->type == VIDEO_TEXSHEET_32x32)
01761     sprintf(buf, "  Type:    32x32");
01762   else if(sheet->type == VIDEO_TEXSHEET_32x64)
01763     sprintf(buf, "  Type:    32x64");
01764   else if(sheet->type == VIDEO_TEXSHEET_64x64)
01765     sprintf(buf, "  Type:    64x64");
01766   else if(sheet->type == VIDEO_TEXSHEET_ANY)
01767     sprintf(buf, "  Type:    Any size");
01768 
01769   MoveRelative(0, -20);
01770   if(!DrawText(buf))
01771   {
01772     _PopContext();
01773     return false;
01774   }
01775 
01776   sprintf(buf, "  Static:  %d", sheet->is_static);
01777   MoveRelative(0, -20);
01778   if(!DrawText(buf))
01779   {
01780     _PopContext();
01781     return false;
01782   }
01783 
01784   sprintf(buf, "  TexID:   %d", sheet->tex_ID);
01785   MoveRelative(0, -20);
01786   if(!DrawText(buf))
01787   {
01788     _PopContext();
01789     return false;
01790   }
01791 
01792   _PopContext();
01793   return true;
01794 }
01795 
01796 
01797 
01798 //-----------------------------------------------------------------------------
01799 // _DeleteImage: decreases the reference count on an image, and deletes it
01800 //               if zero is reached. Note that for images larger than 512x512,
01801 //               there is no reference counting; we just delete it immediately
01802 //               because we don't want huge textures sitting around in memory
01803 //-----------------------------------------------------------------------------
01804 
01805 bool GameVideo::_DeleteImage(Image *const img)
01806 {
01807   // If the image is grayscale, also perform a delete for the color image one
01808   if (img->grayscale)
01809   {
01810     // The filename of the color image is the grayscale one but without "_grayscale" (10 characters) at the end
01811     string filename (img->filename,0,img->filename.length()-10);
01812 
01813     map<string,Image*>::iterator it = _images.find(filename);
01814     if (it == _images.end())
01815     {
01816       if (VIDEO_DEBUG)
01817         cerr << "Attemp to delete a color copy didn't work" << endl;
01818       return false;
01819     }
01820 
01821     _DeleteImage(it->second);
01822   }
01823 
01824   if(img->width > 512 || img->height > 512)
01825   {
01826     // Remove the image and texture sheet completely
01827     _RemoveSheet(img->texture_sheet);
01828     _RemoveImage(img);
01829   }
01830   else
01831   {
01832     // For smaller images, simply mark them as free in the memory manager
01833     --(img->ref_count);
01834     if(img->ref_count <= 0)
01835     {
01836       img->texture_sheet->FreeImage(img);
01837     }
01838   }
01839 
01840   return true;
01841 }
01842 
01843 
01844 
01845 
01846 //-----------------------------------------------------------------------------
01847 // _RemoveSheet: removes a texture sheet from the internal std::vector
01848 //-----------------------------------------------------------------------------
01849 
01850 bool GameVideo::_RemoveSheet(TexSheet *sheet)
01851 {
01852   if(_tex_sheets.empty())
01853   {
01854     return false;
01855   }
01856 
01857   vector<TexSheet*>::iterator iSheet = _tex_sheets.begin();
01858   vector<TexSheet*>::iterator iEnd   = _tex_sheets.end();
01859 
01860   // Search std::vector for pointer matching sheet and remove it
01861   while(iSheet != iEnd)
01862   {
01863     if(*iSheet == sheet)
01864     {
01865       delete sheet;
01866       _tex_sheets.erase(iSheet);
01867       return true;
01868     }
01869     ++iSheet;
01870   }
01871 
01872   // Couldn't find the image
01873   return false;
01874 }
01875 
01876 
01877 
01878 
01879 //-----------------------------------------------------------------------------
01880 // AddImage: adds a new image to a texture sheet
01881 // NOTE: assumes that the image we're adding is still "bound" in DevIL
01882 //-----------------------------------------------------------------------------
01883 
01884 bool TexSheet::AddImage(Image *img, ImageLoadInfo & load_info)
01885 {
01886   // Try inserting into the texture memory manager
01887   bool could_insert = tex_mem_manager->Insert(img);
01888   if(!could_insert)
01889     return false;
01890 
01891   // Now img contains the x, y, width, and height of the subrectangle
01892   // inside the texture sheet, so go ahead and copy that area
01893 
01894   TexSheet *tex_sheet = img->texture_sheet;
01895   if(!tex_sheet)
01896   {
01897     // Technically this should never happen since Insert() returned true
01898     if(VIDEO_DEBUG)
01899     {
01900       cerr << "VIDEO ERROR: texSheet was NULL after tex_mem_manager->Insert() returned true" << endl;
01901     }
01902     return false;
01903   }
01904 
01905   if(!CopyRect(img->x, img->y, load_info))
01906   {
01907     if(VIDEO_DEBUG)
01908       cerr << "VIDEO ERROR: CopyRect() failed in TexSheet::AddImage()!" << endl;
01909     return false;
01910   }
01911 
01912   return true;
01913 }
01914 
01915 
01916 
01917 
01918 //-----------------------------------------------------------------------------
01919 // CopyRect: copies an image into a sub-rectangle of the texture
01920 //-----------------------------------------------------------------------------
01921 
01922 bool TexSheet::CopyRect(int32 x, int32 y, private_video::ImageLoadInfo & load_info)
01923 {
01924   GLenum error;
01925 
01926   if (!VideoManager->_BindTexture(tex_ID))
01927   {
01928     if(VIDEO_DEBUG)
01929     {
01930       cerr << "VIDEO ERROR: could not bind texture in TexSheet::CopyRect()!" << endl;
01931     }
01932     return false;
01933   }
01934 
01935   glTexSubImage2D
01936   (
01937     GL_TEXTURE_2D,    // target
01938     0,      // level
01939     x,      // x offset within tex sheet
01940     y,      // y offset within tex sheet
01941     load_info.width,  // width in pixels of image
01942     load_info.height, // height in pixels of image
01943     GL_RGBA,    // format
01944     GL_UNSIGNED_BYTE, // type
01945     load_info.pixels  // pixels of the sub image
01946   );
01947 
01948   error = glGetError();
01949   if(error)
01950   {
01951     if(VIDEO_DEBUG)
01952     {
01953       cerr << "VIDEO ERROR: glTexSubImage2D() failed in TexSheet::CopyRect()!" << endl;
01954     }
01955     return false;
01956   }
01957 
01958   return true;
01959 }
01960 
01961 bool TexSheet::CopyScreenRect(int32 x, int32 y, const ScreenRect &screen_rect)
01962 {
01963   GLenum error;
01964 
01965   if (!VideoManager->_BindTexture(tex_ID))
01966   {
01967     if(VIDEO_DEBUG)
01968     {
01969       cerr << "VIDEO ERROR: could not bind texture in TexSheet::CopyScreenRect()!" << endl;
01970     }
01971     return false;
01972   }
01973 
01974   glCopyTexSubImage2D
01975   (
01976     GL_TEXTURE_2D,    // target
01977     0,      // level
01978     x,      // x offset within tex sheet
01979     y,      // y offset within tex sheet
01980     screen_rect.left, // left starting pixel of the screen to copy
01981     screen_rect.top - screen_rect.height, // bottom starting pixel of the screen to copy
01982     screen_rect.width,  // width in pixels of image
01983     screen_rect.height  // height in pixels of image
01984   );
01985 
01986   error = glGetError();
01987   if(error)
01988   {
01989     if(VIDEO_DEBUG)
01990     {
01991       cerr << "VIDEO ERROR: glTexSubImage2D() failed in TexSheet::CopyScreenRect()!" << endl;
01992     }
01993     return false;
01994   }
01995 
01996   return true;
01997 }
01998 
01999 
02000 
02001 //-----------------------------------------------------------------------------
02002 // _RemoveImage: removes an image completely from the texture sheet's
02003 //              memory manager so that a new image can be loaded in its place
02004 //-----------------------------------------------------------------------------
02005 
02006 bool TexSheet::RemoveImage(Image *img)
02007 {
02008   return tex_mem_manager->Remove(img);
02009 }
02010 
02011 
02012 
02013 
02014 //-----------------------------------------------------------------------------
02015 // FreeImage: sets the area taken up by the image to "free". However, the
02016 //            image is not removed from any lists yet! It's kept around in
02017 //            case we reload the image in the near future- in that case,
02018 //            we can simply Restore the image instead of reloading from disk.
02019 //-----------------------------------------------------------------------------
02020 
02021 bool TexSheet::FreeImage(Image *img)
02022 {
02023   return tex_mem_manager->Free(img);
02024 }
02025 
02026 
02027 
02028 
02029 //-----------------------------------------------------------------------------
02030 // RestoreImage: If an image is freed using FreeImage, and soon afterwards,
02031 //               we load that image again, this function restores the image
02032 //               without reloading the image from disk.
02033 //-----------------------------------------------------------------------------
02034 
02035 bool TexSheet::RestoreImage(Image *img)
02036 {
02037   return tex_mem_manager->Restore(img);
02038 }
02039 
02040 
02041 
02042 
02043 //-----------------------------------------------------------------------------
02044 // DeleteImage: deletes an image descriptor (this is what the API user should call)
02045 //-----------------------------------------------------------------------------
02046 
02047 bool GameVideo::DeleteImage(ImageDescriptor &id)
02048 {
02049   if(id._animated)
02050   {
02051     return _DeleteImage(dynamic_cast<AnimatedImage &>(id));
02052   }
02053   else
02054   {
02055     return _DeleteImage(dynamic_cast<StillImage &>(id));
02056   }
02057 }
02058 
02059 
02060 
02061 
02062 //-----------------------------------------------------------------------------
02063 // _DeleteImage: deletes an animated image. Actually just goes through each frame
02064 //               and deletes that static image by calling the other _DeleteImage() function
02065 //-----------------------------------------------------------------------------
02066 
02067 bool GameVideo::_DeleteImage(AnimatedImage &id)
02068 {
02069   int32 num_frames = id.GetNumFrames();
02070   bool success = true;
02071 
02072   for(int32 j = 0; j < num_frames; ++j)
02073   {
02074     success &= _DeleteImage(id._frames[j].image);
02075   }
02076 
02077   return success;
02078 }
02079 
02080 
02081 
02082 
02083 //-----------------------------------------------------------------------------
02084 // _DeleteImage: decrements the reference count for all images composing this
02085 //              image descriptor.
02086 //
02087 // NOTE: for images which are 1024x1024 or higher, once their reference count
02088 //       reaches zero, they're immediately deleted. (don't want to keep those
02089 //       in memory if possible). For others, they're simply marked as "free"
02090 //-----------------------------------------------------------------------------
02091 
02092 bool GameVideo::_DeleteImage(StillImage &id)
02093 {
02094   vector<ImageElement>::iterator iImage = id._elements.begin();
02095   vector<ImageElement>::iterator iEnd   = id._elements.end();
02096 
02097   // Loop through all the images inside this descriptor
02098   while(iImage != iEnd)
02099   {
02100     Image *img = (*iImage).image;
02101 
02102     // Only delete the image if the pointer is valid. Some ImageElements
02103     // have a NULL pointer because they are just colored quads
02104 
02105     if(img)
02106     {
02107       if(img->ref_count <= 0)
02108       {
02109         if(VIDEO_DEBUG)
02110           cerr << "VIDEO ERROR: Called DeleteImage() when refcount was already <= 0!" << endl;
02111         return false;
02112       }
02113 
02114       --(img->ref_count);
02115 
02116       if(img->ref_count == 0)
02117       {
02118         // 1. If it's on a large tex sheet (> 512x512), delete it
02119         // Note: We can assume that this is the only image on that texture
02120         //       sheet, so it's safe to delete it. (Big textures always
02121         //       are allocated to their own sheet, by design.)
02122 
02123         if(img->width > 512 || img->height > 512)
02124         {
02125           _DeleteImage(img);  // overloaded DeleteImage for deleting Image
02126         }
02127 
02128         // 2. otherwise, mark it as "freed"
02129 
02130         else if(!img->texture_sheet->FreeImage(img))
02131         {
02132           if(VIDEO_DEBUG)
02133             cerr << "VIDEO ERROR: Could not remove image from texture sheet!" << endl;
02134           return false;
02135         }
02136       }
02137     }
02138 
02139     ++iImage;
02140   }
02141 
02142   id._elements.clear();
02143   id._filename = "";
02144   id._height = id._width = 0;
02145   id._is_static = 0;
02146 
02147   return true;
02148 }
02149 
02150 
02151 
02152 
02153 //-----------------------------------------------------------------------------
02154 // _RemoveImage: removes the image pointer from the std::map
02155 //-----------------------------------------------------------------------------
02156 
02157 bool GameVideo::_RemoveImage(Image *img)
02158 {
02159   // Nothing to do if img is null
02160   if(!img)
02161     return true;
02162 
02163   if(_images.empty())
02164   {
02165     return false;
02166   }
02167 
02168   map<string, Image*>::iterator iImage = _images.begin();
02169   map<string, Image*>::iterator iEnd   = _images.end();
02170 
02171   // Search std::map for pointer matching img and remove it
02172   while(iImage != iEnd)
02173   {
02174     if(iImage->second == img)
02175     {
02176       delete img;
02177       _images.erase(iImage);
02178       return true;
02179     }
02180     ++iImage;
02181   }
02182 
02183   // Couldn't find the image
02184   return false;
02185 }
02186 
02187 
02188 
02189 
02190 //-----------------------------------------------------------------------------
02191 // FixedTexMemMgr constructor
02192 //-----------------------------------------------------------------------------
02193 
02194 FixedTexMemMgr::FixedTexMemMgr
02195 (
02196   TexSheet *tex_sheet,
02197   int32 img_width,
02198   int32 img_height
02199 )
02200 {
02201   _tex_sheet = tex_sheet;
02202 
02203   // Set all the dimensions
02204   _sheet_width  = tex_sheet->width  / img_width;
02205   _sheet_height = tex_sheet->height / img_height;
02206   _image_width  = img_width;
02207   _image_height = img_height;
02208 
02209   // Allocate the blocks array
02210   int32 num_blocks = _sheet_width * _sheet_height;
02211   _blocks = new FixedImageNode[num_blocks];
02212 
02213   // Initialize linked list of open blocks... which, at this point is
02214   // all the blocks!
02215   _open_list_head = &_blocks[0];
02216   _open_list_tail = &_blocks[num_blocks-1];
02217 
02218   // Now initialize all the blocks to proper values
02219   for(int32 i = 0; i < num_blocks - 1; ++i)
02220   {
02221     _blocks[i].next  = &_blocks[i+1];
02222     _blocks[i].image = NULL;
02223     _blocks[i].block_index = i;
02224   }
02225 
02226   _blocks[num_blocks-1].next  = NULL;
02227   _blocks[num_blocks-1].image = NULL;
02228   _blocks[num_blocks-1].block_index = num_blocks - 1;
02229 }
02230 
02231 
02232 
02233 
02234 //-----------------------------------------------------------------------------
02235 // FixedTexMemMgr destructor
02236 //-----------------------------------------------------------------------------
02237 
02238 FixedTexMemMgr::~FixedTexMemMgr()
02239 {
02240   delete [] _blocks;
02241 }
02242 
02243 
02244 
02245 //-----------------------------------------------------------------------------
02246 // Insert: inserts a new block into the texture. If there's no free blocks
02247 //         left, return false
02248 //-----------------------------------------------------------------------------
02249 
02250 bool VariableTexMemMgr::Insert  (Image *img)
02251 {
02252 
02253   // Don't allow insertions into a texture bigger than 512x512...
02254   // This way, if we have a 1024x1024 texture holding a fullscreen background,
02255   // it is always safe to remove the texture sheet from memory when the
02256   // background is unreferenced. That way backgrounds don't stick around in memory.
02257 
02258   if(_sheet_width > 32 || _sheet_height > 32)  // 32 blocks = 512 pixels
02259   {
02260     if(!_blocks[0].free)  // Quick way to test if texsheet's occupied
02261       return false;
02262   }
02263 
02264 
02265   // Find an open block of memory. If none is found, return false
02266 
02267   int32 w = (img->width  + 15) / 16;   // width and height in blocks
02268   int32 h = (img->height + 15) / 16;
02269 
02270   int32 block_x=-1, block_y=-1;
02271 
02272 
02273   // This is a 100% brute force way to allocate a block, just a bunch
02274   // of nested loops. In practice, this actually works fine, because
02275   // the allocator deals with 16x16 blocks instead of trying to worry
02276   // about fitting images with pixel perfect resolution.
02277   // Later, if this turns out to be a bottleneck, we can rewrite this
02278   // algorithm to something more intelligent ^_^
02279   for(int32 y = 0; y < _sheet_height - h + 1; ++y)
02280   {
02281     for(int32 x = 0; x < _sheet_width - w + 1; ++x)
02282     {
02283       int32 furthest_blocker = -1;
02284 
02285       for(int32 dy = 0; dy < h; ++dy)
02286       {
02287         for(int32 dx = 0; dx < w; ++dx)
02288         {
02289           if(!_blocks[(x+dx)+((y+dy)*_sheet_width)].free)
02290           {
02291             furthest_blocker = x+dx;
02292             goto endneighborsearch_GOTO;
02293           }
02294         }
02295       }
02296 
02297       endneighborsearch_GOTO:
02298 
02299       if(furthest_blocker == -1)
02300       {
02301         block_x = x;
02302         block_y = y;
02303         goto endsearch_GOTO;
02304       }
02305     }
02306   }
02307 
02308   endsearch_GOTO:
02309 
02310   if(block_x == -1 || block_y == -1)
02311     return false;
02312 
02313   // Check if there's already an image allocated at this block.
02314   // If so, we have to notify GameVideo that we're ejecting
02315   // this image out of memory to make place for the new one
02316 
02317   // Update blocks
02318   std::set<hoa_video::private_video::Image *> remove_images;
02319 
02320   for(int32 y = block_y; y < block_y + h; ++y)
02321   {
02322     for(int32 x = block_x; x < block_x + w; ++x)
02323     {
02324       int32 index = x + (y * _sheet_width);
02325       // Check if there's already an image at the point we're
02326       // trying to load at. If so, we need to tell GameVideo
02327       // to update its internal vector
02328 
02329       if(_blocks[index].image)
02330       {
02331         remove_images.insert(_blocks[index].image);
02332       }
02333 
02334       _blocks[index].free  = false;
02335       _blocks[index].image = img;
02336 
02337     }
02338   }
02339 
02340   for(std::set<hoa_video::private_video::Image *>::iterator itr = remove_images.begin(); itr != remove_images.end(); itr++)
02341   {
02342     Remove(*itr);
02343     VideoManager->_RemoveImage(*itr);
02344   }
02345 
02346 
02347   // Calculate the actual pixel coordinates given this node's
02348   // block index
02349 
02350   img->x = block_x * 16;
02351   img->y = block_y * 16;
02352 
02353   // Calculate the u,v coordinates
02354 
02355   float sheet_width = (float) _tex_sheet->width;
02356   float sheet_height = (float) _tex_sheet->height;
02357 
02358   img->u1 = float(img->x + 0.5f)               / sheet_width;
02359   img->u2 = float(img->x + img->width - 0.5f)  / sheet_width;
02360   img->v1 = float(img->y + 0.5f)               / sheet_height;
02361   img->v2 = float(img->y + img->height - 0.5f) / sheet_height;
02362 
02363   img->texture_sheet = _tex_sheet;
02364   return true;
02365 }
02366 
02367 
02368 
02369 
02370 //-----------------------------------------------------------------------------
02371 // Remove: completely remove an image.
02372 //         In other words:
02373 //           1. find all the blocks this image owns
02374 //           2. mark all those blocks' image pointers to NULL
02375 //           3. set the "free" flag to true
02376 //-----------------------------------------------------------------------------
02377 
02378 bool VariableTexMemMgr::Remove(Image *img)
02379 {
02380   return SetBlockProperties(img, true, true, true, NULL);
02381 }
02382 
02383 
02384 
02385 
02386 //-----------------------------------------------------------------------------
02387 // SetBlockProperties: goes through all the blocks associated with img, and
02388 //                     updates their "free" and "image" properties if
02389 //                     changeFree and changeImage are true, respectively
02390 //-----------------------------------------------------------------------------
02391 
02392 bool VariableTexMemMgr::SetBlockProperties
02393 (
02394   Image *img,
02395   bool change_free,
02396   bool change_image,
02397   bool free,
02398   Image *new_image
02399 )
02400 {
02401   int32 block_x = img->x / 16;          // Upper-left corner in blocks
02402   int32 block_y = img->y / 16;
02403 
02404   int32 w = (img->width  + 15) / 16;   // Width and height in blocks
02405   int32 h = (img->height + 15) / 16;
02406 
02407   for(int32 y = block_y; y < block_y + h; ++y)
02408   {
02409     for(int32 x = block_x; x < block_x + w; ++x)
02410     {
02411       if(_blocks[x+y*_sheet_width].image == img)
02412       {
02413         if(change_free)
02414           _blocks[x+y*_sheet_width].free  = free;
02415         if(change_image)
02416           _blocks[x+y*_sheet_width].image = new_image;
02417       }
02418     }
02419   }
02420 
02421   return true;
02422 }
02423 
02424 
02425 
02426 
02427 //-----------------------------------------------------------------------------
02428 // Free: marks the blocks containing the image as free
02429 //       NOTE: this assumes that the block isn't ALREADY free
02430 //-----------------------------------------------------------------------------
02431 
02432 bool VariableTexMemMgr::Free(Image *img)
02433 {
02434   return SetBlockProperties(img, true, false, true, NULL);
02435 }
02436 
02437 
02438 
02439 
02440 //-----------------------------------------------------------------------------
02441 // Restore: marks the blocks containing the image as non-free
02442 //-----------------------------------------------------------------------------
02443 
02444 bool VariableTexMemMgr::Restore(Image *img)
02445 {
02446   return SetBlockProperties(img, true, false, false, NULL);
02447 }
02448 
02449 
02450 
02451 
02452 //-----------------------------------------------------------------------------
02453 // Insert: inserts a new block into the texture. If there's no free blocks
02454 //         left, returns false.
02455 //-----------------------------------------------------------------------------
02456 
02457 bool FixedTexMemMgr::Insert(Image *img)
02458 {
02459   // Whoa, nothing on the open list! (no blocks left) return false :(
02460 
02461   if(_open_list_head == NULL)
02462     return false;
02463 
02464   // Otherwise, get and remove the head of the open list
02465 
02466   FixedImageNode *node = _open_list_head;
02467   _open_list_head = _open_list_head->next;
02468 
02469   if(_open_list_head == NULL)
02470   {
02471     // This must mean we just removed the last open block, so
02472     // set the tail to NULL as well
02473     _open_list_tail = NULL;
02474   }
02475   else
02476   {
02477     // Since this is the new head, it's prev pointer should be NULL
02478     _open_list_head->prev = NULL;
02479   }
02480 
02481   node->next = NULL;
02482 
02483   // Check if there's already an image allocated at this block.
02484   // If so, we have to notify GameVideo that we're ejecting
02485   // this image out of memory to make place for the new one
02486 
02487   if(node->image)
02488   {
02489     VideoManager->_RemoveImage(node->image);
02490     node->image = NULL;
02491   }
02492 
02493   // Calculate the actual pixel coordinates given this node's
02494   // block index
02495 
02496   img->x = _image_width  * (node->block_index % _sheet_width);
02497   img->y = _image_height * (node->block_index / _sheet_width);
02498 
02499   // Calculate the u,v coordinates
02500 
02501   float sheet_width = (float) _tex_sheet->width;
02502   float sheet_height = (float) _tex_sheet->height;
02503 
02504   img->u1 = float(img->x + 0.5f)               / sheet_width;
02505   img->u2 = float(img->x + img->width - 0.5f)  / sheet_width;
02506   img->v1 = float(img->y + 0.5f)               / sheet_height;
02507   img->v2 = float(img->y + img->height - 0.5f) / sheet_height;
02508 
02509   img->texture_sheet = _tex_sheet;
02510 
02511   return true;
02512 }
02513 
02514 
02515 
02516 
02517 //-----------------------------------------------------------------------------
02518 // CalculateBlockIndex: returns the block index used up by this image
02519 //-----------------------------------------------------------------------------
02520 
02521 int32 FixedTexMemMgr::_CalculateBlockIndex(Image *img)
02522 {
02523   int32 block_x = img->x / _image_width;
02524   int32 block_y = img->y / _image_height;
02525 
02526   int32 block_index = block_x + _sheet_width * block_y;
02527   return block_index;
02528 }
02529 
02530 
02531 
02532 
02533 //-----------------------------------------------------------------------------
02534 // Remove: completely remove an image.
02535 //         In other words:
02536 //           1. mark its block's image pointer to NULL
02537 //           2. remove it from the open list
02538 //-----------------------------------------------------------------------------
02539 
02540 bool FixedTexMemMgr::Remove(Image *img)
02541 {
02542   // Translate x,y coordinates into a block index
02543   int32 block_index = _CalculateBlockIndex(img);
02544 
02545   // Check to make sure the block is actually owned by this image
02546   if(_blocks[block_index].image != img)
02547   {
02548     // Error, the block that the image thinks it owns is actually not
02549     // owned by that image
02550 
02551     if(VIDEO_DEBUG)
02552       cerr << "VIDEO ERROR: tried to remove a fixed block not owned by this Image" << endl;
02553     return false;
02554   }
02555 
02556   // Set the image to NULL to indicate that this block is completely free
02557   _blocks[block_index].image = NULL;
02558 
02559   // Remove block from the open list
02560   _DeleteNode(block_index);
02561 
02562   return true;
02563 }
02564 
02565 
02566 
02567 
02568 //-----------------------------------------------------------------------------
02569 // DeleteNode: deletes a node from the open list with the given block index
02570 //-----------------------------------------------------------------------------
02571 
02572 void FixedTexMemMgr::_DeleteNode(int32 block_index)
02573 {
02574   if(block_index < 0)
02575     return;
02576 
02577   if(block_index >= _sheet_width * _sheet_height)
02578     return;
02579 
02580   FixedImageNode *node = &_blocks[block_index];
02581 
02582   if(node->prev && node->next)
02583   {
02584     // Node has a prev and next
02585     node->prev->next = node->next;
02586   }
02587   else if(node->prev)
02588   {
02589     // Node is tail of the list
02590     node->prev->next = NULL;
02591     _open_list_tail = node->prev;
02592   }
02593   else if(node->next)
02594   {
02595     // Node is head of the list
02596     _open_list_head = node->next;
02597     node->next->prev = NULL;
02598   }
02599   else
02600   {
02601     // Node is the only element in the list
02602     _open_list_head = NULL;
02603     _open_list_tail = NULL;
02604   }
02605 
02606   // Just for good measure, clear out this node's pointers
02607   node->prev = NULL;
02608   node->next = NULL;
02609 }
02610 
02611 
02612 
02613 
02614 //-----------------------------------------------------------------------------
02615 // Free: marks the block containing the image as free, i.e. on the open list
02616 //       but leaves the image pointer intact in case we decide to restore
02617 //       the block later on
02618 //
02619 //       NOTE: this assumes that the block isn't ALREADY free
02620 //-----------------------------------------------------------------------------
02621 
02622 bool FixedTexMemMgr::Free(Image *img)
02623 {
02624   int32 block_index = _CalculateBlockIndex(img);
02625 
02626   FixedImageNode *node = &_blocks[block_index];
02627 
02628   if(_open_list_tail != NULL)
02629   {
02630     // Simply append to end of list
02631     _open_list_tail->next = node;
02632     node->prev = _open_list_tail;
02633     node->next = NULL;
02634     _open_list_tail = node;
02635   }
02636   else
02637   {
02638     // Special case: empty list
02639     _open_list_head = _open_list_tail = node;
02640     node->next = node->prev = NULL;
02641   }
02642 
02643   return true;
02644 }
02645 
02646 
02647 
02648 
02649 //-----------------------------------------------------------------------------
02650 // Restore: takes a block that was freed and takes it off the open list to
02651 //          mark it as "used" again
02652 //-----------------------------------------------------------------------------
02653 
02654 bool FixedTexMemMgr::Restore(Image *img)
02655 {
02656   int32 block_index = _CalculateBlockIndex(img);
02657   _DeleteNode(block_index);
02658   return true;
02659 }
02660 
02661 
02662 
02663 
02664 //-----------------------------------------------------------------------------
02665 // DEBUG_NextTexSheet: increments to the next texture sheet to show with
02666 //                     _DEBUG_ShowTexSheet().
02667 //-----------------------------------------------------------------------------
02668 
02669 void GameVideo::DEBUG_NextTexSheet()
02670 {
02671   ++_current_debug_TexSheet;
02672 
02673   if(_current_debug_TexSheet >= (int32) _tex_sheets.size())
02674   {
02675     _current_debug_TexSheet = -1;   // disable display
02676   }
02677 }
02678 
02679 
02680 
02681 
02682 //-----------------------------------------------------------------------------
02683 // DEBUG_PrevTexSheet: cycles to the previous texturesheet to show with
02684 //                     _DEBUG_ShowTexSheet().
02685 //-----------------------------------------------------------------------------
02686 
02687 void GameVideo::DEBUG_PrevTexSheet()
02688 {
02689   --_current_debug_TexSheet;
02690 
02691   if(_current_debug_TexSheet < -1)
02692   {
02693     _current_debug_TexSheet = (int32) _tex_sheets.size() - 1;
02694   }
02695 }
02696 
02697 
02698 
02699 
02700 //-----------------------------------------------------------------------------
02701 // ReloadTextures: reloads the texture sheets, after they have been unloaded
02702 //                 most likely due to a change of video mode.
02703 //                 Returns false if any of the textures fail to reload
02704 //-----------------------------------------------------------------------------
02705 
02706 bool GameVideo::ReloadTextures()
02707 {
02708   // Reload texture sheets
02709 
02710   vector<TexSheet *>::iterator iSheet    = _tex_sheets.begin();
02711   vector<TexSheet *>::iterator iSheetEnd = _tex_sheets.end();
02712 
02713   bool success = true;
02714 
02715   while(iSheet != iSheetEnd)
02716   {
02717     TexSheet *sheet = *iSheet;
02718 
02719     if(sheet)
02720     {
02721       if(!sheet->Reload())
02722       {
02723         if(VIDEO_DEBUG)
02724           cerr << "VIDEO_ERROR: in ReloadTextures(), sheet->Reload() failed!" << endl;
02725         success = false;
02726       }
02727     }
02728     else
02729     {
02730       if(VIDEO_DEBUG)
02731         cerr << "VIDEO ERROR: in ReloadTextures(), one of the tex sheets in the vector was NULL!" << endl;
02732       success = false;
02733     }
02734 
02735     ++iSheet;
02736   }
02737 
02738   _DeleteTempTextures();
02739 
02740   if(_uses_lights)
02741     _light_overlay = _CreateBlankGLTexture(1024, 1024);
02742 
02743   return success;
02744 }
02745 
02746 
02747 
02748 
02749 //-----------------------------------------------------------------------------
02750 // UnloadTextures: frees the texture memory taken up by the texture sheets,
02751 //                 but leaves the lists of images intact so we can reload them
02752 //                 Returns false if any of the textures fail to unload.
02753 //-----------------------------------------------------------------------------
02754 
02755 bool GameVideo::UnloadTextures()
02756 {
02757   // Save temporary textures to disk, in other words textures which weren't
02758   // loaded to a file. This way when we recreate the GL context we will
02759   // be able to load them again.
02760   _SaveTempTextures();
02761 
02762   // Unload texture sheets
02763   vector<TexSheet *>::iterator iSheet    = _tex_sheets.begin();
02764   vector<TexSheet *>::iterator iSheetEnd = _tex_sheets.end();
02765 
02766   bool success = true;
02767 
02768   while(iSheet != iSheetEnd)
02769   {
02770     TexSheet *sheet = *iSheet;
02771 
02772     if(sheet)
02773     {
02774       if(!sheet->Unload())
02775       {
02776         if(VIDEO_DEBUG)
02777           cerr << "VIDEO_ERROR: in UnloadTextures(), sheet->Unload() failed!" << endl;
02778         success = false;
02779       }
02780     }
02781     else
02782     {
02783       if(VIDEO_DEBUG)
02784         cerr << "VIDEO ERROR: in UnloadTextures(), one of the tex sheets in the vector was NULL!" << endl;
02785       success = false;
02786     }
02787 
02788     ++iSheet;
02789   }
02790 
02791   if(_light_overlay != 0xFFFFFFFF)
02792   {
02793     _DeleteTexture(_light_overlay);
02794     _light_overlay = 0xFFFFFFFF;
02795   }
02796 
02797   // Clear all font caches
02798   map<string, FontProperties *>::iterator iFontProp    = _font_map.begin();
02799   map<string, FontProperties *>::iterator iFontPropEnd = _font_map.end();
02800 
02801   while(iFontProp != _font_map.end())
02802   {
02803     FontProperties *fp = iFontProp->second;
02804 
02805     if(fp->glyph_cache)
02806     {
02807       for(std::map<uint16, FontGlyph *>::iterator glyphitr = fp->glyph_cache->begin(); glyphitr != fp->glyph_cache->end(); glyphitr++)
02808       {
02809         _DeleteTexture((*glyphitr).second->texture);
02810         delete (*glyphitr).second;
02811       }
02812 
02813       fp->glyph_cache->clear();
02814     }
02815 
02816     ++iFontProp;
02817   }
02818 
02819   return success;
02820 }
02821 
02822 
02823 
02824 
02825 //-----------------------------------------------------------------------------
02826 // _DeleteTexture: wraps call to glDeleteTexture(), adds some checking to see
02827 //                if we deleted the last texture we bound using _BindTexture(),
02828 //                then set the last tex ID to 0xffffffff
02829 //-----------------------------------------------------------------------------
02830 
02831 bool GameVideo::_DeleteTexture(GLuint tex_ID)
02832 {
02833   glDeleteTextures(1, &tex_ID);
02834 
02835   if(_last_tex_ID == tex_ID)
02836     _last_tex_ID = 0xFFFFFFFF;
02837 
02838   if(glGetError())
02839     return false;
02840 
02841   return true;
02842 }
02843 
02844 
02845 
02846 
02847 //-----------------------------------------------------------------------------
02848 // Unload: unloads all memory used by OpenGL for this texture sheet
02849 //         Returns false if we fail to unload, or if the sheet was already
02850 //         unloaded
02851 //-----------------------------------------------------------------------------
02852 
02853 bool TexSheet::Unload()
02854 {
02855   // Check if we're already unloaded
02856   if(!loaded)
02857   {
02858     if(VIDEO_DEBUG)
02859       cerr << "VIDEO ERROR: unloading an already unloaded texture sheet" << endl;
02860     return false;
02861   }
02862 
02863   // Delete the texture
02864   if(!VideoManager->_DeleteTexture(tex_ID))
02865   {
02866     if(VIDEO_DEBUG)
02867       cerr << "VIDEO ERROR: _DeleteTexture() failed in TexSheet::Unload()!" << endl;
02868     return false;
02869   }
02870 
02871   loaded = false;
02872   return true;
02873 }
02874 
02875 
02876 
02877 
02878 //-----------------------------------------------------------------------------
02879 // _CreateBlankGLTexture: creates a blank texture of the given width and height
02880 //                       and returns its OpenGL texture ID.
02881 //                       Returns 0xffffffff on failure
02882 //-----------------------------------------------------------------------------
02883 
02884 GLuint GameVideo::_CreateBlankGLTexture(int32 width, int32 height)
02885 {
02886   // Attempt to create a GL texture with the given width and height
02887   // if texture creation fails, return NULL
02888 
02889   int32 error;
02890 
02891   GLuint tex_ID;
02892 
02893   glGenTextures(1, &tex_ID);
02894   error = glGetError();
02895 
02896   if(!error)   // If there's no error so far, attempt to bind texture
02897   {
02898     _BindTexture(tex_ID);
02899     error = glGetError();
02900 
02901     // If the binding was successful, initialize the texture with glTexImage2D()
02902     if(!error)
02903     {
02904       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
02905       error = glGetError();
02906     }
02907   }
02908 
02909   if(error != 0)   // If there's an error, delete the texture and return error code
02910   {
02911     _DeleteTexture(tex_ID);
02912 
02913     if(VIDEO_DEBUG)
02914     {
02915       cerr << "VIDEO ERROR: failed to create new texture in _CreateBlankGLTexture()." << endl;
02916       cerr << "  OpenGL reported the following error:" << endl << "  ";
02917       char *errString = (char*)gluErrorString(error);
02918       cerr << errString << endl;
02919     }
02920     return 0xffffffff;
02921   }
02922 
02923   // Set linear texture interpolation if we're at non-natural resolution
02924   GLenum filtering_type = VideoManager->_ShouldSmooth() ? GL_LINEAR : GL_NEAREST;
02925 
02926   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering_type );
02927   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering_type );
02928   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
02929   glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
02930 
02931   return tex_ID;
02932 }
02933 
02934 
02935 
02936 
02937 //-----------------------------------------------------------------------------
02938 // Reload: reallocate memory with OpenGL for this texture and load all the images
02939 //         back into it
02940 //         Returns false if we fail to reload or if the sheet was already loaded
02941 //-----------------------------------------------------------------------------
02942 
02943 bool TexSheet::Reload()
02944 {
02945   // Check if we're already loaded
02946   if(loaded)
02947   {
02948     if(VIDEO_DEBUG)
02949       cerr << "VIDEO ERROR: loading an already loaded texture sheet" << endl;
02950     return false;
02951   }
02952 
02953   // Create new OpenGL texture
02954   GLuint tID = VideoManager->_CreateBlankGLTexture(width, height);
02955 
02956   if(tID == 0xFFFFFFFF)
02957   {
02958     if(VIDEO_DEBUG)
02959       cerr << "VIDEO ERROR: _CreateBlankGLTexture() failed in TexSheet::Reload()!" << endl;
02960     return false;
02961   }
02962 
02963   tex_ID = tID;
02964 
02965   // Now the hard part: go through all the images that belong to this texture
02966   // and reload them again. (GameVideo's function, _ReloadImagesToSheet does this)
02967 
02968   if(!VideoManager->_ReloadImagesToSheet(this))
02969   {
02970     if(VIDEO_DEBUG)
02971       cerr << "VIDEO ERROR: CopyImagesToSheet() failed in TexSheet::Reload()!" << endl;
02972     return false;
02973   }
02974 
02975   loaded = true;
02976   return true;
02977 }
02978 
02979 
02980 
02981 
02982 //-----------------------------------------------------------------------------
02983 // _ReloadImagesToSheet: helper function of the GameVideo class to
02984 //                      TexSheet::Reload() to do the dirty work of reloading
02985 //                      image data into the appropriate spots on the texture
02986 //-----------------------------------------------------------------------------
02987 
02988 bool GameVideo::_ReloadImagesToSheet(TexSheet *sheet)
02989 {
02990   // Delete images
02991   map<string, Image *>::iterator iImage     = _images.begin();
02992   map<string, Image *>::iterator iImageEnd  = _images.end();
02993 
02994   std::map <string, private_video::MultiImageInfo> multi_image_info;
02995 
02996   bool success = true;
02997   while(iImage != iImageEnd)
02998   {
02999     Image *i = iImage->second;
03000 
03001     // Check if the current image belongs to this sheet
03002     if(i->texture_sheet == sheet)
03003     {
03004       ImageLoadInfo load_info;
03005 
03006       bool is_multi_image = ( i->tags.find("<X",0) != i->filename.npos);
03007 
03008       if (is_multi_image) // Check if this is a multiimage and load as it
03009       {
03010         ImageLoadInfo image;
03011 
03012         if (multi_image_info.find(i->filename) == multi_image_info.end())
03013         {
03014           // Load the image
03015           if(!_LoadRawImage(i->filename, load_info))
03016           {
03017             if(VIDEO_DEBUG)
03018               cerr << "VIDEO ERROR: _LoadRawImage() failed in _ReloadImagesToSheet()!" << endl;
03019             success = false;
03020           }
03021 
03022           // Copy the part of the image in a buffer
03023           image.height = i->height;
03024           image.width = i->width;
03025           image.pixels = malloc(image.height * image.width * 4);
03026 
03027           MultiImageInfo info;
03028           info.multi_image = load_info;
03029           info.image = image;
03030           multi_image_info[i->filename] = info;
03031         }
03032         else
03033         {
03034           load_info = multi_image_info[i->filename].multi_image;
03035           image = multi_image_info[i->filename].image;
03036         }
03037 
03038         if (!image.pixels)
03039         {
03040           if (VIDEO_DEBUG)
03041             cerr << "VIDEO ERROR: run out of memory in _ReloadImageToSheet()" << endl;
03042           success = false;
03043         }
03044 
03045         uint16 pos0, pos1;
03046         pos0 = i->tags.find("<X", 0);
03047         pos1 = i->tags.find('_', pos0);
03048         uint32 x = atoi( i->tags.substr(pos0+2, pos1).c_str() );
03049         uint32 rows = load_info.height / image.height;
03050         pos0 = i->tags.find("<Y", 0);
03051         pos1 = i->tags.find('_', pos0);
03052         uint32 y = atoi( i->tags.substr(pos0+2, pos1).c_str() );
03053         uint32 cols = load_info.width / image.width;
03054 
03055         for (int32 row=0; row<image.height; row++)
03056         {
03057           memcpy ((uint8*)image.pixels+4*image.width*row, (uint8*)load_info.pixels+(((x*load_info.height/rows)+row)*load_info.width+y*load_info.width/cols)*4, 4*image.width);
03058         }
03059 
03060         // Convert to grayscale if needed
03061         if (i->tags.find("<G>",0) != i->filename.npos)
03062           _ConvertImageToGrayscale(image, image);
03063 
03064         // Copy in the texture the image
03065         if(!sheet->CopyRect(i->x, i->y, image))
03066         {
03067           if(VIDEO_DEBUG)
03068             cerr << "VIDEO ERROR: sheet->CopyRect() failed in _ReloadImagesToSheet()!" << endl;
03069           success = false;
03070         }
03071       }
03072       else    // Load this way if it as a normal image (one image in one file)
03073       {
03074         std::string fname = i->filename;
03075         if (i->tags.find("<T>",0) != i->tags.npos)
03076         {
03077           fname = "img\\temp\\" + fname + ".png";
03078         }
03079 
03080         if(!_LoadRawImage(fname, load_info))
03081         {
03082           if(VIDEO_DEBUG)
03083             cerr << "VIDEO ERROR: _LoadRawImage() failed in _ReloadImagesToSheet()!" << endl;
03084           success = false;
03085         }
03086 
03087         // Convert to grayscale if needed
03088         if (i->tags.find("<G>",0) != i->filename.npos)
03089           _ConvertImageToGrayscale(load_info, load_info);
03090 
03091         if(!sheet->CopyRect(i->x, i->y, load_info))
03092         {
03093           if(VIDEO_DEBUG)
03094             cerr << "VIDEO ERROR: sheet->CopyRect() failed in _ReloadImagesToSheet()!" << endl;
03095           success = false;
03096         }
03097 
03098         if (load_info.pixels)
03099           free (load_info.pixels);
03100       }
03101     }
03102 
03103     ++iImage;
03104   }
03105 
03106   for (map<string,MultiImageInfo>::iterator it=multi_image_info.begin(); it!=multi_image_info.end(); ++it)
03107   {
03108     free ((*it).second.multi_image.pixels);
03109     free ((*it).second.image.pixels);
03110   }
03111 
03112   return success;
03113 }
03114 
03115 
03116 
03117 
03118 //-----------------------------------------------------------------------------
03119 // _SaveTempTextures: save all textures to disk which were not loaded from a file
03120 //-----------------------------------------------------------------------------
03121 
03122 bool GameVideo::_SaveTempTextures()
03123 {
03124   map<string, Image*>::iterator iImage = _images.begin();
03125   map<string, Image*>::iterator iEnd   = _images.end();
03126 
03127   while(iImage != iEnd)
03128   {
03129     Image *image = iImage->second;
03130 
03131     // It's a temporary texture!!
03132     if(image->tags.find("<T>") != string::npos)
03133     {
03134       hoa_video::private_video::ImageLoadInfo buffer;
03135 
03136       _GetBufferFromImage (buffer, image);
03137       _SavePng ("img\\temp\\"+image->filename+".png", buffer);
03138     }
03139 
03140     ++iImage;
03141   }
03142   return true;
03143 }
03144 
03145 
03146 
03147 
03148 //-----------------------------------------------------------------------------
03149 // _DeleteTempTextures: delete all the textures in the temp directory
03150 //-----------------------------------------------------------------------------
03151 
03152 bool GameVideo::_DeleteTempTextures()
03153 {
03154   return CleanDirectory("img\\temp");
03155 }
03156 
03157 
03158 
03159 }  // namespace hoa_video
03160 

Generated on Fri Jul 6 23:11:24 2007 for Hero of Allacrost by  doxygen 1.5.1