Skip to main content
Inspiring
April 15, 2023
Question

Scripting tools to get the color of a particular dot placed placed on an image layer?

  • April 15, 2023
  • 2 replies
  • 2274 views

Hi all.
Does Illustrator have scripting tools to get the color of a particular dot or 3x3 dot patch placed on an image layer?

 

There is an image in the layer, you need to find the color of a certain point of this image...

This topic has been closed for replies.

2 replies

Inventsable
Legend
April 18, 2023

You really should add some more details to this otherwise it forces us to assume a lot of information, like:

  • How the BMP and path are each located or targeted. You say they're in separate layers, I don't know the names or order of these layers or how you intend to use them
  • The color profile and bit depth of the BMP, which I assumed was RGB and 32 instead of BMP, 16 or 24
  • Whether there are any other paths in this file that aren't the same as the one used for pixel analysis
  • Whether your dot will ever be outside the bounds of the image layer
  • Whether there will ever be multiple colors found in the path bounds or only a single one
  • What the script will actually do with this color once found

 

When you don't include details like this it means it takes a lot more work to help you, and if you want to get others to volunteer time and effort on your behalf then you'll find that not making it harder (intentionally or otherwise) for them to help will go a long way in determining whether or not you'll get many responses if not just questions prodding you to add more detail in the first place.

 

Anyway, there is no vanilla scripting API method to do this, but it's still possible given that BMP files are uncompressed pixel data. You have to manually parse the bytes of the BMP to recreate it's pixel data then you can narrow down the pixels to what was inside the bounds of the path relative to item, then narrow it further to the colors within:

 

(function () {
  if (!app.selection.length) {
    alert("Must select the path used for pixel analysis");
    return null;
  }
  Array.prototype.filter = function (callback) {
    var filtered = [];
    for (var i = 0; i < this.length; i++)
      if (callback(this[i], i, this)) filtered.push(this[i]);
    return filtered;
  };
  RGBColor.prototype.create = function (red, green, blue) {
    this.red = red;
    this.green = green;
    this.blue = blue;
    return this;
  };
  function parseBitmap(binaryData) {
    if (binaryData[0] !== 0x42 || binaryData[1] !== 0x4d) {
      alert("Invalid BMP format.");
      return null;
    }
    var pixelOffset =
        binaryData[10] +
        (binaryData[11] << 😎 +
        (binaryData[12] << 16) +
        (binaryData[13] << 24),
      width =
        binaryData[18] +
        (binaryData[19] << 😎 +
        (binaryData[20] << 16) +
        (binaryData[21] << 24),
      height =
        binaryData[22] +
        (binaryData[23] << 😎 +
        (binaryData[24] << 16) +
        (binaryData[25] << 24),
      bitDepth = binaryData[28] + (binaryData[29] << 8),
      bytesPerPixel = bitDepth / 8,
      pixelData = new Array(width * height);
    for (var i = 0; i < pixelData.length; i++) {
      var offset = pixelOffset + i * bytesPerPixel;
      pixelData[i] = {
        r: binaryData[offset + 2],
        g: binaryData[offset + 1],
        b: binaryData[offset],
        a: binaryData[offset + 3],
        x: i % width,
        y: Math.floor(i / width),
      };
    }
    return pixelData;
  }

  function getBinaryFromFile(item) {
    var file = item.file;
    file.open("r");
    file.encoding = "BINARY";
    var data = [];
    while (!file.eof) data.push(file.readch().charCodeAt(0));
    file.close();
    return data;
  }

  function getCoordinateOffsetFromPlacedItem() {
    var haystack = app.activeDocument.placedItems[0].geometricBounds,
      needle = app.selection[0].geometricBounds;
    return {
      x: Math.abs(haystack[0] - needle[0]),
      y: Math.abs(haystack[1] - needle[1]),
      w: Math.abs(needle[2] - needle[0]),
      h: Math.abs(needle[3] - needle[1]),
    };
  }
  function retrieveRGBValuesFromBitmap() {
    var target = app.activeDocument.placedItems[0],
      bounds = getCoordinateOffsetFromPlacedItem();
    var binaryData = getBinaryFromFile(target);
    var parsed = parseBitmap(binaryData);
    var result = parsed.filter(function (pixel) {
      return (
        pixel.x >= bounds.x &&
        pixel.x <= bounds.x + bounds.w &&
        pixel.y >= bounds.y &&
        pixel.y <= bounds.y + bounds.h
      );
    });
    if (!result) {
      alert("Faulty result");
      return null;
    } else {
      // Not really sure what you want to do with this since it isn't stated, so make first result the active app color
      app.activeDocument.defaultFillColor = new RGBColor().create(
        result[0].r,
        result[0].g,
        result[0].b
      );
      var msg =
        "Script found RGB(" +
        result[0].r +
        "," +
        result[0].g +
        "," +
        result[0].b +
        ")";
      alert(msg);
      return null;
    }
  }
  try {
    retrieveRGBValuesFromBitmap();
  } catch (err) {
    alert(err);
  }
})();

 In the test file I'm using, the below square is a BMP image that contains 3 colors: black, red, and blue. The white stroke square is the pathItem in Illustrator I'm using to analyze the pixels of the BMP image and it accurately reads RGB(19,100,216) which is the color of the blue inside the BMP image and contained nowhere else in the active document:

m1b
Community Expert
Community Expert
April 18, 2023

Hi @Inventsable, this is awesome! I was thinking about something like this, but didn't understand what I was doing when it comes to the getting the bitmap binary data.

 

Here's an idea: export BMP of the doc (or artboard?) and read that, rather than an actual BMP link. That way, it doesn't matter what format the linked image is, and it's scale or rotation. Tell me if I've missed something. I would also recommend the user create a 1 x 1pixel square path item for their "guide" object.

 

Anyway, thanks heaps for sharing this approach—I'd never have got that right.

- Mark

Inventsable
Legend
April 19, 2023

Thanks! Also great idea, I went ahead and rewrote it to be an agnostic library with that in mind. Now it'll do every pixel of a given canvas or be much more lightweight and given a single pixel if not a list of coordinates, plus several options and even some lifecycle hooks 👍

 

Instead of it being reliant on a guide or any specific path, I figure any user could relatively easily input the coordinates from a guide object to the arguments. This way it'd remain as flexible as possible and wouldn't be useful solely for this one use case and file structure.

femkeblanco
Legend
April 15, 2023

Is it a raster image?  How is the dot/patch to be targeted? 

AnyONAuthor
Inspiring
April 16, 2023

in the document there are two layers in the first layer is a bitmap, in the second is a path element. You need to get the color value of the rasterized image, according to the location coordinates of the path element