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,22 +1,23 @@
/* /*
* 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.
*/ */
/* /*
@@ -25,40 +26,40 @@
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,10 +85,8 @@ 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 // Create custom CanvasImage object
var image = new CanvasImage(sourceImage), var image = new CanvasImage(sourceImage),
@@ -131,32 +96,31 @@ function createPalette(sourceImage, colorCount){
// Store the RGB values in an array format suitable for quantize function // Store the RGB values in an array format suitable for quantize function
var pixelArray = []; var pixelArray = [];
for (var i = 0; i < pixelCount; i++) { 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 pixel is mostly opaque and not white
if(pixels[i*4+3] >= 125){ if (a >= 125) {
if(!(pixels[i*4] > 250 && pixels[i*4+1] > 250 && pixels[i*4+2] > 250)){ if (!(r > 250 && g > 250 && b > 250)) {
pixelArray.push( [pixels[i*4], pixels[i*4+1], pixels[i*4+2]]); pixelArray.push([r, g, b]);
}
} }
} }
};
// Send array to quantize function which clusters values // Send array to quantize function which clusters values
// using median cut algorithm // 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();
return false;
}
var newPalette = cmap.palette();
// Clean up // Clean up
image.removeCanvas(); image.removeCanvas();
return newPalette; return palette;
} }
@@ -167,7 +131,7 @@ 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;
@@ -181,14 +145,14 @@ function getAverageRGB(sourceImage) {
// 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];
@@ -196,9 +160,9 @@ function getAverageRGB(sourceImage) {
} }
} }
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,8 +177,8 @@ 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 = [];
@@ -226,25 +190,25 @@ function createAreaBasedPalette(sourceImage, colorCount){
// 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)),
colCount = rowCount,
colWidth = Math.round(image.width / colCount), colWidth = Math.round(image.width / colCount),
rowHeight = Math.round(image.height / rowCount); 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. // Loop through pixels section by section.
// At the end of each section, push the average rgb color to palette array. // At the end of each section, push the average rgb color to palette array.
for(var i=0; i<rowCount; i++){ for (var i = 0, vertOffset; i<rowCount; i++) {
vertOffset = i * rowHeight * image.width * 4; vertOffset = i * rowHeight * image.width * 4;
for(var j=0; j<colCount; j++){ for (var j = 0, horizOffset, rgb, count; j<colCount; j++) {
horizOffset = j * colWidth * 4; horizOffset = j * colWidth * 4;
rgb = {r:0, g:0, b:0};
count = 0;
for( var k = 0; k < rowHeight; k++){ for (var k = 0, rowOffset; k < rowHeight; k++) {
rowOffset = k * image.width * 4; rowOffset = k * image.width * 4;
for( var l = 0; l < colWidth; l++){ for (var l = 0, offset; l < colWidth; l++) {
offset = vertOffset + horizOffset + rowOffset + (l * 4); offset = vertOffset + horizOffset + rowOffset + (l * 4);
rgb.r += pixels[offset]; rgb.r += pixels[offset];
rgb.g += pixels[offset+1]; rgb.g += pixels[offset+1];
@@ -253,14 +217,10 @@ function createAreaBasedPalette(sourceImage, colorCount){
} }
} }
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);
palette.push(rgb); palette.push(rgb);
// reset before next section
rgb = {r:0,g:0,b:0};
count = 0;
} }
} }

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