image-js / image-js-typescript

Temporary repository to work on the migration of image-js to TypeScript
https://image-js.github.io/image-js-typescript/
MIT License
5 stars 5 forks source link

add multiply/divide functions #457

Closed EscapedGibbon closed 3 months ago

EscapedGibbon commented 3 months ago

It will probably look something like this:

export function multiply(
  image: Image,
  value: number,
  options: MultiplyOptions = {},
) {
  const {
    channels = new Array(image.components).fill(0).map((value, index) => index),
  } = options;
  if (channels.length === 0) {
    for (let i = 0; i < image.channels; i++) {
      channels.push(i);
    }
  }
  validateChannels(channels, image);

  for (let channel = 0; channel < channels.length; channel++) {
    for (let row = 0; row < image.height; row++) {
      for (let column = 0; column < image.width; column++) {
        const newIntensity =
          image.getValue(column, row, channels[channel]) * value;
        image.setClampedValue(column, row, channel, newIntensity);
      }
    }
  }
}

For division it will be the same but with a different sign and additional check for value === 0. Also by default alpha channel is not multiplied. @stropitek WDYT?

stropitek commented 3 months ago

If the channel option is explicitly set to an empty array, I would expect it to just return the input image or throw. I would return the input image. If passed directly by the user, that was probably unintentional and a mistake, but that channel option could also be derived / computed.

Wouldn't it make sense to provide the usual out option to this method and by default, to create a new image instead of modifying the input?

EscapedGibbon commented 3 months ago
export function multiply(
  image: Image,
  value: number,
  options: MultiplyOptions = {},
) {
  const {
    channels = new Array(image.components).fill(0).map((value, index) => index),
  } = options;
  validateChannels(channels, image);

  const newImage = getOutputImage(image, options, { clone: true });
  if (channels.length === 0) {
    return newImage;
  }
  for (let channel = 0; channel < channels.length; channel++) {
    for (let row = 0; row < newImage.height; row++) {
      for (let column = 0; column < newImage.width; column++) {
        const newIntensity =
          newImage.getValue(column, row, channels[channel]) * value;
        newImage.setClampedValue(column, row, channel, newIntensity);
      }
    }
  }
  return newImage;
}
stropitek commented 3 months ago

At this point you can open up a PR, they are made for reviewing code so i'll be easier for us.