diff --git a/Changelog.md b/Changelog.md index 3e35752..f0fc2ef 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# 0.7.6-alpha: +Bugfixes: +- Fix errors when images are not present in a saved file +- Save images properly + # 0.7.5-alpha: Bugfixes: - Fix errors when files refer to now nonexistant frames diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a5fdefa..d2aee76 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Lightningbeam", - "version": "0.7.5-alpha", + "version": "0.7.6-alpha", "identifier": "org.lightningbeam.core", "build": { "frontendDist": "../src" diff --git a/src/main.js b/src/main.js index 4316e3c..710be1b 100644 --- a/src/main.js +++ b/src/main.js @@ -41,6 +41,7 @@ import { rgbToHsv, multiplyMatrices, growBoundingBox, + createMissingTexturePattern, } from "./utils.js"; import { backgroundColor, @@ -2940,7 +2941,13 @@ class BaseShape { if (this.filled) { ctx.beginPath(); if (this.fillImage) { - let pat = ctx.createPattern(this.fillImage, "no-repeat"); + let pat; + if (this.fillImage instanceof Element || + Object.keys(this.fillImage).length !== 0) { + pat = ctx.createPattern(this.fillImage, "no-repeat"); + } else { + pat = createMissingTexturePattern(ctx) + } ctx.fillStyle = pat; } else { ctx.fillStyle = this.fillStyle; @@ -3162,12 +3169,20 @@ class Shape extends BaseShape { this.inProgress = true; } static fromJSON(json) { + let fillImage = undefined; + if (json.fillImage && Object.keys(json.fillImage).length !== 0) { + let img = new Image(); + img.src = json.fillImage.src + fillImage = img + } else { + fillImage = {} + } const shape = new Shape( json.startx, json.starty, { fillStyle: json.fillStyle, - fillImage: json.fillImage, + fillImage: fillImage, strokeStyle: json.strokeStyle, lineWidth: json.lineWidth, fillShape: json.filled, @@ -3199,7 +3214,11 @@ class Shape extends BaseShape { json.startx = this.startx; json.starty = this.starty; json.fillStyle = this.fillStyle; - json.fillImage = this.fillImage; + if (this.fillImage instanceof Element) { + json.fillImage = { + src: this.fillImage.src + } + } json.strokeStyle = this.fillStyle; json.lineWidth = this.lineWidth; json.filled = this.filled; @@ -4530,6 +4549,10 @@ async function _open(path, returnJson = false) { ); } else { console.error(e); + await messageDialog( + `Error replaying file: ${e}`, + { title: "Error", kind: "error" }, + ); } } } diff --git a/src/utils.js b/src/utils.js index 59318df..d0c996c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -461,6 +461,32 @@ function drawCheckerboardBackground(ctx, x, y, width, height, squareSize) { ctx.fillRect(x, y, width, height); } +const missingTexturePatternCache = new Map(); + +function createMissingTexturePattern(ctx) { + // Return cached pattern if it exists + if (missingTexturePatternCache.has(ctx)) return missingTexturePatternCache.get(ctx); + + // Create an offscreen canvas for the checkerboard pattern + const size = 16; + const patternCanvas = document.createElement('canvas'); + patternCanvas.width = patternCanvas.height = size * 2; + const patternCtx = patternCanvas.getContext('2d'); + + // Draw the magenta and black checkerboard pattern + for (let y = 0; y < 2; y++) { + for (let x = 0; x < 2; x++) { + patternCtx.fillStyle = (x + y) % 2 === 0 ? 'magenta' : 'black'; + patternCtx.fillRect(x * size, y * size, size, size); + } + } + + // Cache and return the pattern + const pattern = ctx.createPattern(patternCanvas, 'repeat'); + missingTexturePatternCache.set(ctx, pattern); + return pattern; +} + function hexToHsl(hex) { // Step 1: Convert hex to RGB let r = parseInt(hex.substring(1, 3), 16) / 255; @@ -919,6 +945,7 @@ export { rgbToHex, rgbToHsv, drawCheckerboardBackground, + createMissingTexturePattern, clamp, signedAngleBetweenVectors, rotateAroundPoint,