1 /* 2 * dimage - pcx.d 3 * by Laszlo Szeremi 4 * 5 * Copyright under Boost Software License. 6 */ 7 8 module leftout.pcx; 9 10 import dimage.base; 11 import dimage.util; 12 static import std.stdio; 13 import bitleveld.datatypes; 14 import std.bitmanip; 15 16 /** 17 * Implements ZSoft PCX file handling. 18 * Dummied out due to complexity. 19 */ 20 public class PCX : Image { 21 /** 22 * Version information 23 */ 24 public enum Version : ubyte { 25 ver2_5 = 0, 26 ver2_8 = 2, 27 ver2_8noPal = 3, 28 paintbrushForWin = 4, 29 ver3_0 = 5, 30 } 31 /** 32 * Implementation of the PCX header 33 */ 34 public struct Header { 35 ubyte identifier = 0x0A; ///PCX ID number 36 ubyte _version; ///Version number 37 ubyte encoding; ///Encoding format 38 ubyte bitsPerPixel; ///Bits per pixel 39 ushort xStart; ///Left of image 40 ushort yStart; ///Top of image 41 ushort xEnd; ///Right of image 42 ushort yEnd; ///Bottom of image 43 ushort hRes; ///Horizontal resolution 44 ushort vRes; ///Vertical resolution 45 ubyte[48] palette; ///16-Color EGA palette 46 ubyte reserved1; ///Unused padding 47 ubyte numBitPlanes; ///N of bitplanes 48 ushort bytesPerLine; ///Size of scanlines in bytes 49 ushort paletteType; ///Palette type 50 ushort hScreenSize; ///Horizontal screen size 51 ushort vScreenSize; ///Vertical screen size 52 ubyte reserved2; ///Unused padding 53 } 54 protected Header header; ///Contains common information about the image 55 protected uint pitch; 56 mixin PlanarAccess3Bit; 57 mixin PlanarAccess4Bit; 58 mixin ChunkyAccess2Bit; 59 mixin MonochromeAccess; 60 /** 61 * Blank constructor for files 62 */ 63 protected this() @nogc @safe pure { 64 65 } 66 /** 67 * Basic constructor to create pcx objects 68 */ 69 public this(Header header, ubyte[] imageData, ubyte[] paletteData = []){ 70 //Validate header 71 this.header = header; 72 if (getPixelFormat == PixelFormat.Undefined) { 73 throw new ImageFormatException("Unsupported pixelformat."); 74 } 75 //Force set version 76 switch (getPixelFormat) { 77 case PixelFormat.Indexed2Bit, PixelFormat.Indexed1Bit: 78 this.header._version = Version.ver2_5; 79 break; 80 case PixelFormat.Indexed8Bit, PixelFormat.RGB888, PixelFormat.RGBA8888: 81 this.header._version = Version.ver3_0; 82 break; 83 case PixelFormat.Planar8Color: 84 this.header._version = Version.paintbrushForWin; 85 break; 86 default: 87 if (paletteData.length) { 88 this.header._version = Version.ver2_8; 89 } else { 90 this.header._version = Version.ver2_8noPal; 91 } 92 break; 93 } 94 if (paletteData.length <= 48) { 95 for (int i ; i < paletteData.length ; i++) { 96 this.header.palette[i] = paletteData[i]; 97 } 98 this.paletteData = this.header.palette[0..$]; 99 } else { 100 this.paletteData = paletteData; 101 } 102 } 103 /** 104 * Loads a *.PCX file. 105 */ 106 public static load (F = std.stdio.File) (F file) { 107 PCX result = new PCX(); 108 ubyte[] buffer; 109 buffer.length = Header.sizeof; 110 buffer = file.rawRead(buffer); 111 if (buffer.length != Header.sizeof) 112 throw new ImageFormatException ("File is corrupted, cannot be read, and/or is not a *.PCX file!"); 113 result.header = reinterpretGet!Header(buffer); 114 buffer.length = result.header.bytesPerLine; 115 for (int y ; y < result.height ; y++){ 116 buffer = file.rawRead(buffer); 117 if (buffer.length == result.header.bytesPerLine) 118 result.imageData ~= buffer; 119 else 120 throw new ImageFormatException ("File is corrupted, cannot be read, and/or is not a *.PCX file!"); 121 } 122 if (file.size - file.tell) { 123 result.paletteData.length = 768; 124 result.paletteData = file.rawRead(result.paletteData); 125 if (result.paletteData.length == 768) 126 throw new ImageFormatException ("File is corrupted, cannot be read, and/or is not a *.PCX file!"); 127 } 128 return result; 129 } 130 protected void setupDelegates() @nogc @safe pure { 131 switch (getPixelFormat) { 132 case PixelFormat.Indexed1Bit: 133 indexReader8Bit = &_readIndex_1bit; 134 indexWriter8Bit = &_writeIndex_1bit; 135 indexReader16bit = &_indexReadUpconv; 136 pixelReader = &_readAndLookup; 137 break; 138 case PixelFormat.Indexed2Bit: 139 indexReader8Bit = &_readIndex_2bit; 140 indexWriter8Bit = &_writeIndex_2bit; 141 indexReader16bit = &_indexReadUpconv; 142 pixelReader = &_readAndLookup; 143 break; 144 case PixelFormat.Indexed8Bit: 145 indexReader8Bit = &_readPixel_8bit; 146 indexWriter8Bit = &_writePixel!(ubyte); 147 indexReader16bit = &_indexReadUpconv; 148 pixelReader = &_readAndLookup; 149 break; 150 case PixelFormat.Planar8Color: 151 indexReader8Bit = &_readIndex_planar_3bit; 152 indexWriter8Bit = &_writeIndex_planar_3bit; 153 indexReader16bit = &_indexReadUpconv; 154 pixelReader = &_readAndLookup; 155 break; 156 case PixelFormat.Planar16Color: 157 indexReader8Bit = &_readIndex_planar_4bit; 158 indexWriter8Bit = &_writeIndex_planar_4bit; 159 indexReader16bit = &_indexReadUpconv; 160 pixelReader = &_readAndLookup; 161 break; 162 case PixelFormat.RGB888: 163 pixelReader = &_readPixelAndUpconv!(Pixel24Bit); 164 break; 165 case PixelFormat.RGBA8888: 166 pixelReader = &_readPixelAndUpconv!(Pixel32BitRGBALE); 167 break; 168 default: 169 break; 170 } 171 } 172 override uint width() @nogc @safe @property const pure { 173 return header.hRes; 174 } 175 override uint height() @nogc @safe @property const pure { 176 return header.vRes; 177 } 178 override bool isIndexed() @nogc @safe @property const pure { 179 return header.bitsPerPixel != 8 || header.numBitPlanes == 1; 180 } 181 override ubyte getBitdepth() @nogc @safe @property const pure { 182 return cast(ubyte)(header.bitsPerPixel * header.numBitPlanes); 183 } 184 override ubyte getPaletteBitdepth() @nogc @safe @property const pure { 185 return isIndexed() ? 24 : 0; 186 } 187 override uint getPixelFormat() @nogc @safe @property const pure { 188 if (header.numBitPlanes == 1) { 189 switch (header.bitsPerPixel) { 190 case 1: return PixelFormat.Indexed1Bit; 191 case 2: return PixelFormat.Indexed2Bit; 192 case 8: return PixelFormat.Indexed8Bit; 193 default: break; 194 } 195 } else if (header.numBitPlanes == 3) { 196 switch (header.bitsPerPixel) { 197 case 1: return PixelFormat.Planar8Color; 198 case 8: return PixelFormat.RGB888; 199 default: break; 200 } 201 } else if (header.numBitPlanes == 4) { 202 switch (header.bitsPerPixel) { 203 case 1: return PixelFormat.Planar16Color; 204 case 8: return PixelFormat.RGBA8888; 205 default: break; 206 } 207 } 208 return PixelFormat.Undefined; 209 } 210 override uint getPalettePixelFormat() @nogc @safe @property const pure { 211 return isIndexed() ? PixelFormat.RGB888 : PixelFormat.Undefined; 212 } 213 /** 214 * Returns the number of planes the image have. 215 * If bitdepth is 1, then the image is a planar indexed image. 216 */ 217 override public ubyte getBitplanes() @nogc @safe @property const pure { 218 return header.bitsPerPixel == 1 ? header.numBitPlanes : 1; 219 } 220 }