Skip to content

Commit

Permalink
Graphics: Graphics.stringMetrics now returns 'unrenderableChars/image…
Browse files Browse the repository at this point in the history
…Count/maxImageHeight' for much more info about rendering strings
  • Loading branch information
gfwilliams committed Nov 17, 2023
1 parent 5b06cd6 commit 27a08f7
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 24 deletions.
2 changes: 1 addition & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
Bangle.js2: Do a soft reset every time we start the SPL06 pressure sensor (stops occasional sensor lockup)
Graphics: wrapString will now wrap lines on comma, dot, dash, and between images
Graphics: Improve PBF font loading to handle v3, plus Espruino extension to handle >10k glyphs in one file
Graphics: Graphics.stringMetrics now returns 'unknownChars' to indicate if a font can't render a character in the String
Fix unicode in object accesses, eg c["\u00FC"]=42 (fix #2429)
Storage: Storage.writeJSON now escapes with a more compact `\x##`/`\#` character escape notation (still valid JSON)
This allows Espruino to save non-unicode strings with characters in the unicode range, and to also re-load them as non-unicode
Graphics: Graphics.stringMetrics now returns 'unrenderableChars/imageCount/maxImageHeight' for much more info about rendering strings

2v19 : Fix Object.values/entries for numeric keys after 2v18 regression (fix #2375)
nRF52: for SD>5 use static buffers for advertising and scan response data (#2367)
Expand Down
71 changes: 48 additions & 23 deletions libs/graphics/jswrap_graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -2224,13 +2224,22 @@ int jswrap_graphics_getFontHeight(JsVar *parent) {
#endif
}

typedef struct {
int stringWidth; // width in pixels
int stringHeight; // height in pixels
bool unrenderableChars; // are any chars in this not renderable in the current font?
#ifndef SAVE_ON_FLASH
int imageCount; // how many inline images are in this string?
int maxImageHeight; // maximum height of image in this string
#endif
} StringMetricsResult;

/** Work out the width and height of a bit of text. If 'lineStartIndex' is -1 the whole string is used
* otherwise *just* the line of text starting at that char index is used
*/
void _jswrap_graphics_stringMetrics(JsGraphics *gfx, JsVar *var, int lineStartIndex, int *stringWidth, int *stringHeight, bool *hasAllChars) {
* otherwise *just* the line of text starting at that char index is used */
void _jswrap_graphics_stringMetrics(JsGraphics *gfx, JsVar *var, int lineStartIndex, StringMetricsResult *result) {
JsGraphicsFontInfo info;
_jswrap_graphics_getFontInfo(gfx, &info);
memset(result, 0, sizeof(result));

int fontHeight = _jswrap_graphics_getFontHeightInternal(gfx, &info);
JsVar *str = jsvAsString(var);
Expand All @@ -2239,7 +2248,7 @@ void _jswrap_graphics_stringMetrics(JsGraphics *gfx, JsVar *var, int lineStartIn
int width = 0;
int height = fontHeight;
int maxWidth = 0;
if (hasAllChars) *hasAllChars = true;

while (jsvStringIteratorHasChar(&it)) {
int ch = jsvStringIteratorGetUTF8CharAndNext(&it);
if (ch=='\n') {
Expand All @@ -2250,31 +2259,34 @@ void _jswrap_graphics_stringMetrics(JsGraphics *gfx, JsVar *var, int lineStartIn
}
#ifndef SAVE_ON_FLASH
if (ch==0) { // If images are described in-line in the string, render them
result->imageCount++;
GfxDrawImageInfo img;
size_t idx = jsvConvertToUTF8Index(str, jsvStringIteratorGetIndex(&it));
if (_jswrap_graphics_parseImage(gfx, str, idx, &img)) {
jsvStringIteratorGotoUTF8(&it, str, idx+img.headerLength+img.bitmapLength);
_jswrap_graphics_freeImageInfo(&img);
// string iterator now points to the next char after image
width += img.width;
if (img.height > result->maxImageHeight)
result->maxImageHeight = img.height;
}
continue;
}
#endif
int w = _jswrap_graphics_getCharWidth(gfx, &info, ch);
width += w;
if (w==0 && hasAllChars) *hasAllChars = false; // assume width=0 means char not found
if (w==0) result->unrenderableChars = true; // assume width=0 means char not found
}
jsvStringIteratorFree(&it);
jsvUnLock(str);
if (stringWidth) *stringWidth = width>maxWidth ? width : maxWidth;
if (stringHeight) *stringHeight = height;
result->stringWidth = width>maxWidth ? width : maxWidth;
result->stringHeight = height;
_jswrap_graphics_freeFontInfo(&info);
}
JsVarInt _jswrap_graphics_stringWidth(JsGraphics *gfx, JsVar *var, int lineStartIndex) {
int w,h;
_jswrap_graphics_stringMetrics(gfx, var, lineStartIndex, &w, &h, NULL);
return w;
StringMetricsResult metrics;
_jswrap_graphics_stringMetrics(gfx, var, lineStartIndex, &metrics);
return metrics.stringWidth;
}

/*JSON{
Expand Down Expand Up @@ -2303,23 +2315,35 @@ JsVarInt jswrap_graphics_stringWidth(JsVar *parent, JsVar *var) {
"params" : [
["str","JsVar","The string"]
],
"return" : ["JsVar","An object containing `{width,height}` of the string"],
"typescript" : "stringMetrics(str: string): { width: number, height: number, unknownChars: boolean };"
"return" : ["JsVar","An object containing `{width,height,etc}` for the string - see below"],
"typescript" : "stringMetrics(str: string): { width: number, height: number, unknownChars: boolean, imageCount : number, maxImageHeight : number };"
}
Return the width and height in pixels of a string of text in the current font.
Return the width and height in pixels of a string of text in the current font. The object returned contains:
```JS
{
width, // Width of the string in pixels
height, // Height of the string in pixels
unrenderableChars, // If true, the string contains characters that the current font isn't able to render.
imageCount, // How many inline images are in this string?
maxImageHeight, // If there are images, what is the maximum height of all images?
}
```
If `unknownChars` is true, it means the string contains characters that the current font isn't able to render.
*/
JsVar* jswrap_graphics_stringMetrics(JsVar *parent, JsVar *var) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0;
int w,h;
bool hasAllChars;
StringMetricsResult metrics;
JsVar *o = jsvNewObject();
if (o) {
_jswrap_graphics_stringMetrics(&gfx, var, -1, &w, &h, &hasAllChars);
jsvObjectSetChildAndUnLock(o, "width", jsvNewFromInteger(w));
jsvObjectSetChildAndUnLock(o, "height", jsvNewFromInteger(h));
jsvObjectSetChildAndUnLock(o, "unknownChars", jsvNewFromBool(!hasAllChars));
_jswrap_graphics_stringMetrics(&gfx, var, -1, &metrics);
jsvObjectSetChildAndUnLock(o, "width", jsvNewFromInteger(metrics.stringWidth));
jsvObjectSetChildAndUnLock(o, "height", jsvNewFromInteger(metrics.stringHeight));
jsvObjectSetChildAndUnLock(o, "unrenderableChars", jsvNewFromBool(metrics.unrenderableChars));
#ifndef SAVE_ON_FLASH
jsvObjectSetChildAndUnLock(o, "imageCount", jsvNewFromInteger(metrics.imageCount));
jsvObjectSetChildAndUnLock(o, "maxImageHeight", jsvNewFromInteger(metrics.maxImageHeight));
#endif
}
return o;
}
Expand Down Expand Up @@ -2537,9 +2561,10 @@ JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *var, int x, int y, bool
if (gfx.data.fontAlignX<2) // 0=center, 1=right, 2=undefined, 3=left
x = startx - (_jswrap_graphics_stringWidth(&gfx, str, 0) * (gfx.data.fontAlignX+1)/2);
if (gfx.data.fontAlignY<2) { // 0=center, 1=bottom, 2=undefined, 3=top
int stringWidth=0, stringHeight=0; // width/height of entire string
_jswrap_graphics_stringMetrics(&gfx, str, -1, &stringWidth, &stringHeight, NULL);
y -= stringHeight * (gfx.data.fontAlignY+1)/2;
StringMetricsResult metrics;
// Get width/height of entire string
_jswrap_graphics_stringMetrics(&gfx, str, -1, &metrics);
y -= metrics.stringHeight * (gfx.data.fontAlignY+1)/2;
}
// figure out clip rectangles
int minX = gfx.data.clipRect.x1, minY = gfx.data.clipRect.y1;
Expand Down

0 comments on commit 27a08f7

Please sign in to comment.