microbitimage.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. /*
  2. * This file is part of the Micro Python project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2015 Damien George, Mark Shannon
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include <string.h>
  27. #include "py/runtime.h"
  28. #include "microbitimage.h"
  29. #include "microbitconstimage.h"
  30. #include "py/runtime0.h"
  31. #include "microbitfont.h"
  32. #define min(a,b) (((a)<(b))?(a):(b))
  33. #define max(a,b) (((a)>(b))?(a):(b))
  34. const monochrome_5by5_t microbit_blank_image = {
  35. { &microbit_image_type },
  36. 1, 0, 0, 0,
  37. { 0, 0, 0 }
  38. };
  39. STATIC void microbit_image_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
  40. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  41. mp_printf(print, "Image(");
  42. if (kind == PRINT_STR)
  43. mp_printf(print, "\n ");
  44. mp_printf(print, "'");
  45. for (int y = 0; y < imageHeight(self); ++y) {
  46. for (int x = 0; x < imageWidth(self); ++x) {
  47. mp_printf(print, "%c", "0123456789"[imageGetPixelValue(self, x, y)]);
  48. }
  49. mp_printf(print, ":");
  50. if (kind == PRINT_STR && y < imageHeight(self)-1)
  51. mp_printf(print, "'\n '");
  52. }
  53. mp_printf(print, "'");
  54. if (kind == PRINT_STR)
  55. mp_printf(print, "\n");
  56. mp_printf(print, ")");
  57. }
  58. uint8_t monochromeGetPixelValue(monochrome_5by5_t * p_mono, mp_int_t x, mp_int_t y) {
  59. unsigned int index = y*5+x;
  60. if (index == 24)
  61. return p_mono->pixel44;
  62. return (p_mono->bits24[index>>3] >> (index&7))&1;
  63. }
  64. uint8_t greyscaleGetPixelValue(greyscale_t * p_greyscale, mp_int_t x, mp_int_t y) {
  65. unsigned int index = y*p_greyscale->width+x;
  66. unsigned int shift = ((index<<2)&4);
  67. return (p_greyscale->byte_data[index>>1] >> shift)&15;
  68. }
  69. void greyscaleSetPixelValue(greyscale_t * p_greyscale, mp_int_t x, mp_int_t y, mp_int_t val) {
  70. unsigned int index = y*p_greyscale->width+x;
  71. unsigned int shift = ((index<<2)&4);
  72. uint8_t mask = 240 >> shift;
  73. p_greyscale->byte_data[index>>1] = (p_greyscale->byte_data[index>>1] & mask) | (val << shift);
  74. }
  75. void greyscaleFill(greyscale_t * p_greyscale, mp_int_t val) {
  76. mp_int_t byte = (val<<4) | val;
  77. for (int i = 0; i < ((p_greyscale->width*p_greyscale->height+1)>>1); i++) {
  78. p_greyscale->byte_data[i] = byte;
  79. }
  80. }
  81. void greyscaleClear(greyscale_t * p_greyscale) {
  82. memset(&p_greyscale->byte_data, 0, (p_greyscale->width*p_greyscale->height+1)>>1);
  83. }
  84. uint8_t imageGetPixelValue(microbit_image_obj_t * p_image, mp_int_t x, mp_int_t y) {
  85. if (p_image->base.five)
  86. return monochromeGetPixelValue(&p_image->monochrome_5by5, x, y)*MAX_BRIGHTNESS;
  87. else
  88. return greyscaleGetPixelValue(&p_image->greyscale, x, y);
  89. }
  90. mp_int_t imageWidth(microbit_image_obj_t * p_image) {
  91. if (p_image->base.five)
  92. return 5;
  93. else
  94. return p_image->greyscale.width;
  95. }
  96. mp_int_t imageHeight(microbit_image_obj_t * p_image) {
  97. if (p_image->base.five)
  98. return 5;
  99. else
  100. return p_image->greyscale.height;
  101. }
  102. STATIC greyscale_t *greyscale_new(mp_int_t w, mp_int_t h) {
  103. greyscale_t *result = m_new_obj_var(greyscale_t, uint8_t, (w*h+1)>>1);
  104. result->base.type = &microbit_image_type;
  105. result->five = 0;
  106. result->width = w;
  107. result->height = h;
  108. return result;
  109. }
  110. greyscale_t * imageCopy(microbit_image_obj_t * p_image) {
  111. mp_int_t w = imageWidth(p_image);
  112. mp_int_t h = imageHeight(p_image);
  113. greyscale_t *result = greyscale_new(w, h);
  114. for (mp_int_t y = 0; y < h; y++) {
  115. for (mp_int_t x = 0; x < w; ++x) {
  116. greyscaleSetPixelValue(result, x,y, imageGetPixelValue(p_image, x,y));
  117. }
  118. }
  119. return result;
  120. }
  121. greyscale_t * imageInvert(microbit_image_obj_t * p_image) {
  122. mp_int_t w = imageWidth(p_image);
  123. mp_int_t h = imageHeight(p_image);
  124. greyscale_t *result = greyscale_new(w, h);
  125. for (mp_int_t y = 0; y < h; y++) {
  126. for (mp_int_t x = 0; x < w; ++x) {
  127. greyscaleSetPixelValue(result, x,y, MAX_BRIGHTNESS - imageGetPixelValue(p_image, x,y));
  128. }
  129. }
  130. return result;
  131. }
  132. STATIC microbit_image_obj_t *image_from_parsed_str(const char *s, mp_int_t len) {
  133. mp_int_t w = 0;
  134. mp_int_t h = 0;
  135. mp_int_t line_len = 0;
  136. greyscale_t *result;
  137. /*First pass -- Establish metadata */
  138. for (int i = 0; i < len; i++) {
  139. char c = s[i];
  140. if (c == '\n' || c == ':') {
  141. w = max(line_len, w);
  142. line_len = 0;
  143. ++h;
  144. } else if (c == ' ') {
  145. ++line_len;
  146. } else if ('c' >= '0' && c <= '9') {
  147. ++line_len;
  148. } else {
  149. mp_raise_ValueError("Unexpected character in Image definition.");
  150. }
  151. }
  152. if (line_len) {
  153. // Omitted trailing terminator
  154. ++h;
  155. w = max(line_len, w);
  156. }
  157. result = greyscale_new(w, h);
  158. mp_int_t x = 0;
  159. mp_int_t y = 0;
  160. /* Second pass -- Fill in data */
  161. for (int i = 0; i < len; i++) {
  162. char c = s[i];
  163. if (c == '\n' || c == ':') {
  164. while (x < w) {
  165. greyscaleSetPixelValue(result, x, y, 0);
  166. x++;
  167. }
  168. ++y;
  169. x = 0;
  170. } else if (c == ' ') {
  171. /* Treat spaces as 0 */
  172. greyscaleSetPixelValue(result, x, y, 0);
  173. ++x;
  174. } else if ('c' >= '0' && c <= '9') {
  175. greyscaleSetPixelValue(result, x, y, c - '0');
  176. ++x;
  177. }
  178. }
  179. if (y < h) {
  180. while (x < w) {
  181. greyscaleSetPixelValue(result, x, y, 0);
  182. x++;
  183. }
  184. }
  185. return (microbit_image_obj_t *)result;
  186. }
  187. 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) {
  188. (void)type_in;
  189. mp_arg_check_num(n_args, n_kw, 0, 3, false);
  190. switch (n_args) {
  191. case 0: {
  192. greyscale_t *image = greyscale_new(5, 5);
  193. greyscaleClear(image);
  194. return image;
  195. }
  196. case 1: {
  197. if (MP_OBJ_IS_STR(args[0])) {
  198. // arg is a string object
  199. mp_uint_t len;
  200. const char *str = mp_obj_str_get_data(args[0], &len);
  201. // make image from string
  202. if (len == 1) {
  203. /* For a single charater, return the font glyph */
  204. return microbit_image_for_char(str[0]);
  205. } else {
  206. /* Otherwise parse the image description string */
  207. return image_from_parsed_str(str, len);
  208. }
  209. } else {
  210. nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
  211. "Image(s) takes a string."));
  212. }
  213. }
  214. case 2:
  215. case 3: {
  216. mp_int_t w = mp_obj_get_int(args[0]);
  217. mp_int_t h = mp_obj_get_int(args[1]);
  218. greyscale_t *image = greyscale_new(w, h);
  219. if (n_args == 2) {
  220. greyscaleClear(image);
  221. } else {
  222. mp_buffer_info_t bufinfo;
  223. mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
  224. if (w < 0 || h < 0 || (size_t)(w * h) != bufinfo.len) {
  225. mp_raise_ValueError("image data is incorrect size");
  226. }
  227. mp_int_t i = 0;
  228. for (mp_int_t y = 0; y < h; y++) {
  229. for (mp_int_t x = 0; x < w; ++x) {
  230. uint8_t val = min(((const uint8_t*)bufinfo.buf)[i], MAX_BRIGHTNESS);
  231. greyscaleSetPixelValue(image, x, y, val);
  232. ++i;
  233. }
  234. }
  235. }
  236. return image;
  237. }
  238. default: {
  239. nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
  240. "Image() takes 0 to 3 arguments"));
  241. }
  242. }
  243. }
  244. static void clear_rect(greyscale_t *img, mp_int_t x0, mp_int_t y0,mp_int_t x1, mp_int_t y1) {
  245. for (int i = x0; i < x1; ++i) {
  246. for (int j = y0; j < y1; ++j) {
  247. greyscaleSetPixelValue(img, i, j, 0);
  248. }
  249. }
  250. }
  251. 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) {
  252. if (w < 0)
  253. w = 0;
  254. if (h < 0)
  255. h = 0;
  256. mp_int_t intersect_x0 = max(max(0, x), -xdest);
  257. mp_int_t intersect_y0 = max(max(0, y), -ydest);
  258. mp_int_t intersect_x1 = min(min(dest->width+x-xdest, imageWidth(src)), x+w);
  259. mp_int_t intersect_y1 = min(min(dest->height+y-ydest, imageHeight(src)), y+h);
  260. mp_int_t xstart, xend, ystart, yend, xdel, ydel;
  261. mp_int_t clear_x0 = max(0, xdest);
  262. mp_int_t clear_y0 = max(0, ydest);
  263. mp_int_t clear_x1 = min(dest->width, xdest+w);
  264. mp_int_t clear_y1 = min(dest->height, ydest+h);
  265. if (intersect_x0 >= intersect_x1 || intersect_y0 >= intersect_y1) {
  266. // Nothing to copy
  267. clear_rect(dest, clear_x0, clear_y0, clear_x1, clear_y1);
  268. return;
  269. }
  270. if (x > xdest) {
  271. xstart = intersect_x0; xend = intersect_x1; xdel = 1;
  272. } else {
  273. xstart = intersect_x1-1; xend = intersect_x0-1; xdel = -1;
  274. }
  275. if (y > ydest) {
  276. ystart = intersect_y0; yend = intersect_y1; ydel = 1;
  277. } else {
  278. ystart = intersect_y1-1; yend = intersect_y0-1; ydel = -1;
  279. }
  280. for (int i = xstart; i != xend; i += xdel) {
  281. for (int j = ystart; j != yend; j += ydel) {
  282. int val = imageGetPixelValue(src, i, j);
  283. greyscaleSetPixelValue(dest, i+xdest-x, j+ydest-y, val);
  284. }
  285. }
  286. // Adjust intersection rectange to dest
  287. intersect_x0 += xdest-x;
  288. intersect_y0 += ydest-y;
  289. intersect_x1 += xdest-x;
  290. intersect_y1 += ydest-y;
  291. // Clear four rectangles in the cleared area surrounding the copied area.
  292. clear_rect(dest, clear_x0, clear_y0, intersect_x0, intersect_y1);
  293. clear_rect(dest, clear_x0, intersect_y1, intersect_x1, clear_y1);
  294. clear_rect(dest, intersect_x1, intersect_y0, clear_x1, clear_y1);
  295. clear_rect(dest, intersect_x0, clear_y0, clear_x1, intersect_y0);
  296. }
  297. greyscale_t *image_shift(microbit_image_obj_t *self, mp_int_t x, mp_int_t y) {
  298. greyscale_t *result = greyscale_new(imageWidth(self), imageWidth(self));
  299. image_blit(self, result, x, y, imageWidth(self), imageWidth(self), 0, 0);
  300. return result;
  301. }
  302. 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) {
  303. if (w < 0)
  304. w = 0;
  305. if (h < 0)
  306. h = 0;
  307. greyscale_t *result = greyscale_new(w, h);
  308. image_blit(img, result, x, y, w, h, 0, 0);
  309. return (microbit_image_obj_t *)result;
  310. }
  311. mp_obj_t microbit_image_width(mp_obj_t self_in) {
  312. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  313. return MP_OBJ_NEW_SMALL_INT(imageWidth(self));
  314. }
  315. MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_width_obj, microbit_image_width);
  316. mp_obj_t microbit_image_height(mp_obj_t self_in) {
  317. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  318. return MP_OBJ_NEW_SMALL_INT(imageHeight(self));
  319. }
  320. MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_height_obj, microbit_image_height);
  321. mp_obj_t microbit_image_get_pixel(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) {
  322. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  323. mp_int_t x = mp_obj_get_int(x_in);
  324. mp_int_t y = mp_obj_get_int(y_in);
  325. if (x < 0 || y < 0) {
  326. mp_raise_ValueError("index cannot be negative");
  327. }
  328. if (x < imageWidth(self) && y < imageHeight(self)) {
  329. return MP_OBJ_NEW_SMALL_INT(imageGetPixelValue(self, x, y));
  330. }
  331. mp_raise_ValueError("index too large");
  332. }
  333. MP_DEFINE_CONST_FUN_OBJ_3(microbit_image_get_pixel_obj, microbit_image_get_pixel);
  334. /* Raise an exception if not mutable */
  335. static void check_mutability(microbit_image_obj_t *self) {
  336. if (self->base.five) {
  337. nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "image cannot be modified (try copying first)"));
  338. }
  339. }
  340. mp_obj_t microbit_image_set_pixel(mp_uint_t n_args, const mp_obj_t *args) {
  341. (void)n_args;
  342. microbit_image_obj_t *self = (microbit_image_obj_t*)args[0];
  343. check_mutability(self);
  344. mp_int_t x = mp_obj_get_int(args[1]);
  345. mp_int_t y = mp_obj_get_int(args[2]);
  346. if (x < 0 || y < 0) {
  347. mp_raise_ValueError("index cannot be negative");
  348. }
  349. mp_int_t bright = mp_obj_get_int(args[3]);
  350. if (bright < 0 || bright > MAX_BRIGHTNESS)
  351. mp_raise_ValueError("brightness out of bounds.");
  352. if (x < imageWidth(self) && y < imageHeight(self)) {
  353. greyscaleSetPixelValue(&(self->greyscale), x, y, bright);
  354. return mp_const_none;
  355. }
  356. mp_raise_ValueError("index too large");
  357. }
  358. MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_set_pixel_obj, 4, 4, microbit_image_set_pixel);
  359. mp_obj_t microbit_image_fill(mp_obj_t self_in, mp_obj_t n_in) {
  360. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  361. check_mutability(self);
  362. mp_int_t n = mp_obj_get_int(n_in);
  363. if (n < 0 || n > MAX_BRIGHTNESS) {
  364. mp_raise_ValueError("brightness out of bounds.");
  365. }
  366. greyscaleFill(&self->greyscale, n);
  367. return mp_const_none;
  368. }
  369. MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_fill_obj, microbit_image_fill);
  370. mp_obj_t microbit_image_blit(mp_uint_t n_args, const mp_obj_t *args) {
  371. microbit_image_obj_t *self = (microbit_image_obj_t*)args[0];
  372. check_mutability(self);
  373. mp_obj_t src = args[1];
  374. if (mp_obj_get_type(src) != &microbit_image_type) {
  375. nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "expecting an image"));
  376. }
  377. if (n_args == 7) {
  378. nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
  379. "must specify both offsets"));
  380. }
  381. mp_int_t x = mp_obj_get_int(args[2]);
  382. mp_int_t y = mp_obj_get_int(args[3]);
  383. mp_int_t w = mp_obj_get_int(args[4]);
  384. mp_int_t h = mp_obj_get_int(args[5]);
  385. if (w < 0 || h < 0) {
  386. mp_raise_ValueError("size cannot be negative");
  387. }
  388. mp_int_t xdest;
  389. mp_int_t ydest;
  390. if (n_args == 6) {
  391. xdest = 0;
  392. ydest = 0;
  393. } else {
  394. xdest = mp_obj_get_int(args[6]);
  395. ydest = mp_obj_get_int(args[7]);
  396. }
  397. image_blit((microbit_image_obj_t *)src, &(self->greyscale), x, y, w, h, xdest, ydest);
  398. return mp_const_none;
  399. }
  400. MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_blit_obj, 6, 8, microbit_image_blit);
  401. mp_obj_t microbit_image_crop(mp_uint_t n_args, const mp_obj_t *args) {
  402. (void)n_args;
  403. microbit_image_obj_t *self = (microbit_image_obj_t*)args[0];
  404. mp_int_t x0 = mp_obj_get_int(args[1]);
  405. mp_int_t y0 = mp_obj_get_int(args[2]);
  406. mp_int_t x1 = mp_obj_get_int(args[3]);
  407. mp_int_t y1 = mp_obj_get_int(args[4]);
  408. return image_crop(self, x0, y0, x1, y1);
  409. }
  410. MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_crop_obj, 5, 5, microbit_image_crop);
  411. mp_obj_t microbit_image_shift_left(mp_obj_t self_in, mp_obj_t n_in) {
  412. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  413. mp_int_t n = mp_obj_get_int(n_in);
  414. return image_shift(self, n, 0);
  415. }
  416. MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_left_obj, microbit_image_shift_left);
  417. mp_obj_t microbit_image_shift_right(mp_obj_t self_in, mp_obj_t n_in) {
  418. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  419. mp_int_t n = mp_obj_get_int(n_in);
  420. return image_shift(self, -n, 0);
  421. }
  422. MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_right_obj, microbit_image_shift_right);
  423. mp_obj_t microbit_image_shift_up(mp_obj_t self_in, mp_obj_t n_in) {
  424. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  425. mp_int_t n = mp_obj_get_int(n_in);
  426. return image_shift(self, 0, n);
  427. }
  428. MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_up_obj, microbit_image_shift_up);
  429. mp_obj_t microbit_image_shift_down(mp_obj_t self_in, mp_obj_t n_in) {
  430. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  431. mp_int_t n = mp_obj_get_int(n_in);
  432. return image_shift(self, 0, -n);
  433. }
  434. MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_down_obj, microbit_image_shift_down);
  435. mp_obj_t microbit_image_copy(mp_obj_t self_in) {
  436. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  437. return imageCopy(self);
  438. }
  439. MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_copy_obj, microbit_image_copy);
  440. mp_obj_t microbit_image_invert(mp_obj_t self_in) {
  441. microbit_image_obj_t *self = (microbit_image_obj_t*)self_in;
  442. return imageInvert(self);
  443. }
  444. MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_invert_obj, microbit_image_invert);
  445. STATIC const mp_rom_map_elem_t microbit_image_locals_dict_table[] = {
  446. { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&microbit_image_width_obj) },
  447. { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&microbit_image_height_obj) },
  448. { MP_ROM_QSTR(MP_QSTR_get_pixel), MP_ROM_PTR(&microbit_image_get_pixel_obj) },
  449. { MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&microbit_image_set_pixel_obj) },
  450. { MP_ROM_QSTR(MP_QSTR_shift_left), MP_ROM_PTR(&microbit_image_shift_left_obj) },
  451. { MP_ROM_QSTR(MP_QSTR_shift_right), MP_ROM_PTR(&microbit_image_shift_right_obj) },
  452. { MP_ROM_QSTR(MP_QSTR_shift_up), MP_ROM_PTR(&microbit_image_shift_up_obj) },
  453. { MP_ROM_QSTR(MP_QSTR_shift_down), MP_ROM_PTR(&microbit_image_shift_down_obj) },
  454. { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&microbit_image_copy_obj) },
  455. { MP_ROM_QSTR(MP_QSTR_crop), MP_ROM_PTR(&microbit_image_crop_obj) },
  456. { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&microbit_image_invert_obj) },
  457. { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&microbit_image_fill_obj) },
  458. { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&microbit_image_blit_obj) },
  459. { MP_ROM_QSTR(MP_QSTR_HEART), MP_ROM_PTR(&microbit_const_image_heart_obj) },
  460. { MP_ROM_QSTR(MP_QSTR_HEART_SMALL), MP_ROM_PTR(&microbit_const_image_heart_small_obj) },
  461. { MP_ROM_QSTR(MP_QSTR_HAPPY), MP_ROM_PTR(&microbit_const_image_happy_obj) },
  462. { MP_ROM_QSTR(MP_QSTR_SMILE), MP_ROM_PTR(&microbit_const_image_smile_obj) },
  463. { MP_ROM_QSTR(MP_QSTR_SAD), MP_ROM_PTR(&microbit_const_image_sad_obj) },
  464. { MP_ROM_QSTR(MP_QSTR_CONFUSED), MP_ROM_PTR(&microbit_const_image_confused_obj) },
  465. { MP_ROM_QSTR(MP_QSTR_ANGRY), MP_ROM_PTR(&microbit_const_image_angry_obj) },
  466. { MP_ROM_QSTR(MP_QSTR_ASLEEP), MP_ROM_PTR(&microbit_const_image_asleep_obj) },
  467. { MP_ROM_QSTR(MP_QSTR_SURPRISED), MP_ROM_PTR(&microbit_const_image_surprised_obj) },
  468. { MP_ROM_QSTR(MP_QSTR_SILLY), MP_ROM_PTR(&microbit_const_image_silly_obj) },
  469. { MP_ROM_QSTR(MP_QSTR_FABULOUS), MP_ROM_PTR(&microbit_const_image_fabulous_obj) },
  470. { MP_ROM_QSTR(MP_QSTR_MEH), MP_ROM_PTR(&microbit_const_image_meh_obj) },
  471. { MP_ROM_QSTR(MP_QSTR_YES), MP_ROM_PTR(&microbit_const_image_yes_obj) },
  472. { MP_ROM_QSTR(MP_QSTR_NO), MP_ROM_PTR(&microbit_const_image_no_obj) },
  473. { MP_ROM_QSTR(MP_QSTR_CLOCK12), MP_ROM_PTR(&microbit_const_image_clock12_obj) },
  474. { MP_ROM_QSTR(MP_QSTR_CLOCK1), MP_ROM_PTR(&microbit_const_image_clock1_obj) },
  475. { MP_ROM_QSTR(MP_QSTR_CLOCK2), MP_ROM_PTR(&microbit_const_image_clock2_obj) },
  476. { MP_ROM_QSTR(MP_QSTR_CLOCK3), MP_ROM_PTR(&microbit_const_image_clock3_obj) },
  477. { MP_ROM_QSTR(MP_QSTR_CLOCK4), MP_ROM_PTR(&microbit_const_image_clock4_obj) },
  478. { MP_ROM_QSTR(MP_QSTR_CLOCK5), MP_ROM_PTR(&microbit_const_image_clock5_obj) },
  479. { MP_ROM_QSTR(MP_QSTR_CLOCK6), MP_ROM_PTR(&microbit_const_image_clock6_obj) },
  480. { MP_ROM_QSTR(MP_QSTR_CLOCK7), MP_ROM_PTR(&microbit_const_image_clock7_obj) },
  481. { MP_ROM_QSTR(MP_QSTR_CLOCK8), MP_ROM_PTR(&microbit_const_image_clock8_obj) },
  482. { MP_ROM_QSTR(MP_QSTR_CLOCK9), MP_ROM_PTR(&microbit_const_image_clock9_obj) },
  483. { MP_ROM_QSTR(MP_QSTR_CLOCK10), MP_ROM_PTR(&microbit_const_image_clock10_obj) },
  484. { MP_ROM_QSTR(MP_QSTR_CLOCK11), MP_ROM_PTR(&microbit_const_image_clock11_obj) },
  485. { MP_ROM_QSTR(MP_QSTR_ARROW_N), MP_ROM_PTR(&microbit_const_image_arrow_n_obj) },
  486. { MP_ROM_QSTR(MP_QSTR_ARROW_NE), MP_ROM_PTR(&microbit_const_image_arrow_ne_obj) },
  487. { MP_ROM_QSTR(MP_QSTR_ARROW_E), MP_ROM_PTR(&microbit_const_image_arrow_e_obj) },
  488. { MP_ROM_QSTR(MP_QSTR_ARROW_SE), MP_ROM_PTR(&microbit_const_image_arrow_se_obj) },
  489. { MP_ROM_QSTR(MP_QSTR_ARROW_S), MP_ROM_PTR(&microbit_const_image_arrow_s_obj) },
  490. { MP_ROM_QSTR(MP_QSTR_ARROW_SW), MP_ROM_PTR(&microbit_const_image_arrow_sw_obj) },
  491. { MP_ROM_QSTR(MP_QSTR_ARROW_W), MP_ROM_PTR(&microbit_const_image_arrow_w_obj) },
  492. { MP_ROM_QSTR(MP_QSTR_ARROW_NW), MP_ROM_PTR(&microbit_const_image_arrow_nw_obj) },
  493. { MP_ROM_QSTR(MP_QSTR_TRIANGLE), MP_ROM_PTR(&microbit_const_image_triangle_obj) },
  494. { MP_ROM_QSTR(MP_QSTR_TRIANGLE_LEFT), MP_ROM_PTR(&microbit_const_image_triangle_left_obj) },
  495. { MP_ROM_QSTR(MP_QSTR_CHESSBOARD), MP_ROM_PTR(&microbit_const_image_chessboard_obj) },
  496. { MP_ROM_QSTR(MP_QSTR_DIAMOND), MP_ROM_PTR(&microbit_const_image_diamond_obj) },
  497. { MP_ROM_QSTR(MP_QSTR_DIAMOND_SMALL), MP_ROM_PTR(&microbit_const_image_diamond_small_obj) },
  498. { MP_ROM_QSTR(MP_QSTR_SQUARE), MP_ROM_PTR(&microbit_const_image_square_obj) },
  499. { MP_ROM_QSTR(MP_QSTR_SQUARE_SMALL), MP_ROM_PTR(&microbit_const_image_square_small_obj) },
  500. { MP_ROM_QSTR(MP_QSTR_RABBIT), MP_ROM_PTR(&microbit_const_image_rabbit) },
  501. { MP_ROM_QSTR(MP_QSTR_COW), MP_ROM_PTR(&microbit_const_image_cow) },
  502. { MP_ROM_QSTR(MP_QSTR_MUSIC_CROTCHET), MP_ROM_PTR(&microbit_const_image_music_crotchet_obj) },
  503. { MP_ROM_QSTR(MP_QSTR_MUSIC_QUAVER), MP_ROM_PTR(&microbit_const_image_music_quaver_obj) },
  504. { MP_ROM_QSTR(MP_QSTR_MUSIC_QUAVERS), MP_ROM_PTR(&microbit_const_image_music_quavers_obj) },
  505. { MP_ROM_QSTR(MP_QSTR_PITCHFORK), MP_ROM_PTR(&microbit_const_image_pitchfork_obj) },
  506. { MP_ROM_QSTR(MP_QSTR_XMAS), MP_ROM_PTR(&microbit_const_image_xmas_obj) },
  507. { MP_ROM_QSTR(MP_QSTR_PACMAN), MP_ROM_PTR(&microbit_const_image_pacman_obj) },
  508. { MP_ROM_QSTR(MP_QSTR_TARGET), MP_ROM_PTR(&microbit_const_image_target_obj) },
  509. { MP_ROM_QSTR(MP_QSTR_ALL_CLOCKS), MP_ROM_PTR(&microbit_const_image_all_clocks_tuple_obj) },
  510. { MP_ROM_QSTR(MP_QSTR_ALL_ARROWS), MP_ROM_PTR(&microbit_const_image_all_arrows_tuple_obj) },
  511. { MP_ROM_QSTR(MP_QSTR_TSHIRT), MP_ROM_PTR(&microbit_const_image_tshirt_obj) },
  512. { MP_ROM_QSTR(MP_QSTR_ROLLERSKATE), MP_ROM_PTR(&microbit_const_image_rollerskate_obj) },
  513. { MP_ROM_QSTR(MP_QSTR_DUCK), MP_ROM_PTR(&microbit_const_image_duck_obj) },
  514. { MP_ROM_QSTR(MP_QSTR_HOUSE), MP_ROM_PTR(&microbit_const_image_house_obj) },
  515. { MP_ROM_QSTR(MP_QSTR_TORTOISE), MP_ROM_PTR(&microbit_const_image_tortoise_obj) },
  516. { MP_ROM_QSTR(MP_QSTR_BUTTERFLY), MP_ROM_PTR(&microbit_const_image_butterfly_obj) },
  517. { MP_ROM_QSTR(MP_QSTR_STICKFIGURE), MP_ROM_PTR(&microbit_const_image_stickfigure_obj) },
  518. { MP_ROM_QSTR(MP_QSTR_GHOST), MP_ROM_PTR(&microbit_const_image_ghost_obj) },
  519. { MP_ROM_QSTR(MP_QSTR_SWORD), MP_ROM_PTR(&microbit_const_image_sword_obj) },
  520. { MP_ROM_QSTR(MP_QSTR_GIRAFFE), MP_ROM_PTR(&microbit_const_image_giraffe_obj) },
  521. { MP_ROM_QSTR(MP_QSTR_SKULL), MP_ROM_PTR(&microbit_const_image_skull_obj) },
  522. { MP_ROM_QSTR(MP_QSTR_UMBRELLA), MP_ROM_PTR(&microbit_const_image_umbrella_obj) },
  523. { MP_ROM_QSTR(MP_QSTR_SNAKE), MP_ROM_PTR(&microbit_const_image_snake_obj) },
  524. };
  525. STATIC MP_DEFINE_CONST_DICT(microbit_image_locals_dict, microbit_image_locals_dict_table);
  526. #define THE_FONT font_pendolino3_5x5_pad3msb
  527. #define ASCII_START 32
  528. #define ASCII_END 126
  529. STATIC const unsigned char *get_font_data_from_char(char c) {
  530. if (c < ASCII_START || c > ASCII_END) {
  531. c = '?';
  532. }
  533. int offset = (c-ASCII_START) * 5;
  534. return THE_FONT + offset;
  535. }
  536. STATIC mp_int_t get_pixel_from_font_data(const unsigned char *data, int x, int y) {
  537. /* The following logic belongs in MicroBitFont */
  538. return ((data[y]>>(4-x))&1);
  539. }
  540. void microbit_image_set_from_char(greyscale_t *img, char c) {
  541. const unsigned char *data = get_font_data_from_char(c);
  542. for (int x = 0; x < 5; ++x) {
  543. for (int y = 0; y < 5; ++y) {
  544. greyscaleSetPixelValue(img, x, y, get_pixel_from_font_data(data, x, y)*MAX_BRIGHTNESS);
  545. }
  546. }
  547. }
  548. microbit_image_obj_t *microbit_image_for_char(char c) {
  549. greyscale_t *result = greyscale_new(5,5);
  550. microbit_image_set_from_char(result, c);
  551. return (microbit_image_obj_t *)result;
  552. }
  553. #if MICROPY_PY_BUILTINS_FLOAT
  554. microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_float_t fval) {
  555. #else
  556. microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_int_t fval) {
  557. #endif
  558. if (fval < 0)
  559. mp_raise_ValueError("Brightness multiplier must not be negative.");
  560. greyscale_t *result = greyscale_new(imageWidth(lhs), imageHeight(lhs));
  561. for (int x = 0; x < imageWidth(lhs); ++x) {
  562. for (int y = 0; y < imageWidth(lhs); ++y) {
  563. #if MICROPY_PY_BUILTINS_FLOAT
  564. int val = min((int)imageGetPixelValue(lhs, x,y)*fval+0.5, MAX_BRIGHTNESS);
  565. #else
  566. int val = min((int)imageGetPixelValue(lhs, x,y)*fval, MAX_BRIGHTNESS);
  567. #endif
  568. greyscaleSetPixelValue(result, x, y, val);
  569. }
  570. }
  571. return (microbit_image_obj_t *)result;
  572. }
  573. microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_image_obj_t *rhs, bool add) {
  574. mp_int_t h = imageHeight(lhs);
  575. mp_int_t w = imageWidth(lhs);
  576. if (imageHeight(rhs) != h || imageWidth(lhs) != w) {
  577. // TODO: verify that image width in test above should really test (lhs != w)
  578. mp_raise_ValueError("Images must be the same size.");
  579. }
  580. greyscale_t *result = greyscale_new(w, h);
  581. for (int x = 0; x < w; ++x) {
  582. for (int y = 0; y < h; ++y) {
  583. int val;
  584. int lval = imageGetPixelValue(lhs, x,y);
  585. int rval = imageGetPixelValue(rhs, x,y);
  586. if (add)
  587. val = min(lval + rval, MAX_BRIGHTNESS);
  588. else
  589. val = max(0, lval - rval);
  590. greyscaleSetPixelValue(result, x, y, val);
  591. }
  592. }
  593. return (microbit_image_obj_t *)result;
  594. }
  595. STATIC mp_obj_t image_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
  596. if (mp_obj_get_type(lhs_in) != &microbit_image_type) {
  597. return MP_OBJ_NULL; // op not supported
  598. }
  599. microbit_image_obj_t *lhs = (microbit_image_obj_t *)lhs_in;
  600. switch(op) {
  601. case MP_BINARY_OP_ADD:
  602. case MP_BINARY_OP_SUBTRACT:
  603. break;
  604. case MP_BINARY_OP_MULTIPLY:
  605. #if MICROPY_PY_BUILTINS_FLOAT
  606. return microbit_image_dim(lhs, mp_obj_get_float(rhs_in));
  607. #else
  608. return microbit_image_dim(lhs, mp_obj_get_int(rhs_in) * 10);
  609. #endif
  610. case MP_BINARY_OP_TRUE_DIVIDE:
  611. #if MICROPY_PY_BUILTINS_FLOAT
  612. return microbit_image_dim(lhs, 1.0/mp_obj_get_float(rhs_in));
  613. #else
  614. break;
  615. case MP_BINARY_OP_FLOOR_DIVIDE:
  616. return microbit_image_dim(lhs, (100/mp_obj_get_int(rhs_in) + 5) / 10);
  617. #endif
  618. default:
  619. return MP_OBJ_NULL; // op not supported
  620. }
  621. if (mp_obj_get_type(rhs_in) != &microbit_image_type) {
  622. return MP_OBJ_NULL; // op not supported
  623. }
  624. return microbit_image_sum(lhs, (microbit_image_obj_t *)rhs_in, op == MP_BINARY_OP_ADD);
  625. }
  626. const mp_obj_type_t microbit_image_type = {
  627. { &mp_type_type },
  628. .name = MP_QSTR_MicroBitImage,
  629. .print = microbit_image_print,
  630. .make_new = microbit_image_make_new,
  631. .call = NULL,
  632. .unary_op = NULL,
  633. .binary_op = image_binary_op,
  634. .attr = NULL,
  635. .subscr = NULL,
  636. .getiter = NULL,
  637. .iternext = NULL,
  638. .buffer_p = {NULL},
  639. .locals_dict = (mp_obj_dict_t*)&microbit_image_locals_dict,
  640. };
  641. typedef struct _scrolling_string_t {
  642. mp_obj_base_t base;
  643. char const *str;
  644. mp_uint_t len;
  645. mp_obj_t ref;
  646. bool monospace;
  647. bool repeat;
  648. } scrolling_string_t;
  649. typedef struct _scrolling_string_iterator_t {
  650. mp_obj_base_t base;
  651. mp_obj_t ref;
  652. greyscale_t *img;
  653. char const *next_char;
  654. char const *start;
  655. char const *end;
  656. uint8_t offset;
  657. uint8_t offset_limit;
  658. bool monospace;
  659. bool repeat;
  660. char right;
  661. } scrolling_string_iterator_t;
  662. extern const mp_obj_type_t microbit_scrolling_string_type;
  663. extern const mp_obj_type_t microbit_scrolling_string_iterator_type;
  664. mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref, bool monospace, bool repeat) {
  665. scrolling_string_t *result = m_new_obj(scrolling_string_t);
  666. result->base.type = &microbit_scrolling_string_type;
  667. result->str = str;
  668. result->len = len;
  669. result->ref = ref;
  670. result->monospace = monospace;
  671. result->repeat = repeat;
  672. return result;
  673. }
  674. STATIC int font_column_non_blank(const unsigned char *font_data, unsigned int col) {
  675. for (int y = 0; y < 5; ++y) {
  676. if (get_pixel_from_font_data(font_data, col, y)) {
  677. return 1;
  678. }
  679. }
  680. return 0;
  681. }
  682. /* Not strictly the rightmost non-blank column, but the rightmost in columns 2,3 or 4. */
  683. STATIC unsigned int rightmost_non_blank_column(const unsigned char *font_data) {
  684. if (font_column_non_blank(font_data, 4)) {
  685. return 4;
  686. }
  687. if (font_column_non_blank(font_data, 3)) {
  688. return 3;
  689. }
  690. return 2;
  691. }
  692. static void restart(scrolling_string_iterator_t *iter) {
  693. iter->next_char = iter->start;
  694. iter->offset = 0;
  695. if (iter->start < iter->end) {
  696. iter->right = *iter->next_char;
  697. if (iter->monospace) {
  698. iter->offset_limit = 5;
  699. } else {
  700. iter->offset_limit = rightmost_non_blank_column(get_font_data_from_char(iter->right)) + 1;
  701. }
  702. } else {
  703. iter->right = ' ';
  704. iter->offset_limit = 5;
  705. }
  706. }
  707. STATIC mp_obj_t get_microbit_scrolling_string_iter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
  708. (void)iter_buf;
  709. scrolling_string_t *str = (scrolling_string_t *)o_in;
  710. scrolling_string_iterator_t *result = m_new_obj(scrolling_string_iterator_t);
  711. result->base.type = &microbit_scrolling_string_iterator_type;
  712. result->img = greyscale_new(5,5);
  713. result->start = str->str;
  714. result->ref = str->ref;
  715. result->monospace = str->monospace;
  716. result->end = result->start + str->len;
  717. result->repeat = str->repeat;
  718. restart(result);
  719. return result;
  720. }
  721. STATIC mp_obj_t microbit_scrolling_string_iter_next(mp_obj_t o_in) {
  722. scrolling_string_iterator_t *iter = (scrolling_string_iterator_t *)o_in;
  723. if (iter->next_char == iter->end && iter->offset == 5) {
  724. if (iter->repeat) {
  725. restart(iter);
  726. greyscaleClear(iter->img);
  727. } else {
  728. return MP_OBJ_STOP_ITERATION;
  729. }
  730. }
  731. for (int x = 0; x < 4; x++) {
  732. for (int y = 0; y < 5; y++) {
  733. greyscaleSetPixelValue(iter->img, x, y, greyscaleGetPixelValue(iter->img, x+1, y));
  734. }
  735. }
  736. for (int y = 0; y < 5; y++) {
  737. greyscaleSetPixelValue(iter->img, 4, y, 0);
  738. }
  739. const unsigned char *font_data;
  740. if (iter->offset < iter->offset_limit) {
  741. font_data = get_font_data_from_char(iter->right);
  742. for (int y = 0; y < 5; ++y) {
  743. int pix = get_pixel_from_font_data(font_data, iter->offset, y)*MAX_BRIGHTNESS;
  744. greyscaleSetPixelValue(iter->img, 4, y, pix);
  745. }
  746. } else if (iter->offset == iter->offset_limit) {
  747. ++iter->next_char;
  748. if (iter->next_char == iter->end) {
  749. iter->right = ' ';
  750. iter->offset_limit = 5;
  751. iter->offset = 0;
  752. } else {
  753. iter->right = *iter->next_char;
  754. font_data = get_font_data_from_char(iter->right);
  755. if (iter->monospace) {
  756. iter->offset = -1;
  757. iter->offset_limit = 5;
  758. } else {
  759. iter->offset = -font_column_non_blank(font_data, 0);
  760. iter->offset_limit = rightmost_non_blank_column(font_data)+1;
  761. }
  762. }
  763. }
  764. ++iter->offset;
  765. return iter->img;
  766. }
  767. const mp_obj_type_t microbit_scrolling_string_type = {
  768. { &mp_type_type },
  769. .name = MP_QSTR_ScrollingString,
  770. .print = NULL,
  771. .make_new = NULL,
  772. .call = NULL,
  773. .unary_op = NULL,
  774. .binary_op = NULL,
  775. .attr = NULL,
  776. .subscr = NULL,
  777. .getiter = get_microbit_scrolling_string_iter,
  778. .iternext = NULL,
  779. .buffer_p = {NULL},
  780. .locals_dict = NULL,
  781. };
  782. const mp_obj_type_t microbit_scrolling_string_iterator_type = {
  783. { &mp_type_type },
  784. .name = MP_QSTR_iterator,
  785. .print = NULL,
  786. .make_new = NULL,
  787. .call = NULL,
  788. .unary_op = NULL,
  789. .binary_op = NULL,
  790. .attr = NULL,
  791. .subscr = NULL,
  792. .getiter = mp_identity_getiter,
  793. .iternext = microbit_scrolling_string_iter_next,
  794. .buffer_p = {NULL},
  795. .locals_dict = NULL,
  796. };
  797. /** Facade types to present a string as a sequence of images.
  798. * These are necessary to avoid allocation during iteration,
  799. * which may happen in interrupt handlers.
  800. */
  801. typedef struct _string_image_facade_t {
  802. mp_obj_base_t base;
  803. mp_obj_t string;
  804. greyscale_t *image;
  805. } string_image_facade_t;
  806. static mp_obj_t string_image_facade_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
  807. if (value == MP_OBJ_SENTINEL) {
  808. // Fill in image
  809. string_image_facade_t *self = (string_image_facade_t *)self_in;
  810. mp_uint_t len;
  811. const char *text = mp_obj_str_get_data(self->string, &len);
  812. mp_uint_t index = mp_get_index(self->base.type, len, index_in, false);
  813. microbit_image_set_from_char(self->image, text[index]);
  814. return self->image;
  815. } else {
  816. return MP_OBJ_NULL; // op not supported
  817. }
  818. }
  819. static mp_obj_t facade_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
  820. string_image_facade_t *self = (string_image_facade_t *)self_in;
  821. switch (op) {
  822. case MP_UNARY_OP_LEN:
  823. return mp_obj_len(self->string);
  824. default: return MP_OBJ_NULL; // op not supported
  825. }
  826. }
  827. static mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in, mp_obj_iter_buf_t *iter_buf);
  828. const mp_obj_type_t string_image_facade_type = {
  829. { &mp_type_type },
  830. .name = MP_QSTR_Facade,
  831. .print = NULL,
  832. .make_new = NULL,
  833. .call = NULL,
  834. .unary_op = facade_unary_op,
  835. .binary_op = NULL,
  836. .attr = NULL,
  837. .subscr = string_image_facade_subscr,
  838. .getiter = microbit_facade_iterator,
  839. .iternext = NULL,
  840. .buffer_p = {NULL},
  841. NULL
  842. };
  843. typedef struct _facade_iterator_t {
  844. mp_obj_base_t base;
  845. mp_obj_t string;
  846. mp_uint_t index;
  847. greyscale_t *image;
  848. } facade_iterator_t;
  849. mp_obj_t microbit_string_facade(mp_obj_t string) {
  850. string_image_facade_t *result = m_new_obj(string_image_facade_t);
  851. result->base.type = &string_image_facade_type;
  852. result->string = string;
  853. result->image = greyscale_new(5,5);
  854. return result;
  855. }
  856. static mp_obj_t microbit_facade_iter_next(mp_obj_t iter_in) {
  857. facade_iterator_t *iter = (facade_iterator_t *)iter_in;
  858. mp_uint_t len;
  859. const char *text = mp_obj_str_get_data(iter->string, &len);
  860. if (iter->index >= len) {
  861. return MP_OBJ_STOP_ITERATION;
  862. }
  863. microbit_image_set_from_char(iter->image, text[iter->index]);
  864. iter->index++;
  865. return iter->image;
  866. }
  867. const mp_obj_type_t microbit_facade_iterator_type = {
  868. { &mp_type_type },
  869. .name = MP_QSTR_iterator,
  870. .print = NULL,
  871. .make_new = NULL,
  872. .call = NULL,
  873. .unary_op = NULL,
  874. .binary_op = NULL,
  875. .attr = NULL,
  876. .subscr = NULL,
  877. .getiter = mp_identity_getiter,
  878. .iternext = microbit_facade_iter_next,
  879. .buffer_p = {NULL},
  880. NULL
  881. };
  882. mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in, mp_obj_iter_buf_t *iter_buf) {
  883. (void)iter_buf;
  884. facade_iterator_t *result = m_new_obj(facade_iterator_t);
  885. string_image_facade_t *iterable = (string_image_facade_t *)iterable_in;
  886. result->base.type = &microbit_facade_iterator_type;
  887. result->string = iterable->string;
  888. result->image = iterable->image;
  889. result->index = 0;
  890. return result;
  891. }