| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973 |
- /*
- * This file is part of the Micro Python project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 Damien George, Mark Shannon
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #include <string.h>
- #include "py/runtime.h"
- #include "microbitimage.h"
- #include "microbitconstimage.h"
- #include "py/runtime0.h"
- #include "microbitfont.h"
- #define min(a,b) (((a)<(b))?(a):(b))
- #define max(a,b) (((a)>(b))?(a):(b))
- const monochrome_5by5_t microbit_blank_image = {
- { µbit_image_type },
- 1, 0, 0, 0,
- { 0, 0, 0 }
- };
- STATIC void microbit_image_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- mp_printf(print, "Image(");
- if (kind == PRINT_STR)
- mp_printf(print, "\n ");
- mp_printf(print, "'");
- for (int y = 0; y < imageHeight(self); ++y) {
- for (int x = 0; x < imageWidth(self); ++x) {
- mp_printf(print, "%c", "0123456789"[imageGetPixelValue(self, x, y)]);
- }
- mp_printf(print, ":");
- if (kind == PRINT_STR && y < imageHeight(self)-1)
- mp_printf(print, "'\n '");
- }
- mp_printf(print, "'");
- if (kind == PRINT_STR)
- mp_printf(print, "\n");
- mp_printf(print, ")");
- }
- uint8_t monochromeGetPixelValue(monochrome_5by5_t * p_mono, mp_int_t x, mp_int_t y) {
- unsigned int index = y*5+x;
- if (index == 24)
- return p_mono->pixel44;
- return (p_mono->bits24[index>>3] >> (index&7))&1;
- }
- uint8_t greyscaleGetPixelValue(greyscale_t * p_greyscale, mp_int_t x, mp_int_t y) {
- unsigned int index = y*p_greyscale->width+x;
- unsigned int shift = ((index<<2)&4);
- return (p_greyscale->byte_data[index>>1] >> shift)&15;
- }
- void greyscaleSetPixelValue(greyscale_t * p_greyscale, mp_int_t x, mp_int_t y, mp_int_t val) {
- unsigned int index = y*p_greyscale->width+x;
- unsigned int shift = ((index<<2)&4);
- uint8_t mask = 240 >> shift;
- p_greyscale->byte_data[index>>1] = (p_greyscale->byte_data[index>>1] & mask) | (val << shift);
- }
- void greyscaleFill(greyscale_t * p_greyscale, mp_int_t val) {
- mp_int_t byte = (val<<4) | val;
- for (int i = 0; i < ((p_greyscale->width*p_greyscale->height+1)>>1); i++) {
- p_greyscale->byte_data[i] = byte;
- }
- }
- void greyscaleClear(greyscale_t * p_greyscale) {
- memset(&p_greyscale->byte_data, 0, (p_greyscale->width*p_greyscale->height+1)>>1);
- }
- uint8_t imageGetPixelValue(microbit_image_obj_t * p_image, mp_int_t x, mp_int_t y) {
- if (p_image->base.five)
- return monochromeGetPixelValue(&p_image->monochrome_5by5, x, y)*MAX_BRIGHTNESS;
- else
- return greyscaleGetPixelValue(&p_image->greyscale, x, y);
- }
- mp_int_t imageWidth(microbit_image_obj_t * p_image) {
- if (p_image->base.five)
- return 5;
- else
- return p_image->greyscale.width;
- }
- mp_int_t imageHeight(microbit_image_obj_t * p_image) {
- if (p_image->base.five)
- return 5;
- else
- return p_image->greyscale.height;
- }
- STATIC greyscale_t *greyscale_new(mp_int_t w, mp_int_t h) {
- greyscale_t *result = m_new_obj_var(greyscale_t, uint8_t, (w*h+1)>>1);
- result->base.type = µbit_image_type;
- result->five = 0;
- result->width = w;
- result->height = h;
- return result;
- }
- greyscale_t * imageCopy(microbit_image_obj_t * p_image) {
- mp_int_t w = imageWidth(p_image);
- mp_int_t h = imageHeight(p_image);
- greyscale_t *result = greyscale_new(w, h);
- for (mp_int_t y = 0; y < h; y++) {
- for (mp_int_t x = 0; x < w; ++x) {
- greyscaleSetPixelValue(result, x,y, imageGetPixelValue(p_image, x,y));
- }
- }
- return result;
- }
- greyscale_t * imageInvert(microbit_image_obj_t * p_image) {
- mp_int_t w = imageWidth(p_image);
- mp_int_t h = imageHeight(p_image);
- greyscale_t *result = greyscale_new(w, h);
- for (mp_int_t y = 0; y < h; y++) {
- for (mp_int_t x = 0; x < w; ++x) {
- greyscaleSetPixelValue(result, x,y, MAX_BRIGHTNESS - imageGetPixelValue(p_image, x,y));
- }
- }
- return result;
- }
- STATIC microbit_image_obj_t *image_from_parsed_str(const char *s, mp_int_t len) {
- mp_int_t w = 0;
- mp_int_t h = 0;
- mp_int_t line_len = 0;
- greyscale_t *result;
- /*First pass -- Establish metadata */
- for (int i = 0; i < len; i++) {
- char c = s[i];
- if (c == '\n' || c == ':') {
- w = max(line_len, w);
- line_len = 0;
- ++h;
- } else if (c == ' ') {
- ++line_len;
- } else if ('c' >= '0' && c <= '9') {
- ++line_len;
- } else {
- mp_raise_ValueError("Unexpected character in Image definition.");
- }
- }
- if (line_len) {
- // Omitted trailing terminator
- ++h;
- w = max(line_len, w);
- }
- result = greyscale_new(w, h);
- mp_int_t x = 0;
- mp_int_t y = 0;
- /* Second pass -- Fill in data */
- for (int i = 0; i < len; i++) {
- char c = s[i];
- if (c == '\n' || c == ':') {
- while (x < w) {
- greyscaleSetPixelValue(result, x, y, 0);
- x++;
- }
- ++y;
- x = 0;
- } else if (c == ' ') {
- /* Treat spaces as 0 */
- greyscaleSetPixelValue(result, x, y, 0);
- ++x;
- } else if ('c' >= '0' && c <= '9') {
- greyscaleSetPixelValue(result, x, y, c - '0');
- ++x;
- }
- }
- if (y < h) {
- while (x < w) {
- greyscaleSetPixelValue(result, x, y, 0);
- x++;
- }
- }
- return (microbit_image_obj_t *)result;
- }
- STATIC mp_obj_t microbit_image_make_new(const mp_obj_type_t *type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
- (void)type_in;
- mp_arg_check_num(n_args, n_kw, 0, 3, false);
- switch (n_args) {
- case 0: {
- greyscale_t *image = greyscale_new(5, 5);
- greyscaleClear(image);
- return image;
- }
- case 1: {
- if (MP_OBJ_IS_STR(args[0])) {
- // arg is a string object
- mp_uint_t len;
- const char *str = mp_obj_str_get_data(args[0], &len);
- // make image from string
- if (len == 1) {
- /* For a single charater, return the font glyph */
- return microbit_image_for_char(str[0]);
- } else {
- /* Otherwise parse the image description string */
- return image_from_parsed_str(str, len);
- }
- } else {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
- "Image(s) takes a string."));
- }
- }
- case 2:
- case 3: {
- mp_int_t w = mp_obj_get_int(args[0]);
- mp_int_t h = mp_obj_get_int(args[1]);
- greyscale_t *image = greyscale_new(w, h);
- if (n_args == 2) {
- greyscaleClear(image);
- } else {
- mp_buffer_info_t bufinfo;
- mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
- if (w < 0 || h < 0 || (size_t)(w * h) != bufinfo.len) {
- mp_raise_ValueError("image data is incorrect size");
- }
- mp_int_t i = 0;
- for (mp_int_t y = 0; y < h; y++) {
- for (mp_int_t x = 0; x < w; ++x) {
- uint8_t val = min(((const uint8_t*)bufinfo.buf)[i], MAX_BRIGHTNESS);
- greyscaleSetPixelValue(image, x, y, val);
- ++i;
- }
- }
- }
- return image;
- }
- default: {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
- "Image() takes 0 to 3 arguments"));
- }
- }
- }
- static void clear_rect(greyscale_t *img, mp_int_t x0, mp_int_t y0,mp_int_t x1, mp_int_t y1) {
- for (int i = x0; i < x1; ++i) {
- for (int j = y0; j < y1; ++j) {
- greyscaleSetPixelValue(img, i, j, 0);
- }
- }
- }
- STATIC void image_blit(microbit_image_obj_t *src, greyscale_t *dest, mp_int_t x, mp_int_t y, mp_int_t w, mp_int_t h, mp_int_t xdest, mp_int_t ydest) {
- if (w < 0)
- w = 0;
- if (h < 0)
- h = 0;
- mp_int_t intersect_x0 = max(max(0, x), -xdest);
- mp_int_t intersect_y0 = max(max(0, y), -ydest);
- mp_int_t intersect_x1 = min(min(dest->width+x-xdest, imageWidth(src)), x+w);
- mp_int_t intersect_y1 = min(min(dest->height+y-ydest, imageHeight(src)), y+h);
- mp_int_t xstart, xend, ystart, yend, xdel, ydel;
- mp_int_t clear_x0 = max(0, xdest);
- mp_int_t clear_y0 = max(0, ydest);
- mp_int_t clear_x1 = min(dest->width, xdest+w);
- mp_int_t clear_y1 = min(dest->height, ydest+h);
- if (intersect_x0 >= intersect_x1 || intersect_y0 >= intersect_y1) {
- // Nothing to copy
- clear_rect(dest, clear_x0, clear_y0, clear_x1, clear_y1);
- return;
- }
- if (x > xdest) {
- xstart = intersect_x0; xend = intersect_x1; xdel = 1;
- } else {
- xstart = intersect_x1-1; xend = intersect_x0-1; xdel = -1;
- }
- if (y > ydest) {
- ystart = intersect_y0; yend = intersect_y1; ydel = 1;
- } else {
- ystart = intersect_y1-1; yend = intersect_y0-1; ydel = -1;
- }
- for (int i = xstart; i != xend; i += xdel) {
- for (int j = ystart; j != yend; j += ydel) {
- int val = imageGetPixelValue(src, i, j);
- greyscaleSetPixelValue(dest, i+xdest-x, j+ydest-y, val);
- }
- }
- // Adjust intersection rectange to dest
- intersect_x0 += xdest-x;
- intersect_y0 += ydest-y;
- intersect_x1 += xdest-x;
- intersect_y1 += ydest-y;
- // Clear four rectangles in the cleared area surrounding the copied area.
- clear_rect(dest, clear_x0, clear_y0, intersect_x0, intersect_y1);
- clear_rect(dest, clear_x0, intersect_y1, intersect_x1, clear_y1);
- clear_rect(dest, intersect_x1, intersect_y0, clear_x1, clear_y1);
- clear_rect(dest, intersect_x0, clear_y0, clear_x1, intersect_y0);
- }
- greyscale_t *image_shift(microbit_image_obj_t *self, mp_int_t x, mp_int_t y) {
- greyscale_t *result = greyscale_new(imageWidth(self), imageWidth(self));
- image_blit(self, result, x, y, imageWidth(self), imageWidth(self), 0, 0);
- return result;
- }
- STATIC microbit_image_obj_t *image_crop(microbit_image_obj_t *img, mp_int_t x, mp_int_t y, mp_int_t w, mp_int_t h) {
- if (w < 0)
- w = 0;
- if (h < 0)
- h = 0;
- greyscale_t *result = greyscale_new(w, h);
- image_blit(img, result, x, y, w, h, 0, 0);
- return (microbit_image_obj_t *)result;
- }
- mp_obj_t microbit_image_width(mp_obj_t self_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- return MP_OBJ_NEW_SMALL_INT(imageWidth(self));
- }
- MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_width_obj, microbit_image_width);
- mp_obj_t microbit_image_height(mp_obj_t self_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- return MP_OBJ_NEW_SMALL_INT(imageHeight(self));
- }
- MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_height_obj, microbit_image_height);
- mp_obj_t microbit_image_get_pixel(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- mp_int_t x = mp_obj_get_int(x_in);
- mp_int_t y = mp_obj_get_int(y_in);
- if (x < 0 || y < 0) {
- mp_raise_ValueError("index cannot be negative");
- }
- if (x < imageWidth(self) && y < imageHeight(self)) {
- return MP_OBJ_NEW_SMALL_INT(imageGetPixelValue(self, x, y));
- }
- mp_raise_ValueError("index too large");
- }
- MP_DEFINE_CONST_FUN_OBJ_3(microbit_image_get_pixel_obj, microbit_image_get_pixel);
- /* Raise an exception if not mutable */
- static void check_mutability(microbit_image_obj_t *self) {
- if (self->base.five) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "image cannot be modified (try copying first)"));
- }
- }
- mp_obj_t microbit_image_set_pixel(mp_uint_t n_args, const mp_obj_t *args) {
- (void)n_args;
- microbit_image_obj_t *self = (microbit_image_obj_t*)args[0];
- check_mutability(self);
- mp_int_t x = mp_obj_get_int(args[1]);
- mp_int_t y = mp_obj_get_int(args[2]);
- if (x < 0 || y < 0) {
- mp_raise_ValueError("index cannot be negative");
- }
- mp_int_t bright = mp_obj_get_int(args[3]);
- if (bright < 0 || bright > MAX_BRIGHTNESS)
- mp_raise_ValueError("brightness out of bounds.");
- if (x < imageWidth(self) && y < imageHeight(self)) {
- greyscaleSetPixelValue(&(self->greyscale), x, y, bright);
- return mp_const_none;
- }
- mp_raise_ValueError("index too large");
- }
- MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_set_pixel_obj, 4, 4, microbit_image_set_pixel);
- mp_obj_t microbit_image_fill(mp_obj_t self_in, mp_obj_t n_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- check_mutability(self);
- mp_int_t n = mp_obj_get_int(n_in);
- if (n < 0 || n > MAX_BRIGHTNESS) {
- mp_raise_ValueError("brightness out of bounds.");
- }
- greyscaleFill(&self->greyscale, n);
- return mp_const_none;
- }
- MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_fill_obj, microbit_image_fill);
- mp_obj_t microbit_image_blit(mp_uint_t n_args, const mp_obj_t *args) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)args[0];
- check_mutability(self);
- mp_obj_t src = args[1];
- if (mp_obj_get_type(src) != µbit_image_type) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "expecting an image"));
- }
- if (n_args == 7) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
- "must specify both offsets"));
- }
- mp_int_t x = mp_obj_get_int(args[2]);
- mp_int_t y = mp_obj_get_int(args[3]);
- mp_int_t w = mp_obj_get_int(args[4]);
- mp_int_t h = mp_obj_get_int(args[5]);
- if (w < 0 || h < 0) {
- mp_raise_ValueError("size cannot be negative");
- }
- mp_int_t xdest;
- mp_int_t ydest;
- if (n_args == 6) {
- xdest = 0;
- ydest = 0;
- } else {
- xdest = mp_obj_get_int(args[6]);
- ydest = mp_obj_get_int(args[7]);
- }
- image_blit((microbit_image_obj_t *)src, &(self->greyscale), x, y, w, h, xdest, ydest);
- return mp_const_none;
- }
- MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_blit_obj, 6, 8, microbit_image_blit);
- mp_obj_t microbit_image_crop(mp_uint_t n_args, const mp_obj_t *args) {
- (void)n_args;
- microbit_image_obj_t *self = (microbit_image_obj_t*)args[0];
- mp_int_t x0 = mp_obj_get_int(args[1]);
- mp_int_t y0 = mp_obj_get_int(args[2]);
- mp_int_t x1 = mp_obj_get_int(args[3]);
- mp_int_t y1 = mp_obj_get_int(args[4]);
- return image_crop(self, x0, y0, x1, y1);
- }
- MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_crop_obj, 5, 5, microbit_image_crop);
- mp_obj_t microbit_image_shift_left(mp_obj_t self_in, mp_obj_t n_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- mp_int_t n = mp_obj_get_int(n_in);
- return image_shift(self, n, 0);
- }
- MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_left_obj, microbit_image_shift_left);
- mp_obj_t microbit_image_shift_right(mp_obj_t self_in, mp_obj_t n_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- mp_int_t n = mp_obj_get_int(n_in);
- return image_shift(self, -n, 0);
- }
- MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_right_obj, microbit_image_shift_right);
- mp_obj_t microbit_image_shift_up(mp_obj_t self_in, mp_obj_t n_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- mp_int_t n = mp_obj_get_int(n_in);
- return image_shift(self, 0, n);
- }
- MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_up_obj, microbit_image_shift_up);
- mp_obj_t microbit_image_shift_down(mp_obj_t self_in, mp_obj_t n_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- mp_int_t n = mp_obj_get_int(n_in);
- return image_shift(self, 0, -n);
- }
- MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_down_obj, microbit_image_shift_down);
- mp_obj_t microbit_image_copy(mp_obj_t self_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- return imageCopy(self);
- }
- MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_copy_obj, microbit_image_copy);
- mp_obj_t microbit_image_invert(mp_obj_t self_in) {
- microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
- return imageInvert(self);
- }
- MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_invert_obj, microbit_image_invert);
- STATIC const mp_rom_map_elem_t microbit_image_locals_dict_table[] = {
- { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(µbit_image_width_obj) },
- { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(µbit_image_height_obj) },
- { MP_ROM_QSTR(MP_QSTR_get_pixel), MP_ROM_PTR(µbit_image_get_pixel_obj) },
- { MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(µbit_image_set_pixel_obj) },
- { MP_ROM_QSTR(MP_QSTR_shift_left), MP_ROM_PTR(µbit_image_shift_left_obj) },
- { MP_ROM_QSTR(MP_QSTR_shift_right), MP_ROM_PTR(µbit_image_shift_right_obj) },
- { MP_ROM_QSTR(MP_QSTR_shift_up), MP_ROM_PTR(µbit_image_shift_up_obj) },
- { MP_ROM_QSTR(MP_QSTR_shift_down), MP_ROM_PTR(µbit_image_shift_down_obj) },
- { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(µbit_image_copy_obj) },
- { MP_ROM_QSTR(MP_QSTR_crop), MP_ROM_PTR(µbit_image_crop_obj) },
- { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(µbit_image_invert_obj) },
- { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(µbit_image_fill_obj) },
- { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(µbit_image_blit_obj) },
- { MP_ROM_QSTR(MP_QSTR_HEART), MP_ROM_PTR(µbit_const_image_heart_obj) },
- { MP_ROM_QSTR(MP_QSTR_HEART_SMALL), MP_ROM_PTR(µbit_const_image_heart_small_obj) },
- { MP_ROM_QSTR(MP_QSTR_HAPPY), MP_ROM_PTR(µbit_const_image_happy_obj) },
- { MP_ROM_QSTR(MP_QSTR_SMILE), MP_ROM_PTR(µbit_const_image_smile_obj) },
- { MP_ROM_QSTR(MP_QSTR_SAD), MP_ROM_PTR(µbit_const_image_sad_obj) },
- { MP_ROM_QSTR(MP_QSTR_CONFUSED), MP_ROM_PTR(µbit_const_image_confused_obj) },
- { MP_ROM_QSTR(MP_QSTR_ANGRY), MP_ROM_PTR(µbit_const_image_angry_obj) },
- { MP_ROM_QSTR(MP_QSTR_ASLEEP), MP_ROM_PTR(µbit_const_image_asleep_obj) },
- { MP_ROM_QSTR(MP_QSTR_SURPRISED), MP_ROM_PTR(µbit_const_image_surprised_obj) },
- { MP_ROM_QSTR(MP_QSTR_SILLY), MP_ROM_PTR(µbit_const_image_silly_obj) },
- { MP_ROM_QSTR(MP_QSTR_FABULOUS), MP_ROM_PTR(µbit_const_image_fabulous_obj) },
- { MP_ROM_QSTR(MP_QSTR_MEH), MP_ROM_PTR(µbit_const_image_meh_obj) },
- { MP_ROM_QSTR(MP_QSTR_YES), MP_ROM_PTR(µbit_const_image_yes_obj) },
- { MP_ROM_QSTR(MP_QSTR_NO), MP_ROM_PTR(µbit_const_image_no_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK12), MP_ROM_PTR(µbit_const_image_clock12_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK1), MP_ROM_PTR(µbit_const_image_clock1_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK2), MP_ROM_PTR(µbit_const_image_clock2_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK3), MP_ROM_PTR(µbit_const_image_clock3_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK4), MP_ROM_PTR(µbit_const_image_clock4_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK5), MP_ROM_PTR(µbit_const_image_clock5_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK6), MP_ROM_PTR(µbit_const_image_clock6_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK7), MP_ROM_PTR(µbit_const_image_clock7_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK8), MP_ROM_PTR(µbit_const_image_clock8_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK9), MP_ROM_PTR(µbit_const_image_clock9_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK10), MP_ROM_PTR(µbit_const_image_clock10_obj) },
- { MP_ROM_QSTR(MP_QSTR_CLOCK11), MP_ROM_PTR(µbit_const_image_clock11_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_N), MP_ROM_PTR(µbit_const_image_arrow_n_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_NE), MP_ROM_PTR(µbit_const_image_arrow_ne_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_E), MP_ROM_PTR(µbit_const_image_arrow_e_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_SE), MP_ROM_PTR(µbit_const_image_arrow_se_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_S), MP_ROM_PTR(µbit_const_image_arrow_s_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_SW), MP_ROM_PTR(µbit_const_image_arrow_sw_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_W), MP_ROM_PTR(µbit_const_image_arrow_w_obj) },
- { MP_ROM_QSTR(MP_QSTR_ARROW_NW), MP_ROM_PTR(µbit_const_image_arrow_nw_obj) },
- { MP_ROM_QSTR(MP_QSTR_TRIANGLE), MP_ROM_PTR(µbit_const_image_triangle_obj) },
- { MP_ROM_QSTR(MP_QSTR_TRIANGLE_LEFT), MP_ROM_PTR(µbit_const_image_triangle_left_obj) },
- { MP_ROM_QSTR(MP_QSTR_CHESSBOARD), MP_ROM_PTR(µbit_const_image_chessboard_obj) },
- { MP_ROM_QSTR(MP_QSTR_DIAMOND), MP_ROM_PTR(µbit_const_image_diamond_obj) },
- { MP_ROM_QSTR(MP_QSTR_DIAMOND_SMALL), MP_ROM_PTR(µbit_const_image_diamond_small_obj) },
- { MP_ROM_QSTR(MP_QSTR_SQUARE), MP_ROM_PTR(µbit_const_image_square_obj) },
- { MP_ROM_QSTR(MP_QSTR_SQUARE_SMALL), MP_ROM_PTR(µbit_const_image_square_small_obj) },
- { MP_ROM_QSTR(MP_QSTR_RABBIT), MP_ROM_PTR(µbit_const_image_rabbit) },
- { MP_ROM_QSTR(MP_QSTR_COW), MP_ROM_PTR(µbit_const_image_cow) },
- { MP_ROM_QSTR(MP_QSTR_MUSIC_CROTCHET), MP_ROM_PTR(µbit_const_image_music_crotchet_obj) },
- { MP_ROM_QSTR(MP_QSTR_MUSIC_QUAVER), MP_ROM_PTR(µbit_const_image_music_quaver_obj) },
- { MP_ROM_QSTR(MP_QSTR_MUSIC_QUAVERS), MP_ROM_PTR(µbit_const_image_music_quavers_obj) },
- { MP_ROM_QSTR(MP_QSTR_PITCHFORK), MP_ROM_PTR(µbit_const_image_pitchfork_obj) },
- { MP_ROM_QSTR(MP_QSTR_XMAS), MP_ROM_PTR(µbit_const_image_xmas_obj) },
- { MP_ROM_QSTR(MP_QSTR_PACMAN), MP_ROM_PTR(µbit_const_image_pacman_obj) },
- { MP_ROM_QSTR(MP_QSTR_TARGET), MP_ROM_PTR(µbit_const_image_target_obj) },
- { MP_ROM_QSTR(MP_QSTR_ALL_CLOCKS), MP_ROM_PTR(µbit_const_image_all_clocks_tuple_obj) },
- { MP_ROM_QSTR(MP_QSTR_ALL_ARROWS), MP_ROM_PTR(µbit_const_image_all_arrows_tuple_obj) },
- { MP_ROM_QSTR(MP_QSTR_TSHIRT), MP_ROM_PTR(µbit_const_image_tshirt_obj) },
- { MP_ROM_QSTR(MP_QSTR_ROLLERSKATE), MP_ROM_PTR(µbit_const_image_rollerskate_obj) },
- { MP_ROM_QSTR(MP_QSTR_DUCK), MP_ROM_PTR(µbit_const_image_duck_obj) },
- { MP_ROM_QSTR(MP_QSTR_HOUSE), MP_ROM_PTR(µbit_const_image_house_obj) },
- { MP_ROM_QSTR(MP_QSTR_TORTOISE), MP_ROM_PTR(µbit_const_image_tortoise_obj) },
- { MP_ROM_QSTR(MP_QSTR_BUTTERFLY), MP_ROM_PTR(µbit_const_image_butterfly_obj) },
- { MP_ROM_QSTR(MP_QSTR_STICKFIGURE), MP_ROM_PTR(µbit_const_image_stickfigure_obj) },
- { MP_ROM_QSTR(MP_QSTR_GHOST), MP_ROM_PTR(µbit_const_image_ghost_obj) },
- { MP_ROM_QSTR(MP_QSTR_SWORD), MP_ROM_PTR(µbit_const_image_sword_obj) },
- { MP_ROM_QSTR(MP_QSTR_GIRAFFE), MP_ROM_PTR(µbit_const_image_giraffe_obj) },
- { MP_ROM_QSTR(MP_QSTR_SKULL), MP_ROM_PTR(µbit_const_image_skull_obj) },
- { MP_ROM_QSTR(MP_QSTR_UMBRELLA), MP_ROM_PTR(µbit_const_image_umbrella_obj) },
- { MP_ROM_QSTR(MP_QSTR_SNAKE), MP_ROM_PTR(µbit_const_image_snake_obj) },
- };
- STATIC MP_DEFINE_CONST_DICT(microbit_image_locals_dict, microbit_image_locals_dict_table);
- #define THE_FONT font_pendolino3_5x5_pad3msb
- #define ASCII_START 32
- #define ASCII_END 126
- STATIC const unsigned char *get_font_data_from_char(char c) {
- if (c < ASCII_START || c > ASCII_END) {
- c = '?';
- }
- int offset = (c-ASCII_START) * 5;
- return THE_FONT + offset;
- }
- STATIC mp_int_t get_pixel_from_font_data(const unsigned char *data, int x, int y) {
- /* The following logic belongs in MicroBitFont */
- return ((data[y]>>(4-x))&1);
- }
- void microbit_image_set_from_char(greyscale_t *img, char c) {
- const unsigned char *data = get_font_data_from_char(c);
- for (int x = 0; x < 5; ++x) {
- for (int y = 0; y < 5; ++y) {
- greyscaleSetPixelValue(img, x, y, get_pixel_from_font_data(data, x, y)*MAX_BRIGHTNESS);
- }
- }
- }
- microbit_image_obj_t *microbit_image_for_char(char c) {
- greyscale_t *result = greyscale_new(5,5);
- microbit_image_set_from_char(result, c);
- return (microbit_image_obj_t *)result;
- }
- #if MICROPY_PY_BUILTINS_FLOAT
- microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_float_t fval) {
- #else
- microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_int_t fval) {
- #endif
- if (fval < 0)
- mp_raise_ValueError("Brightness multiplier must not be negative.");
- greyscale_t *result = greyscale_new(imageWidth(lhs), imageHeight(lhs));
- for (int x = 0; x < imageWidth(lhs); ++x) {
- for (int y = 0; y < imageWidth(lhs); ++y) {
- #if MICROPY_PY_BUILTINS_FLOAT
- int val = min((int)imageGetPixelValue(lhs, x,y)*fval+0.5, MAX_BRIGHTNESS);
- #else
- int val = min((int)imageGetPixelValue(lhs, x,y)*fval, MAX_BRIGHTNESS);
- #endif
- greyscaleSetPixelValue(result, x, y, val);
- }
- }
- return (microbit_image_obj_t *)result;
- }
- microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_image_obj_t *rhs, bool add) {
- mp_int_t h = imageHeight(lhs);
- mp_int_t w = imageWidth(lhs);
- if (imageHeight(rhs) != h || imageWidth(lhs) != w) {
- // TODO: verify that image width in test above should really test (lhs != w)
- mp_raise_ValueError("Images must be the same size.");
- }
- greyscale_t *result = greyscale_new(w, h);
- for (int x = 0; x < w; ++x) {
- for (int y = 0; y < h; ++y) {
- int val;
- int lval = imageGetPixelValue(lhs, x,y);
- int rval = imageGetPixelValue(rhs, x,y);
- if (add)
- val = min(lval + rval, MAX_BRIGHTNESS);
- else
- val = max(0, lval - rval);
- greyscaleSetPixelValue(result, x, y, val);
- }
- }
- return (microbit_image_obj_t *)result;
- }
- STATIC mp_obj_t image_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
- if (mp_obj_get_type(lhs_in) != µbit_image_type) {
- return MP_OBJ_NULL; // op not supported
- }
- microbit_image_obj_t *lhs = (microbit_image_obj_t *)lhs_in;
- switch(op) {
- case MP_BINARY_OP_ADD:
- case MP_BINARY_OP_SUBTRACT:
- break;
- case MP_BINARY_OP_MULTIPLY:
- #if MICROPY_PY_BUILTINS_FLOAT
- return microbit_image_dim(lhs, mp_obj_get_float(rhs_in));
- #else
- return microbit_image_dim(lhs, mp_obj_get_int(rhs_in) * 10);
- #endif
- case MP_BINARY_OP_TRUE_DIVIDE:
- #if MICROPY_PY_BUILTINS_FLOAT
- return microbit_image_dim(lhs, 1.0/mp_obj_get_float(rhs_in));
- #else
- break;
- case MP_BINARY_OP_FLOOR_DIVIDE:
- return microbit_image_dim(lhs, (100/mp_obj_get_int(rhs_in) + 5) / 10);
- #endif
- default:
- return MP_OBJ_NULL; // op not supported
- }
- if (mp_obj_get_type(rhs_in) != µbit_image_type) {
- return MP_OBJ_NULL; // op not supported
- }
- return microbit_image_sum(lhs, (microbit_image_obj_t *)rhs_in, op == MP_BINARY_OP_ADD);
- }
- const mp_obj_type_t microbit_image_type = {
- { &mp_type_type },
- .name = MP_QSTR_MicroBitImage,
- .print = microbit_image_print,
- .make_new = microbit_image_make_new,
- .call = NULL,
- .unary_op = NULL,
- .binary_op = image_binary_op,
- .attr = NULL,
- .subscr = NULL,
- .getiter = NULL,
- .iternext = NULL,
- .buffer_p = {NULL},
- .locals_dict = (mp_obj_dict_t*)µbit_image_locals_dict,
- };
-
- typedef struct _scrolling_string_t {
- mp_obj_base_t base;
- char const *str;
- mp_uint_t len;
- mp_obj_t ref;
- bool monospace;
- bool repeat;
- } scrolling_string_t;
- typedef struct _scrolling_string_iterator_t {
- mp_obj_base_t base;
- mp_obj_t ref;
- greyscale_t *img;
- char const *next_char;
- char const *start;
- char const *end;
- uint8_t offset;
- uint8_t offset_limit;
- bool monospace;
- bool repeat;
- char right;
- } scrolling_string_iterator_t;
- extern const mp_obj_type_t microbit_scrolling_string_type;
- extern const mp_obj_type_t microbit_scrolling_string_iterator_type;
- mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref, bool monospace, bool repeat) {
- scrolling_string_t *result = m_new_obj(scrolling_string_t);
- result->base.type = µbit_scrolling_string_type;
- result->str = str;
- result->len = len;
- result->ref = ref;
- result->monospace = monospace;
- result->repeat = repeat;
- return result;
- }
- STATIC int font_column_non_blank(const unsigned char *font_data, unsigned int col) {
- for (int y = 0; y < 5; ++y) {
- if (get_pixel_from_font_data(font_data, col, y)) {
- return 1;
- }
- }
- return 0;
- }
- /* Not strictly the rightmost non-blank column, but the rightmost in columns 2,3 or 4. */
- STATIC unsigned int rightmost_non_blank_column(const unsigned char *font_data) {
- if (font_column_non_blank(font_data, 4)) {
- return 4;
- }
- if (font_column_non_blank(font_data, 3)) {
- return 3;
- }
- return 2;
- }
- static void restart(scrolling_string_iterator_t *iter) {
- iter->next_char = iter->start;
- iter->offset = 0;
- if (iter->start < iter->end) {
- iter->right = *iter->next_char;
- if (iter->monospace) {
- iter->offset_limit = 5;
- } else {
- iter->offset_limit = rightmost_non_blank_column(get_font_data_from_char(iter->right)) + 1;
- }
- } else {
- iter->right = ' ';
- iter->offset_limit = 5;
- }
- }
- STATIC mp_obj_t get_microbit_scrolling_string_iter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
- (void)iter_buf;
- scrolling_string_t *str = (scrolling_string_t *)o_in;
- scrolling_string_iterator_t *result = m_new_obj(scrolling_string_iterator_t);
- result->base.type = µbit_scrolling_string_iterator_type;
- result->img = greyscale_new(5,5);
- result->start = str->str;
- result->ref = str->ref;
- result->monospace = str->monospace;
- result->end = result->start + str->len;
- result->repeat = str->repeat;
- restart(result);
- return result;
- }
- STATIC mp_obj_t microbit_scrolling_string_iter_next(mp_obj_t o_in) {
- scrolling_string_iterator_t *iter = (scrolling_string_iterator_t *)o_in;
- if (iter->next_char == iter->end && iter->offset == 5) {
- if (iter->repeat) {
- restart(iter);
- greyscaleClear(iter->img);
- } else {
- return MP_OBJ_STOP_ITERATION;
- }
- }
- for (int x = 0; x < 4; x++) {
- for (int y = 0; y < 5; y++) {
- greyscaleSetPixelValue(iter->img, x, y, greyscaleGetPixelValue(iter->img, x+1, y));
- }
- }
- for (int y = 0; y < 5; y++) {
- greyscaleSetPixelValue(iter->img, 4, y, 0);
- }
- const unsigned char *font_data;
- if (iter->offset < iter->offset_limit) {
- font_data = get_font_data_from_char(iter->right);
- for (int y = 0; y < 5; ++y) {
- int pix = get_pixel_from_font_data(font_data, iter->offset, y)*MAX_BRIGHTNESS;
- greyscaleSetPixelValue(iter->img, 4, y, pix);
- }
- } else if (iter->offset == iter->offset_limit) {
- ++iter->next_char;
- if (iter->next_char == iter->end) {
- iter->right = ' ';
- iter->offset_limit = 5;
- iter->offset = 0;
- } else {
- iter->right = *iter->next_char;
- font_data = get_font_data_from_char(iter->right);
- if (iter->monospace) {
- iter->offset = -1;
- iter->offset_limit = 5;
- } else {
- iter->offset = -font_column_non_blank(font_data, 0);
- iter->offset_limit = rightmost_non_blank_column(font_data)+1;
- }
- }
- }
- ++iter->offset;
- return iter->img;
- }
- const mp_obj_type_t microbit_scrolling_string_type = {
- { &mp_type_type },
- .name = MP_QSTR_ScrollingString,
- .print = NULL,
- .make_new = NULL,
- .call = NULL,
- .unary_op = NULL,
- .binary_op = NULL,
- .attr = NULL,
- .subscr = NULL,
- .getiter = get_microbit_scrolling_string_iter,
- .iternext = NULL,
- .buffer_p = {NULL},
- .locals_dict = NULL,
- };
- const mp_obj_type_t microbit_scrolling_string_iterator_type = {
- { &mp_type_type },
- .name = MP_QSTR_iterator,
- .print = NULL,
- .make_new = NULL,
- .call = NULL,
- .unary_op = NULL,
- .binary_op = NULL,
- .attr = NULL,
- .subscr = NULL,
- .getiter = mp_identity_getiter,
- .iternext = microbit_scrolling_string_iter_next,
- .buffer_p = {NULL},
- .locals_dict = NULL,
- };
- /** Facade types to present a string as a sequence of images.
- * These are necessary to avoid allocation during iteration,
- * which may happen in interrupt handlers.
- */
- typedef struct _string_image_facade_t {
- mp_obj_base_t base;
- mp_obj_t string;
- greyscale_t *image;
- } string_image_facade_t;
- static mp_obj_t string_image_facade_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
- if (value == MP_OBJ_SENTINEL) {
- // Fill in image
- string_image_facade_t *self = (string_image_facade_t *)self_in;
- mp_uint_t len;
- const char *text = mp_obj_str_get_data(self->string, &len);
- mp_uint_t index = mp_get_index(self->base.type, len, index_in, false);
- microbit_image_set_from_char(self->image, text[index]);
- return self->image;
- } else {
- return MP_OBJ_NULL; // op not supported
- }
- }
- static mp_obj_t facade_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
- string_image_facade_t *self = (string_image_facade_t *)self_in;
- switch (op) {
- case MP_UNARY_OP_LEN:
- return mp_obj_len(self->string);
- default: return MP_OBJ_NULL; // op not supported
- }
- }
- static mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in, mp_obj_iter_buf_t *iter_buf);
- const mp_obj_type_t string_image_facade_type = {
- { &mp_type_type },
- .name = MP_QSTR_Facade,
- .print = NULL,
- .make_new = NULL,
- .call = NULL,
- .unary_op = facade_unary_op,
- .binary_op = NULL,
- .attr = NULL,
- .subscr = string_image_facade_subscr,
- .getiter = microbit_facade_iterator,
- .iternext = NULL,
- .buffer_p = {NULL},
- NULL
- };
- typedef struct _facade_iterator_t {
- mp_obj_base_t base;
- mp_obj_t string;
- mp_uint_t index;
- greyscale_t *image;
- } facade_iterator_t;
- mp_obj_t microbit_string_facade(mp_obj_t string) {
- string_image_facade_t *result = m_new_obj(string_image_facade_t);
- result->base.type = &string_image_facade_type;
- result->string = string;
- result->image = greyscale_new(5,5);
- return result;
- }
- static mp_obj_t microbit_facade_iter_next(mp_obj_t iter_in) {
- facade_iterator_t *iter = (facade_iterator_t *)iter_in;
- mp_uint_t len;
- const char *text = mp_obj_str_get_data(iter->string, &len);
- if (iter->index >= len) {
- return MP_OBJ_STOP_ITERATION;
- }
- microbit_image_set_from_char(iter->image, text[iter->index]);
- iter->index++;
- return iter->image;
- }
- const mp_obj_type_t microbit_facade_iterator_type = {
- { &mp_type_type },
- .name = MP_QSTR_iterator,
- .print = NULL,
- .make_new = NULL,
- .call = NULL,
- .unary_op = NULL,
- .binary_op = NULL,
- .attr = NULL,
- .subscr = NULL,
- .getiter = mp_identity_getiter,
- .iternext = microbit_facade_iter_next,
- .buffer_p = {NULL},
- NULL
- };
- mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in, mp_obj_iter_buf_t *iter_buf) {
- (void)iter_buf;
- facade_iterator_t *result = m_new_obj(facade_iterator_t);
- string_image_facade_t *iterable = (string_image_facade_t *)iterable_in;
- result->base.type = µbit_facade_iterator_type;
- result->string = iterable->string;
- result->image = iterable->image;
- result->index = 0;
- return result;
- }
|