mirror of
https://github.com/janishutz/color-thief.git
synced 2025-11-25 05:44:24 +00:00
done
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,3 +16,5 @@ rsync-exclude
|
|||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
built
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var fs = require('fs');
|
/* eslint-disable no-undef */
|
||||||
const { resolve } = require('path');
|
import { copyFile } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
color-thief.umd.js duplicated as color-thief.min.js for legacy support
|
color-thief.umd.js duplicated as color-thief.min.js for legacy support
|
||||||
@@ -17,20 +18,20 @@ duplicating the UMD compatible file and giving it that name.
|
|||||||
const umdRelPath = 'dist/color-thief.umd.js';
|
const umdRelPath = 'dist/color-thief.umd.js';
|
||||||
const legacyRelPath = 'dist/color-thief.min.js';
|
const legacyRelPath = 'dist/color-thief.min.js';
|
||||||
|
|
||||||
const umdPath = resolve(process.cwd(), umdRelPath);
|
const umdPath = path.join( path.resolve( path.dirname( '' ) ), umdRelPath );
|
||||||
const legacyPath = resolve(process.cwd(), legacyRelPath);
|
const legacyPath = path.join( path.resolve( path.dirname( '' ) ), legacyRelPath );
|
||||||
|
|
||||||
fs.copyFile(umdPath, legacyPath, (err) => {
|
copyFile( umdPath, legacyPath, ( err ) => {
|
||||||
if (err) throw err;
|
if ( err ) throw err;
|
||||||
console.log(`${umdRelPath} copied to ${legacyRelPath}.`);
|
console.log( `${ umdRelPath } copied to ${ legacyRelPath }.` );
|
||||||
});
|
} );
|
||||||
|
|
||||||
const srcNodeRelPath = 'src/color-thief-node.js';
|
const srcNodeRelPath = 'built/color-thief-node.js';
|
||||||
const distNodeRelPath = 'dist/color-thief.js';
|
const distNodeRelPath = 'dist/color-thief.js';
|
||||||
const srcNodePath = resolve(process.cwd(), srcNodeRelPath);
|
const srcNodePath = path.join( path.resolve( path.dirname( '' ) ), srcNodeRelPath );
|
||||||
const distNodePath = resolve(process.cwd(), distNodeRelPath);
|
const distNodePath = path.join( path.resolve( path.dirname( '' ) ), distNodeRelPath );
|
||||||
|
|
||||||
fs.copyFile(srcNodePath, distNodePath, (err) => {
|
copyFile( srcNodePath, distNodePath, ( err ) => {
|
||||||
if (err) throw err;
|
if ( err ) throw err;
|
||||||
console.log(`${srcNodeRelPath} copied to ${distNodeRelPath}.`);
|
console.log( `${ srcNodeRelPath } copied to ${ distNodeRelPath }.` );
|
||||||
});
|
} );
|
||||||
15
cypress.config.cjs
Normal file
15
cypress.config.cjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const { defineConfig } = require( 'cypress' );
|
||||||
|
|
||||||
|
module.exports = defineConfig( {
|
||||||
|
'e2e': {
|
||||||
|
// We've imported your old cypress plugins here.
|
||||||
|
// You may want to clean this up later by importing these.
|
||||||
|
setupNodeEvents ( on, config ) {
|
||||||
|
return require( './cypress/plugins/index.cjs' )( on, config );
|
||||||
|
},
|
||||||
|
|
||||||
|
'experimentalRunAllSpecs': true,
|
||||||
|
},
|
||||||
|
} );
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
const { defineConfig } = require('cypress')
|
|
||||||
|
|
||||||
module.exports = defineConfig({
|
|
||||||
e2e: {
|
|
||||||
// We've imported your old cypress plugins here.
|
|
||||||
// You may want to clean this up later by importing these.
|
|
||||||
setupNodeEvents(on, config) {
|
|
||||||
return require('./cypress/plugins/index.js')(on, config)
|
|
||||||
},
|
|
||||||
|
|
||||||
experimentalRunAllSpecs: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
if (img.complete) {
|
if (img.complete) {
|
||||||
getColorFromImage(img);
|
getColorFromImage(img);
|
||||||
} else {
|
} else {
|
||||||
image.addEventListener('load', function() {
|
img.addEventListener('load', function() {
|
||||||
getColorFromImage(img);
|
getColorFromImage(img);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,49 +11,58 @@ var images = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Render example images
|
// Render example images
|
||||||
var examplesHTML = Mustache.to_html(document.getElementById('image-tpl').innerHTML, images);
|
console.log( Mustache );
|
||||||
document.getElementById('example-images').innerHTML = examplesHTML;
|
var examplesHTML = Mustache.render( document.getElementById( 'image-tpl' ).innerHTML, images );
|
||||||
|
document.getElementById( 'example-images' ).innerHTML = examplesHTML;
|
||||||
|
|
||||||
// Once images are loaded, process them
|
// Once images are loaded, process them
|
||||||
document.querySelectorAll('.image').forEach((image) => {
|
document.querySelectorAll( '.image' ).forEach( ( image ) => {
|
||||||
const section = image.closest('.image-section');
|
const section = image.closest( '.image-section' );
|
||||||
if (this.complete) {
|
if ( this.complete ) {
|
||||||
showColorsForImage(image, section);
|
showColorsForImage( image, section );
|
||||||
} else {
|
} else {
|
||||||
image.addEventListener('load', function() {
|
image.addEventListener( 'load', function () {
|
||||||
showColorsForImage(image, section);
|
showColorsForImage( image, section );
|
||||||
});
|
// showColorsForImageURLNewAPI( )
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
})
|
} );
|
||||||
|
|
||||||
// Run Color Thief functions and display results below image.
|
// Run Color Thief functions and display results below image.
|
||||||
// We also log execution time of functions for display.
|
// We also log execution time of functions for display.
|
||||||
const showColorsForImage = function(image, section) {
|
const showColorsForImage = function ( image, section ) {
|
||||||
// getColor(img)
|
// getColor(img)
|
||||||
let start = Date.now();
|
let start = Date.now();
|
||||||
let result = colorThief.getColor(image);
|
let result = colorThief.getColor( image );
|
||||||
let elapsedTime = Date.now() - start;
|
let elapsedTime = Date.now() - start;
|
||||||
const colorHTML = Mustache.to_html(document.getElementById('color-tpl').innerHTML, {
|
const colorHTML = Mustache.render( document.getElementById( 'color-tpl' ).innerHTML, {
|
||||||
color: result,
|
'color': result,
|
||||||
colorStr: result.toString(),
|
'colorStr': result.toString(),
|
||||||
elapsedTime
|
elapsedTime
|
||||||
})
|
} );
|
||||||
|
|
||||||
// getPalette(img)
|
// getPalette(img)
|
||||||
let paletteHTML = '';
|
let paletteHTML = '';
|
||||||
let colorCounts = [2, 3, 5, 7, 10, 20];
|
let colorCounts = [
|
||||||
colorCounts.forEach((count) => {
|
2,
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
10,
|
||||||
|
20
|
||||||
|
];
|
||||||
|
colorCounts.forEach( ( count ) => {
|
||||||
let start = Date.now();
|
let start = Date.now();
|
||||||
let result = colorThief.getPalette(image, count);
|
let result = colorThief.getPalette( image, count );
|
||||||
let elapsedTime = Date.now() - start;
|
let elapsedTime = Date.now() - start;
|
||||||
paletteHTML += Mustache.to_html(document.getElementById('palette-tpl').innerHTML, {
|
paletteHTML += Mustache.render( document.getElementById( 'palette-tpl' ).innerHTML, {
|
||||||
count,
|
count,
|
||||||
palette: result,
|
'palette': result,
|
||||||
paletteStr: result.toString(),
|
'paletteStr': result.toString(),
|
||||||
elapsedTime
|
elapsedTime
|
||||||
})
|
} );
|
||||||
});
|
} );
|
||||||
|
|
||||||
const outputEl = section.querySelector('.output');
|
const outputEl = section.querySelector( '.output' );
|
||||||
outputEl.innerHTML += colorHTML + paletteHTML;
|
outputEl.innerHTML += colorHTML + paletteHTML;
|
||||||
};
|
};
|
||||||
|
|||||||
1
dist/color-thief-node.cjs
vendored
Normal file
1
dist/color-thief-node.cjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var t=require("ndarray-pixels"),e=require("@lokesh.dhakar/quantize"),n=require("sharp");function r(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var o=/*#__PURE__*/r(e),u=/*#__PURE__*/r(n),a=function(e,n,r){void 0===n&&(n=10),void 0===r&&(r=10);var a=function(t){var e=t.colorCount,n=t.quality;if(void 0!==e&&Number.isInteger(e)){if(1===e)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");e=Math.max(e,2),e=Math.min(e,20)}else e=10;return(void 0===n||!Number.isInteger(n)||n<1)&&(n=10),{colorCount:e,quality:n}}({colorCount:n,quality:r});return new Promise(function(n,r){(function(e){return new Promise(function(n,r){u.default(e).toBuffer().then(function(t){return u.default(t).metadata().then(function(e){return{buffer:t,format:e.format}})}).then(function(e){return t.getPixels(e.buffer,e.format)}).then(n).catch(r)})})(e).then(function(t){var e=t.shape[0]*t.shape[1],r=function(t,e,n){for(var r=[],o=0;o<e;o+=n){var u=4*o,a=t[u+0],i=t[u+1],f=t[u+2],c=t[u+3];(void 0===c||c>=125)&&(a>250&&i>250&&f>250||r.push([a,i,f]))}return r}(Array.from(t.data),e,a.quality),u=o.default(r,a.colorCount),i=u?u.palette():null;n(i)}).catch(function(t){r(t)})})};module.exports={getColor:function(t,e){return void 0===e&&(e=10),new Promise(function(n,r){a(t,5,e).then(function(t){n(t[0])}).catch(function(t){r(t)})})},getPalette:a};
|
||||||
1
dist/color-thief-node.mjs
vendored
Normal file
1
dist/color-thief-node.mjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{getPixels as t}from"ndarray-pixels";import o from"@lokesh.dhakar/quantize";import n from"sharp";var r=function(r,e,u){void 0===e&&(e=10),void 0===u&&(u=10);var a=function(t){var o=t.colorCount,n=t.quality;if(void 0!==o&&Number.isInteger(o)){if(1===o)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");o=Math.max(o,2),o=Math.min(o,20)}else o=10;return(void 0===n||!Number.isInteger(n)||n<1)&&(n=10),{colorCount:o,quality:n}}({colorCount:e,quality:u});return new Promise(function(e,u){(function(o){return new Promise(function(r,e){n(o).toBuffer().then(function(t){return n(t).metadata().then(function(o){return{buffer:t,format:o.format}})}).then(function(o){return t(o.buffer,o.format)}).then(r).catch(e)})})(r).then(function(t){var n=t.shape[0]*t.shape[1],r=function(t,o,n){for(var r=[],e=0;e<o;e+=n){var u=4*e,a=t[u+0],i=t[u+1],f=t[u+2],c=t[u+3];(void 0===c||c>=125)&&(a>250&&i>250&&f>250||r.push([a,i,f]))}return r}(Array.from(t.data),n,a.quality),u=o(r,a.colorCount),i=u?u.palette():null;e(i)}).catch(function(t){u(t)})})};module.exports={getColor:function(t,o){return void 0===o&&(o=10),new Promise(function(n,e){r(t,5,o).then(function(t){n(t[0])}).catch(function(t){e(t)})})},getPalette:r};
|
||||||
1
dist/color-thief-node.modern.js
vendored
Normal file
1
dist/color-thief-node.modern.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{getPixels as t}from"ndarray-pixels";import e from"@lokesh.dhakar/quantize";import o from"sharp";const r=(r,a=10,n=10)=>{const l=(t=>{let e=t.colorCount,o=t.quality;if(void 0!==e&&Number.isInteger(e)){if(1===e)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");e=Math.max(e,2),e=Math.min(e,20)}else e=10;return(void 0===o||!Number.isInteger(o)||o<1)&&(o=10),{colorCount:e,quality:o}})({colorCount:a,quality:n});return new Promise((a,n)=>{(e=>new Promise((r,a)=>{o(e).toBuffer().then(t=>o(t).metadata().then(e=>({buffer:t,format:e.format}))).then(({buffer:e,format:o})=>t(e,o)).then(r).catch(a)}))(r).then(t=>{const o=t.shape[0]*t.shape[1],r=((t,e,o)=>{const r=[];for(let a=0;a<e;a+=o){const e=4*a,o=t[e+0],n=t[e+1],l=t[e+2],u=t[e+3];(void 0===u||u>=125)&&(o>250&&n>250&&l>250||r.push([o,n,l]))}return r})(Array.from(t.data),o,l.quality),n=e(r,l.colorCount),u=n?n.palette():null;a(u)}).catch(t=>{n(t)})})};module.exports={getColor:(t,e=10)=>new Promise((o,a)=>{r(t,5,e).then(t=>{o(t[0])}).catch(t=>{a(t)})}),getPalette:r};
|
||||||
1
dist/color-thief-node.umd.js
vendored
Normal file
1
dist/color-thief-node.umd.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("ndarray-pixels"),require("@lokesh.dhakar/quantize"),require("sharp")):"function"==typeof define&&define.amd?define(["ndarray-pixels","@lokesh.dhakar/quantize","sharp"],t):t((e||self).ndarrayPixels,e.quantize,e.sharp)}(this,function(e,t,n){function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var r=/*#__PURE__*/o(t),u=/*#__PURE__*/o(n),a=function(t,n,o){void 0===n&&(n=10),void 0===o&&(o=10);var a=function(e){var t=e.colorCount,n=e.quality;if(void 0!==t&&Number.isInteger(t)){if(1===t)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");t=Math.max(t,2),t=Math.min(t,20)}else t=10;return(void 0===n||!Number.isInteger(n)||n<1)&&(n=10),{colorCount:t,quality:n}}({colorCount:n,quality:o});return new Promise(function(n,o){(function(t){return new Promise(function(n,o){u.default(t).toBuffer().then(function(e){return u.default(e).metadata().then(function(t){return{buffer:e,format:t.format}})}).then(function(t){return e.getPixels(t.buffer,t.format)}).then(n).catch(o)})})(t).then(function(e){var t=e.shape[0]*e.shape[1],o=function(e,t,n){for(var o=[],r=0;r<t;r+=n){var u=4*r,a=e[u+0],i=e[u+1],f=e[u+2],l=e[u+3];(void 0===l||l>=125)&&(a>250&&i>250&&f>250||o.push([a,i,f]))}return o}(Array.from(e.data),t,a.quality),u=r.default(o,a.colorCount),i=u?u.palette():null;n(i)}).catch(function(e){o(e)})})};module.exports={getColor:function(e,t){return void 0===t&&(t=10),new Promise(function(n,o){a(e,5,t).then(function(e){n(e[0])}).catch(function(e){o(e)})})},getPalette:a}});
|
||||||
1
dist/color-thief.cjs
vendored
Normal file
1
dist/color-thief.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
158
dist/color-thief.js
vendored
158
dist/color-thief.js
vendored
@@ -1,17 +1,40 @@
|
|||||||
const getPixels = require('get-pixels');
|
/**
|
||||||
const quantize = require('@lokesh.dhakar/quantize');
|
* Color Thief Node v3.0.0
|
||||||
|
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||||
function createPixelArray(imgData, pixelCount, quality) {
|
*
|
||||||
const pixels = imgData;
|
* Thanks
|
||||||
|
* ------
|
||||||
|
* Nick Rabinowitz - For creating quantize.js.
|
||||||
|
* John Schulz - For clean up and optimization. @JFSIII
|
||||||
|
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||||
|
*
|
||||||
|
* License
|
||||||
|
* -------
|
||||||
|
* Copyright Lokesh Dhakar
|
||||||
|
* Released under the MIT license
|
||||||
|
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
// Thanks to this PR for the work of migrating to ndarray-pixels pr https://github.com/lokesh/color-thief/pull/254
|
||||||
|
import { getPixels } from 'ndarray-pixels';
|
||||||
|
import quantize from '@lokesh.dhakar/quantize';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
/**
|
||||||
|
* Create an array of arrays of pixels from an array of pixels
|
||||||
|
* @param {number[]} pixels An array of pixels
|
||||||
|
* @param {number} pixelCount The total number of pixels
|
||||||
|
* @param {number} quality
|
||||||
|
* @returns {number[][]} Returns an array of arrays of pixel values ([ r, g, b ])
|
||||||
|
*/
|
||||||
|
const createPixelArray = (pixels, pixelCount, quality) => {
|
||||||
const pixelArray = [];
|
const pixelArray = [];
|
||||||
|
for (let i = 0; i < pixelCount; i = i + quality) {
|
||||||
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
|
const offset = i * 4;
|
||||||
offset = i * 4;
|
const r = pixels[offset + 0];
|
||||||
r = pixels[offset + 0];
|
const g = pixels[offset + 1];
|
||||||
g = pixels[offset + 1];
|
const b = pixels[offset + 2];
|
||||||
b = pixels[offset + 2];
|
const a = pixels[offset + 3];
|
||||||
a = pixels[offset + 3];
|
|
||||||
|
|
||||||
// If pixel is mostly opaque and not white
|
// If pixel is mostly opaque and not white
|
||||||
if (typeof a === 'undefined' || a >= 125) {
|
if (typeof a === 'undefined' || a >= 125) {
|
||||||
if (!(r > 250 && g > 250 && b > 250)) {
|
if (!(r > 250 && g > 250 && b > 250)) {
|
||||||
@@ -20,80 +43,99 @@ function createPixelArray(imgData, pixelCount, quality) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pixelArray;
|
return pixelArray;
|
||||||
}
|
};
|
||||||
|
/**
|
||||||
function validateOptions(options) {
|
* Validate Color-Thief options
|
||||||
let { colorCount, quality } = options;
|
* @param {{ colorCount: number; quality: number; }} options The options object
|
||||||
|
* @returns {{ colorCount: number, quality: number }} The same object, but validated
|
||||||
|
*/
|
||||||
|
const validateOptions = (options) => {
|
||||||
|
let colorCount = options.colorCount;
|
||||||
|
let quality = options.quality;
|
||||||
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
|
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
|
||||||
colorCount = 10;
|
colorCount = 10;
|
||||||
} else if (colorCount === 1 ) {
|
}
|
||||||
|
else if (colorCount === 1) {
|
||||||
throw new Error('colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()');
|
throw new Error('colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()');
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
colorCount = Math.max(colorCount, 2);
|
colorCount = Math.max(colorCount, 2);
|
||||||
colorCount = Math.min(colorCount, 20);
|
colorCount = Math.min(colorCount, 20);
|
||||||
}
|
}
|
||||||
|
if (typeof quality === 'undefined' ||
|
||||||
if (typeof quality === 'undefined' || !Number.isInteger(quality) || quality < 1) {
|
!Number.isInteger(quality) ||
|
||||||
|
quality < 1) {
|
||||||
quality = 10;
|
quality = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
colorCount,
|
colorCount,
|
||||||
quality
|
quality,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
/**
|
||||||
function loadImg(img) {
|
* Load an image from the disk an pre-process
|
||||||
|
* @param {string} img Path to the image on the disk
|
||||||
|
* @returns {Promise<ndarray.NdArray<Uint8Array>>} Returns the pre-processed image
|
||||||
|
*/
|
||||||
|
const loadImg = (img) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getPixels(img, function(err, data) {
|
sharp(img)
|
||||||
if(err) {
|
.toBuffer()
|
||||||
reject(err)
|
.then((buffer) => sharp(buffer).metadata()
|
||||||
} else {
|
.then((metadata) => ({ buffer, 'format': metadata.format })))
|
||||||
resolve(data);
|
.then(({ buffer, format }) => getPixels(buffer, format))
|
||||||
}
|
.then(resolve)
|
||||||
})
|
.catch(reject);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
/**
|
||||||
function getColor(img, quality) {
|
* Get the dominant color of an image
|
||||||
|
* @param {string} img Path to the image on the disk
|
||||||
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {Promise<ColorThiefResult>} Returns the dominant color
|
||||||
|
*/
|
||||||
|
const getColor = (img, quality = 10) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getPalette(img, 5, quality)
|
getPalette(img, 5, quality)
|
||||||
.then(palette => {
|
.then((palette) => {
|
||||||
resolve(palette[0]);
|
resolve(palette[0]);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
};
|
||||||
|
/**
|
||||||
function getPalette(img, colorCount = 10, quality = 10) {
|
* Get the color palette of an image
|
||||||
|
* @param {string} img Path to the image on the disk
|
||||||
|
* @param {number?} colorCount (Optional) the target amount of colors to try and extract
|
||||||
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {Promise<ColorThiefResult[]>} Returns an array of colors
|
||||||
|
*/
|
||||||
|
const getPalette = (img, colorCount = 10, quality = 10) => {
|
||||||
const options = validateOptions({
|
const options = validateOptions({
|
||||||
colorCount,
|
colorCount,
|
||||||
quality
|
quality,
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
loadImg(img)
|
loadImg(img)
|
||||||
.then(imgData => {
|
.then((imgData) => {
|
||||||
const pixelCount = imgData.shape[0] * imgData.shape[1];
|
const pixelCount = imgData.shape[0] * imgData.shape[1];
|
||||||
const pixelArray = createPixelArray(imgData.data, pixelCount, options.quality);
|
const pixelArray = createPixelArray(Array.from(imgData.data), pixelCount, options.quality);
|
||||||
|
|
||||||
const cmap = quantize(pixelArray, options.colorCount);
|
const cmap = quantize(pixelArray, options.colorCount);
|
||||||
const palette = cmap? cmap.palette() : null;
|
const palette = cmap ? cmap.palette() : null;
|
||||||
|
|
||||||
resolve(palette);
|
resolve(palette);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
})
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
};
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getColor,
|
getColor,
|
||||||
getPalette
|
getPalette,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
3
dist/color-thief.min.js
vendored
3
dist/color-thief.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/color-thief.mjs
vendored
2
dist/color-thief.mjs
vendored
File diff suppressed because one or more lines are too long
1
dist/color-thief.modern.js
vendored
Normal file
1
dist/color-thief.modern.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/color-thief.modern.mjs
vendored
1
dist/color-thief.modern.mjs
vendored
File diff suppressed because one or more lines are too long
3
dist/color-thief.umd.js
vendored
3
dist/color-thief.umd.js
vendored
File diff suppressed because one or more lines are too long
1
dist/color-thief.umd.js.map
vendored
1
dist/color-thief.umd.js.map
vendored
@@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"color-thief.umd.js","sources":["../build/build.js"],"sourcesContent":["var fs = require('fs');\r\nconst { resolve } = require('path');\r\n\r\n/*\r\ncolor-thief.umd.js duplicated as color-thief.min.js for legacy support\r\n\r\nIn Color Thief v2.1 <= there was one distribution file (dist/color-thief.min.js)\r\nand it exposed a global variable ColorThief. Starting from v2.2, the package\r\nincludes multiple dist files for the various module systems. One of these is\r\nthe UMD format which falls back to a global variable if the requirejs AMD format\r\nis not being used. This file is called color-thief.umd.js in the dist folder. We\r\nwant to keep supporting the previous users who were loading\r\ndist/color-thief.min.js and expecting a global var. For this reason we're\r\nduplicating the UMD compatible file and giving it that name.\r\n*/\r\n\r\nconst umdRelPath = 'dist/color-thief.umd.js';\r\nconst legacyRelPath = 'dist/color-thief.min.js';\r\n\r\nconst umdPath = resolve(process.cwd(), umdRelPath);\r\nconst legacyPath = resolve(process.cwd(), legacyRelPath);\r\n\r\nfs.copyFile(umdPath, legacyPath, (err) => {\r\n if (err) throw err;\r\n console.log(`${umdRelPath} copied to ${legacyRelPath}.`);\r\n});\r\n\r\nconst srcNodeRelPath = 'src/color-thief-node.js';\r\nconst distNodeRelPath = 'dist/color-thief.js';\r\nconst srcNodePath = resolve(process.cwd(), srcNodeRelPath);\r\nconst distNodePath = resolve(process.cwd(), distNodeRelPath);\r\n\r\nfs.copyFile(srcNodePath, distNodePath, (err) => {\r\n if (err) throw err;\r\n console.log(`${srcNodeRelPath} copied to ${distNodeRelPath}.`);\r\n});\r\n"],"names":["fs","require","umdPath","resolve","process","cwd","legacyPath","copyFile","err","console","log","const","srcNodePath","distNodePath"],"mappings":"0IAAA,IAAIA,EAAKC,QAAQ,QACGA,QAAQ,gBAkBtBC,EAAUC,EAAQC,QAAQC,MAHb,2BAIbC,EAAaH,EAAQC,QAAQC,MAHb,2BAKtBL,EAAGO,SAASL,EAASI,WAAaE,MAC1BA,EAAK,MAAMA,EACfC,QAAQC,oEAGZC,IAEMC,EAAcT,EAAQC,QAAQC,MAFb,2BAGjBQ,EAAeV,EAAQC,QAAQC,MAFb,uBAIxBL,EAAGO,SAASK,EAAaC,WAAeL,MAChCA,EAAK,MAAMA,EACfC,QAAQC"}
|
|
||||||
1
dist/core.cjs
vendored
Normal file
1
dist/core.cjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports={createPixelArray:function(e,o,r){for(var t=[],n=0;n<o;n+=r){var a=4*n,i=e[a+0],l=e[a+1],u=e[a+2],c=e[a+3];(void 0===c||c>=125)&&(i>250&&l>250&&u>250||t.push([i,l,u]))}return t},validateOptions:function(e){var o=e.colorCount,r=e.quality;if(void 0!==o&&Number.isInteger(o)){if(1===o)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");o=Math.max(o,2),o=Math.min(o,20)}else o=10;return(void 0===r||!Number.isInteger(r)||r<1)&&(r=10),{colorCount:o,quality:r}}};
|
||||||
1
dist/core.mjs
vendored
Normal file
1
dist/core.mjs
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var e={createPixelArray:function(e,o,r){for(var t=[],a=0;a<o;a+=r){var n=4*a,i=e[n+0],l=e[n+1],u=e[n+2],c=e[n+3];(void 0===c||c>=125)&&(i>250&&l>250&&u>250||t.push([i,l,u]))}return t},validateOptions:function(e){var o=e.colorCount,r=e.quality;if(void 0!==o&&Number.isInteger(o)){if(1===o)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");o=Math.max(o,2),o=Math.min(o,20)}else o=10;return(void 0===r||!Number.isInteger(r)||r<1)&&(r=10),{colorCount:o,quality:r}}};export{e as default};
|
||||||
1
dist/core.modern.js
vendored
Normal file
1
dist/core.modern.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var e={createPixelArray:(e,t,o)=>{const r=[];for(let l=0;l<t;l+=o){const t=4*l,o=e[t+0],a=e[t+1],n=e[t+2],i=e[t+3];(void 0===i||i>=125)&&(o>250&&a>250&&n>250||r.push([o,a,n]))}return r},validateOptions:e=>{let t=e.colorCount,o=e.quality;if(void 0!==t&&Number.isInteger(t)){if(1===t)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");t=Math.max(t,2),t=Math.min(t,20)}else t=10;return(void 0===o||!Number.isInteger(o)||o<1)&&(o=10),{colorCount:t,quality:o}}};export{e as default};
|
||||||
1
dist/core.umd.js
vendored
Normal file
1
dist/core.umd.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(e,o){"object"==typeof exports&&"undefined"!=typeof module?module.exports=o():"function"==typeof define&&define.amd?define(o):(e||self).ColorThief=o()}(this,function(){return{createPixelArray:function(e,o,t){for(var n=[],r=0;r<o;r+=t){var i=4*r,l=e[i+0],u=e[i+1],f=e[i+2],a=e[i+3];(void 0===a||a>=125)&&(l>250&&u>250&&f>250||n.push([l,u,f]))}return n},validateOptions:function(e){var o=e.colorCount,t=e.quality;if(void 0!==o&&Number.isInteger(o)){if(1===o)throw new Error("colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()");o=Math.max(o,2),o=Math.min(o,20)}else o=10;return(void 0===t||!Number.isInteger(t)||t<1)&&(t=10),{colorCount:o,quality:t}}}});
|
||||||
@@ -1,75 +1,65 @@
|
|||||||
import stylisticTS from '@stylistic/eslint-plugin';
|
// @ts-check
|
||||||
|
|
||||||
export default [
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import stylistic from '@stylistic/eslint-plugin';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
{
|
{
|
||||||
plugins: {
|
'plugins': {
|
||||||
'@stylistic/ts': stylisticTS,
|
'@stylistic/js': stylistic,
|
||||||
|
'@stylistic/ts': stylistic,
|
||||||
},
|
},
|
||||||
languageOptions: {
|
'rules': {
|
||||||
ecmaVersion: 6,
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
// Error prevention
|
|
||||||
'prefer-const': 'error',
|
|
||||||
'no-async-promise-executor': 'error',
|
|
||||||
'no-cond-assign': 'error',
|
|
||||||
'no-const-assign': 'error',
|
|
||||||
'no-constant-condition': 'error',
|
|
||||||
'no-debugger': 'error',
|
|
||||||
'no-dupe-else-if': 'error',
|
|
||||||
'no-dupe-keys': 'warn',
|
|
||||||
'no-dupe-class-members': 'error',
|
|
||||||
'no-dupe-args': 'error',
|
|
||||||
'no-duplicate-case': 'error',
|
|
||||||
'no-duplicate-imports': 'error',
|
|
||||||
'no-empty-pattern': 'error',
|
|
||||||
'no-fallthrough': 'error',
|
|
||||||
'no-invalid-regexp': 'warn',
|
|
||||||
'no-loss-of-precision': 'warn',
|
|
||||||
'no-import-assign': 'error',
|
|
||||||
'no-irregular-whitespace': 'error',
|
|
||||||
'no-self-assign': 'error',
|
|
||||||
'no-undef': 'warn',
|
|
||||||
'no-unreachable': 'error',
|
|
||||||
'no-unsafe-finally': 'error',
|
|
||||||
'no-unsafe-negation': 'error',
|
|
||||||
'no-unused-private-class-members': 'warn',
|
|
||||||
'no-unused-vars': 'error',
|
|
||||||
'no-use-before-define': 'error',
|
|
||||||
|
|
||||||
// Style
|
|
||||||
'arrow-body-style': [ 'error', 'always' ],
|
|
||||||
'camelcase': 'warn',
|
|
||||||
'curly': [ 'error', 'all' ],
|
|
||||||
'eqeqeq': [ 'error', 'always' ],
|
|
||||||
'func-style': [ 'error', 'expression' ],
|
|
||||||
'no-array-constructor': 'error',
|
|
||||||
'no-else-return': 'error',
|
|
||||||
'no-empty': 'warn',
|
|
||||||
'no-empty-static-block': 'warn',
|
|
||||||
'no-eval': 'error',
|
|
||||||
'no-implicit-coercion': 'error',
|
|
||||||
'no-invalid-this': 'warn',
|
|
||||||
'no-multi-assign': 'warn',
|
|
||||||
'no-new': 'error',
|
|
||||||
'no-redeclare': 'error',
|
|
||||||
'no-script-url': 'error',
|
|
||||||
'no-shadow': 'error',
|
|
||||||
'no-shadow-restricted-names': 'error',
|
|
||||||
'no-underscore-dangle': 'warn',
|
|
||||||
'no-unused-expressions': 'warn',
|
|
||||||
'no-useless-constructor': 'warn',
|
|
||||||
'no-var': 'warn',
|
|
||||||
'prefer-arrow-callback': 'error',
|
|
||||||
'prefer-exponentiation-operator': 'warn',
|
|
||||||
'prefer-numeric-literals': 'error',
|
|
||||||
'require-await': 'warn',
|
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
// https://eslint.style/rules/js/array-bracket-newline
|
'@stylistic/js/array-bracket-newline': [ 'error', { 'multiline': true, 'minItems': 4 } ],
|
||||||
// TODO: Finish up
|
'@stylistic/js/array-bracket-spacing': [ 'error', 'always' ],
|
||||||
'@stylistic/ts/indent': [ 'error', 4 ]
|
'@stylistic/js/array-element-newline': [ 'error', { 'multiline': true, 'minItems': 4 } ],
|
||||||
|
'@stylistic/js/arrow-parens': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/arrow-spacing': [ 'error', { 'before': true, 'after': true } ],
|
||||||
|
'@stylistic/js/block-spacing': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/brace-style': [ 'error', '1tbs' ],
|
||||||
|
'@stylistic/js/comma-spacing': [ 'error', { 'before': false, 'after': true } ],
|
||||||
|
'@stylistic/js/comma-style': [ 'error', 'last' ],
|
||||||
|
'@stylistic/js/dot-location': [ 'error', 'property' ],
|
||||||
|
'@stylistic/js/eol-last': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/function-call-spacing': [ 'error', 'never' ],
|
||||||
|
'@stylistic/js/implicit-arrow-linebreak': [ 'error', 'beside' ],
|
||||||
|
'@stylistic/js/indent': [ 'error', 4 ],
|
||||||
|
'@stylistic/js/key-spacing': [ 'error', { 'beforeColon': false, 'afterColon': true } ],
|
||||||
|
'@stylistic/js/keyword-spacing': [ 'error', { 'before': true, 'after': true } ],
|
||||||
|
'@stylistic/js/lines-between-class-members': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/new-parens': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/no-extra-parens': [ 'error', 'all' ],
|
||||||
|
'@stylistic/js/no-extra-semi': 'error',
|
||||||
|
'@stylistic/js/no-floating-decimal': 'error',
|
||||||
|
'@stylistic/js/no-mixed-operators': 'error',
|
||||||
|
'@stylistic/js/no-mixed-spaces-and-tabs': 'error',
|
||||||
|
'@stylistic/js/no-multi-spaces': 'error',
|
||||||
|
'@stylistic/js/no-trailing-spaces': 'error',
|
||||||
|
'@stylistic/js/no-whitespace-before-property': 'error',
|
||||||
|
'@stylistic/js/object-curly-newline': [ 'error', { 'multiline': true, 'minProperties': 3 } ],
|
||||||
|
'@stylistic/js/object-curly-spacing': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/one-var-declaration-per-line': 'error',
|
||||||
|
'@stylistic/js/quote-props': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/quotes': [ 'error', 'single' ],
|
||||||
|
'@stylistic/js/rest-spread-spacing': [ 'error', 'never' ],
|
||||||
|
'@stylistic/js/semi': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/semi-spacing': [ 'error', { 'before': false, 'after': true } ],
|
||||||
|
'@stylistic/js/semi-style': [ 'error', 'last' ],
|
||||||
|
'@stylistic/js/space-before-blocks': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/space-before-function-paren': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/space-in-parens': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/space-infix-ops': [ 'error', { 'int32Hint': false } ],
|
||||||
|
'@stylistic/js/space-unary-ops': 'error',
|
||||||
|
'@stylistic/js/spaced-comment': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/switch-colon-spacing': 'error',
|
||||||
|
'@stylistic/js/template-curly-spacing': [ 'error', 'always' ],
|
||||||
|
'@stylistic/js/wrap-iife': [ 'error', 'inside' ],
|
||||||
|
'@stylistic/js/wrap-regex': 'error',
|
||||||
|
'@stylistic/ts/type-annotation-spacing': 'error',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
);
|
||||||
|
|||||||
7550
package-lock.json
generated
7550
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@@ -16,34 +16,50 @@
|
|||||||
"photo",
|
"photo",
|
||||||
"canvas"
|
"canvas"
|
||||||
],
|
],
|
||||||
|
"type": "module",
|
||||||
"homepage": "http://lokeshdhakar.com/projects/color-thief/",
|
"homepage": "http://lokeshdhakar.com/projects/color-thief/",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/lokesh/color-thief.git"
|
"url": "https://github.com/lokesh/color-thief.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"source": "src/color-thief.js",
|
"source": "src/color-thief.ts",
|
||||||
"main": "dist/color-thief.js",
|
"main": "dist/color-thief-node.cjs",
|
||||||
"module": "dist/color-thief.mjs",
|
"browser": {
|
||||||
|
"dist/color-thief.mjs": "dist/color-thief.mjs",
|
||||||
|
"dist/color-thief.js": "dist/color-thief.js"
|
||||||
|
},
|
||||||
|
"module": "dist/color-thief-node.mjs",
|
||||||
"umd:main": "dist/color-thief.umd.js",
|
"umd:main": "dist/color-thief.umd.js",
|
||||||
"amdName": "ColorThief",
|
"amdName": "ColorThief",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "microbundle --no-sourcemap && node ./build/build.js",
|
"build": "tsc --declaration && microbundle build --no-sourcemap built/*.js && node ./build/build.mjs",
|
||||||
"watch": "microbundle watch --no-sourcemap",
|
"watch": "microbundle watch --no-sourcemap",
|
||||||
"dev": "http-server",
|
"dev": "http-server",
|
||||||
"test": "mocha && cypress run --config video=false",
|
"test": "mocha && cypress run --config video=false",
|
||||||
"test:browser": "./node_modules/.bin/cypress run --headed --browser chrome",
|
"test:browser": "./node_modules/.bin/cypress run --headed",
|
||||||
"test:node": "mocha",
|
"test:node": "mocha",
|
||||||
"cypress": "./node_modules/.bin/cypress open",
|
"cypress": "./node_modules/.bin/cypress open",
|
||||||
"lint": "eslint ./src && prettier ./src"
|
"lint": "eslint ./src/*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.5.0",
|
||||||
"@stylistic/eslint-plugin": "^2.3.0",
|
"@stylistic/eslint-plugin": "^2.3.0",
|
||||||
|
"@types/eslint__js": "^8.42.3",
|
||||||
|
"chai": "^5.1.1",
|
||||||
|
"chai-as-promised": "^8.0.0",
|
||||||
"cypress": "^13.12.0",
|
"cypress": "^13.12.0",
|
||||||
"eslint": "^8.40.0"
|
"eslint": "^8.57.0",
|
||||||
|
"http-server": "^14.1.1",
|
||||||
|
"microbundle": "^0.15.1",
|
||||||
|
"mocha": "^10.5.2",
|
||||||
|
"mustache": "^4.2.0",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"typescript-eslint": "^7.14.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lokesh.dhakar/quantize": "^1.3.0",
|
"@lokesh.dhakar/quantize": "^1.3.0",
|
||||||
"ndarray-pixels": "^4.1.0"
|
"ndarray-pixels": "^4.1.0",
|
||||||
|
"sharp": "^0.33.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
// TODO: Update using this pr https://github.com/lokesh/color-thief/pull/254
|
|
||||||
const getPixels = require('get-pixels');
|
|
||||||
const quantize = require('@lokesh.dhakar/quantize');
|
|
||||||
|
|
||||||
function createPixelArray(imgData, pixelCount, quality) {
|
|
||||||
const pixels = imgData;
|
|
||||||
const pixelArray = [];
|
|
||||||
|
|
||||||
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
|
|
||||||
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 (typeof a === 'undefined' || a >= 125) {
|
|
||||||
if (!(r > 250 && g > 250 && b > 250)) {
|
|
||||||
pixelArray.push([r, g, b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pixelArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateOptions(options) {
|
|
||||||
let { colorCount, quality } = options;
|
|
||||||
|
|
||||||
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
|
|
||||||
colorCount = 10;
|
|
||||||
} else if (colorCount === 1) {
|
|
||||||
throw new Error(
|
|
||||||
'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
colorCount = Math.max(colorCount, 2);
|
|
||||||
colorCount = Math.min(colorCount, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof quality === 'undefined' ||
|
|
||||||
!Number.isInteger(quality) ||
|
|
||||||
quality < 1
|
|
||||||
) {
|
|
||||||
quality = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
colorCount,
|
|
||||||
quality,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadImg(img) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getPixels(img, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getColor(img, quality) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getPalette(img, 5, quality)
|
|
||||||
.then((palette) => {
|
|
||||||
resolve(palette[0]);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPalette(img, colorCount = 10, quality = 10) {
|
|
||||||
const options = validateOptions({
|
|
||||||
colorCount,
|
|
||||||
quality,
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
loadImg(img)
|
|
||||||
.then((imgData) => {
|
|
||||||
const pixelCount = imgData.shape[0] * imgData.shape[1];
|
|
||||||
const pixelArray = createPixelArray(
|
|
||||||
imgData.data,
|
|
||||||
pixelCount,
|
|
||||||
options.quality,
|
|
||||||
);
|
|
||||||
|
|
||||||
const cmap = quantize(pixelArray, options.colorCount);
|
|
||||||
const palette = cmap ? cmap.palette() : null;
|
|
||||||
|
|
||||||
resolve(palette);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getColor,
|
|
||||||
getPalette,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* Color Thief Node v3.0.0
|
||||||
|
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||||
|
*
|
||||||
|
* Thanks
|
||||||
|
* ------
|
||||||
|
* Nick Rabinowitz - For creating quantize.js.
|
||||||
|
* John Schulz - For clean up and optimization. @JFSIII
|
||||||
|
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||||
|
*
|
||||||
|
* License
|
||||||
|
* -------
|
||||||
|
* Copyright Lokesh Dhakar
|
||||||
|
* Released under the MIT license
|
||||||
|
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Thanks to this PR for the work of migrating to ndarray-pixels pr https://github.com/lokesh/color-thief/pull/254
|
||||||
|
import { getPixels } from 'ndarray-pixels';
|
||||||
|
import quantize from '@lokesh.dhakar/quantize';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import ndarray from 'ndarray';
|
||||||
|
|
||||||
|
interface ColorThiefResult {
|
||||||
|
'r': number;
|
||||||
|
'g': number;
|
||||||
|
'b': number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of arrays of pixels from an array of pixels
|
||||||
|
* @param {number[]} pixels An array of pixels
|
||||||
|
* @param {number} pixelCount The total number of pixels
|
||||||
|
* @param {number} quality
|
||||||
|
* @returns {number[][]} Returns an array of arrays of pixel values ([ r, g, b ])
|
||||||
|
*/
|
||||||
|
const createPixelArray = ( pixels: number[], pixelCount: number, quality: number ): number[][] => {
|
||||||
|
const pixelArray: number[][] = [];
|
||||||
|
|
||||||
|
for ( let i = 0; i < pixelCount; i = i + quality ) {
|
||||||
|
const offset = i * 4;
|
||||||
|
const r = pixels[ offset + 0 ];
|
||||||
|
const g = pixels[ offset + 1 ];
|
||||||
|
const b = pixels[ offset + 2 ];
|
||||||
|
const a = pixels[ offset + 3 ];
|
||||||
|
|
||||||
|
// If pixel is mostly opaque and not white
|
||||||
|
if ( typeof a === 'undefined' || a >= 125 ) {
|
||||||
|
if ( !( r > 250 && g > 250 && b > 250 ) ) {
|
||||||
|
pixelArray.push( [ r, g, b ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pixelArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Color-Thief options
|
||||||
|
* @param {{ colorCount: number; quality: number; }} options The options object
|
||||||
|
* @returns {{ colorCount: number, quality: number }} The same object, but validated
|
||||||
|
*/
|
||||||
|
const validateOptions = ( options: { 'colorCount': number; 'quality': number; } ): { 'colorCount': number; 'quality': number; } => {
|
||||||
|
let colorCount = options.colorCount;
|
||||||
|
let quality = options.quality;
|
||||||
|
|
||||||
|
if ( typeof colorCount === 'undefined' || !Number.isInteger( colorCount ) ) {
|
||||||
|
colorCount = 10;
|
||||||
|
} else if ( colorCount === 1 ) {
|
||||||
|
throw new Error(
|
||||||
|
'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
colorCount = Math.max( colorCount, 2 );
|
||||||
|
colorCount = Math.min( colorCount, 20 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof quality === 'undefined' ||
|
||||||
|
!Number.isInteger( quality ) ||
|
||||||
|
quality < 1
|
||||||
|
) {
|
||||||
|
quality = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
colorCount,
|
||||||
|
quality,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an image from the disk an pre-process
|
||||||
|
* @param {string} img Path to the image on the disk
|
||||||
|
* @returns {Promise<ndarray.NdArray<Uint8Array>>} Returns the pre-processed image
|
||||||
|
*/
|
||||||
|
const loadImg = ( img: string ): Promise<ndarray.NdArray<Uint8Array>> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
sharp( img )
|
||||||
|
.toBuffer()
|
||||||
|
.then( ( buffer ) => sharp( buffer ).metadata()
|
||||||
|
.then( ( metadata ) => ( { buffer, 'format': metadata.format } ) ) )
|
||||||
|
.then( ( { buffer, format } ) => getPixels( buffer, format ) )
|
||||||
|
.then( resolve )
|
||||||
|
.catch( reject );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dominant color of an image
|
||||||
|
* @param {string} img Path to the image on the disk
|
||||||
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {Promise<ColorThiefResult>} Returns the dominant color
|
||||||
|
*/
|
||||||
|
const getColor = ( img: string, quality: number = 10 ): Promise<ColorThiefResult> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
getPalette( img, 5, quality )
|
||||||
|
.then( ( palette ) => {
|
||||||
|
resolve( palette[0] );
|
||||||
|
} )
|
||||||
|
.catch( ( err ) => {
|
||||||
|
reject( err );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the color palette of an image
|
||||||
|
* @param {string} img Path to the image on the disk
|
||||||
|
* @param {number?} colorCount (Optional) the target amount of colors to try and extract
|
||||||
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {Promise<ColorThiefResult[]>} Returns an array of colors
|
||||||
|
*/
|
||||||
|
const getPalette = ( img: string, colorCount: number = 10, quality: number = 10 ): Promise<ColorThiefResult[]> => {
|
||||||
|
const options = validateOptions( {
|
||||||
|
colorCount,
|
||||||
|
quality,
|
||||||
|
} );
|
||||||
|
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
loadImg( img )
|
||||||
|
.then( ( imgData ) => {
|
||||||
|
const pixelCount = imgData.shape[0] * imgData.shape[1];
|
||||||
|
const pixelArray = createPixelArray(
|
||||||
|
Array.from( imgData.data ),
|
||||||
|
pixelCount,
|
||||||
|
options.quality,
|
||||||
|
);
|
||||||
|
|
||||||
|
const cmap = quantize( pixelArray, options.colorCount );
|
||||||
|
const palette = cmap ? cmap.palette() : null;
|
||||||
|
|
||||||
|
resolve( palette );
|
||||||
|
} )
|
||||||
|
.catch( ( err ) => {
|
||||||
|
reject( err );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getColor,
|
||||||
|
getPalette,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
import quantize from '../node_modules/@lokesh.dhakar/quantize/dist/index.mjs';
|
|
||||||
import core from './core.js';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Color Thief v2.4.0
|
|
||||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
|
||||||
*
|
|
||||||
* Thanks
|
|
||||||
* ------
|
|
||||||
* Nick Rabinowitz - For creating quantize.js.
|
|
||||||
* John Schulz - For clean up and optimization. @JFSIII
|
|
||||||
* Nathan Spady - For adding drag and drop support to the demo page.
|
|
||||||
*
|
|
||||||
* License
|
|
||||||
* -------
|
|
||||||
* Copyright Lokesh Dhakar
|
|
||||||
* Released under the MIT license
|
|
||||||
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
|
||||||
*
|
|
||||||
* @license
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const CanvasImage = function (image) {
|
|
||||||
this.canvas = document.createElement('canvas');
|
|
||||||
this.context = this.canvas.getContext('2d');
|
|
||||||
this.width = this.canvas.width = image.naturalWidth;
|
|
||||||
this.height = this.canvas.height = image.naturalHeight;
|
|
||||||
this.context.drawImage(image, 0, 0, this.width, this.height);
|
|
||||||
};
|
|
||||||
|
|
||||||
CanvasImage.prototype.getImageData = function () {
|
|
||||||
return this.context.getImageData(0, 0, this.width, this.height);
|
|
||||||
};
|
|
||||||
|
|
||||||
var ColorThief = function () {};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getColor(sourceImage[, quality])
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
|
|
||||||
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
|
|
||||||
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
|
||||||
* most dominant color.
|
|
||||||
*
|
|
||||||
* */
|
|
||||||
ColorThief.prototype.getColor = function (sourceImage, quality = 10) {
|
|
||||||
const palette = this.getPalette(sourceImage, 5, quality);
|
|
||||||
const dominantColor = palette[0];
|
|
||||||
return dominantColor;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getPalette(sourceImage[, colorCount, quality])
|
|
||||||
* returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
|
|
||||||
*
|
|
||||||
* Use the median cut algorithm provided by quantize.js to cluster similar colors.
|
|
||||||
*
|
|
||||||
* colorCount determines the size of the palette; the number of colors returned. If not set, it
|
|
||||||
* defaults to 10.
|
|
||||||
*
|
|
||||||
* quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
|
|
||||||
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
|
|
||||||
* faster the palette generation but the greater the likelihood that colors will be missed.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
ColorThief.prototype.getPalette = function (sourceImage, colorCount, quality) {
|
|
||||||
const options = core.validateOptions({
|
|
||||||
colorCount,
|
|
||||||
quality,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create custom CanvasImage object
|
|
||||||
const image = new CanvasImage(sourceImage);
|
|
||||||
const imageData = image.getImageData();
|
|
||||||
const pixelCount = image.width * image.height;
|
|
||||||
|
|
||||||
const pixelArray = core.createPixelArray(
|
|
||||||
imageData.data,
|
|
||||||
pixelCount,
|
|
||||||
options.quality,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Send array to quantize function which clusters values
|
|
||||||
// using median cut algorithm
|
|
||||||
const cmap = quantize(pixelArray, options.colorCount);
|
|
||||||
const palette = cmap ? cmap.palette() : null;
|
|
||||||
|
|
||||||
return palette;
|
|
||||||
};
|
|
||||||
|
|
||||||
ColorThief.prototype.getColorFromUrl = function (imageUrl, callback, quality) {
|
|
||||||
const sourceImage = document.createElement('img');
|
|
||||||
|
|
||||||
sourceImage.addEventListener('load', () => {
|
|
||||||
const palette = this.getPalette(sourceImage, 5, quality);
|
|
||||||
const dominantColor = palette[0];
|
|
||||||
callback(dominantColor, imageUrl);
|
|
||||||
});
|
|
||||||
sourceImage.src = imageUrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
ColorThief.prototype.getImageData = function (imageUrl, callback) {
|
|
||||||
let xhr = new XMLHttpRequest();
|
|
||||||
xhr.open('GET', imageUrl, true);
|
|
||||||
xhr.responseType = 'arraybuffer';
|
|
||||||
xhr.onload = function () {
|
|
||||||
if (this.status == 200) {
|
|
||||||
let uInt8Array = new Uint8Array(this.response);
|
|
||||||
i = uInt8Array.length;
|
|
||||||
let binaryString = new Array(i);
|
|
||||||
for (let i = 0; i < uInt8Array.length; i++) {
|
|
||||||
binaryString[i] = String.fromCharCode(uInt8Array[i]);
|
|
||||||
}
|
|
||||||
let data = binaryString.join('');
|
|
||||||
let base64 = window.btoa(data);
|
|
||||||
callback('data:image/png;base64,' + base64);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
};
|
|
||||||
|
|
||||||
ColorThief.prototype.getColorAsync = function (imageUrl, callback, quality) {
|
|
||||||
const thief = this;
|
|
||||||
this.getImageData(imageUrl, function (imageData) {
|
|
||||||
const sourceImage = document.createElement('img');
|
|
||||||
sourceImage.addEventListener('load', function () {
|
|
||||||
const palette = thief.getPalette(sourceImage, 5, quality);
|
|
||||||
const dominantColor = palette[0];
|
|
||||||
callback(dominantColor, this);
|
|
||||||
});
|
|
||||||
sourceImage.src = imageData;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ColorThief;
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import quantize from '../node_modules/@lokesh.dhakar/quantize/dist/index.mjs';
|
import quantize from '../node_modules/@lokesh.dhakar/quantize/dist/index.mjs';
|
||||||
import core from './core.js';
|
import core from './core.js';
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Color Thief v2.4.0
|
* Color Thief v3.0.0
|
||||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||||
*
|
*
|
||||||
* Thanks
|
* Thanks
|
||||||
@@ -17,50 +17,61 @@ import core from './core.js';
|
|||||||
* Released under the MIT license
|
* Released under the MIT license
|
||||||
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||||
*
|
*
|
||||||
* @license
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
CanvasImage Class
|
@class CanvasImage 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.
|
||||||
*/
|
*/
|
||||||
|
class CanvasImage {
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
|
||||||
const CanvasImage = function (image: HTMLImageElement) {
|
context: CanvasRenderingContext2D;
|
||||||
this.canvas = document.createElement('canvas');
|
|
||||||
this.context = this.canvas.getContext('2d');
|
width: number;
|
||||||
|
|
||||||
|
height: number;
|
||||||
|
|
||||||
|
constructor ( image: HTMLImageElement ) {
|
||||||
|
this.canvas = document.createElement( 'canvas' );
|
||||||
|
this.context = this.canvas.getContext( '2d' );
|
||||||
this.width = this.canvas.width = image.naturalWidth;
|
this.width = this.canvas.width = image.naturalWidth;
|
||||||
this.height = this.canvas.height = image.naturalHeight;
|
this.height = this.canvas.height = image.naturalHeight;
|
||||||
this.context.drawImage(image, 0, 0, this.width, this.height);
|
this.context.drawImage( image, 0, 0, this.width, this.height );
|
||||||
};
|
}
|
||||||
|
|
||||||
CanvasImage.prototype.getImageData = function () {
|
getImageData = function () {
|
||||||
return this.context.getImageData(0, 0, this.width, this.height);
|
return this.context.getImageData( 0, 0, this.width, this.height );
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ColorThiefResult {
|
|
||||||
r: number;
|
|
||||||
g: number;
|
|
||||||
b: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface ColorThiefResult {
|
||||||
|
'r': number;
|
||||||
|
'g': number;
|
||||||
|
'b': number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class The main ColorThief class
|
||||||
|
*/
|
||||||
class ColorThief {
|
class ColorThief {
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
* @param {HTMLImageElement} sourceImage:HTMLImageElement
|
* @param {HTMLImageElement} sourceImage:HTMLImageElement
|
||||||
* @param {number} quality Quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
|
|
||||||
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
* most dominant color.
|
* most dominant color.
|
||||||
* @returns {ColorThiefResult} returns {r: num, g: num, b: num}
|
* @returns {ColorThiefResult} returns {r: num, g: num, b: num}
|
||||||
*/
|
*/
|
||||||
getColor(
|
getColor (
|
||||||
sourceImage: HTMLImageElement,
|
sourceImage: HTMLImageElement,
|
||||||
quality: number = 10,
|
quality: number = 10,
|
||||||
): ColorThiefResult {
|
): ColorThiefResult {
|
||||||
const palette = this.getPalette(sourceImage, 5, quality);
|
const palette = this.getPalette( sourceImage, 5, quality );
|
||||||
return palette[0];
|
return palette[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,23 +80,23 @@ class ColorThief {
|
|||||||
* @param {HTMLImageElement} sourceImage The image you want to have processed, as an HTMLImageElement
|
* @param {HTMLImageElement} sourceImage The image you want to have processed, as an HTMLImageElement
|
||||||
* @param {number?} colorCount colorCount determines the size of the palette; the number of colors returned. If not set, it
|
* @param {number?} colorCount colorCount determines the size of the palette; the number of colors returned. If not set, it
|
||||||
* defaults to 10.
|
* defaults to 10.
|
||||||
* @param {number?} quality quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
* faster the palette generation but the greater the likelihood that colors will be missed.
|
* most dominant color.
|
||||||
* @returns {ColorThiefResult[]} returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
|
* @returns {ColorThiefResult[] | null} returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
|
||||||
*/
|
*/
|
||||||
getPalette(
|
getPalette (
|
||||||
sourceImage: HTMLImageElement,
|
sourceImage: HTMLImageElement,
|
||||||
colorCount?: number,
|
colorCount?: number,
|
||||||
quality: number = 10,
|
quality: number = 10,
|
||||||
): ColorThiefResult[] {
|
): ColorThiefResult[] | null {
|
||||||
const options = core.validateOptions({
|
const options = core.validateOptions( {
|
||||||
colorCount,
|
colorCount,
|
||||||
quality,
|
quality,
|
||||||
});
|
} );
|
||||||
|
|
||||||
// Create custom CanvasImage object
|
// Create custom CanvasImage object
|
||||||
const image = new CanvasImage(sourceImage);
|
const image = new CanvasImage( sourceImage );
|
||||||
const imageData = image.getImageData();
|
const imageData = image.getImageData();
|
||||||
const pixelCount = image.width * image.height;
|
const pixelCount = image.width * image.height;
|
||||||
|
|
||||||
@@ -97,83 +108,127 @@ class ColorThief {
|
|||||||
|
|
||||||
// Send array to quantize function which clusters values
|
// Send array to quantize function which clusters values
|
||||||
// using median cut algorithm
|
// using median cut algorithm
|
||||||
const cmap = quantize(pixelArray, options.colorCount);
|
const cmap = quantize( pixelArray, options.colorCount );
|
||||||
const palette = cmap ? cmap.palette() : null;
|
const palette: null | ColorThiefResult[] = cmap ? cmap.palette() : null;
|
||||||
|
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description
|
* [ DEPRECATED ] Get the dominant color of an image, which is fetched from a URL.
|
||||||
* @param {any} imageUrl
|
* @param {string} imageUrl
|
||||||
* @param {any} callback
|
* @param {( color: ColorThiefResult, url: string ) => void} callback The callback function called when the image has finished processing
|
||||||
* @param {any} quality
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
* @returns {any}
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {void}
|
||||||
|
* @deprecated since Version 3.0, use getColorFromURLPromise instead
|
||||||
*/
|
*/
|
||||||
getColorFromUrl(imageUrl, callback, quality) {
|
getColorFromUrl ( imageUrl: string, callback: ( color: ColorThiefResult, url: string ) => void, quality: number = 10 ): void {
|
||||||
const sourceImage = document.createElement('img');
|
const sourceImage = document.createElement( 'img' );
|
||||||
|
|
||||||
sourceImage.addEventListener('load', () => {
|
sourceImage.addEventListener( 'load', () => {
|
||||||
const palette = this.getPalette(sourceImage, 5, quality);
|
const palette = this.getPalette( sourceImage, 5, quality );
|
||||||
const dominantColor = palette[0];
|
const dominantColor = palette[0];
|
||||||
callback(dominantColor, imageUrl);
|
callback( dominantColor, imageUrl );
|
||||||
});
|
} );
|
||||||
sourceImage.src = imageUrl;
|
sourceImage.src = imageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description
|
* [ DEPRECATED ] Get Image Data as a Base64 string from a URL
|
||||||
* @param {string} imageUrl
|
* @param {string} imageUrl The URL to the image. Has to be a full URL, if not relative
|
||||||
* @param {string} callback
|
* @param {( data: string ) => void} callback The callback function called, once download is complete. Will only be called if successful at downloading
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
|
* @deprecated since Version 3.0, use getImageDataFromURL instead
|
||||||
*/
|
*/
|
||||||
getImageData(imageUrl: string, callback) {
|
getImageData ( imageUrl: string, callback: ( data: string ) => void ): void {
|
||||||
let xhr = new XMLHttpRequest();
|
fetch( imageUrl ).then( ( res ) => {
|
||||||
xhr.open('GET', imageUrl, true);
|
if ( res.status === 200 ) {
|
||||||
xhr.responseType = 'arraybuffer';
|
res.arrayBuffer().then( ( response ) => {
|
||||||
|
const uInt8Array = new Uint8Array( response );
|
||||||
fetch(imageUrl).then((res) => {
|
const binaryString = new Array( uInt8Array.length );
|
||||||
if (res.status === 200) {
|
for ( let i = 0; i < uInt8Array.length; i++ ) {
|
||||||
res.arrayBuffer().then((response) => {
|
binaryString[i] = String.fromCharCode( uInt8Array[i] );
|
||||||
const uInt8Array = new Uint8Array(response);
|
|
||||||
let binaryString = new Array(uInt8Array.length);
|
|
||||||
for (let i = 0; i < uInt8Array.length; i++) {
|
|
||||||
binaryString[i] = String.fromCharCode(uInt8Array[i]);
|
|
||||||
}
|
}
|
||||||
let data = binaryString.join('');
|
const data = binaryString.join( '' );
|
||||||
let base64 = window.btoa(data);
|
callback( 'data:image/png;base64,' + window.btoa( data ) );
|
||||||
});
|
} );
|
||||||
}
|
}
|
||||||
});
|
} );
|
||||||
|
|
||||||
xhr.onload = function () {
|
|
||||||
if (this.status == 200) {
|
|
||||||
let uInt8Array = new Uint8Array(this.response);
|
|
||||||
|
|
||||||
callback('data:image/png;base64,' + base64);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description
|
* Get Image Data as a Base64 string from a URL
|
||||||
* @param {any} imageUrl
|
* @param {string} imageUrl The URL to the image. Has to be a full URL, if not relative
|
||||||
* @param {any} callback
|
* @returns {Promise<string>} returns a Promise resolving to a string with the base64 string
|
||||||
* @param {any} quality
|
|
||||||
* @returns {any}
|
|
||||||
*/
|
*/
|
||||||
getColorAsync = function (imageUrl, callback, quality) {
|
getImageDataFromURL ( imageUrl: string ): Promise<string> {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
fetch( imageUrl ).then( ( res ) => {
|
||||||
|
if ( res.status === 200 ) {
|
||||||
|
res.arrayBuffer().then( ( response ) => {
|
||||||
|
const uInt8Array = new Uint8Array( response );
|
||||||
|
const binaryString = new Array( uInt8Array.length );
|
||||||
|
for ( let i = 0; i < uInt8Array.length; i++ ) {
|
||||||
|
binaryString[i] = String.fromCharCode( uInt8Array[i] );
|
||||||
|
}
|
||||||
|
const data = binaryString.join( '' );
|
||||||
|
resolve( 'data:image/png;base64,' + window.btoa( data ) );
|
||||||
|
} );
|
||||||
|
} else {
|
||||||
|
reject( 'ERR_LOADING' );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ DEPRECATED ] Same as getColor, but async
|
||||||
|
* @param {string} imageUrl
|
||||||
|
* @param {( data: ColorThiefResult ) => void} callback
|
||||||
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {void}
|
||||||
|
* @deprecated since Version 3.0, in favour of getColorPromise. Only retained for compatibility
|
||||||
|
*/
|
||||||
|
getColorAsync = ( imageUrl: string, callback: ( data: ColorThiefResult, img: HTMLImageElement ) => void, quality: number | null ): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const thief = this;
|
const thief = this;
|
||||||
this.getImageData(imageUrl, function (imageData) {
|
this.getImageDataFromURL( imageUrl ).then( ( imageData ) => {
|
||||||
const sourceImage = document.createElement('img');
|
const sourceImage = document.createElement( 'img' );
|
||||||
sourceImage.addEventListener('load', function () {
|
sourceImage.addEventListener( 'load', () => {
|
||||||
const palette = thief.getPalette(sourceImage, 5, quality);
|
const palette = thief.getPalette( sourceImage, 5, quality );
|
||||||
const dominantColor = palette[0];
|
callback( palette[ 0 ], sourceImage );
|
||||||
callback(dominantColor, this);
|
} );
|
||||||
});
|
|
||||||
sourceImage.src = imageData;
|
sourceImage.src = imageData;
|
||||||
});
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as getColor, but promise-based
|
||||||
|
* @param {string} imageUrl
|
||||||
|
* @param {number?} quality (Optional) 1 = highest quality, 10 = default. The bigger the number, the
|
||||||
|
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||||
|
* most dominant color.
|
||||||
|
* @returns {Promise<{ 'color': ColorThiefResult, 'img': HTMLImageElement }>} Returns a promise resolving to an object containing the color and the image element
|
||||||
|
*/
|
||||||
|
getColorPromise = ( imageUrl: string, quality: number | null ): Promise<{ 'color': ColorThiefResult, 'img': HTMLImageElement }> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const thief = this;
|
||||||
|
this.getImageDataFromURL( imageUrl ).then( ( imageData ) => {
|
||||||
|
const sourceImage = document.createElement( 'img' );
|
||||||
|
sourceImage.addEventListener( 'load', () => {
|
||||||
|
const palette = thief.getPalette( sourceImage, 5, quality );
|
||||||
|
resolve( { 'color': palette[ 0 ], 'img': sourceImage } );
|
||||||
|
} );
|
||||||
|
sourceImage.src = imageData;
|
||||||
|
} ).catch( ( e ) => {
|
||||||
|
reject( e );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
53
src/core.js
53
src/core.js
@@ -1,53 +0,0 @@
|
|||||||
function createPixelArray(imgData, pixelCount, quality) {
|
|
||||||
const pixels = imgData;
|
|
||||||
const pixelArray = [];
|
|
||||||
|
|
||||||
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
|
|
||||||
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 (typeof a === 'undefined' || a >= 125) {
|
|
||||||
if (!(r > 250 && g > 250 && b > 250)) {
|
|
||||||
pixelArray.push([r, g, b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pixelArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateOptions(options) {
|
|
||||||
let { colorCount, quality } = options;
|
|
||||||
|
|
||||||
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
|
|
||||||
colorCount = 10;
|
|
||||||
} else if (colorCount === 1) {
|
|
||||||
throw new Error(
|
|
||||||
'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
colorCount = Math.max(colorCount, 2);
|
|
||||||
colorCount = Math.min(colorCount, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof quality === 'undefined' ||
|
|
||||||
!Number.isInteger(quality) ||
|
|
||||||
quality < 1
|
|
||||||
) {
|
|
||||||
quality = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
colorCount,
|
|
||||||
quality,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
createPixelArray,
|
|
||||||
validateOptions,
|
|
||||||
};
|
|
||||||
84
src/core.ts
84
src/core.ts
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Color Thief Core v3.0.0
|
||||||
|
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||||
|
*
|
||||||
|
* Thanks
|
||||||
|
* ------
|
||||||
|
* Nick Rabinowitz - For creating quantize.js.
|
||||||
|
* John Schulz - For clean up and optimization. @JFSIII
|
||||||
|
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||||
|
*
|
||||||
|
* License
|
||||||
|
* -------
|
||||||
|
* Copyright Lokesh Dhakar
|
||||||
|
* Released under the MIT license
|
||||||
|
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of arrays of pixels from an array of pixels
|
||||||
|
* @param {number[]} pixels An array of pixels
|
||||||
|
* @param {number} pixelCount The total number of pixels
|
||||||
|
* @param {number} quality
|
||||||
|
* @returns {number[][]} Returns an array of arrays of pixel values ([ r, g, b ])
|
||||||
|
*/
|
||||||
|
const createPixelArray = ( pixels: number[], pixelCount: number, quality: number ): number[][] => {
|
||||||
|
const pixelArray: number[][] = [];
|
||||||
|
|
||||||
|
for ( let i = 0; i < pixelCount; i = i + quality ) {
|
||||||
|
const offset = i * 4;
|
||||||
|
const r = pixels[ offset + 0 ];
|
||||||
|
const g = pixels[ offset + 1 ];
|
||||||
|
const b = pixels[ offset + 2 ];
|
||||||
|
const a = pixels[ offset + 3 ];
|
||||||
|
|
||||||
|
// If pixel is mostly opaque and not white
|
||||||
|
if ( typeof a === 'undefined' || a >= 125 ) {
|
||||||
|
if ( !( r > 250 && g > 250 && b > 250 ) ) {
|
||||||
|
pixelArray.push( [ r, g, b ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pixelArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Color-Thief options
|
||||||
|
* @param {{ colorCount: number; quality: number; }} options The options object
|
||||||
|
* @returns {{ colorCount: number, quality: number }} The same object, but validated
|
||||||
|
*/
|
||||||
|
const validateOptions = ( options: { 'colorCount': number; 'quality': number; } ): { 'colorCount': number; 'quality': number; } => {
|
||||||
|
let colorCount = options.colorCount;
|
||||||
|
let quality = options.quality;
|
||||||
|
|
||||||
|
if ( typeof colorCount === 'undefined' || !Number.isInteger( colorCount ) ) {
|
||||||
|
colorCount = 10;
|
||||||
|
} else if ( colorCount === 1 ) {
|
||||||
|
throw new Error(
|
||||||
|
'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
colorCount = Math.max( colorCount, 2 );
|
||||||
|
colorCount = Math.min( colorCount, 20 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof quality === 'undefined' ||
|
||||||
|
!Number.isInteger( quality ) ||
|
||||||
|
quality < 1
|
||||||
|
) {
|
||||||
|
quality = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
colorCount,
|
||||||
|
quality,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createPixelArray,
|
||||||
|
validateOptions,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
const { resolve } = require('path');
|
import { resolve } from 'path';
|
||||||
const ColorThief = require(resolve(process.cwd(), "dist/color-thief.js"));
|
import ColorThief from '../dist/color-thief-node.cjs';
|
||||||
const img = resolve(process.cwd(), 'cypress/test-pages/img/rainbow-vertical.png');
|
const img = resolve( process.cwd(), 'cypress/test-pages/img/rainbow-vertical.png' );
|
||||||
const chai = require("chai");
|
import { expect as _expect, use } from 'chai';
|
||||||
const expect = chai.expect;
|
const expect = _expect;
|
||||||
chai.use(require("chai-as-promised"));
|
import chaiAsPromised from 'chai-as-promised';
|
||||||
|
use( chaiAsPromised );
|
||||||
|
|
||||||
|
describe( 'getColor()', function () {
|
||||||
|
it( 'returns valid color', function () {
|
||||||
|
return expect( ColorThief.getColor( img ) ).to.eventually.have.lengthOf( 3 );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
describe('getColor()', function() {
|
describe( 'getPalette()', function () {
|
||||||
it('returns valid color', function() {
|
it( 'returns 5 colors when colorCount set to 5', function () {
|
||||||
return expect(ColorThief.getColor(img)).to.eventually.have.lengthOf(3);
|
return expect( ColorThief.getPalette( img, 5 ) ).to.eventually.have.lengthOf( 5 );
|
||||||
});
|
} );
|
||||||
});
|
|
||||||
|
|
||||||
describe('getPalette()', function() {
|
it( 'returns 9 colors when colorCount set to 9', function () {
|
||||||
it('returns 5 colors when colorCount set to 5', function() {
|
return expect( ColorThief.getPalette( img, 9 ) ).to.eventually.have.lengthOf( 9 );
|
||||||
return expect(ColorThief.getPalette(img, 5)).to.eventually.have.lengthOf(5);
|
} );
|
||||||
});
|
} );
|
||||||
|
|
||||||
it('returns 9 colors when colorCount set to 9', function() {
|
|
||||||
return expect(ColorThief.getPalette(img, 9)).to.eventually.have.lengthOf(9);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|||||||
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./built",
|
||||||
|
"allowJs": true,
|
||||||
|
"target": "ES6",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"module": "NodeNext"
|
||||||
|
},
|
||||||
|
"include": [ "./src/**/*" ],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user