mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-19 14:09:31 +02:00
feat: change colors in png init
This commit is contained in:
33
.idea/workspace.xml
generated
33
.idea/workspace.xml
generated
@@ -4,9 +4,18 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: readme">
|
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: create-tool.mjs">
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/assets/grey-pattern.png" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/assets/image.png" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/components/InputHeader.tsx" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/components/input/InputFooter.tsx" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/components/input/ToolFileInput.tsx" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/components/result/ToolFileResult.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/scripts/create-tool.mjs" beforeDir="false" afterPath="$PROJECT_DIR$/scripts/create-tool.mjs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/components/input/ToolTextInput.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/input/ToolTextInput.tsx" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/components/result/ToolTextResult.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/result/ToolTextResult.tsx" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/index.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/meta.ts" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/meta.ts" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
@@ -48,7 +57,7 @@
|
|||||||
"git-widget-placeholder": "main",
|
"git-widget-placeholder": "main",
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
"kotlin-language-version-configured": "true",
|
"kotlin-language-version-configured": "true",
|
||||||
"last_opened_file_path": "C:/Users/HP/IdeaProjects/omni-tools/src/pages/string",
|
"last_opened_file_path": "C:/Users/HP/IdeaProjects/omni-tools/src/assets",
|
||||||
"node.js.detected.package.eslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.tslint": "true",
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
@@ -78,11 +87,11 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\assets" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string\split" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string\split" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\images" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\images" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\public" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\public" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\assets" />
|
|
||||||
</key>
|
</key>
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\components\options" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\components\options" />
|
||||||
@@ -160,7 +169,7 @@
|
|||||||
<workItem from="1719092003308" duration="14856000" />
|
<workItem from="1719092003308" duration="14856000" />
|
||||||
<workItem from="1719164664347" duration="2033000" />
|
<workItem from="1719164664347" duration="2033000" />
|
||||||
<workItem from="1719166718305" duration="1783000" />
|
<workItem from="1719166718305" duration="1783000" />
|
||||||
<workItem from="1719168519203" duration="8034000" />
|
<workItem from="1719168519203" duration="13734000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="feat: use vite and ts">
|
<task id="LOCAL-00001" summary="feat: use vite and ts">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -514,7 +523,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1719187190823</updated>
|
<updated>1719187190823</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="45" />
|
<task id="LOCAL-00045" summary="fix: create-tool.mjs">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1719188162583</created>
|
||||||
|
<option name="number" value="00045" />
|
||||||
|
<option name="presentableId" value="LOCAL-00045" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1719188162583</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="46" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -558,9 +575,9 @@
|
|||||||
<MESSAGE value="fix: build" />
|
<MESSAGE value="fix: build" />
|
||||||
<MESSAGE value="chore: CODEOWNERS" />
|
<MESSAGE value="chore: CODEOWNERS" />
|
||||||
<MESSAGE value="ci: fix" />
|
<MESSAGE value="ci: fix" />
|
||||||
<MESSAGE value="fix: create-tool.mjs" />
|
|
||||||
<MESSAGE value="fix: readme" />
|
<MESSAGE value="fix: readme" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="fix: readme" />
|
<MESSAGE value="fix: create-tool.mjs" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="fix: create-tool.mjs" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XSLT-Support.FileAssociations.UIState">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<expand />
|
<expand />
|
||||||
|
BIN
src/assets/grey-pattern.png
Normal file
BIN
src/assets/grey-pattern.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
src/assets/image.png
Normal file
BIN
src/assets/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
10
src/components/InputHeader.tsx
Normal file
10
src/components/InputHeader.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function InputHeader({ title }: { title: string }) {
|
||||||
|
return (
|
||||||
|
<Typography mb={1} fontSize={30} color={'primary'}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
@@ -29,7 +29,7 @@ export default function ToolHeader({
|
|||||||
description
|
description
|
||||||
}: ToolHeaderProps) {
|
}: ToolHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<Stack direction={'row'} alignItems={'center'} spacing={2} mt={4}>
|
<Stack direction={'row'} alignItems={'center'} spacing={2} my={4}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography mb={2} fontSize={30} color={'primary'}>
|
<Typography mb={2} fontSize={30} color={'primary'}>
|
||||||
{title}
|
{title}
|
||||||
|
24
src/components/input/InputFooter.tsx
Normal file
24
src/components/input/InputFooter.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { Stack } from '@mui/material';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import PublishIcon from '@mui/icons-material/Publish';
|
||||||
|
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function InputFooter({
|
||||||
|
handleImport,
|
||||||
|
handleCopy
|
||||||
|
}: {
|
||||||
|
handleImport: () => void;
|
||||||
|
handleCopy: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Stack mt={1} direction={'row'} spacing={2}>
|
||||||
|
<Button onClick={handleImport} startIcon={<PublishIcon />}>
|
||||||
|
Import from file
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleCopy} startIcon={<ContentPasteIcon />}>
|
||||||
|
Copy to clipboard
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
114
src/components/input/ToolFileInput.tsx
Normal file
114
src/components/input/ToolFileInput.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { Box, styled, TextField, useTheme } from '@mui/material';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||||
|
import InputHeader from '../InputHeader';
|
||||||
|
import InputFooter from './InputFooter';
|
||||||
|
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
||||||
|
import greyPattern from '@assets/grey-pattern.png';
|
||||||
|
|
||||||
|
interface ToolFileInputProps {
|
||||||
|
value: File | null;
|
||||||
|
onChange: (file: File) => void;
|
||||||
|
accept: string[];
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ToolFileInput({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
accept,
|
||||||
|
title = 'File'
|
||||||
|
}: ToolFileInputProps) {
|
||||||
|
const [preview, setPreview] = useState<string | null>(null);
|
||||||
|
const theme = useTheme();
|
||||||
|
const { showSnackBar } = useContext(CustomSnackBarContext);
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(value?.name ?? '')
|
||||||
|
.then(() => showSnackBar('Text copied', 'success'))
|
||||||
|
.catch((err) => {
|
||||||
|
showSnackBar('Failed to copy: ' + err, 'error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (value) {
|
||||||
|
const objectUrl = URL.createObjectURL(value);
|
||||||
|
setPreview(objectUrl);
|
||||||
|
|
||||||
|
// Clean up memory when the component is unmounted or the file changes
|
||||||
|
return () => URL.revokeObjectURL(objectUrl);
|
||||||
|
} else {
|
||||||
|
setPreview(null);
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = event.target.files?.[0];
|
||||||
|
if (file) onChange(file);
|
||||||
|
};
|
||||||
|
const handleImportClick = () => {
|
||||||
|
fileInputRef.current?.click();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<InputHeader title={title} />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
height: 250,
|
||||||
|
border: preview ? 0 : 1,
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: '5'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{preview ? (
|
||||||
|
<Box
|
||||||
|
width={'100%'}
|
||||||
|
height={'100%'}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundImage: `url(${greyPattern})`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={preview}
|
||||||
|
alt="Preview"
|
||||||
|
style={{ maxWidth: '100%', maxHeight: 250 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
onClick={handleImportClick}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: 5,
|
||||||
|
height: '100%',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography color={theme.palette.grey['600']}>
|
||||||
|
Click here to select an image from your device, press Ctrl+V to
|
||||||
|
use an image from your clipboard, drag and drop a file from
|
||||||
|
desktop
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<InputFooter handleCopy={handleCopy} handleImport={handleImportClick} />
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
type="file"
|
||||||
|
accept={accept.join(',')}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@@ -5,6 +5,8 @@ import PublishIcon from '@mui/icons-material/Publish';
|
|||||||
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
||||||
import React, { useContext, useRef } from 'react';
|
import React, { useContext, useRef } from 'react';
|
||||||
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
||||||
|
import InputHeader from '../InputHeader';
|
||||||
|
import InputFooter from './InputFooter';
|
||||||
|
|
||||||
export default function ToolTextInput({
|
export default function ToolTextInput({
|
||||||
value,
|
value,
|
||||||
@@ -45,9 +47,7 @@ export default function ToolTextInput({
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Box id="tool">
|
<Box id="tool">
|
||||||
<Typography fontSize={30} color={'primary'}>
|
<InputHeader title={title} />
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
<TextField
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(event) => onChange(event.target.value)}
|
onChange={(event) => onChange(event.target.value)}
|
||||||
@@ -55,14 +55,7 @@ export default function ToolTextInput({
|
|||||||
multiline
|
multiline
|
||||||
rows={10}
|
rows={10}
|
||||||
/>
|
/>
|
||||||
<Stack mt={1} direction={'row'} spacing={2}>
|
<InputFooter handleCopy={handleCopy} handleImport={handleImportClick} />
|
||||||
<Button onClick={handleImportClick} startIcon={<PublishIcon />}>
|
|
||||||
Import from file
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleCopy} startIcon={<ContentPasteIcon />}>
|
|
||||||
Copy to clipboard
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept="*"
|
accept="*"
|
||||||
|
59
src/components/result/ToolFileResult.tsx
Normal file
59
src/components/result/ToolFileResult.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Box } from '@mui/material';
|
||||||
|
import React from 'react';
|
||||||
|
import InputHeader from '../InputHeader';
|
||||||
|
import greyPattern from '@assets/grey-pattern.png';
|
||||||
|
|
||||||
|
export default function ToolFileResult({
|
||||||
|
title = 'Result',
|
||||||
|
value
|
||||||
|
}: {
|
||||||
|
title?: string;
|
||||||
|
value: File | null;
|
||||||
|
}) {
|
||||||
|
const [preview, setPreview] = React.useState<string | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (value) {
|
||||||
|
const objectUrl = URL.createObjectURL(value);
|
||||||
|
setPreview(objectUrl);
|
||||||
|
|
||||||
|
return () => URL.revokeObjectURL(objectUrl);
|
||||||
|
} else {
|
||||||
|
setPreview(null);
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<InputHeader title={title} />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
height: 250,
|
||||||
|
border: preview ? 0 : 1,
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: '5'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{preview && (
|
||||||
|
<Box
|
||||||
|
width={'100%'}
|
||||||
|
height={'100%'}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundImage: `url(${greyPattern})`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={preview}
|
||||||
|
alt="Result"
|
||||||
|
style={{ maxWidth: '100%', maxHeight: 250 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import DownloadIcon from '@mui/icons-material/Download';
|
|||||||
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
||||||
|
import InputHeader from '../InputHeader';
|
||||||
|
|
||||||
export default function ToolTextResult({
|
export default function ToolTextResult({
|
||||||
title = 'Result',
|
title = 'Result',
|
||||||
@@ -37,9 +38,7 @@ export default function ToolTextResult({
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography fontSize={30} color={'primary'}>
|
<InputHeader title={title} />
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
<TextField value={value} fullWidth multiline rows={10} />
|
<TextField value={value} fullWidth multiline rows={10} />
|
||||||
<Stack mt={1} direction={'row'} spacing={2}>
|
<Stack mt={1} direction={'row'} spacing={2}>
|
||||||
<Button onClick={handleDownload} startIcon={<DownloadIcon />}>
|
<Button onClick={handleDownload} startIcon={<DownloadIcon />}>
|
||||||
|
@@ -1,11 +1,76 @@
|
|||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import ToolTextInput from '../../../../components/input/ToolTextInput';
|
||||||
|
import ToolTextResult from '../../../../components/result/ToolTextResult';
|
||||||
|
import ToolFileInput from '../../../../components/input/ToolFileInput';
|
||||||
|
import ToolFileResult from '../../../../components/result/ToolFileResult';
|
||||||
|
|
||||||
const initialValues = {};
|
const initialValues = {
|
||||||
|
rgba: [0, 0, 0, 0]
|
||||||
|
};
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
// splitSeparator: Yup.string().required('The separator is required')
|
// splitSeparator: Yup.string().required('The separator is required')
|
||||||
});
|
});
|
||||||
export default function ChangeColorsInPng() {
|
export default function ChangeColorsInPng() {
|
||||||
return <Box>Lorem ipsum</Box>;
|
const [input, setInput] = useState<File | null>(null);
|
||||||
|
const [result, setResult] = useState<File | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (input) {
|
||||||
|
const processImage = async (file: File) => {
|
||||||
|
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) {
|
||||||
|
// Check for white pixel
|
||||||
|
if (data[i] === 255 && data[i + 1] === 255 && data[i + 2] === 255) {
|
||||||
|
data[i] = 255; // Red
|
||||||
|
data[i + 1] = 0; // Green
|
||||||
|
data[i + 2] = 0; // 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);
|
||||||
|
}
|
||||||
|
}, [input]);
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<ToolFileInput
|
||||||
|
value={input}
|
||||||
|
onChange={setInput}
|
||||||
|
accept={['image/png']}
|
||||||
|
title={'Input PNG'}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<ToolFileResult title={'Output PNG with new colors'} value={result} />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
import { defineTool } from '@tools/defineTool';
|
import { defineTool } from '@tools/defineTool';
|
||||||
import { lazy } from 'react';
|
import { lazy } from 'react';
|
||||||
import image from '@assets/text.png';
|
import image from '@assets/image.png';
|
||||||
|
|
||||||
export const tool = defineTool('png', {
|
export const tool = defineTool('png', {
|
||||||
name: 'Change colors in png',
|
name: 'Change colors in png',
|
||||||
|
Reference in New Issue
Block a user