@vibrant/image-browser
Advanced tools
Comparing version 4.0.0-alpha.3 to 4.0.0-alpha.4
@@ -1,5 +0,5 @@ | ||
import { ImageData as VibrantImageData, ImageSource, ImageBase } from '@vibrant/image'; | ||
import { ImageBase, ImageSource, ImageData as VibrantImageData } from '@vibrant/image'; | ||
export default class BrowserImage extends ImageBase { | ||
image: HTMLImageElement; | ||
export declare class BrowserImage extends ImageBase { | ||
image: HTMLImageElement | undefined; | ||
private _canvas; | ||
@@ -9,4 +9,8 @@ private _context; | ||
private _height; | ||
private _getCanvas; | ||
private _getContext; | ||
private _getWidth; | ||
private _getHeight; | ||
private _initCanvas; | ||
load(image: ImageSource): Promise<ImageBase>; | ||
load(image: ImageSource): Promise<this>; | ||
clear(): void; | ||
@@ -13,0 +17,0 @@ update(imageData: VibrantImageData): void; |
@@ -12,4 +12,31 @@ import { ImageBase } from "@vibrant/image"; | ||
class BrowserImage extends ImageBase { | ||
_getCanvas() { | ||
if (!this._canvas) { | ||
throw new Error("Canvas is not initialized"); | ||
} | ||
return this._canvas; | ||
} | ||
_getContext() { | ||
if (!this._context) { | ||
throw new Error("Context is not initialized"); | ||
} | ||
return this._context; | ||
} | ||
_getWidth() { | ||
if (!this._width) { | ||
throw new Error("Width is not initialized"); | ||
} | ||
return this._width; | ||
} | ||
_getHeight() { | ||
if (!this._height) { | ||
throw new Error("Height is not initialized"); | ||
} | ||
return this._height; | ||
} | ||
_initCanvas() { | ||
const img = this.image; | ||
if (!img) { | ||
throw new Error("Image is not initialized"); | ||
} | ||
const canvas = this._canvas = document.createElement("canvas"); | ||
@@ -54,3 +81,3 @@ const context = canvas.getContext("2d"); | ||
img.onload = onImageLoad; | ||
img.onerror = (e) => reject(new Error(`Fail to load image: ${src}`)); | ||
img.onerror = (_e) => reject(new Error(`Fail to load image: ${src}`)); | ||
} | ||
@@ -60,25 +87,32 @@ }); | ||
clear() { | ||
this._context.clearRect(0, 0, this._width, this._height); | ||
this._getContext().clearRect(0, 0, this._getWidth(), this._getHeight()); | ||
} | ||
update(imageData) { | ||
this._context.putImageData(imageData, 0, 0); | ||
this._getContext().putImageData(imageData, 0, 0); | ||
} | ||
getWidth() { | ||
return this._width; | ||
return this._getWidth(); | ||
} | ||
getHeight() { | ||
return this._height; | ||
return this._getHeight(); | ||
} | ||
resize(targetWidth, targetHeight, ratio) { | ||
const { _canvas: canvas, _context: context, image: img } = this; | ||
this._width = canvas.width = targetWidth; | ||
this._height = canvas.height = targetHeight; | ||
context.scale(ratio, ratio); | ||
context.drawImage(img, 0, 0); | ||
if (!this.image) { | ||
throw new Error("Image is not initialized"); | ||
} | ||
this._width = this._getCanvas().width = targetWidth; | ||
this._height = this._getCanvas().height = targetHeight; | ||
this._getContext().scale(ratio, ratio); | ||
this._getContext().drawImage(this.image, 0, 0); | ||
} | ||
getPixelCount() { | ||
return this._width * this._height; | ||
return this._getWidth() * this._getHeight(); | ||
} | ||
getImageData() { | ||
return this._context.getImageData(0, 0, this._width, this._height); | ||
return this._getContext().getImageData( | ||
0, | ||
0, | ||
this._getWidth(), | ||
this._getHeight() | ||
); | ||
} | ||
@@ -92,4 +126,4 @@ remove() { | ||
export { | ||
BrowserImage as default | ||
BrowserImage | ||
}; | ||
//# sourceMappingURL=index.js.map |
109
package.json
{ | ||
"name": "@vibrant/image-browser", | ||
"version": "4.0.0-alpha.3", | ||
"description": "Browser vibrant ImageClass implementation", | ||
"scripts": { | ||
"build": "vite build", | ||
"test:lib": "vitest run", | ||
"test:lib:watch": "vitest watch" | ||
}, | ||
"type": "module", | ||
"types": "dist/esm/index.d.ts", | ||
"main": "dist/cjs/index.cjs", | ||
"module": "dist/esm/index.js", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/esm/index.d.ts", | ||
"default": "./dist/esm/index.js" | ||
}, | ||
"require": { | ||
"types": "./dist/cjs/index.d.cts", | ||
"default": "./dist/cjs/index.cjs" | ||
} | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"sideEffects": false, | ||
"files": [ | ||
"dist", | ||
"src" | ||
], | ||
"author": { | ||
"name": "akfish", | ||
"email": "akfish@gmail.com" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/akfish/node-vibrant/issues" | ||
}, | ||
"homepage": "https://github.com/akfish/node-vibrant", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@vibrant/image": "^4.0.0-alpha.3" | ||
}, | ||
"devDependencies": { | ||
"@tanstack/config": "^0.14.2", | ||
"@types/node": "^18.15.3", | ||
"jsdom": "^25.0.1", | ||
"typescript": "^4.5.2", | ||
"vite": "^6.0.3", | ||
"vitest": "^2.1.8" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"gitHead": "2cdb48edcf5398024bd18a6ed07f2ec50a20030b" | ||
"name": "@vibrant/image-browser", | ||
"version": "4.0.0-alpha.4", | ||
"description": "Browser vibrant ImageClass implementation", | ||
"scripts": { | ||
"build": "vite build", | ||
"test:lib": "vitest run", | ||
"test:lib:watch": "vitest watch", | ||
"test:eslint": "eslint ./src ./__tests__" | ||
}, | ||
"type": "module", | ||
"types": "dist/esm/index.d.ts", | ||
"main": "dist/cjs/index.cjs", | ||
"module": "dist/esm/index.js", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/esm/index.d.ts", | ||
"default": "./dist/esm/index.js" | ||
}, | ||
"require": { | ||
"types": "./dist/cjs/index.d.cts", | ||
"default": "./dist/cjs/index.cjs" | ||
} | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"sideEffects": false, | ||
"files": [ | ||
"dist", | ||
"src" | ||
], | ||
"author": { | ||
"name": "akfish", | ||
"email": "akfish@gmail.com" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/akfish/node-vibrant/issues" | ||
}, | ||
"homepage": "https://github.com/akfish/node-vibrant", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@vibrant/image": "^4.0.0-alpha.4" | ||
}, | ||
"devDependencies": { | ||
"@tanstack/config": "^0.14.2", | ||
"@types/node": "^18.15.3", | ||
"jsdom": "^25.0.1", | ||
"typescript": "^4.5.2", | ||
"vite": "^6.0.3", | ||
"vitest": "^2.1.8" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"gitHead": "87c300667e339a8bc4f463d058c0e960f8d18ba9" | ||
} |
# @vibrant/image-browser | ||
Browser vibrant ImageClass implementation | ||
250
src/index.ts
@@ -1,124 +0,170 @@ | ||
import { | ||
ImageOptions, | ||
ImageData as VibrantImageData, | ||
ImageSource, | ||
ImageCallback, | ||
ImageBase, | ||
import { ImageBase, ImageCallback, ImageOptions } from "@vibrant/image"; | ||
import type { | ||
ImageSource, | ||
ImageData as VibrantImageData, | ||
} from "@vibrant/image"; | ||
function isRelativeUrl(url: string): boolean { | ||
const u = new URL(url, location.href); | ||
return ( | ||
u.protocol === location.protocol && | ||
u.host === location.host && | ||
u.port === location.port | ||
); | ||
const u = new URL(url, location.href); | ||
return ( | ||
u.protocol === location.protocol && | ||
u.host === location.host && | ||
u.port === location.port | ||
); | ||
} | ||
function isSameOrigin(a: string, b: string): boolean { | ||
const ua = new URL(a); | ||
const ub = new URL(b); | ||
const ua = new URL(a); | ||
const ub = new URL(b); | ||
// https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy | ||
return ( | ||
ua.protocol === ub.protocol && | ||
ua.hostname === ub.hostname && | ||
ua.port === ub.port | ||
); | ||
// https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy | ||
return ( | ||
ua.protocol === ub.protocol && | ||
ua.hostname === ub.hostname && | ||
ua.port === ub.port | ||
); | ||
} | ||
export default class BrowserImage extends ImageBase { | ||
image: HTMLImageElement; | ||
private _canvas: HTMLCanvasElement; | ||
private _context: CanvasRenderingContext2D; | ||
private _width: number; | ||
private _height: number; | ||
private _initCanvas(): void { | ||
const img = this.image; | ||
const canvas = (this._canvas = document.createElement("canvas")); | ||
const context = canvas.getContext("2d"); | ||
export class BrowserImage extends ImageBase { | ||
image: HTMLImageElement | undefined; | ||
private _canvas: HTMLCanvasElement | undefined; | ||
private _context: CanvasRenderingContext2D | undefined; | ||
private _width: number | undefined; | ||
private _height: number | undefined; | ||
if (!context) throw new ReferenceError("Failed to create canvas context"); | ||
private _getCanvas() { | ||
if (!this._canvas) { | ||
throw new Error("Canvas is not initialized"); | ||
} | ||
this._context = context; | ||
return this._canvas; | ||
} | ||
private _getContext() { | ||
if (!this._context) { | ||
throw new Error("Context is not initialized"); | ||
} | ||
canvas.className = "@vibrant/canvas"; | ||
canvas.style.display = "none"; | ||
return this._context; | ||
} | ||
private _getWidth() { | ||
if (!this._width) { | ||
throw new Error("Width is not initialized"); | ||
} | ||
this._width = canvas.width = img.width; | ||
this._height = canvas.height = img.height; | ||
return this._width; | ||
} | ||
private _getHeight() { | ||
if (!this._height) { | ||
throw new Error("Height is not initialized"); | ||
} | ||
context.drawImage(img, 0, 0); | ||
return this._height; | ||
} | ||
document.body.appendChild(canvas); | ||
} | ||
load(image: ImageSource): Promise<ImageBase> { | ||
let img: HTMLImageElement; | ||
let src: string; | ||
if (typeof image === "string") { | ||
img = document.createElement("img"); | ||
src = image; | ||
private _initCanvas(): void { | ||
const img = this.image; | ||
if (!img) { | ||
throw new Error("Image is not initialized"); | ||
} | ||
const canvas = (this._canvas = document.createElement("canvas")); | ||
const context = canvas.getContext("2d"); | ||
if (!isRelativeUrl(src) && !isSameOrigin(window.location.href, src)) { | ||
img.crossOrigin = "anonymous"; | ||
} | ||
if (!context) throw new ReferenceError("Failed to create canvas context"); | ||
img.src = src; | ||
} else if (image instanceof HTMLImageElement) { | ||
img = image; | ||
src = image.src; | ||
} else { | ||
return Promise.reject( | ||
new Error(`Cannot load buffer as an image in browser`) | ||
); | ||
} | ||
this.image = img; | ||
this._context = context; | ||
return new Promise<ImageBase>((resolve, reject) => { | ||
const onImageLoad = () => { | ||
this._initCanvas(); | ||
resolve(this); | ||
}; | ||
canvas.className = "@vibrant/canvas"; | ||
canvas.style.display = "none"; | ||
if (img.complete) { | ||
// Already loaded | ||
onImageLoad(); | ||
} else { | ||
img.onload = onImageLoad; | ||
img.onerror = (e) => reject(new Error(`Fail to load image: ${src}`)); | ||
} | ||
}); | ||
} | ||
clear(): void { | ||
this._context.clearRect(0, 0, this._width, this._height); | ||
} | ||
update(imageData: VibrantImageData): void { | ||
this._context.putImageData(imageData as ImageData, 0, 0); | ||
} | ||
getWidth(): number { | ||
return this._width; | ||
} | ||
getHeight(): number { | ||
return this._height; | ||
} | ||
resize(targetWidth: number, targetHeight: number, ratio: number): void { | ||
const { _canvas: canvas, _context: context, image: img } = this; | ||
this._width = canvas.width = img.width; | ||
this._height = canvas.height = img.height; | ||
this._width = canvas.width = targetWidth; | ||
this._height = canvas.height = targetHeight; | ||
context.drawImage(img, 0, 0); | ||
context.scale(ratio, ratio); | ||
context.drawImage(img, 0, 0); | ||
} | ||
getPixelCount(): number { | ||
return this._width * this._height; | ||
} | ||
getImageData(): ImageData { | ||
return this._context.getImageData(0, 0, this._width, this._height); | ||
} | ||
remove(): void { | ||
if (this._canvas && this._canvas.parentNode) { | ||
this._canvas.parentNode.removeChild(this._canvas); | ||
} | ||
} | ||
document.body.appendChild(canvas); | ||
} | ||
load(image: ImageSource): Promise<this> { | ||
let img: HTMLImageElement; | ||
let src: string; | ||
if (typeof image === "string") { | ||
img = document.createElement("img"); | ||
src = image; | ||
if (!isRelativeUrl(src) && !isSameOrigin(window.location.href, src)) { | ||
img.crossOrigin = "anonymous"; | ||
} | ||
img.src = src; | ||
} else if (image instanceof HTMLImageElement) { | ||
img = image; | ||
src = image.src; | ||
} else { | ||
return Promise.reject( | ||
new Error(`Cannot load buffer as an image in browser`), | ||
); | ||
} | ||
this.image = img; | ||
return new Promise<this>((resolve, reject) => { | ||
const onImageLoad = () => { | ||
this._initCanvas(); | ||
resolve(this); | ||
}; | ||
if (img.complete) { | ||
// Already loaded | ||
onImageLoad(); | ||
} else { | ||
img.onload = onImageLoad; | ||
img.onerror = (_e) => reject(new Error(`Fail to load image: ${src}`)); | ||
} | ||
}); | ||
} | ||
clear(): void { | ||
this._getContext().clearRect(0, 0, this._getWidth(), this._getHeight()); | ||
} | ||
update(imageData: VibrantImageData): void { | ||
this._getContext().putImageData(imageData as ImageData, 0, 0); | ||
} | ||
getWidth(): number { | ||
return this._getWidth(); | ||
} | ||
getHeight(): number { | ||
return this._getHeight(); | ||
} | ||
resize(targetWidth: number, targetHeight: number, ratio: number): void { | ||
if (!this.image) { | ||
throw new Error("Image is not initialized"); | ||
} | ||
this._width = this._getCanvas().width = targetWidth; | ||
this._height = this._getCanvas().height = targetHeight; | ||
this._getContext().scale(ratio, ratio); | ||
this._getContext().drawImage(this.image, 0, 0); | ||
} | ||
getPixelCount(): number { | ||
return this._getWidth() * this._getHeight(); | ||
} | ||
getImageData(): ImageData { | ||
return this._getContext().getImageData( | ||
0, | ||
0, | ||
this._getWidth(), | ||
this._getHeight(), | ||
); | ||
} | ||
remove(): void { | ||
if (this._canvas && this._canvas.parentNode) { | ||
this._canvas.parentNode.removeChild(this._canvas); | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
27607
412
4