diff --git a/README.md b/README.md index 8d38095..31b4fb9 100644 --- a/README.md +++ b/README.md @@ -168,3 +168,23 @@ function() { return document.createElement("canvas"); } When using Node.js, you will almost definitely override this default, e.g. using the [canvas module](https://www.npmjs.com/package/canvas). + +# mask([mask]) + +If specified, sets the **mask** image used internally +to draw text fitting within the transaparent areas of the provided image. If not specified, returns the current mask image, defaults to null. + +```js +// Mask Example +var img = document.querySelector("img#mask") +img.onload = ()=>{ + d3.layout.cloud() + .size([500, 500]) + .mask(img) +} +``` + +Ensure the image is loaded before using it, typically using the onload event on the image. + +Note: The image must be hosted with the appropriate CORS headers if being hosted on a different origin. + diff --git a/build/d3.layout.cloud.js b/build/d3.layout.cloud.js index 02f2916..d5bbea6 100644 --- a/build/d3.layout.cloud.js +++ b/build/d3.layout.cloud.js @@ -13,6 +13,7 @@ const SPIRALS = { const cw = 1 << 11 >> 5; const ch = 1 << 11; +const random = Math.random; module.exports = function() { var size = [256, 256], @@ -30,7 +31,9 @@ module.exports = function() { timer = null, random = Math.random, cloud = {}, - canvas = cloudCanvas; + canvas = cloudCanvas, + mask = false + maskBoard = null; cloud.canvas = function(_) { return arguments.length ? (canvas = functor(_), cloud) : canvas; @@ -38,7 +41,7 @@ module.exports = function() { cloud.start = function() { var contextAndRatio = getContext(canvas()), - board = zeroArray((size[0] >> 5) * size[1]), + board = mask ? maskBoard : zeroArray((size[0] >> 5) * size[1]), bounds = null, n = words.length, i = -1, @@ -161,6 +164,16 @@ module.exports = function() { return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval; }; + cloud.mask = function(_){ + if(arguments.length){ + mask = _ + maskBoard = createMaskBoard(_,size[0],size[1]) + return cloud + }else{ + return mask + } + }; + cloud.words = function(_) { return arguments.length ? (words = _, cloud) : words; }; @@ -404,6 +417,38 @@ function functor(d) { return typeof d === "function" ? d : function() { return d; }; } +function get_mask_pixels(mask,w,h){ + var canvas = document.createElement('canvas') + canvas.width = w + canvas.height = h; + var ctx = canvas.getContext("2d"); + ctx.drawImage(mask, 0, 0, w, h) + return ctx.getImageData(0, 0, w, h).data +} + +function create_mask_board_from_pixels(mask_pixels, w,h){ + var w32 = w >> 5 //divedes by 32 + var sprite = [] + // Zero the buffer + for (var i = 0; i < h * w32; i++){ + sprite[i] = 0 + } + for (var j = 0; j < h; j++) { + for (var i = 0; i < w; i++) { + var k = w32 * j + (i >> 5); + var m = mask_pixels[(j * w + (i)) << 2] ? 1 << (31 - (i % 32)) : 0; + sprite[k] |= m; + } + } + return sprite.slice(0, w * w32) +} + +function createMaskBoard(mask,w,h){ + var pixels = get_mask_pixels(mask,w,h) + var board = create_mask_board_from_pixels(pixels,w,h) + return board +} + },{"d3-dispatch":2}],2:[function(require,module,exports){ // https://d3js.org/d3-dispatch/ v1.0.6 Copyright 2019 Mike Bostock (function (global, factory) { diff --git a/examples/mask.png b/examples/mask.png new file mode 100644 index 0000000..7d76896 Binary files /dev/null and b/examples/mask.png differ diff --git a/examples/mask_example.html b/examples/mask_example.html new file mode 100644 index 0000000..1dc42f9 --- /dev/null +++ b/examples/mask_example.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.js b/index.js index 7be341e..11514f4 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ const SPIRALS = { const cw = 1 << 11 >> 5; const ch = 1 << 11; +const random = Math.random; module.exports = function() { var size = [256, 256], @@ -29,7 +30,9 @@ module.exports = function() { timer = null, random = Math.random, cloud = {}, - canvas = cloudCanvas; + canvas = cloudCanvas, + mask = false + maskBoard = null; cloud.canvas = function(_) { return arguments.length ? (canvas = functor(_), cloud) : canvas; @@ -37,7 +40,7 @@ module.exports = function() { cloud.start = function() { var contextAndRatio = getContext(canvas()), - board = zeroArray((size[0] >> 5) * size[1]), + board = mask ? maskBoard : zeroArray((size[0] >> 5) * size[1]), bounds = null, n = words.length, i = -1, @@ -160,6 +163,16 @@ module.exports = function() { return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval; }; + cloud.mask = function(_){ + if(arguments.length){ + mask = _ + maskBoard = createMaskBoard(_,size[0],size[1]) + return cloud + }else{ + return mask + } + }; + cloud.words = function(_) { return arguments.length ? (words = _, cloud) : words; }; @@ -402,3 +415,35 @@ function cloudCanvas() { function functor(d) { return typeof d === "function" ? d : function() { return d; }; } + +function get_mask_pixels(mask,w,h){ + var canvas = document.createElement('canvas') + canvas.width = w + canvas.height = h; + var ctx = canvas.getContext("2d"); + ctx.drawImage(mask, 0, 0, w, h) + return ctx.getImageData(0, 0, w, h).data +} + +function create_mask_board_from_pixels(mask_pixels, w,h){ + var w32 = w >> 5 //divedes by 32 + var sprite = [] + // Zero the buffer + for (var i = 0; i < h * w32; i++){ + sprite[i] = 0 + } + for (var j = 0; j < h; j++) { + for (var i = 0; i < w; i++) { + var k = w32 * j + (i >> 5); + var m = mask_pixels[(j * w + (i)) << 2] ? 1 << (31 - (i % 32)) : 0; + sprite[k] |= m; + } + } + return sprite.slice(0, w * w32) +} + +function createMaskBoard(mask,w,h){ + var pixels = get_mask_pixels(mask,w,h) + var board = create_mask_board_from_pixels(pixels,w,h) + return board +}