Shows how to use a display and the onboard SD Card. Requires a display module with an adapter

Dependencies:   DmTftLibrary SDFileSystem mbed

DmDrawBmpBase.cpp

Committer:
displaymodule
Date:
2014-09-01
Revision:
6:592bba211e38
Parent:
5:ed6a302e0917

File content as of revision 6:592bba211e38:

/**********************************************************************************************
 Copyright (c) 2014 DisplayModule. All rights reserved.

 Redistribution and use of this source code, part of this source code or any compiled binary
 based on this source code is permitted as long as the above copyright notice and following
 disclaimer is retained.

 DISCLAIMER:
 THIS SOFTWARE IS SUPPLIED "AS IS" WITHOUT ANY WARRANTIES AND SUPPORT. DISPLAYMODULE ASSUMES
 NO RESPONSIBILITY OR LIABILITY FOR THE USE OF THE SOFTWARE.
 ********************************************************************************************/

#include "DmDrawBmpBase.h"

bool DmDrawBmpBase::drawBitmap(DmTftBase& tft, uint16_t x, uint16_t y, readFunc func, uint32_t userData) {
  _readFunc = func;
  _userData = userData;
  _readPos = 0;
  
  if (readBmpHeader()) {
    if (IsValid565Bitmap()) {
      return draw565Bitmap(tft, x, y);
    }
    if (IsValid888Bitmap()) {
      return draw888Bitmap(tft, x, y);
    }  
  }
  return false;
}

bool DmDrawBmpBase::draw888Bitmap(DmTftBase& tft, uint16_t x, uint16_t y) {
  const uint8_t bytesPerPixel = 3;
  uint8_t red, green, blue;
  uint16_t row, column;
  uint16_t bytesPerRow = (bytesPerPixel*_width + 3) & ~3;
  uint8_t buff[20*bytesPerPixel];
  uint8_t buffPos = sizeof(buff);
  uint16_t clipX = _width;
  uint16_t clipY = _height;

  //make sure image fits
  if ((x + clipX) > tft.width()) {
    clipX = tft.width() - x;
  }
  if ((y + clipY) > tft.height()) {
    clipY = tft.height() - y;
  }
    
  tft.select();
  tft.setAddress(x, y, x+clipX-1, y+clipY-1);
  tft.unSelect();
  
  for(row=0; row<_height; row++) {
    _readPos = _bitmapOffset + (_height - 1 -row ) * bytesPerRow;
    buffPos = sizeof(buff);
    
    for(column=0; column<_width; column++) { 
      if (buffPos >= sizeof(buff)) {
        tft.unSelect();
        _readFunc(_userData, buff, _readPos, sizeof(buff));
        _readPos += sizeof(buff);
        tft.select();
        buffPos = 0;
      }
          
      blue = buff[buffPos++];
      green = buff[buffPos++];
      red = buff[buffPos++];

      if (row < clipY && column < clipX) {
        tft.sendData(Convert888to565(red, green, blue));
      }
    }
  }
  tft.unSelect();
  return true;
}

bool DmDrawBmpBase::draw565Bitmap(DmTftBase& tft, uint16_t x, uint16_t y) {
  const uint8_t bytesPerPixel = 2;
  uint8_t buff[30*bytesPerPixel]; // Should be dividable by bytesPerPixel
  uint8_t buffPos = sizeof(buff); 
  uint16_t bytesPerRow = (bytesPerPixel * _width + 3) & ~3; // bytes Per Row including padding to 4 bytes boundary
  uint16_t paddingSize = bytesPerRow - (bytesPerPixel * _width); // paddingSize for each row
  uint16_t height = -_height; // Change if load bottom-top
  uint16_t pixel;
  uint16_t row, column;
  uint16_t clipX = _width;
  uint16_t clipY = height;
  _readPos = _bitmapOffset;
  
  //_imageFile.seek(_bitmapOffset);
    
  //make sure image fits
  if ((x + clipX) > tft.width()) {
    clipX = tft.width() - x;
  }
  if ((y + clipY) > tft.height()) {
    clipY = tft.height() - y;
  }
    
  tft.select();
  tft.setAddress(x, y, x+clipX-1, y+clipY-1);
  tft.unSelect();

  for(row=0; row<height; row++) {
    for(column=0; column<_width; column++) {
      if (buffPos >= sizeof(buff)) {
        tft.unSelect();
        _readFunc(_userData, buff, _readPos, sizeof(buff));
        _readPos += sizeof(buff);
        //_imageFile.read(buff, sizeof(buff));
        tft.select();
        buffPos = 0;
      }
      pixel = buff[buffPos++] & 0xFF;
      pixel |= buff[buffPos++] << 8;
      if (row < clipY && column < clipX) {
        tft.sendData(pixel);
      }
    }

    if ( paddingSize > 0 ) { // Check if there is padding in the file     
      if ((sizeof(buff) - buffPos) >= paddingSize) { // Most common case, the padding is in the buffer 
        buffPos += paddingSize;
      }
      else { // Padding is not in the buffer, we have to load the buffer from file       
        tft.unSelect();
        _readFunc(_userData, buff, _readPos, sizeof(buff));
        _readPos += sizeof(buff);
//        _imageFile.read(buff, sizeof(buff));
        tft.select();
        buffPos = paddingSize-(sizeof(buff) - buffPos); // paddingSize (0-3) spaceLeftInBuffer (0-3)  where spaceLeftInBuffer < paddingSize
      }
    }
  }
  tft.unSelect();
  
  return true;
}

void DmDrawBmpBase::printBmpHeaderInfo() {
  printf("Image size:         %d\n", _fileSize);
  printf("Image offset:       %d\n", _bitmapOffset);
  printf("Image size:         %d, %d\n", _width, _height);
  printf("BitsPerPixel:       %d\n",_bitsPerPixel);
  printf("Compression:        %d\n",_compression);
  printf("Is 24-bit bmp:      %d\n", IsValid888Bitmap());  
  printf("Is 16-bit 565 bmp:  %d\n", IsValid565Bitmap());  
  printf("Has 565 color mask: %d\n", Is565ColorMask());  
}

bool DmDrawBmpBase::readBmpHeader() {
  if (read16() !=0x4D42){ // read magic byte
    return false;
  }

  _fileSize = read32();
  read32(); // Value depends on application which created the image 
  _bitmapOffset = read32();

  // read DIB header
  _headerSize = read32();
  _width = readInt32();
  _height = readInt32();

  if (read16() != 1) { // number of color planes must be 1
    return false;
  }
  
  _bitsPerPixel = read16();
  _compression = read32();
  
  if (_bitmapOffset == 66 || _bitmapOffset == 70) { // V3 or v2 format
    //setPosition(54);
    _readPos = 54;
    _redMask = read32();
    _greenMask = read32();
    _blueMask = read32();
  }
  else {
    _redMask = 0x00;
    _greenMask = 0x00;
    _blueMask = 0x00;
  }
  
  if (!IsValid888Bitmap() && !IsValid565Bitmap())
  {
    return false;
  }
  
  return true;
}

// In this context a valid bitmap
// - Stored bottom to top
// - 24-bit file
// - No compression
bool DmDrawBmpBase::IsValid888Bitmap() {
  if (_height > 0 && _bitsPerPixel == 24 && _compression == 0)
  {
    return true;
  }
  return false;
}

// In this context a valid bitmap
// - Stored top to bottom
// - 16-bit file
// - Compression 3 (BI_BITFIELDS)
// - Have a 565 Colormask
bool DmDrawBmpBase::IsValid565Bitmap() {
  if (_height < 0 && _bitsPerPixel == 16 && _compression == 3 && Is565ColorMask())
  {
    return true;
  }
  return false;
}

bool DmDrawBmpBase::Is565ColorMask() {
  if (_redMask == 0xF800 && _greenMask == 0x7E0 && _blueMask == 0x1F)
  {
    return true;
  }
  return false;
}

int32_t DmDrawBmpBase::readInt32() {
  int32_t d;
  uint16_t b;

  b = read16();
  d = read16();
  d <<= 16;
  d |= b;
  return d;
}  

uint32_t DmDrawBmpBase::read32() {  
  uint32_t d;
  uint16_t b;

  b = read16();
  d = read16();
  d <<= 16;
  d |= b;
  return d;
}

uint16_t DmDrawBmpBase::read16() {
  //uint16_t d;
  //uint8_t b;
  uint8_t buff[2];
  //b = _imageFile.read();
  //d = _imageFile.read();
  _readFunc(_userData, buff, _readPos, 2);
  _readPos+=2;
  //d <<= 8;
  //d |= b;
  //return d;
  return (buff[1] << 8) | buff[0];
}

// http://stackoverflow.com/questions/2442576/how-does-one-convert-16-bit-rgb565-to-24-bit-rgb888
uint16_t DmDrawBmpBase::Convert888to565(uint8_t red, uint8_t green, uint8_t blue){
  return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
}