From 2fbe0cd79e416a55d9416bb116a5d1bbc3c8da8f Mon Sep 17 00:00:00 2001 From: He4eT Date: Thu, 15 Aug 2024 04:16:45 +0200 Subject: [PATCH] Bootstrap Web Extension --- content_scripts/milje.js | 161 +++++++++++++++++++++++++++++++++++++++ manifest.json | 16 ++++ pages/popup/popup.html | 11 +++ pages/popup/popup.js | 3 + scripts/storage.js | 1 + 5 files changed, 192 insertions(+) create mode 100644 content_scripts/milje.js create mode 100644 manifest.json create mode 100644 pages/popup/popup.html create mode 100644 pages/popup/popup.js create mode 100644 scripts/storage.js diff --git a/content_scripts/milje.js b/content_scripts/milje.js new file mode 100644 index 0000000..703d038 --- /dev/null +++ b/content_scripts/milje.js @@ -0,0 +1,161 @@ +/** + * Sorry, you are not allowed to use import in the content_script :c + * Copy'n'paste here! + */ +const storage = (browser ?? window.chrome)['storage'] + +/* */ + +storage.local.get(['params']) + .then(({params}) => params) + .catch(() => {}) + .then(assureParams) + .then(run) + +function assureParams(params) { + const assureNumber = (rawValue, defaultValue, min, max) => { + let value = Number(rawValue) + + if (isNaN(value)) return defaultValue + value = Math.max(min, value) + value = Math.min(max, value) + + return value + } + const randomSeed = assureNumber(params.randomSeed, 0, 0, Infinity) + + return { + className: params.className ?? 'browser-milje-2077', + /**/ + randomSeed: randomSeed || new Date(), + /**/ + halfPatternSize: assureNumber(params.halfPatternSize, 16, 0, Infinity), + scaleFactor: assureNumber(params.scaleFactor, 16, 0, Infinity), + gridSize: assureNumber(params.gridSize, 3, 0, Infinity), + } +} + +function run({ randomSeed, halfPatternSize, scaleFactor, gridSize }) { + const pattern = generatePattern(halfPatternSize, gridSize, randomSeed) + const miljeCanvas = matrixToCanvas(pattern, halfPatternSize, scaleFactor) + + document.body.appendChild(miljeCanvas) +} + +/* Parts */ + +/** + * LCG using GCC's constants + * https://en.wikipedia.org/wiki/Linear_congruential_generator + */ +const LCG = (seed) => { + let state = seed + const m = 0x80000000 // 2**31 + const a = 1103515245 + const c = 12345 + + return () => { + state = (a * state + c) % m + return state / (m - 1) + } +} + +/* Pattern generation */ + +function generatePattern(halfPatternSize, gridSize, randomSeed) { + const random = LCG(randomSeed) + const matrix = Array(halfPatternSize * 2).fill(null) + .map(() => Array(halfPatternSize * 2).fill(0)) + + const put = (x, y, value) => { + const last = halfPatternSize * 2 - 1 + matrix[x][y] = value + matrix[y][x] = value + + matrix[x][last - y] = value + matrix[last - y][x] = value + + matrix[last - x][y] = value + matrix[y][last - x] = value + + matrix[last - y][last - x] = value + matrix[last - x][last - y] = value + } + + for (let i = 0; i < halfPatternSize; i++) { + for (let j = 0; j < halfPatternSize; j++) { + if ((i % gridSize === 0) || (j % gridSize === 0)) { + put(i, j, 1) + } else { + put(i, j, Number(random() > 0.5)) + } + } + } + return matrix +} + +/* Canvas */ + +function getCanvas(size) { + const canvas = document.createElement('canvas') + canvas.width = size + canvas.height = size + + Object.entries({ + 'position': 'fixed', + 'top': '0%', + 'left': '50%', + 'margin': '0 auto', + 'transform': 'translateY(-50%) translateX(-50%) rotate(45deg)', + 'filter': 'drop-shadow(0px 0px 10px rgba(60,60,60,0.5))', + 'zIndex': '9999', + }).forEach(([key, value]) => { canvas.style[key] = value }) + + return canvas +} + +/** @see https://stackoverflow.com/questions/3448347/how-to-scale-an-imagedata-in-html-canvas */ +function scaleImageData(imageData, scale, ctx) { + const scaled = ctx.createImageData(imageData.width * scale, imageData.height * scale) + const subLine = ctx.createImageData(scale, 1).data + for (let row = 0; row < imageData.height; row++) { + for (let col = 0; col < imageData.width; col++) { + const sourcePixel = imageData.data.subarray( + (row * imageData.width + col) * 4, + (row * imageData.width + col) * 4 + 4) + for (let x = 0; x < scale; x++) { + subLine.set(sourcePixel, x * 4) + } + for (let y = 0; y < scale; y++) { + const destRow = row * scale + y + const destCol = col * scale + scaled.data.set(subLine, (destRow * scaled.width + destCol) * 4) + } + } + } + + return scaled +} + +function matrixToCanvas(matrix, halfPatternSize, scaleFactor) { + const flatMatrix = matrix.flat() + const canvasSize = halfPatternSize * 2 + + const canvas = getCanvas(canvasSize * scaleFactor) + const ctx = canvas.getContext('2d') + + const imageData = ctx.createImageData(canvasSize, canvasSize) + for (let i = 0; i < flatMatrix.length; i++) { + const value = flatMatrix[i] * 255 + const index = i * 4 + imageData.data[index + 0] = value // Red + imageData.data[index + 1] = value // Green + imageData.data[index + 2] = value // Blue + imageData.data[index + 3] = value ? 255 : 0 // Alpha + } + + const scaledImageData = scaleImageData(imageData, scaleFactor, ctx) + ctx.putImageData(scaledImageData, 0, 0) + + return canvas +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..d20c256 --- /dev/null +++ b/manifest.json @@ -0,0 +1,16 @@ +{ + "manifest_version": 3, + "name": "browser-milje-2077", + "description": "Adds a pixelated milje table cloth on top of your browser viewport", + "version": "0.0.1", + "action": { + "default_popup": "pages/popup/popup.html" + }, + "content_scripts": [{ + "js": ["content_scripts/milje.js"], + "matches": ["", "file:///"] + }], + "permissions": [ + "storage" + ] +} diff --git a/pages/popup/popup.html b/pages/popup/popup.html new file mode 100644 index 0000000..654af45 --- /dev/null +++ b/pages/popup/popup.html @@ -0,0 +1,11 @@ + + + + + Milje-2077 + + + test + + + diff --git a/pages/popup/popup.js b/pages/popup/popup.js new file mode 100644 index 0000000..5579f6c --- /dev/null +++ b/pages/popup/popup.js @@ -0,0 +1,3 @@ +import { storage } from '../../scripts/storage.js' + +storage.local.set({'params': {scaleFactor: 128}}) diff --git a/scripts/storage.js b/scripts/storage.js new file mode 100644 index 0000000..33e20ae --- /dev/null +++ b/scripts/storage.js @@ -0,0 +1 @@ +export const storage = (window.browser ?? window.chrome)['storage']