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 java.io.FilterOutputStream;
20a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkimport java.io.IOException;
21a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkimport java.io.OutputStream;
22a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunkpublic class JPEGOutputStream extends FilterOutputStream {
23a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private long JNIPointer = 0; // Used by JNI code. Don't touch.
24a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
25a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private byte[] mTmpBuffer = new byte[1];
26a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private int mWidth = 0;
27a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private int mHeight = 0;
28a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private int mQuality = 0;
29a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private int mFormat = -1;
30a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private boolean mValidConfig = false;
31a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    private boolean mConfigChanged = false;
32a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
33a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public JPEGOutputStream(OutputStream out) {
34a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        super(out);
35a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
36a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
37a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public JPEGOutputStream(OutputStream out, int width, int height, int quality,
38a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            int format) {
39a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        super(out);
40a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        setConfig(width, height, quality, format);
41a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
42a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
43a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public boolean setConfig(int width, int height, int quality, int format) {
44a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        // Clamp quality to range (0, 100]
45a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        quality = Math.max(Math.min(quality, 100), 1);
46a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
47a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        // Make sure format is valid
48a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        switch (format) {
49a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            case JpegConfig.FORMAT_GRAYSCALE:
50a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            case JpegConfig.FORMAT_RGB:
51a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            case JpegConfig.FORMAT_ABGR:
52a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            case JpegConfig.FORMAT_RGBA:
53a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                break;
54a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            default:
55a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                return false;
56a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
57a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
58a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        // If valid, set configuration
59a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        if (width > 0 && height > 0) {
60a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mWidth = width;
61a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mHeight = height;
62a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mFormat = format;
63a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mQuality = quality;
64a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mValidConfig = true;
65a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mConfigChanged = true;
66a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        } else {
67a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            return false;
68a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
69a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
70a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        return mValidConfig;
71a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
72a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
73a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    @Override
74a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public void close() throws IOException {
75a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        cleanup();
76a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        super.close();
77a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
78a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
79a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    @Override
80a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public void write(byte[] buffer, int offset, int length) throws IOException {
81a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        if (offset < 0 || length < 0 || (offset + length) > buffer.length) {
82a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            throw new ArrayIndexOutOfBoundsException(String.format(
83a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                    " buffer length %d, offset %d, length %d",
84a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                    buffer.length, offset, length));
85a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
86a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        if (!mValidConfig) {
87a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            return;
88a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
89a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        if (mConfigChanged) {
90a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            cleanup();
91a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            int flag = setup(out, mWidth, mHeight, mFormat, mQuality);
92a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            switch(flag) {
93a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                case JpegConfig.J_SUCCESS:
94a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                    break; // allow setup to continue
95a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                case JpegConfig.J_ERROR_BAD_ARGS:
96a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                    throw new IllegalArgumentException("Bad arguments to write");
97a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                default:
98a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                    throw new IOException("Error to writing jpeg headers.");
99a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            }
100a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            mConfigChanged = false;
101a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
102a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        int returnCode = JpegConfig.J_ERROR_FATAL;
103a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        try {
104a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            returnCode = writeInputBytes(buffer, offset, length);
105a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        } finally {
106a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            if (returnCode < 0) {
107a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk                cleanup();
108a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            }
109a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
110a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        if (returnCode < 0) {
111a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            throw new IOException("Error writing jpeg stream");
112a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
113a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
114a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
115a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    @Override
116a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public void write(byte[] buffer) throws IOException {
117a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        write(buffer, 0, buffer.length);
118a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
119a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
120a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    @Override
121a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    public void write(int oneByte) throws IOException {
122a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        mTmpBuffer[0] = (byte) oneByte;
123a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        write(mTmpBuffer);
124a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
125a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
126a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    @Override
127a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    protected void finalize() throws Throwable {
128a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        try {
129a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            cleanup();
130a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        } finally {
131a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk            super.finalize();
132a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        }
133a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
134a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
135a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    native private int setup(OutputStream out, int width, int height, int format, int quality);
136a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
137a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    native private void cleanup();
138a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
139a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    native private int writeInputBytes(byte[] inBuffer, int offset, int inCount);
140a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk
141a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    static {
142a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk        System.loadLibrary("jni_jpegstream");
143a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk    }
144a8221bbdb8ece9b02dbf7b72565f9fbc5b314f7cRuben Brunk}
145