Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
widgets/EAImage.cpp
- Committer:
- richardparker
- Date:
- 2010-05-06
- Revision:
- 6:4fe6f365cbeb
- Parent:
- 3:24fbf4dbd7e5
File content as of revision 6:4fe6f365cbeb:
// 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), _cached(false), _cache(NULL), _size(0L), _pos(0L) { // Start from known state. unload(); } EAImage::~EAImage() { unload(); } int EAImage::_feof(FILE* stream) { if (isCached() == true) { 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; } // 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) { // 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 unless cached. if ((fp == NULL) && (isCached() == false)) { 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) { // Invalid path passed in. return false; } // Try and open the file, check type and load width and height. FILE* fp = fopen(path, "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 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); 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; } int pathLen = strlen(path); // If already loaded an image then clear to load the new one. if (_path != NULL) { delete[] _path; _path = NULL; } // Now allocate enough space to hold path. Note +1 for null character. _path = new char[pathLen+1]; // Now copy over passed in path to path variable. strcpy(_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 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); // Empty the cache. if (_cache != NULL) { delete[] _cache; _cache = NULL; } _size = 0L; _pos = 0L; } 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; 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; } // 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; } } // Clear the palette. if (_palette != NULL) { delete[] _palette; _palette = NULL; } if (isCached() == false) { // Close the file and release handle. fclose(fp); } }