'Custom Java Colorspace requires impossible conversions

I need to create a Custom colorspace that will have 5 components. Then I will be drawing on that colorspace using a 5 component paint. Sample code:

        ColorSpace cs5 = new ColorSpace5();

        // Create colored model and BufferedImage that uses the CMY color space
        ColorModel outputCM = new ComponentColorModel(cs5, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
        WritableRaster imageRaster = outputCM.createCompatibleWritableRaster(50, 50);
        BufferedImage bi5 = new BufferedImage(outputCM, imageRaster, false, null);

        // Graphics2D to draw onto the image
        Graphics2D g2 = bi5.createGraphics();

        // Create Paint object that uses 5 components
        ColorPaintCS blackPaint = new ColorPaintCS(new int[] { 0, 0, 0, 255, 125 }, outputCM);
        g2.setPaint(blackPaint);
        g2.fillRect(0, 0, 50, 50);

The problem is that Java ALWAYS converts to ARGB to do the blit operations. Even when you are copying between things with the same Colorspace. I did some digging around in the AWT code and found that

AlphaPaintPipe.renderPathTile, line 152
                        context.lastBlit = Blit.getFromCache(srcData.getSurfaceType(),
                                              comptype,
                                              dstData.getSurfaceType());

both srcData, dstData have the correct colorModel

I think that Blit.getFromCache() needs to be able to return a simple data copy Blit when both src and dst have the same Color Model. The Blit returned only depends on the SurfaceType - and is always OpaqueCopyAnyToArgb - so always does a colorspace conversion to/from ARGB.

Then in MaskBlit.setupGeneralBinaryOp it sets up converters for To/From ARGB.

I tried forcing the convertsrc and convertdst fields in MaskBlit to be null, which avoids the color conversion, but then MakeBlit.MaskBlit (around line 219) calls performop.MaskBlit, and that throws a NPE.

I found what appears to be relevant JDK bugs: https://bugs.openjdk.java.net/browse/JDK-8226923 https://bugs.openjdk.java.net/browse/JDK-6349627

I thought I might be able to make a Blit that would work, but it fails as the base class sun.java2d.loops.MaskBlit has a lot of native methods and subclassing doesn't seem to work.

sun.java2d.loops.GraphicsPrimitive add[] = { new MaskBlitCS() };
sun.java2d.loops.GraphicsPrimitiveMgr.register(add);

fails with a

java.lang.UnsatisfiedLinkError: 'void sun.java2d.loops.GraphicsPrimitiveMgr.initIDs(java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class, java.lang.Class)'
    at java.desktop/sun.java2d.loops.GraphicsPrimitiveMgr.initIDs(Native Method)
    at java.desktop/sun.java2d.loops.GraphicsPrimitiveMgr.<clinit>(GraphicsPrimitiveMgr.java:56)
    at java.desktop/sun.java2d.loops.MaskBlit.<clinit>(MaskBlit.java:112)



public class MaskBlitCS extends sun.java2d.loops.MaskBlit {
    private static final Logger log = LogManager.getLogger();

    public MaskBlitCS(  final SurfaceType srctype,
                    final CompositeType comptype,
                    final SurfaceType dsttype) {
        super(SurfaceType.Any, CompositeType.OpaqueSrcOverNoEa, SurfaceType.Any);
    }

    public MaskBlitCS() {
        super(SurfaceType.Any, CompositeType.OpaqueSrcOverNoEa, SurfaceType.Any);
    }

    @Override
    public synchronized void MaskBlit(  final SurfaceData srcData,
                            final SurfaceData dstData,
                            final Composite comp,
                            final Region clip,
                            final int srcx,
                            final int srcy,
                            final int dstx,
                            final int dsty,
                            final int width,
                            final int height,
                            final byte mask[],
                            final int offset,
                            final int scan) {
        log.debug("MaskBlit: {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}", srcData, dstData, comp, clip, srcx, srcy, dstx, dsty, width, height, offset, scan);
        SurfaceData src, dst;
        Region opclip;
        int sx, sy, dx, dy;

        if (!srcData.getColorModel().equals(dstData.getColorModel())) {
            throw new IllegalArgumentException("Color Models Must Match");
        }

        src = srcData;
        sx = srcx;
        sy = srcy;

        dst = dstData;
        dx = dstx;
        dy = dsty;
        opclip = clip;

        Raster srcR = srcData.getRaster(sx, sy, width, height);
        DataBuffer srcDB = srcR.getDataBuffer();
        Raster dstR = dstData.getRaster(dx, dy, width, height);
        DataBuffer dstDB = dstR.getDataBuffer();

        log.debug("MaskBlit: SRC {}, {}, {}", srcDB.getDataType(), srcDB.getNumBanks(), srcDB.getSize());
        log.debug("MaskBlit: DST {}, {}, {}", dstDB.getDataType(), dstDB.getNumBanks(), dstDB.getSize());

    }
}

Does anyone know of a way to use custom colorspaces without getting the ARGB conversions?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source