1

What I want to do is very simple : I have an image which is basically a single color image with alpha.

EDIT : since my post has been light speed tagged as duplicate, here are the main differences with the other post :

  1. The other topic's image has several colors, mine has only one
  2. The owner accepted answer is the one I implemented... and I'm saying I have an issue with it because it's too slow

It means that all pixels are either :

  • White (255;255;255) and Transparent (0 alpha)
  • or Black (0;0;0) and Opaque (alpha between 0 and 255)

Alpha is not always the same, I have different shades of black.

My image is produced with Photoshop with the following settings :

  • Mode : RGB color, 8 Bits/Channel
  • Format : PNG
  • Compression : Smallest / Slow
  • Interlace : None

Considering it has only one color I suppose I could use other modes (such as Grayscale maybe ?) so tell me if you have suggestions about that.

What I want to do is REPLACE the Black color by another color in my java application.

Reading the other topics it seems that changing the ColorModel is the good thing to do, but I'm honestly totally lost on how to do it correctly.

Here is what I did :

public Test() {
    BufferedImage image, newImage;
    IndexColorModel newModel;

    try {
      image = ImageIO.read(new File("E:\\MyImage.png"));
    } 
    catch (IOException e) {e.printStackTrace();}

    newModel = createColorModel();
    newRaster = newModel.createCompatibleWritableRaster(image.getWidth(), image.getHeight());

    newImage = new BufferedImage(newModel, newRaster, false, null);
    newImage.getGraphics().drawImage(image, 0, 0, null);
}

private IndexColorModel createColorModel() {
    int size = 256;

    byte[] r = new byte[size];
    byte[] g = new byte[size];
    byte[] b = new byte[size];
    byte[] a = new byte[size];

    for (int i = 0; i < size; i++) {
        r[i] = (byte) 21;
        g[i] = (byte) 0;
        b[i] = (byte) 149;
        a[i] = (byte) i;
    }

    return new IndexColorModel(16, size, r, g, b, a);
}

This produces the expected result.

newImage is the same than image with the new color (21;0;149).

But there is a flaw : the following line is too slow :

newImage.getGraphics().drawImage(image, 0, 0, null);

Doing this on a big image can take up to a few seconds, while I need this to be instantaneous.

I'm pretty sure I'm not doing this the good way.

Could you tell me how achieve this goal efficiently ?

Please consider that my image will always be single color with alpha, so suggestions about image format are welcomed.

J. Chomel
  • 8,193
  • 15
  • 41
  • 69
RedFaction
  • 11
  • 2
  • @Andy Turner : Is that all ? I read all other topics including this one for days. I wouldn't ask my question otherwise... – RedFaction May 06 '17 at 22:17

1 Answers1

0

If your input image always in palette + alpha mode, and using an IndexColorModel compatible with the one from your createColorModel(), you should be able to do:

BufferedImage image ...; // As before

IndexColorModel newModel = createColorModel();   
BufferedImage newImage = new BufferedImage(newModel, image.getRaster(), newModel.isAlphaPremultiplied(), null);

This will create a new image, with the new color model, but re-using the raster from the original. No creating of large data arrays or drawing is required, thus it should be very fast. But: Note that image and newImage will now share backing buffer, so any changes drawn in one of them, will be reflected on the other (probably not a problem in your case).

PS: You might need to tweak your createColorModel() method to get the exact result you want, but as I don't have your input file, I can't very if it works or not.

Harald K
  • 26,314
  • 7
  • 65
  • 111
  • Hi haraldK. Thanks for your answer. When doing this, I get a compatibility error between my colorModel and the raster. – RedFaction May 18 '17 at 12:44
  • @RedFaction Yes, I expected that. That's why I wrote you may have to tweak the createColorModel method. But I can't know to what, without seeing your input file. 16 bits for 256 entry palette seems suspicious though. – Harald K May 18 '17 at 12:51
  • You are true. I don't remember why I used 16 bits. I think I had an issue when putting 8, but now 8 works so it's fine. TBH I found a work around for this issue. Now I'm struggling to merge all my single color layers into one multi-colors image in an efficient way. Presently, I'm using Graphics.drawImage method to draw all layers into one single image, but that takes up to 500ms for around 15 layers. Any suggestions ? PS : how could I send you the image ? – RedFaction May 18 '17 at 14:19
  • @RedFaction You can either attach the file in the question (might need more rep), or simply provide a link in your question, to the file in your dropbox, google drive or similar file sharing service. – Harald K May 19 '17 at 07:35