Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions workspace/__ardulib__/Graphics/BitmapReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@

#ifndef BITMAP_READER_H
#define BITMAP_READER_H

#include <SD.h>
#include "XGraphics.h"

enum BitmapReturnCode { // It is invisible to the enduser but very useful for debugging.
BITMAP_SUCCESS,
BITMAP_ERROR_FILE_OPEN,
BITMAP_ERROR_WRONG_FILE_FORMAT,
BITMAP_ERROR_WRONG_BMP
};

struct BitmapHeader {
BitmapHeader()
: bfOffBits(0)
, biSize(0)
, biWidth(0)
, biHeight(0)
, isFlipped(false)
, biPlanes(0)
, biBitCount(0)
, biCompression(0) {}

uint32_t bfOffBits; // The offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found.
uint8_t biSize; // The size of the header.
int16_t biWidth; // The bitmap width in pixels.
int16_t biHeight; // The bitmap height in pixels. Can be < 0.
bool isFlipped; // A BMP is stored bottom-to-top.
uint8_t biPlanes; // The number of color planes, must be 1. Other values used for WIN icons.
uint8_t biBitCount; // the number of bits per pixel, which is the color depth of the image. Typical values are 1, 4, 8, 16, 24 and 32.
uint8_t biCompression; // the compression method being used. Typical values are [0,6].
};

class BitmapReader {
public:
~BitmapReader();

void linkSDDevice(SDClass* sd);

BitmapReturnCode readBitmap(char* bitmapFSPath);
BitmapReturnCode fillScanlineBuffer(char* bitmapFSPath, int16_t scanline, BBox imageBBox, uint16_t* buffer, size_t bufferSize);

private:
SDClass* _sd;
File _file;

BitmapHeader _bitmapHeader;

uint16_t _readLE16();
uint32_t _readLE32();
};

BitmapReader::~BitmapReader() {
if (_file)
_file.close();
}

void BitmapReader::linkSDDevice(SDClass* sd) {
_sd = sd;
}

BitmapReturnCode BitmapReader::readBitmap(char* bitmapFSPath) {

if (!(_file = _sd->open(bitmapFSPath, O_READ))) { // Open a new BMP file.
_file.close();
return BITMAP_ERROR_FILE_OPEN;
}

if (_readLE16() != 0x4D42) { // Check BMP signature.
_file.close();
return BITMAP_ERROR_WRONG_FILE_FORMAT;
}

_readLE32(); // Skip reading bfSize.
_readLE32(); // Skip reading bfReserved.

_bitmapHeader.bfOffBits = _readLE32(); // Read bfOffBits.
_bitmapHeader.biSize = _readLE32(); // Read biSize/bV4Size/bV5Size.

_bitmapHeader.biWidth = _readLE32(); // Read biWidth/bV4Width/bV5Width.
_bitmapHeader.biHeight = _readLE32(); // Read biHeight/bV4Height/bV5Height.
if (_bitmapHeader.biHeight < 0) {
_bitmapHeader.biHeight = -_bitmapHeader.biHeight;
_bitmapHeader.isFlipped = true;
}

_bitmapHeader.biPlanes = _readLE16(); // Read biPlanes/bV4Planes/bV5Planes.
_bitmapHeader.biBitCount = _readLE16(); // Read biBitCount/bV4BitCount/bV5BitCount.
_bitmapHeader.biCompression = _readLE32(); // Read biCompression/bV4V4Compression/bV5Compression.

_file.close();

// Check for only straightforward case.
if (_bitmapHeader.biSize != 40 || _bitmapHeader.biPlanes != 1 || _bitmapHeader.biBitCount != 24 || _bitmapHeader.biCompression != 0) {
_bitmapHeader = BitmapHeader(); // Reset bitmap.
return BITMAP_ERROR_WRONG_BMP;
}

return BITMAP_SUCCESS;
}

BitmapReturnCode BitmapReader::fillScanlineBuffer(char* bitmapFSPath, int16_t scanline, BBox imageBBox, uint16_t* buffer, size_t bufferSize) {
if (!(_file = _sd->open(bitmapFSPath, O_READ))) {
_file.close();
return BITMAP_ERROR_FILE_OPEN;
}

uint16_t imageLine = scanline - imageBBox.pivot.y; // Calculate current line of the image.

if (imageLine > _bitmapHeader.biHeight - 1) // Tile vert. If more than one BMP in the image.
imageLine = imageLine - _bitmapHeader.biHeight * (imageLine / _bitmapHeader.biHeight);

uint16_t bitmapArrayLine = _bitmapHeader.isFlipped ? imageLine : _bitmapHeader.biHeight - imageLine - 1; // Calculate current Bitmap line.

uint8_t emptyBytesCount = _bitmapHeader.biWidth % 4; // The amount of bytes at the BMP scanline must be a multiple of 4 bytes.
uint32_t bitmapLineStart = _bitmapHeader.bfOffBits + bitmapArrayLine * 3 * _bitmapHeader.biWidth + emptyBytesCount * bitmapArrayLine; // The number of starting byte of the line.

for (int16_t x = imageBBox.pivot.x, c = 0; x < imageBBox.pivot.x + imageBBox.width; x++, c++) {

if ((c == 0) || (c % (_bitmapHeader.biWidth - 1) == 0)) // Tile horizontally. If we are at frame.
if (!(_file.seek(bitmapLineStart))) // Return to start.
continue; // If for some case miss the byte, do not read it.

uint16_t color = ((_file.read() >> 3) | ((_file.read() & 0xFC) << 3) | ((_file.read() & 0xF8) << 8)); // in BMP a pixel color is hold as BGR888.
if (x >= 0 && x < bufferSize)
buffer[x] = color;
}

_file.close();
return BITMAP_SUCCESS;
}

uint16_t BitmapReader::_readLE16() {
#if !defined(ESP32) && !defined(ESP8266) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
// Read directly into result. BMP data and variable both little-endian.
uint16_t result;
_file.read(&result, sizeof result);
return result;
#else
// Big-endian or unknown. Byte-by-byte read will perform reversal if needed.
return _file.read() | ((uint16_t)_file.read() << 8);
#endif
}

uint32_t BitmapReader::_readLE32() {
#if !defined(ESP32) && !defined(ESP8266) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
// Read directly into result. BMP data and variable both little-endian.
uint32_t result;
_file.read(&result, sizeof result);
return result;
#else
// Big-endian or unknown. Byte-by-byte read will perform reversal if needed.
return _file.read() | ((uint16_t)_file.read() << 8) | ((uint16_t)_file.read() << 16) | ((uint16_t)_file.read() << 24);
#endif
}

#endif // BITMAP_READER_H
26 changes: 26 additions & 0 deletions workspace/__ardulib__/Graphics/ImageSD.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

#ifndef IMAGE_SD_H
#define IMAGE_SD_H

#include "BitmapReader.h"
#include "XGraphics.h"

class ImageSD : public XGraphics {
private:
BitmapReader _bitmapReader;
BBox _imageBBox;

char* _bitmapFSPath;

public:
ImageSD(XGraphics* parent, SDClass* sd);

bool linkBitmapFSPath(char* bitmapFSPath);

void setImagePosition(int16_t x, int16_t y, int16_t w, int16_t h);
void renderScanline(XRenderer* renderer, int16_t scanline, uint16_t* buffer, size_t bufferSize);
};

#include "ImageSD.inl"

#endif // IMAGE_SD_H
25 changes: 25 additions & 0 deletions workspace/__ardulib__/Graphics/ImageSD.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

ImageSD::ImageSD(XGraphics* parent, SDClass* sd)
: XGraphics(parent) {
_bitmapReader.linkSDDevice(sd);
}

bool ImageSD::linkBitmapFSPath(char* bitmapFSPath) {
_bitmapFSPath = bitmapFSPath;
BitmapReturnCode res = _bitmapReader.readBitmap(_bitmapFSPath);
return res == BITMAP_SUCCESS ? 0 : 1;
}

void ImageSD::setImagePosition(int16_t x, int16_t y, int16_t w, int16_t h) {
_imageBBox.pivot = XVector2<int16_t>(x, y);
_imageBBox.width = w;
_imageBBox.height = h;
}

void ImageSD::renderScanline(XRenderer* renderer, int16_t scanline, uint16_t* buffer, size_t bufferSize) {

if (scanline < _imageBBox.pivot.y || scanline > _imageBBox.pivot.y + _imageBBox.height - 1)
return;

_bitmapReader.fillScanlineBuffer(_bitmapFSPath, scanline, _imageBBox, buffer, bufferSize);
}
9 changes: 9 additions & 0 deletions workspace/__lib__/xod-dev/sd/project.xod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"authors": [
"XOD"
],
"description": "Nodes to work with SDcards",
"license": "AGPL-3.0",
"name": "sd",
"version": "0.33.0"
}
31 changes: 31 additions & 0 deletions workspace/__lib__/xod-dev/sd/sd-device/patch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

{{#global}}
#include <SPI.h>
#include <SD.h>
{{/global}}

struct State {
uint8_t mem[sizeof(SDClass)];
SDClass* sd;
};

using Type = SDClass*;

{{ GENERATED_CODE }}

void evaluate(Context ctx) {

auto state = getState(ctx);

if (isSettingUp()) {
state->sd = new (state->mem) SDClass();
}

auto csPin = getValue<input_CS>(ctx);

if (!state->sd->begin(csPin)) {
raiseError(ctx);
return;
}
emitValue<output_DEV>(ctx, state->sd);
}
36 changes: 36 additions & 0 deletions workspace/__lib__/xod-dev/sd/sd-device/patch.xodp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"description": "Constructs an SDcard device.",
"nodes": [
{
"description": "CS (chip select) pin the SD card reader is connected to. Also known as SS (slave select).",
"id": "ByHVa_XtL",
"label": "CS",
"position": {
"units": "slots",
"x": -12,
"y": -2
},
"type": "xod/patch-nodes/input-port"
},
{
"id": "HklSN6uQYU",
"position": {
"units": "slots",
"x": -12,
"y": -1
},
"type": "xod/patch-nodes/not-implemented-in-xod"
},
{
"description": "SDcard device.",
"id": "Sy6VT_7tL",
"label": "DEV",
"position": {
"units": "slots",
"x": -12,
"y": 0
},
"type": "xod/patch-nodes/output-self"
}
]
}
Loading