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

-- 
Laurent Bourgès

Le ven. 22 août 2025, 14:04, Daniel Gredler <djgred...@gmail.com> 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);
>     }
> }
>
>

Reply via email to