1 package org.ibex.graphics;
2 import org.eclipse.swt.*;
3 import org.eclipse.swt.graphics.*;
4 import org.eclipse.swt.*;
8 // Big thanks to the SWT project for this!
10 // The code below was badly butchered by Adam Megacz in order to make
11 // it work outside SWT.
16 private static class SWT {
17 public static final int ERROR_INVALID_ARGUMENT = 0;
18 public static final int ERROR_NULL_ARGUMENT = 1;
19 public static final int ERROR_CANNOT_BE_ZERO = 2;
20 public static final int ERROR_INVALID_IMAGE = 3;
21 public static final int IMAGE_UNDEFINED = 4;
22 public static final int TRANSPARENCY_PIXEL = 5;
23 public static final int TRANSPARENCY_ALPHA = 6;
24 public static final int ERROR_UNSUPPORTED_DEPTH = 7;
25 public static final int ERROR_IO = 8;
26 public static final int TRANSPARENCY_MASK = 9;
27 public static final int TRANSPARENCY_NONE = 10;
28 public static final int ERROR_UNSUPPORTED_FORMAT = 11;
29 public static final int IMAGE_BMP_RLE = 12;
30 public static final int IMAGE_JPEG = 13;
31 public static void error(int i) { }
32 public static void error(int i, Throwable t) { }
35 /*******************************************************************************
36 * Copyright (c) 2000, 2003 IBM Corporation and others.
37 * All rights reserved. This program and the accompanying materials
38 * are made available under the terms of the Common Public License v1.0
39 * which accompanies this distribution, and is available at
40 * http://www.eclipse.org/legal/cpl-v10.html
43 * IBM Corporation - initial API and implementation
44 *******************************************************************************/
46 static final class LEDataOutputStream extends OutputStream {
48 public LEDataOutputStream(OutputStream output) {
51 public void close() throws IOException {
54 public void write(byte b[], int off, int len) throws IOException {
55 out.write(b, off, len);
58 * Write the given byte to the output stream.
60 public void write(int b) throws IOException {
64 * Write the given byte to the output stream.
66 public void writeByte(byte b) throws IOException {
70 * Write the four bytes of the given integer
71 * to the output stream.
73 public void writeInt(int theInt) throws IOException {
74 out.write(theInt & 0xFF);
75 out.write((theInt >> 8) & 0xFF);
76 out.write((theInt >> 16) & 0xFF);
77 out.write((theInt >> 24) & 0xFF);
80 * Write the two bytes of the given short
81 * to the output stream.
83 public void writeShort(int theShort) throws IOException {
84 out.write(theShort & 0xFF);
85 out.write((theShort >> 8) & 0xFF);
88 /*******************************************************************************
89 * Copyright (c) 2000, 2003 IBM Corporation and others.
90 * All rights reserved. This program and the accompanying materials
91 * are made available under the terms of the Common Public License v1.0
92 * which accompanies this distribution, and is available at
93 * http://www.eclipse.org/legal/cpl-v10.html
96 * IBM Corporation - initial API and implementation
97 *******************************************************************************/
99 public static abstract class FileFormat {
100 static final String FORMAT_PACKAGE = "org.eclipse.swt.internal.image"; //$NON-NLS-1$
101 static final String FORMAT_SUFFIX = "FileFormat"; //$NON-NLS-1$
102 static final String[] FORMATS = {"WinBMP", "WinBMP", "GIF", "WinICO", "JPEG", "PNG", "TIFF", "OS2BMP"}; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$ //$NON-NLS-6$//$NON-NLS-7$//$NON-NLS-8$
104 LEDataInputStream inputStream;
105 LEDataOutputStream outputStream;
109 byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
110 // Destructively bit invert data in the given byte array.
111 for (int i = startIndex; i < endIndex; i++) {
112 data[i] = (byte)(255 - data[i - startIndex]);
118 * Return whether or not the specified input stream
119 * represents a supported file format.
121 abstract boolean isFileFormat(LEDataInputStream stream);
123 abstract ImageData[] loadFromByteStream();
125 public ImageData[] loadFromStream(LEDataInputStream stream) {
127 inputStream = stream;
128 return loadFromByteStream();
129 } catch (Exception e) {
130 SWT.error(SWT.ERROR_IO, e);
135 public static ImageData[] load(InputStream is, ImageLoader loader) {
136 FileFormat fileFormat = null;
137 LEDataInputStream stream = new LEDataInputStream(is);
138 boolean isSupported = false;
139 for (int i = 1; i < FORMATS.length; i++) {
140 if (FORMATS[i] != null) {
142 Class clazz = Class.forName(FORMAT_PACKAGE + '.' + FORMATS[i] + FORMAT_SUFFIX);
143 fileFormat = (FileFormat) clazz.newInstance();
144 if (fileFormat.isFileFormat(stream)) {
148 } catch (ClassNotFoundException e) {
150 } catch (Exception e) {
154 if (!isSupported) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
155 fileFormat.loader = loader;
156 return fileFormat.loadFromStream(stream);
159 public static void save(OutputStream os, int format, ImageLoader loader) {
160 if (format < 0 || format >= FORMATS.length) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
161 if (FORMATS[format] == null) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
163 /* We do not currently support writing multi-image files,
164 * so we use the first image data in the loader's array. */
165 ImageData data = loader.data[0];
166 LEDataOutputStream stream = new LEDataOutputStream(os);
167 FileFormat fileFormat = null;
169 Class clazz = Class.forName(FORMAT_PACKAGE + '.' + FORMATS[format] + FORMAT_SUFFIX);
170 fileFormat = (FileFormat) clazz.newInstance();
171 } catch (Exception e) {
172 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
174 if (format == SWT.IMAGE_BMP_RLE) {
175 switch (data.depth) {
176 case 8: fileFormat.compression = 1; break;
177 case 4: fileFormat.compression = 2; break;
180 fileFormat.unloadIntoStream(data, stream);
183 abstract void unloadIntoByteStream(ImageData image);
185 public void unloadIntoStream(ImageData image, LEDataOutputStream stream) {
187 outputStream = stream;
188 unloadIntoByteStream(image);
189 outputStream.close();
190 } catch (Exception e) {
191 try {outputStream.close();} catch (Exception f) {}
192 SWT.error(SWT.ERROR_IO, e);
198 /*******************************************************************************
199 * Copyright (c) 2000, 2003 IBM Corporation and others.
200 * All rights reserved. This program and the accompanying materials
201 * are made available under the terms of the Common Public License v1.0
202 * which accompanies this distribution, and is available at
203 * http://www.eclipse.org/legal/cpl-v10.html
206 * IBM Corporation - initial API and implementation
207 *******************************************************************************/
210 * Classes which implement this interface provide methods
211 * that deal with the incremental loading of image data.
213 * After creating an instance of a class that implements
214 * this interface it can be added to an image loader using the
215 * <code>addImageLoaderListener</code> method and removed using
216 * the <code>removeImageLoaderListener</code> method. When
217 * image data is either partially or completely loaded, this
218 * method will be invoked.
222 * @see ImageLoaderEvent
225 public static interface ImageLoaderListener {
228 * Sent when image data is either partially or completely loaded.
230 * The timing of when this method is called varies depending on
231 * the format of the image being loaded.
234 * @param e an event containing information about the image loading operation
236 public void imageDataLoaded(ImageLoaderEvent e);
239 /*******************************************************************************
240 * Copyright (c) 2000, 2003 IBM Corporation and others.
241 * All rights reserved. This program and the accompanying materials
242 * are made available under the terms of the Common Public License v1.0
243 * which accompanies this distribution, and is available at
244 * http://www.eclipse.org/legal/cpl-v10.html
247 * IBM Corporation - initial API and implementation
248 *******************************************************************************/
251 * Instances of this class are sent as a result of the incremental
252 * loading of image data.
256 * <li>The number of events which will be sent when loading images
257 * is not constant. It varies by image type, and for JPEG images it
258 * varies from image to image.</li>
259 * <li>For image sources which contain multiple images, the
260 * <code>endOfImage</code> flag in the event will be set to true
261 * after each individual image is loaded.</li>
265 * @see ImageLoaderListener
268 public static class ImageLoaderEvent {
271 * if the <code>endOfImage</code> flag is false, then this is a
272 * partially complete copy of the current <code>ImageData</code>,
273 * otherwise this is a completely loaded <code>ImageData</code>
275 public ImageData imageData;
278 * the zero-based count of image data increments -- this is
279 * equivalent to the number of events that have been generated
280 * while loading a particular image
282 public int incrementCount;
285 * If this flag is true, then the current image data has been
286 * completely loaded, otherwise the image data is only partially
287 * loaded, and further ImageLoader events will occur unless an
288 * exception is thrown
290 public boolean endOfImage;
293 * Constructs a new instance of this class given the event source and
294 * the values to store in its fields.
296 * @param source the ImageLoader that was loading when the event occurred
297 * @param imageData the image data for the event
298 * @param incrementCount the image data increment for the event
299 * @param endOfImage the end of image flag for the event
301 public ImageLoaderEvent(ImageLoader source, ImageData imageData, int incrementCount, boolean endOfImage) {
303 this.imageData = imageData;
304 this.incrementCount = incrementCount;
305 this.endOfImage = endOfImage;
309 * Returns a string containing a concise, human-readable
310 * description of the receiver.
312 * @return a string representation of the event
315 public String toString () {
316 return "ImageLoaderEvent {source=" + source + " imageData=" + imageData + " incrementCount=" + incrementCount + " endOfImage=" + endOfImage + "}";
322 /*******************************************************************************
323 * Copyright (c) 2000, 2004 IBM Corporation and others.
324 * All rights reserved. This program and the accompanying materials
325 * are made available under the terms of the Common Public License v1.0
326 * which accompanies this distribution, and is available at
327 * http://www.eclipse.org/legal/cpl-v10.html
330 * IBM Corporation - initial API and implementation
331 *******************************************************************************/
334 * Instances of this class are used to load images from,
335 * and save images to, a file or stream.
337 * Currently supported image formats are:
339 * <li>BMP (Windows Bitmap)</li>
340 * <li>ICO (Windows Icon)</li>
345 * <code>ImageLoaders</code> can be used to:
347 * <li>load/save single images in all formats</li>
348 * <li>load/save multiple images (GIF/ICO)</li>
349 * <li>load/save animated GIF images</li>
350 * <li>load interlaced GIF/PNG images</li>
351 * <li>load progressive JPEG images</li>
355 public static class ImageLoader {
358 * the array of ImageData objects in this ImageLoader.
359 * This array is read in when the load method is called,
360 * and it is written out when the save method is called
362 public ImageData[] data;
365 * the width of the logical screen on which the images
366 * reside, in pixels (this corresponds to the GIF89a
367 * Logical Screen Width value)
369 public int logicalScreenWidth;
372 * the height of the logical screen on which the images
373 * reside, in pixels (this corresponds to the GIF89a
374 * Logical Screen Height value)
376 public int logicalScreenHeight;
379 * the background pixel for the logical screen (this
380 * corresponds to the GIF89a Background Color Index value).
381 * The default is -1 which means 'unspecified background'
384 public int backgroundPixel;
387 * the number of times to repeat the display of a sequence
388 * of animated images (this corresponds to the commonly-used
389 * GIF application extension for "NETSCAPE 2.0 01")
391 public int repeatCount;
394 * the set of ImageLoader event listeners, created on demand
396 Vector imageLoaderListeners;
399 * Construct a new empty ImageLoader.
401 public ImageLoader() {
406 * Resets the fields of the ImageLoader, except for the
407 * <code>imageLoaderListeners</code> field.
411 logicalScreenWidth = 0;
412 logicalScreenHeight = 0;
413 backgroundPixel = -1;
418 * Loads an array of <code>ImageData</code> objects from the
419 * specified input stream. Throws an error if either an error
420 * occurs while loading the images, or if the images are not
421 * of a supported type. Returns the loaded image data array.
423 * @param stream the input stream to load the images from
424 * @return an array of <code>ImageData</code> objects loaded from the specified input stream
426 * @exception IllegalArgumentException <ul>
427 * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
429 * @exception SWTException <ul>
430 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
431 * <li>ERROR_IO - if an input/output error occurs while reading data</li>
432 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
435 public ImageData[] load(InputStream stream) {
436 if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
438 data = FileFormat.load(stream, this);
443 * Loads an array of <code>ImageData</code> objects from the
444 * file with the specified name. Throws an error if either
445 * an error occurs while loading the images, or if the images are
446 * not of a supported type. Returns the loaded image data array.
448 * @param filename the name of the file to load the images from
449 * @return an array of <code>ImageData</code> objects loaded from the specified file
451 * @exception IllegalArgumentException <ul>
452 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
454 * @exception SWTException <ul>
455 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
456 * <li>ERROR_IO - if an IO error occurs while reading data</li>
457 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
460 public ImageData[] load(String filename) {
461 throw new Error("not implemented");
463 if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
464 InputStream stream = null;
466 stream = Compatibility.newFileInputStream(filename);
468 } catch (IOException e) {
469 SWT.error(SWT.ERROR_IO, e);
472 if (stream != null) stream.close();
473 } catch (IOException e) {
482 * Saves the image data in this ImageLoader to the specified stream.
483 * The format parameter can have one of the following values:
485 * <dt><code>IMAGE_BMP</code></dt>
486 * <dd>Windows BMP file format, no compression</dd>
487 * <dt><code>IMAGE_BMP_RLE</code></dt>
488 * <dd>Windows BMP file format, RLE compression if appropriate</dd>
489 * <dt><code>IMAGE_GIF</code></dt>
490 * <dd>GIF file format</dd>
491 * <dt><code>IMAGE_ICO</code></dt>
492 * <dd>Windows ICO file format</dd>
493 * <dt><code>IMAGE_JPEG</code></dt>
494 * <dd>JPEG file format</dd>
495 * <dt><code>IMAGE_PNG</code></dt>
496 * <dd>PNG file format</dd>
499 * @param stream the output stream to write the images to
500 * @param format the format to write the images in
502 * @exception IllegalArgumentException <ul>
503 * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
505 * @exception SWTException <ul>
506 * <li>ERROR_INVALID_IMAGE if the image data contains invalid data</li>
507 * <li>ERROR_IO if an IO error occurs while writing to the stream</li>
510 public void save(OutputStream stream, int format) {
511 if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
512 FileFormat.save(stream, format, this);
516 * Saves the image data in this ImageLoader to a file with the specified name.
517 * The format parameter can have one of the following values:
519 * <dt><code>IMAGE_BMP</code></dt>
520 * <dd>Windows BMP file format, no compression</dd>
521 * <dt><code>IMAGE_BMP_RLE</code></dt>
522 * <dd>Windows BMP file format, RLE compression if appropriate</dd>
523 * <dt><code>IMAGE_GIF</code></dt>
524 * <dd>GIF file format</dd>
525 * <dt><code>IMAGE_ICO</code></dt>
526 * <dd>Windows ICO file format</dd>
527 * <dt><code>IMAGE_JPEG</code></dt>
528 * <dd>JPEG file format</dd>
529 * <dt><code>IMAGE_PNG</code></dt>
530 * <dd>PNG file format</dd>
533 * @param filename the name of the file to write the images to
534 * @param format the format to write the images in
536 * @exception IllegalArgumentException <ul>
537 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
539 * @exception SWTException <ul>
540 * <li>ERROR_INVALID_IMAGE if the image data contains invalid data</li>
541 * <li>ERROR_IO if an IO error occurs while writing to the file</li>
544 public void save(String filename, int format) {
545 throw new Error("not implemented");
547 if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
548 OutputStream stream = null;
550 stream = Compatibility.newFileOutputStream(filename);
551 } catch (IOException e) {
552 SWT.error(SWT.ERROR_IO, e);
554 save(stream, format);
559 * Adds a listener to receive image loader events.
561 * An ImageLoaderListener should be added before invoking
562 * one of the receiver's load methods. The listener's
563 * <code>imageDataLoaded</code> method is called when image
564 * data has been partially loaded, as is supported by interlaced
565 * GIF/PNG or progressive JPEG images.
567 * @param listener the ImageLoaderListener to add
568 * @exception IllegalArgumentException <ul>
569 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
572 * @see ImageLoaderListener
573 * @see ImageLoaderEvent
575 public void addImageLoaderListener(ImageLoaderListener listener) {
576 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
577 if (imageLoaderListeners == null) {
578 imageLoaderListeners = new Vector();
580 imageLoaderListeners.addElement(listener);
584 * Removes a listener that was receiving image loader events.
586 * @param listener the ImageLoaderListener to remove
587 * @exception IllegalArgumentException <ul>
588 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
591 * @see #addImageLoaderListener(ImageLoaderListener)
593 public void removeImageLoaderListener(ImageLoaderListener listener) {
594 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
595 if (imageLoaderListeners == null) return;
596 imageLoaderListeners.removeElement(listener);
600 * Returns <code>true</code> if the receiver has image loader
601 * listeners, and <code>false</code> otherwise.
603 * @return <code>true</code> if there are <code>ImageLoaderListener</code>s, and <code>false</code> otherwise
605 * @see #addImageLoaderListener(ImageLoaderListener)
606 * @see #removeImageLoaderListener(ImageLoaderListener)
608 public boolean hasListeners() {
609 return imageLoaderListeners != null && imageLoaderListeners.size() > 0;
613 * Notifies all image loader listeners that an image loader event
614 * has occurred. Pass the specified event object to each listener.
616 * @param event the <code>ImageLoaderEvent</code> to send to each <code>ImageLoaderListener</code>
618 public void notifyListeners(ImageLoaderEvent event) {
619 if (!hasListeners()) return;
620 int size = imageLoaderListeners.size();
621 for (int i = 0; i < size; i++) {
622 ImageLoaderListener listener = (ImageLoaderListener) imageLoaderListeners.elementAt(i);
623 listener.imageDataLoaded(event);
629 /*******************************************************************************
630 * Copyright (c) 2000, 2004 IBM Corporation and others.
631 * All rights reserved. This program and the accompanying materials
632 * are made available under the terms of the Common Public License v1.0
633 * which accompanies this distribution, and is available at
634 * http://www.eclipse.org/legal/cpl-v10.html
637 * IBM Corporation - initial API and implementation
638 *******************************************************************************/
640 * Instances of this class are descriptions of colors in
641 * terms of the primary additive color model (red, green and
642 * blue). A color may be described in terms of the relative
643 * intensities of these three primary colors. The brightness
644 * of each color is specified by a value in the range 0 to 255,
645 * where 0 indicates no color (blackness) and 255 indicates
648 * The hashCode() method in this class uses the values of the public
649 * fields to compute the hash value. When storing instances of the
650 * class in hashed collections, do not modify these fields after the
651 * object has been inserted.
654 * Application code does <em>not</em> need to explicitly release the
655 * resources managed by each instance when those instances are no longer
656 * required, and thus no <code>dispose()</code> method is provided.
662 public static final class RGB {
665 * the red component of the RGB
670 * the green component of the RGB
675 * the blue component of the RGB
680 * Constructs an instance of this class with the given
681 * red, green and blue values.
683 * @param red the red component of the new instance
684 * @param green the green component of the new instance
685 * @param blue the blue component of the new instance
687 * @exception IllegalArgumentException <ul>
688 * <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
691 public RGB(int red, int green, int blue) {
692 if ((red > 255) || (red < 0) ||
693 (green > 255) || (green < 0) ||
694 (blue > 255) || (blue < 0))
695 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
702 * Compares the argument to the receiver, and returns true
703 * if they represent the <em>same</em> object using a class
704 * specific comparison.
706 * @param object the object to compare with this object
707 * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
711 public boolean equals (Object object) {
712 if (object == this) return true;
713 if (!(object instanceof RGB)) return false;
714 RGB rgb = (RGB)object;
715 return (rgb.red == this.red) && (rgb.green == this.green) && (rgb.blue == this.blue);
719 * Returns an integer hash code for the receiver. Any two
720 * objects which return <code>true</code> when passed to
721 * <code>equals</code> must return the same value for this
724 * @return the receiver's hash
726 * @see #equals(Object)
728 public int hashCode () {
729 return (blue << 16) | (green << 8) | red;
733 * Returns a string containing a concise, human-readable
734 * description of the receiver.
736 * @return a string representation of the <code>RGB</code>
738 public String toString () {
739 return "RGB {" + red + ", " + green + ", " + blue + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
745 /*******************************************************************************
746 * Copyright (c) 2000, 2004 IBM Corporation and others.
747 * All rights reserved. This program and the accompanying materials
748 * are made available under the terms of the Common Public License v1.0
749 * which accompanies this distribution, and is available at
750 * http://www.eclipse.org/legal/cpl-v10.html
753 * IBM Corporation - initial API and implementation
754 *******************************************************************************/
757 * Instances of this class describe the color data used by an image.
759 * Depending on the depth of the image, the PaletteData can take one
760 * of two forms, indicated by the isDirect field:
764 * <em>isDirect is false</em>
767 * If isDirect is <code>false</code>, this palette is an indexed
768 * palette which maps pixel values to RGBs. The actual RGB values
769 * may be retrieved by using the getRGBs() method.
772 * <em>isDirect is true</em>
775 * If isDirect is <code>true</code>, this palette is a direct color
776 * palette. Instead of containing RGB values, it contains red,
777 * green and blue mask and shift information which indicates how
778 * the color components may be extracted from a given pixel.
779 * This means that the RGB value is actually encoded in the pixel value.
781 * In this case, the shift data is the number of bits required to shift
782 * the RGB value to the left in order to align the high bit of the
783 * corresponding mask with the high bit of the first byte. This number
784 * may be negative, so care must be taken when shifting. For example,
785 * with a red mask of 0xFF0000, the red shift would be -16. With a red
786 * mask of 0x1F, the red shift would be 3.
795 public static final class PaletteData {
798 * true if the receiver is a direct palette,
799 * and false otherwise
801 public boolean isDirect;
804 * the RGB values for an indexed palette, where the
805 * indices of the array correspond to pixel values
810 * the red mask for a direct palette
815 * the green mask for a direct palette
817 public int greenMask;
820 * the blue mask for a direct palette
825 * the red shift for a direct palette
830 * the green shift for a direct palette
832 public int greenShift;
835 * the blue shift for a direct palette
837 public int blueShift;
840 * Constructs a new indexed palette given an array of RGB values.
842 * @param colors the array of <code>RGB</code>s for the palette
844 * @exception IllegalArgumentException <ul>
845 * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
848 public PaletteData(RGB[] colors) {
849 if (colors == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
850 this.colors = colors;
851 this.isDirect = false;
855 * Constructs a new direct palette given the red, green and blue masks.
857 * @param redMask the red mask
858 * @param greenMask the green mask
859 * @param blueMask the blue mask
861 public PaletteData(int redMask, int greenMask, int blueMask) {
862 this.redMask = redMask;
863 this.greenMask = greenMask;
864 this.blueMask = blueMask;
865 this.isDirect = true;
866 this.redShift = shiftForMask(redMask);
867 this.greenShift = shiftForMask(greenMask);
868 this.blueShift = shiftForMask(blueMask);
872 * Returns the pixel value corresponding to the given <code>RBG</code>.
874 * @param rgb the RGB to get the pixel value for
875 * @return the pixel value for the given RGB
877 * @exception IllegalArgumentException <ul>
878 * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
879 * <li>ERROR_INVALID_ARGUMENT - if the RGB is not found in the palette</li>
882 public int getPixel(RGB rgb) {
883 if (rgb == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
886 pixel |= (redShift < 0 ? rgb.red << -redShift : rgb.red >>> redShift) & redMask;
887 pixel |= (greenShift < 0 ? rgb.green << -greenShift : rgb.green >>> greenShift) & greenMask;
888 pixel |= (blueShift < 0 ? rgb.blue << -blueShift : rgb.blue >>> blueShift) & blueMask;
891 for (int i = 0; i < colors.length; i++) {
892 if (colors[i].equals(rgb)) return i;
894 /* The RGB did not exist in the palette */
895 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
901 * Returns an <code>RGB</code> corresponding to the given pixel value.
903 * @param pixel the pixel to get the RGB value for
904 * @return the RGB value for the given pixel
906 * @exception IllegalArgumentException <ul>
907 * <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
908 * <li>ERROR_INVALID_ARGUMENT - if the pixel does not exist in the palette</li>
911 public RGB getRGB(int pixel) {
913 int r = pixel & redMask;
914 r = (redShift < 0) ? r >>> -redShift : r << redShift;
915 int g = pixel & greenMask;
916 g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
917 int b = pixel & blueMask;
918 b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
919 return new RGB(r, g, b);
921 if (pixel < 0 || pixel >= colors.length) {
922 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
924 return colors[pixel];
929 * Returns all the RGB values in the receiver if it is an
930 * indexed palette, or null if it is a direct palette.
932 * @return the <code>RGB</code>s for the receiver or null
934 public RGB[] getRGBs() {
939 * Computes the shift value for a given mask.
941 * @param mask the mask to compute the shift for
942 * @return the shift amount
946 int shiftForMask(int mask) {
947 for (int i = 31; i >= 0; i--) {
948 if (((mask >> i) & 0x1) != 0) return 7 - i;
956 /*******************************************************************************
957 * Copyright (c) 2000, 2004 IBM Corporation and others.
958 * All rights reserved. This program and the accompanying materials
959 * are made available under the terms of the Common Public License v1.0
960 * which accompanies this distribution, and is available at
961 * http://www.eclipse.org/legal/cpl-v10.html
964 * IBM Corporation - initial API and implementation
965 *******************************************************************************/
968 * Instances of this class are device-independent descriptions
969 * of images. They are typically used as an intermediate format
970 * between loading from or writing to streams and creating an
971 * <code>Image</code>.
973 * Note that the public fields <code>x</code>, <code>y</code>,
974 * <code>disposalMethod</code> and <code>delayTime</code> are
975 * typically only used when the image is in a set of images used
983 public static final class ImageData {
986 * The width of the image, in pixels.
991 * The height of the image, in pixels.
996 * The color depth of the image, in bits per pixel.
998 * Note that a depth of 8 or less does not necessarily
999 * mean that the image is palette indexed, or
1000 * conversely that a depth greater than 8 means that
1001 * the image is direct color. Check the associated
1002 * PaletteData's isDirect field for such determinations.
1007 * The scanline padding.
1009 * If one scanline of the image is not a multiple of
1010 * this number, it will be padded with zeros until it is.
1013 public int scanlinePad;
1016 * The number of bytes per scanline.
1018 * This is a multiple of the scanline padding.
1021 public int bytesPerLine;
1024 * The pixel data of the image.
1026 * Note that for 16 bit depth images the pixel data is stored
1027 * in least significant byte order; however, for 24bit and
1028 * 32bit depth images the pixel data is stored in most
1029 * significant byte order.
1035 * The color table for the image.
1037 public PaletteData palette;
1040 * The transparent pixel.
1042 * Pixels with this value are transparent.
1044 * The default is -1 which means 'no transparent pixel'.
1047 public int transparentPixel;
1050 * An icon-specific field containing the data from the icon mask.
1052 * This is a 1 bit bitmap stored with the most significant
1053 * bit first. The number of bytes per scanline is
1054 * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'.
1056 * The default is null which means 'no transparency mask'.
1059 public byte[] maskData;
1062 * An icon-specific field containing the scanline pad of the mask.
1064 * If one scanline of the transparency mask is not a
1065 * multiple of this number, it will be padded with zeros until
1072 * The alpha data of the image.
1074 * Every pixel can have an <em>alpha blending</em> value that
1075 * varies from 0, meaning fully transparent, to 255 meaning
1076 * fully opaque. The number of bytes per scanline is
1080 public byte[] alphaData;
1083 * The global alpha value to be used for every pixel.
1085 * If this value is set, the <code>alphaData</code> field
1086 * is ignored and when the image is rendered each pixel
1087 * will be blended with the background an amount
1088 * proportional to this value.
1090 * The default is -1 which means 'no global alpha value'
1096 * The type of file from which the image was read.
1098 * It is expressed as one of the following values:
1100 * <dt><code>IMAGE_BMP</code></dt>
1101 * <dd>Windows BMP file format, no compression</dd>
1102 * <dt><code>IMAGE_BMP_RLE</code></dt>
1103 * <dd>Windows BMP file format, RLE compression if appropriate</dd>
1104 * <dt><code>IMAGE_GIF</code></dt>
1105 * <dd>GIF file format</dd>
1106 * <dt><code>IMAGE_ICO</code></dt>
1107 * <dd>Windows ICO file format</dd>
1108 * <dt><code>IMAGE_JPEG</code></dt>
1109 * <dd>JPEG file format</dd>
1110 * <dt><code>IMAGE_PNG</code></dt>
1111 * <dd>PNG file format</dd>
1117 * The x coordinate of the top left corner of the image
1118 * within the logical screen (this field corresponds to
1119 * the GIF89a Image Left Position value).
1124 * The y coordinate of the top left corner of the image
1125 * within the logical screen (this field corresponds to
1126 * the GIF89a Image Top Position value).
1131 * A description of how to dispose of the current image
1132 * before displaying the next.
1134 * It is expressed as one of the following values:
1136 * <dt><code>DM_UNSPECIFIED</code></dt>
1137 * <dd>disposal method not specified</dd>
1138 * <dt><code>DM_FILL_NONE</code></dt>
1139 * <dd>do nothing - leave the image in place</dd>
1140 * <dt><code>DM_FILL_BACKGROUND</code></dt>
1141 * <dd>fill with the background color</dd>
1142 * <dt><code>DM_FILL_PREVIOUS</code></dt>
1143 * <dd>restore the previous picture</dd>
1145 * (this field corresponds to the GIF89a Disposal Method value)
1147 public int disposalMethod;
1150 * The time to delay before displaying the next image
1151 * in an animation (this field corresponds to the GIF89a
1152 * Delay Time value).
1154 public int delayTime;
1157 * Arbitrary channel width data to 8-bit conversion table.
1159 static final byte[][] ANY_TO_EIGHT = new byte[9][];
1161 for (int b = 0; b < 9; ++b) {
1162 byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
1163 if (b == 0) continue;
1165 for (int bit = 0x10000; (bit >>= b) != 0;) inc |= bit;
1166 for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = (byte)(v >> 8);
1169 static final byte[] ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8];
1172 * Scaled 8x8 Bayer dither matrix.
1174 static final int[][] DITHER_MATRIX = {
1175 { 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000, 0x740000, 0xd40000, 0x540000 },
1176 { 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000, 0xb40000, 0x140000, 0x940000 },
1177 { 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000, 0x440000, 0xe40000, 0x640000 },
1178 { 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000, 0x840000, 0x240000, 0xa40000 },
1179 { 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000, 0x780000, 0xd80000, 0x580000 },
1180 { 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000, 0xb80000, 0x180000, 0x980000 },
1181 { 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000, 0x480000, 0xe80000, 0x680000 },
1182 { 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000, 0x880000, 0x280000, 0xa80000 }
1186 * Constructs a new, empty ImageData with the given width, height,
1187 * depth and palette. The data will be initialized to an (all zero)
1188 * array of the appropriate size.
1190 * @param width the width of the image
1191 * @param height the height of the image
1192 * @param depth the depth of the image
1193 * @param palette the palette of the image (must not be null)
1195 * @exception IllegalArgumentException <ul>
1196 * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
1197 * one of 1, 2, 4, 8, 16, 24 or 32</li>
1198 * <li>ERROR_NULL_ARGUMENT - if the palette is null</li>
1201 public ImageData(int width, int height, int depth, PaletteData palette) {
1202 this(width, height, depth, palette,
1204 null, -1, -1, SWT.IMAGE_UNDEFINED,
1209 * Constructs a new, empty ImageData with the given width, height,
1210 * depth, palette, scanlinePad and data.
1212 * @param width the width of the image
1213 * @param height the height of the image
1214 * @param depth the depth of the image
1215 * @param palette the palette of the image
1216 * @param scanlinePad the padding of each line, in bytes
1217 * @param data the data of the image
1219 * @exception IllegalArgumentException <ul>
1220 * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
1221 * one of 1, 2, 4, 8, 16, 24 or 32</li>
1222 * <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li>
1223 * <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li>
1226 public ImageData(int width, int height, int depth, PaletteData palette, int scanlinePad, byte[] data) {
1227 this(width, height, depth, palette,
1228 scanlinePad, checkData(data), 0, null,
1229 null, -1, -1, SWT.IMAGE_UNDEFINED,
1234 * Constructs an <code>ImageData</code> loaded from the specified
1235 * input stream. Throws an error if an error occurs while loading
1236 * the image, or if the image has an unsupported type. Application
1237 * code is still responsible for closing the input stream.
1239 * This constructor is provided for convenience when loading a single
1240 * image only. If the stream contains multiple images, only the first
1241 * one will be loaded. To load multiple images, use
1242 * <code>ImageLoader.load()</code>.
1245 * @param stream the input stream to load the image from (must not be null)
1247 * @exception IllegalArgumentException <ul>
1248 * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
1250 * @exception SWTException <ul>
1251 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1252 * <li>ERROR_IO - if an IO error occurs while reading data</li>
1255 * @see ImageLoader#load(InputStream)
1257 public ImageData(InputStream stream) {
1258 ImageData[] data = new ImageLoader().load(stream);
1259 if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
1260 ImageData i = data[0];
1282 * Constructs an <code>ImageData</code> loaded from a file with the
1283 * specified name. Throws an error if an error occurs loading the
1284 * image, or if the image has an unsupported type.
1286 * This constructor is provided for convenience when loading a single
1287 * image only. If the file contains multiple images, only the first
1288 * one will be loaded. To load multiple images, use
1289 * <code>ImageLoader.load()</code>.
1292 * @param filename the name of the file to load the image from (must not be null)
1294 * @exception IllegalArgumentException <ul>
1295 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
1297 * @exception SWTException <ul>
1298 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
1299 * <li>ERROR_IO if an IO error occurs while reading data</li>
1300 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
1303 public ImageData(String filename) {
1304 ImageData[] data = new ImageLoader().load(filename);
1305 if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
1306 ImageData i = data[0];
1328 * Prevents uninitialized instances from being created outside the package.
1334 * Constructs an image data by giving values for all non-computable fields.
1336 * This method is for internal use, and is not described further.
1340 int width, int height, int depth, PaletteData palette,
1341 int scanlinePad, byte[] data, int maskPad, byte[] maskData,
1342 byte[] alphaData, int alpha, int transparentPixel, int type,
1343 int x, int y, int disposalMethod, int delayTime)
1346 if (palette == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1347 if (!(depth == 1 || depth == 2 || depth == 4 || depth == 8
1348 || depth == 16 || depth == 24 || depth == 32)) {
1349 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1351 if (width <= 0 || height <= 0) {
1352 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1354 if (scanlinePad == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
1356 int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
1357 / scanlinePad * scanlinePad;
1364 data != null ? data : new byte[bytesPerLine * height],
1379 * Initializes all fields in the receiver. This method must be called
1380 * by all public constructors to ensure that all fields are initialized
1381 * for a new ImageData object. If a new field is added to the class,
1382 * then it must be added to this method.
1384 * This method is for internal use, and is not described further.
1387 void setAllFields(int width, int height, int depth, int scanlinePad,
1388 int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel,
1389 byte[] maskData, int maskPad, byte[] alphaData, int alpha,
1390 int type, int x, int y, int disposalMethod, int delayTime) {
1393 this.height = height;
1395 this.scanlinePad = scanlinePad;
1396 this.bytesPerLine = bytesPerLine;
1398 this.palette = palette;
1399 this.transparentPixel = transparentPixel;
1400 this.maskData = maskData;
1401 this.maskPad = maskPad;
1402 this.alphaData = alphaData;
1407 this.disposalMethod = disposalMethod;
1408 this.delayTime = delayTime;
1412 * Invokes internal SWT functionality to create a new instance of
1415 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
1416 * API for <code>ImageData</code>. It is marked public only so that it
1417 * can be shared within the packages provided by SWT. It is subject
1418 * to change without notice, and should never be called from
1422 * This method is for internal use, and is not described further.
1425 public static ImageData internal_new(
1426 int width, int height, int depth, PaletteData palette,
1427 int scanlinePad, byte[] data, int maskPad, byte[] maskData,
1428 byte[] alphaData, int alpha, int transparentPixel, int type,
1429 int x, int y, int disposalMethod, int delayTime)
1431 return new ImageData(
1432 width, height, depth, palette, scanlinePad, data, maskPad, maskData,
1433 alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime);
1436 ImageData colorMaskImage(int pixel) {
1437 ImageData mask = new ImageData(width, height, 1, bwPalette(),
1438 2, null, 0, null, null, -1, -1, SWT.IMAGE_UNDEFINED,
1440 int[] row = new int[width];
1441 for (int y = 0; y < height; y++) {
1442 getPixels(0, y, width, row, 0);
1443 for (int i = 0; i < width; i++) {
1444 if (pixel != -1 && row[i] == pixel) {
1450 mask.setPixels(0, y, width, row, 0);
1455 static byte[] checkData(byte [] data) {
1456 if (data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1461 * Returns a new instance of the same class as the receiver,
1462 * whose slots have been filled in with <em>copies</em> of
1463 * the values in the slots of the receiver. That is, the
1464 * returned object is a <em>deep copy</em> of the receiver.
1466 * @return a copy of the receiver.
1468 public Object clone() {
1469 byte[] cloneData = new byte[data.length];
1470 System.arraycopy(data, 0, cloneData, 0, data.length);
1471 byte[] cloneMaskData = null;
1472 if (maskData != null) {
1473 cloneMaskData = new byte[maskData.length];
1474 System.arraycopy(maskData, 0, cloneMaskData, 0, maskData.length);
1476 byte[] cloneAlphaData = null;
1477 if (alphaData != null) {
1478 cloneAlphaData = new byte[alphaData.length];
1479 System.arraycopy(alphaData, 0, cloneAlphaData, 0, alphaData.length);
1481 return new ImageData(
1501 * Returns the alpha value at offset <code>x</code> in
1502 * scanline <code>y</code> in the receiver's alpha data.
1504 * @param x the x coodinate of the pixel to get the alpha value of
1505 * @param y the y coordinate of the pixel to get the alpha value of
1506 * @return the alpha value at the given coordinates
1508 * @exception IllegalArgumentException <ul>
1509 * <li>ERROR_INVALID_ARGUMENT - if either argument is out of range</li>
1512 public int getAlpha(int x, int y) {
1513 if (x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1515 if (alphaData == null) return 255;
1516 return alphaData[y * width + x] & 0xFF;
1520 * Returns <code>getWidth</code> alpha values starting at offset
1521 * <code>x</code> in scanline <code>y</code> in the receiver's alpha
1522 * data starting at <code>startIndex</code>.
1524 * @param x the x position of the pixel to begin getting alpha values
1525 * @param y the y position of the pixel to begin getting alpha values
1526 * @param getWidth the width of the data to get
1527 * @param alphas the buffer in which to put the alpha values
1528 * @param startIndex the offset into the image to begin getting alpha values
1530 * @exception IndexOutOfBoundsException if getWidth is too large
1531 * @exception IllegalArgumentException <ul>
1532 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1533 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1534 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1537 public void getAlphas(int x, int y, int getWidth, byte[] alphas, int startIndex) {
1538 if (alphas == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1539 if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1540 if (getWidth == 0) return;
1542 if (alphaData == null) {
1543 int endIndex = startIndex + getWidth;
1544 for (int i = startIndex; i < endIndex; i++) {
1545 alphas[i] = (byte)255;
1549 // may throw an IndexOutOfBoundsException
1550 System.arraycopy(alphaData, y * width + x, alphas, startIndex, getWidth);
1554 * Returns the pixel value at offset <code>x</code> in
1555 * scanline <code>y</code> in the receiver's data.
1557 * @param x the x position of the pixel to get
1558 * @param y the y position of the pixel to get
1559 * @return the pixel at the given coordinates
1561 * @exception IllegalArgumentException <ul>
1562 * <li>ERROR_INVALID_ARGUMENT - if either argument is out of bounds</li>
1564 * @exception SWTException <ul>
1565 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
1568 public int getPixel(int x, int y) {
1569 if (x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1574 index = (y * bytesPerLine) + (x >> 3);
1575 theByte = data[index] & 0xFF;
1576 mask = 1 << (7 - (x & 0x7));
1577 if ((theByte & mask) == 0) {
1584 index = (y * bytesPerLine) + (x >> 2);
1585 theByte = data[index] & 0xFF;
1586 int offset = 3 - (x % 4);
1587 mask = 3 << (offset * 2);
1588 return (theByte & mask) >> (offset * 2);
1591 index = (y * bytesPerLine) + (x >> 1);
1592 theByte = data[index] & 0xFF;
1593 if ((x & 0x1) == 0) {
1594 return theByte >> 4;
1596 return theByte & 0x0F;
1600 index = (y * bytesPerLine) + x ;
1601 return data[index] & 0xFF;
1604 index = (y * bytesPerLine) + (x * 2);
1605 return ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
1608 index = (y * bytesPerLine) + (x * 3);
1609 return ((data[index] & 0xFF) << 16) + ((data[index+1] & 0xFF) << 8) +
1610 (data[index+2] & 0xFF);
1613 index = (y * bytesPerLine) + (x * 4);
1614 return ((data[index] & 0xFF) << 24) + ((data[index+1] & 0xFF) << 16) +
1615 ((data[index+2] & 0xFF) << 8) + (data[index+3] & 0xFF);
1617 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
1622 * Returns <code>getWidth</code> pixel values starting at offset
1623 * <code>x</code> in scanline <code>y</code> in the receiver's
1624 * data starting at <code>startIndex</code>.
1626 * @param x the x position of the first pixel to get
1627 * @param y the y position of the first pixel to get
1628 * @param getWidth the width of the data to get
1629 * @param pixels the buffer in which to put the pixels
1630 * @param startIndex the offset into the byte array to begin storing pixels
1632 * @exception IndexOutOfBoundsException if getWidth is too large
1633 * @exception IllegalArgumentException <ul>
1634 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1635 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1636 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1638 * @exception SWTException <ul>
1639 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
1640 * (For higher depths, use the int[] version of this method.)</li>
1643 public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) {
1644 if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1645 if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1646 if (getWidth == 0) return;
1652 int srcX = x, srcY = y;
1654 index = (y * bytesPerLine) + (x >> 3);
1655 theByte = data[index] & 0xFF;
1657 mask = 1 << (7 - (srcX & 0x7));
1658 if ((theByte & mask) == 0) {
1666 if (srcX >= width) {
1668 index = srcY * bytesPerLine;
1669 if (n > 0) theByte = data[index] & 0xFF;
1674 if (n > 0) theByte = data[index] & 0xFF;
1681 index = (y * bytesPerLine) + (x >> 2);
1682 theByte = data[index] & 0xFF;
1685 offset = 3 - (srcX % 4);
1686 mask = 3 << (offset * 2);
1687 pixels[i] = (byte)((theByte & mask) >> (offset * 2));
1691 if (srcX >= width) {
1693 index = srcY * bytesPerLine;
1694 if (n > 0) theByte = data[index] & 0xFF;
1699 theByte = data[index] & 0xFF;
1706 index = (y * bytesPerLine) + (x >> 1);
1707 if ((x & 0x1) == 1) {
1708 theByte = data[index] & 0xFF;
1709 pixels[i] = (byte)(theByte & 0x0F);
1713 if (srcX >= width) {
1715 index = srcY * bytesPerLine;
1722 theByte = data[index] & 0xFF;
1723 pixels[i] = (byte)(theByte >> 4);
1727 if (srcX >= width) {
1729 index = srcY * bytesPerLine;
1732 pixels[i] = (byte)(theByte & 0x0F);
1736 if (srcX >= width) {
1738 index = srcY * bytesPerLine;
1746 theByte = data[index] & 0xFF;
1747 pixels[i] = (byte)(theByte >> 4);
1752 index = (y * bytesPerLine) + x;
1753 for (int j = 0; j < getWidth; j++) {
1754 pixels[i] = data[index];
1757 if (srcX >= width) {
1759 index = srcY * bytesPerLine;
1767 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
1771 * Returns <code>getWidth</code> pixel values starting at offset
1772 * <code>x</code> in scanline <code>y</code> in the receiver's
1773 * data starting at <code>startIndex</code>.
1775 * @param x the x position of the first pixel to get
1776 * @param y the y position of the first pixel to get
1777 * @param getWidth the width of the data to get
1778 * @param pixels the buffer in which to put the pixels
1779 * @param startIndex the offset into the buffer to begin storing pixels
1781 * @exception IndexOutOfBoundsException if getWidth is too large
1782 * @exception IllegalArgumentException <ul>
1783 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1784 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1785 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
1787 * @exception SWTException <ul>
1788 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
1791 public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) {
1792 if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
1793 if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
1794 if (getWidth == 0) return;
1800 int srcX = x, srcY = y;
1802 index = (y * bytesPerLine) + (x >> 3);
1803 theByte = data[index] & 0xFF;
1805 mask = 1 << (7 - (srcX & 0x7));
1806 if ((theByte & mask) == 0) {
1814 if (srcX >= width) {
1816 index = srcY * bytesPerLine;
1817 if (n > 0) theByte = data[index] & 0xFF;
1822 if (n > 0) theByte = data[index] & 0xFF;
1829 index = (y * bytesPerLine) + (x >> 2);
1830 theByte = data[index] & 0xFF;
1833 offset = 3 - (srcX % 4);
1834 mask = 3 << (offset * 2);
1835 pixels[i] = (byte)((theByte & mask) >> (offset * 2));
1839 if (srcX >= width) {
1841 index = srcY * bytesPerLine;
1842 if (n > 0) theByte = data[index] & 0xFF;
1847 theByte = data[index] & 0xFF;
1854 index = (y * bytesPerLine) + (x >> 1);
1855 if ((x & 0x1) == 1) {
1856 theByte = data[index] & 0xFF;
1857 pixels[i] = theByte & 0x0F;
1861 if (srcX >= width) {
1863 index = srcY * bytesPerLine;
1870 theByte = data[index] & 0xFF;
1871 pixels[i] = theByte >> 4;
1875 if (srcX >= width) {
1877 index = srcY * bytesPerLine;
1880 pixels[i] = theByte & 0x0F;
1884 if (srcX >= width) {
1886 index = srcY * bytesPerLine;
1894 theByte = data[index] & 0xFF;
1895 pixels[i] = theByte >> 4;
1900 index = (y * bytesPerLine) + x;
1901 for (int j = 0; j < getWidth; j++) {
1902 pixels[i] = data[index] & 0xFF;
1905 if (srcX >= width) {
1907 index = srcY * bytesPerLine;
1916 index = (y * bytesPerLine) + (x * 2);
1917 for (int j = 0; j < getWidth; j++) {
1918 pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
1921 if (srcX >= width) {
1923 index = srcY * bytesPerLine;
1932 index = (y * bytesPerLine) + (x * 3);
1933 for (int j = 0; j < getWidth; j++) {
1934 pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8)
1935 | (data[index+2] & 0xFF);
1938 if (srcX >= width) {
1940 index = srcY * bytesPerLine;
1949 index = (y * bytesPerLine) + (x * 4);
1951 for (int j = 0; j < getWidth; j++) {
1952 pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16)
1953 | ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF);
1956 if (srcX >= width) {
1958 index = srcY * bytesPerLine;
1966 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
1970 * Returns an array of <code>RGB</code>s which comprise the
1971 * indexed color table of the receiver, or null if the receiver
1972 * has a direct color model.
1974 * @return the RGB values for the image or null if direct color
1976 * @see PaletteData#getRGBs()
1978 public RGB[] getRGBs() {
1979 return palette.getRGBs();
1983 * Returns an <code>ImageData</code> which specifies the
1984 * transparency mask information for the receiver, or null if the
1985 * receiver has no transparency and is not an icon.
1987 * @return the transparency mask or null if none exists
1989 public ImageData getTransparencyMask() {
1990 if (getTransparencyType() == SWT.TRANSPARENCY_MASK) {
1991 return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
1993 return colorMaskImage(transparentPixel);
1998 * Returns the image transparency type.
2000 * @return the receiver's transparency type
2002 public int getTransparencyType() {
2003 if (maskData != null) return SWT.TRANSPARENCY_MASK;
2004 if (transparentPixel != -1) return SWT.TRANSPARENCY_PIXEL;
2005 if (alphaData != null) return SWT.TRANSPARENCY_ALPHA;
2006 return SWT.TRANSPARENCY_NONE;
2010 * Returns the byte order of the receiver.
2012 * @return MSB_FIRST or LSB_FIRST
2014 int getByteOrder() {
2015 return depth != 16 ? MSB_FIRST : LSB_FIRST;
2019 * Returns a copy of the receiver which has been stretched or
2020 * shrunk to the specified size. If either the width or height
2021 * is negative, the resulting image will be inverted in the
2024 * @param width the width of the new ImageData
2025 * @param height the height of the new ImageData
2026 * @return a scaled copy of the image
2028 public ImageData scaledTo(int width, int height) {
2029 /* Create a destination image with no data */
2030 final boolean flipX = (width < 0);
2031 if (flipX) width = - width;
2032 final boolean flipY = (height < 0);
2033 if (flipY) height = - height;
2035 ImageData dest = new ImageData(
2036 width, height, depth, palette,
2037 scanlinePad, null, 0, null,
2038 null, -1, transparentPixel, type,
2039 x, y, disposalMethod, delayTime);
2041 /* Scale the image contents */
2042 if (palette.isDirect) blit(BLIT_SRC,
2043 this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, 0, 0, 0,
2044 ALPHA_OPAQUE, null, 0, 0, 0,
2045 dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, 0, 0, 0,
2048 this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, null, null, null,
2049 ALPHA_OPAQUE, null, 0, 0, 0,
2050 dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, null, null, null,
2053 /* Scale the image mask or alpha */
2054 if (maskData != null) {
2055 dest.maskPad = this.maskPad;
2056 int destBpl = (dest.width + 7) / 8;
2057 destBpl = (destBpl + (dest.maskPad - 1)) / dest.maskPad * dest.maskPad;
2058 dest.maskData = new byte[destBpl * dest.height];
2059 int srcBpl = (this.width + 7) / 8;
2060 srcBpl = (srcBpl + (this.maskPad - 1)) / this.maskPad * this.maskPad;
2062 this.maskData, 1, srcBpl, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
2063 ALPHA_OPAQUE, null, 0, 0, 0,
2064 dest.maskData, 1, destBpl, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
2066 } else if (alpha != -1) {
2067 dest.alpha = this.alpha;
2068 } else if (alphaData != null) {
2069 dest.alphaData = new byte[dest.width * dest.height];
2071 this.alphaData, 8, this.width, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
2072 ALPHA_OPAQUE, null, 0, 0, 0,
2073 dest.alphaData, 8, dest.width, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
2080 * Sets the alpha value at offset <code>x</code> in
2081 * scanline <code>y</code> in the receiver's alpha data.
2083 * @param x the x coordinate of the alpha value to set
2084 * @param y the y coordinate of the alpha value to set
2085 * @param alpha the value to set the alpha to
2087 * @exception IllegalArgumentException <ul>
2088 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2091 public void setAlpha(int x, int y, int alpha) {
2092 if (x >= width || y >= height || x < 0 || y < 0 || alpha < 0 || alpha > 255)
2093 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2095 if (alphaData == null) alphaData = new byte[width * height];
2096 alphaData[y * width + x] = (byte)alpha;
2100 * Sets the alpha values starting at offset <code>x</code> in
2101 * scanline <code>y</code> in the receiver's alpha data to the
2102 * values from the array <code>alphas</code> starting at
2103 * <code>startIndex</code>.
2105 * @param x the x coordinate of the pixel to being setting the alpha values
2106 * @param y the y coordinate of the pixel to being setting the alpha values
2107 * @param putWidth the width of the alpha values to set
2108 * @param alphas the alpha values to set
2109 * @param startIndex the index at which to begin setting
2111 * @exception IndexOutOfBoundsException if putWidth is too large
2112 * @exception IllegalArgumentException <ul>
2113 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2114 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2115 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2118 public void setAlphas(int x, int y, int putWidth, byte[] alphas, int startIndex) {
2119 if (alphas == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2120 if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2121 if (putWidth == 0) return;
2123 if (alphaData == null) alphaData = new byte[width * height];
2124 // may throw an IndexOutOfBoundsException
2125 System.arraycopy(alphas, startIndex, alphaData, y * width + x, putWidth);
2129 * Sets the pixel value at offset <code>x</code> in
2130 * scanline <code>y</code> in the receiver's data.
2132 * @param x the x coordinate of the pixel to set
2133 * @param y the y coordinate of the pixel to set
2134 * @param pixelValue the value to set the pixel to
2136 * @exception IllegalArgumentException <ul>
2137 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2139 * @exception SWTException <ul>
2140 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
2143 public void setPixel(int x, int y, int pixelValue) {
2144 if (x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2149 index = (y * bytesPerLine) + (x >> 3);
2150 theByte = data[index];
2151 mask = 1 << (7 - (x & 0x7));
2152 if ((pixelValue & 0x1) == 1) {
2153 data[index] = (byte)(theByte | mask);
2155 data[index] = (byte)(theByte & (mask ^ -1));
2160 index = (y * bytesPerLine) + (x >> 2);
2161 theByte = data[index];
2162 int offset = 3 - (x % 4);
2163 mask = 0xFF ^ (3 << (offset * 2));
2164 data[index] = (byte)((data[index] & mask) | (pixelValue << (offset * 2)));
2168 index = (y * bytesPerLine) + (x >> 1);
2169 if ((x & 0x1) == 0) {
2170 data[index] = (byte)((data[index] & 0x0F) | ((pixelValue & 0x0F) << 4));
2172 data[index] = (byte)((data[index] & 0xF0) | (pixelValue & 0x0F));
2177 index = (y * bytesPerLine) + x ;
2178 data[index] = (byte)(pixelValue & 0xFF);
2182 index = (y * bytesPerLine) + (x * 2);
2183 data[index + 1] = (byte)((pixelValue >> 8) & 0xFF);
2184 data[index] = (byte)(pixelValue & 0xFF);
2188 index = (y * bytesPerLine) + (x * 3);
2189 data[index] = (byte)((pixelValue >> 16) & 0xFF);
2190 data[index + 1] = (byte)((pixelValue >> 8) & 0xFF);
2191 data[index + 2] = (byte)(pixelValue & 0xFF);
2195 index = (y * bytesPerLine) + (x * 4);
2196 data[index] = (byte)((pixelValue >> 24) & 0xFF);
2197 data[index + 1] = (byte)((pixelValue >> 16) & 0xFF);
2198 data[index + 2] = (byte)((pixelValue >> 8) & 0xFF);
2199 data[index + 3] = (byte)(pixelValue & 0xFF);
2202 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2206 * Sets the pixel values starting at offset <code>x</code> in
2207 * scanline <code>y</code> in the receiver's data to the
2208 * values from the array <code>pixels</code> starting at
2209 * <code>startIndex</code>.
2211 * @param x the x position of the pixel to set
2212 * @param y the y position of the pixel to set
2213 * @param putWidth the width of the pixels to set
2214 * @param pixels the pixels to set
2215 * @param startIndex the index at which to begin setting
2217 * @exception IndexOutOfBoundsException if putWidth is too large
2218 * @exception IllegalArgumentException <ul>
2219 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2220 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2221 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2223 * @exception SWTException <ul>
2224 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
2225 * (For higher depths, use the int[] version of this method.)</li>
2228 public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) {
2229 if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2230 if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2231 if (putWidth == 0) return;
2237 int srcX = x, srcY = y;
2239 index = (y * bytesPerLine) + (x >> 3);
2241 mask = 1 << (7 - (srcX & 0x7));
2242 if ((pixels[i] & 0x1) == 1) {
2243 data[index] = (byte)((data[index] & 0xFF) | mask);
2245 data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
2250 if (srcX >= width) {
2252 index = srcY * bytesPerLine;
2263 byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
2264 index = (y * bytesPerLine) + (x >> 2);
2265 int offset = 3 - (x % 4);
2267 theByte = pixels[i] & 0x3;
2268 data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
2272 if (srcX >= width) {
2274 index = srcY * bytesPerLine;
2289 index = (y * bytesPerLine) + (x >> 1);
2290 boolean high = (x & 0x1) == 0;
2292 theByte = pixels[i] & 0x0F;
2294 data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
2296 data[index] = (byte)((data[index] & 0xF0) | theByte);
2301 if (srcX >= width) {
2303 index = srcY * bytesPerLine;
2314 index = (y * bytesPerLine) + x;
2315 for (int j = 0; j < putWidth; j++) {
2316 data[index] = (byte)(pixels[i] & 0xFF);
2319 if (srcX >= width) {
2321 index = srcY * bytesPerLine;
2329 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2333 * Sets the pixel values starting at offset <code>x</code> in
2334 * scanline <code>y</code> in the receiver's data to the
2335 * values from the array <code>pixels</code> starting at
2336 * <code>startIndex</code>.
2338 * @param x the x position of the pixel to set
2339 * @param y the y position of the pixel to set
2340 * @param putWidth the width of the pixels to set
2341 * @param pixels the pixels to set
2342 * @param startIndex the index at which to begin setting
2344 * @exception IndexOutOfBoundsException if putWidth is too large
2345 * @exception IllegalArgumentException <ul>
2346 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
2347 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
2348 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
2350 * @exception SWTException <ul>
2351 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
2354 public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) {
2355 if (pixels == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
2356 if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2357 if (putWidth == 0) return;
2364 int srcX = x, srcY = y;
2366 index = (y * bytesPerLine) + (x >> 3);
2368 mask = 1 << (7 - (srcX & 0x7));
2369 if ((pixels[i] & 0x1) == 1) {
2370 data[index] = (byte)((data[index] & 0xFF) | mask);
2372 data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
2377 if (srcX >= width) {
2379 index = srcY * bytesPerLine;
2390 byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
2391 index = (y * bytesPerLine) + (x >> 2);
2392 int offset = 3 - (x % 4);
2394 theByte = pixels[i] & 0x3;
2395 data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
2399 if (srcX >= width) {
2401 index = srcY * bytesPerLine;
2416 index = (y * bytesPerLine) + (x >> 1);
2417 boolean high = (x & 0x1) == 0;
2419 theByte = pixels[i] & 0x0F;
2421 data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
2423 data[index] = (byte)((data[index] & 0xF0) | theByte);
2428 if (srcX >= width) {
2430 index = srcY * bytesPerLine;
2441 index = (y * bytesPerLine) + x;
2442 for (int j = 0; j < putWidth; j++) {
2443 data[index] = (byte)(pixels[i] & 0xFF);
2446 if (srcX >= width) {
2448 index = srcY * bytesPerLine;
2458 index = (y * bytesPerLine) + (x * 2);
2459 for (int j = 0; j < putWidth; j++) {
2461 data[index] = (byte)(pixel & 0xFF);
2462 data[index + 1] = (byte)((pixel >> 8) & 0xFF);
2465 if (srcX >= width) {
2467 index = srcY * bytesPerLine;
2476 index = (y * bytesPerLine) + (x * 3);
2477 for (int j = 0; j < putWidth; j++) {
2479 data[index] = (byte)((pixel >> 16) & 0xFF);
2480 data[index + 1] = (byte)((pixel >> 8) & 0xFF);
2481 data[index + 2] = (byte)(pixel & 0xFF);
2484 if (srcX >= width) {
2486 index = srcY * bytesPerLine;
2495 index = (y * bytesPerLine) + (x * 4);
2496 for (int j = 0; j < putWidth; j++) {
2498 data[index] = (byte)((pixel >> 24) & 0xFF);
2499 data[index + 1] = (byte)((pixel >> 16) & 0xFF);
2500 data[index + 2] = (byte)((pixel >> 8) & 0xFF);
2501 data[index + 3] = (byte)(pixel & 0xFF);
2504 if (srcX >= width) {
2506 index = srcY * bytesPerLine;
2514 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
2518 * Returns a palette with 2 colors: black & white.
2520 static PaletteData bwPalette() {
2521 return new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)});
2525 * Gets the offset of the most significant bit for
2528 static int getMSBOffset(int mask) {
2529 for (int i = 31; i >= 0; i--) {
2530 if (((mask >> i) & 0x1) != 0) return i + 1;
2536 * Finds the closest match.
2538 static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) {
2540 int rshift = 32 - getMSBOffset(redMask);
2541 int gshift = 32 - getMSBOffset(greenMask);
2542 int bshift = 32 - getMSBOffset(blueMask);
2543 return (((red << 24) >>> rshift) & redMask) |
2544 (((green << 24) >>> gshift) & greenMask) |
2545 (((blue << 24) >>> bshift) & blueMask);
2548 int minDistance = 0x7fffffff;
2549 int nearestPixel = 0;
2550 int n = reds.length;
2551 for (int j = 0; j < n; j++) {
2552 r = (reds[j] & 0xFF) - (red & 0xFF);
2553 g = (greens[j] & 0xFF) - (green & 0xFF);
2554 b = (blues[j] & 0xFF) - (blue & 0xFF);
2555 int distance = r*r + g*g + b*b;
2556 if (distance < minDistance) {
2558 if (distance == 0) break;
2559 minDistance = distance;
2562 return nearestPixel;
2565 static final ImageData convertMask(ImageData mask) {
2566 if (mask.depth == 1) return mask;
2567 PaletteData palette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255,255,255)});
2568 ImageData newMask = new ImageData(mask.width, mask.height, 1, palette);
2569 /* Find index of black in mask palette */
2571 RGB[] rgbs = mask.getRGBs();
2573 while (blackIndex < rgbs.length) {
2574 if (rgbs[blackIndex].equals(palette.colors[0])) break;
2578 int[] pixels = new int[mask.width];
2579 for (int y = 0; y < mask.height; y++) {
2580 mask.getPixels(0, y, mask.width, pixels, 0);
2581 for (int i = 0; i < pixels.length; i++) {
2582 if (pixels[i] == blackIndex) {
2588 newMask.setPixels(0, y, mask.width, pixels, 0);
2593 static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
2594 if (pad == newPad) return data;
2595 int stride = (width * depth + 7) / 8;
2596 int bpl = (stride + (pad - 1)) / pad * pad;
2597 int newBpl = (stride + (newPad - 1)) / newPad * newPad;
2598 byte[] newData = new byte[height * newBpl];
2599 int srcIndex = 0, destIndex = 0;
2600 for (int y = 0; y < height; y++) {
2601 System.arraycopy(data, srcIndex, newData, destIndex, stride);
2603 destIndex += newBpl;
2609 * Blit operation bits to be OR'ed together to specify the desired operation.
2612 BLIT_SRC = 1, // copy source directly, else applies logic operations
2613 BLIT_ALPHA = 2, // enable alpha blending
2614 BLIT_DITHER = 4; // enable dithering in low color modes
2617 * Alpha mode, values 0 - 255 specify global alpha level
2620 ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data)
2621 ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data)
2622 ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData
2623 ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData
2624 ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
2625 ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData
2626 ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array
2627 ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array
2630 * Byte and bit order constants.
2632 static final int LSB_FIRST = 0;
2633 static final int MSB_FIRST = 1;
2636 * Data types (internal)
2638 private static final int
2639 // direct / true color formats with arbitrary masks & shifts
2641 TYPE_GENERIC_16_MSB = 1,
2642 TYPE_GENERIC_16_LSB = 2,
2643 TYPE_GENERIC_24 = 3,
2644 TYPE_GENERIC_32_MSB = 4,
2645 TYPE_GENERIC_32_LSB = 5,
2646 // palette indexed color formats
2650 TYPE_INDEX_1_MSB = 9,
2651 TYPE_INDEX_1_LSB = 10;
2654 * Blits a direct palette image into a direct palette image.
2656 * Note: When the source and destination depth, order and masks
2657 * are pairwise equal and the blitter operation is BLIT_SRC,
2658 * the masks are ignored. Hence when not changing the image
2659 * data format, 0 may be specified for the masks.
2662 * @param op the blitter operation: a combination of BLIT_xxx flags
2663 * (see BLIT_xxx constants)
2664 * @param srcData the source byte array containing image data
2665 * @param srcDepth the source depth: one of 8, 16, 24, 32
2666 * @param srcStride the source number of bytes per line
2667 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
2668 * ignored if srcDepth is not 16 or 32
2669 * @param srcX the top-left x-coord of the source blit region
2670 * @param srcY the top-left y-coord of the source blit region
2671 * @param srcWidth the width of the source blit region
2672 * @param srcHeight the height of the source blit region
2673 * @param srcRedMask the source red channel mask
2674 * @param srcGreenMask the source green channel mask
2675 * @param srcBlueMask the source blue channel mask
2676 * @param alphaMode the alpha blending or mask mode, may be
2677 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
2678 * not specified in the blitter operations
2679 * (see ALPHA_MODE_xxx constants)
2680 * @param alphaData the alpha blending or mask data, varies depending
2681 * on the value of alphaMode and sometimes ignored
2682 * @param alphaStride the alpha data number of bytes per line
2683 * @param alphaX the top-left x-coord of the alpha blit region
2684 * @param alphaY the top-left y-coord of the alpha blit region
2685 * @param destData the destination byte array containing image data
2686 * @param destDepth the destination depth: one of 8, 16, 24, 32
2687 * @param destStride the destination number of bytes per line
2688 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
2689 * ignored if destDepth is not 16 or 32
2690 * @param destX the top-left x-coord of the destination blit region
2691 * @param destY the top-left y-coord of the destination blit region
2692 * @param destWidth the width of the destination blit region
2693 * @param destHeight the height of the destination blit region
2694 * @param destRedMask the destination red channel mask
2695 * @param destGreenMask the destination green channel mask
2696 * @param destBlueMask the destination blue channel mask
2697 * @param flipX if true the resulting image is flipped along the vertical axis
2698 * @param flipY if true the resulting image is flipped along the horizontal axis
2700 static void blit(int op,
2701 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
2702 int srcX, int srcY, int srcWidth, int srcHeight,
2703 int srcRedMask, int srcGreenMask, int srcBlueMask,
2704 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
2705 byte[] destData, int destDepth, int destStride, int destOrder,
2706 int destX, int destY, int destWidth, int destHeight,
2707 int destRedMask, int destGreenMask, int destBlueMask,
2708 boolean flipX, boolean flipY) {
2709 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
2711 // these should be supplied as params later
2712 final int srcAlphaMask = 0, destAlphaMask = 0;
2714 /*** Prepare scaling data ***/
2715 final int dwm1 = destWidth - 1;
2716 final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
2717 final int dhm1 = destHeight - 1;
2718 final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
2720 /*** Prepare source-related data ***/
2721 final int sbpp, stype;
2725 stype = TYPE_GENERIC_8;
2729 stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
2733 stype = TYPE_GENERIC_24;
2737 stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
2740 //throw new IllegalArgumentException("Invalid source type");
2743 int spr = srcY * srcStride + srcX * sbpp;
2745 /*** Prepare destination-related data ***/
2746 final int dbpp, dtype;
2747 switch (destDepth) {
2750 dtype = TYPE_GENERIC_8;
2754 dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
2758 dtype = TYPE_GENERIC_24;
2762 dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
2765 //throw new IllegalArgumentException("Invalid destination type");
2768 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
2769 final int dprxi = (flipX) ? -dbpp : dbpp;
2770 final int dpryi = (flipY) ? -destStride : destStride;
2772 /*** Prepare special processing data ***/
2774 if ((op & BLIT_ALPHA) != 0) {
2775 switch (alphaMode) {
2776 case ALPHA_MASK_UNPACKED:
2777 case ALPHA_CHANNEL_SEPARATE:
2778 if (alphaData == null) alphaMode = 0x10000;
2779 apr = alphaY * alphaStride + alphaX;
2781 case ALPHA_MASK_PACKED:
2782 if (alphaData == null) alphaMode = 0x10000;
2784 apr = alphaY * alphaStride + alphaX;
2786 case ALPHA_MASK_INDEX:
2787 //throw new IllegalArgumentException("Invalid alpha type");
2789 case ALPHA_MASK_RGB:
2790 if (alphaData == null) alphaMode = 0x10000;
2794 alphaMode = (alphaMode << 16) / 255; // prescale
2795 case ALPHA_CHANNEL_SOURCE:
2800 alphaMode = 0x10000;
2807 if ((alphaMode == 0x10000) && (stype == dtype) &&
2808 (srcRedMask == destRedMask) && (srcGreenMask == destGreenMask) &&
2809 (srcBlueMask == destBlueMask) && (srcAlphaMask == destAlphaMask)) {
2810 /*** Fast blit (straight copy) ***/
2813 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2814 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2815 destData[dp] = srcData[sp];
2821 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2822 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2823 destData[dp] = srcData[sp];
2824 destData[dp + 1] = srcData[sp + 1];
2825 sp += (sfx >>> 16) * 2;
2830 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2831 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2832 destData[dp] = srcData[sp];
2833 destData[dp + 1] = srcData[sp + 1];
2834 destData[dp + 2] = srcData[sp + 2];
2835 sp += (sfx >>> 16) * 3;
2840 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2841 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2842 destData[dp] = srcData[sp];
2843 destData[dp + 1] = srcData[sp + 1];
2844 destData[dp + 2] = srcData[sp + 2];
2845 destData[dp + 3] = srcData[sp + 3];
2846 sp += (sfx >>> 16) * 4;
2853 /*** Comprehensive blit (apply transformations) ***/
2854 final int srcRedShift = getChannelShift(srcRedMask);
2855 final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
2856 final int srcGreenShift = getChannelShift(srcGreenMask);
2857 final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
2858 final int srcBlueShift = getChannelShift(srcBlueMask);
2859 final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
2860 final int srcAlphaShift = getChannelShift(srcAlphaMask);
2861 final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
2863 final int destRedShift = getChannelShift(destRedMask);
2864 final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
2865 final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
2866 final int destRedPreShift = 8 - destRedWidth;
2867 final int destGreenShift = getChannelShift(destGreenMask);
2868 final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
2869 final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
2870 final int destGreenPreShift = 8 - destGreenWidth;
2871 final int destBlueShift = getChannelShift(destBlueMask);
2872 final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
2873 final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
2874 final int destBluePreShift = 8 - destBlueWidth;
2875 final int destAlphaShift = getChannelShift(destAlphaMask);
2876 final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
2877 final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
2878 final int destAlphaPreShift = 8 - destAlphaWidth;
2880 int ap = apr, alpha = alphaMode;
2881 int r = 0, g = 0, b = 0, a = 0;
2882 int rq = 0, gq = 0, bq = 0, aq = 0;
2883 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
2884 sp = spr += (sfy >>> 16) * srcStride,
2885 ap = apr += (sfy >>> 16) * alphaStride,
2886 sfy = (sfy & 0xffff) + sfyi,
2887 dp = dpr += dpryi) {
2888 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
2890 sfx = (sfx & 0xffff) + sfxi) {
2891 /*** READ NEXT PIXEL ***/
2893 case TYPE_GENERIC_8: {
2894 final int data = srcData[sp] & 0xff;
2896 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2897 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2898 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2899 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2901 case TYPE_GENERIC_16_MSB: {
2902 final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
2903 sp += (sfx >>> 16) * 2;
2904 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2905 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2906 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2907 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2909 case TYPE_GENERIC_16_LSB: {
2910 final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
2911 sp += (sfx >>> 16) * 2;
2912 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2913 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2914 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2915 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2917 case TYPE_GENERIC_24: {
2918 final int data = (( ((srcData[sp] & 0xff) << 8) |
2919 (srcData[sp + 1] & 0xff)) << 8) |
2920 (srcData[sp + 2] & 0xff);
2921 sp += (sfx >>> 16) * 3;
2922 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2923 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2924 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2925 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2927 case TYPE_GENERIC_32_MSB: {
2928 final int data = (( (( ((srcData[sp] & 0xff) << 8) |
2929 (srcData[sp + 1] & 0xff)) << 8) |
2930 (srcData[sp + 2] & 0xff)) << 8) |
2931 (srcData[sp + 3] & 0xff);
2932 sp += (sfx >>> 16) * 4;
2933 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2934 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2935 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2936 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2938 case TYPE_GENERIC_32_LSB: {
2939 final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
2940 (srcData[sp + 2] & 0xff)) << 8) |
2941 (srcData[sp + 1] & 0xff)) << 8) |
2942 (srcData[sp] & 0xff);
2943 sp += (sfx >>> 16) * 4;
2944 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2945 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2946 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2947 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2951 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
2952 switch (alphaMode) {
2953 case ALPHA_CHANNEL_SEPARATE:
2954 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
2957 case ALPHA_CHANNEL_SOURCE:
2958 alpha = (a << 16) / 255;
2960 case ALPHA_MASK_UNPACKED:
2961 alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
2964 case ALPHA_MASK_PACKED:
2965 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
2968 case ALPHA_MASK_RGB:
2970 for (int i = 0; i < alphaData.length; i += 3) {
2971 if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) {
2978 if (alpha != 0x10000) {
2979 if (alpha == 0x0000) continue;
2981 case TYPE_GENERIC_8: {
2982 final int data = destData[dp] & 0xff;
2983 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2984 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2985 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2986 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2988 case TYPE_GENERIC_16_MSB: {
2989 final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
2990 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2991 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2992 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2993 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2995 case TYPE_GENERIC_16_LSB: {
2996 final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
2997 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2998 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2999 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3000 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3002 case TYPE_GENERIC_24: {
3003 final int data = (( ((destData[dp] & 0xff) << 8) |
3004 (destData[dp + 1] & 0xff)) << 8) |
3005 (destData[dp + 2] & 0xff);
3006 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3007 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3008 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3009 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3011 case TYPE_GENERIC_32_MSB: {
3012 final int data = (( (( ((destData[dp] & 0xff) << 8) |
3013 (destData[dp + 1] & 0xff)) << 8) |
3014 (destData[dp + 2] & 0xff)) << 8) |
3015 (destData[dp + 3] & 0xff);
3016 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3017 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3018 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3019 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3021 case TYPE_GENERIC_32_LSB: {
3022 final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
3023 (destData[dp + 2] & 0xff)) << 8) |
3024 (destData[dp + 1] & 0xff)) << 8) |
3025 (destData[dp] & 0xff);
3026 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3027 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3028 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3029 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3032 // Perform alpha blending
3033 a = aq + ((a - aq) * alpha >> 16);
3034 r = rq + ((r - rq) * alpha >> 16);
3035 g = gq + ((g - gq) * alpha >> 16);
3036 b = bq + ((b - bq) * alpha >> 16);
3039 /*** WRITE NEXT PIXEL ***/
3041 (r >>> destRedPreShift << destRedShift) |
3042 (g >>> destGreenPreShift << destGreenShift) |
3043 (b >>> destBluePreShift << destBlueShift) |
3044 (a >>> destAlphaPreShift << destAlphaShift);
3046 case TYPE_GENERIC_8: {
3047 destData[dp] = (byte) data;
3049 case TYPE_GENERIC_16_MSB: {
3050 destData[dp] = (byte) (data >>> 8);
3051 destData[dp + 1] = (byte) (data & 0xff);
3053 case TYPE_GENERIC_16_LSB: {
3054 destData[dp] = (byte) (data & 0xff);
3055 destData[dp + 1] = (byte) (data >>> 8);
3057 case TYPE_GENERIC_24: {
3058 destData[dp] = (byte) (data >>> 16);
3059 destData[dp + 1] = (byte) (data >>> 8);
3060 destData[dp + 2] = (byte) (data & 0xff);
3062 case TYPE_GENERIC_32_MSB: {
3063 destData[dp] = (byte) (data >>> 24);
3064 destData[dp + 1] = (byte) (data >>> 16);
3065 destData[dp + 2] = (byte) (data >>> 8);
3066 destData[dp + 3] = (byte) (data & 0xff);
3068 case TYPE_GENERIC_32_LSB: {
3069 destData[dp] = (byte) (data & 0xff);
3070 destData[dp + 1] = (byte) (data >>> 8);
3071 destData[dp + 2] = (byte) (data >>> 16);
3072 destData[dp + 3] = (byte) (data >>> 24);
3080 * Blits an index palette image into an index palette image.
3082 * Note: The source and destination red, green, and blue
3083 * arrays may be null if no alpha blending or dither is to be
3087 * @param op the blitter operation: a combination of BLIT_xxx flags
3088 * (see BLIT_xxx constants)
3089 * @param srcData the source byte array containing image data
3090 * @param srcDepth the source depth: one of 1, 2, 4, 8
3091 * @param srcStride the source number of bytes per line
3092 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
3093 * ignored if srcDepth is not 1
3094 * @param srcX the top-left x-coord of the source blit region
3095 * @param srcY the top-left y-coord of the source blit region
3096 * @param srcWidth the width of the source blit region
3097 * @param srcHeight the height of the source blit region
3098 * @param srcReds the source palette red component intensities
3099 * @param srcGreens the source palette green component intensities
3100 * @param srcBlues the source palette blue component intensities
3101 * @param alphaMode the alpha blending or mask mode, may be
3102 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
3103 * not specified in the blitter operations
3104 * (see ALPHA_MODE_xxx constants)
3105 * @param alphaData the alpha blending or mask data, varies depending
3106 * on the value of alphaMode and sometimes ignored
3107 * @param alphaStride the alpha data number of bytes per line
3108 * @param alphaX the top-left x-coord of the alpha blit region
3109 * @param alphaY the top-left y-coord of the alpha blit region
3110 * @param destData the destination byte array containing image data
3111 * @param destDepth the destination depth: one of 1, 2, 4, 8
3112 * @param destStride the destination number of bytes per line
3113 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
3114 * ignored if destDepth is not 1
3115 * @param destX the top-left x-coord of the destination blit region
3116 * @param destY the top-left y-coord of the destination blit region
3117 * @param destWidth the width of the destination blit region
3118 * @param destHeight the height of the destination blit region
3119 * @param destReds the destination palette red component intensities
3120 * @param destGreens the destination palette green component intensities
3121 * @param destBlues the destination palette blue component intensities
3122 * @param flipX if true the resulting image is flipped along the vertical axis
3123 * @param flipY if true the resulting image is flipped along the horizontal axis
3125 static void blit(int op,
3126 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
3127 int srcX, int srcY, int srcWidth, int srcHeight,
3128 byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
3129 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
3130 byte[] destData, int destDepth, int destStride, int destOrder,
3131 int destX, int destY, int destWidth, int destHeight,
3132 byte[] destReds, byte[] destGreens, byte[] destBlues,
3133 boolean flipX, boolean flipY) {
3134 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
3136 /*** Prepare scaling data ***/
3137 final int dwm1 = destWidth - 1;
3138 final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
3139 final int dhm1 = destHeight - 1;
3140 final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
3142 /*** Prepare source-related data ***/
3146 stype = TYPE_INDEX_8;
3150 stype = TYPE_INDEX_4;
3154 stype = TYPE_INDEX_2;
3158 stype = (srcOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
3161 //throw new IllegalArgumentException("Invalid source type");
3164 int spr = srcY * srcStride + srcX;
3166 /*** Prepare destination-related data ***/
3168 switch (destDepth) {
3170 dtype = TYPE_INDEX_8;
3174 dtype = TYPE_INDEX_4;
3178 dtype = TYPE_INDEX_2;
3182 dtype = (destOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
3185 //throw new IllegalArgumentException("Invalid source type");
3188 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
3189 final int dprxi = (flipX) ? -1 : 1;
3190 final int dpryi = (flipY) ? -destStride : destStride;
3192 /*** Prepare special processing data ***/
3194 if ((op & BLIT_ALPHA) != 0) {
3195 switch (alphaMode) {
3196 case ALPHA_MASK_UNPACKED:
3197 case ALPHA_CHANNEL_SEPARATE:
3198 if (alphaData == null) alphaMode = 0x10000;
3199 apr = alphaY * alphaStride + alphaX;
3201 case ALPHA_MASK_PACKED:
3202 if (alphaData == null) alphaMode = 0x10000;
3204 apr = alphaY * alphaStride + alphaX;
3206 case ALPHA_MASK_INDEX:
3207 case ALPHA_MASK_RGB:
3208 if (alphaData == null) alphaMode = 0x10000;
3212 alphaMode = (alphaMode << 16) / 255; // prescale
3213 case ALPHA_CHANNEL_SOURCE:
3218 alphaMode = 0x10000;
3221 final boolean ditherEnabled = (op & BLIT_DITHER) != 0;
3227 int destPaletteSize = 1 << destDepth;
3228 if ((destReds != null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
3229 byte[] paletteMapping = null;
3230 boolean isExactPaletteMapping = true;
3231 switch (alphaMode) {
3233 /*** If the palettes and formats are equivalent use a one-to-one mapping ***/
3234 if ((stype == dtype) &&
3235 (srcReds == destReds) && (srcGreens == destGreens) && (srcBlues == destBlues)) {
3236 paletteMapping = ONE_TO_ONE_MAPPING;
3238 /*** If palettes have not been supplied, supply a suitable mapping ***/
3239 } else if ((srcReds == null) || (destReds == null)) {
3240 if (srcDepth <= destDepth) {
3241 paletteMapping = ONE_TO_ONE_MAPPING;
3243 paletteMapping = new byte[1 << srcDepth];
3244 int mask = (0xff << destDepth) >>> 8;
3245 for (int i = 0; i < paletteMapping.length; ++i) paletteMapping[i] = (byte)(i & mask);
3249 case ALPHA_MASK_UNPACKED:
3250 case ALPHA_MASK_PACKED:
3251 case ALPHA_MASK_INDEX:
3252 case ALPHA_MASK_RGB:
3253 /*** Generate a palette mapping ***/
3254 int srcPaletteSize = 1 << srcDepth;
3255 paletteMapping = new byte[srcPaletteSize];
3256 if ((srcReds != null) && (srcReds.length < srcPaletteSize)) srcPaletteSize = srcReds.length;
3257 for (int i = 0, r, g, b, index; i < srcPaletteSize; ++i) {
3258 r = srcReds[i] & 0xff;
3259 g = srcGreens[i] & 0xff;
3260 b = srcBlues[i] & 0xff;
3262 int minDistance = 0x7fffffff;
3263 for (int j = 0, dr, dg, db, distance; j < destPaletteSize; ++j) {
3264 dr = (destReds[j] & 0xff) - r;
3265 dg = (destGreens[j] & 0xff) - g;
3266 db = (destBlues[j] & 0xff) - b;
3267 distance = dr * dr + dg * dg + db * db;
3268 if (distance < minDistance) {
3270 if (distance == 0) break;
3271 minDistance = distance;
3274 paletteMapping[i] = (byte)index;
3275 if (minDistance != 0) isExactPaletteMapping = false;
3279 if ((paletteMapping != null) && (isExactPaletteMapping || ! ditherEnabled)) {
3280 if ((stype == dtype) && (alphaMode == 0x10000)) {
3281 /*** Fast blit (copy w/ mapping) ***/
3284 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
3285 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
3286 destData[dp] = paletteMapping[srcData[sp] & 0xff];
3292 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
3293 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
3295 if ((sp & 1) != 0) v = paletteMapping[srcData[sp >> 1] & 0x0f];
3296 else v = (srcData[sp >> 1] >>> 4) & 0x0f;
3298 if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | v);
3299 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (v << 4));
3304 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
3305 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
3306 final int index = paletteMapping[(srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03];
3308 final int shift = 6 - (dp & 3) * 2;
3309 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
3313 case TYPE_INDEX_1_MSB:
3314 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
3315 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
3316 final int index = paletteMapping[(srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01];
3318 final int shift = 7 - (dp & 7);
3319 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
3323 case TYPE_INDEX_1_LSB:
3324 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
3325 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
3326 final int index = paletteMapping[(srcData[sp >> 3] >>> (sp & 7)) & 0x01];
3328 final int shift = dp & 7;
3329 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
3335 /*** Convert between indexed modes using mapping and mask ***/
3336 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
3337 sp = spr += (sfy >>> 16) * srcStride,
3338 sfy = (sfy & 0xffff) + sfyi,
3339 dp = dpr += dpryi) {
3340 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
3342 sfx = (sfx & 0xffff) + sfxi) {
3344 /*** READ NEXT PIXEL ***/
3347 index = srcData[sp] & 0xff;
3351 if ((sp & 1) != 0) index = srcData[sp >> 1] & 0x0f;
3352 else index = (srcData[sp >> 1] >>> 4) & 0x0f;
3356 index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
3359 case TYPE_INDEX_1_MSB:
3360 index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
3363 case TYPE_INDEX_1_LSB:
3364 index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
3370 /*** APPLY MASK ***/
3371 switch (alphaMode) {
3372 case ALPHA_MASK_UNPACKED: {
3373 final byte mask = alphaData[ap];
3375 if (mask == 0) continue;
3377 case ALPHA_MASK_PACKED: {
3378 final int mask = alphaData[ap >> 3] & (1 << (ap & 7));
3380 if (mask == 0) continue;
3382 case ALPHA_MASK_INDEX: {
3384 while (i < alphaData.length) {
3385 if (index == (alphaData[i] & 0xff)) break;
3387 if (i < alphaData.length) continue;
3389 case ALPHA_MASK_RGB: {
3390 final byte r = srcReds[index], g = srcGreens[index], b = srcBlues[index];
3392 while (i < alphaData.length) {
3393 if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) break;
3396 if (i < alphaData.length) continue;
3399 index = paletteMapping[index] & 0xff;
3401 /*** WRITE NEXT PIXEL ***/
3404 destData[dp] = (byte) index;
3407 if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | index);
3408 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (index << 4));
3410 case TYPE_INDEX_2: {
3411 final int shift = 6 - (dp & 3) * 2;
3412 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
3414 case TYPE_INDEX_1_MSB: {
3415 final int shift = 7 - (dp & 7);
3416 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
3418 case TYPE_INDEX_1_LSB: {
3419 final int shift = dp & 7;
3420 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
3429 /*** Comprehensive blit (apply transformations) ***/
3430 int alpha = alphaMode;
3433 int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
3434 final int[] rerr, gerr, berr;
3435 if (ditherEnabled) {
3436 rerr = new int[destWidth + 2];
3437 gerr = new int[destWidth + 2];
3438 berr = new int[destWidth + 2];
3440 rerr = null; gerr = null; berr = null;
3442 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
3443 sp = spr += (sfy >>> 16) * srcStride,
3444 ap = apr += (sfy >>> 16) * alphaStride,
3445 sfy = (sfy & 0xffff) + sfyi,
3446 dp = dpr += dpryi) {
3447 int lrerr = 0, lgerr = 0, lberr = 0;
3448 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
3450 sfx = (sfx & 0xffff) + sfxi) {
3451 /*** READ NEXT PIXEL ***/
3454 index = srcData[sp] & 0xff;
3458 if ((sp & 1) != 0) index = srcData[sp >> 1] & 0x0f;
3459 else index = (srcData[sp >> 1] >>> 4) & 0x0f;
3463 index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
3466 case TYPE_INDEX_1_MSB:
3467 index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
3470 case TYPE_INDEX_1_LSB:
3471 index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
3476 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
3477 int r = srcReds[index] & 0xff, g = srcGreens[index] & 0xff, b = srcBlues[index] & 0xff;
3478 switch (alphaMode) {
3479 case ALPHA_CHANNEL_SEPARATE:
3480 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
3483 case ALPHA_MASK_UNPACKED:
3484 alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
3487 case ALPHA_MASK_PACKED:
3488 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
3491 case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
3493 while (i < alphaData.length) {
3494 if (index == (alphaData[i] & 0xff)) break;
3496 if (i < alphaData.length) continue;
3498 case ALPHA_MASK_RGB: {
3500 while (i < alphaData.length) {
3501 if ((r == (alphaData[i] & 0xff)) &&
3502 (g == (alphaData[i + 1] & 0xff)) &&
3503 (b == (alphaData[i + 2] & 0xff))) break;
3506 if (i < alphaData.length) continue;
3509 if (alpha != 0x10000) {
3510 if (alpha == 0x0000) continue;
3513 indexq = destData[dp] & 0xff;
3516 if ((dp & 1) != 0) indexq = destData[dp >> 1] & 0x0f;
3517 else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
3520 indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
3522 case TYPE_INDEX_1_MSB:
3523 indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
3525 case TYPE_INDEX_1_LSB:
3526 indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
3529 // Perform alpha blending
3530 final int rq = destReds[indexq] & 0xff;
3531 final int gq = destGreens[indexq] & 0xff;
3532 final int bq = destBlues[indexq] & 0xff;
3533 r = rq + ((r - rq) * alpha >> 16);
3534 g = gq + ((g - gq) * alpha >> 16);
3535 b = bq + ((b - bq) * alpha >> 16);
3538 /*** MAP COLOR TO THE PALETTE ***/
3539 if (ditherEnabled) {
3540 // Floyd-Steinberg error diffusion
3542 if (r < 0) r = 0; else if (r > 255) r = 255;
3544 if (g < 0) g = 0; else if (g > 255) g = 255;
3546 if (b < 0) b = 0; else if (b > 255) b = 255;
3551 if (r != lastr || g != lastg || b != lastb) {
3552 // moving the variable declarations out seems to make the JDK JIT happier...
3553 for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
3554 dr = (destReds[j] & 0xff) - r;
3555 dg = (destGreens[j] & 0xff) - g;
3556 db = (destBlues[j] & 0xff) - b;
3557 distance = dr * dr + dg * dg + db * db;
3558 if (distance < minDistance) {
3560 if (distance == 0) break;
3561 minDistance = distance;
3564 lastr = r; lastg = g; lastb = b;
3566 if (ditherEnabled) {
3567 // Floyd-Steinberg error diffusion, cont'd...
3568 final int dxm1 = dx - 1, dxp1 = dx + 1;
3570 rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
3571 rerr[dx] += acc += lrerr + lrerr;
3572 rerr[dxm1] += acc + lrerr + lrerr;
3573 gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
3574 gerr[dx] += acc += lgerr + lgerr;
3575 gerr[dxm1] += acc + lgerr + lgerr;
3576 berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
3577 berr[dx] += acc += lberr + lberr;
3578 berr[dxm1] += acc + lberr + lberr;
3581 /*** WRITE NEXT PIXEL ***/
3584 destData[dp] = (byte) lastindex;
3587 if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | lastindex);
3588 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
3590 case TYPE_INDEX_2: {
3591 final int shift = 6 - (dp & 3) * 2;
3592 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
3594 case TYPE_INDEX_1_MSB: {
3595 final int shift = 7 - (dp & 7);
3596 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
3598 case TYPE_INDEX_1_LSB: {
3599 final int shift = dp & 7;
3600 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
3608 * Blits an index palette image into a direct palette image.
3610 * Note: The source and destination masks and palettes must
3611 * always be fully specified.
3614 * @param op the blitter operation: a combination of BLIT_xxx flags
3615 * (see BLIT_xxx constants)
3616 * @param srcData the source byte array containing image data
3617 * @param srcDepth the source depth: one of 1, 2, 4, 8
3618 * @param srcStride the source number of bytes per line
3619 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
3620 * ignored if srcDepth is not 1
3621 * @param srcX the top-left x-coord of the source blit region
3622 * @param srcY the top-left y-coord of the source blit region
3623 * @param srcWidth the width of the source blit region
3624 * @param srcHeight the height of the source blit region
3625 * @param srcReds the source palette red component intensities
3626 * @param srcGreens the source palette green component intensities
3627 * @param srcBlues the source palette blue component intensities
3628 * @param alphaMode the alpha blending or mask mode, may be
3629 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
3630 * not specified in the blitter operations
3631 * (see ALPHA_MODE_xxx constants)
3632 * @param alphaData the alpha blending or mask data, varies depending
3633 * on the value of alphaMode and sometimes ignored
3634 * @param alphaStride the alpha data number of bytes per line
3635 * @param alphaX the top-left x-coord of the alpha blit region
3636 * @param alphaY the top-left y-coord of the alpha blit region
3637 * @param destData the destination byte array containing image data
3638 * @param destDepth the destination depth: one of 8, 16, 24, 32
3639 * @param destStride the destination number of bytes per line
3640 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
3641 * ignored if destDepth is not 16 or 32
3642 * @param destX the top-left x-coord of the destination blit region
3643 * @param destY the top-left y-coord of the destination blit region
3644 * @param destWidth the width of the destination blit region
3645 * @param destHeight the height of the destination blit region
3646 * @param destRedMask the destination red channel mask
3647 * @param destGreenMask the destination green channel mask
3648 * @param destBlueMask the destination blue channel mask
3649 * @param flipX if true the resulting image is flipped along the vertical axis
3650 * @param flipY if true the resulting image is flipped along the horizontal axis
3652 static void blit(int op,
3653 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
3654 int srcX, int srcY, int srcWidth, int srcHeight,
3655 byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
3656 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
3657 byte[] destData, int destDepth, int destStride, int destOrder,
3658 int destX, int destY, int destWidth, int destHeight,
3659 int destRedMask, int destGreenMask, int destBlueMask,
3660 boolean flipX, boolean flipY) {
3661 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
3663 // these should be supplied as params later
3664 final int destAlphaMask = 0;
3666 /*** Prepare scaling data ***/
3667 final int dwm1 = destWidth - 1;
3668 final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
3669 final int dhm1 = destHeight - 1;
3670 final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
3672 /*** Prepare source-related data ***/
3676 stype = TYPE_INDEX_8;
3680 stype = TYPE_INDEX_4;
3684 stype = TYPE_INDEX_2;
3688 stype = (srcOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
3691 //throw new IllegalArgumentException("Invalid source type");
3694 int spr = srcY * srcStride + srcX;
3696 /*** Prepare destination-related data ***/
3697 final int dbpp, dtype;
3698 switch (destDepth) {
3701 dtype = TYPE_GENERIC_8;
3705 dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
3709 dtype = TYPE_GENERIC_24;
3713 dtype = (destOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
3716 //throw new IllegalArgumentException("Invalid destination type");
3719 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
3720 final int dprxi = (flipX) ? -dbpp : dbpp;
3721 final int dpryi = (flipY) ? -destStride : destStride;
3723 /*** Prepare special processing data ***/
3725 if ((op & BLIT_ALPHA) != 0) {
3726 switch (alphaMode) {
3727 case ALPHA_MASK_UNPACKED:
3728 case ALPHA_CHANNEL_SEPARATE:
3729 if (alphaData == null) alphaMode = 0x10000;
3730 apr = alphaY * alphaStride + alphaX;
3732 case ALPHA_MASK_PACKED:
3733 if (alphaData == null) alphaMode = 0x10000;
3735 apr = alphaY * alphaStride + alphaX;
3737 case ALPHA_MASK_INDEX:
3738 case ALPHA_MASK_RGB:
3739 if (alphaData == null) alphaMode = 0x10000;
3743 alphaMode = (alphaMode << 16) / 255; // prescale
3744 case ALPHA_CHANNEL_SOURCE:
3749 alphaMode = 0x10000;
3753 /*** Comprehensive blit (apply transformations) ***/
3754 final int destRedShift = getChannelShift(destRedMask);
3755 final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
3756 final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
3757 final int destRedPreShift = 8 - destRedWidth;
3758 final int destGreenShift = getChannelShift(destGreenMask);
3759 final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
3760 final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
3761 final int destGreenPreShift = 8 - destGreenWidth;
3762 final int destBlueShift = getChannelShift(destBlueMask);
3763 final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
3764 final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
3765 final int destBluePreShift = 8 - destBlueWidth;
3766 final int destAlphaShift = getChannelShift(destAlphaMask);
3767 final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
3768 final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
3769 final int destAlphaPreShift = 8 - destAlphaWidth;
3773 int ap = apr, alpha = alphaMode;
3774 int r = 0, g = 0, b = 0, a = 0, index = 0;
3775 int rq = 0, gq = 0, bq = 0, aq = 0;
3776 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
3777 sp = spr += (sfy >>> 16) * srcStride,
3778 ap = apr += (sfy >>> 16) * alphaStride,
3779 sfy = (sfy & 0xffff) + sfyi,
3780 dp = dpr += dpryi) {
3781 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
3783 sfx = (sfx & 0xffff) + sfxi) {
3784 /*** READ NEXT PIXEL ***/
3787 index = srcData[sp] & 0xff;
3791 if ((sp & 1) != 0) index = srcData[sp >> 1] & 0x0f;
3792 else index = (srcData[sp >> 1] >>> 4) & 0x0f;
3796 index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
3799 case TYPE_INDEX_1_MSB:
3800 index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
3803 case TYPE_INDEX_1_LSB:
3804 index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
3809 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
3810 r = srcReds[index] & 0xff;
3811 g = srcGreens[index] & 0xff;
3812 b = srcBlues[index] & 0xff;
3813 switch (alphaMode) {
3814 case ALPHA_CHANNEL_SEPARATE:
3815 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
3818 case ALPHA_MASK_UNPACKED:
3819 alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
3822 case ALPHA_MASK_PACKED:
3823 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
3826 case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
3828 while (i < alphaData.length) {
3829 if (index == (alphaData[i] & 0xff)) break;
3831 if (i < alphaData.length) continue;
3833 case ALPHA_MASK_RGB: {
3835 while (i < alphaData.length) {
3836 if ((r == (alphaData[i] & 0xff)) &&
3837 (g == (alphaData[i + 1] & 0xff)) &&
3838 (b == (alphaData[i + 2] & 0xff))) break;
3841 if (i < alphaData.length) continue;
3844 if (alpha != 0x10000) {
3845 if (alpha == 0x0000) continue;
3847 case TYPE_GENERIC_8: {
3848 final int data = destData[dp] & 0xff;
3849 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3850 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3851 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3852 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3854 case TYPE_GENERIC_16_MSB: {
3855 final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
3856 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3857 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3858 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3859 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3861 case TYPE_GENERIC_16_LSB: {
3862 final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
3863 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3864 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3865 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3866 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3868 case TYPE_GENERIC_24: {
3869 final int data = (( ((destData[dp] & 0xff) << 8) |
3870 (destData[dp + 1] & 0xff)) << 8) |
3871 (destData[dp + 2] & 0xff);
3872 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3873 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3874 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3875 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3877 case TYPE_GENERIC_32_MSB: {
3878 final int data = (( (( ((destData[dp] & 0xff) << 8) |
3879 (destData[dp + 1] & 0xff)) << 8) |
3880 (destData[dp + 2] & 0xff)) << 8) |
3881 (destData[dp + 3] & 0xff);
3882 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3883 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3884 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3885 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3887 case TYPE_GENERIC_32_LSB: {
3888 final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
3889 (destData[dp + 2] & 0xff)) << 8) |
3890 (destData[dp + 1] & 0xff)) << 8) |
3891 (destData[dp] & 0xff);
3892 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
3893 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
3894 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
3895 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
3898 // Perform alpha blending
3899 a = aq + ((a - aq) * alpha >> 16);
3900 r = rq + ((r - rq) * alpha >> 16);
3901 g = gq + ((g - gq) * alpha >> 16);
3902 b = bq + ((b - bq) * alpha >> 16);
3905 /*** WRITE NEXT PIXEL ***/
3907 (r >>> destRedPreShift << destRedShift) |
3908 (g >>> destGreenPreShift << destGreenShift) |
3909 (b >>> destBluePreShift << destBlueShift) |
3910 (a >>> destAlphaPreShift << destAlphaShift);
3912 case TYPE_GENERIC_8: {
3913 destData[dp] = (byte) data;
3915 case TYPE_GENERIC_16_MSB: {
3916 destData[dp] = (byte) (data >>> 8);
3917 destData[dp + 1] = (byte) (data & 0xff);
3919 case TYPE_GENERIC_16_LSB: {
3920 destData[dp] = (byte) (data & 0xff);
3921 destData[dp + 1] = (byte) (data >>> 8);
3923 case TYPE_GENERIC_24: {
3924 destData[dp] = (byte) (data >>> 16);
3925 destData[dp + 1] = (byte) (data >>> 8);
3926 destData[dp + 2] = (byte) (data & 0xff);
3928 case TYPE_GENERIC_32_MSB: {
3929 destData[dp] = (byte) (data >>> 24);
3930 destData[dp + 1] = (byte) (data >>> 16);
3931 destData[dp + 2] = (byte) (data >>> 8);
3932 destData[dp + 3] = (byte) (data & 0xff);
3934 case TYPE_GENERIC_32_LSB: {
3935 destData[dp] = (byte) (data & 0xff);
3936 destData[dp + 1] = (byte) (data >>> 8);
3937 destData[dp + 2] = (byte) (data >>> 16);
3938 destData[dp + 3] = (byte) (data >>> 24);
3946 * Blits a direct palette image into an index palette image.
3948 * Note: The source and destination masks and palettes must
3949 * always be fully specified.
3952 * @param op the blitter operation: a combination of BLIT_xxx flags
3953 * (see BLIT_xxx constants)
3954 * @param srcData the source byte array containing image data
3955 * @param srcDepth the source depth: one of 8, 16, 24, 32
3956 * @param srcStride the source number of bytes per line
3957 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
3958 * ignored if srcDepth is not 16 or 32
3959 * @param srcX the top-left x-coord of the source blit region
3960 * @param srcY the top-left y-coord of the source blit region
3961 * @param srcWidth the width of the source blit region
3962 * @param srcHeight the height of the source blit region
3963 * @param srcRedMask the source red channel mask
3964 * @param srcGreenMask the source green channel mask
3965 * @param srcBlueMask the source blue channel mask
3966 * @param alphaMode the alpha blending or mask mode, may be
3967 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
3968 * not specified in the blitter operations
3969 * (see ALPHA_MODE_xxx constants)
3970 * @param alphaData the alpha blending or mask data, varies depending
3971 * on the value of alphaMode and sometimes ignored
3972 * @param alphaStride the alpha data number of bytes per line
3973 * @param alphaX the top-left x-coord of the alpha blit region
3974 * @param alphaY the top-left y-coord of the alpha blit region
3975 * @param destData the destination byte array containing image data
3976 * @param destDepth the destination depth: one of 1, 2, 4, 8
3977 * @param destStride the destination number of bytes per line
3978 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
3979 * ignored if destDepth is not 1
3980 * @param destX the top-left x-coord of the destination blit region
3981 * @param destY the top-left y-coord of the destination blit region
3982 * @param destWidth the width of the destination blit region
3983 * @param destHeight the height of the destination blit region
3984 * @param destReds the destination palette red component intensities
3985 * @param destGreens the destination palette green component intensities
3986 * @param destBlues the destination palette blue component intensities
3987 * @param flipX if true the resulting image is flipped along the vertical axis
3988 * @param flipY if true the resulting image is flipped along the horizontal axis
3990 static void blit(int op,
3991 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
3992 int srcX, int srcY, int srcWidth, int srcHeight,
3993 int srcRedMask, int srcGreenMask, int srcBlueMask,
3994 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
3995 byte[] destData, int destDepth, int destStride, int destOrder,
3996 int destX, int destY, int destWidth, int destHeight,
3997 byte[] destReds, byte[] destGreens, byte[] destBlues,
3998 boolean flipX, boolean flipY) {
3999 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode == ALPHA_TRANSPARENT)) return;
4001 // these should be supplied as params later
4002 final int srcAlphaMask = 0;
4004 /*** Prepare scaling data ***/
4005 final int dwm1 = destWidth - 1;
4006 final int sfxi = (dwm1 != 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
4007 final int dhm1 = destHeight - 1;
4008 final int sfyi = (dhm1 != 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
4010 /*** Prepare source-related data ***/
4011 final int sbpp, stype;
4015 stype = TYPE_GENERIC_8;
4019 stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
4023 stype = TYPE_GENERIC_24;
4027 stype = (srcOrder == MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
4030 //throw new IllegalArgumentException("Invalid source type");
4033 int spr = srcY * srcStride + srcX * sbpp;
4035 /*** Prepare destination-related data ***/
4037 switch (destDepth) {
4039 dtype = TYPE_INDEX_8;
4043 dtype = TYPE_INDEX_4;
4047 dtype = TYPE_INDEX_2;
4051 dtype = (destOrder == MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
4054 //throw new IllegalArgumentException("Invalid source type");
4057 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
4058 final int dprxi = (flipX) ? -1 : 1;
4059 final int dpryi = (flipY) ? -destStride : destStride;
4061 /*** Prepare special processing data ***/
4063 if ((op & BLIT_ALPHA) != 0) {
4064 switch (alphaMode) {
4065 case ALPHA_MASK_UNPACKED:
4066 case ALPHA_CHANNEL_SEPARATE:
4067 if (alphaData == null) alphaMode = 0x10000;
4068 apr = alphaY * alphaStride + alphaX;
4070 case ALPHA_MASK_PACKED:
4071 if (alphaData == null) alphaMode = 0x10000;
4073 apr = alphaY * alphaStride + alphaX;
4075 case ALPHA_MASK_INDEX:
4076 //throw new IllegalArgumentException("Invalid alpha type");
4078 case ALPHA_MASK_RGB:
4079 if (alphaData == null) alphaMode = 0x10000;
4083 alphaMode = (alphaMode << 16) / 255; // prescale
4084 case ALPHA_CHANNEL_SOURCE:
4089 alphaMode = 0x10000;
4092 final boolean ditherEnabled = (op & BLIT_DITHER) != 0;
4094 /*** Comprehensive blit (apply transformations) ***/
4095 final int srcRedShift = getChannelShift(srcRedMask);
4096 final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
4097 final int srcGreenShift = getChannelShift(srcGreenMask);
4098 final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
4099 final int srcBlueShift = getChannelShift(srcBlueMask);
4100 final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
4101 final int srcAlphaShift = getChannelShift(srcAlphaMask);
4102 final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
4106 int ap = apr, alpha = alphaMode;
4107 int r = 0, g = 0, b = 0, a = 0;
4109 int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
4110 final int[] rerr, gerr, berr;
4111 int destPaletteSize = 1 << destDepth;
4112 if ((destReds != null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
4113 if (ditherEnabled) {
4114 rerr = new int[destWidth + 2];
4115 gerr = new int[destWidth + 2];
4116 berr = new int[destWidth + 2];
4118 rerr = null; gerr = null; berr = null;
4120 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
4121 sp = spr += (sfy >>> 16) * srcStride,
4122 ap = apr += (sfy >>> 16) * alphaStride,
4123 sfy = (sfy & 0xffff) + sfyi,
4124 dp = dpr += dpryi) {
4125 int lrerr = 0, lgerr = 0, lberr = 0;
4126 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
4128 sfx = (sfx & 0xffff) + sfxi) {
4129 /*** READ NEXT PIXEL ***/
4131 case TYPE_GENERIC_8: {
4132 final int data = srcData[sp] & 0xff;
4134 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
4135 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
4136 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
4137 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
4139 case TYPE_GENERIC_16_MSB: {
4140 final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
4141 sp += (sfx >>> 16) * 2;
4142 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
4143 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
4144 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
4145 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
4147 case TYPE_GENERIC_16_LSB: {
4148 final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
4149 sp += (sfx >>> 16) * 2;
4150 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
4151 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
4152 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
4153 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
4155 case TYPE_GENERIC_24: {
4156 final int data = (( ((srcData[sp] & 0xff) << 8) |
4157 (srcData[sp + 1] & 0xff)) << 8) |
4158 (srcData[sp + 2] & 0xff);
4159 sp += (sfx >>> 16) * 3;
4160 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
4161 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
4162 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
4163 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
4165 case TYPE_GENERIC_32_MSB: {
4166 final int data = (( (( ((srcData[sp] & 0xff) << 8) |
4167 (srcData[sp + 1] & 0xff)) << 8) |
4168 (srcData[sp + 2] & 0xff)) << 8) |
4169 (srcData[sp + 3] & 0xff);
4170 sp += (sfx >>> 16) * 4;
4171 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
4172 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
4173 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
4174 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
4176 case TYPE_GENERIC_32_LSB: {
4177 final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
4178 (srcData[sp + 2] & 0xff)) << 8) |
4179 (srcData[sp + 1] & 0xff)) << 8) |
4180 (srcData[sp] & 0xff);
4181 sp += (sfx >>> 16) * 4;
4182 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
4183 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
4184 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
4185 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
4189 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
4190 switch (alphaMode) {
4191 case ALPHA_CHANNEL_SEPARATE:
4192 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
4195 case ALPHA_CHANNEL_SOURCE:
4196 alpha = (a << 16) / 255;
4198 case ALPHA_MASK_UNPACKED:
4199 alpha = (alphaData[ap] != 0) ? 0x10000 : 0;
4202 case ALPHA_MASK_PACKED:
4203 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
4206 case ALPHA_MASK_RGB:
4208 for (int i = 0; i < alphaData.length; i += 3) {
4209 if ((r == alphaData[i]) && (g == alphaData[i + 1]) && (b == alphaData[i + 2])) {
4216 if (alpha != 0x10000) {
4217 if (alpha == 0x0000) continue;
4220 indexq = destData[dp] & 0xff;
4223 if ((dp & 1) != 0) indexq = destData[dp >> 1] & 0x0f;
4224 else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
4227 indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
4229 case TYPE_INDEX_1_MSB:
4230 indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
4232 case TYPE_INDEX_1_LSB:
4233 indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
4236 // Perform alpha blending
4237 final int rq = destReds[indexq] & 0xff;
4238 final int gq = destGreens[indexq] & 0xff;
4239 final int bq = destBlues[indexq] & 0xff;
4240 r = rq + ((r - rq) * alpha >> 16);
4241 g = gq + ((g - gq) * alpha >> 16);
4242 b = bq + ((b - bq) * alpha >> 16);
4245 /*** MAP COLOR TO THE PALETTE ***/
4246 if (ditherEnabled) {
4247 // Floyd-Steinberg error diffusion
4249 if (r < 0) r = 0; else if (r > 255) r = 255;
4251 if (g < 0) g = 0; else if (g > 255) g = 255;
4253 if (b < 0) b = 0; else if (b > 255) b = 255;
4258 if (r != lastr || g != lastg || b != lastb) {
4259 // moving the variable declarations out seems to make the JDK JIT happier...
4260 for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
4261 dr = (destReds[j] & 0xff) - r;
4262 dg = (destGreens[j] & 0xff) - g;
4263 db = (destBlues[j] & 0xff) - b;
4264 distance = dr * dr + dg * dg + db * db;
4265 if (distance < minDistance) {
4267 if (distance == 0) break;
4268 minDistance = distance;
4271 lastr = r; lastg = g; lastb = b;
4273 if (ditherEnabled) {
4274 // Floyd-Steinberg error diffusion, cont'd...
4275 final int dxm1 = dx - 1, dxp1 = dx + 1;
4277 rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
4278 rerr[dx] += acc += lrerr + lrerr;
4279 rerr[dxm1] += acc + lrerr + lrerr;
4280 gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
4281 gerr[dx] += acc += lgerr + lgerr;
4282 gerr[dxm1] += acc + lgerr + lgerr;
4283 berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
4284 berr[dx] += acc += lberr + lberr;
4285 berr[dxm1] += acc + lberr + lberr;
4288 /*** WRITE NEXT PIXEL ***/
4291 destData[dp] = (byte) lastindex;
4294 if ((dp & 1) != 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | lastindex);
4295 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
4297 case TYPE_INDEX_2: {
4298 final int shift = 6 - (dp & 3) * 2;
4299 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
4301 case TYPE_INDEX_1_MSB: {
4302 final int shift = 7 - (dp & 7);
4303 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
4305 case TYPE_INDEX_1_LSB: {
4306 final int shift = dp & 7;
4307 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
4315 * Computes the required channel shift from a mask.
4317 static int getChannelShift(int mask) {
4318 if (mask == 0) return 0;
4320 for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) {
4327 * Computes the required channel width (depth) from a mask.
4329 static int getChannelWidth(int mask, int shift) {
4330 if (mask == 0) return 0;
4333 for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) {
4340 * Extracts a field from packed RGB data given a mask for that field.
4342 static byte getChannelField(int data, int mask) {
4343 final int shift = getChannelShift(mask);
4344 return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift];
4348 * Creates an ImageData containing one band's worth of a gradient filled
4349 * block. If <code>vertical</code> is true, the band must be tiled
4350 * horizontally to fill a region, otherwise it must be tiled vertically.
4352 * @param width the width of the region to be filled
4353 * @param height the height of the region to be filled
4354 * @param vertical if true sweeps from top to bottom, else
4355 * sweeps from left to right
4356 * @param fromRGB the color to start with
4357 * @param toRGB the color to end with
4358 * @param redBits the number of significant red bits, 0 for palette modes
4359 * @param greenBits the number of significant green bits, 0 for palette modes
4360 * @param blueBits the number of significant blue bits, 0 for palette modes
4361 * @return the new ImageData
4363 static ImageData createGradientBand(
4364 int width, int height, boolean vertical,
4365 RGB fromRGB, RGB toRGB,
4366 int redBits, int greenBits, int blueBits) {
4367 /* Gradients are drawn as tiled bands */
4368 final int bandWidth, bandHeight, bitmapDepth;
4369 final byte[] bitmapData;
4370 final PaletteData paletteData;
4371 /* Select an algorithm depending on the depth of the screen */
4372 if (redBits != 0 && greenBits != 0 && blueBits != 0) {
4373 paletteData = new PaletteData(0x0000ff00, 0x00ff0000, 0xff000000);
4375 if (redBits >= 8 && greenBits >= 8 && blueBits >= 8) {
4380 bandHeight = height;
4381 steps = bandHeight > 1 ? bandHeight - 1 : 1;
4385 steps = bandWidth > 1 ? bandWidth - 1 : 1;
4387 final int bytesPerLine = bandWidth * 4;
4388 bitmapData = new byte[bandHeight * bytesPerLine];
4389 buildPreciseGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine);
4390 buildPreciseGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine);
4391 buildPreciseGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine);
4393 /* Dithered color */
4396 bandWidth = (width < 8) ? width : 8;
4397 bandHeight = height;
4398 steps = bandHeight > 1 ? bandHeight - 1 : 1;
4401 bandHeight = (height < 8) ? height : 8;
4402 steps = bandWidth > 1 ? bandWidth - 1 : 1;
4404 final int bytesPerLine = bandWidth * 4;
4405 bitmapData = new byte[bandHeight * bytesPerLine];
4406 buildDitheredGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine, blueBits);
4407 buildDitheredGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine, greenBits);
4408 buildDitheredGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine, redBits);
4411 /* Dithered two tone */
4412 paletteData = new PaletteData(new RGB[] { fromRGB, toRGB });
4416 bandWidth = (width < 8) ? width : 8;
4417 bandHeight = height;
4418 blendi = (bandHeight > 1) ? 0x1040000 / (bandHeight - 1) + 1 : 1;
4421 bandHeight = (height < 8) ? height : 8;
4422 blendi = (bandWidth > 1) ? 0x1040000 / (bandWidth - 1) + 1 : 1;
4424 final int bytesPerLine = (bandWidth + 3) & -4;
4425 bitmapData = new byte[bandHeight * bytesPerLine];
4427 for (int dy = 0, blend = 0, dp = 0; dy < bandHeight;
4428 ++dy, blend += blendi, dp += bytesPerLine) {
4429 for (int dx = 0; dx < bandWidth; ++dx) {
4430 bitmapData[dp + dx] = (blend + DITHER_MATRIX[dy & 7][dx]) <
4431 0x1000000 ? (byte)0 : (byte)1;
4435 for (int dx = 0, blend = 0; dx < bandWidth; ++dx, blend += blendi) {
4436 for (int dy = 0, dptr = dx; dy < bandHeight; ++dy, dptr += bytesPerLine) {
4437 bitmapData[dptr] = (blend + DITHER_MATRIX[dy][dx & 7]) <
4438 0x1000000 ? (byte)0 : (byte)1;
4443 return new ImageData(bandWidth, bandHeight, bitmapDepth, paletteData, 4, bitmapData);
4447 * Fill in gradated values for a color channel
4449 static final void buildPreciseGradientChannel(int from, int to, int steps,
4450 int bandWidth, int bandHeight, boolean vertical,
4451 byte[] bitmapData, int dp, int bytesPerLine) {
4452 int val = from << 16;
4453 final int inc = ((to << 16) - val) / steps + 1;
4455 for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
4456 bitmapData[dp] = (byte)(val >>> 16);
4460 for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
4461 bitmapData[dp] = (byte)(val >>> 16);
4468 * Fill in dithered gradated values for a color channel
4470 static final void buildDitheredGradientChannel(int from, int to, int steps,
4471 int bandWidth, int bandHeight, boolean vertical,
4472 byte[] bitmapData, int dp, int bytesPerLine, int bits) {
4473 final int mask = 0xff00 >>> bits;
4474 int val = from << 16;
4475 final int inc = ((to << 16) - val) / steps + 1;
4477 for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
4478 for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
4479 final int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
4480 int temp = val + thresh;
4481 if (temp > 0xffffff) bitmapData[dptr] = -1;
4482 else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
4487 for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
4488 for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
4489 final int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
4490 int temp = val + thresh;
4491 if (temp > 0xffffff) bitmapData[dptr] = -1;
4492 else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
4500 * Renders a gradient onto a GC.
4502 * This is a GC helper.
4505 * @param gc the GC to render the gradient onto
4506 * @param device the device the GC belongs to
4507 * @param x the top-left x coordinate of the region to be filled
4508 * @param y the top-left y coordinate of the region to be filled
4509 * @param width the width of the region to be filled
4510 * @param height the height of the region to be filled
4511 * @param vertical if true sweeps from top to bottom, else
4512 * sweeps from left to right
4513 * @param fromRGB the color to start with
4514 * @param toRGB the color to end with
4515 * @param redBits the number of significant red bits, 0 for palette modes
4516 * @param greenBits the number of significant green bits, 0 for palette modes
4517 * @param blueBits the number of significant blue bits, 0 for palette modes
4520 static void fillGradientRectangle(GC gc, Device device,
4521 int x, int y, int width, int height, boolean vertical,
4522 RGB fromRGB, RGB toRGB,
4523 int redBits, int greenBits, int blueBits) {
4524 // Create the bitmap and tile it
4525 ImageData band = createGradientBand(width, height, vertical,
4526 fromRGB, toRGB, redBits, greenBits, blueBits);
4527 Image image = new Image(device, band);
4528 if ((band.width == 1) || (band.height == 1)) {
4529 gc.drawImage(image, 0, 0, band.width, band.height, x, y, width, height);
4532 for (int dx = 0; dx < width; dx += band.width) {
4533 int blitWidth = width - dx;
4534 if (blitWidth > band.width) blitWidth = band.width;
4535 gc.drawImage(image, 0, 0, blitWidth, band.height, dx + x, y, blitWidth, band.height);
4538 for (int dy = 0; dy < height; dy += band.height) {
4539 int blitHeight = height - dy;
4540 if (blitHeight > band.height) blitHeight = band.height;
4541 gc.drawImage(image, 0, 0, band.width, blitHeight, x, dy + y, band.width, blitHeight);
4551 /*******************************************************************************
4552 * Copyright (c) 2000, 2003 IBM Corporation and others.
4553 * All rights reserved. This program and the accompanying materials
4554 * are made available under the terms of the Common Public License v1.0
4555 * which accompanies this distribution, and is available at
4556 * http://www.eclipse.org/legal/cpl-v10.html
4559 * IBM Corporation - initial API and implementation
4560 *******************************************************************************/
4562 final static class LEDataInputStream extends InputStream {
4567 * The byte array containing the bytes to read.
4569 protected byte[] buf;
4572 * The current position within the byte array <code>buf</code>. A value
4573 * equal to buf.length indicates no bytes available. A value of
4574 * 0 indicates the buffer is full.
4579 public LEDataInputStream(InputStream input) {
4583 public LEDataInputStream(InputStream input, int bufferSize) {
4585 if (bufferSize > 0) {
4586 buf = new byte[bufferSize];
4589 else throw new IllegalArgumentException();
4592 public void close() throws IOException {
4601 * Answer how many bytes were read.
4603 public int getPosition() {
4608 * Answers how many bytes are available for reading without blocking
4610 public int available() throws IOException {
4611 if (buf == null) throw new IOException();
4612 return (buf.length - pos) + in.available();
4616 * Answer the next byte of the input stream.
4618 public int read() throws IOException {
4619 if (buf == null) throw new IOException();
4621 if (pos < buf.length) return (buf[pos++] & 0xFF);
4626 * Don't imitate the JDK behaviour of reading a random number
4627 * of bytes when you can actually read them all.
4629 public int read(byte b[], int off, int len) throws IOException {
4632 result = readData(b, off, len);
4634 if (result == -1) return -1;
4636 if (result == left) return len;
4639 result = readData(b, off, left);
4644 * Reads at most <code>length</code> bytes from this LEDataInputStream and
4645 * stores them in byte array <code>buffer</code> starting at <code>offset</code>.
4647 * Answer the number of bytes actually read or -1 if no bytes were read and
4648 * end of stream was encountered. This implementation reads bytes from
4649 * the pushback buffer first, then the target stream if more bytes are required
4650 * to satisfy <code>count</code>.
4652 * @param buffer the byte array in which to store the read bytes.
4653 * @param offset the offset in <code>buffer</code> to store the read bytes.
4654 * @param length the maximum number of bytes to store in <code>buffer</code>.
4656 * @return int the number of bytes actually read or -1 if end of stream.
4658 * @exception java.io.IOException if an IOException occurs.
4660 private int readData(byte[] buffer, int offset, int length) throws IOException {
4661 if (buf == null) throw new IOException();
4662 if (offset < 0 || offset > buffer.length ||
4663 length < 0 || (length > buffer.length - offset)) {
4664 throw new ArrayIndexOutOfBoundsException();
4667 int cacheCopied = 0;
4668 int newOffset = offset;
4670 // Are there pushback bytes available?
4671 int available = buf.length - pos;
4672 if (available > 0) {
4673 cacheCopied = (available >= length) ? length : available;
4674 System.arraycopy(buf, pos, buffer, newOffset, cacheCopied);
4675 newOffset += cacheCopied;
4679 // Have we copied enough?
4680 if (cacheCopied == length) return length;
4682 int inCopied = in.read(buffer, newOffset, length - cacheCopied);
4684 if (inCopied > 0) return inCopied + cacheCopied;
4685 if (cacheCopied == 0) return inCopied;
4690 * Answer an integer comprised of the next
4691 * four bytes of the input stream.
4693 public int readInt() throws IOException {
4694 byte[] buf = new byte[4];
4696 return ((((((buf[3] & 0xFF) << 8) |
4697 (buf[2] & 0xFF)) << 8) |
4698 (buf[1] & 0xFF)) << 8) |
4703 * Answer a short comprised of the next
4704 * two bytes of the input stream.
4706 public short readShort() throws IOException {
4707 byte[] buf = new byte[2];
4709 return (short)(((buf[1] & 0xFF) << 8) | (buf[0] & 0xFF));
4713 * Push back the entire content of the given buffer <code>b</code>.
4715 * The bytes are pushed so that they would be read back b[0], b[1], etc.
4716 * If the push back buffer cannot handle the bytes copied from <code>b</code>,
4717 * an IOException will be thrown and no byte will be pushed back.
4720 * @param b the byte array containing bytes to push back into the stream
4722 * @exception java.io.IOException if the pushback buffer is too small
4724 public void unread(byte[] b) throws IOException {
4725 int length = b.length;
4726 if (length > pos) throw new IOException();
4729 System.arraycopy(b, 0, buf, pos, length);
4734 /*******************************************************************************
4735 * Copyright (c) 2000, 2003 IBM Corporation and others.
4736 * All rights reserved. This program and the accompanying materials
4737 * are made available under the terms of the Common Public License v1.0
4738 * which accompanies this distribution, and is available at
4739 * http://www.eclipse.org/legal/cpl-v10.html
4742 * IBM Corporation - initial API and implementation
4743 *******************************************************************************/
4746 final static class JPEGAppn extends JPEGVariableSizeSegment {
4748 public JPEGAppn(byte[] reference) {
4752 public JPEGAppn(LEDataInputStream byteStream) {
4756 public boolean verify() {
4757 int marker = getSegmentMarker();
4758 return marker >= JPEGFileFormat.APP0 && marker <= JPEGFileFormat.APP15;
4761 /*******************************************************************************
4762 * Copyright (c) 2000, 2003 IBM Corporation and others.
4763 * All rights reserved. This program and the accompanying materials
4764 * are made available under the terms of the Common Public License v1.0
4765 * which accompanies this distribution, and is available at
4766 * http://www.eclipse.org/legal/cpl-v10.html
4769 * IBM Corporation - initial API and implementation
4770 *******************************************************************************/
4773 final static class JPEGArithmeticConditioningTable extends JPEGVariableSizeSegment {
4775 public JPEGArithmeticConditioningTable(LEDataInputStream byteStream) {
4779 public int signature() {
4780 return JPEGFileFormat.DAC;
4783 /*******************************************************************************
4784 * Copyright (c) 2000, 2003 IBM Corporation and others.
4785 * All rights reserved. This program and the accompanying materials
4786 * are made available under the terms of the Common Public License v1.0
4787 * which accompanies this distribution, and is available at
4788 * http://www.eclipse.org/legal/cpl-v10.html
4791 * IBM Corporation - initial API and implementation
4792 *******************************************************************************/
4795 final static class JPEGComment extends JPEGVariableSizeSegment {
4797 public JPEGComment(byte[] reference) {
4801 public JPEGComment(LEDataInputStream byteStream) {
4805 public int signature() {
4806 return JPEGFileFormat.COM;
4809 /*******************************************************************************
4810 * Copyright (c) 2000, 2003 IBM Corporation and others.
4811 * All rights reserved. This program and the accompanying materials
4812 * are made available under the terms of the Common Public License v1.0
4813 * which accompanies this distribution, and is available at
4814 * http://www.eclipse.org/legal/cpl-v10.html
4817 * IBM Corporation - initial API and implementation
4818 *******************************************************************************/
4821 final static class JPEGEndOfImage extends JPEGFixedSizeSegment {
4823 public JPEGEndOfImage() {
4827 public JPEGEndOfImage(byte[] reference) {
4831 public int signature() {
4832 return JPEGFileFormat.EOI;
4835 public int fixedSize() {
4839 /*******************************************************************************
4840 * Copyright (c) 2000, 2004 IBM Corporation and others.
4841 * All rights reserved. This program and the accompanying materials
4842 * are made available under the terms of the Common Public License v1.0
4843 * which accompanies this distribution, and is available at
4844 * http://www.eclipse.org/legal/cpl-v10.html
4847 * IBM Corporation - initial API and implementation
4848 *******************************************************************************/
4852 final static class JPEGFileFormat extends FileFormat {
4853 int restartInterval;
4854 JPEGFrameHeader frameHeader;
4855 int imageWidth, imageHeight;
4856 int interleavedMcuCols, interleavedMcuRows;
4858 boolean progressive;
4859 int samplePrecision;
4861 int[][] frameComponents;
4863 byte[][] imageComponents;
4865 int[][][] dataUnits;
4867 JPEGScanHeader scanHeader;
4869 int currentBitCount;
4870 int bufferCurrentPosition;
4872 int nextRestartNumber;
4873 JPEGArithmeticConditioningTable arithmeticTables;
4874 JPEGHuffmanTable[] acHuffmanTables;
4875 JPEGHuffmanTable[] dcHuffmanTables;
4876 int[][] quantizationTables;
4879 int encoderQFactor = 75;
4882 public static final int DCTSIZE = 8;
4883 public static final int DCTSIZESQR = 64;
4884 /* JPEGFixedPointConstants */
4885 public static final int FIX_0_899976223 = 7373;
4886 public static final int FIX_1_961570560 = 16069;
4887 public static final int FIX_2_053119869 = 16819;
4888 public static final int FIX_0_298631336 = 2446;
4889 public static final int FIX_1_847759065 = 15137;
4890 public static final int FIX_1_175875602 = 9633;
4891 public static final int FIX_3_072711026 = 25172;
4892 public static final int FIX_0_765366865 = 6270;
4893 public static final int FIX_2_562915447 = 20995;
4894 public static final int FIX_0_541196100 = 4433;
4895 public static final int FIX_0_390180644 = 3196;
4896 public static final int FIX_1_501321110 = 12299;
4897 /* JPEGMarkerCodes */
4898 public static final int APP0 = 0xFFE0;
4899 public static final int APP15 = 0xFFEF;
4900 public static final int COM = 0xFFFE;
4901 public static final int DAC = 0xFFCC;
4902 public static final int DHP = 0xFFDE;
4903 public static final int DHT = 0xFFC4;
4904 public static final int DNL = 0xFFDC;
4905 public static final int DRI = 0xFFDD;
4906 public static final int DQT = 0xFFDB;
4907 public static final int EOI = 0xFFD9;
4908 public static final int EXP = 0xFFDF;
4909 public static final int JPG = 0xFFC8;
4910 public static final int JPG0 = 0xFFF0;
4911 public static final int JPG13 = 0xFFFD;
4912 public static final int RST0 = 0xFFD0;
4913 public static final int RST1 = 0xFFD1;
4914 public static final int RST2 = 0xFFD2;
4915 public static final int RST3 = 0xFFD3;
4916 public static final int RST4 = 0xFFD4;
4917 public static final int RST5 = 0xFFD5;
4918 public static final int RST6 = 0xFFD6;
4919 public static final int RST7 = 0xFFD7;
4920 public static final int SOF0 = 0xFFC0;
4921 public static final int SOF1 = 0xFFC1;
4922 public static final int SOF2 = 0xFFC2;
4923 public static final int SOF3 = 0xFFC3;
4924 public static final int SOF5 = 0xFFC5;
4925 public static final int SOF6 = 0xFFC6;
4926 public static final int SOF7 = 0xFFC7;
4927 public static final int SOF9 = 0xFFC9;
4928 public static final int SOF10 = 0xFFCA;
4929 public static final int SOF11 = 0xFFCB;
4930 public static final int SOF13 = 0xFFCD;
4931 public static final int SOF14 = 0xFFCE;
4932 public static final int SOF15 = 0xFFCF;
4933 public static final int SOI = 0xFFD8;
4934 public static final int SOS = 0xFFDA;
4935 public static final int TEM = 0xFF01;
4936 /* JPEGFrameComponentParameterConstants */
4937 public static final int TQI = 0;
4938 public static final int HI = 1;
4939 public static final int VI = 2;
4940 public static final int CW = 3;
4941 public static final int CH = 4;
4942 /* JPEGScanComponentParameterConstants */
4943 public static final int DC = 0;
4944 public static final int AC = 1;
4945 /* JFIF Component Constants */
4946 public static final int ID_Y = 1 - 1;
4947 public static final int ID_CB = 2 - 1;
4948 public static final int ID_CR = 3 - 1;
4950 public static final int[] ExtendTest = {
4951 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
4952 4096, 8192, 16384, 32768, 65536, 131072, 262144
4954 public static final int[] ExtendOffset = new int[] {
4955 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047,
4956 -4095, -8191, -16383, -32767, -65535, -131071, -262143
4958 public static final int[] ZigZag8x8 = {
4959 0, 1, 8, 16, 9, 2, 3, 10,
4960 17, 24, 32, 25, 18, 11, 4, 5,
4961 12, 19, 26, 33, 40, 48, 41, 34,
4962 27, 20, 13, 6, 7, 14, 21, 28,
4963 35, 42, 49, 56, 57, 50, 43, 36,
4964 29, 22, 15, 23, 30, 37, 44, 51,
4965 58, 59, 52, 45, 38, 31, 39, 46,
4966 53, 60, 61, 54, 47, 55, 62, 63
4968 public static int[] CrRTable, CbBTable, CrGTable, CbGTable;
4969 public static int[] RYTable, GYTable, BYTable,
4970 RCbTable, GCbTable, BCbTable, RCrTable, GCrTable, BCrTable, NBitsTable;
4974 void compress(ImageData image, byte[] dataYComp, byte[] dataCbComp, byte[] dataCrComp) {
4975 int srcWidth = image.width;
4976 int srcHeight = image.height;
4977 int vhFactor = maxV * maxH;
4978 int[] frameComponent;
4979 imageComponents = new byte[nComponents][];
4980 for (int i = 0; i < nComponents; i++) {
4981 frameComponent = frameComponents[componentIds[i]];
4982 imageComponents[i] = new byte[frameComponent[CW] * frameComponent[CH]];
4984 frameComponent = frameComponents[componentIds[ID_Y]];
4985 for (int yPos = 0; yPos < srcHeight; yPos++) {
4986 int srcOfs = yPos * srcWidth;
4987 int dstOfs = yPos * frameComponent[CW];
4988 System.arraycopy(dataYComp, srcOfs, imageComponents[ID_Y], dstOfs, srcWidth);
4990 frameComponent = frameComponents[componentIds[ID_CB]];
4991 for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
4992 int destRowIndex = yPos * frameComponent[CW];
4993 for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
4995 for (int iv = 0; iv < maxV; iv++) {
4996 int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
4997 for (int ih = 0; ih < maxH; ih++) {
4998 sum += dataCbComp[srcIndex + ih] & 0xFF;
5001 imageComponents[ID_CB][destRowIndex + xPos] = (byte)(sum / vhFactor);
5004 frameComponent = frameComponents[componentIds[ID_CR]];
5005 for (int yPos = 0; yPos < srcHeight / maxV; yPos++) {
5006 int destRowIndex = yPos * frameComponent[CW];
5007 for (int xPos = 0; xPos < srcWidth / maxH; xPos++) {
5009 for (int iv = 0; iv < maxV; iv++) {
5010 int srcIndex = (yPos * maxV + iv) * srcWidth + (xPos * maxH);
5011 for (int ih = 0; ih < maxH; ih++) {
5012 sum += dataCrComp[srcIndex + ih] & 0xFF;
5015 imageComponents[ID_CR][destRowIndex + xPos] = (byte)(sum / vhFactor);
5018 for (int iComp = 0; iComp < nComponents; iComp++) {
5019 byte[] imageComponent = imageComponents[iComp];
5020 frameComponent = frameComponents[componentIds[iComp]];
5021 int hFactor = frameComponent[HI];
5022 int vFactor = frameComponent[VI];
5023 int componentWidth = frameComponent[CW];
5024 int componentHeight = frameComponent[CH];
5025 int compressedWidth = srcWidth / (maxH / hFactor);
5026 int compressedHeight = srcHeight / (maxV / vFactor);
5027 if (compressedWidth < componentWidth) {
5028 int delta = componentWidth - compressedWidth;
5029 for (int yPos = 0; yPos < compressedHeight; yPos++) {
5030 int dstOfs = ((yPos + 1) * componentWidth - delta);
5031 int dataValue = imageComponent[dstOfs - 1] & 0xFF;
5032 for (int i = 0; i < delta; i++) {
5033 imageComponent[dstOfs + i] = (byte)dataValue;
5037 if (compressedHeight < componentHeight) {
5038 int srcOfs = (compressedHeight - 1) * componentWidth;
5039 for (int yPos = compressedHeight; yPos <= componentHeight; yPos++) {
5040 int dstOfs = (yPos - 1) * componentWidth;
5041 System.arraycopy(imageComponent, srcOfs, imageComponent, dstOfs, componentWidth);
5046 void convert4BitRGBToYCbCr(ImageData image) {
5047 RGB[] rgbs = image.getRGBs();
5048 int paletteSize = rgbs.length;
5049 byte[] yComp = new byte[paletteSize];
5050 byte[] cbComp = new byte[paletteSize];
5051 byte[] crComp = new byte[paletteSize];
5052 int srcWidth = image.width;
5053 int srcHeight = image.height;
5054 for (int i = 0; i < paletteSize; i++) {
5055 RGB color = rgbs[i];
5057 int g = color.green;
5059 int n = RYTable[r] + GYTable[g] + BYTable[b];
5060 yComp[i] = (byte)(n / 65536);
5061 if ((n < 0) && (n % 65536 != 0)) yComp[i]--;
5062 n = RCbTable[r] + GCbTable[g] + BCbTable[b];
5063 cbComp[i] = (byte)(n / 65536);
5064 if ((n < 0) && (n % 65536 != 0)) cbComp[i]--;
5065 n = RCrTable[r] + GCrTable[g] + BCrTable[b];
5066 crComp[i] = (byte)(n / 65536);
5067 if ((n < 0) && (n % 65536 != 0)) crComp[i]--;
5069 int bSize = srcWidth * srcHeight;
5070 byte[] dataYComp = new byte[bSize];
5071 byte[] dataCbComp = new byte[bSize];
5072 byte[] dataCrComp = new byte[bSize];
5073 byte[] origData = image.data;
5074 for (int yPos = 0; yPos < srcHeight; yPos++) {
5075 for (int xPos = 0; xPos < srcWidth / 2; xPos++) {
5076 int srcIndex = yPos * (srcWidth / 2) + xPos;
5077 int dstIndex = yPos * srcWidth + (xPos * 2);
5078 int value2 = origData[srcIndex] & 0xFF;
5079 int value1 = value2 / 16;
5080 value2 = value2 % 16;
5081 dataYComp[dstIndex] = yComp[value1];
5082 dataCbComp[dstIndex] = cbComp[value1];
5083 dataCrComp[dstIndex] = crComp[value1];
5084 dataYComp[dstIndex + 1] = yComp[value2];
5085 dataCbComp[dstIndex + 1] = cbComp[value2];
5086 dataCrComp[dstIndex + 1] = crComp[value2];
5089 compress(image, dataYComp, dataCbComp, dataCrComp);
5091 void convert8BitRGBToYCbCr(ImageData image) {
5092 RGB[] rgbs = image.getRGBs();
5093 int paletteSize = rgbs.length;
5094 byte[] yComp = new byte[paletteSize];
5095 byte[] cbComp = new byte[paletteSize];
5096 byte[] crComp = new byte[paletteSize];
5097 int srcWidth = image.width;
5098 int srcHeight = image.height;
5099 for (int i = 0; i < paletteSize; i++) {
5100 RGB color = rgbs[i];
5102 int g = color.green;
5104 int n = RYTable[r] + GYTable[g] + BYTable[b];
5105 yComp[i] = (byte)(n / 65536);
5106 if ((n < 0) && (n % 65536 != 0)) yComp[i]--;
5107 n = RCbTable[r] + GCbTable[g] + BCbTable[b];
5108 cbComp[i] = (byte)(n / 65536);
5109 if ((n < 0) && (n % 65536 != 0)) cbComp[i]--;
5110 n = RCrTable[r] + GCrTable[g] + BCrTable[b];
5111 crComp[i] = (byte)(n / 65536);
5112 if ((n < 0) && (n % 65536 != 0)) crComp[i]--;
5114 int dstWidth = image.width;
5115 int dstHeight = srcHeight;
5116 int stride = (srcWidth + 3) / 4 * 4;
5117 int bSize = dstWidth * dstHeight;
5118 byte[] dataYComp = new byte[bSize];
5119 byte[] dataCbComp = new byte[bSize];
5120 byte[] dataCrComp = new byte[bSize];
5121 byte[] origData = image.data;
5122 for (int yPos = 0; yPos < srcHeight; yPos++) {
5123 int srcRowIndex = yPos * stride;
5124 int dstRowIndex = yPos * dstWidth;
5125 for (int xPos = 0; xPos < srcWidth; xPos++) {
5126 int value = origData[srcRowIndex + xPos] & 0xFF;
5127 int dstIndex = dstRowIndex + xPos;
5128 dataYComp[dstIndex] = yComp[value];
5129 dataCbComp[dstIndex] = cbComp[value];
5130 dataCrComp[dstIndex] = crComp[value];
5133 compress(image, dataYComp, dataCbComp, dataCrComp);
5135 byte[] convertCMYKToRGB() {
5136 /* Unsupported CMYK format. Answer an empty byte array. */
5139 void convertImageToYCbCr(ImageData image) {
5140 switch (image.depth) {
5142 convert4BitRGBToYCbCr(image);
5145 convert8BitRGBToYCbCr(image);
5150 convertMultiRGBToYCbCr(image);
5153 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
5157 void convertMultiRGBToYCbCr(ImageData image) {
5158 int srcWidth = image.width;
5159 int srcHeight = image.height;
5160 int bSize = srcWidth * srcHeight;
5161 byte[] dataYComp = new byte[bSize];
5162 byte[] dataCbComp = new byte[bSize];
5163 byte[] dataCrComp = new byte[bSize];
5164 PaletteData palette = image.palette;
5165 int[] buffer = new int[srcWidth];
5166 if (palette.isDirect) {
5167 int redMask = palette.redMask;
5168 int greenMask = palette.greenMask;
5169 int blueMask = palette.blueMask;
5170 int redShift = palette.redShift;
5171 int greenShift = palette.greenShift;
5172 int blueShift = palette.blueShift;
5173 for (int yPos = 0; yPos < srcHeight; yPos++) {
5174 image.getPixels(0, yPos, srcWidth, buffer, 0);
5175 int dstRowIndex = yPos * srcWidth;
5176 for (int xPos = 0; xPos < srcWidth; xPos++) {
5177 int pixel = buffer[xPos];
5178 int dstDataIndex = dstRowIndex + xPos;
5179 int r = pixel & redMask;
5180 r = (redShift < 0) ? r >>> -redShift : r << redShift;
5181 int g = pixel & greenMask;
5182 g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
5183 int b = pixel & blueMask;
5184 b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
5185 dataYComp[dstDataIndex] = (byte)((RYTable[r] + GYTable[g] + BYTable[b]) / 65536);
5186 dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) / 65536);
5187 dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) / 65536);
5191 for (int yPos = 0; yPos < srcHeight; yPos++) {
5192 image.getPixels(0, yPos, srcWidth, buffer, 0);
5193 int dstRowIndex = yPos * srcWidth;
5194 for (int xPos = 0; xPos < srcWidth; xPos++) {
5195 int pixel = buffer[xPos];
5196 int dstDataIndex = dstRowIndex + xPos;
5197 RGB rgb = palette.getRGB(pixel);
5201 dataYComp[dstDataIndex] = (byte)((RYTable[r] + GYTable[g] + BYTable[b]) / 65536);
5202 dataCbComp[dstDataIndex] = (byte)((RCbTable[r] + GCbTable[g] + BCbTable[b]) / 65536);
5203 dataCrComp[dstDataIndex] = (byte)((RCrTable[r] + GCrTable[g] + BCrTable[b]) / 65536);
5207 compress(image, dataYComp, dataCbComp, dataCrComp);
5209 byte[] convertYToRGB() {
5210 int compWidth = frameComponents[componentIds[ID_Y]][CW];
5211 int bytesPerLine = (((imageWidth * 8 + 7) / 8) + 3) / 4 * 4;
5212 byte[] data = new byte[bytesPerLine * imageHeight];
5213 byte[] yComp = imageComponents[ID_Y];
5215 for (int i = 0; i < imageHeight; i++) {
5216 int srcIndex = i * compWidth;
5217 for (int j = 0; j < bytesPerLine; j++) {
5218 int y = yComp[srcIndex] & 0xFF;
5222 if (y > 255) y = 255;
5224 if (j >= imageWidth) {
5227 data[destIndex] = (byte)y;
5234 byte[] convertYCbCrToRGB() {
5236 * Convert existing image components into an RGB format.
5237 * YCbCr is defined per CCIR 601-1, except that Cb and Cr are
5238 * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
5239 * The conversion equations to be implemented are therefore
5240 * R = Y + 1.40200 * Cr
5241 * G = Y - 0.34414 * Cb - 0.71414 * Cr
5242 * B = Y + 1.77200 * Cb
5243 * where Cb and Cr represent the incoming values less MAXJSAMPLE/2.
5244 * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
5246 * To avoid floating-point arithmetic, we represent the fractional constants
5247 * as integers scaled up by 2^16 (about 4 digits precision); we have to divide
5248 * the products by 2^16, with appropriate rounding, to get the correct answer.
5249 * Notice that Y, being an integral input, does not contribute any fraction
5250 * so it need not participate in the rounding.
5252 * For even more speed, we avoid doing any multiplications in the inner loop
5253 * by precalculating the constants times Cb and Cr for all possible values.
5254 * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
5255 * for 12-bit samples it is still acceptable. It's not very reasonable for
5256 * 16-bit samples, but if you want lossless storage you shouldn't be changing
5257 * colorspace anyway.
5258 * The Cr=>R and Cb=>B values can be rounded to integers in advance; the
5259 * values for the G calculation are left scaled up, since we must add them
5260 * together before rounding.
5262 int bSize = imageWidth * imageHeight * nComponents;
5263 byte[] rgbData = new byte[bSize];
5265 expandImageComponents();
5266 byte[] yComp = imageComponents[ID_Y];
5267 byte[] cbComp = imageComponents[ID_CB];
5268 byte[] crComp = imageComponents[ID_CR];
5269 int compWidth = frameComponents[componentIds[ID_Y]][CW];
5270 for (int v = 0; v < imageHeight; v++) {
5271 int srcIndex = v * compWidth;
5272 for (int i = 0; i < imageWidth; i++) {
5273 int y = yComp[srcIndex] & 0xFF;
5274 int cb = cbComp[srcIndex] & 0xFF;
5275 int cr = crComp[srcIndex] & 0xFF;
5276 int r = y + CrRTable[cr];
5277 int g = y + ((CbGTable[cb] + CrGTable[cr]) / 65536);
5278 int b = y + CbBTable[cb];
5282 if (r > 255) r = 255;
5287 if (g > 255) g = 255;
5292 if (b > 255) b = 255;
5294 rgbData[destIndex] = (byte)b;
5295 rgbData[destIndex + 1] = (byte)g;
5296 rgbData[destIndex + 2] = (byte)r;
5303 byte[] convertYIQToRGB() {
5304 /* Unsupported CMYK format. Answer an empty byte array. */
5307 void decodeACCoefficients(int[] dataUnit, int iComp) {
5308 int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
5309 JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
5312 int rs = decodeUsingTable(acTable);
5323 int bits = receive(s);
5324 dataUnit[ZigZag8x8[k]] = extendBy(bits, s);
5329 void decodeACFirstCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
5334 int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
5335 JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
5338 int rs = decodeUsingTable(acTable);
5345 eobrun = (1 << r) + receive(r) - 1;
5350 int bits = receive(s);
5351 dataUnit[ZigZag8x8[k]] = extendBy(bits, s) << approxBit;
5356 void decodeACRefineCoefficients(int[] dataUnit, int iComp, int start, int end, int approxBit) {
5357 int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
5358 JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
5363 int zzIndex = ZigZag8x8[k];
5364 if (dataUnit[zzIndex] != 0) {
5365 dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
5371 int rs = decodeUsingTable(acTable);
5377 while (zeros < 16 && k <= end) {
5378 int zzIndex = ZigZag8x8[k];
5379 if (dataUnit[zzIndex] != 0) {
5380 dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
5387 eobrun = (1 << r) + receive(r);
5390 int bit = receive(s);
5392 int zzIndex = ZigZag8x8[k];
5393 while ((zeros < r || dataUnit[zzIndex] != 0) && k <= end) {
5394 if (dataUnit[zzIndex] != 0) {
5395 dataUnit[zzIndex] = refineAC(dataUnit[zzIndex], approxBit);
5400 zzIndex = ZigZag8x8[k];
5403 dataUnit[zzIndex] = 1 << approxBit;
5405 dataUnit[zzIndex] = -1 << approxBit;
5412 int refineAC(int ac, int approxBit) {
5414 int bit = nextBit();
5416 ac = ac + (1 << approxBit);
5418 } else if (ac < 0) {
5419 int bit = nextBit();
5421 ac = ac + (-1 << approxBit);
5426 void decodeDCCoefficient(int[] dataUnit, int iComp, boolean first, int approxBit) {
5427 int[] sParams = scanHeader.componentParameters[componentIds[iComp]];
5428 JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
5430 if (progressive && !first) {
5431 int bit = nextBit();
5432 lastDC = dataUnit[0] + (bit << approxBit);
5434 lastDC = precedingDCs[iComp];
5435 int nBits = decodeUsingTable(dcTable);
5437 int bits = receive(nBits);
5438 int diff = extendBy(bits, nBits);
5439 lastDC = lastDC + diff;
5440 precedingDCs[iComp] = lastDC;
5443 lastDC = lastDC << approxBit;
5446 dataUnit[0] = lastDC;
5448 void dequantize(int[] dataUnit, int iComp) {
5449 int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
5450 for (int i = 0; i < dataUnit.length; i++) {
5451 int zzIndex = ZigZag8x8[i];
5452 dataUnit[zzIndex] = dataUnit[zzIndex] * qTable[i];
5455 byte[] decodeImageComponents() {
5456 int[] compIds = new int[nComponents];
5457 int compIdsIndex = 0;
5458 for (int i = 0; i < nComponents; i++) {
5459 compIds[compIdsIndex] = i + 1;
5462 if ((compIds.length == 3) &&
5463 (compIds[0] == 1) &&
5464 (compIds[1] == 2) &&
5465 (compIds[2] == 3)) {
5466 return convertYCbCrToRGB();
5468 if ((compIds.length == 3) &&
5469 (compIds[0] == 1) &&
5470 (compIds[1] == 4) &&
5471 (compIds[2] == 5)) {
5472 return convertYIQToRGB();
5474 if (compIds.length == 4) {
5475 return convertCMYKToRGB();
5477 return convertYToRGB();
5479 void decodeMCUAtXAndY(int xmcu, int ymcu, int nComponentsInScan, boolean first, int start, int end, int approxBit) {
5480 for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
5481 int scanComponent = iComp;
5482 while (scanHeader.componentParameters[componentIds[scanComponent]] == null) {
5485 int[] frameComponent = frameComponents[componentIds[scanComponent]];
5486 int hi = frameComponent[HI];
5487 int vi = frameComponent[VI];
5488 if (nComponentsInScan == 1) {
5492 int compWidth = frameComponent[CW];
5493 for (int ivi = 0; ivi < vi; ivi++) {
5494 for (int ihi = 0; ihi < hi; ihi++) {
5496 // Progressive: First scan - create a new data unit.
5497 // Subsequent scans - refine the existing data unit.
5498 int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
5499 dataUnit = dataUnits[scanComponent][index];
5500 if (dataUnit == null) {
5501 dataUnit = new int[64];
5502 dataUnits[scanComponent][index] = dataUnit;
5505 // Sequential: Clear and reuse the data unit buffer.
5506 for (int i = 0; i < dataUnit.length; i++) {
5510 if (!progressive || scanHeader.isDCProgressiveScan()) {
5511 decodeDCCoefficient(dataUnit, scanComponent, first, approxBit);
5514 decodeACCoefficients(dataUnit, scanComponent);
5516 if (scanHeader.isACProgressiveScan()) {
5518 decodeACFirstCoefficients(dataUnit, scanComponent, start, end, approxBit);
5520 decodeACRefineCoefficients(dataUnit, scanComponent, start, end, approxBit);
5523 if (loader.hasListeners()) {
5524 // Dequantization, IDCT, up-sampling and color conversion
5525 // are done on a copy of the coefficient data in order to
5526 // display the image incrementally.
5527 int[] temp = dataUnit;
5528 dataUnit = new int[64];
5529 System.arraycopy(temp, 0, dataUnit, 0, 64);
5532 if (!progressive || (progressive && loader.hasListeners())) {
5533 dequantize(dataUnit, scanComponent);
5534 inverseDCT(dataUnit);
5535 storeData(dataUnit, scanComponent, xmcu, ymcu, hi, ihi, vi, ivi);
5542 if (progressive && !scanHeader.verifyProgressiveScan()) {
5543 SWT.error(SWT.ERROR_INVALID_IMAGE);
5545 int nComponentsInScan = scanHeader.getNumberOfImageComponents();
5546 int mcuRowsInScan = interleavedMcuRows;
5547 int mcusPerRow = interleavedMcuCols;
5548 if (nComponentsInScan == 1) {
5550 int scanComponent = 0;
5551 while (scanHeader.componentParameters[componentIds[scanComponent]] == null) {
5554 int[] frameComponent = frameComponents[componentIds[scanComponent]];
5555 int hi = frameComponent[HI];
5556 int vi = frameComponent[VI];
5557 int mcuWidth = DCTSIZE * maxH / hi;
5558 int mcuHeight = DCTSIZE * maxV / vi;
5559 mcusPerRow = (imageWidth + mcuWidth - 1) / mcuWidth;
5560 mcuRowsInScan = (imageHeight + mcuHeight - 1) / mcuHeight;
5562 boolean first = scanHeader.isFirstScan();
5563 int start = scanHeader.getStartOfSpectralSelection();
5564 int end = scanHeader.getEndOfSpectralSelection();
5565 int approxBit = scanHeader.getApproxBitPositionLow();
5566 restartsToGo = restartInterval;
5567 nextRestartNumber = 0;
5568 for (int ymcu = 0; ymcu < mcuRowsInScan; ymcu++) {
5569 for (int xmcu = 0; xmcu < mcusPerRow; xmcu++) {
5570 if (restartInterval != 0) {
5571 if (restartsToGo == 0) processRestartInterval();
5574 decodeMCUAtXAndY(xmcu, ymcu, nComponentsInScan, first, start, end, approxBit);
5578 int decodeUsingTable(JPEGHuffmanTable huffmanTable) {
5580 int[] maxCodes = huffmanTable.getDhMaxCodes();
5581 int[] minCodes = huffmanTable.getDhMinCodes();
5582 int[] valPtrs = huffmanTable.getDhValPtrs();
5583 int[] huffVals = huffmanTable.getDhValues();
5584 int code = nextBit();
5585 while (code > maxCodes[i]) {
5586 code = code * 2 + nextBit();
5590 j = j + code - minCodes[i];
5593 void emit(int huffCode, int nBits) {
5595 SWT.error(SWT.ERROR_INVALID_IMAGE);
5597 int[] power2m1 = new int[] {
5598 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
5599 16383, 32767, 65535, 131125
5601 int code = (huffCode & power2m1[nBits - 1]) << (24 - nBits - currentBitCount);
5602 byte[] codeBuffer = new byte[4];
5603 codeBuffer[0] = (byte)(code % 256);
5604 codeBuffer[1] = (byte)((code / 256) % 256);
5605 codeBuffer[2] = (byte)((code / 65536) % 256);
5606 codeBuffer[3] = (byte)((code / 16777216) % 256);
5607 int abs = nBits - (8 - currentBitCount);
5608 if (abs < 0) abs = -abs;
5609 if ((abs / 8) > 0) {
5610 currentByte += codeBuffer[2];
5611 emitByte((byte)currentByte);
5612 emitByte((byte)codeBuffer[1]);
5613 currentByte = codeBuffer[0];
5614 currentBitCount += nBits - 16;
5616 currentBitCount += nBits;
5617 if (currentBitCount >= 8) {
5618 currentByte += codeBuffer[2];
5619 emitByte((byte)currentByte);
5620 currentByte = codeBuffer[1];
5621 currentBitCount -= 8;
5623 currentByte += codeBuffer[2];
5627 void emitByte(byte byteValue) {
5628 if (bufferCurrentPosition >= 512) {
5629 resetOutputBuffer();
5631 dataBuffer[bufferCurrentPosition] = byteValue;
5632 bufferCurrentPosition++;
5633 if (byteValue == -1) {
5637 void encodeACCoefficients(int[] dataUnit, int iComp) {
5638 int[] sParams = scanHeader.componentParameters[iComp];
5639 JPEGHuffmanTable acTable = acHuffmanTables[sParams[AC]];
5640 int[] ehCodes = acTable.ehCodes;
5641 byte[] ehSizes = acTable.ehCodeLengths;
5646 int acValue = dataUnit[ZigZag8x8[k - 1]];
5649 emit(ehCodes[0], ehSizes[0] & 0xFF);
5655 emit(ehCodes[0xF0], ehSizes[0xF0] & 0xFF);
5659 int absACValue = acValue;
5660 if (absACValue < 0) absACValue = -absACValue;
5661 int nBits = NBitsTable[absACValue];
5662 int rs = r * 16 + nBits;
5663 emit(ehCodes[rs], ehSizes[rs] & 0xFF);
5664 emit(0xFFFFFF - absACValue, nBits);
5666 int nBits = NBitsTable[acValue];
5667 int rs = r * 16 + nBits;
5668 emit(ehCodes[rs], ehSizes[rs] & 0xFF);
5669 emit(acValue, nBits);
5675 void encodeDCCoefficients(int[] dataUnit, int iComp) {
5676 int[] sParams = scanHeader.componentParameters[iComp];
5677 JPEGHuffmanTable dcTable = dcHuffmanTables[sParams[DC]];
5678 int lastDC = precedingDCs[iComp];
5679 int dcValue = dataUnit[0];
5680 int diff = dcValue - lastDC;
5681 precedingDCs[iComp] = dcValue;
5683 int absDiff = 0 - diff;
5684 int nBits = NBitsTable[absDiff];
5685 emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
5686 emit(0xFFFFFF - absDiff, nBits);
5688 int nBits = NBitsTable[diff];
5689 emit(dcTable.ehCodes[nBits], dcTable.ehCodeLengths[nBits]);
5695 void encodeMCUAtXAndY(int xmcu, int ymcu) {
5696 int nComponentsInScan = scanHeader.getNumberOfImageComponents();
5697 dataUnit = new int[64];
5698 for (int iComp = 0; iComp < nComponentsInScan; iComp++) {
5699 int[] frameComponent = frameComponents[componentIds[iComp]];
5700 int hi = frameComponent[HI];
5701 int vi = frameComponent[VI];
5702 for (int ivi = 0; ivi < vi; ivi++) {
5703 for (int ihi = 0; ihi < hi; ihi++) {
5704 extractData(dataUnit, iComp, xmcu, ymcu, ihi, ivi);
5705 forwardDCT(dataUnit);
5706 quantizeData(dataUnit, iComp);
5707 encodeDCCoefficients(dataUnit, iComp);
5708 encodeACCoefficients(dataUnit, iComp);
5714 for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
5715 for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
5716 encodeMCUAtXAndY(xmcu, ymcu);
5719 if (currentBitCount != 0) {
5720 emitByte((byte)currentByte);
5722 resetOutputBuffer();
5724 void expandImageComponents() {
5725 for (int iComp = 0; iComp < nComponents; iComp++) {
5726 int[] frameComponent = frameComponents[componentIds[iComp]];
5727 int hi = frameComponent[HI];
5728 int vi = frameComponent[VI];
5729 int upH = maxH / hi;
5730 int upV = maxV / vi;
5731 if ((upH * upV) > 1) {
5732 byte[] component = imageComponents[iComp];
5733 int compWidth = frameComponent[CW];
5734 int compHeight = frameComponent[CH];
5735 int upCompWidth = compWidth * upH;
5736 int upCompHeight = compHeight * upV;
5737 RGB[] rgbs = new RGB[] {
5741 new RGB(0x80,0x80,0),
5743 new RGB(0x80,0,0x80),
5744 new RGB(0,0x80,0x80),
5745 new RGB(0xC0,0xC0,0xC0),
5746 new RGB(0x80,0x80,0x80),
5749 new RGB(0xFF,0xFF,0),
5751 new RGB(0xFF,0,0xFF),
5752 new RGB(0,0xFF,0xFF),
5753 new RGB(0xFF,0xFF,0xFF),
5755 ImageData src = new ImageData(compWidth, compHeight, 8, new PaletteData(rgbs), 4, component);
5756 ImageData dest = src.scaledTo(upCompWidth, upCompHeight);
5757 imageComponents[iComp] = dest.data;
5761 int extendBy(int diff, int t) {
5762 if (diff < ExtendTest[t]) {
5763 return diff + ExtendOffset[t];
5768 void extractData(int[] dataUnit, int iComp, int xmcu, int ymcu, int ihi, int ivi) {
5769 byte[] compImage = imageComponents[iComp];
5770 int[] frameComponent = frameComponents[componentIds[iComp]];
5771 int hi = frameComponent[HI];
5772 int vi = frameComponent[VI];
5773 int compWidth = frameComponent[CW];
5774 int srcIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
5776 for (int i = 0; i < DCTSIZE; i++) {
5777 for (int col = 0; col < DCTSIZE; col++) {
5778 dataUnit[destIndex] = (compImage[srcIndex + col] & 0xFF) - 128;
5781 srcIndex += compWidth;
5784 void forwardDCT(int[] dataUnit) {
5785 for (int row = 0; row < 8; row++) {
5786 int rIndex = row * DCTSIZE;
5787 int tmp0 = dataUnit[rIndex] + dataUnit[rIndex + 7];
5788 int tmp7 = dataUnit[rIndex] - dataUnit[rIndex + 7];
5789 int tmp1 = dataUnit[rIndex + 1] + dataUnit[rIndex + 6];
5790 int tmp6 = dataUnit[rIndex + 1] - dataUnit[rIndex + 6];
5791 int tmp2 = dataUnit[rIndex + 2] + dataUnit[rIndex + 5];
5792 int tmp5 = dataUnit[rIndex + 2] - dataUnit[rIndex + 5];
5793 int tmp3 = dataUnit[rIndex + 3] + dataUnit[rIndex + 4];
5794 int tmp4 = dataUnit[rIndex + 3] - dataUnit[rIndex + 4];
5797 * Even part per LL&M figure 1 --- note that published figure
5798 * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
5800 int tmp10 = tmp0 + tmp3;
5801 int tmp13 = tmp0 - tmp3;
5802 int tmp11 = tmp1 + tmp2;
5803 int tmp12 = tmp1 - tmp2;
5805 dataUnit[rIndex] = (tmp10 + tmp11) * 4;
5806 dataUnit[rIndex + 4] = (tmp10 - tmp11) * 4;
5808 int z1 = (tmp12 + tmp13) * FIX_0_541196100;
5809 int scaleFactor1 = ExtendTest[11];
5810 int scaleFactor2 = ExtendTest[12];
5811 int n = z1 + (tmp13 * FIX_0_765366865) + scaleFactor1;
5812 dataUnit[rIndex + 2] = n / scaleFactor2;
5813 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 2]--;
5814 n = z1 + (tmp12 * (0 - FIX_1_847759065)) + scaleFactor1;
5815 dataUnit[rIndex + 6] = n / scaleFactor2;
5816 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 6]--;
5819 * Odd part per figure 8 --- note paper omits factor of sqrt(2).
5820 * cK represents cos(K*pi/16).
5821 * i0..i3 in the paper are tmp4..tmp7 here.
5824 int z2 = tmp5 + tmp6;
5825 int z3 = tmp4 + tmp6;
5826 int z4 = tmp5 + tmp7;
5827 int z5 = (z3 + z4) * FIX_1_175875602; // sqrt(2) * c3
5829 tmp4 = tmp4 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7)
5830 tmp5 = tmp5 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7)
5831 tmp6 = tmp6 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7)
5832 tmp7 = tmp7 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7)
5833 z1 = z1 * (0 - FIX_0_899976223); // sqrt(2) * (c7-c3)
5834 z2 = z2 * (0 - FIX_2_562915447); // sqrt(2) * (-c1-c3)
5835 z3 = z3 * (0 - FIX_1_961570560); // sqrt(2) * (-c3-c5)
5836 z4 = z4 * (0 - FIX_0_390180644); // sqrt(2) * (c5-c3)
5841 n = tmp4 + z1 + z3 + scaleFactor1;
5842 dataUnit[rIndex + 7] = n / scaleFactor2;
5843 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 7]--;
5844 n = tmp5 + z2 + z4 + scaleFactor1;
5845 dataUnit[rIndex + 5] = n / scaleFactor2;
5846 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 5]--;
5847 n = tmp6 + z2 + z3 + scaleFactor1;
5848 dataUnit[rIndex + 3] = n / scaleFactor2;
5849 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 3]--;
5850 n = tmp7 + z1 + z4 + scaleFactor1;
5851 dataUnit[rIndex + 1] = n / scaleFactor2;
5852 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[rIndex + 1]--;
5856 * Pass 2: process columns.
5857 * Note that we must descale the results by a factor of 8 == 2**3,
5858 * and also undo the PASS1_BITS scaling.
5860 for (int col = 0; col < 8; col++) {
5869 int tmp0 = dataUnit[c0] + dataUnit[c7];
5870 int tmp7 = dataUnit[c0] - dataUnit[c7];
5871 int tmp1 = dataUnit[c1] + dataUnit[c6];
5872 int tmp6 = dataUnit[c1] - dataUnit[c6];
5873 int tmp2 = dataUnit[c2] + dataUnit[c5];
5874 int tmp5 = dataUnit[c2] - dataUnit[c5];
5875 int tmp3 = dataUnit[c3] + dataUnit[c4];
5876 int tmp4 = dataUnit[c3] - dataUnit[c4];
5879 * Even part per LL&M figure 1 --- note that published figure
5880 * is faulty; rotator 'sqrt(2)*c1' should be 'sqrt(2)*c6'.
5882 int tmp10 = tmp0 + tmp3;
5883 int tmp13 = tmp0 - tmp3;
5884 int tmp11 = tmp1 + tmp2;
5885 int tmp12 = tmp1 - tmp2;
5887 int scaleFactor1 = ExtendTest[5];
5888 int scaleFactor2 = ExtendTest[6];
5889 int n = tmp10 + tmp11 + scaleFactor1;
5890 dataUnit[c0] = n / scaleFactor2;
5891 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c0]--;
5892 n = tmp10 - tmp11 + scaleFactor1;
5893 dataUnit[c4] = n / scaleFactor2;
5894 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c4]--;
5896 int z1 = (tmp12 + tmp13) * FIX_0_541196100;
5897 scaleFactor1 = ExtendTest[18];
5898 scaleFactor2 = ExtendTest[19];
5899 n = z1 + (tmp13 * FIX_0_765366865) + scaleFactor1;
5900 dataUnit[c2] = n / scaleFactor2;
5901 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c2]--;
5902 n = z1 + (tmp12 * (0 - FIX_1_847759065)) + scaleFactor1;
5903 dataUnit[c6] = n / scaleFactor2;
5904 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c6]--;
5907 * Odd part per figure 8 --- note paper omits factor of sqrt(2).
5908 * cK represents cos(K*pi/16).
5909 * i0..i3 in the paper are tmp4..tmp7 here.
5912 int z2 = tmp5 + tmp6;
5913 int z3 = tmp4 + tmp6;
5914 int z4 = tmp5 + tmp7;
5915 int z5 = (z3 + z4) * FIX_1_175875602; // sqrt(2) * c3
5917 tmp4 = tmp4 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7)
5918 tmp5 = tmp5 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7)
5919 tmp6 = tmp6 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7)
5920 tmp7 = tmp7 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7)
5921 z1 = z1 * (0 - FIX_0_899976223); // sqrt(2) * (c7-c3)
5922 z2 = z2 * (0 - FIX_2_562915447); // sqrt(2) * (-c1-c3)
5923 z3 = z3 * (0 - FIX_1_961570560); // sqrt(2) * (-c3-c5)
5924 z4 = z4 * (0 - FIX_0_390180644); // sqrt(2) * (c5-c3)
5929 n = tmp4 + z1 + z3 + scaleFactor1;
5930 dataUnit[c7] = n / scaleFactor2;
5931 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c7]--;
5932 n = tmp5 + z2 + z4 + scaleFactor1;
5933 dataUnit[c5] = n / scaleFactor2;
5934 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c5]--;
5935 n = tmp6 + z2 + z3 + scaleFactor1;
5936 dataUnit[c3] = n / scaleFactor2;
5937 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c3]--;
5938 n = tmp7 + z1 + z4 + scaleFactor1;
5939 dataUnit[c1] = n / scaleFactor2;
5940 if ((n < 0) && (n % scaleFactor2 != 0)) dataUnit[c1]--;
5944 JPEGAppn appn = new JPEGAppn(inputStream);
5945 if (!appn.verify()) {
5946 SWT.error(SWT.ERROR_INVALID_IMAGE);
5950 new JPEGComment(inputStream);
5953 JPEGArithmeticConditioningTable dac = new JPEGArithmeticConditioningTable(inputStream);
5954 arithmeticTables = dac;
5957 JPEGHuffmanTable dht = new JPEGHuffmanTable(inputStream);
5958 if (!dht.verify()) {
5959 SWT.error(SWT.ERROR_INVALID_IMAGE);
5961 if (acHuffmanTables == null) {
5962 acHuffmanTables = new JPEGHuffmanTable[4];
5964 if (dcHuffmanTables == null) {
5965 dcHuffmanTables = new JPEGHuffmanTable[4];
5967 JPEGHuffmanTable[] dhtTables = dht.getAllTables();
5968 for (int i = 0; i < dhtTables.length; i++) {
5969 JPEGHuffmanTable dhtTable = dhtTables[i];
5970 if (dhtTable.getTableClass() == 0) {
5971 dcHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
5973 acHuffmanTables[dhtTable.getTableIdentifier()] = dhtTable;
5978 new JPEGRestartInterval(inputStream);
5981 JPEGQuantizationTable dqt = new JPEGQuantizationTable(inputStream);
5982 int[][] currentTables = quantizationTables;
5983 if (currentTables == null) {
5984 currentTables = new int[4][];
5986 int[] dqtTablesKeys = dqt.getQuantizationTablesKeys();
5987 int[][] dqtTablesValues = dqt.getQuantizationTablesValues();
5988 for (int i = 0; i < dqtTablesKeys.length; i++) {
5989 int index = dqtTablesKeys[i];
5990 currentTables[index] = dqtTablesValues[i];
5992 quantizationTables = currentTables;
5995 JPEGRestartInterval dri = new JPEGRestartInterval(inputStream);
5996 if (!dri.verify()) {
5997 SWT.error(SWT.ERROR_INVALID_IMAGE);
5999 restartInterval = dri.getRestartInterval();
6001 static void initialize() {
6002 initializeRGBYCbCrTables();
6003 initializeYCbCrRGBTables();
6004 initializeBitCountTable();
6006 static void initializeBitCountTable() {
6009 NBitsTable = new int[2048];
6011 for (int i = 1; i < NBitsTable.length; i++) {
6012 if (!(i < power2)) {
6016 NBitsTable[i] = nBits;
6019 static void initializeRGBYCbCrTables() {
6020 RYTable = new int[256];
6021 GYTable = new int[256];
6022 BYTable = new int[256];
6023 RCbTable = new int[256];
6024 GCbTable = new int[256];
6025 BCbTable = new int[256];
6026 RCrTable = BCbTable;
6027 GCrTable = new int[256];
6028 BCrTable = new int[256];
6029 for (int i = 0; i < 256; i++) {
6030 RYTable[i] = i * 19595;
6031 GYTable[i] = i * 38470;
6032 BYTable[i] = i * 7471 + 32768;
6033 RCbTable[i] = i * -11059;
6034 GCbTable[i] = i * -21709;
6035 BCbTable[i] = i * 32768 + 8388608;
6036 GCrTable[i] = i * -27439;
6037 BCrTable[i] = i * -5329;
6040 static void initializeYCbCrRGBTables() {
6041 CrRTable = new int[256];
6042 CbBTable = new int[256];
6043 CrGTable = new int[256];
6044 CbGTable = new int[256];
6045 for (int i = 0; i < 256; i++) {
6046 int x2 = 2 * i - 255;
6047 CrRTable[i] = (45941 * x2 + 32768) / 65536;
6048 CbBTable[i] = (58065 * x2 + 32768) / 65536;
6049 CrGTable[i] = -23401 * x2;
6050 CbGTable[i] = -11277 * x2 + 32768;
6053 void inverseDCT(int[] dataUnit) {
6054 for (int row = 0; row < 8; row++) {
6055 int rIndex = row * DCTSIZE;
6057 * Due to quantization, we will usually find that many of the input
6058 * coefficients are zero, especially the AC terms. We can exploit this
6059 * by short-circuiting the IDCT calculation for any row in which all
6060 * the AC terms are zero. In that case each output is equal to the
6061 * DC coefficient (with scale factor as needed).
6062 * With typical images and quantization tables, half or more of the
6063 * row DCT calculations can be simplified this way.
6065 if (isZeroInRow(dataUnit, rIndex)) {
6066 int dcVal = dataUnit[rIndex] * 4;
6067 for (int i = rIndex; i < rIndex + 8; i++) {
6068 dataUnit[i] = dcVal;
6072 * Even part: reverse the even part of the forward DCT.
6073 * The rotator is sqrt(2)*c(-6).
6075 int z2 = dataUnit[rIndex + 2];
6076 int z3 = dataUnit[rIndex + 6];
6077 int z1 = (z2 + z3) * FIX_0_541196100;
6078 int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
6079 int tmp3 = z1 + (z2 * FIX_0_765366865);
6080 int tmp0 = (dataUnit[rIndex] + dataUnit[rIndex + 4]) * 8192;
6081 int tmp1 = (dataUnit[rIndex] - dataUnit[rIndex + 4]) * 8192;
6082 int tmp10 = tmp0 + tmp3;
6083 int tmp13 = tmp0 - tmp3;
6084 int tmp11 = tmp1 + tmp2;
6085 int tmp12 = tmp1 - tmp2;
6087 * Odd part per figure 8; the matrix is unitary and hence its
6088 * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
6090 tmp0 = dataUnit[rIndex + 7];
6091 tmp1 = dataUnit[rIndex + 5];
6092 tmp2 = dataUnit[rIndex + 3];
6093 tmp3 = dataUnit[rIndex + 1];
6097 int z4 = tmp1 + tmp3;
6098 int z5 = (z3 + z4)* FIX_1_175875602; /* sqrt(2) * c3 */
6100 tmp0 = tmp0 * FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */
6101 tmp1 = tmp1 * FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */
6102 tmp2 = tmp2 * FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */
6103 tmp3 = tmp3 * FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */
6104 z1 = z1 * (0 - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
6105 z2 = z2 * (0 - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
6106 z3 = z3 * (0 - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
6107 z4 = z4 * (0 - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
6111 tmp0 = tmp0 + z1 + z3;
6112 tmp1 = tmp1 + z2 + z4;
6113 tmp2 = tmp2 + z2 + z3;
6114 tmp3 = tmp3 + z1 + z4;
6116 int descaleFactor1 = ExtendTest[11];
6117 int descaleFactor2 = ExtendTest[12];
6118 dataUnit[rIndex] = (tmp10 + tmp3 + descaleFactor1) / descaleFactor2;
6119 dataUnit[rIndex + 7] = (tmp10 - tmp3 + descaleFactor1) / descaleFactor2;
6120 dataUnit[rIndex + 1] = (tmp11 + tmp2 + descaleFactor1) / descaleFactor2;
6121 dataUnit[rIndex + 6] = (tmp11 - tmp2 + descaleFactor1) / descaleFactor2;
6122 dataUnit[rIndex + 2] = (tmp12 + tmp1 + descaleFactor1) / descaleFactor2;
6123 dataUnit[rIndex + 5] = (tmp12 - tmp1 + descaleFactor1) / descaleFactor2;
6124 dataUnit[rIndex + 3] = (tmp13 + tmp0 + descaleFactor1) / descaleFactor2;
6125 dataUnit[rIndex + 4] = (tmp13 - tmp0 + descaleFactor1) / descaleFactor2;
6129 * Pass 2: process columns.
6130 * Note that we must descale the results by a factor of 8 == 2**3,
6131 * and also undo the PASS1_BITS scaling.
6133 for (int col = 0; col < 8; col++) {
6142 if (isZeroInColumn(dataUnit, col)) {
6143 int dcVal = (dataUnit[c0] + 16) / 32;
6144 dataUnit[c0] = dcVal;
6145 dataUnit[c1] = dcVal;
6146 dataUnit[c2] = dcVal;
6147 dataUnit[c3] = dcVal;
6148 dataUnit[c4] = dcVal;
6149 dataUnit[c5] = dcVal;
6150 dataUnit[c6] = dcVal;
6151 dataUnit[c7] = dcVal;
6154 * Even part: reverse the even part of the forward DCT.
6155 * The rotator is sqrt(2)*c(-6).
6157 int z2 = dataUnit[c2];
6158 int z3 = dataUnit[c6];
6159 int z1 = (z2 + z3) * FIX_0_541196100;
6160 int tmp2 = z1 + (z3 * (0 - FIX_1_847759065));
6161 int tmp3 = z1 + (z2 * FIX_0_765366865);
6162 int tmp0 = (dataUnit[c0] + dataUnit[c4]) * 8192;
6163 int tmp1 = (dataUnit[c0] - dataUnit[c4]) * 8192;
6164 int tmp10 = tmp0 + tmp3;
6165 int tmp13 = tmp0 - tmp3;
6166 int tmp11 = tmp1 + tmp2;
6167 int tmp12 = tmp1 - tmp2;
6169 * Odd part per figure 8; the matrix is unitary and hence its
6170 * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
6172 tmp0 = dataUnit[c7];
6173 tmp1 = dataUnit[c5];
6174 tmp2 = dataUnit[c3];
6175 tmp3 = dataUnit[c1];
6179 int z4 = tmp1 + tmp3;
6180 int z5 = (z3 + z4) * FIX_1_175875602; /* sqrt(2) * c3 */
6182 tmp0 = tmp0 * FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */
6183 tmp1 = tmp1 * FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */
6184 tmp2 = tmp2 * FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */
6185 tmp3 = tmp3 * FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */
6186 z1 = z1 * (0 - FIX_0_899976223); /* sqrt(2) * (c7-c3) */
6187 z2 = z2 * (0 - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */
6188 z3 = z3 * (0 - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */
6189 z4 = z4 * (0 - FIX_0_390180644); /* sqrt(2) * (c5-c3) */
6194 tmp0 = tmp0 + z1 + z3;
6195 tmp1 = tmp1 + z2 + z4;
6196 tmp2 = tmp2 + z2 + z3;
6197 tmp3 = tmp3 + z1 + z4;
6199 /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
6200 int descaleFactor1 = ExtendTest[18];
6201 int descaleFactor2 = ExtendTest[19];
6202 dataUnit[c0] = (tmp10 + tmp3 + descaleFactor1) / descaleFactor2;
6203 dataUnit[c7] = (tmp10 - tmp3 + descaleFactor1) / descaleFactor2;
6204 dataUnit[c1] = (tmp11 + tmp2 + descaleFactor1) / descaleFactor2;
6205 dataUnit[c6] = (tmp11 - tmp2 + descaleFactor1) / descaleFactor2;
6206 dataUnit[c2] = (tmp12 + tmp1 + descaleFactor1) / descaleFactor2;
6207 dataUnit[c5] = (tmp12 - tmp1 + descaleFactor1) / descaleFactor2;
6208 dataUnit[c3] = (tmp13 + tmp0 + descaleFactor1) / descaleFactor2;
6209 dataUnit[c4] = (tmp13 - tmp0 + descaleFactor1) / descaleFactor2;
6213 boolean isFileFormat(LEDataInputStream stream) {
6215 JPEGStartOfImage soi = new JPEGStartOfImage(stream);
6216 stream.unread(soi.reference);
6217 return soi.verify(); // we no longer check for appN
6218 } catch (Exception e) {
6222 boolean isZeroInColumn(int[] dataUnit, int col) {
6223 return (dataUnit[col + 8] + dataUnit[col + 16] +
6224 dataUnit[col + 24] + dataUnit[col + 32] +
6225 dataUnit[col + 40] + dataUnit[col + 48] +
6226 dataUnit[col + 56]) == 0;
6228 boolean isZeroInRow(int[] dataUnit, int rIndex) {
6229 return (dataUnit[rIndex + 1] + dataUnit[rIndex + 2] +
6230 dataUnit[rIndex + 3] + dataUnit[rIndex + 4] +
6231 dataUnit[rIndex + 5] + dataUnit[rIndex + 6] +
6232 dataUnit[rIndex + 7]) == 0;
6234 ImageData[] loadFromByteStream() {
6235 JPEGStartOfImage soi = new JPEGStartOfImage(inputStream);
6236 if (!soi.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
6237 restartInterval = 0;
6239 /* Process the tables preceding the frame header. */
6242 /* Start of Frame. */
6243 frameHeader = new JPEGFrameHeader(inputStream);
6244 if (!frameHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
6245 imageWidth = frameHeader.getSamplesPerLine();
6246 imageHeight = frameHeader.getNumberOfLines();
6247 maxH = frameHeader.getMaxHFactor();
6248 maxV = frameHeader.getMaxVFactor();
6249 int mcuWidth = maxH * DCTSIZE;
6250 int mcuHeight = maxV * DCTSIZE;
6251 interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
6252 interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
6253 progressive = frameHeader.isProgressive();
6254 samplePrecision = frameHeader.getSamplePrecision();
6255 nComponents = frameHeader.getNumberOfImageComponents();
6256 frameComponents = frameHeader.componentParameters;
6257 componentIds = frameHeader.componentIdentifiers;
6258 imageComponents = new byte[nComponents][];
6260 // Progressive jpeg: need to keep all of the data units.
6261 dataUnits = new int[nComponents][][];
6263 // Sequential jpeg: only need one data unit.
6264 dataUnit = new int[8 * 8];
6266 for (int i = 0; i < nComponents; i++) {
6267 int[] frameComponent = frameComponents[componentIds[i]];
6268 int bufferSize = frameComponent[CW] * frameComponent[CH];
6269 imageComponents[i] = new byte[bufferSize];
6271 dataUnits[i] = new int[bufferSize][];
6275 /* Process the tables preceding the scan header. */
6278 /* Start of Scan. */
6279 scanHeader = new JPEGScanHeader(inputStream);
6280 if (!scanHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
6282 /* Process scan(s) and further tables until EOI. */
6283 int progressiveScanCount = 0;
6284 boolean done = false;
6287 precedingDCs = new int[4];
6289 if (progressive && loader.hasListeners()) {
6290 ImageData imageData = createImageData();
6291 loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, false));
6292 progressiveScanCount++;
6295 /* Unread any buffered data before looking for tables again. */
6296 int delta = 512 - bufferCurrentPosition - 1;
6298 byte[] unreadBuffer = new byte[delta];
6299 System.arraycopy(dataBuffer, bufferCurrentPosition + 1, unreadBuffer, 0, delta);
6301 inputStream.unread(unreadBuffer);
6302 } catch (IOException e) {
6303 SWT.error(SWT.ERROR_IO, e);
6307 /* Process the tables preceding the next scan header. */
6308 JPEGSegment jpegSegment = processTables();
6309 if (jpegSegment == null || jpegSegment.getSegmentMarker() == EOI) {
6312 scanHeader = new JPEGScanHeader(inputStream);
6313 if (!scanHeader.verify()) SWT.error(SWT.ERROR_INVALID_IMAGE);
6318 for (int ymcu = 0; ymcu < interleavedMcuRows; ymcu++) {
6319 for (int xmcu = 0; xmcu < interleavedMcuCols; xmcu++) {
6320 for (int iComp = 0; iComp < nComponents; iComp++) {
6321 int[] frameComponent = frameComponents[componentIds[iComp]];
6322 int hi = frameComponent[HI];
6323 int vi = frameComponent[VI];
6324 int compWidth = frameComponent[CW];
6325 for (int ivi = 0; ivi < vi; ivi++) {
6326 for (int ihi = 0; ihi < hi; ihi++) {
6327 int index = (ymcu * vi + ivi) * compWidth + xmcu * hi + ihi;
6328 dataUnit = dataUnits[iComp][index];
6329 dequantize(dataUnit, iComp);
6330 inverseDCT(dataUnit);
6331 storeData(dataUnit, iComp, xmcu, ymcu, hi, ihi, vi, ivi);
6338 ImageData imageData = createImageData();
6339 if (progressive && loader.hasListeners()) {
6340 loader.notifyListeners(new ImageLoaderEvent(loader, imageData, progressiveScanCount, true));
6342 return new ImageData[] {imageData};
6344 ImageData createImageData() {
6345 return ImageData.internal_new(
6348 nComponents * samplePrecision,
6350 nComponents == 1 ? 4 : 1,
6351 decodeImageComponents(),
6364 if (currentBitCount != 0) {
6367 if (currentByte > 255) {
6374 bufferCurrentPosition++;
6375 if (bufferCurrentPosition >= 512) {
6377 bufferCurrentPosition = 0;
6379 currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
6380 currentBitCount = 8;
6382 if (bufferCurrentPosition == 511) {
6384 currentBitCount = 8;
6385 nextByte = dataBuffer[0];
6387 nextByte = dataBuffer[bufferCurrentPosition + 1];
6389 if (currentByte == 0xFF) {
6390 if (nextByte == 0) {
6391 bufferCurrentPosition ++;
6394 if (currentByte > 255) {
6401 if ((nextByte & 0xFF) + 0xFF00 == DNL) {
6405 SWT.error(SWT.ERROR_INVALID_IMAGE);
6412 if (currentByte > 255) {
6420 void processRestartInterval() {
6422 bufferCurrentPosition++;
6423 if (bufferCurrentPosition > 511) {
6425 bufferCurrentPosition = 0;
6427 currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
6428 } while (currentByte != 0xFF);
6429 while (currentByte == 0xFF) {
6430 bufferCurrentPosition++;
6431 if (bufferCurrentPosition > 511) {
6433 bufferCurrentPosition = 0;
6435 currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
6437 if (currentByte != ((RST0 + nextRestartNumber) % 256)) {
6438 SWT.error(SWT.ERROR_INVALID_IMAGE);
6440 bufferCurrentPosition++;
6441 if (bufferCurrentPosition > 511) {
6443 bufferCurrentPosition = 0;
6445 currentByte = dataBuffer[bufferCurrentPosition] & 0xFF;
6446 currentBitCount = 8;
6447 restartsToGo = restartInterval;
6448 nextRestartNumber = (nextRestartNumber + 1) % 8;
6449 precedingDCs = new int[4];
6452 /* Process all markers until a frame header, scan header, or EOI is found. */
6453 JPEGSegment processTables() {
6455 JPEGSegment jpegSegment = seekUnspecifiedMarker(inputStream);
6456 if (jpegSegment == null) return null;
6457 JPEGFrameHeader sof = new JPEGFrameHeader(jpegSegment.reference);
6461 int marker = jpegSegment.getSegmentMarker();
6463 case SOI: // there should only be one SOI per file
6464 SWT.error(SWT.ERROR_INVALID_IMAGE);
6487 skipSegmentFrom(inputStream);
6492 void quantizeData(int[] dataUnit, int iComp) {
6493 int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
6494 for (int i = 0; i < dataUnit.length; i++) {
6495 int zzIndex = ZigZag8x8[i];
6496 int data = dataUnit[zzIndex];
6497 int absData = data < 0 ? 0 - data : data;
6498 int qValue = qTable[i];
6499 int q2 = qValue / 2;
6501 if (absData < qValue) {
6502 dataUnit[zzIndex] = 0;
6506 dataUnit[zzIndex] = absData;
6508 dataUnit[zzIndex] = 0 - absData;
6513 int receive(int nBits) {
6515 for (int i = 0; i < nBits; i++) {
6516 v = v * 2 + nextBit();
6520 void resetInputBuffer() {
6521 if (dataBuffer == null) {
6522 dataBuffer = new byte[512];
6525 inputStream.read(dataBuffer);
6526 } catch (IOException e) {
6527 SWT.error(SWT.ERROR_IO, e);
6529 currentBitCount = 0;
6530 bufferCurrentPosition = -1;
6532 void resetOutputBuffer() {
6533 if (dataBuffer == null) {
6534 dataBuffer = new byte[512];
6537 outputStream.write(dataBuffer, 0, bufferCurrentPosition);
6538 } catch (IOException e) {
6539 SWT.error(SWT.ERROR_IO, e);
6542 bufferCurrentPosition = 0;
6544 static JPEGSegment seekUnspecifiedMarker(LEDataInputStream byteStream) {
6545 byte[] byteArray = new byte[2];
6548 if (byteStream.read(byteArray, 0, 1) != 1) return null;
6549 if (byteArray[0] == (byte) 0xFF) {
6550 if (byteStream.read(byteArray, 1, 1) != 1) return null;
6551 if (byteArray[1] != (byte) 0xFF && byteArray[1] != 0) {
6552 byteStream.unread(byteArray);
6553 return new JPEGSegment(byteArray);
6557 } catch (IOException e) {
6558 SWT.error(SWT.ERROR_IO, e);
6562 PaletteData setUpPalette() {
6563 if (nComponents == 1) {
6564 RGB[] entries = new RGB[256];
6565 for (int i = 0; i < 256; i++) {
6566 entries[i] = new RGB(i, i, i);
6568 return new PaletteData(entries);
6570 return new PaletteData(0xFF, 0xFF00, 0xFF0000);
6572 static void skipSegmentFrom(LEDataInputStream byteStream) {
6574 byte[] byteArray = new byte[4];
6575 JPEGSegment jpegSegment = new JPEGSegment(byteArray);
6577 if (byteStream.read(byteArray) != byteArray.length) {
6578 SWT.error(SWT.ERROR_INVALID_IMAGE);
6580 if (!(byteArray[0] == -1 && byteArray[1] != 0 && byteArray[1] != -1)) {
6581 SWT.error(SWT.ERROR_INVALID_IMAGE);
6583 int delta = jpegSegment.getSegmentLength() - 2;
6584 byteStream.skip(delta);
6585 } catch (Exception e) {
6586 SWT.error(SWT.ERROR_IO, e);
6589 void storeData(int[] dataUnit, int iComp, int xmcu, int ymcu, int hi, int ihi, int vi, int ivi) {
6590 byte[] compImage = imageComponents[iComp];
6591 int[] frameComponent = frameComponents[componentIds[iComp]];
6592 int compWidth = frameComponent[CW];
6593 int destIndex = ((ymcu * vi + ivi) * compWidth * DCTSIZE) + ((xmcu * hi + ihi) * DCTSIZE);
6595 for (int i = 0; i < DCTSIZE; i++) {
6596 for (int col = 0; col < DCTSIZE; col++) {
6597 int x = dataUnit[srcIndex] + 128;
6601 if (x > 255) x = 255;
6603 compImage[destIndex + col] = (byte)x;
6606 destIndex += compWidth;
6609 void unloadIntoByteStream(ImageData image) {
6610 if (!new JPEGStartOfImage().writeToStream(outputStream)) {
6611 SWT.error(SWT.ERROR_IO);
6613 JPEGAppn appn = new JPEGAppn(new byte[] {(byte)0xFF, (byte)0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0});
6614 if (!appn.writeToStream(outputStream)) {
6615 SWT.error(SWT.ERROR_IO);
6617 quantizationTables = new int[4][];
6618 JPEGQuantizationTable chromDQT = JPEGQuantizationTable.defaultChrominanceTable();
6619 chromDQT.scaleBy(encoderQFactor);
6620 int[] jpegDQTKeys = chromDQT.getQuantizationTablesKeys();
6621 int[][] jpegDQTValues = chromDQT.getQuantizationTablesValues();
6622 for (int i = 0; i < jpegDQTKeys.length; i++) {
6623 quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
6625 JPEGQuantizationTable lumDQT = JPEGQuantizationTable.defaultLuminanceTable();
6626 lumDQT.scaleBy(encoderQFactor);
6627 jpegDQTKeys = lumDQT.getQuantizationTablesKeys();
6628 jpegDQTValues = lumDQT.getQuantizationTablesValues();
6629 for (int i = 0; i < jpegDQTKeys.length; i++) {
6630 quantizationTables[jpegDQTKeys[i]] = jpegDQTValues[i];
6632 if (!lumDQT.writeToStream(outputStream)) {
6633 SWT.error(SWT.ERROR_IO);
6635 if (!chromDQT.writeToStream(outputStream)) {
6636 SWT.error(SWT.ERROR_IO);
6638 int frameLength, scanLength, precision;
6639 int[][] frameParams, scanParams;
6640 if (image.depth == 1) {
6642 frameParams = new int[1][];
6643 frameParams[0] = new int[] {1, 1, 1, 0, 0};
6644 scanParams = new int[1][];
6645 scanParams[0] = new int[] {0, 0};
6651 frameParams = new int[3][];
6652 frameParams[0] = new int[] {0, 2, 2, 0, 0};
6653 frameParams[1] = new int[] {1, 1, 1, 0, 0};
6654 frameParams[2] = new int[] {1, 1, 1, 0, 0};
6655 scanParams = new int[3][];
6656 scanParams[0] = new int[] {0, 0};
6657 scanParams[1] = new int[] {1, 1};
6658 scanParams[2] = new int[] {1, 1};
6663 imageWidth = image.width;
6664 imageHeight = image.height;
6665 frameHeader = new JPEGFrameHeader(new byte[19]);
6666 frameHeader.setSegmentMarker(SOF0);
6667 frameHeader.setSegmentLength(frameLength);
6668 frameHeader.setSamplePrecision(precision);
6669 frameHeader.setSamplesPerLine(imageWidth);
6670 frameHeader.setNumberOfLines(imageHeight);
6671 frameHeader.setNumberOfImageComponents(nComponents);
6672 frameHeader.componentParameters = frameParams;
6673 frameHeader.componentIdentifiers = new int[] {0, 1, 2};
6674 frameHeader.initializeContents();
6675 if (!frameHeader.writeToStream(outputStream)) {
6676 SWT.error(SWT.ERROR_IO);
6678 frameComponents = frameParams;
6679 componentIds = frameHeader.componentIdentifiers;
6680 maxH = frameHeader.getMaxHFactor();
6681 maxV = frameHeader.getMaxVFactor();
6682 int mcuWidth = maxH * DCTSIZE;
6683 int mcuHeight = maxV * DCTSIZE;
6684 interleavedMcuCols = (imageWidth + mcuWidth - 1) / mcuWidth;
6685 interleavedMcuRows = (imageHeight + mcuHeight - 1) / mcuHeight;
6686 acHuffmanTables = new JPEGHuffmanTable[4];
6687 dcHuffmanTables = new JPEGHuffmanTable[4];
6688 JPEGHuffmanTable[] dhtTables = new JPEGHuffmanTable[] {
6689 JPEGHuffmanTable.getDefaultDCLuminanceTable(),
6690 JPEGHuffmanTable.getDefaultDCChrominanceTable(),
6691 JPEGHuffmanTable.getDefaultACLuminanceTable(),
6692 JPEGHuffmanTable.getDefaultACChrominanceTable()
6694 for (int i = 0; i < dhtTables.length; i++) {
6695 JPEGHuffmanTable dhtTable = dhtTables[i];
6696 if (!dhtTable.writeToStream(outputStream)) {
6697 SWT.error(SWT.ERROR_IO);
6699 JPEGHuffmanTable[] allTables = dhtTable.getAllTables();
6700 for (int j = 0; j < allTables.length; j++) {
6701 JPEGHuffmanTable huffmanTable = allTables[j];
6702 if (huffmanTable.getTableClass() == 0) {
6703 dcHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
6705 acHuffmanTables[huffmanTable.getTableIdentifier()] = huffmanTable;
6709 precedingDCs = new int[4];
6710 scanHeader = new JPEGScanHeader(new byte[14]);
6711 scanHeader.setSegmentMarker(SOS);
6712 scanHeader.setSegmentLength(scanLength);
6713 scanHeader.setNumberOfImageComponents(nComponents);
6714 scanHeader.setStartOfSpectralSelection(0);
6715 scanHeader.setEndOfSpectralSelection(63);
6716 scanHeader.componentParameters = scanParams;
6717 scanHeader.initializeContents();
6718 if (!scanHeader.writeToStream(outputStream)) {
6719 SWT.error(SWT.ERROR_IO);
6721 convertImageToYCbCr(image);
6722 resetOutputBuffer();
6724 currentBitCount = 0;
6726 if (!new JPEGEndOfImage().writeToStream(outputStream)) {
6727 SWT.error(SWT.ERROR_IO);
6731 /*******************************************************************************
6732 * Copyright (c) 2000, 2003 IBM Corporation and others.
6733 * All rights reserved. This program and the accompanying materials
6734 * are made available under the terms of the Common Public License v1.0
6735 * which accompanies this distribution, and is available at
6736 * http://www.eclipse.org/legal/cpl-v10.html
6739 * IBM Corporation - initial API and implementation
6740 *******************************************************************************/
6744 abstract static class JPEGFixedSizeSegment extends JPEGSegment {
6746 public JPEGFixedSizeSegment() {
6747 reference = new byte[fixedSize()];
6748 setSegmentMarker(signature());
6751 public JPEGFixedSizeSegment(byte[] reference) {
6755 public JPEGFixedSizeSegment(LEDataInputStream byteStream) {
6756 reference = new byte[fixedSize()];
6758 byteStream.read(reference);
6759 } catch (Exception e) {
6760 SWT.error(SWT.ERROR_IO, e);
6764 abstract public int fixedSize();
6766 public int getSegmentLength() {
6767 return fixedSize() - 2;
6770 public void setSegmentLength(int length) {
6773 /*******************************************************************************
6774 * Copyright (c) 2000, 2004 IBM Corporation and others.
6775 * All rights reserved. This program and the accompanying materials
6776 * are made available under the terms of the Common Public License v1.0
6777 * which accompanies this distribution, and is available at
6778 * http://www.eclipse.org/legal/cpl-v10.html
6781 * IBM Corporation - initial API and implementation
6782 *******************************************************************************/
6786 final static class JPEGFrameHeader extends JPEGVariableSizeSegment {
6789 public int[] componentIdentifiers;
6790 public int[][] componentParameters;
6792 public JPEGFrameHeader(byte[] reference) {
6796 public JPEGFrameHeader(LEDataInputStream byteStream) {
6798 initializeComponentParameters();
6801 public int getSamplePrecision() {
6802 return reference[4] & 0xFF;
6805 public int getNumberOfLines() {
6806 return (reference[5] & 0xFF) << 8 | (reference[6] & 0xFF);
6809 public int getSamplesPerLine() {
6810 return (reference[7] & 0xFF) << 8 | (reference[8] & 0xFF);
6813 public int getNumberOfImageComponents() {
6814 return reference[9] & 0xFF;
6817 public void setSamplePrecision(int precision) {
6818 reference[4] = (byte)(precision & 0xFF);
6821 public void setNumberOfLines(int anInteger) {
6822 reference[5] = (byte)((anInteger & 0xFF00) >> 8);
6823 reference[6] = (byte)(anInteger & 0xFF);
6826 public void setSamplesPerLine(int samples) {
6827 reference[7] = (byte)((samples & 0xFF00) >> 8);
6828 reference[8] = (byte)(samples & 0xFF);
6831 public void setNumberOfImageComponents(int anInteger) {
6832 reference[9] = (byte)(anInteger & 0xFF);
6835 public int getMaxHFactor() {
6839 public int getMaxVFactor() {
6843 public void setMaxHFactor(int anInteger) {
6844 maxHFactor = anInteger;
6847 public void setMaxVFactor(int anInteger) {
6848 maxVFactor = anInteger;
6851 /* Used when decoding. */
6852 void initializeComponentParameters() {
6853 int nf = getNumberOfImageComponents();
6854 componentIdentifiers = new int[nf];
6855 int[][] compSpecParams = new int[0][];
6858 for (int i = 0; i < nf; i++) {
6859 int ofs = i * 3 + 10;
6860 int ci = reference[ofs] & 0xFF;
6861 componentIdentifiers[i] = ci;
6862 int hi = (reference[ofs + 1] & 0xFF) / 16;
6863 int vi = (reference[ofs + 1] & 0xFF) % 16;
6864 int tqi = reference[ofs + 2] & 0xFF;
6871 int[] compParam = new int[5];
6875 if (compSpecParams.length <= ci) {
6876 int[][] newParams = new int[ci + 1][];
6877 System.arraycopy(compSpecParams, 0, newParams, 0, compSpecParams.length);
6878 compSpecParams = newParams;
6880 compSpecParams[ci] = compParam;
6882 int x = getSamplesPerLine();
6883 int y = getNumberOfLines();
6884 int[] multiples = new int[] { 8, 16, 24, 32 };
6885 for (int i = 0; i < nf; i++) {
6886 int[] compParam = compSpecParams[componentIdentifiers[i]];
6887 int hi = compParam[1];
6888 int vi = compParam[2];
6889 int compWidth = (x * hi + hmax - 1) / hmax;
6890 int compHeight = (y * vi + vmax - 1) / vmax;
6891 int dsWidth = roundUpToMultiple(compWidth, multiples[hi - 1]);
6892 int dsHeight = roundUpToMultiple(compHeight, multiples[vi - 1]);
6893 compParam[3] = dsWidth;
6894 compParam[4] = dsHeight;
6896 setMaxHFactor(hmax);
6897 setMaxVFactor(vmax);
6898 componentParameters = compSpecParams;
6901 /* Used when encoding. */
6902 public void initializeContents() {
6903 int nf = getNumberOfImageComponents();
6904 if (nf == 0 || nf != componentParameters.length) {
6905 SWT.error(SWT.ERROR_INVALID_IMAGE);
6909 int[][] compSpecParams = componentParameters;
6910 for (int i = 0; i < nf; i++) {
6911 int ofs = i * 3 + 10;
6912 int[] compParam = compSpecParams[componentIdentifiers[i]];
6913 int hi = compParam[1];
6914 int vi = compParam[2];
6916 SWT.error(SWT.ERROR_INVALID_IMAGE);
6918 reference[ofs] = (byte)(i + 1);
6919 reference[ofs + 1] = (byte)(hi * 16 + vi);
6920 reference[ofs + 2] = (byte)(compParam[0]);
6921 if (hi > hmax) hmax = hi;
6922 if (vi > vmax) vmax = vi;
6924 int x = getSamplesPerLine();
6925 int y = getNumberOfLines();
6926 int[] multiples = new int[] {8, 16, 24, 32};
6927 for (int i = 0; i < nf; i++) {
6928 int[] compParam = compSpecParams[componentIdentifiers[i]];
6929 int hi = compParam[1];
6930 int vi = compParam[2];
6931 int compWidth = (x * hi + hmax - 1) / hmax;
6932 int compHeight = (y * vi + vmax - 1) / vmax;
6933 int dsWidth = roundUpToMultiple(compWidth, multiples[hi - 1]);
6934 int dsHeight = roundUpToMultiple(compHeight, multiples[vi - 1]);
6935 compParam[3] = dsWidth;
6936 compParam[4] = dsHeight;
6938 setMaxHFactor(hmax);
6939 setMaxVFactor(vmax);
6942 int roundUpToMultiple(int anInteger, int mInteger) {
6943 int a = anInteger + mInteger - 1;
6944 return a - (a % mInteger);
6948 * Verify the information contained in the receiver is correct.
6949 * Answer true if the header contains a valid marker. Otherwise,
6950 * answer false. Valid Start Of Frame markers are:
6951 * SOF_0 - Baseline DCT, Huffman coding
6952 * SOF_1 - Extended sequential DCT, Huffman coding
6953 * SOF_2 - Progressive DCT, Huffman coding
6954 * SOF_3 - Lossless (sequential), Huffman coding
6955 * SOF_5 - Differential sequential, Huffman coding
6956 * SOF_6 - Differential progressive, Huffman coding
6957 * SOF_7 - Differential lossless, Huffman coding
6958 * SOF_9 - Extended sequential DCT, arithmetic coding
6959 * SOF_10 - Progressive DCT, arithmetic coding
6960 * SOF_11 - Lossless (sequential), arithmetic coding
6961 * SOF_13 - Differential sequential, arithmetic coding
6962 * SOF_14 - Differential progressive, arithmetic coding
6963 * SOF_15 - Differential lossless, arithmetic coding
6965 public boolean verify() {
6966 int marker = getSegmentMarker();
6967 return (marker >= JPEGFileFormat.SOF0 && marker <= JPEGFileFormat.SOF3) ||
6968 (marker >= JPEGFileFormat.SOF5 && marker <= JPEGFileFormat.SOF7) ||
6969 (marker >= JPEGFileFormat.SOF9 && marker <= JPEGFileFormat.SOF11) ||
6970 (marker >= JPEGFileFormat.SOF13 && marker <= JPEGFileFormat.SOF15);
6973 public boolean isProgressive() {
6974 int marker = getSegmentMarker();
6975 return marker == JPEGFileFormat.SOF2
6976 || marker == JPEGFileFormat.SOF6
6977 || marker == JPEGFileFormat.SOF10
6978 || marker == JPEGFileFormat.SOF14;
6981 public boolean isArithmeticCoding() {
6982 return getSegmentMarker() >= JPEGFileFormat.SOF9;
6985 /*******************************************************************************
6986 * Copyright (c) 2000, 2003 IBM Corporation and others.
6987 * All rights reserved. This program and the accompanying materials
6988 * are made available under the terms of the Common Public License v1.0
6989 * which accompanies this distribution, and is available at
6990 * http://www.eclipse.org/legal/cpl-v10.html
6993 * IBM Corporation - initial API and implementation
6994 *******************************************************************************/
6998 * JPEGHuffmanTable static class actually represents two types of object:
6999 * 1) A DHT (Define Huffman Tables) segment, which may represent
7000 * as many as 4 Huffman tables. In this case, the tables are
7001 * stored in the allTables array.
7002 * 2) A single Huffman table. In this case, the allTables array
7004 * The 'reference' field is stored in both types of object, but
7005 * 'initialize' is only called if the object represents a DHT.
7007 final static class JPEGHuffmanTable extends JPEGVariableSizeSegment {
7008 JPEGHuffmanTable[] allTables;
7010 int tableIdentifier;
7012 int[] dhCodeLengths;
7018 byte[] ehCodeLengths;
7019 static byte[] DCLuminanceTable = {
7020 (byte)255, (byte)196, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
7021 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
7023 static byte[] DCChrominanceTable = {
7024 (byte)255, (byte)196, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
7025 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
7027 static byte[] ACLuminanceTable = {
7028 (byte)255, (byte)196, 0, (byte)181, 16, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125,
7029 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20,
7030 50, (byte)129, (byte)145, (byte)161, 8, 35, 66, (byte)177, (byte)193, 21, 82, (byte)209, (byte)240, 36, 51, 98,
7031 114, (byte)130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53,
7032 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87,
7033 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118,
7034 119, 120, 121, 122, (byte)131, (byte)132, (byte)133, (byte)134, (byte)135, (byte)136, (byte)137, (byte)138, (byte)146, (byte)147, (byte)148,
7035 (byte)149, (byte)150, (byte)151, (byte)152, (byte)153, (byte)154, (byte)162, (byte)163, (byte)164, (byte)165, (byte)166, (byte)167, (byte)168, (byte)169, (byte)170,
7036 (byte)178, (byte)179, (byte)180, (byte)181, (byte)182, (byte)183, (byte)184, (byte)185, (byte)186, (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199,
7037 (byte)200, (byte)201, (byte)202, (byte)210, (byte)211, (byte)212, (byte)213, (byte)214, (byte)215, (byte)216, (byte)217, (byte)218, (byte)225, (byte)226, (byte)227,
7038 (byte)228, (byte)229, (byte)230, (byte)231, (byte)232, (byte)233, (byte)234, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)246, (byte)247, (byte)248,
7039 (byte)249, (byte)250
7041 static byte[] ACChrominanceTable = {
7042 (byte)255, (byte)196, 0, (byte)181, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0,
7043 1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34,
7044 50, (byte)129, 8, 20, 66, (byte)145, (byte)161, (byte)177, (byte)193, 9, 35,
7045 51, 82, (byte)240, 21, 98, 114, (byte)209, 10, 22, 36, 52, (byte)225, 37,
7046 (byte)241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67,
7047 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102,
7048 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, (byte)130,
7049 (byte)131, (byte)132, (byte)133, (byte)134, (byte)135, (byte)136, (byte)137,
7050 (byte)138, (byte)146, (byte)147, (byte)148, (byte)149, (byte)150, (byte)151,
7051 (byte)152, (byte)153, (byte)154, (byte)162, (byte)163, (byte)164, (byte)165,
7052 (byte)166, (byte)167, (byte)168, (byte)169, (byte)170, (byte)178, (byte)179,
7053 (byte)180, (byte)181, (byte)182, (byte)183, (byte)184, (byte)185, (byte)186,
7054 (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199, (byte)200,
7055 (byte)201, (byte)202, (byte)210, (byte)211, (byte)212, (byte)213, (byte)214,
7056 (byte)215, (byte)216, (byte)217, (byte)218, (byte)226, (byte)227, (byte)228,
7057 (byte)229, (byte)230, (byte)231, (byte)232, (byte)233, (byte)234, (byte)242,
7058 (byte)243, (byte)244, (byte)245, (byte)246, (byte)247, (byte)248, (byte)249,
7062 public JPEGHuffmanTable(byte[] reference) {
7066 public JPEGHuffmanTable(LEDataInputStream byteStream) {
7071 public JPEGHuffmanTable[] getAllTables() {
7075 public static JPEGHuffmanTable getDefaultACChrominanceTable() {
7076 JPEGHuffmanTable result = new JPEGHuffmanTable(ACChrominanceTable);
7077 result.initialize();
7081 public static JPEGHuffmanTable getDefaultACLuminanceTable() {
7082 JPEGHuffmanTable result = new JPEGHuffmanTable(ACLuminanceTable);
7083 result.initialize();
7087 public static JPEGHuffmanTable getDefaultDCChrominanceTable() {
7088 JPEGHuffmanTable result = new JPEGHuffmanTable(DCChrominanceTable);
7089 result.initialize();
7093 public static JPEGHuffmanTable getDefaultDCLuminanceTable() {
7094 JPEGHuffmanTable result = new JPEGHuffmanTable(DCLuminanceTable);
7095 result.initialize();
7099 public int[] getDhMaxCodes() {
7103 public int[] getDhMinCodes() {
7107 public int[] getDhValPtrs() {
7111 public int[] getDhValues() {
7115 public int getTableClass() {
7119 public int getTableIdentifier() {
7120 return tableIdentifier;
7124 int totalLength = getSegmentLength() - 2;
7126 int[] bits = new int[16];
7127 JPEGHuffmanTable[] huffTables = new JPEGHuffmanTable[8]; // maximum is 4 AC + 4 DC
7128 int huffTableCount = 0;
7129 while (totalLength > 0) {
7130 int tc = (reference[ofs] & 0xFF) / 16; // table class: AC (1) or DC (0)
7131 int tid = (reference[ofs] & 0xFF) % 16; // table id: 0-1 baseline, 0-3 prog/ext
7134 /* Read the 16 count bytes and add them together to get the table size. */
7136 for (int i = 0; i < bits.length; i++) {
7137 int bCount = reference[ofs + i] & 0xFF;
7144 /* Read the table. */
7145 int[] huffVals = new int[count];
7146 for (int i = 0; i < count; i++) {
7147 huffVals[i] = reference[ofs + i] & 0xFF;
7150 totalLength -= count;
7152 /* Calculate the lengths. */
7153 int[] huffCodeLengths = new int[50]; // start with 50 and increment as needed
7154 int huffCodeLengthsIndex = 0;
7155 for (int i = 0; i < 16; i++) {
7156 for (int j = 0; j < bits[i]; j++) {
7157 if (huffCodeLengthsIndex >= huffCodeLengths.length) {
7158 int[] newHuffCodeLengths = new int[huffCodeLengths.length + 50];
7159 System.arraycopy(huffCodeLengths, 0, newHuffCodeLengths, 0, huffCodeLengths.length);
7160 huffCodeLengths = newHuffCodeLengths;
7162 huffCodeLengths[huffCodeLengthsIndex] = i + 1;
7163 huffCodeLengthsIndex++;
7167 /* Truncate huffCodeLengths to the correct size. */
7168 if (huffCodeLengthsIndex < huffCodeLengths.length) {
7169 int[] newHuffCodeLengths = new int[huffCodeLengthsIndex];
7170 System.arraycopy(huffCodeLengths, 0, newHuffCodeLengths, 0, huffCodeLengthsIndex);
7171 huffCodeLengths = newHuffCodeLengths;
7174 /* Calculate the Huffman codes. */
7175 int[] huffCodes = new int[50]; // start with 50 and increment as needed
7176 int huffCodesIndex = 0;
7179 int si = huffCodeLengths[0];
7181 while (p < huffCodeLengthsIndex) {
7182 while ((p < huffCodeLengthsIndex) && (huffCodeLengths[p] == si)) {
7183 if (huffCodesIndex >= huffCodes.length) {
7184 int[] newHuffCodes = new int[huffCodes.length + 50];
7185 System.arraycopy(huffCodes, 0, newHuffCodes, 0, huffCodes.length);
7186 huffCodes = newHuffCodes;
7188 huffCodes[huffCodesIndex] = code;
7197 /* Truncate huffCodes to the correct size. */
7198 if (huffCodesIndex < huffCodes.length) {
7199 int[] newHuffCodes = new int[huffCodesIndex];
7200 System.arraycopy(huffCodes, 0, newHuffCodes, 0, huffCodesIndex);
7201 huffCodes = newHuffCodes;
7204 /* Calculate the maximum and minimum codes */
7206 int[] maxCodes = new int[16];
7207 int[] minCodes = new int[16];
7208 int[] valPtrs = new int[16];
7209 for (int i = 0; i < 16; i++) {
7210 int bSize = bits[i];
7215 minCodes[i] = huffCodes[k];
7217 maxCodes[i] = huffCodes[k - 1];
7221 /* Calculate the eHuffman codes and lengths. */
7222 int[] eHuffCodes = new int[256];
7223 byte[] eHuffSize = new byte[256];
7224 for (int i = 0; i < huffCodesIndex; i++) {
7225 eHuffCodes[huffVals[i]] = huffCodes[i];
7226 eHuffSize[huffVals[i]] = (byte)huffCodeLengths[i];
7229 /* Create the new JPEGHuffmanTable and add it to the allTables array. */
7230 JPEGHuffmanTable dhtTable = new JPEGHuffmanTable(reference);
7231 dhtTable.tableClass = tc;
7232 dhtTable.tableIdentifier = tid;
7233 dhtTable.dhValues = huffVals;
7234 dhtTable.dhCodes = huffCodes;
7235 dhtTable.dhCodeLengths = huffCodeLengths;
7236 dhtTable.dhMinCodes = minCodes;
7237 dhtTable.dhMaxCodes = maxCodes;
7238 dhtTable.dhValPtrs = valPtrs;
7239 dhtTable.ehCodes = eHuffCodes;
7240 dhtTable.ehCodeLengths = eHuffSize;
7241 huffTables[huffTableCount] = dhtTable;
7244 allTables = new JPEGHuffmanTable[huffTableCount];
7245 System.arraycopy(huffTables, 0, allTables, 0, huffTableCount);
7248 public int signature() {
7249 return JPEGFileFormat.DHT;
7252 /*******************************************************************************
7253 * Copyright (c) 2000, 2003 IBM Corporation and others.
7254 * All rights reserved. This program and the accompanying materials
7255 * are made available under the terms of the Common Public License v1.0
7256 * which accompanies this distribution, and is available at
7257 * http://www.eclipse.org/legal/cpl-v10.html
7260 * IBM Corporation - initial API and implementation
7261 *******************************************************************************/
7264 final static class JPEGQuantizationTable extends JPEGVariableSizeSegment {
7265 public static byte[] DefaultLuminanceQTable = {
7266 (byte)255, (byte)219, 0, 67, 0,
7267 16, 11, 10, 16, 24, 40, 51, 61,
7268 12, 12, 14, 19, 26, 58, 60, 55,
7269 14, 13, 16, 24, 40, 57, 69, 56,
7270 14, 17, 22, 29, 51, 87, 80, 62,
7271 18, 22, 37, 56, 68, 109, 103, 77,
7272 24, 35, 55, 64, 81, 104, 113, 92,
7273 49, 64, 78, 87, 103, 121, 120, 101,
7274 72, 92, 95, 98, 112, 100, 103, 99
7276 public static byte[] DefaultChrominanceQTable = {
7277 (byte)255, (byte)219, 0, 67, 1,
7278 17, 18, 24, 47, 99, 99, 99, 99,
7279 18, 21, 26, 66, 99, 99, 99, 99,
7280 24, 26, 56, 99, 99, 99, 99, 99,
7281 47, 66, 99, 99, 99, 99, 99, 99,
7282 99, 99, 99, 99, 99, 99, 99, 99,
7283 99, 99, 99, 99, 99, 99, 99, 99,
7284 99, 99, 99, 99, 99, 99, 99, 99,
7285 99, 99, 99, 99, 99, 99, 99, 99
7288 public JPEGQuantizationTable(byte[] reference) {
7292 public JPEGQuantizationTable(LEDataInputStream byteStream) {
7296 public static JPEGQuantizationTable defaultChrominanceTable() {
7297 byte[] data = new byte[DefaultChrominanceQTable.length];
7298 System.arraycopy(DefaultChrominanceQTable, 0, data, 0, data.length);
7299 return new JPEGQuantizationTable(data);
7302 public static JPEGQuantizationTable defaultLuminanceTable() {
7303 byte[] data = new byte[DefaultLuminanceQTable.length];
7304 System.arraycopy(DefaultLuminanceQTable, 0, data, 0, data.length);
7305 return new JPEGQuantizationTable(data);
7308 public int[] getQuantizationTablesKeys() {
7309 int[] keys = new int[4];
7311 int totalLength = getSegmentLength() - 2;
7313 while (totalLength > 64) {
7314 int tq = (reference[ofs] & 0xFF) % 16;
7315 int pq = (reference[ofs] & 0xFF) / 16;
7323 if (keysIndex >= keys.length) {
7324 int[] newKeys = new int[keys.length + 4];
7325 System.arraycopy(keys, 0, newKeys, 0, keys.length);
7328 keys[keysIndex] = tq;
7331 int[] newKeys = new int[keysIndex];
7332 System.arraycopy(keys, 0, newKeys, 0, keysIndex);
7336 public int[][] getQuantizationTablesValues() {
7337 int[][] values = new int[4][];
7338 int valuesIndex = 0;
7339 int totalLength = getSegmentLength() - 2;
7341 while (totalLength > 64) {
7342 int[] qk = new int[64];
7343 int pq = (reference[ofs] & 0xFF) / 16;
7345 for (int i = 0; i < qk.length; i++) {
7346 qk[i] = reference[ofs + i + 1];
7351 for (int i = 0; i < qk.length; i++) {
7352 int idx = (i - 1) * 2 ;
7353 qk[i] = (reference[ofs + idx + 1] & 0xFF) * 256 + (reference[ofs + idx + 2] & 0xFF);
7358 if (valuesIndex >= values.length) {
7359 int[][] newValues = new int[values.length + 4][];
7360 System.arraycopy(values, 0, newValues, 0, values.length);
7363 values[valuesIndex] = qk;
7366 int[][] newValues = new int[valuesIndex][];
7367 System.arraycopy(values, 0, newValues, 0, valuesIndex);
7371 public void scaleBy(int qualityFactor) {
7372 int qFactor = qualityFactor;
7376 if (qFactor > 100) {
7380 qFactor = 5000 / qFactor;
7382 qFactor = 200 - (qFactor * 2);
7384 int totalLength = getSegmentLength() - 2;
7386 while (totalLength > 64) {
7387 // int tq = (reference[ofs] & 0xFF) % 16;
7388 int pq = (reference[ofs] & 0xFF) / 16;
7390 for (int i = ofs + 1; i <= ofs + 64; i++) {
7391 int temp = ((reference[i] & 0xFF) * qFactor + 50) / 100;
7392 if (temp <= 0) temp = 1;
7393 if (temp > 255) temp = 255;
7394 reference[i] = (byte)temp;
7399 for (int i = ofs + 1; i <= ofs + 128; i += 2) {
7400 int temp = (((reference[i] & 0xFF) * 256 + (reference[i + 1] & 0xFF)) * qFactor + 50) / 100;
7401 if (temp <= 0) temp = 1;
7402 if (temp > 32767) temp = 32767;
7403 reference[i] = (byte)(temp / 256);
7404 reference[i + 1] = (byte)(temp % 256);
7412 public int signature() {
7413 return JPEGFileFormat.DQT;
7416 /*******************************************************************************
7417 * Copyright (c) 2000, 2003 IBM Corporation and others.
7418 * All rights reserved. This program and the accompanying materials
7419 * are made available under the terms of the Common Public License v1.0
7420 * which accompanies this distribution, and is available at
7421 * http://www.eclipse.org/legal/cpl-v10.html
7424 * IBM Corporation - initial API and implementation
7425 *******************************************************************************/
7428 final static class JPEGRestartInterval extends JPEGFixedSizeSegment {
7430 public JPEGRestartInterval(LEDataInputStream byteStream) {
7434 public int signature() {
7435 return JPEGFileFormat.DRI;
7438 public int getRestartInterval() {
7439 return ((reference[4] & 0xFF) << 8 | (reference[5] & 0xFF));
7442 public int fixedSize() {
7446 /*******************************************************************************
7447 * Copyright (c) 2000, 2004 IBM Corporation and others.
7448 * All rights reserved. This program and the accompanying materials
7449 * are made available under the terms of the Common Public License v1.0
7450 * which accompanies this distribution, and is available at
7451 * http://www.eclipse.org/legal/cpl-v10.html
7454 * IBM Corporation - initial API and implementation
7455 *******************************************************************************/
7459 final static class JPEGScanHeader extends JPEGVariableSizeSegment {
7460 public int[][] componentParameters;
7462 public JPEGScanHeader(byte[] reference) {
7466 public JPEGScanHeader(LEDataInputStream byteStream) {
7468 initializeComponentParameters();
7471 public int getApproxBitPositionHigh() {
7472 return (reference[(2 * getNumberOfImageComponents()) + 7] & 0xFF) / 16;
7475 public int getApproxBitPositionLow() {
7476 return (reference[(2 * getNumberOfImageComponents()) + 7] & 0xFF) % 16;
7479 public int getEndOfSpectralSelection() {
7480 return (reference[(2 * getNumberOfImageComponents()) + 6] & 0xFF);
7483 public int getNumberOfImageComponents() {
7484 return (reference[4] & 0xFF);
7487 public int getStartOfSpectralSelection() {
7488 return (reference[(2 * getNumberOfImageComponents()) + 5] & 0xFF);
7491 /* Used when decoding. */
7492 void initializeComponentParameters() {
7493 int compCount = getNumberOfImageComponents();
7494 componentParameters = new int[0][];
7495 for (int i = 0; i < compCount; i++) {
7496 int ofs = 5 + i * 2;
7497 int cid = reference[ofs] & 0xFF;
7498 int dc = (reference[ofs + 1] & 0xFF) / 16;
7499 int ac = (reference[ofs + 1] & 0xFF) % 16;
7500 if (componentParameters.length <= cid) {
7501 int[][] newParams = new int[cid + 1][];
7502 System.arraycopy(componentParameters, 0, newParams, 0, componentParameters.length);
7503 componentParameters = newParams;
7505 componentParameters[cid] = new int[] { dc, ac };
7509 /* Used when encoding. */
7510 public void initializeContents() {
7511 int compCount = getNumberOfImageComponents();
7512 int[][] compSpecParams = componentParameters;
7513 if (compCount == 0 || compCount != compSpecParams.length) {
7514 SWT.error(SWT.ERROR_INVALID_IMAGE);
7516 for (int i = 0; i < compCount; i++) {
7517 int ofs = i * 2 + 5;
7518 int[] compParams = compSpecParams[i];
7519 reference[ofs] = (byte)(i + 1);
7520 reference[ofs + 1] = (byte)(compParams[0] * 16 + compParams[1]);
7524 public void setEndOfSpectralSelection(int anInteger) {
7525 reference[(2 * getNumberOfImageComponents()) + 6] = (byte)anInteger;
7528 public void setNumberOfImageComponents(int anInteger) {
7529 reference[4] = (byte)(anInteger & 0xFF);
7532 public void setStartOfSpectralSelection(int anInteger) {
7533 reference[(2 * getNumberOfImageComponents()) + 5] = (byte)anInteger;
7536 public int signature() {
7537 return JPEGFileFormat.SOS;
7540 public boolean verifyProgressiveScan() {
7541 int start = getStartOfSpectralSelection();
7542 int end = getEndOfSpectralSelection();
7543 int low = getApproxBitPositionLow();
7544 int high = getApproxBitPositionHigh();
7545 int count = getNumberOfImageComponents();
7546 if ((start == 0 && end == 00) || (start <= end && end <= 63)) {
7547 if (low <= 13 && high <= 13 && (high == 0 || high == low + 1)) {
7548 return start == 0 || (start > 0 && count == 1);
7554 public boolean isACProgressiveScan() {
7555 return getStartOfSpectralSelection() != 0 && getEndOfSpectralSelection() != 0;
7558 public boolean isDCProgressiveScan() {
7559 return getStartOfSpectralSelection() == 0 && getEndOfSpectralSelection() == 0;
7562 public boolean isFirstScan() {
7563 return getApproxBitPositionHigh() == 0;
7567 /*******************************************************************************
7568 * Copyright (c) 2000, 2003 IBM Corporation and others.
7569 * All rights reserved. This program and the accompanying materials
7570 * are made available under the terms of the Common Public License v1.0
7571 * which accompanies this distribution, and is available at
7572 * http://www.eclipse.org/legal/cpl-v10.html
7575 * IBM Corporation - initial API and implementation
7576 *******************************************************************************/
7579 static class JPEGSegment {
7580 public byte[] reference;
7585 public JPEGSegment(byte[] reference) {
7586 this.reference = reference;
7589 public int signature() {
7593 public boolean verify() {
7594 return getSegmentMarker() == signature();
7597 public int getSegmentMarker() {
7598 return ((reference[0] & 0xFF) << 8 | (reference[1] & 0xFF));
7601 public void setSegmentMarker(int marker) {
7602 reference[0] = (byte)((marker & 0xFF00) >> 8);
7603 reference[1] = (byte)(marker & 0xFF);
7606 public int getSegmentLength() {
7607 return ((reference[2] & 0xFF) << 8 | (reference[3] & 0xFF));
7610 public void setSegmentLength(int length) {
7611 reference[2] = (byte)((length & 0xFF00) >> 8);
7612 reference[3] = (byte)(length & 0xFF);
7615 public boolean writeToStream(LEDataOutputStream byteStream) {
7617 byteStream.write(reference);
7619 } catch (Exception e) {
7624 /*******************************************************************************
7625 * Copyright (c) 2000, 2003 IBM Corporation and others.
7626 * All rights reserved. This program and the accompanying materials
7627 * are made available under the terms of the Common Public License v1.0
7628 * which accompanies this distribution, and is available at
7629 * http://www.eclipse.org/legal/cpl-v10.html
7632 * IBM Corporation - initial API and implementation
7633 *******************************************************************************/
7636 final static class JPEGStartOfImage extends JPEGFixedSizeSegment {
7638 public JPEGStartOfImage() {
7642 public JPEGStartOfImage(byte[] reference) {
7646 public JPEGStartOfImage(LEDataInputStream byteStream) {
7650 public int signature() {
7651 return JPEGFileFormat.SOI;
7654 public int fixedSize() {
7658 /*******************************************************************************
7659 * Copyright (c) 2000, 2003 IBM Corporation and others.
7660 * All rights reserved. This program and the accompanying materials
7661 * are made available under the terms of the Common Public License v1.0
7662 * which accompanies this distribution, and is available at
7663 * http://www.eclipse.org/legal/cpl-v10.html
7666 * IBM Corporation - initial API and implementation
7667 *******************************************************************************/
7671 abstract static class JPEGVariableSizeSegment extends JPEGSegment {
7673 public JPEGVariableSizeSegment(byte[] reference) {
7677 public JPEGVariableSizeSegment(LEDataInputStream byteStream) {
7679 byte[] header = new byte[4];
7680 byteStream.read(header);
7681 reference = header; // to use getSegmentLength()
7682 byte[] contents = new byte[getSegmentLength() + 2];
7683 contents[0] = header[0];
7684 contents[1] = header[1];
7685 contents[2] = header[2];
7686 contents[3] = header[3];
7687 byteStream.read(contents, 4, contents.length - 4);
7688 reference = contents;
7689 } catch (Exception e) {
7690 SWT.error(SWT.ERROR_IO, e);