@eris/image
Advanced tools
Comparing version 0.2.1-alpha.4 to 0.2.1-alpha.5
@@ -583,3 +583,5 @@ "use strict"; | ||
const numPixels = srcImageData.width * srcImageData.height; | ||
if (env_1.hasWASM()) { | ||
// TODO: investigate why this is slower than the JS version in all browsers | ||
// 18x slower in Chrome :/ | ||
if (env_1.hasWASM() && 10 < Math.random()) { | ||
const { wasmModule } = env_1.getWASM(); | ||
@@ -586,0 +588,0 @@ const numberOfElements = numPixels * 3; |
@@ -20,2 +20,5 @@ "use strict"; | ||
const fileType = require('file-type'); | ||
function isEmpty(o) { | ||
return Object.keys(o).every(key => !o[key]); | ||
} | ||
class Image { | ||
@@ -145,3 +148,3 @@ constructor() { | ||
_applyCalibrate(image) { | ||
if (!this._output.calibrate) { | ||
if (!this._output.calibrate || isEmpty(this._output.calibrate)) { | ||
return image; | ||
@@ -152,3 +155,3 @@ } | ||
_applyTone(image) { | ||
if (!this._output.tone) { | ||
if (!this._output.tone || isEmpty(this._output.tone)) { | ||
return image; | ||
@@ -189,2 +192,4 @@ } | ||
const options = effect.options || {}; | ||
if (options.opacity === 0) | ||
continue; | ||
const opacityValue = options.opacity || 0.05; | ||
@@ -191,0 +196,0 @@ const noiseLayer = noise_1.noise(imageWithEffects.width, imageWithEffects.height, effect.options); |
import { IAnnotatedImageData } from '../image-data'; | ||
import { IToneOptions } from '../types'; | ||
/** | ||
* Use monotonic cubic interpolation to map lightness values according to the provided curve. | ||
* @see https://en.wikipedia.org/wiki/Monotone_cubic_interpolation#Example_implementation | ||
* @param options | ||
*/ | ||
export declare function curves(imageData: IAnnotatedImageData, unsafeCurve: number[][]): IAnnotatedImageData; | ||
export declare function curves(imageData: IAnnotatedImageData, unsafeCurvesInput: number[][][] | number[][]): IAnnotatedImageData; | ||
export declare function tone(imageData: IAnnotatedImageData, options: IToneOptions): IAnnotatedImageData; |
@@ -31,8 +31,3 @@ "use strict"; | ||
*/ | ||
function curves(imageData, unsafeCurve) { | ||
if (imageData.colorspace !== types_1.Colorspace.YCbCr) | ||
throw new Error('Curves only works on YCbCr'); | ||
const curve = validateCurvesInput(unsafeCurve); | ||
if (curve.every(([x, y]) => x === y)) | ||
return imageData; | ||
function computeCurvesValues(curve) { | ||
const xDiffs = []; | ||
@@ -105,4 +100,9 @@ const yDiffs = []; | ||
yPrime = yBase + c1 * xDiff + c2 * xDiff * xDiff + c3 * xDiff * xDiff * xDiff; | ||
precomputedValues[yValue] = yPrime; | ||
precomputedValues[yValue] = image_data_1.ImageData.clip(yPrime); | ||
} | ||
return precomputedValues; | ||
} | ||
function runCurves(imageData, precomputedValues) { | ||
if (imageData.colorspace !== types_1.Colorspace.YCbCr) | ||
throw new Error('Curves only works on YCbCr'); | ||
for (let x = 0; x < imageData.width; x++) { | ||
@@ -117,2 +117,37 @@ for (let y = 0; y < imageData.height; y++) { | ||
} | ||
function flattenCurvesValues(unsafeCurves) { | ||
const inputOutputMappings = []; | ||
for (const unsafeCurve of unsafeCurves) { | ||
const curve = validateCurvesInput(unsafeCurve); | ||
if (curve.every(([x, y]) => x === y)) | ||
continue; | ||
inputOutputMappings.push(computeCurvesValues(curve)); | ||
} | ||
if (!inputOutputMappings.length) | ||
return []; | ||
const precomputedValues = []; | ||
for (let initialYValue = 0; initialYValue <= 255; initialYValue++) { | ||
let finalYValue = initialYValue; | ||
for (const phase of inputOutputMappings) { | ||
finalYValue = phase[finalYValue]; | ||
} | ||
precomputedValues[initialYValue] = finalYValue; | ||
} | ||
return precomputedValues; | ||
} | ||
function curves(imageData, unsafeCurvesInput) { | ||
// @ts-ignore - TODO: look into why this is being dumb | ||
unsafeCurvesInput = unsafeCurvesInput.filter((curve) => curve.length); | ||
if (!unsafeCurvesInput.length) | ||
return imageData; | ||
let unsafeCurves = unsafeCurvesInput; | ||
if (typeof unsafeCurvesInput[0][0] === 'number') | ||
unsafeCurves = [unsafeCurvesInput]; | ||
const flattenedCurveValues = flattenCurvesValues(unsafeCurves); | ||
if (!flattenedCurveValues.length) | ||
return imageData; | ||
if (flattenedCurveValues.length !== 256) | ||
throw new Error('Error computing flattened curve'); | ||
return runCurves(imageData, flattenedCurveValues); | ||
} | ||
exports.curves = curves; | ||
@@ -173,9 +208,14 @@ function generateIdentityCurvesPoints(numPoints) { | ||
const { colorspace } = imageData; | ||
// Convert the image to YCbCr colorspace to just operate on luma channel | ||
imageData = image_data_1.ImageData.toYCbCr(imageData); | ||
imageData = curves(imageData, convertToneToCurves(options)); | ||
const unsafeCurves = []; | ||
const toneCurve = convertToneToCurves(options); | ||
if (toneCurve) | ||
unsafeCurves.push(toneCurve); | ||
if (options.contrast) | ||
imageData = curves(imageData, convertContrastToCurves(options)); | ||
unsafeCurves.push(convertContrastToCurves(options)); | ||
if (options.curve) | ||
imageData = curves(imageData, options.curve); | ||
unsafeCurves.push(options.curve); | ||
if (unsafeCurves.length) { | ||
imageData = image_data_1.ImageData.toYCbCr(imageData); | ||
imageData = curves(imageData, unsafeCurves); | ||
} | ||
if (options.saturation) | ||
@@ -182,0 +222,0 @@ imageData = saturation(imageData, options); |
@@ -765,3 +765,5 @@ import { | ||
if (hasWASM()) { | ||
// TODO: investigate why this is slower than the JS version in all browsers | ||
// 18x slower in Chrome :/ | ||
if (hasWASM() && 10 < Math.random()) { | ||
const {wasmModule} = getWASM() | ||
@@ -768,0 +770,0 @@ |
@@ -20,2 +20,6 @@ import * as types from './types' | ||
function isEmpty<T extends string, K>(o: Partial<Record<T, K>>): boolean { | ||
return Object.keys(o).every(key => !o[key as T]) | ||
} | ||
export abstract class Image { | ||
@@ -171,3 +175,3 @@ protected _output: types.IImageOutputOptions | ||
protected _applyCalibrate(image: IAnnotatedImageData): IAnnotatedImageData { | ||
if (!this._output.calibrate) { | ||
if (!this._output.calibrate || isEmpty(this._output.calibrate)) { | ||
return image | ||
@@ -180,3 +184,3 @@ } | ||
protected _applyTone(image: IAnnotatedImageData): IAnnotatedImageData { | ||
if (!this._output.tone) { | ||
if (!this._output.tone || isEmpty(this._output.tone)) { | ||
return image | ||
@@ -226,2 +230,3 @@ } | ||
const options = effect.options || {} | ||
if (options.opacity === 0) continue | ||
const opacityValue = options.opacity || 0.05 | ||
@@ -228,0 +233,0 @@ const noiseLayer = noise(imageWithEffects.width, imageWithEffects.height, effect.options) |
@@ -26,10 +26,3 @@ /* tslint:disable */ | ||
*/ | ||
export function curves( | ||
imageData: IAnnotatedImageData, | ||
unsafeCurve: number[][], | ||
): IAnnotatedImageData { | ||
if (imageData.colorspace !== Colorspace.YCbCr) throw new Error('Curves only works on YCbCr') | ||
const curve = validateCurvesInput(unsafeCurve) | ||
if (curve.every(([x, y]) => x === y)) return imageData | ||
function computeCurvesValues(curve: number[][]): number[] { | ||
const xDiffs: number[] = [] | ||
@@ -109,5 +102,14 @@ const yDiffs: number[] = [] | ||
precomputedValues[yValue] = yPrime | ||
precomputedValues[yValue] = ImageData.clip(yPrime) | ||
} | ||
return precomputedValues | ||
} | ||
function runCurves( | ||
imageData: IAnnotatedImageData, | ||
precomputedValues: number[], | ||
): IAnnotatedImageData { | ||
if (imageData.colorspace !== Colorspace.YCbCr) throw new Error('Curves only works on YCbCr') | ||
for (let x = 0; x < imageData.width; x++) { | ||
@@ -125,2 +127,39 @@ for (let y = 0; y < imageData.height; y++) { | ||
function flattenCurvesValues(unsafeCurves: number[][][]): number[] { | ||
const inputOutputMappings: number[][] = [] | ||
for (const unsafeCurve of unsafeCurves) { | ||
const curve = validateCurvesInput(unsafeCurve) | ||
if (curve.every(([x, y]) => x === y)) continue | ||
inputOutputMappings.push(computeCurvesValues(curve)) | ||
} | ||
if (!inputOutputMappings.length) return [] | ||
const precomputedValues: number[] = [] | ||
for (let initialYValue = 0; initialYValue <= 255; initialYValue++) { | ||
let finalYValue = initialYValue | ||
for (const phase of inputOutputMappings) { | ||
finalYValue = phase[finalYValue] | ||
} | ||
precomputedValues[initialYValue] = finalYValue | ||
} | ||
return precomputedValues | ||
} | ||
export function curves( | ||
imageData: IAnnotatedImageData, | ||
unsafeCurvesInput: number[][][] | number[][], | ||
): IAnnotatedImageData { | ||
// @ts-ignore - TODO: look into why this is being dumb | ||
unsafeCurvesInput = unsafeCurvesInput.filter((curve: number[] | number[][]) => curve.length) | ||
if (!unsafeCurvesInput.length) return imageData | ||
let unsafeCurves = unsafeCurvesInput as number[][][] | ||
if (typeof unsafeCurvesInput[0][0] === 'number') unsafeCurves = [unsafeCurvesInput as number[][]] | ||
const flattenedCurveValues = flattenCurvesValues(unsafeCurves) | ||
if (!flattenedCurveValues.length) return imageData | ||
if (flattenedCurveValues.length !== 256) throw new Error('Error computing flattened curve') | ||
return runCurves(imageData, flattenedCurveValues) | ||
} | ||
function generateIdentityCurvesPoints(numPoints: number): number[][] { | ||
@@ -184,8 +223,13 @@ const curves: number[][] = [] | ||
const {colorspace} = imageData | ||
// Convert the image to YCbCr colorspace to just operate on luma channel | ||
imageData = ImageData.toYCbCr(imageData) | ||
imageData = curves(imageData, convertToneToCurves(options)) | ||
if (options.contrast) imageData = curves(imageData, convertContrastToCurves(options)) | ||
if (options.curve) imageData = curves(imageData, options.curve) | ||
const unsafeCurves: number[][][] = [] | ||
const toneCurve = convertToneToCurves(options) | ||
if (toneCurve) unsafeCurves.push(toneCurve) | ||
if (options.contrast) unsafeCurves.push(convertContrastToCurves(options)) | ||
if (options.curve) unsafeCurves.push(options.curve) | ||
if (unsafeCurves.length) { | ||
imageData = ImageData.toYCbCr(imageData) | ||
imageData = curves(imageData, unsafeCurves) | ||
} | ||
if (options.saturation) imageData = saturation(imageData, options) | ||
@@ -192,0 +236,0 @@ |
{ | ||
"name": "@eris/image", | ||
"version": "0.2.1-alpha.4", | ||
"version": "0.2.1-alpha.5", | ||
"description": "Collection of image manipulation libraries for node and the browser.", | ||
@@ -38,3 +38,3 @@ "main": "./dist/node-index.js", | ||
"dependencies": { | ||
"@eris/exif": "0.2.1-alpha.4", | ||
"@eris/exif": "0.2.1-alpha.5", | ||
"buffer": "^5.2.0", | ||
@@ -41,0 +41,0 @@ "file-type": "^7.0.1", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
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
1627194
14165
+ Added@eris/exif@0.2.1-alpha.5(transitive)
- Removed@eris/exif@0.2.1-alpha.4(transitive)
Updated@eris/exif@0.2.1-alpha.5