Skip to content

Commit 014bb92

Browse files
committed
refactor dir struct, style improve, and use worker
1 parent c9677fb commit 014bb92

File tree

7 files changed

+243
-117
lines changed

7 files changed

+243
-117
lines changed

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/target
2-
32
Cargo.lock
43

54
encoded.png
6-
recovered.png
5+
recovered.png
6+
7+
pkg
8+
node_modules
9+
package-lock.json

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
wasm:
2+
@echo "Building WASM module..."
3+
wasm-pack build --target web --out-dir ./app/pkg
4+
5+
.PHONY: wasm

app/index.html

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<link rel="stylesheet" href="./style.css">
7+
</head>
8+
9+
<body>
10+
<title>Image encoder</title>
11+
12+
<div id="input-container">
13+
<div id="secret-container">
14+
<label>Select an image file.</label>
15+
<input type="file" id="fileInput" accept="image/*" required>
16+
<br>
17+
<label for="secretInput">Secret:</label>
18+
<input id="secretInput" placeholder="Enter your secret here" autocomplete="off">
19+
</div>
20+
21+
<div id="button-container">
22+
<button id="encodeButton">Encode</button>
23+
<button id="decodeButton">Decode</button>
24+
</div>
25+
</div>
26+
27+
<div id="output-container">
28+
<div id="downloadBtnContainer">
29+
<button id="downloadBtn" style="display: none;">Download Image</button>
30+
</div>
31+
<label id="hint">Enter a secret and select an image file to encode / decode.</label>
32+
<div id="inputImg"></div>
33+
<div id="output"></div>
34+
<div id="error"></div>
35+
</div>
36+
37+
</body>
38+
39+
<script type="module">
40+
if (!window.WebAssembly) {
41+
alert("WebAssembly is not supported in your browser. Please use a modern browser.");
42+
}
43+
44+
const secretInput = document.getElementById('secretInput');
45+
const fileInput = document.getElementById('fileInput');
46+
const inputImgDiv = document.getElementById('inputImg');
47+
const outputDiv = document.getElementById('output');
48+
const hintLabel = document.getElementById('hint');
49+
const encodeButton = document.getElementById('encodeButton');
50+
const decodeButton = document.getElementById('decodeButton');
51+
const downloadBtn = document.getElementById('downloadBtn');
52+
const errorDiv = document.getElementById('error');
53+
const worker = new Worker('./worker.js', { type: 'module' });
54+
55+
async function getInputBlob() {
56+
const file = fileInput.files[0];
57+
const arrayBuffer = await file.arrayBuffer();
58+
return new Uint8Array(arrayBuffer);
59+
}
60+
61+
function checkInput() {
62+
if (secretInput.value.trim() === '') {
63+
showError("Secret cannot be empty");
64+
throw new Error("Secret cannot be empty");
65+
}
66+
if (fileInput.files.length === 0) {
67+
showError("No image file selected");
68+
throw new Error("No image file selected");
69+
}
70+
}
71+
72+
async function showError(msg) {
73+
resetOutput();
74+
errorDiv.textContent = msg;
75+
errorDiv.style.display = 'block';
76+
console.error(msg);
77+
}
78+
79+
function resetOutput() {
80+
hintLabel.textContent = 'Enter a secret and select an image file to encode / decode.';
81+
inputImgDiv.innerHTML = '';
82+
outputDiv.innerHTML = '';
83+
errorDiv.textContent = '';
84+
errorDiv.style.display = 'none';
85+
downloadBtn.style.display = 'none';
86+
}
87+
88+
async function showImage(imBlob) {
89+
resetOutput();
90+
hintLabel.textContent = '';
91+
const blob = new Blob([imBlob], { type: 'image/png' });
92+
const url = URL.createObjectURL(blob);
93+
94+
const imgElem = document.createElement('img');
95+
imgElem.src = url;
96+
imgElem.alt = 'Encoded Image';
97+
outputDiv.appendChild(imgElem);
98+
99+
downloadBtn.style.display = 'block';
100+
downloadBtn.onclick = () => {
101+
const a = document.createElement('a');
102+
a.href = url;
103+
a.download = 'encoded_image.png';
104+
document.body.appendChild(a);
105+
a.click();
106+
document.body.removeChild(a);
107+
};
108+
}
109+
110+
async function encode_image() {
111+
checkInput();
112+
resetOutput();
113+
114+
const inputBlob = await getInputBlob();
115+
116+
hintLabel.textContent = `Encoding...`;
117+
118+
worker.postMessage({type: 'encode', buffer: inputBlob, secret: secretInput.value });
119+
worker.onmessage = async (event) => {
120+
if (event.data.error) {
121+
await showError(`Error encoding: ${event.data.error}`);
122+
console.error(event.data.error);
123+
return;
124+
}
125+
const outputBlob = event.data.buffer;
126+
await showImage(outputBlob);
127+
};
128+
129+
}
130+
131+
async function decode_image() {
132+
checkInput();
133+
resetOutput();
134+
135+
const inputBlob = await getInputBlob();
136+
137+
hintLabel.textContent = `Decoding...`;
138+
139+
let outputBlob;
140+
worker.postMessage({type: 'decode', buffer: inputBlob, secret: secretInput.value });
141+
worker.onmessage = async (event) => {
142+
if (event.data.error) {
143+
await showError(`Error decoding: ${event.data.error}`);
144+
console.error(event.data.error);
145+
return;
146+
}
147+
outputBlob = event.data.buffer;
148+
await showImage(outputBlob);
149+
};
150+
}
151+
152+
encodeButton.addEventListener('click', encode_image);
153+
decodeButton.addEventListener('click', decode_image);
154+
fileInput.addEventListener('change', ()=>{
155+
resetOutput();
156+
hintLabel.textContent += ' (Below is the selected image)';
157+
const file = fileInput.files[0];
158+
if (file) {
159+
const imgElem = document.createElement('img');
160+
imgElem.src = URL.createObjectURL(file);
161+
imgElem.alt = 'Selected Image';
162+
inputImgDiv.innerHTML = '';
163+
inputImgDiv.appendChild(imgElem);
164+
}
165+
});
166+
167+
</script>
168+
</html>

app/style.css

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
body {
3+
font-family: Arial, sans-serif;
4+
padding: 1rem;
5+
margin-top: 1.5rem;
6+
max-width: 800px;
7+
margin-right: auto;
8+
margin-left: auto;
9+
}
10+
11+
div#secret-container {
12+
display: flex;
13+
flex-direction: column;
14+
}
15+
16+
div#input-container {
17+
display: flex;
18+
flex-direction: column;
19+
max-width: 100%;
20+
padding: 0.5rem;
21+
margin-top: 0.5rem;
22+
border-radius: 0.2rem;
23+
24+
gap: 0.5rem;
25+
transition: all 0.3s ease-in-out;
26+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
27+
}
28+
29+
div#output-container {
30+
margin-top: 1rem;
31+
display: flex;
32+
flex-direction: column;
33+
max-width: 100%;
34+
background-color: whitesmoke;
35+
padding: 0.5rem;
36+
border-radius: 0.2rem;
37+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
38+
gap: 0.25rem;
39+
}
40+
41+
img {
42+
max-width: 100%;
43+
height: auto;
44+
}
45+
46+
div#error {
47+
color: red;
48+
font-weight: bold;
49+
}

app/worker.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import init, { encode, decode } from './pkg/chaotic_enc.js';
2+
3+
await init();
4+
self.onmessage = async function(event) {
5+
const { type, buffer, secret } = event.data;
6+
7+
if (type === 'encode') {
8+
const encoded = encode(buffer, secret);
9+
self.postMessage({ type: 'encoded', buffer: encoded });
10+
}
11+
12+
if (type === 'decode') {
13+
const decoded = decode(buffer, secret);
14+
self.postMessage({ type: 'decoded', buffer: decoded });
15+
}
16+
}

index.html

Lines changed: 0 additions & 115 deletions
This file was deleted.

original.png

-237 KB
Binary file not shown.

0 commit comments

Comments
 (0)