Transparent colors draw lines with weird artifacts - java

I have a code example that produces weird results when trying to draw lines with semitransparent colors. This only seems to happen for lines with an "upward" direction. I'm using an older JDK 1.8.0_102, but the issue is also reproduced on JDK 13.0.1. This sure seems like a bug, but maybe there's an explanation?
EDIT: The "weird artifacts" from the title are better described as "double width". Please read the comments below the question for more detail. TLDR; the problem persists - a weird behavior IS reproduced with the below code.
BufferedImage image = new BufferedImage(46, 46, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.setBackground(Color.WHITE);
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
graphics.setColor(new Color(0, 0, 0, 255));
graphics.drawLine(5, 20, 20, 5);
graphics.drawLine(25, 5, 40, 20);
graphics.setColor(new Color(0, 0, 0, 128));
graphics.drawLine(5, 40, 20, 25);
graphics.drawLine(25, 25, 40, 40);
graphics.dispose();
ImageIO.write(image, "png", new File("image.png"));
The result from this is the following image:
EDIT #2: During further investigation, I tried setting the Graphics2D stroke property of width 2, in hopes it would at least get me consistent behavior, but unfortunately, I was greeted with more inconsistency - "uppward" lines are arguably 2 pixels wide, while "downward" lines seem to be 3 pixels wide. This is the output of simply adding this line before drawing anything:
graphics.setStroke(new BasicStroke(2));

Related

Straight lines and text jagged after updating

I was away from my project for 6 months, coming back to it I upgraded to Eclipse 2020.12 which installs/uses the open java version 15. I did a lot of programming before noting this issues and comparing it to prior build but here's the issue...
My STRAIGHT lines and text are a lot more jagged than they used to be. I'm not sure how to show an image here that isn't online anymore, but a straight line has thicker and thinner areas to it, and text is really jagged.
Any thoughts? Is it the open JDK? I plan on distributing and don't want to use the commercial version and Eclipse no longer supports release 8 it says.
Thanks!
super.paintComponent(g);
String[] splitData = null; // holds string split into pieces
boolean success = false;
Graphics2D g2 = (Graphics2D)g;
AffineTransform transform = g2.getTransform(); // save current transform values
g.setColor(Color.black);
g2.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); // set width of stroke
String rgb = props.getProperty(CFG.colorBack.toString()); // set backcolor of plot
thisControl.setBackground(process.getColor(rgb));
g.drawLine(10, 30, 500, 30);
Font font = new Font("SanSerif", Font.BOLD, 20);
g2.setFont(font);
g.drawString("test", 300, 50);

How to create a transparent shape in the image

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)

Java 8 graphics glitch when stroking sub-pixel coordinates on Linux

It seems that stroking on sub-pixel coordinates became broken in Java 8.
I have three sets of cases, shown on screenshots (columns represent cases, rows represent different stroke widths) :
Java 7u51 (400% scale)
Java 8u60 (400% scale)
Fill and stroke on the same coordinates. Works as intended, stroked area is larger than the filling area.
Stroking is shrunk (by stroke width) and centered (by half of the width) to be inside bounds of the filling region. This part is broken in Java 8 for 1px stroke, where painting occurs on a sub-pixel coordinate (first row); 3px stroke doesn't have this problem (third row). It seems that 0.5 is rounded up for the 1px stroke.
Filling rectangle is shrunk an centered the same way of case 2. I need this on graphics, which support sub-pixel drawing, to make non-overlapping fill when cells are overlapping. Here you can see that fill operation rounds down 0.5 to 0, so it's only stroking problem.
The code is below:
import static java.awt.BasicStroke.*;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class TestCase
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Test case");
frame.setSize(115, 115);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new TestPanel());
frame.setVisible(true);
}
private static class TestPanel extends JPanel
{
TestPanel()
{
setOpaque(true);
}
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
g2.fill(getBounds());
Rectangle2D rect = new Rectangle2D.Double();
Color background = new Color(0, 255, 255);
Color border = new Color(255, 0, 0, 128);
Stroke STROKE_1PX = new BasicStroke(1, CAP_SQUARE, JOIN_MITER);
Stroke STROKE_2PX = new BasicStroke(2, CAP_SQUARE, JOIN_MITER);
Stroke STROKE_3PX = new BasicStroke(3, CAP_SQUARE, JOIN_MITER);
g2.translate(10, 10);
/**
* Filling and stroking by original coordinates
*/
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_1PX);
g2.draw(rect);
g2.translate(0, 35);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_2PX);
g2.draw(rect);
g2.translate(0, 35);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_3PX);
g2.draw(rect);
/**
* Stroking is shrunk to be inside the filling rect
*/
g2.translate(35, -70);
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
rect.setRect(0.5, 0.5, 24, 24);
g2.setColor(border);
g2.setStroke(STROKE_1PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
rect.setRect(1, 1, 23, 23);
g2.setColor(border);
g2.setStroke(STROKE_2PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
rect.setRect(1.5, 1.5, 22, 22);
g2.setColor(border);
g2.setStroke(STROKE_3PX);
g2.draw(rect);
/**
* Filling rect is additionally shrunk and centered
*/
g2.translate(35, -70);
rect.setRect(0.5, 0.5, 24, 24);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_1PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(1, 1, 23, 23);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_2PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(1.5, 1.5, 22, 22);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_3PX);
g2.draw(rect);
}
}
}
As I tested, Java 7 doesn't have this problem (tried on 7u51), Windows (8u77) and Mac (8u60) too. Tried on Ubuntu (8u60 and 8u77) and Linux Mint (8u60) on different machines and the bug was here.
Did anyone faced such issue? Is there any general workaround?
I cannot simply handle 1px case in places where stroking is used. It's because there is a lot of places and I'm working with different Graphics2D implementations and it seems, that from what I've used the issue reproduces only on SunGraphics2D. This means I need to use instanceOf in these places to not break common logic.
From bug report discussion:
It is an xrender bug -Dsun.java2d.xrender=false cures it.
I haven't checked this solution myself as I didn't received any notifications from bug reporting system besides that it was reviewed. And as it was Linux-only problem it was decided to wait for official fixes as there was not so many Linux users among our customers.
I would suggest you extend Graphics2D and add correctional measures there on the places where needed.
That way you can keep your main flow consistent, and add all other "logic" errors can be handled where they need to be handled.
This saves you the overhead of modifying N files and undoing those changes if it gets fixed, and keeping the logic where it should be.

Graphics.clearRect() won't work for me

Hi I've searched the web for a while but the solves I've encountered don't work for me. So here's my issue:
I'm making some graphs on my GUI by using Graphics and drawLine(), which works perfectly fine for me - the first time. When I tell my program to redraw another graph on the same area it just messes things up.
public void paintComponent(Graphics g) {
super.paintComponent(g);
int max = 100;
g.clearRect(0, 0, 205, 200); <--
g.drawRect(10, 10, 200, 200);
g.drawString(""+max, 0, 10);
g.drawLine(5, 110, 10, 110);
g.drawString("0", 0, 275);
...Drawing the actual graph here...
}
Now the idea was that everytime I draw this I first lay out a white surface, so it removes anything drawn earlier. But it doesn't work.
I'm quite new to Graphics so please don't hesitate to comment if I'm doing it completely wrong.
Thanks in advance.
-Alex

How do I set the background of a dynamically created BufferedImage instance to transparent?

I am trying to create a BufferedImage instance which contains a rounded rectangle of a certain colour and is transparent everywhere else.
I am using the following code to create the image
private BufferedImage createChromImage() {
BufferedImage I = new BufferedImage(350, 20, ColorSpace.TYPE_RGB);
Graphics2D g2 = I.createGraphics();
g2.setPaint(new GradientPaint(0, 0, Color.DARK_GRAY, 100,
100, Color.BLUE, false));
g2.fillRoundRect(0, 0, 350, 20, 10, 10);
return I;
}
I end up with a rounded rectangle on a black background, is there a way in which I can get it on a transparent background. I suspect it will require a different ColorSpace setting, but I not sure which.. any help is much appreciated.
You can't have a transparent background in an image that does not support transparency. RGB is a 24-bit image with no transparency. Instead, you want to use BufferedImage.TYPE_INT_ARGB as the argument to BufferedImage's constructor: that will give you an alpha channel to play with, which will allow transparency.

Categories