/** * Pastes a given bitmap at the given co-ordinates. * * Any pixels in the relevant 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. Defaults to 0. * * @param y The uppermost Y co-ordinate in this image where the given image should be pasted. Defaults to 0. * * @param alpha set to 1 if transparency clear pixels in given image should be treated as transparent. Set to 0 otherwise. Defaults to 0. * * @return The number of pixels written. * * @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); // a big heart * i.paste(i, -5, 0); // 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. if (x >= getWidth() || y >= getHeight() || x+image.getWidth() <= 0 || y+image.getHeight() <= 0) return 0; //Calculate the number of byte we need to copy in each dimension. 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); // Calculate sane start pointer. pIn = image.ptr->data; pIn += (x < 0) ? -x : 0; pIn += (y < 0) ? -image.getWidth()*y : 0; pOut = getBitmap(); pOut += (x > 0) ? x : 0; pOut += (y > 0) ? getWidth()*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); pxWritten++; } } pIn += image.getWidth(); pOut += getWidth(); } } else { for (int i=0; i<cy; i++) { memcpy(pOut, pIn, cx); pxWritten += cx; pIn += image.getWidth(); pOut += getWidth(); } } return pxWritten; }
/** * Scrolls the given image across the display, from right to left. * Returns immediately, and executes the animation asynchronously. * * @param image The image to display. * * @param delay The time between updates, in milliseconds. Defaults * to: MICROBIT_DEFAULT_SCROLL_SPEED. * * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE. * * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER. * * @code * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n"); * display.scrollAsync(i,100,1); * @endcode */ int MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride) { //sanitise the delay value if(delay <= 0) return MICROBIT_INVALID_PARAMETER; // If the display is free, it's our turn to display. if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED) { scrollingImagePosition = stride < 0 ? width : -image.getWidth(); scrollingImageStride = stride; scrollingImage = image; scrollingImageRendered = false; animationDelay = stride == 0 ? 0 : delay; animationTick = 0; animationMode = ANIMATION_MODE_SCROLL_IMAGE; } else { return MICROBIT_BUSY; } return MICROBIT_OK; }