It doesn't seem like a change that needs a project.
I would make sure that call to colorModel.getRGB(..) isn't needed and I
think that you aren't doing that.
Basically it needs to be sRGB already (as that is the target color space).
-phil.
On 9/4/25 1:37 PM, Daniel Gredler wrote:
Hi Jeremy, Laurent,
Thanks for having a look. What sorts of changes are you thinking of
when you propose a separate project?
For myself, I don't really have a bigger-picture vision of systematic
changes that I'd like to make. Similar to JDK-8337681, JDK-8344668 and
JDK-8356814, this suggestion is just an ad hoc optimization
opportunity that came up based on spending some time with a profiler.
My takeaway right now is that this specific suggestion doesn't look
obviously stupid to anyone, and is worth triple checking on my end. If
my local sanity checks all come back green, I would create a small
issue in JBS and submit a PR, and then let the review process carry on
as usual. It can take a little time, but it seems to me that useful
optimizations, submitted individually, can be incorporated without too
many issues.
Which takes me back to my original question -- what type of change(s)
are you thinking about, where proving things out in a separate project
is necessary?
Take care,
Daniel
On Sat, Aug 30, 2025 at 11:47 AM Laurent Bourgès
<[email protected]> wrote:
Hi guys,
I do love optimizing java2d, so Ido support & sponsor your
buffered image (ARGB PRE or not) changes.
Let's start a github project to start this concrete patch... as I
did for the marlin renderer or we could use the marlin repository
to host your changes to java.awt or java2d packages.
See jdk (25?) branch:
https://github.com/bourgesl/marlin-renderer/tree/jdk/src/main/java/sun/java2d
Cheers,
Laurent
-------- Message d'origine --------
Le 22/08/2025 14:04, Daniel Gredler a écrit :
Hi all,
`BufferedImage.getRGB(int, int, int, int, int[], int, int)` is
often used for processing of individual image pixels. A common
pattern is to loop through each row of pixels, calling this
method once per row to populate the row pixel `int[]` and then
process it.
There are many types of `BufferedImage`, but one of the most
common types is `TYPE_INT_ARGB`. Based on a quick search on
GitHub, about one third of all BufferedImages are of this type
[1]. This is also the representation
which `BufferedImage.getRGB(int, int, int, int, int[], int,
int)` uses for its output.
I think there may be an opportunity here
(in `BufferedImage.getRGB(int, int, int, int, int[], int,
int)`) to skip the pixel-by-pixel color model conversion if
the `BufferedImage` is already of type `TYPE_INT_ARGB`, which
is relatively common. See here [2] for what this optimization
could look like.
In my local testing, a simple test program [3] went from
running in 220 seconds without the change to running in 7
seconds with the change. Separately, a real-world program
which uses the row-by-row pixel access pattern went from
running in 45 seconds to running in 29 seconds.
Does this look like a good change to those of you who know
this part of the code? Am I missing something that might make
this dangerous or undesirable? Is it making too many
assumptions? I know this area is fraught with gotchas -- color
models, color spaces, strides, etc.
Thanks!
Daniel
---
[1]
BufferedImage.TYPE_CUSTOM: 2k
BufferedImage.TYPE_INT_RGB: 114k
BufferedImage.TYPE_INT_ARGB: 93k << 35%
BufferedImage.TYPE_INT_ARGB_PRE: 5k
BufferedImage.TYPE_INT_BGR: 4k
BufferedImage.TYPE_3BYTE_BGR: 10k
BufferedImage.TYPE_4BYTE_ABGR: 9k
BufferedImage.TYPE_4BYTE_ABGR_PRE: 2k
BufferedImage.TYPE_USHORT_565_RGB: 1k
BufferedImage.TYPE_USHORT_555_RGB: 1k
BufferedImage.TYPE_BYTE_GRAY: 11k
BufferedImage.TYPE_USHORT_GRAY: 2k
BufferedImage.TYPE_BYTE_BINARY: 5k
BufferedImage.TYPE_BYTE_INDEXED: 3k
Total: 262k
[2]
https://github.com/gredler/jdk/commit/b98f6cdf7573b7e89067c757890193517aeb472e
[3]
public final class PerfTest {
public static void main(final String[] args) {
int w = 1_000;
int h = 1_000;
int accumulator = 0;
BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
int[] row = new int[w];
long start = System.currentTimeMillis();
for (int i = 0; i < 100_000; i++) {
for (int y = 0; y < h; y++) {
image.getRGB(0, y, w, 1, row, 0, w);
accumulator += row[i % w];
}
}
long end = System.currentTimeMillis();
System.out.println("Total time: " + ((end - start) /
1_000) + " seconds");
System.out.println("Accumulator: " + accumulator);
}
}