1 /* 2 * dimage - base.d 3 * by Laszlo Szeremi 4 * 5 * Copyright under Boost Software License. 6 */ 7 8 module dimage.base; 9 10 import std.bitmanip; 11 import std.range : InputRange; 12 import std.conv : to; 13 14 import dimage.util; 15 16 import bitleveld.datatypes; 17 18 public import dimage.types; 19 public import dimage.exceptions; 20 21 /** 22 * Interface for accessing metadata within images. 23 * Any metadata that's not supported should return null. 24 */ 25 public interface ImageMetadata { 26 public string getID() @safe pure; 27 public string getAuthor() @safe pure; 28 public string getComment() @safe pure; 29 public string getJobName() @safe pure; 30 public string getSoftwareInfo() @safe pure; 31 public string getSoftwareVersion() @safe pure; 32 public string getDescription() @safe pure; 33 public string getSource() @safe pure; 34 public string getCopyright() @safe pure; 35 public string getCreationTimeStr() @safe pure; 36 37 public string setID(string val) @safe pure; 38 public string setAuthor(string val) @safe pure; 39 public string setComment(string val) @safe pure; 40 public string setJobName(string val) @safe pure; 41 public string setSoftwareInfo(string val) @safe pure; 42 public string setSoftwareVersion(string val) @safe pure; 43 public string setDescription(string val) @safe pure; 44 public string setSource(string val) @safe pure; 45 public string setCopyright(string val) @safe pure; 46 public string setCreationTime(string val) @safe pure; 47 } 48 /** 49 * Allows to access custom-tagged textual metadata in images. 50 */ 51 public interface CustomImageMetadata : ImageMetadata { 52 /** 53 * Returns the metadata with the given `id`. 54 * Returns null if not found. 55 */ 56 public string getMetadata(string id) @safe pure; 57 /** 58 * Sets the given metadata to `val` at the given `id`, then returns the new value. 59 */ 60 public string setMetadata(string id, string val) @safe pure; 61 } 62 /** 63 * Interface for common multi-image (eg. animation) functions. 64 */ 65 public interface MultiImage { 66 ///Returns which image is being set to be worked on. 67 public uint getCurrentImage() @safe pure; 68 ///Sets which image is being set to be worked on. 69 public uint setCurrentImage(uint frame) @safe pure; 70 ///Sets the current image to the static if available 71 public void setStaticImage() @safe pure; 72 ///Number of images in a given multi-image. 73 public uint nOfImages() @property @safe @nogc pure const; 74 ///Returns the frame duration in hmsec if animation for the given frame. 75 ///Returns 0 if not an animation. 76 public uint frameTime() @property @safe @nogc pure const; 77 ///Returns true if the multi-image is animated 78 public bool isAnimation() @property @safe @nogc pure const; 79 } 80 /** 81 * Basic palette wrapper. 82 */ 83 public interface IPalette { 84 ///Returns the number of indexes within the palette. 85 public @property size_t length() @nogc @safe pure nothrow const; 86 ///Returns the bitdepth of the palette. 87 public @property ubyte bitDepth() @nogc @safe pure nothrow const; 88 ///Returns the color format of the palette. 89 public @property uint paletteFormat() @nogc @safe pure nothrow const; 90 ///Converts the palette to the given format if supported 91 public IPalette convTo(uint format) @safe; 92 ///Reads palette in standard indexed format 93 public ARGB8888 read(size_t index) @safe pure; 94 ///Reads palette in standard floating point format 95 public RGBA_f32 readF(size_t index) @safe pure; 96 ///Returns the raw data cast to ubyte 97 public ubyte[] raw() @safe pure; 98 } 99 /** 100 * Contains palette information. 101 * Implements some range capabilities. 102 */ 103 public class Palette(T) : IPalette { 104 protected T[] data; ///Raw data 105 //protected size_t _length;///The number of items in the palette (should be less than 65536) 106 protected size_t begin, end; 107 protected uint format; 108 protected ubyte _bitDepth; 109 ///CTOR 110 this(T[] data, uint format, ubyte bitDepth) @nogc @safe pure nothrow { 111 this.data = data; 112 this.format = format; 113 _bitDepth = bitDepth; 114 end = data.length; 115 } 116 ///Copy CTOR 117 this(Palette!T src) @nogc @safe pure nothrow { 118 data = src.data; 119 format = src.format; 120 _bitDepth = src._bitDepth; 121 end = data.length; 122 } 123 ///Returns the number of indexes within the palette. 124 public @property size_t length() @nogc @safe pure nothrow const { 125 return data.length; 126 } 127 ///Returns the bitdepth of the palette. 128 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 129 return _bitDepth; 130 } 131 ///Returns the color format of the palette. 132 public @property uint paletteFormat() @nogc @safe pure nothrow const { 133 return format; 134 } 135 ///Converts the palette to the given format if supported. 136 public IPalette convTo(uint format) @safe { 137 IPalette result; 138 void converter(OutputType)() @safe { 139 OutputType[] array; 140 array.length = data.length; 141 for(int i ; i < data.length ; i++) 142 array[i] = OutputType(data[i]); 143 result = new Palette!OutputType(array, format, getBitDepth(format)); 144 } 145 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 146 case PixelFormat.RGB888: 147 if(format & PixelFormat.BigEndian) 148 converter!(RGB888BE); 149 else 150 converter!(RGB888); 151 break; 152 case PixelFormat.RGBX8888: 153 if(format & PixelFormat.BigEndian) 154 converter!(RGBA8888BE); 155 else 156 converter!(RGBA8888); 157 break; 158 case PixelFormat.XRGB8888: 159 if(format & PixelFormat.BigEndian) 160 converter!(ARGB8888BE); 161 else 162 converter!(ARGB8888); 163 break; 164 case PixelFormat.RGB565: 165 converter!(RGB565); 166 break; 167 case PixelFormat.RGBX5551: 168 converter!(RGBA5551); 169 break; 170 default: 171 throw new ImageFormatException("Format not supported"); 172 } 173 return result; 174 } 175 ///Returns the raw data as an array. 176 public T[] getRawData() @nogc @safe pure nothrow { 177 return data; 178 } 179 ///Reads palette in standard format. 180 public ARGB8888 read(size_t index) @safe pure { 181 if (index < data.length) return ARGB8888(data[index]); 182 else throw new PaletteBoundsException("Palette is being read out of bounds!"); 183 } 184 ///Reads palette in standard format. 185 public RGBA_f32 readF(size_t index) @safe pure { 186 if (index < data.length) return RGBA_f32(data[index]); 187 else throw new PaletteBoundsException("Palette is being read out of bounds!"); 188 } 189 ///Palette indexing. 190 public ref T opIndex(size_t index) @safe pure { 191 if (index < data.length) return data[index]; 192 else throw new PaletteBoundsException("Palette is being read out of bounds!"); 193 } 194 /+ 195 ///Assigns a value to the given index 196 public T opIndexAssign(T value, size_t index) @safe pure { 197 if (index < data.length) return data[index] = value; 198 else throw new PaletteBoundsException("Palette is being read out of bounds!"); 199 }+/ 200 ///Returns true if the range have reached its end 201 public @property bool empty() @nogc @safe pure nothrow const { 202 return begin == end; 203 } 204 ///Returns the element at the front 205 public ref T front() @nogc @safe pure nothrow { 206 return data[begin]; 207 } 208 alias opDollar = length; 209 ///Moves the front pointer forward by one 210 public void popFront() @nogc @safe pure nothrow { 211 if (begin != end) begin++; 212 } 213 ///Moves the front pointer forward by one and returns the element 214 public ref T moveFront() @nogc @safe pure nothrow { 215 if (begin != end) return data[begin++]; 216 else return data[begin]; 217 } 218 ///Returns the element at the back 219 public ref T back() @nogc @safe pure nothrow { 220 return data[end - 1]; 221 } 222 ///Moves the back pointer backward by one 223 public void popBack() @nogc @safe pure nothrow { 224 if (begin != end) end--; 225 } 226 ///Moves the back pointer backward and returns the element 227 public ref T moveBack() @nogc @safe pure nothrow { 228 if (begin != end) return data[--end]; 229 else return data[end]; 230 } 231 ///Creates a copy with the front and back pointers reset 232 public Palette!T save() @safe pure nothrow { 233 return new Palette!T(this); 234 } 235 ///Returns the raw data cast to ubyte 236 public ubyte[] raw() @safe pure { 237 return reinterpretCast!ubyte(data); 238 } 239 } 240 /** 241 * Palette with separate alpha field, used primarily by PNG. 242 */ 243 public class PaletteWithSepA(T) : Palette!T { 244 protected ubyte[] alphaField; 245 ///CTOR 246 this(T[] data, ubyte[] alphaField, uint format, ubyte bitDepth) @nogc @safe pure nothrow { 247 assert(data.length == alphaField.length); 248 super(data, format, bitDepth); 249 this.alphaField = alphaField; 250 } 251 ///Reads palette in standard format. 252 public override ARGB8888 read(size_t index) @safe pure { 253 if (index < data.length) return ARGB8888(data[index].r, data[index].g, data[index].b, alphaField[index]); 254 else throw new PaletteBoundsException("Palette is being read out of bounds!"); 255 } 256 ///Reads palette in standard format. 257 public override RGBA_f32 readF(size_t index) @safe pure { 258 if (index < data.length) { 259 RGBA_f32 result = RGBA_f32(data[index]); 260 result.fA = alphaField[index] * (1.0 / ubyte.max); 261 return result; 262 } 263 else throw new PaletteBoundsException("Palette is being read out of bounds!"); 264 } 265 ///Returns the raw data cast to ubyte 266 public override ubyte[] raw() @safe pure { 267 return reinterpretCast!ubyte(data) ~ alphaField; 268 } 269 ///Converts the palette to the given format if supported. 270 public override IPalette convTo(uint format) @safe { 271 IPalette result; 272 void converter(OutputType)() @safe { 273 OutputType[] array; 274 array.length = data.length; 275 for(int i ; i < data.length ; i++) 276 array[i] = OutputType(read(i)); 277 result = new Palette!OutputType(array, format, getBitDepth(format)); 278 } 279 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 280 case PixelFormat.RGB888: 281 if(format & PixelFormat.BigEndian) 282 converter!(RGB888BE); 283 else 284 converter!(RGB888); 285 break; 286 case PixelFormat.RGBX8888: 287 if(format & PixelFormat.BigEndian) 288 converter!(RGBA8888BE); 289 else 290 converter!(RGBA8888); 291 break; 292 case PixelFormat.XRGB8888: 293 if(format & PixelFormat.BigEndian) 294 converter!(ARGB8888BE); 295 else 296 converter!(ARGB8888); 297 break; 298 case PixelFormat.RGB565: 299 converter!(RGB565); 300 break; 301 case PixelFormat.RGBX5551: 302 converter!(RGBA5551); 303 break; 304 default: 305 throw new ImageFormatException("Format not supported"); 306 } 307 return result; 308 } 309 } 310 /** 311 * Auxiliary data wrapper. 312 * Stores data like filters for PNG images. 313 */ 314 public interface AuxData { 315 ///Returns the type of the auxiliary data. 316 public @property uint type() @nogc @safe pure nothrow const; 317 } 318 /** 319 * Frame data for animation. 320 */ 321 public class AnimData { 322 public uint hOffset; ///Horizontal offset 323 public uint vOffset; ///Vertical offset 324 public uint hold; ///msecs to display this animation 325 } 326 /** 327 * Basic imagedata wrapper. 328 */ 329 public interface IImageData { 330 ///Returns the width of the image. 331 public @property uint width() @nogc @safe pure nothrow const; 332 ///Returns the height of the image. 333 public @property uint height() @nogc @safe pure nothrow const; 334 ///Returns the bitdepth of the image. 335 public @property ubyte bitDepth() @nogc @safe pure nothrow const; 336 ///Returns the number of bitplanes per image. 337 ///Default should be 1. 338 public @property ubyte bitplanes() @nogc @safe pure nothrow const; 339 ///Returns the color format of the image. 340 public @property uint pixelFormat() @nogc @safe pure nothrow const; 341 ///Converts the imagedata to the given format if supported 342 public IImageData convTo(uint format) @safe; 343 ///Reads the image at the given point in ARGB32 format. 344 ///Does palette lookup if needed. 345 public ARGB8888 read(uint x, uint y) @safe pure; 346 ///Reads the image at the given point in RGBA_f32 format. 347 ///Does palette lookup if needed. 348 public RGBA_f32 readF(uint x, uint y) @safe pure; 349 ///Flips the image horizontally 350 public void flipHorizontal() @safe pure; 351 ///Flips the image vertically 352 public void flipVertical() @safe pure; 353 ///Returns the raw data cast to ubyte 354 public ubyte[] raw() @safe pure; 355 } 356 /** 357 * Imagedata container. 358 */ 359 public class ImageData(T) : IImageData { 360 protected T[] data; 361 protected uint _width, _height, _pixelFormat; 362 protected ubyte _bitDepth; 363 ///CTOR 364 public this(T[] data, uint width, uint height, uint pixelFormat, ubyte bitDepth) @safe pure { 365 //assert(data.length == _width * _height); 366 this.data = data; 367 this._width = width; 368 this._height = height; 369 this._pixelFormat = pixelFormat; 370 this._bitDepth = bitDepth; 371 } 372 ///CTOR with no preexisting image data 373 public this(uint width, uint height, uint pixelFormat, ubyte bitDepth) @safe pure { 374 //assert(data.length == _width * _height); 375 this.data.length = width * height; 376 this._width = width; 377 this._height = height; 378 this._pixelFormat = pixelFormat; 379 this._bitDepth = bitDepth; 380 } 381 ///Returns the raw data 382 public @property T[] getData() @nogc @safe pure nothrow { 383 return data; 384 } 385 ///Returns the raw data cast to ubyte 386 public ubyte[] raw() @safe pure { 387 return reinterpretCast!ubyte(data); 388 } 389 ///Returns the width of the image. 390 public @property uint width() @nogc @safe pure nothrow const { 391 return _width; 392 } 393 ///Returns the height of the image. 394 public @property uint height() @nogc @safe pure nothrow const { 395 return _height; 396 } 397 ///Returns the bitdepth of the image. 398 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 399 return _bitDepth; 400 } 401 402 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 403 return 1; 404 } 405 406 public @property uint pixelFormat() @nogc @safe pure nothrow const { 407 return _pixelFormat; 408 } 409 410 public IImageData convTo(uint format) @safe { 411 IImageData result; 412 void converter(OutputType)() @safe { 413 OutputType[] array; 414 array.length = data.length; 415 for(int i ; i < data.length ; i++) 416 array[i] = OutputType(data[i]); 417 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 418 } 419 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 420 case PixelFormat.Grayscale16Bit: 421 ushort[] datastream; 422 datastream.length = _width * _height; 423 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 424 for (int y ; y < _height ; y++) { 425 for (int x ; x < _width ; x++) { 426 const RGBA_f32 pixel = readF(x,y); 427 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 428 } 429 } 430 result = mid; 431 break; 432 case PixelFormat.Grayscale8Bit: 433 ubyte[] datastream; 434 datastream.length = _width * _height; 435 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 436 for (int y ; y < _height ; y++) { 437 for (int x ; x < _width ; x++) { 438 const RGBA_f32 pixel = readF(x,y); 439 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 440 } 441 } 442 result = mid; 443 break; 444 case PixelFormat.YX88: 445 if(format & PixelFormat.BigEndian) 446 converter!(YA88BE); 447 else 448 converter!(YA88); 449 break; 450 case PixelFormat.RGB888: 451 if(format & PixelFormat.BigEndian) 452 converter!(RGB888BE); 453 else 454 converter!(RGB888); 455 break; 456 case PixelFormat.RGBX8888: 457 if(format & PixelFormat.BigEndian) 458 converter!(RGBA8888BE); 459 else 460 converter!(RGBA8888); 461 break; 462 case PixelFormat.XRGB8888: 463 if(format & PixelFormat.BigEndian) 464 converter!(ARGB8888BE); 465 else 466 converter!(ARGB8888); 467 break; 468 case PixelFormat.RGB565: 469 converter!(RGB565); 470 break; 471 case PixelFormat.RGBX5551: 472 converter!(RGBA5551); 473 break; 474 default: 475 throw new ImageFormatException("Format not supported"); 476 } 477 return result; 478 } 479 public ARGB8888 read(uint x, uint y) @safe pure { 480 if(x < _width && y < _height) return ARGB8888(data[x + (y * _width)]); 481 else throw new ImageBoundsException("Image is being read out of bounds!"); 482 } 483 public RGBA_f32 readF(uint x, uint y) @safe pure { 484 if(x < _width && y < _height) return RGBA_f32(data[x + (y * _width)]); 485 else throw new ImageBoundsException("Image is being read out of bounds!"); 486 } 487 public ref T opIndex(uint x, uint y) @safe pure { 488 if(x < _width && y < _height) return data[x + (y * _width)]; 489 else throw new ImageBoundsException("Image is being read out of bounds!"); 490 } 491 ///Flips the image horizontally 492 public void flipHorizontal() @safe pure { 493 for (uint y ; y < _height ; y++) { 494 for (uint x ; x < _width / 2 ; x++) { 495 const T tmp = opIndex(x, y); 496 opIndex(x, y) = opIndex(_width - x, y); 497 opIndex(_width - x, y) = tmp; 498 } 499 } 500 } 501 ///Flips the image vertically 502 public void flipVertical() @safe pure { 503 import std.algorithm.mutation : swapRanges; 504 for (uint y ; y < _height / 2 ; y++) { 505 const uint y0 = _height - y - 1; 506 T[] a = data[(y * _width)..((y + 1) * _width)]; 507 T[] b = data[(y0 * _width)..((y0 + 1) * _width)]; 508 swapRanges(a, b); 509 } 510 } 511 } 512 /** 513 * Monochrome imagedata container for 8 and 16 bit types. 514 */ 515 public class MonochromeImageData (T) : IImageData { 516 public static immutable double fYStepping = 1.0 / T.max; 517 protected T[] data; 518 protected uint _width, _height, _pixelFormat; 519 protected ubyte _bitDepth; 520 public this(T[] data, uint width, uint height, uint pixelFormat, ubyte bitDepth) @safe pure { 521 //assert(data.length == _width * _height); 522 this.data = data; 523 this._width = width; 524 this._height = height; 525 this._pixelFormat = pixelFormat; 526 this._bitDepth = bitDepth; 527 } 528 ///CTOR with no preexisting image data 529 public this(uint width, uint height, uint pixelFormat, ubyte bitDepth) @safe pure { 530 //assert(data.length == _width * _height); 531 this.data.length = width * height; 532 this._width = width; 533 this._height = height; 534 this._pixelFormat = pixelFormat; 535 this._bitDepth = bitDepth; 536 } 537 ///Returns the width of the image. 538 public @property uint width() @nogc @safe pure nothrow const { 539 return _width; 540 } 541 ///Returns the height of the image. 542 public @property uint height() @nogc @safe pure nothrow const { 543 return _height; 544 } 545 ///Returns the bitdepth of the image. 546 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 547 static if(is(T == ubyte)) return 8; 548 else return 16; 549 } 550 ///Returns the number of bitplanes per image. 551 ///Default should be 1. 552 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 553 return 1; 554 } 555 ///Returns the color format of the image. 556 public @property uint pixelFormat() @nogc @safe pure nothrow const { 557 static if(is(T == ubyte)) return PixelFormat.Grayscale8Bit; 558 else return PixelFormat.Grayscale16Bit; 559 } 560 ///Converts the imagedata to the given format if supported 561 public IImageData convTo(uint format) @safe { 562 IImageData result; 563 void converter(OutputType)() @safe { 564 OutputType[] array; 565 array.length = data.length; 566 for(int i ; i < data.length ; i++) 567 array[i] = OutputType(data[i] * fYStepping); 568 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 569 } 570 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 571 case PixelFormat.Grayscale16Bit: 572 ushort[] datastream; 573 datastream.length = _width * _height; 574 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 575 for (int y ; y < _height ; y++) { 576 for (int x ; x < _width ; x++) { 577 const RGBA_f32 pixel = readF(x,y); 578 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 579 } 580 } 581 result = mid; 582 break; 583 case PixelFormat.Grayscale8Bit: 584 ubyte[] datastream; 585 datastream.length = _width * _height; 586 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 587 for (int y ; y < _height ; y++) { 588 for (int x ; x < _width ; x++) { 589 const RGBA_f32 pixel = readF(x,y); 590 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 591 } 592 } 593 result = mid; 594 break; 595 case PixelFormat.YX88: 596 if(format & PixelFormat.BigEndian) 597 converter!(YA88BE); 598 else 599 converter!(YA88); 600 break; 601 case PixelFormat.RGB888: 602 if(format & PixelFormat.BigEndian) 603 converter!(RGB888BE); 604 else 605 converter!(RGB888); 606 break; 607 case PixelFormat.RGBX8888: 608 if(format & PixelFormat.BigEndian) 609 converter!(RGBA8888BE); 610 else 611 converter!(RGBA8888); 612 break; 613 case PixelFormat.XRGB8888: 614 if(format & PixelFormat.BigEndian) 615 converter!(ARGB8888BE); 616 else 617 converter!(ARGB8888); 618 break; 619 case PixelFormat.RGB565: 620 converter!(RGB565); 621 break; 622 case PixelFormat.RGBX5551: 623 converter!(RGBA5551); 624 break; 625 default: 626 throw new ImageFormatException("Format not supported"); 627 } 628 return result; 629 } 630 ///Reads the image at the given point in ARGB32 format. 631 public ARGB8888 read(uint x, uint y) @safe pure { 632 static if (is(T == ubyte)) { 633 if(x < _width && y < _height) return ARGB8888(data[x + (y * _width)]); 634 else throw new ImageBoundsException("Image is being read out of bounds!"); 635 } else static if (is(T == ushort)) { 636 if(x < _width && y < _height) return ARGB8888(cast(ubyte)(cast(uint)data[x + (y * _width)]>>>8)); 637 else throw new ImageBoundsException("Image is being read out of bounds!"); 638 } 639 } 640 ///Reads the image at the given point in RGBA_f32 format. 641 public RGBA_f32 readF(uint x, uint y) @safe pure { 642 if(x < _width && y < _height) return RGBA_f32(data[x + (y * _width)] * fYStepping); 643 else throw new ImageBoundsException("Image is being read out of bounds!"); 644 } 645 public ref T opIndex(uint x, uint y) @safe pure { 646 if(x < _width && y < _height) return data[x + (y * _width)]; 647 else throw new ImageBoundsException("Image is being read out of bounds!"); 648 } 649 ///Flips the image horizontally 650 public void flipHorizontal() @safe pure { 651 for (uint y ; y < _height ; y++) { 652 for (uint x ; x < _width / 2 ; x++) { 653 const T tmp = opIndex(x, y); 654 opIndex(x, y) = opIndex(_width - x, y); 655 opIndex(_width - x, y) = tmp; 656 } 657 } 658 } 659 ///Flips the image vertically 660 public void flipVertical() @safe pure { 661 import std.algorithm.mutation : swapRanges; 662 for (uint y ; y < _height / 2 ; y++) { 663 const uint y0 = _height - y - 1; 664 T[] a = data[(y * _width)..((y + 1) * _width)]; 665 T[] b = data[(y0 * _width)..((y0 + 1) * _width)]; 666 swapRanges(a, b); 667 } 668 } 669 ///Returns the raw data cast to ubyte 670 public ubyte[] raw() @safe pure { 671 return reinterpretCast!ubyte(data); 672 } 673 } 674 /** 675 * 4 Bit indexed image data. 676 */ 677 public class MonochromeImageData4Bit : IImageData { 678 public static immutable double fYStepping = 1.0 / 15; 679 protected ubyte[] data; 680 protected NibbleArray accessor; 681 protected uint _width, _height, _pitch; 682 ///CTOR 683 public this(ubyte[] data, uint width, uint height) @safe pure { 684 this.data = data; 685 this._width = width; 686 this._height = height; 687 _pitch = _width + (_width % 2); 688 accessor = NibbleArray(data, _pitch * _height); 689 } 690 ///CTOR with no preexisting image data 691 public this(uint width, uint height) @safe pure { 692 //assert(data.length == _width * _height); 693 this._width = width; 694 this._height = height; 695 _pitch = _width + (_width % 2); 696 this.data.length = _pitch * _height; 697 accessor = NibbleArray(data, _pitch * _height); 698 } 699 ///Returns the raw data 700 public @property NibbleArray getData() @nogc @safe pure nothrow { 701 return accessor; 702 } 703 ///Returns the raw data cast to ubyte 704 public ubyte[] raw() @safe pure { 705 return data; 706 } 707 ///Returns the width of the image. 708 public @property uint width() @nogc @safe pure nothrow const { 709 return _width; 710 } 711 ///Returns the height of the image. 712 public @property uint height() @nogc @safe pure nothrow const { 713 return _height; 714 } 715 716 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 717 return 4; 718 } 719 720 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 721 return 1; 722 } 723 724 public @property uint pixelFormat() @nogc @safe pure nothrow const { 725 return PixelFormat.Grayscale4Bit; 726 } 727 728 public IImageData convTo(uint format) @safe { 729 IImageData result; 730 void converter(OutputType)() @safe { 731 OutputType[] array; 732 array.reserve(_width * _height); 733 for (uint y ; y < _height ; y++) { 734 for (uint x ; x < _width ; x++) { 735 array ~= OutputType(readF(x, y)); 736 } 737 } 738 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 739 } 740 741 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 742 743 case PixelFormat.Grayscale16Bit: 744 ushort[] datastream; 745 datastream.length = _width * _height; 746 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 747 for (int y ; y < _height ; y++) { 748 for (int x ; x < _width ; x++) { 749 const ubyte val = opIndex(x, y); 750 mid[x,y] = cast(ushort)(val * 0x1111); 751 } 752 } 753 result = mid; 754 break; 755 case PixelFormat.Grayscale8Bit: 756 ubyte[] datastream; 757 datastream.length = _width * _height; 758 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 759 for (int y ; y < _height ; y++) { 760 for (int x ; x < _width ; x++) { 761 const ubyte val = opIndex(x, y); 762 mid[x,y] = cast(ubyte)(val<<4 | val); 763 } 764 } 765 result = mid; 766 break; 767 case PixelFormat.YX88: 768 if(format & PixelFormat.BigEndian) 769 converter!(YA88BE); 770 else 771 converter!(YA88); 772 break; 773 case PixelFormat.RGB888: 774 if(format & PixelFormat.BigEndian) 775 converter!(RGB888BE); 776 else 777 converter!(RGB888); 778 break; 779 case PixelFormat.RGBX8888: 780 if(format & PixelFormat.BigEndian) 781 converter!(RGBA8888BE); 782 else 783 converter!(RGBA8888); 784 break; 785 case PixelFormat.XRGB8888: 786 if(format & PixelFormat.BigEndian) 787 converter!(ARGB8888BE); 788 else 789 converter!(ARGB8888); 790 break; 791 case PixelFormat.RGB565: 792 converter!(RGB565); 793 break; 794 case PixelFormat.RGBX5551: 795 converter!(RGBA5551); 796 break; 797 default: 798 throw new ImageFormatException("Format not supported"); 799 } 800 return result; 801 } 802 public ARGB8888 read(uint x, uint y) @safe pure { 803 const ubyte val = opIndex(x,y); 804 return ARGB8888(val<<4 | val); 805 } 806 public RGBA_f32 readF(uint x, uint y) @safe pure { 807 const ubyte val = opIndex(x,y); 808 return RGBA_f32(val * fYStepping); 809 } 810 public ubyte opIndex(uint x, uint y) @safe pure { 811 if(x < _width && y < _height) return accessor[x + (y * _pitch)]; 812 else throw new ImageBoundsException("Image is being read out of bounds!"); 813 } 814 public ubyte opIndexAssign(ubyte val, uint x, uint y) @safe pure { 815 if(x < _width && y < _height) return accessor[x + (y * _pitch)] = val; 816 else throw new ImageBoundsException("Image is being read out of bounds!"); 817 } 818 ///Flips the image horizontally 819 public void flipHorizontal() @safe pure { 820 for (uint y ; y < _height ; y++) { 821 for (uint x ; x < _width>>>1 ; x++) { 822 const ubyte tmp = opIndex(x, y); 823 opIndexAssign(opIndex(_width - x, y), x, y); 824 opIndexAssign(tmp, _width - x, y); 825 } 826 } 827 } 828 ///Flips the image vertically 829 public void flipVertical() @safe pure { 830 import std.algorithm.mutation : swapRanges; 831 for (uint y ; y < _height / 2 ; y++) { 832 const uint y0 = _height - y - 1; 833 ubyte[] a = data[(y * _pitch/2)..((y + 1) * _pitch/2)]; 834 ubyte[] b = data[(y0 * _pitch/2)..((y0 + 1) * _pitch/2)]; 835 swapRanges(a, b); 836 } 837 } 838 } 839 /** 840 * 2 Bit grayscale image data. 841 */ 842 public class MonochromeImageData2Bit : IImageData { 843 public static immutable double fYStepping = 1.0 / 3; 844 protected ubyte[] data; 845 protected QuadArray accessor; 846 protected uint _width, _height, _pitch; 847 ///CTOR 848 public this(ubyte[] data, uint width, uint height) @safe pure { 849 this.data = data; 850 this._width = width; 851 this._height = height; 852 _pitch = width; 853 _pitch += width % 4 ? 4 - width % 4 : 0; 854 accessor = QuadArray(data, _pitch * _height); 855 } 856 ///CTOR without preexisting data 857 public this(uint width, uint height) @safe pure { 858 this._width = width; 859 this._height = height; 860 _pitch = _width; 861 _pitch += _width % 4 ? 4 - _width % 4 : 0; 862 data.length = _pitch * _height; 863 accessor = QuadArray(data, _pitch * _height); 864 } 865 ///Returns the raw data 866 public @property QuadArray getData() @nogc @safe pure nothrow { 867 return accessor; 868 } 869 ///Returns the raw data cast to ubyte 870 public ubyte[] raw() @safe pure { 871 return data; 872 } 873 ///Returns the width of the image. 874 public @property uint width() @nogc @safe pure nothrow const { 875 return _width; 876 } 877 ///Returns the height of the image. 878 public @property uint height() @nogc @safe pure nothrow const { 879 return _height; 880 } 881 882 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 883 return 2; 884 } 885 886 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 887 return 1; 888 } 889 890 public @property uint pixelFormat() @nogc @safe pure nothrow const { 891 return PixelFormat.Grayscale2Bit; 892 } 893 894 public IImageData convTo(uint format) @safe { 895 IImageData result; 896 void converter(OutputType)() @safe { 897 OutputType[] array; 898 array.reserve(_width * _height); 899 for (uint y ; y < _height ; y++) { 900 for (uint x ; x < _width ; x++) { 901 array ~= OutputType(readF(x, y)); 902 } 903 } 904 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 905 } 906 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 907 case PixelFormat.Grayscale16Bit: 908 ushort[] datastream; 909 datastream.length = _width * _height; 910 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 911 for (int y ; y < _height ; y++) { 912 for (int x ; x < _width ; x++) { 913 const ubyte val = opIndex(x, y); 914 mid[x,y] = cast(ushort)(val * 0b0101_0101_0101_0101); 915 } 916 } 917 result = mid; 918 break; 919 case PixelFormat.Grayscale8Bit: 920 ubyte[] datastream; 921 datastream.length = _width * _height; 922 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 923 for (int y ; y < _height ; y++) { 924 for (int x ; x < _width ; x++) { 925 const ubyte val = opIndex(x, y); 926 mid[x,y] = cast(ubyte)(val * 0b0101_0101); 927 } 928 } 929 result = mid; 930 break; 931 case PixelFormat.YX88: 932 if(format & PixelFormat.BigEndian) 933 converter!(YA88BE); 934 else 935 converter!(YA88); 936 break; 937 case PixelFormat.RGB888: 938 if(format & PixelFormat.BigEndian) 939 converter!(RGB888BE); 940 else 941 converter!(RGB888); 942 break; 943 case PixelFormat.RGBX8888: 944 if(format & PixelFormat.BigEndian) 945 converter!(RGBA8888BE); 946 else 947 converter!(RGBA8888); 948 break; 949 case PixelFormat.XRGB8888: 950 if(format & PixelFormat.BigEndian) 951 converter!(ARGB8888BE); 952 else 953 converter!(ARGB8888); 954 break; 955 case PixelFormat.RGB565: 956 converter!(RGB565); 957 break; 958 case PixelFormat.RGBX5551: 959 converter!(RGBA5551); 960 break; 961 default: 962 throw new ImageFormatException("Format not supported"); 963 } 964 return result; 965 } 966 public ARGB8888 read(uint x, uint y) @safe pure { 967 const ubyte val = opIndex(x,y); 968 return ARGB8888(val<<6 | val<<4 | val<<2 | val); 969 } 970 public RGBA_f32 readF(uint x, uint y) @safe pure { 971 const ubyte val = opIndex(x,y); 972 return RGBA_f32(val * fYStepping); 973 } 974 public ubyte opIndex(uint x, uint y) @safe pure { 975 if(x < _width && y < _height) return accessor[x + (y * _pitch)]; 976 else throw new ImageBoundsException("Image is being read out of bounds!"); 977 } 978 public ubyte opIndexAssign(ubyte val, uint x, uint y) @safe pure { 979 if(x < _width && y < _height) return accessor[x + (y * _pitch)] = val; 980 else throw new ImageBoundsException("Image is being read out of bounds!"); 981 } 982 ///Flips the image horizontally 983 public void flipHorizontal() @safe pure { 984 for (uint y ; y < _height ; y++) { 985 for (uint x ; x < _width>>>1 ; x++) { 986 const ubyte tmp = opIndex(x, y); 987 opIndexAssign(opIndex(_width - x, y), x, y); 988 opIndexAssign(tmp, _width - x, y); 989 } 990 } 991 } 992 ///Flips the image vertically 993 public void flipVertical() @safe pure { 994 import std.algorithm.mutation : swapRanges; 995 for (uint y ; y < _height / 2 ; y++) { 996 const uint y0 = _height - y - 1; 997 ubyte[] a = data[(y * _pitch/4)..((y + 1) * _pitch/4)]; 998 ubyte[] b = data[(y0 * _pitch/4)..((y0 + 1) * _pitch/4)]; 999 swapRanges(a, b); 1000 } 1001 } 1002 } 1003 /** 1004 * Monochrome 1 bit access 1005 */ 1006 public class MonochromeImageData1Bit : IImageData { 1007 protected ubyte[] data; 1008 protected BitArray accessor; 1009 protected uint _width, _height, _pitch; 1010 ///CTOR 1011 public this(ubyte[] data, uint _width, uint _height) @trusted pure { 1012 this.data = data; 1013 this._width = _width; 1014 this._height = _height; 1015 _pitch = _width; 1016 _pitch += width % 8 ? 8 - width % 8 : 0; 1017 accessor = BitArray(data, _pitch * _height); 1018 } 1019 ///Returns the raw data 1020 public @property BitArray getData() @nogc @safe pure nothrow { 1021 return accessor; 1022 } 1023 ///Returns the raw data cast to ubyte 1024 public ubyte[] raw() @safe pure { 1025 return data; 1026 } 1027 ///Returns the width of the image. 1028 public @property uint width() @nogc @safe pure nothrow const { 1029 return _width; 1030 } 1031 ///Returns the height of the image. 1032 public @property uint height() @nogc @safe pure nothrow const { 1033 return _height; 1034 } 1035 1036 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 1037 return 1; 1038 } 1039 1040 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 1041 return 1; 1042 } 1043 1044 public @property uint pixelFormat() @nogc @safe pure nothrow const { 1045 return PixelFormat.Grayscale1Bit; 1046 } 1047 1048 public IImageData convTo(uint format) @safe { 1049 IImageData result; 1050 void converter(OutputType)() @safe { 1051 OutputType[] array; 1052 array.reserve(_width * _height); 1053 for (uint y ; y < _height ; y++) { 1054 for (uint x ; x < _width ; x++) { 1055 array ~= OutputType(readF(x, y)); 1056 } 1057 } 1058 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 1059 } 1060 1061 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 1062 case PixelFormat.Grayscale16Bit: 1063 ushort[] datastream; 1064 datastream.length = _width * _height; 1065 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 1066 for (int y ; y < _height ; y++) { 1067 for (int x ; x < _width ; x++) { 1068 const RGBA_f32 pixel = readF(x,y); 1069 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 1070 } 1071 } 1072 result = mid; 1073 break; 1074 case PixelFormat.Grayscale8Bit: 1075 ubyte[] datastream; 1076 datastream.length = _width * _height; 1077 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 1078 for (int y ; y < _height ; y++) { 1079 for (int x ; x < _width ; x++) { 1080 const RGBA_f32 pixel = readF(x,y); 1081 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 1082 } 1083 } 1084 result = mid; 1085 break; 1086 case PixelFormat.YX88: 1087 if(format & PixelFormat.BigEndian) 1088 converter!(YA88BE); 1089 else 1090 converter!(YA88); 1091 break; 1092 case PixelFormat.RGB888: 1093 if(format & PixelFormat.BigEndian) 1094 converter!(RGB888BE); 1095 else 1096 converter!(RGB888); 1097 break; 1098 case PixelFormat.RGBX8888: 1099 if(format & PixelFormat.BigEndian) 1100 converter!(RGBA8888BE); 1101 else 1102 converter!(RGBA8888); 1103 break; 1104 case PixelFormat.XRGB8888: 1105 if(format & PixelFormat.BigEndian) 1106 converter!(ARGB8888BE); 1107 else 1108 converter!(ARGB8888); 1109 break; 1110 case PixelFormat.RGB565: 1111 converter!(RGB565); 1112 break; 1113 case PixelFormat.RGBX5551: 1114 converter!(RGBA5551); 1115 break; 1116 default: 1117 throw new ImageFormatException("Format not supported"); 1118 } 1119 return result; 1120 } 1121 1122 public ARGB8888 read(uint x, uint y) @safe pure { 1123 if(x < _width && y < _height) return opIndex(x, y) ? ARGB8888(255) : ARGB8888(0); 1124 else throw new ImageBoundsException("Image is being read out of bounds!"); 1125 } 1126 public RGBA_f32 readF(uint x, uint y) @safe pure { 1127 if(x < _width && y < _height) return opIndex(x, y) ? RGBA_f32(1.0) : RGBA_f32(0.0); 1128 else throw new ImageBoundsException("Image is being read out of bounds!"); 1129 } 1130 public bool opIndex(uint x, uint y) @trusted pure { 1131 if(x < _width && y < _height) return accessor[x + (y * _pitch)]; 1132 else throw new ImageBoundsException("Image is being read out of bounds!"); 1133 } 1134 public ubyte opIndexAssign(bool val, uint x, uint y) @trusted pure { 1135 if(x < _width && y < _height) return accessor[x + (y * _pitch)] = val; 1136 else throw new ImageBoundsException("Image is being read out of bounds!"); 1137 } 1138 ///Flips the image horizontally 1139 public void flipHorizontal() @safe pure { 1140 for (uint y ; y < _height ; y++) { 1141 for (uint x ; x < _width>>>1 ; x++) { 1142 const bool tmp = opIndex(x, y); 1143 opIndexAssign(opIndex(_width - x, y), x, y); 1144 opIndexAssign(tmp, _width - x, y); 1145 } 1146 } 1147 } 1148 ///Flips the image vertically 1149 public void flipVertical() @safe pure { 1150 import std.algorithm.mutation : swapRanges; 1151 for (uint y ; y < _height / 2 ; y++) { 1152 const uint y0 = _height - y - 1; 1153 ubyte[] a = data[(y * _pitch/8)..((y + 1) * _pitch/8)]; 1154 ubyte[] b = data[(y0 * _pitch/8)..((y0 + 1) * _pitch/8)]; 1155 swapRanges(a, b); 1156 } 1157 } 1158 } 1159 /** 1160 * Indexed imagedata container for ubyte and ushort based formats 1161 */ 1162 public class IndexedImageData (T) : IImageData { 1163 protected T[] data; 1164 public IPalette palette; 1165 protected uint _width, _height; 1166 ///CTOR 1167 public this(T[] data, IPalette palette, uint width, uint height) @safe pure { 1168 this.data = data; 1169 this.palette = palette; 1170 this._width = width; 1171 this._height = height; 1172 } 1173 ///CTOR with no preexisting image data 1174 public this(IPalette palette, uint width, uint height) @safe pure { 1175 //assert(data.length == _width * _height); 1176 this.data.length = width * height; 1177 this.palette = palette; 1178 this._width = width; 1179 this._height = height; 1180 } 1181 ///Returns the raw data 1182 public @property T[] getData() @nogc @safe pure nothrow { 1183 return data; 1184 } 1185 ///Returns the raw data cast to ubyte 1186 public ubyte[] raw() @safe pure { 1187 return reinterpretCast!ubyte(data); 1188 } 1189 ///Returns the width of the image. 1190 public @property uint width() @nogc @safe pure nothrow const { 1191 return _width; 1192 } 1193 ///Returns the height of the image. 1194 public @property uint height() @nogc @safe pure nothrow const { 1195 return _height; 1196 } 1197 1198 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 1199 static if(is(T == ubyte)) return 8; 1200 else return 16; 1201 } 1202 1203 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 1204 return 1; 1205 } 1206 1207 public @property uint pixelFormat() @nogc @safe pure nothrow const { 1208 static if(is(T == ubyte)) return PixelFormat.Indexed8Bit; 1209 else return PixelFormat.Indexed16Bit; 1210 } 1211 1212 public IImageData convTo(uint format) @safe { 1213 IImageData result; 1214 void converter(OutputType)() @safe { 1215 OutputType[] array; 1216 array.length = data.length; 1217 for(int i ; i < data.length ; i++) 1218 array[i] = OutputType(palette.read(data[i])); 1219 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 1220 } 1221 void upconv(OutputType)() @safe { 1222 OutputType[] array; 1223 array.length = data.length; 1224 for(int i ; i < data.length ; i++) 1225 array[i] = data[i]; 1226 result = new IndexedImageData!OutputType(array, palette, _width, _height); 1227 } 1228 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 1229 case PixelFormat.Indexed16Bit: 1230 upconv!ushort; 1231 break; 1232 case PixelFormat.Grayscale16Bit: 1233 ushort[] datastream; 1234 datastream.length = _width * _height; 1235 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 1236 for (int y ; y < _height ; y++) { 1237 for (int x ; x < _width ; x++) { 1238 const RGBA_f32 pixel = readF(x,y); 1239 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 1240 } 1241 } 1242 result = mid; 1243 break; 1244 case PixelFormat.Grayscale8Bit: 1245 ubyte[] datastream; 1246 datastream.length = _width * _height; 1247 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 1248 for (int y ; y < _height ; y++) { 1249 for (int x ; x < _width ; x++) { 1250 const RGBA_f32 pixel = readF(x,y); 1251 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 1252 } 1253 } 1254 result = mid; 1255 break; 1256 case PixelFormat.YX88: 1257 if(format & PixelFormat.BigEndian) 1258 converter!(YA88BE); 1259 else 1260 converter!(YA88); 1261 break; 1262 case PixelFormat.RGB888: 1263 if(format & PixelFormat.BigEndian) 1264 converter!(RGB888BE); 1265 else 1266 converter!(RGB888); 1267 break; 1268 case PixelFormat.RGBX8888: 1269 if(format & PixelFormat.BigEndian) 1270 converter!(RGBA8888BE); 1271 else 1272 converter!(RGBA8888); 1273 break; 1274 case PixelFormat.XRGB8888: 1275 if(format & PixelFormat.BigEndian) 1276 converter!(ARGB8888BE); 1277 else 1278 converter!(ARGB8888); 1279 break; 1280 case PixelFormat.RGB565: 1281 converter!(RGB565); 1282 break; 1283 case PixelFormat.RGBX5551: 1284 converter!(RGBA5551); 1285 break; 1286 default: 1287 throw new ImageFormatException("Format not supported"); 1288 } 1289 return result; 1290 } 1291 public ARGB8888 read(uint x, uint y) @safe pure { 1292 if(x < _width && y < _height) return palette.read(data[x + (y * _width)]); 1293 else throw new ImageBoundsException("Image is being read out of bounds!"); 1294 } 1295 public RGBA_f32 readF(uint x, uint y) @safe pure { 1296 if(x < _width && y < _height) return palette.readF(data[x + (y * _width)]); 1297 else throw new ImageBoundsException("Image is being read out of bounds!"); 1298 } 1299 public ref T opIndex(uint x, uint y) @safe pure { 1300 if(x < _width && y < _height) return data[x + (y * _width)]; 1301 else throw new ImageBoundsException("Image is being read out of bounds!"); 1302 } 1303 ///Flips the image horizontally 1304 public void flipHorizontal() @safe pure { 1305 for (uint y ; y < _height ; y++) { 1306 for (uint x ; x < _width>>>1 ; x++) { 1307 const T tmp = opIndex(x, y); 1308 opIndex(x, y) = opIndex(_width - x, y); 1309 opIndex(_width - x, y) = tmp; 1310 } 1311 } 1312 } 1313 ///Flips the image vertically 1314 public void flipVertical() @safe pure { 1315 import std.algorithm.mutation : swapRanges; 1316 for (uint y ; y < _height / 2 ; y++) { 1317 const uint y0 = _height - y - 1; 1318 T[] a = data[(y * _width)..((y + 1) * _width)]; 1319 T[] b = data[(y0 * _width)..((y0 + 1) * _width)]; 1320 swapRanges(a, b); 1321 } 1322 } 1323 } 1324 /** 1325 * 4 Bit indexed image data. 1326 */ 1327 public class IndexedImageData4Bit : IImageData { 1328 protected ubyte[] data; 1329 protected NibbleArray accessor; 1330 public IPalette palette; 1331 protected uint _width, _height, _pitch; 1332 ///CTOR 1333 public this(ubyte[] data, IPalette palette, uint width, uint height) @safe pure { 1334 this.data = data; 1335 this.palette = palette; 1336 this._width = width; 1337 this._height = height; 1338 _pitch = _width + (_width % 2); 1339 accessor = NibbleArray(data, _pitch * _height); 1340 } 1341 ///CTOR with no preexisting image data 1342 public this(IPalette palette, uint width, uint height) @safe pure { 1343 //assert(data.length == _width * _height); 1344 this._width = width; 1345 this._height = height; 1346 _pitch = _width + (_width % 2); 1347 this.data.length = _pitch * _height; 1348 accessor = NibbleArray(data, _pitch * _height); 1349 this.palette = palette; 1350 } 1351 ///Returns the raw data 1352 public @property NibbleArray getData() @nogc @safe pure nothrow { 1353 return accessor; 1354 } 1355 ///Returns the raw data cast to ubyte 1356 public ubyte[] raw() @safe pure { 1357 return data; 1358 } 1359 ///Returns the width of the image. 1360 public @property uint width() @nogc @safe pure nothrow const { 1361 return _width; 1362 } 1363 ///Returns the height of the image. 1364 public @property uint height() @nogc @safe pure nothrow const { 1365 return _height; 1366 } 1367 1368 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 1369 return 4; 1370 } 1371 1372 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 1373 return 1; 1374 } 1375 1376 public @property uint pixelFormat() @nogc @safe pure nothrow const { 1377 return PixelFormat.Indexed4Bit; 1378 } 1379 1380 public IImageData convTo(uint format) @safe { 1381 IImageData result; 1382 void converter(OutputType)() @safe { 1383 OutputType[] array; 1384 array.length = data.length; 1385 for(int i ; i < data.length ; i++) 1386 array[i] = OutputType(palette.read(data[i])); 1387 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 1388 } 1389 void upconv(OutputType)() @safe { 1390 IndexedImageData!OutputType iid = new IndexedImageData!OutputType(palette, _width, _height); 1391 for(int y ; y < _height ; y++) 1392 for(int x ; x < _width ; x++) 1393 iid[x, y] = opIndex(x, y); 1394 result = iid; 1395 } 1396 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 1397 case PixelFormat.Indexed16Bit: 1398 upconv!ushort; 1399 break; 1400 case PixelFormat.Indexed8Bit: 1401 upconv!ubyte; 1402 break; 1403 case PixelFormat.Grayscale16Bit: 1404 ushort[] datastream; 1405 datastream.length = _width * _height; 1406 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 1407 for (int y ; y < _height ; y++) { 1408 for (int x ; x < _width ; x++) { 1409 const RGBA_f32 pixel = readF(x,y); 1410 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 1411 } 1412 } 1413 result = mid; 1414 break; 1415 case PixelFormat.Grayscale8Bit: 1416 ubyte[] datastream; 1417 datastream.length = _width * _height; 1418 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 1419 for (int y ; y < _height ; y++) { 1420 for (int x ; x < _width ; x++) { 1421 const RGBA_f32 pixel = readF(x,y); 1422 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 1423 } 1424 } 1425 result = mid; 1426 break; 1427 case PixelFormat.YX88: 1428 if(format & PixelFormat.BigEndian) 1429 converter!(YA88BE); 1430 else 1431 converter!(YA88); 1432 break; 1433 case PixelFormat.RGB888: 1434 if(format & PixelFormat.BigEndian) 1435 converter!(RGB888BE); 1436 else 1437 converter!(RGB888); 1438 break; 1439 case PixelFormat.RGBX8888: 1440 if(format & PixelFormat.BigEndian) 1441 converter!(RGBA8888BE); 1442 else 1443 converter!(RGBA8888); 1444 break; 1445 case PixelFormat.XRGB8888: 1446 if(format & PixelFormat.BigEndian) 1447 converter!(ARGB8888BE); 1448 else 1449 converter!(ARGB8888); 1450 break; 1451 case PixelFormat.RGB565: 1452 converter!(RGB565); 1453 break; 1454 case PixelFormat.RGBX5551: 1455 converter!(RGBA5551); 1456 break; 1457 default: 1458 throw new ImageFormatException("Format not supported"); 1459 } 1460 return result; 1461 } 1462 public ARGB8888 read(uint x, uint y) @safe pure { 1463 if(x < _width && y < _height) return palette.read(accessor[x + (y * _pitch)]); 1464 else throw new ImageBoundsException("Image is being read out of bounds!"); 1465 } 1466 public RGBA_f32 readF(uint x, uint y) @safe pure { 1467 if(x < _width && y < _height) return palette.readF(accessor[x + (y * _pitch)]); 1468 else throw new ImageBoundsException("Image is being read out of bounds!"); 1469 } 1470 public ubyte opIndex(uint x, uint y) @safe pure { 1471 if(x < _width && y < _height) return accessor[x + (y * _pitch)]; 1472 else throw new ImageBoundsException("Image is being read out of bounds!"); 1473 } 1474 public ubyte opIndexAssign(ubyte val, uint x, uint y) @safe pure { 1475 if(x < _width && y < _height) return accessor[x + (y * _pitch)] = val; 1476 else throw new ImageBoundsException("Image is being read out of bounds!"); 1477 } 1478 ///Flips the image horizontally 1479 public void flipHorizontal() @safe pure { 1480 for (uint y ; y < _height ; y++) { 1481 for (uint x ; x < _width>>>1 ; x++) { 1482 const ubyte tmp = opIndex(x, y); 1483 opIndexAssign(opIndex(_width - x, y), x, y); 1484 opIndexAssign(tmp, _width - x, y); 1485 } 1486 } 1487 } 1488 ///Flips the image vertically 1489 public void flipVertical() @safe pure { 1490 import std.algorithm.mutation : swapRanges; 1491 for (uint y ; y < _height / 2 ; y++) { 1492 const uint y0 = _height - y - 1; 1493 ubyte[] a = data[(y * _pitch/2)..((y + 1) * _pitch/2)]; 1494 ubyte[] b = data[(y0 * _pitch/2)..((y0 + 1) * _pitch/2)]; 1495 swapRanges(a, b); 1496 } 1497 } 1498 } 1499 /** 1500 * 2 Bit indexed image data. 1501 */ 1502 public class IndexedImageData2Bit : IImageData { 1503 protected ubyte[] data; 1504 protected QuadArray accessor; 1505 public IPalette palette; 1506 protected uint _width, _height, _pitch; 1507 ///CTOR 1508 public this(ubyte[] data, IPalette palette, uint width, uint height) @safe pure { 1509 this.data = data; 1510 this.palette = palette; 1511 this._width = width; 1512 this._height = height; 1513 _pitch = width; 1514 _pitch += width % 4 ? 4 - width % 4 : 0; 1515 accessor = QuadArray(data, _pitch * _height); 1516 } 1517 ///CTOR without preexisting data 1518 public this(IPalette palette, uint width, uint height) @safe pure { 1519 this.palette = palette; 1520 this._width = width; 1521 this._height = height; 1522 _pitch = _width; 1523 _pitch += _width % 4 ? 4 - _width % 4 : 0; 1524 data.length = _pitch * _height; 1525 accessor = QuadArray(data, _pitch * _height); 1526 } 1527 ///Returns the raw data 1528 public @property QuadArray getData() @nogc @safe pure nothrow { 1529 return accessor; 1530 } 1531 ///Returns the raw data cast to ubyte 1532 public ubyte[] raw() @safe pure { 1533 return data; 1534 } 1535 ///Returns the width of the image. 1536 public @property uint width() @nogc @safe pure nothrow const { 1537 return _width; 1538 } 1539 ///Returns the height of the image. 1540 public @property uint height() @nogc @safe pure nothrow const { 1541 return _height; 1542 } 1543 1544 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 1545 return 2; 1546 } 1547 1548 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 1549 return 1; 1550 } 1551 1552 public @property uint pixelFormat() @nogc @safe pure nothrow const { 1553 return PixelFormat.Indexed2Bit; 1554 } 1555 1556 public IImageData convTo(uint format) @safe { 1557 IImageData result; 1558 void converter(OutputType)() @safe { 1559 OutputType[] array; 1560 array.length = data.length; 1561 for(int i ; i < data.length ; i++) 1562 array[i] = OutputType(palette.read(data[i])); 1563 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 1564 } 1565 void upconv(OutputType)() @safe { 1566 IndexedImageData!OutputType iid = new IndexedImageData!OutputType(palette, _width, _height); 1567 for(int y ; y < _height ; y++) 1568 for(int x ; x < _width ; x++) 1569 iid[x, y] = opIndex(x, y); 1570 result = iid; 1571 } 1572 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 1573 case PixelFormat.Indexed16Bit: 1574 upconv!ushort; 1575 break; 1576 case PixelFormat.Indexed8Bit: 1577 upconv!ubyte; 1578 break; 1579 case PixelFormat.Indexed4Bit: 1580 IndexedImageData4Bit iid = new IndexedImageData4Bit(palette, _width, _height); 1581 for(int y ; y < _height ; y++) 1582 for(int x ; x < _width ; x++) 1583 iid[x, y] = opIndex(x, y); 1584 result = iid; 1585 break; 1586 case PixelFormat.Grayscale16Bit: 1587 ushort[] datastream; 1588 datastream.length = _width * _height; 1589 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 1590 for (int y ; y < _height ; y++) { 1591 for (int x ; x < _width ; x++) { 1592 const RGBA_f32 pixel = readF(x,y); 1593 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 1594 } 1595 } 1596 result = mid; 1597 break; 1598 case PixelFormat.Grayscale8Bit: 1599 ubyte[] datastream; 1600 datastream.length = _width * _height; 1601 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 1602 for (int y ; y < _height ; y++) { 1603 for (int x ; x < _width ; x++) { 1604 const RGBA_f32 pixel = readF(x,y); 1605 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 1606 } 1607 } 1608 result = mid; 1609 break; 1610 case PixelFormat.YX88: 1611 if(format & PixelFormat.BigEndian) 1612 converter!(YA88BE); 1613 else 1614 converter!(YA88); 1615 break; 1616 case PixelFormat.RGB888: 1617 if(format & PixelFormat.BigEndian) 1618 converter!(RGB888BE); 1619 else 1620 converter!(RGB888); 1621 break; 1622 case PixelFormat.RGBX8888: 1623 if(format & PixelFormat.BigEndian) 1624 converter!(RGBA8888BE); 1625 else 1626 converter!(RGBA8888); 1627 break; 1628 case PixelFormat.XRGB8888: 1629 if(format & PixelFormat.BigEndian) 1630 converter!(ARGB8888BE); 1631 else 1632 converter!(ARGB8888); 1633 break; 1634 case PixelFormat.RGB565: 1635 converter!(RGB565); 1636 break; 1637 case PixelFormat.RGBX5551: 1638 converter!(RGBA5551); 1639 break; 1640 default: 1641 throw new ImageFormatException("Format not supported"); 1642 } 1643 return result; 1644 } 1645 public ARGB8888 read(uint x, uint y) @safe pure { 1646 if(x < _width && y < _height) return palette.read(accessor[x + (y * _pitch)]); 1647 else throw new ImageBoundsException("Image is being read out of bounds!"); 1648 } 1649 public RGBA_f32 readF(uint x, uint y) @safe pure { 1650 if(x < _width && y < _height) return palette.readF(accessor[x + (y * _pitch)]); 1651 else throw new ImageBoundsException("Image is being read out of bounds!"); 1652 } 1653 public ubyte opIndex(uint x, uint y) @safe pure { 1654 if(x < _width && y < _height) return accessor[x + (y * _pitch)]; 1655 else throw new ImageBoundsException("Image is being read out of bounds!"); 1656 } 1657 public ubyte opIndexAssign(ubyte val, uint x, uint y) @safe pure { 1658 if(x < _width && y < _height) return accessor[x + (y * _pitch)] = val; 1659 else throw new ImageBoundsException("Image is being read out of bounds!"); 1660 } 1661 ///Flips the image horizontally 1662 public void flipHorizontal() @safe pure { 1663 for (uint y ; y < _height ; y++) { 1664 for (uint x ; x < _width>>>1 ; x++) { 1665 const ubyte tmp = opIndex(x, y); 1666 opIndexAssign(opIndex(_width - x, y),x, y); 1667 opIndexAssign(tmp, _width - x, y); 1668 } 1669 } 1670 } 1671 ///Flips the image vertically 1672 public void flipVertical() @safe pure { 1673 import std.algorithm.mutation : swapRanges; 1674 for (uint y ; y < _height / 2 ; y++) { 1675 const uint y0 = _height - y - 1; 1676 ubyte[] a = data[(y * _pitch/4)..((y + 1) * _pitch/4)]; 1677 ubyte[] b = data[(y0 * _pitch/4)..((y0 + 1) * _pitch/4)]; 1678 swapRanges(a, b); 1679 } 1680 } 1681 } 1682 /** 1683 * Monochrome 1 bit access 1684 */ 1685 public class IndexedImageData1Bit : IImageData { 1686 protected ubyte[] data; 1687 protected BitArray accessor; 1688 public IPalette palette; 1689 protected uint _width, _height, _pitch; 1690 ///CTOR 1691 public this(ubyte[] data, IPalette palette, uint _width, uint _height) @trusted pure { 1692 this.data = data; 1693 this.palette = palette; 1694 this._width = _width; 1695 this._height = _height; 1696 _pitch = _width; 1697 _pitch += width % 8 ? 8 - width % 8 : 0; 1698 accessor = BitArray(data, _pitch * _height); 1699 } 1700 ///Returns the raw data 1701 public @property BitArray getData() @nogc @safe pure nothrow { 1702 return accessor; 1703 } 1704 ///Returns the raw data cast to ubyte 1705 public ubyte[] raw() @safe pure { 1706 return data; 1707 } 1708 ///Returns the width of the image. 1709 public @property uint width() @nogc @safe pure nothrow const { 1710 return _width; 1711 } 1712 ///Returns the height of the image. 1713 public @property uint height() @nogc @safe pure nothrow const { 1714 return _height; 1715 } 1716 1717 public @property ubyte bitDepth() @nogc @safe pure nothrow const { 1718 return 1; 1719 } 1720 1721 public @property ubyte bitplanes() @nogc @safe pure nothrow const { 1722 return 1; 1723 } 1724 1725 public @property uint pixelFormat() @nogc @safe pure nothrow const { 1726 return PixelFormat.Indexed1Bit; 1727 } 1728 1729 public IImageData convTo(uint format) @safe { 1730 IImageData result; 1731 void converter(OutputType)() @safe { 1732 OutputType[] array; 1733 array.length = data.length; 1734 for(int i ; i < data.length ; i++) 1735 array[i] = OutputType(palette.read(data[i])); 1736 result = new ImageData!OutputType(array, _width, _height, format, getBitDepth(format)); 1737 } 1738 void upconv(OutputType)() @safe { 1739 IndexedImageData!OutputType iid = new IndexedImageData!OutputType(palette, _width, _height); 1740 for(int y ; y < _height ; y++) 1741 for(int x ; x < _width ; x++) 1742 iid[x, y] = opIndex(x, y); 1743 result = iid; 1744 } 1745 switch (format & ~(PixelFormat.BigEndian | PixelFormat.ValidAlpha)) { 1746 case PixelFormat.Indexed16Bit: 1747 upconv!ushort; 1748 break; 1749 case PixelFormat.Indexed8Bit: 1750 upconv!ubyte; 1751 break; 1752 case PixelFormat.Indexed4Bit: 1753 IndexedImageData4Bit iid = new IndexedImageData4Bit(palette, _width, _height); 1754 for(int y ; y < _height ; y++) 1755 for(int x ; x < _width ; x++) 1756 iid[x, y] = opIndex(x, y) ? 0x01 : 0x00; 1757 result = iid; 1758 break; 1759 case PixelFormat.Indexed2Bit: 1760 IndexedImageData2Bit iid = new IndexedImageData2Bit(palette, _width, _height); 1761 for(int y ; y < _height ; y++) 1762 for(int x ; x < _width ; x++) 1763 iid[x, y] = opIndex(x, y) ? 0x01 : 0x00; 1764 result = iid; 1765 break; 1766 case PixelFormat.Grayscale16Bit: 1767 ushort[] datastream; 1768 datastream.length = _width * _height; 1769 MonochromeImageData!ushort mid = new MonochromeImageData!ushort(datastream, _width, _height, format, 16); 1770 for (int y ; y < _height ; y++) { 1771 for (int x ; x < _width ; x++) { 1772 const RGBA_f32 pixel = readF(x,y); 1773 mid[x,y] = cast(ushort)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ushort.fYStepping); 1774 } 1775 } 1776 result = mid; 1777 break; 1778 case PixelFormat.Grayscale8Bit: 1779 ubyte[] datastream; 1780 datastream.length = _width * _height; 1781 MonochromeImageData!ubyte mid = new MonochromeImageData!ubyte(datastream, _width, _height, format, 8); 1782 for (int y ; y < _height ; y++) { 1783 for (int x ; x < _width ; x++) { 1784 const RGBA_f32 pixel = readF(x,y); 1785 mid[x,y] = cast(ubyte)((pixel.fR * 0.2125 + pixel.fG * 0.7154 + pixel.fB * 0.0721) / MonochromeImageData!ubyte.fYStepping); 1786 } 1787 } 1788 result = mid; 1789 break; 1790 case PixelFormat.YX88: 1791 if(format & PixelFormat.BigEndian) 1792 converter!(YA88BE); 1793 else 1794 converter!(YA88); 1795 break; 1796 case PixelFormat.RGB888: 1797 if(format & PixelFormat.BigEndian) 1798 converter!(RGB888BE); 1799 else 1800 converter!(RGB888); 1801 break; 1802 case PixelFormat.RGBX8888: 1803 if(format & PixelFormat.BigEndian) 1804 converter!(RGBA8888BE); 1805 else 1806 converter!(RGBA8888); 1807 break; 1808 case PixelFormat.XRGB8888: 1809 if(format & PixelFormat.BigEndian) 1810 converter!(ARGB8888BE); 1811 else 1812 converter!(ARGB8888); 1813 break; 1814 case PixelFormat.RGB565: 1815 converter!(RGB565); 1816 break; 1817 case PixelFormat.RGBX5551: 1818 converter!(RGBA5551); 1819 break; 1820 default: 1821 throw new ImageFormatException("Format not supported"); 1822 } 1823 return result; 1824 } 1825 1826 public ARGB8888 read(uint x, uint y) @safe pure { 1827 return palette.read(opIndex(x, y)); 1828 } 1829 public RGBA_f32 readF(uint x, uint y) @safe pure { 1830 return palette.readF(opIndex(x, y)); 1831 } 1832 public bool opIndex(uint x, uint y) @trusted pure { 1833 if(x < _width && y < _height) return accessor[x + (y * _pitch)]; 1834 else throw new ImageBoundsException("Image is being read out of bounds!"); 1835 } 1836 public ubyte opIndexAssign(bool val, uint x, uint y) @trusted pure { 1837 if(x < _width && y < _height) return accessor[x + (y * _pitch)] = val; 1838 else throw new ImageBoundsException("Image is being read out of bounds!"); 1839 } 1840 ///Flips the image horizontally 1841 public void flipHorizontal() @safe pure { 1842 for (uint y ; y < _height ; y++) { 1843 for (uint x ; x < _width>>>1 ; x++) { 1844 const bool tmp = opIndex(x, y); 1845 opIndexAssign(opIndex(_width - x, y), x, y); 1846 opIndexAssign(tmp, _width - x, y); 1847 } 1848 } 1849 } 1850 ///Flips the image vertically 1851 public void flipVertical() @safe pure { 1852 import std.algorithm.mutation : swapRanges; 1853 for (uint y ; y < _height / 2 ; y++) { 1854 const uint y0 = _height - y - 1; 1855 ubyte[] a = data[(y * _pitch/8)..((y + 1) * _pitch/8)]; 1856 ubyte[] b = data[(y0 * _pitch/8)..((y0 + 1) * _pitch/8)]; 1857 swapRanges(a, b); 1858 } 1859 } 1860 } 1861 /** 1862 * All image classes should be derived from this base. 1863 * Implements some basic functionality, such as reading and writing pixels, basic data storage, and basic information. 1864 * Pixeldata should be stored decompressed, but indexing should be preserved on loading with the opinion of upconverting 1865 * to truecolor. 1866 */ 1867 abstract class Image{ 1868 /** 1869 * Contains palette data and information 1870 */ 1871 protected IPalette _palette; 1872 /** 1873 * Contains image data and information. 1874 */ 1875 protected IImageData _imageData; 1876 protected ubyte mod; ///used for fast access of indexes DEPRECATED! 1877 protected ubyte shift; ///used for fast access of indexes DEPRECATED! 1878 1879 /+protected @safe pure ubyte delegate(uint x, uint y) indexReader8Bit; ///Used for bypassing typechecking when reading pixels 1880 protected @safe pure ushort delegate(uint x, uint y) indexReader16bit; ///Used for bypassing typechecking when reading pixels 1881 protected @safe pure ubyte delegate(uint x, uint y, ubyte val) indexWriter8Bit; ///Used for bypassing typechecking when writing pixels 1882 protected @safe pure ushort delegate(uint x, uint y, ushort val) indexWriter16bit; ///Used for bypassing typechecking when writing pixels 1883 protected @safe pure ARGB8888 delegate(uint x, uint y) pixelReader; //Used for bypassing typechecking 1884 protected @safe pure ARGB8888 delegate(ushort i) paletteReader; //Used for bypassing typechecking 1885 +/ 1886 1887 /+protected uint pitch; ///Contains the precalculated scanline size with the occassional padding for 8bit values.+/ 1888 ///Returns the width of the image in pixels. 1889 @property uint width() @nogc @safe pure nothrow const { 1890 return _imageData.width; 1891 } 1892 ///Returns the height of the image in pixels. 1893 @property uint height() @nogc @safe pure nothrow const { 1894 return _imageData.height; 1895 } 1896 ///Returns true if the image is indexed. 1897 @property bool isIndexed() @nogc @safe pure nothrow const { 1898 return _palette !is null; 1899 } 1900 ///Returns the number of bits used per sample. 1901 @property ubyte getBitdepth() @nogc @safe pure nothrow const { 1902 return _imageData.bitDepth; 1903 } 1904 ///Returns the number of bits used per colormap entry. 1905 @property ubyte getPaletteBitdepth() @nogc @safe pure nothrow const { 1906 if (_palette) return _palette.bitDepth; 1907 else return 0; 1908 } 1909 ///Returns the pixelformat of the image. See enumerator `PixelFormat` for more info. 1910 @property uint getPixelFormat() @nogc @safe pure nothrow const { 1911 return _imageData.pixelFormat; 1912 } 1913 ///Returns the pixelformat of the palette. See enumerator `PixelFormat` for more info. 1914 @property uint getPalettePixelFormat() @nogc @safe pure nothrow const { 1915 if (_palette) return _palette.paletteFormat; 1916 else return PixelFormat.Undefined; 1917 } 1918 ///Returns the background color index if there's any. Returns -1 if there's no background color, -2 if background color is not indexed. 1919 @property int backgroundColorIndex() @nogc @safe pure nothrow const { 1920 return -1; 1921 } 1922 ///Returns the background color if there's any, or a default value otherwise. 1923 @property ARGB8888 backgroundColor() @nogc @safe pure nothrow const { 1924 return ARGB8888.init; 1925 } 1926 /** 1927 * Returns the number of planes the image have. 1928 * Default is one. 1929 */ 1930 public ubyte getBitplanes() @safe pure { 1931 return _imageData.bitplanes; 1932 } 1933 /** 1934 * Returns a palette range, which can be used to read the palette. 1935 */ 1936 public IPalette palette() @safe @property pure { 1937 return _palette; 1938 } 1939 /** 1940 * Returns the image data. 1941 */ 1942 public IImageData imageData() @safe @property pure { 1943 return _imageData; 1944 } 1945 /** 1946 * Reads a single 32bit pixel. If the image is indexed, a color lookup will be done. 1947 */ 1948 public ARGB8888 readPixel(uint x, uint y) @safe pure { 1949 return _imageData.read(x, y); 1950 } 1951 /** 1952 * Looks up the index on the palette, then returns the color value as a 32 bit value. 1953 */ 1954 public ARGB8888 readPalette(size_t index) @safe pure { 1955 return _palette.read(index); 1956 } 1957 /** 1958 * Flips the image on the vertical axis. Useful to set images to the correct top-left screen origin point. 1959 */ 1960 public void flipVertical() @safe pure { 1961 _imageData.flipVertical; 1962 } 1963 /** 1964 * Flips the image on the vertical axis. Useful to set images to the correct top-left screen origin point. 1965 */ 1966 public void flipHorizontal() @safe pure { 1967 _imageData.flipVertical; 1968 } 1969 ///Returns true if the image originates from the top 1970 public bool topOrigin() @property @nogc @safe pure const { 1971 return false; 1972 } 1973 ///Returns true if the image originates from the right 1974 public bool rightSideOrigin() @property @nogc @safe pure const { 1975 return false; 1976 } 1977 }