In case anyone familiar with JavaScript is interested, I wrote this script (tested in Firefox 19) which lets the user specify the number of tiles (bricks) of each colour and browse for an image file with the same number of pixels.
The luma (brightness) of each pixel is ranked and assigned one of the listed colours. Any ties are arbitrarily assigned to either possible colour (as JavaScript's sort is unstable).
http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Mona_Lisa_headcrop.jpg/20px-Mona_Lisa_headcrop.jpg
is an image which fits the example tile count.
<html>
<head><title>Make mosaic given tile colour count</title></head>
<body><form onSubmit="return false;">
<textarea id="counts" rows="3" cols="80">// Ordered from darkest to lightest
colours = [ '#000000', '#993300', '#cc0000', '#ff9900', '#ffff00', '#ffffff' ];
tiles = [ 110, 40, 80, 60, 120, 90 ];
tileWidth = tileHeight = 16;</textarea><br />
<input type="file" id="file" size="80" />
<img id="img" align="top" /><br />
<input type="button" value="Go!" onClick="loadImage()" /><br /><br />
<canvas id="canvas" style="background:#808080;border:2px groove;"></canvas>
<script>
var tileWidth = 16, tileHeight = 16, colours, tiles;
// Based on http://jsfiddle.net/fWLJ9/
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img = document.getElementById('img');
var imgData;
function loadImage() {
var url = window.URL || window.webkitURL;
var src = url.createObjectURL(document.getElementById('file').files[0]);
img.src = src;
img.onload = function() {
resizeCanvas();
context.drawImage(img, 0, 0);
url.revokeObjectURL(src);
imgData = context.getImageData(0, 0, img.width, img.height).data;
makeMosaic();
}
}
function resizeCanvas() {
canvas.width = img.width * tileWidth - 1;
canvas.height = img.height * tileHeight - 1;
}
function makeMosaic() {
// Check counts are correct
eval(document.getElementById('counts').value);
var count = 0;
for (var i = 0; i < colours.length; ++i) { count += tiles[i]; }
if (count != img.width * img.height) {
alert(img.width * img.height + ' pixels but ' + count + ' tiles!');
return;
}
// Read pixels' luma
var lumas = new Array();
for (var y = 0; y < img.height; ++y) {
for (var x = 0; x < img.width; ++x) {
lumas.push({ x:x, y:y, luma:getPixelLuma(x, y) });
}
}
// Sort in ascending order
lumas.sort(function(a, b) { return a.luma - b.luma });
// Assign pixels to colours
resizeCanvas();
count = 0;
for (var i = 0; i < colours.length; ++i) {
context.fillStyle = colours[i];
for (var j = 0; j < tiles[i]; ++j) {
context.fillRect(lumas[count].x * tileWidth, lumas[count].y * tileHeight,
tileWidth - 1, tileHeight - 1);
++count;
}
}
}
function getPixelLuma(x, y) {
var pixelOffset = (x + y * img.width) * 4;
return (0.21 * imgData[pixelOffset] + 0.72 * imgData[pixelOffset + 1] +
0.07 * imgData[pixelOffset + 2]).toFixed(2);
}
</script>
</form></body>
</html>