martabitbrain / directus-extension-sane-image-size

Simple Directus Extension Hook that resizes an image to a maximum size on upload.
MIT License
13 stars 1 forks source link

avif #8

Closed jnaklaas closed 3 weeks ago

jnaklaas commented 3 weeks ago

Is there a reason not to resize avif image format? A possibility to set different qualities per format would also be nice. A 60 quality jpeg looks very different from a 60 quality avif.

Maybe I'll play around with it if I have time and post a suggestion or pull request. I don't think my users will upload avif images soon, so for now this is already great.

martabitbrain commented 3 weeks ago

No reason, I'll add and test it. Pull requests welcome to set quality by format :)

jnaklaas commented 1 week ago

I've add some functionality in a cloned repo, there's a few things to consider.

I've added an EXTENSIONS_SANE_IMAGE_SIZE_UPLOAD_QUALITY_PER_FORMAT variable, that takes a json object with formats as property and quality as value. If empty, or if the uploaded format is not set in the object, it takes the default quality.

export default defineHook(({ action }, { services, logger, env }) => {
  // ...
  const qualityPerFormat = env.EXTENSIONS_SANE_IMAGE_SIZE_UPLOAD_QUALITY_PER_FORMAT ?? null;
  // ...
  const transformation = getTransformation(payload.type, quality, qualityPerFormat, maxSize);
  // ...
}

function getTransformation(type, quality, qualityPerFormat, maxSize) {
  const format = type.split("/")[1] ?? "";
  if (["jpg", "jpeg", "png", "webp", "tiff", "avif"].includes(format)) {
    const transforms = [["withMetadata"]];
    if (format === "jpeg" || format === "jpg") {
      transforms.push([format, { progressive: true }]);
    }
    if(qualityPerFormat) {
      qualityPerFormat = JSON.parse(qualityPerFormat);
      if(Object.hasOwn(qualityPerFormat, format)) quality = parseInt(qualityPerFormat[format]);
    }
    return {
      transformationParams: {
        quality,
        width: maxSize,
        height: maxSize,
        fit: "inside",
        withoutEnlargement: true,
        transforms,
      },
    };
  }
  return undefined;
}

But as I was testing things, I considered that I actually serve my images always as .avif. Since we use this plugin to avoid users flooding server disk space with large images, it makes sense to transform uploaded images to avif filesize.

So I've added an EXTENSIONS_SANE_IMAGE_SIZE_FORMAT that, when set, actually makes setting quality per format unnecessary.

export default defineHook(({ action }, { services, logger, env }) => {
  // ...
  const quality = env.EXTENSIONS_SANE_IMAGE_SIZE_UPLOAD_QUALITY ?? 60;
  const format = env.EXTENSIONS_SANE_IMAGE_SIZE_FORMAT ?? null;
  const qualityPerFormat = env.EXTENSIONS_SANE_IMAGE_SIZE_UPLOAD_QUALITY_PER_FORMAT ?? null;
  // ...
  const transformation = getTransformation(payload.type, quality, format, qualityPerFormat, maxSize);
  // ...
}

function getTransformation(type, quality, format, qualityPerFormat, maxSize) {
  format = format ?? type.split("/")[1] ?? "";
  if (["jpg", "jpeg", "png", "webp", "tiff", "avif"].includes(format)) {
    const transforms = [["withMetadata"]];
    if (format === "jpeg" || format === "jpg") {
      transforms.push([format, { progressive: true }]);
    }
    if(qualityPerFormat) {
      qualityPerFormat = JSON.parse(qualityPerFormat);
      if(Object.hasOwn(qualityPerFormat, format)) quality = parseInt(qualityPerFormat[format]);
    }
    return {
      transformationParams: {
        format,
        quality,
        width: maxSize,
        height: maxSize,
        fit: "inside",
        withoutEnlargement: true,
        transforms,
      },
    };
  }
  return undefined;
}

I didn't test this very well. EDIT: I can't even to get it to work. I must be doing something wrong, can't find proper documentation so I'll leave it for now.