Node-vibrant: [๊ธฐ๋Šฅ] webp ์ง€์›

์— ๋งŒ๋“  2017๋…„ 07์›” 07์ผ  ยท  12์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: Vibrant-Colors/node-vibrant

๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ์‰ฌ์šด ์ž‘์—…์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ webp ์ง€์›์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์€ ๊ฝค ๋ฉ‹์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ํ•ด์š”.

help wanted wontfix

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

fyi: ๋‹ค์Œ์€ ์ƒคํ”„๋ฅผ ์‚ฌ์šฉํ•œ ImageClass ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ธฐ๋ณธ ํ˜•์‹ ์™ธ์—๋„ webp์™€ svg๋ฅผ ๋ชจ๋‘ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์ฃผ์„์— ์„ค๋ช…๋œ ํฌ๊ธฐ ์กฐ์ • ๋ฌธ์ œ๋กœ ์ธํ•ด ์ฝ”๋“œ๋Š” load ๋ฉ”์„œ๋“œ์—์„œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•˜์—ฌ node-vibrant์—์„œ ์˜ค๋Š” ๋ชจ๋“  ํฌ๊ธฐ ์กฐ์ • ๋ช…๋ น์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

import * as sharp from "sharp";
import {ImageBase, ImageSource} from "@vibrant/image";

class SharpImage extends ImageBase {
  private _image: ImageData = undefined as any;

  async load(image: ImageSource): Promise<ImageBase> {
    if (typeof image === "string" || image instanceof Buffer) {
      const {data, info} = await sharp(image)
        .resize(200, 200, {fit: "inside", withoutEnlargement: true})
        .ensureAlpha()
        .raw()
        .toBuffer({resolveWithObject: true});
      this._image = {
        width: info.width,
        height: info.height,
        data: (data as unknown) as Uint8ClampedArray,
      };
      return this;
    } else {
      return Promise.reject(
        new Error("Cannot load image from HTMLImageElement in node environment")
      );
    }
  }
  clear(): void {}
  update(): void {}
  getWidth(): number {
    return this._image.width;
  }
  getHeight(): number {
    return this._image.height;
  }
  resize(targetWidth: number, targetHeight: number, ratio: number): void {
    // done in the load step, ignoring any maxDimension or quality options
  }
  getPixelCount(): number {
    const {width, height} = this._image;
    return width * height;
  }
  getImageData(): ImageData {
    return this._image;
  }
  remove(): void {}
}

๋ชจ๋“  12 ๋Œ“๊ธ€

์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๊ธ€์Ž„, ํ˜„์žฌ ์ด๋ฏธ์ง€๋ฅผ ํ”ฝ์…€ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” <canvas> ์˜ํ•ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ง€์›ํ•˜๋Š” ํ˜•์‹์˜ ๋ฌธ์ œ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. [browser.ts]๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.
node.js์—์„œ๋Š” ์ˆœ์ˆ˜ํ•œ JavaScript ๊ตฌํ˜„์ด๊ธฐ ๋•Œ๋ฌธ์— [jimp]๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€ ํ”Œ๋žซํผ์—์„œ ๊ธฐ๋ณธ ๊ตฌํ˜„์œผ๋กœ ์ค‘๋‹จ๋  ์ˆ˜ ์žˆ๋Š” ์ผ๋ถ€ ๋ฐ”์ด๋„ˆ๋ฆฌ/์ข…์†์„ฑ์„ ๋„์ž…ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. [node.ts]๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

node-vibrant๋Š” ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ ํ˜•์‹ ์ง€์›์€ ImageClass ๋ฅผ ํ†ตํ•ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ž์‹ ์˜ ImageClass ํ•˜๊ณ  Vibrant.DefaultOpts.ImageClass = YourCustomImageClass ์„ธํŠธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜๋‚˜๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด

  • ์ถ”์ƒ ํด๋ž˜์Šค [ ImageBase ]๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋˜๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ [ Image ] ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

์ถœ๋ ฅ ํ”ฝ์…€ ๋ฐฐ์—ด์€ [ ImageData.data ]์™€ ๊ฐ™์€ ํ˜•์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. "0์—์„œ 255(ํฌํ•จ) ์‚ฌ์ด์˜ ์ •์ˆ˜ ๊ฐ’์„ ๊ฐ–๋Š” RGBA ์ˆœ์„œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” 1์ฐจ์› ๋ฐฐ์—ด"์ž…๋‹ˆ๋‹ค.

๋…ธ๋“œ์— ๋Œ€ํ•œ webp ๋””์ฝ”๋” ํŒจํ‚ค์ง€๋ฅผ ์ฐพ๋Š” ๊ฒƒ์€ ์ •๋ง ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

์—„์ฒญ๋‚œ! ๊ฐ์‚ฌ ํ•ด์š”! TypeScript๋ฅผ ์ž˜ํ•˜์ง€๋Š” ๋ชปํ•˜์ง€๋งŒ ๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

๋ฉ‹์ง„!

์ฐธ๊ณ ๋กœ ์ €๋Š” node-vibrant๋ฅผ ๋ฒ„์ „ 3.1.0์šฉ์œผ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์€ ํŒจํ‚ค์ง€๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ด๋ฏธ์ง€ ํด๋ž˜์Šค๋Š” ์ž์ฒด npm ํŒจํ‚ค์ง€๋กœ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.

์ฐธ์กฐ ๊ตฌํ˜„์„ ์œ„ํ•ด @vibrant/image-node ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์—ฌ์ „ํžˆ ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์ „์ฒด ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋ถ„๊ธฐํ•˜๋Š” ๋Œ€์‹  ์ด ํ”„๋กœ์ ํŠธ์—์„œ ํ•˜๋‚˜์˜ @vibrant/image ์ข…์†์„ฑ๋งŒ ํ•„์š”ํ•˜๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ . ๋ฐ”๋ผ๊ฑด๋Œ€ ๊ทธ๊ฒƒ์€ ์ผ์„ ๋‹จ์ˆœํ™” ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—„์ฒญ๋‚œ !

๋‚˜๋Š” ๋งŽ์€ ์ด์ ์œผ๋กœ ์ธํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ webp ํ˜•์‹์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ด๋™ํ•˜๊ธฐ ์‹œ์ž‘ํ•œ Discord ๋ด‡์—์„œ ์ด ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ด ๊ธฐ๋Šฅ์€ ์ €์—๊ฒŒ ํฐ ๋„์›€์ด ๋˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. wontfix ๋ ˆ์ด๋ธ”์ด ํ‘œ์‹œ๋˜์–ด ์ •๋ง ๊ฑฑ์ •๋ฉ๋‹ˆ๋‹ค.

@nitriques "๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ณด๊ธฐ"๋ฅผ ์•„์ง @akfish ๊ฒฐ๊ตญ ์ž‘์—…ํ•  ๊ฑด๊ฐ€์š”?

@Favna ๊ธฐ๋ณธ ํŒจํ‚ค์ง€/

์—ฌ๊ฐ€ ์‹œ๊ฐ„์— ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ ์ค‘์ด๋ฉฐ ์ด ๊ธฐ๋Šฅ์€ ์šฐ์„  ์ˆœ์œ„ ๋ชฉ๋ก์— ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๊ณง ๊ณ ์ณ์ง€์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™๋‹ค.

@ํŒŒ๋ธŒ๋‚˜

"๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ณด๋Š” ๊ฒƒ"์„ ์•„์ง ํ•ด๋ณธ ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์˜ˆ, ๋‚ด๊ฐ€ ๋ณด๋Š” ๋ชจ๋“  ๊ฒƒ์€ ๊ธฐ๋ณธ ์• ๋“œ์˜จ์ž…๋‹ˆ๋‹ค.

์ข‹์€ ์ ์€ ๋‚ด๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ณง ๋‘˜๋Ÿฌ๋ณผ ์‹œ๊ฐ„์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์„ ํƒ์  ํ”Œ๋ž˜๊ทธ๋ฅผ ํ†ตํ•ด jimp ๋Œ€์‹  sharp ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์‹ญ๋‹ˆ๊นŒ? v0.20 ์ƒคํ”„๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์‹œ์Šคํ…œ์—์„œ npm install ์™ธ์— ์ถ”๊ฐ€ ์„ค์น˜ ๋‹จ๊ณ„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ webp ๋ฐ svg ์ง€์›๊ณผ ํ•จ๊ป˜ ์ œ๊ณต๋˜๋ฉฐ ๊ธฐ๋ณธ ๋ชจ๋“ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋น ๋ฅธ ์†”๋ฃจ์…˜์„ ์•ฝ์†ํ•ฉ๋‹ˆ๋‹ค.

์ƒคํ”„ ๊ธฐ๋ฐ˜ ImageClass ์„ ๋งŒ๋“ค๋ ค๊ณ  ํ•˜์ง€๋งŒ resize / scaleDown ๋Š” ๋™๊ธฐ์‹์ด์–ด์•ผ ํ•˜์ง€๋งŒ ์ƒคํ”„์˜ ํฌ๊ธฐ ์กฐ์ • ์ž‘์—…์€ ๋น„๋™๊ธฐ์‹์ด๋ฏ€๋กœ ๊ฐ„๋‹จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

fyi: ๋‹ค์Œ์€ ์ƒคํ”„๋ฅผ ์‚ฌ์šฉํ•œ ImageClass ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ธฐ๋ณธ ํ˜•์‹ ์™ธ์—๋„ webp์™€ svg๋ฅผ ๋ชจ๋‘ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์ฃผ์„์— ์„ค๋ช…๋œ ํฌ๊ธฐ ์กฐ์ • ๋ฌธ์ œ๋กœ ์ธํ•ด ์ฝ”๋“œ๋Š” load ๋ฉ”์„œ๋“œ์—์„œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•˜์—ฌ node-vibrant์—์„œ ์˜ค๋Š” ๋ชจ๋“  ํฌ๊ธฐ ์กฐ์ • ๋ช…๋ น์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

import * as sharp from "sharp";
import {ImageBase, ImageSource} from "@vibrant/image";

class SharpImage extends ImageBase {
  private _image: ImageData = undefined as any;

  async load(image: ImageSource): Promise<ImageBase> {
    if (typeof image === "string" || image instanceof Buffer) {
      const {data, info} = await sharp(image)
        .resize(200, 200, {fit: "inside", withoutEnlargement: true})
        .ensureAlpha()
        .raw()
        .toBuffer({resolveWithObject: true});
      this._image = {
        width: info.width,
        height: info.height,
        data: (data as unknown) as Uint8ClampedArray,
      };
      return this;
    } else {
      return Promise.reject(
        new Error("Cannot load image from HTMLImageElement in node environment")
      );
    }
  }
  clear(): void {}
  update(): void {}
  getWidth(): number {
    return this._image.width;
  }
  getHeight(): number {
    return this._image.height;
  }
  resize(targetWidth: number, targetHeight: number, ratio: number): void {
    // done in the load step, ignoring any maxDimension or quality options
  }
  getPixelCount(): number {
    const {width, height} = this._image;
    return width * height;
  }
  getImageData(): ImageData {
    return this._image;
  }
  remove(): void {}
}
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰

๊ด€๋ จ ๋ฌธ์ œ

eggers picture eggers  ยท  3์ฝ”๋ฉ˜ํŠธ

amirping picture amirping  ยท  6์ฝ”๋ฉ˜ํŠธ

Kikobeats picture Kikobeats  ยท  9์ฝ”๋ฉ˜ํŠธ

inbarshani picture inbarshani  ยท  4์ฝ”๋ฉ˜ํŠธ

stelasido picture stelasido  ยท  15์ฝ”๋ฉ˜ํŠธ