001/* ImageOutputStream.java --
002   Copyright (C) 2004  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.imageio.stream;
040
041import java.io.IOException;
042import java.io.UTFDataFormatException;
043import java.nio.ByteOrder;
044
045/**
046 * @author Michael Koch (konqueror@gmx.de)
047 */
048public abstract class ImageOutputStreamImpl extends ImageInputStreamImpl
049  implements ImageOutputStream
050{
051  public ImageOutputStreamImpl()
052  {
053    // Do nothing here.
054  }
055
056  protected final void flushBits()
057    throws IOException
058  {
059    checkClosed();
060    if (bitOffset != 0)
061      {
062        int offset = bitOffset;
063        int partial = read();
064        if (partial < 0)
065          {
066            partial = 0;
067            bitOffset = 0;
068          }
069        else
070          {
071            seek(getStreamPosition() - 1);
072            partial &= -1 << (8 - offset);
073          }
074        write(partial);
075      }
076  }
077
078  public void write(byte[] data)
079    throws IOException
080  {
081    write(data, 0, data.length);
082  }
083
084  public abstract void write(byte[] data, int offset, int len)
085    throws IOException;
086
087  public abstract void write(int value)
088    throws IOException;
089
090  public void writeBit(int bit)
091    throws IOException
092  {
093    writeBits(1L & bit, 1);
094  }
095
096  public void writeBits(long bits, int numBits)
097    throws IOException
098  {
099    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
169  public void writeBoolean(boolean value)
170    throws IOException
171  {
172    writeByte(value ? 1 : 0);
173  }
174
175  public void writeByte(int value)
176    throws IOException
177  {
178    write(value & 0xff);
179  }
180
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
191  public void writeChar(int value)
192    throws IOException
193  {
194    writeShort(value);
195  }
196
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
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
212  public void writeDouble(double value)
213    throws IOException
214  {
215    writeLong(Double.doubleToLongBits(value));
216  }
217
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
225  public void writeFloat(float value)
226    throws IOException
227  {
228    writeInt(Float.floatToIntBits(value));
229  }
230
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
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
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
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
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
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
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
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}