RFR: 8352407: PixelInterleavedSampleModel with unused components throws RasterFormatException: Incorrect pixel stride [v2]
Nikita Gubarkov
ngubarkov at openjdk.org
Thu Apr 3 06:38:53 UTC 2025
On Thu, 3 Apr 2025 06:09:03 GMT, Sergey Bylokhov <serb at 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 pat...
>
> Probably, the changes similar to https://github.com/openjdk/jdk/pull/24378 will help in some cases. However, for the generic case with random rasters, it will be necessary to determine whether the gaps at the end of the image are actually stored in the raster or not.
> as of now, we simply don't have generic native loops that support pixel gaps
That was kinda what I was actually implementing in Vulkan when I faced this issue. I just accepted any pixel-interleaved surface with 4-byte stride and assumed that I can blit it directly. Just because the stride is 4 bytes, I know I can copy the whole scanline in one go into a matching Vulkan format, like B8G8R8A8_UNORM, and I can handle any component order (including missing alpha channel) with simple swizzling.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/24111#discussion_r2026301933
More information about the client-libs-dev
mailing list