001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032
033 package com.imagero.uio.io;
034
035 import java.io.ByteArrayInputStream;
036 import java.io.ByteArrayOutputStream;
037 import java.io.File;
038 import java.io.FileInputStream;
039 import java.io.FileOutputStream;
040 import java.io.FilterInputStream;
041 import java.io.IOException;
042 import java.io.InputStream;
043 import java.io.OutputStream;
044
045
046 /**
047 * remove all App blocks from jpeg file
048 * <br>
049 * how to use:
050 * <br>
051 * <pre>
052 * //read data from file and save filtered data to another file
053 * File fs = new File("source.jpg");
054 * File fd = new File("dest.jpg");
055 * JpegFilterInputStream.filter(fs, fd);
056 *
057 * or
058 *
059 * //filter data in byte array
060 * byte [] source = ...
061 * byte [] dest = JpegFilterInputStream.filter(source);
062 *
063 * or
064 *
065 * //filter data from InputStream
066 * InputStream in;
067 * OutputStream out;
068 * JpegFilterInputStream.filter(in, out);
069 *
070 * or
071 *
072 * //just wrap InputStream
073 * JpegFilterInputStream jfis = new JpegFilterInputStream(in);
074 *
075 * </pre>
076 * @author Andrey Kuznetsov
077 */
078 public class JpegFilterInputStream extends FilterInputStream {
079
080 public static final int APP_0 = 0xE0;
081 public static final int APP_1 = 0xE1;
082 public static final int APP_2 = 0xE2;
083 public static final int APP_3 = 0xE3;
084 public static final int APP_4 = 0xE4;
085 public static final int APP_5 = 0xE5;
086 public static final int APP_6 = 0xE6;
087 public static final int APP_7 = 0xE7;
088 public static final int APP_8 = 0xE8;
089 public static final int APP_9 = 0xE9;
090 public static final int APP_10 = 0xEA;
091 public static final int APP_11 = 0xEB;
092 public static final int APP_12 = 0xEC;
093 public static final int APP_13 = 0xED;
094 public static final int APP_14 = 0xEE;
095 public static final int APP_15 = 0xEF;
096
097 public static final int[] allMarkers = new int[]{
098 APP_0, APP_1, APP_2, APP_3, APP_4, APP_5, APP_6, APP_7,
099 APP_8, APP_9, APP_10, APP_11, APP_12, APP_13, APP_14, APP_15
100 };
101
102 public static final int[] defaultMarkers = new int[]{
103 APP_1, APP_2, APP_3, APP_4, APP_5, APP_6, APP_7,
104 APP_8, APP_9, APP_10, APP_11, APP_12, APP_13, APP_14, APP_15
105 };
106
107 public static void filterFile(File src, File dest) throws IOException {
108 FileInputStream in = new FileInputStream(src);
109 FileOutputStream out = new FileOutputStream(dest);
110
111 filter(in, out);
112 IOutils.closeStream(out);
113 IOutils.closeStream(in);
114 }
115
116 /**
117 * filter out all markers (except App0)
118 * @param in InputStream
119 * @param out
120 * @throws java.io.IOException
121 */
122 public static void filter(InputStream in, OutputStream out) throws IOException {
123 filter(in, out, defaultMarkers);
124 }
125
126 public static void filter(InputStream in0, OutputStream out, int[] markers) throws IOException {
127 JpegFilterInputStream in = new JpegFilterInputStream(in0, markers);
128 int a = 0;
129 while (a >= 0) {
130 a = in.read();
131 out.write(a);
132 }
133 }
134
135 /**
136 * filter out all markers (except App0)
137 * @param data
138 * @return byte array
139 * @throws IOException
140 */
141 public static byte[] filter(byte[] data) throws IOException {
142 return filter(data, defaultMarkers);
143 }
144
145 public static byte[] filter(byte[] data, int[] markers) throws IOException {
146 final ByteArrayInputStream in0 = new ByteArrayInputStream(data);
147 ByteArrayOutputStream bout = new ByteArrayOutputStream();
148 filter(in0, bout, markers);
149 return bout.toByteArray();
150 }
151
152 int[] markers;
153
154 /**
155 * create JpegFilterInputStream (filter out all markers except App0)
156 * @param in InputStream (with valid JPEG stream)
157 */
158 public JpegFilterInputStream(InputStream in) {
159 this(in, JpegFilterInputStream.defaultMarkers);
160 }
161
162 /**
163 * create JpegFilterInputStream
164 * @param in InputStream (with valid JPEG stream)
165 * @param markers markers to filter out
166 */
167 public JpegFilterInputStream(InputStream in, int[] markers) {
168 super(in);
169 this.markers = markers;
170 }
171
172 private boolean markerOn;
173
174 public int read() throws IOException {
175 int a = in.read();
176 if (!markerOn) {
177 if (a == 0xFF) {
178 markerOn = true;
179 }
180 return a;
181 }
182 else {
183 if (isAppMarker(a)) {
184 int length = (in.read() << 8) + in.read() - 2;
185 // Sys.out.println("length:" + length);
186 in.skip(length);
187 int b = in.read();
188 if (b != 0xFF) {
189 throw new IOException("marker???");
190 }
191 return read();
192 }
193 else {
194 markerOn = false;
195 return a;
196 }
197 }
198 }
199
200 public int read(byte b[]) throws IOException {
201 return read(b, 0, b.length);
202 }
203
204 public int read(byte b[], int off, int len) throws IOException {
205 if (b == null) {
206 throw new NullPointerException();
207 }
208 if (off + len > b.length || off < 0) {
209 throw new ArrayIndexOutOfBoundsException();
210 }
211 int read = 1;
212 int a = read();
213 if (a == -1) {
214 return -1;
215 }
216 b[off] = (byte) a;
217 for (int i = off + 1; i < len; i++) {
218 a = read();
219 if (a == -1) {
220 break;
221 }
222 read++;
223 b[i] = (byte) a;
224 }
225 return read;
226 }
227
228 public boolean isAppMarker(int a) {
229 for (int i = 0; i < markers.length; i++) {
230 if (a == markers[i]) {
231 return true;
232 }
233 }
234 return false;
235 }
236 }