| import { BLOCK_SIZE } from '../constants.js'; |
|
|
| |
| |
| |
| export default class SpriteBlockRenderer { |
| |
| |
| |
| |
| |
| |
| |
| |
| static createBlockTexture(scene, colorPalette, level, key, colorIndex) { |
| const canvas = document.createElement('canvas'); |
| const ctx = canvas.getContext('2d'); |
| ctx.imageSmoothingEnabled = false; |
| canvas.width = BLOCK_SIZE; |
| canvas.height = BLOCK_SIZE; |
|
|
| |
| const color = colorPalette[colorIndex % colorPalette.length]; |
| const r = (color >> 16) & 0xFF; |
| const g = (color >> 8) & 0xFF; |
| const b = color & 0xFF; |
|
|
| |
| const spriteSheet = scene.textures.get('blocks-spritesheet').getSourceImage(); |
| const spriteX = (level - 1) * BLOCK_SIZE; |
|
|
| const tempCanvas = document.createElement('canvas'); |
| const tempCtx = tempCanvas.getContext('2d'); |
| tempCtx.imageSmoothingEnabled = false; |
| tempCanvas.width = spriteSheet.width; |
| tempCanvas.height = spriteSheet.height; |
| tempCtx.drawImage(spriteSheet, 0, 0); |
|
|
| const spriteData = tempCtx.getImageData(spriteX, 0, BLOCK_SIZE, BLOCK_SIZE); |
| const pixels = spriteData.data; |
|
|
| |
| const outputData = ctx.createImageData(BLOCK_SIZE, BLOCK_SIZE); |
| const output = outputData.data; |
|
|
| |
| |
| for (let i = 0; i < pixels.length; i += 4) { |
| const alpha = pixels[i + 3]; |
|
|
| if (alpha > 0) { |
| |
| const brightness = pixels[i]; |
|
|
| |
| |
| |
| |
| const multiplier = 0.5 + (brightness / 255) * 1.0; |
|
|
| |
| output[i] = Math.min(255, Math.floor(r * multiplier)); |
| output[i + 1] = Math.min(255, Math.floor(g * multiplier)); |
| output[i + 2] = Math.min(255, Math.floor(b * multiplier)); |
| output[i + 3] = 255; |
| } else { |
| |
| output[i] = 0; |
| output[i + 1] = 0; |
| output[i + 2] = 0; |
| output[i + 3] = 0; |
| } |
| } |
|
|
| ctx.putImageData(outputData, 0, 0); |
|
|
| |
| const texture = scene.textures.addCanvas(key, canvas); |
| texture.setFilter(Phaser.Textures.FilterMode.NEAREST); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| static createCrushTexture(scene, color, frameIndex, key) { |
| |
| if (scene.textures.exists(key)) { |
| return; |
| } |
|
|
| const canvas = document.createElement('canvas'); |
| const ctx = canvas.getContext('2d'); |
| ctx.imageSmoothingEnabled = false; |
| canvas.width = BLOCK_SIZE; |
| canvas.height = BLOCK_SIZE; |
|
|
| const r = (color >> 16) & 0xFF; |
| const g = (color >> 8) & 0xFF; |
| const b = color & 0xFF; |
|
|
| |
| const spriteSheet = scene.textures.get('crush-spritesheet').getSourceImage(); |
| const spriteX = frameIndex * BLOCK_SIZE; |
|
|
| const tempCanvas = document.createElement('canvas'); |
| const tempCtx = tempCanvas.getContext('2d'); |
| tempCtx.imageSmoothingEnabled = false; |
| tempCanvas.width = spriteSheet.width; |
| tempCanvas.height = spriteSheet.height; |
| tempCtx.drawImage(spriteSheet, 0, 0); |
|
|
| const spriteData = tempCtx.getImageData(spriteX, 0, BLOCK_SIZE, BLOCK_SIZE); |
| const pixels = spriteData.data; |
|
|
| const outputData = ctx.createImageData(BLOCK_SIZE, BLOCK_SIZE); |
| const output = outputData.data; |
|
|
| |
| for (let i = 0; i < pixels.length; i += 4) { |
| const brightness = pixels[i]; |
| const alpha = pixels[i + 3]; |
|
|
| |
| if (brightness >= 200 || alpha === 0) { |
| output[i] = 0; |
| output[i + 1] = 0; |
| output[i + 2] = 0; |
| output[i + 3] = 0; |
| } else { |
| |
| |
| const multiplier = 0.3 + (brightness / 255) * 0.9; |
| output[i] = Math.min(255, Math.floor(r * multiplier)); |
| output[i + 1] = Math.min(255, Math.floor(g * multiplier)); |
| output[i + 2] = Math.min(255, Math.floor(b * multiplier)); |
| output[i + 3] = 255; |
| } |
| } |
|
|
| ctx.putImageData(outputData, 0, 0); |
|
|
| const texture = scene.textures.addCanvas(key, canvas); |
| if (texture) { |
| texture.setFilter(Phaser.Textures.FilterMode.NEAREST); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| static enhancePalette(palette) { |
| const enhanced = []; |
|
|
| for (let i = 0; i < palette.length; i++) { |
| let color = palette[i]; |
| let r = (color >> 16) & 0xFF; |
| let g = (color >> 8) & 0xFF; |
| let b = color & 0xFF; |
|
|
| |
| const contrastFactor = 0.2; |
| r = Math.min(255, Math.max(0, Math.floor(128 + (r - 128) * (1 + contrastFactor)))); |
| g = Math.min(255, Math.max(0, Math.floor(128 + (g - 128) * (1 + contrastFactor)))); |
| b = Math.min(255, Math.max(0, Math.floor(128 + (b - 128) * (1 + contrastFactor)))); |
|
|
| enhanced.push((r << 16) | (g << 8) | b); |
| } |
|
|
| return enhanced; |
| } |
|
|
| |
| |
| |
| |
| |
| static ensureDistinctColors(palette) { |
| const result = [palette[0]]; |
| |
| for (let i = 1; i < palette.length; i++) { |
| let color = palette[i]; |
| let attempts = 0; |
| |
| |
| while (attempts < 10) { |
| let tooSimilar = false; |
| |
| for (let j = 0; j < result.length; j++) { |
| if (this.colorDistance(color, result[j]) < 100) { |
| tooSimilar = true; |
| break; |
| } |
| } |
| |
| if (!tooSimilar) break; |
| |
| |
| let r = (color >> 16) & 0xFF; |
| let g = (color >> 8) & 0xFF; |
| let b = color & 0xFF; |
| |
| r = (r + 60) % 256; |
| g = (g + 40) % 256; |
| b = (b + 80) % 256; |
| |
| color = (r << 16) | (g << 8) | b; |
| attempts++; |
| } |
| |
| result.push(color); |
| } |
| |
| return result; |
| } |
|
|
| |
| |
| |
| static colorDistance(c1, c2) { |
| const r1 = (c1 >> 16) & 0xFF; |
| const g1 = (c1 >> 8) & 0xFF; |
| const b1 = c1 & 0xFF; |
| const r2 = (c2 >> 16) & 0xFF; |
| const g2 = (c2 >> 8) & 0xFF; |
| const b2 = c2 & 0xFF; |
| |
| return Math.sqrt((r1-r2)**2 + (g1-g2)**2 + (b1-b2)**2); |
| } |
| } |
|
|
|
|