2015-08-12 10:53:41 +00:00
/**
* Class definition for a MicroBitImage .
*
* An MicroBitImage is a simple bitmap representation of an image .
* n . b . This is a mutable , managed type .
*/
# include "MicroBit.h"
2015-10-25 17:03:14 +00:00
2015-08-12 10:53:41 +00:00
/*
* The null image . We actally create a small one byte buffer here , just to keep NULL pointers out of the equation .
*/
2015-10-27 15:02:42 +00:00
static const uint16_t empty [ ] __attribute__ ( ( aligned ( 4 ) ) ) = { 0xffff , 1 , 1 , 0 , } ;
2015-10-25 17:03:14 +00:00
MicroBitImage MicroBitImage : : EmptyImage ( ( ImageData * ) ( void * ) empty ) ;
2015-08-12 10:53:41 +00:00
/**
* Default Constructor .
* Creates a new reference to the empty MicroBitImage bitmap
*
* Example :
* @ code
* MicroBitImage i ( ) ; //an empty image
* @ endcode
*/
MicroBitImage : : MicroBitImage ( )
{
// Create new reference to the EmptyImage and we're done.
init_empty ( ) ;
}
/**
* Constructor .
* Create a blank bitmap representation of a given size .
*
* @ 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 ( const int16_t x , const int16_t y )
{
this - > init ( x , y , NULL ) ;
}
/**
* Copy Constructor .
* Add ourselves as a reference to an existing MicroBitImage .
*
* @ param image The MicroBitImage to reference .
*
* Example :
* @ code
* MicroBitImage i ( " 0,1,0,1,0 \n " ) ;
* MicroBitImage i2 ( i ) ; //points to i
* @ endcode
*/
MicroBitImage : : MicroBitImage ( const MicroBitImage & image )
{
2015-10-25 03:58:03 +00:00
ptr = image . ptr ;
ptr - > incr ( ) ;
2015-08-12 10:53:41 +00:00
}
/**
* Constructor .
* Create a blank bitmap representation of a given size .
*
* @ param s A text based representation of the image given whitespace delimited numeric values .
*
* Example :
* @ code
* MicroBitImage i ( " 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n " ) ; // 5x5 image
* @ endcode
*/
MicroBitImage : : MicroBitImage ( const char * s )
{
int width = 0 ;
int height = 0 ;
int count = 0 ;
int digit = 0 ;
char parseBuf [ 10 ] ;
const char * parseReadPtr ;
char * parseWritePtr ;
uint8_t * bitmapPtr ;
if ( s = = NULL )
{
init_empty ( ) ;
return ;
}
// First pass: Parse the string to determine the geometry of the image.
// We do this from first principles to avoid unecessary load of the strtok() libs etc.
parseReadPtr = s ;
while ( * parseReadPtr )
{
if ( isdigit ( * parseReadPtr ) )
{
// Ignore numbers.
digit = 1 ;
}
else if ( * parseReadPtr = = ' \n ' )
{
if ( digit )
{
count + + ;
digit = 0 ;
}
height + + ;
width = count > width ? count : width ;
count = 0 ;
}
else
{
if ( digit )
{
count + + ;
digit = 0 ;
}
}
parseReadPtr + + ;
}
2015-10-25 03:58:03 +00:00
this - > init ( width , height , NULL ) ;
2015-08-12 10:53:41 +00:00
// Second pass: collect the data.
parseReadPtr = s ;
parseWritePtr = parseBuf ;
2015-10-25 03:58:03 +00:00
bitmapPtr = this - > getBitmap ( ) ;
2015-08-12 10:53:41 +00:00
while ( * parseReadPtr )
{
if ( isdigit ( * parseReadPtr ) )
{
* parseWritePtr = * parseReadPtr ;
parseWritePtr + + ;
}
else
{
* parseWritePtr = 0 ;
if ( parseWritePtr > parseBuf )
{
* bitmapPtr = atoi ( parseBuf ) ;
bitmapPtr + + ;
parseWritePtr = parseBuf ;
}
}
parseReadPtr + + ;
}
}
2015-10-25 17:03:14 +00:00
/**
* Constructor .
* Create an image from a specially prepared constant array , with no copying . Will call ptr - > incr ( ) .
*
2015-10-27 15:02:42 +00:00
* @ param ptr The literal - first two bytes should be 0xff , then width , 0 , height , 0 , and the bitmap . Width and height are 16 bit . The literal has to be 4 - byte aligned .
2015-10-25 17:03:14 +00:00
*
* Example :
* @ code
2015-10-27 15:02:42 +00:00
* static const uint8_t heart [ ] __attribute__ ( ( aligned ( 4 ) ) ) = { 0xff , 0xff , 10 , 0 , 5 , 0 , 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
2015-10-25 17:03:14 +00:00
* MicroBitImage i ( ( ImageData * ) ( void * ) heart ) ;
* @ endcode
*/
MicroBitImage : : MicroBitImage ( ImageData * p )
{
ptr = p ;
ptr - > incr ( ) ;
}
/**
* Get current ptr , do not decr ( ) it , and set the current instance to empty image .
* This is to be used by specialized runtimes which pass ImageData around .
*/
ImageData * MicroBitImage : : leakData ( )
{
ImageData * res = ptr ;
init_empty ( ) ;
return res ;
}
2015-08-12 10:53:41 +00:00
/**
* Constructor .
* Create a bitmap representation of a given size , based on a given buffer .
*
* @ param x the width of the image .
* @ param y the height of the image .
* @ param bitmap a 2 D array representing the image .
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* @ endcode
*/
MicroBitImage : : MicroBitImage ( const int16_t x , const int16_t y , const uint8_t * bitmap )
{
this - > init ( x , y , bitmap ) ;
}
/**
* Destructor .
* Removes buffer resources held by the instance .
*/
MicroBitImage : : ~ MicroBitImage ( )
{
2015-10-25 03:58:03 +00:00
ptr - > decr ( ) ;
2015-08-12 10:53:41 +00:00
}
/**
* Internal constructor which defaults to the EmptyImage instance variable
*/
void MicroBitImage : : init_empty ( )
{
2015-10-25 18:38:00 +00:00
ptr = ( ImageData * ) ( void * ) empty ;
2015-08-12 10:53:41 +00:00
}
/**
* Internal constructor which provides sanity checking and initialises class properties .
*
* @ param x the width of the image
* @ param y the height of the image
* @ param bitmap an array of integers that make up an image .
*/
void MicroBitImage : : init ( const int16_t x , const int16_t y , const uint8_t * bitmap )
{
//sanity check size of image - you cannot have a negative sizes
2015-10-27 15:02:42 +00:00
if ( x < 0 | | y < 0 )
2015-08-12 10:53:41 +00:00
{
init_empty ( ) ;
return ;
}
2015-10-25 03:58:03 +00:00
2015-08-12 10:53:41 +00:00
// Create a copy of the array
2015-10-25 03:58:03 +00:00
ptr = ( ImageData * ) malloc ( 4 + x * y ) ;
2015-10-25 15:30:05 +00:00
ptr - > init ( ) ;
2015-10-25 03:58:03 +00:00
ptr - > width = x ;
ptr - > height = y ;
2015-08-12 10:53:41 +00:00
// 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).
if ( bitmap )
this - > printImage ( x , y , bitmap ) ;
else
this - > clear ( ) ;
}
/**
* Copy assign operation .
*
* Called when one MicroBitImage is assigned the value of another using the ' = ' operator .
* Decrement our reference count and free up the buffer as necessary .
* Then , update our buffer to refer to that of the supplied MicroBitImage ,
* and increase its reference count .
*
* @ param s The MicroBitImage to reference .
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* MicroBitImage i1 ( ) ;
* i1 = 1 ; // i1 now references i
* @ endcode
*/
MicroBitImage & MicroBitImage : : operator = ( const MicroBitImage & i )
{
2015-10-25 03:58:03 +00:00
if ( ptr = = i . ptr )
2015-08-12 10:53:41 +00:00
return * this ;
2015-10-25 03:58:03 +00:00
ptr - > decr ( ) ;
ptr = i . ptr ;
ptr - > incr ( ) ;
2015-08-12 10:53:41 +00:00
return * this ;
}
/**
* Equality operation .
*
* Called when one MicroBitImage is tested to be equal to another using the ' = = ' operator .
*
* @ param i The MicroBitImage to test ourselves against .
* @ return true if this MicroBitImage is identical to the one supplied , false otherwise .
*
* Example :
* @ code
* MicroBitImage i ( ) ;
* MicroBitImage i1 ( ) ;
*
* if ( i = = i1 ) //will be true
* print ( " true " ) ;
* @ endcode
*/
bool MicroBitImage : : operator = = ( const MicroBitImage & i )
{
2015-10-25 03:58:03 +00:00
if ( ptr = = i . ptr )
2015-08-12 10:53:41 +00:00
return true ;
else
2015-10-25 15:30:05 +00:00
return ( ptr - > width = = i . ptr - > width & & ptr - > height = = i . ptr - > height & & ( memcmp ( getBitmap ( ) , i . ptr - > data , getSize ( ) ) = = 0 ) ) ;
2015-08-12 10:53:41 +00:00
}
/**
* Clears all pixels in this image
*
* Example :
* @ code
* MicroBitImage i ( " 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n " ) ; // 5x5 image
* i . clear ( ) ;
* @ endcode
*/
void MicroBitImage : : clear ( )
{
2015-10-25 03:58:03 +00:00
memclr ( getBitmap ( ) , getSize ( ) ) ;
2015-08-12 10:53:41 +00:00
}
/**
* Sets the pixel at the given co - ordinates to a given value .
* @ param x The co - ordinate of the pixel to change w . r . t . top left origin .
* @ param y The co - ordinate of the pixel to change w . r . t . top left origin .
* @ param value The new value of the pixel ( the brightness level 0 - 255 )
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* MicroBitImage i ( " 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n " ) ; // 5x5 image
* i . setPixelValue ( 0 , 0 , 255 ) ;
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : setPixelValue ( int16_t x , int16_t y , uint8_t value )
2015-08-12 10:53:41 +00:00
{
//sanity check
2015-10-25 03:58:03 +00:00
if ( x > = getWidth ( ) | | y > = getHeight ( ) | | x < 0 | | y < 0 )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
this - > getBitmap ( ) [ y * getWidth ( ) + x ] = value ;
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Determines the value of a given pixel .
2015-10-25 21:51:33 +00:00
*
* @ param x The x co - ordinate of the pixel to read . Must be within the dimensions of the image .
* @ param y The y co - ordinate of the pixel to read . Must be within the dimensions of the image .
* @ return The value assigned to the given pixel location ( the brightness level 0 - 255 ) , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* MicroBitImage i ( " 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n 1,0,1,0,1 \n 0,1,0,1,0 \n " ) ; // 5x5 image
* i . getPixelValue ( 0 , 0 ) ; //should be 0;
* @ endcode
*/
int MicroBitImage : : getPixelValue ( int16_t x , int16_t y )
{
//sanity check
2015-10-25 03:58:03 +00:00
if ( x > = getWidth ( ) | | y > = getHeight ( ) | | x < 0 | | y < 0 )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
return this - > getBitmap ( ) [ y * getWidth ( ) + x ] ;
2015-08-12 10:53:41 +00:00
}
/**
* Replaces the content of this image with that of a given
* 2 D array representing the image .
* Origin is in the top left corner of the image .
*
2015-10-25 21:51:33 +00:00
* @ param x the width of the image . Must be within the dimensions of the image .
* @ param y the width of the image . Must be within the dimensions of the image .
2015-08-12 10:53:41 +00:00
* @ param bitmap a 2 D array representing the image .
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK on success , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( ) ;
* i . printImage ( 0 , 0 , heart ) ;
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : printImage ( int16_t width , int16_t height , const uint8_t * bitmap )
2015-08-12 10:53:41 +00:00
{
const uint8_t * pIn ;
uint8_t * pOut ;
int pixelsToCopyX , pixelsToCopyY ;
// Sanity check.
if ( width < = 0 | | width < = 0 | | bitmap = = NULL )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
// Calcualte sane start pointer.
2015-10-25 03:58:03 +00:00
pixelsToCopyX = min ( width , this - > getWidth ( ) ) ;
pixelsToCopyY = min ( height , this - > getHeight ( ) ) ;
2015-08-12 10:53:41 +00:00
pIn = bitmap ;
2015-10-25 03:58:03 +00:00
pOut = this - > getBitmap ( ) ;
2015-08-12 10:53:41 +00:00
// Copy the image, stride by stride.
for ( int i = 0 ; i < pixelsToCopyY ; i + + )
{
memcpy ( pOut , pIn , pixelsToCopyX ) ;
pIn + = width ;
2015-10-25 03:58:03 +00:00
pOut + = this - > getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Pastes a given bitmap at the given co - ordinates .
* Any pixels in the relvant area of this image are replaced .
*
* @ param image The MicroBitImage to paste .
* @ param x The leftmost X co - ordinate in this image where the given image should be pasted .
* @ 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 .
* @ return The number of pixels written .
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ; //if you show this image - you will see a big heart
* i . paste ( - 5 , 0 , i ) ; //displays a small heart :)
* @ endcode
*/
int MicroBitImage : : paste ( const MicroBitImage & image , int16_t x , int16_t y , uint8_t alpha )
{
uint8_t * pIn , * pOut ;
int cx , cy ;
int pxWritten = 0 ;
// Sanity check.
// We permit writes that overlap us, but ones that are clearly out of scope we can filter early.
2015-10-25 03:58:03 +00:00
if ( x > = getWidth ( ) | | y > = getHeight ( ) | | x + image . getWidth ( ) < = 0 | | y + image . getHeight ( ) < = 0 )
2015-08-12 10:53:41 +00:00
return 0 ;
//Calculate the number of byte we need to copy in each dimension.
2015-10-25 03:58:03 +00:00
cx = x < 0 ? min ( image . getWidth ( ) + x , getWidth ( ) ) : min ( image . getWidth ( ) , getWidth ( ) - x ) ;
cy = y < 0 ? min ( image . getHeight ( ) + y , getHeight ( ) ) : min ( image . getHeight ( ) , getHeight ( ) - y ) ;
2015-08-12 10:53:41 +00:00
2015-10-25 21:51:33 +00:00
// Calculate sane start pointer.
2015-10-25 03:58:03 +00:00
pIn = image . ptr - > data ;
2015-08-12 10:53:41 +00:00
pIn + = ( x < 0 ) ? - x : 0 ;
2015-10-25 03:58:03 +00:00
pIn + = ( y < 0 ) ? - image . getWidth ( ) * y : 0 ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
pOut = getBitmap ( ) ;
2015-08-12 10:53:41 +00:00
pOut + = ( x > 0 ) ? x : 0 ;
2015-10-25 03:58:03 +00:00
pOut + = ( y > 0 ) ? getWidth ( ) * y : 0 ;
2015-08-12 10:53:41 +00:00
// 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 ) ;
pxWritten + + ;
}
}
2015-10-25 03:58:03 +00:00
pIn + = image . getWidth ( ) ;
pOut + = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
}
else
{
for ( int i = 0 ; i < cy ; i + + )
{
memcpy ( pOut , pIn , cx ) ;
pxWritten + = cx ;
2015-10-25 03:58:03 +00:00
pIn + = image . getWidth ( ) ;
pOut + = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
}
return pxWritten ;
}
/**
* Prints a character to the display at the given location
*
* @ param c The character to display .
* @ param x The x co - ordinate of on the image to place the top left of the character
* @ param y The y co - ordinate of on the image to place the top left of the character
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK on success , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* MicroBitImage i ( 5 , 5 ) ;
* i . print ( ' a ' , 0 , 0 ) ;
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : print ( char c , int16_t x , int16_t y )
2015-08-12 10:53:41 +00:00
{
unsigned char v ;
int x1 , y1 ;
MicroBitFont font = uBit . display . getFont ( ) ;
// Sanity check. Silently ignore anything out of bounds.
2015-10-25 03:58:03 +00:00
if ( x > = getWidth ( ) | | y > = getHeight ( ) | | c < MICROBIT_FONT_ASCII_START | | c > font . asciiEnd )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
// Paste.
int offset = ( c - MICROBIT_FONT_ASCII_START ) * 5 ;
for ( int row = 0 ; row < MICROBIT_FONT_HEIGHT ; row + + )
{
v = ( char ) * ( font . characters + offset ) ;
offset + + ;
// Update our Y co-ord write position
y1 = y + row ;
for ( int col = 0 ; col < MICROBIT_FONT_WIDTH ; col + + )
{
// Update our X co-ord write position
x1 = x + col ;
2015-10-25 03:58:03 +00:00
if ( x1 < getWidth ( ) & & y1 < getHeight ( ) )
this - > getBitmap ( ) [ y1 * getWidth ( ) + x1 ] = ( v & ( 0x10 > > col ) ) ? 255 : 0 ;
2015-08-12 10:53:41 +00:00
}
}
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Shifts the pixels in this Image a given number of pixels to the Left .
*
* @ param n The number of pixels to shift .
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK on success , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ; //if you show this image - you will see a big heart
* i . shiftLeft ( 5 ) ; //displays a small heart :)
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : shiftLeft ( int16_t n )
2015-08-12 10:53:41 +00:00
{
2015-10-25 03:58:03 +00:00
uint8_t * p = getBitmap ( ) ;
int pixels = getWidth ( ) - n ;
2015-08-12 10:53:41 +00:00
if ( n < = 0 )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
if ( n > = getWidth ( ) )
2015-08-12 10:53:41 +00:00
{
clear ( ) ;
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 03:58:03 +00:00
for ( int y = 0 ; y < getHeight ( ) ; y + + )
2015-08-12 10:53:41 +00:00
{
// Copy, and blank fill the rightmost column.
memcpy ( p , p + n , pixels ) ;
memclr ( p + pixels , n ) ;
2015-10-25 03:58:03 +00:00
p + = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Shifts the pixels in this Image a given number of pixels to the Right .
*
* @ param n The number of pixels to shift .
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK on success , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* i . shiftLeft ( 5 ) ; //displays a small heart :)
* i . shiftRight ( 5 ) ; //displays a big heart :)
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : shiftRight ( int16_t n )
2015-08-12 10:53:41 +00:00
{
2015-10-25 03:58:03 +00:00
uint8_t * p = getBitmap ( ) ;
int pixels = getWidth ( ) - n ;
2015-08-12 10:53:41 +00:00
if ( n < = 0 )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
if ( n > = getWidth ( ) )
2015-08-12 10:53:41 +00:00
{
clear ( ) ;
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 03:58:03 +00:00
for ( int y = 0 ; y < getHeight ( ) ; y + + )
2015-08-12 10:53:41 +00:00
{
// Copy, and blank fill the leftmost column.
memmove ( p + n , p , pixels ) ;
memclr ( p , n ) ;
2015-10-25 03:58:03 +00:00
p + = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Shifts the pixels in this Image a given number of pixels to Upward .
*
* @ param n The number of pixels to shift .
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK on success , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* i . shiftUp ( 1 ) ;
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : shiftUp ( int16_t n )
2015-08-12 10:53:41 +00:00
{
uint8_t * pOut , * pIn ;
if ( n < = 0 )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
if ( n > = getHeight ( ) )
2015-08-12 10:53:41 +00:00
{
clear ( ) ;
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 03:58:03 +00:00
pOut = getBitmap ( ) ;
pIn = getBitmap ( ) + getWidth ( ) * n ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
for ( int y = 0 ; y < getHeight ( ) ; y + + )
2015-08-12 10:53:41 +00:00
{
// Copy, and blank fill the leftmost column.
2015-10-25 03:58:03 +00:00
if ( y < getHeight ( ) - n )
memcpy ( pOut , pIn , getWidth ( ) ) ;
2015-08-12 10:53:41 +00:00
else
2015-10-25 03:58:03 +00:00
memclr ( pOut , getWidth ( ) ) ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
pIn + = getWidth ( ) ;
pOut + = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Shifts the pixels in this Image a given number of pixels to Downward .
*
* @ param n The number of pixels to shift .
2015-10-25 21:51:33 +00:00
* @ return MICROBIT_OK on success , or MICROBIT_INVALID_PARAMETER .
2015-08-12 10:53:41 +00:00
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* i . shiftDown ( 1 ) ;
* @ endcode
*/
2015-10-25 21:51:33 +00:00
int MicroBitImage : : shiftDown ( int16_t n )
2015-08-12 10:53:41 +00:00
{
uint8_t * pOut , * pIn ;
if ( n < = 0 )
2015-10-25 21:51:33 +00:00
return MICROBIT_INVALID_PARAMETER ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
if ( n > = getHeight ( ) )
2015-08-12 10:53:41 +00:00
{
clear ( ) ;
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 03:58:03 +00:00
pOut = getBitmap ( ) + getWidth ( ) * ( getHeight ( ) - 1 ) ;
pIn = pOut - getWidth ( ) * n ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
for ( int y = 0 ; y < getHeight ( ) ; y + + )
2015-08-12 10:53:41 +00:00
{
// Copy, and blank fill the leftmost column.
2015-10-25 03:58:03 +00:00
if ( y < getHeight ( ) - n )
memcpy ( pOut , pIn , getWidth ( ) ) ;
2015-08-12 10:53:41 +00:00
else
2015-10-25 03:58:03 +00:00
memclr ( pOut , getWidth ( ) ) ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
pIn - = getWidth ( ) ;
pOut - = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
}
2015-10-25 21:51:33 +00:00
return MICROBIT_OK ;
2015-08-12 10:53:41 +00:00
}
/**
* Converts the bitmap to a csv string .
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* uBit . serial . printString ( i . toString ( ) ) ; // "0,1,0,1,0,0,0,0,0,0\n..."
* @ endcode
*/
ManagedString MicroBitImage : : toString ( )
{
//width including commans and \n * height
2015-10-25 03:58:03 +00:00
int stringSize = getSize ( ) * 2 ;
2015-08-12 10:53:41 +00:00
//plus one for string terminator
char parseBuffer [ stringSize + 1 ] ;
parseBuffer [ stringSize ] = ' \0 ' ;
2015-10-25 03:58:03 +00:00
uint8_t * bitmapPtr = getBitmap ( ) ;
2015-08-12 10:53:41 +00:00
int parseIndex = 0 ;
int widthCount = 0 ;
while ( parseIndex < stringSize )
{
if ( * bitmapPtr )
parseBuffer [ parseIndex ] = ' 1 ' ;
else
parseBuffer [ parseIndex ] = ' 0 ' ;
parseIndex + + ;
2015-10-25 03:58:03 +00:00
if ( widthCount = = getWidth ( ) - 1 )
2015-08-12 10:53:41 +00:00
{
parseBuffer [ parseIndex ] = ' \n ' ;
widthCount = 0 ;
}
else
{
parseBuffer [ parseIndex ] = ' , ' ;
widthCount + + ;
}
parseIndex + + ;
bitmapPtr + + ;
}
return ManagedString ( parseBuffer ) ;
}
/**
* Crops the image to the given dimensions
*
* @ param startx the location to start the crop in the x - axis
* @ param starty the location to start the crop in the y - axis
* @ param width the width of the desired cropped region
* @ param height the height of the desired cropped region
*
* Example :
* @ code
* const uint8_t heart [ ] = { 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , } ; // a cute heart
* MicroBitImage i ( 10 , 5 , heart ) ;
* uBit . serial . printString ( i . crop ( 0 , 0 , 2 , 2 ) . toString ( ) ) ; // "0,1\n1,1\n"
* @ endcode
*/
MicroBitImage MicroBitImage : : crop ( int startx , int starty , int cropWidth , int cropHeight )
{
int newWidth = startx + cropWidth ;
int newHeight = starty + cropHeight ;
2015-10-25 03:58:03 +00:00
if ( newWidth > = getWidth ( ) | | newWidth < = 0 )
newWidth = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
2015-10-25 03:58:03 +00:00
if ( newHeight > = getHeight ( ) | | newHeight < = 0 )
newHeight = getHeight ( ) ;
2015-08-12 10:53:41 +00:00
//allocate our storage.
uint8_t cropped [ newWidth * newHeight ] ;
//calculate the pointer to where we want to begin cropping
2015-10-25 03:58:03 +00:00
uint8_t * copyPointer = getBitmap ( ) + ( getWidth ( ) * starty ) + startx ;
2015-08-12 10:53:41 +00:00
//get a reference to our storage
uint8_t * pastePointer = cropped ;
//go through row by row and select our image.
for ( int i = starty ; i < newHeight ; i + + )
{
memcpy ( pastePointer , copyPointer , newWidth ) ;
2015-10-25 03:58:03 +00:00
copyPointer + = getWidth ( ) ;
2015-08-12 10:53:41 +00:00
pastePointer + = newHeight ;
}
return MicroBitImage ( newWidth , newHeight , cropped ) ;
}
2015-10-25 17:03:14 +00:00
/**
* Check if image is read - only ( i . e . , residing in flash ) .
*/
bool MicroBitImage : : isReadOnly ( )
{
return ptr - > isReadOnly ( ) ;
}
/**