最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - Why are the colors mixed up in this screenshot taken using Robot.js? - Stack Overflow

programmeradmin0浏览0评论

I'm using Robot.js to take screenshots (I don't really want to install any other heavy packages over 1 mb, but I'm already using Robot.js and no, Jimp isn't what I'm looking for). Since the captureScreen method of Robot.js returns a raw pixel buffer, I'm converting the buffer to a png buffer.

However, I can't figure out why my conversion mixes up the colors of the screenshot. This is my current code:

const fs = require('fs');
const zlib = require('zlib');
const robot = require("@jitsi/robotjs");

const { width, height } = robot.getScreenSize();
const bitDepth = 8;
const colorType = 6;
const bytesPerPixel = 4;
const rawPixelData = robot.captureScreen().image;

const filteredData = Buffer.alloc((width * bytesPerPixel + 1) * height);
for (let i = 0; i < height; i++) {
  filteredData[i * (width * bytesPerPixel + 1)] = 0;
  rawPixelData.copy(filteredData, i * (width * bytesPerPixel + 1) + 1, i * width * bytesPerPixel, (i + 1) * width * bytesPerPixel);
};
const compressedData = zlib.deflateSync(filteredData);

function crc32(buf) {
  let crc = 0xffffffff;
  for (let i = 0; i < buf.length; i++) {
    crc ^= buf[i];
    for (let j = 0; j < 8; j++) {
      if (crc & 1) {
        crc = (crc >>> 1) ^ 0xedb88320;
      } else {
        crc >>>= 1;
      }
    }
  }
  return Buffer.from([(crc ^ 0xffffffff) >>> 24, (crc ^ 0xffffffff) >>> 16, (crc ^ 0xffffffff) >>> 8, (crc ^ 0xffffffff) & 0xff]);
}

function createChunk(type, data) {
  const length = Buffer.alloc(4);
  length.writeUInt32BE(data.length, 0);

  const typeBuffer = Buffer.from(type, "ascii");
  const crc = crc32(Buffer.concat([typeBuffer, data]));

  return Buffer.concat([length, typeBuffer, data, crc]);
}

const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);

const ihdrData = Buffer.alloc(13);
ihdrData.writeUInt32BE(width, 0);
ihdrData.writeUInt32BE(height, 4);
ihdrData.writeUInt8(bitDepth, 8);
ihdrData.writeUInt8(colorType, 9);
ihdrData.writeUInt8(0, 10);
ihdrData.writeUInt8(0, 11);
ihdrData.writeUInt8(0, 12);

const ihdrChunk = createChunk("IHDR", ihdrData);
const idatChunk = createChunk("IDAT", compressedData);
const iendChunk = createChunk("IEND", Buffer.alloc(0));

const pngData = Buffer.concat([pngSignature, ihdrChunk, idatChunk, iendChunk]);

fs.writeFileSync("./output.png", pngData);
console.log("PNG file written: output.png");

As an example, this is what a screenshot of my VS Code session currently looks like:

Any help would be greatly appreciated! ;)

I'm using Robot.js to take screenshots (I don't really want to install any other heavy packages over 1 mb, but I'm already using Robot.js and no, Jimp isn't what I'm looking for). Since the captureScreen method of Robot.js returns a raw pixel buffer, I'm converting the buffer to a png buffer.

However, I can't figure out why my conversion mixes up the colors of the screenshot. This is my current code:

const fs = require('fs');
const zlib = require('zlib');
const robot = require("@jitsi/robotjs");

const { width, height } = robot.getScreenSize();
const bitDepth = 8;
const colorType = 6;
const bytesPerPixel = 4;
const rawPixelData = robot.captureScreen().image;

const filteredData = Buffer.alloc((width * bytesPerPixel + 1) * height);
for (let i = 0; i < height; i++) {
  filteredData[i * (width * bytesPerPixel + 1)] = 0;
  rawPixelData.copy(filteredData, i * (width * bytesPerPixel + 1) + 1, i * width * bytesPerPixel, (i + 1) * width * bytesPerPixel);
};
const compressedData = zlib.deflateSync(filteredData);

function crc32(buf) {
  let crc = 0xffffffff;
  for (let i = 0; i < buf.length; i++) {
    crc ^= buf[i];
    for (let j = 0; j < 8; j++) {
      if (crc & 1) {
        crc = (crc >>> 1) ^ 0xedb88320;
      } else {
        crc >>>= 1;
      }
    }
  }
  return Buffer.from([(crc ^ 0xffffffff) >>> 24, (crc ^ 0xffffffff) >>> 16, (crc ^ 0xffffffff) >>> 8, (crc ^ 0xffffffff) & 0xff]);
}

function createChunk(type, data) {
  const length = Buffer.alloc(4);
  length.writeUInt32BE(data.length, 0);

  const typeBuffer = Buffer.from(type, "ascii");
  const crc = crc32(Buffer.concat([typeBuffer, data]));

  return Buffer.concat([length, typeBuffer, data, crc]);
}

const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);

const ihdrData = Buffer.alloc(13);
ihdrData.writeUInt32BE(width, 0);
ihdrData.writeUInt32BE(height, 4);
ihdrData.writeUInt8(bitDepth, 8);
ihdrData.writeUInt8(colorType, 9);
ihdrData.writeUInt8(0, 10);
ihdrData.writeUInt8(0, 11);
ihdrData.writeUInt8(0, 12);

const ihdrChunk = createChunk("IHDR", ihdrData);
const idatChunk = createChunk("IDAT", compressedData);
const iendChunk = createChunk("IEND", Buffer.alloc(0));

const pngData = Buffer.concat([pngSignature, ihdrChunk, idatChunk, iendChunk]);

fs.writeFileSync("./output.png", pngData);
console.log("PNG file written: output.png");

As an example, this is what a screenshot of my VS Code session currently looks like:

Any help would be greatly appreciated! ;)

Share Improve this question asked Feb 7 at 14:24 Dinoscape DinoscapeDinoscape Dinoscape 541 silver badge3 bronze badges 2
  • " If anybody is having the same problem, here's the solution:" - self-answers are welcome on SO, consider posting it. FWIW this is the reason why a screenshot of color swatch would be helpful in such case – Estus Flask Commented Feb 7 at 14:26
  • @EstusFlask Yeah, imma do that. Thanks anyways! – Dinoscape Dinoscape Commented Feb 7 at 14:54
Add a comment  | 

1 Answer 1

Reset to default 1

For future reference, Robotjs uses BGRA instead of RGBA. This correctly rearranges the colors:

 rawPixelData[idx] (Blue) → filteredData[fi + 2]
 rawPixelData[idx + 1] (Green) → filteredData[fi + 1]
 rawPixelData[idx + 2] (Red) → filteredData[fi]
 rawPixelData[idx + 3] (Alpha) remains the same

So the updated code would look like this:

const fs = require('fs');
const zlib = require('zlib');
const robot = require("@jitsi/robotjs");

const { width, height } = robot.getScreenSize();
const bitDepth = 8;
const colorType = 6;
const bytesPerPixel = 4;
const rawPixelData = robot.captureScreen().image;

const filteredData = Buffer.alloc((width * bytesPerPixel + 1) * height);
for (let i = 0; i < height; i++) {
  filteredData[i * (width * bytesPerPixel + 1)] = 0; // PNG scanline filter type 0
  for (let j = 0; j < width; j++) {
    const idx = (i * width + j) * bytesPerPixel;
    const fi = i * (width * bytesPerPixel + 1) + 1 + j * bytesPerPixel;

    // Convert BGRA to RGBA
    filteredData[fi] = rawPixelData[idx + 2];     // Red
    filteredData[fi + 1] = rawPixelData[idx + 1]; // Green
    filteredData[fi + 2] = rawPixelData[idx];     // Blue
    filteredData[fi + 3] = rawPixelData[idx + 3]; // Alpha
  };
};
const compressedData = zlib.deflateSync(filteredData);

function crc32(buf) {
  let crc = 0xffffffff;
  for (let i = 0; i < buf.length; i++) {
    crc ^= buf[i];
    for (let j = 0; j < 8; j++) {
      if (crc & 1) {
        crc = (crc >>> 1) ^ 0xedb88320;
      } else {
        crc >>>= 1;
      }
    }
  }
  return Buffer.from([(crc ^ 0xffffffff) >>> 24, (crc ^ 0xffffffff) >>> 16, (crc ^ 0xffffffff) >>> 8, (crc ^ 0xffffffff) & 0xff]);
}

function createChunk(type, data) {
  const length = Buffer.alloc(4);
  length.writeUInt32BE(data.length, 0);

  const typeBuffer = Buffer.from(type, "ascii");
  const crc = crc32(Buffer.concat([typeBuffer, data]));

  return Buffer.concat([length, typeBuffer, data, crc]);
}

const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);

const ihdrData = Buffer.alloc(13);
ihdrData.writeUInt32BE(width, 0);
ihdrData.writeUInt32BE(height, 4);
ihdrData.writeUInt8(bitDepth, 8);
ihdrData.writeUInt8(colorType, 9);
ihdrData.writeUInt8(0, 10);
ihdrData.writeUInt8(0, 11);
ihdrData.writeUInt8(0, 12);

const ihdrChunk = createChunk("IHDR", ihdrData);
const idatChunk = createChunk("IDAT", compressedData);
const iendChunk = createChunk("IEND", Buffer.alloc(0));

const pngData = Buffer.concat([pngSignature, ihdrChunk, idatChunk, iendChunk]);

fs.writeFileSync("./output.png", pngData);
console.log("PNG file written: output.png");

I hope this helps! ;)

发布评论

评论列表(0)

  1. 暂无评论