Richard Parker / EALCD
Revision:
7:6cf21b018420
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/images/EAImage.cpp	Mon Nov 01 13:07:40 2010 +0000
@@ -0,0 +1,453 @@
+// Copyright 2010 Richard Parker
+
+#include "mbed.h"
+
+#include "EAImage.h"
+
+#include "../graphics/EAPen.h"
+#include "../graphics/EAColor.h"
+#include "../screen/EALCD.h"
+
+EAImage::EAImage()
+:   _path(NULL),
+    _palette(NULL),
+    _dataOffset(0),
+    _mask(false)
+{
+    // Start from known state.
+    unload();
+}
+
+EAImage::~EAImage()
+{
+    unload();
+}
+
+int EAImage::_feof(FILE* stream)
+{
+    return feof(stream);
+}
+
+
+int EAImage::_fseek(FILE* stream, long int offset, int origin)
+{
+    return fseek(stream, offset, origin);
+}
+
+size_t EAImage::_fread(void* ptr, size_t size, size_t count, FILE* stream)
+{
+    return fread(ptr, size, count, stream);
+}
+
+bool EAImage::_loadPalette(FILE* fp)
+{
+    // Can't do anything with a null pointer unless cached.
+    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 std::string& path)
+{
+    if (path.empty() == true)
+    {
+        // Invalid path passed in.
+        return false;
+    }
+    
+    // Try and open the file, check type and load width and height.
+    FILE* fp = fopen(path.c_str(), "r");
+    if (fp == NULL)
+    {
+        return false;
+    }
+
+    unsigned int bufint = 0;
+    unsigned int offset = 0;
+
+    // Read the magic numbers at start.
+    fread(&bufint, 1, 2, fp);   
+    if (bufint != 0x4d42)
+    {
+        // Clean up file handle.
+        fclose(fp);
+        unload();
+    
+        // Header incorrect.
+        return false;
+    }
+       
+    // Now read the bmp file size.
+    fread(&bufint, 1, 4, fp);
+      
+    // Now read the two creator values and throw away.
+    fread(&bufint, 1, 2, fp);
+    fread(&bufint, 1, 2, fp);
+    
+    // Now read the offset for the bitmap data.
+    fread(&bufint, 1, 4, fp);
+    offset = bufint;
+         
+    // Retrieve the header.
+    fread(&_header, sizeof(EABMPHeader), 1, fp);
+          
+    // 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);        
+        unload();
+    
+        // Header incorrect.
+        return false;
+    }
+    
+    // Set the values for later.
+    _dataOffset = offset;
+    setWidth(info().width);
+    setHeight(info().height);
+      
+    // 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)
+    {
+        fclose(fp);
+        
+        return false;
+    }
+
+    // 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 std::string& path)
+{
+    if (path.empty() == true)
+    {
+        // Reset all of the state.
+        unload();
+
+        // Invalid path passed in.
+        return false;
+    }
+
+    if (_loadHeader(path) == false)
+    {
+        // Reset all of the state.
+        unload();
+
+        return false;
+    }
+
+    _path = path;
+       
+    // Image loaded successfully.
+    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 palette.
+    if (_palette != NULL)
+    {
+        //delete[] _palette;
+        _palette = NULL;
+    }
+    
+    // Clear the path.
+    _path.clear();
+
+    // Clear the data offset.
+    _dataOffset = 0;
+    
+    // Set the size to 0.
+    setWidth(0);
+    setHeight(0);
+}
+
+void EAImage::paint(EALCD& lcd)
+{
+    paint(lcd, 0, 0, width(), height());
+}
+
+void EAImage::paint(EALCD& lcd, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
+{
+    if (isValid() == false)
+    {
+        return;
+    }
+
+    // Don't allow draw out of range.
+    if (x + w > width())
+    {
+        return;
+    }
+
+    if (y + h > height())
+    {
+        return;
+    }
+    
+    // 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 = NULL;
+    fp = fopen(_path.c_str(), "r");
+    if (fp == NULL)
+    {
+        return;
+    }
+           
+    // Skip the header and size.
+    _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); 
+                            
+    // Now read the data.
+    while (!_feof(fp))
+    {    
+        wordOffset = _xWordOffset(x);
+               
+        int sizeRead = _fread(&buffer, 4, wordsInRow, fp);
+        
+        for (int i = 0; i < sizeRead; 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;
+            
+            // When written all required pixels exit.
+            if (xD == w)
+            {
+                xD = 0;
+                break;
+            }            
+        }
+
+        // Keep count of curernt y value.
+        yD++;
+        if (yD == h)
+        {
+            break;
+        }            
+    }
+    
+    // Close the file and release handle.
+    fclose(fp);
+}
+
+