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