blob: c49d3759c752c2f4249653d85e3e98551f73614f [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gallery3d.jpegstream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class JPEGOutputStream extends FilterOutputStream {
private long JNIPointer = 0; // Used by JNI code. Don't touch.
private byte[] mTmpBuffer = new byte[1];
private int mWidth = 0;
private int mHeight = 0;
private int mQuality = 0;
private int mFormat = -1;
private boolean mValidConfig = false;
private boolean mConfigChanged = false;
public JPEGOutputStream(OutputStream out) {
super(out);
}
public JPEGOutputStream(OutputStream out, int width, int height, int quality,
int format) {
super(out);
setConfig(width, height, quality, format);
}
public boolean setConfig(int width, int height, int quality, int format) {
// Clamp quality to range (0, 100]
quality = Math.max(Math.min(quality, 100), 1);
// Make sure format is valid
switch (format) {
case JpegConfig.FORMAT_GRAYSCALE:
case JpegConfig.FORMAT_RGB:
case JpegConfig.FORMAT_ABGR:
case JpegConfig.FORMAT_RGBA:
break;
default:
return false;
}
// If valid, set configuration
if (width > 0 && height > 0) {
mWidth = width;
mHeight = height;
mFormat = format;
mQuality = quality;
mValidConfig = true;
mConfigChanged = true;
} else {
return false;
}
return mValidConfig;
}
@Override
public void close() throws IOException {
cleanup();
super.close();
}
@Override
public void write(byte[] buffer, int offset, int length) throws IOException {
if (offset < 0 || length < 0 || (offset + length) > buffer.length) {
throw new ArrayIndexOutOfBoundsException(String.format(
" buffer length %d, offset %d, length %d",
buffer.length, offset, length));
}
if (!mValidConfig) {
return;
}
if (mConfigChanged) {
cleanup();
int flag = setup(out, mWidth, mHeight, mFormat, mQuality);
switch(flag) {
case JpegConfig.J_SUCCESS:
break; // allow setup to continue
case JpegConfig.J_ERROR_BAD_ARGS:
throw new IllegalArgumentException("Bad arguments to write");
default:
throw new IOException("Error to writing jpeg headers.");
}
mConfigChanged = false;
}
int returnCode = JpegConfig.J_ERROR_FATAL;
try {
returnCode = writeInputBytes(buffer, offset, length);
} finally {
if (returnCode < 0) {
cleanup();
}
}
if (returnCode < 0) {
throw new IOException("Error writing jpeg stream");
}
}
@Override
public void write(byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}
@Override
public void write(int oneByte) throws IOException {
mTmpBuffer[0] = (byte) oneByte;
write(mTmpBuffer);
}
@Override
protected void finalize() throws Throwable {
try {
cleanup();
} finally {
super.finalize();
}
}
native private int setup(OutputStream out, int width, int height, int format, int quality);
native private void cleanup();
native private int writeInputBytes(byte[] inBuffer, int offset, int inCount);
static {
System.loadLibrary("jni_jpegstream");
}
}