File: ImageCodec.java

package info (click to toggle)
java-imaging-utilities 0.14.3-7
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 2,556 kB
  • sloc: java: 31,233; python: 71; xml: 31; makefile: 26; sh: 5
file content (896 lines) | stat: -rw-r--r-- 26,810 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
/*
 * ImageCodec
 * 
 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt.
 * All rights reserved.
 */

package net.sourceforge.jiu.codecs;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Vector;
import net.sourceforge.jiu.ops.MissingParameterException;
import net.sourceforge.jiu.ops.Operation;
import net.sourceforge.jiu.ops.WrongParameterException;
import net.sourceforge.jiu.data.PixelImage;

/**
 * The base class for <em>image codecs</em>, operations to read images from or write them to streams.
 * A codec should support one file format only.
 * The word codec is derived from <em>enCOder DECoder</em>.
 * 
 * <h3>Usage</h3>
 * The codecs differ quite a bit in what they support.
 * But here are two code snippets that demonstrate how to do loading and saving in general.
 *
 * <h4>Load image</h4>
 * <pre>
 * ImageCodec codec = new BMPCodec(); // BMPCodec is just an example
 * codec.setFile("image.bmp", CodecMode.LOAD);
 * codec.process();
 * PixelImage image = codec.getImage();
 * </pre>
 *
 * <h4>Save image</h4>
 * <pre>
 * PixelImage image = ...; // the image to be saved
 * ImageCodec codec = new BMPCodec(); // BMPCodec is just an example
 * codec.setFile("image.bmp", CodecMode.SAVE);
 * codec.setImage(image);
 * codec.process();
 * </pre>
 *
 * <h3>I/O objects</h3>
 * There are several set and get methods for I/O objects, including
 * DataInput, DataOutput, InputStream, OutputStream and RandomAccessFile.
 * If you are just using the codec (and not developing one) make it easier
 * for yourself and use {@link #setFile(String, CodecMode)}.
 * That way the picking of the right type of I/O class and the creation of a 
 * buffered stream wrapper is done automatically.
 * <p>
 * Codecs have different requirements concerning I/O objects.
 * If an image is to be loaded, it's enough for some formats to linearly read
 * from an {@link java.io.InputStream} to load the image.
 * However, some formats (like TIFF) require random access.
 * <p>
 * When implementing a codec, take care that as many I/O classes as possible can be used.
 * If possible, call {@link #getInputAsDataInput} when loading and {@link #getOutputAsDataOutput}
 * when saving.
 * That way, input / output streams, RandomAccessFiles and arbitrary DataInput / DataOutput objects
 * can be used.
 * <p>
 * <h3>Mode</h3>
 * Codecs can be used to save images or load them, or both.
 * As was g; by default, no mode (of enumeration type {@link CodecMode}) 
 * is specified and {@link #getMode()} returns <code>null</code>.
 * Mode only has two possible values, {@link CodecMode#LOAD} and
 * {@link CodecMode#SAVE}.
 * In some cases, the codec can find out whether to load or save from the I/O objects
 * that were given to it; if it has an input stream, something must be loaded,
 * if it has an output stream, something is to be saved.
 * If a codec demands a {@link RandomAccessFile}, there is no way to find out
 * the mode automatically, that is why {@link #setRandomAccessFile} also has an 
 * argument of type {@link CodecMode}.
 * <p>
 * <strong>Bounds</strong>; to load or save only part of an image.
 * Defining bounds is optional; by default, the complete image is loaded
 * or saved (no bounds).
 * Using {@link #setBounds(int, int, int, int)}, one can specify the 
 * rectangle which will be loaded or saved.
 * <p>
 * <strong>PixelImage object</strong>; get and set methods for the image which is to be 
 * loaded or saved.
 * If an image is to be loaded, a PixelImage object can optionally be specified so that the image will
 * be written to that object; image type and resolution must of course match the image
 * from input.
 * Normally, the codec will create the appropriate image object
 * itself.
 * If an image is to be saved, an image object <em>must</em> be provided, otherwise there
 * is nothing to do.
 * <p>
 * <strong>Image index</strong>; the index of the image that is to be loaded (int value, default
 * is 0). For image formats that support more than one image in one stream, the index of the
 * image to be loaded (zero-based) can be specified using {@link #setImageIndex(int)}.
 *
 * <h3>Textual comments</h3>
 * Some file formats allow for the inclusion of textual comments, to
 * store a description, creator, copyright owner or anything else within the image
 * file without actually drawing that text on the image itself.
 * Some codecs support reading and writing of comments.
 *
 * <h3>Other methods</h3>
 * <p>
 * Each file format must be able to return its name ({@link #getFormatName()}) and
 * file extensions that are typical for it ({@link #getFileExtensions()}).
 * <p>
 * A related method suggests a file extension for a given PixelImage object ({@link #suggestFileExtension(PixelImage)}).
 * That method need not be implemented, the default version returns simply <code>null</code>.
 * However, it is encouraged that codec implementors provide this method as well.
 * Most file formats only have one typical extension (e. g. <code>.bmp</code>).
 * However, for a file format like PNM, the extension depends on the image type (a grayscale
 * image would end in <code>.pgm</code>, a color image in <code>.ppm</code>).
 * <p>
 * @author Marco Schmidt
 */
public abstract class ImageCodec extends Operation
{
	private int boundsX1;
	private int boundsY1;
	private int boundsX2;
	private int boundsY2;
	private boolean boundsAvail;
	private int boundsWidth;
	private int boundsHeight;
	private Vector comments;
	private int dpiX;
	private int dpiY;
	private DataInput din;
	private DataOutput dout;
	private PixelImage image;
	private int imageIndex;
	private InputStream in;
	private CodecMode mode;
	private OutputStream out;
	private RandomAccessFile raf;

	/**
	 * This constructor will be called by descendants.
	 * The bounds state is initialized to <em>no bounds</em>.
	 */
	public ImageCodec()
	{
		super();
		comments = new Vector();
		removeBounds();
	}

	/**
	 * Appends a comment to the internal list of comments.
	 * If the argument comment is non-null, it will be added to the internal
	 * list of comment strings.
	 * @param comment the comment to be added
	 */
	public void appendComment(String comment)
	{
		if (comment != null)
		{
			comments.addElement(comment);
		}
	}

	/**
	 * If bounds were defined for this codec, this method tests if the 
	 * bounds rectangle fits into the rectangle <code>(0, 0) / (width - 1, height - 1)</code>.
	 * If the bounds are incorrect, a {@link WrongParameterException} 
	 * is thrown, otherwise nothing happens.
	 * To be used within codecs that support the bounds concept.
	 */
	public void checkBounds(int width, int height) throws WrongParameterException
	{
		if (!hasBounds())
		{
			return;
		}
		int x1 = getBoundsX1();
		if (x1 >= width)
		{
			throw new WrongParameterException("Codec bounds x1 (" + x1 +
				") must be smaller than image width (" + width + ").");
		}
		int x2 = getBoundsX2();
		if (x2 >= width)
		{
			throw new WrongParameterException("Codec bounds x2 (" + x2 +
				") must be smaller than image width (" + width + ").");
		}
		int y1 = getBoundsY1();
		if (y1 >= height)
		{
			throw new WrongParameterException("Codec bounds y1 (" + y1 +
				") must be smaller than image height (" + height + ").");
		}
		int y2 = getBoundsY2();
		if (y2 >= height)
		{
			throw new WrongParameterException("Codec bounds y2 (" + y2 +
				") must be smaller than image height (" + height + ").");
		}
	}

	/**
	 * If an image object was provided to be used for loading via {@link #setImage},
	 * this method checks if its resolution is the same as the bounds' resolution.
	 * If the two differ, a {@link net.sourceforge.jiu.ops.WrongParameterException} is thrown.
	 * @throws WrongParameterException if image resolution and bounds dimension differ
	 */
	public void checkImageResolution() throws WrongParameterException
	{
		PixelImage image = getImage();
		if (image != null)
		{
			if (image.getWidth() != getBoundsWidth())
			{
				throw new WrongParameterException("Specified input image must have width equal to getBoundsWidth().");
			}
			if (image.getHeight() != getBoundsHeight())
			{
				throw new WrongParameterException("Specified input image must have height equal to getBoundsHeight().");
			}
		}
	}

	/**
	 * Calls the close method of all input and output I/O objects
	 * that were given to this object.
	 * Catches and ignores any IOException objects that may be
	 * thrown in the process.
	 * Note that not all I/O objects have a close method (e.g. {@link java.io.DataInput}
	 * and {@link java.io.DataOutput} have not).
	 */
	public void close()
	{
		try
		{
			if (in != null)
			{
				in.close();
			}
			if (out != null)
			{
				out.close();
			}
			if (raf != null)
			{
				raf.close();
			}
		}
		catch (IOException ioe)
		{
		}
	}

	/**
	 * Returns x coordinate of the upper left corner of the bounds.
	 * Bounds must have been specified using {@link #setBounds(int, int, int, int)},
	 * otherwise the return value is undefined.
	 * @return x coordinate of the upper left corner of the bounds
	 */
	public int getBoundsX1()
	{
		return boundsX1;
	}

	/**
	 * Returns x coordinate of the lower right corner of the bounds.
	 * Bounds must have been specified using {@link #setBounds(int, int, int, int)},
	 * otherwise the return value is undefined.
	 * @return x coordinate of the lower right corner of the bounds
	 */
	public int getBoundsX2()
	{
		return boundsX2;
	}

	/**
	 * Returns y coordinate of the upper left corner of the bounds.
	 * Bounds must have been specified using {@link #setBounds(int, int, int, int)},
	 * otherwise the return value is undefined.
	 * @return y coordinate of the upper left corner of the bounds
	 */
	public int getBoundsY1()
	{
		return boundsY1;
	}

	/**
	 * Returns y coordinate of the lower right corner of the bounds.
	 * Bounds must have been specified using {@link #setBounds(int, int, int, int)},
	 * otherwise the return value is undefined.
	 * @return y coordinate of the lower right corner of the bounds
	 */
	public int getBoundsY2()
	{
		return boundsY2;
	}

	/**
	 * Returns the height of the rectangle specified by bounds.
	 * Bounds must have been specified using {@link #setBounds(int, int, int, int)},
	 * otherwise the return value is undefined.
	 * This equals {@link #getBoundsY2()} - {@link #getBoundsY1()} + 1.
	 * @return height of bounds rectangle
	 */
	public int getBoundsHeight()
	{
		return boundsHeight;
	}

	/**
	 * Returns the width of the rectangle specified by bounds.
	 * Bounds must have been specified using {@link #setBounds(int, int, int, int)},
	 * otherwise the return value is undefined.
	 * This equals {@link #getBoundsX2()} - {@link #getBoundsX1()} + 1.
	 * @return width of bounds rectangle
	 */
	public int getBoundsWidth()
	{
		return boundsWidth;
	}

	/**
	 * Returns a comment from the internal list of comments.
	 * @param index the index of the comment to be returned, must be from 
	 *  <code>0</code> to {@link #getNumComments()}<code> - 1</code>; if this is not
	 *  the case, <code>null</code> will be returned
	 * @see #getNumComments
	 * @see #appendComment
	 * @see #removeAllComments
	 */
	public String getComment(int index)
	{
		if (index >= 0 && index < comments.size())
		{
			return (String)comments.elementAt(index);
		}
		else
		{
			return null;
		}
	}

	/**
	 * Returns a {@link java.io.DataInput} object if one was provided 
	 * via {@link #setDataInput(DataInput)} or <code>null</code> otherwise.
	 * @return the DataInput object
	 */
	public DataInput getDataInput()
	{
		return din;
	}

	/**
	 * Returns a {@link java.io.DataOutput} object if one was provided 
	 * via {@link #setDataOutput(DataOutput)} or <code>null</code> otherwise.
	 * @return the DataInput object
	 */
	public DataOutput getDataOutput()
	{
		return dout;
	}

	/**
	 * Returns the horizontal physical resolution of the image associated
	 * with this codec.
	 * This resolution value was either retrieved from an image file or 
	 * set via {@link #setDpi(int, int)}.
	 * @return horizontal physical resolution in dpi
	 * @see #getDpiY
	 */
	public int getDpiX()
	{
		return dpiX;
	}

	/**
	 * Returns the vertical physical resolution of the image associated
	 * with this codec.
	 * This resolution value was either retrieved from an image file or 
	 * set via {@link #setDpi(int, int)}.
	 * @return horizontal physical resolution in dpi
	 * @see #getDpiX
	 */
	public int getDpiY()
	{
		return dpiY;
	}

	/** 
	 * Returns all file extensions that are typical for this file format.
	 * The default implementation in ImageCodec returns <code>null</code>.
	 * The file extension strings should include a leading dot
	 * and are supposed to be lower case (if that is allowed for
	 * the given file format).
	 * Example: <code>{".jpg", ".jpeg"}</code> for the JPEG file format.
	 * @return String array with typical file extensions
	 */
	public String[] getFileExtensions()
	{
		return null;
	}

	/**
	 * Returns the name of the file format supported by this codec.
	 * All classes extending {@link ImageCodec} must override this method.
	 * When overriding, leave out any words in a particular language so
	 * that this format name can be understood by everyone.
	 * Usually it is enough to return the format creator plus a typical 
	 * abbreviation, e.g. <code>Microsoft BMP</code> or <code>Portable Anymap (PNM)</code>.
	 * @return name of the file format supported by this codec
	 */
	public abstract String getFormatName();

	/**
	 * Returns the image object stored in this codec.
	 * This is either an image given to this object via
	 * {@link #setImage(PixelImage)} or it was created by the codec
	 * itself during a loading operation.
	 * @return PixelImage object stored in this codec
	 */
	public PixelImage getImage()
	{
		return image;
	}

	/**
	 * Returns the zero-based index of the image to be loaded.
	 * Default is zero.
	 * @return zero-based image index value
	 */
	public int getImageIndex()
	{
		return imageIndex;
	}

	/**
	 * Returns a {@link java.io.DataInput} object if one was specified
	 * using {@link #setDataInput(DataInput)}, 
	 * or creates a {@link java.io.DataInputStream} if an 
	 * {@link java.io.InputStream} was specified,
	 * or returns a {@link java.io.RandomAccessFile} if one was specified
	 * (RandomAccessFile implements DataInput).
	 * If neither of those has been given to this object, <code>null</code> is returned.
	 * @return DataInput object or <code>null</code>
	 */
	public DataInput getInputAsDataInput()
	{
		DataInput din = getDataInput();
		if (din != null)
		{
			return din;
		}
		RandomAccessFile raf = getRandomAccessFile();
		if (getMode() == CodecMode.LOAD && raf != null)
		{
			return raf;
		}
		InputStream in = getInputStream();
		if (in != null)
		{
			if (in instanceof DataInput)
			{
				return (DataInput)in;
			}
			else
			{
				return new DataInputStream(in);
			}
		}
		return null;
	}

	/**
	 * Returns an {@link java.io.InputStream} object that was given to 
	 * this codec via {@link #setInputStream(InputStream)} 
	 * (or <code>null</code> otherwise).
	 * @return InputStream object
	 */
	public InputStream getInputStream()
	{
		return in;
	}

	/**
	 * Return the <a target="_top" href="http://www.faqs.org/rfcs/rfc2045.html">MIME</a> 
	 * (Multipurpose Internet Mail Extensions) type strings for this format, or <code>null</code>
	 * if none are available.
	 * @return MIME type strings or null
	 */
	public abstract String[] getMimeTypes();

	/** 
	 * Returns the mode this codec is in.
	 * Can be <code>null</code>, so that the codec will have to find out
	 * itself what to do.
	 * @return codec mode (load or save)
	 */
	public CodecMode getMode()
	{
		return mode;
	}

	/**
	 * Returns the current number of comments in the internal comment list.
	 * @return number of comments in the internal comment list
	 */
	public int getNumComments()
	{
		return comments.size();
	}

	/**
	 * Attempts to return an output object as a {@link java.io.DataOutput} object.
	 * @return a DataOutput object or null if that was not possible
	 */
	public DataOutput getOutputAsDataOutput()
	{
		DataOutput dout = getDataOutput();
		if (dout != null)
		{
			return dout;
		}
		OutputStream out = getOutputStream();
		if (out != null)
		{
			if (out instanceof DataOutput)
			{
				return (DataOutput)out;
			}
			else
			{
				return new DataOutputStream(out);
			}
		}
		RandomAccessFile raf = getRandomAccessFile();
		if (raf != null && getMode() == CodecMode.SAVE)
		{
			return raf;
		}
		return null;
	}

	/**
	 * Returns an {@link java.io.OutputStream} object that was given to 
	 * this codec via {@link #setOutputStream(OutputStream)} 
	 * (or <code>null</code> otherwise).
	 * @return OutputStream object
	 */
	public OutputStream getOutputStream()
	{
		return out;
	}

	/**
	 * Returns a {@link java.io.RandomAccessFile} object that was given to 
	 * this codec via {@link #setRandomAccessFile(RandomAccessFile, CodecMode)} 
	 * (or <code>null</code> otherwise).
	 * @return RandomAccessFile object
	 */
	public RandomAccessFile getRandomAccessFile()
	{
		return raf;
	}

	/**
	 * Returns if bounds have been specified.
	 * @return if bounds have been specified
	 * @see #removeBounds()
	 * @see #setBounds(int, int, int, int)
	 */
	public boolean hasBounds()
	{
		return boundsAvail;
	}

	protected void initModeFromIOObjects() throws MissingParameterException
	{
		if (getMode() != null)
		{
			return;
		}
		if (getInputStream() != null || getDataInput() != null)
		{
			mode = CodecMode.LOAD;
		}
		else
		if (getOutputStream() != null || getDataOutput() != null)
		{
			mode = CodecMode.SAVE;
		}
		else
		{
			throw new MissingParameterException("No streams or files available.");
		}
	}

	/**
	 * Returns if this codec is able to load images in the file format supported by this codec.
	 * If <code>true</code> is returned this does not necessarily mean that all files in this
	 * format can be read, but at least some.
	 * @return if loading is supported
	 */
	public abstract boolean isLoadingSupported();

	/**
	 * Returns if this codec is able to save images in the file format supported by this codec.
	 * If <code>true</code> is returned this does not necessarily mean that all types files in this
	 * format can be written, but at least some.
	 * @return if saving is supported
	 */
	public abstract boolean isSavingSupported();

	/**
	 * Returns if an image row given by its number (zero-based) must be loaded
	 * in the context of the current bounds.
	 * <p>
	 * Example: if vertical bounds have been set to 34 and 37, image rows 34 to
	 * 37 as arguments to this method would result in <code>true</code>, anything
	 * else (e.g. 12 or 45) would result in <code>false</code>.
	 *
	 * @param row the number of the row to be checked
	 * @return if row must be loaded, regarding the current bounds
	 */
	public boolean isRowRequired(int row)
	{
		if (hasBounds())
		{
			return (row >= boundsY1 && row <= boundsY2);
		}
		else
		{
			return (row >= 0 && row < getImage().getHeight());
		}
	}

	/** 
	 * Returns if the tile formed by the argument coordinates 
	 * form a rectangle that overlaps with the bounds.
	 * If no bounds were defined, returns <code>true</code>.
	 * @param x1 
	 * @param y1 
	 * @param x2 
	 * @param y2 
	 * @return if the argument tile is required
	 */
	public boolean isTileRequired(int x1, int y1, int x2, int y2)
	{
		if (hasBounds())
		{
			return !
				(getBoundsY2() < y1 ||
				 getBoundsY1() > y2 ||
				 getBoundsX2() < x1 ||
				 getBoundsX1() > x2);
		}
		else
		{
			return true;
		}
	}

	/**
	 * Removes all entries from the internal list of comments.
	 */
	public void removeAllComments()
	{
		comments.removeAllElements();
	}

	/**
	 * If bounds were set using {@link #setBounds(int, int, int, int)}, these
	 * bounds are no longer regarded after the call to this method.
	 */
	public void removeBounds()
	{
		boundsAvail = false;
	}

	/**
	 * Sets the bounds of a rectangular part of the image that
	 * is to be loaded or saved, instead of the complete image.
	 */
	public void setBounds(int x1, int y1, int x2, int y2)
	{
		if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1)
		{
			throw new IllegalArgumentException("Not a valid bounds rectangle: " +
				"x1=" + x1 + ", y1=" + y1 + ", x2=" + x2 + ", y2=" + y2);
		}
		boundsX1 = x1;
		boundsY1 = y1;
		boundsX2 = x2;
		boundsY2 = y2;
		boundsAvail = true;
		boundsWidth = x2 - x1 + 1;
		boundsHeight = y2 - y1 + 1;
	}

	/**
	 * If no bounds have been set ({@link #hasBounds()} returns <code>false</code>),
	 * this method will set the bounds to <code>0, 0, width - 1, height - 1</code>.
	 * By calling this method somewhere in the codec, no distinction has to
	 * be made for the two cases <em>bounds have been defined</em> and 
	 * <em>bounds have not been defined</em>.
	 * @param width width of the image to be loaded or saved
	 * @param height height of the image to be loaded or saved
	 */
	public void setBoundsIfNecessary(int width, int height)
	{
		if (!hasBounds())
		{
			setBounds(0, 0, width - 1, height - 1);
		}
	}

	/**
	 * Specifies a DataInput object to be used for loading.
	 * @param dataInput DataInput object to be used for loading an image
	 */
	public void setDataInput(DataInput dataInput)
	{
		din = dataInput;
	}

	/**
	 * Sets a {@link java.io.DataOutput} object to be used for saving
	 * an image.
	 * @param dataOutput the object to be used for output
	 */
	public void setDataOutput(DataOutput dataOutput)
	{
		dout = dataOutput;
	}

	/**
	 * Sets the DPI values to be stored in the file to the argument values.
	 * @param horizontalDpi horizontal physical resolution in DPI (dots per inch)
	 * @param verticalDpi vertical physical resolution in DPI (dots per inch)
	 * @see #getDpiX
	 * @see #getDpiY
	 */
	public void setDpi(int horizontalDpi, int verticalDpi)
	{
		dpiX = horizontalDpi;
		dpiY = verticalDpi;
	}

	/**
	 * Gives a File object and a codec mode to this codec and attempts
	 * to initialize the appropriate I/O objects.
	 * Simply calls {@link #setFile(String, CodecMode)} with the absolute
	 * path of the File object.
	 * @param file File object for the file to be used
	 * @param codecMode defines whether an image is to be loaded from or saved to the file
	 */
	public void setFile(File file, CodecMode codecMode) throws 
		IOException, 
		UnsupportedCodecModeException
	{
		setFile(file.getAbsolutePath(), codecMode);
	}

	/**
	 * Gives a file name and codec mode to the codec which will then
	 * try to create the corresponding I/O object.
	 * The default implementation in ImageCodec creates a DataInputStream object
	 * wrapped around a BufferedInputStream wrapped around a FileInputStream for
	 * CodecMode.LOAD.
	 * Similar for CodecMode.SAVE: a DataOutputStream around a BufferedOutputStream
	 * object around a FileOutputStream object.
	 * Codecs that need different I/O objects must override this method
	 * (some codecs may need random access and thus require a RandomAccessFile object).
	 * @param fileName name of the file to be used for loading or saving
	 * @param codecMode defines whether file is to be used for loading or saving
	 */
	public void setFile(String fileName, CodecMode codecMode) throws 
		IOException, 
		UnsupportedCodecModeException
	{
		if (codecMode == CodecMode.LOAD)
		{
			if (isLoadingSupported())
			{
				setInputStream(new BufferedInputStream(new FileInputStream(fileName)));
			}
			else
			{
				throw new UnsupportedCodecModeException("Loading is not supported for this codec (" + getFormatName() + ").");
			}
		}
		else
		{
			if (isSavingSupported())
			{
				setOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
			}
			else
			{
				throw new UnsupportedCodecModeException("Saving is not supported for this codec (" + getFormatName() + ").");
			}
		}
	}

	/**
	 * Give an image to this codec to be used for loading an image into it
	 * or saving the image.
	 * @param img image object to save or to load data into
	 */
	public void setImage(PixelImage img)
	{
		image = img;
	}

	/**
	 * Sets the index of the image to be loaded to the argument value
	 * (which must be zero or larger).
	 * @param index int index value (zero-based) of the image to be loaded
	 * @throws IllegalArgumentException if the argument is negative
	 */
	public void setImageIndex(int index)
	{
		if (index < 0)
		{
			throw new IllegalArgumentException("The index must be 0 or larger.");
		}
		imageIndex = index;
	}

	/**
	 * An {@link java.io.InputStream} can be given to this codec using this method.
	 * @param inputStream InputStream object to read from
	 */
	public void setInputStream(InputStream inputStream)
	{
		in = inputStream;
	}

	/**
	 * A method to give an {@link java.io.OutputStream} to this codec to be used 
	 * for saving an image.
	 * @param outputStream the output stream to be used by this codec
	 */
	public void setOutputStream(OutputStream outputStream)
	{
		out = outputStream;
	}

	/**
	 * A method to give a {@link java.io.RandomAccessFile} to this codec to be used 
	 * for loading or saving an image.
	 * It is not possible to determine from a RandomAccessFile object whether it
	 * was opened in read-only or read-and-write mode.
	 * To let the codec know whether the object is to be used for loading or saving
	 * the second argument is of type CodecMode.
	 * @param randomAccessFile the file to be used for loading or saving
	 * @param codecMode tells the codec whether the file is to be used for loading or saving
	 */
	public void setRandomAccessFile(RandomAccessFile randomAccessFile, CodecMode codecMode)
	{
		if (randomAccessFile == null)
		{
			throw new IllegalArgumentException("Argument RandomAccessFile must be non-null.");
		}
		if (codecMode == null)
		{
			throw new IllegalArgumentException("Argument codec mode must be non-null.");
		}
		raf = randomAccessFile;
		mode = codecMode;
	}

	/**
	 * Attempts to suggest a filename extension.
	 * The type of the argument image will be taken into consideration,
	 * although this will be necessary for some file formats only (as an
	 * example, PNM has different extensions for different image types, see
	 * {@link PNMCodec}). 
	 * This default implementation always returns <code>null</code>.
	 * @param image the image that is to be written to a file
	 * @return the file extension, including a leading dot, or <code>null</code> if no file extension can be recommended
	 */
	public String suggestFileExtension(PixelImage image)
	{
		return null;
	}
}