release v1.0 with all of jfsiii edits

This commit is contained in:
Lokesh Dhakar
2012-04-22 01:04:48 -04:00
4 changed files with 191 additions and 304 deletions

View File

@@ -35,7 +35,6 @@
</div> <!--! end of #container --> </div> <!--! end of #container -->
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.2.min.js"><\/script>')</script> <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/jquery.lettering.js"></script>
<script src="js/libs/mustache.js"></script> <script src="js/libs/mustache.js"></script>
@@ -65,69 +64,6 @@
{{/images}} {{/images}}
</script> </script>
<script> <script src="index.js"></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>
</body> </body>
</html> </html>

45
index.js Normal file
View 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);
});
});

View File

@@ -1,64 +1,65 @@
/* /*
* Image Palette v0.1 * Image Palette v1.0
* by Lokesh Dhakar - http://www.lokeshdhakar.com * by Lokesh Dhakar - http://www.lokeshdhakar.com
* *
* Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/ * 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 * # Thanks
* and licensed under the MIT license. Big props to Nick as this is where the magic happens. * 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 * CanvasImage
* == Functions * ## Functions
* getDominantColor() * getDominantColor()
* createPalette() * createPalette()
* getAverageRGB() * getAverageRGB()
* createAreaBasedPalette() * createAreaBasedPalette()
* *
* Requires jquery and quantize.js. * Requires jquery and quantize.js.
*/ */
/* /*
CanvasImage Class CanvasImage Class
Class that wraps the html image element and canvas. Class that wraps the html image element and canvas.
It also simplifies some of the canvas context manipulation It also simplifies some of the canvas context manipulation
with a set of helper functions. with a set of helper functions.
*/ */
var CanvasImage = function(image){ var CanvasImage = function (image) {
// If jquery object is passed in, get html element // If jquery object is passed in, get html element
this.imgEl = (image.jquery)? image[0]: image; this.imgEl = (image.jquery) ? image[0] : image;
this.canvas = document.createElement('canvas'), this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext('2d'); 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.width = this.canvas.width = $(this.imgEl).width();
this.height = this.canvas.height = $(this.imgEl).height(); 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() { CanvasImage.prototype.clear = function () {
this.context.clearRect(0, 0, this.width, this.height); this.context.clearRect(0, 0, this.width, this.height);
} };
CanvasImage.prototype.update = function(imageData) { CanvasImage.prototype.update = function (imageData) {
this.context.putImageData(imageData, 0, 0); this.context.putImageData(imageData, 0, 0);
} };
CanvasImage.prototype.getPixelCount = function() { CanvasImage.prototype.getPixelCount = function () {
return this.width * this.height; return this.width * this.height;
} };
CanvasImage.prototype.getImageData = function() { CanvasImage.prototype.getImageData = function () {
return this.context.getImageData(0, 0, this.width, this.height); return this.context.getImageData(0, 0, this.width, this.height);
} };
CanvasImage.prototype.removeCanvas = function() { CanvasImage.prototype.removeCanvas = function () {
$(this.canvas).remove(); $(this.canvas).remove();
} };
/* /*
@@ -66,50 +67,16 @@ CanvasImage.prototype.removeCanvas = function() {
* returns {r: num, g: num, b: num} * returns {r: num, g: num, b: num}
* *
* Use the median cut algorithm provided by quantize.js to cluster similar * Use the median cut algorithm provided by quantize.js to cluster similar
* colors and return the base color from the largest cluster. * colors and return the base color from the largest cluster. */
*/ function getDominantColor(sourceImage) {
function getDominantColor(sourceImage){
var palette = []; var palette = createPalette(sourceImage, 5);
var dominant = palette[0];
// Create custom CanvasImage object return dominant;
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]};
} }
/* /*
* createPalette(sourceImage, colorCount) * createPalette(sourceImage, colorCount)
* returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...] * returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
@@ -118,45 +85,42 @@ function getDominantColor(sourceImage){
* colors. * colors.
* *
* BUGGY: Function does not always return the requested amount of colors. It can be +/- 2. * 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 // Store the RGB values in an array format suitable for quantize function
var image = new CanvasImage(sourceImage), var pixelArray = [];
imageData = image.getImageData(), for (var i = 0, offset, r, g, b, a; i < pixelCount; i++) {
pixels = imageData.data, offset = i * 4;
pixelCount = image.getPixelCount(); 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 // Send array to quantize function which clusters values
var pixelArray = []; // using median cut algorithm
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 var cmap = MMCQ.quantize(pixelArray, colorCount);
// using median cut algorithm var palette = cmap.palette();
var cmap = MMCQ.quantize(pixelArray, colorCount);
if ( cmap === false ) { // Clean up
// Clean up image.removeCanvas();
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. * Add up all pixels RGB values and return average.
* Tends to return muddy gray/brown color. Most likely, you'll be better * Tends to return muddy gray/brown color. Most likely, you'll be better
* off using getDominantColor() instead. * off using getDominantColor() instead.
*/ */
function getAverageRGB(sourceImage) { function getAverageRGB(sourceImage) {
// Config // Config
var sampleSize = 10; var sampleSize = 10;
// Create custom CanvasImage object // Create custom CanvasImage object
var image = new CanvasImage(sourceImage), var image = new CanvasImage(sourceImage),
imageData = image.getImageData(), imageData = image.getImageData(),
pixels = imageData.data, pixels = imageData.data,
pixelCount = image.getPixelCount(); pixelCount = image.getPixelCount();
// Reset vars // Reset vars
var i = 0, var i = 0,
count = 0, count = 0,
rgb = {r:0,g:0,b:0}; rgb = {r:0, g:0, b:0};
// Loop through every # pixels. (# is set in Config above via the blockSize var) // Loop through every # pixels. (# is set in Config above via the blockSize var)
// Add all the red values together, repeat for blue and green. // Add all the red values together, repeat for blue and green.
// Last step, divide by the number of pixels checked to get average. // Last step, divide by the number of pixels checked to get average.
while ( (i += sampleSize * 4) < pixelCount ) { while ( (i += sampleSize * 4) < pixelCount ) {
// if pixel is mostly opaque // if pixel is mostly opaque
if(pixels[i+3] > 125){ if (pixels[i+3] > 125) {
++count; ++count;
rgb.r += pixels[i]; rgb.r += pixels[i];
rgb.g += pixels[i+1]; rgb.g += pixels[i+1];
rgb.b += pixels[i+2]; rgb.b += pixels[i+2];
} }
} }
rgb.r = Math.floor(rgb.r/count); rgb.r = ~~(rgb.r/count);
rgb.g = Math.floor(rgb.g/count); rgb.g = ~~(rgb.g/count);
rgb.b = Math.floor(rgb.b/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. * 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 // Create custom CanvasImage object
var image = new CanvasImage(sourceImage), var image = new CanvasImage(sourceImage),
imageData = image.getImageData(), imageData = image.getImageData(),
pixels = imageData.data, pixels = imageData.data,
pixelCount = image.getPixelCount(); pixelCount = image.getPixelCount();
// How big a pixel area does each palette color get // How big a pixel area does each palette color get
var rowCount = colCount = Math.round(Math.sqrt(colorCount)), var rowCount = Math.round(Math.sqrt(colorCount)),
colWidth = Math.round(image.width / colCount), colCount = rowCount,
rowHeight = Math.round(image.height / rowCount); colWidth = Math.round(image.width / colCount),
rowHeight = Math.round(image.height / rowCount);
var count = offset = rowOffset = vertOffset = horizOffset = 0, // Loop through pixels section by section.
rgb = {r:0,g:0,b:0}; // 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. for (var j = 0, horizOffset, rgb, count; j<colCount; j++) {
// At the end of each section, push the average rgb color to palette array. horizOffset = j * colWidth * 4;
for(var i=0; i<rowCount; i++){ rgb = {r:0, g:0, b:0};
vertOffset = i * rowHeight * image.width * 4; count = 0;
for(var j=0; j<colCount; j++){ for (var k = 0, rowOffset; k < rowHeight; k++) {
horizOffset = j * colWidth * 4; rowOffset = k * image.width * 4;
for( var k = 0; k < rowHeight; k++){ for (var l = 0, offset; l < colWidth; l++) {
rowOffset = k * image.width * 4; 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 = ~~(rgb.r/count);
rgb.r += pixels[offset]; rgb.g = ~~(rgb.g/count);
rgb.g += pixels[offset+1]; rgb.b = ~~(rgb.b/count);
rgb.b += pixels[offset+2]; palette.push(rgb);
count++; }
} }
} return palette;
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;
} }

View File

@@ -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);