/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.jpeg.internal;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageWriteParam;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import org.bytedeco.javacpp.SizeTPointer;
import org.weasis.image.jni.ImageParameters;
import org.weasis.image.jni.NativeCodec;
import org.weasis.image.jni.NativeImage;
import org.weasis.image.jni.StreamSegment;
import org.weasis.jpeg.JpegParameters;
import org.weasis.jpeg.NativeJPEGImage;
import org.weasis.jpeg.cpp.libijg;
import org.weasis.jpeg.cpp.libijg.ByteStreamInfo;
import org.weasis.jpeg.cpp.libijg.JlsParameters;
public class CharlsCodec implements NativeCodec {
public CharlsCodec() {
}
@Override
public String readHeader(NativeImage nImage) throws IOException {
int ret = 0;
StreamSegment seg = nImage.getStreamSegment();
if (seg != null) {
try (JlsParameters p = new JlsParameters(); SizeTPointer size = new SizeTPointer(1)) {
ByteBuffer buffer = seg.getDirectByteBuffer(0);
size.put(buffer.limit());
try (ByteStreamInfo input = libijg.FromByteArray(buffer, size)) {
ret = libijg.JpegLsReadHeaderStream(input, p);
}
if (ret == libijg.OK) {
setParameters((JpegParameters) nImage.getImageParameters(), p);
}
buffer.clear();
}
}
return ret == 0 ? null : libijg.getErrorMessage(ret);
}
@Override
public String decompress(NativeImage nImage, ImageReadParam param) throws IOException {
int ret = 0;
StreamSegment seg = nImage.getStreamSegment();
if (seg != null) {
// Set JlsParameters in first position to load native library
try (JlsParameters p = new JlsParameters();
SizeTPointer size = new SizeTPointer(1);
SizeTPointer size2 = new SizeTPointer(1);) {
// When multiple fragments segments, aggregate them in the byteBuffer.
ByteBuffer buffer = seg.getDirectByteBuffer(0, seg.getSegLength().length - 1);
ByteBuffer outBuf;
size.put(buffer.limit());
try (ByteStreamInfo input = libijg.FromByteArray(buffer, size)) {
JpegParameters params = (JpegParameters) nImage.getImageParameters();
if (params.getBytesPerLine() == 0) {
ret = libijg.JpegLsReadHeaderStream(input, p);
buffer.clear();
if (ret == libijg.OK) {
setParameters(params, p);
} else {
return "Cannot read JPEG-LS header!";
}
} else {
p.width(params.getWidth());
p.height(params.getHeight());
p.bitspersample(params.getBitsPerSample());
p.components(params.getSamplesPerPixel());
p.bytesperline(params.getBytesPerLine());
p.allowedlossyerror(params.getAllowedLossyError());
}
// p.outputBgr('1'); // convert RGB to BGR
p.colorTransform(0); // default (RGB)
// Build outputStream here and transform to an array
outBuf = ByteBuffer.allocateDirect(p.bytesperline() * p.height());
outBuf.order(ByteOrder.nativeOrder()); // Not test with big endian system
size2.put(outBuf.limit());
try (ByteStreamInfo outStream = libijg.FromByteArray(outBuf, size2)) {
ret = libijg.JpegLsDecodeStream(outStream, input, p);
}
}
// keep a reference to be not garbage collected
buffer.clear();
if (ret == libijg.OK) {
int bps = p.bitspersample();
nImage.setOutputBuffer((bps > 8 && bps <= 16) ? outBuf.asShortBuffer() : outBuf);
}
}
}
return ret == 0 ? null : libijg.getErrorMessage(ret);
}
@Override
public String compress(NativeImage nImage, ImageOutputStream ouputStream, ImageWriteParam param) throws IOException {
int ret = 0;
if (nImage != null && ouputStream != null && nImage.getInputBuffer() != null) {
try (JlsParameters p = new JlsParameters()) {
JpegParameters params = (JpegParameters) nImage.getImageParameters();
if (params.getBitsPerSample() != 8 && params.getBitsPerSample() != 16) {
return "JPGLS codec supports only 8 and 16-bit per pixel!";
}
int components = params.getSamplesPerPixel();
if (components != 1 && components != 3 && components != 4) {
return "JPGLS codec supports only 1, 3 and 4 bands!";
}
// Band mode
if (components == 3 || components == 4) {
// Interleaved by pixel
p.ilv(libijg.ILV_SAMPLE);
} else {
p.ilv(libijg.ILV_NONE);
}
p.width(params.getWidth());
p.height(params.getHeight());
p.bitspersample(params.getBitsPerSample());
p.components(components);
p.bytesperline(params.getBytesPerLine());
p.allowedlossyerror(params.getAllowedLossyError());
Buffer b = nImage.getInputBuffer();
ByteBuffer buffer;
if (b instanceof ByteBuffer) {
buffer = ByteBuffer.allocateDirect(b.limit());
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put((ByteBuffer) b);
} else if (b instanceof ShortBuffer) {
ShortBuffer sBuf = (ShortBuffer) b;
buffer = ByteBuffer.allocateDirect(sBuf.limit() * 2);
buffer.order(ByteOrder.LITTLE_ENDIAN);
while (sBuf.hasRemaining()) {
buffer.putShort(sBuf.get());
}
} else {
return "JPGLS codec exception: not valid input buffer";
}
buffer.flip();
try (SizeTPointer size = new SizeTPointer(1);
SizeTPointer size2 = new SizeTPointer(1);
SizeTPointer bytesWritten = new SizeTPointer(1)) {
ByteBuffer outBuf;
size.put(buffer.limit());
try (ByteStreamInfo input = libijg.FromByteArray(buffer, size)) {
// Build outputStream here and transform to an array: 12 => 8 for getting byte and plus 4 is the
// limit for decreasing the size
outBuf = ByteBuffer.allocateDirect(params.getWidth() * params.getHeight()
* params.getSamplesPerPixel() * params.getBitsPerSample() / 12);
outBuf.order(params.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
size2.put(outBuf.limit());
try (ByteStreamInfo outStream = libijg.FromByteArray(outBuf, size2)) {
ret = libijg.JpegLsEncodeStream(outStream, bytesWritten, input, p);
}
}
// keep a reference to be not garbage collected
buffer.clear();
if (ret == libijg.OK) {
outBuf.rewind();
NativeImage.writeByteBuffer(ouputStream, outBuf, (int) bytesWritten.get());
}
}
} finally {
nImage.setInputBuffer(null);
}
}
return ret == 0 ? null : libijg.getErrorMessage(ret);
}
@Override
public void dispose() {
}
@Override
public NativeJPEGImage buildImage(ImageInputStream iis) throws IOException {
SOFSegment sof = JpegCodec.getSOFSegment(iis);
NativeJPEGImage img = new NativeJPEGImage();
if (sof != null) {
if (sof.getMarker() != 0xFFF7) {
throw new IllegalArgumentException("Stream without JPEG-LS marker!");
}
ImageParameters params = img.getJpegParameters();
params.setWidth(sof.getSamplesPerLine());
params.setHeight(sof.getLines());
// Adjust tile size to image size for writer.
params.setTileWidth(params.getWidth());
params.setTileHeight(params.getHeight());
params.setBitsPerSample(sof.getSamplePrecision());
int nbBands = sof.getComponents();
params.setSamplesPerPixel(nbBands);
params.setBytesPerLine(
params.getWidth() * params.getSamplesPerPixel() * ((params.getBitsPerSample() + 7) / 8));
params.setFormat(nbBands == 1 ? ImageParameters.CM_GRAY
: nbBands == 3 ? ImageParameters.CM_S_RGB : ImageParameters.CM_S_RGBA);
}
return img;
}
private static void setParameters(JpegParameters params, org.weasis.jpeg.cpp.libijg.JlsParameters p) {
if (params != null && p != null) {
params.setWidth(p.width());
params.setHeight(p.height());
/*
* Adjust tile size to image size. RenderedImage will handle only one tile as this decoder doesn't support
* well region reading (slow).
*/
params.setTileWidth(params.getWidth());
params.setTileHeight(params.getHeight());
params.setBitsPerSample(p.bitspersample());
int nbBands = p.components();
params.setSamplesPerPixel(nbBands);
params.setBytesPerLine(p.bytesperline());
params.setAllowedLossyError(p.allowedlossyerror());
params.setFormat(nbBands == 1 ? ImageParameters.CM_GRAY
: nbBands == 3 ? ImageParameters.CM_S_RGB : ImageParameters.CM_S_RGBA);
}
}
}