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