es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Node.js – Escribir y codificar un archivo PNG utilizando únicamente los módulos FS y ZLIB.

Estoy tratando de generar una imagen .png a partir de una cadena de color hexadecimal (#RRGGBB), sin utilizar módulos como pngjs o jimp.

La cadena en hexadecimal se ve así:

let colors = “ff000000ff000000ffff00ff00ffffffff00”;

Según este artículo, todos los archivos png deben comenzar con la firma ‘b’\x89PNG\r\n\x1a\n’ y tener 3 “chunks” (IHDR, IDAT e IEND), cada uno con la siguiente estructura:

(Data Length, Chunk Type, Chunk Data y CRC)

Y según las especificaciones de png, se comprime con deflate de zlib, por lo que he creado este código:

const fs = require(“fs”);
const zlib = require(“zlib”);

let colors = “ff000000ff000000ffff00ff00ffffffff00”;
let data = b'\x89PNG\r\n\x1a\n'0d00IHDR030002008200000002400IDAT${colors}0000IEND;

zlib.deflateRaw(data, (err, buffer) =>{
fs.writeFile(“output.png”, buffer, (err)=>{if (err) throw err;});
});

Para una mejor legibilidad, los datos divididos se ven así:

b’\x89PNG\r\n\x1a\n’ // firma
0d00 // longitud de los datos del chunk (13)
IHDR // tipo de chunk

/* datos del chunk */
0300 // ancho de la imagen (3)
0200 // alto de la imagen (2)
8 // profundidad de bits (8)
2 // tipo de color (2)
0 // método de compresión (0)
0 // método de filtro (0)
0 // método de entrelazado (0)
0000 // CRC (?)

2400 // longitud de los datos del chunk (36)
IDAT // tipo de chunk
ff000000ff000000ffff00ff00ffffffff00 // datos del chunk (#RRGGBB)
0000 // CRC (?)
IEND

Lo que esperaba obtener como resultado era la siguiente imagen (en su tamaño de 3×2), pero todo lo que tengo es un archivo png aparentemente inválido que mi visor de imágenes no admite.

¿Es realmente posible escribir un archivo png válido de esta manera? Si es así, ¿cómo se hace?

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Es posible crear un archivo PNG válido de esta manera, pero el código proporcionado tiene algunos errores que deben corregirse.

    En primer lugar, las longitudes de datos y los valores CRC para los trozos IHDR e IDAT son incorrectos. Deben calcularse en función de la longitud real de los datos del trozo y del tipo de trozo, respectivamente.

    En segundo lugar, los colores en los datos del trozo deben convertirse de una cadena hexadecimal a bytes. Una forma de hacer esto es utilizando un bucle for para dividir la cadena en subcadenas de 2 caracteres, y luego convertir cada subcadena en un byte utilizando parseInt().

    Aquí está el código corregido:

    const fs = require("fs");
    const zlib = require("zlib");
    
    let colors = "ff000000ff000000ffff00ff00ffffffff00";
    
    // convertir la cadena de colores en bytes
    let bytes = [];
    for (let i = 0; i < colors.length; i += 2) {
      bytes.push(parseInt(colors.substr(i, 2), 16));
    }
    
    // crear datos PNG
    let data = Buffer.alloc(65); // asignar un búfer con tamaño fijo
    "\x89PNG\r\n\x1a\n".copy(data); // copiar la firma
    data.writeUInt32BE(13, 8); // longitud de datos del trozo IHDR
    "IHDR".copy(data, 12); // tipo de trozo del trozo IHDR
    data.writeUInt32BE(3, 16); // ancho de la imagen
    data.writeUInt32BE(2, 20); // altura de la imagen
    data.writeUInt8(8, 24); // profundidad de bits
    data.writeUInt8(2, 25); // tipo de color
    "\x00\x00\x00".copy(data, 26, 0, 3); // métodos de compresión, filtro y entrelazado
    let crc1 = zlib.crc32(data.slice(12, 29));
    data.writeUInt32BE(crc1, 29); // CRC del trozo IHDR
    data.writeUInt32BE(36, 33); // longitud de datos del trozo IDAT
    "IDAT".copy(data, 37); // tipo de trozo del trozo IDAT
    Buffer.from(bytes).copy(data, 41); // agregar colores a los datos del trozo IDAT
    let crc2 = zlib.crc32(data.slice(37, 78));
    data.writeUInt32BE(crc2, 78); // CRC del trozo IDAT
    "IEND".copy(data, 82); // agregar el trozo IEND
    
    // comprimir datos PNG con zlib
    zlib.deflateRaw(data, (err, buffer) => {
      fs.writeFile("output.png", buffer, (err) => {
        if (err) throw err;
      });
    });
    

    En resumen, para crear un archivo PNG válido a partir de una cadena hexadecimal de colores sin usar módulos externos, debes:

    • Convertir la cadena de colores en bytes.
    • Crear los datos PNG con las secuencias de bytes correctas para la firma y los trozos (incluyendo las longitudes de datos y los valores CRC correctos).
    • Comprimir los datos PNG con zlib.

    Haciendo esto, deberías poder generar una imagen PNG válida.

Comments are closed.