mirror of
https://github.com/janishutz/color-thief.git
synced 2025-11-25 13:54:25 +00:00
release v1.0 with all of jfsiii edits
This commit is contained in:
66
index.html
66
index.html
@@ -35,7 +35,6 @@
|
||||
</div> <!--! end of #container -->
|
||||
|
||||
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.2.min.js"><\/script>')</script>
|
||||
<script src="js/libs/jquery.imagesloaded.js"></script>
|
||||
<script src="js/libs/jquery.lettering.js"></script>
|
||||
<script src="js/libs/mustache.js"></script>
|
||||
|
||||
@@ -65,69 +64,6 @@
|
||||
{{/images}}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
|
||||
// Use mustache.js templating to create layout
|
||||
|
||||
var imageArray = { images: [
|
||||
{"file": "3.jpg"},
|
||||
{"file": "4.jpg"},
|
||||
{"file": "5.jpg"},
|
||||
{"file": "logo1.png"},
|
||||
{"file": "icon1.png", "colorCount": "4", "class": "fbIcon"}
|
||||
]};
|
||||
|
||||
var html = Mustache.to_html($('#template').html(), imageArray);
|
||||
$('#main').append(html);
|
||||
|
||||
// Use lettering.js to give letter by letter styling control for the h1 title
|
||||
$("h1").lettering();
|
||||
|
||||
|
||||
// Once images are loaded, loop through each one, getting dominant color
|
||||
// and palette and displaying them.
|
||||
$('img').imagesLoaded(function(){
|
||||
|
||||
$('img').each(function(index){
|
||||
|
||||
var imageSection = $(this).closest('.imageSection'),
|
||||
swatchEl;
|
||||
|
||||
// Dominant Color
|
||||
var dominantColor = getDominantColor(this);
|
||||
|
||||
swatchEl = $('<div>', {
|
||||
'class': 'swatch'
|
||||
}).css('background-color','rgba('+dominantColor.r+','+dominantColor.g+ ','+dominantColor.b+', 1)');
|
||||
imageSection.find('.dominantColor .swatches').append(swatchEl);
|
||||
|
||||
|
||||
|
||||
// Palette
|
||||
var colorCount = $(this).attr('data-colorcount')? $(this).data('colorcount'): 10;
|
||||
var medianPalette = createPalette(this, colorCount);
|
||||
|
||||
var medianCutPalette = imageSection.find('.medianCutPalette .swatches');
|
||||
$.each(medianPalette, function(index, value){
|
||||
swatchEl = $('<div>', {
|
||||
'class': 'swatch'
|
||||
}).css('background-color','rgba('+value[0]+','+value[1]+ ','+value[2]+', 1)');
|
||||
medianCutPalette.append(swatchEl);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
_uacct = "UA-2196019-1";
|
||||
urchinTracker();
|
||||
</script>
|
||||
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
45
index.js
Normal file
45
index.js
Normal file
@@ -0,0 +1,45 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
// Use mustache.js templating to create layout
|
||||
|
||||
var imageArray = { images: [
|
||||
{"file": "3.jpg"},
|
||||
{"file": "4.jpg"},
|
||||
{"file": "5.jpg"},
|
||||
{"file": "logo1.png"},
|
||||
{"file": "icon1.png", "colorCount": "4", "class": "fbIcon"}
|
||||
]};
|
||||
|
||||
var html = Mustache.to_html($('#template').html(), imageArray);
|
||||
$('#main').append(html);
|
||||
|
||||
// Use lettering.js to give letter by letter styling control for the h1 title
|
||||
$("h1").lettering();
|
||||
|
||||
|
||||
// For each image:
|
||||
// Once image is loaded, get dominant color and palette and display them.
|
||||
$('img').bind('load', function (event) {
|
||||
var image = event.target;
|
||||
var $image = $(image);
|
||||
var imageSection = $image.closest('.imageSection');
|
||||
var appendColors = function (colors, root) {
|
||||
$.each(colors, function (index, value) {
|
||||
var swatchEl = $('<div>', {'class': 'swatch'})
|
||||
.css('background-color', 'rgba('+ value +', 1)');
|
||||
root.append(swatchEl);
|
||||
});
|
||||
};
|
||||
|
||||
// Dominant Color
|
||||
var dominantColor = getDominantColor(image);
|
||||
var dominantSwatch = imageSection.find('.dominantColor .swatches');
|
||||
appendColors([dominantColor], dominantSwatch);
|
||||
|
||||
// Palette
|
||||
var colorCount = $image.attr('data-colorcount') ? $image.data('colorcount') : 10;
|
||||
var medianPalette = createPalette(image, colorCount);
|
||||
var medianCutPalette = imageSection.find('.medianCutPalette .swatches');
|
||||
appendColors(medianPalette, medianCutPalette);
|
||||
});
|
||||
});
|
||||
@@ -1,64 +1,65 @@
|
||||
/*
|
||||
* Image Palette v0.1
|
||||
* Image Palette v1.0
|
||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||
*
|
||||
* Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
|
||||
*
|
||||
* The median cut palette function uses quantize.js which is written by Nick Rabinowitz
|
||||
* and licensed under the MIT license. Big props to Nick as this is where the magic happens.
|
||||
* # Thanks
|
||||
* Nick Rabinowitz: Created quantize.js which is used by the median cut palette function. This handles all the hard clustering math.
|
||||
* John Schulz: All around mad genius who helped clean and optimize the code. @JFSIII
|
||||
*
|
||||
* == Classes
|
||||
* ## Classes
|
||||
* CanvasImage
|
||||
* == Functions
|
||||
* ## Functions
|
||||
* getDominantColor()
|
||||
* createPalette()
|
||||
* getAverageRGB()
|
||||
* createAreaBasedPalette()
|
||||
*
|
||||
* Requires jquery and quantize.js.
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
CanvasImage Class
|
||||
Class that wraps the html image element and canvas.
|
||||
It also simplifies some of the canvas context manipulation
|
||||
with a set of helper functions.
|
||||
CanvasImage Class
|
||||
Class that wraps the html image element and canvas.
|
||||
It also simplifies some of the canvas context manipulation
|
||||
with a set of helper functions.
|
||||
*/
|
||||
var CanvasImage = function(image){
|
||||
// If jquery object is passed in, get html element
|
||||
this.imgEl = (image.jquery)? image[0]: image;
|
||||
var CanvasImage = function (image) {
|
||||
// If jquery object is passed in, get html element
|
||||
this.imgEl = (image.jquery) ? image[0] : image;
|
||||
|
||||
this.canvas = document.createElement('canvas'),
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext('2d');
|
||||
|
||||
document.body.appendChild(this.canvas);
|
||||
document.body.appendChild(this.canvas);
|
||||
|
||||
this.width = this.canvas.width = $(this.imgEl).width(),
|
||||
this.height = this.canvas.height = $(this.imgEl).height();
|
||||
this.width = this.canvas.width = $(this.imgEl).width();
|
||||
this.height = this.canvas.height = $(this.imgEl).height();
|
||||
|
||||
this.context.drawImage(this.imgEl, 0, 0);
|
||||
}
|
||||
this.context.drawImage(this.imgEl, 0, 0);
|
||||
};
|
||||
|
||||
CanvasImage.prototype.clear = function() {
|
||||
this.context.clearRect(0, 0, this.width, this.height);
|
||||
}
|
||||
CanvasImage.prototype.clear = function () {
|
||||
this.context.clearRect(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
CanvasImage.prototype.update = function(imageData) {
|
||||
this.context.putImageData(imageData, 0, 0);
|
||||
}
|
||||
CanvasImage.prototype.update = function (imageData) {
|
||||
this.context.putImageData(imageData, 0, 0);
|
||||
};
|
||||
|
||||
CanvasImage.prototype.getPixelCount = function() {
|
||||
return this.width * this.height;
|
||||
}
|
||||
CanvasImage.prototype.getPixelCount = function () {
|
||||
return this.width * this.height;
|
||||
};
|
||||
|
||||
CanvasImage.prototype.getImageData = function() {
|
||||
return this.context.getImageData(0, 0, this.width, this.height);
|
||||
}
|
||||
CanvasImage.prototype.getImageData = function () {
|
||||
return this.context.getImageData(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
CanvasImage.prototype.removeCanvas = function() {
|
||||
$(this.canvas).remove();
|
||||
}
|
||||
CanvasImage.prototype.removeCanvas = function () {
|
||||
$(this.canvas).remove();
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
@@ -66,50 +67,16 @@ CanvasImage.prototype.removeCanvas = function() {
|
||||
* returns {r: num, g: num, b: num}
|
||||
*
|
||||
* Use the median cut algorithm provided by quantize.js to cluster similar
|
||||
* colors and return the base color from the largest cluster.
|
||||
*/
|
||||
function getDominantColor(sourceImage){
|
||||
* colors and return the base color from the largest cluster. */
|
||||
function getDominantColor(sourceImage) {
|
||||
|
||||
var palette = [];
|
||||
var palette = createPalette(sourceImage, 5);
|
||||
var dominant = palette[0];
|
||||
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
|
||||
// Store the RGB values in an array format suitable for quantize function
|
||||
var pixelArray = [];
|
||||
for (var i = 0; i < pixelCount; i++) {
|
||||
// If pixel is mostly opaque and not white
|
||||
if(pixels[i*4+3] >= 125){
|
||||
if(!(pixels[i*4] > 250 && pixels[i*4+1] > 250 && pixels[i*4+2] > 250)){
|
||||
pixelArray.push( [pixels[i*4], pixels[i*4+1], pixels[i*4+2]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Send array to quantize function which clusters values
|
||||
// using median cut algorithm
|
||||
var cmap = MMCQ.quantize(pixelArray, 5);
|
||||
|
||||
if ( cmap === false ) {
|
||||
// Clean up
|
||||
image.removeCanvas();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var newPalette = cmap.palette();
|
||||
|
||||
// Clean up
|
||||
image.removeCanvas();
|
||||
|
||||
return {r: newPalette[0][0], g: newPalette[0][1], b: newPalette[0][2]};
|
||||
return dominant;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* createPalette(sourceImage, colorCount)
|
||||
* returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
|
||||
@@ -118,45 +85,42 @@ function getDominantColor(sourceImage){
|
||||
* colors.
|
||||
*
|
||||
* BUGGY: Function does not always return the requested amount of colors. It can be +/- 2.
|
||||
*/
|
||||
function createPalette(sourceImage, colorCount){
|
||||
*/
|
||||
function createPalette(sourceImage, colorCount) {
|
||||
|
||||
var palette = [];
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
// Store the RGB values in an array format suitable for quantize function
|
||||
var pixelArray = [];
|
||||
for (var i = 0, offset, r, g, b, a; i < pixelCount; i++) {
|
||||
offset = i * 4;
|
||||
r = pixels[offset + 0];
|
||||
g = pixels[offset + 1];
|
||||
b = pixels[offset + 2];
|
||||
a = pixels[offset + 3];
|
||||
// If pixel is mostly opaque and not white
|
||||
if (a >= 125) {
|
||||
if (!(r > 250 && g > 250 && b > 250)) {
|
||||
pixelArray.push([r, g, b]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the RGB values in an array format suitable for quantize function
|
||||
var pixelArray = [];
|
||||
for (var i = 0; i < pixelCount; i++) {
|
||||
// If pixel is mostly opaque and not white
|
||||
if(pixels[i*4+3] >= 125){
|
||||
if(!(pixels[i*4] > 250 && pixels[i*4+1] > 250 && pixels[i*4+2] > 250)){
|
||||
pixelArray.push( [pixels[i*4], pixels[i*4+1], pixels[i*4+2]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Send array to quantize function which clusters values
|
||||
// using median cut algorithm
|
||||
|
||||
// Send array to quantize function which clusters values
|
||||
// using median cut algorithm
|
||||
var cmap = MMCQ.quantize(pixelArray, colorCount);
|
||||
var cmap = MMCQ.quantize(pixelArray, colorCount);
|
||||
var palette = cmap.palette();
|
||||
|
||||
if ( cmap === false ) {
|
||||
// Clean up
|
||||
image.removeCanvas();
|
||||
// Clean up
|
||||
image.removeCanvas();
|
||||
|
||||
return false;
|
||||
}
|
||||
return palette;
|
||||
|
||||
var newPalette = cmap.palette();
|
||||
|
||||
// Clean up
|
||||
image.removeCanvas();
|
||||
|
||||
return newPalette;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,40 +131,40 @@ function createPalette(sourceImage, colorCount){
|
||||
* Add up all pixels RGB values and return average.
|
||||
* Tends to return muddy gray/brown color. Most likely, you'll be better
|
||||
* off using getDominantColor() instead.
|
||||
*/
|
||||
*/
|
||||
function getAverageRGB(sourceImage) {
|
||||
// Config
|
||||
var sampleSize = 10;
|
||||
// Config
|
||||
var sampleSize = 10;
|
||||
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
|
||||
// Reset vars
|
||||
var i = 0,
|
||||
count = 0,
|
||||
rgb = {r:0,g:0,b:0};
|
||||
// Reset vars
|
||||
var i = 0,
|
||||
count = 0,
|
||||
rgb = {r:0, g:0, b:0};
|
||||
|
||||
// Loop through every # pixels. (# is set in Config above via the blockSize var)
|
||||
// Add all the red values together, repeat for blue and green.
|
||||
// Last step, divide by the number of pixels checked to get average.
|
||||
// Loop through every # pixels. (# is set in Config above via the blockSize var)
|
||||
// Add all the red values together, repeat for blue and green.
|
||||
// Last step, divide by the number of pixels checked to get average.
|
||||
while ( (i += sampleSize * 4) < pixelCount ) {
|
||||
// if pixel is mostly opaque
|
||||
if(pixels[i+3] > 125){
|
||||
++count;
|
||||
rgb.r += pixels[i];
|
||||
rgb.g += pixels[i+1];
|
||||
rgb.b += pixels[i+2];
|
||||
}
|
||||
// if pixel is mostly opaque
|
||||
if (pixels[i+3] > 125) {
|
||||
++count;
|
||||
rgb.r += pixels[i];
|
||||
rgb.g += pixels[i+1];
|
||||
rgb.b += pixels[i+2];
|
||||
}
|
||||
}
|
||||
|
||||
rgb.r = Math.floor(rgb.r/count);
|
||||
rgb.g = Math.floor(rgb.g/count);
|
||||
rgb.b = Math.floor(rgb.b/count);
|
||||
rgb.r = ~~(rgb.r/count);
|
||||
rgb.g = ~~(rgb.g/count);
|
||||
rgb.b = ~~(rgb.b/count);
|
||||
|
||||
return rgb;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,56 +177,52 @@ function getAverageRGB(sourceImage) {
|
||||
*
|
||||
* BUGGY: Function does not always return the requested amount of colors. It can be +/- 2.
|
||||
*
|
||||
*/
|
||||
function createAreaBasedPalette(sourceImage, colorCount){
|
||||
*/
|
||||
function createAreaBasedPalette(sourceImage, colorCount) {
|
||||
|
||||
var palette = [];
|
||||
var palette = [];
|
||||
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
// Create custom CanvasImage object
|
||||
var image = new CanvasImage(sourceImage),
|
||||
imageData = image.getImageData(),
|
||||
pixels = imageData.data,
|
||||
pixelCount = image.getPixelCount();
|
||||
|
||||
|
||||
// How big a pixel area does each palette color get
|
||||
var rowCount = colCount = Math.round(Math.sqrt(colorCount)),
|
||||
colWidth = Math.round(image.width / colCount),
|
||||
rowHeight = Math.round(image.height / rowCount);
|
||||
// How big a pixel area does each palette color get
|
||||
var rowCount = Math.round(Math.sqrt(colorCount)),
|
||||
colCount = rowCount,
|
||||
colWidth = Math.round(image.width / colCount),
|
||||
rowHeight = Math.round(image.height / rowCount);
|
||||
|
||||
var count = offset = rowOffset = vertOffset = horizOffset = 0,
|
||||
rgb = {r:0,g:0,b:0};
|
||||
// Loop through pixels section by section.
|
||||
// At the end of each section, push the average rgb color to palette array.
|
||||
for (var i = 0, vertOffset; i<rowCount; i++) {
|
||||
vertOffset = i * rowHeight * image.width * 4;
|
||||
|
||||
// Loop through pixels section by section.
|
||||
// At the end of each section, push the average rgb color to palette array.
|
||||
for(var i=0; i<rowCount; i++){
|
||||
vertOffset = i * rowHeight * image.width * 4;
|
||||
for (var j = 0, horizOffset, rgb, count; j<colCount; j++) {
|
||||
horizOffset = j * colWidth * 4;
|
||||
rgb = {r:0, g:0, b:0};
|
||||
count = 0;
|
||||
|
||||
for(var j=0; j<colCount; j++){
|
||||
horizOffset = j * colWidth * 4;
|
||||
for (var k = 0, rowOffset; k < rowHeight; k++) {
|
||||
rowOffset = k * image.width * 4;
|
||||
|
||||
for( var k = 0; k < rowHeight; k++){
|
||||
rowOffset = k * image.width * 4;
|
||||
for (var l = 0, offset; l < colWidth; l++) {
|
||||
offset = vertOffset + horizOffset + rowOffset + (l * 4);
|
||||
rgb.r += pixels[offset];
|
||||
rgb.g += pixels[offset+1];
|
||||
rgb.b += pixels[offset+2];
|
||||
count++;
|
||||
}
|
||||
|
||||
for( var l = 0; l < colWidth; l++){
|
||||
offset = vertOffset + horizOffset + rowOffset + (l * 4);
|
||||
rgb.r += pixels[offset];
|
||||
rgb.g += pixels[offset+1];
|
||||
rgb.b += pixels[offset+2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
rgb.r = ~~(rgb.r/count);
|
||||
rgb.g = ~~(rgb.g/count);
|
||||
rgb.b = ~~(rgb.b/count);
|
||||
palette.push(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
rgb.r = Math.floor(rgb.r/count);
|
||||
rgb.g = Math.floor(rgb.g/count);
|
||||
rgb.b = Math.floor(rgb.b/count);
|
||||
palette.push(rgb);
|
||||
|
||||
// reset before next section
|
||||
rgb = {r:0,g:0,b:0};
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return palette;
|
||||
return palette;
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*!
|
||||
* jQuery imagesLoaded plugin v1.0.4
|
||||
* http://github.com/desandro/imagesloaded
|
||||
*
|
||||
* MIT License. by Paul Irish et al.
|
||||
*/
|
||||
|
||||
(function($, undefined) {
|
||||
|
||||
// $('#my-container').imagesLoaded(myFunction)
|
||||
// or
|
||||
// $('img').imagesLoaded(myFunction)
|
||||
|
||||
// execute a callback when all images have loaded.
|
||||
// needed because .load() doesn't work on cached images
|
||||
|
||||
// callback function gets image collection as argument
|
||||
// `this` is the container
|
||||
|
||||
$.fn.imagesLoaded = function( callback ) {
|
||||
var $this = this,
|
||||
$images = $this.find('img').add( $this.filter('img') ),
|
||||
len = $images.length,
|
||||
blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
|
||||
|
||||
function triggerCallback() {
|
||||
callback.call( $this, $images );
|
||||
}
|
||||
|
||||
function imgLoaded( event ) {
|
||||
if ( --len <= 0 && event.target.src !== blank ){
|
||||
setTimeout( triggerCallback );
|
||||
$images.unbind( 'load error', imgLoaded );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !len ) {
|
||||
triggerCallback();
|
||||
}
|
||||
|
||||
$images.bind( 'load error', imgLoaded ).each( function() {
|
||||
// cached images don't fire load sometimes, so we reset src.
|
||||
if (this.complete || this.complete === undefined){
|
||||
var src = this.src;
|
||||
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
|
||||
// data uri bypasses webkit log warning (thx doug jones)
|
||||
this.src = blank;
|
||||
this.src = src;
|
||||
}
|
||||
});
|
||||
|
||||
return $this;
|
||||
};
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user