├── .gitignore ├── License.md ├── PcxFileType.cs ├── PcxFileType.csproj ├── PcxFileType.sln ├── PcxSaveConfigToken.cs ├── PcxSaveConfigWidget.cs ├── PcxSaveConfigWidget.resx ├── Properties └── AssemblyInfo.cs ├── Quantize ├── OctreeQuantizer.cs ├── PaletteQuantizer.cs ├── PaletteTable.cs └── Quantizer.cs ├── ReadMe.md └── _Preset_Palettes_Example_File ├── Preset_Pallettes.pst_pal.txt └── __readme.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | obj/ 3 | *.suo 4 | *.user 5 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | 2 | Paint.NET PCX Plugin 3 | Copyright (C) 2006-2015 Joshua Bell (inexorabletash@gmail.com) 4 | 5 | Portions Copyright (c) 2006 Rick Brewster, Chris Crosetto, Dennis Dietrich, 6 | Tom Jackson, Michael Kelsey, Brandon Ortiz, Craig Taylor, Chris Trevino, 7 | and Luke Walker. 8 | 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a 11 | copy of this software and associated documentation files (the "Software"), 12 | to deal in the Software without restriction, including without limitation 13 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | and/or sell copies of the Software, and to permit persons to whom the 15 | Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /PcxFileType.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // PCX Plugin for Paint.NET 3 | // Copyright (C) 2006 Joshua Bell (inexorabletash@gmail.com) 4 | // Portions Copyright (C) 2006 Rick Brewster, et. al. 5 | // See License.txt for complete licensing and attribution information. 6 | ///////////////////////////////////////////////////////////////////////////////// 7 | 8 | // References: 9 | // * PCX File Format - http://courses.ece.uiuc.edu/ece390/books/labmanual/graphics-pcx.html 10 | // * PCX File Format - http://www.fileformat.info/format/pcx/ 11 | // * EGA Color Palette - http://wasteland.wikispaces.com/EGA+Colour+Palette 12 | 13 | // Test images: 14 | // * 1, 2, 8, 24: http://www.efg2.com/Lab/Library/Delphi/Graphics/FileFormatsAndConversion.htm (PCX.ZIP) 15 | // * 8, 24: http://www.fileformat.info/format/pcx/sample/index.htm 16 | // * 1: http://memory.loc.gov/ammem/help/view.html 17 | 18 | // NOTE: Only supports saving 256-color RLE encoded images 19 | 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// 21 | //Modified by EzArIk(Thomas C. Maylam [on (GB)11/20/2016]) to support the use of preset Pallettes.// 22 | //////////////////////////////////////////////////////////////////////////////////////////////////// 23 | using PaintDotNet; 24 | using PaintDotNet.IO; 25 | using PcxFileTypePlugin.Quantize; 26 | using System; 27 | using System.Collections.Generic; 28 | using System.Drawing; 29 | using System.Drawing.Imaging; 30 | using System.IO; 31 | using System.Runtime.InteropServices; 32 | 33 | 34 | namespace PcxFileTypePlugin 35 | { 36 | #region Paint.NET Registration 37 | // Register our PcxFileType object 38 | public class PcxFileTypes : IFileTypeFactory 39 | { 40 | public static readonly FileType Pcx = new PcxFileType(); 41 | private static FileType[] fileTypes = new FileType[] { Pcx }; 42 | 43 | public FileType[] GetFileTypeInstances() 44 | { 45 | return (FileType[])fileTypes.Clone(); 46 | } 47 | } 48 | #endregion 49 | 50 | [Guid("D22EBDD9-0A86-4e00-957B-550010B82C09")] 51 | public class PcxFileType : FileType 52 | { 53 | #region Paint.NET File Format API 54 | 55 | public PcxFileType() : base( 56 | "PCX", 57 | FileTypeFlags.SupportsSaving | FileTypeFlags.SupportsLoading, 58 | new string[] { ".pcx" }) // has extension of '.pcx' 59 | { 60 | } 61 | 62 | public override SaveConfigWidget CreateSaveConfigWidget() 63 | { 64 | return new PcxSaveConfigWidget(); 65 | } 66 | 67 | protected override SaveConfigToken OnCreateDefaultSaveConfigToken() 68 | { 69 | return new PcxSaveConfigToken(0, false, false, 8, true, false, null, 0); 70 | } 71 | 72 | #endregion 73 | 74 | #region File Structure, Header and Constants 75 | 76 | //////////////////////////////////////////////////////////// 77 | // PCX File Structure 78 | // 79 | // Header 128 bytes 80 | // 81 | // Pixel Data scan0 plane0 82 | // scan0 plane1 83 | // .. 84 | // scan0 planeN 85 | // scan1 plane0 86 | // scan1 plane1 87 | // .. 88 | // scan1 planeN 89 | // ... 90 | // scanM planeN 91 | // 92 | // Palette 0x0C 93 | // (8-bit only) r0,g0,b0 94 | // r1,g1,b1 95 | // ... 96 | // r256,g256,b256 97 | //////////////////////////////////////////////////////////// 98 | 99 | //////////////////////////////////////////////////////////// 100 | // struct PCXHeader 101 | // { 102 | // BYTE Manufacturer; // Constant Flag 10 = ZSoft .PCX 103 | // BYTE Version; // Version Information 104 | // // 0 = Version 2.5 105 | // // 2 = Version 2.8 w/palette information 106 | // // 3 = Version 2.8 w/o palette information 107 | // // 4 = (PC Paintbrush for Windows) 108 | // // 5 = Version 3.0 109 | // BYTE Encoding; // 1 = .PCX run length encoding 110 | // BYTE BitsPerPixel; // Number of bits/pixel per plane (1, 2, 4 or 8) 111 | // WORD XMin; // Picture Dimensions 112 | // WORD YMin; // (Xmin, Ymin) - (Xmax - Ymax) inclusive 113 | // WORD XMax; 114 | // WORD YMax; 115 | // WORD HDpi; // Horizontal Resolution of creating device 116 | // WORD VDpi; // Vertical Resolution of creating device 117 | // BYTE ColorMap[48]; // Color palette for 16-color palette 118 | // BYTE Reserved; 119 | // BYTE NPlanes; // Number of color planes 120 | // WORD BytesPerLine; // Number of bytes per scan line per color plane (always even for .PCX files) 121 | // WORD PaletteInfo; // How to interpret palette - 1 = color/BW, 2 = grayscale 122 | // BYTE Filler[58]; 123 | // }; 124 | //////////////////////////////////////////////////////////// 125 | 126 | private enum PcxId : byte 127 | { 128 | ZSoftPCX = 10 129 | }; 130 | 131 | private enum PcxVersion : byte 132 | { 133 | Version2_5 = 0, 134 | Version2_8_Palette = 2, 135 | Version2_8_DefaultPalette = 3, 136 | Version3_0 = 5 137 | }; 138 | 139 | private enum PcxEncoding : byte 140 | { 141 | None = 0, 142 | RunLengthEncoded = 1 143 | }; 144 | 145 | private enum PcxPaletteType : byte 146 | { 147 | Indexed = 1, 148 | Grayscale = 2 149 | }; 150 | 151 | private const int PcxRleMask = 0xC0; 152 | private const int PcxPaletteMarker = 0x0C; 153 | 154 | 155 | private class PcxHeader 156 | { 157 | public PcxId id = PcxId.ZSoftPCX; 158 | public PcxVersion version = PcxVersion.Version3_0; 159 | public PcxEncoding encoding = PcxEncoding.RunLengthEncoded; 160 | public byte bitsPerPixel; 161 | public ushort xMin; 162 | public ushort yMin; 163 | public ushort xMax; 164 | public ushort yMax; 165 | public ushort hDpi; 166 | public ushort vDpi; 167 | public byte[] colorMap = new byte[48]; 168 | public byte reserved = 0; 169 | public byte nPlanes; 170 | public ushort bytesPerLine; 171 | public PcxPaletteType paletteInfo; 172 | public byte[] filler = new byte[58]; 173 | 174 | public void Write(Stream output) 175 | { 176 | output.WriteByte((byte)id); 177 | output.WriteByte((byte)version); 178 | output.WriteByte((byte)encoding); 179 | output.WriteByte(bitsPerPixel); 180 | StreamExtensions.WriteUInt16(output, xMin); 181 | StreamExtensions.WriteUInt16(output, yMin); 182 | StreamExtensions.WriteUInt16(output, xMax); 183 | StreamExtensions.WriteUInt16(output, yMax); 184 | StreamExtensions.WriteUInt16(output, hDpi); 185 | StreamExtensions.WriteUInt16(output, vDpi); 186 | for (int i = 0; i < colorMap.Length; i++) 187 | { 188 | output.WriteByte(colorMap[i]); 189 | } 190 | output.WriteByte(reserved); 191 | output.WriteByte(nPlanes); 192 | StreamExtensions.WriteUInt16(output, bytesPerLine); 193 | StreamExtensions.WriteUInt16(output, (ushort)PcxPaletteType.Indexed); 194 | for (int i = 0; i < filler.Length; i++) 195 | { 196 | output.WriteByte(filler[i]); 197 | } 198 | 199 | } 200 | 201 | public PcxHeader() 202 | { 203 | } 204 | 205 | public PcxHeader(Stream input) 206 | { 207 | id = (PcxId)ReadByte(input); 208 | version = (PcxVersion)ReadByte(input); 209 | encoding = (PcxEncoding)ReadByte(input); 210 | bitsPerPixel = ReadByte(input); 211 | xMin = ReadUInt16(input); 212 | yMin = ReadUInt16(input); 213 | xMax = ReadUInt16(input); 214 | yMax = ReadUInt16(input); 215 | hDpi = ReadUInt16(input); 216 | vDpi = ReadUInt16(input); 217 | for (int i = 0; i < colorMap.Length; i++) 218 | colorMap[i] = ReadByte(input); 219 | reserved = ReadByte(input); 220 | nPlanes = ReadByte(input); 221 | bytesPerLine = ReadUInt16(input); 222 | paletteInfo = (PcxPaletteType)ReadUInt16(input); 223 | for (int i = 0; i < filler.Length; i++) 224 | filler[i] = ReadByte(input); 225 | } 226 | 227 | private byte ReadByte(Stream input) 228 | { 229 | int byteRead = input.ReadByte(); 230 | if (byteRead == -1) 231 | throw new EndOfStreamException(); 232 | 233 | return (byte)byteRead; 234 | } 235 | 236 | private ushort ReadUInt16(Stream input) 237 | { 238 | int shortRead = StreamExtensions.ReadUInt16(input); 239 | if (shortRead == -1) 240 | throw new EndOfStreamException(); 241 | return (ushort)shortRead; 242 | } 243 | } 244 | 245 | #endregion 246 | 247 | #region Palette 248 | 249 | /** 250 | * PcxPalette 251 | * 252 | * Always either 16 or 256 entries 253 | */ 254 | private class PcxPalette 255 | { 256 | public static readonly uint[] MONO_PALETTE = new uint[] { 0x000000, 0xFFFFFF, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 }; 257 | public static readonly uint[] CGA_PALETTE = new uint[] { 0x000000, 0x00AAAA, 0xAA00AA, 0xAAAAAA, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 }; 258 | public static readonly uint[] EGA_PALETTE = new uint[] { 0x000000, 0x0000A8, 0x00A800, 0x00A8A8, 0xA80000, 0xA800A8, 0xA85400, 0xA8A8A8, 0x545454, 0x5454FE, 0x54FE54, 0x54FEFE, 0xFE5454, 0xFE54FE, 0xFEFE54, 0xFEFEFE }; 259 | 260 | private ColorBgra[] m_palette; 261 | 262 | public uint Size 263 | { 264 | get { return (uint)m_palette.Length; } 265 | } 266 | 267 | public ColorBgra this[uint index] 268 | { 269 | get { return m_palette[index]; } 270 | set { m_palette[index] = value; } 271 | } 272 | 273 | public PcxPalette(uint size) 274 | { 275 | if (size != 2 && size != 16 && size != 256) 276 | throw new FormatException("Unsupported palette size"); 277 | 278 | m_palette = new ColorBgra[size]; 279 | } 280 | 281 | public PcxPalette(ColorPalette palette) 282 | { 283 | Color[] entries = palette.Entries; 284 | int length = entries.Length; 285 | 286 | // Ignore transparent padding entries from OctreeQuantizer 287 | int padding = Array.FindIndex(entries, c => c.ToArgb() == Color.Transparent.ToArgb()); 288 | if (padding != -1) 289 | length = padding; 290 | 291 | uint size; 292 | if (length <= 2) 293 | size = 2; 294 | else if (length <= 16) 295 | size = 16; 296 | else if (length <= 256) 297 | size = 256; 298 | else 299 | throw new FormatException("Unsupported palette size"); 300 | 301 | m_palette = new ColorBgra[size]; 302 | 303 | for (uint i = 0; i < length; i++) 304 | m_palette[i] = ColorBgra.FromColor(entries[i]); 305 | 306 | // Fill rest of the palette with black 307 | for (uint i = size - 1; i >= length; i--) 308 | m_palette[i] = ColorBgra.Black; 309 | } 310 | 311 | public PcxPalette(Stream input, int size) 312 | { 313 | if (size != 16 && size != 256) 314 | throw new FormatException("Unsupported palette size"); 315 | 316 | m_palette = new ColorBgra[size]; 317 | 318 | for (int i = 0; i < m_palette.Length; ++i) 319 | { 320 | int red = input.ReadByte(); 321 | if (red == -1) 322 | throw new EndOfStreamException(); 323 | 324 | int green = input.ReadByte(); 325 | if (green == -1) 326 | throw new EndOfStreamException(); 327 | 328 | int blue = input.ReadByte(); 329 | if (blue == -1) 330 | throw new EndOfStreamException(); 331 | 332 | m_palette[i] = ColorBgra.FromBgra((byte)blue, (byte)green, (byte)red, 255); 333 | } 334 | } 335 | 336 | public void Write(Stream output) 337 | { 338 | for (int i = 0; i < m_palette.Length; i++) 339 | { 340 | ColorBgra c = m_palette[i]; 341 | output.WriteByte(c.R); 342 | output.WriteByte(c.G); 343 | output.WriteByte(c.B); 344 | } 345 | } 346 | 347 | public byte[] ToColorMap() 348 | { 349 | if (m_palette.Length > 16) 350 | throw new FormatException("Trying to write an unsupported palette size to a header ColorMap"); 351 | 352 | byte[] colorMap = new byte[48]; 353 | uint index = 0; 354 | for (int i = 0; i < m_palette.Length; ++i) 355 | { 356 | ColorBgra c = m_palette[i]; 357 | 358 | colorMap[index++] = c.R; 359 | colorMap[index++] = c.G; 360 | colorMap[index++] = c.B; 361 | } 362 | 363 | while (index < colorMap.Length) 364 | colorMap[index++] = 0; 365 | 366 | return colorMap; 367 | } 368 | 369 | public static PcxPalette FromColorMap(byte[] colorMap) 370 | { 371 | if (colorMap == null) 372 | throw new ArgumentNullException("colorMap"); 373 | if (colorMap.Length != 48) 374 | throw new FormatException("Trying to read an unsupported palette size from a header ColorMap"); 375 | 376 | PcxPalette palette = new PcxPalette(16); 377 | 378 | uint index = 0; 379 | for (uint i = 0; i < 16; i++) 380 | { 381 | byte r = colorMap[index++]; 382 | byte g = colorMap[index++]; 383 | byte b = colorMap[index++]; 384 | 385 | palette[i] = ColorBgra.FromBgr(b, g, r); 386 | } 387 | 388 | return palette; 389 | } 390 | 391 | public static PcxPalette FromEgaPalette(uint[] egaPalette) 392 | { 393 | if (egaPalette == null) 394 | throw new ArgumentNullException("egaPalette"); 395 | if (egaPalette.Length != 16) 396 | throw new FormatException("Trying to read an unsupported palette size from a header ColorMap"); 397 | 398 | PcxPalette palette = new PcxPalette(16); 399 | 400 | for (uint i = 0; i < 16; i++) 401 | { 402 | byte r = (byte)((egaPalette[i] >> 16) & 0xff); 403 | byte g = (byte)((egaPalette[i] >> 8) & 0xff); 404 | byte b = (byte)((egaPalette[i]) & 0xff); 405 | 406 | palette[i] = ColorBgra.FromBgr(b, g, r); 407 | } 408 | 409 | return palette; 410 | } 411 | 412 | public string Serialize() 413 | { 414 | System.Text.StringBuilder sb = new System.Text.StringBuilder(); 415 | foreach (ColorBgra colorBgra in m_palette) 416 | { 417 | if (sb.Length > 0) 418 | sb.Append(","); 419 | 420 | Color c = colorBgra.ToColor(); 421 | uint argb = (uint)c.ToArgb(); 422 | string s = argb.ToString(System.Globalization.CultureInfo.InvariantCulture); 423 | sb.Append(s); 424 | } 425 | 426 | return sb.ToString(); 427 | } 428 | 429 | public static List Deserialize(string s) 430 | { 431 | List colors = new List(); 432 | 433 | string[] ss = s.Split(new char[] { ',' }); 434 | foreach (string item in ss) 435 | { 436 | uint argb = uint.Parse(item, System.Globalization.CultureInfo.InvariantCulture); 437 | Color color = Color.FromArgb((int)argb); 438 | colors.Add(color); 439 | } 440 | 441 | return colors; 442 | } 443 | } 444 | 445 | #endregion 446 | 447 | 448 | protected override Document OnLoad(Stream input) 449 | { 450 | // 451 | // Load and validate header 452 | // 453 | PcxHeader header = new PcxHeader(input); 454 | 455 | if (header.id != PcxId.ZSoftPCX) 456 | throw new FormatException("Not a PCX file."); 457 | 458 | if (header.version != PcxVersion.Version3_0 && 459 | header.version != PcxVersion.Version2_8_Palette && 460 | header.version != PcxVersion.Version2_8_DefaultPalette && 461 | header.version != PcxVersion.Version2_5) 462 | throw new FormatException(String.Format("Unsupported PCX version: {0}", header.version)); 463 | 464 | if (header.bitsPerPixel != 1 && 465 | header.bitsPerPixel != 2 && 466 | header.bitsPerPixel != 4 && 467 | header.bitsPerPixel != 8) 468 | throw new FormatException(String.Format("Unsupported PCX bits per pixel: {0} bits per pixel", header.bitsPerPixel)); 469 | 470 | int width = header.xMax - header.xMin + 1; 471 | int height = header.yMax - header.yMin + 1; 472 | if (width < 0 || height < 0 || width > 0xffff || height > 0xffff) 473 | throw new FormatException(String.Format("Invalid image dimensions: ({0},{1})-({2},{3})", header.xMin, header.yMin, header.xMax, header.yMax)); 474 | 475 | // Pixels per line, including PCX's even-number-of-pixels buffer 476 | int pixelsPerLine = header.bytesPerLine * 8 /*bitsPerByte*/ / header.bitsPerPixel; 477 | 478 | // Bits per pixel, including all bit planes 479 | int bitsPerPixel = header.bitsPerPixel * header.nPlanes; 480 | 481 | if (bitsPerPixel != 1 && 482 | bitsPerPixel != 2 && 483 | bitsPerPixel != 4 && 484 | bitsPerPixel != 8 && 485 | bitsPerPixel != 24) 486 | throw new FormatException(String.Format("Unsupported PCX bit depth: {0}", bitsPerPixel)); 487 | 488 | // 489 | // Load the palette 490 | // 491 | PcxPalette palette; 492 | 493 | if (bitsPerPixel == 1) 494 | { 495 | // HACK: Monochrome images don't always include a resonable palette in v3.0. 496 | // Default them to black and white in all cases 497 | 498 | palette = PcxPalette.FromEgaPalette(PcxPalette.MONO_PALETTE); 499 | } 500 | else if (bitsPerPixel < 8) 501 | { 502 | // 16-color palette in the ColorMap portion of the header 503 | 504 | switch (header.version) 505 | { 506 | case PcxVersion.Version2_5: 507 | case PcxVersion.Version2_8_DefaultPalette: 508 | { 509 | uint[] paletteEga; 510 | 511 | switch (bitsPerPixel) 512 | { 513 | // 4-color CGA palette 514 | case 2: 515 | paletteEga = PcxPalette.CGA_PALETTE; 516 | break; 517 | 518 | // 16-color EGA palette 519 | default: 520 | case 4: 521 | paletteEga = PcxPalette.EGA_PALETTE; 522 | break; 523 | } 524 | 525 | palette = PcxPalette.FromEgaPalette(paletteEga); 526 | break; 527 | } 528 | 529 | default: 530 | case PcxVersion.Version2_8_Palette: 531 | case PcxVersion.Version3_0: 532 | { 533 | palette = PcxPalette.FromColorMap(header.colorMap); 534 | break; 535 | } 536 | 537 | } 538 | } 539 | else if (bitsPerPixel == 8) 540 | { 541 | // 256-color palette is saved at the end of the file, with one byte marker 542 | 543 | long dataPosition = input.Position; 544 | input.Seek(-(1 + (256 * 3)), SeekOrigin.End); 545 | 546 | if (input.ReadByte() != PcxPaletteMarker) 547 | throw new FormatException("PCX palette marker not present in file"); 548 | 549 | palette = new PcxPalette(input, 256); 550 | 551 | input.Seek(dataPosition, SeekOrigin.Begin); 552 | } 553 | else 554 | { 555 | // Dummy palette for 24-bit images 556 | 557 | palette = new PcxPalette(256); 558 | } 559 | 560 | 561 | // 562 | // Load the pixel data 563 | // 564 | BitmapLayer layer = Layer.CreateBackgroundLayer(width, height); 565 | 566 | try 567 | { 568 | Surface surface = layer.Surface; 569 | surface.Clear((ColorBgra)0xffffffff); 570 | 571 | // Accumulate indices across bit planes 572 | uint[] indexBuffer = new uint[width]; 573 | 574 | for (int y = 0; y < height; y++) 575 | { 576 | MemoryBlock dstRow = surface.GetRow(y); 577 | Array.Clear(indexBuffer, 0, width); 578 | 579 | int offset = 0; 580 | 581 | // Decode the RLE byte stream 582 | PcxByteReader byteReader = (header.encoding == PcxEncoding.RunLengthEncoded) 583 | ? new PcxRleByteReader(input) 584 | : (PcxByteReader)new PcxRawByteReader(input); 585 | 586 | // Read indices of a given length out of the byte stream 587 | PcxIndexReader indexReader = new PcxIndexReader(byteReader, header.bitsPerPixel); 588 | 589 | // Planes are stored consecutively for each scan line 590 | for (int plane = 0; plane < header.nPlanes; plane++) 591 | { 592 | for (int x = 0; x < pixelsPerLine; x++) 593 | { 594 | uint index = indexReader.ReadIndex(); 595 | 596 | // Account for padding bytes 597 | if (x < width) 598 | indexBuffer[x] = indexBuffer[x] | (index << (plane * header.bitsPerPixel)); 599 | } 600 | } 601 | 602 | for (int x = 0; x < width; x++) 603 | { 604 | uint index = indexBuffer[x]; 605 | ColorBgra color; 606 | 607 | if (bitsPerPixel == 24) 608 | { 609 | byte r = (byte)((index) & 0xff); 610 | byte g = (byte)((index >> 8) & 0xff); 611 | byte b = (byte)((index >> 16) & 0xff); 612 | 613 | color = ColorBgra.FromBgr(b, g, r); 614 | } 615 | else 616 | { 617 | color = palette[index]; 618 | } 619 | 620 | dstRow[offset + 0] = color[0]; 621 | dstRow[offset + 1] = color[1]; 622 | dstRow[offset + 2] = color[2]; 623 | dstRow[offset + 3] = color[3]; 624 | 625 | offset += ColorBgra.SizeOf; 626 | } 627 | } 628 | 629 | Document document = new Document(surface.Width, surface.Height); 630 | document.Layers.Add(layer); 631 | document.Metadata.SetUserValue("Palette", palette.Serialize()); 632 | document.Metadata.SetUserValue("BitDepth", bitsPerPixel.ToString(System.Globalization.CultureInfo.InvariantCulture)); 633 | 634 | return document; 635 | } 636 | catch 637 | { 638 | if (layer != null) 639 | { 640 | layer.Dispose(); 641 | layer = null; 642 | } 643 | 644 | throw; 645 | } 646 | } 647 | 648 | 649 | protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback) 650 | { 651 | PcxSaveConfigToken pcxToken = (PcxSaveConfigToken)token; 652 | 653 | scratchSurface.Clear(ColorBgra.FromBgra(255, 255, 255, 0)); 654 | 655 | using (RenderArgs ra = new RenderArgs(scratchSurface)) 656 | { 657 | input.Render(ra, true); 658 | } 659 | 660 | for (int y = 0; y < scratchSurface.Height; ++y) 661 | { 662 | unsafe 663 | { 664 | ColorBgra* ptr = scratchSurface.GetRowAddressUnchecked(y); 665 | 666 | for (int x = 0; x < scratchSurface.Width; ++x) 667 | { 668 | if (ptr->A < pcxToken.Threshold) 669 | { 670 | ptr->Bgra = 0; 671 | } 672 | else 673 | { 674 | if (pcxToken.PreMultiplyAlpha) 675 | { 676 | int r = ((ptr->R * ptr->A) + (255 * (255 - ptr->A))) / 255; 677 | int g = ((ptr->G * ptr->A) + (255 * (255 - ptr->A))) / 255; 678 | int b = ((ptr->B * ptr->A) + (255 * (255 - ptr->A))) / 255; 679 | int a = 255; 680 | 681 | *ptr = ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a); 682 | } 683 | else 684 | { 685 | ptr->Bgra |= 0xff000000; 686 | } 687 | } 688 | 689 | ++ptr; 690 | } 691 | } 692 | } 693 | 694 | // TODO: Use FileType.Quantize() for new palette cases 695 | using (Bitmap bitmap = scratchSurface.CreateAliasedBitmap(input.Bounds, true)) 696 | { 697 | Quantizer quantizer; 698 | 699 | string serializedPalette = input.Metadata.GetUserValue("Palette"); 700 | int bitsPerPixel = 0; 701 | Int32.TryParse(input.Metadata.GetUserValue("BitDepth"), out bitsPerPixel); 702 | 703 | if (pcxToken.UseOriginalPalette && serializedPalette != null) 704 | { 705 | List palette = PcxPalette.Deserialize(serializedPalette); 706 | if (bitsPerPixel > 0) 707 | palette = palette.GetRange(0, 1 << bitsPerPixel); 708 | quantizer = new PaletteQuantizer(palette); 709 | } 710 | else if (pcxToken.preset_palette == true && pcxToken.preset_palette_string != null) 711 | { 712 | List palette = PcxPalette.Deserialize(pcxToken.preset_palette_string); 713 | if (bitsPerPixel > 0) 714 | palette = palette.GetRange(0, 1 << bitsPerPixel); 715 | quantizer = new PaletteQuantizer(palette); 716 | } 717 | else 718 | { 719 | quantizer = new OctreeQuantizer(255, 8); 720 | } 721 | 722 | quantizer.DitherLevel = pcxToken.DitherLevel; 723 | 724 | using (Bitmap quantized = quantizer.Quantize(bitmap, callback)) 725 | { 726 | SavePcx(quantized, output, pcxToken, callback); 727 | } 728 | } 729 | } 730 | 731 | 732 | private void SavePcx(Bitmap bitmap, Stream output, PcxSaveConfigToken token, ProgressEventHandler progressCallback) 733 | { 734 | // 735 | // NOTE: Only supports 16 or 256 color PCX files (single plane) 736 | // 737 | 738 | // Determine palette size 739 | if (bitmap.Palette.Entries.Length == 0 || bitmap.Palette.Entries.Length > 256) 740 | throw new FormatException("Unsupported palette size"); 741 | 742 | PcxPalette palette = new PcxPalette(bitmap.Palette); 743 | 744 | // 745 | // Write header 746 | // 747 | PcxHeader header = new PcxHeader(); 748 | header.version = PcxVersion.Version3_0; 749 | header.encoding = token.RleCompress ? PcxEncoding.RunLengthEncoded : PcxEncoding.None; 750 | header.bitsPerPixel = 751 | palette.Size == 2 ? (byte)1 : 752 | palette.Size == 16 ? (byte)4 : 753 | palette.Size == 256 ? (byte)8 : (byte)0; 754 | header.xMin = 0; 755 | header.yMin = 0; 756 | header.xMax = (ushort)(bitmap.Width - 1); // inclusive 757 | header.yMax = (ushort)(bitmap.Height - 1); // inclusive 758 | header.hDpi = (ushort)bitmap.HorizontalResolution; 759 | header.vDpi = (ushort)bitmap.VerticalResolution; 760 | header.nPlanes = 1; 761 | header.bytesPerLine = (ushort)(bitmap.Width * header.bitsPerPixel / 8 /*bitsPerByte*/); 762 | if (header.bytesPerLine % 2 == 1) ++header.bytesPerLine; // Must be even 763 | header.paletteInfo = PcxPaletteType.Indexed; 764 | 765 | if (palette.Size <= 16) 766 | header.colorMap = palette.ToColorMap(); 767 | 768 | header.Write(output); 769 | 770 | int pixelsPerLine = header.bytesPerLine * 8 /*bitsPerByte*/ / header.bitsPerPixel; 771 | 772 | // 773 | // Write pixel data 774 | // 775 | BitmapData bitmapData = bitmap.LockBits( 776 | new Rectangle(0, 0, bitmap.Width, bitmap.Height), 777 | ImageLockMode.ReadOnly, 778 | PixelFormat.Format8bppIndexed); 779 | 780 | try 781 | { 782 | for (int y = 0; y < bitmap.Height; y++) 783 | { 784 | byte[] row = new byte[bitmap.Width]; 785 | 786 | unsafe 787 | { 788 | byte* pData = (byte*)bitmapData.Scan0.ToPointer() + (y * bitmapData.Stride); 789 | Marshal.Copy(new IntPtr(pData), row, 0, bitmap.Width); 790 | } 791 | 792 | PcxByteWriter byteWriter = (header.encoding == PcxEncoding.RunLengthEncoded) 793 | ? new PcxRleByteWriter(output) 794 | : (PcxByteWriter)new PcxRawByteWriter(output); 795 | 796 | PcxIndexWriter indexWriter = new PcxIndexWriter(byteWriter, header.bitsPerPixel); 797 | 798 | for (int x = 0; x < pixelsPerLine; x++) 799 | { 800 | if (x < bitmap.Width) 801 | { 802 | indexWriter.WriteIndex(row[x]); 803 | } 804 | else 805 | { 806 | indexWriter.WriteIndex(0); // Padding until to produce the required number of bytes 807 | } 808 | } 809 | 810 | indexWriter.Flush(); // Write out any buffered bits 811 | 812 | byteWriter.Flush(); // Force RLE encoding reset 813 | 814 | if (progressCallback != null) 815 | progressCallback(this, new ProgressEventArgs(100.0 * ((bitmap.Height - y) / (double)bitmap.Height))); 816 | } 817 | } 818 | finally 819 | { 820 | bitmap.UnlockBits(bitmapData); 821 | } 822 | 823 | // 824 | // Write palette 825 | // 826 | if (palette.Size == 256) 827 | { 828 | output.WriteByte(PcxPaletteMarker); 829 | palette.Write(output); 830 | } 831 | } 832 | 833 | 834 | #region Index Reader/Writer 835 | /** 836 | * Classes to handle reading/writing indices of various bit depths to/from encoded streams. 837 | * 838 | */ 839 | 840 | private class PcxIndexReader 841 | { 842 | private PcxByteReader m_reader; 843 | private uint m_bitsPerPixel; 844 | private uint m_bitMask; 845 | 846 | private uint m_bitsRemaining = 0; 847 | private uint m_byteRead; 848 | 849 | public PcxIndexReader(PcxByteReader reader, uint bitsPerPixel) 850 | { 851 | if (!(bitsPerPixel == 1 || bitsPerPixel == 2 || bitsPerPixel == 4 || bitsPerPixel == 8)) 852 | throw new ArgumentException("bitsPerPixel must be 1, 2, 4 or 8", "bitsPerPixel"); 853 | 854 | m_reader = reader; 855 | m_bitsPerPixel = bitsPerPixel; 856 | m_bitMask = (uint)((1 << (int)m_bitsPerPixel) - 1); 857 | } 858 | 859 | public uint ReadIndex() 860 | { 861 | // NOTE: This does not work for non-power-of-two bits per pixel (e.g. 6) 862 | // since it does not concatenate shift adjacent bytes together 863 | 864 | if (m_bitsRemaining == 0) 865 | { 866 | m_byteRead = m_reader.ReadByte(); 867 | m_bitsRemaining = 8; 868 | } 869 | 870 | // NOTE: Reads from the most significant bits 871 | uint index = (m_byteRead >> (int)(8 - m_bitsPerPixel)) & m_bitMask; 872 | m_byteRead = m_byteRead << (int)m_bitsPerPixel; 873 | m_bitsRemaining -= m_bitsPerPixel; 874 | 875 | return index; 876 | } 877 | } 878 | 879 | private class PcxIndexWriter 880 | { 881 | private PcxByteWriter m_writer; 882 | private uint m_bitsPerPixel; 883 | private uint m_bitMask; 884 | 885 | private uint m_bitsUsed = 0; 886 | private uint m_byteAccumulated; 887 | 888 | public PcxIndexWriter(PcxByteWriter writer, uint bitsPerPixel) 889 | { 890 | if (!(bitsPerPixel == 1 || bitsPerPixel == 2 || bitsPerPixel == 4 || bitsPerPixel == 8)) 891 | throw new ArgumentException("bitsPerPixel must be 1, 2, 4 or 8", "bitsPerPixel"); 892 | 893 | m_writer = writer; 894 | m_bitsPerPixel = bitsPerPixel; 895 | m_bitMask = (uint)((1 << (int)m_bitsPerPixel) - 1); 896 | } 897 | 898 | public void WriteIndex(uint index) 899 | { 900 | // NOTE: This does not work for non-power-of-two bits per pixel (e.g. 6) 901 | // since it does not concatenate shift adjacent bytes together 902 | 903 | // NOTE: Are bits shifted onto the right end? Especially at the right edge of the picture? 904 | m_byteAccumulated = (m_byteAccumulated << (int)m_bitsPerPixel) | (index & m_bitMask); 905 | m_bitsUsed += m_bitsPerPixel; 906 | 907 | if (m_bitsUsed == 8) 908 | Flush(); 909 | } 910 | 911 | public void Flush() 912 | { 913 | // TODO: If this is called when not all bytes are used, are the bits on the right end? 914 | 915 | // BUG: If there's nothing to flush, this will execute twice! 916 | // Contrariwise, this will never call the RLE flush! 917 | 918 | if (m_bitsUsed > 0) 919 | { 920 | m_writer.WriteByte((byte)m_byteAccumulated); 921 | m_bitsUsed = 0; 922 | } 923 | } 924 | } 925 | #endregion 926 | 927 | #region RLE Encoding/Decoding 928 | 929 | /** 930 | * Classes to handle RLE encoding/decoding to/from streams. 931 | * 932 | */ 933 | 934 | private abstract class PcxByteReader 935 | { 936 | public abstract byte ReadByte(); 937 | } 938 | 939 | private abstract class PcxByteWriter 940 | { 941 | public abstract void WriteByte(byte value); 942 | public abstract void Flush(); 943 | } 944 | 945 | private class PcxRawByteReader : PcxByteReader 946 | { 947 | private Stream m_stream; 948 | public PcxRawByteReader(Stream stream) 949 | { 950 | m_stream = stream; 951 | } 952 | public override byte ReadByte() 953 | { 954 | return (byte)m_stream.ReadByte(); 955 | } 956 | } 957 | 958 | private class PcxRawByteWriter : PcxByteWriter 959 | { 960 | private Stream m_stream; 961 | public PcxRawByteWriter(Stream stream) 962 | { 963 | m_stream = stream; 964 | } 965 | public override void WriteByte(byte value) 966 | { 967 | m_stream.WriteByte(value); 968 | } 969 | public override void Flush() 970 | { 971 | // no-op 972 | } 973 | } 974 | 975 | private class PcxRleByteWriter : PcxByteWriter 976 | { 977 | private Stream m_stream; 978 | private byte m_lastValue; 979 | private uint m_count = 0; 980 | 981 | public PcxRleByteWriter(Stream output) 982 | { 983 | m_stream = output; 984 | } 985 | 986 | public override void WriteByte(byte value) 987 | { 988 | if (m_count == 0 || m_count == 63 || value != m_lastValue) 989 | { 990 | Flush(); 991 | 992 | m_lastValue = value; 993 | m_count = 1; 994 | } 995 | else 996 | { 997 | m_count++; 998 | } 999 | } 1000 | 1001 | public override void Flush() 1002 | { 1003 | if (m_count == 0) 1004 | return; 1005 | 1006 | if ((m_count > 1) || ((m_count == 1) && ((m_lastValue & PcxRleMask) == PcxRleMask))) 1007 | { 1008 | m_stream.WriteByte((byte)(PcxRleMask | m_count)); 1009 | m_stream.WriteByte(m_lastValue); 1010 | m_count = 0; 1011 | } 1012 | else 1013 | { 1014 | m_stream.WriteByte(m_lastValue); 1015 | m_count = 0; 1016 | } 1017 | } 1018 | 1019 | 1020 | } 1021 | 1022 | private class PcxRleByteReader : PcxByteReader 1023 | { 1024 | private Stream m_stream; 1025 | private uint m_count = 0; 1026 | private byte m_rleValue; 1027 | 1028 | public PcxRleByteReader(Stream input) 1029 | { 1030 | m_stream = input; 1031 | } 1032 | 1033 | public override byte ReadByte() 1034 | { 1035 | if (m_count > 0) 1036 | { 1037 | m_count--; 1038 | return m_rleValue; 1039 | } 1040 | 1041 | byte code = (byte)m_stream.ReadByte(); 1042 | 1043 | if ((code & PcxRleMask) == PcxRleMask) 1044 | { 1045 | m_count = (uint)(code & (PcxRleMask ^ 0xff)); 1046 | m_rleValue = (byte)m_stream.ReadByte(); 1047 | 1048 | m_count--; 1049 | return m_rleValue; 1050 | } 1051 | 1052 | return code; 1053 | } 1054 | } 1055 | 1056 | #endregion 1057 | } 1058 | } 1059 | -------------------------------------------------------------------------------- /PcxFileType.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.50727 7 | 2.0 8 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81} 9 | Library 10 | Properties 11 | PcxFileTypePlugin 12 | PcxFileType 13 | OnBuildSuccess 14 | v4.6 15 | 16 | 17 | 18 | 19 | 2.0 20 | 21 | 22 | 23 | true 24 | full 25 | false 26 | obj\Debug\ 27 | DEBUG;TRACE 28 | prompt 29 | 4 30 | true 31 | Off 32 | false 33 | 34 | 35 | none 36 | true 37 | obj\Release\ 38 | TRACE 39 | prompt 40 | 4 41 | true 42 | false 43 | 44 | 45 | 46 | ..\..\..\..\..\..\Program Files\paint.net\PaintDotNet.Base.dll 47 | False 48 | 49 | 50 | ..\..\..\..\..\..\Program Files\paint.net\PaintDotNet.Core.dll 51 | False 52 | 53 | 54 | ..\..\..\..\..\..\Program Files\paint.net\PaintDotNet.Data.dll 55 | False 56 | 57 | 58 | ..\..\..\..\..\..\Program Files\paint.net\PaintDotNet.Effects.dll 59 | False 60 | 61 | 62 | ..\..\..\..\..\..\Program Files\paint.net\PaintDotNet.Framework.dll 63 | False 64 | 65 | 66 | ..\..\..\..\..\..\Program Files\paint.net\PaintDotNet.Resources.dll 67 | False 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | UserControl 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Designer 89 | PcxSaveConfigWidget.cs 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /PcxFileType.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Express 2013 for Web 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcxFileType", "PcxFileType.csproj", "{8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|Mixed Platforms = Debug|Mixed Platforms 12 | Debug|Win32 = Debug|Win32 13 | Debug|x64 = Debug|x64 14 | Release|Any CPU = Release|Any CPU 15 | Release|Mixed Platforms = Release|Mixed Platforms 16 | Release|Win32 = Release|Win32 17 | Release|x64 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 23 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 24 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|Win32.ActiveCfg = Debug|Any CPU 25 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|Win32.Build.0 = Debug|Any CPU 26 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Debug|x64.Build.0 = Debug|Any CPU 28 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 31 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|Mixed Platforms.Build.0 = Release|Any CPU 32 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|Win32.ActiveCfg = Release|Any CPU 33 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|Win32.Build.0 = Release|Any CPU 34 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|x64.ActiveCfg = Release|Any CPU 35 | {8CA4EBFD-46EA-437B-AD52-685C3E0BEC81}.Release|x64.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /PcxSaveConfigToken.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // PCX Plugin for Paint.NET 3 | // Copyright (C) 2006 Joshua Bell (inexorabletash@gmail.com) 4 | // Portions Copyright (C) 2006 Rick Brewster, et. al. 5 | // See License.txt for complete licensing and attribution information. 6 | ///////////////////////////////////////////////////////////////////////////////// 7 | 8 | using System; 9 | using PaintDotNet; 10 | 11 | namespace PcxFileTypePlugin 12 | { 13 | [Serializable] 14 | public class PcxSaveConfigToken 15 | : SaveConfigToken 16 | { 17 | public override object Clone() 18 | { 19 | return new PcxSaveConfigToken(this); 20 | } 21 | 22 | public PcxSaveConfigToken(int threshold, bool preMultiplyAlpha, bool useOriginalPalette, int ditherLevel, bool rle, bool preset_palette, string preset_palette_string, int preset_palette_arr_position) 23 | { 24 | this.threshold = threshold; 25 | this.preMultiplyAlpha = preMultiplyAlpha; 26 | this.useOriginalPalette = useOriginalPalette; 27 | this.ditherLevel = ditherLevel; 28 | this.rle = rle; 29 | this.preset_palette = preset_palette; 30 | this.preset_palette_string = preset_palette_string; 31 | this.preset_palette_arr_position = preset_palette_arr_position; 32 | Validate(); 33 | } 34 | 35 | protected PcxSaveConfigToken(PcxSaveConfigToken copyMe) 36 | { 37 | this.threshold = copyMe.threshold; 38 | this.preMultiplyAlpha = copyMe.preMultiplyAlpha; 39 | this.useOriginalPalette = copyMe.useOriginalPalette; 40 | this.ditherLevel = copyMe.ditherLevel; 41 | this.rle = copyMe.rle; 42 | this.preset_palette = copyMe.preset_palette; 43 | this.preset_palette_string = copyMe.preset_palette_string; 44 | this.preset_palette_arr_position = copyMe.preset_palette_arr_position; 45 | } 46 | 47 | private const int minThreshold = 0; 48 | private const int maxThreshold = 255; 49 | private const int minDitherLevel = 0; 50 | private const int maxDitherLevel = 8; 51 | 52 | private int threshold; 53 | private bool preMultiplyAlpha; 54 | private bool useOriginalPalette; 55 | private int ditherLevel; 56 | private bool rle; 57 | 58 | //----New-Pal-Stuff------ 59 | public bool preset_palette; 60 | public string/*[]*/ preset_palette_string; 61 | public int preset_palette_arr_position; 62 | //----New-Pal-Stuff------ 63 | 64 | public int Threshold 65 | { 66 | get 67 | { 68 | return threshold; 69 | } 70 | 71 | set 72 | { 73 | if (value < minThreshold || value > maxThreshold) 74 | throw new ArgumentOutOfRangeException("threshold must be " + minThreshold + " to " + maxThreshold + ", inclusive"); 75 | 76 | this.threshold = value; 77 | } 78 | } 79 | public bool PreMultiplyAlpha 80 | { 81 | get 82 | { 83 | return this.preMultiplyAlpha; 84 | } 85 | 86 | set 87 | { 88 | this.preMultiplyAlpha = value; 89 | } 90 | } 91 | public bool UseOriginalPalette 92 | { 93 | get 94 | { 95 | return this.useOriginalPalette; 96 | } 97 | 98 | set 99 | { 100 | this.useOriginalPalette = value; 101 | } 102 | } 103 | public int DitherLevel 104 | { 105 | get 106 | { 107 | return this.ditherLevel; 108 | } 109 | 110 | set 111 | { 112 | if (value < minDitherLevel || value > maxDitherLevel) 113 | throw new ArgumentOutOfRangeException("ditherLevel must be " + minDitherLevel + " to " + maxDitherLevel + ", inclusive"); 114 | 115 | this.ditherLevel = value; 116 | } 117 | } 118 | public bool RleCompress 119 | { 120 | get 121 | { 122 | return this.rle; 123 | } 124 | 125 | set 126 | { 127 | this.rle = value; 128 | } 129 | } 130 | 131 | /* public bool preset_palette 132 | { 133 | get 134 | { 135 | return this.preset_palette; 136 | } 137 | 138 | set 139 | { 140 | this.preset_palette = value; 141 | } 142 | }*/ 143 | 144 | 145 | public override void Validate() 146 | { 147 | if (this.threshold < minThreshold || this.threshold > maxThreshold) 148 | throw new ArgumentOutOfRangeException(String.Format("threshold must be {0} to {1}, inclusive", minThreshold, maxThreshold)); 149 | 150 | if (this.ditherLevel < minDitherLevel || this.ditherLevel > maxDitherLevel) 151 | throw new ArgumentOutOfRangeException(String.Format("ditherLevel must be {0} to {1}, inclusive", minDitherLevel, maxDitherLevel)); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /PcxSaveConfigWidget.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // PCX Plugin for Paint.NET 3 | // Copyright (C) 2006 Joshua Bell (inexorabletash@gmail.com) 4 | // Portions Copyright (C) 2006 Rick Brewster, et. al. 5 | // See License.txt for complete licensing and attribution information. 6 | ///////////////////////////////////////////////////////////////////////////////// 7 | 8 | ///////////////////////////////////////////////////////////////////////////////////////////////////// 9 | //Modified by EzArIk(Thomas C. Maylam [on: (GB)11/20/2016]) to support the use of preset Pallettes.// 10 | ///////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | using System; 13 | using System.Collections; 14 | using System.ComponentModel; 15 | using System.Drawing; 16 | using System.Windows.Forms; 17 | using PaintDotNet; 18 | using System.IO; 19 | 20 | 21 | namespace PcxFileTypePlugin 22 | { 23 | public class PcxSaveConfigWidget 24 | : PaintDotNet.SaveConfigWidget 25 | { 26 | private System.Windows.Forms.TrackBar thresholdSlider; 27 | private System.Windows.Forms.Label thresholdLabel; 28 | private System.Windows.Forms.NumericUpDown thresholdUpDown; 29 | private System.Windows.Forms.CheckBox preMultiplyAlphaCheckBox; 30 | private System.Windows.Forms.CheckBox useOriginalPaletteCheckBox; 31 | private System.Windows.Forms.NumericUpDown ditherUpDown; 32 | private System.Windows.Forms.Label ditherLabel; 33 | private System.Windows.Forms.TrackBar ditherSlider; 34 | private CheckBox preset_palette_check_box; 35 | private Panel panel1; 36 | private ComboBox preset_palettes_combo_box; 37 | private System.ComponentModel.IContainer components = null; 38 | 39 | public PcxSaveConfigWidget() 40 | { 41 | // This call is required by the Windows Form Designer. 42 | InitializeComponent(); 43 | } 44 | 45 | protected override void InitFileType() 46 | { 47 | this.fileType = new PcxFileType(); 48 | } 49 | 50 | //'Global' Widgit Strings: 51 | string[] import_pal, import_name; 52 | //string preset_palette_sender; 53 | //'Global' Widgit Strings: 54 | 55 | void load_set_palette() 56 | { 57 | // FileStream import_pal_file; 58 | StreamReader test_stream_reader; 59 | // string pal_path = ""; 60 | string in_line = ""; 61 | int import_maximum = 0, import_count = 0; 62 | //bool did_it_work = false; 63 | 64 | 65 | ////////////// 66 | //Important!// 67 | ////////////// 68 | 69 | int found_key = -1; 70 | 71 | // pal_path = GetCurrentDirectory(); 72 | try { 73 | 74 | using (test_stream_reader = new StreamReader("FileTypes/Preset_Pallettes.pst_pal.txt")) 75 | { 76 | import_count = 0; 77 | import_maximum = 1; 78 | // ((PcxSaveConfigToken)this.Token).preset_palette_string = new string[1]; 79 | import_pal = new string[1]; 80 | import_name = new string[1]; 81 | 82 | // Get the start of the files list, this part defines how many entries to look for. 83 | 84 | while ((in_line = test_stream_reader.ReadLine()) != null) 85 | { 86 | 87 | if (in_line == "NUM" && found_key == -1) 88 | { 89 | // this line tells the program that the next row in the file should contain the maximum number of entries: 90 | found_key = 0; 91 | continue; 92 | } 93 | 94 | if (found_key == 0) 95 | { 96 | // this line gets the number in the next row and sets it as the maximum value for entries: 97 | import_maximum = Convert.ToInt32(in_line); 98 | // ((PcxSaveConfigToken)this.Token).preset_palette_string = new string[import_maximum]; 99 | import_pal = new string[import_maximum]; 100 | import_name = new string[import_maximum]; 101 | found_key = 1; 102 | } 103 | 104 | if (in_line == "ENT" && found_key == 1) 105 | { 106 | //Looks For the entry Flag and progresses to the next stage it it finds it. 107 | found_key = 2; 108 | continue; 109 | } 110 | 111 | if (found_key == 2) 112 | { 113 | import_name[import_count] = in_line; 114 | preset_palettes_combo_box.Items.Insert(import_count, import_name[import_count]); 115 | preset_palettes_combo_box.SelectedIndex = 0; 116 | //update_preset_palette_string_token(); 117 | found_key = 3; 118 | continue; 119 | } 120 | 121 | 122 | if (found_key == 3) 123 | { 124 | import_pal[import_count] = in_line; 125 | found_key = 1; 126 | import_count++; 127 | } 128 | 129 | if (import_count >= import_maximum) 130 | { 131 | break; 132 | } 133 | 134 | } 135 | 136 | if (in_line == null) 137 | { 138 | throw new ArgumentNullException(); 139 | } 140 | 141 | } 142 | 143 | } 144 | catch (Exception e) { 145 | // did_it_work = false; 146 | preset_palette_check_box.Enabled = false; 147 | preset_palettes_combo_box.Text = "Error:"+ e; 148 | //preset_palettes_combo_box.ValueMember.Remove(0, preset_palettes_combo_box.ValueMember.Length); 149 | } 150 | 151 | //if (did_it_work == true) { 152 | 153 | // } 154 | //import_pal; 155 | //((PcxSaveConfigToken)this.Token).preset_palette_string = "4278190080,4279894016,4281535232,4282843911,4286513152,4286578688,4288348160,4294901760,4294934592,4294927460,4294934399,4294937226,4293248421,4294950847,4293445832,4294888410,4294960099,4294638311,4289013286,4293092452,4290139428,4288355072,4291990162,4293178246,4292590519,4293059288,4285081861,4291792751,4292585855,4294572243,4293388263,4283568915,4283967748,4290143560,4291793008,4286595072,4290147912,4291529547,4291868833,4281473801,4283183616,4285018377,4291404401,4291472010,4292006329,4279833861,4281873415,4287714596,4288371483,4289762851,4288713562,4293059266,4283714565,4284634112,4285165568,4286545664,4287137800,4288387867,4290097004,4291217776,4291341457,4292269220,4284638476,4285372708,4286950726,4287740744,4289571717,4291085230,4278195968,4278200064,4281223217,4281956608,4280640805,4285377060,4288141395,4290174061,4289973679,4292664476,4278883594,4285377096,4287220105,4289393578,4293132259,4278216192,4283157323,4287688591,4291677905,4285396589,4290888123,4290167500,4278218029,4283617681,4278197276,4290049780,4278206528,4279080084,4278220149,4282943624,4285376689,4286488254,4288138964,4291946739,4289967827,4291219441,4278519064,4279061901,4286558429,4288131788,4293323514,4279045681,4278784594,4281627827,4283394524,4287006406,4285641201,4278190195,4278782356,4282469829,4285230064,4281412572,4281944028,4287663611,4289442810,4289843701,4278979839,4278223103,4278222783,4288074488,4279908954,4285198840,4278255615,4286644223,4279645750,4284078553,4279979129,4281162119,4281364367,4280557081,4281545805,4280894803,4280427106,4281750641,4281939599,4283060371,4284443813,4285811640,4280025116,4283183440,4282983314,4284950188,4287780315,4287784667,4291134975,4283501905,4287780279,4285410267,4294338801,4292516314,4291933899,4294890750,4294837502,4294960633,4281413677,4285468784,4290000052,4294940927,4287760786,4294884314,4294939089,4285408072,4289539437,4282723610,4283123755,4285082664,4287714891,4289755250,4281936661,4286006555,4286598946,4294356336,4285025330,4294306988,4294966980,4291213347,4290687546,4293980265,4291744803,4294966923,4288387840,4290174024,4286085176,4287335243,4282989618,4282936612,4285028947,4279769112,4290290572,4285822800,4283322190,4287137649,4289891193,4289504919,4287598735,4289834928,4284179045,4285492847,4288062635,4285954190,4283259757,4287072170,4286669121,4287261293,4285372781,4285370440,4282936903,4290178413,4282614595,4287746852,4290178376,4294934028,4288355099,4281109291,4294901887,4278255488,4292048084,4282978962,4286578943,4282978999,4290218459,4285961368,4285871477,4281167213,4287784594,4289695631,4290349728,4291139504,4290740159,4294235852,4283570437,4294940037,4294954389,4294945791,4294952371,4294940017,4294953408,4294967295,4294908415,4286388735,4279893885,4294908185,4294967052,4279893785,4279835135,4294933785,4294111986,4294902015"; 156 | 157 | 158 | } 159 | 160 | void update_preset_palette_string_token(){ 161 | ((PcxSaveConfigToken)this.Token).preset_palette_string = import_pal[preset_palettes_combo_box.SelectedIndex]; 162 | } 163 | 164 | protected override void InitTokenFromWidget() 165 | { 166 | ( (PcxSaveConfigToken)this.Token ).Threshold = this.thresholdSlider.Value; 167 | ( (PcxSaveConfigToken)this.Token ).DitherLevel = this.ditherSlider.Value; 168 | ( (PcxSaveConfigToken)this.Token ).PreMultiplyAlpha = this.preMultiplyAlphaCheckBox.Checked; 169 | ( (PcxSaveConfigToken)this.Token ).UseOriginalPalette = this.useOriginalPaletteCheckBox.Checked; 170 | ( (PcxSaveConfigToken)this.Token ).preset_palette = this.preset_palette_check_box.Checked; 171 | ( (PcxSaveConfigToken)this.Token ).preset_palette_arr_position = this.preset_palettes_combo_box.SelectedIndex; 172 | if(import_pal != null) ( (PcxSaveConfigToken)this.Token ).preset_palette_string = this.import_pal[this.preset_palettes_combo_box.SelectedIndex]; 173 | //preset_palette_sender 174 | } 175 | 176 | protected override void InitWidgetFromToken( PaintDotNet.SaveConfigToken token ) 177 | { 178 | this.thresholdSlider.Value = ( (PcxSaveConfigToken)token ).Threshold; 179 | this.ditherSlider.Value = ( (PcxSaveConfigToken)token ).DitherLevel; 180 | this.preMultiplyAlphaCheckBox.Checked = ( (PcxSaveConfigToken)token ).PreMultiplyAlpha; 181 | this.useOriginalPaletteCheckBox.Checked = ( (PcxSaveConfigToken)token ).UseOriginalPalette; 182 | this.preset_palette_check_box.Checked = ( (PcxSaveConfigToken)token ).preset_palette; 183 | 184 | try 185 | { 186 | this.preset_palettes_combo_box.SelectedIndex = ((PcxSaveConfigToken)this.Token).preset_palette_arr_position; 187 | }catch { } 188 | //((PcxSaveConfigToken)this.Token).preset_palette_string = import_pal[((PcxSaveConfigToken)this.Token).preset_palette_arr_position]; 189 | } 190 | 191 | /// 192 | /// Clean up any resources being used. 193 | /// 194 | protected override void Dispose( bool disposing ) 195 | { 196 | if( disposing ) 197 | { 198 | if( components != null ) 199 | { 200 | components.Dispose(); 201 | components = null; 202 | } 203 | } 204 | 205 | base.Dispose( disposing ); 206 | } 207 | 208 | #region Designer generated code 209 | /// 210 | /// Required method for Designer support - do not modify 211 | /// the contents of this method with the code editor. 212 | /// 213 | private void InitializeComponent() 214 | { 215 | this.thresholdSlider = new System.Windows.Forms.TrackBar(); 216 | this.thresholdLabel = new System.Windows.Forms.Label(); 217 | this.thresholdUpDown = new System.Windows.Forms.NumericUpDown(); 218 | this.preMultiplyAlphaCheckBox = new System.Windows.Forms.CheckBox(); 219 | this.useOriginalPaletteCheckBox = new System.Windows.Forms.CheckBox(); 220 | this.ditherUpDown = new System.Windows.Forms.NumericUpDown(); 221 | this.ditherLabel = new System.Windows.Forms.Label(); 222 | this.ditherSlider = new System.Windows.Forms.TrackBar(); 223 | this.preset_palette_check_box = new System.Windows.Forms.CheckBox(); 224 | this.panel1 = new System.Windows.Forms.Panel(); 225 | this.preset_palettes_combo_box = new System.Windows.Forms.ComboBox(); 226 | ((System.ComponentModel.ISupportInitialize)(this.thresholdSlider)).BeginInit(); 227 | ((System.ComponentModel.ISupportInitialize)(this.thresholdUpDown)).BeginInit(); 228 | ((System.ComponentModel.ISupportInitialize)(this.ditherUpDown)).BeginInit(); 229 | ((System.ComponentModel.ISupportInitialize)(this.ditherSlider)).BeginInit(); 230 | this.panel1.SuspendLayout(); 231 | this.SuspendLayout(); 232 | // 233 | // thresholdSlider 234 | // 235 | this.thresholdSlider.Location = new System.Drawing.Point(0, 35); 236 | this.thresholdSlider.Maximum = 255; 237 | this.thresholdSlider.Name = "thresholdSlider"; 238 | this.thresholdSlider.Size = new System.Drawing.Size(180, 45); 239 | this.thresholdSlider.TabIndex = 2; 240 | this.thresholdSlider.TickFrequency = 32; 241 | this.thresholdSlider.Value = 1; 242 | this.thresholdSlider.ValueChanged += new System.EventHandler(this.thresholdSlider_ValueChanged); 243 | // 244 | // thresholdLabel 245 | // 246 | this.thresholdLabel.Location = new System.Drawing.Point(6, 3); 247 | this.thresholdLabel.Name = "thresholdLabel"; 248 | this.thresholdLabel.Size = new System.Drawing.Size(106, 31); 249 | this.thresholdLabel.TabIndex = 0; 250 | this.thresholdLabel.Text = "&Transparency threshold:"; 251 | // 252 | // thresholdUpDown 253 | // 254 | this.thresholdUpDown.Location = new System.Drawing.Point(115, 14); 255 | this.thresholdUpDown.Maximum = new decimal(new int[] { 256 | 255, 257 | 0, 258 | 0, 259 | 0}); 260 | this.thresholdUpDown.Name = "thresholdUpDown"; 261 | this.thresholdUpDown.Size = new System.Drawing.Size(56, 20); 262 | this.thresholdUpDown.TabIndex = 1; 263 | this.thresholdUpDown.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 264 | this.thresholdUpDown.Value = new decimal(new int[] { 265 | 1, 266 | 0, 267 | 0, 268 | 0}); 269 | this.thresholdUpDown.ValueChanged += new System.EventHandler(this.thresholdUpDown_ValueChanged); 270 | this.thresholdUpDown.Enter += new System.EventHandler(this.thresholdUpDown_Enter); 271 | this.thresholdUpDown.Leave += new System.EventHandler(this.thresholdUpDown_Leave); 272 | // 273 | // preMultiplyAlphaCheckBox 274 | // 275 | this.preMultiplyAlphaCheckBox.Location = new System.Drawing.Point(8, 146); 276 | this.preMultiplyAlphaCheckBox.Name = "preMultiplyAlphaCheckBox"; 277 | this.preMultiplyAlphaCheckBox.Size = new System.Drawing.Size(168, 20); 278 | this.preMultiplyAlphaCheckBox.TabIndex = 6; 279 | this.preMultiplyAlphaCheckBox.Text = "&Multiply by alpha channel"; 280 | this.preMultiplyAlphaCheckBox.CheckedChanged += new System.EventHandler(this.preMultiplyAlphaCheckBox_CheckedChanged); 281 | // 282 | // useOriginalPaletteCheckBox 283 | // 284 | this.useOriginalPaletteCheckBox.Location = new System.Drawing.Point(8, 163); 285 | this.useOriginalPaletteCheckBox.Name = "useOriginalPaletteCheckBox"; 286 | this.useOriginalPaletteCheckBox.Size = new System.Drawing.Size(168, 21); 287 | this.useOriginalPaletteCheckBox.TabIndex = 7; 288 | this.useOriginalPaletteCheckBox.Text = "&Use original palette"; 289 | this.useOriginalPaletteCheckBox.CheckedChanged += new System.EventHandler(this.useOriginalPaletteCheckBox_CheckedChanged); 290 | // 291 | // ditherUpDown 292 | // 293 | this.ditherUpDown.Location = new System.Drawing.Point(115, 81); 294 | this.ditherUpDown.Maximum = new decimal(new int[] { 295 | 8, 296 | 0, 297 | 0, 298 | 0}); 299 | this.ditherUpDown.Name = "ditherUpDown"; 300 | this.ditherUpDown.Size = new System.Drawing.Size(56, 20); 301 | this.ditherUpDown.TabIndex = 4; 302 | this.ditherUpDown.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 303 | this.ditherUpDown.Value = new decimal(new int[] { 304 | 1, 305 | 0, 306 | 0, 307 | 0}); 308 | this.ditherUpDown.ValueChanged += new System.EventHandler(this.ditherUpDown_ValueChanged); 309 | this.ditherUpDown.Enter += new System.EventHandler(this.ditherUpDown_Enter); 310 | this.ditherUpDown.Leave += new System.EventHandler(this.ditherUpDown_Leave); 311 | // 312 | // ditherLabel 313 | // 314 | this.ditherLabel.Location = new System.Drawing.Point(6, 83); 315 | this.ditherLabel.Name = "ditherLabel"; 316 | this.ditherLabel.Size = new System.Drawing.Size(106, 18); 317 | this.ditherLabel.TabIndex = 3; 318 | this.ditherLabel.Text = "&Dithering level:"; 319 | // 320 | // ditherSlider 321 | // 322 | this.ditherSlider.LargeChange = 2; 323 | this.ditherSlider.Location = new System.Drawing.Point(0, 102); 324 | this.ditherSlider.Maximum = 8; 325 | this.ditherSlider.Name = "ditherSlider"; 326 | this.ditherSlider.Size = new System.Drawing.Size(180, 45); 327 | this.ditherSlider.TabIndex = 5; 328 | this.ditherSlider.Value = 1; 329 | this.ditherSlider.ValueChanged += new System.EventHandler(this.ditherSlider_ValueChanged); 330 | // 331 | // preset_palette_check_box 332 | // 333 | this.preset_palette_check_box.BackColor = System.Drawing.SystemColors.ButtonShadow; 334 | this.preset_palette_check_box.ForeColor = System.Drawing.SystemColors.ControlText; 335 | this.preset_palette_check_box.Location = new System.Drawing.Point(5, 2); 336 | this.preset_palette_check_box.Name = "preset_palette_check_box"; 337 | this.preset_palette_check_box.Size = new System.Drawing.Size(166, 19); 338 | this.preset_palette_check_box.TabIndex = 8; 339 | this.preset_palette_check_box.Text = "&Use Preset Palettes"; 340 | this.preset_palette_check_box.UseVisualStyleBackColor = false; 341 | this.preset_palette_check_box.CheckedChanged += new System.EventHandler(this.preset_palette_check_box_CheckedChanged); 342 | // 343 | // panel1 344 | // 345 | this.panel1.BackColor = System.Drawing.SystemColors.ButtonShadow; 346 | this.panel1.Controls.Add(this.preset_palettes_combo_box); 347 | this.panel1.Controls.Add(this.preset_palette_check_box); 348 | this.panel1.ForeColor = System.Drawing.SystemColors.ButtonShadow; 349 | this.panel1.Location = new System.Drawing.Point(3, 183); 350 | this.panel1.Name = "panel1"; 351 | this.panel1.Size = new System.Drawing.Size(174, 45); 352 | this.panel1.TabIndex = 10; 353 | // 354 | // preset_palettes_combo_box 355 | // 356 | this.preset_palettes_combo_box.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 357 | this.preset_palettes_combo_box.FormattingEnabled = true; 358 | this.preset_palettes_combo_box.Location = new System.Drawing.Point(6, 21); 359 | this.preset_palettes_combo_box.Name = "preset_palettes_combo_box"; 360 | this.preset_palettes_combo_box.Size = new System.Drawing.Size(162, 21); 361 | this.preset_palettes_combo_box.TabIndex = 9; 362 | this.preset_palettes_combo_box.SelectedIndexChanged += new System.EventHandler(this.preset_palettes_combo_box_SelectedIndexChanged); 363 | // 364 | // PcxSaveConfigWidget 365 | // 366 | this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); 367 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; 368 | this.Controls.Add(this.panel1); 369 | this.Controls.Add(this.useOriginalPaletteCheckBox); 370 | this.Controls.Add(this.preMultiplyAlphaCheckBox); 371 | this.Controls.Add(this.thresholdUpDown); 372 | this.Controls.Add(this.thresholdLabel); 373 | this.Controls.Add(this.thresholdSlider); 374 | this.Controls.Add(this.ditherUpDown); 375 | this.Controls.Add(this.ditherLabel); 376 | this.Controls.Add(this.ditherSlider); 377 | this.Name = "PcxSaveConfigWidget"; 378 | this.Size = new System.Drawing.Size(180, 230); 379 | this.Load += new System.EventHandler(this.PcxSaveConfigWidget_Load); 380 | ((System.ComponentModel.ISupportInitialize)(this.thresholdSlider)).EndInit(); 381 | ((System.ComponentModel.ISupportInitialize)(this.thresholdUpDown)).EndInit(); 382 | ((System.ComponentModel.ISupportInitialize)(this.ditherUpDown)).EndInit(); 383 | ((System.ComponentModel.ISupportInitialize)(this.ditherSlider)).EndInit(); 384 | this.panel1.ResumeLayout(false); 385 | this.ResumeLayout(false); 386 | this.PerformLayout(); 387 | 388 | } 389 | #endregion 390 | 391 | private void thresholdSlider_ValueChanged( object sender, System.EventArgs e ) 392 | { 393 | if( this.thresholdUpDown.Value != (decimal)this.thresholdSlider.Value ) 394 | { 395 | this.thresholdUpDown.Value = (decimal)this.thresholdSlider.Value; 396 | } 397 | 398 | UpdateToken(); 399 | } 400 | 401 | private void thresholdUpDown_ValueChanged( object sender, System.EventArgs e ) 402 | { 403 | if( this.thresholdSlider.Value != (int)this.thresholdUpDown.Value ) 404 | { 405 | this.thresholdSlider.Value = (int)this.thresholdUpDown.Value; 406 | } 407 | } 408 | 409 | private void thresholdUpDown_Leave( object sender, System.EventArgs e ) 410 | { 411 | thresholdUpDown_ValueChanged( sender, e ); 412 | } 413 | 414 | private void thresholdUpDown_Enter( object sender, System.EventArgs e ) 415 | { 416 | thresholdUpDown.Select( 0, thresholdUpDown.Text.Length ); 417 | } 418 | 419 | private void useOriginalPaletteCheckBox_CheckedChanged( object sender, System.EventArgs e ) 420 | { 421 | if (useOriginalPaletteCheckBox.Checked == true) preset_palette_check_box.Checked = false; 422 | UpdateToken(); 423 | } 424 | 425 | private void preMultiplyAlphaCheckBox_CheckedChanged( object sender, System.EventArgs e ) 426 | { 427 | UpdateToken(); 428 | } 429 | 430 | private void ditherSlider_ValueChanged( object sender, EventArgs e ) 431 | { 432 | if( this.ditherUpDown.Value != (decimal)this.ditherSlider.Value ) 433 | { 434 | this.ditherUpDown.Value = (decimal)this.ditherSlider.Value; 435 | } 436 | 437 | UpdateToken(); 438 | } 439 | 440 | private void ditherUpDown_Enter( object sender, EventArgs e ) 441 | { 442 | ditherUpDown.Select( 0, thresholdUpDown.Text.Length ); 443 | } 444 | 445 | private void ditherUpDown_ValueChanged( object sender, EventArgs e ) 446 | { 447 | if( this.ditherSlider.Value != (int)this.ditherUpDown.Value ) 448 | { 449 | this.ditherSlider.Value = (int)this.ditherUpDown.Value; 450 | } 451 | } 452 | 453 | private void ditherUpDown_Leave( object sender, EventArgs e ) 454 | { 455 | ditherUpDown_ValueChanged( sender, e ); 456 | } 457 | 458 | private void PcxSaveConfigWidget_Load(object sender, EventArgs e) 459 | { 460 | load_set_palette(); 461 | } 462 | 463 | private void preset_palette_check_box_CheckedChanged(object sender, EventArgs e) 464 | { 465 | if (preset_palette_check_box.Checked == true) useOriginalPaletteCheckBox.Checked = false; 466 | UpdateToken(); 467 | } 468 | 469 | private void preset_palettes_combo_box_SelectedIndexChanged(object sender, EventArgs e) 470 | { 471 | //update_preset_palette_string_token(); 472 | UpdateToken(); 473 | } 474 | } 475 | } 476 | 477 | -------------------------------------------------------------------------------- /PcxSaveConfigWidget.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PCXFileType")] 9 | [assembly: AssemblyDescription("PCX file type plug in for Paint.NET")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PCXFileType")] 13 | [assembly: AssemblyCopyright("Copyright © 2006-2015 Joshua Bell")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid( "4B81B994-20E9-49d6-8D26-E47B2916396D" )] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("0.9.5.0")] 35 | [assembly: AssemblyFileVersion("0.9.5.0")] 36 | -------------------------------------------------------------------------------- /Quantize/OctreeQuantizer.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Paint.NET 3 | // Copyright (C) Rick Brewster, Chris Crosetto, Dennis Dietrich, Tom Jackson, 4 | // Michael Kelsey, Brandon Ortiz, Craig Taylor, Chris Trevino, 5 | // and Luke Walker 6 | // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. 7 | // See src/setup/License.rtf for complete licensing and attribution information. 8 | ///////////////////////////////////////////////////////////////////////////////// 9 | 10 | ///////////////////////////////////////////////////////////////////////////////// 11 | // Copied for Paint.NET PCX Plugin 12 | // Copyright (C) Joshua Bell 13 | ///////////////////////////////////////////////////////////////////////////////// 14 | 15 | 16 | // Based on: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/colorquant.asp 17 | 18 | using PaintDotNet; 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Drawing; 22 | using System.Drawing.Imaging; 23 | 24 | namespace PcxFileTypePlugin.Quantize 25 | { 26 | /// 27 | /// Quantize using an Octree 28 | /// 29 | internal unsafe class OctreeQuantizer 30 | : Quantizer 31 | { 32 | /// 33 | /// Stores the tree 34 | /// 35 | private Octree _octree; 36 | 37 | /// 38 | /// Maximum allowed color depth 39 | /// 40 | private int _maxColors; 41 | 42 | /// 43 | /// Construct the octree quantizer 44 | /// 45 | /// 46 | /// The Octree quantizer is a two pass algorithm. The initial pass sets up the octree, 47 | /// the second pass quantizes a color based on the nodes in the tree 48 | /// 49 | /// The maximum number of colors to return 50 | /// The number of significant bits 51 | public OctreeQuantizer(int maxColors, int maxColorBits) 52 | : base(false) 53 | { 54 | if (maxColors > 255) 55 | throw new ArgumentOutOfRangeException("maxColors", maxColors, "The number of colors should be less than 256"); 56 | 57 | if ((maxColorBits < 1) |(maxColorBits > 8)) 58 | throw new ArgumentOutOfRangeException("maxColorBits", maxColorBits, "This should be between 1 and 8"); 59 | 60 | _octree = new Octree(maxColorBits); 61 | _maxColors = maxColors; 62 | } 63 | 64 | /// 65 | /// Process the pixel in the first pass of the algorithm 66 | /// 67 | /// The pixel to quantize 68 | /// 69 | /// This function need only be overridden if your quantize algorithm needs two passes, 70 | /// such as an Octree quantizer. 71 | /// 72 | protected override void InitialQuantizePixel(ColorBgra *pixel) 73 | { 74 | _octree.AddColor(pixel); 75 | } 76 | 77 | /// 78 | /// Override this to process the pixel in the second pass of the algorithm 79 | /// 80 | /// The pixel to quantize 81 | /// The quantized value 82 | protected override byte QuantizePixel(ColorBgra *pixel) 83 | { 84 | byte paletteIndex = (byte)_maxColors; // The color at [_maxColors] is set to transparent 85 | 86 | // Get the palette index if this non-transparent 87 | if (pixel->A > 0) 88 | { 89 | paletteIndex = (byte)_octree.GetPaletteIndex(pixel); 90 | } 91 | 92 | return paletteIndex; 93 | } 94 | 95 | /// 96 | /// Retrieve the palette for the quantized image 97 | /// 98 | /// Any old palette, this is overrwritten 99 | /// The new color palette 100 | protected override ColorPalette GetPalette(ColorPalette original) 101 | { 102 | // First off convert the octree to _maxColors colors 103 | List palette = _octree.Palletize(_maxColors - 1); 104 | 105 | // Then convert the palette based on those colors 106 | for (int index = 0; index < palette.Count; index++) 107 | { 108 | original.Entries[index] = palette[index]; 109 | } 110 | 111 | #if ORIGINAL_CODE 112 | for (int i = palette.Count; i < original.Entries.Length; ++i) 113 | { 114 | original.Entries[i] = Color.FromArgb(255, 0, 0, 0); 115 | } 116 | 117 | // Add the transparent color 118 | original.Entries[_maxColors] = Color.FromArgb(0, 0, 0, 0); 119 | #else // PCX Plugin 120 | // For PCX: Pad with transparency 121 | for (int i = palette.Count; i < original.Entries.Length; ++i) 122 | original.Entries[i] = Color.Transparent; 123 | #endif 124 | return original; 125 | } 126 | 127 | /// 128 | /// Class which does the actual quantization 129 | /// 130 | private class Octree 131 | { 132 | /// 133 | /// Construct the octree 134 | /// 135 | /// The maximum number of significant bits in the image 136 | public Octree(int maxColorBits) 137 | { 138 | _maxColorBits = maxColorBits; 139 | _leafCount = 0; 140 | _reducibleNodes = new OctreeNode[9]; 141 | _root = new OctreeNode(0, _maxColorBits, this); 142 | _previousColor = 0; 143 | _previousNode = null; 144 | } 145 | 146 | /// 147 | /// Add a given color value to the octree 148 | /// 149 | /// 150 | public void AddColor(ColorBgra *pixel) 151 | { 152 | // Check if this request is for the same color as the last 153 | if (_previousColor == pixel->Bgra) 154 | { 155 | // If so, check if I have a previous node setup. This will only ocurr if the first color in the image 156 | // happens to be black, with an alpha component of zero. 157 | if (null == _previousNode) 158 | { 159 | _previousColor = pixel->Bgra; 160 | _root.AddColor(pixel, _maxColorBits, 0, this); 161 | } 162 | else 163 | { 164 | // Just update the previous node 165 | _previousNode.Increment(pixel); 166 | } 167 | } 168 | else 169 | { 170 | _previousColor = pixel->Bgra; 171 | _root.AddColor(pixel, _maxColorBits, 0, this); 172 | } 173 | } 174 | 175 | /// 176 | /// Reduce the depth of the tree 177 | /// 178 | public void Reduce() 179 | { 180 | int index; 181 | 182 | // Find the deepest level containing at least one reducible node 183 | for (index = _maxColorBits - 1; (index > 0) && (null == _reducibleNodes[index]); index--) 184 | { 185 | // intentionally blank 186 | } 187 | 188 | // Reduce the node most recently added to the list at level 'index' 189 | OctreeNode node = _reducibleNodes[index]; 190 | _reducibleNodes[index] = node.NextReducible; 191 | 192 | // Decrement the leaf count after reducing the node 193 | _leafCount -= node.Reduce(); 194 | 195 | // And just in case I've reduced the last color to be added, and the next color to 196 | // be added is the same, invalidate the previousNode... 197 | _previousNode = null; 198 | } 199 | 200 | /// 201 | /// Get/Set the number of leaves in the tree 202 | /// 203 | public int Leaves 204 | { 205 | get 206 | { 207 | return _leafCount; 208 | } 209 | 210 | set 211 | { 212 | _leafCount = value; 213 | } 214 | } 215 | 216 | /// 217 | /// Return the array of reducible nodes 218 | /// 219 | protected OctreeNode[] ReducibleNodes 220 | { 221 | get 222 | { 223 | return _reducibleNodes; 224 | } 225 | } 226 | 227 | /// 228 | /// Keep track of the previous node that was quantized 229 | /// 230 | /// The node last quantized 231 | protected void TrackPrevious(OctreeNode node) 232 | { 233 | _previousNode = node; 234 | } 235 | 236 | private Color[] _palette; 237 | private PaletteTable paletteTable; 238 | 239 | /// 240 | /// Convert the nodes in the octree to a palette with a maximum of colorCount colors 241 | /// 242 | /// The maximum number of colors 243 | /// A list with the palettized colors 244 | public List Palletize(int colorCount) 245 | { 246 | while (Leaves > colorCount) 247 | { 248 | Reduce(); 249 | } 250 | 251 | // Now palettize the nodes 252 | List palette = new List(Leaves); 253 | int paletteIndex = 0; 254 | 255 | _root.ConstructPalette(palette, ref paletteIndex); 256 | 257 | // And return the palette 258 | this._palette = palette.ToArray(); 259 | this.paletteTable = null; 260 | 261 | return palette; 262 | } 263 | 264 | /// 265 | /// Get the palette index for the passed color 266 | /// 267 | /// 268 | /// 269 | public int GetPaletteIndex(ColorBgra *pixel) 270 | { 271 | int ret = -1; 272 | 273 | ret = _root.GetPaletteIndex(pixel, 0); 274 | 275 | if (ret < 0) 276 | { 277 | if (this.paletteTable == null) 278 | { 279 | this.paletteTable = new PaletteTable(this._palette); 280 | } 281 | 282 | ret = this.paletteTable.FindClosestPaletteIndex(pixel->ToColor()); 283 | } 284 | 285 | return ret; 286 | } 287 | 288 | /// 289 | /// Mask used when getting the appropriate pixels for a given node 290 | /// 291 | private static int[] mask = new int[8] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; 292 | 293 | /// 294 | /// The root of the octree 295 | /// 296 | private OctreeNode _root; 297 | 298 | /// 299 | /// Number of leaves in the tree 300 | /// 301 | private int _leafCount; 302 | 303 | /// 304 | /// Array of reducible nodes 305 | /// 306 | private OctreeNode[] _reducibleNodes; 307 | 308 | /// 309 | /// Maximum number of significant bits in the image 310 | /// 311 | private int _maxColorBits; 312 | 313 | /// 314 | /// Store the last node quantized 315 | /// 316 | private OctreeNode _previousNode; 317 | 318 | /// 319 | /// Cache the previous color quantized 320 | /// 321 | private uint _previousColor; 322 | 323 | /// 324 | /// Class which encapsulates each node in the tree 325 | /// 326 | protected class OctreeNode 327 | { 328 | /// 329 | /// Construct the node 330 | /// 331 | /// The level in the tree = 0 - 7 332 | /// The number of significant color bits in the image 333 | /// The tree to which this node belongs 334 | public OctreeNode(int level, int colorBits, Octree octree) 335 | { 336 | // Construct the new node 337 | _leaf = (level == colorBits); 338 | 339 | _red = 0; 340 | _green = 0; 341 | _blue = 0; 342 | _pixelCount = 0; 343 | 344 | // If a leaf, increment the leaf count 345 | if (_leaf) 346 | { 347 | octree.Leaves++; 348 | _nextReducible = null; 349 | _children = null; 350 | } 351 | else 352 | { 353 | // Otherwise add this to the reducible nodes 354 | _nextReducible = octree.ReducibleNodes[level]; 355 | octree.ReducibleNodes[level] = this; 356 | _children = new OctreeNode[8]; 357 | } 358 | } 359 | 360 | /// 361 | /// Add a color into the tree 362 | /// 363 | /// The color 364 | /// The number of significant color bits 365 | /// The level in the tree 366 | /// The tree to which this node belongs 367 | public void AddColor(ColorBgra *pixel, int colorBits, int level, Octree octree) 368 | { 369 | // Update the color information if this is a leaf 370 | if (_leaf) 371 | { 372 | Increment(pixel); 373 | 374 | // Setup the previous node 375 | octree.TrackPrevious(this); 376 | } 377 | else 378 | { 379 | // Go to the next level down in the tree 380 | int shift = 7 - level; 381 | int index = ((pixel->R & mask[level]) >> (shift - 2)) | 382 | ((pixel->G & mask[level]) >> (shift - 1)) | 383 | ((pixel->B & mask[level]) >> (shift)); 384 | 385 | OctreeNode child = _children[index]; 386 | 387 | if (null == child) 388 | { 389 | // Create a new child node & store in the array 390 | child = new OctreeNode(level + 1, colorBits, octree); 391 | _children[index] = child; 392 | } 393 | 394 | // Add the color to the child node 395 | child.AddColor(pixel, colorBits, level + 1, octree); 396 | } 397 | 398 | } 399 | 400 | /// 401 | /// Get/Set the next reducible node 402 | /// 403 | public OctreeNode NextReducible 404 | { 405 | get 406 | { 407 | return _nextReducible; 408 | } 409 | 410 | set 411 | { 412 | _nextReducible = value; 413 | } 414 | } 415 | 416 | /// 417 | /// Return the child nodes 418 | /// 419 | public OctreeNode[] Children 420 | { 421 | get 422 | { 423 | return _children; 424 | } 425 | } 426 | 427 | /// 428 | /// Reduce this node by removing all of its children 429 | /// 430 | /// The number of leaves removed 431 | public int Reduce() 432 | { 433 | int children = 0; 434 | _red = 0; 435 | _green = 0; 436 | _blue = 0; 437 | 438 | // Loop through all children and add their information to this node 439 | for (int index = 0; index < 8; index++) 440 | { 441 | if (null != _children[index]) 442 | { 443 | _red += _children[index]._red; 444 | _green += _children[index]._green; 445 | _blue += _children[index]._blue; 446 | _pixelCount += _children[index]._pixelCount; 447 | ++children; 448 | _children[index] = null; 449 | } 450 | } 451 | 452 | // Now change this to a leaf node 453 | _leaf = true; 454 | 455 | // Return the number of nodes to decrement the leaf count by 456 | return(children - 1); 457 | } 458 | 459 | /// 460 | /// Traverse the tree, building up the color palette 461 | /// 462 | /// The palette 463 | /// The current palette index 464 | public void ConstructPalette(List palette, ref int paletteIndex) 465 | { 466 | if (_leaf) 467 | { 468 | // Consume the next palette index 469 | _paletteIndex = paletteIndex++; 470 | 471 | // And set the color of the palette entry 472 | int r = _red / _pixelCount; 473 | int g = _green / _pixelCount; 474 | int b = _blue / _pixelCount; 475 | 476 | palette.Add(Color.FromArgb(r, g, b)); 477 | } 478 | else 479 | { 480 | // Loop through children looking for leaves 481 | for (int index = 0; index < 8; index++) 482 | { 483 | if (null != _children[index]) 484 | { 485 | _children[index].ConstructPalette(palette, ref paletteIndex); 486 | } 487 | } 488 | } 489 | } 490 | 491 | /// 492 | /// Return the palette index for the passed color 493 | /// 494 | public int GetPaletteIndex(ColorBgra *pixel, int level) 495 | { 496 | int paletteIndex = _paletteIndex; 497 | 498 | if (!_leaf) 499 | { 500 | int shift = 7 - level; 501 | int index = ((pixel->R & mask[level]) >> (shift - 2)) | 502 | ((pixel->G & mask[level]) >> (shift - 1)) | 503 | ((pixel->B & mask[level]) >> (shift)); 504 | 505 | if (null != _children[index]) 506 | { 507 | paletteIndex = _children[index].GetPaletteIndex(pixel, level + 1); 508 | } 509 | else 510 | { 511 | paletteIndex = -1; 512 | } 513 | } 514 | 515 | return paletteIndex; 516 | } 517 | 518 | /// 519 | /// Increment the pixel count and add to the color information 520 | /// 521 | public void Increment(ColorBgra *pixel) 522 | { 523 | ++_pixelCount; 524 | _red += pixel->R; 525 | _green += pixel->G; 526 | _blue += pixel->B; 527 | } 528 | 529 | /// 530 | /// Flag indicating that this is a leaf node 531 | /// 532 | private bool _leaf; 533 | 534 | /// 535 | /// Number of pixels in this node 536 | /// 537 | private int _pixelCount; 538 | 539 | /// 540 | /// Red component 541 | /// 542 | private int _red; 543 | 544 | /// 545 | /// Green Component 546 | /// 547 | private int _green; 548 | 549 | /// 550 | /// Blue component 551 | /// 552 | private int _blue; 553 | 554 | /// 555 | /// Pointers to any child nodes 556 | /// 557 | private OctreeNode[] _children; 558 | 559 | /// 560 | /// Pointer to next reducible node 561 | /// 562 | private OctreeNode _nextReducible; 563 | 564 | /// 565 | /// The index of this node in the palette 566 | /// 567 | private int _paletteIndex; 568 | } 569 | } 570 | } 571 | } 572 | -------------------------------------------------------------------------------- /Quantize/PaletteQuantizer.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Paint.NET 3 | // Copyright (C) Rick Brewster, Chris Crosetto, Dennis Dietrich, Tom Jackson, 4 | // Michael Kelsey, Brandon Ortiz, Craig Taylor, Chris Trevino, 5 | // and Luke Walker 6 | // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. 7 | // See src/setup/License.rtf for complete licensing and attribution information. 8 | ///////////////////////////////////////////////////////////////////////////////// 9 | 10 | ///////////////////////////////////////////////////////////////////////////////// 11 | // Copied for Paint.NET PCX Plugin 12 | // Copyright (C) Joshua Bell 13 | ///////////////////////////////////////////////////////////////////////////////// 14 | 15 | // Based on: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/colorquant.asp 16 | 17 | using PaintDotNet; 18 | using System.Collections.Generic; 19 | using System.Drawing; 20 | using System.Drawing.Imaging; 21 | 22 | namespace PcxFileTypePlugin.Quantize 23 | { 24 | /// 25 | /// Summary description for PaletteQuantizer. 26 | /// 27 | internal unsafe class PaletteQuantizer 28 | : Quantizer 29 | { 30 | /// 31 | /// Lookup table for colors 32 | /// 33 | private Dictionary _colorMap; 34 | 35 | /// 36 | /// List of all colors in the palette 37 | /// 38 | private Color[] _colors; 39 | 40 | /// 41 | /// Construct the palette quantizer 42 | /// 43 | /// The color palette to quantize to 44 | /// 45 | /// Palette quantization only requires a single quantization step 46 | /// 47 | public PaletteQuantizer(List palette) 48 | : base(true) 49 | { 50 | _colorMap = new Dictionary(); 51 | _colors = new Color[palette.Count]; 52 | palette.CopyTo(_colors); 53 | } 54 | 55 | /// 56 | /// Override this to process the pixel in the second pass of the algorithm 57 | /// 58 | /// The pixel to quantize 59 | /// The quantized value 60 | protected override byte QuantizePixel(ColorBgra* pixel) 61 | { 62 | byte colorIndex = 0; 63 | uint colorHash = pixel->Bgra; 64 | 65 | // Check if the color is in the lookup table 66 | if (_colorMap.ContainsKey(colorHash)) 67 | { 68 | colorIndex = _colorMap[colorHash]; 69 | } 70 | else 71 | { 72 | // Not found - loop through the palette and find the nearest match. 73 | // Firstly check the alpha value - if 0, lookup the transparent color 74 | if (0 == pixel->A) 75 | { 76 | // Transparent. Lookup the first color with an alpha value of 0 77 | for (int index = 0; index < _colors.Length; index++) 78 | { 79 | if (0 == _colors[index].A) 80 | { 81 | colorIndex = (byte)index; 82 | break; 83 | } 84 | } 85 | } 86 | else 87 | { 88 | // Not transparent... 89 | int leastDistance = int.MaxValue; 90 | int red = pixel->R; 91 | int green = pixel->G; 92 | int blue = pixel->B; 93 | 94 | // Loop through the entire palette, looking for the closest color match 95 | for (int index = 0; index < _colors.Length; index++) 96 | { 97 | Color paletteColor = _colors[index]; 98 | 99 | int redDistance = paletteColor.R - red; 100 | int greenDistance = paletteColor.G - green; 101 | int blueDistance = paletteColor.B - blue; 102 | 103 | int distance = (redDistance * redDistance) + (greenDistance * greenDistance) + 104 | (blueDistance * blueDistance); 105 | 106 | if (distance < leastDistance) 107 | { 108 | colorIndex = (byte)index; 109 | leastDistance = distance; 110 | 111 | // And if it's an exact match, exit the loop 112 | if (0 == distance) 113 | { 114 | break; 115 | } 116 | } 117 | } 118 | } 119 | 120 | // Now I have the color, pop it into the hashtable for next time 121 | _colorMap.Add(colorHash, colorIndex); 122 | } 123 | 124 | return colorIndex; 125 | } 126 | 127 | /// 128 | /// Retrieve the palette for the quantized image 129 | /// 130 | /// Any old palette, this is overrwritten 131 | /// The new color palette 132 | protected override ColorPalette GetPalette(ColorPalette palette) 133 | { 134 | for (int index = 0; index < _colors.Length; index++) 135 | { 136 | palette.Entries[index] = _colors[index]; 137 | } 138 | 139 | #if ORIGINAL_CODE 140 | #else // PCX Plugin 141 | // For PCX: Pad with transparency 142 | for (int i = _colors.Length; i < palette.Entries.Length; ++i) 143 | palette.Entries[i] = Color.Transparent; 144 | #endif 145 | 146 | return palette; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Quantize/PaletteTable.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Paint.NET 3 | // Copyright (C) Rick Brewster, Chris Crosetto, Dennis Dietrich, Tom Jackson, 4 | // Michael Kelsey, Brandon Ortiz, Craig Taylor, Chris Trevino, 5 | // and Luke Walker 6 | // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. 7 | // See src/setup/License.rtf for complete licensing and attribution information. 8 | ///////////////////////////////////////////////////////////////////////////////// 9 | 10 | ///////////////////////////////////////////////////////////////////////////////// 11 | // Copied for Paint.NET PCX Plugin 12 | // Copyright (C) Joshua Bell 13 | ///////////////////////////////////////////////////////////////////////////////// 14 | 15 | using System.Drawing; 16 | 17 | namespace PcxFileTypePlugin.Quantize 18 | { 19 | public sealed class PaletteTable 20 | { 21 | private Color[] palette; 22 | 23 | public Color this[int index] 24 | { 25 | get 26 | { 27 | return this.palette[index]; 28 | } 29 | 30 | set 31 | { 32 | this.palette[index] = value; 33 | } 34 | } 35 | 36 | private int GetDistanceSquared(Color a, Color b) 37 | { 38 | int dsq = 0; // delta squared 39 | int v; 40 | 41 | v = a.B - b.B; 42 | dsq += v * v; 43 | v = a.G - b.G; 44 | dsq += v * v; 45 | v = a.R - b.R; 46 | dsq += v * v; 47 | 48 | return dsq; 49 | } 50 | 51 | public int FindClosestPaletteIndex(Color pixel) 52 | { 53 | int dsqBest = int.MaxValue; 54 | int ret = 0; 55 | 56 | for (int i = 0; i < this.palette.Length; ++i) 57 | { 58 | int dsq = GetDistanceSquared(this.palette[i], pixel); 59 | 60 | if (dsq < dsqBest) 61 | { 62 | dsqBest = dsq; 63 | ret = i; 64 | } 65 | } 66 | 67 | return ret; 68 | } 69 | 70 | public PaletteTable(Color[] palette) 71 | { 72 | this.palette = (Color[])palette.Clone(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Quantize/Quantizer.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Paint.NET 3 | // Copyright (C) Rick Brewster, Chris Crosetto, Dennis Dietrich, Tom Jackson, 4 | // Michael Kelsey, Brandon Ortiz, Craig Taylor, Chris Trevino, 5 | // and Luke Walker 6 | // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. 7 | // See src/setup/License.rtf for complete licensing and attribution information. 8 | ///////////////////////////////////////////////////////////////////////////////// 9 | 10 | ///////////////////////////////////////////////////////////////////////////////// 11 | // Copied for Paint.NET PCX Plugin 12 | // Copyright (C) Joshua Bell 13 | ///////////////////////////////////////////////////////////////////////////////// 14 | 15 | // Based on: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/colorquant.asp 16 | 17 | using PaintDotNet; 18 | using System; 19 | using System.Drawing; 20 | using System.Drawing.Imaging; 21 | 22 | namespace PcxFileTypePlugin.Quantize 23 | { 24 | /// 25 | /// Summary description for Class1. 26 | /// 27 | internal unsafe abstract class Quantizer 28 | { 29 | /// 30 | /// Flag used to indicate whether a single pass or two passes are needed for quantization. 31 | /// 32 | private bool _singlePass; 33 | 34 | protected bool highquality; 35 | public bool HighQuality 36 | { 37 | get 38 | { 39 | return highquality; 40 | } 41 | 42 | set 43 | { 44 | highquality = value; 45 | } 46 | } 47 | 48 | protected int ditherLevel; 49 | public int DitherLevel 50 | { 51 | get 52 | { 53 | return this.ditherLevel; 54 | } 55 | 56 | set 57 | { 58 | this.ditherLevel = value; 59 | } 60 | } 61 | 62 | /// 63 | /// Construct the quantizer 64 | /// 65 | /// If true, the quantization only needs to loop through the source pixels once 66 | /// 67 | /// If you construct this class with a true value for singlePass, then the code will, when quantizing your image, 68 | /// only call the 'QuantizeImage' function. If two passes are required, the code will call 'InitialQuantizeImage' 69 | /// and then 'QuantizeImage'. 70 | /// 71 | public Quantizer(bool singlePass) 72 | { 73 | _singlePass = singlePass; 74 | } 75 | 76 | /// 77 | /// Quantize an image and return the resulting output bitmap 78 | /// 79 | /// The image to quantize 80 | /// A quantized version of the image 81 | public Bitmap Quantize(Image source, ProgressEventHandler progressCallback) 82 | { 83 | // Get the size of the source image 84 | int height = source.Height; 85 | int width = source.Width; 86 | 87 | // And construct a rectangle from these dimensions 88 | Rectangle bounds = new Rectangle(0, 0, width, height); 89 | 90 | // First off take a 32bpp copy of the image 91 | Bitmap copy; 92 | 93 | if (source is Bitmap && source.PixelFormat == PixelFormat.Format32bppArgb) 94 | { 95 | copy = (Bitmap)source; 96 | } 97 | else 98 | { 99 | copy = new Bitmap(width, height, PixelFormat.Format32bppArgb); 100 | 101 | // Now lock the bitmap into memory 102 | using (Graphics g = Graphics.FromImage(copy)) 103 | { 104 | g.PageUnit = GraphicsUnit.Pixel; 105 | 106 | // Draw the source image onto the copy bitmap, 107 | // which will effect a widening as appropriate. 108 | g.DrawImage(source, 0, 0, bounds.Width, bounds.Height); 109 | } 110 | } 111 | 112 | // And construct an 8bpp version 113 | Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed); 114 | 115 | // Define a pointer to the bitmap data 116 | BitmapData sourceData = null; 117 | 118 | try 119 | { 120 | // Get the source image bits and lock into memory 121 | sourceData = copy.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 122 | 123 | // Call the FirstPass function if not a single pass algorithm. 124 | // For something like an octree quantizer, this will run through 125 | // all image pixels, build a data structure, and create a palette. 126 | if (!_singlePass) 127 | { 128 | FirstPass(sourceData, width, height, progressCallback); 129 | } 130 | 131 | // Then set the color palette on the output bitmap. I'm passing in the current palette 132 | // as there's no way to construct a new, empty palette. 133 | output.Palette = this.GetPalette(output.Palette); 134 | 135 | // Then call the second pass which actually does the conversion 136 | SecondPass(sourceData, output, width, height, bounds, progressCallback); 137 | } 138 | 139 | finally 140 | { 141 | // Ensure that the bits are unlocked 142 | copy.UnlockBits(sourceData); 143 | } 144 | 145 | if (copy != source) 146 | { 147 | copy.Dispose(); 148 | } 149 | 150 | // Last but not least, return the output bitmap 151 | return output; 152 | } 153 | 154 | /// 155 | /// Execute the first pass through the pixels in the image 156 | /// 157 | /// The source data 158 | /// The width in pixels of the image 159 | /// The height in pixels of the image 160 | protected virtual void FirstPass(BitmapData sourceData, int width, int height, ProgressEventHandler progressCallback) 161 | { 162 | // Define the source data pointers. The source row is a byte to 163 | // keep addition of the stride value easier (as this is in bytes) 164 | byte* pSourceRow = (byte*)sourceData.Scan0.ToPointer(); 165 | Int32* pSourcePixel; 166 | 167 | // Loop through each row 168 | for (int row = 0; row < height; row++) 169 | { 170 | // Set the source pixel to the first pixel in this row 171 | pSourcePixel = (Int32*)pSourceRow; 172 | 173 | // And loop through each column 174 | for (int col = 0; col < width; col++, pSourcePixel++) 175 | { 176 | InitialQuantizePixel((ColorBgra *)pSourcePixel); 177 | } 178 | 179 | // Add the stride to the source row 180 | pSourceRow += sourceData.Stride; 181 | 182 | if (progressCallback != null) 183 | { 184 | progressCallback(this, new ProgressEventArgs(100.0 * (((double)(row + 1) / (double)height) / 2.0))); 185 | } 186 | } 187 | } 188 | 189 | /// 190 | /// Execute a second pass through the bitmap 191 | /// 192 | /// The source bitmap, locked into memory 193 | /// The output bitmap 194 | /// The width in pixels of the image 195 | /// The height in pixels of the image 196 | /// The bounding rectangle 197 | protected virtual void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds, ProgressEventHandler progressCallback) 198 | { 199 | BitmapData outputData = null; 200 | Color[] pallete = output.Palette.Entries; 201 | int weight = ditherLevel; 202 | 203 | try 204 | { 205 | // Lock the output bitmap into memory 206 | outputData = output.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); 207 | 208 | // Define the source data pointers. The source row is a byte to 209 | // keep addition of the stride value easier (as this is in bytes) 210 | byte* pSourceRow = (byte *)sourceData.Scan0.ToPointer(); 211 | Int32* pSourcePixel = (Int32 *)pSourceRow; 212 | 213 | // Now define the destination data pointers 214 | byte* pDestinationRow = (byte *)outputData.Scan0.ToPointer(); 215 | byte* pDestinationPixel = pDestinationRow; 216 | 217 | int[] errorThisRowR = new int[width + 1]; 218 | int[] errorThisRowG = new int[width + 1]; 219 | int[] errorThisRowB = new int[width + 1]; 220 | 221 | for (int row = 0; row < height; row++) 222 | { 223 | int[] errorNextRowR = new int[width + 1]; 224 | int[] errorNextRowG = new int[width + 1]; 225 | int[] errorNextRowB = new int[width + 1]; 226 | 227 | int ptrInc; 228 | 229 | if ((row & 1) == 0) 230 | { 231 | pSourcePixel = (Int32*)pSourceRow; 232 | pDestinationPixel = pDestinationRow; 233 | ptrInc = +1; 234 | } 235 | else 236 | { 237 | pSourcePixel = (Int32*)pSourceRow + width - 1; 238 | pDestinationPixel = pDestinationRow + width - 1; 239 | ptrInc = -1; 240 | } 241 | 242 | // Loop through each pixel on this scan line 243 | for (int col = 0; col < width; ++col) 244 | { 245 | // Quantize the pixel 246 | ColorBgra srcPixel = *(ColorBgra *)pSourcePixel; 247 | ColorBgra target = new ColorBgra(); 248 | 249 | target.B = Int32Util.ClampToByte(srcPixel.B - ((errorThisRowB[col] * weight) / 8)); 250 | target.G = Int32Util.ClampToByte(srcPixel.G - ((errorThisRowG[col] * weight) / 8)); 251 | target.R = Int32Util.ClampToByte(srcPixel.R - ((errorThisRowR[col] * weight) / 8)); 252 | target.A = srcPixel.A; 253 | 254 | byte pixelValue = QuantizePixel(&target); 255 | *pDestinationPixel = pixelValue; 256 | 257 | ColorBgra actual = ColorBgra.FromColor(pallete[pixelValue]); 258 | 259 | int errorR = actual.R - target.R; 260 | int errorG = actual.G - target.G; 261 | int errorB = actual.B - target.B; 262 | 263 | // Floyd-Steinberg Error Diffusion: 264 | // a) 7/16 error goes to x+1 265 | // b) 5/16 error goes to y+1 266 | // c) 3/16 error goes to x-1,y+1 267 | // d) 1/16 error goes to x+1,y+1 268 | 269 | const int a = 7; 270 | const int b = 5; 271 | const int c = 3; 272 | 273 | int errorRa = (errorR * a) / 16; 274 | int errorRb = (errorR * b) / 16; 275 | int errorRc = (errorR * c) / 16; 276 | int errorRd = errorR - errorRa - errorRb - errorRc; 277 | 278 | int errorGa = (errorG * a) / 16; 279 | int errorGb = (errorG * b) / 16; 280 | int errorGc = (errorG * c) / 16; 281 | int errorGd = errorG - errorGa - errorGb - errorGc; 282 | 283 | int errorBa = (errorB * a) / 16; 284 | int errorBb = (errorB * b) / 16; 285 | int errorBc = (errorB * c) / 16; 286 | int errorBd = errorB - errorBa - errorBb - errorBc; 287 | 288 | errorThisRowR[col + 1] += errorRa; 289 | errorThisRowG[col + 1] += errorGa; 290 | errorThisRowB[col + 1] += errorBa; 291 | 292 | errorNextRowR[width - col] += errorRb; 293 | errorNextRowG[width - col] += errorGb; 294 | errorNextRowB[width - col] += errorBb; 295 | 296 | if (col != 0) 297 | { 298 | errorNextRowR[width - (col - 1)] += errorRc; 299 | errorNextRowG[width - (col - 1)] += errorGc; 300 | errorNextRowB[width - (col - 1)] += errorBc; 301 | } 302 | 303 | errorNextRowR[width - (col + 1)] += errorRd; 304 | errorNextRowG[width - (col + 1)] += errorGd; 305 | errorNextRowB[width - (col + 1)] += errorBd; 306 | 307 | unchecked 308 | { 309 | pSourcePixel += ptrInc; 310 | pDestinationPixel += ptrInc; 311 | } 312 | } 313 | 314 | // Add the stride to the source row 315 | pSourceRow += sourceData.Stride; 316 | 317 | // And to the destination row 318 | pDestinationRow += outputData.Stride; 319 | 320 | if (progressCallback != null) 321 | { 322 | progressCallback(this, new ProgressEventArgs(100.0 * (0.5 + ((double)(row + 1) / (double)height) / 2.0))); 323 | } 324 | 325 | errorThisRowB = errorNextRowB; 326 | errorThisRowG = errorNextRowG; 327 | errorThisRowR = errorNextRowR; 328 | } 329 | } 330 | 331 | finally 332 | { 333 | // Ensure that I unlock the output bits 334 | output.UnlockBits(outputData); 335 | } 336 | } 337 | 338 | /// 339 | /// Override this to process the pixel in the first pass of the algorithm 340 | /// 341 | /// The pixel to quantize 342 | /// 343 | /// This function need only be overridden if your quantize algorithm needs two passes, 344 | /// such as an Octree quantizer. 345 | /// 346 | protected virtual void InitialQuantizePixel(ColorBgra *pixel) 347 | { 348 | } 349 | 350 | /// 351 | /// Override this to process the pixel in the second pass of the algorithm 352 | /// 353 | /// The pixel to quantize 354 | /// The quantized value 355 | protected abstract byte QuantizePixel(ColorBgra *pixel); 356 | 357 | /// 358 | /// Retrieve the palette for the quantized image 359 | /// 360 | /// Any old palette, this is overrwritten 361 | /// The new color palette 362 | protected abstract ColorPalette GetPalette(ColorPalette original); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | PcxFileType : A file type plugin for Paint.NET 2 | ---------------------------------------------- 3 | 4 | History 5 | ------- 6 | * 2006-10-30 - 0.9.0.0 - Original release 7 | * 2006-12-24 - 0.9.1.0 - Added "Use original palette" option 8 | * 2007-01-25 - 0.9.2.0 - Fixed RLE compression writing 9 | * 2007-10-21 - 0.9.3.0 - Force 1-bit images to black/white palette 10 | * 2014-06-24 - 0.9.4.0 - Compatibility with Paint.NET 4.0 11 | * 2015-01-09 - 0.9.5.0 - Build for .NET Framework 4.5 12 | * 2016-10-13 - 0.9.5.1 - [EzArIk]: Added (somewhat crude but effective) pre-set palette support, 13 | included example pre-set file, (also made the project reference Paint.net Api automatically.) 14 | 15 | Summary 16 | ------- 17 | This is a file type plugin for [Paint.NET](http://www.getpaint.net/), 18 | which adds support for the ZSoft Corporation PC Paintbrush "PCX" image 19 | format. 20 | 21 | Supported are: 22 | 23 | * Loading 1-, 2-, 4-, 8- and 24-bit color PCX files 24 | * Saving 4- and 8-bit PCX files 25 | 26 | Paint.NET Forum Topic: 27 | 28 | * http://forums.getpaint.net/index.php?/topic/2135-pcx-plug-in/ 29 | 30 | PCX File Format References 31 | -------------------------- 32 | * http://en.wikipedia.org/wiki/PCX 33 | * http://www.qzx.com/pc-gpe/pcx.txt 34 | * http://www.fileformat.info/format/pcx/ 35 | * http://courses.ece.uiuc.edu/ece390/books/labmanual/graphics-pcx.html 36 | 37 | Loading 38 | ------- 39 | The following image formats can be loaded: 40 | 41 | * PCX Versions 2.5, 2.8 and 3.0 42 | * 1, 2, 4 and 8-bit (2, 4, 16 and 256-color) palettized images 43 | * 24-bit "true color" images 44 | * Run-length encoded and unencoded 45 | * 2-, 4-, and 16-color default palettes (monochrome, CGA, EGA) 46 | * 16-color and 256-color custom palettes 47 | * multiple bit planes (common for 4-bit and required for 24-bit) 48 | 49 | Saving 50 | ------ 51 | Saving as PCX always attempts to create an 8-bit (256-color) 52 | palettized image, although if the palette ends up being 16 colors 53 | or less a 4-bit palette is used instead. Saving as 24-bit PCX is 54 | not supported. 55 | 56 | The same options are presented as for Paint.NET's GIF file type 57 | support: 58 | 59 | * Transparency threshold 60 | * Dithering level 61 | * Multiply by alpha channel 62 | 63 | The PCX format does not support transparency, but the transparency 64 | threshold can be used to adjust how tranparency present in the image 65 | is applied to the pixels before the image is flattened. 66 | 67 | Two more options are present but one only has an effect if the image 68 | was originally loaded as a palettized PCX image: 69 | 70 | * Use original palette 71 | 72 | If selected, the original palette will be used when saving. 73 | 74 | The second option is: 75 | * Use Pre-set palette 76 | 77 | If selected, the images palette can be overridden with one of the pre-set palettes stored in Preset_Pallettes.pst_pal.txt, 78 | these can be changed by editing that text file, for the current official Preset_Pallettes.pst_pal.txt look in the folder 79 | named: _Preset_Palettes_Example_File (a readme in that folder describes where to place the palette file to use it.) 80 | 81 | Installation 82 | ------------ 83 | Place the PcxFileType.DLL file in the the 'FileTypes' folder in your 84 | Paint.NET installation location. For example, in: 85 | 86 | C:\Program Files\Paint.NET\FileTypes 87 | 88 | The new file type will be available when you next start Paint.NET. 89 | 90 | To open PCX files in Paint.NET by default, right-click a PCX file 91 | in Windows Explorer, select "Open With" then "Choose Program...", 92 | select Paint.NET (you may have to click "Browse...") and then check 93 | "Always use the selected program to open this kind of file" 94 | 95 | 96 | License 97 | ------- 98 | This work is distributed under the terms and conditions of the MIT 99 | license. See License.md for more information. 100 | 101 | 102 | Feedback & Bug Reporting 103 | ------------------------ 104 | Bug reports can be filed at: 105 | 106 | https://github.com/inexorabletash/PcxFileType/issues 107 | 108 | -------------------------------------------------------------------------------- /_Preset_Palettes_Example_File/Preset_Pallettes.pst_pal.txt: -------------------------------------------------------------------------------- 1 | EzArIks Preset Pallette File for the PCX plugin. 2 | This file contains Preset Palette Data, the first value should be N, this tells the program that the next line contains the maximum number of entries, this must not exceed the actual number of entries in the file!!!, Each Entry must begin with E the next line must contain a name for the entry, the next line after that is the pallette entry, these can be coppied from a (PaintDotNet).PDN file (which has been saved from a .PCX file opened with the PCX file Plugin) and opened with a text editor, e.g. Notepad, found after the pallette tag, there must be 256 entries all in one line, as found in the file. 3 | NUM 4 | 4 5 | ENT 6 | Q2_Quake2 7 | 4278190080,4279176975,4280229663,4281282351,4282335039,4283124555,4284177243,4285229931,4286282619,4287335307,4288387995,4289440683,4290493371,4291546059,4292598747,4293651435,4284697379,4284171039,4283645727,4283382555,4282857243,4282330903,4282067735,4281542419,4281279251,4281016083,4280752911,4280489743,4279964427,4279701259,4279439111,4279175943,4284440431,4284177255,4284175199,4283912027,4283648851,4283385675,4282859331,4282334011,4282070839,4281544495,4281281323,4280756007,4280492835,4279966491,4279703319,4279440147,4287592275,4286276419,4285750075,4284960559,4291794763,4289166139,4287325999,4285485863,4293631783,4291529507,4289689375,4287849243,4286009111,4284168975,4282328843,4280489735,4289149739,4288622371,4288097051,4287309587,4286521103,4285732619,4284946183,4283896576,4283109120,4282584832,4282060544,4281535232,4281010944,4280486656,4279961344,4279437056,4286275403,4285749059,4285223743,4284960571,4284434231,4283908915,4283645743,4283119403,4282594087,4282330915,4281804571,4281279255,4280752915,4280227599,4279701259,4279175943,4285479703,4284430103,4283641623,4282592023,4281803539,4280752911,4279964427,4279175943,4289944399,4290739055,4291533715,4292328375,4291549151,4289972179,4288657347,4287080375,4285765543,4284188571,4282873739,4281296767,4279718767,4279454567,4279190363,4278927187,4278662987,4278660927,4278658867,4278198059,4278195999,4278193939,4278191883,4278190080,4287321943,4286795599,4286269255,4285743939,4285217595,4284691251,4284165935,4283902763,4283114275,4282326815,4281539355,4281013011,4280225551,4279438091,4278912775,4278190080,4288126843,4287600499,4287073131,4286546787,4286020447,4285756247,4285229903,4284703559,4284177219,4283387707,4282598195,4281808683,4281282339,4280492827,4279703315,4279176971,4288629567,4287841079,4287314735,4286527271,4286000931,4285213467,4284687127,4283899667,4283374351,4282586891,4281799435,4281011975,4280224519,4279699200,4278910976,4278190080,4286020559,4285494211,4284967863,4284703655,4284177307,4283651983,4283125631,4282861427,4282335079,4281808727,4281282379,4280756031,4280491823,4279965475,4279439127,4278912775,4288392059,4287602543,4287076195,4286286679,4285760331,4284970819,4284444475,4283918131,4283128615,4282339099,4281811731,4281285387,4280495879,4279968512,4279441152,4278914816,4278255360,4280542991,4282372891,4283677479,4284458799,4284452659,4284447539,4294967295,4294967251,4294967207,4294967167,4294967123,4294967079,4294961951,4294956823,4294950671,4294945543,4294939392,4293885696,4293094144,4292040448,4291249920,4290198272,4289407744,4288356096,4287567616,4286516992,4285728512,4284416000,4282843136,4281270272,4279959552,4293853184,4281808895,4294901760,4278190335,4281019171,4279966487,4279440143,4293629823,4290999123,4288632627,4286267163,4293645255,4291275675,4289170295,4287064919,4288633683 8 | ENT 9 | Q2_RO0K_Outskirts 10 | 4278190080,4279894016,4281535232,4282843911,4286513152,4286578688,4288348160,4294901760,4294934592,4294927460,4294934399,4294937226,4293248421,4294950847,4293445832,4294888410,4294960099,4294638311,4289013286,4293092452,4290139428,4288355072,4291990162,4293178246,4292590519,4293059288,4285081861,4291792751,4292585855,4294572243,4293388263,4283568915,4283967748,4290143560,4291793008,4286595072,4290147912,4291529547,4291868833,4281473801,4283183616,4285018377,4291404401,4291472010,4292006329,4279833861,4281873415,4287714596,4288371483,4289762851,4288713562,4293059266,4283714565,4284634112,4285165568,4286545664,4287137800,4288387867,4290097004,4291217776,4291341457,4292269220,4284638476,4285372708,4286950726,4287740744,4289571717,4291085230,4278195968,4278200064,4281223217,4281956608,4280640805,4285377060,4288141395,4290174061,4289973679,4292664476,4278883594,4285377096,4287220105,4289393578,4293132259,4278216192,4283157323,4287688591,4291677905,4285396589,4290888123,4290167500,4278218029,4283617681,4278197276,4290049780,4278206528,4279080084,4278220149,4282943624,4285376689,4286488254,4288138964,4291946739,4289967827,4291219441,4278519064,4279061901,4286558429,4288131788,4293323514,4279045681,4278784594,4281627827,4283394524,4287006406,4285641201,4278190195,4278782356,4282469829,4285230064,4281412572,4281944028,4287663611,4289442810,4289843701,4278979839,4278223103,4278222783,4288074488,4279908954,4285198840,4278255615,4286644223,4279645750,4284078553,4279979129,4281162119,4281364367,4280557081,4281545805,4280894803,4280427106,4281750641,4281939599,4283060371,4284443813,4285811640,4280025116,4283183440,4282983314,4284950188,4287780315,4287784667,4291134975,4283501905,4287780279,4285410267,4294338801,4292516314,4291933899,4294890750,4294837502,4294960633,4281413677,4285468784,4290000052,4294940927,4287760786,4294884314,4294939089,4285408072,4289539437,4282723610,4283123755,4285082664,4287714891,4289755250,4281936661,4286006555,4286598946,4294356336,4285025330,4294306988,4294966980,4291213347,4290687546,4293980265,4291744803,4294966923,4288387840,4290174024,4286085176,4287335243,4282989618,4282936612,4285028947,4279769112,4290290572,4285822800,4283322190,4287137649,4289891193,4289504919,4287598735,4289834928,4284179045,4285492847,4288062635,4285954190,4283259757,4287072170,4286669121,4287261293,4285372781,4285370440,4282936903,4290178413,4282614595,4287746852,4290178376,4294934028,4288355099,4281109291,4294901887,4278255488,4292048084,4282978962,4286578943,4282978999,4290218459,4285961368,4285871477,4281167213,4287784594,4289695631,4290349728,4291139504,4290740159,4294235852,4283570437,4294940037,4294954389,4294945791,4294952371,4294940017,4294953408,4294967295,4294908415,4286388735,4279893885,4294908185,4294967052,4279893785,4279835135,4294933785,4294111986,4294902015 11 | ENT 12 | Q2_RO0K154H_psycadelica 13 | 4278190097,4278190114,4278190131,4281532433,4281532450,4281532467,4278190148,4278190165,4278190182,4278190199,4281532484,4281532501,4281532518,4281532535,4284874769,4284874786,4284874803,4284874820,4284874837,4284874854,4284874871,4278190216,4278190233,4278190250,4278190267,4281532552,4281532569,4281532586,4281532603,4281532620,4284874888,4284874905,4284874922,4284874939,4284874956,4284874973,4278255377,4278255411,4281597696,4281597713,4281597730,4281597747,4278255428,4278255445,4278255462,4278255479,4281597764,4281597781,4281597798,4281597815,4284940032,4284940049,4284940066,4284940083,4284940100,4284940117,4284940134,4284940151,4278255496,4278255513,4278255530,4278255547,4281597832,4281597849,4281597866,4281597883,4278255581,4281597900,4281597917,4284940168,4284940185,4284940202,4284940219,4284940236,4284940253,4284940270,4288217122,4288217139,4288217156,4288217173,4288217190,4288217207,4291559458,4291559475,4291559492,4291559509,4291559526,4291559543,4294901828,4294901845,4294901862,4294901879,4288217224,4288217241,4288217258,4288217275,4288217292,4288217309,4291559560,4291559577,4291559594,4291559611,4294901896,4294901913,4291559628,4288282385,4288282402,4288282419,4288282436,4288282453,4288282470,4288282487,4291624755,4291624772,4291624789,4291624806,4291624823,4294967125,4294967142,4294967159,4288282504,4288282521,4288282538,4288282555,4288282572,4288282589,4288282606,4291624840,4291624857,4291624874,4291624891,4294967176,4294967193,4294967210,4294967227,4291624908,4291624925,4291624942,4294967244,4294967261,4294967278,4294967295,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080 14 | ENT 15 | Purple_Sepia 16 | 4278190080,4278977290,4279304974,4279436560,4279830549,4279961879,4280093209,4280159002,4280224539,4280290075,4280815650,4281209895,4281341224,4281472811,4281669677,4281801263,4281801520,4281867314,4281998643,4282129715,4282129972,4282195765,4282261558,4282327351,4282393143,4282590267,4282655548,4282721340,4282787133,4282853183,4282918720,4282984513,4283050306,4283181891,4283182148,4283313477,4283444807,4283510856,4283642442,4283707979,4283839564,4283905357,4283905614,4284036944,4284168529,4284234322,4284234579,4284300372,4284365909,4284497495,4284563543,4284629336,4284695129,4284826459,4284958301,4285089631,4285155424,4285287009,4285287266,4285418596,4285550182,4285550694,4285616487,4285682280,4285813866,4285879659,4285945452,4286011245,4286208624,4286274417,4286274673,4286472052,4286537845,4286669174,4286735224,4286801017,4286867067,4286998652,4287064702,4287130495,4287261825,4287327874,4287393923,4287459716,4287459717,4287525510,4287657095,4287788937,4287854730,4287920523,4287986315,4288052108,4288052366,4288118159,4288183952,4288250000,4288315793,4288381330,4288447380,4288513173,4288579222,4288645015,4288710808,4288711065,4288776858,4288842651,4288843163,4289040541,4289040542,4289172384,4289238177,4289238434,4289304227,4289369764,4289435557,4289435813,4289501862,4289567655,4289633192,4289765291,4289831083,4289896620,4289962413,4290028974,4290094511,4290094512,4290094768,4290226610,4290292659,4290358196,4290490038,4290556088,4290687674,4290819259,4290819516,4290885566,4291017152,4291082944,4291083457,4291149507,4291346885,4291347141,4291544521,4291610826,4291676619,4291742412,4291807949,4291874255,4292269782,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080,4278190080 -------------------------------------------------------------------------------- /_Preset_Palettes_Example_File/__readme.txt: -------------------------------------------------------------------------------- 1 | place the: Preset_Pallettes.pst_pal.txt file from this directory into your paint.net FileTypes directory, 2 | e.g. "C:\Program Files\paint.net\FileTypes" 3 | --------------------------------------------------------------------------------