Add various code comments
parent
f453615478
commit
ba12fef1c6
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component is a modal settings dialog for changing the background color.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides a visualization of the current brush size and shape and controls for changing them.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { OverflowMenu, OverflowMenuItem } from "carbon-components-svelte";
|
||||
import { FilledCircle, FilledSquare, type PixelPosition } from "../types/shapes"
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component is a modal that provides settings for changing the checkerboard background.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component shows a given color swatch/index and provides controls for adding and replacing swatches within the palette.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Button } from "carbon-components-svelte";
|
||||
import { AddLarge, ColorSwitch } from "carbon-icons-svelte";
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides HSV controls for adjusting RGBA input values.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { HSV2RGB, RGB2HSV } from "../types/colors"
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides a modal for deleting a swatch from the palette.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Checkbox, Column, Dropdown, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||
import type { LoadedFile } from "../types/file"
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides a draggable floating panel that contains arbitrary content.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import {
|
||||
ModalHeader,
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides a modal for adjusting the grid settings.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Column, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component is a modal that provides settings for changing the theme.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Column, Dropdown, Grid, Modal, NumberInput, Row, TextInput } from "carbon-components-svelte";
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component is a full 2D pixel editor.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides importing an indexed or RGBA PNG file with options for how to interpret columns and rows as groups and animations.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { GetFilePath, OpenFileBytes } from '../../wailsjs/go/main/App.js'
|
||||
import { data } from '../../wailsjs/go/models.js'
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides a modal for creating a new file.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { onMount } from 'svelte';
|
||||
import { data } from '../../wailsjs/go/models.js'
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component shows swatches of a given palette and provides controls for selecting, moving, and deleting swatches.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import type { Color } from '../types/palette'
|
||||
import { ReplaceSwatchUndoable, type LoadedFile, AddSwatchUndoable, MoveSwatchUndoable } from '../types/file'
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
@component
|
||||
|
||||
This component provides a sprite stack view of a file.
|
||||
-->
|
||||
<script lang='ts'>
|
||||
import { Grid, Row, Column, Checkbox, Slider } from "carbon-components-svelte"
|
||||
import type { LoadedFile } from "src/types/file"
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import type { PixelPosition } from "./shapes"
|
||||
|
||||
/**
|
||||
* @type {Canvas}
|
||||
*
|
||||
* Canvas provides a way to store and manipulate 2D pixel data.
|
||||
*/
|
||||
export class Canvas {
|
||||
width: number
|
||||
height: number
|
||||
|
@ -36,21 +41,27 @@ export class Canvas {
|
|||
return canvas
|
||||
}
|
||||
|
||||
// clear sets all pixels to 0.
|
||||
clear() {
|
||||
for (let i = 0; i < this.pixels.length; i++) {
|
||||
this.pixels[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// refreshCanvas redraws the canvas with the current pixel data.
|
||||
refreshCanvas() {
|
||||
this.canvas.width = this.width
|
||||
this.canvas.height = this.height
|
||||
let ctx = this.canvas.getContext('2d')
|
||||
ctx.putImageData(this.imageData, 0, 0)
|
||||
}
|
||||
|
||||
// setPalette sets the palette to the provided value.
|
||||
setPalette(palette: Uint32Array) {
|
||||
this.palette = palette
|
||||
}
|
||||
|
||||
// getPaletteAsRGBA returns the RGBA values of the palette color at the provided index.
|
||||
getPaletteAsRGBA(index: number): { r: number, g: number, b: number, a: number } {
|
||||
if (index < 0 || index >= this.palette.length) {
|
||||
return { r: 0, g: 0, b: 0, a: 0 }
|
||||
|
@ -63,12 +74,16 @@ export class Canvas {
|
|||
a: (color >> 24) & 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
// setPaletteFromUint8Array sets the palette to the provided value.
|
||||
setPaletteFromUint8Array(palette: Uint8Array) {
|
||||
this.palette = new Uint32Array(palette.length / 4)
|
||||
for (let i = 0; i < palette.length; i += 4) {
|
||||
this.palette[i / 4] = new Uint32Array(palette.buffer.slice(i, i + 4))[0]
|
||||
}
|
||||
}
|
||||
|
||||
// setPixelsFromUint8Array sets the pixel data to the provided value.
|
||||
setPixelsFromUint8Array(pixels: Uint8Array) {
|
||||
this.pixels = pixels
|
||||
this.imageData = new ImageData(this.width, this.height)
|
||||
|
@ -80,12 +95,16 @@ export class Canvas {
|
|||
this.imageData.data[i * 4 + 3] = (color >> 24) & 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
// getPixel gets the index at the provided pixel position.
|
||||
getPixel(x: number, y: number): number {
|
||||
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
||||
return -1
|
||||
}
|
||||
return this.pixels[y * this.width + x]
|
||||
}
|
||||
|
||||
// setPixel sets the index at the provided pixel position.
|
||||
setPixel(x: number, y: number, index: number) {
|
||||
this.pixels[y * this.width + x] = index
|
||||
let color = this.palette[index]
|
||||
|
@ -98,6 +117,8 @@ export class Canvas {
|
|||
this.imageData.data[(y * this.width + x) * 4 + 2] = b
|
||||
this.imageData.data[(y * this.width + x) * 4 + 3] = a
|
||||
}
|
||||
|
||||
// setPixelRGBA sets the given pixel position to the provided RGBA values. If the RGBA values do not exist in the palette, they are automatically added.
|
||||
setPixelRGBA(x: number, y: number, r: number, g: number, b: number, a: number) {
|
||||
this.pixels[y * this.width + x] = this.addPaletteColor(r, g, b, a)
|
||||
this.imageData.data[(y * this.width + x) * 4 + 0] = r
|
||||
|
@ -105,6 +126,8 @@ export class Canvas {
|
|||
this.imageData.data[(y * this.width + x) * 4 + 2] = b
|
||||
this.imageData.data[(y * this.width + x) * 4 + 3] = a
|
||||
}
|
||||
|
||||
// addPaletteColor adds the provided RGBA values to the palette if it does not already exist, and returns the index of the color in the palette.
|
||||
addPaletteColor(r: number, g: number, b: number, a: number): number {
|
||||
// Check if the color is already in the palette
|
||||
for (let i = 0; i < this.palette.length; i++) {
|
||||
|
@ -124,6 +147,8 @@ export class Canvas {
|
|||
|
||||
return index
|
||||
}
|
||||
|
||||
// getClosestPaletteColor returns the RGBA and index values of the closest color in the palette to the provided RGBA values.
|
||||
getClosestPaletteColor(r: number, g: number, b: number, a: number): {r: number, g: number, b: number, a: number, index: number} {
|
||||
let similarityMap = this.palette.map((color) => {
|
||||
let r2 = color & 0xFF
|
||||
|
@ -148,9 +173,13 @@ export class Canvas {
|
|||
index: closestIndex
|
||||
}
|
||||
}
|
||||
|
||||
// addNewPaletteColor adds the provided RGBA values to the palette.
|
||||
addNewPaletteColor(r: number, g: number, b: number, a: number) {
|
||||
this.palette = new Uint32Array([...this.palette, new Uint32Array([(a << 24) | (b << 16) | (g << 8) | r])[0]])
|
||||
}
|
||||
|
||||
// insertPaletteColor inserts the provided RGBA values into the palette at the provided index.
|
||||
insertPaletteColor(index: number, r: number, g: number, b: number, a: number) {
|
||||
let newPalette = new Uint32Array(this.palette.length + 1)
|
||||
for (let i = 0; i < index; i++) {
|
||||
|
@ -162,6 +191,8 @@ export class Canvas {
|
|||
}
|
||||
this.palette = newPalette
|
||||
}
|
||||
|
||||
// removePaletteIndex removes the palette entry at the provided index. This also updates the pixel data to reflect the change.
|
||||
removePaletteIndex(index: number) {
|
||||
if (index < 0) {
|
||||
index = this.palette.length - index
|
||||
|
@ -182,9 +213,13 @@ export class Canvas {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replacePaletteColor replaces the palette entry at the provided index with the provided RGBA values.
|
||||
replacePaletteColor(index: number, r: number, g: number, b: number, a: number) {
|
||||
this.palette[index] = new Uint32Array([(a << 24) | (b << 16) | (g << 8) | r])[0]
|
||||
}
|
||||
|
||||
// hasPaletteColor returns whether the palette contains the provided RGBA values.
|
||||
hasPaletteColor(r: number, g: number, b: number, a: number): boolean {
|
||||
for (let color of this.palette) {
|
||||
if ((color & 0xFF) === r && ((color >> 8) & 0xFF) === g && ((color >> 16) & 0xFF) === b && ((color >> 24) & 0xFF) === a) {
|
||||
|
@ -193,11 +228,15 @@ export class Canvas {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// swapPaletteColors swaps the palette entries at the provided indices.
|
||||
swapPaletteColors(index1: number, index2: number) {
|
||||
let temp = this.palette[index1]
|
||||
this.palette[index1] = this.palette[index2]
|
||||
this.palette[index2] = temp
|
||||
}
|
||||
|
||||
// movePaletteColor moves the palette entry at the provided index to the new index.
|
||||
movePaletteColor(from: number, to: number) {
|
||||
let temp = this.palette[from]
|
||||
if (from < to) {
|
||||
|
@ -211,9 +250,13 @@ export class Canvas {
|
|||
}
|
||||
this.palette[to] = temp
|
||||
}
|
||||
|
||||
// setFakePalette updates the fake palette to the provided value.
|
||||
setFakePalette(palette: Uint32Array | undefined) {
|
||||
this.fakePalette = palette
|
||||
}
|
||||
|
||||
// refreshImageData updates the ImageData with the current pixel data.
|
||||
refreshImageData() {
|
||||
let palette = this.palette
|
||||
if (this.fakePalette) {
|
||||
|
@ -231,7 +274,7 @@ export class Canvas {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns the an ImageData containing the canvas contents clipped to the provided pixel mask.
|
||||
// getImageDataFromMask the ImageData containing the canvas contents clipped to the provided pixel mask.
|
||||
getImageDataFromMask(mask: PixelPosition[]): {imageData: ImageData, x: number, y: number, w: number, h: number} {
|
||||
// Get minimum x position from mask.
|
||||
let minX = 9999999
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// floatsToBytes converts 0-1 to a 0-255 range.
|
||||
function floatsToBytes(colors: number[]): number[] {
|
||||
if (colors.length <= 0) return colors
|
||||
if (colors[0] <= 1.0) {
|
||||
|
@ -8,6 +9,7 @@ function floatsToBytes(colors: number[]): number[] {
|
|||
return colors
|
||||
}
|
||||
|
||||
// bytesToFloats converts 0-255 to a 0-1 range.
|
||||
function bytesToFloats(colors: number[]): number[] {
|
||||
if (colors.length <= 0) return colors
|
||||
if (colors[0] > 1.0) {
|
||||
|
@ -18,6 +20,7 @@ function bytesToFloats(colors: number[]): number[] {
|
|||
return colors
|
||||
}
|
||||
|
||||
// HSV2RGB converts HSV to RGB.
|
||||
export function HSV2RGB(hsv: number[]): number[] {
|
||||
let hh: number, p: number, q: number, t: number, ff: number, i: number
|
||||
let rgb: number[] = []
|
||||
|
@ -75,6 +78,7 @@ export function HSV2RGB(hsv: number[]): number[] {
|
|||
return floatsToBytes(rgb)
|
||||
}
|
||||
|
||||
// RGB2HSV converts RGB to HSV.
|
||||
export function RGB2HSV(rgb: number[]): number[] {
|
||||
rgb = bytesToFloats(rgb)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { PixelPosition } from "./shapes"
|
||||
|
||||
// SelectionArea is basically a canvas and pixel mask that is used to represent a selection area. It provides marching ants to visualize the selection.
|
||||
export class SelectionArea {
|
||||
public marchingCanvas: HTMLCanvasElement
|
||||
private marchStep: number = 0
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// PixelPosition represents a coordinate and index.
|
||||
export interface PixelPosition {
|
||||
x: number
|
||||
y: number
|
||||
index: number
|
||||
}
|
||||
|
||||
// FilledCircle returns an array of PixelPositions that correspond to a filled circle.
|
||||
export function FilledCircle(x: number, y: number, radius: number, index: number): PixelPosition[] {
|
||||
let pixels: PixelPosition[] = []
|
||||
|
||||
|
@ -18,6 +20,7 @@ export function FilledCircle(x: number, y: number, radius: number, index: number
|
|||
return pixels
|
||||
}
|
||||
|
||||
// FilledSquare returns an array of PixelPositions that correspond to a filled square.
|
||||
export function FilledSquare(x: number, y: number, size: number, index: number): PixelPosition[] {
|
||||
let pixels: PixelPosition[] = []
|
||||
|
||||
|
@ -32,6 +35,7 @@ export function FilledSquare(x: number, y: number, size: number, index: number):
|
|||
return pixels
|
||||
}
|
||||
|
||||
// RandomSpray returns an array of PixelPositions that correspond to a random spray of pixels.
|
||||
export function RandomSpray(x: number, y: number, radius: number, density: number, index: number): PixelPosition[] {
|
||||
let pixels: PixelPosition[] = []
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ interface Pointer {
|
|||
|
||||
export type BrushType = "circle" | "square"
|
||||
|
||||
// Tool is an interface that receives pointer events and can act upon a ToolContext (which contains data such as the current file).
|
||||
export interface Tool {
|
||||
isActive(): boolean
|
||||
pointerDown(ctx: ToolContext, ptr: Pointer): void
|
||||
|
@ -23,34 +24,41 @@ export interface Tool {
|
|||
pointerUp(ctx: ToolContext, ptr: Pointer): void
|
||||
}
|
||||
|
||||
// BrushToolContext provides context specific to the brush tool.
|
||||
export interface BrushToolContext {
|
||||
brushSize: number
|
||||
brushType: BrushType
|
||||
colorIndex: number
|
||||
}
|
||||
|
||||
// SprayToolContext provides context specific to the spray tool.
|
||||
export interface SprayToolContext {
|
||||
radius: number
|
||||
density: number
|
||||
colorIndex: number
|
||||
}
|
||||
|
||||
// EraserToolContext provides context specific to the eraser tool.
|
||||
export interface EraserToolContext {
|
||||
brushSize: number
|
||||
brushType: BrushType
|
||||
}
|
||||
|
||||
// FloodToolContext provides context specific to the flood tool.
|
||||
export interface FloodToolContext {
|
||||
colorIndex: number
|
||||
}
|
||||
|
||||
// SelectionToolContext provides context specific to the selection tool.
|
||||
export interface SelectionToolContext {
|
||||
}
|
||||
|
||||
// PickerToolContext provides context specific to the picker tool.
|
||||
export interface PickerToolContext {
|
||||
setColorIndex(index: number): void
|
||||
}
|
||||
|
||||
// BrushTool is a tool that allows the user to draw with a brush.
|
||||
export class BrushTool implements Tool {
|
||||
private lastX: number
|
||||
private lastY: number
|
||||
|
@ -139,6 +147,7 @@ export class BrushTool implements Tool {
|
|||
}
|
||||
}
|
||||
|
||||
// EraserTool is basically the BrushTool, but with the color index set to 0 (meaning a transparent pixel).
|
||||
export class EraserTool extends BrushTool {
|
||||
pointerDown(ctx: ToolContext & EraserToolContext, ptr: Pointer) {
|
||||
super.pointerDown({...ctx, colorIndex: 0}, ptr)
|
||||
|
@ -148,6 +157,7 @@ export class EraserTool extends BrushTool {
|
|||
}
|
||||
}
|
||||
|
||||
// SprayTool implements a spray can tool.
|
||||
export class SprayTool implements Tool {
|
||||
private active: boolean
|
||||
private lastX: number
|
||||
|
@ -192,6 +202,7 @@ export class SprayTool implements Tool {
|
|||
}
|
||||
}
|
||||
|
||||
// FillTool implements a flood fill tool.
|
||||
export class FillTool implements Tool {
|
||||
private active: boolean
|
||||
isActive(): boolean {
|
||||
|
@ -239,6 +250,7 @@ export class FillTool implements Tool {
|
|||
}
|
||||
}
|
||||
|
||||
// PickerTool allows picking the color index from the canvas.
|
||||
export class PickerTool implements Tool {
|
||||
private active: boolean
|
||||
isActive(): boolean {
|
||||
|
@ -263,6 +275,7 @@ export class PickerTool implements Tool {
|
|||
}
|
||||
}
|
||||
|
||||
// SelectionTool allows selecting an area of the canvas.
|
||||
export class SelectionTool implements Tool {
|
||||
private active: boolean
|
||||
private startX: number
|
||||
|
@ -332,6 +345,7 @@ export class SelectionTool implements Tool {
|
|||
}
|
||||
}
|
||||
|
||||
// MagicWandTool implements a magic wand tool.
|
||||
export class MagicWandTool implements Tool {
|
||||
private active: boolean
|
||||
|
||||
|
@ -385,6 +399,7 @@ export class MagicWandTool implements Tool {
|
|||
}
|
||||
}
|
||||
|
||||
// MoveTool implements a tool to move pixels within a selection.
|
||||
export class MoveTool implements Tool {
|
||||
private active: boolean
|
||||
private startX: number
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// UndoableStack provides an undo/redo system.
|
||||
export class UndoableStack<T> {
|
||||
private target: T;
|
||||
private stack: Undoable<T>[] = [];
|
||||
|
@ -97,11 +98,13 @@ export class UndoableStack<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// Undoable is an interface for undoable actions stored in the stack.
|
||||
export interface Undoable<T> {
|
||||
apply(t: T): void;
|
||||
unapply(t: T): void;
|
||||
}
|
||||
|
||||
// UndoableGroup is a group of undoable actions. This is used to group multiple actions, such as drawing with a pen, into one undoable action.
|
||||
export class UndoableGroup<T> {
|
||||
private items: Undoable<T>[];
|
||||
|
||||
|
|
Loading…
Reference in New Issue