diff --git a/apps/clockbg/ChangeLog b/apps/clockbg/ChangeLog index 69f23b7f2e..d433028038 100644 --- a/apps/clockbg/ChangeLog +++ b/apps/clockbg/ChangeLog @@ -11,4 +11,6 @@ Default (solid colour) now uses a variety of shades Added .load/.unload to allow changes when fast loading clocks 0.09: Add more plasma and ring bg colours, random plasma color uses all the available colours -0.10: Added blobs background \ No newline at end of file +0.10: Added blobs background +0.11: Added gradient background + Bangle.js 2 can use colorpicker module for custom plasma and gradient colors \ No newline at end of file diff --git a/apps/clockbg/README.md b/apps/clockbg/README.md index e1b5e23ee8..a3b6f40325 100644 --- a/apps/clockbg/README.md +++ b/apps/clockbg/README.md @@ -11,14 +11,15 @@ You can either: * Go to [the Clock Backgrounds app](https://banglejs.com/apps/?id=clockbg) in the App Loader and use pre-made image backgrounds (or upload your own) * Go to the `Backgrounds` app on the Bangle itself, and choose between: - * `Solid Color` - one color that never changes - * `Random Color` - a new color every time the clock starts - * `Image` - choose from a previously uploaded image - * `Squares` - a randomly generated pattern of squares in the selected color palette - * `Plasma` - a randomly generated 'plasma' pattern of squares in the selected color palette (random noise with a gaussian filter applied) - * `Rings` - randomly generated rings in the selected color palette - * `Tris` - randomly generated overlapping triangles in the selected color palette - + * `Solid Color` - One color that never changes + * `Random Color` - A new color every time the clock starts + * `Image` - Choose from a previously uploaded image + * `Squares` - A randomly generated pattern of squares in the selected color palette + * `Plasma` - A randomly generated 'plasma' pattern of squares in the selected color palette (random noise with a gaussian filter applied) + * `Rings` - Randomly generated rings in the selected color palette + * `Tris` - Randomly generated overlapping triangles in the selected color palette + * `Blobs` - Randomly generated blobs of color in the selected color palette + * `Gradient` - A gradient from top to bottom with the selected colors ## Usage in code @@ -54,7 +55,7 @@ A few features could be added that would really improve functionality: * When 'fast loading', 'random' backgrounds don't update at the moment (calling `.reload` can fix this now, but it slows things down) * Support for >1 image to be uploaded (requires some image management in `interface.html`), and choose randomly between them -* Support for gradients (random colors) +* Support for random colored gradients * More types of auto-generated pattern (as long as they can be generated quickly or in the background) * Storing 'clear' areas of uploaded images so clocks can easily position themselves * Some backgrounds could update themselves in the background (eg a mandelbrot could calculate the one it should display next time while the watch is running) \ No newline at end of file diff --git a/apps/clockbg/lib.js b/apps/clockbg/lib.js index 5940292aed..64aa08d479 100644 --- a/apps/clockbg/lib.js +++ b/apps/clockbg/lib.js @@ -66,7 +66,39 @@ exports.reload = function() { bg.palette.set(settings.colors.map(c=>g.toColor(c))); settings.img = bg; settings.imgOpt = {scale:g.getWidth()/16}; - } else if (settings.style=="blobs") { // ~25ms + + } else if (settings.style=="gradient") { // ~60ms + settings.style = "image"; + let c = settings.colors; + function pc(s) { + if (typeof s==="number") return s; + s=s.replace('#',''); + if (s.length==3) s=s[0]+s[0]+s[1]+s[1]+s[2]+s[2]; + return parseInt(s,16); + } + let a=pc(c[0]), b=pc(c[1]); + let bg=Graphics.createArrayBuffer(16,16,4,{msb:true}); + bg.palette=new Uint16Array(16); + let ar=(a>>16)&255,ag=(a>>8)&255,ab=a&255; + let br=(b>>16)&255,bgv=(b>>8)&255,bb=b&255; + for (let i=0;i<5;i++) { + let t=i/4; + bg.palette[i]=g.toColor((ar+(br-ar)*t)/255,(ag+(bgv-ag)*t)/255,(ab+(bb-ab)*t)/255); + } + // 4bpp, 8 bytes/row. boundaries always at rows 3,6,9,12 for 16 rows/5 colors + let buf=new Uint8Array(bg.buffer), r; + buf.fill(0x00, 0, 24); // rows 0-2: color 0 + r=Math.random()*65536|0; for(let x=24;x<32;x++,r>>=2) buf[x]=((r&1?1:0)<<4)|(r&2?1:0); + buf.fill(0x11, 32, 48); // rows 4-5: color 1 + r=Math.random()*65536|0; for(let x=48;x<56;x++,r>>=2) buf[x]=((r&1?2:1)<<4)|(r&2?2:1); + buf.fill(0x22, 56, 72); // rows 7-8: color 2 + r=Math.random()*65536|0; for(let x=72;x<80;x++,r>>=2) buf[x]=((r&1?3:2)<<4)|(r&2?3:2); + buf.fill(0x33, 80, 96); // rows 10-11: color 3 + r=Math.random()*65536|0; for(let x=96;x<104;x++,r>>=2) buf[x]=((r&1?4:3)<<4)|(r&2?4:3); + buf.fill(0x44,104,128); // rows 13-15: color 4 + settings.img=bg; + settings.imgOpt={scale:g.getWidth()/16}; + }else if (settings.style=="blobs") { // ~25ms settings.style = "image"; const S=11; // image size const Z=88,W=Z/S,H=Z/S; @@ -223,4 +255,4 @@ exports.fillRect = function(rect,y,x2,y2) { // load background exports.reload(); -//exports.fillRect(Bangle.appRect); // testing +//exports.fillRect(Bangle.appRect); // testing \ No newline at end of file diff --git a/apps/clockbg/metadata.json b/apps/clockbg/metadata.json index 81ab226aa6..17994c65c5 100644 --- a/apps/clockbg/metadata.json +++ b/apps/clockbg/metadata.json @@ -1,10 +1,10 @@ { "id": "clockbg", "name": "Clock Backgrounds", "shortName":"Backgrounds", - "version": "0.10", + "version": "0.11", "description": "Library that allows clocks to include a custom background (generated on demand or uploaded).", "icon": "app.png", - "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"},{"url":"screenshot5.png"},{"url":"screenshot6.png"}], + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"},{"url":"screenshot5.png"},{"url":"screenshot6.png"},{"url":"screenshot7.png"}], "type": "module", "readme": "README.md", "provides_modules" : ["clockbg"], diff --git a/apps/clockbg/screenshot7.png b/apps/clockbg/screenshot7.png new file mode 100644 index 0000000000..4b082df236 Binary files /dev/null and b/apps/clockbg/screenshot7.png differ diff --git a/apps/clockbg/settings.js b/apps/clockbg/settings.js index 1c936235b8..6db04ff6d6 100644 --- a/apps/clockbg/settings.js +++ b/apps/clockbg/settings.js @@ -9,10 +9,70 @@ delete settings.fn; if (settings.style!="color") delete settings.color; - if (!["randomcolor","squares","plasma","rings","tris","blobs"].includes(settings.style)) + if (!["randomcolor","squares","plasma","rings","tris","blobs","gradient"].includes(settings.style)) delete settings.colors; require("Storage").writeJSON("clockbg.json", settings); } + +function colorRange(from, to, steps) { + function parse(hex) { + hex = hex.replace("#", ""); + if (hex.length === 3) + hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; + return [ + parseInt(hex.slice(0,2), 16), + parseInt(hex.slice(2,4), 16), + parseInt(hex.slice(4,6), 16) + ]; + } + function toHex(r, g, b) { + return "#" + + Math.round(r).toString(16).padStart(2,"0") + + Math.round(g).toString(16).padStart(2,"0") + + Math.round(b).toString(16).padStart(2,"0"); + } + var a = parse(from); + var b = parse(to); + var result = []; + for (var i = 0; i < steps; i++) { + var t = i / (steps - 1); + result.push(toHex( + a[0] + (b[0] - a[0]) * t, + a[1] + (b[1] - a[1]) * t, + a[2] + (b[2] - a[2]) * t + )); + } + return result; + } +let customMenu=function(style, col1name, col2name, bck) { + var col1 = "", col2 = ""; + function buildMenu() { + var menu = {"":{title:/*LANG*/"Custom", back:bck}}; + menu[col1name] = function() { + require("colorpicker").show({ + back: buildMenu, + onSelect: function(c) { col1 = c } + }); + }; + menu[col2name] = function() { + require("colorpicker").show({ + back: function() { buildMenu(); }, + onSelect: function(c) { col2 = c} + }); + }; + if (col1 !== "" && col2 !== "") { + menu[/*LANG*/"Save"] = function() { + settings.style = style; + settings.colors=[col1,col2] + if(style=="plasma")settings.colors = colorRange(col1, col2, 16); + saveSettings(); + showMainMenu(); + }; + } + E.showMenu(menu); + } + buildMenu(); +} function getColorsImage(cols) { var bpp = 1; @@ -32,40 +92,53 @@ E.showMenu({ "" : {title:/*LANG*/"Background", back:showMainMenu}, /*LANG*/"Solid Color" : function() { - var cols = ["#F00","#0F0","#FF0", - "#00F","#F0F","#0FF", - "#000","#888","#fff",]; - var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; - cols.forEach(col => { - menu["-"+getColorsImage([col])] = () => { + + require("colorpicker").show({ + back:showMainMenu, + onSelect:function(col){ settings.style = "color"; settings.color = col; saveSettings(); - showMainMenu(); - }; - }); - E.showMenu(menu); + } + }) }, /*LANG*/"Random Color" : function() { - var cols = [ - ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"], - ["#F00","#0F0","#00F"], - ["#FF0","#F0F","#0FF"], - ["#00f","#0bf","#0f7","#3f0","#ff0","#f30","#f07","#b0f"], - ["#66f","#6df","#6fb","#8f6","#ff6","#f86","#f6b","#d6f"], - ["#007","#057","#073","#170","#770","#710","#703","#507"] - // Please add some more! - ]; - var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; - cols.forEach(col => { - menu[getColorsImage(col)] = () => { - settings.style = "randomcolor"; - settings.colors = col; - saveSettings(); - showMainMenu(); + if(process.env.BOARD=="BANGLEJS2"){ + let opts={ + onSelect:function(colors){ + settings.style = "randomcolor"; + settings.colors = colors; + saveSettings(); + }, + back:showMainMenu, + multiSelect:true, }; - }); - E.showMenu(menu); + if(settings.style=="randomcolor"){ + opts.startingSelection=settings.colors; + } + require("colorpicker").show(opts); + }else{ + var cols = [ + ["#F00","#0F0","#FF0","#00F","#F0F","#0FF"], + ["#F00","#0F0","#00F"], + ["#FF0","#F0F","#0FF"], + ["#00f","#0bf","#0f7","#3f0","#ff0","#f30","#f07","#b0f"], + ["#66f","#6df","#6fb","#8f6","#ff6","#f86","#f6b","#d6f"], + ["#007","#057","#073","#170","#770","#710","#703","#507"] + // Please add some more! + ]; + var menu = {"":{title:/*LANG*/"Colors", back:showModeMenu}}; + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "randomcolor"; + settings.colors = col; + saveSettings(); + showMainMenu(); + } + }) + E.showMenu(menu); + } + }, /*LANG*/"Image" : function() { let images = require("Storage").list(/clockbg\..*\.img/); @@ -120,36 +193,43 @@ E.showMenu(menu); }, /*LANG*/"Plasma" : function() { - var cols = [ // list of color palettes used as possible square colours - 16 entries - ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], - ["#44f","#48f","#4df","#4fe","#4fa","#4f6","#7f4","#bf4","#ff4","#fb4","#f74","#f46","#f4a","#f4e","#d4f","#84f"], - ["#009","#039","#079","#098","#094","#091","#290","#590","#990","#950","#920","#901","#904","#908","#709","#309"], - ["#fff","#fef","#fdf","#fcf","#fbf","#fae","#f9e","#f8e","#f7e","#f6e","#f5d","#f4d","#f3d","#f2d","#f1d","#f0c"], - ["#fff","#eff","#dff","#cef","#bef","#adf","#9df","#8df","#7cf","#6cf","#5bf","#4bf","#3bf","#2af","#1af","#09f"], - ["#000","#110","#220","#330","#440","#550","#660","#770","#880","#990","#aa0","#bb0","#cc0","#dd0","#ee0","#ff0"], - ["#000","#010","#020","#130","#140","#250","#260","#270","#380","#390","#4a0","#4b0","#5c0","#5d0","#5e0","#6f0"], - ["#fff","#efe","#dfd","#cfc","#bfb","#afa","#9f9","#8f8","#7f7","#6f6","#5f5","#4f4","#3f3","#2f2","#1f1","#0f0"], - ["#fff","#fee","#fdd","#fcc","#fbb","#faa","#f99","#f88","#f77","#f66","#f55","#f44","#f33","#f22","#f11","#f00"], + let showMenu = () => { + var cols = [ // list of color palettes used as possible square colours - 16 entries + ["#00f","#05f","#0bf","#0fd","#0f7","#0f1","#3f0","#9f0","#ff0","#f90","#f30","#f01","#f07","#f0d","#b0f","#50f"], + ["#44f","#48f","#4df","#4fe","#4fa","#4f6","#7f4","#bf4","#ff4","#fb4","#f74","#f46","#f4a","#f4e","#d4f","#84f"], + ["#009","#039","#079","#098","#094","#091","#290","#590","#990","#950","#920","#901","#904","#908","#709","#309"], + ["#fff","#fef","#fdf","#fcf","#fbf","#fae","#f9e","#f8e","#f7e","#f6e","#f5d","#f4d","#f3d","#f2d","#f1d","#f0c"], + ["#fff","#eff","#dff","#cef","#bef","#adf","#9df","#8df","#7cf","#6cf","#5bf","#4bf","#3bf","#2af","#1af","#09f"], + ["#000","#110","#220","#330","#440","#550","#660","#770","#880","#990","#aa0","#bb0","#cc0","#dd0","#ee0","#ff0"], + ["#000","#010","#020","#130","#140","#250","#260","#270","#380","#390","#4a0","#4b0","#5c0","#5d0","#5e0","#6f0"], + ["#fff","#efe","#dfd","#cfc","#bfb","#afa","#9f9","#8f8","#7f7","#6f6","#5f5","#4f4","#3f3","#2f2","#1f1","#0f0"], + ["#fff","#fee","#fdd","#fcc","#fbb","#faa","#f99","#f88","#f77","#f66","#f55","#f44","#f33","#f22","#f11","#f00"], - // Please add some more! - ]; - var menu = {"":{title:/*LANG*/"Plasma", back:showModeMenu}, - /*LANG*/"Random" : () => { - settings.style = "plasma"; - settings.colors = cols; - saveSettings(); - showMainMenu(); - }}; - cols.forEach(col => { - menu[getColorsImage(col)] = () => { - settings.style = "plasma"; - settings.colors = col; - saveSettings(); - showMainMenu(); + // Please add some more! + ]; + + var plasmaMenu = {"":{title:/*LANG*/"Plasma", back:showModeMenu}, + /*LANG*/"Random" : () => { + settings.style = "plasma"; + settings.colors = cols; + saveSettings(); + showMainMenu(); + }, }; - }); - E.showMenu(menu); + if(process.env.BOARD === "BANGLEJS2")plasmaMenu[/*LANG*/"Custom"]= () => customMenu("plasma", /*LANG*/"Background",/*LANG*/"Foreground", showMenu ); + cols.forEach(col => { + plasmaMenu[getColorsImage(col)] = () => { + settings.style = "plasma"; + settings.colors = col; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(plasmaMenu); + } + showMenu() }, + /*LANG*/"Rings" : function() { var cols = [ // list of color palettes used as possible square colours - 2 entries ["#ff0","#f00"], // yellow/red @@ -232,6 +312,41 @@ }); E.showMenu(menu); }, + /*LANG*/"Gradient" : function() { + let showMenu=()=>{ + var cols = [ // list of color palettes used as gradient colors + ["#0ff","#00f"], + ["#ff0","#0f0"], + ["#0f0","#f00"], + ["#0ff","#f0f"], + ["#fff","#0ff"] + + + // Please add some more! + ]; + + var menu = {"":{title:/*LANG*/"Gradient", back:showModeMenu}, + /*LANG*/"Random" : () => { + settings.style = "gradient"; + settings.colors = cols; + saveSettings(); + showMainMenu(); + }, + }; + if(process.env.BOARD === "BANGLEJS2")menu[/*LANG*/"Custom"]= () => customMenu("gradient", /*LANG*/"Top",/*LANG*/"Bottom",showMenu); + cols.forEach(col => { + menu[getColorsImage(col)] = () => { + settings.style = "gradient"; + settings.colors = col; + saveSettings(); + showMainMenu(); + }; + }); + E.showMenu(menu); + } + showMenu() + } + }); } @@ -250,6 +365,8 @@ }); } + showMainMenu(); + /* Scripts for generating colors. Change the values in HSBtoRGB to generate different effects @@ -270,6 +387,6 @@ }).join(",")) */ - - showMainMenu(); - }) + + +})