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