import { NativeModules } from "react-native"; import { HTMLColorNames } from "./htmlColorNames"; //@ts-ignore const jsiColorModule: { getPixelColor(imageFile: string, x: number, y: number): string; computeColorDistance( r1: number, g1: number, b1: number, r2: number, g2: number, b2: number ): number; getItem(key: string): string; // @ts-ignore } = global; export function isJsiColorLoaded() { return typeof jsiColorModule.getItem === "function"; } if (!isJsiColorLoaded()) { const result = NativeModules.SimpleJsi.install(); if (!result) throw new Error("JSI bindings were not installed for: SimpleJsi Module"); if (!isJsiColorLoaded()) { throw new Error("JSI bindings were not installed for: SimpleJsi Module"); } } export interface AdditionalPixelOptions { imageWidth: number; imageHeight: number; imageViewWidth: number; imageViewHeight: number; } /** * Get the color of a pixel in an image. * @param imageFile The filepath to the image you are trying to get the color * of. Make sure that the image is not preceeded by a file:// scheme, since it * will not be able to find the file then. * @param x X-coordinate of the pixel you want the color of, starting at 0. * Make sure that you are not trying to get the X-coordinate inside of an * image view, but of the actual image. Otherwise, you can pass in the width * and height of image and image view as options. * @param y Y-coordinate of the pixel you want the color of, starting at 0. * Make sure that you are not trying to get the Y-coordinate inside of an * image view, but of the actual image. Otherwise, you can pass in the width * and height of image and image view as options. * @returns string String that represents the color in rgb(R, G, B) format. This * can be used to pass a color to a CSS property. */ export function getPixelColor( imageFile: string, x: number, y: number, options?: AdditionalPixelOptions ): string { if (!options) { return jsiColorModule.getPixelColor(imageFile, x, y); } const { imageWidth, imageHeight, imageViewWidth, imageViewHeight } = options; const xRatio = imageViewWidth / imageWidth; const yRatio = imageViewHeight / imageHeight; return jsiColorModule.getPixelColor(imageFile, x * xRatio, y * yRatio); } /** * Computes the distance between two colors. * @param rgb1 Color in rgb(R, G, B) format. * @param rgb2 Color in rgb(R, G, B) format. * @returns number Distance between two colors, using the CIEDE2000 algorithm. */ export function computeColorDistance(rgb1: string, rgb2: string): number { const [r1, g1, b1] = rgb1.replace(/[^\d,]/g, "").split(","); const [r2, g2, b2] = rgb2.replace(/[^\d,]/g, "").split(","); const r1_ = parseInt(r1); const g1_ = parseInt(g1); const b1_ = parseInt(b1); const r2_ = parseInt(r2); const g2_ = parseInt(g2); const b2_ = parseInt(b2); return jsiColorModule.computeColorDistance(r1_, g1_, b1_, r2_, g2_, b2_); } export interface ClosestColorNameOptions { /** * Whether it should return the closest color or a list of colors sorted by * their distance. * @default true */ onlyClosest?: boolean; /** * The colors to compare against. * @example * ```ts * [ * { * name: "red", * rgb: "rgb(255, 0, 0)", * }, * { * name: "green", * rgb: "rgb(0, 255, 0)", * }, * { * name: "blue", * rgb: "rgb(0, 0, 255)", * }, * ] * ``` * @default HTML color names (https://www.w3schools.com/tags/ref_colornames.asp). */ colors?: Array; } export interface ColorName { /** * The name of the color. * @example "red" */ name: string; /** * The rgb(R, G, B) representation of the color. * @example rgb(255, 0, 0) */ rgb: string; } export { HTMLColorNames }; export interface ColorDistance { /** * The name of the color. * @example "red" */ name: string; /** * The distance between the color and the given color according to the CIEDE * 2000 algorithm. */ distance: number; } /** * Finds the closest colors to the given color in a the set of HTML colors or * a custom color list. * @param rgb The color in rgb(R, G, B) format. * @param options Options for the function. * @returns string The closest color to the given color or an ordered list of * colors according to their distance. */ export function getClosestColorName( rgb: string, options?: ClosestColorNameOptions ): Array | string { const { onlyClosest = true, colors = HTMLColorNames } = options || {}; const colorDistances = colors.map((color) => { return { name: color.name, distance: computeColorDistance(rgb, color.rgb), }; }); colorDistances.sort((a, b) => a.distance - b.distance); if (onlyClosest) { return colorDistances[0].name; } else { return colorDistances; } }