Sunday, August 22, 2010

Manipulate PNG palette

I am not a graphic designer, thus I don't know how to create fancy icons/images
for my apps and Microsoft
Paint
is just enough for my skills.

For all my apps I've used black-and-white PNG files without transparency and that is ok, but... black and white?! I should be
adding colors.

Image class does not have a method to easily change colors. A workaround could be to call getRGB method and iterate the rgbData array.
A better way is to read the file content, change the palette bytes and create an image from the resulting data. First lets create a helper
method to read a whole InputStream and return a byte array with its content:


private byte [] readStream (InputStream in)
throws java.io.IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte [] buff = new byte [1024];
int size = in.read(buff);

while (size >= 0) {
baos.write(buff, 0, size);
size = in.read(buff);
}

return baos.toByteArray();
}

Next we have to find where is the palette chunk inside the byte array. Here is another helper method:


// return index where P of PLTE is found at buff array or -1
private int getPLTEIndex (byte [] buff) {
int i = -1;
// 4 == "PLTE".size()
if (buff != null && buff.length >= 4) {
boolean foundPalete = false;
boolean endOfBuff = false;
do {
i++;
foundPalete = buff[i] == 'P'
&& buff[i +1] == 'L'
&& buff[i +2] == 'T'
&& buff[i +3] == 'E';
endOfBuff = (i +4 >= buff.length);
} while (!foundPalete && !endOfBuff);
if (endOfBuff) {
i = -1;
}
}
return i;
}

And, finally, a method to change a color from the palette of PNG files with color type 3:


private byte [] setRGBColor (byte [] buff, int colorIndex, int colorNewValue) {
int i = getPLTEIndex(buff);
if (i >= 0) {
i += 4; // 4 == "PLTE".size()
i += (colorIndex * 3); // 3 == RGB bytes
if (i + 3 <= buff.length) {
buff[i] = (byte) (((colorNewValue & 0x00ff0000) >> 16) & 0xff);
buff[i +1] = (byte) (((colorNewValue & 0x0000ff00) >> 8) & 0xff);
buff[i +2] = (byte) ((colorNewValue & 0x000000ff) & 0xff);
}
}
return buff;
}

Below is a sample on how to use all the methods:

InputStream in = getClass().getResourceAsStream("/e.png");
try {
byte [] buff = readStream(in);
Image original = Image.createImage(buff, 0, buff.length);
buff = setRGBColor(buff, 0, 0x00ff0000); // set 1st color to red
buff = setRGBColor(buff, 1, 0x0000ff00); // set 2nd color to green
Image updated = Image.createImage(buff, 0, buff.length);
} catch (IOException ex) {
ex.printStackTrace();
}

Related topics:

Sunday, July 11, 2010

Blog audience 2T10

From 2010 March, 15 to June, 14 this blog had 215 visits from 54 countries.

The top 10 countries are:


  • Brazil: 34

  • Indonesia: 25

  • India: 15

  • United States: 11

  • Italy: 7

  • Russia: 7

  • Germany: 6

  • Portugual: 6

  • Poland: 4



Romania, United Kingdom, Sweden and Canada has left the top 10 list. New to the list are Italy, Russia and Poland.

Related topics:

Saturday, July 10, 2010

Alert setTimeout FOREVER

So you are showing error messages, but no emails are coming in. Is your application faultless? Or all users do not bother to write down some lines and click send?

Maybe your users do not have a chance to see the error message because it was automatically dismissed before they could read it. How can it happen? - you ask.

Not calling setTimeout method on an Alert object will leave the timeout as the default value. Which value? The Virtual Machine implementation will decide, but the more important part: it is not Alert.FOREVER!

From Alert documentation (the very first line): "An alert is a screen that shows data to the user and waits for a certain period of time before proceeding to the next Displayable."

See? If you, sloppy developer, does not set a timeout the Alert will be automatically dismissed and your user will blink and ask "What was that?".

To the rescue an updated version of showExceptionAlert method below:


public void showExceptionAlert (Exception e) {
StringBuffer msg = new StringBuffer(getErrorMessage());
Display d = Display.getDisplay(this);

msg.append(e.getClass().getName());
if (e.getMessage() != null) {
msg.append("\n").append(e.getMessage());
}

Alert a = new Alert(getErrorTitle(), msg.toString(),
null /*alertImage*/, AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
d.setCurrent(a, d.getCurrent());
}


getErrorMessage() will load the message prefix and getErrorTitle() will load the Alert title based on current locale (i18n).

Related topics: