comparison dwt/graphics/ImageData.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children ab8b5765e3d1
comparison
equal deleted inserted replaced
-1:000000000000 0:380af2bdd8e5
1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 module dwt.graphics.ImageData;
12
13 import dwt.dwthelper.utils;
14
15
16 import java.io.InputStream;
17
18 import dwt.DWT;
19 import dwt.DWTException;
20 import dwt.internal.CloneableCompatibility;
21
22 /**
23 * Instances of this class are device-independent descriptions
24 * of images. They are typically used as an intermediate format
25 * between loading from or writing to streams and creating an
26 * <code>Image</code>.
27 * <p>
28 * Note that the public fields <code>x</code>, <code>y</code>,
29 * <code>disposalMethod</code> and <code>delayTime</code> are
30 * typically only used when the image is in a set of images used
31 * for animation.
32 * </p>
33 *
34 * @see Image
35 * @see ImageLoader
36 */
37
38 public final class ImageData implements CloneableCompatibility {
39
40 /**
41 * The width of the image, in pixels.
42 */
43 public int width;
44
45 /**
46 * The height of the image, in pixels.
47 */
48 public int height;
49
50 /**
51 * The color depth of the image, in bits per pixel.
52 * <p>
53 * Note that a depth of 8 or less does not necessarily
54 * mean that the image is palette indexed, or
55 * conversely that a depth greater than 8 means that
56 * the image is direct color. Check the associated
57 * PaletteData's isDirect field for such determinations.
58 */
59 public int depth;
60
61 /**
62 * The scanline padding.
63 * <p>
64 * If one scanline of the image is not a multiple of
65 * this number, it will be padded with zeros until it is.
66 * </p>
67 */
68 public int scanlinePad;
69
70 /**
71 * The number of bytes per scanline.
72 * <p>
73 * This is a multiple of the scanline padding.
74 * </p>
75 */
76 public int bytesPerLine;
77
78 /**
79 * The pixel data of the image.
80 * <p>
81 * Note that for 16 bit depth images the pixel data is stored
82 * in least significant byte order; however, for 24bit and
83 * 32bit depth images the pixel data is stored in most
84 * significant byte order.
85 * </p>
86 */
87 public byte[] data;
88
89 /**
90 * The color table for the image.
91 */
92 public PaletteData palette;
93
94 /**
95 * The transparent pixel.
96 * <p>
97 * Pixels with this value are transparent.
98 * </p><p>
99 * The default is -1 which means 'no transparent pixel'.
100 * </p>
101 */
102 public int transparentPixel;
103
104 /**
105 * An icon-specific field containing the data from the icon mask.
106 * <p>
107 * This is a 1 bit bitmap stored with the most significant
108 * bit first. The number of bytes per scanline is
109 * '((width + 7) / 8 + (maskPad - 1)) / maskPad * maskPad'.
110 * </p><p>
111 * The default is null which means 'no transparency mask'.
112 * </p>
113 */
114 public byte[] maskData;
115
116 /**
117 * An icon-specific field containing the scanline pad of the mask.
118 * <p>
119 * If one scanline of the transparency mask is not a
120 * multiple of this number, it will be padded with zeros until
121 * it is.
122 * </p>
123 */
124 public int maskPad;
125
126 /**
127 * The alpha data of the image.
128 * <p>
129 * Every pixel can have an <em>alpha blending</em> value that
130 * varies from 0, meaning fully transparent, to 255 meaning
131 * fully opaque. The number of bytes per scanline is
132 * 'width'.
133 * </p>
134 */
135 public byte[] alphaData;
136
137 /**
138 * The global alpha value to be used for every pixel.
139 * <p>
140 * If this value is set, the <code>alphaData</code> field
141 * is ignored and when the image is rendered each pixel
142 * will be blended with the background an amount
143 * proportional to this value.
144 * </p><p>
145 * The default is -1 which means 'no global alpha value'
146 * </p>
147 */
148 public int alpha;
149
150 /**
151 * The type of file from which the image was read.
152 *
153 * It is expressed as one of the following values:
154 * <dl>
155 * <dt><code>IMAGE_BMP</code></dt>
156 * <dd>Windows BMP file format, no compression</dd>
157 * <dt><code>IMAGE_BMP_RLE</code></dt>
158 * <dd>Windows BMP file format, RLE compression if appropriate</dd>
159 * <dt><code>IMAGE_GIF</code></dt>
160 * <dd>GIF file format</dd>
161 * <dt><code>IMAGE_ICO</code></dt>
162 * <dd>Windows ICO file format</dd>
163 * <dt><code>IMAGE_JPEG</code></dt>
164 * <dd>JPEG file format</dd>
165 * <dt><code>IMAGE_PNG</code></dt>
166 * <dd>PNG file format</dd>
167 * </dl>
168 */
169 public int type;
170
171 /**
172 * The x coordinate of the top left corner of the image
173 * within the logical screen (this field corresponds to
174 * the GIF89a Image Left Position value).
175 */
176 public int x;
177
178 /**
179 * The y coordinate of the top left corner of the image
180 * within the logical screen (this field corresponds to
181 * the GIF89a Image Top Position value).
182 */
183 public int y;
184
185 /**
186 * A description of how to dispose of the current image
187 * before displaying the next.
188 *
189 * It is expressed as one of the following values:
190 * <dl>
191 * <dt><code>DM_UNSPECIFIED</code></dt>
192 * <dd>disposal method not specified</dd>
193 * <dt><code>DM_FILL_NONE</code></dt>
194 * <dd>do nothing - leave the image in place</dd>
195 * <dt><code>DM_FILL_BACKGROUND</code></dt>
196 * <dd>fill with the background color</dd>
197 * <dt><code>DM_FILL_PREVIOUS</code></dt>
198 * <dd>restore the previous picture</dd>
199 * </dl>
200 * (this field corresponds to the GIF89a Disposal Method value)
201 */
202 public int disposalMethod;
203
204 /**
205 * The time to delay before displaying the next image
206 * in an animation (this field corresponds to the GIF89a
207 * Delay Time value).
208 */
209 public int delayTime;
210
211 /**
212 * Arbitrary channel width data to 8-bit conversion table.
213 */
214 static final byte[][] ANY_TO_EIGHT = new byte[9][];
215 static {
216 for (int b = 0; b < 9; ++b) {
217 byte[] data = ANY_TO_EIGHT[b] = new byte[1 << b];
218 if (b is 0) continue;
219 int inc = 0;
220 for (int bit = 0x10000; (bit >>= b) !is 0;) inc |= bit;
221 for (int v = 0, p = 0; v < 0x10000; v+= inc) data[p++] = (byte)(v >> 8);
222 }
223 }
224 static final byte[] ONE_TO_ONE_MAPPING = ANY_TO_EIGHT[8];
225
226 /**
227 * Scaled 8x8 Bayer dither matrix.
228 */
229 static final int[][] DITHER_MATRIX = {
230 { 0xfc0000, 0x7c0000, 0xdc0000, 0x5c0000, 0xf40000, 0x740000, 0xd40000, 0x540000 },
231 { 0x3c0000, 0xbc0000, 0x1c0000, 0x9c0000, 0x340000, 0xb40000, 0x140000, 0x940000 },
232 { 0xcc0000, 0x4c0000, 0xec0000, 0x6c0000, 0xc40000, 0x440000, 0xe40000, 0x640000 },
233 { 0x0c0000, 0x8c0000, 0x2c0000, 0xac0000, 0x040000, 0x840000, 0x240000, 0xa40000 },
234 { 0xf00000, 0x700000, 0xd00000, 0x500000, 0xf80000, 0x780000, 0xd80000, 0x580000 },
235 { 0x300000, 0xb00000, 0x100000, 0x900000, 0x380000, 0xb80000, 0x180000, 0x980000 },
236 { 0xc00000, 0x400000, 0xe00000, 0x600000, 0xc80000, 0x480000, 0xe80000, 0x680000 },
237 { 0x000000, 0x800000, 0x200000, 0xa00000, 0x080000, 0x880000, 0x280000, 0xa80000 }
238 };
239
240 /**
241 * Constructs a new, empty ImageData with the given width, height,
242 * depth and palette. The data will be initialized to an (all zero)
243 * array of the appropriate size.
244 *
245 * @param width the width of the image
246 * @param height the height of the image
247 * @param depth the depth of the image
248 * @param palette the palette of the image (must not be null)
249 *
250 * @exception IllegalArgumentException <ul>
251 * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
252 * one of 1, 2, 4, 8, 16, 24 or 32</li>
253 * <li>ERROR_NULL_ARGUMENT - if the palette is null</li>
254 * </ul>
255 */
256 public ImageData(int width, int height, int depth, PaletteData palette) {
257 this(width, height, depth, palette,
258 4, null, 0, null,
259 null, -1, -1, DWT.IMAGE_UNDEFINED,
260 0, 0, 0, 0);
261 }
262
263 /**
264 * Constructs a new, empty ImageData with the given width, height,
265 * depth, palette, scanlinePad and data.
266 *
267 * @param width the width of the image
268 * @param height the height of the image
269 * @param depth the depth of the image
270 * @param palette the palette of the image
271 * @param scanlinePad the padding of each line, in bytes
272 * @param data the data of the image
273 *
274 * @exception IllegalArgumentException <ul>
275 * <li>ERROR_INVALID_ARGUMENT - if the width or height is negative, or if the depth is not
276 * one of 1, 2, 4, 8, 16, 24 or 32, or the data array is too small to contain the image data</li>
277 * <li>ERROR_NULL_ARGUMENT - if the palette or data is null</li>
278 * <li>ERROR_CANNOT_BE_ZERO - if the scanlinePad is zero</li>
279 * </ul>
280 */
281 public ImageData(int width, int height, int depth, PaletteData palette, int scanlinePad, byte[] data) {
282 this(width, height, depth, palette,
283 scanlinePad, checkData(data), 0, null,
284 null, -1, -1, DWT.IMAGE_UNDEFINED,
285 0, 0, 0, 0);
286 }
287
288 /**
289 * Constructs an <code>ImageData</code> loaded from the specified
290 * input stream. Throws an error if an error occurs while loading
291 * the image, or if the image has an unsupported type. Application
292 * code is still responsible for closing the input stream.
293 * <p>
294 * This constructor is provided for convenience when loading a single
295 * image only. If the stream contains multiple images, only the first
296 * one will be loaded. To load multiple images, use
297 * <code>ImageLoader.load()</code>.
298 * </p><p>
299 * This constructor may be used to load a resource as follows:
300 * </p>
301 * <pre>
302 * static ImageData loadImageData (Class clazz, String string) {
303 * InputStream stream = clazz.getResourceAsStream (string);
304 * if (stream is null) return null;
305 * ImageData imageData = null;
306 * try {
307 * imageData = new ImageData (stream);
308 * } catch (DWTException ex) {
309 * } finally {
310 * try {
311 * stream.close ();
312 * } catch (IOException ex) {}
313 * }
314 * return imageData;
315 * }
316 * </pre>
317 *
318 * @param stream the input stream to load the image from (must not be null)
319 *
320 * @exception IllegalArgumentException <ul>
321 * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
322 * </ul>
323 * @exception DWTException <ul>
324 * <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
325 * <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
326 * <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
327 * </ul>
328 *
329 * @see ImageLoader#load(InputStream)
330 */
331 public ImageData(InputStream stream) {
332 ImageData[] data = ImageDataLoader.load(stream);
333 if (data.length < 1) DWT.error(DWT.ERROR_INVALID_IMAGE);
334 ImageData i = data[0];
335 setAllFields(
336 i.width,
337 i.height,
338 i.depth,
339 i.scanlinePad,
340 i.bytesPerLine,
341 i.data,
342 i.palette,
343 i.transparentPixel,
344 i.maskData,
345 i.maskPad,
346 i.alphaData,
347 i.alpha,
348 i.type,
349 i.x,
350 i.y,
351 i.disposalMethod,
352 i.delayTime);
353 }
354
355 /**
356 * Constructs an <code>ImageData</code> loaded from a file with the
357 * specified name. Throws an error if an error occurs loading the
358 * image, or if the image has an unsupported type.
359 * <p>
360 * This constructor is provided for convenience when loading a single
361 * image only. If the file contains multiple images, only the first
362 * one will be loaded. To load multiple images, use
363 * <code>ImageLoader.load()</code>.
364 * </p>
365 *
366 * @param filename the name of the file to load the image from (must not be null)
367 *
368 * @exception IllegalArgumentException <ul>
369 * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
370 * </ul>
371 * @exception DWTException <ul>
372 * <li>ERROR_IO - if an IO error occurs while reading from the file</li>
373 * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
374 * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
375 * </ul>
376 */
377 public ImageData(String filename) {
378 ImageData[] data = ImageDataLoader.load(filename);
379 if (data.length < 1) DWT.error(DWT.ERROR_INVALID_IMAGE);
380 ImageData i = data[0];
381 setAllFields(
382 i.width,
383 i.height,
384 i.depth,
385 i.scanlinePad,
386 i.bytesPerLine,
387 i.data,
388 i.palette,
389 i.transparentPixel,
390 i.maskData,
391 i.maskPad,
392 i.alphaData,
393 i.alpha,
394 i.type,
395 i.x,
396 i.y,
397 i.disposalMethod,
398 i.delayTime);
399 }
400
401 /**
402 * Prevents uninitialized instances from being created outside the package.
403 */
404 ImageData() {
405 }
406
407 /**
408 * Constructs an image data by giving values for all non-computable fields.
409 * <p>
410 * This method is for internal use, and is not described further.
411 * </p>
412 */
413 ImageData(
414 int width, int height, int depth, PaletteData palette,
415 int scanlinePad, byte[] data, int maskPad, byte[] maskData,
416 byte[] alphaData, int alpha, int transparentPixel, int type,
417 int x, int y, int disposalMethod, int delayTime)
418 {
419
420 if (palette is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
421 if (!(depth is 1 || depth is 2 || depth is 4 || depth is 8
422 || depth is 16 || depth is 24 || depth is 32)) {
423 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
424 }
425 if (width <= 0 || height <= 0) {
426 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
427 }
428 if (scanlinePad is 0) DWT.error (DWT.ERROR_CANNOT_BE_ZERO);
429
430 int bytesPerLine = (((width * depth + 7) / 8) + (scanlinePad - 1))
431 / scanlinePad * scanlinePad;
432
433 /*
434 * When the image is being loaded from a PNG, we need to use the theoretical minimum
435 * number of bytes per line to check whether there is enough data, because the actual
436 * number of bytes per line is calculated based on the given depth, which may be larger
437 * than the actual depth of the PNG.
438 */
439 int minBytesPerLine = type is DWT.IMAGE_PNG ? ((((width + 7) / 8) + 3) / 4) * 4 : bytesPerLine;
440 if (data !is null && data.length < minBytesPerLine * height) {
441 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
442 }
443 setAllFields(
444 width,
445 height,
446 depth,
447 scanlinePad,
448 bytesPerLine,
449 data !is null ? data : new byte[bytesPerLine * height],
450 palette,
451 transparentPixel,
452 maskData,
453 maskPad,
454 alphaData,
455 alpha,
456 type,
457 x,
458 y,
459 disposalMethod,
460 delayTime);
461 }
462
463 /**
464 * Initializes all fields in the receiver. This method must be called
465 * by all public constructors to ensure that all fields are initialized
466 * for a new ImageData object. If a new field is added to the class,
467 * then it must be added to this method.
468 * <p>
469 * This method is for internal use, and is not described further.
470 * </p>
471 */
472 void setAllFields(int width, int height, int depth, int scanlinePad,
473 int bytesPerLine, byte[] data, PaletteData palette, int transparentPixel,
474 byte[] maskData, int maskPad, byte[] alphaData, int alpha,
475 int type, int x, int y, int disposalMethod, int delayTime) {
476
477 this.width = width;
478 this.height = height;
479 this.depth = depth;
480 this.scanlinePad = scanlinePad;
481 this.bytesPerLine = bytesPerLine;
482 this.data = data;
483 this.palette = palette;
484 this.transparentPixel = transparentPixel;
485 this.maskData = maskData;
486 this.maskPad = maskPad;
487 this.alphaData = alphaData;
488 this.alpha = alpha;
489 this.type = type;
490 this.x = x;
491 this.y = y;
492 this.disposalMethod = disposalMethod;
493 this.delayTime = delayTime;
494 }
495
496 /**
497 * Invokes internal DWT functionality to create a new instance of
498 * this class.
499 * <p>
500 * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
501 * API for <code>ImageData</code>. It is marked public only so that it
502 * can be shared within the packages provided by DWT. It is subject
503 * to change without notice, and should never be called from
504 * application code.
505 * </p>
506 * <p>
507 * This method is for internal use, and is not described further.
508 * </p>
509 */
510 public static ImageData internal_new(
511 int width, int height, int depth, PaletteData palette,
512 int scanlinePad, byte[] data, int maskPad, byte[] maskData,
513 byte[] alphaData, int alpha, int transparentPixel, int type,
514 int x, int y, int disposalMethod, int delayTime)
515 {
516 return new ImageData(
517 width, height, depth, palette, scanlinePad, data, maskPad, maskData,
518 alphaData, alpha, transparentPixel, type, x, y, disposalMethod, delayTime);
519 }
520
521 ImageData colorMaskImage(int pixel) {
522 ImageData mask = new ImageData(width, height, 1, bwPalette(),
523 2, null, 0, null, null, -1, -1, DWT.IMAGE_UNDEFINED,
524 0, 0, 0, 0);
525 int[] row = new int[width];
526 for (int y = 0; y < height; y++) {
527 getPixels(0, y, width, row, 0);
528 for (int i = 0; i < width; i++) {
529 if (pixel !is -1 && row[i] is pixel) {
530 row[i] = 0;
531 } else {
532 row[i] = 1;
533 }
534 }
535 mask.setPixels(0, y, width, row, 0);
536 }
537 return mask;
538 }
539
540 static byte[] checkData(byte [] data) {
541 if (data is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
542 return data;
543 }
544
545 /**
546 * Returns a new instance of the same class as the receiver,
547 * whose slots have been filled in with <em>copies</em> of
548 * the values in the slots of the receiver. That is, the
549 * returned object is a <em>deep copy</em> of the receiver.
550 *
551 * @return a copy of the receiver.
552 */
553 public Object clone() {
554 byte[] cloneData = new byte[data.length];
555 System.arraycopy(data, 0, cloneData, 0, data.length);
556 byte[] cloneMaskData = null;
557 if (maskData !is null) {
558 cloneMaskData = new byte[maskData.length];
559 System.arraycopy(maskData, 0, cloneMaskData, 0, maskData.length);
560 }
561 byte[] cloneAlphaData = null;
562 if (alphaData !is null) {
563 cloneAlphaData = new byte[alphaData.length];
564 System.arraycopy(alphaData, 0, cloneAlphaData, 0, alphaData.length);
565 }
566 return new ImageData(
567 width,
568 height,
569 depth,
570 palette,
571 scanlinePad,
572 cloneData,
573 maskPad,
574 cloneMaskData,
575 cloneAlphaData,
576 alpha,
577 transparentPixel,
578 type,
579 x,
580 y,
581 disposalMethod,
582 delayTime);
583 }
584
585 /**
586 * Returns the alpha value at offset <code>x</code> in
587 * scanline <code>y</code> in the receiver's alpha data.
588 *
589 * @param x the x coordinate of the pixel to get the alpha value of
590 * @param y the y coordinate of the pixel to get the alpha value of
591 * @return the alpha value at the given coordinates
592 *
593 * @exception IllegalArgumentException <ul>
594 * <li>ERROR_INVALID_ARGUMENT - if either argument is out of range</li>
595 * </ul>
596 */
597 public int getAlpha(int x, int y) {
598 if (x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
599
600 if (alphaData is null) return 255;
601 return alphaData[y * width + x] & 0xFF;
602 }
603
604 /**
605 * Returns <code>getWidth</code> alpha values starting at offset
606 * <code>x</code> in scanline <code>y</code> in the receiver's alpha
607 * data starting at <code>startIndex</code>.
608 *
609 * @param x the x position of the pixel to begin getting alpha values
610 * @param y the y position of the pixel to begin getting alpha values
611 * @param getWidth the width of the data to get
612 * @param alphas the buffer in which to put the alpha values
613 * @param startIndex the offset into the image to begin getting alpha values
614 *
615 * @exception IndexOutOfBoundsException if getWidth is too large
616 * @exception IllegalArgumentException <ul>
617 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
618 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
619 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
620 * </ul>
621 */
622 public void getAlphas(int x, int y, int getWidth, byte[] alphas, int startIndex) {
623 if (alphas is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
624 if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
625 if (getWidth is 0) return;
626
627 if (alphaData is null) {
628 int endIndex = startIndex + getWidth;
629 for (int i = startIndex; i < endIndex; i++) {
630 alphas[i] = (byte)255;
631 }
632 return;
633 }
634 // may throw an IndexOutOfBoundsException
635 System.arraycopy(alphaData, y * width + x, alphas, startIndex, getWidth);
636 }
637
638 /**
639 * Returns the pixel value at offset <code>x</code> in
640 * scanline <code>y</code> in the receiver's data.
641 *
642 * @param x the x position of the pixel to get
643 * @param y the y position of the pixel to get
644 * @return the pixel at the given coordinates
645 *
646 * @exception IllegalArgumentException <ul>
647 * <li>ERROR_INVALID_ARGUMENT - if either argument is out of bounds</li>
648 * </ul>
649 * @exception DWTException <ul>
650 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
651 * </ul>
652 */
653 public int getPixel(int x, int y) {
654 if (x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
655 int index;
656 int theByte;
657 int mask;
658 switch (depth) {
659 case 32:
660 index = (y * bytesPerLine) + (x * 4);
661 return ((data[index] & 0xFF) << 24) + ((data[index+1] & 0xFF) << 16) +
662 ((data[index+2] & 0xFF) << 8) + (data[index+3] & 0xFF);
663 case 24:
664 index = (y * bytesPerLine) + (x * 3);
665 return ((data[index] & 0xFF) << 16) + ((data[index+1] & 0xFF) << 8) +
666 (data[index+2] & 0xFF);
667 case 16:
668 index = (y * bytesPerLine) + (x * 2);
669 return ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
670 case 8:
671 index = (y * bytesPerLine) + x ;
672 return data[index] & 0xFF;
673 case 4:
674 index = (y * bytesPerLine) + (x >> 1);
675 theByte = data[index] & 0xFF;
676 if ((x & 0x1) is 0) {
677 return theByte >> 4;
678 } else {
679 return theByte & 0x0F;
680 }
681 case 2:
682 index = (y * bytesPerLine) + (x >> 2);
683 theByte = data[index] & 0xFF;
684 int offset = 3 - (x % 4);
685 mask = 3 << (offset * 2);
686 return (theByte & mask) >> (offset * 2);
687 case 1:
688 index = (y * bytesPerLine) + (x >> 3);
689 theByte = data[index] & 0xFF;
690 mask = 1 << (7 - (x & 0x7));
691 if ((theByte & mask) is 0) {
692 return 0;
693 } else {
694 return 1;
695 }
696 }
697 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
698 return 0;
699 }
700
701 /**
702 * Returns <code>getWidth</code> pixel values starting at offset
703 * <code>x</code> in scanline <code>y</code> in the receiver's
704 * data starting at <code>startIndex</code>.
705 *
706 * @param x the x position of the first pixel to get
707 * @param y the y position of the first pixel to get
708 * @param getWidth the width of the data to get
709 * @param pixels the buffer in which to put the pixels
710 * @param startIndex the offset into the byte array to begin storing pixels
711 *
712 * @exception IndexOutOfBoundsException if getWidth is too large
713 * @exception IllegalArgumentException <ul>
714 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
715 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
716 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
717 * </ul>
718 * @exception DWTException <ul>
719 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4 or 8
720 * (For higher depths, use the int[] version of this method.)</li>
721 * </ul>
722 */
723 public void getPixels(int x, int y, int getWidth, byte[] pixels, int startIndex) {
724 if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
725 if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
726 if (getWidth is 0) return;
727 int index;
728 int theByte;
729 int mask = 0;
730 int n = getWidth;
731 int i = startIndex;
732 int srcX = x, srcY = y;
733 switch (depth) {
734 case 8:
735 index = (y * bytesPerLine) + x;
736 for (int j = 0; j < getWidth; j++) {
737 pixels[i] = data[index];
738 i++;
739 srcX++;
740 if (srcX >= width) {
741 srcY++;
742 index = srcY * bytesPerLine;
743 srcX = 0;
744 } else {
745 index++;
746 }
747 }
748 return;
749 case 4:
750 index = (y * bytesPerLine) + (x >> 1);
751 if ((x & 0x1) is 1) {
752 theByte = data[index] & 0xFF;
753 pixels[i] = (byte)(theByte & 0x0F);
754 i++;
755 n--;
756 srcX++;
757 if (srcX >= width) {
758 srcY++;
759 index = srcY * bytesPerLine;
760 srcX = 0;
761 } else {
762 index++;
763 }
764 }
765 while (n > 1) {
766 theByte = data[index] & 0xFF;
767 pixels[i] = (byte)(theByte >> 4);
768 i++;
769 n--;
770 srcX++;
771 if (srcX >= width) {
772 srcY++;
773 index = srcY * bytesPerLine;
774 srcX = 0;
775 } else {
776 pixels[i] = (byte)(theByte & 0x0F);
777 i++;
778 n--;
779 srcX++;
780 if (srcX >= width) {
781 srcY++;
782 index = srcY * bytesPerLine;
783 srcX = 0;
784 } else {
785 index++;
786 }
787 }
788 }
789 if (n > 0) {
790 theByte = data[index] & 0xFF;
791 pixels[i] = (byte)(theByte >> 4);
792 }
793 return;
794 case 2:
795 index = (y * bytesPerLine) + (x >> 2);
796 theByte = data[index] & 0xFF;
797 int offset;
798 while (n > 0) {
799 offset = 3 - (srcX % 4);
800 mask = 3 << (offset * 2);
801 pixels[i] = (byte)((theByte & mask) >> (offset * 2));
802 i++;
803 n--;
804 srcX++;
805 if (srcX >= width) {
806 srcY++;
807 index = srcY * bytesPerLine;
808 if (n > 0) theByte = data[index] & 0xFF;
809 srcX = 0;
810 } else {
811 if (offset is 0) {
812 index++;
813 theByte = data[index] & 0xFF;
814 }
815 }
816 }
817 return;
818 case 1:
819 index = (y * bytesPerLine) + (x >> 3);
820 theByte = data[index] & 0xFF;
821 while (n > 0) {
822 mask = 1 << (7 - (srcX & 0x7));
823 if ((theByte & mask) is 0) {
824 pixels[i] = 0;
825 } else {
826 pixels[i] = 1;
827 }
828 i++;
829 n--;
830 srcX++;
831 if (srcX >= width) {
832 srcY++;
833 index = srcY * bytesPerLine;
834 if (n > 0) theByte = data[index] & 0xFF;
835 srcX = 0;
836 } else {
837 if (mask is 1) {
838 index++;
839 if (n > 0) theByte = data[index] & 0xFF;
840 }
841 }
842 }
843 return;
844 }
845 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
846 }
847
848 /**
849 * Returns <code>getWidth</code> pixel values starting at offset
850 * <code>x</code> in scanline <code>y</code> in the receiver's
851 * data starting at <code>startIndex</code>.
852 *
853 * @param x the x position of the first pixel to get
854 * @param y the y position of the first pixel to get
855 * @param getWidth the width of the data to get
856 * @param pixels the buffer in which to put the pixels
857 * @param startIndex the offset into the buffer to begin storing pixels
858 *
859 * @exception IndexOutOfBoundsException if getWidth is too large
860 * @exception IllegalArgumentException <ul>
861 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
862 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
863 * <li>ERROR_INVALID_ARGUMENT - if getWidth is negative</li>
864 * </ul>
865 * @exception DWTException <ul>
866 * <li>ERROR_UNSUPPORTED_DEPTH - if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
867 * </ul>
868 */
869 public void getPixels(int x, int y, int getWidth, int[] pixels, int startIndex) {
870 if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
871 if (getWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
872 if (getWidth is 0) return;
873 int index;
874 int theByte;
875 int mask;
876 int n = getWidth;
877 int i = startIndex;
878 int srcX = x, srcY = y;
879 switch (depth) {
880 case 32:
881 index = (y * bytesPerLine) + (x * 4);
882 i = startIndex;
883 for (int j = 0; j < getWidth; j++) {
884 pixels[i] = ((data[index] & 0xFF) << 24) | ((data[index+1] & 0xFF) << 16)
885 | ((data[index+2] & 0xFF) << 8) | (data[index+3] & 0xFF);
886 i++;
887 srcX++;
888 if (srcX >= width) {
889 srcY++;
890 index = srcY * bytesPerLine;
891 srcX = 0;
892 } else {
893 index += 4;
894 }
895 }
896 return;
897 case 24:
898 index = (y * bytesPerLine) + (x * 3);
899 for (int j = 0; j < getWidth; j++) {
900 pixels[i] = ((data[index] & 0xFF) << 16) | ((data[index+1] & 0xFF) << 8)
901 | (data[index+2] & 0xFF);
902 i++;
903 srcX++;
904 if (srcX >= width) {
905 srcY++;
906 index = srcY * bytesPerLine;
907 srcX = 0;
908 } else {
909 index += 3;
910 }
911 }
912 return;
913 case 16:
914 index = (y * bytesPerLine) + (x * 2);
915 for (int j = 0; j < getWidth; j++) {
916 pixels[i] = ((data[index+1] & 0xFF) << 8) + (data[index] & 0xFF);
917 i++;
918 srcX++;
919 if (srcX >= width) {
920 srcY++;
921 index = srcY * bytesPerLine;
922 srcX = 0;
923 } else {
924 index += 2;
925 }
926 }
927 return;
928 case 8:
929 index = (y * bytesPerLine) + x;
930 for (int j = 0; j < getWidth; j++) {
931 pixels[i] = data[index] & 0xFF;
932 i++;
933 srcX++;
934 if (srcX >= width) {
935 srcY++;
936 index = srcY * bytesPerLine;
937 srcX = 0;
938 } else {
939 index++;
940 }
941 }
942 return;
943 case 4:
944 index = (y * bytesPerLine) + (x >> 1);
945 if ((x & 0x1) is 1) {
946 theByte = data[index] & 0xFF;
947 pixels[i] = theByte & 0x0F;
948 i++;
949 n--;
950 srcX++;
951 if (srcX >= width) {
952 srcY++;
953 index = srcY * bytesPerLine;
954 srcX = 0;
955 } else {
956 index++;
957 }
958 }
959 while (n > 1) {
960 theByte = data[index] & 0xFF;
961 pixels[i] = theByte >> 4;
962 i++;
963 n--;
964 srcX++;
965 if (srcX >= width) {
966 srcY++;
967 index = srcY * bytesPerLine;
968 srcX = 0;
969 } else {
970 pixels[i] = theByte & 0x0F;
971 i++;
972 n--;
973 srcX++;
974 if (srcX >= width) {
975 srcY++;
976 index = srcY * bytesPerLine;
977 srcX = 0;
978 } else {
979 index++;
980 }
981 }
982 }
983 if (n > 0) {
984 theByte = data[index] & 0xFF;
985 pixels[i] = theByte >> 4;
986 }
987 return;
988 case 2:
989 index = (y * bytesPerLine) + (x >> 2);
990 theByte = data[index] & 0xFF;
991 int offset;
992 while (n > 0) {
993 offset = 3 - (srcX % 4);
994 mask = 3 << (offset * 2);
995 pixels[i] = (byte)((theByte & mask) >> (offset * 2));
996 i++;
997 n--;
998 srcX++;
999 if (srcX >= width) {
1000 srcY++;
1001 index = srcY * bytesPerLine;
1002 if (n > 0) theByte = data[index] & 0xFF;
1003 srcX = 0;
1004 } else {
1005 if (offset is 0) {
1006 index++;
1007 theByte = data[index] & 0xFF;
1008 }
1009 }
1010 }
1011 return;
1012 case 1:
1013 index = (y * bytesPerLine) + (x >> 3);
1014 theByte = data[index] & 0xFF;
1015 while (n > 0) {
1016 mask = 1 << (7 - (srcX & 0x7));
1017 if ((theByte & mask) is 0) {
1018 pixels[i] = 0;
1019 } else {
1020 pixels[i] = 1;
1021 }
1022 i++;
1023 n--;
1024 srcX++;
1025 if (srcX >= width) {
1026 srcY++;
1027 index = srcY * bytesPerLine;
1028 if (n > 0) theByte = data[index] & 0xFF;
1029 srcX = 0;
1030 } else {
1031 if (mask is 1) {
1032 index++;
1033 if (n > 0) theByte = data[index] & 0xFF;
1034 }
1035 }
1036 }
1037 return;
1038 }
1039 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
1040 }
1041
1042 /**
1043 * Returns an array of <code>RGB</code>s which comprise the
1044 * indexed color table of the receiver, or null if the receiver
1045 * has a direct color model.
1046 *
1047 * @return the RGB values for the image or null if direct color
1048 *
1049 * @see PaletteData#getRGBs()
1050 */
1051 public RGB[] getRGBs() {
1052 return palette.getRGBs();
1053 }
1054
1055 /**
1056 * Returns an <code>ImageData</code> which specifies the
1057 * transparency mask information for the receiver. If the
1058 * receiver has no transparency or is not an icon, returns
1059 * an opaque mask.
1060 *
1061 * @return the transparency mask
1062 */
1063 public ImageData getTransparencyMask() {
1064 if (getTransparencyType() is DWT.TRANSPARENCY_MASK) {
1065 return new ImageData(width, height, 1, bwPalette(), maskPad, maskData);
1066 } else {
1067 return colorMaskImage(transparentPixel);
1068 }
1069 }
1070
1071 /**
1072 * Returns the image transparency type, which will be one of
1073 * <code>DWT.TRANSPARENCY_NONE</code>, <code>DWT.TRANSPARENCY_MASK</code>,
1074 * <code>DWT.TRANSPARENCY_PIXEL</code> or <code>DWT.TRANSPARENCY_ALPHA</code>.
1075 *
1076 * @return the receiver's transparency type
1077 */
1078 public int getTransparencyType() {
1079 if (maskData !is null) return DWT.TRANSPARENCY_MASK;
1080 if (transparentPixel !is -1) return DWT.TRANSPARENCY_PIXEL;
1081 if (alphaData !is null) return DWT.TRANSPARENCY_ALPHA;
1082 return DWT.TRANSPARENCY_NONE;
1083 }
1084
1085 /**
1086 * Returns the byte order of the receiver.
1087 *
1088 * @return MSB_FIRST or LSB_FIRST
1089 */
1090 int getByteOrder() {
1091 return depth !is 16 ? MSB_FIRST : LSB_FIRST;
1092 }
1093
1094 /**
1095 * Returns a copy of the receiver which has been stretched or
1096 * shrunk to the specified size. If either the width or height
1097 * is negative, the resulting image will be inverted in the
1098 * associated axis.
1099 *
1100 * @param width the width of the new ImageData
1101 * @param height the height of the new ImageData
1102 * @return a scaled copy of the image
1103 */
1104 public ImageData scaledTo(int width, int height) {
1105 /* Create a destination image with no data */
1106 final bool flipX = (width < 0);
1107 if (flipX) width = - width;
1108 final bool flipY = (height < 0);
1109 if (flipY) height = - height;
1110
1111 ImageData dest = new ImageData(
1112 width, height, depth, palette,
1113 scanlinePad, null, 0, null,
1114 null, -1, transparentPixel, type,
1115 x, y, disposalMethod, delayTime);
1116
1117 /* Scale the image contents */
1118 if (palette.isDirect) blit(BLIT_SRC,
1119 this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, 0, 0, 0,
1120 ALPHA_OPAQUE, null, 0, 0, 0,
1121 dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, 0, 0, 0,
1122 flipX, flipY);
1123 else blit(BLIT_SRC,
1124 this.data, this.depth, this.bytesPerLine, this.getByteOrder(), 0, 0, this.width, this.height, null, null, null,
1125 ALPHA_OPAQUE, null, 0, 0, 0,
1126 dest.data, dest.depth, dest.bytesPerLine, dest.getByteOrder(), 0, 0, dest.width, dest.height, null, null, null,
1127 flipX, flipY);
1128
1129 /* Scale the image mask or alpha */
1130 if (maskData !is null) {
1131 dest.maskPad = this.maskPad;
1132 int destBpl = (dest.width + 7) / 8;
1133 destBpl = (destBpl + (dest.maskPad - 1)) / dest.maskPad * dest.maskPad;
1134 dest.maskData = new byte[destBpl * dest.height];
1135 int srcBpl = (this.width + 7) / 8;
1136 srcBpl = (srcBpl + (this.maskPad - 1)) / this.maskPad * this.maskPad;
1137 blit(BLIT_SRC,
1138 this.maskData, 1, srcBpl, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
1139 ALPHA_OPAQUE, null, 0, 0, 0,
1140 dest.maskData, 1, destBpl, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
1141 flipX, flipY);
1142 } else if (alpha !is -1) {
1143 dest.alpha = this.alpha;
1144 } else if (alphaData !is null) {
1145 dest.alphaData = new byte[dest.width * dest.height];
1146 blit(BLIT_SRC,
1147 this.alphaData, 8, this.width, MSB_FIRST, 0, 0, this.width, this.height, null, null, null,
1148 ALPHA_OPAQUE, null, 0, 0, 0,
1149 dest.alphaData, 8, dest.width, MSB_FIRST, 0, 0, dest.width, dest.height, null, null, null,
1150 flipX, flipY);
1151 }
1152 return dest;
1153 }
1154
1155 /**
1156 * Sets the alpha value at offset <code>x</code> in
1157 * scanline <code>y</code> in the receiver's alpha data.
1158 *
1159 * @param x the x coordinate of the alpha value to set
1160 * @param y the y coordinate of the alpha value to set
1161 * @param alpha the value to set the alpha to
1162 *
1163 * @exception IllegalArgumentException <ul>
1164 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1165 * </ul>
1166 */
1167 public void setAlpha(int x, int y, int alpha) {
1168 if (x >= width || y >= height || x < 0 || y < 0 || alpha < 0 || alpha > 255)
1169 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1170
1171 if (alphaData is null) alphaData = new byte[width * height];
1172 alphaData[y * width + x] = (byte)alpha;
1173 }
1174
1175 /**
1176 * Sets the alpha values starting at offset <code>x</code> in
1177 * scanline <code>y</code> in the receiver's alpha data to the
1178 * values from the array <code>alphas</code> starting at
1179 * <code>startIndex</code>.
1180 *
1181 * @param x the x coordinate of the pixel to being setting the alpha values
1182 * @param y the y coordinate of the pixel to being setting the alpha values
1183 * @param putWidth the width of the alpha values to set
1184 * @param alphas the alpha values to set
1185 * @param startIndex the index at which to begin setting
1186 *
1187 * @exception IndexOutOfBoundsException if putWidth is too large
1188 * @exception IllegalArgumentException <ul>
1189 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1190 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1191 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
1192 * </ul>
1193 */
1194 public void setAlphas(int x, int y, int putWidth, byte[] alphas, int startIndex) {
1195 if (alphas is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
1196 if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1197 if (putWidth is 0) return;
1198
1199 if (alphaData is null) alphaData = new byte[width * height];
1200 // may throw an IndexOutOfBoundsException
1201 System.arraycopy(alphas, startIndex, alphaData, y * width + x, putWidth);
1202 }
1203
1204 /**
1205 * Sets the pixel value at offset <code>x</code> in
1206 * scanline <code>y</code> in the receiver's data.
1207 *
1208 * @param x the x coordinate of the pixel to set
1209 * @param y the y coordinate of the pixel to set
1210 * @param pixelValue the value to set the pixel to
1211 *
1212 * @exception IllegalArgumentException <ul>
1213 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1214 * </ul>
1215 * @exception DWTException <ul>
1216 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
1217 * </ul>
1218 */
1219 public void setPixel(int x, int y, int pixelValue) {
1220 if (x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1221 int index;
1222 byte theByte;
1223 int mask;
1224 switch (depth) {
1225 case 32:
1226 index = (y * bytesPerLine) + (x * 4);
1227 data[index] = (byte)((pixelValue >> 24) & 0xFF);
1228 data[index + 1] = (byte)((pixelValue >> 16) & 0xFF);
1229 data[index + 2] = (byte)((pixelValue >> 8) & 0xFF);
1230 data[index + 3] = (byte)(pixelValue & 0xFF);
1231 return;
1232 case 24:
1233 index = (y * bytesPerLine) + (x * 3);
1234 data[index] = (byte)((pixelValue >> 16) & 0xFF);
1235 data[index + 1] = (byte)((pixelValue >> 8) & 0xFF);
1236 data[index + 2] = (byte)(pixelValue & 0xFF);
1237 return;
1238 case 16:
1239 index = (y * bytesPerLine) + (x * 2);
1240 data[index + 1] = (byte)((pixelValue >> 8) & 0xFF);
1241 data[index] = (byte)(pixelValue & 0xFF);
1242 return;
1243 case 8:
1244 index = (y * bytesPerLine) + x ;
1245 data[index] = (byte)(pixelValue & 0xFF);
1246 return;
1247 case 4:
1248 index = (y * bytesPerLine) + (x >> 1);
1249 if ((x & 0x1) is 0) {
1250 data[index] = (byte)((data[index] & 0x0F) | ((pixelValue & 0x0F) << 4));
1251 } else {
1252 data[index] = (byte)((data[index] & 0xF0) | (pixelValue & 0x0F));
1253 }
1254 return;
1255 case 2:
1256 index = (y * bytesPerLine) + (x >> 2);
1257 theByte = data[index];
1258 int offset = 3 - (x % 4);
1259 mask = 0xFF ^ (3 << (offset * 2));
1260 data[index] = (byte)((data[index] & mask) | (pixelValue << (offset * 2)));
1261 return;
1262 case 1:
1263 index = (y * bytesPerLine) + (x >> 3);
1264 theByte = data[index];
1265 mask = 1 << (7 - (x & 0x7));
1266 if ((pixelValue & 0x1) is 1) {
1267 data[index] = (byte)(theByte | mask);
1268 } else {
1269 data[index] = (byte)(theByte & (mask ^ -1));
1270 }
1271 return;
1272 }
1273 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
1274 }
1275
1276 /**
1277 * Sets the pixel values starting at offset <code>x</code> in
1278 * scanline <code>y</code> in the receiver's data to the
1279 * values from the array <code>pixels</code> starting at
1280 * <code>startIndex</code>.
1281 *
1282 * @param x the x position of the pixel to set
1283 * @param y the y position of the pixel to set
1284 * @param putWidth the width of the pixels to set
1285 * @param pixels the pixels to set
1286 * @param startIndex the index at which to begin setting
1287 *
1288 * @exception IndexOutOfBoundsException if putWidth is too large
1289 * @exception IllegalArgumentException <ul>
1290 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1291 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1292 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
1293 * </ul>
1294 * @exception DWTException <ul>
1295 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8
1296 * (For higher depths, use the int[] version of this method.)</li>
1297 * </ul>
1298 */
1299 public void setPixels(int x, int y, int putWidth, byte[] pixels, int startIndex) {
1300 if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
1301 if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1302 if (putWidth is 0) return;
1303 int index;
1304 int theByte;
1305 int mask;
1306 int n = putWidth;
1307 int i = startIndex;
1308 int srcX = x, srcY = y;
1309 switch (depth) {
1310 case 8:
1311 index = (y * bytesPerLine) + x;
1312 for (int j = 0; j < putWidth; j++) {
1313 data[index] = (byte)(pixels[i] & 0xFF);
1314 i++;
1315 srcX++;
1316 if (srcX >= width) {
1317 srcY++;
1318 index = srcY * bytesPerLine;
1319 srcX = 0;
1320 } else {
1321 index++;
1322 }
1323 }
1324 return;
1325 case 4:
1326 index = (y * bytesPerLine) + (x >> 1);
1327 bool high = (x & 0x1) is 0;
1328 while (n > 0) {
1329 theByte = pixels[i] & 0x0F;
1330 if (high) {
1331 data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
1332 } else {
1333 data[index] = (byte)((data[index] & 0xF0) | theByte);
1334 }
1335 i++;
1336 n--;
1337 srcX++;
1338 if (srcX >= width) {
1339 srcY++;
1340 index = srcY * bytesPerLine;
1341 high = true;
1342 srcX = 0;
1343 } else {
1344 if (!high) index++;
1345 high = !high;
1346 }
1347 }
1348 return;
1349 case 2:
1350 byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
1351 index = (y * bytesPerLine) + (x >> 2);
1352 int offset = 3 - (x % 4);
1353 while (n > 0) {
1354 theByte = pixels[i] & 0x3;
1355 data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
1356 i++;
1357 n--;
1358 srcX++;
1359 if (srcX >= width) {
1360 srcY++;
1361 index = srcY * bytesPerLine;
1362 offset = 0;
1363 srcX = 0;
1364 } else {
1365 if (offset is 0) {
1366 index++;
1367 offset = 3;
1368 } else {
1369 offset--;
1370 }
1371 }
1372 }
1373 return;
1374 case 1:
1375 index = (y * bytesPerLine) + (x >> 3);
1376 while (n > 0) {
1377 mask = 1 << (7 - (srcX & 0x7));
1378 if ((pixels[i] & 0x1) is 1) {
1379 data[index] = (byte)((data[index] & 0xFF) | mask);
1380 } else {
1381 data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
1382 }
1383 i++;
1384 n--;
1385 srcX++;
1386 if (srcX >= width) {
1387 srcY++;
1388 index = srcY * bytesPerLine;
1389 srcX = 0;
1390 } else {
1391 if (mask is 1) {
1392 index++;
1393 }
1394 }
1395 }
1396 return;
1397 }
1398 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
1399 }
1400
1401 /**
1402 * Sets the pixel values starting at offset <code>x</code> in
1403 * scanline <code>y</code> in the receiver's data to the
1404 * values from the array <code>pixels</code> starting at
1405 * <code>startIndex</code>.
1406 *
1407 * @param x the x position of the pixel to set
1408 * @param y the y position of the pixel to set
1409 * @param putWidth the width of the pixels to set
1410 * @param pixels the pixels to set
1411 * @param startIndex the index at which to begin setting
1412 *
1413 * @exception IndexOutOfBoundsException if putWidth is too large
1414 * @exception IllegalArgumentException <ul>
1415 * <li>ERROR_NULL_ARGUMENT - if pixels is null</li>
1416 * <li>ERROR_INVALID_ARGUMENT - if x or y is out of bounds</li>
1417 * <li>ERROR_INVALID_ARGUMENT - if putWidth is negative</li>
1418 * </ul>
1419 * @exception DWTException <ul>
1420 * <li>ERROR_UNSUPPORTED_DEPTH if the depth is not one of 1, 2, 4, 8, 16, 24 or 32</li>
1421 * </ul>
1422 */
1423 public void setPixels(int x, int y, int putWidth, int[] pixels, int startIndex) {
1424 if (pixels is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
1425 if (putWidth < 0 || x >= width || y >= height || x < 0 || y < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1426 if (putWidth is 0) return;
1427 int index;
1428 int theByte;
1429 int mask;
1430 int n = putWidth;
1431 int i = startIndex;
1432 int pixel;
1433 int srcX = x, srcY = y;
1434 switch (depth) {
1435 case 32:
1436 index = (y * bytesPerLine) + (x * 4);
1437 for (int j = 0; j < putWidth; j++) {
1438 pixel = pixels[i];
1439 data[index] = (byte)((pixel >> 24) & 0xFF);
1440 data[index + 1] = (byte)((pixel >> 16) & 0xFF);
1441 data[index + 2] = (byte)((pixel >> 8) & 0xFF);
1442 data[index + 3] = (byte)(pixel & 0xFF);
1443 i++;
1444 srcX++;
1445 if (srcX >= width) {
1446 srcY++;
1447 index = srcY * bytesPerLine;
1448 srcX = 0;
1449 } else {
1450 index += 4;
1451 }
1452 }
1453 return;
1454 case 24:
1455 index = (y * bytesPerLine) + (x * 3);
1456 for (int j = 0; j < putWidth; j++) {
1457 pixel = pixels[i];
1458 data[index] = (byte)((pixel >> 16) & 0xFF);
1459 data[index + 1] = (byte)((pixel >> 8) & 0xFF);
1460 data[index + 2] = (byte)(pixel & 0xFF);
1461 i++;
1462 srcX++;
1463 if (srcX >= width) {
1464 srcY++;
1465 index = srcY * bytesPerLine;
1466 srcX = 0;
1467 } else {
1468 index += 3;
1469 }
1470 }
1471 return;
1472 case 16:
1473 index = (y * bytesPerLine) + (x * 2);
1474 for (int j = 0; j < putWidth; j++) {
1475 pixel = pixels[i];
1476 data[index] = (byte)(pixel & 0xFF);
1477 data[index + 1] = (byte)((pixel >> 8) & 0xFF);
1478 i++;
1479 srcX++;
1480 if (srcX >= width) {
1481 srcY++;
1482 index = srcY * bytesPerLine;
1483 srcX = 0;
1484 } else {
1485 index += 2;
1486 }
1487 }
1488 return;
1489 case 8:
1490 index = (y * bytesPerLine) + x;
1491 for (int j = 0; j < putWidth; j++) {
1492 data[index] = (byte)(pixels[i] & 0xFF);
1493 i++;
1494 srcX++;
1495 if (srcX >= width) {
1496 srcY++;
1497 index = srcY * bytesPerLine;
1498 srcX = 0;
1499 } else {
1500 index++;
1501 }
1502 }
1503 return;
1504 case 4:
1505 index = (y * bytesPerLine) + (x >> 1);
1506 bool high = (x & 0x1) is 0;
1507 while (n > 0) {
1508 theByte = pixels[i] & 0x0F;
1509 if (high) {
1510 data[index] = (byte)((data[index] & 0x0F) | (theByte << 4));
1511 } else {
1512 data[index] = (byte)((data[index] & 0xF0) | theByte);
1513 }
1514 i++;
1515 n--;
1516 srcX++;
1517 if (srcX >= width) {
1518 srcY++;
1519 index = srcY * bytesPerLine;
1520 high = true;
1521 srcX = 0;
1522 } else {
1523 if (!high) index++;
1524 high = !high;
1525 }
1526 }
1527 return;
1528 case 2:
1529 byte [] masks = { (byte)0xFC, (byte)0xF3, (byte)0xCF, (byte)0x3F };
1530 index = (y * bytesPerLine) + (x >> 2);
1531 int offset = 3 - (x % 4);
1532 while (n > 0) {
1533 theByte = pixels[i] & 0x3;
1534 data[index] = (byte)((data[index] & masks[offset]) | (theByte << (offset * 2)));
1535 i++;
1536 n--;
1537 srcX++;
1538 if (srcX >= width) {
1539 srcY++;
1540 index = srcY * bytesPerLine;
1541 offset = 3;
1542 srcX = 0;
1543 } else {
1544 if (offset is 0) {
1545 index++;
1546 offset = 3;
1547 } else {
1548 offset--;
1549 }
1550 }
1551 }
1552 return;
1553 case 1:
1554 index = (y * bytesPerLine) + (x >> 3);
1555 while (n > 0) {
1556 mask = 1 << (7 - (srcX & 0x7));
1557 if ((pixels[i] & 0x1) is 1) {
1558 data[index] = (byte)((data[index] & 0xFF) | mask);
1559 } else {
1560 data[index] = (byte)((data[index] & 0xFF) & (mask ^ -1));
1561 }
1562 i++;
1563 n--;
1564 srcX++;
1565 if (srcX >= width) {
1566 srcY++;
1567 index = srcY * bytesPerLine;
1568 srcX = 0;
1569 } else {
1570 if (mask is 1) {
1571 index++;
1572 }
1573 }
1574 }
1575 return;
1576 }
1577 DWT.error(DWT.ERROR_UNSUPPORTED_DEPTH);
1578 }
1579
1580 /**
1581 * Returns a palette with 2 colors: black & white.
1582 */
1583 static PaletteData bwPalette() {
1584 return new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255, 255, 255)});
1585 }
1586
1587 /**
1588 * Gets the offset of the most significant bit for
1589 * the given mask.
1590 */
1591 static int getMSBOffset(int mask) {
1592 for (int i = 31; i >= 0; i--) {
1593 if (((mask >> i) & 0x1) !is 0) return i + 1;
1594 }
1595 return 0;
1596 }
1597
1598 /**
1599 * Finds the closest match.
1600 */
1601 static int closestMatch(int depth, byte red, byte green, byte blue, int redMask, int greenMask, int blueMask, byte[] reds, byte[] greens, byte[] blues) {
1602 if (depth > 8) {
1603 int rshift = 32 - getMSBOffset(redMask);
1604 int gshift = 32 - getMSBOffset(greenMask);
1605 int bshift = 32 - getMSBOffset(blueMask);
1606 return (((red << 24) >>> rshift) & redMask) |
1607 (((green << 24) >>> gshift) & greenMask) |
1608 (((blue << 24) >>> bshift) & blueMask);
1609 }
1610 int r, g, b;
1611 int minDistance = 0x7fffffff;
1612 int nearestPixel = 0;
1613 int n = reds.length;
1614 for (int j = 0; j < n; j++) {
1615 r = (reds[j] & 0xFF) - (red & 0xFF);
1616 g = (greens[j] & 0xFF) - (green & 0xFF);
1617 b = (blues[j] & 0xFF) - (blue & 0xFF);
1618 int distance = r*r + g*g + b*b;
1619 if (distance < minDistance) {
1620 nearestPixel = j;
1621 if (distance is 0) break;
1622 minDistance = distance;
1623 }
1624 }
1625 return nearestPixel;
1626 }
1627
1628 static final ImageData convertMask(ImageData mask) {
1629 if (mask.depth is 1) return mask;
1630 PaletteData palette = new PaletteData(new RGB[] {new RGB(0, 0, 0), new RGB(255,255,255)});
1631 ImageData newMask = new ImageData(mask.width, mask.height, 1, palette);
1632 /* Find index of black in mask palette */
1633 int blackIndex = 0;
1634 RGB[] rgbs = mask.getRGBs();
1635 if (rgbs !is null) {
1636 while (blackIndex < rgbs.length) {
1637 if (rgbs[blackIndex].equals(palette.colors[0])) break;
1638 blackIndex++;
1639 }
1640 }
1641 int[] pixels = new int[mask.width];
1642 for (int y = 0; y < mask.height; y++) {
1643 mask.getPixels(0, y, mask.width, pixels, 0);
1644 for (int i = 0; i < pixels.length; i++) {
1645 if (pixels[i] is blackIndex) {
1646 pixels[i] = 0;
1647 } else {
1648 pixels[i] = 1;
1649 }
1650 }
1651 newMask.setPixels(0, y, mask.width, pixels, 0);
1652 }
1653 return newMask;
1654 }
1655
1656 static final byte[] convertPad(byte[] data, int width, int height, int depth, int pad, int newPad) {
1657 if (pad is newPad) return data;
1658 int stride = (width * depth + 7) / 8;
1659 int bpl = (stride + (pad - 1)) / pad * pad;
1660 int newBpl = (stride + (newPad - 1)) / newPad * newPad;
1661 byte[] newData = new byte[height * newBpl];
1662 int srcIndex = 0, destIndex = 0;
1663 for (int y = 0; y < height; y++) {
1664 System.arraycopy(data, srcIndex, newData, destIndex, stride);
1665 srcIndex += bpl;
1666 destIndex += newBpl;
1667 }
1668 return newData;
1669 }
1670
1671 /**
1672 * Blit operation bits to be OR'ed together to specify the desired operation.
1673 */
1674 static final int
1675 BLIT_SRC = 1, // copy source directly, else applies logic operations
1676 BLIT_ALPHA = 2, // enable alpha blending
1677 BLIT_DITHER = 4; // enable dithering in low color modes
1678
1679 /**
1680 * Alpha mode, values 0 - 255 specify global alpha level
1681 */
1682 static final int
1683 ALPHA_OPAQUE = 255, // Fully opaque (ignores any alpha data)
1684 ALPHA_TRANSPARENT = 0, // Fully transparent (ignores any alpha data)
1685 ALPHA_CHANNEL_SEPARATE = -1, // Use alpha channel from separate alphaData
1686 ALPHA_CHANNEL_SOURCE = -2, // Use alpha channel embedded in sourceData
1687 ALPHA_MASK_UNPACKED = -3, // Use transparency mask formed by bytes in alphaData (non-zero is opaque)
1688 ALPHA_MASK_PACKED = -4, // Use transparency mask formed by packed bits in alphaData
1689 ALPHA_MASK_INDEX = -5, // Consider source palette indices transparent if in alphaData array
1690 ALPHA_MASK_RGB = -6; // Consider source RGBs transparent if in RGB888 format alphaData array
1691
1692 /**
1693 * Byte and bit order constants.
1694 */
1695 static final int LSB_FIRST = 0;
1696 static final int MSB_FIRST = 1;
1697
1698 /**
1699 * Data types (internal)
1700 */
1701 private static final int
1702 // direct / true color formats with arbitrary masks & shifts
1703 TYPE_GENERIC_8 = 0,
1704 TYPE_GENERIC_16_MSB = 1,
1705 TYPE_GENERIC_16_LSB = 2,
1706 TYPE_GENERIC_24 = 3,
1707 TYPE_GENERIC_32_MSB = 4,
1708 TYPE_GENERIC_32_LSB = 5,
1709 // palette indexed color formats
1710 TYPE_INDEX_8 = 6,
1711 TYPE_INDEX_4 = 7,
1712 TYPE_INDEX_2 = 8,
1713 TYPE_INDEX_1_MSB = 9,
1714 TYPE_INDEX_1_LSB = 10;
1715
1716 /**
1717 * Blits a direct palette image into a direct palette image.
1718 * <p>
1719 * Note: When the source and destination depth, order and masks
1720 * are pairwise equal and the blitter operation is BLIT_SRC,
1721 * the masks are ignored. Hence when not changing the image
1722 * data format, 0 may be specified for the masks.
1723 * </p>
1724 *
1725 * @param op the blitter operation: a combination of BLIT_xxx flags
1726 * (see BLIT_xxx constants)
1727 * @param srcData the source byte array containing image data
1728 * @param srcDepth the source depth: one of 8, 16, 24, 32
1729 * @param srcStride the source number of bytes per line
1730 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
1731 * ignored if srcDepth is not 16 or 32
1732 * @param srcX the top-left x-coord of the source blit region
1733 * @param srcY the top-left y-coord of the source blit region
1734 * @param srcWidth the width of the source blit region
1735 * @param srcHeight the height of the source blit region
1736 * @param srcRedMask the source red channel mask
1737 * @param srcGreenMask the source green channel mask
1738 * @param srcBlueMask the source blue channel mask
1739 * @param alphaMode the alpha blending or mask mode, may be
1740 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
1741 * not specified in the blitter operations
1742 * (see ALPHA_MODE_xxx constants)
1743 * @param alphaData the alpha blending or mask data, varies depending
1744 * on the value of alphaMode and sometimes ignored
1745 * @param alphaStride the alpha data number of bytes per line
1746 * @param alphaX the top-left x-coord of the alpha blit region
1747 * @param alphaY the top-left y-coord of the alpha blit region
1748 * @param destData the destination byte array containing image data
1749 * @param destDepth the destination depth: one of 8, 16, 24, 32
1750 * @param destStride the destination number of bytes per line
1751 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
1752 * ignored if destDepth is not 16 or 32
1753 * @param destX the top-left x-coord of the destination blit region
1754 * @param destY the top-left y-coord of the destination blit region
1755 * @param destWidth the width of the destination blit region
1756 * @param destHeight the height of the destination blit region
1757 * @param destRedMask the destination red channel mask
1758 * @param destGreenMask the destination green channel mask
1759 * @param destBlueMask the destination blue channel mask
1760 * @param flipX if true the resulting image is flipped along the vertical axis
1761 * @param flipY if true the resulting image is flipped along the horizontal axis
1762 */
1763 static void blit(int op,
1764 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
1765 int srcX, int srcY, int srcWidth, int srcHeight,
1766 int srcRedMask, int srcGreenMask, int srcBlueMask,
1767 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
1768 byte[] destData, int destDepth, int destStride, int destOrder,
1769 int destX, int destY, int destWidth, int destHeight,
1770 int destRedMask, int destGreenMask, int destBlueMask,
1771 bool flipX, bool flipY) {
1772 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
1773
1774 // these should be supplied as params later
1775 final int srcAlphaMask = 0, destAlphaMask = 0;
1776
1777 /*** Prepare scaling data ***/
1778 final int dwm1 = destWidth - 1;
1779 final int sfxi = (dwm1 !is 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
1780 final int dhm1 = destHeight - 1;
1781 final int sfyi = (dhm1 !is 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
1782
1783 /*** Prepare source-related data ***/
1784 final int sbpp, stype;
1785 switch (srcDepth) {
1786 case 8:
1787 sbpp = 1;
1788 stype = TYPE_GENERIC_8;
1789 break;
1790 case 16:
1791 sbpp = 2;
1792 stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
1793 break;
1794 case 24:
1795 sbpp = 3;
1796 stype = TYPE_GENERIC_24;
1797 break;
1798 case 32:
1799 sbpp = 4;
1800 stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
1801 break;
1802 default:
1803 //throw new IllegalArgumentException("Invalid source type");
1804 return;
1805 }
1806 int spr = srcY * srcStride + srcX * sbpp;
1807
1808 /*** Prepare destination-related data ***/
1809 final int dbpp, dtype;
1810 switch (destDepth) {
1811 case 8:
1812 dbpp = 1;
1813 dtype = TYPE_GENERIC_8;
1814 break;
1815 case 16:
1816 dbpp = 2;
1817 dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
1818 break;
1819 case 24:
1820 dbpp = 3;
1821 dtype = TYPE_GENERIC_24;
1822 break;
1823 case 32:
1824 dbpp = 4;
1825 dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
1826 break;
1827 default:
1828 //throw new IllegalArgumentException("Invalid destination type");
1829 return;
1830 }
1831 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
1832 final int dprxi = (flipX) ? -dbpp : dbpp;
1833 final int dpryi = (flipY) ? -destStride : destStride;
1834
1835 /*** Prepare special processing data ***/
1836 int apr;
1837 if ((op & BLIT_ALPHA) !is 0) {
1838 switch (alphaMode) {
1839 case ALPHA_MASK_UNPACKED:
1840 case ALPHA_CHANNEL_SEPARATE:
1841 if (alphaData is null) alphaMode = 0x10000;
1842 apr = alphaY * alphaStride + alphaX;
1843 break;
1844 case ALPHA_MASK_PACKED:
1845 if (alphaData is null) alphaMode = 0x10000;
1846 alphaStride <<= 3;
1847 apr = alphaY * alphaStride + alphaX;
1848 break;
1849 case ALPHA_MASK_INDEX:
1850 //throw new IllegalArgumentException("Invalid alpha type");
1851 return;
1852 case ALPHA_MASK_RGB:
1853 if (alphaData is null) alphaMode = 0x10000;
1854 apr = 0;
1855 break;
1856 default:
1857 alphaMode = (alphaMode << 16) / 255; // prescale
1858 case ALPHA_CHANNEL_SOURCE:
1859 apr = 0;
1860 break;
1861 }
1862 } else {
1863 alphaMode = 0x10000;
1864 apr = 0;
1865 }
1866
1867 /*** Blit ***/
1868 int dp = dpr;
1869 int sp = spr;
1870 if ((alphaMode is 0x10000) && (stype is dtype) &&
1871 (srcRedMask is destRedMask) && (srcGreenMask is destGreenMask) &&
1872 (srcBlueMask is destBlueMask) && (srcAlphaMask is destAlphaMask)) {
1873 /*** Fast blit (straight copy) ***/
1874 switch (sbpp) {
1875 case 1:
1876 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
1877 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
1878 destData[dp] = srcData[sp];
1879 sp += (sfx >>> 16);
1880 }
1881 }
1882 break;
1883 case 2:
1884 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
1885 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
1886 destData[dp] = srcData[sp];
1887 destData[dp + 1] = srcData[sp + 1];
1888 sp += (sfx >>> 16) * 2;
1889 }
1890 }
1891 break;
1892 case 3:
1893 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
1894 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
1895 destData[dp] = srcData[sp];
1896 destData[dp + 1] = srcData[sp + 1];
1897 destData[dp + 2] = srcData[sp + 2];
1898 sp += (sfx >>> 16) * 3;
1899 }
1900 }
1901 break;
1902 case 4:
1903 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
1904 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
1905 destData[dp] = srcData[sp];
1906 destData[dp + 1] = srcData[sp + 1];
1907 destData[dp + 2] = srcData[sp + 2];
1908 destData[dp + 3] = srcData[sp + 3];
1909 sp += (sfx >>> 16) * 4;
1910 }
1911 }
1912 break;
1913 }
1914 return;
1915 }
1916 /*** Comprehensive blit (apply transformations) ***/
1917 final int srcRedShift = getChannelShift(srcRedMask);
1918 final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
1919 final int srcGreenShift = getChannelShift(srcGreenMask);
1920 final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
1921 final int srcBlueShift = getChannelShift(srcBlueMask);
1922 final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
1923 final int srcAlphaShift = getChannelShift(srcAlphaMask);
1924 final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
1925
1926 final int destRedShift = getChannelShift(destRedMask);
1927 final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
1928 final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
1929 final int destRedPreShift = 8 - destRedWidth;
1930 final int destGreenShift = getChannelShift(destGreenMask);
1931 final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
1932 final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
1933 final int destGreenPreShift = 8 - destGreenWidth;
1934 final int destBlueShift = getChannelShift(destBlueMask);
1935 final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
1936 final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
1937 final int destBluePreShift = 8 - destBlueWidth;
1938 final int destAlphaShift = getChannelShift(destAlphaMask);
1939 final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
1940 final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
1941 final int destAlphaPreShift = 8 - destAlphaWidth;
1942
1943 int ap = apr, alpha = alphaMode;
1944 int r = 0, g = 0, b = 0, a = 0;
1945 int rq = 0, gq = 0, bq = 0, aq = 0;
1946 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
1947 sp = spr += (sfy >>> 16) * srcStride,
1948 ap = apr += (sfy >>> 16) * alphaStride,
1949 sfy = (sfy & 0xffff) + sfyi,
1950 dp = dpr += dpryi) {
1951 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
1952 dp += dprxi,
1953 sfx = (sfx & 0xffff) + sfxi) {
1954 /*** READ NEXT PIXEL ***/
1955 switch (stype) {
1956 case TYPE_GENERIC_8: {
1957 final int data = srcData[sp] & 0xff;
1958 sp += (sfx >>> 16);
1959 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
1960 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
1961 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
1962 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
1963 } break;
1964 case TYPE_GENERIC_16_MSB: {
1965 final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
1966 sp += (sfx >>> 16) * 2;
1967 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
1968 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
1969 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
1970 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
1971 } break;
1972 case TYPE_GENERIC_16_LSB: {
1973 final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
1974 sp += (sfx >>> 16) * 2;
1975 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
1976 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
1977 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
1978 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
1979 } break;
1980 case TYPE_GENERIC_24: {
1981 final int data = (( ((srcData[sp] & 0xff) << 8) |
1982 (srcData[sp + 1] & 0xff)) << 8) |
1983 (srcData[sp + 2] & 0xff);
1984 sp += (sfx >>> 16) * 3;
1985 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
1986 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
1987 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
1988 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
1989 } break;
1990 case TYPE_GENERIC_32_MSB: {
1991 final int data = (( (( ((srcData[sp] & 0xff) << 8) |
1992 (srcData[sp + 1] & 0xff)) << 8) |
1993 (srcData[sp + 2] & 0xff)) << 8) |
1994 (srcData[sp + 3] & 0xff);
1995 sp += (sfx >>> 16) * 4;
1996 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
1997 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
1998 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
1999 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2000 } break;
2001 case TYPE_GENERIC_32_LSB: {
2002 final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
2003 (srcData[sp + 2] & 0xff)) << 8) |
2004 (srcData[sp + 1] & 0xff)) << 8) |
2005 (srcData[sp] & 0xff);
2006 sp += (sfx >>> 16) * 4;
2007 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
2008 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
2009 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
2010 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
2011 } break;
2012 }
2013
2014 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
2015 switch (alphaMode) {
2016 case ALPHA_CHANNEL_SEPARATE:
2017 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
2018 ap += (sfx >> 16);
2019 break;
2020 case ALPHA_CHANNEL_SOURCE:
2021 alpha = (a << 16) / 255;
2022 break;
2023 case ALPHA_MASK_UNPACKED:
2024 alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
2025 ap += (sfx >> 16);
2026 break;
2027 case ALPHA_MASK_PACKED:
2028 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
2029 ap += (sfx >> 16);
2030 break;
2031 case ALPHA_MASK_RGB:
2032 alpha = 0x10000;
2033 for (int i = 0; i < alphaData.length; i += 3) {
2034 if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) {
2035 alpha = 0x0000;
2036 break;
2037 }
2038 }
2039 break;
2040 }
2041 if (alpha !is 0x10000) {
2042 if (alpha is 0x0000) continue;
2043 switch (dtype) {
2044 case TYPE_GENERIC_8: {
2045 final int data = destData[dp] & 0xff;
2046 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2047 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2048 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2049 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2050 } break;
2051 case TYPE_GENERIC_16_MSB: {
2052 final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
2053 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2054 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2055 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2056 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2057 } break;
2058 case TYPE_GENERIC_16_LSB: {
2059 final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
2060 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2061 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2062 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2063 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2064 } break;
2065 case TYPE_GENERIC_24: {
2066 final int data = (( ((destData[dp] & 0xff) << 8) |
2067 (destData[dp + 1] & 0xff)) << 8) |
2068 (destData[dp + 2] & 0xff);
2069 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2070 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2071 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2072 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2073 } break;
2074 case TYPE_GENERIC_32_MSB: {
2075 final int data = (( (( ((destData[dp] & 0xff) << 8) |
2076 (destData[dp + 1] & 0xff)) << 8) |
2077 (destData[dp + 2] & 0xff)) << 8) |
2078 (destData[dp + 3] & 0xff);
2079 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2080 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2081 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2082 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2083 } break;
2084 case TYPE_GENERIC_32_LSB: {
2085 final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
2086 (destData[dp + 2] & 0xff)) << 8) |
2087 (destData[dp + 1] & 0xff)) << 8) |
2088 (destData[dp] & 0xff);
2089 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2090 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2091 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2092 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2093 } break;
2094 }
2095 // Perform alpha blending
2096 a = aq + ((a - aq) * alpha >> 16);
2097 r = rq + ((r - rq) * alpha >> 16);
2098 g = gq + ((g - gq) * alpha >> 16);
2099 b = bq + ((b - bq) * alpha >> 16);
2100 }
2101
2102 /*** WRITE NEXT PIXEL ***/
2103 final int data =
2104 (r >>> destRedPreShift << destRedShift) |
2105 (g >>> destGreenPreShift << destGreenShift) |
2106 (b >>> destBluePreShift << destBlueShift) |
2107 (a >>> destAlphaPreShift << destAlphaShift);
2108 switch (dtype) {
2109 case TYPE_GENERIC_8: {
2110 destData[dp] = (byte) data;
2111 } break;
2112 case TYPE_GENERIC_16_MSB: {
2113 destData[dp] = (byte) (data >>> 8);
2114 destData[dp + 1] = (byte) (data & 0xff);
2115 } break;
2116 case TYPE_GENERIC_16_LSB: {
2117 destData[dp] = (byte) (data & 0xff);
2118 destData[dp + 1] = (byte) (data >>> 8);
2119 } break;
2120 case TYPE_GENERIC_24: {
2121 destData[dp] = (byte) (data >>> 16);
2122 destData[dp + 1] = (byte) (data >>> 8);
2123 destData[dp + 2] = (byte) (data & 0xff);
2124 } break;
2125 case TYPE_GENERIC_32_MSB: {
2126 destData[dp] = (byte) (data >>> 24);
2127 destData[dp + 1] = (byte) (data >>> 16);
2128 destData[dp + 2] = (byte) (data >>> 8);
2129 destData[dp + 3] = (byte) (data & 0xff);
2130 } break;
2131 case TYPE_GENERIC_32_LSB: {
2132 destData[dp] = (byte) (data & 0xff);
2133 destData[dp + 1] = (byte) (data >>> 8);
2134 destData[dp + 2] = (byte) (data >>> 16);
2135 destData[dp + 3] = (byte) (data >>> 24);
2136 } break;
2137 }
2138 }
2139 }
2140 }
2141
2142 /**
2143 * Blits an index palette image into an index palette image.
2144 * <p>
2145 * Note: The source and destination red, green, and blue
2146 * arrays may be null if no alpha blending or dither is to be
2147 * performed.
2148 * </p>
2149 *
2150 * @param op the blitter operation: a combination of BLIT_xxx flags
2151 * (see BLIT_xxx constants)
2152 * @param srcData the source byte array containing image data
2153 * @param srcDepth the source depth: one of 1, 2, 4, 8
2154 * @param srcStride the source number of bytes per line
2155 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
2156 * ignored if srcDepth is not 1
2157 * @param srcX the top-left x-coord of the source blit region
2158 * @param srcY the top-left y-coord of the source blit region
2159 * @param srcWidth the width of the source blit region
2160 * @param srcHeight the height of the source blit region
2161 * @param srcReds the source palette red component intensities
2162 * @param srcGreens the source palette green component intensities
2163 * @param srcBlues the source palette blue component intensities
2164 * @param alphaMode the alpha blending or mask mode, may be
2165 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
2166 * not specified in the blitter operations
2167 * (see ALPHA_MODE_xxx constants)
2168 * @param alphaData the alpha blending or mask data, varies depending
2169 * on the value of alphaMode and sometimes ignored
2170 * @param alphaStride the alpha data number of bytes per line
2171 * @param alphaX the top-left x-coord of the alpha blit region
2172 * @param alphaY the top-left y-coord of the alpha blit region
2173 * @param destData the destination byte array containing image data
2174 * @param destDepth the destination depth: one of 1, 2, 4, 8
2175 * @param destStride the destination number of bytes per line
2176 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
2177 * ignored if destDepth is not 1
2178 * @param destX the top-left x-coord of the destination blit region
2179 * @param destY the top-left y-coord of the destination blit region
2180 * @param destWidth the width of the destination blit region
2181 * @param destHeight the height of the destination blit region
2182 * @param destReds the destination palette red component intensities
2183 * @param destGreens the destination palette green component intensities
2184 * @param destBlues the destination palette blue component intensities
2185 * @param flipX if true the resulting image is flipped along the vertical axis
2186 * @param flipY if true the resulting image is flipped along the horizontal axis
2187 */
2188 static void blit(int op,
2189 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
2190 int srcX, int srcY, int srcWidth, int srcHeight,
2191 byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
2192 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
2193 byte[] destData, int destDepth, int destStride, int destOrder,
2194 int destX, int destY, int destWidth, int destHeight,
2195 byte[] destReds, byte[] destGreens, byte[] destBlues,
2196 bool flipX, bool flipY) {
2197 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
2198
2199 /*** Prepare scaling data ***/
2200 final int dwm1 = destWidth - 1;
2201 final int sfxi = (dwm1 !is 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
2202 final int dhm1 = destHeight - 1;
2203 final int sfyi = (dhm1 !is 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
2204
2205 /*** Prepare source-related data ***/
2206 final int stype;
2207 switch (srcDepth) {
2208 case 8:
2209 stype = TYPE_INDEX_8;
2210 break;
2211 case 4:
2212 srcStride <<= 1;
2213 stype = TYPE_INDEX_4;
2214 break;
2215 case 2:
2216 srcStride <<= 2;
2217 stype = TYPE_INDEX_2;
2218 break;
2219 case 1:
2220 srcStride <<= 3;
2221 stype = (srcOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
2222 break;
2223 default:
2224 //throw new IllegalArgumentException("Invalid source type");
2225 return;
2226 }
2227 int spr = srcY * srcStride + srcX;
2228
2229 /*** Prepare destination-related data ***/
2230 final int dtype;
2231 switch (destDepth) {
2232 case 8:
2233 dtype = TYPE_INDEX_8;
2234 break;
2235 case 4:
2236 destStride <<= 1;
2237 dtype = TYPE_INDEX_4;
2238 break;
2239 case 2:
2240 destStride <<= 2;
2241 dtype = TYPE_INDEX_2;
2242 break;
2243 case 1:
2244 destStride <<= 3;
2245 dtype = (destOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
2246 break;
2247 default:
2248 //throw new IllegalArgumentException("Invalid source type");
2249 return;
2250 }
2251 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
2252 final int dprxi = (flipX) ? -1 : 1;
2253 final int dpryi = (flipY) ? -destStride : destStride;
2254
2255 /*** Prepare special processing data ***/
2256 int apr;
2257 if ((op & BLIT_ALPHA) !is 0) {
2258 switch (alphaMode) {
2259 case ALPHA_MASK_UNPACKED:
2260 case ALPHA_CHANNEL_SEPARATE:
2261 if (alphaData is null) alphaMode = 0x10000;
2262 apr = alphaY * alphaStride + alphaX;
2263 break;
2264 case ALPHA_MASK_PACKED:
2265 if (alphaData is null) alphaMode = 0x10000;
2266 alphaStride <<= 3;
2267 apr = alphaY * alphaStride + alphaX;
2268 break;
2269 case ALPHA_MASK_INDEX:
2270 case ALPHA_MASK_RGB:
2271 if (alphaData is null) alphaMode = 0x10000;
2272 apr = 0;
2273 break;
2274 default:
2275 alphaMode = (alphaMode << 16) / 255; // prescale
2276 case ALPHA_CHANNEL_SOURCE:
2277 apr = 0;
2278 break;
2279 }
2280 } else {
2281 alphaMode = 0x10000;
2282 apr = 0;
2283 }
2284 final bool ditherEnabled = (op & BLIT_DITHER) !is 0;
2285
2286 /*** Blit ***/
2287 int dp = dpr;
2288 int sp = spr;
2289 int ap = apr;
2290 int destPaletteSize = 1 << destDepth;
2291 if ((destReds !is null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
2292 byte[] paletteMapping = null;
2293 bool isExactPaletteMapping = true;
2294 switch (alphaMode) {
2295 case 0x10000:
2296 /*** If the palettes and formats are equivalent use a one-to-one mapping ***/
2297 if ((stype is dtype) &&
2298 (srcReds is destReds) && (srcGreens is destGreens) && (srcBlues is destBlues)) {
2299 paletteMapping = ONE_TO_ONE_MAPPING;
2300 break;
2301 /*** If palettes have not been supplied, supply a suitable mapping ***/
2302 } else if ((srcReds is null) || (destReds is null)) {
2303 if (srcDepth <= destDepth) {
2304 paletteMapping = ONE_TO_ONE_MAPPING;
2305 } else {
2306 paletteMapping = new byte[1 << srcDepth];
2307 int mask = (0xff << destDepth) >>> 8;
2308 for (int i = 0; i < paletteMapping.length; ++i) paletteMapping[i] = (byte)(i & mask);
2309 }
2310 break;
2311 }
2312 case ALPHA_MASK_UNPACKED:
2313 case ALPHA_MASK_PACKED:
2314 case ALPHA_MASK_INDEX:
2315 case ALPHA_MASK_RGB:
2316 /*** Generate a palette mapping ***/
2317 int srcPaletteSize = 1 << srcDepth;
2318 paletteMapping = new byte[srcPaletteSize];
2319 if ((srcReds !is null) && (srcReds.length < srcPaletteSize)) srcPaletteSize = srcReds.length;
2320 for (int i = 0, r, g, b, index; i < srcPaletteSize; ++i) {
2321 r = srcReds[i] & 0xff;
2322 g = srcGreens[i] & 0xff;
2323 b = srcBlues[i] & 0xff;
2324 index = 0;
2325 int minDistance = 0x7fffffff;
2326 for (int j = 0, dr, dg, db, distance; j < destPaletteSize; ++j) {
2327 dr = (destReds[j] & 0xff) - r;
2328 dg = (destGreens[j] & 0xff) - g;
2329 db = (destBlues[j] & 0xff) - b;
2330 distance = dr * dr + dg * dg + db * db;
2331 if (distance < minDistance) {
2332 index = j;
2333 if (distance is 0) break;
2334 minDistance = distance;
2335 }
2336 }
2337 paletteMapping[i] = (byte)index;
2338 if (minDistance !is 0) isExactPaletteMapping = false;
2339 }
2340 break;
2341 }
2342 if ((paletteMapping !is null) && (isExactPaletteMapping || ! ditherEnabled)) {
2343 if ((stype is dtype) && (alphaMode is 0x10000)) {
2344 /*** Fast blit (copy w/ mapping) ***/
2345 switch (stype) {
2346 case TYPE_INDEX_8:
2347 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2348 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2349 destData[dp] = paletteMapping[srcData[sp] & 0xff];
2350 sp += (sfx >>> 16);
2351 }
2352 }
2353 break;
2354 case TYPE_INDEX_4:
2355 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2356 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2357 final int v;
2358 if ((sp & 1) !is 0) v = paletteMapping[srcData[sp >> 1] & 0x0f];
2359 else v = (srcData[sp >> 1] >>> 4) & 0x0f;
2360 sp += (sfx >>> 16);
2361 if ((dp & 1) !is 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | v);
2362 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (v << 4));
2363 }
2364 }
2365 break;
2366 case TYPE_INDEX_2:
2367 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2368 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2369 final int index = paletteMapping[(srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03];
2370 sp += (sfx >>> 16);
2371 final int shift = 6 - (dp & 3) * 2;
2372 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
2373 }
2374 }
2375 break;
2376 case TYPE_INDEX_1_MSB:
2377 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2378 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2379 final int index = paletteMapping[(srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01];
2380 sp += (sfx >>> 16);
2381 final int shift = 7 - (dp & 7);
2382 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
2383 }
2384 }
2385 break;
2386 case TYPE_INDEX_1_LSB:
2387 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy, sp = spr += (sfy >>> 16) * srcStride, sfy = (sfy & 0xffff) + sfyi, dp = dpr += dpryi) {
2388 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx, dp += dprxi, sfx = (sfx & 0xffff) + sfxi) {
2389 final int index = paletteMapping[(srcData[sp >> 3] >>> (sp & 7)) & 0x01];
2390 sp += (sfx >>> 16);
2391 final int shift = dp & 7;
2392 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
2393 }
2394 }
2395 break;
2396 }
2397 } else {
2398 /*** Convert between indexed modes using mapping and mask ***/
2399 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
2400 sp = spr += (sfy >>> 16) * srcStride,
2401 sfy = (sfy & 0xffff) + sfyi,
2402 dp = dpr += dpryi) {
2403 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
2404 dp += dprxi,
2405 sfx = (sfx & 0xffff) + sfxi) {
2406 int index;
2407 /*** READ NEXT PIXEL ***/
2408 switch (stype) {
2409 case TYPE_INDEX_8:
2410 index = srcData[sp] & 0xff;
2411 sp += (sfx >>> 16);
2412 break;
2413 case TYPE_INDEX_4:
2414 if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f;
2415 else index = (srcData[sp >> 1] >>> 4) & 0x0f;
2416 sp += (sfx >>> 16);
2417 break;
2418 case TYPE_INDEX_2:
2419 index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
2420 sp += (sfx >>> 16);
2421 break;
2422 case TYPE_INDEX_1_MSB:
2423 index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
2424 sp += (sfx >>> 16);
2425 break;
2426 case TYPE_INDEX_1_LSB:
2427 index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
2428 sp += (sfx >>> 16);
2429 break;
2430 default:
2431 return;
2432 }
2433 /*** APPLY MASK ***/
2434 switch (alphaMode) {
2435 case ALPHA_MASK_UNPACKED: {
2436 final byte mask = alphaData[ap];
2437 ap += (sfx >> 16);
2438 if (mask is 0) continue;
2439 } break;
2440 case ALPHA_MASK_PACKED: {
2441 final int mask = alphaData[ap >> 3] & (1 << (ap & 7));
2442 ap += (sfx >> 16);
2443 if (mask is 0) continue;
2444 } break;
2445 case ALPHA_MASK_INDEX: {
2446 int i = 0;
2447 while (i < alphaData.length) {
2448 if (index is (alphaData[i] & 0xff)) break;
2449 }
2450 if (i < alphaData.length) continue;
2451 } break;
2452 case ALPHA_MASK_RGB: {
2453 final byte r = srcReds[index], g = srcGreens[index], b = srcBlues[index];
2454 int i = 0;
2455 while (i < alphaData.length) {
2456 if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) break;
2457 i += 3;
2458 }
2459 if (i < alphaData.length) continue;
2460 } break;
2461 }
2462 index = paletteMapping[index] & 0xff;
2463
2464 /*** WRITE NEXT PIXEL ***/
2465 switch (dtype) {
2466 case TYPE_INDEX_8:
2467 destData[dp] = (byte) index;
2468 break;
2469 case TYPE_INDEX_4:
2470 if ((dp & 1) !is 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | index);
2471 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (index << 4));
2472 break;
2473 case TYPE_INDEX_2: {
2474 final int shift = 6 - (dp & 3) * 2;
2475 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (index << shift));
2476 } break;
2477 case TYPE_INDEX_1_MSB: {
2478 final int shift = 7 - (dp & 7);
2479 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
2480 } break;
2481 case TYPE_INDEX_1_LSB: {
2482 final int shift = dp & 7;
2483 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (index << shift));
2484 } break;
2485 }
2486 }
2487 }
2488 }
2489 return;
2490 }
2491
2492 /*** Comprehensive blit (apply transformations) ***/
2493 int alpha = alphaMode;
2494 int index = 0;
2495 int indexq = 0;
2496 int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
2497 final int[] rerr, gerr, berr;
2498 if (ditherEnabled) {
2499 rerr = new int[destWidth + 2];
2500 gerr = new int[destWidth + 2];
2501 berr = new int[destWidth + 2];
2502 } else {
2503 rerr = null; gerr = null; berr = null;
2504 }
2505 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
2506 sp = spr += (sfy >>> 16) * srcStride,
2507 ap = apr += (sfy >>> 16) * alphaStride,
2508 sfy = (sfy & 0xffff) + sfyi,
2509 dp = dpr += dpryi) {
2510 int lrerr = 0, lgerr = 0, lberr = 0;
2511 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
2512 dp += dprxi,
2513 sfx = (sfx & 0xffff) + sfxi) {
2514 /*** READ NEXT PIXEL ***/
2515 switch (stype) {
2516 case TYPE_INDEX_8:
2517 index = srcData[sp] & 0xff;
2518 sp += (sfx >>> 16);
2519 break;
2520 case TYPE_INDEX_4:
2521 if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f;
2522 else index = (srcData[sp >> 1] >>> 4) & 0x0f;
2523 sp += (sfx >>> 16);
2524 break;
2525 case TYPE_INDEX_2:
2526 index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
2527 sp += (sfx >>> 16);
2528 break;
2529 case TYPE_INDEX_1_MSB:
2530 index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
2531 sp += (sfx >>> 16);
2532 break;
2533 case TYPE_INDEX_1_LSB:
2534 index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
2535 sp += (sfx >>> 16);
2536 break;
2537 }
2538
2539 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
2540 int r = srcReds[index] & 0xff, g = srcGreens[index] & 0xff, b = srcBlues[index] & 0xff;
2541 switch (alphaMode) {
2542 case ALPHA_CHANNEL_SEPARATE:
2543 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
2544 ap += (sfx >> 16);
2545 break;
2546 case ALPHA_MASK_UNPACKED:
2547 alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
2548 ap += (sfx >> 16);
2549 break;
2550 case ALPHA_MASK_PACKED:
2551 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
2552 ap += (sfx >> 16);
2553 break;
2554 case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
2555 int i = 0;
2556 while (i < alphaData.length) {
2557 if (index is (alphaData[i] & 0xff)) break;
2558 }
2559 if (i < alphaData.length) continue;
2560 } break;
2561 case ALPHA_MASK_RGB: {
2562 int i = 0;
2563 while (i < alphaData.length) {
2564 if ((r is (alphaData[i] & 0xff)) &&
2565 (g is (alphaData[i + 1] & 0xff)) &&
2566 (b is (alphaData[i + 2] & 0xff))) break;
2567 i += 3;
2568 }
2569 if (i < alphaData.length) continue;
2570 } break;
2571 }
2572 if (alpha !is 0x10000) {
2573 if (alpha is 0x0000) continue;
2574 switch (dtype) {
2575 case TYPE_INDEX_8:
2576 indexq = destData[dp] & 0xff;
2577 break;
2578 case TYPE_INDEX_4:
2579 if ((dp & 1) !is 0) indexq = destData[dp >> 1] & 0x0f;
2580 else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
2581 break;
2582 case TYPE_INDEX_2:
2583 indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
2584 break;
2585 case TYPE_INDEX_1_MSB:
2586 indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
2587 break;
2588 case TYPE_INDEX_1_LSB:
2589 indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
2590 break;
2591 }
2592 // Perform alpha blending
2593 final int rq = destReds[indexq] & 0xff;
2594 final int gq = destGreens[indexq] & 0xff;
2595 final int bq = destBlues[indexq] & 0xff;
2596 r = rq + ((r - rq) * alpha >> 16);
2597 g = gq + ((g - gq) * alpha >> 16);
2598 b = bq + ((b - bq) * alpha >> 16);
2599 }
2600
2601 /*** MAP COLOR TO THE PALETTE ***/
2602 if (ditherEnabled) {
2603 // Floyd-Steinberg error diffusion
2604 r += rerr[dx] >> 4;
2605 if (r < 0) r = 0; else if (r > 255) r = 255;
2606 g += gerr[dx] >> 4;
2607 if (g < 0) g = 0; else if (g > 255) g = 255;
2608 b += berr[dx] >> 4;
2609 if (b < 0) b = 0; else if (b > 255) b = 255;
2610 rerr[dx] = lrerr;
2611 gerr[dx] = lgerr;
2612 berr[dx] = lberr;
2613 }
2614 if (r !is lastr || g !is lastg || b !is lastb) {
2615 // moving the variable declarations out seems to make the JDK JIT happier...
2616 for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
2617 dr = (destReds[j] & 0xff) - r;
2618 dg = (destGreens[j] & 0xff) - g;
2619 db = (destBlues[j] & 0xff) - b;
2620 distance = dr * dr + dg * dg + db * db;
2621 if (distance < minDistance) {
2622 lastindex = j;
2623 if (distance is 0) break;
2624 minDistance = distance;
2625 }
2626 }
2627 lastr = r; lastg = g; lastb = b;
2628 }
2629 if (ditherEnabled) {
2630 // Floyd-Steinberg error diffusion, cont'd...
2631 final int dxm1 = dx - 1, dxp1 = dx + 1;
2632 int acc;
2633 rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
2634 rerr[dx] += acc += lrerr + lrerr;
2635 rerr[dxm1] += acc + lrerr + lrerr;
2636 gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
2637 gerr[dx] += acc += lgerr + lgerr;
2638 gerr[dxm1] += acc + lgerr + lgerr;
2639 berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
2640 berr[dx] += acc += lberr + lberr;
2641 berr[dxm1] += acc + lberr + lberr;
2642 }
2643
2644 /*** WRITE NEXT PIXEL ***/
2645 switch (dtype) {
2646 case TYPE_INDEX_8:
2647 destData[dp] = (byte) lastindex;
2648 break;
2649 case TYPE_INDEX_4:
2650 if ((dp & 1) !is 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | lastindex);
2651 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
2652 break;
2653 case TYPE_INDEX_2: {
2654 final int shift = 6 - (dp & 3) * 2;
2655 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
2656 } break;
2657 case TYPE_INDEX_1_MSB: {
2658 final int shift = 7 - (dp & 7);
2659 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
2660 } break;
2661 case TYPE_INDEX_1_LSB: {
2662 final int shift = dp & 7;
2663 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
2664 } break;
2665 }
2666 }
2667 }
2668 }
2669
2670 /**
2671 * Blits an index palette image into a direct palette image.
2672 * <p>
2673 * Note: The source and destination masks and palettes must
2674 * always be fully specified.
2675 * </p>
2676 *
2677 * @param op the blitter operation: a combination of BLIT_xxx flags
2678 * (see BLIT_xxx constants)
2679 * @param srcData the source byte array containing image data
2680 * @param srcDepth the source depth: one of 1, 2, 4, 8
2681 * @param srcStride the source number of bytes per line
2682 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
2683 * ignored if srcDepth is not 1
2684 * @param srcX the top-left x-coord of the source blit region
2685 * @param srcY the top-left y-coord of the source blit region
2686 * @param srcWidth the width of the source blit region
2687 * @param srcHeight the height of the source blit region
2688 * @param srcReds the source palette red component intensities
2689 * @param srcGreens the source palette green component intensities
2690 * @param srcBlues the source palette blue component intensities
2691 * @param alphaMode the alpha blending or mask mode, may be
2692 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
2693 * not specified in the blitter operations
2694 * (see ALPHA_MODE_xxx constants)
2695 * @param alphaData the alpha blending or mask data, varies depending
2696 * on the value of alphaMode and sometimes ignored
2697 * @param alphaStride the alpha data number of bytes per line
2698 * @param alphaX the top-left x-coord of the alpha blit region
2699 * @param alphaY the top-left y-coord of the alpha blit region
2700 * @param destData the destination byte array containing image data
2701 * @param destDepth the destination depth: one of 8, 16, 24, 32
2702 * @param destStride the destination number of bytes per line
2703 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
2704 * ignored if destDepth is not 16 or 32
2705 * @param destX the top-left x-coord of the destination blit region
2706 * @param destY the top-left y-coord of the destination blit region
2707 * @param destWidth the width of the destination blit region
2708 * @param destHeight the height of the destination blit region
2709 * @param destRedMask the destination red channel mask
2710 * @param destGreenMask the destination green channel mask
2711 * @param destBlueMask the destination blue channel mask
2712 * @param flipX if true the resulting image is flipped along the vertical axis
2713 * @param flipY if true the resulting image is flipped along the horizontal axis
2714 */
2715 static void blit(int op,
2716 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
2717 int srcX, int srcY, int srcWidth, int srcHeight,
2718 byte[] srcReds, byte[] srcGreens, byte[] srcBlues,
2719 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
2720 byte[] destData, int destDepth, int destStride, int destOrder,
2721 int destX, int destY, int destWidth, int destHeight,
2722 int destRedMask, int destGreenMask, int destBlueMask,
2723 bool flipX, bool flipY) {
2724 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
2725
2726 // these should be supplied as params later
2727 final int destAlphaMask = 0;
2728
2729 /*** Prepare scaling data ***/
2730 final int dwm1 = destWidth - 1;
2731 final int sfxi = (dwm1 !is 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
2732 final int dhm1 = destHeight - 1;
2733 final int sfyi = (dhm1 !is 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
2734
2735 /*** Prepare source-related data ***/
2736 final int stype;
2737 switch (srcDepth) {
2738 case 8:
2739 stype = TYPE_INDEX_8;
2740 break;
2741 case 4:
2742 srcStride <<= 1;
2743 stype = TYPE_INDEX_4;
2744 break;
2745 case 2:
2746 srcStride <<= 2;
2747 stype = TYPE_INDEX_2;
2748 break;
2749 case 1:
2750 srcStride <<= 3;
2751 stype = (srcOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
2752 break;
2753 default:
2754 //throw new IllegalArgumentException("Invalid source type");
2755 return;
2756 }
2757 int spr = srcY * srcStride + srcX;
2758
2759 /*** Prepare destination-related data ***/
2760 final int dbpp, dtype;
2761 switch (destDepth) {
2762 case 8:
2763 dbpp = 1;
2764 dtype = TYPE_GENERIC_8;
2765 break;
2766 case 16:
2767 dbpp = 2;
2768 dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
2769 break;
2770 case 24:
2771 dbpp = 3;
2772 dtype = TYPE_GENERIC_24;
2773 break;
2774 case 32:
2775 dbpp = 4;
2776 dtype = (destOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
2777 break;
2778 default:
2779 //throw new IllegalArgumentException("Invalid destination type");
2780 return;
2781 }
2782 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX) * dbpp;
2783 final int dprxi = (flipX) ? -dbpp : dbpp;
2784 final int dpryi = (flipY) ? -destStride : destStride;
2785
2786 /*** Prepare special processing data ***/
2787 int apr;
2788 if ((op & BLIT_ALPHA) !is 0) {
2789 switch (alphaMode) {
2790 case ALPHA_MASK_UNPACKED:
2791 case ALPHA_CHANNEL_SEPARATE:
2792 if (alphaData is null) alphaMode = 0x10000;
2793 apr = alphaY * alphaStride + alphaX;
2794 break;
2795 case ALPHA_MASK_PACKED:
2796 if (alphaData is null) alphaMode = 0x10000;
2797 alphaStride <<= 3;
2798 apr = alphaY * alphaStride + alphaX;
2799 break;
2800 case ALPHA_MASK_INDEX:
2801 case ALPHA_MASK_RGB:
2802 if (alphaData is null) alphaMode = 0x10000;
2803 apr = 0;
2804 break;
2805 default:
2806 alphaMode = (alphaMode << 16) / 255; // prescale
2807 case ALPHA_CHANNEL_SOURCE:
2808 apr = 0;
2809 break;
2810 }
2811 } else {
2812 alphaMode = 0x10000;
2813 apr = 0;
2814 }
2815
2816 /*** Comprehensive blit (apply transformations) ***/
2817 final int destRedShift = getChannelShift(destRedMask);
2818 final int destRedWidth = getChannelWidth(destRedMask, destRedShift);
2819 final byte[] destReds = ANY_TO_EIGHT[destRedWidth];
2820 final int destRedPreShift = 8 - destRedWidth;
2821 final int destGreenShift = getChannelShift(destGreenMask);
2822 final int destGreenWidth = getChannelWidth(destGreenMask, destGreenShift);
2823 final byte[] destGreens = ANY_TO_EIGHT[destGreenWidth];
2824 final int destGreenPreShift = 8 - destGreenWidth;
2825 final int destBlueShift = getChannelShift(destBlueMask);
2826 final int destBlueWidth = getChannelWidth(destBlueMask, destBlueShift);
2827 final byte[] destBlues = ANY_TO_EIGHT[destBlueWidth];
2828 final int destBluePreShift = 8 - destBlueWidth;
2829 final int destAlphaShift = getChannelShift(destAlphaMask);
2830 final int destAlphaWidth = getChannelWidth(destAlphaMask, destAlphaShift);
2831 final byte[] destAlphas = ANY_TO_EIGHT[destAlphaWidth];
2832 final int destAlphaPreShift = 8 - destAlphaWidth;
2833
2834 int dp = dpr;
2835 int sp = spr;
2836 int ap = apr, alpha = alphaMode;
2837 int r = 0, g = 0, b = 0, a = 0, index = 0;
2838 int rq = 0, gq = 0, bq = 0, aq = 0;
2839 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
2840 sp = spr += (sfy >>> 16) * srcStride,
2841 ap = apr += (sfy >>> 16) * alphaStride,
2842 sfy = (sfy & 0xffff) + sfyi,
2843 dp = dpr += dpryi) {
2844 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
2845 dp += dprxi,
2846 sfx = (sfx & 0xffff) + sfxi) {
2847 /*** READ NEXT PIXEL ***/
2848 switch (stype) {
2849 case TYPE_INDEX_8:
2850 index = srcData[sp] & 0xff;
2851 sp += (sfx >>> 16);
2852 break;
2853 case TYPE_INDEX_4:
2854 if ((sp & 1) !is 0) index = srcData[sp >> 1] & 0x0f;
2855 else index = (srcData[sp >> 1] >>> 4) & 0x0f;
2856 sp += (sfx >>> 16);
2857 break;
2858 case TYPE_INDEX_2:
2859 index = (srcData[sp >> 2] >>> (6 - (sp & 3) * 2)) & 0x03;
2860 sp += (sfx >>> 16);
2861 break;
2862 case TYPE_INDEX_1_MSB:
2863 index = (srcData[sp >> 3] >>> (7 - (sp & 7))) & 0x01;
2864 sp += (sfx >>> 16);
2865 break;
2866 case TYPE_INDEX_1_LSB:
2867 index = (srcData[sp >> 3] >>> (sp & 7)) & 0x01;
2868 sp += (sfx >>> 16);
2869 break;
2870 }
2871
2872 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
2873 r = srcReds[index] & 0xff;
2874 g = srcGreens[index] & 0xff;
2875 b = srcBlues[index] & 0xff;
2876 switch (alphaMode) {
2877 case ALPHA_CHANNEL_SEPARATE:
2878 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
2879 ap += (sfx >> 16);
2880 break;
2881 case ALPHA_MASK_UNPACKED:
2882 alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
2883 ap += (sfx >> 16);
2884 break;
2885 case ALPHA_MASK_PACKED:
2886 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
2887 ap += (sfx >> 16);
2888 break;
2889 case ALPHA_MASK_INDEX: { // could speed up using binary search if we sorted the indices
2890 int i = 0;
2891 while (i < alphaData.length) {
2892 if (index is (alphaData[i] & 0xff)) break;
2893 }
2894 if (i < alphaData.length) continue;
2895 } break;
2896 case ALPHA_MASK_RGB: {
2897 int i = 0;
2898 while (i < alphaData.length) {
2899 if ((r is (alphaData[i] & 0xff)) &&
2900 (g is (alphaData[i + 1] & 0xff)) &&
2901 (b is (alphaData[i + 2] & 0xff))) break;
2902 i += 3;
2903 }
2904 if (i < alphaData.length) continue;
2905 } break;
2906 }
2907 if (alpha !is 0x10000) {
2908 if (alpha is 0x0000) continue;
2909 switch (dtype) {
2910 case TYPE_GENERIC_8: {
2911 final int data = destData[dp] & 0xff;
2912 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2913 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2914 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2915 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2916 } break;
2917 case TYPE_GENERIC_16_MSB: {
2918 final int data = ((destData[dp] & 0xff) << 8) | (destData[dp + 1] & 0xff);
2919 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2920 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2921 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2922 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2923 } break;
2924 case TYPE_GENERIC_16_LSB: {
2925 final int data = ((destData[dp + 1] & 0xff) << 8) | (destData[dp] & 0xff);
2926 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2927 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2928 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2929 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2930 } break;
2931 case TYPE_GENERIC_24: {
2932 final int data = (( ((destData[dp] & 0xff) << 8) |
2933 (destData[dp + 1] & 0xff)) << 8) |
2934 (destData[dp + 2] & 0xff);
2935 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2936 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2937 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2938 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2939 } break;
2940 case TYPE_GENERIC_32_MSB: {
2941 final int data = (( (( ((destData[dp] & 0xff) << 8) |
2942 (destData[dp + 1] & 0xff)) << 8) |
2943 (destData[dp + 2] & 0xff)) << 8) |
2944 (destData[dp + 3] & 0xff);
2945 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2946 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2947 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2948 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2949 } break;
2950 case TYPE_GENERIC_32_LSB: {
2951 final int data = (( (( ((destData[dp + 3] & 0xff) << 8) |
2952 (destData[dp + 2] & 0xff)) << 8) |
2953 (destData[dp + 1] & 0xff)) << 8) |
2954 (destData[dp] & 0xff);
2955 rq = destReds[(data & destRedMask) >>> destRedShift] & 0xff;
2956 gq = destGreens[(data & destGreenMask) >>> destGreenShift] & 0xff;
2957 bq = destBlues[(data & destBlueMask) >>> destBlueShift] & 0xff;
2958 aq = destAlphas[(data & destAlphaMask) >>> destAlphaShift] & 0xff;
2959 } break;
2960 }
2961 // Perform alpha blending
2962 a = aq + ((a - aq) * alpha >> 16);
2963 r = rq + ((r - rq) * alpha >> 16);
2964 g = gq + ((g - gq) * alpha >> 16);
2965 b = bq + ((b - bq) * alpha >> 16);
2966 }
2967
2968 /*** WRITE NEXT PIXEL ***/
2969 final int data =
2970 (r >>> destRedPreShift << destRedShift) |
2971 (g >>> destGreenPreShift << destGreenShift) |
2972 (b >>> destBluePreShift << destBlueShift) |
2973 (a >>> destAlphaPreShift << destAlphaShift);
2974 switch (dtype) {
2975 case TYPE_GENERIC_8: {
2976 destData[dp] = (byte) data;
2977 } break;
2978 case TYPE_GENERIC_16_MSB: {
2979 destData[dp] = (byte) (data >>> 8);
2980 destData[dp + 1] = (byte) (data & 0xff);
2981 } break;
2982 case TYPE_GENERIC_16_LSB: {
2983 destData[dp] = (byte) (data & 0xff);
2984 destData[dp + 1] = (byte) (data >>> 8);
2985 } break;
2986 case TYPE_GENERIC_24: {
2987 destData[dp] = (byte) (data >>> 16);
2988 destData[dp + 1] = (byte) (data >>> 8);
2989 destData[dp + 2] = (byte) (data & 0xff);
2990 } break;
2991 case TYPE_GENERIC_32_MSB: {
2992 destData[dp] = (byte) (data >>> 24);
2993 destData[dp + 1] = (byte) (data >>> 16);
2994 destData[dp + 2] = (byte) (data >>> 8);
2995 destData[dp + 3] = (byte) (data & 0xff);
2996 } break;
2997 case TYPE_GENERIC_32_LSB: {
2998 destData[dp] = (byte) (data & 0xff);
2999 destData[dp + 1] = (byte) (data >>> 8);
3000 destData[dp + 2] = (byte) (data >>> 16);
3001 destData[dp + 3] = (byte) (data >>> 24);
3002 } break;
3003 }
3004 }
3005 }
3006 }
3007
3008 /**
3009 * Blits a direct palette image into an index palette image.
3010 * <p>
3011 * Note: The source and destination masks and palettes must
3012 * always be fully specified.
3013 * </p>
3014 *
3015 * @param op the blitter operation: a combination of BLIT_xxx flags
3016 * (see BLIT_xxx constants)
3017 * @param srcData the source byte array containing image data
3018 * @param srcDepth the source depth: one of 8, 16, 24, 32
3019 * @param srcStride the source number of bytes per line
3020 * @param srcOrder the source byte ordering: one of MSB_FIRST or LSB_FIRST;
3021 * ignored if srcDepth is not 16 or 32
3022 * @param srcX the top-left x-coord of the source blit region
3023 * @param srcY the top-left y-coord of the source blit region
3024 * @param srcWidth the width of the source blit region
3025 * @param srcHeight the height of the source blit region
3026 * @param srcRedMask the source red channel mask
3027 * @param srcGreenMask the source green channel mask
3028 * @param srcBlueMask the source blue channel mask
3029 * @param alphaMode the alpha blending or mask mode, may be
3030 * an integer 0-255 for global alpha; ignored if BLIT_ALPHA
3031 * not specified in the blitter operations
3032 * (see ALPHA_MODE_xxx constants)
3033 * @param alphaData the alpha blending or mask data, varies depending
3034 * on the value of alphaMode and sometimes ignored
3035 * @param alphaStride the alpha data number of bytes per line
3036 * @param alphaX the top-left x-coord of the alpha blit region
3037 * @param alphaY the top-left y-coord of the alpha blit region
3038 * @param destData the destination byte array containing image data
3039 * @param destDepth the destination depth: one of 1, 2, 4, 8
3040 * @param destStride the destination number of bytes per line
3041 * @param destOrder the destination byte ordering: one of MSB_FIRST or LSB_FIRST;
3042 * ignored if destDepth is not 1
3043 * @param destX the top-left x-coord of the destination blit region
3044 * @param destY the top-left y-coord of the destination blit region
3045 * @param destWidth the width of the destination blit region
3046 * @param destHeight the height of the destination blit region
3047 * @param destReds the destination palette red component intensities
3048 * @param destGreens the destination palette green component intensities
3049 * @param destBlues the destination palette blue component intensities
3050 * @param flipX if true the resulting image is flipped along the vertical axis
3051 * @param flipY if true the resulting image is flipped along the horizontal axis
3052 */
3053 static void blit(int op,
3054 byte[] srcData, int srcDepth, int srcStride, int srcOrder,
3055 int srcX, int srcY, int srcWidth, int srcHeight,
3056 int srcRedMask, int srcGreenMask, int srcBlueMask,
3057 int alphaMode, byte[] alphaData, int alphaStride, int alphaX, int alphaY,
3058 byte[] destData, int destDepth, int destStride, int destOrder,
3059 int destX, int destY, int destWidth, int destHeight,
3060 byte[] destReds, byte[] destGreens, byte[] destBlues,
3061 bool flipX, bool flipY) {
3062 if ((destWidth <= 0) || (destHeight <= 0) || (alphaMode is ALPHA_TRANSPARENT)) return;
3063
3064 // these should be supplied as params later
3065 final int srcAlphaMask = 0;
3066
3067 /*** Prepare scaling data ***/
3068 final int dwm1 = destWidth - 1;
3069 final int sfxi = (dwm1 !is 0) ? (int)((((long)srcWidth << 16) - 1) / dwm1) : 0;
3070 final int dhm1 = destHeight - 1;
3071 final int sfyi = (dhm1 !is 0) ? (int)((((long)srcHeight << 16) - 1) / dhm1) : 0;
3072
3073 /*** Prepare source-related data ***/
3074 final int sbpp, stype;
3075 switch (srcDepth) {
3076 case 8:
3077 sbpp = 1;
3078 stype = TYPE_GENERIC_8;
3079 break;
3080 case 16:
3081 sbpp = 2;
3082 stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB;
3083 break;
3084 case 24:
3085 sbpp = 3;
3086 stype = TYPE_GENERIC_24;
3087 break;
3088 case 32:
3089 sbpp = 4;
3090 stype = (srcOrder is MSB_FIRST) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB;
3091 break;
3092 default:
3093 //throw new IllegalArgumentException("Invalid source type");
3094 return;
3095 }
3096 int spr = srcY * srcStride + srcX * sbpp;
3097
3098 /*** Prepare destination-related data ***/
3099 final int dtype;
3100 switch (destDepth) {
3101 case 8:
3102 dtype = TYPE_INDEX_8;
3103 break;
3104 case 4:
3105 destStride <<= 1;
3106 dtype = TYPE_INDEX_4;
3107 break;
3108 case 2:
3109 destStride <<= 2;
3110 dtype = TYPE_INDEX_2;
3111 break;
3112 case 1:
3113 destStride <<= 3;
3114 dtype = (destOrder is MSB_FIRST) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB;
3115 break;
3116 default:
3117 //throw new IllegalArgumentException("Invalid source type");
3118 return;
3119 }
3120 int dpr = ((flipY) ? destY + dhm1 : destY) * destStride + ((flipX) ? destX + dwm1 : destX);
3121 final int dprxi = (flipX) ? -1 : 1;
3122 final int dpryi = (flipY) ? -destStride : destStride;
3123
3124 /*** Prepare special processing data ***/
3125 int apr;
3126 if ((op & BLIT_ALPHA) !is 0) {
3127 switch (alphaMode) {
3128 case ALPHA_MASK_UNPACKED:
3129 case ALPHA_CHANNEL_SEPARATE:
3130 if (alphaData is null) alphaMode = 0x10000;
3131 apr = alphaY * alphaStride + alphaX;
3132 break;
3133 case ALPHA_MASK_PACKED:
3134 if (alphaData is null) alphaMode = 0x10000;
3135 alphaStride <<= 3;
3136 apr = alphaY * alphaStride + alphaX;
3137 break;
3138 case ALPHA_MASK_INDEX:
3139 //throw new IllegalArgumentException("Invalid alpha type");
3140 return;
3141 case ALPHA_MASK_RGB:
3142 if (alphaData is null) alphaMode = 0x10000;
3143 apr = 0;
3144 break;
3145 default:
3146 alphaMode = (alphaMode << 16) / 255; // prescale
3147 case ALPHA_CHANNEL_SOURCE:
3148 apr = 0;
3149 break;
3150 }
3151 } else {
3152 alphaMode = 0x10000;
3153 apr = 0;
3154 }
3155 final bool ditherEnabled = (op & BLIT_DITHER) !is 0;
3156
3157 /*** Comprehensive blit (apply transformations) ***/
3158 final int srcRedShift = getChannelShift(srcRedMask);
3159 final byte[] srcReds = ANY_TO_EIGHT[getChannelWidth(srcRedMask, srcRedShift)];
3160 final int srcGreenShift = getChannelShift(srcGreenMask);
3161 final byte[] srcGreens = ANY_TO_EIGHT[getChannelWidth(srcGreenMask, srcGreenShift)];
3162 final int srcBlueShift = getChannelShift(srcBlueMask);
3163 final byte[] srcBlues = ANY_TO_EIGHT[getChannelWidth(srcBlueMask, srcBlueShift)];
3164 final int srcAlphaShift = getChannelShift(srcAlphaMask);
3165 final byte[] srcAlphas = ANY_TO_EIGHT[getChannelWidth(srcAlphaMask, srcAlphaShift)];
3166
3167 int dp = dpr;
3168 int sp = spr;
3169 int ap = apr, alpha = alphaMode;
3170 int r = 0, g = 0, b = 0, a = 0;
3171 int indexq = 0;
3172 int lastindex = 0, lastr = -1, lastg = -1, lastb = -1;
3173 final int[] rerr, gerr, berr;
3174 int destPaletteSize = 1 << destDepth;
3175 if ((destReds !is null) && (destReds.length < destPaletteSize)) destPaletteSize = destReds.length;
3176 if (ditherEnabled) {
3177 rerr = new int[destWidth + 2];
3178 gerr = new int[destWidth + 2];
3179 berr = new int[destWidth + 2];
3180 } else {
3181 rerr = null; gerr = null; berr = null;
3182 }
3183 for (int dy = destHeight, sfy = sfyi; dy > 0; --dy,
3184 sp = spr += (sfy >>> 16) * srcStride,
3185 ap = apr += (sfy >>> 16) * alphaStride,
3186 sfy = (sfy & 0xffff) + sfyi,
3187 dp = dpr += dpryi) {
3188 int lrerr = 0, lgerr = 0, lberr = 0;
3189 for (int dx = destWidth, sfx = sfxi; dx > 0; --dx,
3190 dp += dprxi,
3191 sfx = (sfx & 0xffff) + sfxi) {
3192 /*** READ NEXT PIXEL ***/
3193 switch (stype) {
3194 case TYPE_GENERIC_8: {
3195 final int data = srcData[sp] & 0xff;
3196 sp += (sfx >>> 16);
3197 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
3198 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
3199 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
3200 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
3201 } break;
3202 case TYPE_GENERIC_16_MSB: {
3203 final int data = ((srcData[sp] & 0xff) << 8) | (srcData[sp + 1] & 0xff);
3204 sp += (sfx >>> 16) * 2;
3205 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
3206 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
3207 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
3208 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
3209 } break;
3210 case TYPE_GENERIC_16_LSB: {
3211 final int data = ((srcData[sp + 1] & 0xff) << 8) | (srcData[sp] & 0xff);
3212 sp += (sfx >>> 16) * 2;
3213 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
3214 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
3215 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
3216 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
3217 } break;
3218 case TYPE_GENERIC_24: {
3219 final int data = (( ((srcData[sp] & 0xff) << 8) |
3220 (srcData[sp + 1] & 0xff)) << 8) |
3221 (srcData[sp + 2] & 0xff);
3222 sp += (sfx >>> 16) * 3;
3223 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
3224 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
3225 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
3226 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
3227 } break;
3228 case TYPE_GENERIC_32_MSB: {
3229 final int data = (( (( ((srcData[sp] & 0xff) << 8) |
3230 (srcData[sp + 1] & 0xff)) << 8) |
3231 (srcData[sp + 2] & 0xff)) << 8) |
3232 (srcData[sp + 3] & 0xff);
3233 sp += (sfx >>> 16) * 4;
3234 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
3235 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
3236 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
3237 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
3238 } break;
3239 case TYPE_GENERIC_32_LSB: {
3240 final int data = (( (( ((srcData[sp + 3] & 0xff) << 8) |
3241 (srcData[sp + 2] & 0xff)) << 8) |
3242 (srcData[sp + 1] & 0xff)) << 8) |
3243 (srcData[sp] & 0xff);
3244 sp += (sfx >>> 16) * 4;
3245 r = srcReds[(data & srcRedMask) >>> srcRedShift] & 0xff;
3246 g = srcGreens[(data & srcGreenMask) >>> srcGreenShift] & 0xff;
3247 b = srcBlues[(data & srcBlueMask) >>> srcBlueShift] & 0xff;
3248 a = srcAlphas[(data & srcAlphaMask) >>> srcAlphaShift] & 0xff;
3249 } break;
3250 }
3251
3252 /*** DO SPECIAL PROCESSING IF REQUIRED ***/
3253 switch (alphaMode) {
3254 case ALPHA_CHANNEL_SEPARATE:
3255 alpha = ((alphaData[ap] & 0xff) << 16) / 255;
3256 ap += (sfx >> 16);
3257 break;
3258 case ALPHA_CHANNEL_SOURCE:
3259 alpha = (a << 16) / 255;
3260 break;
3261 case ALPHA_MASK_UNPACKED:
3262 alpha = (alphaData[ap] !is 0) ? 0x10000 : 0;
3263 ap += (sfx >> 16);
3264 break;
3265 case ALPHA_MASK_PACKED:
3266 alpha = (alphaData[ap >> 3] << ((ap & 7) + 9)) & 0x10000;
3267 ap += (sfx >> 16);
3268 break;
3269 case ALPHA_MASK_RGB:
3270 alpha = 0x10000;
3271 for (int i = 0; i < alphaData.length; i += 3) {
3272 if ((r is alphaData[i]) && (g is alphaData[i + 1]) && (b is alphaData[i + 2])) {
3273 alpha = 0x0000;
3274 break;
3275 }
3276 }
3277 break;
3278 }
3279 if (alpha !is 0x10000) {
3280 if (alpha is 0x0000) continue;
3281 switch (dtype) {
3282 case TYPE_INDEX_8:
3283 indexq = destData[dp] & 0xff;
3284 break;
3285 case TYPE_INDEX_4:
3286 if ((dp & 1) !is 0) indexq = destData[dp >> 1] & 0x0f;
3287 else indexq = (destData[dp >> 1] >>> 4) & 0x0f;
3288 break;
3289 case TYPE_INDEX_2:
3290 indexq = (destData[dp >> 2] >>> (6 - (dp & 3) * 2)) & 0x03;
3291 break;
3292 case TYPE_INDEX_1_MSB:
3293 indexq = (destData[dp >> 3] >>> (7 - (dp & 7))) & 0x01;
3294 break;
3295 case TYPE_INDEX_1_LSB:
3296 indexq = (destData[dp >> 3] >>> (dp & 7)) & 0x01;
3297 break;
3298 }
3299 // Perform alpha blending
3300 final int rq = destReds[indexq] & 0xff;
3301 final int gq = destGreens[indexq] & 0xff;
3302 final int bq = destBlues[indexq] & 0xff;
3303 r = rq + ((r - rq) * alpha >> 16);
3304 g = gq + ((g - gq) * alpha >> 16);
3305 b = bq + ((b - bq) * alpha >> 16);
3306 }
3307
3308 /*** MAP COLOR TO THE PALETTE ***/
3309 if (ditherEnabled) {
3310 // Floyd-Steinberg error diffusion
3311 r += rerr[dx] >> 4;
3312 if (r < 0) r = 0; else if (r > 255) r = 255;
3313 g += gerr[dx] >> 4;
3314 if (g < 0) g = 0; else if (g > 255) g = 255;
3315 b += berr[dx] >> 4;
3316 if (b < 0) b = 0; else if (b > 255) b = 255;
3317 rerr[dx] = lrerr;
3318 gerr[dx] = lgerr;
3319 berr[dx] = lberr;
3320 }
3321 if (r !is lastr || g !is lastg || b !is lastb) {
3322 // moving the variable declarations out seems to make the JDK JIT happier...
3323 for (int j = 0, dr, dg, db, distance, minDistance = 0x7fffffff; j < destPaletteSize; ++j) {
3324 dr = (destReds[j] & 0xff) - r;
3325 dg = (destGreens[j] & 0xff) - g;
3326 db = (destBlues[j] & 0xff) - b;
3327 distance = dr * dr + dg * dg + db * db;
3328 if (distance < minDistance) {
3329 lastindex = j;
3330 if (distance is 0) break;
3331 minDistance = distance;
3332 }
3333 }
3334 lastr = r; lastg = g; lastb = b;
3335 }
3336 if (ditherEnabled) {
3337 // Floyd-Steinberg error diffusion, cont'd...
3338 final int dxm1 = dx - 1, dxp1 = dx + 1;
3339 int acc;
3340 rerr[dxp1] += acc = (lrerr = r - (destReds[lastindex] & 0xff)) + lrerr + lrerr;
3341 rerr[dx] += acc += lrerr + lrerr;
3342 rerr[dxm1] += acc + lrerr + lrerr;
3343 gerr[dxp1] += acc = (lgerr = g - (destGreens[lastindex] & 0xff)) + lgerr + lgerr;
3344 gerr[dx] += acc += lgerr + lgerr;
3345 gerr[dxm1] += acc + lgerr + lgerr;
3346 berr[dxp1] += acc = (lberr = b - (destBlues[lastindex] & 0xff)) + lberr + lberr;
3347 berr[dx] += acc += lberr + lberr;
3348 berr[dxm1] += acc + lberr + lberr;
3349 }
3350
3351 /*** WRITE NEXT PIXEL ***/
3352 switch (dtype) {
3353 case TYPE_INDEX_8:
3354 destData[dp] = (byte) lastindex;
3355 break;
3356 case TYPE_INDEX_4:
3357 if ((dp & 1) !is 0) destData[dp >> 1] = (byte)((destData[dp >> 1] & 0xf0) | lastindex);
3358 else destData[dp >> 1] = (byte)((destData[dp >> 1] & 0x0f) | (lastindex << 4));
3359 break;
3360 case TYPE_INDEX_2: {
3361 final int shift = 6 - (dp & 3) * 2;
3362 destData[dp >> 2] = (byte)(destData[dp >> 2] & ~(0x03 << shift) | (lastindex << shift));
3363 } break;
3364 case TYPE_INDEX_1_MSB: {
3365 final int shift = 7 - (dp & 7);
3366 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
3367 } break;
3368 case TYPE_INDEX_1_LSB: {
3369 final int shift = dp & 7;
3370 destData[dp >> 3] = (byte)(destData[dp >> 3] & ~(0x01 << shift) | (lastindex << shift));
3371 } break;
3372 }
3373 }
3374 }
3375 }
3376
3377 /**
3378 * Computes the required channel shift from a mask.
3379 */
3380 static int getChannelShift(int mask) {
3381 if (mask is 0) return 0;
3382 int i;
3383 for (i = 0; ((mask & 1) is 0) && (i < 32); ++i) {
3384 mask >>>= 1;
3385 }
3386 return i;
3387 }
3388
3389 /**
3390 * Computes the required channel width (depth) from a mask.
3391 */
3392 static int getChannelWidth(int mask, int shift) {
3393 if (mask is 0) return 0;
3394 int i;
3395 mask >>>= shift;
3396 for (i = shift; ((mask & 1) !is 0) && (i < 32); ++i) {
3397 mask >>>= 1;
3398 }
3399 return i - shift;
3400 }
3401
3402 /**
3403 * Extracts a field from packed RGB data given a mask for that field.
3404 */
3405 static byte getChannelField(int data, int mask) {
3406 final int shift = getChannelShift(mask);
3407 return ANY_TO_EIGHT[getChannelWidth(mask, shift)][(data & mask) >>> shift];
3408 }
3409
3410 /**
3411 * Creates an ImageData containing one band's worth of a gradient filled
3412 * block. If <code>vertical</code> is true, the band must be tiled
3413 * horizontally to fill a region, otherwise it must be tiled vertically.
3414 *
3415 * @param width the width of the region to be filled
3416 * @param height the height of the region to be filled
3417 * @param vertical if true sweeps from top to bottom, else
3418 * sweeps from left to right
3419 * @param fromRGB the color to start with
3420 * @param toRGB the color to end with
3421 * @param redBits the number of significant red bits, 0 for palette modes
3422 * @param greenBits the number of significant green bits, 0 for palette modes
3423 * @param blueBits the number of significant blue bits, 0 for palette modes
3424 * @return the new ImageData
3425 */
3426 static ImageData createGradientBand(
3427 int width, int height, bool vertical,
3428 RGB fromRGB, RGB toRGB,
3429 int redBits, int greenBits, int blueBits) {
3430 /* Gradients are drawn as tiled bands */
3431 final int bandWidth, bandHeight, bitmapDepth;
3432 final byte[] bitmapData;
3433 final PaletteData paletteData;
3434 /* Select an algorithm depending on the depth of the screen */
3435 if (redBits !is 0 && greenBits !is 0 && blueBits !is 0) {
3436 paletteData = new PaletteData(0x0000ff00, 0x00ff0000, 0xff000000);
3437 bitmapDepth = 32;
3438 if (redBits >= 8 && greenBits >= 8 && blueBits >= 8) {
3439 /* Precise color */
3440 final int steps;
3441 if (vertical) {
3442 bandWidth = 1;
3443 bandHeight = height;
3444 steps = bandHeight > 1 ? bandHeight - 1 : 1;
3445 } else {
3446 bandWidth = width;
3447 bandHeight = 1;
3448 steps = bandWidth > 1 ? bandWidth - 1 : 1;
3449 }
3450 final int bytesPerLine = bandWidth * 4;
3451 bitmapData = new byte[bandHeight * bytesPerLine];
3452 buildPreciseGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine);
3453 buildPreciseGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine);
3454 buildPreciseGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine);
3455 } else {
3456 /* Dithered color */
3457 final int steps;
3458 if (vertical) {
3459 bandWidth = (width < 8) ? width : 8;
3460 bandHeight = height;
3461 steps = bandHeight > 1 ? bandHeight - 1 : 1;
3462 } else {
3463 bandWidth = width;
3464 bandHeight = (height < 8) ? height : 8;
3465 steps = bandWidth > 1 ? bandWidth - 1 : 1;
3466 }
3467 final int bytesPerLine = bandWidth * 4;
3468 bitmapData = new byte[bandHeight * bytesPerLine];
3469 buildDitheredGradientChannel(fromRGB.blue, toRGB.blue, steps, bandWidth, bandHeight, vertical, bitmapData, 0, bytesPerLine, blueBits);
3470 buildDitheredGradientChannel(fromRGB.green, toRGB.green, steps, bandWidth, bandHeight, vertical, bitmapData, 1, bytesPerLine, greenBits);
3471 buildDitheredGradientChannel(fromRGB.red, toRGB.red, steps, bandWidth, bandHeight, vertical, bitmapData, 2, bytesPerLine, redBits);
3472 }
3473 } else {
3474 /* Dithered two tone */
3475 paletteData = new PaletteData(new RGB[] { fromRGB, toRGB });
3476 bitmapDepth = 8;
3477 final int blendi;
3478 if (vertical) {
3479 bandWidth = (width < 8) ? width : 8;
3480 bandHeight = height;
3481 blendi = (bandHeight > 1) ? 0x1040000 / (bandHeight - 1) + 1 : 1;
3482 } else {
3483 bandWidth = width;
3484 bandHeight = (height < 8) ? height : 8;
3485 blendi = (bandWidth > 1) ? 0x1040000 / (bandWidth - 1) + 1 : 1;
3486 }
3487 final int bytesPerLine = (bandWidth + 3) & -4;
3488 bitmapData = new byte[bandHeight * bytesPerLine];
3489 if (vertical) {
3490 for (int dy = 0, blend = 0, dp = 0; dy < bandHeight;
3491 ++dy, blend += blendi, dp += bytesPerLine) {
3492 for (int dx = 0; dx < bandWidth; ++dx) {
3493 bitmapData[dp + dx] = (blend + DITHER_MATRIX[dy & 7][dx]) <
3494 0x1000000 ? (byte)0 : (byte)1;
3495 }
3496 }
3497 } else {
3498 for (int dx = 0, blend = 0; dx < bandWidth; ++dx, blend += blendi) {
3499 for (int dy = 0, dptr = dx; dy < bandHeight; ++dy, dptr += bytesPerLine) {
3500 bitmapData[dptr] = (blend + DITHER_MATRIX[dy][dx & 7]) <
3501 0x1000000 ? (byte)0 : (byte)1;
3502 }
3503 }
3504 }
3505 }
3506 return new ImageData(bandWidth, bandHeight, bitmapDepth, paletteData, 4, bitmapData);
3507 }
3508
3509 /*
3510 * Fill in gradated values for a color channel
3511 */
3512 static final void buildPreciseGradientChannel(int from, int to, int steps,
3513 int bandWidth, int bandHeight, bool vertical,
3514 byte[] bitmapData, int dp, int bytesPerLine) {
3515 int val = from << 16;
3516 final int inc = ((to << 16) - val) / steps + 1;
3517 if (vertical) {
3518 for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
3519 bitmapData[dp] = (byte)(val >>> 16);
3520 val += inc;
3521 }
3522 } else {
3523 for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
3524 bitmapData[dp] = (byte)(val >>> 16);
3525 val += inc;
3526 }
3527 }
3528 }
3529
3530 /*
3531 * Fill in dithered gradated values for a color channel
3532 */
3533 static final void buildDitheredGradientChannel(int from, int to, int steps,
3534 int bandWidth, int bandHeight, bool vertical,
3535 byte[] bitmapData, int dp, int bytesPerLine, int bits) {
3536 final int mask = 0xff00 >>> bits;
3537 int val = from << 16;
3538 final int inc = ((to << 16) - val) / steps + 1;
3539 if (vertical) {
3540 for (int dy = 0; dy < bandHeight; ++dy, dp += bytesPerLine) {
3541 for (int dx = 0, dptr = dp; dx < bandWidth; ++dx, dptr += 4) {
3542 final int thresh = DITHER_MATRIX[dy & 7][dx] >>> bits;
3543 int temp = val + thresh;
3544 if (temp > 0xffffff) bitmapData[dptr] = -1;
3545 else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
3546 }
3547 val += inc;
3548 }
3549 } else {
3550 for (int dx = 0; dx < bandWidth; ++dx, dp += 4) {
3551 for (int dy = 0, dptr = dp; dy < bandHeight; ++dy, dptr += bytesPerLine) {
3552 final int thresh = DITHER_MATRIX[dy][dx & 7] >>> bits;
3553 int temp = val + thresh;
3554 if (temp > 0xffffff) bitmapData[dptr] = -1;
3555 else bitmapData[dptr] = (byte)((temp >>> 16) & mask);
3556 }
3557 val += inc;
3558 }
3559 }
3560 }
3561
3562 /**
3563 * Renders a gradient onto a GC.
3564 * <p>
3565 * This is a GC helper.
3566 * </p>
3567 *
3568 * @param gc the GC to render the gradient onto
3569 * @param device the device the GC belongs to
3570 * @param x the top-left x coordinate of the region to be filled
3571 * @param y the top-left y coordinate of the region to be filled
3572 * @param width the width of the region to be filled
3573 * @param height the height of the region to be filled
3574 * @param vertical if true sweeps from top to bottom, else
3575 * sweeps from left to right
3576 * @param fromRGB the color to start with
3577 * @param toRGB the color to end with
3578 * @param redBits the number of significant red bits, 0 for palette modes
3579 * @param greenBits the number of significant green bits, 0 for palette modes
3580 * @param blueBits the number of significant blue bits, 0 for palette modes
3581 */
3582 static void fillGradientRectangle(GC gc, Device device,
3583 int x, int y, int width, int height, bool vertical,
3584 RGB fromRGB, RGB toRGB,
3585 int redBits, int greenBits, int blueBits) {
3586 /* Create the bitmap and tile it */
3587 ImageData band = createGradientBand(width, height, vertical,
3588 fromRGB, toRGB, redBits, greenBits, blueBits);
3589 Image image = new Image(device, band);
3590 if ((band.width is 1) || (band.height is 1)) {
3591 gc.drawImage(image, 0, 0, band.width, band.height, x, y, width, height);
3592 } else {
3593 if (vertical) {
3594 for (int dx = 0; dx < width; dx += band.width) {
3595 int blitWidth = width - dx;
3596 if (blitWidth > band.width) blitWidth = band.width;
3597 gc.drawImage(image, 0, 0, blitWidth, band.height, dx + x, y, blitWidth, band.height);
3598 }
3599 } else {
3600 for (int dy = 0; dy < height; dy += band.height) {
3601 int blitHeight = height - dy;
3602 if (blitHeight > band.height) blitHeight = band.height;
3603 gc.drawImage(image, 0, 0, band.width, blitHeight, x, dy + y, band.width, blitHeight);
3604 }
3605 }
3606 }
3607 image.dispose();
3608 }
3609
3610 }