// // 八叉树算法// (function () {// var OctreeNode = function () {// this.isLeaf = false;// this.pixelCount = 0;// this.red = 0;// this.green = 0;// this.blue = 0;// this.children = [null, null, null, null, null, null, null, null];// this.next = null;// }// var root = null,// leafNum = 0,// colorMap = null,// reducible = null;// function createNode(index, level) {// var node = new OctreeNode();// if (level === 7) {// node.isLeaf = true;// leafNum++;// } else {// // 将其丢到第 level 层的 reducible 链表中// node.next = reducible[level];// reducible[level] = node;// }// return node;// }// function addColor(node, color, level) {// if (node.isLeaf) {// node.pixelCount += 1;// node.red += color.r;// node.green += color.g;// node.bllue += color.b;// }// else {// var str = "";// var r = color.r.toString(2);// var g = color.g.toString(2);// var b = color.b.toString(2);// while (r.length < 8) r = '0' + r;// while (g.length < 8) g = '0' + g;// while (b.length < 8) b = '0' + b;// str += r[level];// str += g[level];// str += b[level];// var index = parseInt(str, 2);// if (null === node.children[index]) {// node.children[index] = createNode(index, level + 1);// }// if (undefined === node.children[index]) {// console.log(index, level, color.r.toString(2));// }// addColor(node.children[index], color, level + 1);// }// }// function reduceTree() {// // 找到最深层次的并且有可合并节点的链表// var level = 6;// while (null == reducible[level]) {// level -= 1;// }// // 取出链表头并将其从链表中移除// var node = reducible[level];// reducible[level] = node.next;// // 合并子节点// var r = 0;// var g = 0;// var b = 0;// var count = 0;// for (var i = 0; i < 8; i++) {// if (null === node.children[i]) continue;// r += node.children[i].red;// g += node.children[i].green;// b += node.children[i].blue;// count += node.children[i].pixelCount;// leafNum--;// }// // 赋值// node.isLeaf = true;// node.red = r;// node.green = g;// node.blue = b;// node.pixelCount = count;// leafNum++;// }// function buidOctree(imageData, maxColors) {// var total = imageData.length / 4;// for (var i = 0; i < total; i++) {// // 添加颜色// addColor(root, {// r: imageData[i * 4],// g: imageData[i * 4 + 1],// b: imageData[i * 4 + 2]// }, 0);// // 合并叶子节点// while (leafNum > maxColors) reduceTree();// }// }// function colorsStats(node, object) {// if (node.isLeaf) {// var r = parseInt(node.red / node.pixelCount);// var g = parseInt(node.green / node.pixelCount);// var b = parseInt(node.blue / node.pixelCount);// var color = r + ',' + g + ',' + b;// if (object[color]) object[color] += node.pixelCount;// else object[color] = node.pixelCount;// return;// }// for (var i = 0; i < 8; i++) {// if (null !== node.children[i]) {// colorsStats(node.children[i], object);// }// }// }// window.themeColor = function (img, callback) {// var canvas = document.createElement('canvas'),// ctx = canvas.getContext('2d'),// width = 0,// height = 0,// imageData = null,// length = 0,// blockSize = 1;// width = canvas.width = img.width;// height = canvas.height = img.height;// ctx.drawImage(img, 0, 0, width, height);// imageData = ctx.getImageData(0, 0, width, height).data;// root = new OctreeNode();// colorMap = {};// reducible = {};// leafNum = 0;// buidOctree(imageData, 8)// colorsStats(root, colorMap)// var arr = [];// for (var key in colorMap) {// arr.push(key);// }// arr.sort(function (a, b) {// return colorMap[a] - colorMap[b];// })// arr.forEach(function (item, index) {// arr[index] = item.split(',')// })// callback(arr)// }// })()// 中位切分法(function () { /** * 颜色盒子类 * * @param {Array} colorRange [[rMin, rMax],[gMin, gMax], [bMin, bMax]] 颜色范围 * @param {any} total 像素总数, imageData / 4 * @param {any} data 像素数据集合 */ function ColorBox(colorRange, total, data) { this.colorRange = colorRange; this.total = total; this.data = data; this.volume = (colorRange[0][1] - colorRange[0][0]) * (colorRange[1][1] - colorRange[1][0]) * (colorRange[2][1] - colorRange[2][0]); this.rank = this.total * (this.volume); } ColorBox.prototype.getColor = function () { var total = this.total; var data = this.data; var redCount = 0, greenCount = 0, blueCount = 0; for (var i = 0; i < total; i++) { redCount += data[i * 4]; greenCount += data[i * 4 + 1]; blueCount += data[i * 4 + 2]; } return [parseInt(redCount / total), parseInt(greenCount / total), parseInt(blueCount / total)]; } // 获取切割边 function getCutSide(colorRange) { // r:0,g:1,b:2 var arr = []; for (var i = 0; i < 3; i++) { arr.push(colorRange[i][1] - colorRange[i][0]); } return arr.indexOf(Math.max(arr[0], arr[1], arr[2])); } // 切割颜色范围 function cutRange(colorRange, colorSide, cutValue) { var arr1 = []; var arr2 = []; colorRange.forEach(function (item) { arr1.push(item.slice()); arr2.push(item.slice()); }) arr1[colorSide][1] = cutValue; arr2[colorSide][0] = cutValue; return [arr1, arr2]; } // 找到出现次数为中位数的颜色 function getMedianColor(colorCountMap, total) { var arr = []; for (var key in colorCountMap) { arr.push({ color: parseInt(key), count: colorCountMap[key] }) } var sortArr = __quickSort(arr); var medianCount = 0; var medianColor = 0; var medianIndex = Math.floor(sortArr.length / 2) for (var i = 0; i <= medianIndex; i++) { medianCount += sortArr[i].count; } return { color: parseInt(sortArr[medianIndex].color), count: medianCount } // 另一种切割颜色判断方法,根据数量和差值的乘积进行判断,自己试验后发现效果不如中位数方法,但是少了排序,性能应该有所提高 // var count = 0; // var colorMin = arr[0].color; // var colorMax = arr[arr.length - 1].color // for (var i = 0; i < arr.length; i++) { // count += arr[i].count; // var item = arr[i]; // if (count * (item.color - colorMin) > (total - count) * (colorMax - item.color)) { // return { // color: item.color, // count: count // } // } // } return { color: colorMax, count: count } function __quickSort(arr) { if (arr.length <= 1) { return arr; } var pivotIndex = Math.floor(arr.length / 2), pivot = arr.splice(pivotIndex, 1)[0]; var left = [], right = []; for (var i = 0; i < arr.length; i++) { if (arr[i].count <= pivot.count) { left.push(arr[i]); } else { right.push(arr[i]); } } return __quickSort(left).concat([pivot], __quickSort(right)); } } // 切割颜色盒子 function cutBox(colorBox) { var colorRange = colorBox.colorRange, cutSide = getCutSide(colorRange), colorCountMap = {}, total = colorBox.total, data = colorBox.data; // 统计出各个值的数量 for (var i = 0; i < total; i++) { var color = data[i * 4 + cutSide]; if (colorCountMap[color]) { colorCountMap[color] += 1; } else { colorCountMap[color] = 1; } } var medianColor = getMedianColor(colorCountMap, total); var cutValue = medianColor.color; var cutCount = medianColor.count; var newRange = cutRange(colorRange, cutSide, cutValue); var box1 = new ColorBox(newRange[0], cutCount, data.slice(0, cutCount * 4)), box2 = new ColorBox(newRange[1], total - cutCount, data.slice(cutCount * 4)) return [box1, box2]; } // 队列切割 function queueCut(queue, num) { while (queue.length < num) { queue.sort(function (a, b) { return a.rank - b.rank }); var colorBox = queue.pop(); var result = cutBox(colorBox); queue = queue.concat(result); } return queue.slice(0, 8) } function themeColor(img, callback) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), width = 0, height = 0, imageData = null, length = 0, blockSize = 1, cubeArr = []; width = canvas.width = img.width; height = canvas.height = img.height; ctx.drawImage(img, 0, 0, width, height); imageData = ctx.getImageData(0, 0, width, height).data; var total = imageData.length / 4; var rMin = 255, rMax = 0, gMin = 255, gMax = 0, bMin = 255, bMax = 0; // 获取范围 for (var i = 0; i < total; i++) { var red = imageData[i * 4], green = imageData[i * 4 + 1], blue = imageData[i * 4 + 2]; if (red < rMin) { rMin = red; } if (red > rMax) { rMax = red; } if (green < gMin) { gMin = green; } if (green > gMax) { gMax = green; } if (blue < bMin) { bMin = blue; } if (blue > bMax) { bMax = blue; } } var colorRange = [[rMin, rMax], [gMin, gMax], [bMin, bMax]]; var colorBox = new ColorBox(colorRange, total, imageData); var colorBoxArr = queueCut([colorBox], 8); var colorArr = []; for (var j = 0; j < colorBoxArr.length; j++) { colorBoxArr[j].total && colorArr.push(colorBoxArr[j].getColor()) } callback(colorArr); } window.themeColor = themeColor})()