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.
Fork of MicroBitDALImageRewrite by
Diff: MicroBitImage.cpp
- Revision:
- 5:8bf639bbedb5
- Parent:
- 1:3e0360107f98
--- a/MicroBitImage.cpp Fri May 15 22:23:17 2015 +0000 +++ b/MicroBitImage.cpp Sat May 16 22:28:56 2015 +0000 @@ -30,11 +30,32 @@ * * @param x the width of the image. * @param y the height of the image. + * + * Bitmap buffer is linear, with 8 bits per pixel, row by row, + * top to bottom with no word alignment. Stride is therefore the image width in pixels. + * in where w and h are width and height respectively, the layout is therefore: + * + * |[0,0]...[w,o][1,0]...[w,1] ... [[w,h] + * + * A copy of the image is made in RAM, as images are mutable. + * + * TODO: Consider an immutable flavour, which might save us RAM for animation spritesheets... + * ...as these could be kept in FLASH. */ MicroBitImage::MicroBitImage(int x, int y) { this->init(x,y,NULL); +} +/** + * Copy Constructor. + * Clone an existing MicroBitImage. + * + * @param image The MicroBitImage to clone. + */ +MicroBitImage::MicroBitImage(MicroBitImage &image) +{ + this->init(image.getWidth(),image.getHeight(),image.bitmap); } /** @@ -43,37 +64,32 @@ * * @param x the width of the image. * @param y the height of the image. - * @param bitmap a 2D array representing the image. - * + * @param the bitmap buffer to copy. + * */ -MicroBitImage::MicroBitImage(int x, int y, int **bitmap) +MicroBitImage::MicroBitImage(int x, int y, uint8_t *bitmap) { this->init(x,y,bitmap); } MicroBitImage::~MicroBitImage() { - for (int i = 0; i < height; i++) - delete[] this->bitmap[i]; - delete[] this->bitmap; } -void MicroBitImage::init(int x, int y, int **bitmap) +void MicroBitImage::init(int x, int y, uint8_t *bitmap) { // Create a copy of the array this->width = x; this->height = y; - // create a jagged array to represent the image. We use a jagged rather than 2D array here to - // ease type checking across the myriad of languages that might target us. - this->bitmap = new int*[width]; - - for (int i = 0; i < width; i++) - this->bitmap[i] = new int[height]; + // create a linear buffer to represent the image. We could use a jagged/2D array here, but experimentation + // showed this had a negative effect on memory management (heap fragmentation etc). + + this->bitmap = new uint8_t[width*height]; if (bitmap) - this->printImage(0,0,bitmap); + this->printImage(x,y,bitmap); else this->clear(); } @@ -85,9 +101,7 @@ */ void MicroBitImage::clear() { - for (int x = 0; x < width; x++) - for (int y = 0; y < height; y++) - this->bitmap[x][y] = 0; + memclr(this->bitmap, width*height); } @@ -98,35 +112,54 @@ * @param y The co-ordinate of the pixel to change w.r.t. top left origin. * @param value The new value of the pixel. */ -void MicroBitImage::setPixelValue(int x , int y, int value) +void MicroBitImage::setPixelValue(int x , int y, uint8_t value) { - this->bitmap[x][y] = value; + this->bitmap[y*width+x] = value; } /** * Determined the value of a given pixel. * @return The value assigned to the givne pixel location */ -int MicroBitImage::getPixelValue(int x , int y) +uint8_t MicroBitImage::getPixelValue(int x , int y) { - return this->bitmap[x][y]; + return this->bitmap[y*width+x]; } /** * Replaces the content of this image with that of a given - * 2D array representing the image. - * Origin is in the top left corner of the image. + * bitmap representation of an image. + * + * Data is copied. Any out of range data is safely ignored. * * @param x the width of the image. * @param y the height of the image. - * @param bitmap a 2D array representing the image. + * @param linear bitmap representing the image. * */ -void MicroBitImage::printImage(int x, int y, int **bitmap) +void MicroBitImage::printImage(int width, int height, uint8_t *bitmap) { - for (int i = 0; i < min(x,width); i++) - for (int j = 0; j < min(y,height); j++) - this->bitmap[i][j] = bitmap[i][j]; + uint8_t *pIn, *pOut; + int pixelsToCopyX, pixelsToCopyY; + + // Sanity check. + if (width <= 0 || width <= 0) + return; + + // Calcualte sane start pointer. + pixelsToCopyX = min(width,this->width); + pixelsToCopyY = min(height,this->height); + + pIn = bitmap; + pOut = this->bitmap; + + // Copy the image, stride by stride. + for (int i=0; i<pixelsToCopyY; i++) + { + memcpy(pOut, pIn, pixelsToCopyX); + pIn += width; + pOut += this->width; + } } /** @@ -138,17 +171,55 @@ * @param y The uppermost Y co-ordinate in this image where the given image should be pasted. * @param alpha set to 1 if transparency clear pixels in given image should be treated as transparent. Set to 0 otherwise. */ -void MicroBitImage::paste(MicroBitImage *image, int x, int y, int alpha) +void MicroBitImage::paste(MicroBitImage &image, int x, int y, int alpha) { + uint8_t *pIn, *pOut; + int cx, cy; + // Sanity check. - if (x >= width || y >= height) + // We permit writes that overlap us, but ones that are clearly out of scope we can filter early. + if (x >= width || y >= height || x+image.width <= 0 || y+image.height <= 0) return; + + //Calculate the number of byte we need to copy in each dimension. + cx = x < 0 ? min(image.width + x, width) : min(image.width, width - x); + cy = y < 0 ? min(image.height + y, height) : min(image.height, height - y); + + // Calcualte sane start pointer. + pIn = image.bitmap; + pOut = bitmap; + pOut += (x > 0) ? x : 0; + pOut += (y > 0) ? width*y : 0; + + // Copy the image, stride by stride + // If we want primitive transparecy, we do this byte by byte. + // If we don't, use a more efficient block memory copy instead. Every little helps! + + if (alpha) + { + for (int i=0; i<cy; i++) + { + for (int j=0; j<cx; j++) + { + // Copy this byte if appropriate. + if (*(pIn+j) != 0) + *(pOut+j) = *(pIn+j); + } - // Paste. - for (int i = 0; i < min(image->width,width-x); i++) - for (int j = 0; j < min(image->height,height-y); j++) - if (!(alpha && bitmap[i][j] == 0)) - this->bitmap[i+x][j+y] = bitmap[i][j]; + pIn += image.width; + pOut += width; + } + } + else + { + for (int i=0; i<cy; i++) + { + memcpy(pOut, pIn, cx); + + pIn += image.width; + pOut += width; + } + } } /** @@ -160,8 +231,8 @@ */ void MicroBitImage::print(char c, int x, int y) { - unsigned char v; + int x1, y1; // Sanity check. Silently ignore anything out of bounds. if (x >= width || y >= height || c < MICROBIT_FONT_ASCII_START || c > MICROBIT_FONT_ASCII_END) @@ -179,10 +250,17 @@ else offset++; + // Update our Y co-ord write position + y1 = y+row; + for (int col = 0; col < MICROBIT_FONT_WIDTH; col++) - if (x+col < width && y+row < height) - this->bitmap[x+col][y+row] = (v & (0x08 >> col)) ? 1 : 0; - + { + // Update our X co-ord write position + x1 = x+col; + + if (x1 < width && y1 < height) + this->bitmap[y1*width+x1] = (v & (0x08 >> col)) ? 255 : 0; + } } } @@ -194,14 +272,20 @@ */ void MicroBitImage::shiftLeft(int n) { - for (int i = 0; i < width-1 ; i++) - for (int j = 0; j < height; j++) - this->bitmap[i][j] = this->bitmap[i+1][j]; - - // Blank fill the rightmost column. - for (int j = 0; j < height; j++) - this->bitmap[width-1][j] = 0; - + uint8_t *p = bitmap; + int pixels = width-n; + + if (n <= 0 || n > width) + return; + + + for (int y = 0; y < height; y++) + { + // Copy, and blank fill the rightmost column. + memcpy(p, p+n, pixels); + memclr(p+pixels, n); + p += width; + } } /** @@ -211,13 +295,19 @@ */ void MicroBitImage::shiftRight(int n) { - for (int i = 0; i < width-1 ; i++) - for (int j = 0; j < height; j++) - this->bitmap[i+1][j] = this->bitmap[i][j]; + uint8_t *p = bitmap; + int pixels = width-n; + + if (n <= 0 || n > width) + return; - // Blank fill the leftmost column. - for (int j = 0; j < height; j++) - this->bitmap[0][j] = 0; + for (int y = 0; y < height; y++) + { + // Copy, and blank fill the leftmost column. + memcpy(p+n, p, pixels); + memclr(p, n); + p += width; + } } @@ -228,13 +318,25 @@ */ void MicroBitImage::shiftUp(int n) { - for (int i = 0; i < width ; i++) - for (int j = 0; j < height-1; j++) - this->bitmap[i][j] = this->bitmap[i][j+1]; - - // Blank fill the bottommost row. - for (int i = 0; i < width; i++) - this->bitmap[i][height] = 0; + uint8_t *pOut, *pIn; + + if (n <= 0 || n > height) + return; + + pOut = bitmap; + pIn = bitmap+width*n; + + for (int y = 0; y < height; y++) + { + // Copy, and blank fill the leftmost column. + if (y < height-n) + memcpy(pOut, pIn, width); + else + memclr(pOut, width); + + pIn += width; + pOut += width; + } } @@ -245,14 +347,25 @@ */ void MicroBitImage::shiftDown(int n) { - for (int i = 0; i < width ; i++) - for (int j = 0; j < height-1; j++) - this->bitmap[i][j+1] = this->bitmap[i][j]; - - // Blank fill the topmost row. - for (int i = 0; i < height; i++) - this->bitmap[i][0] = 0; - + uint8_t *pOut, *pIn; + + if (n <= 0 || n > height) + return; + + pOut = bitmap + width*(height-1); + pIn = pOut - width*n; + + for (int y = 0; y < height; y++) + { + // Copy, and blank fill the leftmost column. + if (y < height-n) + memcpy(pOut, pIn, width); + else + memclr(pOut, width); + + pIn -= width; + pOut -= width; + } } /** * Gets the width of this image.