pngjs icon indicating copy to clipboard operation
pngjs copied to clipboard

BGRA/ARGB -> PNG

Open IsaiahJTurner opened this issue 9 years ago • 2 comments

I have pixel data in BGRA and ARGB format as per https://github.com/electron/electron/blob/master/docs/api/web-contents.md#webcontentsbeginframesubscriptioncallback and am trying to convert it to a PNG but can't seem to figure out how. Here's what I have so far.

var data = Buffer.allocUnsafe(buffer.length);

var size = mainWindow.getSize(); // [w, h]

var format;

var pixel = 0; // total buffer offset that inc's by 1 for each pixel
var curr = 0; // 1-4 current R/G/B/A index
var offset = 0;
// These two if statements turn the BGRA or ARGB buffer into an RGBA buffer.
if (os.endianness() === "BE") {
  format = "ARGB";
  for (var argbValue of buffer.values()) {
    curr++;
    offset = pixel * 4;
    switch (curr) {
      case 1: { // A
        offset += 3;
        break;
      }
      case 2: { // R
        offset += 0;
        break;
      }
      case 3: { // G
        offset += 1;
        break;
      }
      default: { // B
        offset += 2;
        break;
      }
    }
    data.fill(argbValue, offset, offset + 1);
    if (curr === 4) {
      pixel++;
      curr = 0;
    }
  }
} else {
  format = "BGRA";
  for (var bgraValue of buffer.values()) {
    curr++;
    offset = pixel * 4;
    switch (curr) {
      case 1: { // B
        offset += 2;
        break;
      }
      case 2: { // G
        offset += 1;
        break;
      }
      case 3: { // R
        offset += 0;
        break;
      }
      default: { // A
        bgraValue = 0;
        offset += 3;
        break;
      }
    }
    data.fill(bgraValue, offset, offset + 1);
    if (curr === 4) {
      pixel++;
      curr = 0;
    }
  }
}

// new PNG's don't emit `parsed` so here my thinking was i could create an empty PNG and then read that PNG

new PNG({ // creates empty PNG of correct size
  width: size[0],
  height: size[1],
  inputHasAlpha: true
}).pack().pipe(new PNG({
  // copies empty PNG so `parsed` will be called
}).on('parsed', function() {
  // sets PNG data to the RGBA buffer we already have
  this.data.fill(data)
  // saves that to a file (this part saves a PNG that is 100% transparent) oddly, the file sizes for these entirely transparent PNGs are not always the same.
  this.pack().pipe(fs.createWriteStream('imgs/' + (imgIndex++) + '-out.png'));
}))

IsaiahJTurner avatar May 24 '16 00:05 IsaiahJTurner

Hi,

I'm low on time, but this looks suspiscous:

.pack().pipe(new PNG({
  // copies empty PNG so `parsed` will be called
}).on('parsed', function() {

why not just create the new png and then fill data then pack.. like our example

https://github.com/lukeapage/pngjs/blob/master/examples/newfile.js#L4

lukeapage avatar May 25 '16 03:05 lukeapage

@IsaiahJTurner Did you solve your problem ? getting a hard time to make it work.

@lukeapage i got unexpected output when trying to convert argb to png, the result here.

my code

const buffer: Buffer = prop.data;
if (buffer.length > 0) {
  let width = buffer.readUInt32LE(0); // the first 4 bytes is the width
  let height = buffer.readUInt32LE(4); // the second 4 bytes is the height

  let png = new PNG({ width, height, inputHasAlpha: true });

  let output = true;
  for (var y = 0; y < height; ++y) {
    for (var x = 0; x < width; ++x) {
      var p = (4 * (y * width + x));
        try {
          png.data[p] = buffer.readUInt32LE((p + 1) + 8);
          png.data[p + 1] = buffer.readUInt32LE((p + 2) + 8);
          png.data[p + 2] = buffer.readUInt32LE((p + 3) + 8);
          png.data[p + 3] = buffer.readUInt32LE(p + 8);
        } catch (err) {
          output = false;
          continue;
        }
      }
    }
    if (output) png.pack().pipe(fs.createWriteStream(wid + '.png'));
}

any clue why the image like that ? i've tried using other image data but always have purple color on it.

ghost avatar Feb 18 '18 06:02 ghost