|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Kirill Grouchnikov's BlogFebruary 2005 ArchivesHow to create your own iconsPosted by kirillcool on February 25, 2005 at 05:50 AM | Permalink | Comments (16)Almost every application with GUI needs icons. And they better be sexy. And stylish. And consistent. And small. Here are few tips for programatically creating icons using Java 2D features First, here is a screenshot of a demo that shows icons created using the techniques described in this entry. As you can see, it can paint any letter on any background, creating an optional plus sign and optional arrow that can point either to the right or to the left (note that the actual quality of the icons in GUI is better then saved JPEG version): Here is an enlarged shot of a letter G: The important visual details in the icons:
And now for the interesting part - the code itself. First we create a completely transparent image. This way, when the icon will be drawn on non-white background, it will blend in nicely:
BufferedImage image = new BufferedImage(ICON_DIMENSION, ICON_DIMENSION,
BufferedImage.TYPE_INT_ARGB);
// set completely transparent
for (int col = 0; col < ICON_DIMENSION; col++) {
for (int row = 0; row < ICON_DIMENSION; row++) {
image.setRGB(col, row, 0x0);
}
}
Now, retrieve the graphics context of this image and set rendering hints for antialiasing
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Now, we fill a circle with specified background color
graphics.setColor(backgroundColor); graphics.fillOval(0, 0, ICON_DIMENSION - 1, ICON_DIMENSION - 1);Now, before we draw the border, we create a whitish spot. This one is not particularly tricky - based on the distance from a pixel to the spotlight location, make the color of the current pixel closer to white color (or the color of the spotlight). Important - don't forget to preserve the transparency (or opacity) of each pixel:
// create a whitish spot in the left-top corner of the icon
double id4 = ICON_DIMENSION / 4.0;
double spotX = id4;
double spotY = id4;
for (int col = 0; col < ICON_DIMENSION; col++) {
for (int row = 0; row < ICON_DIMENSION; row++) {
// distance to spot
double dx = col - spotX;
double dy = row - spotY;
double dist = Math.sqrt(dx * dx + dy * dy);
// distance of 0.0 - comes 90% to Color.white
// distance of ICON_DIMENSION - stays the same
if (dist > ICON_DIMENSION) {
dist = ICON_DIMENSION;
}
int currColor = image.getRGB(col, row);
int transp = (currColor >>> 24) & 0xFF;
int oldR = (currColor >>> 16) & 0xFF;
int oldG = (currColor >>> 8) & 0xFF;
int oldB = (currColor >>> 0) & 0xFF;
double coef = 0.9 - 0.9 * dist / ICON_DIMENSION;
int dr = 255 - oldR;
int dg = 255 - oldG;
int db = 255 - oldB;
int newR = (int) (oldR + coef * dr);
int newG = (int) (oldG + coef * dg);
int newB = (int) (oldB + coef * db);
int newColor = (transp << 24) | (newR << 16) | (newG << 8)
| newB;
image.setRGB(col, row, newColor);
}
}
Now, draw the outline of the icon in black. Here, based on the background color, we can choose the color of the border as lying somewhere 70-80% on the way from the background color to black. In this way, the icon will have matching border color.
// draw outline of the icon graphics.setColor(Color.black); graphics.drawOval(0, 0, ICON_DIMENSION - 1, ICON_DIMENSION - 1);Now, take the input letter and make it capital (this looks much better on icons). Then, set font that is a few pixels smaller than the icon dimension. Compute the bounds of this letter, and set the position for this letter so that it will be centered in the icon's center:
letter = Character.toUpperCase(letter);
graphics.setFont(new Font("Arial", Font.BOLD, ICON_DIMENSION-5));
FontRenderContext frc = graphics.getFontRenderContext();
TextLayout mLayout = new TextLayout("" + letter, graphics.getFont(),
frc);
float x = (float) (-.5 + (ICON_DIMENSION - mLayout.getBounds()
.getWidth()) / 2);
float y = ICON_DIMENSION
- (float) ((ICON_DIMENSION - mLayout.getBounds().getHeight()) / 2);
Now we can draw the letter (in black color):
// draw the letter
graphics.drawString("" + letter, x, y);
Put optional plus sign in the top-right corner of the icon. First, create a semi-transparent white background for it, and then draw a red plus-sign two pixels thick:
// if collection - draw '+' sign
if (
int height = 6;
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
// set completely transparent
for (int col = 0; col < width; col++) {
for (int row = 0; row < height; row++) {
image.setRGB(col, row, 0x0);
}
}
Get the graphics context, set antialiasing hint and draw an arrow of specified color:
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// draw arrow
Polygon pol = new Polygon();
int ya = 3;
pol.addPoint(1, ya);
pol.addPoint(width / 2 + 3, ya);
pol.addPoint(width / 2 + 3, ya + 2);
pol.addPoint(width - 1, ya);
pol.addPoint(width / 2 + 3, ya - 2);
pol.addPoint(width / 2 + 3, ya);
graphics.setColor(color);
graphics.drawPolygon(pol);
And now for the tricky part - we have to compute the halo. Here, if an arrow pixel was completely opaque, it should have less transparent halo than arrow pixel that was only partly opaque (as on arrow's head for example). Here, we create another image with the halo footprint, and then draw the original arrow on top of it. Each arrow pixel contributes to its 8 neighbouring pixels. The final opacity of the halo footprint is the maximal opacity of all neighbouring arrow pixels:
// create semi-transparent halo around arrow (to make it stand
// out)
BufferedImage fimage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
// set completely transparent
for (int col = 0; col < width; col++) {
for (int row = 0; row < height; row++) {
fimage.setRGB(col, row, 0x0);
}
}
Graphics2D fgraphics = (Graphics2D) fimage.getGraphics();
for (int col = 0; col < width; col++) {
int xs = Math.max(0, col - 1);
int xe = Math.min(width - 1, col + 1);
for (int row = 0; row < height; row++) {
int ys = Math.max(0, row - 1);
int ye = Math.min(height - 1, row + 1);
int currColor = image.getRGB(col, row);
int opacity = (currColor >>> 24) & 0xFF;
if (opacity > 0) {
// mark all pixels in 3*3 area
for (int x = xs; x <= xe; x++) {
for (int y = ys; y <= ye; y++) {
int oldOpacity = (fimage.getRGB(x, y) >>> 24) & 0xFF;
int newOpacity = Math.max(oldOpacity, opacity);
// set semi-transparent white
int newColor = (newOpacity << 24) | (255 << 16) |
(255 << 8) | 255;
fimage.setRGB(x, y, newColor);
}
}
}
}
}
The final step - reduce the opacity of the halo by 30%. This is needed to reduce complete opacity around vertical and horizontal lines:
// reduce opacity of all pixels by 30%
for (int col = 0; col < width; col++) {
for (int row = 0; row < height; row++) {
int oldOpacity = (fimage.getRGB(col, row) >>> 24) & 0xFF;
int newOpacity = (int)(0.7*oldOpacity);
int newColor = (newOpacity << 24) | (255 << 16) |
(255 << 8) | 255;
fimage.setRGB(col, row, newColor);
}
}
Now, draw the original arrow on top of its halo
// draw the original arrow image on top of the halo fgraphics.drawImage(image, 0, 0, null);Going back to the original image (the letter and optional plus sign) - draw the arrow image on top of it:
BufferedImage arrowImage = getArrowImage(arrowColor, ICON_DIMENSION);
graphics.drawImage(arrowImage, 0, ICON_DIMENSION
- arrowImage.getHeight(), null);
Arguably, this was a lot of code. On the other hand, you can now create icon with any letter, any background, optional plus sign and optional arrow of any color. See how nice your application can become:IDE lockdown - give my Java backPosted by kirillcool on February 03, 2005 at 01:07 PM | Permalink | Comments (16)As aptly put in this article, there are two types of programmers, those that go with languages and those that go with tools. Given that we all go with Java, what about the tools? Looking at the market, we have at least five major players, Eclipse, JBuilder, NetBeans, JDeveloper and IntelliJ (which, unfortunately, has no free version). How to choose which one is the best for you? Here there are number of options:
At work, we use Eclipse. At home, I use IntelliJ. The more i program with both tools, the more i get used to the quick shortcuts (instead of lifting my fat fingers and moving that heavy mouse across the screen to the menus). Looking back, i see that most of the time i use eight different functionalities (in no particular order):
The more i use these features in Eclipse and IntelliJ, the more i get confused and use wrong combination in wrong IDE. It gets worse at the beginning of the programming session, but it doesn't get much better after that, when i try to tell myself to use the right combinations. I was thinking right now about the natural language analogy and why babies seem to easily pick two different languages at the same time. The analogy is out of place here: although the shortcuts refer to the same functionality, they are in the same Ctrl+Shift+Alt+Key language. It gets even worse in debugging:
So what, you may say, just stick with one IDE and you are set. When something better comes up, just move to it and forget about the old one. The problem here is that not only you present yourself (say in CV) as Java programmer, but now you are tempted to mention that you are expert in a particular IDE. The current trend of hiring J2EE programmers that only have experience in a particular application server may well project into IDEs. Although tying language and working environment may prove fruitful in the short run, you are restricting your options and the mindset in the long run. I vote with both hands for IDE. They are indespensable for debugging, refactoring, integration with third part extensions. I won't go back to emacs or pico. But each vendor implementing 200+ shortcuts that agree only on Ctrl+S being Save? Maybe we need a JSR for the key bindings. I won't even go into creating my own keymaps or choosing one of the predefined keymaps that simulate the rival IDEs. If they are there, why won't you just stick with them? The people will not choose the IDE because of the keymaps, they will only be more than happy to know that they can revert to your IDE without the need to learn new keymap set. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|