summaryrefslogtreecommitdiffstats
path: root/gallerycommon/src/com/android
diff options
context:
space:
mode:
authorRuben Brunk <rubenbrunk@google.com>2013-06-28 20:02:54 -0700
committerRuben Brunk <rubenbrunk@google.com>2013-07-11 13:41:29 -0700
commit8d8d5cf2943da78ca5f84d1729b081312c031e6a (patch)
treef4c60b00a469733ff11901bd8fdc030c8f537f5c /gallerycommon/src/com/android
parent3ea8558300780ca5269d75230f78eadf4100876c (diff)
downloadandroid_packages_apps_Snap-8d8d5cf2943da78ca5f84d1729b081312c031e6a.tar.gz
android_packages_apps_Snap-8d8d5cf2943da78ca5f84d1729b081312c031e6a.tar.bz2
android_packages_apps_Snap-8d8d5cf2943da78ca5f84d1729b081312c031e6a.zip
Added jpeg streaming classes.
- Provides streaming operations for decompressing/compressing JPEG files. - Allows pixel operations to be performed on large JPEG images without holding the entire bitmap in memory. Change-Id: I597ddf282b59d2ba6d6bca4722208121e3728f94
Diffstat (limited to 'gallerycommon/src/com/android')
-rw-r--r--gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java193
-rw-r--r--gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java144
-rw-r--r--gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java32
-rw-r--r--gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java80
4 files changed, 449 insertions, 0 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java b/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java
new file mode 100644
index 000000000..44ccd4c6b
--- /dev/null
+++ b/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java
@@ -0,0 +1,193 @@
+/*
+ * 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 android.graphics.Point;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class JPEGInputStream extends FilterInputStream {
+ private long JNIPointer = 0; // Used by JNI code. Don't touch.
+
+ private boolean mValidConfig = false;
+ private boolean mConfigChanged = false;
+ private int mFormat = -1;
+ private byte[] mTmpBuffer = new byte[1];
+ private int mWidth = 0;
+ private int mHeight = 0;
+
+ public JPEGInputStream(InputStream in) {
+ super(in);
+ }
+
+ public JPEGInputStream(InputStream in, int format) {
+ super(in);
+ setConfig(format);
+ }
+
+ public boolean setConfig(int format) {
+ // 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;
+ }
+ mFormat = format;
+ mValidConfig = true;
+ mConfigChanged = true;
+ return true;
+ }
+
+ public Point getDimensions() throws IOException {
+ if (mValidConfig) {
+ applyConfigChange();
+ return new Point(mWidth, mHeight);
+ }
+ return null;
+ }
+
+ @Override
+ public int available() {
+ return 0; // TODO
+ }
+
+ @Override
+ public void close() throws IOException {
+ cleanup();
+ super.close();
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public int read() throws IOException {
+ read(mTmpBuffer, 0, 1);
+ return 0xFF & mTmpBuffer[0];
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ return read(buffer, 0, buffer.length);
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int count) throws IOException {
+ if (offset < 0 || count < 0 || (offset + count) > buffer.length) {
+ throw new ArrayIndexOutOfBoundsException(String.format(
+ " buffer length %d, offset %d, length %d",
+ buffer.length, offset, count));
+ }
+ if (!mValidConfig) {
+ return 0;
+ }
+ applyConfigChange();
+ int flag = JpegConfig.J_ERROR_FATAL;
+ try {
+ flag = readDecodedBytes(buffer, offset, count);
+ } finally {
+ if (flag < 0) {
+ cleanup();
+ }
+ }
+ if (flag < 0) {
+ switch (flag) {
+ case JpegConfig.J_DONE:
+ return -1; // Returns -1 after reading EOS.
+ default:
+ throw new IOException("Error reading jpeg stream");
+ }
+ }
+ return flag;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ throw new IOException("Reset not supported.");
+ }
+
+ @Override
+ public long skip(long byteCount) throws IOException {
+ if (byteCount <= 0) {
+ return 0;
+ }
+ // Shorten skip to a reasonable amount
+ int flag = skipDecodedBytes((int) (0x7FFFFFFF & byteCount));
+ if (flag < 0) {
+ switch (flag) {
+ case JpegConfig.J_DONE:
+ return 0; // Returns 0 after reading EOS.
+ default:
+ throw new IOException("Error skipping jpeg stream");
+ }
+ }
+ return flag;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ cleanup();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void applyConfigChange() throws IOException {
+ if (mConfigChanged) {
+ cleanup();
+ Point dimens = new Point(0, 0);
+ int flag = setup(dimens, in, mFormat);
+ switch(flag) {
+ case JpegConfig.J_SUCCESS:
+ break; // allow setup to continue
+ case JpegConfig.J_ERROR_BAD_ARGS:
+ throw new IllegalArgumentException("Bad arguments to read");
+ default:
+ throw new IOException("Error to reading jpeg headers.");
+ }
+ mWidth = dimens.x;
+ mHeight = dimens.y;
+ mConfigChanged = false;
+ }
+ }
+
+ native private int setup(Point dimens, InputStream in, int format);
+
+ native private void cleanup();
+
+ native private int readDecodedBytes( byte[] inBuffer, int offset, int inCount);
+
+ native private int skipDecodedBytes(int bytes);
+
+ static {
+ System.loadLibrary("jni_jpegstream");
+ }
+}
diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java b/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java
new file mode 100644
index 000000000..c49d3759c
--- /dev/null
+++ b/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java
@@ -0,0 +1,144 @@
+/*
+ * 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");
+ }
+}
diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java b/gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java
new file mode 100644
index 000000000..e514e3b8d
--- /dev/null
+++ b/gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public interface JpegConfig {
+ // Pixel formats
+ public static final int FORMAT_GRAYSCALE = 0x001; // 1 byte/pixel
+ public static final int FORMAT_RGB = 0x003; // 3 bytes/pixel RGBRGBRGBRGB...
+ public static final int FORMAT_RGBA = 0x004; // 4 bytes/pixel RGBARGBARGBARGBA...
+ public static final int FORMAT_ABGR = 0x104; // 4 bytes/pixel ABGRABGRABGR...
+
+ // Jni error codes
+ static final int J_SUCCESS = 0;
+ static final int J_ERROR_FATAL = -1;
+ static final int J_ERROR_BAD_ARGS = -2;
+ static final int J_EXCEPTION = -3;
+ static final int J_DONE = -4;
+}
diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java b/gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java
new file mode 100644
index 000000000..abd8f681e
--- /dev/null
+++ b/gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java
@@ -0,0 +1,80 @@
+/*
+ * 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.nio.ByteOrder;
+
+public class StreamUtils {
+
+ private StreamUtils() {
+ }
+
+ /**
+ * Copies the input byte array into the output int array with the given
+ * endianness. If input is not a multiple of 4, ignores the last 1-3 bytes
+ * and returns true.
+ */
+ public static boolean byteToIntArray(int[] output, byte[] input, ByteOrder endianness) {
+ int length = input.length - (input.length % 4);
+ if (output.length * 4 < length) {
+ throw new ArrayIndexOutOfBoundsException("Output array is too short to hold input");
+ }
+ if (endianness == ByteOrder.BIG_ENDIAN) {
+ for (int i = 0, j = 0; i < output.length; i++, j += 4) {
+ output[i] = ((input[j] & 0xFF) << 24) | ((input[j + 1] & 0xFF) << 16)
+ | ((input[j + 2] & 0xFF) << 8) | ((input[j + 3] & 0xFF));
+ }
+ } else {
+ for (int i = 0, j = 0; i < output.length; i++, j += 4) {
+ output[i] = ((input[j + 3] & 0xFF) << 24) | ((input[j + 2] & 0xFF) << 16)
+ | ((input[j + 1] & 0xFF) << 8) | ((input[j] & 0xFF));
+ }
+ }
+ return input.length % 4 != 0;
+ }
+
+ public static int[] byteToIntArray(byte[] input, ByteOrder endianness) {
+ int[] output = new int[input.length / 4];
+ byteToIntArray(output, input, endianness);
+ return output;
+ }
+
+ /**
+ * Uses native endianness.
+ */
+ public static int[] byteToIntArray(byte[] input) {
+ return byteToIntArray(input, ByteOrder.nativeOrder());
+ }
+
+ /**
+ * Returns the number of bytes in a pixel for a given format defined in
+ * JpegConfig.
+ */
+ public static int pixelSize(int format) {
+ switch (format) {
+ case JpegConfig.FORMAT_ABGR:
+ case JpegConfig.FORMAT_RGBA:
+ return 4;
+ case JpegConfig.FORMAT_RGB:
+ return 3;
+ case JpegConfig.FORMAT_GRAYSCALE:
+ return 1;
+ default:
+ return -1;
+ }
+ }
+}