mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-15 17:44:02 +01:00
feat: create transparent png
This commit is contained in:
38
.idea/workspace.xml
generated
38
.idea/workspace.xml
generated
@@ -4,15 +4,15 @@
|
|||||||
<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="refactor: tool input and result">
|
||||||
<change afterPath="$PROJECT_DIR$/src/components/ToolInputAndResult.tsx" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/pages/image/png/create-transparent/index.tsx" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/pages/image/png/create-transparent/meta.ts" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/pages/image/png/create-transparent/service.ts" 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$/src/components/options/ToolOptionGroups.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" 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/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/home/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/index.tsx" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/pages/image/png/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/image/png/index.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/string/join/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/join/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/string/split/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/split/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/string/to-morse/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/to-morse/index.tsx" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -187,15 +187,7 @@
|
|||||||
<workItem from="1719168519203" duration="17675000" />
|
<workItem from="1719168519203" duration="17675000" />
|
||||||
<workItem from="1719197816332" duration="1453000" />
|
<workItem from="1719197816332" duration="1453000" />
|
||||||
<workItem from="1719273044735" duration="9847000" />
|
<workItem from="1719273044735" duration="9847000" />
|
||||||
<workItem from="1719294110005" duration="1967000" />
|
<workItem from="1719294110005" duration="3709000" />
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00007" summary="fix: netlify">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1718833519062</created>
|
|
||||||
<option name="number" value="00007" />
|
|
||||||
<option name="presentableId" value="LOCAL-00007" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1718833519062</updated>
|
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00008" summary="fix: index title">
|
<task id="LOCAL-00008" summary="fix: index title">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -581,7 +573,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1719283122691</updated>
|
<updated>1719283122691</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="56" />
|
<task id="LOCAL-00056" summary="refactor: tool input and result">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1719296145698</created>
|
||||||
|
<option name="number" value="00056" />
|
||||||
|
<option name="presentableId" value="LOCAL-00056" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1719296145699</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="57" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -602,7 +602,6 @@
|
|||||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||||
<option name="CHECK_NEW_TODO" value="false" />
|
<option name="CHECK_NEW_TODO" value="false" />
|
||||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||||
<MESSAGE value="fix: deploy message" />
|
|
||||||
<MESSAGE value="chore: tools by category" />
|
<MESSAGE value="chore: tools by category" />
|
||||||
<MESSAGE value="feat: copy and import file" />
|
<MESSAGE value="feat: copy and import file" />
|
||||||
<MESSAGE value="refactor: tool options components" />
|
<MESSAGE value="refactor: tool options components" />
|
||||||
@@ -627,7 +626,8 @@
|
|||||||
<MESSAGE value="chore: printRunningSum" />
|
<MESSAGE value="chore: printRunningSum" />
|
||||||
<MESSAGE value="chore: sum tests" />
|
<MESSAGE value="chore: sum tests" />
|
||||||
<MESSAGE value="fix: readme" />
|
<MESSAGE value="fix: readme" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="fix: readme" />
|
<MESSAGE value="refactor: tool input and result" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="refactor: tool input and result" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XSLT-Support.FileAssociations.UIState">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<expand />
|
<expand />
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import Button from '@mui/material/Button';
|
|||||||
const exampleTools: { label: string; url: string }[] = [
|
const exampleTools: { label: string; url: string }[] = [
|
||||||
{
|
{
|
||||||
label: 'Create a transparent image',
|
label: 'Create a transparent image',
|
||||||
url: ''
|
url: '/png/create-transparent'
|
||||||
},
|
},
|
||||||
{ label: 'Convert text to morse code', url: '/string/to-morse' },
|
{ label: 'Convert text to morse code', url: '/string/to-morse' },
|
||||||
{ label: 'Change GIF speed', url: '' },
|
{ label: 'Change GIF speed', url: '' },
|
||||||
|
|||||||
156
src/pages/image/png/create-transparent/index.tsx
Normal file
156
src/pages/image/png/create-transparent/index.tsx
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import { Box } from '@mui/material';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import ToolFileInput from '../../../../components/input/ToolFileInput';
|
||||||
|
import ToolFileResult from '../../../../components/result/ToolFileResult';
|
||||||
|
import ToolOptions from '../../../../components/options/ToolOptions';
|
||||||
|
import { Formik, useFormikContext } from 'formik';
|
||||||
|
import ColorSelector from '../../../../components/options/ColorSelector';
|
||||||
|
import Color from 'color';
|
||||||
|
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
|
||||||
|
import ToolInputAndResult from '../../../../components/ToolInputAndResult';
|
||||||
|
import ToolOptionGroups from '../../../../components/options/ToolOptionGroups';
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
fromColor: 'white',
|
||||||
|
similarity: '10'
|
||||||
|
};
|
||||||
|
const validationSchema = Yup.object({
|
||||||
|
// splitSeparator: Yup.string().required('The separator is required')
|
||||||
|
});
|
||||||
|
export default function ChangeColorsInPng() {
|
||||||
|
const [input, setInput] = useState<File | null>(null);
|
||||||
|
const [result, setResult] = useState<File | null>(null);
|
||||||
|
|
||||||
|
const FormikListenerComponent = ({ input }: { input: File }) => {
|
||||||
|
const { values } = useFormikContext<typeof initialValues>();
|
||||||
|
const { fromColor, similarity } = values;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let fromRgb: [number, number, number];
|
||||||
|
try {
|
||||||
|
//@ts-ignore
|
||||||
|
fromRgb = Color(fromColor).rgb().array();
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const processImage = async (
|
||||||
|
file: File,
|
||||||
|
fromColor: [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;
|
||||||
|
|
||||||
|
const colorDistance = (
|
||||||
|
c1: [number, number, number],
|
||||||
|
c2: [number, number, number]
|
||||||
|
) => {
|
||||||
|
return Math.sqrt(
|
||||||
|
Math.pow(c1[0] - c2[0], 2) +
|
||||||
|
Math.pow(c1[1] - c2[1], 2) +
|
||||||
|
Math.pow(c1[2] - c2[2], 2)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const maxColorDistance = Math.sqrt(
|
||||||
|
Math.pow(255, 2) + Math.pow(255, 2) + Math.pow(255, 2)
|
||||||
|
);
|
||||||
|
const similarityThreshold = (similarity / 100) * maxColorDistance;
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const currentColor: [number, number, number] = [
|
||||||
|
data[i],
|
||||||
|
data[i + 1],
|
||||||
|
data[i + 2]
|
||||||
|
];
|
||||||
|
if (colorDistance(currentColor, fromColor) <= similarityThreshold) {
|
||||||
|
data[i + 3] = 0; // Set alpha to 0 (transparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, Number(similarity));
|
||||||
|
}, [input, fromColor]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<ToolInputAndResult
|
||||||
|
input={
|
||||||
|
<ToolFileInput
|
||||||
|
value={input}
|
||||||
|
onChange={setInput}
|
||||||
|
accept={['image/png']}
|
||||||
|
title={'Input PNG'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
result={
|
||||||
|
<ToolFileResult
|
||||||
|
title={'Transparent PNG'}
|
||||||
|
value={result}
|
||||||
|
extension={'png'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ToolOptions>
|
||||||
|
<Formik
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={() => {}}
|
||||||
|
>
|
||||||
|
{({ setFieldValue, values }) => (
|
||||||
|
<Box>
|
||||||
|
{input && <FormikListenerComponent input={input} />}
|
||||||
|
<ToolOptionGroups
|
||||||
|
groups={[
|
||||||
|
{
|
||||||
|
title: 'From color and similarity',
|
||||||
|
component: (
|
||||||
|
<Box>
|
||||||
|
<ColorSelector
|
||||||
|
value={values.fromColor}
|
||||||
|
onChange={(val) => setFieldValue('fromColor', val)}
|
||||||
|
description={'Replace this color (from color)'}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.similarity}
|
||||||
|
onChange={(val) => setFieldValue('similarity', val)}
|
||||||
|
description={
|
||||||
|
'Match this % of similar colors of the from color. For example, 10% white will match white and a little bit of gray.'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</ToolOptions>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
13
src/pages/image/png/create-transparent/meta.ts
Normal file
13
src/pages/image/png/create-transparent/meta.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineTool } from '@tools/defineTool';
|
||||||
|
import { lazy } from 'react';
|
||||||
|
// import image from '@assets/text.png';
|
||||||
|
|
||||||
|
export const tool = defineTool('png', {
|
||||||
|
name: 'Create transparent PNG',
|
||||||
|
path: 'create-transparent',
|
||||||
|
// image,
|
||||||
|
description:
|
||||||
|
"World's simplest online Portable Network Graphics transparency maker. Just import your PNG image in the editor on the left and you will instantly get a transparent PNG on the right. Free, quick, and very powerful. Import a PNG – get a transparent PNG.",
|
||||||
|
keywords: ['create', 'transparent'],
|
||||||
|
component: lazy(() => import('./index'))
|
||||||
|
});
|
||||||
0
src/pages/image/png/create-transparent/service.ts
Normal file
0
src/pages/image/png/create-transparent/service.ts
Normal file
@@ -1,3 +1,4 @@
|
|||||||
|
import { tool as pngCreateTransparent } from './create-transparent/meta';
|
||||||
import { tool as changeColorsInPng } from './change-colors-in-png/meta';
|
import { tool as changeColorsInPng } from './change-colors-in-png/meta';
|
||||||
|
|
||||||
export const pngTools = [changeColorsInPng];
|
export const pngTools = [changeColorsInPng, pngCreateTransparent];
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Box, Stack } from '@mui/material';
|
import { Box, Stack } from '@mui/material';
|
||||||
import Grid from '@mui/material/Grid';
|
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import ToolTextInput from '../../../components/input/ToolTextInput';
|
import ToolTextInput from '../../../components/input/ToolTextInput';
|
||||||
import ToolTextResult from '../../../components/result/ToolTextResult';
|
import ToolTextResult from '../../../components/result/ToolTextResult';
|
||||||
|
|||||||
Reference in New Issue
Block a user