From b91cb353cdcb37bebe4c2d0fae0ae4a3bbf2082a Mon Sep 17 00:00:00 2001 From: EscapedGibbon Date: Wed, 25 Sep 2024 11:27:08 +0200 Subject: [PATCH] feat: add setBlendedVisiblePixel to drawLineOnImage --- src/draw/drawLineOnImage.ts | 3 +- src/utils/setBlendedVisiblePixel.ts | 67 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/utils/setBlendedVisiblePixel.ts diff --git a/src/draw/drawLineOnImage.ts b/src/draw/drawLineOnImage.ts index 762d099bc..149463d7c 100644 --- a/src/draw/drawLineOnImage.ts +++ b/src/draw/drawLineOnImage.ts @@ -4,6 +4,7 @@ import { Image } from '../Image'; import { Point } from '../utils/geometry/points'; import { getDefaultColor } from '../utils/getDefaultColor'; import { getOutputImage } from '../utils/getOutputImage'; +import { setBlendedVisiblePixel } from '../utils/setBlendedVisiblePixel'; import checkProcessable from '../utils/validators/checkProcessable'; import { validateColor } from '../utils/validators/validators'; @@ -56,7 +57,7 @@ export function drawLineOnImage( Math.round(origin.column + to.column), Math.round(origin.row + to.row), (column: number, row: number) => { - newImage.setVisiblePixel(column, row, color); + setBlendedVisiblePixel(newImage, column, row, { color }); }, ); return newImage; diff --git a/src/utils/setBlendedVisiblePixel.ts b/src/utils/setBlendedVisiblePixel.ts new file mode 100644 index 000000000..7ae133e91 --- /dev/null +++ b/src/utils/setBlendedVisiblePixel.ts @@ -0,0 +1,67 @@ +import { Image } from '../Image'; +import { Mask } from '../Mask'; + +import { getDefaultColor } from './getDefaultColor'; +import { assert } from './validators/assert'; + +export interface SetBlendedVisiblePixelOptions { + /** + * Color with which to blend the image pixel. + * @default `'Opaque black'`. + */ + color?: number[]; +} + +/** + * Blend the given pixel with the pixel at the specified location in the image if the pixel is in image's bounds. + * @param image - The image with which to blend. + * @param column - Column of the target pixel. + * @param row - Row of the target pixel. + * @param options - Set blended pixel options. + */ +export function setBlendedVisiblePixel( + image: Image | Mask, + column: number, + row: number, + options: SetBlendedVisiblePixelOptions = {}, +) { + const { color = getDefaultColor(image) } = options; + + if (!image.alpha) { + image.setVisiblePixel(column, row, color); + } else { + assert(image instanceof Image); + + const sourceAlpha = color.at(-1) as number; + + if (sourceAlpha === image.maxValue) { + image.setVisiblePixel(column, row, color); + return; + } + + const targetAlpha = image.getValue(column, row, image.channels - 1); + + const newAlpha = + sourceAlpha + targetAlpha * (1 - sourceAlpha / image.maxValue); + if (column >= 0 && column < image.width && row >= 0 && row < image.height) { + image.setValue(column, row, image.channels - 1, newAlpha); + } + for (let component = 0; component < image.components; component++) { + const sourceComponent = color[component]; + const targetComponent = image.getValue(column, row, component); + + const newComponent = + (sourceComponent * sourceAlpha + + targetComponent * targetAlpha * (1 - sourceAlpha / image.maxValue)) / + newAlpha; + if ( + column >= 0 && + column < image.width && + row >= 0 && + row < image.height + ) { + image.setValue(column, row, component, newComponent); + } + } + } +}