Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Anti-aliasing in image clipping to non-rectangular sub-images

54 views
Skip to first unread message

Kenneth P. Turvey

unread,
Aug 13, 2008, 8:55:10 PM8/13/08
to
I've got a simple problem that I'm sure someone in this group can help me
with. Let me say what I'm currently doing first.

I have an image that has some transparent portions and I want to cut a
circle out of the image. So first I trim it down to a square subimage
and then I go through it pixel by pixel and set the alpha channel to
clear for those pixels outside a circle with the sub-image's diameter.

This works great. I get close to exactly what I want. The customer has
added a new requirement that I need to deal with now. The sub-image
should have an anti-aliased border. That is, the pixels on the outside
of the circle shouldn't simply be on or off, but they should have an
alpha channel value that would most look the sub-image like a circle.

This is what you get when you use drawOval().

My thought is to use drawOval() to create a mask for the image, but I'm
not sure how to combine the mask with the image. I can't simply set the
alpha channel of the sub image to be the same as the alpha channel of my
mask since the image may have some transparency in it too. I'm quite
sure that Java has a way to combine these images. So how is it done?

Thanks.

--
Kenneth P. Turvey <kt-u...@squeakydolphin.com>
http://www.electricsenator.net

The only two things that are infinite in size are the universe
and human stupidity. And I'm not completely sure about the universe.
-- Albert Einstein

Peter Duniho

unread,
Aug 13, 2008, 10:07:14 PM8/13/08
to
On Wed, 13 Aug 2008 17:55:10 -0700, Kenneth P. Turvey
<kt-u...@squeakydolphin.com> wrote:

> I've got a simple problem that I'm sure someone in this group can help me
> with. Let me say what I'm currently doing first.
>
> I have an image that has some transparent portions and I want to cut a
> circle out of the image.

Do you want to remove a circular area from inside the image? Or do you
want to use only a ciruclar area from inside the image as your new image?

That is, the transformed image, will it be the original image with a hole
in it? Or will it be the original image with everything outside the
circular area excluded?

This description:

> So first I trim it down to a square subimage
> and then I go through it pixel by pixel and set the alpha channel to
> clear for those pixels outside a circle with the sub-image's diameter.

Makes me think you're taking a circular subset of the image, but other
parts of your message seem to contradict that. I'm a bit confused.

If I have understood the goal correctly, then it seems to me that the
easiest way to transform your original image is to draw it into a new
image, clipping to the circular shape you want.

For example, here's some code that works when just drawing into the
Graphics2D passed to the paintComponent() method:

// Where x, y, width, and height describe your circular area and image
is a reference
// to the image you want to clip

Area areaOval = new Area(new Arc2D.Double(x, y, width, height, 0, 360,
Arc2D.PIE));
Shape shapeClipSave = gfx2.getClip();

gfx2.setClip(areaOval);
gfx2.drawImage(image, 0, 0, null);
gfx2.setClip(shapeClipSave);

You should be able to use basically the same thing drawing into a
Graphics2D instance you get from a new BufferedImage instance. Just make
sure you've enabled anti-aliased rendering, and when the image is drawn
clipped into the new BufferedImage, the edges should wind up anti-aliased.

Pete

Kenneth P. Turvey

unread,
Apr 27, 2011, 11:48:02 AM4/27/11
to
To: comp.lang.java.gui,comp.l

On Wed, 13 Aug 2008 19:07:14 -0700, Peter Duniho wrote:

> On Wed, 13 Aug 2008 17:55:10 -0700, Kenneth P. Turvey
> <kt-u...@squeakydolphin.com> wrote:
>

>> I've got a simple problem that I'm sure someone in this group can help
>> me with. Let me say what I'm currently doing first.
>>
>> I have an image that has some transparent portions and I want to cut a
>> circle out of the image.
>

> Do you want to remove a circular area from inside the image? Or do you
> want to use only a ciruclar area from inside the image as your new
> image?
>
> That is, the transformed image, will it be the original image with a
> hole in it? Or will it be the original image with everything outside
> the circular area excluded?
>
> This description:
>

>> So first I trim it down to a square subimage and then I go through it
>> pixel by pixel and set the alpha channel to clear for those pixels
>> outside a circle with the sub-image's diameter.
>

> Makes me think you're taking a circular subset of the image, but other
> parts of your message seem to contradict that. I'm a bit confused.
>
> If I have understood the goal correctly, then it seems to me that the
> easiest way to transform your original image is to draw it into a new
> image, clipping to the circular shape you want.
>
> For example, here's some code that works when just drawing into the
> Graphics2D passed to the paintComponent() method:
>
> // Where x, y, width, and height describe your circular area and
> image
> is a reference
> // to the image you want to clip
>
> Area areaOval = new Area(new Arc2D.Double(x, y, width, height, 0,
> 360,
> Arc2D.PIE));
> Shape shapeClipSave = gfx2.getClip();
>
> gfx2.setClip(areaOval);
> gfx2.drawImage(image, 0, 0, null);
> gfx2.setClip(shapeClipSave);
>
> You should be able to use basically the same thing drawing into a
> Graphics2D instance you get from a new BufferedImage instance. Just
> make sure you've enabled anti-aliased rendering, and when the image is
> drawn clipped into the new BufferedImage, the edges should wind up
> anti-aliased.
>
> Pete

I tried what you suggested, but with disappointing results. Here's the
altered method:

private void clearOutsideCircle() {
assert image.getWidth() == image.getHeight() : "Image should be
square";
int radius = image.getWidth() / 2;
Area areaOval = new Area(new Arc2D.Double(0, 0, image.getWidth(),
image.getHeight(), 0, 360, Arc2D.PIE));
BufferedImage newImage = new BufferedImage(image.getWidth(),
image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = newImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setClip(areaOval);
graphics.drawImage(image, 0, 0, null);
image = newImage;
}

After this the image still doesn't look anti-aliased. It seems like the
clipping region is either on or off.

Do you see anything in the code that might be the problem? Any other
suggestions?

Unix gives you just enough rope to hang yourself -- and then a couple
of more feet, just to be sure.
-- Eric Allman

---
* Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

Kenneth P. Turvey

unread,
Aug 13, 2008, 11:02:09 PM8/13/08
to
On Wed, 13 Aug 2008 19:07:14 -0700, Peter Duniho wrote:

I could go pixel by pixel through the image and use the minimum alpha of
the mask and the image itself. That would be pretty close to what I
want, but it seems ugly. Isn't there a way to combine an image with an
alpha mask in an easy way?

Thanks.

If you pick up a starving dog and make him prosperous, he will not
bite you. This is the principal difference between a man and a dog.
-- Mark Twain

John B. Matthews

unread,
Apr 27, 2011, 11:48:02 AM4/27/11
to
To: comp.lang.java.gui,comp.l
In article <48a39fd0$0$2158$ec3e...@news.usenetmonster.com>,

Some implementations do better than others at honoring the hints. Try
this with any .jpg:

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class Clip extends JPanel {

private BufferedImage image;
private Ellipse2D.Double border = new Ellipse2D.Double();

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Clip();
}
});
}

public Clip() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
try {
image = ImageIO.read(new File("clip.jpg"));
} catch (IOException ioe) {
System.err.println(ioe);
System.exit(1);
}
frame.setSize(new Dimension(
image.getWidth(), image.getHeight()));
frame.setVisible(true);
}

public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

int width = getWidth();
int height = getHeight();
g2d.setPaint(Color.BLUE);
g2d.fillRect(0, 0, width, height);
border.setFrame(0, 0, width, height);
g2d.setClip(border);
g2d.drawImage(image, 0, 0, width, height, this);
}
}
--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews

Knute Johnson

unread,
Aug 13, 2008, 11:52:48 PM8/13/08
to
Kenneth P. Turvey wrote:
> I've got a simple problem that I'm sure someone in this group can help me
> with. Let me say what I'm currently doing first.
>
> I have an image that has some transparent portions and I want to cut a
> circle out of the image. So first I trim it down to a square subimage
> and then I go through it pixel by pixel and set the alpha channel to
> clear for those pixels outside a circle with the sub-image's diameter.
>
> This works great. I get close to exactly what I want. The customer has
> added a new requirement that I need to deal with now. The sub-image
> should have an anti-aliased border. That is, the pixels on the outside
> of the circle shouldn't simply be on or off, but they should have an
> alpha channel value that would most look the sub-image like a circle.
>
> This is what you get when you use drawOval().
>
> My thought is to use drawOval() to create a mask for the image, but I'm
> not sure how to combine the mask with the image. I can't simply set the
> alpha channel of the sub image to be the same as the alpha channel of my
> mask since the image may have some transparency in it too. I'm quite
> sure that Java has a way to combine these images. So how is it done?
>
> Thanks.
>

I'm assuming you want to draw an image inside of an anti-aliased ring of
background color. If that's not what you want ignore the code below.

import java.awt.*;
import java.awt.event.*;


import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class FuzzyHole extends JPanel {
BufferedImage image,mask;

public FuzzyHole() {
try {
// load the image
image = ImageIO.read(new File("kittens.jpg"));
// setPreferredSize to size of image
setPreferredSize(new Dimension(
image.getWidth(),image.getHeight()));
// create mask image
mask = new BufferedImage(image.getWidth(),image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = mask.createGraphics();
// turn on anti-aliasing
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// fill with background color
g.setColor(Color.WHITE);
g.fillRect(0,0,image.getWidth(),image.getHeight());
// set composite to clear
g.setComposite(AlphaComposite.Clear);
// punch alpha whole in mask

g.fillOval(image.getWidth()/2-100,image.getHeight()/2-100,200,200);
// set composite to SrcOver
g.setComposite(AlphaComposite.SrcOver);
// set stroke a little bigger to avoid rings
g.setStroke(new BasicStroke(2f));
// draw concentric circles with increasing alpha
for (int i=0; i<23; i++) {
// background color with alpha
g.setColor(new Color(255,255,255,255-i*11));

g.drawOval(image.getWidth()/2-100+i,image.getHeight()/2-100+i,
200-i*2,200-i*2);
}
g.dispose();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}

public void paintComponent(Graphics g) {
g.drawImage(image,0,0,null);
g.drawImage(mask,0,0,null);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {

JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FuzzyHole fh = new FuzzyHole();
f.add(fh,BorderLayout.CENTER);
f.pack();
f.setVisible(true);
}
});
}
}

--

Knute Johnson
email s/nospam/knute2008/

--
Posted via NewsDemon.com - Premium Uncensored Newsgroup Service
------->>>>>>http://www.NewsDemon.com<<<<<<------
Unlimited Access, Anonymous Accounts, Uncensored Broadband Access

Knute Johnson

unread,
Aug 13, 2008, 11:57:55 PM8/13/08
to
Kenneth P. Turvey wrote:
> I've got a simple problem that I'm sure someone in this group can help me
> with. Let me say what I'm currently doing first.
>
> I have an image that has some transparent portions and I want to cut a
> circle out of the image. So first I trim it down to a square subimage
> and then I go through it pixel by pixel and set the alpha channel to
> clear for those pixels outside a circle with the sub-image's diameter.
>
> This works great. I get close to exactly what I want. The customer has
> added a new requirement that I need to deal with now. The sub-image
> should have an anti-aliased border. That is, the pixels on the outside
> of the circle shouldn't simply be on or off, but they should have an
> alpha channel value that would most look the sub-image like a circle.
>
> This is what you get when you use drawOval().
>
> My thought is to use drawOval() to create a mask for the image, but I'm
> not sure how to combine the mask with the image. I can't simply set the
> alpha channel of the sub image to be the same as the alpha channel of my
> mask since the image may have some transparency in it too. I'm quite
> sure that Java has a way to combine these images. So how is it done?
>
> Thanks.
>

I just answered this post in comp.lang.gui and now I see the back and
forth. If all you want is an anti-aliased mask use the code below.
Don't mess with your image, it's just too much work. If you want a
fancier matte around the image see the other post.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class test extends JPanel {
BufferedImage image,mask;

public test() {


try {
// load the image
image = ImageIO.read(new File("kittens.jpg"));
// setPreferredSize to size of image
setPreferredSize(new Dimension(
image.getWidth(),image.getHeight()));
// create mask image
mask = new BufferedImage(image.getWidth(),image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = mask.createGraphics();
// turn on anti-aliasing
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// fill with background color
g.setColor(Color.WHITE);
g.fillRect(0,0,image.getWidth(),image.getHeight());
// set composite to clear
g.setComposite(AlphaComposite.Clear);
// punch alpha whole in mask

g.fillOval(image.getWidth()/2-100,image.getHeight()/2-100,200,200);

g.dispose();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}

public void paintComponent(Graphics g) {
g.drawImage(image,0,0,null);
g.drawImage(mask,0,0,null);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

test fh = new test();

Peter Duniho

unread,
Aug 14, 2008, 1:45:48 AM8/14/08
to
On Wed, 13 Aug 2008 20:00:32 -0700, Kenneth P. Turvey
<kt-u...@squeakydolphin.com> wrote:

> [...]


> After this the image still doesn't look anti-aliased. It seems like the
> clipping region is either on or off.
>
> Do you see anything in the code that might be the problem? Any other
> suggestions?

Nope. Your code looks fine to me (not sure why you want ABGR instead of
ARGB, but either works fine on my computer).

Using basically the same code here, it generates an anti-aliased circular
subset of the original image, just as intended.

Java 5, Mac OS 10.4.11

My best guess: you are either using some Java implementation that has poor
or non-existent anti-aliasing support, or you're just mistaken about
whether the results are anti-aliased or not. I admit, by default I'd
assume that neither is the case, but given that the code is simple and it
works fine here, I've got to assume one or the other is actually the case
after all. :)

If you're not using an up-to-date, mainstream Java implementation, then
you should try the same code on one that is. If you already are (or if
doing so produces the same results), then maybe you can post an example
image that's been processed by your code somewhere that we can download
and look at it.

Pete

Knute Johnson

unread,
Aug 14, 2008, 1:59:01 AM8/14/08
to

Graphics2D.clip() does not anti-alias.

John B. Matthews

unread,
Aug 14, 2008, 2:05:55 AM8/14/08
to
In article <48a3ad42$0$4044$b9f6...@news.newsdemon.com>,
Knute Johnson <nos...@rabbitbrush.frazmtn.com> wrote:

[...]


> I just answered this post in comp.lang.gui and now I see the back and
> forth. If all you want is an anti-aliased mask use the code below.
> Don't mess with your image, it's just too much work. If you want a
> fancier matte around the image see the other post.

[...]

Knute: Thanks for both examples! I hadn't seen AlphaComposite before;
it's way better than looping through WritableRaster.

[Followup-To comp.lang.java.gui]

Peter Duniho

unread,
Aug 14, 2008, 2:22:33 AM8/14/08
to
On Wed, 13 Aug 2008 22:59:01 -0700, Knute Johnson
<nos...@rabbitbrush.frazmtn.com> wrote:

> Graphics2D.clip() does not anti-alias.

It does on my Mac. YMMV.

Kenneth P. Turvey

unread,
Aug 14, 2008, 4:03:44 AM8/14/08
to

Any ideas on how to do this then?

What is a gun toting economist on drugs?

Kenneth P. Turvey

unread,
Aug 14, 2008, 4:05:15 AM8/14/08
to

I'm not seeing it here either. Let me take a closer look. I should be
able to see the anti-aliasing even on a simple mat background shouldn't I?

Luge strategy? Lie flat and try not to die.
-- Tim Steeves

Kenneth P. Turvey

unread,
Aug 14, 2008, 4:13:16 AM8/14/08
to
On Wed, 13 Aug 2008 20:57:55 -0700, Knute Johnson wrote:

> I just answered this post in comp.lang.gui and now I see the back and
> forth. If all you want is an anti-aliased mask use the code below.
> Don't mess with your image, it's just too much work. If you want a
> fancier matte around the image see the other post.
>
> import java.awt.*;
> import java.awt.event.*;

[Snip]

Thanks. I can create the alpha mask. I understand how to do that. The
problem I'm having is that I have an image with some alpha in it and now
I want to apply the mask to the image. The idea is to get an image that
is circular, where everything outside the circle is blank, and where the
edges of the image are anti-aliased so when it is placed on top of a
background, it looks like a neat circle without jagged edges.

I'd like to be able to do this without going through the image pixel by
pixel, but that might be the best way to accomplish it, I guess.

A computer lets you make more mistakes faster than any invention in
human history with the possible exceptions of handguns and tequila.
-- Mitch Ratliffe, Technology Review, April, 1992

Peter Duniho

unread,
Aug 14, 2008, 5:14:10 AM8/14/08
to
On Thu, 14 Aug 2008 01:05:15 -0700, Kenneth P. Turvey
<kt-u...@squeakydolphin.com> wrote:

> On Wed, 13 Aug 2008 23:22:33 -0700, Peter Duniho wrote:
>
>> On Wed, 13 Aug 2008 22:59:01 -0700, Knute Johnson
>> <nos...@rabbitbrush.frazmtn.com> wrote:
>>
>>> Graphics2D.clip() does not anti-alias.
>>
>> It does on my Mac. YMMV.
>
> I'm not seeing it here either. Let me take a closer look. I should be
> able to see the anti-aliasing even on a simple mat background shouldn't
> I?

I'm not entirely sure what you mean. Did you mean "matte background", as
in a single-color background?

If so, then assuming the background is a different color than that on
which you're viewing the transformed (clipped) version, yes...I'd think
you should be able to note the anti-aliasing, if it's happening.

Now, that said...spurred by yours and Knute's comments, I tried
_disabling_ anti-aliasing in my code, and I still get anti-aliased
rendering. This makes me suspect that the anti-aliasing is being done at
the platform level, not under the control of the Java run-time itself.

So, I guess the best answer I can provide is "run your code on a Mac". :)

As a solution to your immediate problem, you may find that overkill. I
can't say that I'd disagree. But it's all I can come up with at the
moment. :)

Pete

John B. Matthews

unread,
Aug 14, 2008, 10:34:51 AM8/14/08
to
In article <op.ufvc9...@petes-computer.local>,
"Peter Duniho" <NpOeS...@nnowslpianmk.com> wrote:

> On Thu, 14 Aug 2008 01:05:15 -0700, Kenneth P. Turvey
> <kt-u...@squeakydolphin.com> wrote:
>
> > On Wed, 13 Aug 2008 23:22:33 -0700, Peter Duniho wrote:
> >
> >> On Wed, 13 Aug 2008 22:59:01 -0700, Knute Johnson
> >> <nos...@rabbitbrush.frazmtn.com> wrote:
> >>
> >>> Graphics2D.clip() does not anti-alias.
> >>
> >> It does on my Mac. YMMV.

[...]


> Now, that said...spurred by yours and Knute's comments, I tried
> _disabling_ anti-aliasing in my code, and I still get anti-aliased
> rendering.

I get the same effect, looking with Pixie at high magnification.

> This makes me suspect that the anti-aliasing is being done at the
> platform level, not under the control of the Java run-time itself.

I think so. Setting -Dapple.awt.graphics.UseQuartz=false prevents the
anti-aliasing even when the hint is on.

[...]

Knute Johnson

unread,
Aug 14, 2008, 11:20:49 AM8/14/08
to
Kenneth P. Turvey wrote:
> On Wed, 13 Aug 2008 20:57:55 -0700, Knute Johnson wrote:
>
>> I just answered this post in comp.lang.gui and now I see the back and
>> forth. If all you want is an anti-aliased mask use the code below.
>> Don't mess with your image, it's just too much work. If you want a
>> fancier matte around the image see the other post.
>>
>> import java.awt.*;
>> import java.awt.event.*;
> [Snip]
>
> Thanks. I can create the alpha mask. I understand how to do that. The
> problem I'm having is that I have an image with some alpha in it and now
> I want to apply the mask to the image. The idea is to get an image that
> is circular, where everything outside the circle is blank, and where the
> edges of the image are anti-aliased so when it is placed on top of a
> background, it looks like a neat circle without jagged edges.
>
> I'd like to be able to do this without going through the image pixel by
> pixel, but that might be the best way to accomplish it, I guess.

Well there's a truth that it took me a while to learn and that is that
you can't draw alpha into an image.

So if you must do it that way, replace the alpha around your image, in
concentric circles. Decrease the alpha as you move away from the center.

I'm curious though why you can't use the mask approach even with your
image with alpha. Maybe show us the image?

Daniele Futtorovic

unread,
Aug 14, 2008, 1:51:09 PM8/14/08
to
On 14/08/2008 17:20, Knute Johnson allegedly wrote:
> Well there's a truth that it took me a while to learn and that is that
> you can't draw alpha into an image.

Must be jolly interesting if it's something that took you a while to
learn. So... what does that sentence mean?

--
DF.

Knute Johnson

unread,
Aug 14, 2008, 8:03:04 PM8/14/08
to

If you have an image, you basically can't draw on it (with the usual
Graphics(2D) methods) and reduce the alpha value of the pixels in the
image. Say you have a pixel that is white with an alpha of 255. There
is no way to draw on the image to change that pixel's alpha to another
value. Unless of course you use an AlphaComposite with a value of
Clear. That will change it to a black pixel with an alpha of 0.

Daniele Futtorovic

unread,
Aug 14, 2008, 9:24:52 PM8/14/08
to
On 15/08/2008 02:03, Knute Johnson allegedly wrote:
> Daniele Futtorovic wrote:
>> On 14/08/2008 17:20, Knute Johnson allegedly wrote:
>>> Well there's a truth that it took me a while to learn and that is
>>> that you can't draw alpha into an image.
>>
>> Must be jolly interesting if it's something that took you a while to
>> learn. So... what does that sentence mean?
>>
>
> If you have an image, you basically can't draw on it (with the usual
> Graphics(2D) methods) and reduce the alpha value of the pixels in the
> image. Say you have a pixel that is white with an alpha of 255. There
> is no way to draw on the image to change that pixel's alpha to another
> value.

I thought that was what the AlphaComposites were for...

> Unless of course you use an AlphaComposite with a value of
> Clear. That will change it to a black pixel with an alpha of 0.

... ah.

To get this right, are you positively sure there is no way? Not even
playing with XOR paint?

--
DF.

Knute Johnson

unread,
Aug 14, 2008, 11:36:57 PM8/14/08
to

I can't swear that XOR paint won't but I couldn't make it work. Of
course that is not definitive :-).

John B. Matthews

unread,
Aug 15, 2008, 12:51:52 AM8/15/08
to
In article <48a4f9d9$0$4009$b9f6...@news.newsdemon.com>,
Knute Johnson <nos...@rabbitbrush.frazmtn.com> wrote:

> Daniele Futtorovic wrote:
> > On 15/08/2008 02:03, Knute Johnson allegedly wrote:
> >> Daniele Futtorovic wrote:
> >>> On 14/08/2008 17:20, Knute Johnson allegedly wrote:
> >>>> Well there's a truth that it took me a while to learn and that
> >>>> is that you can't draw alpha into an image.
> >>>
> >>> Must be jolly interesting if it's something that took you a while
> >>> to learn. So... what does that sentence mean?
> >>
> >> If you have an image, you basically can't draw on it (with the
> >> usual Graphics(2D) methods) and reduce the alpha value of the
> >> pixels in the image. Say you have a pixel that is white with an
> >> alpha of 255. There is no way to draw on the image to change that
> >> pixel's alpha to another value.
> >
> > I thought that was what the AlphaComposites were for...
> >
> >> Unless of course you use an AlphaComposite with a value of Clear.
> >> That will change it to a black pixel with an alpha of 0.
> >
> > ... ah.
> >
> > To get this right, are you positively sure there is no way? Not
> > even playing with XOR paint?
>
> I can't swear that XOR paint won't but I couldn't make it work. Of
> course that is not definitive :-).

I've been tinkering with Knute's examples and reading the API. I'd be
grateful if someone could comment critically on what I think I
understand:

0. You can paint with any color you want: opaque, translucent,
transparent. That does not modify the alpha component of the destination
pixel, except to overwrite the pixel with entirely new ARGB values.
(That's how I interpreted, "can't draw alpha into an image.")

1. You can change the alpha component of a pixel in a BufferedImage
using get/setRGB() or getRaster(), but you have to loop through the
pixels to effect the change.

2. Each of the 12 Composites in AlphaComposite implements a rule for
combining source and destination pixels when drawing. When you draw in a
Graphics2D that has a Composite set, each destination pixel is set
according to the Composite's rule and the alpha component of the chosen
color.

3. The rule in AlphaComposite.Clear says, "Anything that's painted
becomes transparent black [Color(0, true)] in the destination image," as
if it were a stencil with the painted part punched out. When this
stencil is later drawn, any underlying image pixels "show through" the
transparent ones.

4. The rule in AlphaComposite.Src says, "Any painted pixel gets set to
the the current color and alpha." Knute used SrcOver for the rings of
fuzziness, but Src is identical for a transparent destination.

Knute Johnson

unread,
Aug 15, 2008, 1:41:48 PM8/15/08
to

Assuming you are using the default AlphaComposite which is SrcOver.

> 1. You can change the alpha component of a pixel in a BufferedImage
> using get/setRGB() or getRaster(), but you have to loop through the
> pixels to effect the change.

Yes.

> 2. Each of the 12 Composites in AlphaComposite implements a rule for
> combining source and destination pixels when drawing. When you draw in a
> Graphics2D that has a Composite set, each destination pixel is set
> according to the Composite's rule and the alpha component of the chosen
> color.

Yes. And you can change the alpha value of an image with the right
AlphaComposite.

> 3. The rule in AlphaComposite.Clear says, "Anything that's painted
> becomes transparent black [Color(0, true)] in the destination image," as
> if it were a stencil with the painted part punched out. When this
> stencil is later drawn, any underlying image pixels "show through" the
> transparent ones.

Yes.

> 4. The rule in AlphaComposite.Src says, "Any painted pixel gets set to
> the the current color and alpha." Knute used SrcOver for the rings of
> fuzziness, but Src is identical for a transparent destination.

Yes. For the fuzzy ring I used SrcOver because that is the default
AlphaComposite. In the FuzzyHole example the result is the same if you
use Src, DstOver, or DstAtop. You would have to check actual pixel
values to confirm that but they look the same.

IBM has an excellent article on the Porter-Duff rules and a Java program
you can use to experiment with.

http://www.ibm.com/developerworks/java/library/j-mer0918/

These rules while looking simple are very complicated and the results
are not (at least to me) always obvious.

If you play with that CompositeIt program, check the 4 AlphaComposites
above. Be sure the set the destination alpha to 0 to simulate the alpha
hole in the mask image.

So while my "truth" that you can't draw alpha into an image isn't 100%
true, it pretty much sums up what you have to do.

John B. Matthews

unread,
Aug 15, 2008, 2:52:36 PM8/15/08
to
In article <48a5bfdb$0$4005$b9f6...@news.newsdemon.com>,
Knute Johnson <nos...@rabbitbrush.frazmtn.com> wrote:

[...]

Thank you for commenting on this.

> IBM has an excellent article on the Porter-Duff rules and a Java
> program you can use to experiment with.
>
> http://www.ibm.com/developerworks/java/library/j-mer0918/

Excellent! The graphics look very much like the table in Porter and
Duff's article:

<http://keithp.com/~keithp/porterduff/p253-porter.pdf>

[...]

> So while my "truth" that you can't draw alpha into an image isn't
> 100% true, it pretty much sums up what you have to do.

I see the truth in it.

Knute Johnson

unread,
Aug 15, 2008, 2:59:22 PM8/15/08
to
John B. Matthews wrote:
> In article <48a5bfdb$0$4005$b9f6...@news.newsdemon.com>,
> Knute Johnson <nos...@rabbitbrush.frazmtn.com> wrote:
>
> [...]
>
> Thank you for commenting on this.
>
>> IBM has an excellent article on the Porter-Duff rules and a Java
>> program you can use to experiment with.
>>
>> http://www.ibm.com/developerworks/java/library/j-mer0918/
>
> Excellent! The graphics look very much like the table in Porter and
> Duff's article:
>
> <http://keithp.com/~keithp/porterduff/p253-porter.pdf>

That's a good one I haven't seen, I've saved it!

>> So while my "truth" that you can't draw alpha into an image isn't
>> 100% true, it pretty much sums up what you have to do.
>
> I see the truth in it.
>

Thanks,

Daniele Futtorovic

unread,
Apr 27, 2011, 11:48:08 AM4/27/11
to
To: comp.lang.java.gui

On 15/08/2008 02:03, Knute Johnson allegedly wrote:
> Daniele Futtorovic wrote:
>> On 14/08/2008 17:20, Knute Johnson allegedly wrote:
>>> Well there's a truth that it took me a while to learn and that is
>>> that you can't draw alpha into an image.
>>
>> Must be jolly interesting if it's something that took you a while to
>> learn. So... what does that sentence mean?
>
> If you have an image, you basically can't draw on it (with the usual
> Graphics(2D) methods) and reduce the alpha value of the pixels in the
> image. Say you have a pixel that is white with an alpha of 255. There
> is no way to draw on the image to change that pixel's alpha to another
> value. Unless of course you use an AlphaComposite with a value of
> Clear. That will change it to a black pixel with an alpha of 0.

Hmmm. Having played around, and more importantly thought about it a bit,
I'll have to concur with your assessment.

For the record, here's the last state of my experiments. It achieves the
desired effect, but it's cheating (drawing twice and clipping in-between):

<code>
package scratch;

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;

import javax.swing.*;
import javax.imageio.*;

/**
*
* @author da.futt
*/
public class XORTest
{
public static void main(String[] ss) {
EventQueue.invokeLater(
new Loader() {
public BufferedImage loadImage() throws Exception {
return
ImageIO.read(XORTest.class.getResource("animage.png"));
}
});
}

private static abstract class Loader
implements Runnable
{
public abstract BufferedImage loadImage()
throws Exception;

private Image prepareImage()
throws Exception
{
BufferedImage i = loadImage();

BufferedImage bi = new BufferedImage(i.getWidth(),
i.getHeight(),
BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g = bi.createGraphics();

g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

int d = Math.min(i.getWidth(), i.getHeight()) >> 1;

Shape ell = new Ellipse2D.Float((i.getWidth() - d) >> 1,
(i.getHeight() - d) >> 1, d, d);

Shape oldclip = g.getClip();

g.clip(ell);

g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, .3f));
g.drawImage(i, 0, 0, null);

g.setClip(oldclip);

g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
g.drawImage(i, 0, 0, null);

g.dispose();

return bi;
}

public void run() {
try {
run0();
}
catch (Exception x) {
x.printStackTrace();
}
}

private void run0()
throws Exception
{
JFrame f = new JFrame("XOR paint test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new BackgroundPanel(new BorderLayout()));

f.getContentPane().setPreferredSize(new Dimension(300, 200));

f.getContentPane().add(new JLabel(new ImageIcon(
prepareImage())), BorderLayout.CENTER);

f.pack();

f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

private static class BackgroundPanel
extends JPanel
{
private static final int MARMOR_SIDE_PX = 20;

public BackgroundPanel() {
super();
}

public BackgroundPanel(LayoutManager layout) {
super(layout);
}

@Override
public void paintComponent(Graphics g) {
Color white = new Color(0xf0f0f0),
black = new Color(0x0f0f0f);

int x = 0, y = 0, line = 0, col = 0;

for (final int h = getHeight(), w = getWidth(); y < h;
y += MARMOR_SIDE_PX, line++, col = 0, x = 0)
{
for (; x < w; x += MARMOR_SIDE_PX, col++) {
g.setColor((col + line) % 2 == 0 ? black : white);
g.fillRect(x, y, MARMOR_SIDE_PX, MARMOR_SIDE_PX);
}
}
}
}
}
</code>

--
DF.

Knute Johnson

unread,
Aug 15, 2008, 5:14:07 PM8/15/08
to

I like that! And the checkerboard pattern is a great idea too. I think
I'll steal that one for the future :-).

Daniele Futtorovic

unread,
Aug 15, 2008, 5:27:59 PM8/15/08
to
On 15/08/2008 23:14, Knute Johnson allegedly wrote:
>
> I like that!

Thanks. I've just modified it to draw a String ("WATERMARK" ;) ) across
the image. Went all right. Basically, it will work for anything you can
get a java.awt.Shape for.

> And the checkerboard pattern is a great idea too. I think
> I'll steal that one for the future :-).

Ah, right, "checkerboard". Now how did I come up with marble? :)

--
DF.

John B. Matthews

unread,
Aug 16, 2008, 12:06:17 AM8/16/08
to
In article <g84sd1$jjr$1...@registered.motzarella.org>,
Daniele Futtorovic <da.fut...@laposte.invalid> wrote:

When I saw MARMOR_SIDE_PX, I assumed you had a marble chess board.
[Guessing by what marmor means in Latin.]

0 new messages