clockinfo: Add drawFilledImage to allow drawing icons with a separately coloured middle0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle

pebblepp/analogquadclk: not use drawFilledImage fn
Add twotwoclock, a 2x2 digital clock with clockinfos
pull/3384/head
Gordon Williams 2024-05-03 11:31:33 +01:00
parent 6f0440d5e8
commit b4cd690c48
15 changed files with 231 additions and 33 deletions

View File

@ -1,2 +1,3 @@
0.01: New Clock!
0.02: Fix fastloading memory leak and clockinfo overwritten by hands
0.02: Fix fastloading memory leak and clockinfo overwritten by hands
0.03: Use new clockinfo lib with function to render images wirh borders

View File

@ -102,6 +102,7 @@
// Show launcher when middle button pressed
Bangle.setUI({
mode: "clock",
redraw : draw,
remove: function() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
@ -116,9 +117,6 @@
Bangle.loadWidgets();
require("widget_utils").hide();
// used for clockinfo image rendering
let clockInfoG = Graphics.createArrayBuffer(28, 28, 2, {msb:true});
clockInfoG.transparent = 3;
// render clockinfos
let clockInfoDraw = function(itm, info, options) {
// itm: the item containing name/hasRange/etc
@ -139,18 +137,8 @@
fg = g.toColor("#f00");
}
if (info.img) {
//g.drawImage(info.img, left ? 2 : W - 27, top ? 18 : H - 41); // draw the image
// fiddle around colouring the border and inside of the image
clockInfoG.clear(1);
// do a border - images need to be transparent for this
clockInfoG.setColor(2).drawImage(info.img, 1,1).drawImage(info.img, 3,1).
drawImage(info.img, 1,3).drawImage(info.img, 3,3);
clockInfoG.setColor(1).drawImage(info.img, 2,2); // main image
clockInfoG.floodFill(27,27,3); // flood fill edge to transparent
clockInfoG.palette = new Uint16Array([bg,fg,bg/*border*/, g.toColor("#888")]);
g.drawImage(clockInfoG, imgx-1, imgy-1);
}
if (info.img)
require("clock_info").drawBorderedImage(info.img,imgx,imgy);
g.setFont("6x8:2").setFontAlign(left ? -1 : 1, -1);
g.setColor(bg).drawString(info.text, textx-2, texty). // draw the text background

View File

@ -1,7 +1,7 @@
{ "id": "analogquadclk",
"name": "Analog Quad Clock",
"shortName":"Quad Clock",
"version":"0.02",
"version":"0.03",
"description": "An analog clock with clockinfos in each of the 4 corners, allowing 4 different data types to be rendered at once",
"icon": "icon.png",
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" } ],

View File

@ -10,3 +10,4 @@
0.09: Save clkinfo settings on kill and remove
0.10: Fix focus bug when changing focus between two clock infos
0.11: Prepend swipe listener if possible
0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle

View File

@ -371,6 +371,46 @@ exports.addInteractive = function(menu, options) {
return options;
};
/* clockinfos usually return a 24x24 image. This draws that image but
recolors it such that it is transparent, with the middle of the image as background
and the image itself as foreground. options is passed to g.drawImage */
exports.drawFilledImage = function(img,x,y,options) {
if (!img) return;
if (!g.floodFill/*2v18+*/) return g.drawImage(img,x,y,options);
let gfx = exports.imgGfx;
if (!gfx) {
gfx = exports.imgGfx = Graphics.createArrayBuffer(26, 26, 2, {msb:true});
gfx.transparent = 3;
gfx.palette = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor("#888"), g.toColor("#888")]);
}
/* img is (usually) a black and white transparent image. But we really would like the bits in
the middle of it to be white. So what we do is we draw a slightly bigger rectangle in white,
draw the image, and then flood-fill the rectangle back to the background color. floodFill
was only added in 2v18 so we have to check for it and fallback if not. */
gfx.clear(1).setColor(1).drawImage(img, 1,1).floodFill(0,0,3);
var scale = (options && options.scale) || 1;
return g.drawImage(gfx, x-scale,y-scale,options);
};
/* clockinfos usually return a 24x24 image. This creates a 26x26 gfx of the image but
recolors it such that it is transparent, with the middle and border of the image as background
and the image itself as foreground. options is passed to g.drawImage */
exports.drawBorderedImage = function(img,x,y,options) {
if (!img) return;
if (!g.floodFill/*2v18+*/) return g.drawImage(img,x,y,options);
let gfx = exports.imgGfxB;
if (!gfx) {
gfx = exports.imgGfxB = Graphics.createArrayBuffer(28, 28, 2, {msb:true});
gfx.transparent = 3;
gfx.palette = new Uint16Array([g.theme.bg, g.theme.fg, g.theme.bg/*border*/, g.toColor("#888")]);
}
gfx.clear(1).setColor(2).drawImage(img, 1,1).drawImage(img, 3,1).drawImage(img, 1,3).drawImage(img, 3,3); // border
gfx.setColor(1).drawImage(img, 2,2); // main image
gfx.floodFill(27,27,3); // flood fill edge to transparent
var o = ((options && options.scale) || 1)*2;
return g.drawImage(gfx, x-o,y-o,options);
};
// Code for testing (plots all elements from first list)
/*
g.clear();

View File

@ -1,11 +1,11 @@
{ "id": "clock_info",
"name": "Clock Info Module",
"shortName": "Clock Info",
"version":"0.11",
"version":"0.12",
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
"icon": "app.png",
"type": "module",
"tags": "clkinfo",
"tags": "clkinfo,clockinfo",
"supports" : ["BANGLEJS2"],
"provides_modules" : ["clock_info"],
"readme": "README.md",

View File

@ -5,4 +5,5 @@
0.04: Ensure we only scale down clockinfo text if it really won't fit
0.05: Minor code improvements
0.06: Use the clockbg library to allow custom image backgrounds
0.07: Fix automatic coloring of middle of clockinfo images if clockinfo image goes right to the top or left
0.07: Fix automatic coloring of middle of clockinfo images if clockinfo image goes right to the top or left
0.08: Use new clockinfo lib with function to render images wirh borders

View File

@ -86,18 +86,7 @@ let clockInfoDraw = (itm, info, options) => {
if (info.img) { // draw the image
// TODO: we could replace certain images with our own ones here...
y = options.y+8;
if (g.floodFill) {
/* img is (usually) a black and white transparent image. But we really would like the bits in
the middle of it to be white. So what we do is we draw a slightly bigger rectangle in white,
draw the image, and then flood-fill the rectangle back to the background color. floodFill
was only added in 2v18 so we have to check for it and fallback if not. */
clockInfoG.setBgColor(0).clearRect(0,0,25,25);
clockInfoG.setColor(1).drawImage(info.img, 1,1);
clockInfoG.floodFill(0,0,3);
g.drawImage(clockInfoG, midx-26,y-2,{scale:2});
} else { // fallback
g.drawImage(info.img, midx-24,y,{scale:2});
}
require("clock_info").drawFilledImage(info.img,midx-24,y,{scale:2});
}
g.setFontLECO1976Regular22().setFontAlign(0, 0);
var txt = info.text.toString().toUpperCase();
@ -125,6 +114,7 @@ let clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
// Show launcher when middle button pressed
Bangle.setUI({
mode : "clock",
redraw : draw,
remove : function() {
// Called to unload all of the clock app
if (drawTimeout) clearTimeout(drawTimeout);

View File

@ -2,7 +2,7 @@
"id": "pebblepp",
"name": "Pebble++ Clock",
"shortName": "Pebble++",
"version": "0.07",
"version": "0.08",
"description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],

View File

@ -0,0 +1 @@
0.01: New Clock!

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgcgyUEy/f5HwgENy3bl//AAkkyMky3f4vkDoMarUly/7CImBCIP/pvggFIgVJokSpO/CIUAgMk239t/9yVAgFBlEXGokfHgoAEjhbDn4RMv4DB/wRBwAXCg/8DokcuAEBvwRRg4RBg//AQIRBn4CCCKOcI40H+4RH3g7DnwRHI4efOAf+CJeSpX9k1JkxHHCIlP/9s5IRGFoRHCGon8CJccgP4jkPa4IRKzlxZAKzN7/xEAPwUIQRNxwRBfZRZECgIRKyVpgmypIXB5k/VIjSC7EQpMgyShBz+MnH/x0AAQP4gf//EGCIMSpMkz+AnBNBnHjx9/8f//c/5MkiwRB34RBjkOgHgh9/gf9/N/5M0GoIRB+AgC/H8IwX77c//M2CIW3+BZE+4CB/vbv/7k2W6EM2fQCIu///v7YGBk3/yENSooAHk3j+ENCBiYD9p9Ba5IPB4BOCPoIRLAAQRBAgWAg4ECCgIRBQYQRBLIYRIAAgRLHYkHLJojECJSiC75HHIgvbt5ZJCJRZGCIx9KCIwDC+IRBWaEfCKA1DABnty4RPtsCvcmdgLuDAA3btsAp8mAgO3CBH+BgMgp/2CJw="))

159
apps/twotwoclock/app.js Normal file
View File

@ -0,0 +1,159 @@
/*
// to calculate 'numerals' by adding a border around text
require("Font4x5Numeric").add(Graphics);
function getImageForNumber(n) {
g.setFont("4x5Numeric:3x2");
var sz = g.stringMetrics(n), s=1;
var b = Graphics.createArrayBuffer(12 + s*2, sz.height+s*2, 2, {msb:true});
b.setFont("4x5Numeric:3x2").setFontAlign(0,-1);
b.transparent = 2;
b.setColor(b.transparent);
b.fillRect(0,0,b.getWidth(),b.getHeight());
b.setColor(0);
var x = s+6, y = s;
for (var ix=-s;ix<=s;ix++)
for (var iy=-s;iy<=s;iy++)
b.drawString(n, x+ix, y+iy);
b.setColor(3);
b.drawString(n, x, y);
print('atob("'+btoa((b.asImage("string")))+'"),');
}
for (var i=0;i<10;i++)
getImageForNumber(i);
*/
{
let numerals = [
atob("DgyCAgAAAqP//yo///Kj8D8qPyPyo/I/Kj8j8qPyPyo/A/Kj//8qP//yoAAAKg=="),
atob("DgyCAqgAqqqPyqqo/Kqqj8qqqPyqqo/Kqqj8qqqPyqqo/Kqqj8qqqPyqqoAKqg=="),
atob("DgyCAgAAAqP//yo///KgAD8qAAPyo///Kj//8qPwACo/AAKj//8qP//yoAAAKg=="),
atob("DgyCAgAAAqP//yo///KgAD8qAAPyo///Kj//8qAAPyoAA/Kj//8qP//yoAAAKg=="),
atob("DgyCAgAgAqPyPyo/I/Kj8j8qPwPyo///Kj//8qAAPyqqo/Kqqj8qqqPyqqoAKg=="),
atob("DgyCAgAAAqP//yo///Kj8AAqPwACo///Kj//8qAAPyoAA/Kj//8qP//yoAAAKg=="),
atob("DgyCAgAAAqP//yo///Kj8AAqPwACo///Kj//8qPwPyo/A/Kj//8qP//yoAAAKg=="),
atob("DgyCAgAAAqP//yo///KgAD8qqqPyqqo/Kqqj8qqqPyqqo/Kqqj8qqqPyqqoAKg=="),
atob("DgyCAgAAAqP//yo///Kj8D8qPwPyo///Kj//8qPwPyo/A/Kj//8qP//yoAAAKg=="),
atob("DgyCAgAAAqP//yo///Kj8D8qPwPyo///Kj//8qAAPyoAA/Kj//8qP//yoAAAKg==")
];
Graphics.prototype.setFontLECO1976Regular = function() {
// Actual height 24 (23 - 0)
return this.setFontCustom(
E.toString(require('heatshrink').decompress(atob('ADX/+eA//3AQwUI/wCKByIAFh04HYP/GoICHj08gACFChYjCAAsf/FwAQPwAQsevHw/14/4CFBYUev4CGjk/+CSG4ACFwHh4ICB45HB55KCBwJ7BgP4gEDEQMHDQMfLIM/AQN9AQP54YgBAQPBBAMBAQjUJQZXzIIICMCIQCE8YCBgZKBd6gAJvxxBQ4JuB/H/86CD/kAn/gP4IZFBASOBDIJqCDQIgCEwQsCABESAQNzRwM/AQTOJBYd/CIMzLxUBCgMBJAICFh5dBARAUIAA0DfIQCHHaYCJaI4pBNIMHAQ4AICgUfwEBfAMPAQKyBSQbvCMhDJBCgICH+A1BARYaLfwxKBDqp0N/IRB/YCH+fHARn/ARHDKZnxExosGQCC8CAREDARosS54LBAQ5cPTAQCE8IsIHxQsg/ACH+ACNQaRWZFiXDBYIXCAQgsgh0DwEeg4CGCIoUE/EeAQ4UIgfgAQ0HUQICCh94AQcOnEAj08AQY7JCI4CXAAscjgOGh4vBI4UHuBWGMQsB4AmGPwQCI+P3wACG+YCBEAP/ART4G//4ARBJBvH3/4CKx4CDC4ICGVpALBwACI+ZHBARYaLFiXHARgXGv+A/0/FiXwg4CV8EDFj34h4RDv//gE//0Aj/8WaaGNAQpZXcBwCF+AjB8CDT+JTLHYQCE8ICBKyMDIgICLDRYsRYYx0CUILpLARA+dL4UPAQMfAQM/NAQCB/aVE4YCBwJrUJoICSEyBHCj/wDIP+CoQdBj4CEv5oBBwMf/AHBZz//BwKJCh/gfAIsBgBBCn6/edlnzLIICLU4QCIFI/AAQpKBARgRB/ACIFAN4AQhZM+KwB+LyB+P8AQKzB+IRC54CB44CB4ICB4CGH4YREAQnzARyYCAQnhWY5JBARLsMCg5tBbR8AfYICLDRgABb4S3C/4CCHIM/BIMHBIYXBj4CBv+AgYIBn62BC4QTCEYQpCLgTEBCIgGBj4mCAQU/EwhNCEIIQDEgP/GQIaCgJEEAoQLCFgQXCCwJBDCoOA8EDVoKGB/gdB/w7Bn4aBh50CW4IFCBYV+n4mBC4IdCEYQpCZAZQCAQ8DAQRNBAQwXKAQQAF8IXB+YCI44CM/4CI4ZWDAAV/f4IAIP4QCFuCLBDQTmCVoRZDSoMPZYqqBPYI4GFhA+IJQQXCA'))),
32,
atob("CAcMDw8WEAcKCg0NBwoHCg8NDw8PDw8ODw8HBw0NDQ8RDw8PDw8ODw8HDg8NFBAPDhAPDw0PDxYPDw8JCgk="),
24|65536
);
};
Graphics.prototype.setFontLECO1976Regular14 = function() {
// Actual height 14 (13 - 0)
return this.setFontCustom(
atob('AAAAAAAAAAAD+w/sAAAAA8APAAAA8APAAAAMwP/D/wMwDMD/w/8DMAAAAAD8w/M8z/M/zPM/DPwAAAAPwD8QzcP/D/AHgD/D/wzMI/APwAAAAD/w/8MzDMwzMM/DPwDAAADwA8AAAAAAD8H/74f4BwAAAA4B/z8/8D8AAAAAeAPwD8AeAHgAAAAAAAAYAGAH4B+AGABgAAAAAAHgB4AAAAAYAGABgAYAAAAAABgAYAAAAQA8D/D+A8AAAAAA/8P/DAwwMMDD/w/8AAAAAwMMDD/w/8ADAAwAAO/DvwzMMzDMw/MPzAAAAAMDDMwzMMzDMw/8P/AAAAAPwD8ADAAwAMA/8P/AAAAAP3D9wzMMzDMwz8M/AAAAAP/D/wzMMzDMwz8M/AAA4AOADAAwAMAD/w/8AAAAA/8P/DMwzMMzD/w/8AAAAA/MPzDMwzMMzD/w/8AAAAAYYGGAAAAAGHhh4AABwAcAPgDYB3AYwAAAAAZgGYBmAZgGYBmAAAAABjAdwDYA+AHABwAAA4AOADOwzsMwD8A/AAAAAA//P/zAM37N+zZs/7P+wAAAAP/D/wzAMwDMA/8P/AAAAAP/D/wzMMzDMw/8P/AAAAAP/D/wwMMDDAwwMMDAAAAAP/D/wwMMDDhw/8H+AAAAAP/D/wzMMzDMwzMMDAAAAAP/D/wzAMwDMAzAMAAAA/8P/DAwzMMzDPwz8AAAAA/8P/AMADAAwD/w/8AAAAA/8P/AAAwMMDDAwwMMDD/w/8AAAAA/8P/AcAPAPwDvwj8AAAAA/8P/AAwAMADAAwAAP/D/w/AB+AHwA8B+B+A/8P/AAA/8P/D/wfAB8AHw/8P/AAAAAP/D/wwMMDDAw/8P/AAAAAP/D/wzAMwDMA/APwAAA/8P/DAwwMMDD/8//AAwAA/8P/DOAzwM/D9w/EAAAAA/MPzDMwzMMzDPwz8AADAAwAMAD/w/8MADAAwAAAD/w/8ADAAwAMP/D/wAAPAD8AP4AfAHwP4PwDgAAAPgD/gH8AfB/w/AP4A/wA8D/D/A8AAADAw4cP/A/APwH+DzwwMAAAAA/APwAPwD8A/D8A/AAAAAAz8M/DMwzMMzD8w/MAAAAA/////ADwA4ADwA/wB/ADwAAMAPAD////8A'),
32,
atob("BAQHCQkNCQQGBggIBAYEBgkHCQkJCQkICQkEBAcIBwkKCQkJCQkICQkECAkHDAkJCAkJCQgJCQ0JCQkFBgU="),
14|65536
);
};
// timeout used to update every minute
let drawTimeout;
// schedule a draw for the next minute
let queueDraw = function() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, 60000 - (Date.now() % 60000));
};
let draw = function() {
queueDraw();
var d = new Date();
var hr = d.getHours().toString().padStart(2,0);
var mn = d.getMinutes().toString().padStart(2,0);
var date = require("locale").date(new Date()).split(" ").slice(0,2).join(" ").toUpperCase();
var x = 6, y = 16, w = 55, h = 67, datesz = 20, s=5;
g.reset();
background.fillRect(x, y, x + w*2, y + h*2 + datesz);
var dx = x+w, dy = y+h+datesz-10;
g.setFont("LECO1976Regular").setFontAlign(0,0);
g.setColor(g.theme.bg).drawString(date, dx+3,dy-3).drawString(date, dx+3,dy+3);
g.drawString(date, dx-3,dy-3).drawString(date, dx-3,dy+3);
g.drawString(date, dx,dy-3).drawString(date, dx,dy+3);
g.drawString(date, dx-3,dy).drawString(date, dx+3,dy);
g.setColor(g.theme.fg).drawString(date, dx,dy);
g.drawImage(numerals[hr[0]], x, y, {scale:s});
g.drawImage(numerals[hr[1]], x+w, y, {scale:s});
g.drawImage(numerals[mn[0]], x, y+h+datesz, {scale:s});
g.drawImage(numerals[mn[1]], x+w, y+h+datesz, {scale:s});
};
let clockInfoMenuA, clockInfoMenuB;
// Show launcher when middle button pressed
Bangle.setUI({
mode: "clock",
redraw : draw,
remove: function() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
if (clockInfoMenuA) clockInfoMenuA.remove();
if (clockInfoMenuB) clockInfoMenuB.remove();
require("widget_utils").show(); // re-show widgets
}
});
Bangle.loadWidgets();
require("widget_utils").swipeOn();
let R = Bangle.appRect;
let background = require("clockbg");
background.fillRect(R);
draw();
g.flip();
// Load the clock infos
let clockInfoW = 54;
let clockInfoH = g.getHeight()>>1;
let clockInfoItems = require("clock_info").load();
let clockInfoDraw = (itm, info, options) => {
// itm: the item containing name/hasRange/etc
// info: data returned from itm.get() containing text/img/etc
// options: options passed into addInteractive
// Clear the background - if focussed, add a border
g.reset().setBgColor(g.theme.bg).setColor(g.theme.fg);
var b = 0; // border
if (options.focus) { // white border
b = 4;
g.clearRect(options.x, options.y, options.x+options.w-1, options.y+options.h-1);
}
background.fillRect(options.x+b, options.y+b, options.x+options.w-1-b, options.y+options.h-1-b);
// we're drawing center-aligned here
if (info.img)
require("clock_info").drawBorderedImage(info.img,options.x+3, options.y+3, {scale:2});
g.setFont("LECO1976Regular").setFontAlign(0, -1);
var txt = info.text.toString().toUpperCase();
if (g.stringWidth(txt) > options.w) // if too big, smaller font
g.setFont("LECO1976Regular14");
if (g.stringWidth(txt) > options.w) {// if still too big, split to 2 lines
var l = g.wrapString(txt, options.w);
txt = l.slice(0,2).join("\n") + (l.length>2)?"...":"";
}
var x = options.x+options.w/2, y = options.y+54;
g.setColor(g.theme.bg).drawString(txt, x-2, y). // draw the text background
drawString(txt, x+2, y).
drawString(txt, x, y-2).
drawString(txt, x, y+2);
// draw the text, with border
g.setColor(g.theme.fg).drawString(txt, x, y);
};
clockInfoMenuA = require("clock_info").addInteractive(clockInfoItems, {
app:"pebblepp",
x : g.getWidth()-clockInfoW, y: 0, w: clockInfoW, h:clockInfoH,
draw : clockInfoDraw
});
clockInfoMenuB = require("clock_info").addInteractive(clockInfoItems, {
app:"pebblepp",
x : g.getWidth()-clockInfoW, y: clockInfoH, w: clockInfoW, h:clockInfoH,
draw : clockInfoDraw
});
}

BIN
apps/twotwoclock/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,16 @@
{ "id": "twotwoclock",
"name": "TwoTwo Clock",
"shortName":"22 Clock",
"version":"0.01",
"description": "A clock with the time split over two lines, with custom backgrounds and two ClockInfos",
"icon": "icon.png",
"type": "clock",
"tags": "clock,clkinfo",
"supports" : ["BANGLEJS2"],
"dependencies" : { "clock_info":"module", "clockbg":"module" },
"screenshots": [{"url":"screenshot.png"}],
"storage": [
{"name":"twotwoclock.app.js","url":"app.js"},
{"name":"twotwoclock.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB