1 /* ImageOutputStream.java --
2    Copyright (C) 2004  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.imageio.stream;
40 
41 import java.io.IOException;
42 import java.io.UTFDataFormatException;
43 import java.nio.ByteOrder;
44 
45 /**
46  * @author Michael Koch (konqueror@gmx.de)
47  */
48 public abstract class ImageOutputStreamImpl extends ImageInputStreamImpl
49   implements ImageOutputStream
50 {
ImageOutputStreamImpl()51   public ImageOutputStreamImpl()
52   {
53     // Do nothing here.
54   }
55 
flushBits()56   protected final void flushBits()
57     throws IOException
58   {
59     checkClosed();
60     if (bitOffset != 0)
61       {
62         int offset = bitOffset;
63         int partial = read();
64         if (partial < 0)
65           {
66             partial = 0;
67             bitOffset = 0;
68           }
69         else
70           {
71             seek(getStreamPosition() - 1);
72             partial &= -1 << (8 - offset);
73           }
74         write(partial);
75       }
76   }
77 
write(byte[] data)78   public void write(byte[] data)
79     throws IOException
80   {
81     write(data, 0, data.length);
82   }
83 
write(byte[] data, int offset, int len)84   public abstract void write(byte[] data, int offset, int len)
85     throws IOException;
86 
write(int value)87   public abstract void write(int value)
88     throws IOException;
89 
writeBit(int bit)90   public void writeBit(int bit)
91     throws IOException
92   {
93     writeBits(1L & bit, 1);
94   }
95 
writeBits(long bits, int numBits)96   public void writeBits(long bits, int numBits)
97     throws IOException
98   {
99     checkClosed();
100     // Append chunk of bits to any preexisting bits, if any.
101     if (getStreamPosition() > 0 || bitOffset > 0)
102       {
103         int offs = bitOffset;
104         int partial = read();
105         if (partial != -1)
106           seek(getStreamPosition() - 1);
107         else
108           partial = 0;
109         if (numBits + offs < 8)
110           {
111             // Append complete bits to partial byte.
112             int shift = 8 - (offs + numBits);
113             int mask = -1 >>> (32 - numBits);
114             partial &= ~(mask << shift);
115             partial |= (bits & mask) << shift;
116             write(partial);
117             seek(getStreamPosition() - 1);
118             bitOffset = offs + numBits;
119             numBits = 0;
120           }
121         else
122           {
123             // Append bits and decrease numBits accordingly.
124             int num = 8 - offs;
125             int mask = -1 >>> (32 - num);
126             partial &= ~mask;
127             partial |= (bits >> (numBits - num)) & mask;
128             write(partial);
129             numBits -= num;
130           }
131       }
132 
133     // Write out whole chunks, if any.
134     if (numBits > 7)
135       {
136         int remaining = numBits % 8;
137         for (int numBytes = numBits / 8; numBytes > 0; numBytes--)
138           {
139             int shift = (numBytes - 1) * 8 + remaining;
140             int value = (int) ((shift == 0) ? bits & 0xff
141                                             : (bits >> shift) & 0xff);
142             write(value);
143           }
144         numBits = remaining;
145       }
146 
147     // Write remaing partial bytes.
148     if (numBits != 0)
149       {
150         int partial = read();
151         if (partial == -1)
152           {
153             seek(getStreamPosition() - 1);
154           }
155         else
156           {
157             partial = 0;
158           }
159         int shift = 8 - numBits;
160         int mask = -1 >>> (32 - numBits);
161         partial &= ~(mask << shift);
162         partial |= (bits & mask) << shift;
163         write(partial);
164         seek(getStreamPosition() - 1);
165         bitOffset = numBits;
166       }
167   }
168 
writeBoolean(boolean value)169   public void writeBoolean(boolean value)
170     throws IOException
171   {
172     writeByte(value ? 1 : 0);
173   }
174 
writeByte(int value)175   public void writeByte(int value)
176     throws IOException
177   {
178     write(value & 0xff);
179   }
180 
writeBytes(String data)181   public void writeBytes(String data)
182     throws IOException
183   {
184     // This is bogus, but it is how the method is specified.
185     // Sun ought to deprecate this method.
186     int len = data.length();
187     for (int i = 0; i < len; ++i)
188       writeByte(data.charAt(i));
189   }
190 
writeChar(int value)191   public void writeChar(int value)
192     throws IOException
193   {
194     writeShort(value);
195   }
196 
writeChars(char[] data, int offset, int len)197   public void writeChars(char[] data, int offset, int len)
198     throws IOException
199   {
200     for(int i = 0; i < len; ++len)
201       writeChar(data[offset + i]);
202   }
203 
writeChars(String data)204   public void writeChars(String data)
205     throws IOException
206   {
207     int len = data.length();
208     for (int i = 0; i < len; ++i)
209       writeChar(data.charAt(i));
210   }
211 
writeDouble(double value)212   public void writeDouble(double value)
213     throws IOException
214   {
215     writeLong(Double.doubleToLongBits(value));
216   }
217 
writeDoubles(double[] data, int offset, int len)218   public void writeDoubles(double[] data, int offset, int len)
219     throws IOException
220   {
221     for(int i = 0; i < len; ++len)
222       writeDouble(data[offset + i]);
223   }
224 
writeFloat(float value)225   public void writeFloat(float value)
226     throws IOException
227   {
228     writeInt(Float.floatToIntBits(value));
229   }
230 
writeFloats(float[] data, int offset, int len)231   public void writeFloats(float[] data, int offset, int len)
232     throws IOException
233   {
234     for(int i = 0; i < len; ++len)
235       writeFloat(data[offset + i]);
236   }
237 
writeInt(int value)238   public void writeInt(int value)
239     throws IOException
240   {
241     if (getByteOrder() == ByteOrder.LITTLE_ENDIAN)
242       {
243         buffer[0] = ((byte) value);
244         buffer[1] = ((byte) (value >> 8));
245         buffer[2] = ((byte) (value >> 16));
246         buffer[3] = ((byte) (value >> 24));
247       }
248     else
249       {
250         buffer[0] = ((byte) (value >> 24));
251         buffer[1] = ((byte) (value >> 16));
252         buffer[2] = ((byte) (value >> 8));
253         buffer[3] = ((byte) value);
254       }
255 
256     write(buffer, 0, 4);
257   }
258 
writeInts(int[] data, int offset, int len)259   public void writeInts(int[] data, int offset, int len)
260     throws IOException
261   {
262     for(int i = 0; i < len; ++len)
263       writeInt(data[offset + i]);
264   }
265 
writeLong(long value)266   public void writeLong(long value)
267     throws IOException
268   {
269     if (getByteOrder() == ByteOrder.LITTLE_ENDIAN)
270       {
271         buffer[0] = ((byte) value);
272         buffer[1] = ((byte) (value >> 8));
273         buffer[2] = ((byte) (value >> 16));
274         buffer[3] = ((byte) (value >> 24));
275         buffer[4] = ((byte) (value >> 32));
276         buffer[5] = ((byte) (value >> 40));
277         buffer[6] = ((byte) (value >> 48));
278         buffer[7] = ((byte) (value >> 56));
279       }
280     else
281       {
282         buffer[0] = ((byte) (value >> 56));
283         buffer[1] = ((byte) (value >> 48));
284         buffer[2] = ((byte) (value >> 40));
285         buffer[3] = ((byte) (value >> 32));
286         buffer[4] = ((byte) (value >> 24));
287         buffer[5] = ((byte) (value >> 16));
288         buffer[6] = ((byte) (value >> 8));
289         buffer[7] = ((byte) value);
290       }
291 
292     write(buffer, 0, 8);
293   }
294 
writeLongs(long[] data, int offset, int len)295   public void writeLongs(long[] data, int offset, int len)
296     throws IOException
297   {
298     for(int i = 0; i < len; ++len)
299       writeLong(data[offset + i]);
300   }
301 
writeShort(int value)302   public void writeShort(int value)
303     throws IOException
304   {
305     if (getByteOrder() == ByteOrder.LITTLE_ENDIAN)
306       {
307         buffer[0] = ((byte) value);
308         buffer[1] = ((byte) (value >> 8));
309       }
310     else
311       {
312         buffer[0] = ((byte) (value >> 8));
313         buffer[1] = ((byte) value);
314       }
315 
316     write(buffer, 0, 2);
317   }
318 
writeShorts(short[] data, int offset, int len)319   public void writeShorts(short[] data, int offset, int len)
320     throws IOException
321   {
322     for(int i = 0; i < len; ++len)
323       writeShort(data[offset + i]);
324   }
325 
writeUTF(String value)326   public void writeUTF(String value)
327     throws IOException
328   {
329     // NOTE: this code comes directly from DataOutputStream.
330     int len = value.length();
331     int sum = 0;
332 
333     for (int i = 0; i < len && sum <= 65535; ++i)
334       {
335         char c = value.charAt(i);
336         if (c >= '\u0001' && c <= '\u007f')
337           sum += 1;
338         else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff'))
339           sum += 2;
340         else
341           sum += 3;
342       }
343 
344     if (sum > 65535)
345       throw new UTFDataFormatException ();
346 
347     int pos = 0;
348     byte[] buf = new byte[sum];
349 
350     for (int i = 0; i < len; ++i)
351       {
352         char c = value.charAt(i);
353         if (c >= '\u0001' && c <= '\u007f')
354           buf[pos++] = (byte) c;
355         else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff'))
356           {
357             buf[pos++] = (byte) (0xc0 | (0x1f & (c >> 6)));
358             buf[pos++] = (byte) (0x80 | (0x3f & c));
359           }
360         else
361           {
362             // JSL says the first byte should be or'd with 0xc0, but
363             // that is a typo.  Unicode says 0xe0, and that is what is
364             // consistent with DataInputStream.
365             buf[pos++] = (byte) (0xe0 | (0x0f & (c >> 12)));
366             buf[pos++] = (byte) (0x80 | (0x3f & (c >> 6)));
367             buf[pos++] = (byte) (0x80 | (0x3f & c));
368           }
369       }
370 
371     writeShort (sum);
372     write(buf, 0, sum);
373   }
374 }
375