{"id":44655617,"url":"https://github.com/AuburnSounds/gamut","last_synced_at":"2026-02-27T09:01:20.507Z","repository":{"id":41539294,"uuid":"390000158","full_name":"AuburnSounds/gamut","owner":"AuburnSounds","description":"Image encoding and decoding library for D.","archived":false,"fork":false,"pushed_at":"2025-11-23T23:12:54.000Z","size":1741,"stargazers_count":47,"open_issues_count":25,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-11-24T01:11:23.926Z","etag":null,"topics":["bc7","dds","gif","image","jpeg","png","qoi","qoix","tga"],"latest_commit_sha":null,"homepage":"","language":"D","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AuburnSounds.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-07-27T13:47:25.000Z","updated_at":"2025-11-23T23:12:46.000Z","dependencies_parsed_at":"2023-10-21T09:28:36.557Z","dependency_job_id":"d2d990cd-fa04-4ef1-9bdb-6d2575dc3090","html_url":"https://github.com/AuburnSounds/gamut","commit_stats":null,"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"purl":"pkg:github/AuburnSounds/gamut","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AuburnSounds%2Fgamut","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AuburnSounds%2Fgamut/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AuburnSounds%2Fgamut/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AuburnSounds%2Fgamut/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AuburnSounds","download_url":"https://codeload.github.com/AuburnSounds/gamut/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AuburnSounds%2Fgamut/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29888777,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T08:34:21.514Z","status":"ssl_error","status_checked_at":"2026-02-27T08:32:38.035Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bc7","dds","gif","image","jpeg","png","qoi","qoix","tga"],"created_at":"2026-02-14T22:00:25.252Z","updated_at":"2026-02-27T09:01:20.500Z","avatar_url":"https://github.com/AuburnSounds.png","language":"D","readme":"# Gamut\n\nGamut (DUB package: `gamut`) is an image decoding/encoding library for D.\n\nInspired by the FreeImage design, the Image concept is monomorphic and can do it all.\n\nGamut tries to have the fastest and most memory-conscious image decoders available in pure D code.\nIt is `nothrow @nogc @safe` for usage in -betterC and in disabled-runtime D.\n\n\n## Decoding\n\n- PNG: 8-bit and 16-bit, L/LA/RGB/RGBA\n- JPEG: 8-bit, L/RGB/RGBA, baseline and progressive\n- JPEG XL: 8-bit, RGB (no alpha support), encoded with `cjxl -e 4` or lower\n- TGA: 8-bit, indexed, L/LA/RGB/RGBA\n- GIF: indexed, animation support\n- BMP: indexed 1/4/8-bit no-RLE, 8-bit RGB/RGBA \n- SQZ: 8-bit, RGB\n- QOI: 8-bit, RGB/RGBA\n- QOIX: 8-bit, 10-bit, L/LA/RGB/RGBA. _Improvement upon QOI. This format may change between major Gamut tags, so is not a storage format._\n\n## Encoding\n\n- PNG. 8-bit, 16-bit, L/LA/RGB/RGBA\n- JPEG: 8-bit, greyscale/RGB, baseline\n- TGA: 8-bit, RGB/RGBA\n- GIF: 8-bit, RGBA, animation support\n- BMP: 8-bit, RGB/RGBA\n- SQZ: 8-bit, RGB (default to 2.5 bpp)\n- QOI: 8-bit, RGB/RGBA\n- QOIX: 8-bit, 10-bit, L/LA/RGB/RGBA, premultiplied alpha\n- DDS: BC7 encoded, 8-bit, RGB/RGBA\n\n\n\n## Changelog\n\n- **v3.3** Added [SQZ](https://github.com/MarcioPais/SQZ) input and output. A MIT-licensed codec that beat guetzli-encoded JPEG by about 30%! It also encodes faster, but decodes 2x slower than baseline JPEG. Quality comparisons [here](https://encode.su/threads/4183-SQZ-Low-complexity-scalable-lossless-and-lossy-image-compression-library).\n- **v3** Added premultiplied alpha pixel types. **BREAKING**.\n    * Decoders are now allowed to return any type if you do not specify `LOAD_PREMUL` or `LOAD_NO_PREMUL`.\n      Update your loading code. \n    * Introduce `image.premultiply()` and `image.unpremultiply()`.\n    * QOIX supports encoding premultiplied. Saves space and decoding times for transparent overlays. Only save space for 8-bit though.\n- **v2.6** Added JPEG XL input. 8-bit, no alpha, `cjxl --effort 4` or lower, raw streams not ISO BMFF.\n- **v2.5** Added BMP input.\n- **v2.4** Added BMP output.\n- **v2.3** Added GIF input and GIF output. Added multilayer images.\n- **v2.2** Added 16-bit PNG output.\n- **v2.1** Added TGA format support.\n- **v2** QOIX bitstream changed. Ways to disown and deallocate image allocation pointer. It's safe to update to latest tag in the same major version. Do keep a 16-bit source in case the bitstream changes.\n- **v1** Initial release.\n\n## Why QOIX?\n\nOur benchmark results for 8-bit color images:\n\n| Codec | decode mpps | encode mpps | bit-per-pixel |\n|-------|-------------|-------------|---------------|\n| PNG (stb) | 89.73   | 14.34       | 10.29693      |\n| QOI   | 201.9       | 150.8       | 10.35162      |\n| QOIX  | 179.0       | 125.0       | 7.93607       |\n\n\n- QOIX and QOI generally outperforms PNG in decoding speed and encoding speed.\n- QOIX outperforms QOI in compression efficiency at the cost of speed:\n  * because it's based upon better intra predictors\n  * because it is followed by LZ4, which removes some of the QOI worst cases.\n- QOIX adds support for 8-bit greyscale and greyscale + alpha images, with a \"QOI-plane\" custom codec.\n- QOIX adds support for 10-bit images, with a \"QOI-10b\" custom codec. It drops the last 6 bits of precision (lossy) to outperform PNG 16-bit in every way for some use cases.\n- QOIX support for premultiplied alpha brings even more speed and compression for transparent images.\n\nUse the `convert` tool to encode QOIX.\n\n\n\u0026nbsp;\n\n\n----\n\n\n\u0026nbsp;\n\n\n\n# Gamut API documentation\n\n## 1. `Image` basics\n\n\u003e **Key concept:**\n\u003e The `Image` struct is where most of the public API resides.\n\n### **1.1 Get the dimensions of an image:**\n  ```d\n  Image image = Image(800, 600);\n  int w = image.width();\n  int h = image.height();\n  assert(w == 800 \u0026\u0026 h == 600);\n  ```\n\n### **1.2 Get the pixel format of an image:**\n  ```d\n  Image image = Image(800, 600);\n  PixelType type = image.type();\n  assert(type == PixelType.rgba8); // rgba8 is default if not provided\n  ```\n\n  \u003e **Key concept:** `PixelType` completely describes the pixel format, for example `PixelType.rgb8` is a 24-bit format with one byte for red, green and blue components each (in that order). Nothing is specified about the color space though.\n\n  Here are the possible `PixelType`:\n\n\n  ```d\n  enum PixelType\n  {\n      l8,\n      l16,\n      lf32,\n      \n      la8,\n      la16,\n      laf32,\n      lap8,\n      lap16,\n      lapf32,\n\n      rgb8, \n      rgb16,\n      rgbf32,\n\n      rgba8,\n      rgba16,\n      rgbaf32\n      rgbap8,\n      rgbap16,\n      rgbapf32\n  }\n  ```\n\n  For now, all pixels format have one to four components:\n  - 1 component is implicitely Greyscale\n  - 2 components is implicitely Greyscale + alpha\n  - 3 components is implicitely Red + Green + Blue\n  - 4 components is implicitely Red + Green + Blue + Alpha\n\n  _**Bit-depth:** Each of these components can be represented in 8-bit, 16-bit, or 32-bit floating-point (0.0f to 1.0f range)._\n\n  _**Alpha premultiplication:** When an alpha channel exist, both premultiplied and non-premultiplied variants exist._\n\n\n### **1.3 Create an image:**\n\n\u003e Different ways to create an `Image`:\n\u003e - `create()` or regular constructor `this()` creates a new owned image filled with zeros.\n\u003e - `createNoInit()` or `setSize()` creates a new owned uninitialized image.\n\u003e - `createView()` creates a view into existing data.\n\u003e - `createNoData()` creates a new image with no data pointed to (still has a type, size...).\n\n  ```d\n  // Create with transparent black.\n  Image image = Image(640, 480, PixelType.rgba8); \n  image.create(640, 480, PixelType.rgba8);\n\n  // Create with no initialization.\n  image.setSize(640, 480, PixelType.rgba8);\n  image.createNoInit(640, 480, PixelType.rgba8);\n\n  // Create view into existing data. Existing data is borrowed.\n  image.createView(data.ptr, w, h, PixelType.rgb8, pitchbytes);\n  ```\n\n - At creation time, the `Image` forgets about its former life, and leaves any `isError()` state or former data/type\n - `Image.init` is in `isError()` state\n - `isValid()` can be used instead of `!isError()`\n - Being valid == not being error == having a `PixelType`\n\n\n\u0026nbsp;\n\n\n----\n\n\n\u0026nbsp;\n\n\n## 2. Loading and saving an image\n\n### **2.1 Load an `Image` from a file:**\n\nAnother way to create an `Image` is to load an encoded image.\n\n  ```d\n  Image image;\n  image.loadFromFile(\"logo.png\");\n  if (image.isError)\n      throw new Exception(image.errorMessage);\n  ```\n\n  You can then read `width()`, `height()`, `type()`, etc...\n\n  \u003e **There is no exceptions in Gamut.** Instead the Image itself has an error API:\n  \u003e - `bool isError()` return `true` if the `Image` is in an error state. In an error state, the image can't be used anymore until recreated (for example, loading another file).\n  \u003e - `const(char)[] errorMessage()` is then available, and is guaranteed to be zero-terminated with an extra byte.\n\n\n### **2.2 Load an image from memory:**\n  ```d\n  auto pngBytes = cast(const(ubyte)[]) import(\"logo.png\"); \n  Image image;\n  image.loadFromMemory(pngBytes);\n  if (!image.isValid) \n      throw new Exception(image.errorMessage());\n  ```\n  \u003e **Key concept:** You can force the loaded image to be a certain type using `LoadFlags`, or call `convertTo()` after load.\n\n  Here are the possible `LoadFlags`:\n  ```d\n  LOAD_NORMAL      // Default: preserve type from original.\n  \n  LOAD_ALPHA       // Force one alpha channel.\n  LOAD_NO_ALPHA    // Force zero alpha channel.\n\n  LOAD_GREYSCALE   // Force greyscale.\n  LOAD_RGB         // Force RGB values.\n  \n  LOAD_8BIT        // Force 8-bit `ubyte` per component.\n  LOAD_16BIT       // Force 16-bit `ushort` per component.\n  LOAD_FP32        // Force 32-bit `float` per component.\n\n  LOAD_PREMUL      // Force premultiplied alpha representation (if alpha exists)\n  LOAD_NO_PREMUL   // Force non-premultiplied alpha representation (if alpha exists)\n  ```\n\n  Example:\n  ```d\n  Image image;  \n  image.loadFromMemory(pngBytes, LOAD_RGB | LOAD_ALPHA | LOAD_8BIT | LOAD_NO_PREMUL);  // force PixelType.rgba8 \n  ```\n  Not all load flags are compatible, for example `LOAD_8BIT` and `LOAD_16BIT` cannot be used together.\n    \n\n### **2.3 Convert to another `PixelType`:**\n\nHowever, load flags are not the only way to select a `PixelType`, you can provide one explicitely with `convertTo`.\n\n  ```d\n  // Convert to grey + one alpha channel, 16-bit\n  image.convertTo(PixelType.la16); \n\n  // Convert to RGB + one alpha channel, 8-bit\n  image.convertTo(PixelType.rgba8); \n  ```\n\n\n### **2.4 Save an image to a file:**\n\n  ```d\n  Image image;\n  if (!image.saveToFile(\"output.png\"))\n      throw new Exception(\"Writing output.png failed\");\n  ```\n\n  \u003e **Key concept:** `ImageFormat` is simply the codecs/containers files Gamut encode and decodes to.\n\n  ```d\n  enum ImageFormat\n  {\n      unknown,\n      JPEG,\n      PNG,\n      QOI,\n      QOIX,\n      DDS,\n      TGA,\n      GIF,\n      JXL,\n      SQZ\n  }\n  ```\n\n  This can be used to avoid inferring the output format from the filename:\n  ```d\n  Image image;\n  if (!image.saveToFile(ImageFormat.PNG, \"output.png\"))\n      throw new Exception(\"Writing output.png failed\");\n  ```\n\n### **2.5 Save an image to memory:**\n\n  ```d\n  Image image;\n  ubyte[] qoixEncoded = image.saveToMemory(ImageFormat.QOIX);\n  scope(exit) freeEncodedImage(qoixEncoded);\n  ```\n\n  The returned slice must be freed up with `freeEncodedImage`.\n\n\n\n### **2.6 Convert an image to QOIX for faster load**\n\n```d\n  Image image;\n  image.loadFromFile(\"input.png\");\n  image.saveToFile(\"output.qoix\"); // .qoix loads faster\n  ```\n\n\n\u0026nbsp;\n\n\n----\n\n\n\u0026nbsp;\n\n\n## 3. Accessing image pixels\n\n### **3.1 Get the row pitch, in bytes:**\n  ```d\n  int pitch = image.pitchInBytes();\n  ```\n\n  \u003e **Key concept:** The image `pitch` is the distance between the start of two consecutive scanlines, in bytes.\n  **IMPORTANT: This pitch can be negative.**\n\n### **3.2 Access a row of pixels:**\n  ```d\n  void* scan = image.scanptr(y);  // get pointer to start of pixel row\n  void[] row = image.scanline(y); // get slice of pixel row\n  ```\n  \u003e **Key concept:** The scanline is `void*` because the type it points to depends upon the `PixelType`. In a given scanline, the bytes `scan[0..abs(pitchInBytes())]` are all accessible, even if they may be outside of the image (trailing pixels, gap bytes for alignment, border pixels).\n\n\n### **3.3 Iterate on pixels:**\n  ```d\n  assert(image.type == PixelType.rgba16);\n  assert(image.hasData());\n  for (int y = 0; y \u003c image.height(); ++y)\n  {\n      ushort* scan = cast(ushort*) image.scanptr(y);\n      for (int x = 0; x \u003c image.width(); ++x)\n      {\n          ushort r = scan[4*x + 0];\n          ushort g = scan[4*x + 1];\n          ushort b = scan[4*x + 2];\n          ushort a = scan[4*x + 3];\n      }\n  }\n  ```\n  \u003e **Key concept:** The default is that you do not access pixels in a contiguous manner. See 4. for layout constraints that allow you to get all pixels at once.\n\n\n\n### **3.4 Process pixels:**\n\nHere is how to process pixels of an `rgba8` image in-place.\n\n  ```d\n  void liftGammaGain(ref Image image, \n                     float lift,  // 0 to 1\n                     float gamma, \n                     float gain)\n  {\n      assert(image.type == PixelType.rgba8);\n      assert(image.hasData());\n      for (int y = 0; y \u003c image.height(); ++y)\n      {\n          byte* scan = cast(ubyte*) image.scanptr(y);\n          for (int x = 0; x \u003c image.width(); ++x)\n          {\n              float r = scan[4*x + 0] / 255.0f;\n              float g = scan[4*x + 1] / 255.0f;\n              float b = scan[4*x + 2] / 255.0f;\n              float a = scan[4*x + 3] / 255.0f;\n              r = (gain * (r + lift * (1-r)))^^(1/gamma);\n              g = (gain * (g + lift * (1-r)))^^(1/gamma);\n              b = (gain * (b + lift * (1-r)))^^(1/gamma);\n              if (r \u003c 0) r = 0;\n              if (g \u003c 0) g = 0;\n              if (b \u003c 0) b = 0;\n              if (r \u003e 1) r = 1;\n              if (g \u003e 1) g = 1;\n              if (b \u003e 1) b = 1;\n              scan[4*x+0] = cast(ubyte)(r * 255);\n              scan[4*x+1] = cast(ubyte)(g * 255);\n              scan[4*x+2] = cast(ubyte)(b * 255);\n          }\n      }\n  }   \n  ```\n\n  \u003e **Key concept:** `.scanptr()` pointers are untyped.\n\n\n\u0026nbsp;\n\n\n----\n\n\n\u0026nbsp;\n\n\n\n## 4. Layout constraints\n\nOne of the most interesting feature of Gamut!\nImages in Gamut can follow given constraints over the data layout.  \n\n  \u003e **Key concept:** `LayoutConstraint` are carried by images all their life.\n\nExample:\n\n  ```d\n  // Do nothing in particular.\n  LayoutConstraint constraints = LAYOUT_DEFAULT; // 0 = default\n\n  // Layout can be given directly at image creation or afterwards.\n  Image image;  \n  image.loadFromMemory(pngBytes, constraints); \n\n  // Now the image has a 1 pixel border (at least).\n  // Changing the layout only reallocates if needed.\n  image.setLayout(LAYOUT_BORDER_1);\n  \n  // Those layout constraints are preserved.\n  // (but: not the excess bytes content, if reallocated)\n  image.convertToGreyscale();\n  assert(image.layoutConstraints() == LAYOUT_BORDER_1);   \n  ```\n\n**Important:** Layout constraints are about the minimum guarantee you want. Your image may be _more_ constrained than that in practice, but you can't rely on that.   \n- If you don't specify `LAYOUT_VERT_STRAIGHT`, you should expect your image to be possibly stored upside-down, and account for that possibility.\n- If you don't specify `LAYOUT_SCANLINE_ALIGNED_16`, you should not expect your scanlines to be aligned on 16-byte boundaries, even though that can happen accidentally.\n\n\nBeware not to accidentally reset constraints when resizing:\n```d\n// If you do not provide layout constraints, \n// the one choosen is 0, the most permissive.\nimage.setSize(640, 480, PixelType.rgba8, LAYOUT_TRAILING_3);\n```\n\n\n### 4.1 Scanline alignment\n    \n  \u003e **Scanline alignment** guarantees minimum alignment of each scanline.\n\n```d\nLAYOUT_SCANLINE_ALIGNED_1 = 0\nLAYOUT_SCANLINE_ALIGNED_2\nLAYOUT_SCANLINE_ALIGNED_4\nLAYOUT_SCANLINE_ALIGNED_8\nLAYOUT_SCANLINE_ALIGNED_16\nLAYOUT_SCANLINE_ALIGNED_32\nLAYOUT_SCANLINE_ALIGNED_64\nLAYOUT_SCANLINE_ALIGNED_128\n```\n\n### 4.2 Layout multiplicity  \n\n\u003e **Multiplicity** guarantees access to pixels 1, 2, 4 or 8 at a time. It does this with excess pixels at the end of the scanline, but they need not exist if the scanline has the right width.\n\n```d\nLAYOUT_MULTIPLICITY_1 = 0\nLAYOUT_MULTIPLICITY_2\nLAYOUT_MULTIPLICITY_4\nLAYOUT_MULTIPLICITY_8\n```\nTogether with scanline alignment, this allow processing a scanline using aligned SIMD without processing the last few pixels differently.\n     \n\n### 4.3 Trailing pixels\n\n\u003e **Trailing pixels** gives you up to 7 excess pixels after each scanline. \n```d\nLAYOUT_TRAILING_0 = 0\nLAYOUT_TRAILING_1\nLAYOUT_TRAILING_3\nLAYOUT_TRAILING_7\n```\n\nAllows unaligned SIMD access by itself.\n\n### 4.4 Pixel border\n\n\u003e **Border** gives you up to 3 excess pixels around an image, eg. for filtering.\n```d\nLAYOUT_BORDER_0 = 0\nLAYOUT_BORDER_1\nLAYOUT_BORDER_2\nLAYOUT_BORDER_3\n```\n\n### 4.5 Forcing pixels to be upside down or straight\n\u003e **Vertical** constraint forces the image to be stored in a certain vertical direction (by default: any).\n```d\nLAYOUT_VERT_FLIPPED\nLAYOUT_VERT_STRAIGHT\n```\n\n\n### 4.6 Gapless pixel access\n\u003e The **Gapless** constraint force the image to have contiguous scanlines without excess bytes.\n```d\nLAYOUT_GAPLESS\n```\n\nIf you have both `LAYOUT_GAPLESS` and `LAYOUT_VERT_STRAIGHT`, then you can access a slice of all pixels at once, with the `ubyte[] allPixelsAtOnce()` method.\n\n  ```d\n  image.setSize(640, 480, PixelType.rgba8, LAYOUT_GAPLESS | LAYOUT_VERT_STRAIGHT);\n  ubyte[] allpixels = image.allPixelsAtOnce(y);\n  ```\n\n`LAYOUT_GAPLESS` is incompatible with constraints that needs excess bytes, like borders, scanline alignment, trailing pixels...\n\n\n\u0026nbsp;\n\n\n----\n\n\n\u0026nbsp;\n\n\n\n## 5. Geometric transforms\n\nGamut provides a few geometric transforms.\n\n```d\nImage image;\nimage.flipHorizontal(); // Flip image pixels horizontally.\nimage.flipVertical();   // Flip image vertically (pixels or logically, depending on layout)\n```\n\n\n\u0026nbsp;\n\n\n----\n\n\n\u0026nbsp;\n\n\n## 6. Multi-layer images\n\n### 6.1 Create multi-layer images\n\nAll `Image` have a number of layers.\n```d\nImage image;\nimage.create(640 ,480);\nassert(image.layers == 1); // typical image has one layer\nassert(image.hasOneLayer);\n```\n\n- Create a multi-layer image, cleared with zeroes:\n```d\n// This single image has 24 black layers.\nimage.createLayered(800, 600, 24); \nassert(image.layers == 24);\n```\n- Create a multi-layer uninitialized image:\n```d\n// Make space for 24 800x600 rgba8 different images.\nimage.createLayeredNoInit(800, 600, 24);\nassert(image.layers == 24);\n```\n\n- Create a multi-layer as a view into existing data:\n```d\n// Create view into existing data.\n// layerOffsetBytes is byte offset between first scanlines \n// of two consecutive layers.\nimage.createLayeredViewFromData(data.ptr, \n                                w, h, numLaters, \n                                PixelType.rgb8, \n                                pitchbytes,\n                                layerOffsetBytes);\n```\n\n\u003e Gamut **Image** is secretly similar to 2D Array Texture in OpenGL. Each layer is store consecutively in memory.\n\n\n### 6.2 Get individual layer\n\n`image.layer(int index)` return non-owning view of a single-layer.\n\n```d\nImage image;\nimage.create(640, 480, 5);\nassert(image.layer(4).width  == 640);\nassert(image.layer(4).height == 480);\nassert(image.layer(4).layers ==   1);\n```\n\n\u003e **Key concept:** All image operations work on all layers by default. \n\n\n\u003e **Regarding layout:** Each layer has its own border, trailing bytes... and follow the same layout constraints. Moreover, `LAYOUT_GAPLESS` also constrain the layers to be immediately next in memory, without any byte (like it constrain the scanlines). The layers **cannot** be stored in reverse order.\n\n\n### 6.2 Get sub-range of layers\n\n`image.layerRange(int start, int stop)` return non-owning view of a several layers.\n\n\n\n\n### 6.3 Access layer pixels\n\n- Get a pointer to a scanline: \n\n```d\n// Get the 160th scanline of layer 2.\nvoid* scan = image.layerptr(2, 160);\n```\n\n- Get a slice of a whole scanline: \n\n```d\n// Get the 160th scanline of layer 2.\nvoid[] line = image.layerline(2, 160);\n```\n\nActually, `scanptr(y)` and `scanline(y)` only access the layer index 0.\n```d\n// Get the 160th scanline of layer 0.\nvoid* scan = image.scanptr(160);\nvoid[] line = image.scanline(160);\n```\n\n\u003e  **Key concept:** First layer has index 0.\n\nConsequently, there are two ways to access pixel data in `Image`:\n\n```d\n// Two different ways to access layer pixels.\nassert(image.layer(2).scanline(160) == image.layerline(2, 160)\n```\n\u003e **The calls*:* \n\u003e  - `image.layerptr(layer, y)`\n\u003e  - `image.layerline(layer, y)`\n\u003e\n\u003e _are like:_\n\u003e\n\u003e - `image.scanptr(y)`\n\u003e - `image.scanline(y)`\n\u003e\n\u003e _but take a **layer index**._\n \n","funding_links":[],"categories":["compression"],"sub_categories":["ImageCodec"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAuburnSounds%2Fgamut","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAuburnSounds%2Fgamut","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAuburnSounds%2Fgamut/lists"}