Login  Register

Re: Speeding up writing to JPG file

Posted by Cmar on Jul 25, 2016; 9:10am
URL: https://forum.jogamp.org/Speeding-up-writing-to-JPG-file-tp4036909p4036954.html

Thanks, I'll definitely try TurboJPEG today, I didn't realize it already has java bindings (I was trying to avoid doing that myself). I'll also keep an eye on this thread and see how this thing works out.

Currently the GL -> BufferedImage transfer can be made faster by assuming the target image is TYPE_3BYTE_BGR and using GL_BGR as the GLPixelBuffer/TextureData format, to skip the swizzling post-process. Obviously an encoder to skip the whole BufferedImage step would be better, but this is still an improvement:

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.nio.ByteBuffer;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2ES3;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.util.GLBuffers;
import com.jogamp.opengl.util.GLPixelBuffer;
import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes;
import com.jogamp.opengl.util.GLPixelBuffer.GLPixelBufferProvider;
import com.jogamp.opengl.util.GLPixelStorageModes;
import com.jogamp.opengl.util.texture.TextureData;

public class BufferUtil {
    private GLPixelBuffer readPixelBuffer;
    private TextureData texData;
    private final GLPixelStorageModes psm;
    private final int tmp[] = new int[1];
    private final GLPixelBufferProvider pixelBufferProvider;
    private final GLPixelAttributes pixelAttribs;
	
    public BufferUtil() {
        psm = new GLPixelStorageModes();
        pixelBufferProvider = GLPixelBuffer.defaultProviderNoRowStride;
        pixelAttribs = new GLPixelAttributes(GL.GL_BGR, GL.GL_UNSIGNED_BYTE);
    }
    
    public void read(GL gl, BufferedImage destination) {
        if (destination.getType() != BufferedImage.TYPE_3BYTE_BGR) {
            throw new IllegalArgumentException("Destination image must have a type of TYPE_3BYTE_BGR");
        }
        
        readPixels(gl, destination.getWidth(), destination.getHeight());

        final byte[] imageData = ((DataBufferByte) destination.getRaster().getDataBuffer()).getData();
        ByteBuffer buf = (ByteBuffer) texData.getBuffer();
        if (buf == null) {
            buf = (ByteBuffer) texData.getMipmapData()[0];
        }
        buf.rewind();
        buf.get(imageData);
        buf.rewind();
    }
    
    public BufferedImage createCompatibleImage(int width, int height) {
        return new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    }
    
    private boolean readPixels(final GL gl, final int width, final int height) {
        final int glerr0 = gl.glGetError();
        if (GL.GL_NO_ERROR != glerr0) {
            System.err.println("Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x" + Integer.toHexString(glerr0));
        }

        final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.pfmt.comp.bytesPerPixel(), width, height, 1, true);

        if (readPixelBuffer == null || readPixelBuffer.requiresNewBuffer(gl, width, height, readPixelSize)) {
            readPixelBuffer = pixelBufferProvider.allocate(gl, null, pixelAttribs, true, width, height, 1, readPixelSize);
            Buffers.rangeCheckBytes(readPixelBuffer.buffer, readPixelSize);
            try {
                texData = new TextureData(gl.getGLProfile(), pixelAttribs.format, width, height, 0, pixelAttribs, false, false, false, readPixelBuffer.buffer, null);
            }
            catch (final Exception e) {
                texData = null;
                readPixelBuffer = null;
                throw new RuntimeException("can not fetch offscreen texture", e);
            }
        }

        boolean res = readPixelBuffer != null && readPixelBuffer.isValid();
        if (res) {
            psm.setPackAlignment(gl, 1);
            if (gl.isGL2ES3()) {
                final GL2ES3 gl2es3 = gl.getGL2ES3();
                psm.setPackRowLength(gl2es3, width);
                gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer());
            }
            readPixelBuffer.clear();
            try {
                gl.glReadPixels(0, 0, width, height, pixelAttribs.format, pixelAttribs.type, readPixelBuffer.buffer);
            }
            catch (final GLException gle) {
                res = false;
                gle.printStackTrace();
            }
            readPixelBuffer.position(readPixelSize);
            readPixelBuffer.flip();
            final int glerr1 = gl.glGetError();
            if (GL.GL_NO_ERROR != glerr1) {
                System.err.println("GLReadBufferUtil.readPixels: readPixels error 0x" + Integer.toHexString(glerr1) + " " + width + "x" + height + ", " + pixelAttribs + ", " + readPixelBuffer + ", sz " + readPixelSize);
                res = false;
            }
            
            psm.restore(gl);
        }
        return res;
    }
}