How can i combine 2 composites in to one? Let me explain:
BufferedImage copy = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = copy.createGraphics();
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
Now we have a transparent image.
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(img, 0, 0, null);
Now we have an exact copy of the "img" including the transparent areas.
g2d.setComposite(BlendComposite.getInstance(BlendingMode.MULTIPLY));
g2d.setColor(overlayColor);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
(BlendComposite from http://www.curious-creature.com/2006/09/20/new-blendings-modes-for-java2d/)
At this point the the multiply composite has given the image a nice color (overlayColor).
But the transparent areas now have the same color as overlayColor.
How can i prevent the transparent area's from getting the overlayColor?
#haraldK point me to a nice working solution:
Paint the original (img) over the copy (using the existing g2d) with
DstIn AlphaComposite?
I gave it a try just after the multiply step:
g2d.setComposite(AlphaComposite.DstIn);
g2d.drawImage(img, 0, 0, null);
And it now works, i have my transparency back!
For anyone with the same problem:
AlphaComposite.DstIn:
If pixels in the source and the destination overlap, the alpha from the source is applied to the destination pixels in the overlapping area. If the alpha = 1.0, the pixels in the overlapping area are unchanged; if the alpha is 0.0, pixels in the overlapping area are cleared.
http://docs.oracle.com/javase/tutorial/2d/advanced/compositing.html
Related
I have an image now I want to create a transparent shape in the image so that I can put another image in that shape.
I want to achieve something like this
Is there any way to do this programmatically in java or python. If not How can I do this manually.
I have linux environment and some image editor But I am not able to do this even manually.
So, the following makes use of:
The 2D Graphics API in particular, Compositing Graphics
The ImageIO API
So, starting with this...
I want to add this behind it (but peeking through)
The first thing we need to do is make a "alpha" based mask of the area we want to become transparent. For this, I'm using a RadialGradientPaint, as it allows me to produce a "fading" effect around the edges.
Now, I've already done all the math (used a simple image editor to work it out), so I know where I want the "hole" appear
BufferedImage city = ImageIO.read(new File("/Users/swhitehead/Downloads/City.jpg"));
BufferedImage mask = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
Color transparent = new Color(255, 0, 0, 0);
Color fill = Color.RED;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point2D.Double(955, 185),
185,
new float[]{0f, 0.75f, 1f},
new Color[]{transparent, transparent, fill});
g2d.setPaint(rgp);
g2d.fill(new Rectangle(0, 0, mask.getWidth(), mask.getHeight()));
g2d.dispose();
Next, we use a AlphaComposite to paint the mask over the original (city) image...
BufferedImage masked = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = masked.createGraphics();
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, masked.getWidth(), masked.getHeight());
g2d.drawImage(city, 0, 0, null);
g2d.setComposite(AlphaComposite.DstAtop);
g2d.drawImage(mask, 0, 0, null);
g2d.dispose();
This generates something like...
The "white" hole is actually transparent, it's just the page is also white :/
Finally, we combine the masked image with the background image...
BufferedImage composite = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = composite.createGraphics();
g2d.drawImage(background, city.getWidth() - background.getWidth(), 0, null);
g2d.drawImage(masked, 0, 0, null);
g2d.dispose();
to produce something like...
So, based on your description, this is what I assume you mean
From here:
# import the necessary packages
from __future__ import print_function
import numpy as np
import cv2
# load the image
image = cv2.imread("pic.jpg")
# loop over the alpha transparency values
for alpha in np.arange(0, 1.1, 0.1)[::-1]:
# create two copies of the original image -- one for
# the overlay and one for the final output image
overlay = image.copy()
output = image.copy()
# draw a red rectangle surrounding Adrian in the image
# along with the text "PyImageSearch" at the top-left
# corner
cv2.rectangle(overlay, (420, 205), (595, 385),
(0, 0, 255), -1)
cv2.putText(overlay, "PyImageSearch: alpha={}".format(alpha),
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 3)
# apply the overlay
cv2.addWeighted(overlay, alpha, output, 1 - alpha,
0, output)
# show the output image
print("alpha={}, beta={}".format(alpha, 1 - alpha))
cv2.imshow("Output", output)
cv2.waitKey(0)
I want to copy a part of BufferedImage, but the copying form is not a simple square but rather a square rotated on some angle. As an output I want to get the BufferedImage with width and height equals to the copying square size and contents from the initial image, where copying square intersects the initial image.
What is the simplest way to do it?
A simple approach would be to create a new image with the desired size, transform a Graphics2D of this image according to the selected square region, and then simply paint the original image into this graphics:
private static BufferedImage extractRegion(
BufferedImage source, Point2D center, Point2D size, double angleRad)
{
int w = (int)size.getX();
int h = (int)size.getY();
BufferedImage result =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = result.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.translate(size.getX() / 2, size.getY() / 2);
g.rotate(angleRad);
g.translate(-center.getX(), -center.getY());
g.drawImage(source, 0, 0, null);
g.dispose();
return result;
}
I have an off-screen BufferedImage, constructed with the type BufferedImage.TYPE_INT_ARGB. It can contain anything, and I'm looking for a way to (fairly efficiently) completely overwrite the image with transparent pixels, resulting in an 'invisible' image.
Using something like this:
(bufimg.getGraphics()).setColor(new Color(10, 10, 100, 0));
(bufimg.getGraphics()).fillRect (0, 0, x, y);
Has no effect. One possible method might be just to write over every pixel in the BufferedImage, but I'm not sure this is the best solution. How would you do it?
[edit]
The Graphics documentation advises against using clearRect for off-screen images, but I have tried it with the same results as above.
[edit2]
After experimenting with MeBigFatGuy's code (thanks!), it does clear an image. But it also stops further painting to that image (or appears to). This code for example:
BufferedImage img = new BufferedImage (600, 600, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.createGraphics ()
g.drawLine (100, 100, 500, 500);
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g.setComposite(composite);
g.setColor(new Color(0, 0, 0, 0));
g.fillRect(0, 0, 600, 600);
graphicsAI.setColor(new Color (10, 10, 10, 255));
graphicsAI.drawLine (100, 100, 500, 500);
Results in nothing seen on the image (I'm drawing the image to a JPanel). Is this something to do with the addition of alpha values?
After you clear the background with the CLEAR composite, you need to set it back to SRC_OVER to draw normally again. ex:
//clear
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g2.fillRect(0,0,256,256);
//reset composite
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
//draw
g2.setPaint(Color.RED);
g2.fillOval(50,50,100,100);
You could get the underlying int[] array of your BufferedImage (make sure to use a compatible format: that is, one that is backed by an int[]).
Then fill the int[] with ints whose alpha value are 0 (0 will do ; )
A System.arraycopy will be very fast.
You have to know that directly writing in the int[] is a lot faster than using setRGB.
Now BufferedImage are a bit of a black art in Java: depending on what you're doing and on which platform/JVM you're doing it, you may lose hardware acceleration (which may never have been there in the first place anyway). In addition to that, you may very well not care at all about hardware acceleration anyway because you may not be working on, say, a game requiring 60+ FPS to be playable etc.
This is a very complicated topic and there's more than one way to skin the BufferedImage cat. As far as I'm concerned I work directly in the int[] when I've got to mess at the pixel level because I think it makes much more sense than trying to use higher-level drawing primitives and I do really don't care about the potential lost of hardware acceleration.
If you cast the Graphics object to a Graphics2D object, you can set a Composite object thru
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, 10, 10);
For the sake of completeness, here is a working, testing, and fast function that is cross-platform compliant.
static public BufferedImage createTransparentBufferedImage(int width, int height) {
// BufferedImage is actually already transparent on my system, but that isn't
// guaranteed across platforms.
BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufferedImage.createGraphics();
// To be sure, we use clearRect, which will (unlike fillRect) totally replace
// the current pixels with the desired color, even if it's fully transparent.
graphics.setBackground(new Color(0, true));
graphics.clearRect(0, 0, width, height);
graphics.dispose();
return bufferedImage;
}
Despite you saying it doesn't work, I used clearRect quite fine.
Clears the specified rectangle by filling it with the background color
of the current drawing surface. This operation does not use the
current paint mode.
Beginning with Java 1.1, the background color of offscreen images may
be system dependent. Applications should use setColor followed by
fillRect to ensure that an offscreen image is cleared to a specific
color.
Fills the specified rectangle. The left and right edges of the
rectangle are at x and x + width - 1. The top and bottom edges are at
y and y + height - 1. The resulting rectangle covers an area width
pixels wide by height pixels tall. The rectangle is filled using the
graphics context's current color.
It is not clearly stated here that one will set the rectangle to the background color, while the other will paint with the foreground color on top of the current colors, but it's what it seems to do.
This is pure speculation, but I think the note about offscreen images relates to Graphics objects obtained from offscreen AWT components, as they are native. I can hardly imagine how the background color of a BufferedImage could be system dependent. As the API doc is for Graphics, this could be a generalization not applying to the BufferedImage case.
My testing code:
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage img = new BufferedImage(200, 300, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
//fill right half with opaque white
g.setColor(Color.WHITE);
g.fillRect(100, 0, 100, 300);
//leave top third as it is
//fill middle third with transparent color
g.setColor(new Color(0, true));
g.fillRect(0, 100, 200, 100);
//clear bottom third with transparent color
g.setBackground(new Color(0, true));
g.clearRect(0, 200, 200, 100);
g.dispose();
jf.add(new JLabel(new ImageIcon(img)));
jf.pack();
jf.setVisible(true);
the result is two white squares, top right. Where no white was painted, or clearRect was used to overwrite the white, the result is a light gray, the frame's default background color.
Performance-wise, it's regular drawing. arraycopy might well be faster, I don't know, but at least this is likely hardware accelerated just as any other drawing operation.
A plus point versus the array solution is a) no additional memory and b) independence from the color model; this should work no matter how the image was set up.
A minus point versus the Composite solution is that it only allows clearing rectangles; setting the composite allows you to clear any kind of shape.
Setting the background of the graphics Object seems to do the job:
g.setBackground(new Color(0, 0, 0, 0));
(at least when drawing images for scaling purposes)
I know the difference in memory usage between byte, unsigned short, and integer, but when it comes to a BufferedImage, is there a 'speed' difference between them?
I have been using the Image type in my code to store images, but I require an alpha layer. Using BufferedImage provides me with ARGB, but my code is /considerably/ slower after making the change from the Image type (and it was only changed for a few objects), so I'm looking for all the performance improvement I can get.
I'm not sure how stupid of a question this may be, so I thank you in response for any replies.
Tanaki,
I have found that, when in need of using an alpha channel in a BufferedImage, the best is to premultiply the alpha channel. For example:
// Create an ARGB image
BufferedImage bi = new BufferedImage(512, 512, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
// Fill the background (for illustration)
g.setColor(Color.black);
g.fill(new Rectangle(0, 0, 512, 512));
AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
// Keep the original composite
Composite original = g.getComposite();
g.setComposite(alpha);
// Paint with transparency
Rectangle r = new Rectangle(100, 200, 50, 50);
g.setColor(Color.magenta);
g.fillRect(r);
g.setComposite(original);
// ... paint further shapes or images as necessary
// ...
g.dispose();
// Convert to a premultiplied alpha image for fast painting to a Canvas
BufferedImage biPre = new BufferedImage(512, 512, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D gPre = biPre.createGraphics();
gPre.drawImage(bi, 0, 0, null);
gPre.dispose();
// clean up:
bi.flush();
// Now use biPre for painting to a Canvas, or a Component.
// ...
// Remember to flush it when done!
biPre.flush();
The reason for painting first to a TYPE_INT_ARGB is to ensure that all alpha gets painted as you expected (not pre-multiplied every time!). Then, when done, paint the whole image onto a TYPE_INT_ARGB_PRE, which is then able to bring the data to the screen with good speed.
I am working on creating an online image editing tool.Looking for some refernce how can I add an image with white space on right side.For example see this image
Presumably, you want to create a new image from an existing image, where the new image has white space on the left and right?
Suppose the unpadded image was a BufferedImage and is called 'image'. Suppose the amount of whitespace you want on each side is 'w'. What you want to do is create a new BufferedImage wider than the original, then paint the entire thing white, and finally draw the smaller image on top of it:
BufferedImage newImage = new BufferedImage(image.getWidth() + 2 * w, image.getHeight(), image.getType());
Graphics g = newImage.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, image.getWidth() + 2 * w, image.getHeight());
g.drawImage(image, w, 0, null);
g.dispose();
If anyone comes upon a similar problem, I would definitively recommend imgScalr. You can add padding with literally one line imageSource= Scalr.pad(imageSource,pad,Color.White);.
Create a new BufferedImage object of the right size; use Graphics.fillRect() to paint it white; draw the image into the top-left corner with drawImage(); then save your new image.