# All files / src/lib png.ts

100% Statements 35/35
100% Branches 15/15
100% Functions 8/8
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    1193x

    1x

    1x
    1x
    5x
    1x
    1x

    1x

    2x

    7x
    6x

    6x

    6x
    5x

    1x

    2x

    1x
    1x
    1x
    1x

    1x
    2x
    2x

    2x

    2x

    2x
    2x
    2x
    2x

    2x

    1x

    3x

     import { PNG } from 'pngjs'
    import RGBAColor from '../types/RGBAColor'

    /**
     * Renders a PNG Blob stream to a base64 PNG.
     *
     * @param {PNG} png
     * @returns {Promise<string>} base64 representation
     */
    function blobToBase64 (png: PNG): Promise<string> {
      const chunks: Uint8Array[] = []

      return new Promise((resolve) => {
        png.pack()
        png.on('data', (c: Uint8Array) => chunks.push(c))
        png.on('end', () => {
          const result = Buffer.concat(chunks)

          resolve('data:image/png;base64,' + result.toString('base64'))
        })
      })
    }

    /**
     * Writes the PNG instance to a buffer.
     *
     * @param {PNG} png - image instance
     * @returns {string}
     */
    function getBuffer (png: PNG): Buffer {
      return PNG.sync.write(png)
    }

    /**
     * Converts the given hexadecimal number to RGBA.
     *
     * @param {string} hex - 6-digit or 8-digit RGB(A) representation in hex
     * @returns {RGBAColor} RGBA
     */
    function getRgbaColor (hex = '000000FF'): RGBAColor {
      const colors = [
        ...hex.match(/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i) ?? []
      ]
      const alpha = parseInt(colors[4], 16)

      if (colors.length >= 3) {
        return {
          red: parseInt(colors[1], 16),
          green: parseInt(colors[2], 16),
          blue: parseInt(colors[3], 16),
          alpha: !isNaN(alpha) ? alpha : 255
        }
      }

      // default to solid black if color is invalid
      return {
        red: 0,
        green: 0,
        blue: 0,
        alpha: 255
      }
    }

    /**
     * Returns true if two RGB colors are equal.
     *
     * @param {RGBAColor} a
     * @param {RGBAColor} b
     * @returns {boolean}
     */
    function isEqualColor (a: RGBAColor, b: RGBAColor): boolean {
      return a.red === b.red && a.green === b.green && a.blue === b.blue
    }

    /**
     * Renders RGB 24 bitmap into an image instance of PNG
     *
     * @param {number[]} bitmap - containing RGB values
     * @param {number} width - width of bitmap
     * @param {number} height  height of bitmap
     * @returns {PNG} instance of PNG
     */
    function render (bitmap: number[], width: number, height: number, backgroundColor?: string, foregroundColor?: string): PNG {
      const png = new PNG({ width, height })
      const backgroundColorRgba = getRgbaColor(backgroundColor)
      const foregroundColorRgba = getRgbaColor(foregroundColor)
      let i = 0

      for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
          const color: RGBAColor = {
            red: bitmap[i],
            green: bitmap[i + 1],
            blue: bitmap[i + 2]
          }
          const rgba = isEqualColor(color, backgroundColorRgba)
            ? backgroundColorRgba
            : foregroundColorRgba
          const pos = (png.width * y + x) << 2

          png.data[pos] = rgba.red
          png.data[pos + 1] = rgba.green
          png.data[pos + 2] = rgba.blue
          png.data[pos + 3] = rgba.alpha || 0

          i += 3
        }
      }

      return png
    }

    export default {
      blobToBase64,
      getBuffer,
      getRgbaColor,
      render
    }