Skip to content

Commit 3831f79

Browse files
committed
feat: use lokesh.dhakar/quantize for consistent result
1 parent c931a9a commit 3831f79

File tree

4 files changed

+43
-14
lines changed

4 files changed

+43
-14
lines changed

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "use-material-you",
3-
"version": "1.0.6",
3+
"version": "1.0.7",
44
"description": "React hook to create dynamic schemes and variants based on M3/material-color-utilities",
55
"author": "Maicon Carraro (https://github.com/maiconcarraro)",
66
"homepage": "https://github.com/maiconcarraro/use-material-you",
@@ -35,6 +35,7 @@
3535
"format:styles": "stylelint ./**/*.{css,scss} --fix"
3636
},
3737
"dependencies": {
38+
"@lokesh.dhakar/quantize": "^1.4.0",
3839
"@material/material-color-utilities": "^0.3.0"
3940
},
4041
"peerDependencies": {

src/quantize.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module "@lokesh.dhakar/quantize" {
2+
function quantize(
3+
pixels: number[][],
4+
maxColors: number,
5+
): { palette: () => [[number, number, number]] } | null;
6+
export = quantize;
7+
}

src/utils.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import {
2-
argbFromRgb,
3-
QuantizerCelebi,
4-
Score,
5-
} from "@material/material-color-utilities";
1+
import { argbFromRgb } from "@material/material-color-utilities";
2+
import quantize from "@lokesh.dhakar/quantize";
63

74
export function findDominantColorsFromPixelData(
85
pixelData: Uint8ClampedArray | Uint8Array,
96
amount: number = 3,
107
): number[] {
11-
const pixels: number[] = [];
8+
const pixels: number[][] = [];
129
for (let i = 0; i < pixelData.length; i += 4) {
1310
const r = pixelData[i]!;
1411
const g = pixelData[i + 1]!;
@@ -17,13 +14,26 @@ export function findDominantColorsFromPixelData(
1714
if (a < 255) {
1815
continue;
1916
}
20-
const argb = argbFromRgb(r, g, b);
21-
pixels.push(argb);
17+
pixels.push([r, g, b]);
2218
}
2319

24-
const result = QuantizerCelebi.quantize(pixels, 128);
25-
const ranked = Score.score(result);
26-
return ranked.slice(0, amount);
20+
// Replace Material quantize because of inconsistency: https://github.com/material-foundation/material-color-utilities/issues/132
21+
// const result = QuantizerCelebi.quantize(pixels, 128);
22+
// const ranked = Score.score(result);
23+
24+
try {
25+
const cmap = quantize(pixels, amount);
26+
27+
if (!cmap) {
28+
return [];
29+
}
30+
31+
const palette = cmap.palette();
32+
return palette.map(([r, g, b]) => argbFromRgb(r, g, b)).slice(0, amount);
33+
} catch (err) {
34+
console.error(err);
35+
return [];
36+
}
2737
}
2838

2939
// Original function: https://github.com/material-foundation/material-color-utilities/blob/be615fc90286787bbe0c04ef58a6987e0e8fdc29/typescript/utils/image_utils.ts#L29
@@ -33,10 +43,14 @@ export async function sourceColorFromImage(
3343
amount: number = 3,
3444
grid?: Array<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9>,
3545
) {
46+
const isPartialImage = grid && grid.length > 0 && grid.length < 9;
47+
3648
// Convert Image data to Pixel Array
3749
const imageBytes = await new Promise<Uint8ClampedArray>((resolve, reject) => {
3850
const canvas = document.createElement("canvas");
39-
const context = canvas.getContext("2d");
51+
const context = canvas.getContext("2d", {
52+
willReadFrequently: isPartialImage,
53+
});
4054
if (!context) {
4155
reject(new Error("Could not get canvas context"));
4256
return;
@@ -46,7 +60,7 @@ export async function sourceColorFromImage(
4660
canvas.height = image.height;
4761
context.drawImage(image, 0, 0);
4862

49-
if (grid) {
63+
if (isPartialImage) {
5064
const cellWidth = image.width / 3;
5165
const cellHeight = image.height / 3;
5266

0 commit comments

Comments
 (0)