1a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk/* 2a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * Copyright (C) 2013 The Android Open Source Project 3a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * 4a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * Licensed under the Apache License, Version 2.0 (the "License"); 5a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * you may not use this file except in compliance with the License. 6a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * You may obtain a copy of the License at 7a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * 8a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * http://www.apache.org/licenses/LICENSE-2.0 9a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * 10a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * Unless required by applicable law or agreed to in writing, software 11a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * distributed under the License is distributed on an "AS IS" BASIS, 12a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * See the License for the specific language governing permissions and 14a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk * limitations under the License. 15a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk */ 16a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 17a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkpackage com.android.gallery3d.jpegstream; 18a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 19a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkimport android.graphics.Point; 20a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 21a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkimport java.io.FilterInputStream; 22a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkimport java.io.IOException; 23a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkimport java.io.InputStream; 24a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 25a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkpublic class JPEGInputStream extends FilterInputStream { 26a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private long JNIPointer = 0; // Used by JNI code. Don't touch. 27a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 28a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private boolean mValidConfig = false; 29a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private boolean mConfigChanged = false; 30a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private int mFormat = -1; 31a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private byte[] mTmpBuffer = new byte[1]; 32a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private int mWidth = 0; 33a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private int mHeight = 0; 34a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 35a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public JPEGInputStream(InputStream in) { 36a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk super(in); 37a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 38a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 39a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public JPEGInputStream(InputStream in, int format) { 40a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk super(in); 41a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk setConfig(format); 42a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 43a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 44a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public boolean setConfig(int format) { 45a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk // Make sure format is valid 46a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk switch (format) { 47a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.FORMAT_GRAYSCALE: 48a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.FORMAT_RGB: 49a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.FORMAT_ABGR: 50a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.FORMAT_RGBA: 51a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk break; 52a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk default: 53a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return false; 54a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 55a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk mFormat = format; 56a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk mValidConfig = true; 57a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk mConfigChanged = true; 58a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return true; 59a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 60a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 61a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public Point getDimensions() throws IOException { 62a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (mValidConfig) { 63a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk applyConfigChange(); 64a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return new Point(mWidth, mHeight); 65a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 66a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return null; 67a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 68a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 69a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 70a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public int available() { 71a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return 0; // TODO 72a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 73a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 74a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 75a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public void close() throws IOException { 76a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk cleanup(); 77a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk super.close(); 78a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 79a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 80a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 81a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public synchronized void mark(int readlimit) { 82a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk // Do nothing 83a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 84a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 85a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 86a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public boolean markSupported() { 87a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return false; 88a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 89a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 90a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 91a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public int read() throws IOException { 92a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk read(mTmpBuffer, 0, 1); 93a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return 0xFF & mTmpBuffer[0]; 94a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 95a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 96a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 97a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public int read(byte[] buffer) throws IOException { 98a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return read(buffer, 0, buffer.length); 99a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 100a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 101a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 102a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public int read(byte[] buffer, int offset, int count) throws IOException { 103a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (offset < 0 || count < 0 || (offset + count) > buffer.length) { 104a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk throw new ArrayIndexOutOfBoundsException(String.format( 105a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk " buffer length %d, offset %d, length %d", 106a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk buffer.length, offset, count)); 107a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 108a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (!mValidConfig) { 109a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return 0; 110a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 111a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk applyConfigChange(); 112a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk int flag = JpegConfig.J_ERROR_FATAL; 113a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk try { 114a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk flag = readDecodedBytes(buffer, offset, count); 115a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } finally { 116a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (flag < 0) { 117a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk cleanup(); 118a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 119a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 120a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (flag < 0) { 121a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk switch (flag) { 122a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.J_DONE: 123a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return -1; // Returns -1 after reading EOS. 124a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk default: 125a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk throw new IOException("Error reading jpeg stream"); 126a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 127a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 128a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return flag; 129a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 130a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 131a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 132a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public synchronized void reset() throws IOException { 133a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk throw new IOException("Reset not supported."); 134a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 135a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 136a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 137a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk public long skip(long byteCount) throws IOException { 138a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (byteCount <= 0) { 139a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return 0; 140a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 141a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk // Shorten skip to a reasonable amount 142a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk int flag = skipDecodedBytes((int) (0x7FFFFFFF & byteCount)); 143a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (flag < 0) { 144a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk switch (flag) { 145a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.J_DONE: 146a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return 0; // Returns 0 after reading EOS. 147a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk default: 148a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk throw new IOException("Error skipping jpeg stream"); 149a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 150a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 151a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk return flag; 152a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 153a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 154a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk @Override 155a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk protected void finalize() throws Throwable { 156a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk try { 157a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk cleanup(); 158a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } finally { 159a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk super.finalize(); 160a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 161a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 162a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 163a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk private void applyConfigChange() throws IOException { 164a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk if (mConfigChanged) { 165a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk cleanup(); 166a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk Point dimens = new Point(0, 0); 167a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk int flag = setup(dimens, in, mFormat); 168a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk switch(flag) { 169a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.J_SUCCESS: 170a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk break; // allow setup to continue 171a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk case JpegConfig.J_ERROR_BAD_ARGS: 172a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk throw new IllegalArgumentException("Bad arguments to read"); 173a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk default: 174a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk throw new IOException("Error to reading jpeg headers."); 175a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 176a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk mWidth = dimens.x; 177a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk mHeight = dimens.y; 178a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk mConfigChanged = false; 179a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 180a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 181a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 182a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk native private int setup(Point dimens, InputStream in, int format); 183a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 184a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk native private void cleanup(); 185a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 186a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk native private int readDecodedBytes( byte[] inBuffer, int offset, int inCount); 187a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 188a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk native private int skipDecodedBytes(int bytes); 189a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk 190a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk static { 191a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk System.loadLibrary("jni_jpegstream"); 192a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk } 193a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk} 194