Richard Parker / EALCD
Revision:
1:f04bcaea1d60
Parent:
0:839ecbf5cb2a
Child:
3:24fbf4dbd7e5
diff -r 839ecbf5cb2a -r f04bcaea1d60 widgets/EAImage.cpp
--- a/widgets/EAImage.cpp	Thu Feb 11 12:21:18 2010 +0000
+++ b/widgets/EAImage.cpp	Thu Mar 04 10:54:06 2010 +0000
@@ -10,14 +10,75 @@
 
 EAImage::EAImage()
 :   _path(NULL),
-    _dataOffset(0)
+    _palette(NULL),
+    _dataOffset(0),
+    _mask(false)
 {
+    // Start from known state.
+    unload();
 }
 
 EAImage::~EAImage()
 {
 }
 
+bool EAImage::_loadPalette(FILE* fp)
+{
+    // Can't do anything with a null pointer.
+    if (fp == NULL)
+    {
+        return false;
+    }
+    
+    // Clear any previously loaded palette.
+    if (_palette != NULL)
+    {
+        delete[] _palette;
+        _palette = NULL;
+    }
+    
+    // If the number of bits is not less than 16 then return after having cleared the 
+    // palette. There is no palette required.
+    if (info().bitsPerPixel >= 16)
+    {
+        return true;
+    }
+    
+    // First create a palette of the required size.
+    unsigned int noColors = info().noColors;
+    
+    // When 0 the number of colors is equal to 2^n where n is the bits per pixel.
+    if (noColors == 0)
+    {
+        noColors = pow((double)2, info().bitsPerPixel);
+    }
+    
+    // Create the palette and assign the memory.
+    _palette = new EAColor[noColors];
+    
+    // Now parse the palette. First seek to the start of the palette. This must be the 
+    // start of the bitmap data minus the number of colour entries multiplied by 4 (the
+    // colours are stored as 4 byte long entries, the last byte can be ignored).
+    unsigned int offset = _dataOffset - (noColors*4);
+    
+    // Seek to the start of the table.
+    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);
+        
+        if (sizeRead == 1)
+        {
+            _palette[i].setRgb(buffer[2], buffer[1], buffer[0]);
+        }
+    }
+    
+    return true;
+}
+
 bool EAImage::_loadHeader(const char* path)
 {
     if (path == NULL)
@@ -35,7 +96,6 @@
 
     unsigned int bufint = 0;
     unsigned int offset = 0;
-    EABMPHeader header;
 
     // Read the magic numbers at start.
     fread(&bufint, 1, 2, fp);   
@@ -60,11 +120,11 @@
     offset = bufint;
          
     // Retrieve the header.
-    fread(&header, sizeof(EABMPHeader), 1, fp);
+    fread(&_header, sizeof(EABMPHeader), 1, fp);
           
-    // Make sure the bits per pixel is 16 and compression type is bit fields.
-    if ((header.bitsPerPixel != 16) || (header.compressionType != 3))
-    {   
+    // Make sure the compression type is a value that can be dealt with. 
+    if ((info().compressionType != 3) && (info().compressionType != 0))
+    {      
         // Clean up file handle.
         fclose(fp);
     
@@ -74,25 +134,140 @@
     
     // Set the values for later.
     _dataOffset = offset;
-    setWidth(header.width);
-    setHeight(header.height);
-          
+    setWidth(info().width);
+    setHeight(info().height);
+             
     // Close the file.
     fclose(fp);
     
     return true;
 }
 
+int EAImage::_wordsInRow()
+{
+    // If there were no padding to 32 bit boundaries this would be the number of bits per row 
+    // in the file.
+    int bitsPerWidth = width() * info().bitsPerPixel;
+       
+    // This is the row size with padding on the end.
+    int remainder = (bitsPerWidth % 32);
+    int bitsPerRow = (remainder == 0) ? bitsPerWidth : bitsPerWidth + (32 - remainder);
+    
+    // Return the size in number of words.
+    return (bitsPerRow / 32);
+}
+
+int EAImage::_wordForX(unsigned int x)
+{
+    int bitForX = x * info().bitsPerPixel;
+    
+    return (bitForX / 32);
+}
+
+int EAImage::_xWordOffset(unsigned int x)
+{
+    int bitForX = x * info().bitsPerPixel;
+    
+    return (bitForX % 32);
+}
+
+unsigned short EAImage::_getColourAtOffset(unsigned int word, int offset)
+{
+    // Sort bytes for endianness.
+    unsigned char* cptr;
+    unsigned short result = 0x0000;
+
+    // Now need to decide how to cast the value to a colour.
+    switch (info().bitsPerPixel)
+    {
+        // Swap the bytes around.
+        case 32:
+        case 4:
+        case 8:
+        case 1:
+            unsigned char tmp;
+            cptr = (unsigned char*)&word;
+            tmp = cptr[0];
+            cptr[0] = cptr[3];
+            cptr[3] = tmp;
+            tmp = cptr[1];
+            cptr[1] = cptr[2];
+            cptr[2] = tmp;
+        break;
+
+        default:
+        // Swap the 16bits around.
+        case 16:
+            unsigned char tmpa;
+            unsigned char tmpb;
+            cptr = (unsigned char*)&word;
+            tmpa = cptr[1];
+            tmpb = cptr[0];
+            cptr[1] = cptr[3];
+            cptr[0] = cptr[2];
+            cptr[3] = tmpa;
+            cptr[2] = tmpb;
+        break;
+    }
+
+    // First shift off the pixels above.
+    word = word << offset;
+    
+    // Now shift down so that pixel is in lsb.
+    word = word >> (32 - info().bitsPerPixel);
+
+    EAColor c;
+            
+    // Now need to decide how to cast the value to a colour.
+    switch (info().bitsPerPixel)
+    {
+        case 8:
+        case 4:
+        case 1:
+            if (_palette != NULL)
+            {
+                result = _palette[word].rawValue();
+            } else {
+                result = 0x0000;
+            }
+        break;
+
+        // By default just cast to unsigned short and return.        
+        default:
+        case 16:
+            result = (unsigned short)(word);
+        break;
+        
+        case 24:                        
+        case 32:         
+            unsigned char b = ((word << 0) >> 24);
+            unsigned char g = ((word << 8) >> 24);
+            unsigned char r = ((word << 16) >> 24);
+                       
+            c.setRgb(r, g, b);
+            result = c.rawValue();
+        break;
+    }
+    
+    return result;
+}
+
 bool EAImage::load(const char* path)
 {
     if (path == NULL)
     {
+        // Reset all of the state.
+        unload();
+
         // Invalid path passed in.
         return false;
     }
 
     if (_loadHeader(path) == false)
     {
+        // Reset all of the state.
+        unload();
+
         return false;
     }
 
@@ -115,6 +290,46 @@
     return true;
 }
 
+void EAImage::unload()
+{
+    // Empty the header struct.
+    _header.headerSize = 0;
+    _header.width = 0;
+    _header.height = 0;
+    _header.noColorPlanes = 0;
+    _header.bitsPerPixel = 0;
+    _header.compressionType = 0;
+    _header.bmpSize = 0;
+    _header.horizontalRes = 0;
+    _header.verticalRes = 0;
+    _header.noColors = 0;
+    _header.noImportantColors = 0;
+
+    // Clear the path.
+    if (_path != NULL)
+    {
+        delete[] _path;
+        _path = NULL;
+    }
+    
+    // Clear the palette.
+    if (_palette != NULL)
+    {
+        delete[] _palette;
+        _palette = NULL;
+    }
+
+    // Clear the data offset.
+    _dataOffset = 0;
+    
+    // Set the size to 0.
+    setWidth(0);
+    setHeight(0);
+    
+    // Set as not a mask.
+    _mask = false;
+}
+
 void EAImage::paint(EALCD& lcd)
 {
     paint(lcd, 0, 0, width(), height());
@@ -138,11 +353,16 @@
         return;
     }
     
-    // Buffer to hold load one line at a time.
-    int pixelsInLine = width() + (width() % 2);
-    unsigned short buffer[pixelsInLine];
+    // Buffer to hold load one line at a time this must be large enough to hold all of the data used 
+    // for a line in the file. Note that the line in the file is padded so that it always ends on a 
+    // 32 bit boundary.
+    int wordsInRow = _wordsInRow();
+    unsigned int buffer[wordsInRow];
     int xD = 0;
     int yD = 0;
+    int wordOffset = 0;
+    int bitsPerPixel = info().bitsPerPixel;
+    unsigned short data = 0;
     
     // Try and open the file, skip straight to the data.
     FILE* fp = fopen(_path, "r");
@@ -151,6 +371,10 @@
         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);
     
@@ -159,19 +383,49 @@
     lcd._moveTo(this->x(), this->y()); 
     
     // Move in the file to the first pixel in the window.
-    fseek(fp, (y*pixelsInLine*2)+(x*2), SEEK_CUR);
-           
+    fseek(fp, (y*wordsInRow*4)+(_wordForX(x)*4), SEEK_CUR); 
+                            
     // Now read the data.
     while (!feof(fp))
     {    
-        int sizeRead = fread(&buffer, 2, pixelsInLine, fp);
+        wordOffset = _xWordOffset(x);
+               
+        int sizeRead = fread(&buffer, 4, wordsInRow, fp);
         
         for (int i = 0; i < sizeRead; i++)
-        {
-            lcd._writeToDisplay(buffer[i]);
+        {        
+            while (wordOffset < 32)
+            {           
+                // 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.
+                    if (data == 0x0000)
+                    {
+                        lcd.noop();
+                    } else {
+                        lcd._writeToDisplay(lcd.pen().color().rawValue());
+                    }
+                } else {
+                    // Not a mask so just use colour that have loaded.
+                    lcd._writeToDisplay(data);
+                }
+                
+                // Got to next pixel in the word.
+                wordOffset += bitsPerPixel;
+
+                // Keep count of current x value.
+                xD++;
+                if (xD == w)
+                {
+                    break;
+                }            
+            }
+            wordOffset = 0;
             
-            // Keep count of current x value.
-            xD++;
+            // When written all required pixels exit.
             if (xD == w)
             {
                 xD = 0;
@@ -187,9 +441,15 @@
         }            
     }
     
+    // Clear the palette.
+    if (_palette != NULL)
+    {
+        delete[] _palette;
+        _palette = NULL;
+    }
+    
     // Close the file and release handle.
     fclose(fp);
-    
 }