comparison org/eclipse/swt/graphics/ImageData.d @ 5:de77855733ca

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