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