class LeptonicaFrameConverter extends FrameConverter<PIX> {
    static { Loader.load(org.bytedeco.leptonica.global.leptonica.class); }

    PIX pix;
    BytePointer frameData, pixData;
    ByteBuffer frameBuffer, pixBuffer;

    static boolean isEqual(Frame frame, PIX pix) {
        return pix != null && frame != null && frame.image != null && frame.image.length > 0
                && frame.imageWidth == pix.w() && frame.imageHeight == pix.h()
                && frame.imageChannels == pix.d() / 8 && frame.imageDepth == Frame.DEPTH_UBYTE
                && (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)
                    || new Pointer(frame.image[0]).address() == pix.data().address())
                && frame.imageStride * Math.abs(frame.imageDepth) / 8 == pix.wpl() * 4;
    }

    public PIX convert(Frame frame) {
        if (frame == null || frame.image == null) {
            return null;
        } else if (frame.opaque instanceof PIX) {
            return (PIX)frame.opaque;
        } else if (!isEqual(frame, pix)) {
            Pointer data;
            if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
                if (pixData == null || pixData.capacity() < frame.imageHeight * frame.imageStride) {
                    if (pixData != null) {
                        pixData.releaseReference();
                    }
                    pixData = new BytePointer(frame.imageHeight * frame.imageStride).retainReference();
                }
                data = pixData;
                pixBuffer = data.asByteBuffer().order(ByteOrder.BIG_ENDIAN);
            } else {
                data = new Pointer(frame.image[0].position(0));
            }
            if (pix != null) {
                pix.releaseReference();
            }
            pix = PIX.create(frame.imageWidth, frame.imageHeight, frame.imageChannels * 8, data)
                     .wpl(frame.imageStride / 4 * Math.abs(frame.imageDepth) / 8).retainReference();
        }

        if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
            ((ByteBuffer)pixBuffer.position(0)).asIntBuffer()
                    .put(((ByteBuffer)frame.image[0].position(0)).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer());
        }
        return pix;
    }

    public Frame convert(PIX pix) {<FILL_FUNCTION_BODY>}

    @Override public void close() {
        super.close();
        if (pix != null) {
            pix.releaseReference();
            pix = null;
        }
        if (pixData != null) {
            pixData.releaseReference();
            pixData = null;
        }
        if (frameData != null) {
            frameData.releaseReference();
            frameData = null;
        }
    }
}

        if (pix == null) {
            return null;
        }

        PIX tempPix = null;
        if (pix.colormap() != null) {
            tempPix = pix = pixRemoveColormap(pix, REMOVE_CMAP_TO_FULL_COLOR);
        } else if (pix.d() < 8) {
            switch (pix.d()) {
                case 1:
                    tempPix = pix = pixConvert1To8(null, pix, (byte)0, (byte)255);
                    break;
                case 2:
                    tempPix = pix = pixConvert2To8(pix, (byte)0, (byte)85, (byte)170, (byte)255, 0);
                    break;
                case 4:
                    tempPix = pix = pixConvert4To8(pix, 0);
                    break;
                default:
                    assert false;
            }
        }

        if (!isEqual(frame, pix)) {
            frame = new Frame();
            frame.imageWidth = pix.w();
            frame.imageHeight = pix.h();
            frame.imageDepth = Frame.DEPTH_UBYTE;
            frame.imageChannels = pix.d() / 8;
            frame.imageStride = pix.wpl() * 4;
            if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
                if (frameData == null || frameData.capacity() < frame.imageHeight * frame.imageStride) {
                    if (frameData != null) {
                        frameData.releaseReference();
                    }
                    frameData = new BytePointer(frame.imageHeight * frame.imageStride).retainReference();
                }
                frameBuffer = frameData.asByteBuffer().order(ByteOrder.LITTLE_ENDIAN);
                frame.opaque = frameData;
                frame.image = new Buffer[] { frameBuffer };
            } else {
                if (tempPix != null) {
                    if (this.pix != null) {
                        this.pix.releaseReference();
                    }
                    this.pix = pix = pix.clone();
                }
                frame.opaque = pix;
                frame.image = new Buffer[] { pix.createBuffer() };
            }
        }

        if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
            ((ByteBuffer)frameBuffer.position(0)).asIntBuffer()
                    .put(pix.createBuffer().order(ByteOrder.BIG_ENDIAN).asIntBuffer());
        }

        if (tempPix != null) {
            pixDestroy(tempPix);
        }
        return frame;
    
<methods>public non-sealed void <init>() ,public void close() ,public abstract org.bytedeco.javacv.Frame convert(PIX) ,public abstract PIX convert(org.bytedeco.javacv.Frame) <variables>protected org.bytedeco.javacv.Frame frame