commit 6f0ac057be378dc1f270cd4e5eca5440bb8590c6 Author: Nathan Kunicki Date: Fri Nov 17 23:53:24 2017 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d321655 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/* +static/js/* \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..ba822ce --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + BittMapp + + + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..58eaba6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "bittmapp", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "typescript": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.1.tgz", + "integrity": "sha1-7znN6ierrAtQAkLWcmq5DgyEZjE=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..07fa868 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "bittmapp", + "version": "0.0.1", + "description": "BittMapp - A bitmap image editor", + "scripts": { + "build": "tsc" + }, + "author": "Nathan Kunicki ", + "license": "MIT", + "devDependencies": { + "typescript": "^2.6.1" + } +} diff --git a/src/bittmappeditor.ts b/src/bittmappeditor.ts new file mode 100644 index 0000000..e7fd888 --- /dev/null +++ b/src/bittmappeditor.ts @@ -0,0 +1,228 @@ +interface BittMappEditorConstructorOptions { + canvas: HTMLCanvasElement, + canvasWidth: number, + canvasHeight: number, + width: number, + height: number +} + + +class BittMappEditor { + + + public canvasWidth: number; + public canvasHeight: number; + + private _canvas: HTMLCanvasElement; + private _context: CanvasRenderingContext2D; + + private _width: number; + private _height: number; + private _scale: number; + private _deviceRatio: number; + + private _pixelWidth: number = 0; + private _pixelHeight: number = 0; + + private _mouseDown: boolean = false; + private _mouseButton: number; + + private _data: Uint8Array; + + + constructor (options: BittMappEditorConstructorOptions) { + + options = options || {}; + + if (options.canvas) { + this._canvas = options.canvas; + } else { + throw new Error("BittMappEditor must be initialized with a canvas."); + } + + if (options.canvasWidth) { + this.canvasWidth = options.canvasWidth; + } else { + throw new Error("BittMappEditor must be constructed with a canvasWidth."); + } + + if (options.canvasHeight) { + this.canvasHeight = options.canvasHeight; + } else { + throw new Error("BittMappEditor must be constructed with a canvasHeight."); + } + + if (options.width) { + this._width = options.width; + } else { + throw new Error("BittMappEditor must be constructed with a width."); + } + + if (options.height) { + this._height = options.height; + } else { + throw new Error("BittMappEditor must be constructed with a height."); + } + + this._context = this._canvas.getContext("2d"); + + const deviceRatio: number = window.devicePixelRatio, + backingStoreRatio: number = (this._context as any).backingStorePixelRatio || 1; + + this._scale = deviceRatio / backingStoreRatio; + this._deviceRatio = deviceRatio; + + this._canvas.width = this.canvasWidth * this._scale; + this._canvas.height = this.canvasHeight * this._scale; + this._canvas.style.width = `${this.canvasWidth}px`; + this._canvas.style.height = `${this.canvasHeight}px`; + + this._context.setTransform(this._scale, 0, 0, this._scale, 0, 0); + + this.resize(); + + this._canvas.addEventListener("contextmenu", (event) => { + event.preventDefault(); + }); + + this._canvas.addEventListener("mousedown", (event) => { + this._mouseDown = true; + this._mouseButton = event.button; + this._handleMouseEvent(event, this._mouseButton); + }); + + this._canvas.addEventListener("mousemove", (event) => { + if (this._mouseDown) { + this._handleMouseEvent(event, this._mouseButton); + } + }); + + this._canvas.addEventListener("mouseup", (event) => { + this._mouseDown = false; + }); + + } + + + setPixel (x: number, y: number) { + const byte: number = ((y * 4) + Math.floor(x / 8)), + mask: number = 1 << (x % 8); + this._data[byte] = this._data[byte] |= mask; + } + + + unsetPixel (x: number, y: number) { + const byte: number = ((y * 4) + Math.floor(x / 8)), + mask: number = 1 << (x % 8); + this._data[byte] = this._data[byte] &= ~mask; + } + + + resize (width: number = this._width, height: number = this._height) { + this._data = new Uint8Array((width / 8) * height); + this._pixelWidth = this.canvasWidth / this._width; + this._pixelHeight = this.canvasHeight / this._height; + this._redraw(); + } + + + set height (height: number) { + this.resize(this._width, height); + } + + + set width (width: number) { + this.resize(width, this._height); + } + + + _handleMouseEvent (event: MouseEvent, button: number) { + + const pixelX: number = Math.floor(event.offsetX / this._pixelWidth), + pixelY: number = Math.floor(event.offsetY / this._pixelHeight); + + if (button === 0) { + this.setPixel(pixelX, pixelY); + } else if (button === 2) { + this.unsetPixel(pixelX, pixelY); + } + + this._redraw(); + + } + + + _redraw () { + this._drawGrid(); + this._drawPixels(); + } + + + _drawGrid () { + + this._context.fillStyle = "#FFFFFF"; + this._context.strokeStyle = "#FF0000"; + this._context.fillRect(0, 0, this.canvasWidth, this.canvasHeight); + + this._context.strokeStyle = "#FF0000"; + this._context.beginPath(); + this._context.moveTo(0, 0); + this._context.lineTo(this.canvasWidth, 0); + this._context.stroke(); + + this._context.beginPath(); + this._context.moveTo(0, 0); + this._context.lineTo(0, this.canvasHeight); + this._context.stroke(); + + for (let i: number = 0; i < (this._width); i++) { + + const x: number = Math.floor((this.canvasWidth / this._width) * (i + 1)); + + this._context.beginPath(); + this._context.moveTo(x, 0); + this._context.lineTo(x, this.canvasHeight); + this._context.stroke(); + + } + + for (let j: number = 0; j < (this._height); j++) { + + const y: number = Math.floor((this.canvasHeight / this._height) * (j + 1)); + + this._context.beginPath(); + this._context.moveTo(0, y); + this._context.lineTo(this.canvasWidth, y); + this._context.stroke(); + + } + + } + + + _drawPixels () { + + this._context.fillStyle = "#000000"; + this._context.strokeStyle = "#FFFFFFF"; + + for (let i: number = 0; i < this._height; i++) { + for (let j: number = 0; j < (this._width / 8); j++) { + + let byte: number = this._data[(i * 4) + j]; + + for (let k: number = 0; k < 8; k++) { + if (byte & 1) { + + this._context.fillRect(0.5 + (((j * 8) + k) * this._pixelWidth), 0.5 + (i * this._pixelHeight), this._pixelWidth - 1, this._pixelHeight - 1); + + } + byte = byte >> 1; + } + + } + } + + } + + +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..74695d1 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,11 @@ +window.onload = function () { + + let editor: BittMappEditor = new BittMappEditor({ + canvas: document.getElementById("editor"), + canvasWidth: 640, + canvasHeight: 640, + width: 32, + height: 32 + }); + +}; \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000..f07174c --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,3 @@ +body { + background-color: #666666; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5ace558 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "none", + "strict": true, + "outDir": "static/js" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules" + ] + } \ No newline at end of file