diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index f0965e2..0e6a02e 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,8 +4,15 @@
-
-
+
+
+
+
+
+
+
+
+
@@ -218,11 +225,11 @@
+
-
@@ -400,15 +407,7 @@
-
-
-
-
- 1740662154978
-
-
-
- 1740662154978
+
@@ -794,7 +793,15 @@
1743621506634
-
+
+
+ 1743621602590
+
+
+
+ 1743621602590
+
+
@@ -841,7 +848,6 @@
-
@@ -866,7 +872,8 @@
-
+
+
diff --git a/src/pages/tools/image/png/change-colors-in-png/index.tsx b/src/pages/tools/image/generic/change-colors/index.tsx
similarity index 57%
rename from src/pages/tools/image/png/change-colors-in-png/index.tsx
rename to src/pages/tools/image/generic/change-colors/index.tsx
index 2916e1d..1eb5745 100644
--- a/src/pages/tools/image/png/change-colors-in-png/index.tsx
+++ b/src/pages/tools/image/generic/change-colors/index.tsx
@@ -6,10 +6,10 @@ import { GetGroupsType } from '@components/options/ToolOptions';
import ColorSelector from '@components/options/ColorSelector';
import Color from 'color';
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
-import { areColorsSimilar } from 'utils/color';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolImageInput from '@components/input/ToolImageInput';
+import { processImage } from './service';
const initialValues = {
fromColor: 'white',
@@ -19,7 +19,7 @@ const initialValues = {
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
});
-export default function ChangeColorsInPng({ title }: ToolComponentProps) {
+export default function ChangeColorsInImage({ title }: ToolComponentProps) {
const [input, setInput] = useState(null);
const [result, setResult] = useState(null);
@@ -36,54 +36,10 @@ export default function ChangeColorsInPng({ title }: ToolComponentProps) {
} catch (err) {
return;
}
- const processImage = async (
- file: File,
- fromColor: [number, number, number],
- toColor: [number, number, number],
- similarity: number
- ) => {
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
- if (ctx == null) return;
- const img = new Image();
- img.src = URL.createObjectURL(file);
- await img.decode();
-
- canvas.width = img.width;
- canvas.height = img.height;
- ctx.drawImage(img, 0, 0);
-
- const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
- const data: Uint8ClampedArray = imageData.data;
-
- for (let i = 0; i < data.length; i += 4) {
- const currentColor: [number, number, number] = [
- data[i],
- data[i + 1],
- data[i + 2]
- ];
- if (areColorsSimilar(currentColor, fromColor, similarity)) {
- data[i] = toColor[0]; // Red
- data[i + 1] = toColor[1]; // Green
- data[i + 2] = toColor[2]; // Blue
- }
- }
-
- ctx.putImageData(imageData, 0, 0);
-
- canvas.toBlob((blob) => {
- if (blob) {
- const newFile = new File([blob], file.name, {
- type: 'image/png'
- });
- setResult(newFile);
- }
- }, 'image/png');
- };
-
- processImage(input, fromRgb, toRgb, Number(similarity));
+ processImage(input, fromRgb, toRgb, Number(similarity), setResult);
};
+
const getGroups: GetGroupsType = ({
values,
updateField
@@ -127,22 +83,11 @@ export default function ChangeColorsInPng({ title }: ToolComponentProps) {
}
- resultComponent={
-
- }
- toolInfo={{
- title: 'Make Colors Transparent',
- description:
- 'This tool allows you to make specific colors in a PNG image transparent. You can select the color to replace and adjust the similarity threshold to include similar colors.'
- }}
+ resultComponent={}
/>
);
}
diff --git a/src/pages/tools/image/generic/change-colors/meta.ts b/src/pages/tools/image/generic/change-colors/meta.ts
new file mode 100644
index 0000000..287a327
--- /dev/null
+++ b/src/pages/tools/image/generic/change-colors/meta.ts
@@ -0,0 +1,13 @@
+import { defineTool } from '@tools/defineTool';
+import { lazy } from 'react';
+
+export const tool = defineTool('image-generic', {
+ name: 'Change colors in image',
+ path: 'change-colors',
+ icon: 'cil:color-fill',
+ description:
+ "World's simplest online Image color changer. Just import your image (JPG, PNG, SVG) in the editor on the left, select which colors to change, and you'll instantly get a new image with the new colors on the right. Free, quick, and very powerful. Import an image – replace its colors.",
+ shortDescription: 'Quickly swap colors in a image',
+ keywords: ['change', 'colors', 'in', 'png', 'image', 'jpg'],
+ component: lazy(() => import('./index'))
+});
diff --git a/src/pages/tools/image/generic/change-colors/service.ts b/src/pages/tools/image/generic/change-colors/service.ts
new file mode 100644
index 0000000..a35ed1d
--- /dev/null
+++ b/src/pages/tools/image/generic/change-colors/service.ts
@@ -0,0 +1,169 @@
+import { areColorsSimilar } from '@utils/color';
+
+export const processImage = async (
+ file: File,
+ fromColor: [number, number, number],
+ toColor: [number, number, number],
+ similarity: number,
+ setResult: (result: File | null) => void
+): Promise => {
+ if (file.type === 'image/svg+xml') {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ if (!e.target?.result) return;
+
+ let svgContent = e.target.result as string;
+ const toColorHex = rgbToHex(toColor[0], toColor[1], toColor[2]);
+
+ // Replace hex colors with various formats (#fff, #ffffff)
+ const hexRegexShort = new RegExp(`#[0-9a-f]{3}\\b`, 'gi');
+ const hexRegexLong = new RegExp(`#[0-9a-f]{6}\\b`, 'gi');
+
+ svgContent = svgContent.replace(hexRegexShort, (match) => {
+ // Expand short hex to full form for comparison
+ const expanded =
+ '#' + match[1] + match[1] + match[2] + match[2] + match[3] + match[3];
+ const matchRgb = hexToRgb(expanded);
+ if (matchRgb && areColorsSimilar(matchRgb, fromColor, similarity)) {
+ return toColorHex;
+ }
+ return match;
+ });
+
+ svgContent = svgContent.replace(hexRegexLong, (match) => {
+ const matchRgb = hexToRgb(match);
+ if (matchRgb && areColorsSimilar(matchRgb, fromColor, similarity)) {
+ return toColorHex;
+ }
+ return match;
+ });
+
+ // Replace RGB colors
+ const rgbRegex = new RegExp(
+ `rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)`,
+ 'gi'
+ );
+ svgContent = svgContent.replace(rgbRegex, (match, r, g, b) => {
+ const matchRgb: [number, number, number] = [
+ parseInt(r),
+ parseInt(g),
+ parseInt(b)
+ ];
+ if (areColorsSimilar(matchRgb, fromColor, similarity)) {
+ return `rgb(${toColor[0]}, ${toColor[1]}, ${toColor[2]})`;
+ }
+ return match;
+ });
+
+ // Replace RGBA colors (preserving alpha)
+ const rgbaRegex = new RegExp(
+ `rgba\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*([\\d.]+)\\s*\\)`,
+ 'gi'
+ );
+ svgContent = svgContent.replace(rgbaRegex, (match, r, g, b, a) => {
+ const matchRgb: [number, number, number] = [
+ parseInt(r),
+ parseInt(g),
+ parseInt(b)
+ ];
+ if (areColorsSimilar(matchRgb, fromColor, similarity)) {
+ return `rgba(${toColor[0]}, ${toColor[1]}, ${toColor[2]}, ${a})`;
+ }
+ return match;
+ });
+
+ // Replace named SVG colors if they match our target color
+ const namedColors = {
+ red: [255, 0, 0],
+ green: [0, 128, 0],
+ blue: [0, 0, 255],
+ black: [0, 0, 0],
+ white: [255, 255, 255]
+ // Add more named colors as needed
+ };
+
+ Object.entries(namedColors).forEach(([name, rgb]) => {
+ if (
+ areColorsSimilar(
+ rgb as [number, number, number],
+ fromColor,
+ similarity
+ )
+ ) {
+ const colorRegex = new RegExp(`\\b${name}\\b`, 'gi');
+ svgContent = svgContent.replace(colorRegex, toColorHex);
+ }
+ });
+
+ // Create new file with modified content
+ const newFile = new File([svgContent], file.name, {
+ type: 'image/svg+xml'
+ });
+ setResult(newFile);
+ };
+ reader.readAsText(file);
+ return;
+ }
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ if (ctx == null) return;
+ const img = new Image();
+
+ img.src = URL.createObjectURL(file);
+ await img.decode();
+
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage(img, 0, 0);
+
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data: Uint8ClampedArray = imageData.data;
+
+ for (let i = 0; i < data.length; i += 4) {
+ const currentColor: [number, number, number] = [
+ data[i],
+ data[i + 1],
+ data[i + 2]
+ ];
+ if (areColorsSimilar(currentColor, fromColor, similarity)) {
+ data[i] = toColor[0]; // Red
+ data[i + 1] = toColor[1]; // Green
+ data[i + 2] = toColor[2]; // Blue
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+
+ canvas.toBlob((blob) => {
+ if (blob) {
+ const newFile = new File([blob], file.name, {
+ type: file.type
+ });
+ setResult(newFile);
+ }
+ }, file.type);
+};
+
+const rgbToHex = (r: number, g: number, b: number): string => {
+ return (
+ '#' +
+ [r, g, b]
+ .map((x) => {
+ const hex = x.toString(16);
+ return hex.length === 1 ? '0' + hex : hex;
+ })
+ .join('')
+ );
+};
+
+// Helper function to parse hex to RGB
+const hexToRgb = (hex: string): [number, number, number] | null => {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result
+ ? [
+ parseInt(result[1], 16),
+ parseInt(result[2], 16),
+ parseInt(result[3], 16)
+ ]
+ : null;
+};
diff --git a/src/pages/tools/image/generic/index.ts b/src/pages/tools/image/generic/index.ts
index 07c41cc..d40fefa 100644
--- a/src/pages/tools/image/generic/index.ts
+++ b/src/pages/tools/image/generic/index.ts
@@ -1,4 +1,5 @@
import { tool as resizeImage } from './resize/meta';
import { tool as compressImage } from './compress/meta';
+import { tool as changeColors } from './change-colors/meta';
-export const imageGenericTools = [resizeImage, compressImage];
+export const imageGenericTools = [resizeImage, compressImage, changeColors];
diff --git a/src/pages/tools/image/png/change-colors-in-png/change-colors-in-png.e2e.spec.ts b/src/pages/tools/image/png/change-colors-in-png/change-colors-in-png.e2e.spec.ts
deleted file mode 100644
index 2fb228a..0000000
--- a/src/pages/tools/image/png/change-colors-in-png/change-colors-in-png.e2e.spec.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { expect, test } from '@playwright/test';
-import { Buffer } from 'buffer';
-import path from 'path';
-import Jimp from 'jimp';
-import { convertHexToRGBA } from '@utils/color';
-
-test.describe('Change colors in png', () => {
- test.beforeEach(async ({ page }) => {
- await page.goto('/png/change-colors-in-png');
- });
-
- // test('should change pixel color', async ({ page }) => {
- // // Upload image
- // const fileInput = page.locator('input[type="file"]');
- // const imagePath = path.join(__dirname, 'test.png');
- // await fileInput?.setInputFiles(imagePath);
- //
- // await page.getByTestId('from-color-input').fill('#FF0000');
- // const toColor = '#0000FF';
- // await page.getByTestId('to-color-input').fill(toColor);
- //
- // // Click on download
- // const downloadPromise = page.waitForEvent('download');
- // await page.getByText('Save as').click();
- //
- // // Intercept and read downloaded PNG
- // const download = await downloadPromise;
- // const downloadStream = await download.createReadStream();
- //
- // const chunks = [];
- // for await (const chunk of downloadStream) {
- // chunks.push(chunk);
- // }
- // const fileContent = Buffer.concat(chunks);
- //
- // expect(fileContent.length).toBeGreaterThan(0);
- //
- // // Check that the first pixel is transparent
- // const image = await Jimp.read(fileContent);
- // const color = image.getPixelColor(0, 0);
- // expect(color).toBe(convertHexToRGBA(toColor));
- // });
-});
diff --git a/src/pages/tools/image/png/change-colors-in-png/meta.ts b/src/pages/tools/image/png/change-colors-in-png/meta.ts
deleted file mode 100644
index d2e92ef..0000000
--- a/src/pages/tools/image/png/change-colors-in-png/meta.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { defineTool } from '@tools/defineTool';
-import { lazy } from 'react';
-
-export const tool = defineTool('png', {
- name: 'Change colors in png',
- path: 'change-colors-in-png',
- icon: 'cil:color-fill',
- description:
- "World's simplest online Portable Network Graphics (PNG) color changer. Just import your PNG image in the editor on the left, select which colors to change, and you'll instantly get a new PNG with the new colors on the right. Free, quick, and very powerful. Import a PNG – replace its colors.",
- shortDescription: 'Quickly swap colors in a PNG image',
- keywords: ['change', 'colors', 'in', 'png'],
- component: lazy(() => import('./index'))
-});
diff --git a/src/pages/tools/image/png/change-colors-in-png/test.png b/src/pages/tools/image/png/change-colors-in-png/test.png
deleted file mode 100644
index e08bfef..0000000
Binary files a/src/pages/tools/image/png/change-colors-in-png/test.png and /dev/null differ
diff --git a/src/pages/tools/image/png/index.ts b/src/pages/tools/image/png/index.ts
index f248619..e4c4433 100644
--- a/src/pages/tools/image/png/index.ts
+++ b/src/pages/tools/image/png/index.ts
@@ -2,14 +2,12 @@ import { tool as pngCrop } from './crop/meta';
import { tool as pngCompressPng } from './compress-png/meta';
import { tool as convertJgpToPng } from './convert-jgp-to-png/meta';
import { tool as pngCreateTransparent } from './create-transparent/meta';
-import { tool as changeColorsInPng } from './change-colors-in-png/meta';
import { tool as changeOpacity } from './change-opacity/meta';
import { tool as removeBackground } from './remove-background/meta';
export const pngTools = [
pngCompressPng,
pngCreateTransparent,
- changeColorsInPng,
convertJgpToPng,
changeOpacity,
pngCrop,