Initial upload
This commit is contained in:
commit
8d953e1252
13 changed files with 7521 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
/node_modules
|
||||
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "image-effects"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.78"
|
||||
base64 = "0.13.0"
|
||||
image = "0.23.14"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.55"
|
||||
features = ["console"]
|
||||
8
README.md
Normal file
8
README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Image Effects
|
||||
|
||||
A WebAssembly program developed by learning through [WebAssembly: A Practical Guide](https://academy.zerotomastery.io/p/learn-webassembly) course from [ZeroToMastery](https://academy.zerotomastery.io/).
|
||||
|
||||
Developed with:
|
||||
- [Rust](https://www.rust-lang.org/)
|
||||
- [Webpack](https://webpack.js.org/)
|
||||
- [WebAssembly](https://webassembly.org/).
|
||||
1
dist/235.index.js
vendored
Normal file
1
dist/235.index.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
"use strict";(self.webpackChunkimage_effects=self.webpackChunkimage_effects||[]).push([[235],{235:(e,n,t)=>{t.a(e,(async e=>{t.r(n),t.d(n,{__wbg_log_3445347661d4505e:()=>r.JV,__wbindgen_object_drop_ref:()=>r.ug,__wbindgen_string_new:()=>r.h4,__wbindgen_throw:()=>r.Or,grayscale:()=>r.se});var r=t(838),o=e([r]);r=(o.then?await o:o)[0]}))},838:(e,n,t)=>{t.a(e,(async r=>{t.d(n,{se:()=>y,h4:()=>m,ug:()=>p,JV:()=>v,Or:()=>k});var o=t(530);e=t.hmd(e);var u=r([o]);o=(u.then?await u:u)[0];let c=new("undefined"==typeof TextDecoder?(0,e.require)("util").TextDecoder:TextDecoder)("utf-8",{ignoreBOM:!0,fatal:!0});c.decode();let i=null;function _(){return null!==i&&i.buffer===o.memory.buffer||(i=new Uint8Array(o.memory.buffer)),i}function f(e,n){return c.decode(_().subarray(e,e+n))}const d=new Array(32).fill(void 0);d.push(void 0,null,!0,!1);let l=d.length;function a(e){return d[e]}let g=0,s=new("undefined"==typeof TextEncoder?(0,e.require)("util").TextEncoder:TextEncoder)("utf-8");const b="function"==typeof s.encodeInto?function(e,n){return s.encodeInto(e,n)}:function(e,n){const t=s.encode(e);return n.set(t),{read:e.length,written:t.length}};let w=null;function h(){return null!==w&&w.buffer===o.memory.buffer||(w=new Int32Array(o.memory.buffer)),w}function y(e){try{const c=o.__wbindgen_add_to_stack_pointer(-16);var n=function(e,n,t){if(void 0===t){const t=s.encode(e),r=n(t.length);return _().subarray(r,r+t.length).set(t),g=t.length,r}let r=e.length,o=n(r);const u=_();let c=0;for(;c<r;c++){const n=e.charCodeAt(c);if(n>127)break;u[o+c]=n}if(c!==r){0!==c&&(e=e.slice(c)),o=t(o,r,r=c+3*e.length);const n=_().subarray(o+c,o+r);c+=b(e,n).written}return g=c,o}(e,o.__wbindgen_malloc,o.__wbindgen_realloc),t=g;o.grayscale(c,n,t);var r=h()[c/4+0],u=h()[c/4+1];return f(r,u)}finally{o.__wbindgen_add_to_stack_pointer(16),o.__wbindgen_free(r,u)}}function m(e,n){return function(e){l===d.length&&d.push(d.length+1);const n=l;return l=d[n],d[n]=e,n}(f(e,n))}function p(e){!function(e){const n=a(e);(function(e){e<36||(d[e]=l,l=e)})(e)}(e)}function v(e){console.log(a(e))}function k(e,n){throw new Error(f(e,n))}}))},530:(e,n,t)=>{var r=([r])=>t.v(n,e.id,"99e2b8c4b08132ad96c8",{"./index_bg.js":{__wbindgen_string_new:r.h4,__wbindgen_object_drop_ref:r.ug,__wbg_log_3445347661d4505e:r.JV,__wbindgen_throw:r.Or}});t.a(e,(e=>{var n=e([t(838)]);return n.then?n.then(r):r(n)}),1)}}]);
|
||||
BIN
dist/99e2b8c4b08132ad96c8.module.wasm
vendored
Normal file
BIN
dist/99e2b8c4b08132ad96c8.module.wasm
vendored
Normal file
Binary file not shown.
26
dist/index.html
vendored
Normal file
26
dist/index.html
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Image Effects</title><link rel="stylesheet" href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"/><link href="https://fonts.googleapis.com"/><link href="https://fonts.gstatic.com" crossorigin/><link href="https://fonts.googleapis.com/css?family=Pacifico" rel="stylesheet"/><style>h1 {
|
||||
font-family: "Pacifico", cursive;
|
||||
}
|
||||
.bg {
|
||||
animation: slide 3s ease-in-out infinite alternate;
|
||||
background-image: linear-gradient(
|
||||
-60deg,
|
||||
#acffad 50%,
|
||||
#50cb95 50%
|
||||
);
|
||||
}
|
||||
.bg:nth-child(2) {
|
||||
animation-direction: alternate-reverse;
|
||||
animation-duration: 4s;
|
||||
}
|
||||
.bg:nth-child(3) {
|
||||
animation-duration: 5s;
|
||||
}
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateX(-25%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(25%);
|
||||
}
|
||||
}</style><script defer="defer" src="index.js"></script></head><body><div class="bg fixed inset-y-0 -inset-x-2/4 opacity-50 z-0"></div><div class="bg fixed inset-y-0 -inset-x-2/4 opacity-50 z-0"></div><div class="bg fixed inset-y-0 -inset-x-2/4 opacity-50 z-0"></div><div class="flex items-center justify-center h-screen relative z-100"><div class="bg-white bg-opacity-95 border shadow-lg p-10 text-center"><h1 class="text-5xl mb-8">Image Effects</h1><p class="mb-4">Need to do some basic image manipulation? Just upload your image below. We'll take care of the rest.</p><label class="bg-pink-600 text-white w-full p-6 block"><input type="file" id="upload" accept=".png" class="hidden"/> Upload PNG Image</label> <img id="new-img" class="w-auto mx-auto"/></div></div></body></html>
|
||||
1
dist/index.js
vendored
Normal file
1
dist/index.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
(()=>{var e,t,r,n,o,a,i,s={},c={};function l(e){var t=c[e];if(void 0!==t)return t.exports;var r=c[e]={id:e,loaded:!1,exports:{}};return s[e](r,r.exports,l),r.loaded=!0,r.exports}l.m=s,e="function"==typeof Symbol?Symbol("webpack then"):"__webpack_then__",t="function"==typeof Symbol?Symbol("webpack exports"):"__webpack_exports__",r=e=>{e&&(e.forEach((e=>e.r--)),e.forEach((e=>e.r--?e.r++:e())))},n=e=>!--e.r&&e(),o=(e,t)=>e?e.push(t):n(t),l.a=(a,i,s)=>{var c,l,u,p=s&&[],d=a.exports,f=!0,m=!1,b=(t,r,n)=>{m||(m=!0,r.r+=t.length,t.map(((t,o)=>t[e](r,n))),m=!1)},h=new Promise(((e,t)=>{u=t,l=()=>(e(d),r(p),p=0)}));h[t]=d,h[e]=(e,t)=>{if(f)return n(e);c&&b(c,e,t),o(p,e),h.catch(t)},a.exports=h,i((a=>{if(!a)return l();var i,s;c=(a=>a.map((a=>{if(null!==a&&"object"==typeof a){if(a[e])return a;if(a.then){var i=[];a.then((e=>{s[t]=e,r(i),i=0}));var s={};return s[e]=(e,t)=>(o(i,e),a.catch(t)),s}}var c={};return c[e]=e=>n(e),c[t]=a,c})))(a);var u=new Promise(((e,r)=>{(i=()=>e(s=c.map((e=>e[t])))).r=0,b(c,i,r)}));return i.r?u:s})).then(l,u),f=!1},l.d=(e,t)=>{for(var r in t)l.o(t,r)&&!l.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},l.f={},l.e=e=>Promise.all(Object.keys(l.f).reduce(((t,r)=>(l.f[r](e,t),t)),[])),l.u=e=>e+".index.js",l.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),l.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:()=>{throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},i="image-effects:",l.l=(e,t,r,n)=>{if(a[e])a[e].push(t);else{var o,s;if(void 0!==r)for(var c=document.getElementsByTagName("script"),u=0;u<c.length;u++){var p=c[u];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==i+r){o=p;break}}o||(s=!0,(o=document.createElement("script")).charset="utf-8",o.timeout=120,l.nc&&o.setAttribute("nonce",l.nc),o.setAttribute("data-webpack",i+r),o.src=e),a[e]=[t];var d=(t,r)=>{o.onerror=o.onload=null,clearTimeout(f);var n=a[e];if(delete a[e],o.parentNode&&o.parentNode.removeChild(o),n&&n.forEach((e=>e(r))),t)return t(r)},f=setTimeout(d.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=d.bind(null,o.onerror),o.onload=d.bind(null,o.onload),s&&document.head.appendChild(o)}},l.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.v=(e,t,r,n)=>{var o=fetch(l.p+""+r+".module.wasm");return"function"==typeof WebAssembly.instantiateStreaming?WebAssembly.instantiateStreaming(o,n).then((t=>Object.assign(e,t.instance.exports))):o.then((e=>e.arrayBuffer())).then((e=>WebAssembly.instantiate(e,n))).then((t=>Object.assign(e,t.instance.exports)))},(()=>{var e;l.g.importScripts&&(e=l.g.location+"");var t=l.g.document;if(!e&&t&&(t.currentScript&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName("script");r.length&&(e=r[r.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),l.p=e})(),(()=>{var e={179:0};l.f.j=(t,r)=>{var n=l.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var o=new Promise(((r,o)=>n=e[t]=[r,o]));r.push(n[2]=o);var a=l.p+l.u(t),i=new Error;l.l(a,(r=>{if(l.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var o=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;i.message="Loading chunk "+t+" failed.\n("+o+": "+a+")",i.name="ChunkLoadError",i.type=o,i.request=a,n[1](i)}}),"chunk-"+t,t)}};var t=(t,r)=>{var n,o,[a,i,s]=r,c=0;if(a.some((t=>0!==e[t]))){for(n in i)l.o(i,n)&&(l.m[n]=i[n]);s&&s(l)}for(t&&t(r);c<a.length;c++)o=a[c],l.o(e,o)&&e[o]&&e[o][0](),e[a[c]]=0},r=self.webpackChunkimage_effects=self.webpackChunkimage_effects||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})(),async function(){let e=null;try{e=await l.e(235).then(l.bind(l,235))}catch(e){return void console.error(e)}console.log(e);const t=document.getElementById("upload"),r=new FileReader;r.onloadend=()=>{const t=r.result.replace(/^data:image\/(png|jpeg|jpg);base64,/,"");let n=e.grayscale(t);document.getElementById("new-img").setAttribute("src",n)},t.addEventListener("change",(()=>{r.readAsDataURL(t.files[0])}))}()})();
|
||||
7282
package-lock.json
generated
Normal file
7282
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
23
package.json
Normal file
23
package.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "image-effects",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"serve": "webpack serve --mode=development",
|
||||
"build": "webpack --mode=production"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"webpack": "^5.60.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"wasm-pack": "^0.10.1"
|
||||
}
|
||||
}
|
||||
76
public/index.html
Normal file
76
public/index.html
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Image Effects</title>
|
||||
<!-- unpkg.com Tailwind CSS -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
|
||||
/>
|
||||
<link href="https://fonts.googleapis.com" />
|
||||
<link href="https://fonts.gstatic.com" crossorigin />
|
||||
<!-- Pacifico font from googleapis.com -->
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Pacifico"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
h1 {
|
||||
font-family: "Pacifico", cursive;
|
||||
}
|
||||
.bg {
|
||||
animation: slide 3s ease-in-out infinite alternate;
|
||||
background-image: linear-gradient(
|
||||
-60deg,
|
||||
#acffad 50%,
|
||||
#50cb95 50%
|
||||
);
|
||||
}
|
||||
.bg:nth-child(2) {
|
||||
animation-direction: alternate-reverse;
|
||||
animation-duration: 4s;
|
||||
}
|
||||
.bg:nth-child(3) {
|
||||
animation-duration: 5s;
|
||||
}
|
||||
@keyframes slide {
|
||||
0% {
|
||||
transform: translateX(-25%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(25%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg fixed inset-y-0 -inset-x-2/4 opacity-50 z-0"></div>
|
||||
<div class="bg fixed inset-y-0 -inset-x-2/4 opacity-50 z-0"></div>
|
||||
<div class="bg fixed inset-y-0 -inset-x-2/4 opacity-50 z-0"></div>
|
||||
<div class="flex items-center justify-center h-screen relative z-100">
|
||||
<div
|
||||
class="bg-white bg-opacity-95 border shadow-lg p-10 text-center"
|
||||
>
|
||||
<h1 class="text-5xl mb-8">Image Effects</h1>
|
||||
<p class="mb-4">
|
||||
Need to do some basic image manipulation? Just upload your
|
||||
image below. We'll take care of the rest.
|
||||
</p>
|
||||
<label class="bg-pink-600 text-white w-full p-6 block">
|
||||
<!-- File Input -->
|
||||
<input
|
||||
type="file"
|
||||
id="upload"
|
||||
accept=".png"
|
||||
class="hidden"
|
||||
/>
|
||||
Upload PNG Image
|
||||
</label>
|
||||
<img id="new-img" class="w-auto mx-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
30
public/main.js
Normal file
30
public/main.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
async function init() {
|
||||
let rustApp = null
|
||||
|
||||
try {
|
||||
rustApp = await import("../pkg")
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(rustApp)
|
||||
|
||||
const input = document.getElementById("upload")
|
||||
const fileReader = new FileReader()
|
||||
|
||||
fileReader.onloadend = () => {
|
||||
const base64 = fileReader.result.replace(
|
||||
/^data:image\/(png|jpeg|jpg);base64,/,
|
||||
""
|
||||
)
|
||||
let img_data_url = rustApp.grayscale(base64)
|
||||
document.getElementById("new-img").setAttribute("src", img_data_url)
|
||||
}
|
||||
|
||||
input.addEventListener("change", () => {
|
||||
fileReader.readAsDataURL(input.files[0])
|
||||
})
|
||||
}
|
||||
|
||||
init()
|
||||
31
src/lib.rs
Normal file
31
src/lib.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console::log_1 as log;
|
||||
use base64::{ encode, decode };
|
||||
use image::load_from_memory;
|
||||
use image::ImageOutputFormat::Png;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn grayscale(encoded_file: &str) -> String {
|
||||
log(&"Grayscale called".into());
|
||||
|
||||
let base64_to_vector = decode(encoded_file).unwrap();
|
||||
log(&"Image decoded".into());
|
||||
|
||||
let mut img = load_from_memory(&base64_to_vector).unwrap();
|
||||
log(&"Image loaded".into());
|
||||
|
||||
img = img.grayscale();
|
||||
log(&"Grayscale effect applied".into());
|
||||
|
||||
let mut buffer = vec![];
|
||||
img.write_to(&mut buffer, Png).unwrap();
|
||||
log(&"New image written".into());
|
||||
|
||||
let encoded_img = encode(&buffer);
|
||||
let data_url = format!(
|
||||
"data:image/png;base64,{}",
|
||||
encoded_img
|
||||
);
|
||||
|
||||
data_url
|
||||
}
|
||||
22
webpack.config.js
Normal file
22
webpack.config.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
const path = require("path")
|
||||
const HTMLWebpackPlugin = require("html-webpack-plugin")
|
||||
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin")
|
||||
|
||||
module.exports = {
|
||||
entry: "./public/main.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "index.js",
|
||||
},
|
||||
plugins: [
|
||||
new HTMLWebpackPlugin({
|
||||
template: "./public/index.html",
|
||||
}),
|
||||
new WasmPackPlugin({
|
||||
crateDirectory: path.resolve(__dirname, "."),
|
||||
}),
|
||||
],
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue