This commit is contained in:
2024-06-28 13:32:22 +02:00
parent 18cad78969
commit bfbb5d64de
34 changed files with 8132 additions and 739 deletions
+2
View File
@@ -16,3 +16,5 @@ rsync-exclude
node_modules
.idea
built
+16 -15
View File
@@ -1,5 +1,6 @@
var fs = require('fs');
const { resolve } = require('path');
/* eslint-disable no-undef */
import { copyFile } from 'fs';
import path from 'path';
/*
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 legacyRelPath = 'dist/color-thief.min.js';
const umdPath = resolve(process.cwd(), umdRelPath);
const legacyPath = resolve(process.cwd(), legacyRelPath);
const umdPath = path.join( path.resolve( path.dirname( '' ) ), umdRelPath );
const legacyPath = path.join( path.resolve( path.dirname( '' ) ), legacyRelPath );
fs.copyFile(umdPath, legacyPath, (err) => {
if (err) throw err;
console.log(`${umdRelPath} copied to ${legacyRelPath}.`);
});
copyFile( umdPath, legacyPath, ( err ) => {
if ( err ) throw err;
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 srcNodePath = resolve(process.cwd(), srcNodeRelPath);
const distNodePath = resolve(process.cwd(), distNodeRelPath);
const srcNodePath = path.join( path.resolve( path.dirname( '' ) ), srcNodeRelPath );
const distNodePath = path.join( path.resolve( path.dirname( '' ) ), distNodeRelPath );
fs.copyFile(srcNodePath, distNodePath, (err) => {
if (err) throw err;
console.log(`${srcNodeRelPath} copied to ${distNodeRelPath}.`);
});
copyFile( srcNodePath, distNodePath, ( err ) => {
if ( err ) throw err;
console.log( `${ srcNodeRelPath } copied to ${ distNodeRelPath }.` );
} );
+15
View 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,
},
} );
-13
View File
@@ -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,
},
})
+1 -1
View File
@@ -28,7 +28,7 @@
if (img.complete) {
getColorFromImage(img);
} else {
image.addEventListener('load', function() {
img.addEventListener('load', function() {
getColorFromImage(img);
});
}
+34 -25
View File
@@ -11,49 +11,58 @@ var images = [
];
// Render example images
var examplesHTML = Mustache.to_html(document.getElementById('image-tpl').innerHTML, images);
document.getElementById('example-images').innerHTML = examplesHTML;
console.log( Mustache );
var examplesHTML = Mustache.render( document.getElementById( 'image-tpl' ).innerHTML, images );
document.getElementById( 'example-images' ).innerHTML = examplesHTML;
// Once images are loaded, process them
document.querySelectorAll('.image').forEach((image) => {
const section = image.closest('.image-section');
if (this.complete) {
showColorsForImage(image, section);
document.querySelectorAll( '.image' ).forEach( ( image ) => {
const section = image.closest( '.image-section' );
if ( this.complete ) {
showColorsForImage( image, section );
} else {
image.addEventListener('load', function() {
showColorsForImage(image, section);
});
image.addEventListener( 'load', function () {
showColorsForImage( image, section );
// showColorsForImageURLNewAPI( )
} );
}
})
} );
// Run Color Thief functions and display results below image.
// We also log execution time of functions for display.
const showColorsForImage = function(image, section) {
const showColorsForImage = function ( image, section ) {
// getColor(img)
let start = Date.now();
let result = colorThief.getColor(image);
let result = colorThief.getColor( image );
let elapsedTime = Date.now() - start;
const colorHTML = Mustache.to_html(document.getElementById('color-tpl').innerHTML, {
color: result,
colorStr: result.toString(),
const colorHTML = Mustache.render( document.getElementById( 'color-tpl' ).innerHTML, {
'color': result,
'colorStr': result.toString(),
elapsedTime
})
} );
// getPalette(img)
let paletteHTML = '';
let colorCounts = [2, 3, 5, 7, 10, 20];
colorCounts.forEach((count) => {
let colorCounts = [
2,
3,
5,
7,
10,
20
];
colorCounts.forEach( ( count ) => {
let start = Date.now();
let result = colorThief.getPalette(image, count);
let result = colorThief.getPalette( image, count );
let elapsedTime = Date.now() - start;
paletteHTML += Mustache.to_html(document.getElementById('palette-tpl').innerHTML, {
paletteHTML += Mustache.render( document.getElementById( 'palette-tpl' ).innerHTML, {
count,
palette: result,
paletteStr: result.toString(),
'palette': result,
'paletteStr': result.toString(),
elapsedTime
})
});
} );
} );
const outputEl = section.querySelector('.output');
const outputEl = section.querySelector( '.output' );
outputEl.innerHTML += colorHTML + paletteHTML;
};
+1
View 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
View 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
View 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
View 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
View File
File diff suppressed because one or more lines are too long
+108 -66
View File
@@ -1,17 +1,40 @@
const getPixels = require('get-pixels');
const quantize = require('@lokesh.dhakar/quantize');
function createPixelArray(imgData, pixelCount, quality) {
const pixels = imgData;
/**
* 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';
/**
* 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 = [];
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];
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)) {
@@ -20,80 +43,99 @@ function createPixelArray(imgData, pixelCount, quality) {
}
}
return pixelArray;
}
function validateOptions(options) {
let { colorCount, quality } = options;
};
/**
* 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) => {
let colorCount = options.colorCount;
let quality = options.quality;
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
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()');
} else {
}
else {
colorCount = Math.max(colorCount, 2);
colorCount = Math.min(colorCount, 20);
}
if (typeof quality === 'undefined' || !Number.isInteger(quality) || quality < 1) {
if (typeof quality === 'undefined' ||
!Number.isInteger(quality) ||
quality < 1) {
quality = 10;
}
return {
colorCount,
quality
}
}
function loadImg(img) {
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) => {
return new Promise((resolve, reject) => {
getPixels(img, function(err, data) {
if(err) {
reject(err)
} else {
resolve(data);
}
})
sharp(img)
.toBuffer()
.then((buffer) => sharp(buffer).metadata()
.then((metadata) => ({ buffer, 'format': metadata.format })))
.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) => {
getPalette(img, 5, quality)
.then(palette => {
resolve(palette[0]);
})
.catch(err => {
reject(err);
})
.then((palette) => {
resolve(palette[0]);
})
.catch((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({
colorCount,
quality
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);
})
.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
getPalette,
};
+2 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
-1
View File
File diff suppressed because one or more lines are too long
+2 -1
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -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
View 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
View 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
View 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
View 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}}}});
+60 -70
View File
@@ -1,75 +1,65 @@
import stylisticTS from '@stylistic/eslint-plugin';
// @ts-check
export default [
{
plugins: {
'@stylistic/ts': stylisticTS,
},
languageOptions: {
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',
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': {
'@stylistic/js': stylistic,
'@stylistic/ts': stylistic,
},
'rules': {
// Formatting
// https://eslint.style/rules/js/array-bracket-newline
// TODO: Finish up
'@stylistic/ts/indent': [ 'error', 4 ]
'@stylistic/js/array-bracket-newline': [ 'error', { 'multiline': true, 'minItems': 4 } ],
'@stylistic/js/array-bracket-spacing': [ 'error', 'always' ],
'@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',
}
}
]
);
+7430 -120
View File
File diff suppressed because it is too large Load Diff
+24 -8
View File
@@ -16,34 +16,50 @@
"photo",
"canvas"
],
"type": "module",
"homepage": "http://lokeshdhakar.com/projects/color-thief/",
"repository": {
"type": "git",
"url": "https://github.com/lokesh/color-thief.git"
},
"license": "MIT",
"source": "src/color-thief.js",
"main": "dist/color-thief.js",
"module": "dist/color-thief.mjs",
"source": "src/color-thief.ts",
"main": "dist/color-thief-node.cjs",
"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",
"amdName": "ColorThief",
"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",
"dev": "http-server",
"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",
"cypress": "./node_modules/.bin/cypress open",
"lint": "eslint ./src && prettier ./src"
"lint": "eslint ./src/*"
},
"devDependencies": {
"@eslint/js": "^9.5.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",
"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": {
"@lokesh.dhakar/quantize": "^1.3.0",
"ndarray-pixels": "^4.1.0"
"ndarray-pixels": "^4.1.0",
"sharp": "^0.33.4"
}
}
-108
View File
@@ -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,
};
+169
View File
@@ -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,
};
-147
View File
@@ -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;
+143 -88
View File
@@ -1,8 +1,8 @@
import quantize from '../node_modules/@lokesh.dhakar/quantize/dist/index.mjs';
import core from './core.js';
/*
* Color Thief v2.4.0
/**
* Color Thief v3.0.0
* by Lokesh Dhakar - http://www.lokeshdhakar.com
*
* Thanks
@@ -17,50 +17,61 @@ import core from './core.js';
* Released under the MIT license
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
*
* @license
* @license MIT
*/
/*
CanvasImage Class
Class that wraps the html image element and canvas.
/**
@class CanvasImage that wraps the html image element and canvas.
It also simplifies some of the canvas context manipulation
with a set of helper functions.
*/
class CanvasImage {
canvas: HTMLCanvasElement;
const CanvasImage = function (image: HTMLImageElement) {
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);
};
context: CanvasRenderingContext2D;
CanvasImage.prototype.getImageData = function () {
return this.context.getImageData(0, 0, this.width, this.height);
};
width: number;
interface ColorThiefResult {
r: number;
g: number;
b: 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.height = this.canvas.height = image.naturalHeight;
this.context.drawImage( image, 0, 0, this.width, this.height );
}
getImageData = function () {
return this.context.getImageData( 0, 0, this.width, this.height );
};
}
interface ColorThiefResult {
'r': number;
'g': number;
'b': number;
}
/**
* @class The main ColorThief class
*/
class ColorThief {
/**
* Use the median cut algorithm provided by quantize.js to cluster similar
* colors and return the base color from the largest cluster.
* @param {HTMLImageElement} sourceImage:HTMLImageElement
* @param {number} quality 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
* @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 {ColorThiefResult} returns {r: num, g: num, b: num}
*/
getColor(
getColor (
sourceImage: HTMLImageElement,
quality: number = 10,
): ColorThiefResult {
const palette = this.getPalette(sourceImage, 5, quality);
const palette = this.getPalette( sourceImage, 5, quality );
return palette[0];
}
@@ -69,23 +80,23 @@ class ColorThief {
* @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
* defaults to 10.
* @param {number?} quality 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.
* @returns {ColorThiefResult[]} returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
* @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 {ColorThiefResult[] | null} returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
*/
getPalette(
getPalette (
sourceImage: HTMLImageElement,
colorCount?: number,
quality: number = 10,
): ColorThiefResult[] {
const options = core.validateOptions({
): ColorThiefResult[] | null {
const options = core.validateOptions( {
colorCount,
quality,
});
} );
// Create custom CanvasImage object
const image = new CanvasImage(sourceImage);
const image = new CanvasImage( sourceImage );
const imageData = image.getImageData();
const pixelCount = image.width * image.height;
@@ -97,83 +108,127 @@ class ColorThief {
// Send array to quantize function which clusters values
// using median cut algorithm
const cmap = quantize(pixelArray, options.colorCount);
const palette = cmap ? cmap.palette() : null;
const cmap = quantize( pixelArray, options.colorCount );
const palette: null | ColorThiefResult[] = cmap ? cmap.palette() : null;
return palette;
}
/**
* Description
* @param {any} imageUrl
* @param {any} callback
* @param {any} quality
* @returns {any}
* [ DEPRECATED ] Get the dominant color of an image, which is fetched from a URL.
* @param {string} imageUrl
* @param {( color: ColorThiefResult, url: string ) => void} callback The callback function called when the image has finished processing
* @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, use getColorFromURLPromise instead
*/
getColorFromUrl(imageUrl, callback, quality) {
const sourceImage = document.createElement('img');
getColorFromUrl ( imageUrl: string, callback: ( color: ColorThiefResult, url: string ) => void, quality: number = 10 ): void {
const sourceImage = document.createElement( 'img' );
sourceImage.addEventListener('load', () => {
const palette = this.getPalette(sourceImage, 5, quality);
sourceImage.addEventListener( 'load', () => {
const palette = this.getPalette( sourceImage, 5, quality );
const dominantColor = palette[0];
callback(dominantColor, imageUrl);
});
callback( dominantColor, imageUrl );
} );
sourceImage.src = imageUrl;
}
/**
* Description
* @param {string} imageUrl
* @param {string} callback
* [ DEPRECATED ] Get Image Data as a Base64 string from a URL
* @param {string} imageUrl The URL to the image. Has to be a full URL, if not relative
* @param {( data: string ) => void} callback The callback function called, once download is complete. Will only be called if successful at downloading
* @returns {void}
* @deprecated since Version 3.0, use getImageDataFromURL instead
*/
getImageData(imageUrl: string, callback) {
let xhr = new XMLHttpRequest();
xhr.open('GET', imageUrl, true);
xhr.responseType = 'arraybuffer';
fetch(imageUrl).then((res) => {
if (res.status === 200) {
res.arrayBuffer().then((response) => {
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]);
getImageData ( imageUrl: string, callback: ( data: string ) => void ): void {
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] );
}
let data = binaryString.join('');
let base64 = window.btoa(data);
});
const data = binaryString.join( '' );
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
* @param {any} imageUrl
* @param {any} callback
* @param {any} quality
* @returns {any}
* Get Image Data as a Base64 string from a URL
* @param {string} imageUrl The URL to the image. Has to be a full URL, if not relative
* @returns {Promise<string>} returns a Promise resolving to a string with the base64 string
*/
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;
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);
});
this.getImageDataFromURL( imageUrl ).then( ( imageData ) => {
const sourceImage = document.createElement( 'img' );
sourceImage.addEventListener( 'load', () => {
const palette = thief.getPalette( sourceImage, 5, quality );
callback( palette[ 0 ], sourceImage );
} );
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
View File
@@ -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
View File
@@ -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,
};
+20 -20
View File
@@ -1,24 +1,24 @@
const { resolve } = require('path');
const ColorThief = require(resolve(process.cwd(), "dist/color-thief.js"));
const img = resolve(process.cwd(), 'cypress/test-pages/img/rainbow-vertical.png');
const chai = require("chai");
const expect = chai.expect;
chai.use(require("chai-as-promised"));
import { resolve } from 'path';
import ColorThief from '../dist/color-thief-node.cjs';
const img = resolve( process.cwd(), 'cypress/test-pages/img/rainbow-vertical.png' );
import { expect as _expect, use } from 'chai';
const expect = _expect;
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() {
it('returns valid color', function() {
return expect(ColorThief.getColor(img)).to.eventually.have.lengthOf(3);
});
});
describe( 'getPalette()', function () {
it( 'returns 5 colors when colorCount set to 5', function () {
return expect( ColorThief.getPalette( img, 5 ) ).to.eventually.have.lengthOf( 5 );
} );
describe('getPalette()', function() {
it('returns 5 colors when colorCount set to 5', function() {
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);
});
});
it( 'returns 9 colors when colorCount set to 9', function () {
return expect( ColorThief.getPalette( img, 9 ) ).to.eventually.have.lengthOf( 9 );
} );
} );
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"outDir": "./built",
"allowJs": true,
"target": "ES6",
"skipLibCheck": true,
"moduleResolution": "NodeNext",
"module": "NodeNext"
},
"include": [ "./src/**/*" ],
}