On Wed, 2 Apr 2025 20:12:48 GMT, Nikita Gubarkov <ngubar...@openjdk.org> wrote:

> > is it possible for pixelStride to be smaller than maxBandOff?
> 
> Surprisingly, yes! Band offsets are arbitrary, even for 
> `PixelInterleavedSampleModel`, the only restriction is that `maxBandOff - 
> minBandOff <= pixelStride`

But isn't it essentially the same thing? It just shifts the "pixelStride" 
window in one direction or another.

> Both could be correct and there doesn't seem to be any other info, which 
> could allow us to determine this unambiguously. This "real starting pixel 
> offset" is implicitly assumed, when working with [known types in native 
> code](https://github.com/openjdk/jdk/blob/209e72d311234c8279289172dab2cbb255e4fed9/src/java.desktop/share/classes/sun/awt/image/BufImgSurfaceData.java#L319),
>  but is explicitly stored nowhere.

It depends on where and when the ColorModel/SampleModel/Raster were created. If 
the raster was created by our code and we can determine the starting offsets, 
and if we can confirm that the gaps are safe to read, then we could set some 
flags. These flags could be recognized by the pipelines to enable fast-path 
optimizations.

Just one example:

    private static BufferedImage makeCustom3BYTE_BGR() {
        ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
        int[] nBits = {8, 8, 8};
        int[] bOffs = {2, 1, 0};
        ColorModel colorModel = new ComponentColorModel(cs, nBits, false, false,
                                                        Transparency.OPAQUE,
                                                        DataBuffer.TYPE_BYTE);
        
        PixelInterleavedSampleModel sm =
                new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
                    2, 1, 400, 4000, bOffs);
        WritableRaster raster = Raster.createWritableRaster(sm, null);
        
        System.out.println("raster.getDataBuffer().getSize() = " + 
raster.getDataBuffer().getSize());
        return new BufferedImage(colorModel, raster, true, null);
    }

        BufferedImage bi = new BufferedImage(3000, 3000, TYPE_INT_ARGB);
        BufferedImage src = makeCustom3BYTE_BGR();
        Graphics2D g2d = bi.createGraphics();
        g2d.setTransform(new AffineTransform());
        g2d.drawImage(src, 0, 0, null);
        g2d.dispose();


The size of the image is "403", meaning it does not include the gap after the 
last pixel data.

Now, if we attempt to blit this into various pipelines, we may encounter 
different slow/fast paths:

- CMM code will bail out since such formats are not supported.
- OGL/Metal code 
[supports](https://github.com/openjdk/jdk/blob/b01026abaab0b65f9ec0920d66a8ff1fa868d351/src/java.desktop/share/native/common/java2d/opengl/OGLBlitLoops.c#L409C40-L409C51)
 gaps in scanlines but does not support gaps between pixels. As a result, the 
slow path will be used via an intermediate ARGB_PRE surface.
- Native loops will also bail out, falling back to the most [generic java 
implementation](https://github.com/openjdk/jdk/blob/b01026abaab0b65f9ec0920d66a8ff1fa868d351/src/java.desktop/share/classes/sun/java2d/loops/CustomComponent.java#L144).
    
In this particular case, we can probably optimize the handling, but how should 
we deal with similar random rasters we might encounter? 
getDataElements/setDataElements is the only solution similar to 
OpaqueCopyArgbToAny?

>But what if our last pixel also contains padding, which is not resident? Then 
>we would have to repeat the [same 
>procedure](https://github.com/openjdk/jdk/blob/130b0cdaa6604da47a893e5425547acf3d5253f4/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java#L247)
> with finding the last used component and so on.

Yes, that is how it is usually implemented. When creating a surface, image, or 
raster, validation is performed, and specific flags are set(like pixel data 
format + all offsets). Then, in the pipeline, a blit operation can be 
registered for such an image with the given flag and a known destination. This 
blit operation will know how to handle pixel data properly while skipping any 
gaps.

>But the problem with this is that there is no such info in SurfaceDataRasInfo, 
>which could allow us figure out, which components are really used and which 
>bytes are just padding. Moreover, SurfaceDataRasInfo doesn't even have a total 
>size field, so we can't even clamp our copy range to some boundary.

It seems that, as of now, we simply don't have generic native loops that 
support pixel gaps. However, for known formats, there are many specialized 
loops, and that information is stored not in SurfaceDataRasInfo but in various 
macro-based 
[loops](https://github.com/openjdk/jdk/blob/b01026abaab0b65f9ec0920d66a8ff1fa868d351/src/java.desktop/share/native/libawt/java2d/loops/IntRgb.h#L38).

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/24111#discussion_r2026221415

Reply via email to