1// Copyright (c) 2013 Pieroxy <[email protected]> 2// This work is free. You can redistribute it and/or modify it 3// under the terms of the WTFPL, Version 2 4// For more information see LICENSE.txt or http://www.wtfpl.net/ 5// 6// For more information, the home page: 7// http://pieroxy.net/blog/pages/color-finder/index.html 8// 9// Detection of the most prominent color in an image 10// version 1.1.1 11 12function ColorFinder(colorFactorCallback) { 13 this.callback = colorFactorCallback; 14 this.getMostProminentColor = function(imgEl) { 15 var rgb = null; 16 if (!this.callback) this.callback = function() { return 1; }; 17 var data = this.getImageData(imgEl); 18 rgb = this.getMostProminentRGBImpl(data, 6, rgb, this.callback); 19 rgb = this.getMostProminentRGBImpl(data, 4, rgb, this.callback); 20 rgb = this.getMostProminentRGBImpl(data, 2, rgb, this.callback); 21 rgb = this.getMostProminentRGBImpl(data, 0, rgb, this.callback); 22 return rgb; 23 }; 24 25 this.getImageData = function(imgEl, degrade, rgbMatch, colorFactorCallback) { 26 27 var rgb, 28 canvas = document.createElement('canvas'), 29 context = canvas.getContext && canvas.getContext('2d'), 30 data, width, height, key, 31 i = -4, 32 db={}, 33 length,r,g,b, 34 count = 0; 35 36 if (!context) { 37 return defaultRGB; 38 } 39 40 height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height; 41 width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width; 42 43 context.drawImage(imgEl, 0, 0); 44 45 try { 46 data = context.getImageData(0, 0, width, height); 47 } catch(e) { 48 /* security error, img on diff domain */ 49 return null; 50 } 51 52 length = data.data.length; 53 54 var factor = Math.max(1,Math.round(length/5000)); 55 var result = {}; 56 57 while ( (i += 4*factor) < length ) { 58 if (data.data[i+3]>32) { 59 key = (data.data[i]>>degrade) + "," + (data.data[i+1]>>degrade) + "," + (data.data[i+2]>>degrade); 60 if (!result.hasOwnProperty(key)) { 61 rgb = {r:data.data[i], g:data.data[i+1], b:data.data[i+2],count:1}; 62 rgb.weight = this.callback(rgb.r, rgb.g, rgb.b); 63 if (rgb.weight<=0) rgb.weight = 1e-10; 64 result[key]=rgb; 65 } else { 66 rgb=result[key]; 67 rgb.count++; 68 } 69 } 70 } 71 72 return result; 73 74 }; 75 76 this.getMostProminentRGBImpl = function(pixels, degrade, rgbMatch, colorFactorCallback) { 77 78 var rgb = {r:0,g:0,b:0,count:0,d:degrade}, 79 db={}, 80 pixel,pixelKey,pixelGroupKey, 81 length,r,g,b, 82 count = 0; 83 84 85 for (pixelKey in pixels) { 86 pixel = pixels[pixelKey]; 87 totalWeight = pixel.weight * pixel.count; 88 ++count; 89 if (this.doesRgbMatch(rgbMatch, pixel.r, pixel.g, pixel.b)) { 90 pixelGroupKey = (pixel.r>>degrade) + "," + (pixel.g>>degrade) + "," + (pixel.b>>degrade); 91 if (db.hasOwnProperty(pixelGroupKey)) 92 db[pixelGroupKey]+=totalWeight; 93 else 94 db[pixelGroupKey]=totalWeight; 95 } 96 } 97 98 for (i in db) { 99 data = i.split(","); 100 r = data[0]; 101 g = data[1]; 102 b = data[2]; 103 count = db[i]; 104 105 if (count>rgb.count) { 106 rgb.count = count; 107 data = i.split(","); 108 rgb.r = r; 109 rgb.g = g; 110 rgb.b = b; 111 } 112 } 113 114 return rgb; 115 116 }; 117 118 this.doesRgbMatch = function(rgb,r,g,b) { 119 if (rgb==null) return true; 120 r = r >> rgb.d; 121 g = g >> rgb.d; 122 b = b >> rgb.d; 123 return rgb.r == r && rgb.g == g && rgb.b == b; 124 } 125 126}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.