Richard Parker / EALCD
Revision:
6:4fe6f365cbeb
Parent:
3:24fbf4dbd7e5
--- a/widgets/EAImage.cpp	Tue Apr 27 23:13:21 2010 +0000
+++ b/widgets/EAImage.cpp	Thu May 06 23:32:14 2010 +0000
@@ -12,7 +12,11 @@
 :   _path(NULL),
     _palette(NULL),
     _dataOffset(0),
-    _mask(false)
+    _mask(false),
+    _cached(false),
+    _cache(NULL),
+    _size(0L),
+    _pos(0L)
 {
     // Start from known state.
     unload();
@@ -20,23 +24,166 @@
 
 EAImage::~EAImage()
 {
-    if (_path != NULL)
+    unload();
+}
+
+int EAImage::_feof(FILE* stream)
+{
+    if (isCached() == true)
     {
-        delete[] _path;
-        _path = NULL;
+        return (_pos < _size) ? 0 : 1;
+    } else {
+        return feof(stream);
+    }
+}
+
+
+int EAImage::_fseek(FILE* stream, long int offset, int origin)
+{
+    if (isCached() == true)
+    {   
+        switch (origin)
+        {
+            default:
+            case SEEK_SET:
+                _pos = offset;
+            break;
+            case SEEK_CUR:
+                _pos = _pos + offset;
+            break;
+            case SEEK_END:
+                _pos = (_size-1) + offset;
+            break;
+        }
+        
+        // Make sure position within range.
+        if (_pos < 0)
+        {
+            _pos = 0;
+            return 0;
+        }
+        
+        if (_pos > (_size-1))
+        {
+            _pos = (_size-1);
+            return 0;
+        }
+        
+        // Error.
+        return 1;
+    } else {
+        return fseek(stream, offset, origin);
+    }
+}
+
+size_t EAImage::_fread(void* ptr, size_t size, size_t count, FILE* stream)
+{
+    if (isCached() == true)
+    {
+        // Truncate size to fit array if needed.
+        if (_pos + (count*size) > _size)
+        {
+            count = (_size - _pos)/size;
+        }
+        
+        if (count <= 0)
+        {
+            return 0;
+        }
+        
+        // Now copy out of cache to array.
+        memcpy(ptr, &_cache[_pos], count*size);
+        
+        // Update the file position.
+        _pos = _pos + (count*size);
+        
+        // Return the number of bytes read.
+        return count;
+    } else {
+        return fread(ptr, size, count, stream);
+    }
+}
+
+bool EAImage::_loadCache(FILE* fp)
+{
+    // Can't do anything with a null pointer.
+    if (fp == NULL)
+    {
+        return false;
     }
     
-    if (_palette != NULL)
+    // Clear any previously loaded cache.
+    if (_cache != NULL)
+    {
+        delete[] _cache;
+        _cache = NULL;
+    }
+    _size = 0L;
+    _pos = 0L;
+
+    if (isCached() == false)
+    {
+        return true;
+    }
+
+    // Get the size of the file.    
+    fseek(fp, 0, SEEK_END);
+    long int size = ftell(fp);
+    
+    if (size == -1)
+    {
+        // Something went wronmg with the command.
+        return false;
+    }
+
+    // Now create a cache large enough to hold all of the image data.
+    _cache = new char[size];
+    
+    if (_cache == NULL)
     {
-        delete[] _palette;
-        _palette = NULL;
+        // Unable to allocate enough space for the font.
+        return false;
     }
+
+    // Now rewind file pointer back to the beginning.
+    rewind(fp);
+    
+    const unsigned int CHUNK_SIZE = 100;
+    long int sizeRead = 0;
+    long int totalSizeRead = 0;
+        
+    // Load in small chunks at a time.
+    while (!feof(fp))
+    {
+        // Read into the buffer.
+        sizeRead = fread(&_cache[totalSizeRead], 1, CHUNK_SIZE, fp);
+                        
+        // Update total count of amount of data loaded.
+        totalSizeRead += sizeRead;
+    }
+    
+    // Check that the whole file loaded.
+    if (totalSizeRead != size)
+    {
+        // Clear cache.        
+        delete[] _cache;
+        _cache = NULL;
+        
+        // Return error.
+        return false;
+    }
+    
+    // Record size and position.
+    _size = size;
+    _pos = 0L;
+    
+    return true;
 }
 
 bool EAImage::_loadPalette(FILE* fp)
 {
-    // Can't do anything with a null pointer.
-    if (fp == NULL)
+    // Can't do anything with a null pointer unless cached.
+    if ((fp == NULL) && (isCached() == false))
     {
         return false;
     }
@@ -73,13 +220,13 @@
     unsigned int offset = _dataOffset - (noColors*4);
     
     // Seek to the start of the table.
-    fseek(fp, offset, SEEK_SET);
+    _fseek(fp, offset, SEEK_SET);
     unsigned char buffer[4];
     
     // Iterate through the table filling the palette as needed.
     for (int i = 0; i < noColors; i++)
     {
-        int sizeRead = fread(&buffer, 4, 1, fp);
+        int sizeRead = _fread(&buffer, 4, 1, fp);
         
         if (sizeRead == 1)
         {
@@ -114,6 +261,7 @@
     {
         // Clean up file handle.
         fclose(fp);
+        unload();
     
         // Header incorrect.
         return false;
@@ -137,7 +285,8 @@
     if ((info().compressionType != 3) && (info().compressionType != 0))
     {      
         // Clean up file handle.
-        fclose(fp);
+        fclose(fp);        
+        unload();
     
         // Header incorrect.
         return false;
@@ -147,6 +296,17 @@
     _dataOffset = offset;
     setWidth(info().width);
     setHeight(info().height);
+    
+    // If the image is to be cached then load the cache.
+    if (_loadCache(fp) == false)
+    {
+        // Clean up file handle.
+        fclose(fp);        
+        unload();
+    
+        // Header incorrect.
+        return false;
+    }
              
     // Close the file.
     fclose(fp);
@@ -337,8 +497,14 @@
     setWidth(0);
     setHeight(0);
     
-    // Set as not a mask.
-    _mask = false;
+    // Empty the cache.
+    if (_cache != NULL)
+    {
+        delete[] _cache;
+        _cache = NULL;
+    }    
+    _size = 0L;
+    _pos = 0L;
 }
 
 void EAImage::paint(EALCD& lcd)
@@ -376,32 +542,44 @@
     unsigned short data = 0;
     
     // Try and open the file, skip straight to the data.
-    FILE* fp = fopen(_path, "r");
-    if (fp == NULL)
+    FILE* fp = NULL;
+    if (isCached() == false)
     {
+        fp = fopen(_path, "r");
+        if (fp == NULL)
+        {
+            return;
+        }
+    }
+        
+    // If the bits per pixel are less than 16 then the bitmap is using a colour 
+    // palette which should be loaded first.
+    if (_loadPalette(fp) == false)
+    {
+        if (isCached() == false)
+        {
+            fclose(fp);
+        }
+        
         return;
     }
     
-    // If the bits per pixel are less than 16 then the bitmap is using a colour 
-    // palette which should be loaded first.
-    _loadPalette(fp);   
-    
     // Skip the header and size.
-    fseek(fp, _dataOffset, SEEK_SET);
+    _fseek(fp, _dataOffset, SEEK_SET);
     
     // Prepare the lcd for drawing.
     lcd._window(this->x(), this->y(), w, h);
     lcd._moveTo(this->x(), this->y()); 
     
     // Move in the file to the first pixel in the window.
-    fseek(fp, (y*wordsInRow*4)+(_wordForX(x)*4), SEEK_CUR); 
+    _fseek(fp, (y*wordsInRow*4)+(_wordForX(x)*4), SEEK_CUR); 
                             
     // Now read the data.
-    while (!feof(fp))
+    while (!_feof(fp))
     {    
         wordOffset = _xWordOffset(x);
                
-        int sizeRead = fread(&buffer, 4, wordsInRow, fp);
+        int sizeRead = _fread(&buffer, 4, wordsInRow, fp);
         
         for (int i = 0; i < sizeRead; i++)
         {        
@@ -409,7 +587,7 @@
             {           
                 // Convert the colour to a 16 bit colour value that can be written directly to the screen.                
                 data = _getColourAtOffset(buffer[i], wordOffset);
-
+                
                 if (isMask() == true)
                 {
                     // When a mask the 0x0000 value is transparent anything else is drawn with the pen color.
@@ -419,7 +597,7 @@
                     } else {
                         lcd._writeToDisplay(lcd.pen().color().rawValue());
                     }
-                } else {
+                } else {                    
                     // Not a mask so just use colour that have loaded.
                     lcd._writeToDisplay(data);
                 }
@@ -459,8 +637,11 @@
         _palette = NULL;
     }
     
-    // Close the file and release handle.
-    fclose(fp);
+    if (isCached() == false)
+    {
+        // Close the file and release handle.
+        fclose(fp);
+    }
 }