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