diff --git a/v1-com-officielle/glitcher/glitcher.css b/v1-com-officielle/glitcher/glitcher.css
new file mode 100644
index 0000000..07f1fd5
--- /dev/null
+++ b/v1-com-officielle/glitcher/glitcher.css
@@ -0,0 +1,55 @@
+/*=============Typo*/
+@font-face {
+ font-family: 'lineal';
+ src: url('../typo/Lineal-Heavy.ttf');
+}
+
+@font-face {
+ font-family: 'pressStart2P';
+ src: url('../typo/PressStart2P-Regular.ttf');
+}
+
+@font-face {
+ font-family: 'velvelyne';
+ src: url('../typo/Velvelyne-Light.ttf') format('truetype');
+ font-weight:lighter;
+}
+
+@font-face {
+ font-family: 'velvelyne';
+ src:url('../typo/Velvelyne-Bold.ttf') format('truetype');
+ font-weight: bold;
+}
+
+/*============General*/
+
+body, html {
+ padding: 0;
+ margin: 0;
+}
+
+:root {
+ --back-color: black;
+ --main-color: white;
+ --accent-color: #3CFF00;
+ --neon-color: #3CFF00;
+
+ color: var(--main-color);
+ font-size: 1rem;
+ background: var(--back-color);
+ font-family: 'velvelyne', sans-serif;
+ font-weight: bold;
+}
+
+h1 {
+ font-family: 'lineal', sans-serif;
+ font-weight: bolder;
+}
+
+#ui-canvas {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 100vw;
+ max-height: 100vh;
+}
\ No newline at end of file
diff --git a/v1-com-officielle/glitcher/glitcher.js b/v1-com-officielle/glitcher/glitcher.js
new file mode 100644
index 0000000..f7ad40b
--- /dev/null
+++ b/v1-com-officielle/glitcher/glitcher.js
@@ -0,0 +1,28 @@
+import { Glitcher } from "./lib/glitcher";
+
+const UI_GLITCHER = new Glitcher(document.getElementById("ui-canvas"))
+const FORM = document.getElementById("glitcher-form")
+
+async function updateFromForm(){
+ let data = new FormData(FORM)
+
+ let image = data.get("image")
+ if(image){
+ await UI_GLITCHER.setImage(image)
+ } else {
+ UI_GLITCHER.clearImage()
+ }
+
+ UI_GLITCHER.render()
+}
+
+for(let el of FORM.elements){
+ el.addEventListener("change", () => FORM.requestSubmit())
+}
+
+FORM.addEventListener("submit", e => {
+ e.preventDefault()
+ updateFromForm()
+ })
+
+updateFromForm()
\ No newline at end of file
diff --git a/v1-com-officielle/glitcher/index.html b/v1-com-officielle/glitcher/index.html
new file mode 100644
index 0000000..df9a2d5
--- /dev/null
+++ b/v1-com-officielle/glitcher/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ Drags&Nerds Glitcher
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/v1-com-officielle/glitcher/lib/base.frag b/v1-com-officielle/glitcher/lib/base.frag
new file mode 100644
index 0000000..a5db85e
--- /dev/null
+++ b/v1-com-officielle/glitcher/lib/base.frag
@@ -0,0 +1,6 @@
+varying highp vec2 vTextureCoord;
+uniform sampler2D uImageSampler;
+
+void main() {
+ gl_FragColor = texture2D(uImageSampler, vTextureCoord);
+}
\ No newline at end of file
diff --git a/v1-com-officielle/glitcher/lib/base.vert b/v1-com-officielle/glitcher/lib/base.vert
new file mode 100644
index 0000000..395c746
--- /dev/null
+++ b/v1-com-officielle/glitcher/lib/base.vert
@@ -0,0 +1,9 @@
+attribute vec4 aVertexPosition;
+attribute vec2 aTextureCoord;
+
+varying highp vec2 vTextureCoord;
+
+void main() {
+ gl_Position = aVertexPosition;
+ vTextureCoord = aTextureCoord;
+}
diff --git a/v1-com-officielle/glitcher/lib/glitcher.js b/v1-com-officielle/glitcher/lib/glitcher.js
new file mode 100644
index 0000000..798e69a
--- /dev/null
+++ b/v1-com-officielle/glitcher/lib/glitcher.js
@@ -0,0 +1,221 @@
+import resizeToFit from 'intrinsic-scale';
+import baseVertSource from "./base.vert?raw"
+import baseFragSource from "./base.frag?raw"
+
+export class Glitcher {
+
+ /** @type {HTMLCanvasElement} */
+ canvas
+
+ /**
+ * @param {HTMLCanvasElement} canvas
+ */
+ constructor(canvas) {
+ this.canvas = canvas
+ this.init()
+ }
+
+ init(){
+ this.ctx = this.canvas.getContext("webgl")
+ if(!this.ctx){
+ throw new Error("WebGL isn't supported")
+ }
+
+ let baseVertShader = this.ctx.createShader(this.ctx.VERTEX_SHADER)
+ this.ctx.shaderSource(baseVertShader, baseVertSource)
+ this.ctx.compileShader(baseVertShader)
+
+ if (!this.ctx.getShaderParameter(baseVertShader, this.ctx.COMPILE_STATUS)) {
+ throw new Error(`failed to compile base.vert: ${this.ctx.getShaderInfoLog(baseVertShader)}`)
+ }
+
+ let baseFragShader = this.ctx.createShader(this.ctx.FRAGMENT_SHADER)
+ this.ctx.shaderSource(baseFragShader, baseFragSource)
+ this.ctx.compileShader(baseFragShader)
+
+ if (!this.ctx.getShaderParameter(baseFragShader, this.ctx.COMPILE_STATUS)) {
+ throw new Error(`failed to compile base.frag: ${this.ctx.getShaderInfoLog(baseFragShader)}`)
+ }
+
+ let baseShaderProgram = this.ctx.createProgram()
+ this.ctx.attachShader(baseShaderProgram, baseVertShader)
+ this.ctx.attachShader(baseShaderProgram, baseFragShader)
+ this.ctx.linkProgram(baseShaderProgram)
+
+ if (!this.ctx.getProgramParameter(baseShaderProgram, this.ctx.LINK_STATUS)) {
+ throw new Error(`failed to link base shaders: ${this.ctx.getProgramInfoLog(baseShaderProgram)}`)
+ }
+
+ this.baseProgram = baseShaderProgram;
+ this.aVertexPosition = this.ctx.getAttribLocation(baseShaderProgram, "aVertexPosition")
+ this.aTextureCoord = this.ctx.getAttribLocation(baseShaderProgram, "aTextureCoord")
+ this.uImageSampler = this.ctx.getUniformLocation(baseShaderProgram, "uImageSampler")
+
+ let panelPositionBuffer = this.ctx.createBuffer()
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelPositionBuffer)
+ const positions = [
+ -1.0, 1.0,
+ 1.0, 1.0,
+ -1.0, -1.0,
+ 1.0, -1.0,
+ ]
+ this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(positions), this.ctx.STATIC_DRAW)
+ this.panelPositionsBuffer = panelPositionBuffer
+
+ let panelUVBuffer = this.ctx.createBuffer()
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelUVBuffer)
+ const uv = [
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0,
+ ]
+ this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(uv), this.ctx.STATIC_DRAW)
+ this.panelUVBuffer = panelUVBuffer
+
+ let imageTexture = this.ctx.createTexture()
+ this.imageTexture = imageTexture;
+ this.clearImage();
+ }
+
+ clearImage(){
+ this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture)
+ this.ctx.texImage2D(
+ this.ctx.TEXTURE_2D,
+ 0,
+ this.ctx.RGBA,
+ 1, 1,
+ 0,
+ this.ctx.RGBA,
+ this.ctx.UNSIGNED_BYTE,
+ new Uint8Array([0, 0, 0, 0])
+ )
+ }
+
+ /**
+ * @param {File} imageFile
+ */
+ async setImage(imageFile){
+ if(imageFile == this.currentImageFile){
+ return
+ }
+
+ if(this.currentImage){
+ URL.revokeObjectURL(this.currentImage.src)
+ }
+
+ let image = new Image()
+ const loadProm = new Promise((res, rej) => {
+ function ok(){
+ image.removeEventListener("error", nok)
+ res()
+ }
+
+ function nok(){
+ image.removeEventListener("load", ok)
+ rej()
+ }
+
+ image.addEventListener("load", ok)
+ image.addEventListener("error", nok)
+ })
+ image.src = URL.createObjectURL(imageFile)
+
+ try {
+ await loadProm
+ } catch(e) {
+ URL.revokeObjectURL(image.src)
+ throw e
+ }
+ this.resize(image.width, image.height)
+
+ this.currentImageFile = imageFile
+ this.currentImage = image
+
+ this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture)
+ this.ctx.texImage2D(
+ this.ctx.TEXTURE_2D,
+ 0,
+ this.ctx.RGBA,
+ this.ctx.RGBA,
+ this.ctx.UNSIGNED_BYTE,
+ this.currentImage
+ )
+
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.LINEAR);
+ }
+
+ resize(width, height){
+ let computedStyle = window.getComputedStyle(this.canvas)
+ let maxWidth = parseFloat(computedStyle["max-width"])
+ let maxHeight = parseFloat(computedStyle["max-height"])
+
+ if(window.devicePixelRatio){
+ maxWidth *= window.devicePixelRatio
+ maxHeight *= window.devicePixelRatio
+ }
+
+ if(Number.isNaN(maxWidth)){
+ maxWidth = width
+ }
+
+ if(Number.isNaN(maxHeight)){
+ maxHeight = height
+ }
+
+ let resize = resizeToFit("contain", {width, height}, {width: maxWidth, height: maxHeight})
+
+ this.canvas.width = resize.width
+ this.canvas.height = resize.height
+ this.ctx.viewport(0, 0, this.canvas.width, this.canvas.height);
+ }
+
+ render(){
+ if(!this.ctx){
+ throw new Error("Glitcher not initialized, please run init()")
+ }
+
+ this.ctx.clearColor(0.0, 0.0, 0.0, 0.0)
+ this.ctx.clearDepth(1.0)
+ this.ctx.enable(this.ctx.DEPTH_TEST)
+ this.ctx.enable(this.ctx.LEQUAL)
+
+ this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT)
+
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.panelPositionsBuffer)
+ this.ctx.vertexAttribPointer(
+ this.aVertexPosition,
+ 2, // N components per iteration
+ this.ctx.FLOAT,
+ false, //Normalize,
+ 0, // (stride) how many bytes to get from one set of values to the next
+ 0, // Start offset
+ )
+ this.ctx.enableVertexAttribArray(this.aVertexPosition)
+
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.panelUVBuffer)
+ this.ctx.vertexAttribPointer(
+ this.aTextureCoord,
+ 2,
+ this.ctx.FLOAT,
+ false,
+ 0,
+ 0,
+ )
+ this.ctx.enableVertexAttribArray(this.aTextureCoord)
+
+ this.ctx.activeTexture(this.ctx.TEXTURE0)
+ this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture)
+ this.ctx.uniform1i(this.uImageSampler, 0)
+
+ this.ctx.useProgram(this.baseProgram)
+
+ this.ctx.drawArrays(
+ this.ctx.TRIANGLE_STRIP,
+ 0, // Vertex offset
+ 4, // Total vertex
+ );
+ }
+}
\ No newline at end of file
diff --git a/v1-com-officielle/package-lock.json b/v1-com-officielle/package-lock.json
index 2204690..5bfe706 100644
--- a/v1-com-officielle/package-lock.json
+++ b/v1-com-officielle/package-lock.json
@@ -8,6 +8,7 @@
"name": "v1-com-officielle",
"version": "0.0.0",
"dependencies": {
+ "intrinsic-scale": "^5.0.0",
"vite-svg-loader": "^5.1.1",
"vue": "^3.5.25",
"vue3-clipboard": "^1.0.0",
@@ -1442,6 +1443,18 @@
"delegate": "^3.1.2"
}
},
+ "node_modules/intrinsic-scale": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/intrinsic-scale/-/intrinsic-scale-5.0.0.tgz",
+ "integrity": "sha512-NJY7170uG8gYkaGGV0ddakq5VAWSvM2FKhARgXj8OQwod1hQFRBWCXn7dk35bGd+vsRYYnJQw/Qpebk8MWp4dg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fregante"
+ }
+ },
"node_modules/keycode": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz",
diff --git a/v1-com-officielle/package.json b/v1-com-officielle/package.json
index e11f394..f96e110 100644
--- a/v1-com-officielle/package.json
+++ b/v1-com-officielle/package.json
@@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "intrinsic-scale": "^5.0.0",
"vite-svg-loader": "^5.1.1",
"vue": "^3.5.25",
"vue3-clipboard": "^1.0.0",
diff --git a/v1-com-officielle/vite.config.js b/v1-com-officielle/vite.config.js
index 57a0efa..aee2945 100644
--- a/v1-com-officielle/vite.config.js
+++ b/v1-com-officielle/vite.config.js
@@ -6,13 +6,21 @@ import svgLoader from 'vite-svg-loader'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), svgLoader()],
- server:{
+ server: {
host: '0.0.0.0',
- port:8080
+ port: 8080
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
+ },
+ build: {
+ rollupOptions: {
+ input: {
+ main: path.resolve(import.meta.dirname, 'index.html'),
+ glitcher: path.resolve(import.meta.dirname, 'glitcher/index.html'),
+ },
+ },
}
})