Skip to content

Commit 1437aea

Browse files
markshannondpgeorge
authored andcommitted
Improve legibility of scrolling text.
1 parent ab272f8 commit 1437aea

File tree

7 files changed

+69
-22
lines changed

7 files changed

+69
-22
lines changed

examples/compass.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
# You need to spin the microbit about a few times to help calibrate the compass
88

99
# Scroll a message to encourage user to help calibrate the compass
10-
# "Spin" "Me!" reads better than "Spin Me!" due to lack of kerning
11-
display.scroll("Spin")
12-
display.scroll("Me!")
10+
display.scroll("Spin Me!")
1311

1412
# Start calibrating
1513
compass.calibrate()

inc/genhdr/qstrdefs.generated.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ QDEF(MP_QSTR_shift_left, (const byte*)"\xa1\x0a" "shift_left")
305305
QDEF(MP_QSTR_shift_right, (const byte*)"\xba\x0b" "shift_right")
306306
QDEF(MP_QSTR_shift_up, (const byte*)"\xdf\x08" "shift_up")
307307
QDEF(MP_QSTR_shift_down, (const byte*)"\x48\x0a" "shift_down")
308+
QDEF(MP_QSTR_monospace, (const byte*)"\xe2\x09" "monospace")
308309
QDEF(MP_QSTR_HEART, (const byte*)"\x0f\x05" "HEART")
309310
QDEF(MP_QSTR_HEART_SMALL, (const byte*)"\xcf\x0b" "HEART_SMALL")
310311
QDEF(MP_QSTR_HAPPY, (const byte*)"\x15\x05" "HAPPY")

inc/microbit/MicroBitCustomConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@
1919
#define MICROBIT_BLE_EVENT_SERVICE 0
2020
#define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE 0
2121

22+
// Slow this down a bit as proportional spacing reduces length
23+
#define MICROBIT_DEFAULT_SCROLL_SPEED 150
24+
2225
#endif

inc/microbit/microbitimage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ typedef union _microbit_image_obj_t {
6363
microbit_image_obj_t *microbit_image_for_char(char c);
6464
mp_obj_t microbit_image_slice(microbit_image_obj_t *img, mp_int_t start, mp_int_t width, mp_int_t stride);
6565
/* ref exists so that we can pull a string out of an object and not have it GC'ed while oterating over it */
66-
mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref);
66+
mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref, bool monospace);
6767

6868
#define SMALL_IMAGE(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p44) \
6969
{ \

inc/microbit/qstrdefsport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Q(shift_left)
6363
Q(shift_right)
6464
Q(shift_up)
6565
Q(shift_down)
66+
Q(monospace)
6667
Q(HEART)
6768
Q(HEART_SMALL)
6869
Q(HAPPY)

source/microbit/microbitdisplay.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,25 +352,26 @@ STATIC mp_obj_t microbit_display_animate_func(mp_uint_t n_args, const mp_obj_t *
352352
MP_DEFINE_CONST_FUN_OBJ_KW(microbit_display_animate_obj, 2, microbit_display_animate_func);
353353

354354
void microbit_display_scroll(microbit_display_obj_t *self, const char* str, mp_int_t len, bool wait) {
355-
mp_obj_t iterable = scrolling_string_image_iterable(str, len, NULL);
355+
mp_obj_t iterable = scrolling_string_image_iterable(str, len, NULL, false);
356356
microbit_display_animate(self, iterable, MICROBIT_DEFAULT_SCROLL_SPEED, wait, false);
357357
}
358358

359359

360360
mp_obj_t microbit_display_scroll_func(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
361361
static const mp_arg_t scroll_allowed_args[] = {
362-
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
363-
{ MP_QSTR_delay, MP_ARG_INT, {.u_int = MICROBIT_DEFAULT_SCROLL_SPEED} },
364-
{ MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
365-
{ MP_QSTR_loop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
362+
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
363+
{ MP_QSTR_delay, MP_ARG_INT, {.u_int = MICROBIT_DEFAULT_SCROLL_SPEED} },
364+
{ MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
365+
{ MP_QSTR_loop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
366+
{ MP_QSTR_monospace, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
366367
};
367368
// Parse the args.
368369
microbit_display_obj_t *self = (microbit_display_obj_t*)pos_args[0];
369370
mp_arg_val_t args[MP_ARRAY_SIZE(scroll_allowed_args)];
370371
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(scroll_allowed_args), scroll_allowed_args, args);
371372
mp_uint_t len;
372373
const char* str = mp_obj_str_get_data(args[0].u_obj, &len);
373-
mp_obj_t iterable = scrolling_string_image_iterable(str, len, args[0].u_obj);
374+
mp_obj_t iterable = scrolling_string_image_iterable(str, len, args[0].u_obj, args[4].u_bool /*monospace?*/);
374375
microbit_display_animate(self, iterable, args[1].u_int /*delay*/, args[2].u_bool/*wait?*/, args[3].u_bool /*loop?*/);
375376
return mp_const_none;
376377
}

source/microbit/microbitimage.cpp

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -517,15 +517,15 @@ STATIC const unsigned char *get_font_data_from_char(char c) {
517517

518518
STATIC mp_int_t get_pixel_from_font_data(const unsigned char *data, int x, int y) {
519519
/* The following logic belongs in MicroBitFont */
520-
return ((data[y]>>(4-x))&1)*MAX_BRIGHTNESS;
520+
return ((data[y]>>(4-x))&1);
521521
}
522522

523523
microbit_image_obj_t *microbit_image_for_char(char c) {
524524
const unsigned char *data = get_font_data_from_char(c);
525525
greyscale_t *result = greyscale_new(5,5);
526526
for (int x = 0; x < 5; ++x) {
527527
for (int y = 0; y < 5; ++y) {
528-
result->setPixelValue(x, y, get_pixel_from_font_data(data, x, y));
528+
result->setPixelValue(x, y, get_pixel_from_font_data(data, x, y)*MAX_BRIGHTNESS);
529529
}
530530
}
531531
return (microbit_image_obj_t *)result;
@@ -702,6 +702,7 @@ typedef struct _scrolling_string_t {
702702
char const *str;
703703
mp_uint_t len;
704704
mp_obj_t ref;
705+
bool monospace;
705706
} scrolling_string_t;
706707

707708
typedef struct _scrolling_string_iterator_t {
@@ -711,21 +712,44 @@ typedef struct _scrolling_string_iterator_t {
711712
char const *next_char;
712713
char const *end;
713714
uint8_t offset;
715+
uint8_t offset_limit;
716+
bool monospace;
714717
char right;
715718
} scrolling_string_iterator_t;
716719

717720
extern const mp_obj_type_t microbit_scrolling_string_type;
718721
extern const mp_obj_type_t microbit_scrolling_string_iterator_type;
719722

720-
mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref) {
723+
mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref, bool monospace) {
721724
scrolling_string_t *result = m_new_obj(scrolling_string_t);
722725
result->base.type = &microbit_scrolling_string_type;
723726
result->str = str;
724727
result->len = len;
725728
result->ref = ref;
729+
result->monospace = monospace;
726730
return result;
727731
}
728732

733+
STATIC int font_column_non_blank(const unsigned char *font_data, unsigned int col) {
734+
for (int y = 0; y < 5; ++y) {
735+
if (get_pixel_from_font_data(font_data, col, y)) {
736+
return 1;
737+
}
738+
}
739+
return 0;
740+
}
741+
742+
/* Not strictly the rightmost non-blank column, but the rightmost in columns 2,3 or 4. */
743+
STATIC unsigned int rightmost_non_blank_column(const unsigned char *font_data) {
744+
if (font_column_non_blank(font_data, 4)) {
745+
return 4;
746+
}
747+
if (font_column_non_blank(font_data, 3)) {
748+
return 3;
749+
}
750+
return 2;
751+
}
752+
729753
STATIC mp_obj_t get_microbit_scrolling_string_iter(mp_obj_t o_in) {
730754
scrolling_string_t *str = (scrolling_string_t *)o_in;
731755
scrolling_string_iterator_t *result = m_new_obj(scrolling_string_iterator_t);
@@ -734,11 +758,19 @@ STATIC mp_obj_t get_microbit_scrolling_string_iter(mp_obj_t o_in) {
734758
result->offset = 0;
735759
result->next_char = str->str;
736760
result->ref = str->ref;
761+
result->monospace = str->monospace;
737762
result->end = result->next_char + str->len;
738-
if (str->len)
763+
if (str->len) {
739764
result->right = *result->next_char;
740-
else
765+
if (result->monospace) {
766+
result->offset_limit = 5;
767+
} else {
768+
result->offset_limit = rightmost_non_blank_column(get_font_data_from_char(result->right)) + 1;
769+
}
770+
} else {
741771
result->right = ' ';
772+
result->offset_limit = 5;
773+
}
742774
return result;
743775
}
744776

@@ -749,19 +781,30 @@ STATIC mp_obj_t microbit_scrolling_string_iter_next(mp_obj_t o_in) {
749781
}
750782
greyscale_t *result = iter->img->shiftLeft(1);
751783
iter->img = (microbit_image_obj_t*)result;
752-
const unsigned char *font_data = get_font_data_from_char(iter->right);
753-
if (iter->offset < 5) {
784+
const unsigned char *font_data;
785+
if (iter->offset < iter->offset_limit) {
786+
font_data = get_font_data_from_char(iter->right);
754787
for (int y = 0; y < 5; ++y) {
755-
int pix = get_pixel_from_font_data(font_data, iter->offset, y);
788+
int pix = get_pixel_from_font_data(font_data, iter->offset, y)*MAX_BRIGHTNESS;
756789
result->setPixelValue(4, y, pix);
757790
}
758-
} else if (iter->offset == 6) {
791+
} else if (iter->offset == iter->offset_limit) {
759792
++iter->next_char;
760-
if (iter->next_char == iter->end)
793+
if (iter->next_char == iter->end) {
761794
iter->right = ' ';
762-
else
795+
iter->offset_limit = 5;
796+
iter->offset = 0;
797+
} else {
763798
iter->right = *iter->next_char;
764-
iter->offset = -1;
799+
font_data = get_font_data_from_char(iter->right);
800+
if (iter->monospace) {
801+
iter->offset = -1;
802+
iter->offset_limit = 5;
803+
} else {
804+
iter->offset = -font_column_non_blank(font_data, 0);
805+
iter->offset_limit = rightmost_non_blank_column(font_data)+1;
806+
}
807+
}
765808
}
766809
++iter->offset;
767810
return result;

0 commit comments

Comments
 (0)