CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

TGASharpLib.cs (220069B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // The Command & Conquer Map Editor and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // The Command & Conquer Map Editor and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 /*                         MIT License
     16                  Copyright (c) 2017 TGASharpLib
     17 
     18 Permission is hereby granted, free of charge, to any person obtaining a copy
     19 of this software and associated documentation files (the "Software"), to deal
     20 in the Software without restriction, including without limitation the rights
     21 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     22 copies of the Software, and to permit persons to whom the Software is
     23 furnished to do so, subject to the following conditions:
     24 
     25 The above copyright notice and this permission notice shall be included in all
     26 copies or substantial portions of the Software.
     27 
     28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     29 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     30 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     31 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     32 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     33 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     34 SOFTWARE.
     35 */
     36 
     37 using System;
     38 using System.Collections.Generic;
     39 using System.Text;
     40 using System.Drawing;
     41 using System.Drawing.Imaging;
     42 using System.IO;
     43 using System.Runtime.InteropServices;
     44 
     45 namespace TGASharpLib
     46 {
     47     #region Enums
     48     /// <summary>
     49     /// <para>The first 128 Color Map Type codes are reserved for use by Truevision,
     50     /// while the second set of 128 Color Map Type codes(128 to 255) may be used for
     51     /// developer applications.</para>
     52     /// True-Color images do not normally make use of the color map field, but some current
     53     /// applications store palette information or developer-defined information in this field.
     54     /// It is best to check Field 3, Image Type, to make sure you have a file which can use the
     55     /// data stored in the Color Map Field.
     56     /// Otherwise ignore the information. When saving or creating files for True-Color
     57     /// images do not use this field and set it to Zero to ensure compatibility. Please refer
     58     /// to the Developer Area specification for methods of storing developer defined information.
     59     /// </summary>
     60     public enum TgaColorMapType : byte
     61     {
     62         NoColorMap = 0,
     63         ColorMap = 1,
     64         Truevision_2,
     65         Truevision_3,
     66         Truevision_4,
     67         Truevision_5,
     68         Truevision_6,
     69         Truevision_7,
     70         Truevision_8,
     71         Truevision_9,
     72         Truevision_10,
     73         Truevision_11,
     74         Truevision_12,
     75         Truevision_13,
     76         Truevision_14,
     77         Truevision_15,
     78         Truevision_16,
     79         Truevision_17,
     80         Truevision_18,
     81         Truevision_19,
     82         Truevision_20,
     83         Truevision_21,
     84         Truevision_22,
     85         Truevision_23,
     86         Truevision_24,
     87         Truevision_25,
     88         Truevision_26,
     89         Truevision_27,
     90         Truevision_28,
     91         Truevision_29,
     92         Truevision_30,
     93         Truevision_31,
     94         Truevision_32,
     95         Truevision_33,
     96         Truevision_34,
     97         Truevision_35,
     98         Truevision_36,
     99         Truevision_37,
    100         Truevision_38,
    101         Truevision_39,
    102         Truevision_40,
    103         Truevision_41,
    104         Truevision_42,
    105         Truevision_43,
    106         Truevision_44,
    107         Truevision_45,
    108         Truevision_46,
    109         Truevision_47,
    110         Truevision_48,
    111         Truevision_49,
    112         Truevision_50,
    113         Truevision_51,
    114         Truevision_52,
    115         Truevision_53,
    116         Truevision_54,
    117         Truevision_55,
    118         Truevision_56,
    119         Truevision_57,
    120         Truevision_58,
    121         Truevision_59,
    122         Truevision_60,
    123         Truevision_61,
    124         Truevision_62,
    125         Truevision_63,
    126         Truevision_64,
    127         Truevision_65,
    128         Truevision_66,
    129         Truevision_67,
    130         Truevision_68,
    131         Truevision_69,
    132         Truevision_70,
    133         Truevision_71,
    134         Truevision_72,
    135         Truevision_73,
    136         Truevision_74,
    137         Truevision_75,
    138         Truevision_76,
    139         Truevision_77,
    140         Truevision_78,
    141         Truevision_79,
    142         Truevision_80,
    143         Truevision_81,
    144         Truevision_82,
    145         Truevision_83,
    146         Truevision_84,
    147         Truevision_85,
    148         Truevision_86,
    149         Truevision_87,
    150         Truevision_88,
    151         Truevision_89,
    152         Truevision_90,
    153         Truevision_91,
    154         Truevision_92,
    155         Truevision_93,
    156         Truevision_94,
    157         Truevision_95,
    158         Truevision_96,
    159         Truevision_97,
    160         Truevision_98,
    161         Truevision_99,
    162         Truevision_100,
    163         Truevision_101,
    164         Truevision_102,
    165         Truevision_103,
    166         Truevision_104,
    167         Truevision_105,
    168         Truevision_106,
    169         Truevision_107,
    170         Truevision_108,
    171         Truevision_109,
    172         Truevision_110,
    173         Truevision_111,
    174         Truevision_112,
    175         Truevision_113,
    176         Truevision_114,
    177         Truevision_115,
    178         Truevision_116,
    179         Truevision_117,
    180         Truevision_118,
    181         Truevision_119,
    182         Truevision_120,
    183         Truevision_121,
    184         Truevision_122,
    185         Truevision_123,
    186         Truevision_124,
    187         Truevision_125,
    188         Truevision_126,
    189         Truevision_127,
    190         Other_128,
    191         Other_129,
    192         Other_130,
    193         Other_131,
    194         Other_132,
    195         Other_133,
    196         Other_134,
    197         Other_135,
    198         Other_136,
    199         Other_137,
    200         Other_138,
    201         Other_139,
    202         Other_140,
    203         Other_141,
    204         Other_142,
    205         Other_143,
    206         Other_144,
    207         Other_145,
    208         Other_146,
    209         Other_147,
    210         Other_148,
    211         Other_149,
    212         Other_150,
    213         Other_151,
    214         Other_152,
    215         Other_153,
    216         Other_154,
    217         Other_155,
    218         Other_156,
    219         Other_157,
    220         Other_158,
    221         Other_159,
    222         Other_160,
    223         Other_161,
    224         Other_162,
    225         Other_163,
    226         Other_164,
    227         Other_165,
    228         Other_166,
    229         Other_167,
    230         Other_168,
    231         Other_169,
    232         Other_170,
    233         Other_171,
    234         Other_172,
    235         Other_173,
    236         Other_174,
    237         Other_175,
    238         Other_176,
    239         Other_177,
    240         Other_178,
    241         Other_179,
    242         Other_180,
    243         Other_181,
    244         Other_182,
    245         Other_183,
    246         Other_184,
    247         Other_185,
    248         Other_186,
    249         Other_187,
    250         Other_188,
    251         Other_189,
    252         Other_190,
    253         Other_191,
    254         Other_192,
    255         Other_193,
    256         Other_194,
    257         Other_195,
    258         Other_196,
    259         Other_197,
    260         Other_198,
    261         Other_199,
    262         Other_200,
    263         Other_201,
    264         Other_202,
    265         Other_203,
    266         Other_204,
    267         Other_205,
    268         Other_206,
    269         Other_207,
    270         Other_208,
    271         Other_209,
    272         Other_210,
    273         Other_211,
    274         Other_212,
    275         Other_213,
    276         Other_214,
    277         Other_215,
    278         Other_216,
    279         Other_217,
    280         Other_218,
    281         Other_219,
    282         Other_220,
    283         Other_221,
    284         Other_222,
    285         Other_223,
    286         Other_224,
    287         Other_225,
    288         Other_226,
    289         Other_227,
    290         Other_228,
    291         Other_229,
    292         Other_230,
    293         Other_231,
    294         Other_232,
    295         Other_233,
    296         Other_234,
    297         Other_235,
    298         Other_236,
    299         Other_237,
    300         Other_238,
    301         Other_239,
    302         Other_240,
    303         Other_241,
    304         Other_242,
    305         Other_243,
    306         Other_244,
    307         Other_245,
    308         Other_246,
    309         Other_247,
    310         Other_248,
    311         Other_249,
    312         Other_250,
    313         Other_251,
    314         Other_252,
    315         Other_253,
    316         Other_254,
    317         Other_255
    318     }
    319 
    320     /// <summary>
    321     /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
    322     /// <para>When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits
    323     /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the
    324     /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you
    325     /// must still parse the color map data 16 bits at a time and ignore the 16th bit.</para>
    326     /// <para>When working with a TARGA M8 card you would select 24 bits (8 bits per primary)
    327     /// since the color map is defined as 256 entries of 24 bit color values.</para>
    328     /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per
    329     /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your
    330     /// application’s use of look-up tables. It is suggested that when working with 16-bit and
    331     /// 32-bit color images, you store them as True-Color images and do not use the color map 
    332     /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited
    333     /// to storing look-up table information.
    334     /// </summary>
    335     public enum TgaColorMapEntrySize : byte
    336     {
    337         Other = 0,
    338         X1R5G5B5 = 15,
    339         A1R5G5B5 = 16,
    340         R8G8B8 = 24,
    341         A8R8G8B8 = 32
    342     }
    343 
    344     /// <summary>
    345     /// Truevision has currently defined seven image types:
    346     /// <para>0 - No Image Data Included;</para>
    347     /// <para>1 - Uncompressed, Color-mapped Image;</para>
    348     /// <para>2 - Uncompressed, True-color Image;</para>
    349     /// <para>3 - Uncompressed, Black-and-white Image;</para>
    350     /// <para>9 - Run-length encoded, Color-mapped Image;</para>
    351     /// <para>10 - Run-length encoded, True-color Image;</para>
    352     /// <para>11 - Run-length encoded, Black-and-white Image.</para>
    353     /// Image Data Type codes 0 to 127 are reserved for use by Truevision for general applications.
    354     /// Image Data Type codes 128 to 255 may be used for developer applications.
    355     /// </summary>
    356     public enum TgaImageType : byte
    357     {
    358         NoImageData = 0,
    359         Uncompressed_ColorMapped = 1,
    360         Uncompressed_TrueColor,
    361         Uncompressed_BlackWhite,
    362         _Truevision_4,
    363         _Truevision_5,
    364         _Truevision_6,
    365         _Truevision_7,
    366         _Truevision_8,
    367         RLE_ColorMapped = 9,
    368         RLE_TrueColor,
    369         RLE_BlackWhite,
    370         _Truevision_12,
    371         _Truevision_13,
    372         _Truevision_14,
    373         _Truevision_15,
    374         _Truevision_16,
    375         _Truevision_17,
    376         _Truevision_18,
    377         _Truevision_19,
    378         _Truevision_20,
    379         _Truevision_21,
    380         _Truevision_22,
    381         _Truevision_23,
    382         _Truevision_24,
    383         _Truevision_25,
    384         _Truevision_26,
    385         _Truevision_27,
    386         _Truevision_28,
    387         _Truevision_29,
    388         _Truevision_30,
    389         _Truevision_31,
    390         _Truevision_32,
    391         _Truevision_33,
    392         _Truevision_34,
    393         _Truevision_35,
    394         _Truevision_36,
    395         _Truevision_37,
    396         _Truevision_38,
    397         _Truevision_39,
    398         _Truevision_40,
    399         _Truevision_41,
    400         _Truevision_42,
    401         _Truevision_43,
    402         _Truevision_44,
    403         _Truevision_45,
    404         _Truevision_46,
    405         _Truevision_47,
    406         _Truevision_48,
    407         _Truevision_49,
    408         _Truevision_50,
    409         _Truevision_51,
    410         _Truevision_52,
    411         _Truevision_53,
    412         _Truevision_54,
    413         _Truevision_55,
    414         _Truevision_56,
    415         _Truevision_57,
    416         _Truevision_58,
    417         _Truevision_59,
    418         _Truevision_60,
    419         _Truevision_61,
    420         _Truevision_62,
    421         _Truevision_63,
    422         _Truevision_64,
    423         _Truevision_65,
    424         _Truevision_66,
    425         _Truevision_67,
    426         _Truevision_68,
    427         _Truevision_69,
    428         _Truevision_70,
    429         _Truevision_71,
    430         _Truevision_72,
    431         _Truevision_73,
    432         _Truevision_74,
    433         _Truevision_75,
    434         _Truevision_76,
    435         _Truevision_77,
    436         _Truevision_78,
    437         _Truevision_79,
    438         _Truevision_80,
    439         _Truevision_81,
    440         _Truevision_82,
    441         _Truevision_83,
    442         _Truevision_84,
    443         _Truevision_85,
    444         _Truevision_86,
    445         _Truevision_87,
    446         _Truevision_88,
    447         _Truevision_89,
    448         _Truevision_90,
    449         _Truevision_91,
    450         _Truevision_92,
    451         _Truevision_93,
    452         _Truevision_94,
    453         _Truevision_95,
    454         _Truevision_96,
    455         _Truevision_97,
    456         _Truevision_98,
    457         _Truevision_99,
    458         _Truevision_100,
    459         _Truevision_101,
    460         _Truevision_102,
    461         _Truevision_103,
    462         _Truevision_104,
    463         _Truevision_105,
    464         _Truevision_106,
    465         _Truevision_107,
    466         _Truevision_108,
    467         _Truevision_109,
    468         _Truevision_110,
    469         _Truevision_111,
    470         _Truevision_112,
    471         _Truevision_113,
    472         _Truevision_114,
    473         _Truevision_115,
    474         _Truevision_116,
    475         _Truevision_117,
    476         _Truevision_118,
    477         _Truevision_119,
    478         _Truevision_120,
    479         _Truevision_121,
    480         _Truevision_122,
    481         _Truevision_123,
    482         _Truevision_124,
    483         _Truevision_125,
    484         _Truevision_126,
    485         _Truevision_127,
    486         _Other_128,
    487         _Other_129,
    488         _Other_130,
    489         _Other_131,
    490         _Other_132,
    491         _Other_133,
    492         _Other_134,
    493         _Other_135,
    494         _Other_136,
    495         _Other_137,
    496         _Other_138,
    497         _Other_139,
    498         _Other_140,
    499         _Other_141,
    500         _Other_142,
    501         _Other_143,
    502         _Other_144,
    503         _Other_145,
    504         _Other_146,
    505         _Other_147,
    506         _Other_148,
    507         _Other_149,
    508         _Other_150,
    509         _Other_151,
    510         _Other_152,
    511         _Other_153,
    512         _Other_154,
    513         _Other_155,
    514         _Other_156,
    515         _Other_157,
    516         _Other_158,
    517         _Other_159,
    518         _Other_160,
    519         _Other_161,
    520         _Other_162,
    521         _Other_163,
    522         _Other_164,
    523         _Other_165,
    524         _Other_166,
    525         _Other_167,
    526         _Other_168,
    527         _Other_169,
    528         _Other_170,
    529         _Other_171,
    530         _Other_172,
    531         _Other_173,
    532         _Other_174,
    533         _Other_175,
    534         _Other_176,
    535         _Other_177,
    536         _Other_178,
    537         _Other_179,
    538         _Other_180,
    539         _Other_181,
    540         _Other_182,
    541         _Other_183,
    542         _Other_184,
    543         _Other_185,
    544         _Other_186,
    545         _Other_187,
    546         _Other_188,
    547         _Other_189,
    548         _Other_190,
    549         _Other_191,
    550         _Other_192,
    551         _Other_193,
    552         _Other_194,
    553         _Other_195,
    554         _Other_196,
    555         _Other_197,
    556         _Other_198,
    557         _Other_199,
    558         _Other_200,
    559         _Other_201,
    560         _Other_202,
    561         _Other_203,
    562         _Other_204,
    563         _Other_205,
    564         _Other_206,
    565         _Other_207,
    566         _Other_208,
    567         _Other_209,
    568         _Other_210,
    569         _Other_211,
    570         _Other_212,
    571         _Other_213,
    572         _Other_214,
    573         _Other_215,
    574         _Other_216,
    575         _Other_217,
    576         _Other_218,
    577         _Other_219,
    578         _Other_220,
    579         _Other_221,
    580         _Other_222,
    581         _Other_223,
    582         _Other_224,
    583         _Other_225,
    584         _Other_226,
    585         _Other_227,
    586         _Other_228,
    587         _Other_229,
    588         _Other_230,
    589         _Other_231,
    590         _Other_232,
    591         _Other_233,
    592         _Other_234,
    593         _Other_235,
    594         _Other_236,
    595         _Other_237,
    596         _Other_238,
    597         _Other_239,
    598         _Other_240,
    599         _Other_241,
    600         _Other_242,
    601         _Other_243,
    602         _Other_244,
    603         _Other_245,
    604         _Other_246,
    605         _Other_247,
    606         _Other_248,
    607         _Other_249,
    608         _Other_250,
    609         _Other_251,
    610         _Other_252,
    611         _Other_253,
    612         _Other_254,
    613         _Other_255
    614     }
    615 
    616     /// <summary>
    617     /// Number of bits per pixel. This number includes the Attribute or Alpha channel bits.
    618     /// Common values are 8, 16, 24 and 32 but other pixel depths could be used.
    619     /// </summary>
    620     public enum TgaPixelDepth : byte
    621     {
    622         Other = 0,
    623         Bpp8 = 8,
    624         Bpp16 = 16,
    625         Bpp24 = 24,
    626         Bpp32 = 32
    627     }
    628 
    629     /// <summary>
    630     /// Used to indicate the order in which pixel data is transferred from the file to the screen.
    631     /// (Bit 4 (bit 0 in enum) is for left-to-right ordering and bit 5 (bit 1 in enum) is for
    632     /// topto-bottom ordering as shown below.)
    633     /// </summary>
    634     public enum TgaImgOrigin : byte
    635     {
    636         BottomLeft = 0,
    637         BottomRight,
    638         TopLeft,
    639         TopRight
    640     }
    641 
    642     /// <summary>
    643     /// Contains a value which specifies the type of Alpha channel
    644     /// data contained in the file. Value Meaning:
    645     /// <para>0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)</para>
    646     /// <para>1: undefined data in the Alpha field, can be ignored</para>
    647     /// <para>2: undefined data in the Alpha field, but should be retained</para>
    648     /// <para>3: useful Alpha channel data is present</para>
    649     /// <para>4: pre-multiplied Alpha(see description below)</para>
    650     /// <para>5 -127: RESERVED</para>
    651     /// <para>128-255: Un-assigned</para>
    652     /// <para>Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the
    653     /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates
    654     /// that the pixel is completely transparent and a value of 1 indicates that the pixel is
    655     /// completely opaque(assume all component values have been normalized).</para>
    656     /// <para>A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a
    657     /// transparency of one-half. For numerous reasons(including image compositing) is is better to
    658     /// pre-multiply the individual color components with the value in the Alpha channel.</para>
    659     /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0).
    660     /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components
    661     /// of the pixel have already been scaled by the value in the Alpha channel.
    662     /// </summary>
    663     public enum TgaAttrType : byte
    664     {
    665         NoAlpha = 0,
    666         UndefinedAlphaCanBeIgnored,
    667         UndefinedAlphaButShouldBeRetained,
    668         UsefulAlpha,
    669         PreMultipliedAlpha,
    670         _Reserved_5,
    671         _Reserved_6,
    672         _Reserved_7,
    673         _Reserved_8,
    674         _Reserved_9,
    675         _Reserved_10,
    676         _Reserved_11,
    677         _Reserved_12,
    678         _Reserved_13,
    679         _Reserved_14,
    680         _Reserved_15,
    681         _Reserved_16,
    682         _Reserved_17,
    683         _Reserved_18,
    684         _Reserved_19,
    685         _Reserved_20,
    686         _Reserved_21,
    687         _Reserved_22,
    688         _Reserved_23,
    689         _Reserved_24,
    690         _Reserved_25,
    691         _Reserved_26,
    692         _Reserved_27,
    693         _Reserved_28,
    694         _Reserved_29,
    695         _Reserved_30,
    696         _Reserved_31,
    697         _Reserved_32,
    698         _Reserved_33,
    699         _Reserved_34,
    700         _Reserved_35,
    701         _Reserved_36,
    702         _Reserved_37,
    703         _Reserved_38,
    704         _Reserved_39,
    705         _Reserved_40,
    706         _Reserved_41,
    707         _Reserved_42,
    708         _Reserved_43,
    709         _Reserved_44,
    710         _Reserved_45,
    711         _Reserved_46,
    712         _Reserved_47,
    713         _Reserved_48,
    714         _Reserved_49,
    715         _Reserved_50,
    716         _Reserved_51,
    717         _Reserved_52,
    718         _Reserved_53,
    719         _Reserved_54,
    720         _Reserved_55,
    721         _Reserved_56,
    722         _Reserved_57,
    723         _Reserved_58,
    724         _Reserved_59,
    725         _Reserved_60,
    726         _Reserved_61,
    727         _Reserved_62,
    728         _Reserved_63,
    729         _Reserved_64,
    730         _Reserved_65,
    731         _Reserved_66,
    732         _Reserved_67,
    733         _Reserved_68,
    734         _Reserved_69,
    735         _Reserved_70,
    736         _Reserved_71,
    737         _Reserved_72,
    738         _Reserved_73,
    739         _Reserved_74,
    740         _Reserved_75,
    741         _Reserved_76,
    742         _Reserved_77,
    743         _Reserved_78,
    744         _Reserved_79,
    745         _Reserved_80,
    746         _Reserved_81,
    747         _Reserved_82,
    748         _Reserved_83,
    749         _Reserved_84,
    750         _Reserved_85,
    751         _Reserved_86,
    752         _Reserved_87,
    753         _Reserved_88,
    754         _Reserved_89,
    755         _Reserved_90,
    756         _Reserved_91,
    757         _Reserved_92,
    758         _Reserved_93,
    759         _Reserved_94,
    760         _Reserved_95,
    761         _Reserved_96,
    762         _Reserved_97,
    763         _Reserved_98,
    764         _Reserved_99,
    765         _Reserved_100,
    766         _Reserved_101,
    767         _Reserved_102,
    768         _Reserved_103,
    769         _Reserved_104,
    770         _Reserved_105,
    771         _Reserved_106,
    772         _Reserved_107,
    773         _Reserved_108,
    774         _Reserved_109,
    775         _Reserved_110,
    776         _Reserved_111,
    777         _Reserved_112,
    778         _Reserved_113,
    779         _Reserved_114,
    780         _Reserved_115,
    781         _Reserved_116,
    782         _Reserved_117,
    783         _Reserved_118,
    784         _Reserved_119,
    785         _Reserved_120,
    786         _Reserved_121,
    787         _Reserved_122,
    788         _Reserved_123,
    789         _Reserved_124,
    790         _Reserved_125,
    791         _Reserved_126,
    792         _Reserved_127,
    793         _UnAssigned_128,
    794         _UnAssigned_129,
    795         _UnAssigned_130,
    796         _UnAssigned_131,
    797         _UnAssigned_132,
    798         _UnAssigned_133,
    799         _UnAssigned_134,
    800         _UnAssigned_135,
    801         _UnAssigned_136,
    802         _UnAssigned_137,
    803         _UnAssigned_138,
    804         _UnAssigned_139,
    805         _UnAssigned_140,
    806         _UnAssigned_141,
    807         _UnAssigned_142,
    808         _UnAssigned_143,
    809         _UnAssigned_144,
    810         _UnAssigned_145,
    811         _UnAssigned_146,
    812         _UnAssigned_147,
    813         _UnAssigned_148,
    814         _UnAssigned_149,
    815         _UnAssigned_150,
    816         _UnAssigned_151,
    817         _UnAssigned_152,
    818         _UnAssigned_153,
    819         _UnAssigned_154,
    820         _UnAssigned_155,
    821         _UnAssigned_156,
    822         _UnAssigned_157,
    823         _UnAssigned_158,
    824         _UnAssigned_159,
    825         _UnAssigned_160,
    826         _UnAssigned_161,
    827         _UnAssigned_162,
    828         _UnAssigned_163,
    829         _UnAssigned_164,
    830         _UnAssigned_165,
    831         _UnAssigned_166,
    832         _UnAssigned_167,
    833         _UnAssigned_168,
    834         _UnAssigned_169,
    835         _UnAssigned_170,
    836         _UnAssigned_171,
    837         _UnAssigned_172,
    838         _UnAssigned_173,
    839         _UnAssigned_174,
    840         _UnAssigned_175,
    841         _UnAssigned_176,
    842         _UnAssigned_177,
    843         _UnAssigned_178,
    844         _UnAssigned_179,
    845         _UnAssigned_180,
    846         _UnAssigned_181,
    847         _UnAssigned_182,
    848         _UnAssigned_183,
    849         _UnAssigned_184,
    850         _UnAssigned_185,
    851         _UnAssigned_186,
    852         _UnAssigned_187,
    853         _UnAssigned_188,
    854         _UnAssigned_189,
    855         _UnAssigned_190,
    856         _UnAssigned_191,
    857         _UnAssigned_192,
    858         _UnAssigned_193,
    859         _UnAssigned_194,
    860         _UnAssigned_195,
    861         _UnAssigned_196,
    862         _UnAssigned_197,
    863         _UnAssigned_198,
    864         _UnAssigned_199,
    865         _UnAssigned_200,
    866         _UnAssigned_201,
    867         _UnAssigned_202,
    868         _UnAssigned_203,
    869         _UnAssigned_204,
    870         _UnAssigned_205,
    871         _UnAssigned_206,
    872         _UnAssigned_207,
    873         _UnAssigned_208,
    874         _UnAssigned_209,
    875         _UnAssigned_210,
    876         _UnAssigned_211,
    877         _UnAssigned_212,
    878         _UnAssigned_213,
    879         _UnAssigned_214,
    880         _UnAssigned_215,
    881         _UnAssigned_216,
    882         _UnAssigned_217,
    883         _UnAssigned_218,
    884         _UnAssigned_219,
    885         _UnAssigned_220,
    886         _UnAssigned_221,
    887         _UnAssigned_222,
    888         _UnAssigned_223,
    889         _UnAssigned_224,
    890         _UnAssigned_225,
    891         _UnAssigned_226,
    892         _UnAssigned_227,
    893         _UnAssigned_228,
    894         _UnAssigned_229,
    895         _UnAssigned_230,
    896         _UnAssigned_231,
    897         _UnAssigned_232,
    898         _UnAssigned_233,
    899         _UnAssigned_234,
    900         _UnAssigned_235,
    901         _UnAssigned_236,
    902         _UnAssigned_237,
    903         _UnAssigned_238,
    904         _UnAssigned_239,
    905         _UnAssigned_240,
    906         _UnAssigned_241,
    907         _UnAssigned_242,
    908         _UnAssigned_243,
    909         _UnAssigned_244,
    910         _UnAssigned_245,
    911         _UnAssigned_246,
    912         _UnAssigned_247,
    913         _UnAssigned_248,
    914         _UnAssigned_249,
    915         _UnAssigned_250,
    916         _UnAssigned_251,
    917         _UnAssigned_252,
    918         _UnAssigned_253,
    919         _UnAssigned_254,
    920         _UnAssigned_255
    921     }
    922     #endregion
    923 
    924     #region Classes
    925     public class TgaColorKey : ICloneable
    926     {
    927         byte a = 0;
    928         byte r = 0;
    929         byte g = 0;
    930         byte b = 0;
    931 
    932         public TgaColorKey()
    933         {
    934         }
    935 
    936         /// <summary>
    937         /// Make <see cref="TgaColorKey"/> from ARGB bytes.
    938         /// </summary>
    939         /// <param name="A">Alpha value.</param>
    940         /// <param name="R">Red value.</param>
    941         /// <param name="G">Green value.</param>
    942         /// <param name="B">Blue value.</param>
    943         public TgaColorKey(byte A, byte R, byte G, byte B)
    944         {
    945             a = A;
    946             r = R;
    947             g = G;
    948             b = B;
    949         }
    950 
    951         /// <summary>
    952         /// Make <see cref="TgaColorKey"/> from ARGB bytes.
    953         /// </summary>
    954         /// <param name="Bytes">Array of bytes(byte[4]).</param>
    955         public TgaColorKey(byte[] Bytes)
    956         {
    957             if (Bytes == null)
    958                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
    959             if (Bytes.Length != Size)
    960                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
    961 
    962             Color color = Color.FromArgb(BitConverter.ToInt32(Bytes, 0));
    963             a = color.A;
    964             r = color.R;
    965             g = color.G;
    966             b = color.B;
    967         }
    968 
    969         /// <summary>
    970         /// Make <see cref="TgaColorKey"/> from <see cref="int"/>.
    971         /// </summary>
    972         /// <param name="ARGB">32bit ARGB integer color value.</param>
    973         public TgaColorKey(int ARGB)
    974         {
    975             Color ColorARGB = Color.FromArgb(ARGB);
    976             a = ColorARGB.A;
    977             r = ColorARGB.R;
    978             g = ColorARGB.G;
    979             b = ColorARGB.B;
    980         }
    981 
    982         /// <summary>
    983         /// Make <see cref="TgaColorKey"/> from <see cref="Color"/>.
    984         /// </summary>
    985         /// <param name="color">GDI+ <see cref="Color"/> value.</param>
    986         public TgaColorKey(Color color)
    987         {
    988             a = color.A;
    989             r = color.R;
    990             g = color.G;
    991             b = color.B;
    992         }
    993 
    994         /// <summary>
    995         /// Gets or sets alpha color value.
    996         /// </summary>
    997         public byte A
    998         {
    999             get { return a; }
   1000             set { a = value; }
   1001         }
   1002 
   1003         /// <summary>
   1004         /// Gets or sets red color value.
   1005         /// </summary>
   1006         public byte R
   1007         {
   1008             get { return r; }
   1009             set { r = value; }
   1010         }
   1011 
   1012         /// <summary>
   1013         /// Gets or sets green color value.
   1014         /// </summary>
   1015         public byte G
   1016         {
   1017             get { return g; }
   1018             set { g = value; }
   1019         }
   1020 
   1021         /// <summary>
   1022         /// Gets or sets blue color value.
   1023         /// </summary>
   1024         public byte B
   1025         {
   1026             get { return b; }
   1027             set { b = value; }
   1028         }
   1029 
   1030         /// <summary>
   1031         /// Gets TGA Field size in bytes.
   1032         /// </summary>
   1033         public const int Size = 4;
   1034 
   1035         /// <summary>
   1036         /// Make full independed copy of <see cref="TgaColorKey"/>.
   1037         /// </summary>
   1038         /// <returns>Copy of <see cref="TgaColorKey"/></returns>
   1039         public TgaColorKey Clone()
   1040         {
   1041             return new TgaColorKey(a, r, g, b);
   1042         }
   1043 
   1044         /// <summary>
   1045         /// Make full independed copy of <see cref="TgaColorKey"/>.
   1046         /// </summary>
   1047         /// <returns>Copy of <see cref="TgaColorKey"/></returns>
   1048         object ICloneable.Clone()
   1049         {
   1050             return Clone();
   1051         }
   1052 
   1053         public override bool Equals(object obj)
   1054         {
   1055             return ((obj is TgaColorKey) ? Equals((TgaColorKey)obj) : false);
   1056         }
   1057 
   1058         public bool Equals(TgaColorKey item)
   1059         {
   1060             return (a == item.a && r == item.r && g == item.g && b == item.b);
   1061         }
   1062 
   1063         public static bool operator ==(TgaColorKey item1, TgaColorKey item2)
   1064         {
   1065             if (ReferenceEquals(item1, null))
   1066                 return ReferenceEquals(item2, null);
   1067 
   1068             if (ReferenceEquals(item2, null))
   1069                 return ReferenceEquals(item1, null);
   1070 
   1071             return item1.Equals(item2);
   1072         }
   1073 
   1074         public static bool operator !=(TgaColorKey item1, TgaColorKey item2)
   1075         {
   1076             return !(item1 == item2);
   1077         }
   1078 
   1079         public override int GetHashCode()
   1080         {
   1081             return ToInt().GetHashCode();
   1082         }
   1083 
   1084         /// <summary>
   1085         /// Gets <see cref="TgaColorKey"/> like string.
   1086         /// </summary>
   1087         /// <returns>String in ARGB format.</returns>
   1088         public override string ToString()
   1089         {
   1090             return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}",
   1091                 nameof(A), a, nameof(R), r, nameof(G), g, nameof(B), b);
   1092         }
   1093 
   1094         /// <summary>
   1095         /// Convert <see cref="TgaColorKey"/> to byte array.
   1096         /// </summary>
   1097         /// <returns>Byte array with length = 4.</returns>
   1098         public byte[] ToBytes()
   1099         {
   1100             return BitConverter.GetBytes(ToInt());
   1101         }
   1102 
   1103         /// <summary>
   1104         /// Gets <see cref="TgaColorKey"/> like GDI+ <see cref="Color"/>.
   1105         /// </summary>
   1106         /// <returns><see cref="Color"/> value of <see cref="TgaColorKey"/>.</returns>
   1107         public Color ToColor()
   1108         {
   1109             return Color.FromArgb(a, r, g, b);
   1110         }
   1111 
   1112         /// <summary>
   1113         /// Gets <see cref="TgaColorKey"/> like ARGB <see cref="int"/>.
   1114         /// </summary>
   1115         /// <returns>ARGB <see cref="int"/> value of <see cref="TgaColorKey"/>.</returns>
   1116         public int ToInt()
   1117         {
   1118             return ToColor().ToArgb();
   1119         }
   1120     }
   1121 
   1122     /// <summary>
   1123     /// This field (5 bytes) and its sub-fields describe the color map (if any) used for the image.
   1124     /// If the Color Map Type field is set to zero, indicating that no color map exists, then
   1125     /// these 5 bytes should be set to zero. These bytes always must be written to the file.
   1126     /// </summary>
   1127     public class TgaColorMapSpec : ICloneable
   1128     {
   1129         ushort firstEntryIndex = 0;
   1130         ushort colorMapLength = 0;
   1131         TgaColorMapEntrySize colorMapEntrySize = TgaColorMapEntrySize.Other;
   1132 
   1133         /// <summary>
   1134         /// Make new <see cref="TgaColorMapSpec"/>.
   1135         /// </summary>
   1136         public TgaColorMapSpec()
   1137         {
   1138         }
   1139 
   1140         /// <summary>
   1141         /// Make <see cref="TgaColorMapSpec"/> from bytes.
   1142         /// </summary>
   1143         /// <param name="Bytes">Array of bytes(byte[5]).</param>
   1144         public TgaColorMapSpec(byte[] Bytes)
   1145         {
   1146             if (Bytes == null)
   1147                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   1148             if (Bytes.Length != Size)
   1149                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   1150 
   1151             firstEntryIndex = BitConverter.ToUInt16(Bytes, 0);
   1152             colorMapLength = BitConverter.ToUInt16(Bytes, 2);
   1153             colorMapEntrySize = (TgaColorMapEntrySize)Bytes[4];
   1154         }
   1155 
   1156         /// <summary>
   1157         /// Field 4.1 (2 bytes):
   1158         /// Index of the first color map entry. Index refers to the starting entry in loading
   1159         /// the color map.
   1160         /// <para>Example: If you would have 1024 entries in the entire color map but you only
   1161         /// need to store 72 of those entries, this field allows you to start in the middle of
   1162         /// the color-map (e.g., position 342).</para>
   1163         /// </summary>
   1164         public ushort FirstEntryIndex
   1165         {
   1166             get { return firstEntryIndex; }
   1167             set { firstEntryIndex = value; }
   1168         }
   1169 
   1170         /// <summary>
   1171         /// Field 4.2 (2 bytes):
   1172         /// Total number of color map entries included.
   1173         /// </summary>
   1174         public ushort ColorMapLength
   1175         {
   1176             get { return colorMapLength; }
   1177             set { colorMapLength = value; }
   1178         }
   1179 
   1180         /// <summary>
   1181         /// Field 4.3 (1 byte):
   1182         /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
   1183         /// <para>When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits
   1184         /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the
   1185         /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you
   1186         /// must still parse the color map data 16 bits at a time and ignore the 16th bit.</para>
   1187         /// <para>When working with a TARGA M8 card you would select 24 bits (8 bits per primary)
   1188         /// since the color map is defined as 256 entries of 24 bit color values.</para>
   1189         /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per
   1190         /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your
   1191         /// application’s use of look-up tables. It is suggested that when working with 16-bit and
   1192         /// 32-bit color images, you store them as True-Color images and do not use the color map 
   1193         /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited
   1194         /// to storing look-up table information.
   1195         /// </summary>
   1196         public TgaColorMapEntrySize ColorMapEntrySize
   1197         {
   1198             get { return colorMapEntrySize; }
   1199             set { colorMapEntrySize = value; }
   1200         }
   1201 
   1202         /// <summary>
   1203         /// Gets TGA Field size in bytes.
   1204         /// </summary>
   1205         public const int Size = 5;
   1206 
   1207         /// <summary>
   1208         /// Make full independed copy of <see cref="TgaColorMapSpec"/>.
   1209         /// </summary>
   1210         /// <returns>Copy of <see cref="TgaColorMapSpec"/></returns>
   1211         public TgaColorMapSpec Clone()
   1212         {
   1213             return new TgaColorMapSpec(ToBytes());
   1214         }
   1215 
   1216         /// <summary>
   1217         /// Make full independed copy of <see cref="TgaColorMapSpec"/>.
   1218         /// </summary>
   1219         /// <returns>Copy of <see cref="TgaColorMapSpec"/></returns>
   1220         object ICloneable.Clone()
   1221         {
   1222             return Clone();
   1223         }
   1224 
   1225         public override bool Equals(object obj)
   1226         {
   1227             return ((obj is TgaColorMapSpec) ? Equals((TgaColorMapSpec)obj) : false);
   1228         }
   1229 
   1230         public bool Equals(TgaColorMapSpec item)
   1231         {
   1232             return (firstEntryIndex == item.firstEntryIndex &&
   1233                 colorMapLength == item.colorMapLength &&
   1234                 colorMapEntrySize == item.colorMapEntrySize);
   1235         }
   1236 
   1237         public static bool operator ==(TgaColorMapSpec item1, TgaColorMapSpec item2)
   1238         {
   1239             if (ReferenceEquals(item1, null))
   1240                 return ReferenceEquals(item2, null);
   1241 
   1242             if (ReferenceEquals(item2, null))
   1243                 return ReferenceEquals(item1, null);
   1244 
   1245             return item1.Equals(item2);
   1246         }
   1247 
   1248         public static bool operator !=(TgaColorMapSpec item1, TgaColorMapSpec item2)
   1249         {
   1250             return !(item1 == item2);
   1251         }
   1252 
   1253         public override int GetHashCode()
   1254         {
   1255             unchecked
   1256             {
   1257                 return (firstEntryIndex << 16 | colorMapLength).GetHashCode() ^ colorMapEntrySize.GetHashCode();
   1258             }
   1259         }
   1260 
   1261         public override string ToString()
   1262         {
   1263             return String.Format("{0}={1}, {2}={3}, {4}={5}", nameof(FirstEntryIndex), FirstEntryIndex,
   1264                 nameof(ColorMapLength), ColorMapLength, nameof(ColorMapEntrySize), ColorMapEntrySize);
   1265         }
   1266 
   1267         /// <summary>
   1268         /// Convert ColorMapSpec to byte array.
   1269         /// </summary>
   1270         /// <returns>Byte array with length = 5.</returns>
   1271         public byte[] ToBytes()
   1272         {
   1273             return BitConverterExt.ToBytes(firstEntryIndex, colorMapLength, (byte)colorMapEntrySize);
   1274         }
   1275     }
   1276 
   1277     public class TgaComment : ICloneable
   1278     {
   1279         const int StrNLen = 80; //80 ASCII chars + 1 '\0' = 81 per SrtN!
   1280         string origString = String.Empty;
   1281         char blankSpaceChar = TgaString.DefaultBlankSpaceChar;
   1282 
   1283         public TgaComment()
   1284         {
   1285         }
   1286 
   1287         public TgaComment(string Str, char BlankSpaceChar = '\0')
   1288         {
   1289             if (Str == null)
   1290                 throw new ArgumentNullException(nameof(Str) + " = null!");
   1291 
   1292             origString = Str;
   1293             blankSpaceChar = BlankSpaceChar;
   1294         }
   1295 
   1296         public TgaComment(byte[] Bytes)
   1297         {
   1298             if (Bytes == null)
   1299                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   1300             if (Bytes.Length != Size)
   1301                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   1302 
   1303             string s = Encoding.ASCII.GetString(Bytes, 0, StrNLen);
   1304             s += Encoding.ASCII.GetString(Bytes, 81, StrNLen);
   1305             s += Encoding.ASCII.GetString(Bytes, 162, StrNLen);
   1306             s += Encoding.ASCII.GetString(Bytes, 243, StrNLen);
   1307 
   1308             switch (s[s.Length - 1])
   1309             {
   1310                 case '\0':
   1311                 case ' ':
   1312                     blankSpaceChar = s[s.Length - 1];
   1313                     origString = s.TrimEnd(new char[] { s[s.Length - 1] });
   1314                     break;
   1315                 default:
   1316                     origString = s;
   1317                     break;
   1318             }
   1319         }
   1320 
   1321         /// <summary>
   1322         /// Gets TGA Field size in bytes.
   1323         /// </summary>
   1324         public const int Size = 81 * 4;
   1325 
   1326         public string OriginalString
   1327         {
   1328             get { return origString; }
   1329             set { origString = value; }
   1330         }
   1331 
   1332         public char BlankSpaceChar
   1333         {
   1334             get { return blankSpaceChar; }
   1335             set { blankSpaceChar = value; }
   1336         }
   1337 
   1338         /// <summary>
   1339         /// Make full independed copy of <see cref="TgaComment"/>.
   1340         /// </summary>
   1341         /// <returns>Copy of <see cref="TgaComment"/></returns>
   1342         public TgaComment Clone()
   1343         {
   1344             return new TgaComment(origString, blankSpaceChar);
   1345         }
   1346 
   1347         /// <summary>
   1348         /// Make full independed copy of <see cref="TgaComment"/>.
   1349         /// </summary>
   1350         /// <returns>Copy of <see cref="TgaComment"/></returns>
   1351         object ICloneable.Clone()
   1352         {
   1353             return Clone();
   1354         }
   1355 
   1356         public override bool Equals(object obj)
   1357         {
   1358             return ((obj is TgaComment) ? Equals((TgaComment)obj) : false);
   1359         }
   1360 
   1361         public bool Equals(TgaComment item)
   1362         {
   1363             return (origString == item.origString && blankSpaceChar == item.blankSpaceChar);
   1364         }
   1365 
   1366         public static bool operator ==(TgaComment item1, TgaComment item2)
   1367         {
   1368             if (ReferenceEquals(item1, null))
   1369                 return ReferenceEquals(item2, null);
   1370 
   1371             if (ReferenceEquals(item2, null))
   1372                 return ReferenceEquals(item1, null);
   1373 
   1374             return item1.Equals(item2);
   1375         }
   1376 
   1377         public static bool operator !=(TgaComment item1, TgaComment item2)
   1378         {
   1379             return !(item1 == item2);
   1380         }
   1381 
   1382         public override int GetHashCode()
   1383         {
   1384             return origString.GetHashCode() ^ blankSpaceChar.GetHashCode();
   1385         }
   1386 
   1387         /// <summary>
   1388         /// Get ASCII-Like string with string-terminators, example: "Line1 \0\0 Line2 \0\0\0".
   1389         /// </summary>
   1390         /// <returns>String with replaced string-terminators to "\0".</returns>
   1391         public override string ToString()
   1392         {
   1393             return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0");
   1394         }
   1395 
   1396         /// <summary>
   1397         /// Get ASCII-Like string to first string-terminator, example:
   1398         /// "Some string \0 Some Data \0" - > "Some string".
   1399         /// </summary>
   1400         /// <returns>String to first string-terminator.</returns>
   1401         public string GetString()
   1402         {
   1403             String Str = Encoding.ASCII.GetString(ToBytes());
   1404             for (int i = 1; i < 4; i++)
   1405                 Str = Str.Insert((StrNLen + 1) * i + i - 1, "\n");
   1406             return Str.Replace("\0", String.Empty).TrimEnd(new char[] { '\n' });
   1407         }
   1408 
   1409         /// <summary>
   1410         /// Convert <see cref="TgaComment"/> to byte array.
   1411         /// </summary>
   1412         /// <returns>Byte array, every byte is ASCII symbol.</returns>
   1413         public byte[] ToBytes()
   1414         {
   1415             return ToBytes(origString, blankSpaceChar);
   1416         }
   1417 
   1418         /// <summary>
   1419         /// Convert <see cref="TgaComment"/> to byte array.
   1420         /// </summary>
   1421         /// <param name="Str">Input string.</param>
   1422         /// <param name="BlankSpaceChar">Char for filling blank space in string.</param>
   1423         /// <returns>Byte array, every byte is ASCII symbol.</returns>
   1424         public static byte[] ToBytes(string Str, char BlankSpaceChar = '\0')
   1425         {
   1426             char[] C = new char[81 * 4];
   1427 
   1428             for (int i = 0; i < C.Length; i++)
   1429             {
   1430                 if ((i + 82) % 81 == 0)
   1431                     C[i] = TgaString.DefaultEndingChar;
   1432                 else
   1433                 {
   1434                     int Index = i - i / 81;
   1435                     C[i] = (Index < Str.Length ? Str[Index] : BlankSpaceChar);
   1436                 }
   1437             }
   1438             return Encoding.ASCII.GetBytes(C);
   1439         }
   1440     }
   1441 
   1442     public class TgaDateTime : ICloneable
   1443     {
   1444         ushort month = 0;
   1445         ushort day = 0;
   1446         ushort year = 0;
   1447         ushort hour = 0;
   1448         ushort minute = 0;
   1449         ushort second = 0;
   1450 
   1451         /// <summary>
   1452         /// Make empty <see cref="TgaDateTime"/>.
   1453         /// </summary>
   1454         public TgaDateTime()
   1455         {
   1456         }
   1457 
   1458         /// <summary>
   1459         /// Make <see cref="TgaDateTime"/> from <see cref="DateTime"/>.
   1460         /// </summary>
   1461         /// <param name="DateAndTime">Some <see cref="DateTime"/> variable.</param>
   1462         public TgaDateTime(DateTime DateAndTime)
   1463         {
   1464             month = (ushort)DateAndTime.Month;
   1465             day = (ushort)DateAndTime.Day;
   1466             year = (ushort)DateAndTime.Year;
   1467             hour = (ushort)DateAndTime.Hour;
   1468             minute = (ushort)DateAndTime.Minute;
   1469             second = (ushort)DateAndTime.Second;
   1470         }
   1471 
   1472         /// <summary>
   1473         /// Make <see cref="TgaDateTime"/> from ushort values.
   1474         /// </summary>
   1475         /// <param name="Month">Month (1 - 12).</param>
   1476         /// <param name="Day">Day (1 - 31).</param>
   1477         /// <param name="Year">Year (4 digit, ie. 1989).</param>
   1478         /// <param name="Hour">Hour (0 - 23).</param>
   1479         /// <param name="Minute">Minute (0 - 59).</param>
   1480         /// <param name="Second">Second (0 - 59).</param>
   1481         public TgaDateTime(ushort Month, ushort Day, ushort Year, ushort Hour, ushort Minute, ushort Second)
   1482         {
   1483             month = Month;
   1484             day = Day;
   1485             year = Year;
   1486             hour = Hour;
   1487             minute = Minute;
   1488             second = Second;
   1489         }
   1490 
   1491         /// <summary>
   1492         /// Make <see cref="TgaDateTime"/> from bytes.
   1493         /// </summary>
   1494         /// <param name="Bytes">Array of bytes(byte[12]).</param>
   1495         public TgaDateTime(byte[] Bytes)
   1496         {
   1497             if (Bytes == null)
   1498                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   1499             else if (Bytes.Length != Size)
   1500                 throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!");
   1501 
   1502             month = BitConverter.ToUInt16(Bytes, 0);
   1503             day = BitConverter.ToUInt16(Bytes, 2);
   1504             year = BitConverter.ToUInt16(Bytes, 4);
   1505             hour = BitConverter.ToUInt16(Bytes, 6);
   1506             minute = BitConverter.ToUInt16(Bytes, 8);
   1507             second = BitConverter.ToUInt16(Bytes, 10);
   1508         }
   1509 
   1510         /// <summary>
   1511         /// Gets or Sets month (1 - 12).
   1512         /// </summary>
   1513         public ushort Month
   1514         {
   1515             get { return month; }
   1516             set { month = value; }
   1517         }
   1518 
   1519         /// <summary>
   1520         /// Gets or Sets day (1 - 31).
   1521         /// </summary>
   1522         public ushort Day
   1523         {
   1524             get { return day; }
   1525             set { day = value; }
   1526         }
   1527 
   1528         /// <summary>
   1529         /// Gets or Sets year (4 digit, ie. 1989).
   1530         /// </summary>
   1531         public ushort Year
   1532         {
   1533             get { return year; }
   1534             set { year = value; }
   1535         }
   1536 
   1537         /// <summary>
   1538         /// Gets or Sets hour (0 - 23).
   1539         /// </summary>
   1540         public ushort Hour
   1541         {
   1542             get { return hour; }
   1543             set { hour = value; }
   1544         }
   1545 
   1546         /// <summary>
   1547         /// Gets or Sets minute (0 - 59).
   1548         /// </summary>
   1549         public ushort Minute
   1550         {
   1551             get { return minute; }
   1552             set { minute = value; }
   1553         }
   1554 
   1555         /// <summary>
   1556         /// Gets or Sets second (0 - 59).
   1557         /// </summary>
   1558         public ushort Second
   1559         {
   1560             get { return second; }
   1561             set { second = value; }
   1562         }
   1563 
   1564         /// <summary>
   1565         /// Gets TGA Field size in bytes.
   1566         /// </summary>
   1567         public const int Size = 12;
   1568 
   1569         /// <summary>
   1570         /// Make full independed copy of <see cref="TgaDateTime"/>.
   1571         /// </summary>
   1572         /// <returns>Copy of <see cref="TgaDateTime"/></returns>
   1573         public TgaDateTime Clone()
   1574         {
   1575             return new TgaDateTime(month, day, year, hour, minute, second);
   1576         }
   1577 
   1578         /// <summary>
   1579         /// Make full independed copy of <see cref="TgaDateTime"/>.
   1580         /// </summary>
   1581         /// <returns>Copy of <see cref="TgaDateTime"/></returns>
   1582         object ICloneable.Clone()
   1583         {
   1584             return Clone();
   1585         }
   1586 
   1587         public override bool Equals(object obj)
   1588         {
   1589             return ((obj is TgaDateTime) ? Equals((TgaDateTime)obj) : false);
   1590         }
   1591 
   1592         public bool Equals(TgaDateTime item)
   1593         {
   1594             return (
   1595                 month == item.month &&
   1596                 day == item.day &&
   1597                 year == item.year &&
   1598                 hour == item.hour &&
   1599                 minute == item.minute &&
   1600                 second == item.second);
   1601         }
   1602 
   1603         public static bool operator ==(TgaDateTime item1, TgaDateTime item2)
   1604         {
   1605             if (ReferenceEquals(item1, null))
   1606                 return ReferenceEquals(item2, null);
   1607 
   1608             if (ReferenceEquals(item2, null))
   1609                 return ReferenceEquals(item1, null);
   1610 
   1611             return item1.Equals(item2);
   1612         }
   1613 
   1614         public static bool operator !=(TgaDateTime item1, TgaDateTime item2)
   1615         {
   1616             return !(item1 == item2);
   1617         }
   1618 
   1619         public override int GetHashCode()
   1620         {
   1621             unchecked
   1622             {
   1623                 int hash = 17;
   1624                 hash = hash * 23 + (month << 16 | hour).GetHashCode();
   1625                 hash = hash * 23 + (day << 16 | minute).GetHashCode();
   1626                 hash = hash * 23 + (year << 16 | second).GetHashCode();
   1627                 return hash;
   1628             }
   1629         }
   1630 
   1631         /// <summary>
   1632         /// Gets <see cref="TgaDateTime"/> like string.
   1633         /// </summary>
   1634         /// <returns>String in "1990.01.23 1:02:03" format.</returns>
   1635         public override string ToString()
   1636         {
   1637             return String.Format("{0:D4}.{1:D2}.{2:D2} {3}:{4:D2}:{5:D2}", year, month, day, hour, minute, second);
   1638         }
   1639 
   1640         /// <summary>
   1641         /// Convert <see cref="TgaDateTime"/> to byte array.
   1642         /// </summary>
   1643         /// <returns>Byte array with length = 12.</returns>
   1644         public byte[] ToBytes()
   1645         {
   1646             return BitConverterExt.ToBytes(month, day, year, hour, minute, second);
   1647         }
   1648 
   1649         /// <summary>
   1650         /// Gets <see cref="TgaDateTime"/> like <see cref="DateTime"/>.
   1651         /// </summary>
   1652         /// <returns><see cref="DateTime"/> value of <see cref="TgaDateTime"/>.</returns>
   1653         public DateTime ToDateTime()
   1654         {
   1655             return new DateTime(year, month, day, hour, minute, second);
   1656         }
   1657     }
   1658 
   1659     public class TgaDevEntry : ICloneable
   1660     {
   1661         // Directory
   1662         ushort fieldTag = 0;
   1663         uint fieldFileOffset = 0;
   1664         // Field
   1665         byte[] data = null;
   1666 
   1667         /// <summary>
   1668         /// Make empty <see cref="TgaDevEntry"/>.
   1669         /// </summary>
   1670         public TgaDevEntry()
   1671         {
   1672         }
   1673 
   1674         /// <summary>
   1675         /// Make <see cref="TgaDevEntry"/> from other <see cref="TgaDevEntry"/>.
   1676         /// </summary>
   1677         /// <param name="Entry">Some <see cref="TgaDevEntry"/> variable.</param>
   1678         public TgaDevEntry(TgaDevEntry Entry)
   1679         {
   1680             if (Entry == null)
   1681                 throw new ArgumentNullException();
   1682 
   1683             fieldTag = Entry.fieldTag;
   1684             fieldFileOffset = Entry.fieldFileOffset;
   1685             data = BitConverterExt.ToBytes(Entry.data);
   1686         }
   1687 
   1688         /// <summary>
   1689         /// Make <see cref="TgaDevEntry"/> from <see cref="Tag"/>, <see cref="Offset"/> and <see cref="FieldSize"/>.
   1690         /// </summary>
   1691         /// <param name="Tag">TAG ID (0 - 65535). See <see cref="Tag"/>.</param>
   1692         /// <param name="Offset">TAG file offset in bytes. See <see cref="Offset"/>.</param>
   1693         /// <param name="Data">This is DevEntry Field Data. See <see cref="Data"/>.</param>
   1694         public TgaDevEntry(ushort Tag, uint Offset, byte[] Data = null)
   1695         {
   1696             fieldTag = Tag;
   1697             fieldFileOffset = Offset;
   1698             data = Data;
   1699         }
   1700 
   1701         /// <summary>
   1702         /// Make <see cref="TgaDevEntry"/> from bytes.
   1703         /// </summary>
   1704         /// <param name="Bytes">Array of bytes(byte[6] or bigger, if <see cref="Data"/> exist).</param>
   1705         public TgaDevEntry(byte[] Bytes)
   1706         {
   1707             if (Bytes == null)
   1708                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   1709             else if (Bytes.Length < 6)
   1710                 throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be >= 6!");
   1711 
   1712             fieldTag = BitConverter.ToUInt16(Bytes, 0);
   1713             fieldFileOffset = BitConverter.ToUInt32(Bytes, 2);
   1714 
   1715             if (Bytes.Length > 6)
   1716                 data = BitConverterExt.GetElements(Bytes, 6, Bytes.Length - 6);
   1717         }
   1718 
   1719         /// <summary>
   1720         /// Each TAG is a value in the range of 0 to 65535. Values from 0 - 32767 are available for developer use,
   1721         /// while values from 32768 - 65535 are reserved for Truevision.
   1722         /// </summary>
   1723         public ushort Tag
   1724         {
   1725             get { return fieldTag; }
   1726             set { fieldTag = value; }
   1727         }
   1728 
   1729         /// <summary>
   1730         /// This OFFSET is a number of bytes from the beginning of the file to the start of the field
   1731         /// referenced by the tag.
   1732         /// </summary>
   1733         public uint Offset
   1734         {
   1735             get { return fieldFileOffset; }
   1736             set { fieldFileOffset = value; }
   1737         }
   1738 
   1739         /// <summary>
   1740         /// Field DATA.
   1741         /// Although the size and format of the actual Developer Area fields are totally up to the developer,
   1742         /// please define your formats to address future considerations you might have concerning your fields.
   1743         /// This means that if you anticipate changing a field, build flexibility into the format to make these
   1744         /// changes easy on other developers.Major changes to an existing TAG’s definition should never happen.
   1745         /// </summary>
   1746         public byte[] Data
   1747         {
   1748             get { return data; }
   1749             set { data = value; }
   1750         }
   1751 
   1752         /// <summary>
   1753         /// The FIELD SIZE is a number of bytes in the field. Same like: <see cref="Data.Length"/>,
   1754         /// if <see cref="Data"/> is null, return -1.
   1755         /// </summary>
   1756         public int FieldSize
   1757         {
   1758             get
   1759             {
   1760                 if (Data == null)
   1761                     return -1;
   1762 
   1763                 return Data.Length;
   1764             }
   1765         }
   1766 
   1767         /// <summary>
   1768         /// Gets TGA <see cref="TgaDevEntry"/> size in bytes (Always constant and equal 10!).
   1769         /// It is not <see cref="FieldSize"/>! It is just size of entry sizeof(ushort + uint + uint).
   1770         /// </summary>
   1771         public const int Size = 10;
   1772 
   1773         /// <summary>
   1774         /// Make full independed copy of <see cref="TgaDevEntry"/>.
   1775         /// </summary>
   1776         /// <returns>Copy of <see cref="TgaDevEntry"/></returns>
   1777         public TgaDevEntry Clone()
   1778         {
   1779             return new TgaDevEntry(this);
   1780         }
   1781 
   1782         /// <summary>
   1783         /// Make full independed copy of <see cref="TgaDevEntry"/>.
   1784         /// </summary>
   1785         /// <returns>Copy of <see cref="TgaDevEntry"/></returns>
   1786         object ICloneable.Clone()
   1787         {
   1788             return Clone();
   1789         }
   1790 
   1791         public override bool Equals(object obj)
   1792         {
   1793             return ((obj is TgaDevEntry) ? Equals((TgaDevEntry)obj) : false);
   1794         }
   1795 
   1796         public bool Equals(TgaDevEntry item)
   1797         {
   1798             return (fieldTag == item.fieldTag &&
   1799                 fieldFileOffset == item.fieldFileOffset &&
   1800                 BitConverterExt.IsArraysEqual(data, item.data));
   1801         }
   1802 
   1803         public static bool operator ==(TgaDevEntry item1, TgaDevEntry item2)
   1804         {
   1805             if (ReferenceEquals(item1, null))
   1806                 return ReferenceEquals(item2, null);
   1807 
   1808             if (ReferenceEquals(item2, null))
   1809                 return ReferenceEquals(item1, null);
   1810 
   1811             return item1.Equals(item2);
   1812         }
   1813 
   1814         public static bool operator !=(TgaDevEntry item1, TgaDevEntry item2)
   1815         {
   1816             return !(item1 == item2);
   1817         }
   1818 
   1819         public override int GetHashCode()
   1820         {
   1821             unchecked
   1822             {
   1823                 int hash = 17;
   1824                 hash = hash * 23 + fieldTag.GetHashCode();
   1825                 hash = hash * 23 + fieldFileOffset.GetHashCode();
   1826 
   1827                 if (data != null)
   1828                     for (int i = 0; i < data.Length; i++)
   1829                         hash = hash * 23 + data[i].GetHashCode();
   1830 
   1831                 return hash;
   1832             }
   1833         }
   1834 
   1835         /// <summary>
   1836         /// Gets <see cref="TgaDevEntry"/> like string.
   1837         /// </summary>
   1838         /// <returns>String in "Tag={0}, Offset={1}, FieldSize={2}" format.</returns>
   1839         public override string ToString()
   1840         {
   1841             return String.Format("{0}={1}, {1}={2}, {3}={4}", nameof(Tag), fieldTag,
   1842                 nameof(Offset), fieldFileOffset, nameof(FieldSize), FieldSize);
   1843         }
   1844 
   1845         /// <summary>
   1846         /// Convert <see cref="TgaDevEntry"/> to byte array. (Not include <see cref="Data"/>!).
   1847         /// </summary>
   1848         /// <returns>Byte array with length = 10.</returns>
   1849         public byte[] ToBytes()
   1850         {
   1851             return BitConverterExt.ToBytes(fieldTag, fieldFileOffset, (data == null ? 0 : data.Length));
   1852         }
   1853     } //Not full ToBytes()
   1854 
   1855     public class TgaFraction : ICloneable
   1856     {
   1857         ushort numerator = 0;
   1858         ushort denominator = 0;
   1859 
   1860         /// <summary>
   1861         /// Make <see cref="TgaFraction"/> from <see cref="Numerator"/> and <see cref="Denominator"/>.
   1862         /// </summary>
   1863         /// <param name="Numerator">Numerator value.</param>
   1864         /// <param name="Denominator">Denominator value.</param>
   1865         public TgaFraction(ushort Numerator = 0, ushort Denominator = 0)
   1866         {
   1867             numerator = Numerator;
   1868             denominator = Denominator;
   1869         }
   1870 
   1871         /// <summary>
   1872         /// Make <see cref="TgaFraction"/> from bytes.
   1873         /// </summary>
   1874         /// <param name="Bytes">Array of bytes(byte[4]).</param>
   1875         public TgaFraction(byte[] Bytes)
   1876         {
   1877             if (Bytes == null)
   1878                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   1879             if (Bytes.Length != Size)
   1880                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   1881 
   1882             numerator = BitConverter.ToUInt16(Bytes, 0);
   1883             denominator = BitConverter.ToUInt16(Bytes, 2);
   1884         }
   1885 
   1886         /// <summary>
   1887         /// Gets or sets numerator value.
   1888         /// </summary>
   1889         public ushort Numerator
   1890         {
   1891             get { return numerator; }
   1892             set { numerator = value; }
   1893         }
   1894 
   1895         /// <summary>
   1896         /// Gets or sets denominator value.
   1897         /// </summary>
   1898         public ushort Denominator
   1899         {
   1900             get { return denominator; }
   1901             set { denominator = value; }
   1902         }
   1903 
   1904         /// <summary>
   1905         /// Get aspect ratio = <see cref="Numerator"/> / <see cref="Denominator"/>.
   1906         /// </summary>
   1907         public float AspectRatio
   1908         {
   1909             get
   1910             {
   1911                 if (numerator == denominator)
   1912                     return 1f;
   1913 
   1914                 return numerator / (float)denominator;
   1915             }
   1916         }
   1917 
   1918         /// <summary>
   1919         /// Gets Empty <see cref="TgaFraction"/>, all values are 0.
   1920         /// </summary>
   1921         public static readonly TgaFraction Empty = new TgaFraction();
   1922 
   1923         /// <summary>
   1924         /// Gets One <see cref="TgaFraction"/>, all values are 1 (ones, 1 / 1 = 1).
   1925         /// </summary>
   1926         public static readonly TgaFraction One = new TgaFraction(1, 1);
   1927 
   1928         /// <summary>
   1929         /// Gets TGA Field size in bytes.
   1930         /// </summary>
   1931         public const int Size = 4;
   1932 
   1933         /// <summary>
   1934         /// Make full independed copy of <see cref="TgaFraction"/>.
   1935         /// </summary>
   1936         /// <returns>Copy of <see cref="TgaFraction"/></returns>
   1937         public TgaFraction Clone()
   1938         {
   1939             return new TgaFraction(numerator, denominator);
   1940         }
   1941 
   1942         /// <summary>
   1943         /// Make full independed copy of <see cref="TgaFraction"/>.
   1944         /// </summary>
   1945         /// <returns>Copy of <see cref="TgaFraction"/></returns>
   1946         object ICloneable.Clone()
   1947         {
   1948             return Clone();
   1949         }
   1950 
   1951         public override bool Equals(object obj)
   1952         {
   1953             return ((obj is TgaFraction) ? Equals((TgaFraction)obj) : false);
   1954         }
   1955 
   1956         public bool Equals(TgaFraction item)
   1957         {
   1958             return (numerator == item.numerator && denominator == item.denominator);
   1959         }
   1960 
   1961         public static bool operator ==(TgaFraction item1, TgaFraction item2)
   1962         {
   1963             if (ReferenceEquals(item1, null))
   1964                 return ReferenceEquals(item2, null);
   1965 
   1966             if (ReferenceEquals(item2, null))
   1967                 return ReferenceEquals(item1, null);
   1968 
   1969             return item1.Equals(item2);
   1970         }
   1971 
   1972         public static bool operator !=(TgaFraction item1, TgaFraction item2)
   1973         {
   1974             return !(item1 == item2);
   1975         }
   1976 
   1977         public override int GetHashCode()
   1978         {
   1979             return (numerator << 16 | denominator).GetHashCode();
   1980         }
   1981 
   1982         /// <summary>
   1983         /// Gets <see cref="TgaFraction"/> like string.
   1984         /// </summary>
   1985         /// <returns>String in "Numerator=1, Denominator=2" format.</returns>
   1986         public override string ToString()
   1987         {
   1988             return String.Format("{0}={1}, {2}={3}", nameof(Numerator), numerator,
   1989                 nameof(Denominator), denominator);
   1990         }
   1991 
   1992         /// <summary>
   1993         /// Convert <see cref="TgaFraction"/> to byte array.
   1994         /// </summary>
   1995         /// <returns>Byte array with length = 4.</returns>
   1996         public byte[] ToBytes()
   1997         {
   1998             return BitConverterExt.ToBytes(numerator, denominator);
   1999         }
   2000     }
   2001 
   2002     /// <summary>
   2003     /// Contains image origin bits and alpha channel bits(or number of overlay bits)
   2004     /// </summary>
   2005     public class TgaImageDescriptor : ICloneable
   2006     {
   2007         TgaImgOrigin imageOrigin = 0; //bits 5-4
   2008         byte alphaChannelBits = 0; //bits 3-0
   2009 
   2010         /// <summary>
   2011         /// Make empty <see cref="TgaImageDescriptor"/>.
   2012         /// </summary>
   2013         public TgaImageDescriptor()
   2014         {
   2015         }
   2016 
   2017         /// <summary>
   2018         /// Make <see cref="TgaImageDescriptor"/> from bytes.
   2019         /// </summary>
   2020         /// <param name="b">ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for
   2021         /// <see cref="ImageOrigin"/>, 3-0 used as alpha channel bits or number of overlay bits.</param>
   2022         public TgaImageDescriptor(byte b)
   2023         {
   2024             imageOrigin = (TgaImgOrigin)((b & 0x30) >> 4);
   2025             alphaChannelBits = (byte)(b & 0x0F);
   2026         }
   2027 
   2028         /// <summary>
   2029         /// Gets or Sets Image Origin bits (select from enum only, don'n use 5-4 bits!).
   2030         /// </summary>
   2031         public TgaImgOrigin ImageOrigin
   2032         {
   2033             get { return imageOrigin; }
   2034             set { imageOrigin = value; }
   2035         }
   2036 
   2037         /// <summary>
   2038         /// Gets or Sets alpha channel bits or number of overlay bits.
   2039         /// </summary>
   2040         public byte AlphaChannelBits
   2041         {
   2042             get { return alphaChannelBits; }
   2043             set { alphaChannelBits = value; }
   2044         }
   2045 
   2046         /// <summary>
   2047         /// Gets TGA Field size in bytes.
   2048         /// </summary>
   2049         public const int Size = 1;
   2050 
   2051         /// <summary>
   2052         /// Make full copy of <see cref="TgaImageDescriptor"/>.
   2053         /// </summary>
   2054         /// <returns>Full independent copy of <see cref="TgaImageDescriptor"/>.</returns>
   2055         public TgaImageDescriptor Clone()
   2056         {
   2057             return new TgaImageDescriptor(ToByte());
   2058         }
   2059 
   2060         /// <summary>
   2061         /// Make full copy of <see cref="TgaImageDescriptor"/>.
   2062         /// </summary>
   2063         /// <returns>Full independent copy of <see cref="TgaImageDescriptor"/>.</returns>
   2064         object ICloneable.Clone()
   2065         {
   2066             return Clone();
   2067         }
   2068 
   2069         public override bool Equals(object obj)
   2070         {
   2071             return ((obj is TgaImageDescriptor) ? Equals((TgaImageDescriptor)obj) : false);
   2072         }
   2073 
   2074         public bool Equals(TgaImageDescriptor item)
   2075         {
   2076             return (imageOrigin == item.imageOrigin && alphaChannelBits == item.alphaChannelBits);
   2077         }
   2078 
   2079         public static bool operator ==(TgaImageDescriptor item1, TgaImageDescriptor item2)
   2080         {
   2081             if (ReferenceEquals(item1, null))
   2082                 return ReferenceEquals(item2, null);
   2083 
   2084             if (ReferenceEquals(item2, null))
   2085                 return ReferenceEquals(item1, null);
   2086 
   2087             return item1.Equals(item2);
   2088         }
   2089 
   2090         public static bool operator !=(TgaImageDescriptor item1, TgaImageDescriptor item2)
   2091         {
   2092             return !(item1 == item2);
   2093         }
   2094 
   2095         public override int GetHashCode()
   2096         {
   2097             unchecked
   2098             {
   2099                 return ((int)ImageOrigin << 4 | alphaChannelBits).GetHashCode();
   2100             }
   2101         }
   2102 
   2103         public override string ToString()
   2104         {
   2105             return String.Format("{0}={1}, {2}={3}, ImageDescriptor_AsByte={4}", nameof(ImageOrigin),
   2106                 imageOrigin, nameof(AlphaChannelBits), alphaChannelBits, ToByte());
   2107         }
   2108 
   2109         /// <summary>
   2110         /// Gets ImageDescriptor byte.
   2111         /// </summary>
   2112         /// <returns>ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for imageOrigin,
   2113         /// 3-0 used as alpha channel bits or number of overlay bits.</returns>
   2114         public byte ToByte()
   2115         {
   2116             return (byte)(((int)imageOrigin << 4) | alphaChannelBits);
   2117         }
   2118     }
   2119 
   2120     /// <summary>
   2121     /// Image Specification - Field 5 (10 bytes):
   2122     /// <para>This field and its sub-fields describe the image screen location, size and pixel depth.
   2123     /// These information is always written to the file.</para>
   2124     /// </summary>
   2125     public class TgaImageSpec : ICloneable
   2126     {
   2127         ushort x_Origin = 0;
   2128         ushort y_Origin = 0;
   2129         ushort imageWidth = 0;
   2130         ushort imageHeight = 0;
   2131         TgaPixelDepth pixelDepth = TgaPixelDepth.Other;
   2132         TgaImageDescriptor imageDescriptor = new TgaImageDescriptor();
   2133 
   2134         public TgaImageSpec()
   2135         {
   2136         }
   2137 
   2138         /// <summary>
   2139         /// Make ImageSpec from values.
   2140         /// </summary>
   2141         /// <param name="X_Origin">These specify the absolute horizontal coordinate for the lower
   2142         /// left corner of the image as it is positioned on a display device having an origin at
   2143         /// the lower left of the screen(e.g., the TARGA series).</param>
   2144         /// <param name="Y_Origin">These specify the absolute vertical coordinate for the lower
   2145         /// left corner of the image as it is positioned on a display device having an origin at
   2146         /// the lower left of the screen(e.g., the TARGA series).</param>
   2147         /// <param name="ImageWidth">This field specifies the width of the image in pixels.</param>
   2148         /// <param name="ImageHeight">This field specifies the height of the image in pixels.</param>
   2149         /// <param name="PixelDepth">This field indicates the number of bits per pixel. This number
   2150         /// includes the Attribute or Alpha channel bits. Common values are 8, 16, 24 and 32 but
   2151         /// other pixel depths could be used.</param>
   2152         /// <param name="ImageDescriptor">Contains image origin bits and alpha channel bits
   2153         /// (or number of overlay bits).</param>
   2154         public TgaImageSpec(ushort X_Origin, ushort Y_Origin, ushort ImageWidth, ushort ImageHeight,
   2155             TgaPixelDepth PixelDepth, TgaImageDescriptor ImageDescriptor)
   2156         {
   2157             x_Origin = X_Origin;
   2158             y_Origin = Y_Origin;
   2159             imageWidth = ImageWidth;
   2160             imageHeight = ImageHeight;
   2161             pixelDepth = PixelDepth;
   2162             imageDescriptor = ImageDescriptor;
   2163         }
   2164 
   2165         /// <summary>
   2166         /// Make ImageSpec from bytes.
   2167         /// </summary>
   2168         /// <param name="Bytes">Array of bytes(byte[10]).</param>
   2169         public TgaImageSpec(byte[] Bytes)
   2170         {
   2171             if (Bytes == null)
   2172                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   2173             if (Bytes.Length != Size)
   2174                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   2175 
   2176             x_Origin = BitConverter.ToUInt16(Bytes, 0);
   2177             y_Origin = BitConverter.ToUInt16(Bytes, 2);
   2178             imageWidth = BitConverter.ToUInt16(Bytes, 4);
   2179             imageHeight = BitConverter.ToUInt16(Bytes, 6);
   2180             pixelDepth = (TgaPixelDepth)Bytes[8];
   2181             imageDescriptor = new TgaImageDescriptor(Bytes[9]);
   2182         }
   2183 
   2184         /// <summary>
   2185         /// These specify the absolute horizontal coordinate for the lower left corner of the image
   2186         /// as it is positioned on a display device having an origin at the lower left of the
   2187         /// screen(e.g., the TARGA series).
   2188         /// </summary>
   2189         public ushort X_Origin
   2190         {
   2191             get { return x_Origin; }
   2192             set { x_Origin = value; }
   2193         }
   2194 
   2195         /// <summary>
   2196         /// These specify the absolute vertical coordinate for the lower left corner of the image
   2197         /// as it is positioned on a display device having an origin at the lower left of the
   2198         /// screen(e.g., the TARGA series).
   2199         /// </summary>
   2200         public ushort Y_Origin
   2201         {
   2202             get { return y_Origin; }
   2203             set { y_Origin = value; }
   2204         }
   2205 
   2206         /// <summary>
   2207         /// This field specifies the width of the image in pixels.
   2208         /// </summary>
   2209         public ushort ImageWidth
   2210         {
   2211             get { return imageWidth; }
   2212             set { imageWidth = value; }
   2213         }
   2214 
   2215         /// <summary>
   2216         /// This field specifies the height of the image in pixels.
   2217         /// </summary>
   2218         public ushort ImageHeight
   2219         {
   2220             get { return imageHeight; }
   2221             set { imageHeight = value; }
   2222         }
   2223 
   2224         /// <summary>
   2225         /// This field indicates the number of bits per pixel. This number includes the Attribute or
   2226         /// Alpha channel bits. Common values are 8, 16, 24 and 32 but other pixel depths could be used.
   2227         /// </summary>
   2228         public TgaPixelDepth PixelDepth
   2229         {
   2230             get { return pixelDepth; }
   2231             set { pixelDepth = value; }
   2232         }
   2233 
   2234         /// <summary>
   2235         /// Contains image origin bits and alpha channel bits(or number of overlay bits).
   2236         /// </summary>
   2237         public TgaImageDescriptor ImageDescriptor
   2238         {
   2239             get { return imageDescriptor; }
   2240             set { imageDescriptor = value; }
   2241         }
   2242 
   2243         /// <summary>
   2244         /// Gets TGA Field size in bytes.
   2245         /// </summary>
   2246         public const int Size = 10;
   2247 
   2248         /// <summary>
   2249         /// Make full copy of <see cref="TgaImageDescriptor"/>.
   2250         /// </summary>
   2251         /// <returns></returns>
   2252         public TgaImageSpec Clone()
   2253         {
   2254             return new TgaImageSpec(ToBytes());
   2255         }
   2256 
   2257         /// <summary>
   2258         /// Make full copy of <see cref="TgaImageDescriptor"/>.
   2259         /// </summary>
   2260         /// <returns></returns>
   2261         object ICloneable.Clone()
   2262         {
   2263             return Clone();
   2264         }
   2265 
   2266         public override bool Equals(object obj)
   2267         {
   2268             return ((obj is TgaImageSpec) ? Equals((TgaImageSpec)obj) : false);
   2269         }
   2270 
   2271         public bool Equals(TgaImageSpec item)
   2272         {
   2273             return (
   2274                 x_Origin == item.x_Origin &&
   2275                 y_Origin == item.y_Origin &&
   2276                 imageWidth == item.imageWidth &&
   2277                 imageHeight == item.imageHeight &&
   2278                 pixelDepth == item.pixelDepth &&
   2279                 imageDescriptor == item.imageDescriptor);
   2280         }
   2281 
   2282         public static bool operator ==(TgaImageSpec item1, TgaImageSpec item2)
   2283         {
   2284             if (ReferenceEquals(item1, null))
   2285                 return ReferenceEquals(item2, null);
   2286 
   2287             if (ReferenceEquals(item2, null))
   2288                 return ReferenceEquals(item1, null);
   2289 
   2290             return item1.Equals(item2);
   2291         }
   2292 
   2293         public static bool operator !=(TgaImageSpec item1, TgaImageSpec item2)
   2294         {
   2295             return !(item1 == item2);
   2296         }
   2297 
   2298         public override int GetHashCode()
   2299         {
   2300             unchecked
   2301             {
   2302                 int hash = 17;
   2303                 hash = hash * 23 + x_Origin.GetHashCode();
   2304                 hash = hash * 23 + y_Origin.GetHashCode();
   2305                 hash = hash * 23 + imageWidth.GetHashCode();
   2306                 hash = hash * 23 + imageHeight.GetHashCode();
   2307                 hash = hash * 23 + pixelDepth.GetHashCode();
   2308 
   2309                 if (imageDescriptor != null)
   2310                     hash = hash * 23 + imageDescriptor.GetHashCode();
   2311 
   2312                 return hash;
   2313             }
   2314         }
   2315 
   2316         public override string ToString()
   2317         {
   2318             return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}, {10}={11}",
   2319                 nameof(X_Origin), x_Origin,
   2320                 nameof(Y_Origin), y_Origin,
   2321                 nameof(ImageWidth), imageWidth,
   2322                 nameof(ImageHeight), imageHeight,
   2323                 nameof(PixelDepth), pixelDepth,
   2324                 nameof(ImageDescriptor), imageDescriptor);
   2325         }
   2326 
   2327         /// <summary>
   2328         /// Convert <see cref="TgaImageSpec"/> to byte array.
   2329         /// </summary>
   2330         /// <returns>Byte array with length = 10.</returns>
   2331         public byte[] ToBytes()
   2332         {
   2333             return BitConverterExt.ToBytes(x_Origin, y_Origin, imageWidth, imageHeight,
   2334                 (byte)pixelDepth, (imageDescriptor == null ? byte.MinValue : imageDescriptor.ToByte()));
   2335         }
   2336     }
   2337 
   2338     /// <summary>
   2339     /// Postage Stamp Image (MaxSize 64x64, uncompressed, PixelDepth like in full image).
   2340     /// </summary>
   2341     public class TgaPostageStampImage : ICloneable
   2342     {
   2343         byte width = 0;
   2344         byte height = 0;
   2345         byte[] data = null;
   2346 
   2347         public TgaPostageStampImage()
   2348         {
   2349         }
   2350 
   2351         /// <summary>
   2352         /// Make <see cref="TgaPostageStampImage"/> from bytes array.
   2353         /// </summary>
   2354         /// <param name="Bytes">Bytes array, first 2 bytes are <see cref="Width"/> and <see cref="Height"/>,
   2355         /// next bytes - image data.</param>
   2356         public TgaPostageStampImage(byte[] Bytes)
   2357         {
   2358             if (Bytes == null)
   2359                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   2360             if (Bytes.Length < 2)
   2361                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + 2 + "!");
   2362 
   2363             width = Bytes[0];
   2364             height = Bytes[1];
   2365 
   2366             if (Bytes.Length > 2)
   2367                 data = BitConverterExt.GetElements(Bytes, 2, Bytes.Length - 2);
   2368         }
   2369 
   2370         /// <summary>
   2371         /// Make <see cref="TgaPostageStampImage"/> from bytes and size.
   2372         /// </summary>
   2373         /// <param name="Width">Image Width.</param>
   2374         /// <param name="Height">Image Height.</param>
   2375         /// <param name="Bytes">Postage Stamp Image Data.</param>
   2376         public TgaPostageStampImage(byte Width, byte Height, byte[] Bytes)
   2377         {
   2378             if (Bytes == null)
   2379                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   2380 
   2381             width = Width;
   2382             height = Height;
   2383             data = Bytes;
   2384         }
   2385 
   2386         /// <summary>
   2387         /// Postage Stamp Image Data
   2388         /// </summary>
   2389         public byte[] Data
   2390         {
   2391             get { return data; }
   2392             set { data = value; }
   2393         }
   2394 
   2395         /// <summary>
   2396         /// Postage Stamp Image Width (maximum = 64).
   2397         /// </summary>
   2398         public byte Width
   2399         {
   2400             get { return width; }
   2401             set { width = value; }
   2402         }
   2403 
   2404         /// <summary>
   2405         /// Postage Stamp Image Height (maximum = 64).
   2406         /// </summary>
   2407         public byte Height
   2408         {
   2409             get { return height; }
   2410             set { height = value; }
   2411         }
   2412 
   2413         /// <summary>
   2414         /// Make full copy of <see cref="TgaPostageStampImage"/>.
   2415         /// </summary>
   2416         /// <returns>Full independent copy of <see cref="TgaPostageStampImage"/>.</returns>
   2417         public TgaPostageStampImage Clone()
   2418         {
   2419             return new TgaPostageStampImage(width, height, BitConverterExt.ToBytes(data));
   2420         }
   2421 
   2422         /// <summary>
   2423         /// Make full copy of <see cref="TgaPostageStampImage"/>.
   2424         /// </summary>
   2425         /// <returns>Full independent copy of <see cref="TgaPostageStampImage"/>.</returns>
   2426         object ICloneable.Clone()
   2427         {
   2428             return Clone();
   2429         }
   2430 
   2431         public override bool Equals(object obj)
   2432         {
   2433             return ((obj is TgaPostageStampImage) ? Equals((TgaPostageStampImage)obj) : false);
   2434         }
   2435 
   2436         public bool Equals(TgaPostageStampImage item)
   2437         {
   2438             return width == item.width && height == item.height && BitConverterExt.IsArraysEqual(data, item.data);
   2439         }
   2440 
   2441         public static bool operator ==(TgaPostageStampImage item1, TgaPostageStampImage item2)
   2442         {
   2443             if (ReferenceEquals(item1, null))
   2444                 return ReferenceEquals(item2, null);
   2445 
   2446             if (ReferenceEquals(item2, null))
   2447                 return ReferenceEquals(item1, null);
   2448 
   2449             return item1.Equals(item2);
   2450         }
   2451 
   2452         public static bool operator !=(TgaPostageStampImage item1, TgaPostageStampImage item2)
   2453         {
   2454             return !(item1 == item2);
   2455         }
   2456 
   2457         public override int GetHashCode()
   2458         {
   2459             unchecked
   2460             {
   2461                 int hash = 27;
   2462                 hash = (13 * hash) + width.GetHashCode();
   2463                 hash = (13 * hash) + height.GetHashCode();
   2464                 if (data != null)
   2465                     for (int i = 0; i < data.Length; i++)
   2466                         hash = (13 * hash) + data[i].GetHashCode();
   2467                 return hash;
   2468             }
   2469         }
   2470 
   2471         public override string ToString()
   2472         {
   2473             return String.Format("{0}={1}, {2}={3}, DataLength={4}",
   2474                 nameof(Width), width, nameof(Height), height, (data == null ? -1 : data.Length));
   2475         }
   2476 
   2477         /// <summary>
   2478         /// Convert <see cref="TgaPostageStampImage"/> to byte array.
   2479         /// </summary>
   2480         /// <returns>Byte array.</returns>
   2481         public byte[] ToBytes()
   2482         {
   2483             return BitConverterExt.ToBytes(width, height, data);
   2484         }
   2485     }
   2486 
   2487     public class TgaSoftVersion : ICloneable
   2488     {
   2489         ushort versionNumber = 0;
   2490         char versionLetter = ' ';
   2491 
   2492         /// <summary>
   2493         /// Gets Empty <see cref="TgaSoftVersion"/>, <see cref="VersionLetter"/> = ' ' (space).
   2494         /// </summary>
   2495         public TgaSoftVersion()
   2496         {
   2497         }
   2498 
   2499         /// <summary>
   2500         /// Make <see cref="TgaSoftVersion"/> from string.
   2501         /// </summary>
   2502         /// <param name="Str">Input string, example: "123d".</param>
   2503         public TgaSoftVersion(string Str)
   2504         {
   2505             if (Str == null)
   2506                 throw new ArgumentNullException();
   2507             if (Str.Length < 3 || Str.Length > 4)
   2508                 throw new ArgumentOutOfRangeException(nameof(Str.Length) + " must be equal 3 or 4!");
   2509 
   2510             bool Res = ushort.TryParse(Str.Substring(0, 3), out versionNumber);
   2511             if (Res && Str.Length == 4)
   2512                 versionLetter = Str[3];
   2513         }
   2514 
   2515         /// <summary>
   2516         /// Make <see cref="TgaSoftVersion"/> from bytes.
   2517         /// </summary>
   2518         /// <param name="Bytes">Bytes array (byte[3]).</param>
   2519         public TgaSoftVersion(byte[] Bytes)
   2520         {
   2521             if (Bytes == null)
   2522                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   2523             if (Bytes.Length != Size)
   2524                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   2525 
   2526             versionNumber = BitConverter.ToUInt16(Bytes, 0);
   2527             versionLetter = Encoding.ASCII.GetString(Bytes, 2, 1)[0];
   2528         }
   2529 
   2530         public TgaSoftVersion(ushort VersionNumber, char VersionLetter = ' ')
   2531         {
   2532             versionNumber = VersionNumber;
   2533             versionLetter = VersionLetter;
   2534         }
   2535 
   2536         public ushort VersionNumber
   2537         {
   2538             get { return versionNumber; }
   2539             set { versionNumber = value; }
   2540         }
   2541 
   2542         public char VersionLetter
   2543         {
   2544             get { return versionLetter; }
   2545             set { versionLetter = value; }
   2546         }
   2547 
   2548         /// <summary>
   2549         /// Gets TGA Field size in bytes.
   2550         /// </summary>
   2551         public const int Size = 3;
   2552 
   2553         /// <summary>
   2554         /// Make full copy of <see cref="TgaSoftVersion"/>.
   2555         /// </summary>
   2556         /// <returns></returns>
   2557         public TgaSoftVersion Clone()
   2558         {
   2559             return new TgaSoftVersion(versionNumber, versionLetter);
   2560         }
   2561 
   2562         /// <summary>
   2563         /// Make full copy of <see cref="TgaSoftVersion"/>.
   2564         /// </summary>
   2565         /// <returns></returns>
   2566         object ICloneable.Clone()
   2567         {
   2568             return Clone();
   2569         }
   2570 
   2571         public override bool Equals(object obj)
   2572         {
   2573             return ((obj is TgaSoftVersion) ? Equals((TgaSoftVersion)obj) : false);
   2574         }
   2575 
   2576         public bool Equals(TgaSoftVersion item)
   2577         {
   2578             return (versionNumber == item.versionNumber && versionLetter == item.versionLetter);
   2579         }
   2580 
   2581         public static bool operator ==(TgaSoftVersion item1, TgaSoftVersion item2)
   2582         {
   2583             if (ReferenceEquals(item1, null))
   2584                 return ReferenceEquals(item2, null);
   2585 
   2586             if (ReferenceEquals(item2, null))
   2587                 return ReferenceEquals(item1, null);
   2588 
   2589             return item1.Equals(item2);
   2590         }
   2591 
   2592         public static bool operator !=(TgaSoftVersion item1, TgaSoftVersion item2)
   2593         {
   2594             return !(item1 == item2);
   2595         }
   2596 
   2597         public override int GetHashCode()
   2598         {
   2599             return versionNumber.GetHashCode() ^ versionLetter.GetHashCode();
   2600         }
   2601 
   2602         public override string ToString()
   2603         {
   2604             return (versionNumber.ToString("000") + versionLetter).TrimEnd(new char[] { ' ', '\0' });
   2605         }
   2606 
   2607         /// <summary>
   2608         /// Convert <see cref="TgaSoftVersion"/> to byte array.
   2609         /// </summary>
   2610         /// <returns>Byte array, <see cref="VersionNumber"/> (2 bytes) and
   2611         /// <see cref="VersionLetter"/> (ASCII symbol).</returns>
   2612         public byte[] ToBytes()
   2613         {
   2614             return ToBytes(versionNumber, versionLetter);
   2615         }
   2616 
   2617         /// <summary>
   2618         /// Convert <see cref="TgaSoftVersion"/> to byte array.
   2619         /// </summary>
   2620         /// <param name="VersionNumber">Set 123 for 1.23 version.</param>
   2621         /// <param name="VersionLetter">Version letter, example: for 'a' - "1.23a".</param>
   2622         /// <returns>Byte array, <see cref="VersionNumber"/> (2 bytes) and <see cref="VersionLetter"/> (ASCII symbol).</returns>
   2623         public static byte[] ToBytes(ushort VersionNumber, char VersionLetter = ' ')
   2624         {
   2625             return BitConverterExt.ToBytes(VersionNumber, Encoding.ASCII.GetBytes(VersionLetter.ToString()));
   2626         }
   2627     }
   2628 
   2629     /// <summary>
   2630     /// Use it for working with ASCII strings in TGA files.
   2631     /// </summary>
   2632     public class TgaString : ICloneable
   2633     {
   2634         public const string XFileSignatuteConst = "TRUEVISION-XFILE";
   2635         public const string DotSymbolConst = ".";
   2636 
   2637         string origString = String.Empty;
   2638         int length = 0;
   2639         char blankSpaceChar = DefaultBlankSpaceChar;
   2640         bool useEnding = false;
   2641 
   2642         public TgaString(bool UseEnding = false)
   2643         {
   2644             useEnding = UseEnding;
   2645         }
   2646 
   2647         public TgaString(byte[] Bytes, bool UseEnding = false)
   2648         {
   2649             if (Bytes == null)
   2650                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   2651 
   2652             length = Bytes.Length;
   2653             useEnding = UseEnding;
   2654             string s = Encoding.ASCII.GetString(Bytes, 0, Bytes.Length - (useEnding ? 1 : 0));
   2655 
   2656             if (s.Length > 0)
   2657                 switch (s[s.Length - 1])
   2658                 {
   2659                     case '\0':
   2660                     case ' ':
   2661                         blankSpaceChar = s[s.Length - 1];
   2662                         origString = s.TrimEnd(new char[] { s[s.Length - 1] });
   2663                         break;
   2664                     default:
   2665                         origString = s;
   2666                         break;
   2667                 }
   2668         }
   2669 
   2670         public TgaString(int Length, bool UseEnding = false)
   2671         {
   2672             length = Length;
   2673             useEnding = UseEnding;
   2674         }
   2675 
   2676         public TgaString(string Str, int Length, bool UseEnding = false, char BlankSpaceChar = '\0')
   2677         {
   2678             if (Str == null)
   2679                 throw new ArgumentNullException(nameof(Str) + " = null!");
   2680 
   2681             origString = Str;
   2682             length = Length;
   2683             blankSpaceChar = BlankSpaceChar;
   2684             useEnding = UseEnding;
   2685         }
   2686 
   2687         public string OriginalString
   2688         {
   2689             get { return origString; }
   2690             set { origString = value; }
   2691         }
   2692 
   2693         public int Length
   2694         {
   2695             get { return length; }
   2696             set { length = value; }
   2697         }
   2698 
   2699         public char BlankSpaceChar
   2700         {
   2701             get { return blankSpaceChar; }
   2702             set { blankSpaceChar = value; }
   2703         }
   2704 
   2705         public bool UseEndingChar
   2706         {
   2707             get { return useEnding; }
   2708             set { useEnding = value; }
   2709         }
   2710 
   2711         /// <summary>
   2712         /// Gets ending char, default '\0'.
   2713         /// </summary>
   2714         public static readonly char DefaultEndingChar = '\0';
   2715 
   2716         /// <summary>
   2717         /// Gets blank space char, value = '\0'.
   2718         /// </summary>
   2719         public static readonly char DefaultBlankSpaceChar = '\0';
   2720 
   2721         /// <summary>
   2722         /// Gets Empty <see cref="TgaString"/>.
   2723         /// </summary>
   2724         public static readonly TgaString Empty = new TgaString();
   2725 
   2726         /// <summary>
   2727         /// Gets <see cref="TgaString"/> with <see cref="DefaultEndingChar"/> = '\0' and <see cref="UseEndingChar"/> = true.
   2728         /// </summary>
   2729         public static readonly TgaString ZeroTerminator = new TgaString(true);
   2730 
   2731         /// <summary>
   2732         /// Gets "." <see cref="TgaString"/> with dot (period) symbol.
   2733         /// </summary>
   2734         public static readonly TgaString DotSymbol = new TgaString(DotSymbolConst, DotSymbolConst.Length);
   2735 
   2736         /// <summary>
   2737         /// Gets "TRUEVISION-XFILE" <see cref="TgaString"/> (TGA File Format Version 2.0 signatute).
   2738         /// </summary>
   2739         public static readonly TgaString XFileSignatute = new TgaString(XFileSignatuteConst, XFileSignatuteConst.Length);
   2740 
   2741         /// <summary>
   2742         /// Make full independed copy of <see cref="TgaString"/>.
   2743         /// </summary>
   2744         /// <returns>Copy of <see cref="TgaString"/></returns>
   2745         public TgaString Clone()
   2746         {
   2747             return new TgaString(origString, length, useEnding, blankSpaceChar);
   2748         }
   2749 
   2750         /// <summary>
   2751         /// Make full independed copy of <see cref="TgaString"/>.
   2752         /// </summary>
   2753         /// <returns>Copy of <see cref="TgaString"/></returns>
   2754         object ICloneable.Clone()
   2755         {
   2756             return Clone();
   2757         }
   2758 
   2759         public override bool Equals(object obj)
   2760         {
   2761             return ((obj is TgaString) ? Equals((TgaString)obj) : false);
   2762         }
   2763 
   2764         public bool Equals(TgaString item)
   2765         {
   2766             return (
   2767                 origString == item.origString &&
   2768                 length == item.length &&
   2769                 blankSpaceChar == item.blankSpaceChar &&
   2770                 useEnding == item.useEnding);
   2771         }
   2772 
   2773         public static bool operator ==(TgaString item1, TgaString item2)
   2774         {
   2775             if (ReferenceEquals(item1, null))
   2776                 return ReferenceEquals(item2, null);
   2777 
   2778             if (ReferenceEquals(item2, null))
   2779                 return ReferenceEquals(item1, null);
   2780 
   2781             return item1.Equals(item2);
   2782         }
   2783 
   2784         public static bool operator !=(TgaString item1, TgaString item2)
   2785         {
   2786             return !(item1 == item2);
   2787         }
   2788 
   2789         public static TgaString operator +(TgaString item1, TgaString item2)
   2790         {
   2791             if (ReferenceEquals(item1, null) || ReferenceEquals(item2, null))
   2792                 throw new ArgumentNullException();
   2793 
   2794             return new TgaString(BitConverterExt.ToBytes(item1.ToBytes(), item2.ToBytes()));
   2795         }
   2796 
   2797         public override int GetHashCode()
   2798         {
   2799             unchecked
   2800             {
   2801                 int hash = 17;
   2802                 hash = hash * 23 + origString.GetHashCode();
   2803                 hash = hash * 23 + length.GetHashCode();
   2804                 hash = hash * 23 + blankSpaceChar.GetHashCode();
   2805                 hash = hash * 23 + useEnding.GetHashCode();
   2806                 return hash;
   2807             }
   2808         }
   2809 
   2810         /// <summary>
   2811         /// Get ASCII-Like string with string-terminators, example: "Some string\0\0\0\0\0".
   2812         /// </summary>
   2813         /// <returns>String with replaced string-terminators to "\0".</returns>
   2814         public override string ToString()
   2815         {
   2816             return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0");
   2817         }
   2818 
   2819         /// <summary>
   2820         /// Get ASCII-Like string to first string-terminator, example:
   2821         /// "Some string \0 Some Data \0" - > "Some string".
   2822         /// </summary>
   2823         /// <returns>String to first string-terminator.</returns>
   2824         public string GetString()
   2825         {
   2826             String Str = Encoding.ASCII.GetString(ToBytes());
   2827             int EndIndex = Str.IndexOf('\0');
   2828             if (EndIndex != -1)
   2829                 Str = Str.Substring(0, EndIndex);
   2830             return Str;
   2831         }
   2832 
   2833         /// <summary>
   2834         /// Convert <see cref="TgaString"/> to byte array.
   2835         /// </summary>
   2836         /// <returns>Byte array, every byte is ASCII symbol.</returns>
   2837         public byte[] ToBytes()
   2838         {
   2839             return ToBytes(origString, length, useEnding, blankSpaceChar);
   2840         }
   2841 
   2842         /// <summary>
   2843         /// Convert <see cref="TgaString"/> to byte array.
   2844         /// </summary>
   2845         /// <param name="str">Input string.</param>
   2846         /// <param name="Length">Length of output ASCII string with Ending char (if used).</param>
   2847         /// <param name="UseEnding">Add <see cref="EndingChr"/> to string or not?</param>
   2848         /// <param name="BlankSpaceChar">Char for filling blank space in string. If this char is '-' (only for example!),
   2849         /// for string "ABC" with <see cref="Length"/> = 7, with <see cref="UseEnding"/> = true,
   2850         /// <see cref="DefaultEndingChar"/> is '\0', result string is "ABC---\0".</param>
   2851         /// <returns>Byte array, every byte is ASCII symbol.</returns>
   2852         public static byte[] ToBytes(string str, int Length, bool UseEnding = true, char BlankSpaceChar = '\0')
   2853         {
   2854             char[] C = new char[Math.Max(Length, (UseEnding ? 1 : 0))];
   2855 
   2856             for (int i = 0; i < C.Length; i++)
   2857                 C[i] = (i < str.Length ? str[i] : BlankSpaceChar);
   2858 
   2859             if (UseEnding)
   2860                 C[C.Length - 1] = DefaultEndingChar;
   2861 
   2862             return Encoding.ASCII.GetBytes(C);
   2863         }
   2864     }
   2865 
   2866     public class TgaTime : ICloneable
   2867     {
   2868         ushort hours = 0;
   2869         ushort minutes = 0;
   2870         ushort seconds = 0;
   2871 
   2872         /// <summary>
   2873         /// Make empty <see cref="TgaTime"/>.
   2874         /// </summary>
   2875         public TgaTime()
   2876         {
   2877         }
   2878 
   2879         /// <summary>
   2880         /// Make <see cref="TgaTime"/> from <see cref="TimeSpan"/>.
   2881         /// </summary>
   2882         /// <param name="Time">Some <see cref="TimeSpan"/> variable.</param>
   2883         public TgaTime(TimeSpan Time)
   2884         {
   2885             hours = (ushort)Time.TotalHours;
   2886             minutes = (ushort)Time.Minutes;
   2887             seconds = (ushort)Time.Seconds;
   2888         }
   2889 
   2890         /// <summary>
   2891         /// Make <see cref="TgaTime"/> from ushort values.
   2892         /// </summary>
   2893         /// <param name="Hours">Hour (0 - 65535).</param>
   2894         /// <param name="Minutes">Minute (0 - 59).</param>
   2895         /// <param name="Seconds">Second (0 - 59).</param>
   2896         public TgaTime(ushort Hours, ushort Minutes, ushort Seconds)
   2897         {
   2898             hours = Hours;
   2899             minutes = Minutes;
   2900             seconds = Seconds;
   2901         }
   2902 
   2903         /// <summary>
   2904         /// Make <see cref="TgaTime"/> from bytes.
   2905         /// </summary>
   2906         /// <param name="Bytes">Array of bytes(byte[6]).</param>
   2907         public TgaTime(byte[] Bytes)
   2908         {
   2909             if (Bytes == null)
   2910                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   2911             else if (Bytes.Length != Size)
   2912                 throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!");
   2913 
   2914             hours = BitConverter.ToUInt16(Bytes, 0);
   2915             minutes = BitConverter.ToUInt16(Bytes, 2);
   2916             seconds = BitConverter.ToUInt16(Bytes, 4);
   2917         }
   2918 
   2919         /// <summary>
   2920         /// Gets or Sets hour (0 - 65535).
   2921         /// </summary>
   2922         public ushort Hours
   2923         {
   2924             get { return hours; }
   2925             set { hours = value; }
   2926         }
   2927 
   2928         /// <summary>
   2929         /// Gets or Sets minute (0 - 59).
   2930         /// </summary>
   2931         public ushort Minutes
   2932         {
   2933             get { return minutes; }
   2934             set { minutes = value; }
   2935         }
   2936 
   2937         /// <summary>
   2938         /// Gets or Sets second (0 - 59).
   2939         /// </summary>
   2940         public ushort Seconds
   2941         {
   2942             get { return seconds; }
   2943             set { seconds = value; }
   2944         }
   2945 
   2946         /// <summary>
   2947         /// Gets TGA Field size in bytes.
   2948         /// </summary>
   2949         public const int Size = 6;
   2950 
   2951         /// <summary>
   2952         /// Make full independed copy of <see cref="TgaTime"/>.
   2953         /// </summary>
   2954         /// <returns>Copy of <see cref="TgaTime"/></returns>
   2955         public TgaTime Clone()
   2956         {
   2957             return new TgaTime(hours, minutes, seconds);
   2958         }
   2959 
   2960         /// <summary>
   2961         /// Make full independed copy of <see cref="TgaTime"/>.
   2962         /// </summary>
   2963         /// <returns>Copy of <see cref="TgaTime"/></returns>
   2964         object ICloneable.Clone()
   2965         {
   2966             return Clone();
   2967         }
   2968 
   2969         public override bool Equals(object obj)
   2970         {
   2971             return ((obj is TgaTime) ? Equals((TgaTime)obj) : false);
   2972         }
   2973 
   2974         public bool Equals(TgaTime item)
   2975         {
   2976             return (hours == item.hours && minutes == item.minutes && seconds == item.seconds);
   2977         }
   2978 
   2979         public static bool operator ==(TgaTime item1, TgaTime item2)
   2980         {
   2981             if (ReferenceEquals(item1, null))
   2982                 return ReferenceEquals(item2, null);
   2983 
   2984             if (ReferenceEquals(item2, null))
   2985                 return ReferenceEquals(item1, null);
   2986 
   2987             return item1.Equals(item2);
   2988         }
   2989 
   2990         public static bool operator !=(TgaTime item1, TgaTime item2)
   2991         {
   2992             return !(item1 == item2);
   2993         }
   2994 
   2995         public override int GetHashCode()
   2996         {
   2997             unchecked
   2998             {
   2999                 int hash = 17;
   3000                 hash = hash * 23 + hours.GetHashCode();
   3001                 hash = hash * 23 + (minutes << 16 | seconds).GetHashCode();
   3002                 return hash;
   3003             }
   3004         }
   3005 
   3006         /// <summary>
   3007         /// Gets <see cref="TgaTime"/> like string.
   3008         /// </summary>
   3009         /// <returns>String in "H:M:S" format.</returns>
   3010         public override string ToString()
   3011         {
   3012             return String.Format("{0}:{1}:{2}", hours, minutes, seconds);
   3013         }
   3014 
   3015         /// <summary>
   3016         /// Convert <see cref="TgaTime"/> to byte array.
   3017         /// </summary>
   3018         /// <returns>Byte array with length = 6.</returns>
   3019         public byte[] ToBytes()
   3020         {
   3021             return BitConverterExt.ToBytes(hours, minutes, seconds);
   3022         }
   3023 
   3024         /// <summary>
   3025         /// Gets <see cref="TgaTime"/> like <see cref="TimeSpan"/>.
   3026         /// </summary>
   3027         /// <returns><see cref="TimeSpan"/> value of <see cref="TgaTime"/>.</returns>
   3028         public TimeSpan ToTimeSpan()
   3029         {
   3030             return new TimeSpan(hours, minutes, seconds);
   3031         }
   3032     }
   3033 
   3034     ////////////////////////////////////////////////////////////////////////////////////////////////
   3035 
   3036     /// <summary>
   3037     /// File Header Area (18 bytes)
   3038     /// </summary>
   3039     public class TgaHeader : ICloneable
   3040     {
   3041         byte idLength = 0;
   3042         TgaColorMapType colorMapType = TgaColorMapType.NoColorMap;
   3043         TgaImageType imageType = TgaImageType.NoImageData;
   3044         TgaColorMapSpec colorMapSpec = new TgaColorMapSpec();
   3045         TgaImageSpec imageSpec = new TgaImageSpec();
   3046 
   3047         /// <summary>
   3048         /// Make empty <see cref="TgaHeader"/>.
   3049         /// </summary>
   3050         public TgaHeader()
   3051         {
   3052         }
   3053 
   3054         /// <summary>
   3055         /// Make <see cref="TgaHeader"/> from bytes.
   3056         /// </summary>
   3057         /// <param name="Bytes">Bytes array (byte[18]).</param>
   3058         public TgaHeader(byte[] Bytes)
   3059         {
   3060             if (Bytes == null)
   3061                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   3062             if (Bytes.Length != Size)
   3063                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   3064 
   3065             idLength = Bytes[0];
   3066             colorMapType = (TgaColorMapType)Bytes[1];
   3067             imageType = (TgaImageType)Bytes[2];
   3068             colorMapSpec = new TgaColorMapSpec(BitConverterExt.GetElements(Bytes, 3, TgaColorMapSpec.Size));
   3069             imageSpec = new TgaImageSpec(BitConverterExt.GetElements(Bytes, 8, TgaImageSpec.Size));
   3070         }
   3071 
   3072         /// <summary>
   3073         /// ID Length - Field 1 (1 byte):
   3074         /// This field identifies the number of bytes contained in the <see cref="ImageID"/> Field.
   3075         /// The maximum number of characters is 255. A value of zero indicates that no Image ID
   3076         /// field is included with the image.
   3077         /// </summary>
   3078         public byte IDLength
   3079         {
   3080             get { return idLength; }
   3081             set { idLength = value; }
   3082         }
   3083 
   3084         /// <summary>
   3085         /// Color Map Type - Field 2 (1 byte):
   3086         /// This field indicates the type of color map (if any) included with the image.
   3087         /// There are currently 2 defined values for this field:
   3088         /// <para>0 - indicates that no color-map data is included with this image;</para>
   3089         /// <para>1 - indicates that a color-map is included with this image.</para>
   3090         /// </summary>
   3091         public TgaColorMapType ColorMapType
   3092         {
   3093             get { return colorMapType; }
   3094             set { colorMapType = value; }
   3095         }
   3096 
   3097         /// <summary>
   3098         /// Image Type - Field 3 (1 byte):
   3099         /// <para>The TGA File Format can be used to store Pseudo-Color, True-Color and Direct-Color images
   3100         /// of various pixel depths.</para>
   3101         /// </summary>
   3102         public TgaImageType ImageType
   3103         {
   3104             get { return imageType; }
   3105             set { imageType = value; }
   3106         }
   3107 
   3108         /// <summary>
   3109         /// Color Map Specification - Field 4 (5 bytes):
   3110         /// <para>This field and its sub-fields describe the color map (if any) used for the image.
   3111         /// If the Color Map Type field is set to zero, indicating that no color map exists, then
   3112         /// these 5 bytes should be set to zero. These bytes always must be written to the file.</para>
   3113         /// </summary>
   3114         public TgaColorMapSpec ColorMapSpec
   3115         {
   3116             get { return colorMapSpec; }
   3117             set { colorMapSpec = value; }
   3118         }
   3119 
   3120         /// <summary>
   3121         /// Image Specification - Field 5 (10 bytes):
   3122         /// <para>This field and its sub-fields describe the image screen location, size and pixel depth.
   3123         /// These information is always written to the file.</para>
   3124         /// </summary>
   3125         public TgaImageSpec ImageSpec
   3126         {
   3127             get { return imageSpec; }
   3128             set { imageSpec = value; }
   3129         }
   3130 
   3131         /// <summary>
   3132         /// Gets TGA Header Section size in bytes.
   3133         /// </summary>
   3134         public const int Size = 18;
   3135 
   3136         /// <summary>
   3137         /// Make full copy of <see cref="TgaHeader"/>.
   3138         /// </summary>
   3139         /// <returns>Full independent copy of <see cref="TgaHeader"/>.</returns>
   3140         public TgaHeader Clone()
   3141         {
   3142             return new TgaHeader(ToBytes());
   3143         }
   3144 
   3145         /// <summary>
   3146         /// Make full copy of <see cref="TgaHeader"/>.
   3147         /// </summary>
   3148         /// <returns>Full independent copy of <see cref="TgaHeader"/>.</returns>
   3149         object ICloneable.Clone()
   3150         {
   3151             return Clone();
   3152         }
   3153 
   3154         public override bool Equals(object obj)
   3155         {
   3156             return ((obj is TgaHeader) ? Equals((TgaHeader)obj) : false);
   3157         }
   3158 
   3159         public bool Equals(TgaHeader item)
   3160         {
   3161             return (idLength == item.idLength &&
   3162                 colorMapType == item.colorMapType &&
   3163                 imageType == item.imageType &&
   3164                 colorMapSpec == item.colorMapSpec &&
   3165                 imageSpec == item.imageSpec);
   3166         }
   3167 
   3168         public static bool operator ==(TgaHeader item1, TgaHeader item2)
   3169         {
   3170             if (ReferenceEquals(item1, null))
   3171                 return ReferenceEquals(item2, null);
   3172 
   3173             if (ReferenceEquals(item2, null))
   3174                 return ReferenceEquals(item1, null);
   3175 
   3176             return item1.Equals(item2);
   3177         }
   3178 
   3179         public static bool operator !=(TgaHeader item1, TgaHeader item2)
   3180         {
   3181             return !(item1 == item2);
   3182         }
   3183 
   3184         public override int GetHashCode()
   3185         {
   3186             unchecked
   3187             {
   3188                 int hash = 17;
   3189                 hash = hash * 23 + (idLength << 24 | (byte)colorMapType << 8 | (byte)imageType).GetHashCode();
   3190 
   3191                 if (colorMapSpec != null)
   3192                     hash = hash * 23 + colorMapSpec.GetHashCode();
   3193 
   3194                 if (imageSpec != null)
   3195                     hash = hash * 23 + imageSpec.GetHashCode();
   3196 
   3197                 return hash;
   3198             }
   3199         }
   3200 
   3201         public override string ToString()
   3202         {
   3203             return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}",
   3204                 nameof(IDLength), idLength,
   3205                 nameof(ColorMapType), colorMapType,
   3206                 nameof(ImageType), imageType,
   3207                 nameof(ColorMapSpec), colorMapSpec,
   3208                 nameof(ImageSpec), imageSpec);
   3209         }
   3210 
   3211         /// <summary>
   3212         /// Convert <see cref="TgaHeader"/> to byte array.
   3213         /// </summary>
   3214         /// <returns>Byte array with size equal <see cref="Size"/>.</returns>
   3215         public byte[] ToBytes()
   3216         {
   3217             return BitConverterExt.ToBytes(idLength, (byte)colorMapType, (byte)imageType,
   3218                 (colorMapSpec == null ? new byte[TgaColorMapSpec.Size] : colorMapSpec.ToBytes()),
   3219                 (imageSpec == null ? new byte[TgaImageSpec.Size] : imageSpec.ToBytes()));
   3220         }
   3221     }
   3222 
   3223     /// <summary>
   3224     /// Image Or ColorMap Area
   3225     /// </summary>
   3226     public class TgaImgOrColMap : ICloneable
   3227     {
   3228         TgaString imageID = null;
   3229         byte[] colorMapData = null;
   3230         byte[] imageData = null;
   3231 
   3232         /// <summary>
   3233         /// Make empty <see cref="TgaImgOrColMap"/>.
   3234         /// </summary>
   3235         public TgaImgOrColMap()
   3236         {
   3237         }
   3238 
   3239         /// <summary>
   3240         /// Make <see cref="TgaImgOrColMap"/> from arrays.
   3241         /// </summary>
   3242         /// <param name="ImageID">This optional field contains identifying information about the image.
   3243         /// The maximum length for this field is 255 bytes. Refer to <see cref="TgaHeader.IDLength"/>
   3244         /// for the length of this field. If field 1 is set to Zero indicating that no Image ID exists
   3245         /// then these bytes are not written to the file.</param>
   3246         /// <param name="ColorMapData">Color Map Data, see <see cref="ColorMapData"/> description.</param>
   3247         /// <param name="ImageData">Image Data, see <see cref="ImageData"/> description.</param>
   3248         public TgaImgOrColMap(TgaString ImageID, byte[] ColorMapData, byte[] ImageData)
   3249         {
   3250             imageID = ImageID;
   3251             colorMapData = ColorMapData;
   3252             imageData = ImageData;
   3253         }
   3254 
   3255         /// <summary>
   3256         /// Image ID - Field 6 (variable):
   3257         /// <para>This optional field contains identifying information about the image. The maximum length
   3258         /// for this field is 255 bytes. Refer to <see cref="TgaHeader.IDLength"/> for the length of this
   3259         /// field. If field 1 is set to Zero indicating that no Image ID exists then these bytes are not
   3260         /// written to the file. Can have text inside (ASCII).</para>
   3261         /// </summary>
   3262         public TgaString ImageID
   3263         {
   3264             get { return imageID; }
   3265             set { imageID = value; }
   3266         }
   3267 
   3268         /// <summary>
   3269         /// Color Map Data - Field 7 (variable):
   3270         /// <para>If the Color Map Type(field 2) field is set to zero indicating that no Color-Map
   3271         /// exists then this field will not be present (i.e., no bytes written to the file).</para>
   3272         /// <para>This variable-length field contains the actual color map information (LUT data).
   3273         /// Field 4.3 specifies the width in bits of each color map entry while Field 4.2 specifies
   3274         /// the number of color map entries in this field. These two fields together are used to
   3275         /// determine the number of bytes contained in field 7.</para>
   3276         /// <para>Each color map entry is stored using an integral number of bytes.The RGB specification
   3277         /// for each color map entry is stored in successive bit-fields in the multi-byte entries.
   3278         /// Each color bit-field is assumed to be MIN(Field4.3/3, 8) bits in length. If Field 4.3
   3279         /// contains 24, then each color specification is 8 bits in length; if Field 4.3 contains 32,
   3280         /// then each color specification is also 8 bits (32/3 gives 10, but 8 is smaller).
   3281         /// Unused bit(s) in the multi-byte entries are assumed to specify attribute bits. The
   3282         /// attribute bit field is often called the Alpha Channel, Overlay Bit(s) or Interrupt Bit(s).</para>
   3283         /// For the TARGA M-8, ATVista and NuVista, the number of bits in a color map specification is
   3284         /// 24 (or 32). The red, green, and blue components are each represented by one byte.
   3285         /// </summary>
   3286         public byte[] ColorMapData
   3287         {
   3288             get { return colorMapData; }
   3289             set { colorMapData = value; }
   3290         }
   3291 
   3292         /// <summary>
   3293         /// Image Data - Field 8 (variable):
   3294         /// <para>This field contains (Width)x(Height) pixels. Each pixel specifies image data in one
   3295         /// of the following formats:</para>
   3296         /// <para>a single color-map index for Pseudo-Color;
   3297         /// Attribute, Red, Green and Blue ordered data for True-Color;
   3298         /// and independent color-map indices for Direct-Color.</para>
   3299         /// <para>The values for Width and Height are specified in Fields 5.3 and 5.4 respectively.
   3300         /// The number of attribute and color-definition bits for each pixel are defined in Fields 5.6
   3301         /// and 5.5, respectively.Each pixel is stored as an integral number of bytes.</para>
   3302         /// </summary>
   3303         public byte[] ImageData
   3304         {
   3305             get { return imageData; }
   3306             set { imageData = value; }
   3307         }
   3308 
   3309         /// <summary>
   3310         /// Make full copy of <see cref="TgaImgOrColMap"/>.
   3311         /// </summary>
   3312         /// <returns>Full independed copy of <see cref="TgaImgOrColMap"/>.</returns>
   3313         public TgaImgOrColMap Clone()
   3314         {
   3315             return new TgaImgOrColMap(
   3316                 (imageID == null ? null : imageID.Clone()),
   3317                 (colorMapData == null ? null : (byte[])colorMapData.Clone()),
   3318                 (imageData == null ? null : (byte[])imageData.Clone()));
   3319         }
   3320 
   3321         /// <summary>
   3322         /// Make full copy of <see cref="TgaImgOrColMap"/>.
   3323         /// </summary>
   3324         /// <returns>Full independed copy of <see cref="TgaImgOrColMap"/>.</returns>
   3325         object ICloneable.Clone()
   3326         {
   3327             return Clone();
   3328         }
   3329 
   3330         public override bool Equals(object obj)
   3331         {
   3332             return ((obj is TgaImgOrColMap) ? Equals((TgaImgOrColMap)obj) : false);
   3333         }
   3334 
   3335         public bool Equals(TgaImgOrColMap item)
   3336         {
   3337             return imageID == item.imageID &&
   3338                 BitConverterExt.IsArraysEqual(colorMapData, item.colorMapData) &&
   3339                 BitConverterExt.IsArraysEqual(imageData, item.imageData);
   3340         }
   3341 
   3342         public static bool operator ==(TgaImgOrColMap item1, TgaImgOrColMap item2)
   3343         {
   3344             if (ReferenceEquals(item1, null))
   3345                 return ReferenceEquals(item2, null);
   3346 
   3347             if (ReferenceEquals(item2, null))
   3348                 return ReferenceEquals(item1, null);
   3349 
   3350             return item1.Equals(item2);
   3351         }
   3352 
   3353         public static bool operator !=(TgaImgOrColMap item1, TgaImgOrColMap item2)
   3354         {
   3355             return !(item1 == item2);
   3356         }
   3357 
   3358         public override int GetHashCode()
   3359         {
   3360             unchecked
   3361             {
   3362                 int hash = 27;
   3363 
   3364                 if (imageID != null)
   3365                     hash = (13 * hash) + imageID.GetHashCode();
   3366                 if (colorMapData != null)
   3367                     for (int i = 0; i < colorMapData.Length; i++)
   3368                         hash = (13 * hash) + colorMapData[i].GetHashCode();
   3369                 if (imageData != null)
   3370                     for (int i = 0; i < imageData.Length; i++)
   3371                         hash = (13 * hash) + imageData[i].GetHashCode();
   3372 
   3373                 return hash;
   3374             }
   3375         }
   3376     } //No ToBytes()
   3377 
   3378     /// <summary>
   3379     /// Developer Area
   3380     /// </summary> //?
   3381     public class TgaDevArea : ICloneable
   3382     {
   3383         List<TgaDevEntry> entries = new List<TgaDevEntry>();
   3384 
   3385         public TgaDevArea()
   3386         {
   3387         }
   3388 
   3389         public TgaDevArea(List<TgaDevEntry> Entries)
   3390         {
   3391             if (Entries == null)
   3392                 throw new ArgumentNullException(nameof(Entries) + " = null!");
   3393 
   3394             entries = Entries;
   3395         }
   3396 
   3397         /// <summary>
   3398         /// Developer Data - Field 9 (variable):
   3399         /// </summary>
   3400         public List<TgaDevEntry> Entries
   3401         {
   3402             get { return entries; }
   3403             set { entries = value; }
   3404         }
   3405 
   3406         public int Count
   3407         {
   3408             get { return entries.Count; }
   3409         }
   3410 
   3411         public TgaDevEntry this[int index]
   3412         {
   3413             get { return entries[index]; }
   3414             set { entries[index] = value; }
   3415         }
   3416 
   3417         /// <summary>
   3418         /// Make full copy of <see cref="TgaDevArea"/>.
   3419         /// </summary>
   3420         /// <returns>Full independent copy of <see cref="TgaDevArea"/>.</returns>
   3421         public TgaDevArea Clone()
   3422         {
   3423             if (entries == null)
   3424                 return new TgaDevArea(null);
   3425 
   3426             List<TgaDevEntry> L = new List<TgaDevEntry>();
   3427             for (int i = 0; i < entries.Count; i++)
   3428                 L.Add(entries[i].Clone());
   3429 
   3430             return new TgaDevArea(L);
   3431         }
   3432 
   3433         /// <summary>
   3434         /// Make full copy of <see cref="TgaDevArea"/>.
   3435         /// </summary>
   3436         /// <returns>Full independent copy of <see cref="TgaDevArea"/>.</returns>
   3437         object ICloneable.Clone()
   3438         {
   3439             return Clone();
   3440         }
   3441 
   3442         public override bool Equals(object obj)
   3443         {
   3444             return ((obj is TgaDevArea) ? Equals((TgaDevArea)obj) : false);
   3445         }
   3446 
   3447         public bool Equals(TgaDevArea item)
   3448         {
   3449             return BitConverterExt.IsListsEqual(entries, item.entries);
   3450         }
   3451 
   3452         public static bool operator ==(TgaDevArea item1, TgaDevArea item2)
   3453         {
   3454             if (ReferenceEquals(item1, null))
   3455                 return ReferenceEquals(item2, null);
   3456 
   3457             if (ReferenceEquals(item2, null))
   3458                 return ReferenceEquals(item1, null);
   3459 
   3460             return item1.Equals(item2);
   3461         }
   3462 
   3463         public static bool operator !=(TgaDevArea item1, TgaDevArea item2)
   3464         {
   3465             return !(item1 == item2);
   3466         }
   3467 
   3468         public override int GetHashCode()
   3469         {
   3470             unchecked
   3471             {
   3472                 int hash = 27;
   3473                 if (entries != null)
   3474                     for (int i = 0; i < entries.Count; i++)
   3475                         hash = (13 * hash) + entries[i].GetHashCode();
   3476                 return hash;
   3477             }
   3478         }
   3479 
   3480         /// <summary>
   3481         /// Convert <see cref="TgaDevArea"/> (without Fields Data, only Directory!) to byte array.
   3482         /// </summary>
   3483         /// <returns>Byte array, Len = (NUMBER_OF_TAGS_IN_THE_DIRECTORY * 10) + 2 bytes in size.
   3484         /// The "+ 2" includes the 2 bytes for the number of tags in the directory.</returns>
   3485         public byte[] ToBytes()
   3486         {
   3487             if (entries == null)
   3488                 throw new Exception(nameof(Entries) + " = null!");
   3489 
   3490             ushort NumberOfEntries = (ushort)Math.Min(ushort.MaxValue, entries.Count);
   3491             List<byte> DevDir = new List<byte>(BitConverter.GetBytes(NumberOfEntries));
   3492 
   3493             for (int i = 0; i < entries.Count; i++)
   3494             {
   3495                 DevDir.AddRange(BitConverter.GetBytes(entries[i].Tag));
   3496                 DevDir.AddRange(BitConverter.GetBytes(entries[i].Offset));
   3497                 DevDir.AddRange(BitConverter.GetBytes(entries[i].FieldSize));
   3498             }
   3499 
   3500             return DevDir.ToArray();
   3501         }
   3502     } //Not full ToBytes()
   3503 
   3504     /// <summary>
   3505     /// Extension Area
   3506     /// </summary>
   3507     public class TgaExtArea : ICloneable
   3508     {
   3509         public const int MinSize = 495; //bytes
   3510 
   3511         ushort extensionSize = MinSize;
   3512         TgaString authorName = new TgaString(41, true);
   3513         TgaComment authorComments = new TgaComment();
   3514         TgaDateTime dateTimeStamp = new TgaDateTime();
   3515         TgaString jobNameOrID = new TgaString(41, true);
   3516         TgaTime jobTime = new TgaTime();
   3517         TgaString softwareID = new TgaString(41, true);
   3518         TgaSoftVersion softVersion = new TgaSoftVersion();
   3519         TgaColorKey keyColor = new TgaColorKey();
   3520         TgaFraction pixelAspectRatio = TgaFraction.Empty;
   3521         TgaFraction gammaValue = TgaFraction.Empty;
   3522         uint colorCorrectionOffset = 0;
   3523         uint postageStampOffset = 0;
   3524         uint scanLineOffset = 0;
   3525         TgaAttrType attributesType = TgaAttrType.NoAlpha;
   3526         uint[] scanLineTable = null;
   3527         TgaPostageStampImage postageStampImage = null;
   3528         ushort[] colorCorrectionTable = null;
   3529         byte[] otherDataInExtensionArea = null;
   3530 
   3531         public TgaExtArea()
   3532         {
   3533         }
   3534 
   3535         /// <summary>
   3536         /// Make <see cref="TgaExtArea"/> from bytes. Warning: <see cref="ScanLineTable"/>,
   3537         /// <see cref="PostageStampImage"/>, <see cref="ColorCorrectionTable"/> not included,
   3538         /// because thea are can be not in the Extension Area of TGA file!
   3539         /// </summary>
   3540         /// <param name="Bytes">Bytes of <see cref="TgaExtArea"/>.</param>
   3541         /// <param name="SLT">Scan Line Table.</param>
   3542         /// <param name="PostImg">Postage Stamp Image.</param>
   3543         /// <param name="CCT">Color Correction Table.</param>
   3544         public TgaExtArea(byte[] Bytes, uint[] SLT = null, TgaPostageStampImage PostImg = null, ushort[] CCT = null)
   3545         {
   3546             if (Bytes == null)
   3547                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   3548             if (Bytes.Length < MinSize)
   3549                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + MinSize + "!");
   3550 
   3551             extensionSize = BitConverter.ToUInt16(Bytes, 0);
   3552             authorName = new TgaString(BitConverterExt.GetElements(Bytes, 2, 41), true);
   3553             authorComments = new TgaComment(BitConverterExt.GetElements(Bytes, 43, TgaComment.Size));
   3554             dateTimeStamp = new TgaDateTime(BitConverterExt.GetElements(Bytes, 367, TgaDateTime.Size));
   3555             jobNameOrID = new TgaString(BitConverterExt.GetElements(Bytes, 379, 41), true);
   3556             jobTime = new TgaTime(BitConverterExt.GetElements(Bytes, 420, TgaTime.Size));
   3557             softwareID = new TgaString(BitConverterExt.GetElements(Bytes, 426, 41), true);
   3558             softVersion = new TgaSoftVersion(BitConverterExt.GetElements(Bytes, 467, TgaSoftVersion.Size));
   3559             keyColor = new TgaColorKey(BitConverterExt.GetElements(Bytes, 470, TgaColorKey.Size));
   3560             pixelAspectRatio = new TgaFraction(BitConverterExt.GetElements(Bytes, 474, TgaFraction.Size));
   3561             gammaValue = new TgaFraction(BitConverterExt.GetElements(Bytes, 478, TgaFraction.Size));
   3562             colorCorrectionOffset = BitConverter.ToUInt32(Bytes, 482);
   3563             postageStampOffset = BitConverter.ToUInt32(Bytes, 486);
   3564             scanLineOffset = BitConverter.ToUInt32(Bytes, 490);
   3565             attributesType = (TgaAttrType)Bytes[494];
   3566 
   3567             if (extensionSize > MinSize)
   3568                 otherDataInExtensionArea = BitConverterExt.GetElements(Bytes, 495, Bytes.Length - MinSize);
   3569 
   3570             scanLineTable = SLT;
   3571             postageStampImage = PostImg;
   3572             colorCorrectionTable = CCT;
   3573         }
   3574 
   3575         #region Properties
   3576         /// <summary>
   3577         /// Extension Size - Field 10 (2 Bytes):
   3578         /// This field is a SHORT field which specifies the number of BYTES in the fixedlength portion of
   3579         /// the Extension Area. For Version 2.0 of the TGA File Format, this number should be set to 495.
   3580         /// If the number found in this field is not 495, then the file will be assumed to be of a
   3581         /// version other than 2.0. If it ever becomes necessary to alter this number, the change
   3582         /// will be controlled by Truevision, and will be accompanied by a revision to the TGA File
   3583         /// Format with an accompanying change in the version number.
   3584         /// </summary>
   3585         public ushort ExtensionSize
   3586         {
   3587             get { return extensionSize; }
   3588             set { extensionSize = value; }
   3589         }
   3590 
   3591         /// <summary>
   3592         /// Author Name - Field 11 (41 Bytes):
   3593         /// Bytes 2-42 - This field is an ASCII field of 41 bytes where the last byte must be a null
   3594         /// (binary zero). This gives a total of 40 ASCII characters for the name. If the field is used,
   3595         /// it should contain the name of the person who created the image (author). If the field is not
   3596         /// used, you may fill it with nulls or a series of blanks(spaces) terminated by a null.
   3597         /// The 41st byte must always be a null.
   3598         /// </summary>
   3599         public TgaString AuthorName
   3600         {
   3601             get { return authorName; }
   3602             set { authorName = value; }
   3603         }
   3604 
   3605         /// <summary>
   3606         /// Author Comments - Field 12 (324 Bytes):
   3607         /// Bytes 43-366 - This is an ASCII field consisting of 324 bytes which are organized as four lines
   3608         /// of 80 characters, each followed by a null terminator.This field is provided, in addition to the
   3609         /// original IMAGE ID field(in the original TGA format), because it was determined that a few
   3610         /// developers had used the IMAGE ID field for their own purposes.This field gives the developer
   3611         /// four lines of 80 characters each, to use as an Author Comment area. Each line is fixed to 81
   3612         /// bytes which makes access to the four lines easy.Each line must be terminated by a null.
   3613         /// If you do not use all 80 available characters in the line, place the null after the last
   3614         /// character and blank or null fill the rest of the line. The 81st byte of each of the four
   3615         /// lines must be null.
   3616         /// </summary>
   3617         public TgaComment AuthorComments
   3618         {
   3619             get { return authorComments; }
   3620             set { authorComments = value; }
   3621         }
   3622 
   3623         /// <summary>
   3624         /// Date/Time Stamp - Field 13 (12 Bytes):
   3625         /// Bytes 367-378 - This field contains a series of 6 SHORT values which define the integer
   3626         /// value for the date and time that the image was saved. This data is formatted as follows:
   3627         /// <para>SHORT 0: Month(1 - 12)</para>
   3628         /// <para>SHORT 1: Day(1 - 31)</para>
   3629         /// <para>SHORT 2: Year(4 digit, ie. 1989)</para>
   3630         /// <para>SHORT 3: Hour(0 - 23)</para>
   3631         /// <para>SHORT 4: Minute(0 - 59)</para>
   3632         /// <para>SHORT 5: Second(0 - 59)</para>
   3633         /// Even though operating systems typically time- and date-stamp files, this feature is
   3634         /// provided because the operating system may change the time and date stamp if the file is
   3635         /// copied. By using this area, you are guaranteed an unmodified region for date and time
   3636         /// recording. If the fields are not used, you should fill them with binary zeros (0).
   3637         /// </summary>
   3638         public TgaDateTime DateTimeStamp
   3639         {
   3640             get { return dateTimeStamp; }
   3641             set { dateTimeStamp = value; }
   3642         }
   3643 
   3644         /// <summary>
   3645         /// Job Name/ID - Field 14 (41 Bytes):
   3646         /// Bytes 379-419 - This field is an ASCII field of 41 bytes where the last byte must be 
   3647         /// a binary zero. This gives a total of 40 ASCII characters for the job name or the ID.
   3648         /// If the field is used, it should contain a name or id tag which refers to the job with
   3649         /// which the image was associated.This allows production companies (and others) to tie
   3650         /// images with jobs by using this field as a job name (i.e., CITY BANK) or job id number
   3651         /// (i.e., CITY023). If the field is not used, you may fill it with a null terminated series
   3652         /// of blanks (spaces) or nulls. In any case, the 41st byte must be a null.
   3653         /// </summary>
   3654         public TgaString JobNameOrID
   3655         {
   3656             get { return jobNameOrID; }
   3657             set { jobNameOrID = value; }
   3658         }
   3659 
   3660         /// <summary>
   3661         /// Job Time - Field 15 (6 Bytes):
   3662         /// Bytes 420-425 - This field contains a series of 3 SHORT values which define the integer
   3663         /// value for the job elapsed time when the image was saved.This data is formatted as follows:
   3664         /// <para>SHORT 0: Hours(0 - 65535)</para>
   3665         /// <para>SHORT 1: Minutes(0 - 59)</para>
   3666         /// <para>SHORT 2: Seconds(0 - 59)</para>
   3667         /// The purpose of this field is to allow production houses (and others) to keep a running total
   3668         /// of the amount of time invested in a particular image. This may be useful for billing, costing,
   3669         /// and time estimating. If the fields are not used, you should fill them with binary zeros (0).
   3670         /// </summary>
   3671         public TgaTime JobTime
   3672         {
   3673             get { return jobTime; }
   3674             set { jobTime = value; }
   3675         }
   3676 
   3677         /// <summary>
   3678         /// Software ID - Field 16 (41 Bytes):
   3679         /// Bytes 426-466 - This field is an ASCII field of 41 bytes where the last byte must be
   3680         /// a binary zero (null). This gives a total of 40 ASCII characters for the Software ID.
   3681         /// The purpose of this field is to allow software to determine and record with what program
   3682         /// a particular image was created.If the field is not used, you may fill it with a
   3683         /// null terminated series of blanks (spaces) or nulls. The 41st byte must always be a null.
   3684         /// </summary>
   3685         public TgaString SoftwareID
   3686         {
   3687             get { return softwareID; }
   3688             set { softwareID = value; }
   3689         }
   3690 
   3691         /// <summary>
   3692         /// Software Version - Field 17 (3 Bytes):
   3693         /// Bytes 467-469 - This field consists of two sub-fields, a SHORT and an ASCII BYTE.
   3694         /// The purpose of this field is to define the version of software defined by the
   3695         /// “Software ID” field above. The SHORT contains the version number as a binary
   3696         /// integer times 100.
   3697         /// <para>Therefore, software version 4.17 would be the integer value 417.This allows for
   3698         /// two decimal positions of sub-version.The ASCII BYTE supports developers who also
   3699         /// tag a release letter to the end. For example, if the version number is 1.17b, then
   3700         /// the SHORT would contain 117. and the ASCII BYTE would contain “b”.
   3701         /// The organization is as follows:</para>
   3702         /// <para>SHORT (Bytes 0 - 1): Version Number * 100</para>
   3703         /// <para>BYTE(Byte 2): Version Letter</para>
   3704         /// If you do not use this field, set the SHORT to binary zero, and the BYTE to a space(“ “)
   3705         /// </summary>
   3706         public TgaSoftVersion SoftVersion
   3707         {
   3708             get { return softVersion; }
   3709             set { softVersion = value; }
   3710         }
   3711 
   3712         /// <summary>
   3713         /// Key Color - Field 18 (4 Bytes):
   3714         /// Bytes 470-473 - This field contains a long value which is the key color in effect at
   3715         /// the time the image is saved. The format is in A:R:G:B where ‘A’ (most significant byte)
   3716         /// is the alpha channel key color(if you don’t have an alpha channel in your application,
   3717         /// keep this byte zero [0]).
   3718         /// <para>The Key Color can be thought of as the ‘background color’ or ‘transparent color’.
   3719         /// This is the color of the ‘non image’ area of the screen, and the same color that the
   3720         /// screen would be cleared to if erased in the application. If you don’t use this field,
   3721         /// set it to all zeros (0). Setting the field to all zeros is the same as selecting a key
   3722         /// color of black.</para>
   3723         /// A good example of a key color is the ‘transparent color’ used in TIPS™ for WINDOW loading/saving.
   3724         /// </summary>
   3725         public TgaColorKey KeyColor
   3726         {
   3727             get { return keyColor; }
   3728             set { keyColor = value; }
   3729         }
   3730 
   3731         /// <summary>
   3732         /// Pixel Aspect Ratio - Field 19 (4 Bytes):
   3733         /// Bytes 474-477 - This field contains two SHORT sub-fields, which when taken together
   3734         /// specify a pixel size ratio.The format is as follows:
   3735         /// <para>SHORT 0: Pixel Ratio Numerator(pixel width)</para>
   3736         /// <para>SHORT 1: Pixel Ratio Denominator(pixel height)</para>
   3737         /// These sub-fields may be used to determine the aspect ratio of a pixel. This is useful when
   3738         /// it is important to preserve the proper aspect ratio of the saved image. If the two values
   3739         /// are set to the same non-zero value, then the image is composed of square pixels. A zero
   3740         /// in the second sub-field (denominator) indicates that no pixel aspect ratio is specified.
   3741         /// </summary>
   3742         public TgaFraction PixelAspectRatio
   3743         {
   3744             get { return pixelAspectRatio; }
   3745             set { pixelAspectRatio = value; }
   3746         }
   3747 
   3748         /// <summary>
   3749         /// Gamma Value - Field 20 (4 Bytes):
   3750         /// Bytes 478-481 - This field contains two SHORT sub-fields, which when taken together in a ratio,
   3751         /// provide a fractional gamma value.The format is as follows:
   3752         /// <para>SHORT 0: Gamma Numerator</para>
   3753         /// <para>SHORT 1: Gamma Denominator</para>
   3754         /// The resulting value should be in the range of 0.0 to 10.0, with only one decimal place of
   3755         /// precision necessary. An uncorrected image (an image with no gamma) should have the value 1.0 as
   3756         /// the result.This may be accomplished by placing thesame, non-zero values in both positions
   3757         /// (i.e., 1/1). If you decide to totally ignore this field, please set the denominator (the second
   3758         /// SHORT) to the value zero. This will indicate that the Gamma Value field is not being used.
   3759         /// </summary>
   3760         public TgaFraction GammaValue
   3761         {
   3762             get { return gammaValue; }
   3763             set { gammaValue = value; }
   3764         }
   3765 
   3766         /// <summary>
   3767         /// Color Correction Offset - Field 21 (4 Bytes):
   3768         /// Bytes 482-485 - This field is a 4-byte field containing a single offset value. This is an offset
   3769         /// from the beginning of the file to the start of the Color Correction table. This table may be
   3770         /// written anywhere between the end of the Image Data field (field 8) and the start of the TGA
   3771         /// File Footer. If the image has no Color Correction Table or if the Gamma Value setting is
   3772         /// sufficient, set this value to zero and do not write a Correction Table anywhere.
   3773         /// </summary>
   3774         public uint ColorCorrectionTableOffset
   3775         {
   3776             get { return colorCorrectionOffset; }
   3777             set { colorCorrectionOffset = value; }
   3778         }
   3779 
   3780         /// <summary>
   3781         /// Postage Stamp Offset - Field 22 (4 Bytes):
   3782         /// Bytes 486-489 - This field is a 4-byte field containing a single offset value. This is an offset
   3783         /// from the beginning of the file to the start of the Postage Stamp Image. The Postage Stamp Image
   3784         /// must be written after Field 25 (Scan Line Table) but before the start of the TGA File Footer.
   3785         /// If no postage stamp is stored, set this field to the value zero (0).
   3786         /// </summary>
   3787         public uint PostageStampOffset
   3788         {
   3789             get { return postageStampOffset; }
   3790             set { postageStampOffset = value; }
   3791         }
   3792 
   3793         /// <summary>
   3794         /// Scan Line Offset - Field 23 (4 Bytes):
   3795         /// Bytes 490-493 - This field is a 4-byte field containing a single offset value. This is an
   3796         /// offset from the beginning of the file to the start of the Scan Line Table.
   3797         /// </summary>
   3798         public uint ScanLineOffset
   3799         {
   3800             get { return scanLineOffset; }
   3801             set { scanLineOffset = value; }
   3802         }
   3803 
   3804         /// <summary>
   3805         /// Attributes Type - Field 24 (1 Byte):
   3806         /// Byte 494 - This single byte field contains a value which specifies the type of Alpha channel
   3807         /// data contained in the file. Value Meaning:
   3808         /// <para>0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero)</para>
   3809         /// <para>1: undefined data in the Alpha field, can be ignored</para>
   3810         /// <para>2: undefined data in the Alpha field, but should be retained</para>
   3811         /// <para>3: useful Alpha channel data is present</para>
   3812         /// <para>4: pre-multiplied Alpha(see description below)</para>
   3813         /// <para>5 -127: RESERVED</para>
   3814         /// <para>128-255: Un-assigned</para>
   3815         /// <para>Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the
   3816         /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates
   3817         /// that the pixel is completely transparent and a value of 1 indicates that the pixel is
   3818         /// completely opaque(assume all component values have been normalized).</para>
   3819         /// <para>A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a
   3820         /// transparency of one-half. For numerous reasons(including image compositing) is is better to
   3821         /// pre-multiply the individual color components with the value in the Alpha channel.</para>
   3822         /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0).
   3823         /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components
   3824         /// of the pixel have already been scaled by the value in the Alpha channel.
   3825         /// </summary>
   3826         public TgaAttrType AttributesType
   3827         {
   3828             get { return attributesType; }
   3829             set { attributesType = value; }
   3830         }
   3831 
   3832         /// <summary>
   3833         /// Scan Line Table - Field 25 (Variable):
   3834         /// This information is provided, at the developers’ request, for two purposes:
   3835         /// <para>1) To make random access of compressed images easy.</para>
   3836         /// <para>2) To allow “giant picture” access in smaller “chunks”.</para>
   3837         /// This table should contain a series of 4-byte offsets.Each offset you write should point to the
   3838         /// start of the next scan line, in the order that the image was saved (i.e., top down or bottom up).
   3839         /// The offset should be from the start of the file.Therefore, you will have a four byte value for
   3840         /// each scan line in your image. This means that if your image is 768 pixels tall, you will have 768,
   3841         /// 4-byte offset pointers (for a total of 3072 bytes). This size is not extreme, and thus this table
   3842         /// can be built and maintained in memory, and then written out at the proper time.
   3843         /// </summary>
   3844         public uint[] ScanLineTable
   3845         {
   3846             get { return scanLineTable; }
   3847             set { scanLineTable = value; }
   3848         }
   3849 
   3850         /// <summary>
   3851         /// Postage Stamp Image - Field 26 (Variable):
   3852         /// The Postage Stamp area is a smaller representation of the original image. This is useful for
   3853         /// “browsing” a collection of image files. If your application can deal with a postage stamp image,
   3854         /// it is recommended that you create one using sub-sampling techniques to create the best
   3855         /// representation possible. The postage stamp image must be stored in the same format as the normal
   3856         /// image specified in the file, but without any compression. The first byte of the postage stamp
   3857         /// image specifies the X size of the stamp in pixels, the second byte of the stamp image specifies the
   3858         /// Y size of the stamp in pixels. Truevision does not recommend stamps larger than 64x64 pixels, and
   3859         /// suggests that any stamps stored larger be clipped. Obviously, the storage of the postage stamp
   3860         /// relies heavily on the storage of the image. The two images are stored using the same format under
   3861         /// the assumption that if you can read the image, then you can read the postage stamp. If the original
   3862         /// image is color mapped, DO NOT average the postage stamp, as you will create new colors not in your map.
   3863         /// </summary>
   3864         public TgaPostageStampImage PostageStampImage
   3865         {
   3866             get { return postageStampImage; }
   3867             set { postageStampImage = value; }
   3868         }
   3869 
   3870         /// <summary>
   3871         /// Color Correction Table - Field 27 (2K Bytes):
   3872         /// The Color Correction Table is a block of 256 x 4 SHORT values, where each set of
   3873         /// four contiguous values are the desired A:R:G:B correction for that entry. This
   3874         /// allows the user to store a correction table for image remapping or LUT driving.
   3875         /// Since each color in the block is a SHORT, the maximum value for a color gun (i.e.,
   3876         /// A, R, G, B) is 65535, and the minimum value is zero.Therefore, BLACK maps to
   3877         /// 0, 0, 0, 0 and WHITE maps to 65535, 65535, 65535, 65535.
   3878         /// </summary>
   3879         public ushort[] ColorCorrectionTable
   3880         {
   3881             get { return colorCorrectionTable; }
   3882             set { colorCorrectionTable = value; }
   3883         }
   3884 
   3885         /// <summary>
   3886         /// Other Data In Extension Area (if <see cref="ExtensionSize"/> > 495).
   3887         /// </summary>
   3888         public byte[] OtherDataInExtensionArea
   3889         {
   3890             get { return otherDataInExtensionArea; }
   3891             set { otherDataInExtensionArea = value; }
   3892         }
   3893         #endregion
   3894 
   3895         /// <summary>
   3896         /// Make full copy of <see cref="TgaExtArea"/>.
   3897         /// </summary>
   3898         /// <returns>Full independent copy of <see cref="TgaExtArea"/>.</returns>
   3899         public TgaExtArea Clone()
   3900         {
   3901             TgaExtArea NewExtArea = new TgaExtArea();
   3902             NewExtArea.extensionSize = extensionSize;
   3903             NewExtArea.authorName = authorName.Clone();
   3904             NewExtArea.authorComments = authorComments.Clone();
   3905             NewExtArea.dateTimeStamp = dateTimeStamp.Clone();
   3906             NewExtArea.jobNameOrID = jobNameOrID.Clone();
   3907             NewExtArea.jobTime = jobTime.Clone();
   3908             NewExtArea.softwareID = softwareID.Clone();
   3909             NewExtArea.softVersion = softVersion.Clone();
   3910             NewExtArea.keyColor = keyColor.Clone();
   3911             NewExtArea.pixelAspectRatio = pixelAspectRatio.Clone();
   3912             NewExtArea.gammaValue = gammaValue.Clone();
   3913             NewExtArea.colorCorrectionOffset = colorCorrectionOffset;
   3914             NewExtArea.postageStampOffset = postageStampOffset;
   3915             NewExtArea.scanLineOffset = scanLineOffset;
   3916             NewExtArea.attributesType = attributesType;
   3917 
   3918             if (scanLineTable != null)
   3919                 NewExtArea.scanLineTable = (uint[])scanLineTable.Clone();
   3920             if (postageStampImage != null)
   3921                 NewExtArea.postageStampImage = new TgaPostageStampImage(postageStampImage.ToBytes());
   3922             if (colorCorrectionTable != null)
   3923                 NewExtArea.colorCorrectionTable = (ushort[])colorCorrectionTable.Clone();
   3924 
   3925             if (otherDataInExtensionArea != null)
   3926                 NewExtArea.otherDataInExtensionArea = (byte[])otherDataInExtensionArea.Clone();
   3927 
   3928             return NewExtArea;
   3929         }
   3930 
   3931         /// <summary>
   3932         /// Make full copy of <see cref="TgaExtArea"/>.
   3933         /// </summary>
   3934         /// <returns>Full independent copy of <see cref="TgaExtArea"/>.</returns>
   3935         object ICloneable.Clone()
   3936         {
   3937             return Clone();
   3938         }
   3939 
   3940         public override bool Equals(object obj)
   3941         {
   3942             return ((obj is TgaExtArea) ? Equals((TgaExtArea)obj) : false);
   3943         }
   3944 
   3945         public bool Equals(TgaExtArea item)
   3946         {
   3947             return (extensionSize == item.extensionSize &&
   3948                 authorName == item.authorName &&
   3949                 authorComments == item.authorComments &&
   3950                 dateTimeStamp == item.dateTimeStamp &&
   3951                 jobNameOrID == item.jobNameOrID &&
   3952                 jobTime == item.jobTime &&
   3953                 softwareID == item.softwareID &&
   3954                 softVersion == item.softVersion &&
   3955                 keyColor == item.keyColor &&
   3956                 pixelAspectRatio == item.pixelAspectRatio &&
   3957                 gammaValue == item.gammaValue &&
   3958                 colorCorrectionOffset == item.colorCorrectionOffset &&
   3959                 postageStampOffset == item.postageStampOffset &&
   3960                 scanLineOffset == item.scanLineOffset &&
   3961                 attributesType == item.attributesType &&
   3962 
   3963                 BitConverterExt.IsArraysEqual(scanLineTable, item.scanLineTable) &&
   3964                 postageStampImage == item.postageStampImage &&
   3965                 BitConverterExt.IsArraysEqual(colorCorrectionTable, item.colorCorrectionTable) &&
   3966 
   3967                 BitConverterExt.IsArraysEqual(otherDataInExtensionArea, item.otherDataInExtensionArea));
   3968         }
   3969 
   3970         public static bool operator ==(TgaExtArea item1, TgaExtArea item2)
   3971         {
   3972             if (ReferenceEquals(item1, null))
   3973                 return ReferenceEquals(item2, null);
   3974 
   3975             if (ReferenceEquals(item2, null))
   3976                 return ReferenceEquals(item1, null);
   3977 
   3978             return item1.Equals(item2);
   3979         }
   3980 
   3981         public static bool operator !=(TgaExtArea item1, TgaExtArea item2)
   3982         {
   3983             return !(item1 == item2);
   3984         }
   3985 
   3986         public override int GetHashCode()
   3987         {
   3988             unchecked
   3989             {
   3990                 int hash = 27;
   3991                 hash = (13 * hash) + extensionSize.GetHashCode();
   3992                 hash = (13 * hash) + authorName.GetHashCode();
   3993                 hash = (13 * hash) + authorComments.GetHashCode();
   3994                 hash = (13 * hash) + dateTimeStamp.GetHashCode();
   3995                 hash = (13 * hash) + jobNameOrID.GetHashCode();
   3996                 hash = (13 * hash) + jobTime.GetHashCode();
   3997                 hash = (13 * hash) + softwareID.GetHashCode();
   3998                 hash = (13 * hash) + softVersion.GetHashCode();
   3999                 hash = (13 * hash) + keyColor.GetHashCode();
   4000                 hash = (13 * hash) + pixelAspectRatio.GetHashCode();
   4001                 hash = (13 * hash) + gammaValue.GetHashCode();
   4002                 hash = (13 * hash) + colorCorrectionOffset.GetHashCode();
   4003                 hash = (13 * hash) + postageStampOffset.GetHashCode();
   4004                 hash = (13 * hash) + scanLineOffset.GetHashCode();
   4005                 hash = (13 * hash) + attributesType.GetHashCode();
   4006 
   4007                 if (scanLineTable != null)
   4008                     for (int i = 0; i < scanLineTable.Length; i++)
   4009                         hash = (13 * hash) + scanLineTable[i].GetHashCode();
   4010 
   4011                 if (postageStampImage != null)
   4012                     hash = (13 * hash) + postageStampImage.GetHashCode();
   4013 
   4014                 if (colorCorrectionTable != null)
   4015                     for (int i = 0; i < colorCorrectionTable.Length; i++)
   4016                         hash = (13 * hash) + colorCorrectionTable[i].GetHashCode();
   4017 
   4018                 if (otherDataInExtensionArea != null)
   4019                     for (int i = 0; i < otherDataInExtensionArea.Length; i++)
   4020                         hash = (13 * hash) + otherDataInExtensionArea[i].GetHashCode();
   4021 
   4022                 return hash;
   4023             }
   4024         }
   4025 
   4026         /// <summary>
   4027         /// Convert <see cref="TgaExtArea"/> to byte array. Warning: <see cref="ScanLineTable"/>,
   4028         /// <see cref="PostageStampImage"/>, <see cref="ColorCorrectionTable"/> not included,
   4029         /// because thea are can be not in the Extension Area of TGA file!
   4030         /// </summary>
   4031         /// <returns>Byte array.</returns>
   4032         public byte[] ToBytes()
   4033         {
   4034             #region Exceptions check
   4035             if (authorName == null)
   4036                 authorName = new TgaString(41, true);
   4037 
   4038             if (authorComments == null)
   4039                 authorComments = new TgaComment();
   4040 
   4041             if (dateTimeStamp == null)
   4042                 dateTimeStamp = new TgaDateTime(DateTime.UtcNow);
   4043 
   4044             if (jobNameOrID == null)
   4045                 jobNameOrID = new TgaString(41, true);
   4046 
   4047             if (jobTime == null)
   4048                 jobTime = new TgaTime();
   4049 
   4050             if (softwareID == null)
   4051                 softwareID = new TgaString(41, true);
   4052 
   4053             if (softVersion == null)
   4054                 softVersion = new TgaSoftVersion();
   4055 
   4056             if (keyColor == null)
   4057                 keyColor = new TgaColorKey();
   4058 
   4059             if (pixelAspectRatio == null)
   4060                 pixelAspectRatio = new TgaFraction();
   4061 
   4062             if (gammaValue == null)
   4063                 gammaValue = new TgaFraction();
   4064             #endregion
   4065 
   4066             return BitConverterExt.ToBytes(
   4067                 extensionSize,
   4068                 authorName.ToBytes(),
   4069                 authorComments.ToBytes(),
   4070                 dateTimeStamp.ToBytes(),
   4071                 jobNameOrID.ToBytes(),
   4072                 jobTime.ToBytes(),
   4073                 softwareID.ToBytes(),
   4074                 softVersion.ToBytes(),
   4075                 keyColor.ToBytes(),
   4076                 pixelAspectRatio.ToBytes(),
   4077                 gammaValue.ToBytes(),
   4078                 colorCorrectionOffset,
   4079                 postageStampOffset,
   4080                 scanLineOffset,
   4081                 (byte)attributesType,
   4082                 otherDataInExtensionArea);
   4083         }
   4084     } //Not full ToBytes()
   4085 
   4086     /// <summary>
   4087     /// File Footer Area
   4088     /// </summary>
   4089     public class TgaFooter : ICloneable
   4090     {
   4091         uint extAreaOffset = 0;
   4092         uint devDirOffset = 0;
   4093         TgaString signature = TgaString.XFileSignatute;
   4094         TgaString reservedChar = TgaString.DotSymbol;
   4095         TgaString zeroStrTerminator = TgaString.ZeroTerminator;
   4096 
   4097         /// <summary>
   4098         /// Make NewXFile format TGA Footer with <see cref="ExtensionAreaOffset"/> = 0 and
   4099         /// <see cref="DeveloperDirectoryOffset"/> = 0.
   4100         /// </summary>
   4101         public TgaFooter()
   4102         {
   4103         }
   4104 
   4105         /// <summary>
   4106         /// Make <see cref="TgaFooter"/> from values.
   4107         /// </summary>
   4108         /// <param name="ExtOff">Extension Area Offset, offset from the beginning of the file.</param>
   4109         /// <param name="DevDirOff">Developer Directory Offset, offset from the beginning of the file.</param>
   4110         /// <param name="Sign">New TGA format signature.</param>
   4111         /// <param name="ReservChr">Reserved Character - ASCII character “.” (period).</param>
   4112         /// <param name="Termin">Binary Zero Terminator, a binary zero which acts as a final terminator.</param>
   4113         public TgaFooter(uint ExtOff, uint DevDirOff, TgaString Sign, TgaString ReservChr, TgaString Termin)
   4114         {
   4115             extAreaOffset = ExtOff;
   4116             devDirOffset = DevDirOff;
   4117             signature = Sign;
   4118             reservedChar = ReservChr;
   4119             zeroStrTerminator = Termin;
   4120         }
   4121 
   4122         /// <summary>
   4123         /// Make <see cref="TgaFooter"/> from bytes (if signature is right).
   4124         /// </summary>
   4125         /// <param name="Bytes">Bytes array (byte[26]).</param>
   4126         public TgaFooter(byte[] Bytes)
   4127         {
   4128             if (Bytes == null)
   4129                 throw new ArgumentNullException(nameof(Bytes) + " = null!");
   4130             if (Bytes.Length != Size)
   4131                 throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!");
   4132 
   4133             extAreaOffset = BitConverter.ToUInt32(Bytes, 0);
   4134             devDirOffset = BitConverter.ToUInt32(Bytes, 4);
   4135             signature = new TgaString(BitConverterExt.GetElements(Bytes, 8, TgaString.XFileSignatuteConst.Length));
   4136             reservedChar = new TgaString(new byte[] { Bytes[24] });
   4137             zeroStrTerminator = new TgaString(new byte[] { Bytes[25] });
   4138         }
   4139 
   4140         /// <summary>
   4141         /// Byte 0-3 - Extension Area Offset - Field 28
   4142         /// The first four bytes (bytes 0-3, the first LONG) of the TGA File Footer contain an
   4143         /// offset from the beginning of the file to the start of the Extension Area. Simply
   4144         /// SEEK to this location to position to the start of the Extension Area. If the
   4145         /// Extension Area Offset is zero, no Extension Area exists in the file.
   4146         /// </summary>
   4147         public uint ExtensionAreaOffset
   4148         {
   4149             get { return extAreaOffset; }
   4150             set { extAreaOffset = value; }
   4151         }
   4152 
   4153         /// <summary>
   4154         /// Byte 4-7 - Developer Directory Offset - Field 29
   4155         /// The next four bytes(bytes 4-7, the second LONG) contain an offset from the
   4156         /// beginning of the file to the start of the Developer Directory. If the Developer
   4157         /// Directory Offset is zero, then the Developer Area does not exist.
   4158         /// </summary>
   4159         public uint DeveloperDirectoryOffset
   4160         {
   4161             get { return devDirOffset; }
   4162             set { devDirOffset = value; }
   4163         }
   4164 
   4165         /// <summary>
   4166         /// Byte 8-23 - Signature - Field 30
   4167         /// This string is exactly 16 bytes long and is formatted exactly as shown below
   4168         /// capital letters), with a hyphen between “TRUEVISION” and “XFILE.” If the
   4169         /// signature is detected, the file is assumed to be of the New TGA format and MAY,
   4170         /// therefore, contain the Developer Area and/or the Extension Area fields.If the
   4171         /// signature is not found, then the file is assumed to be in the Original TGA format.
   4172         /// </summary>
   4173         public TgaString Signature
   4174         {
   4175             get { return signature; }
   4176             set { signature = value; }
   4177         }
   4178 
   4179         /// <summary>
   4180         /// Byte 24 - Reserved Character - Field 31
   4181         /// Byte 24 is an ASCII character “.” (period). This character MUST BE a period or
   4182         /// the file is not considered a proper TGA file.
   4183         /// </summary>
   4184         public TgaString ReservedCharacter
   4185         {
   4186             get { return reservedChar; }
   4187             set { reservedChar = value; }
   4188         }
   4189 
   4190         /// <summary>
   4191         /// Byte 25 - Binary Zero String Terminator - Field 32
   4192         /// Byte 25 is a binary zero which acts as a final terminator and allows the entire TGA
   4193         /// File Footer to be read and utilized as a “C” string.
   4194         /// </summary>
   4195         public TgaString BinaryZeroStringTerminator
   4196         {
   4197             get { return zeroStrTerminator; }
   4198             set { zeroStrTerminator = value; }
   4199         }
   4200 
   4201         /// <summary>
   4202         /// Make full copy of <see cref="TgaFooter"/>.
   4203         /// </summary>
   4204         /// <returns></returns>
   4205         public TgaFooter Clone()
   4206         {
   4207             return new TgaFooter(extAreaOffset, devDirOffset, signature.Clone(),
   4208                 reservedChar.Clone(), zeroStrTerminator.Clone());
   4209         }
   4210 
   4211         /// <summary>
   4212         /// Make full copy of <see cref="TgaFooter"/>.
   4213         /// </summary>
   4214         /// <returns></returns>
   4215         object ICloneable.Clone()
   4216         {
   4217             return Clone();
   4218         }
   4219 
   4220         /// <summary>
   4221         /// Gets TGA Footer Section size in bytes.
   4222         /// </summary>
   4223         public const int Size = 26;
   4224 
   4225         public override bool Equals(object obj)
   4226         {
   4227             return ((obj is TgaFooter) ? Equals((TgaFooter)obj) : false);
   4228         }
   4229 
   4230         public bool Equals(TgaFooter item)
   4231         {
   4232             return (extAreaOffset == item.extAreaOffset &&
   4233                 devDirOffset == item.devDirOffset &&
   4234                 signature == item.signature &&
   4235                 reservedChar == item.reservedChar &&
   4236                 zeroStrTerminator == item.zeroStrTerminator);
   4237         }
   4238 
   4239         public static bool operator ==(TgaFooter item1, TgaFooter item2)
   4240         {
   4241             if (ReferenceEquals(item1, null))
   4242                 return ReferenceEquals(item2, null);
   4243 
   4244             if (ReferenceEquals(item2, null))
   4245                 return ReferenceEquals(item1, null);
   4246 
   4247             return item1.Equals(item2);
   4248         }
   4249 
   4250         public static bool operator !=(TgaFooter item1, TgaFooter item2)
   4251         {
   4252             return !(item1 == item2);
   4253         }
   4254 
   4255         public override int GetHashCode()
   4256         {
   4257             unchecked
   4258             {
   4259                 int hash = 17;
   4260                 hash = hash * 23 + extAreaOffset.GetHashCode();
   4261                 hash = hash * 23 + devDirOffset.GetHashCode();
   4262 
   4263                 if (signature != null)
   4264                     hash = hash * 23 + signature.GetHashCode();
   4265 
   4266                 if (reservedChar != null)
   4267                     hash = hash * 23 + reservedChar.GetHashCode();
   4268 
   4269                 if (zeroStrTerminator != null)
   4270                     hash = hash * 23 + zeroStrTerminator.GetHashCode();
   4271 
   4272                 return hash;
   4273             }
   4274         }
   4275 
   4276         public override string ToString()
   4277         {
   4278             return String.Format("{0}={1}, {2}={3}, FullSignature={4}",
   4279                 nameof(ExtensionAreaOffset), extAreaOffset, nameof(DeveloperDirectoryOffset), devDirOffset,
   4280                 (signature + reservedChar + zeroStrTerminator).ToString());
   4281         }
   4282 
   4283         /// <summary>
   4284         /// Convert <see cref="TgaFooter"/> to byte array.
   4285         /// </summary>
   4286         /// <returns>Byte array with size equal <see cref="Size"/>.</returns>
   4287         public byte[] ToBytes()
   4288         {
   4289             return BitConverterExt.ToBytes(extAreaOffset, devDirOffset,
   4290                 signature.ToBytes(), reservedChar.ToBytes(), zeroStrTerminator.ToBytes());
   4291         }
   4292 
   4293         /// <summary>
   4294         /// Is footer is real footer of TGA File Format Version 2.0?
   4295         /// Checking by <see cref="TgaString.XFileSignatute"/>.
   4296         /// </summary>
   4297         public bool IsFooterCorrect
   4298         {
   4299             get { return signature == TgaString.XFileSignatute; }
   4300         }
   4301     }
   4302 
   4303     ////////////////////////////////////////////////////////////////////////////////////////////////
   4304 
   4305     /// <summary>
   4306     /// Simplify ByteConversion operations, like concatination of byte arrays, comparing and other.
   4307     /// </summary>
   4308     public static class BitConverterExt
   4309     {
   4310         /// <summary>
   4311         /// Combine byte, byte[], (u)short, (u)int, (u)long values to byte[] array.
   4312         /// </summary>
   4313         /// <param name="obj">Array of byte, byte[], (u)short, (u)int, (u)long values.</param>
   4314         /// <returns>Array of bytes, null when some object is null.</returns>
   4315         public static byte[] ToBytes(params object[] obj)
   4316         {
   4317             if (obj == null)
   4318                 return null;
   4319 
   4320             List<byte> BytesList = new List<byte>();
   4321 
   4322             for (int i = 0; i < obj.Length; i++)
   4323             {
   4324                 if (obj[i] == null)
   4325                     continue;
   4326                 else if (obj[i] is byte)
   4327                     BytesList.Add((byte)obj[i]);
   4328                 else if (obj[i] is byte[])
   4329                     BytesList.AddRange((byte[])obj[i]);
   4330                 else if (obj[i] is short)
   4331                     BytesList.AddRange(BitConverter.GetBytes((short)obj[i]));
   4332                 else if (obj[i] is ushort)
   4333                     BytesList.AddRange(BitConverter.GetBytes((ushort)obj[i]));
   4334                 else if (obj[i] is int)
   4335                     BytesList.AddRange(BitConverter.GetBytes((int)obj[i]));
   4336                 else if (obj[i] is uint)
   4337                     BytesList.AddRange(BitConverter.GetBytes((uint)obj[i]));
   4338                 else if (obj[i] is long)
   4339                     BytesList.AddRange(BitConverter.GetBytes((long)obj[i]));
   4340                 else if (obj[i] is ulong)
   4341                     BytesList.AddRange(BitConverter.GetBytes((ulong)obj[i]));
   4342             }
   4343             return BytesList.ToArray();
   4344         }
   4345 
   4346         /// <summary>
   4347         /// Copies a range of elements from an Array starting at the specified source index.
   4348         /// The length and the index are specified as 32-bit integers.
   4349         /// </summary>
   4350         /// <param name="SrcArray">The <see cref="Array"/> that contains the data to copy.</param>
   4351         /// <param name="Offset">A 32-bit integer that represents the index in the
   4352         /// <see cref="SrcArray"/> at which copying begins.</param>
   4353         /// <param name="Count">A 32-bit integer that represents the number of elements to copy.</param>
   4354         /// <returns></returns>
   4355         public static T[] GetElements<T>(T[] SrcArray, int Offset, int Count)
   4356         {
   4357             if (SrcArray == null)
   4358                 throw new ArgumentNullException(nameof(SrcArray) + " is null!");
   4359 
   4360             if (Offset >= SrcArray.Length || Offset < 0)
   4361                 throw new ArgumentOutOfRangeException(nameof(Offset) + " has wrong value!");
   4362 
   4363             if (Count <= 0 || Offset + Count > SrcArray.Length)
   4364                 throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!");
   4365 
   4366             T[] Buff = new T[Count];
   4367             Array.Copy(SrcArray, Offset, Buff, 0, Buff.Length);
   4368             return Buff;
   4369         }
   4370 
   4371         /// <summary>
   4372         /// Compare N-dimensional Arrays.
   4373         /// </summary>
   4374         /// <typeparam name="T">Arrays Type.</typeparam>
   4375         /// <param name="item1">First Array.</param>
   4376         /// <param name="item2">Second Array.</param>
   4377         /// <returns>True, if Arrays are equal.</returns>
   4378         public static bool IsArraysEqual<T>(T[] item1, T[] item2)
   4379         {
   4380             if (ReferenceEquals(item1, item2))
   4381                 return true;
   4382 
   4383             if (item1 == null || item2 == null)
   4384                 return false;
   4385 
   4386             if (item1.Length != item2.Length)
   4387                 return false;
   4388 
   4389             EqualityComparer<T> comparer = EqualityComparer<T>.Default;
   4390             for (int i = 0; i < item1.Length; i++)
   4391                 if (!comparer.Equals(item1[i], item2[i]))
   4392                     return false;
   4393             return true;
   4394         }
   4395 
   4396         /// <summary>
   4397         /// Compare Lists.
   4398         /// </summary>
   4399         /// <typeparam name="T">List Type.</typeparam>
   4400         /// <param name="item1">First List.</param>
   4401         /// <param name="item2">Second List.</param>
   4402         /// <returns>True, if Lists are equal.</returns>
   4403         public static bool IsListsEqual<T>(List<T> item1, List<T> item2)
   4404         {
   4405             if (ReferenceEquals(item1, item2))
   4406                 return true;
   4407 
   4408             if (item1 == null || item2 == null)
   4409                 return false;
   4410 
   4411             if (item1.Count != item2.Count)
   4412                 return false;
   4413 
   4414             for (int i = 0; i < item1.Count; i++)
   4415                 if (!item1[i].Equals(item2[i]))
   4416                     return false;
   4417             return true;
   4418         }
   4419 
   4420         /// <summary>
   4421         /// Compare elements in one Array with different offsets.
   4422         /// </summary>
   4423         /// <typeparam name="T">Array type.</typeparam>
   4424         /// <param name="Arr">Some Array.</param>
   4425         /// <param name="Offset1">First offset.</param>
   4426         /// <param name="Offset2">Second offset.</param>
   4427         /// <param name="Count">Elements count which must be compared.</param>
   4428         /// <returns></returns>
   4429         public static bool IsElementsEqual<T>(T[] Arr, int Offset1, int Offset2, int Count)
   4430         {
   4431             if (Arr == null)
   4432                 throw new ArgumentNullException(nameof(Arr) + " is null!");
   4433 
   4434             if (Offset1 >= Arr.Length || Offset1 < 0)
   4435                 throw new ArgumentOutOfRangeException(nameof(Offset1) + " has wrong value!");
   4436 
   4437             if (Offset2 >= Arr.Length || Offset2 < 0)
   4438                 throw new ArgumentOutOfRangeException(nameof(Offset2) + " has wrong value!");
   4439 
   4440             if (Count <= 0 || Offset1 + Count > Arr.Length || Offset2 + Count > Arr.Length)
   4441                 throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!");
   4442 
   4443             if (Offset1 == Offset2)
   4444                 return true;
   4445 
   4446             for (int i = 0; i < Count; i++)
   4447                 if (!Arr[Offset1 + i].Equals(Arr[Offset2 + i]))
   4448                     return false;
   4449 
   4450             return true;
   4451         }
   4452     }
   4453     #endregion
   4454 
   4455     public class TGA : ICloneable
   4456     {
   4457         public TgaHeader Header = new TgaHeader();
   4458         public TgaImgOrColMap ImageOrColorMapArea = new TgaImgOrColMap();
   4459         public TgaDevArea DevArea = null;
   4460         public TgaExtArea ExtArea = null;
   4461         public TgaFooter Footer = null;
   4462 
   4463         #region TGA Creation, Loading, Saving (all are public, have reference to private metods).
   4464         /// <summary>
   4465         /// Create new empty <see cref="TGA"/> istance.
   4466         /// </summary>
   4467         public TGA()
   4468         {
   4469         }
   4470 
   4471         /// <summary>
   4472         /// Create <see cref="TGA"/> instance with some params. If it must have ColorMap,
   4473         /// check all ColorMap fields and settings after.
   4474         /// </summary>
   4475         /// <param name="Width">Image Width.</param>
   4476         /// <param name="Height">Image Height.</param>
   4477         /// <param name="PixDepth">Image Pixel Depth (bits / pixel), set ColorMap bpp after, if needed!</param>
   4478         /// <param name="ImgType">Image Type (is RLE compressed, ColorMapped or GrayScaled).</param>
   4479         /// <param name="AttrBits">Set numder of Attrbute bits (Alpha channel bits), default: 0, 1, 8.</param>
   4480         /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
   4481         public TGA(ushort Width, ushort Height, TgaPixelDepth PixDepth = TgaPixelDepth.Bpp24,
   4482             TgaImageType ImgType = TgaImageType.Uncompressed_TrueColor, byte AttrBits = 0, bool NewFormat = true)
   4483         {
   4484             if (Width <= 0 || Height <= 0 || PixDepth == TgaPixelDepth.Other)
   4485             {
   4486                 Width = Height = 0;
   4487                 PixDepth = TgaPixelDepth.Other;
   4488                 ImgType = TgaImageType.NoImageData;
   4489                 AttrBits = 0;
   4490             }
   4491             else
   4492             {
   4493                 int BytesPerPixel = (int)Math.Ceiling((double)PixDepth / 8.0);
   4494                 ImageOrColorMapArea.ImageData = new byte[Width * Height * BytesPerPixel];
   4495 
   4496                 if (ImgType == TgaImageType.Uncompressed_ColorMapped || ImgType == TgaImageType.RLE_ColorMapped)
   4497                 {
   4498                     Header.ColorMapType = TgaColorMapType.ColorMap;
   4499                     Header.ColorMapSpec.FirstEntryIndex = 0;
   4500                     Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)Math.Ceiling((double)PixDepth / 8);
   4501                 }
   4502             }
   4503 
   4504             Header.ImageType = ImgType;
   4505             Header.ImageSpec.ImageWidth = Width;
   4506             Header.ImageSpec.ImageHeight = Height;
   4507             Header.ImageSpec.PixelDepth = PixDepth;
   4508             Header.ImageSpec.ImageDescriptor.AlphaChannelBits = AttrBits;
   4509 
   4510             if (NewFormat)
   4511             {
   4512                 Footer = new TgaFooter();
   4513                 ExtArea = new TgaExtArea();
   4514                 ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
   4515                 ExtArea.AttributesType = (AttrBits > 0 ? TgaAttrType.UsefulAlpha : TgaAttrType.NoAlpha);
   4516             }
   4517         }
   4518 
   4519         /// <summary>
   4520         /// Make <see cref="TGA"/> from some <see cref="TGA"/> instance.
   4521         /// Equal to <see cref="TGA.Clone()"/> function.
   4522         /// </summary>
   4523         /// <param name="tga">Original <see cref="TGA"/> instance.</param>
   4524         public TGA(TGA tga)
   4525         {
   4526             Header = tga.Header.Clone();
   4527             ImageOrColorMapArea = tga.ImageOrColorMapArea.Clone();
   4528             DevArea = tga.DevArea.Clone();
   4529             ExtArea = tga.ExtArea.Clone();
   4530             Footer = tga.Footer.Clone();
   4531         }
   4532 
   4533         /// <summary>
   4534         /// Load <see cref="TGA"/> from file.
   4535         /// </summary>
   4536         /// <param name="filename">Full path to TGA file.</param>
   4537         /// <returns>Loaded <see cref="TGA"/> file.</returns>
   4538         public TGA(string filename)
   4539         {
   4540             LoadFunc(filename);
   4541         }
   4542 
   4543         /// <summary>
   4544         /// Make <see cref="TGA"/> from bytes array.
   4545         /// </summary>
   4546         /// <param name="bytes">Bytes array (same like TGA File).</param>
   4547         public TGA(byte[] bytes)
   4548         {
   4549             LoadFunc(bytes);
   4550         }
   4551 
   4552         /// <summary>
   4553         /// Make <see cref="TGA"/> from <see cref="Stream"/>.
   4554         /// For file opening better use <see cref="FromFile(string)"/>.
   4555         /// </summary>
   4556         /// <param name="stream">Some stream. You can use a lot of Stream types, but Stream must support:
   4557         /// <see cref="Stream.CanSeek"/> and <see cref="Stream.CanRead"/>.</param>
   4558         public TGA(Stream stream)
   4559         {
   4560             LoadFunc(stream);
   4561         }
   4562 
   4563         /// <summary>
   4564         /// Make <see cref="TGA"/> from <see cref="Bitmap"/>.
   4565         /// </summary>
   4566         /// <param name="bmp">Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's.</param>
   4567         /// <param name="UseRLE">Use RLE Compression?</param>
   4568         /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
   4569         /// <param name="ColorMap2BytesEntry">Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32.</param>
   4570         public TGA(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false)
   4571         {
   4572             LoadFunc(bmp, UseRLE, NewFormat, ColorMap2BytesEntry);
   4573         }
   4574 
   4575         /// <summary>
   4576         /// Load <see cref="TGA"/> from file.
   4577         /// </summary>
   4578         /// <param name="filename">Full path to TGA file.</param>
   4579         /// <returns>Loaded <see cref="TGA"/> file.</returns>
   4580         public static TGA FromFile(string filename)
   4581         {
   4582             return new TGA(filename);
   4583         }
   4584 
   4585         /// <summary>
   4586         /// Make <see cref="TGA"/> from bytes array.
   4587         /// </summary>
   4588         /// <param name="bytes">Bytes array (same like TGA File).</param>
   4589         public static TGA FromBytes(byte[] bytes)
   4590         {
   4591             return new TGA(bytes);
   4592         }
   4593 
   4594         /// <summary>
   4595         /// Make <see cref="TGA"/> from <see cref="Stream"/>.
   4596         /// For file opening better use <see cref="FromFile(string)"/>.
   4597         /// </summary>
   4598         /// <param name="stream">Some stream. You can use a lot of Stream types, but Stream must support:
   4599         /// <see cref="Stream.CanSeek"/> and <see cref="Stream.CanRead"/>.</param>
   4600         public static TGA FromStream(Stream stream)
   4601         {
   4602             return new TGA(stream);
   4603         }
   4604 
   4605         /// <summary>
   4606         /// Make <see cref="TGA"/> from <see cref="Bitmap"/>.
   4607         /// </summary>
   4608         /// <param name="bmp">Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's.</param>
   4609         /// <param name="UseRLE">Use RLE Compression?</param>
   4610         /// <param name="NewFormat">Use new 2.0 TGA XFile format?</param>
   4611         /// <param name="ColorMap2BytesEntry">Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32.</param>
   4612         public static TGA FromBitmap(Bitmap bmp, bool UseRLE = false,
   4613             bool NewFormat = true, bool ColorMap2BytesEntry = false)
   4614         {
   4615             return new TGA(bmp, UseRLE, NewFormat, ColorMap2BytesEntry);
   4616         }
   4617 
   4618         /// <summary>
   4619         /// Save <see cref="TGA"/> to file.
   4620         /// </summary>
   4621         /// <param name="filename">Full path to file.</param>
   4622         /// <returns>Return "true", if all done or "false", if failed.</returns>
   4623         public bool Save(string filename)
   4624         {
   4625             try
   4626             {
   4627                 bool Result = false;
   4628                 using (FileStream Fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
   4629                 {
   4630                     using (MemoryStream Ms = new MemoryStream())
   4631                     {
   4632                         Result = SaveFunc(Ms);
   4633                         Ms.WriteTo(Fs);
   4634                         Fs.Flush();
   4635                     }
   4636                 }
   4637                 return Result;
   4638             }
   4639             catch
   4640             {
   4641                 return false;
   4642             }
   4643         }
   4644 
   4645         /// <summary>
   4646         /// Save <see cref="TGA"/> to <see cref="Stream"/>.
   4647         /// </summary>
   4648         /// <param name="stream">Some stream, it must support: <see cref="Stream.CanWrite"/>.</param>
   4649         /// <returns>Return "true", if all done or "false", if failed.</returns>
   4650         public bool Save(Stream stream)
   4651         {
   4652             return SaveFunc(stream);
   4653         }
   4654         #endregion
   4655 
   4656         /// <summary>
   4657         /// Gets or Sets Image Width (see <see cref="Header.ImageSpec.ImageWidth"/>).
   4658         /// </summary>
   4659         public ushort Width
   4660         {
   4661             get { return Header.ImageSpec.ImageWidth; }
   4662             set { Header.ImageSpec.ImageWidth = value; }
   4663         }
   4664 
   4665         /// <summary>
   4666         /// Gets or Sets Image Height (see <see cref="Header.ImageSpec.ImageHeight"/>).
   4667         /// </summary>
   4668         public ushort Height
   4669         {
   4670             get { return Header.ImageSpec.ImageHeight; }
   4671             set { Header.ImageSpec.ImageHeight = value; }
   4672         }
   4673 
   4674         /// <summary>
   4675         /// Gets or Sets <see cref="TGA"/> image Size.
   4676         /// </summary>
   4677         public Size Size
   4678         {
   4679             get { return new Size(Header.ImageSpec.ImageWidth, Header.ImageSpec.ImageHeight); }
   4680             set
   4681             {
   4682                 Header.ImageSpec.ImageWidth = (ushort)value.Width;
   4683                 Header.ImageSpec.ImageHeight = (ushort)value.Height;
   4684             }
   4685         }
   4686 
   4687         /// <summary>
   4688         /// Make full independed copy of <see cref="TGA"/>.
   4689         /// </summary>
   4690         /// <returns>Full independed copy of <see cref="TGA"/>.</returns>
   4691         public TGA Clone()
   4692         {
   4693             return new TGA(this);
   4694         }
   4695 
   4696         object ICloneable.Clone()
   4697         {
   4698             return Clone();
   4699         }
   4700 
   4701         /// <summary>
   4702         /// Flip <see cref="TGA"/> directions, for more info see <see cref="TgaImgOrigin"/>.
   4703         /// </summary>
   4704         /// <param name="Horizontal">Flip horizontal.</param>
   4705         /// <param name="Vertical">Flip vertical.</param>
   4706         public void Flip(bool Horizontal = false, bool Vertical = false)
   4707         {
   4708             int NewOrigin = (int)Header.ImageSpec.ImageDescriptor.ImageOrigin;
   4709             NewOrigin = NewOrigin ^ ((Vertical ? 0x20 : 0) | (Horizontal ? 0x10 : 0));
   4710             Header.ImageSpec.ImageDescriptor.ImageOrigin = (TgaImgOrigin)NewOrigin;
   4711         }
   4712 
   4713         /// <summary>
   4714         /// Get information from TGA image.
   4715         /// </summary>
   4716         /// <returns>MultiLine string with info fields (one per line).</returns>
   4717         public string GetInfo()
   4718         {
   4719             StringBuilder SB = new StringBuilder();
   4720 
   4721             SB.AppendLine("Header:");
   4722             SB.AppendLine("\tID Length = " + Header.IDLength);
   4723             SB.AppendLine("\tImage Type = " + Header.ImageType);
   4724             SB.AppendLine("\tHeader -> ImageSpec:");
   4725             SB.AppendLine("\t\tImage Width = " + Header.ImageSpec.ImageWidth);
   4726             SB.AppendLine("\t\tImage Height = " + Header.ImageSpec.ImageHeight);
   4727             SB.AppendLine("\t\tPixel Depth = " + Header.ImageSpec.PixelDepth);
   4728             SB.AppendLine("\t\tImage Descriptor (AsByte) = " + Header.ImageSpec.ImageDescriptor.ToByte());
   4729             SB.AppendLine("\t\tImage Descriptor -> AttributeBits = " + Header.ImageSpec.ImageDescriptor.AlphaChannelBits);
   4730             SB.AppendLine("\t\tImage Descriptor -> ImageOrigin = " + Header.ImageSpec.ImageDescriptor.ImageOrigin);
   4731             SB.AppendLine("\t\tX_Origin = " + Header.ImageSpec.X_Origin);
   4732             SB.AppendLine("\t\tY_Origin = " + Header.ImageSpec.Y_Origin);
   4733             SB.AppendLine("\tColorMap Type = " + Header.ColorMapType);
   4734             SB.AppendLine("\tHeader -> ColorMapSpec:");
   4735             SB.AppendLine("\t\tColorMap Entry Size = " + Header.ColorMapSpec.ColorMapEntrySize);
   4736             SB.AppendLine("\t\tColorMap Length = " + Header.ColorMapSpec.ColorMapLength);
   4737             SB.AppendLine("\t\tFirstEntry Index = " + Header.ColorMapSpec.FirstEntryIndex);
   4738 
   4739             SB.AppendLine("\nImage / Color Map Area:");
   4740             if (Header.IDLength > 0 && ImageOrColorMapArea.ImageID != null)
   4741                 SB.AppendLine("\tImage ID = \"" + ImageOrColorMapArea.ImageID.GetString() + "\"");
   4742             else
   4743                 SB.AppendLine("\tImage ID = null");
   4744 
   4745             if (ImageOrColorMapArea.ImageData != null)
   4746                 SB.AppendLine("\tImage Data Length = " + ImageOrColorMapArea.ImageData.Length);
   4747             else
   4748                 SB.AppendLine("\tImage Data = null");
   4749 
   4750             if (ImageOrColorMapArea.ColorMapData != null)
   4751                 SB.AppendLine("\tColorMap Data Length = " + ImageOrColorMapArea.ColorMapData.Length);
   4752             else
   4753                 SB.AppendLine("\tColorMap Data = null");
   4754 
   4755             SB.AppendLine("\nDevelopers Area:");
   4756             if (DevArea != null)
   4757                 SB.AppendLine("\tCount = " + DevArea.Count);
   4758             else
   4759                 SB.AppendLine("\tDevArea = null");
   4760 
   4761             SB.AppendLine("\nExtension Area:");
   4762             if (ExtArea != null)
   4763             {
   4764                 SB.AppendLine("\tExtension Size = " + ExtArea.ExtensionSize);
   4765                 SB.AppendLine("\tAuthor Name = \"" + ExtArea.AuthorName.GetString() + "\"");
   4766                 SB.AppendLine("\tAuthor Comments = \"" + ExtArea.AuthorComments.GetString() + "\"");
   4767                 SB.AppendLine("\tDate / Time Stamp = " + ExtArea.DateTimeStamp);
   4768                 SB.AppendLine("\tJob Name / ID = \"" + ExtArea.JobNameOrID.GetString() + "\"");
   4769                 SB.AppendLine("\tJob Time = " + ExtArea.JobTime);
   4770                 SB.AppendLine("\tSoftware ID = \"" + ExtArea.SoftwareID.GetString() + "\"");
   4771                 SB.AppendLine("\tSoftware Version = \"" + ExtArea.SoftVersion + "\"");
   4772                 SB.AppendLine("\tKey Color = " + ExtArea.KeyColor);
   4773                 SB.AppendLine("\tPixel Aspect Ratio = " + ExtArea.PixelAspectRatio);
   4774                 SB.AppendLine("\tGamma Value = " + ExtArea.GammaValue);
   4775                 SB.AppendLine("\tColor Correction Table Offset = " + ExtArea.ColorCorrectionTableOffset);
   4776                 SB.AppendLine("\tPostage Stamp Offset = " + ExtArea.PostageStampOffset);
   4777                 SB.AppendLine("\tScan Line Offset = " + ExtArea.ScanLineOffset);
   4778                 SB.AppendLine("\tAttributes Type = " + ExtArea.AttributesType);
   4779 
   4780                 if (ExtArea.ScanLineTable != null)
   4781                     SB.AppendLine("\tScan Line Table = " + ExtArea.ScanLineTable.Length);
   4782                 else
   4783                     SB.AppendLine("\tScan Line Table = null");
   4784 
   4785                 if (ExtArea.PostageStampImage != null)
   4786                     SB.AppendLine("\tPostage Stamp Image: " + ExtArea.PostageStampImage.ToString());
   4787                 else
   4788                     SB.AppendLine("\tPostage Stamp Image = null");
   4789 
   4790                 SB.AppendLine("\tColor Correction Table = " + (ExtArea.ColorCorrectionTable != null));
   4791             }
   4792             else
   4793                 SB.AppendLine("\tExtArea = null");
   4794 
   4795             SB.AppendLine("\nFooter:");
   4796             if (Footer != null)
   4797             {
   4798                 SB.AppendLine("\tExtension Area Offset = " + Footer.ExtensionAreaOffset);
   4799                 SB.AppendLine("\tDeveloper Directory Offset = " + Footer.DeveloperDirectoryOffset);
   4800                 SB.AppendLine("\tSignature (Full) = \"" + Footer.Signature.ToString() +
   4801                     Footer.ReservedCharacter.ToString() + Footer.BinaryZeroStringTerminator.ToString() + "\"");
   4802             }
   4803             else
   4804                 SB.AppendLine("\tFooter = null");
   4805 
   4806             return SB.ToString();
   4807         }
   4808 
   4809         /// <summary>
   4810         /// Check and update all fields with data length and offsets.
   4811         /// </summary>
   4812         /// <returns>Return "true", if all OK or "false", if checking failed.</returns>
   4813         public bool CheckAndUpdateOffsets(out string ErrorStr)
   4814         {
   4815             ErrorStr = String.Empty;
   4816 
   4817             if (Header == null)
   4818             {
   4819                 ErrorStr = "Header = null";
   4820                 return false;
   4821             }
   4822 
   4823             if (ImageOrColorMapArea == null)
   4824             {
   4825                 ErrorStr = "ImageOrColorMapArea = null";
   4826                 return false;
   4827             }
   4828 
   4829             uint Offset = TgaHeader.Size; // Virtual Offset
   4830 
   4831             #region Header
   4832             if (ImageOrColorMapArea.ImageID != null)
   4833             {
   4834                 int StrMaxLen = 255;
   4835                 if (ImageOrColorMapArea.ImageID.UseEndingChar)
   4836                     StrMaxLen--;
   4837 
   4838                 Header.IDLength = (byte)Math.Min(ImageOrColorMapArea.ImageID.OriginalString.Length, StrMaxLen);
   4839                 ImageOrColorMapArea.ImageID.Length = Header.IDLength;
   4840                 Offset += Header.IDLength;
   4841             }
   4842             else
   4843                 Header.IDLength = 0;
   4844             #endregion
   4845 
   4846             #region ColorMap
   4847             if (Header.ColorMapType != TgaColorMapType.NoColorMap)
   4848             {
   4849                 if (Header.ColorMapSpec == null)
   4850                 {
   4851                     ErrorStr = "Header.ColorMapSpec = null";
   4852                     return false;
   4853                 }
   4854 
   4855                 if (Header.ColorMapSpec.ColorMapLength == 0)
   4856                 {
   4857                     ErrorStr = "Header.ColorMapSpec.ColorMapLength = 0";
   4858                     return false;
   4859                 }
   4860 
   4861                 if (ImageOrColorMapArea.ColorMapData == null)
   4862                 {
   4863                     ErrorStr = "ImageOrColorMapArea.ColorMapData = null";
   4864                     return false;
   4865                 }
   4866 
   4867                 int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0);
   4868                 int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel;
   4869 
   4870                 if (LenBytes != ImageOrColorMapArea.ColorMapData.Length)
   4871                 {
   4872                     ErrorStr = "ImageOrColorMapArea.ColorMapData.Length has wrong size!";
   4873                     return false;
   4874                 }
   4875 
   4876                 Offset += (uint)ImageOrColorMapArea.ColorMapData.Length;
   4877             }
   4878             #endregion
   4879 
   4880             #region Image Data
   4881             int BytesPerPixel = 0;
   4882             if (Header.ImageType != TgaImageType.NoImageData)
   4883             {
   4884                 if (Header.ImageSpec == null)
   4885                 {
   4886                     ErrorStr = "Header.ImageSpec = null";
   4887                     return false;
   4888                 }
   4889 
   4890                 if (Header.ImageSpec.ImageWidth == 0 || Header.ImageSpec.ImageHeight == 0)
   4891                 {
   4892                     ErrorStr = "Header.ImageSpec.ImageWidth = 0 or Header.ImageSpec.ImageHeight = 0";
   4893                     return false;
   4894                 }
   4895 
   4896                 if (ImageOrColorMapArea.ImageData == null)
   4897                 {
   4898                     ErrorStr = "ImageOrColorMapArea.ImageData = null";
   4899                     return false;
   4900                 }
   4901 
   4902                 BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
   4903                 if (Width * Height * BytesPerPixel != ImageOrColorMapArea.ImageData.Length)
   4904                 {
   4905                     ErrorStr = "ImageOrColorMapArea.ImageData.Length has wrong size!";
   4906                     return false;
   4907                 }
   4908 
   4909                 if (Header.ImageType >= TgaImageType.RLE_ColorMapped &&
   4910                     Header.ImageType <= TgaImageType.RLE_BlackWhite)
   4911                 {
   4912                     byte[] RLE = RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height);
   4913                     if (RLE == null)
   4914                     {
   4915                         ErrorStr = "RLE Compressing error! Check Image Data size.";
   4916                         return false;
   4917                     }
   4918 
   4919                     Offset += (uint)RLE.Length;
   4920                     RLE = null;
   4921                 }
   4922                 else
   4923                     Offset += (uint)ImageOrColorMapArea.ImageData.Length;
   4924             }
   4925             #endregion
   4926 
   4927             #region Footer, DevArea, ExtArea
   4928             if (Footer != null)
   4929             {
   4930                 #region DevArea
   4931                 if (DevArea != null)
   4932                 {
   4933                     int DevAreaCount = DevArea.Count;
   4934                     for (int i = 0; i < DevAreaCount; i++)
   4935                         if (DevArea[i] == null || DevArea[i].FieldSize <= 0) //Del Empty Entries
   4936                         {
   4937                             DevArea.Entries.RemoveAt(i);
   4938                             DevAreaCount--;
   4939                             i--;
   4940                         }
   4941 
   4942                     if (DevArea.Count <= 0)
   4943                         Footer.DeveloperDirectoryOffset = 0;
   4944 
   4945                     if (DevArea.Count > 2)
   4946                     {
   4947                         DevArea.Entries.Sort((a, b) => { return a.Tag.CompareTo(b.Tag); });
   4948                         for (int i = 0; i < DevArea.Count - 1; i++)
   4949                             if (DevArea[i].Tag == DevArea[i + 1].Tag)
   4950                             {
   4951                                 ErrorStr = "DevArea Enties has same Tags!";
   4952                                 return false;
   4953                             }
   4954                     }
   4955 
   4956                     for (int i = 0; i < DevArea.Count; i++)
   4957                     {
   4958                         DevArea[i].Offset = Offset;
   4959                         Offset += (uint)DevArea[i].FieldSize;
   4960                     }
   4961 
   4962                     Footer.DeveloperDirectoryOffset = Offset;
   4963                     Offset += (uint)(DevArea.Count * 10 + 2);
   4964                 }
   4965                 else
   4966                     Footer.DeveloperDirectoryOffset = 0;
   4967                 #endregion
   4968 
   4969                 #region ExtArea
   4970                 if (ExtArea != null)
   4971                 {
   4972                     ExtArea.ExtensionSize = TgaExtArea.MinSize;
   4973                     if (ExtArea.OtherDataInExtensionArea != null)
   4974                         ExtArea.ExtensionSize += (ushort)ExtArea.OtherDataInExtensionArea.Length;
   4975 
   4976                     ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
   4977 
   4978                     Footer.ExtensionAreaOffset = Offset;
   4979                     Offset += ExtArea.ExtensionSize;
   4980 
   4981                     #region ScanLineTable
   4982                     if (ExtArea.ScanLineTable == null)
   4983                         ExtArea.ScanLineOffset = 0;
   4984                     else
   4985                     {
   4986                         if (ExtArea.ScanLineTable.Length != Height)
   4987                         {
   4988                             ErrorStr = "ExtArea.ScanLineTable.Length != Height";
   4989                             return false;
   4990                         }
   4991 
   4992                         ExtArea.ScanLineOffset = Offset;
   4993                         Offset += (uint)(ExtArea.ScanLineTable.Length * 4);
   4994                     }
   4995                     #endregion
   4996 
   4997                     #region PostageStampImage
   4998                     if (ExtArea.PostageStampImage == null)
   4999                         ExtArea.PostageStampOffset = 0;
   5000                     else
   5001                     {
   5002                         if (ExtArea.PostageStampImage.Width == 0 || ExtArea.PostageStampImage.Height == 0)
   5003                         {
   5004                             ErrorStr = "ExtArea.PostageStampImage Width or Height is equal 0!";
   5005                             return false;
   5006                         }
   5007 
   5008                         if (ExtArea.PostageStampImage.Data == null)
   5009                         {
   5010                             ErrorStr = "ExtArea.PostageStampImage.Data == null";
   5011                             return false;
   5012                         }
   5013 
   5014                         int PImgSB = ExtArea.PostageStampImage.Width * ExtArea.PostageStampImage.Height * BytesPerPixel;
   5015                         if (Header.ImageType != TgaImageType.NoImageData &&
   5016                             ExtArea.PostageStampImage.Data.Length != PImgSB)
   5017                         {
   5018                             ErrorStr = "ExtArea.PostageStampImage.Data.Length is wrong!";
   5019                             return false;
   5020                         }
   5021 
   5022 
   5023                         ExtArea.PostageStampOffset = Offset;
   5024                         Offset += (uint)(ExtArea.PostageStampImage.Data.Length);
   5025                     }
   5026                     #endregion
   5027 
   5028                     #region ColorCorrectionTable
   5029                     if (ExtArea.ColorCorrectionTable == null)
   5030                         ExtArea.ColorCorrectionTableOffset = 0;
   5031                     else
   5032                     {
   5033                         if (ExtArea.ColorCorrectionTable.Length != 1024)
   5034                         {
   5035                             ErrorStr = "ExtArea.ColorCorrectionTable.Length != 256 * 4";
   5036                             return false;
   5037                         }
   5038 
   5039                         ExtArea.ColorCorrectionTableOffset = Offset;
   5040                         Offset += (uint)(ExtArea.ColorCorrectionTable.Length * 2);
   5041                     }
   5042                     #endregion
   5043                 }
   5044                 else
   5045                     Footer.ExtensionAreaOffset = 0;
   5046                 #endregion
   5047 
   5048                 #region Footer
   5049                 if (Footer.ToBytes().Length != TgaFooter.Size)
   5050                 {
   5051                     ErrorStr = "Footer.Length is wrong!";
   5052                     return false;
   5053                 }
   5054 
   5055                 Offset += TgaFooter.Size;
   5056                 #endregion
   5057             }
   5058             #endregion
   5059 
   5060             return true;
   5061         }
   5062 
   5063         #region Convert
   5064         /// <summary>
   5065         /// Convert <see cref="TGA"/> to <see cref="Bitmap"/>.
   5066         /// </summary>
   5067         /// <param name="ForceUseAlpha">Force use alpha channel.</param>
   5068         /// <returns>Bitmap or null, on error.</returns>
   5069         public Bitmap ToBitmap(bool ForceUseAlpha = false)
   5070         {
   5071             return ToBitmapFunc(ForceUseAlpha, false);
   5072         }
   5073 
   5074         /// <summary>
   5075         /// Convert <see cref="TGA"/> to bytes array.
   5076         /// </summary>
   5077         /// <returns>Bytes array, (equal to saved file, but in memory) or null (on error).</returns>
   5078         public byte[] ToBytes()
   5079         {
   5080             try
   5081             {
   5082                 byte[] Bytes;
   5083                 using (MemoryStream ms = new MemoryStream())
   5084                 {
   5085                     Save(ms);
   5086                     Bytes = ms.ToArray();
   5087                     ms.Flush();
   5088                 }
   5089                 return Bytes;
   5090             }
   5091             catch
   5092             {
   5093                 return null;
   5094             }
   5095         }
   5096 
   5097         /// <summary>
   5098         /// Convert TGA Image to new XFile format (v2.0).
   5099         /// </summary>
   5100         public void ToNewFormat()
   5101         {
   5102             if (Footer == null)
   5103                 Footer = new TgaFooter();
   5104 
   5105             if (ExtArea == null)
   5106             {
   5107                 ExtArea = new TgaExtArea();
   5108 
   5109                 ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
   5110 
   5111                 if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0)
   5112                     ExtArea.AttributesType = TgaAttrType.UsefulAlpha;
   5113                 else
   5114                     ExtArea.AttributesType = TgaAttrType.NoAlpha;
   5115             }
   5116         }
   5117         #endregion
   5118 
   5119         #region Private functions
   5120         bool LoadFunc(string filename)
   5121         {
   5122             if (!File.Exists(filename))
   5123                 throw new FileNotFoundException("File: \"" + filename + "\" not found!");
   5124 
   5125             try
   5126             {
   5127                 using (FileStream FS = new FileStream(filename, FileMode.Open, FileAccess.Read))
   5128                     return LoadFunc(FS);
   5129             }
   5130             catch
   5131             {
   5132                 return false;
   5133             }
   5134         }
   5135 
   5136         bool LoadFunc(byte[] bytes)
   5137         {
   5138             if (bytes == null)
   5139                 throw new ArgumentNullException();
   5140 
   5141             try
   5142             {
   5143                 using (MemoryStream FS = new MemoryStream(bytes, false))
   5144                     return LoadFunc(FS);
   5145             }
   5146             catch
   5147             {
   5148                 return false;
   5149             }
   5150         }
   5151 
   5152         bool LoadFunc(Stream stream)
   5153         {
   5154             if (stream == null)
   5155                 throw new ArgumentNullException();
   5156             if (!(stream.CanRead && stream.CanSeek))
   5157                 throw new FileLoadException("Stream reading or seeking is not avaiable!");
   5158 
   5159             try
   5160             {
   5161                 stream.Seek(0, SeekOrigin.Begin);
   5162                 BinaryReader Br = new BinaryReader(stream);
   5163 
   5164                 Header = new TgaHeader(Br.ReadBytes(TgaHeader.Size));
   5165 
   5166                 if (Header.IDLength > 0)
   5167                     ImageOrColorMapArea.ImageID = new TgaString(Br.ReadBytes(Header.IDLength));
   5168 
   5169                 if (Header.ColorMapSpec.ColorMapLength > 0)
   5170                 {
   5171                     int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0);
   5172                     int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel;
   5173                     ImageOrColorMapArea.ColorMapData = Br.ReadBytes(LenBytes);
   5174                 }
   5175 
   5176                 #region Read Image Data
   5177                 int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
   5178                 if (Header.ImageType != TgaImageType.NoImageData)
   5179                 {
   5180                     int ImageDataSize = Width * Height * BytesPerPixel;
   5181                     switch (Header.ImageType)
   5182                     {
   5183                         case TgaImageType.RLE_ColorMapped:
   5184                         case TgaImageType.RLE_TrueColor:
   5185                         case TgaImageType.RLE_BlackWhite:
   5186 
   5187                             int DataOffset = 0;
   5188                             byte PacketInfo;
   5189                             int PacketCount;
   5190                             byte[] RLE_Bytes, RLE_Part;
   5191                             ImageOrColorMapArea.ImageData = new byte[ImageDataSize];
   5192 
   5193                             do
   5194                             {
   5195                                 PacketInfo = Br.ReadByte(); //1 type bit and 7 count bits. Len = Count + 1.
   5196                                 PacketCount = (PacketInfo & 127) + 1;
   5197 
   5198                                 if (PacketInfo >= 128) // bit7 = 1, RLE
   5199                                 {
   5200                                     RLE_Bytes = new byte[PacketCount * BytesPerPixel];
   5201                                     RLE_Part = Br.ReadBytes(BytesPerPixel);
   5202                                     for (int i = 0; i < RLE_Bytes.Length; i++)
   5203                                         RLE_Bytes[i] = RLE_Part[i % BytesPerPixel];
   5204                                 }
   5205                                 else // RAW format
   5206                                     RLE_Bytes = Br.ReadBytes(PacketCount * BytesPerPixel);
   5207 
   5208                                 Buffer.BlockCopy(RLE_Bytes, 0, ImageOrColorMapArea.ImageData, DataOffset, RLE_Bytes.Length);
   5209                                 DataOffset += RLE_Bytes.Length;
   5210                             }
   5211                             while (DataOffset < ImageDataSize);
   5212                             RLE_Bytes = null;
   5213                             break;
   5214 
   5215                         case TgaImageType.Uncompressed_ColorMapped:
   5216                         case TgaImageType.Uncompressed_TrueColor:
   5217                         case TgaImageType.Uncompressed_BlackWhite:
   5218                             ImageOrColorMapArea.ImageData = Br.ReadBytes(ImageDataSize);
   5219                             break;
   5220                     }
   5221                 }
   5222                 #endregion
   5223 
   5224                 #region Try parse Footer
   5225                 stream.Seek(-TgaFooter.Size, SeekOrigin.End);
   5226                 uint FooterOffset = (uint)stream.Position;
   5227                 TgaFooter MbFooter = new TgaFooter(Br.ReadBytes(TgaFooter.Size));
   5228                 if (MbFooter.IsFooterCorrect)
   5229                 {
   5230                     Footer = MbFooter;
   5231                     uint DevDirOffset = Footer.DeveloperDirectoryOffset;
   5232                     uint ExtAreaOffset = Footer.ExtensionAreaOffset;
   5233 
   5234                     #region If Dev Area exist, read it.
   5235                     if (DevDirOffset != 0)
   5236                     {
   5237                         stream.Seek(DevDirOffset, SeekOrigin.Begin);
   5238                         DevArea = new TgaDevArea();
   5239                         uint NumberOfTags = Br.ReadUInt16();
   5240 
   5241                         ushort[] Tags = new ushort[NumberOfTags];
   5242                         uint[] TagOffsets = new uint[NumberOfTags];
   5243                         uint[] TagSizes = new uint[NumberOfTags];
   5244 
   5245                         for (int i = 0; i < NumberOfTags; i++)
   5246                         {
   5247                             Tags[i] = Br.ReadUInt16();
   5248                             TagOffsets[i] = Br.ReadUInt32();
   5249                             TagSizes[i] = Br.ReadUInt32();
   5250                         }
   5251 
   5252                         for (int i = 0; i < NumberOfTags; i++)
   5253                         {
   5254                             stream.Seek(TagOffsets[i], SeekOrigin.Begin);
   5255                             var Ent = new TgaDevEntry(Tags[i], TagOffsets[i], Br.ReadBytes((int)TagSizes[i]));
   5256                             DevArea.Entries.Add(Ent);
   5257                         }
   5258 
   5259                         Tags = null;
   5260                         TagOffsets = null;
   5261                         TagSizes = null;
   5262                     }
   5263                     #endregion
   5264 
   5265                     #region If Ext Area exist, read it.
   5266                     if (ExtAreaOffset != 0)
   5267                     {
   5268                         stream.Seek(ExtAreaOffset, SeekOrigin.Begin);
   5269                         ushort ExtAreaSize = Math.Max((ushort)TgaExtArea.MinSize, Br.ReadUInt16());
   5270                         stream.Seek(ExtAreaOffset, SeekOrigin.Begin);
   5271                         ExtArea = new TgaExtArea(Br.ReadBytes(ExtAreaSize));
   5272 
   5273                         if (ExtArea.ScanLineOffset > 0)
   5274                         {
   5275                             stream.Seek(ExtArea.ScanLineOffset, SeekOrigin.Begin);
   5276                             ExtArea.ScanLineTable = new uint[Height];
   5277                             for (int i = 0; i < ExtArea.ScanLineTable.Length; i++)
   5278                                 ExtArea.ScanLineTable[i] = Br.ReadUInt32();
   5279                         }
   5280 
   5281                         if (ExtArea.PostageStampOffset > 0)
   5282                         {
   5283                             stream.Seek(ExtArea.PostageStampOffset, SeekOrigin.Begin);
   5284                             byte W = Br.ReadByte();
   5285                             byte H = Br.ReadByte();
   5286                             int ImgDataSize = W * H * BytesPerPixel;
   5287                             if (ImgDataSize > 0)
   5288                                 ExtArea.PostageStampImage = new TgaPostageStampImage(W, H, Br.ReadBytes(ImgDataSize));
   5289                         }
   5290 
   5291                         if (ExtArea.ColorCorrectionTableOffset > 0)
   5292                         {
   5293                             stream.Seek(ExtArea.ColorCorrectionTableOffset, SeekOrigin.Begin);
   5294                             ExtArea.ColorCorrectionTable = new ushort[256 * 4];
   5295                             for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++)
   5296                                 ExtArea.ColorCorrectionTable[i] = Br.ReadUInt16();
   5297                         }
   5298                     }
   5299                     #endregion
   5300                 }
   5301                 #endregion
   5302 
   5303                 Br.Close();
   5304                 return true;
   5305             }
   5306             catch
   5307             {
   5308                 return false;
   5309             }
   5310         }
   5311 
   5312         bool LoadFunc(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false)
   5313         {
   5314             if (bmp == null)
   5315                 throw new ArgumentNullException();
   5316 
   5317             try
   5318             {
   5319                 Header.ImageSpec.ImageWidth = (ushort)bmp.Width;
   5320                 Header.ImageSpec.ImageHeight = (ushort)bmp.Height;
   5321                 Header.ImageSpec.ImageDescriptor.ImageOrigin = TgaImgOrigin.TopLeft;
   5322 
   5323                 switch (bmp.PixelFormat)
   5324                 {
   5325                     case PixelFormat.Indexed:
   5326                     case PixelFormat.Gdi:
   5327                     case PixelFormat.Alpha:
   5328                     case PixelFormat.Undefined:
   5329                     case PixelFormat.PAlpha:
   5330                     case PixelFormat.Extended:
   5331                     case PixelFormat.Max:
   5332                     case PixelFormat.Canonical:
   5333                     case PixelFormat.Format16bppRgb565:
   5334                     default:
   5335                         throw new FormatException(nameof(PixelFormat) + " is not supported!");
   5336 
   5337                     case PixelFormat.Format1bppIndexed:
   5338                     case PixelFormat.Format4bppIndexed:
   5339                     case PixelFormat.Format8bppIndexed:
   5340                     case PixelFormat.Format16bppGrayScale:
   5341                     case PixelFormat.Format16bppRgb555:
   5342                     case PixelFormat.Format16bppArgb1555:
   5343                     case PixelFormat.Format24bppRgb:
   5344                     case PixelFormat.Format32bppRgb:
   5345                     case PixelFormat.Format32bppArgb:
   5346                     case PixelFormat.Format32bppPArgb:
   5347                     case PixelFormat.Format48bppRgb:
   5348                     case PixelFormat.Format64bppArgb:
   5349                     case PixelFormat.Format64bppPArgb:
   5350 
   5351                         int bpp = Math.Max(8, Image.GetPixelFormatSize(bmp.PixelFormat));
   5352                         int BytesPP = bpp / 8;
   5353 
   5354                         if (bmp.PixelFormat == PixelFormat.Format16bppRgb555)
   5355                             bpp = 15;
   5356 
   5357                         bool IsAlpha = Image.IsAlphaPixelFormat(bmp.PixelFormat);
   5358                         bool IsPreAlpha = IsAlpha && bmp.PixelFormat.ToString().EndsWith("PArgb");
   5359                         bool IsColorMapped = bmp.PixelFormat.ToString().EndsWith("Indexed");
   5360 
   5361                         Header.ImageSpec.PixelDepth = (TgaPixelDepth)(BytesPP * 8);
   5362 
   5363                         if (IsAlpha)
   5364                         {
   5365                             Header.ImageSpec.ImageDescriptor.AlphaChannelBits = (byte)(BytesPP * 2);
   5366 
   5367                             if (bmp.PixelFormat == PixelFormat.Format16bppArgb1555)
   5368                                 Header.ImageSpec.ImageDescriptor.AlphaChannelBits = 1;
   5369                         }
   5370 
   5371                         #region ColorMap
   5372                         bool IsGrayImage = (bmp.PixelFormat == PixelFormat.Format16bppGrayScale | IsColorMapped);
   5373 
   5374                         if (IsColorMapped && bmp.Palette != null)
   5375                         {
   5376                             Color[] Colors = bmp.Palette.Entries;
   5377 
   5378                             #region Analyze ColorMapType
   5379                             int AlphaSum = 0;
   5380                             bool ColorMapUseAlpha = false;
   5381 
   5382                             for (int i = 0; i < Colors.Length; i++)
   5383                             {
   5384                                 IsGrayImage &= (Colors[i].R == Colors[i].G && Colors[i].G == Colors[i].B);
   5385                                 ColorMapUseAlpha |= (Colors[i].A < 248);
   5386                                 AlphaSum |= Colors[i].A;
   5387                             }
   5388                             ColorMapUseAlpha &= (AlphaSum > 0);
   5389 
   5390                             int CMapBpp = (ColorMap2BytesEntry ? 15 : 24) + (ColorMapUseAlpha ? (ColorMap2BytesEntry ? 1 : 8) : 0);
   5391                             int CMBytesPP = (int)Math.Ceiling(CMapBpp / 8.0);
   5392                             #endregion
   5393 
   5394                             Header.ColorMapSpec.ColorMapLength = Math.Min((ushort)Colors.Length, ushort.MaxValue);
   5395                             Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)CMapBpp;
   5396                             ImageOrColorMapArea.ColorMapData = new byte[Header.ColorMapSpec.ColorMapLength * CMBytesPP];
   5397 
   5398                             byte[] CMapEntry = new byte[CMBytesPP];
   5399 
   5400                             const float To5Bit = 32f / 256f; // Scale value from 8 to 5 bits.
   5401                             for (int i = 0; i < Colors.Length; i++)
   5402                             {
   5403                                 switch (Header.ColorMapSpec.ColorMapEntrySize)
   5404                                 {
   5405                                     case TgaColorMapEntrySize.A1R5G5B5:
   5406                                     case TgaColorMapEntrySize.X1R5G5B5:
   5407                                         int R = (int)(Colors[i].R * To5Bit);
   5408                                         int G = (int)(Colors[i].G * To5Bit) << 5;
   5409                                         int B = (int)(Colors[i].B * To5Bit) << 10;
   5410                                         int A = 0;
   5411 
   5412                                         if (Header.ColorMapSpec.ColorMapEntrySize == TgaColorMapEntrySize.A1R5G5B5)
   5413                                             A = ((Colors[i].A & 0x80) << 15);
   5414 
   5415                                         CMapEntry = BitConverter.GetBytes(A | R | G | B);
   5416                                         break;
   5417 
   5418                                     case TgaColorMapEntrySize.R8G8B8:
   5419                                         CMapEntry[0] = Colors[i].B;
   5420                                         CMapEntry[1] = Colors[i].G;
   5421                                         CMapEntry[2] = Colors[i].R;
   5422                                         break;
   5423 
   5424                                     case TgaColorMapEntrySize.A8R8G8B8:
   5425                                         CMapEntry[0] = Colors[i].B;
   5426                                         CMapEntry[1] = Colors[i].G;
   5427                                         CMapEntry[2] = Colors[i].R;
   5428                                         CMapEntry[3] = Colors[i].A;
   5429                                         break;
   5430 
   5431                                     case TgaColorMapEntrySize.Other:
   5432                                     default:
   5433                                         break;
   5434                                 }
   5435 
   5436                                 Buffer.BlockCopy(CMapEntry, 0, ImageOrColorMapArea.ColorMapData, i * CMBytesPP, CMBytesPP);
   5437                             }
   5438                         }
   5439                         #endregion
   5440 
   5441                         #region ImageType
   5442                         if (UseRLE)
   5443                         {
   5444                             if (IsGrayImage)
   5445                                 Header.ImageType = TgaImageType.RLE_BlackWhite;
   5446                             else if (IsColorMapped)
   5447                                 Header.ImageType = TgaImageType.RLE_ColorMapped;
   5448                             else
   5449                                 Header.ImageType = TgaImageType.RLE_TrueColor;
   5450                         }
   5451                         else
   5452                         {
   5453                             if (IsGrayImage)
   5454                                 Header.ImageType = TgaImageType.Uncompressed_BlackWhite;
   5455                             else if (IsColorMapped)
   5456                                 Header.ImageType = TgaImageType.Uncompressed_ColorMapped;
   5457                             else
   5458                                 Header.ImageType = TgaImageType.Uncompressed_TrueColor;
   5459                         }
   5460 
   5461                         Header.ColorMapType = (IsColorMapped ? TgaColorMapType.ColorMap : TgaColorMapType.NoColorMap);
   5462                         #endregion
   5463 
   5464                         #region NewFormat
   5465                         if (NewFormat)
   5466                         {
   5467                             Footer = new TgaFooter();
   5468                             ExtArea = new TgaExtArea();
   5469                             ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow);
   5470 
   5471                             if (IsAlpha)
   5472                             {
   5473                                 ExtArea.AttributesType = TgaAttrType.UsefulAlpha;
   5474 
   5475                                 if (IsPreAlpha)
   5476                                     ExtArea.AttributesType = TgaAttrType.PreMultipliedAlpha;
   5477                             }
   5478                             else
   5479                             {
   5480                                 ExtArea.AttributesType = TgaAttrType.NoAlpha;
   5481 
   5482                                 if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0)
   5483                                     ExtArea.AttributesType = TgaAttrType.UndefinedAlphaButShouldBeRetained;
   5484                             }
   5485                         }
   5486                         #endregion
   5487 
   5488                         #region Bitmap width is aligned by 32 bits = 4 bytes! Delete it.
   5489                         int StrideBytes = bmp.Width * BytesPP;
   5490                         int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes;
   5491 
   5492                         byte[] ImageData = new byte[(StrideBytes + PaddingBytes) * bmp.Height];
   5493 
   5494                         Rectangle Re = new Rectangle(0, 0, bmp.Width, bmp.Height);
   5495                         BitmapData BmpData = bmp.LockBits(Re, ImageLockMode.ReadOnly, bmp.PixelFormat);
   5496                         Marshal.Copy(BmpData.Scan0, ImageData, 0, ImageData.Length);
   5497                         bmp.UnlockBits(BmpData);
   5498                         BmpData = null;
   5499 
   5500                         if (PaddingBytes > 0) //Need delete bytes align
   5501                         {
   5502                             ImageOrColorMapArea.ImageData = new byte[StrideBytes * bmp.Height];
   5503                             for (int i = 0; i < bmp.Height; i++)
   5504                                 Buffer.BlockCopy(ImageData, i * (StrideBytes + PaddingBytes),
   5505                                     ImageOrColorMapArea.ImageData, i * StrideBytes, StrideBytes);
   5506                         }
   5507                         else
   5508                             ImageOrColorMapArea.ImageData = ImageData;
   5509 
   5510                         ImageData = null;
   5511 
   5512                         // Not official supported, but works (tested on 16bpp GrayScale test images)!
   5513                         if (bmp.PixelFormat == PixelFormat.Format16bppGrayScale)
   5514                         {
   5515                             for (long i = 0; i < ImageOrColorMapArea.ImageData.Length; i++)
   5516                                 ImageOrColorMapArea.ImageData[i] ^= byte.MaxValue;
   5517                         }
   5518                         #endregion
   5519 
   5520                         break;
   5521                 }
   5522 
   5523                 return true;
   5524             }
   5525             catch
   5526             {
   5527                 return false;
   5528             }
   5529         }
   5530 
   5531         bool SaveFunc(Stream stream)
   5532         {
   5533             try
   5534             {
   5535                 if (stream == null)
   5536                     throw new ArgumentNullException();
   5537                 if (!(stream.CanWrite && stream.CanSeek))
   5538                     throw new FileLoadException("Stream writing or seeking is not avaiable!");
   5539 
   5540                 string CheckResult;
   5541                 if (!CheckAndUpdateOffsets(out CheckResult))
   5542                     return false;
   5543 
   5544                 BinaryWriter Bw = new BinaryWriter(stream);
   5545                 Bw.Write(Header.ToBytes());
   5546 
   5547                 if (ImageOrColorMapArea.ImageID != null)
   5548                     Bw.Write(ImageOrColorMapArea.ImageID.ToBytes());
   5549 
   5550                 if (Header.ColorMapType != TgaColorMapType.NoColorMap)
   5551                     Bw.Write(ImageOrColorMapArea.ColorMapData);
   5552 
   5553                 #region ImageData
   5554                 if (Header.ImageType != TgaImageType.NoImageData)
   5555                 {
   5556                     if (Header.ImageType >= TgaImageType.RLE_ColorMapped &&
   5557                         Header.ImageType <= TgaImageType.RLE_BlackWhite)
   5558                         Bw.Write(RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height));
   5559                     else
   5560                         Bw.Write(ImageOrColorMapArea.ImageData);
   5561                 }
   5562                 #endregion
   5563 
   5564                 #region Footer
   5565                 if (Footer != null)
   5566                 {
   5567                     #region DevArea
   5568                     if (DevArea != null)
   5569                     {
   5570                         for (int i = 0; i < DevArea.Count; i++)
   5571                             Bw.Write(DevArea[i].Data);
   5572 
   5573                         Bw.Write((ushort)DevArea.Count);
   5574 
   5575                         for (int i = 0; i < DevArea.Count; i++)
   5576                         {
   5577                             Bw.Write(DevArea[i].Tag);
   5578                             Bw.Write(DevArea[i].Offset);
   5579                             Bw.Write(DevArea[i].FieldSize);
   5580                         }
   5581                     }
   5582                     #endregion
   5583 
   5584                     #region ExtArea
   5585                     if (ExtArea != null)
   5586                     {
   5587                         Bw.Write(ExtArea.ToBytes());
   5588 
   5589                         if (ExtArea.ScanLineTable != null)
   5590                             for (int i = 0; i < ExtArea.ScanLineTable.Length; i++)
   5591                                 Bw.Write(ExtArea.ScanLineTable[i]);
   5592 
   5593                         if (ExtArea.PostageStampImage != null)
   5594                             Bw.Write(ExtArea.PostageStampImage.ToBytes());
   5595 
   5596                         if (ExtArea.ColorCorrectionTable != null)
   5597                             for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++)
   5598                                 Bw.Write(ExtArea.ColorCorrectionTable[i]);
   5599                     }
   5600                     #endregion
   5601 
   5602                     Bw.Write(Footer.ToBytes());
   5603                 }
   5604                 #endregion
   5605 
   5606                 Bw.Flush();
   5607                 stream.Flush();
   5608                 return true;
   5609             }
   5610             catch
   5611             {
   5612                 return false;
   5613             }
   5614         }
   5615 
   5616         /// <summary>
   5617         /// Encode image with RLE compression (used RLE per line)!
   5618         /// </summary>
   5619         /// <param name="ImageData">Image data, bytes array with size = Width * Height * BytesPerPixel.</param>
   5620         /// <param name="Width">Image Width, must be > 0.</param>
   5621         /// <param name="Height">Image Height, must be > 0.</param>
   5622         /// <returns>Bytes array with RLE compressed image data.</returns>
   5623         byte[] RLE_Encode(byte[] ImageData, int Width, int Height)
   5624         {
   5625             if (ImageData == null)
   5626                 throw new ArgumentNullException(nameof(ImageData) + "in null!");
   5627 
   5628             if (Width <= 0 || Height <= 0)
   5629                 throw new ArgumentOutOfRangeException(nameof(Width) + " and " + nameof(Height) + " must be > 0!");
   5630 
   5631             int Bpp = ImageData.Length / Width / Height; // Bytes per pixel
   5632             int ScanLineSize = Width * Bpp;
   5633 
   5634             if (ScanLineSize * Height != ImageData.Length)
   5635                 throw new ArgumentOutOfRangeException("ImageData has wrong Length!");
   5636 
   5637             try
   5638             {
   5639                 int Count = 0;
   5640                 int Pos = 0;
   5641                 bool IsRLE = false;
   5642                 List<byte> Encoded = new List<byte>();
   5643                 byte[] RowData = new byte[ScanLineSize];
   5644 
   5645                 for (int y = 0; y < Height; y++)
   5646                 {
   5647                     Pos = 0;
   5648                     Buffer.BlockCopy(ImageData, y * ScanLineSize, RowData, 0, ScanLineSize);
   5649 
   5650                     while (Pos < ScanLineSize)
   5651                     {
   5652                         if (Pos >= ScanLineSize - Bpp)
   5653                         {
   5654                             Encoded.Add(0);
   5655                             Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, Bpp));
   5656                             Pos += Bpp;
   5657                             break;
   5658                         }
   5659 
   5660                         Count = 0; //1
   5661                         IsRLE = BitConverterExt.IsElementsEqual(RowData, Pos, Pos + Bpp, Bpp);
   5662 
   5663                         for (int i = Pos + Bpp; i < Math.Min(Pos + 128 * Bpp, ScanLineSize) - Bpp; i += Bpp)
   5664                         {
   5665                             if (IsRLE ^ BitConverterExt.IsElementsEqual(RowData, (IsRLE ? Pos : i), i + Bpp, Bpp))
   5666                             {
   5667                                 //Count--;
   5668                                 break;
   5669                             }
   5670                             else
   5671                                 Count++;
   5672                         }
   5673 
   5674                         int CountBpp = (Count + 1) * Bpp;
   5675                         Encoded.Add((byte)(IsRLE ? Count | 128 : Count));
   5676                         Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, (IsRLE ? Bpp : CountBpp)));
   5677                         Pos += CountBpp;
   5678                     }
   5679                 }
   5680 
   5681                 return Encoded.ToArray();
   5682             }
   5683             catch
   5684             {
   5685                 return null;
   5686             }
   5687         }
   5688 
   5689         /// <summary>
   5690         /// Convert <see cref="TGA"/> to <see cref="Bitmap"/>.
   5691         /// </summary>
   5692         /// <param name="ForceUseAlpha">Force use alpha channel.</param>
   5693         /// <param name="PostageStampImage">Get Postage Stamp Image (Thumb) or get main image?</param>
   5694         /// <returns>Bitmap or null, on error.</returns>
   5695         Bitmap ToBitmapFunc(bool ForceUseAlpha = false, bool PostageStampImage = false)
   5696         {
   5697             try
   5698             {
   5699                 #region UseAlpha?
   5700                 bool UseAlpha = true;
   5701                 if (ExtArea != null)
   5702                 {
   5703                     switch (ExtArea.AttributesType)
   5704                     {
   5705                         case TgaAttrType.NoAlpha:
   5706                         case TgaAttrType.UndefinedAlphaCanBeIgnored:
   5707                         case TgaAttrType.UndefinedAlphaButShouldBeRetained:
   5708                             UseAlpha = false;
   5709                             break;
   5710                         case TgaAttrType.UsefulAlpha:
   5711                         case TgaAttrType.PreMultipliedAlpha:
   5712                         default:
   5713                             break;
   5714                     }
   5715                 }
   5716                 UseAlpha = (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0 && UseAlpha) | ForceUseAlpha;
   5717                 #endregion
   5718 
   5719                 #region IsGrayImage
   5720                 bool IsGrayImage = Header.ImageType == TgaImageType.RLE_BlackWhite ||
   5721                     Header.ImageType == TgaImageType.Uncompressed_BlackWhite;
   5722                 #endregion
   5723 
   5724                 #region Get PixelFormat
   5725                 PixelFormat PixFormat = PixelFormat.Format24bppRgb;
   5726 
   5727                 switch (Header.ImageSpec.PixelDepth)
   5728                 {
   5729                     case TgaPixelDepth.Bpp8:
   5730                         PixFormat = PixelFormat.Format8bppIndexed;
   5731                         break;
   5732 
   5733                     case TgaPixelDepth.Bpp16:
   5734                         if (IsGrayImage)
   5735                             PixFormat = PixelFormat.Format16bppGrayScale;
   5736                         else
   5737                             PixFormat = (UseAlpha ? PixelFormat.Format16bppArgb1555 : PixelFormat.Format16bppRgb555);
   5738                         break;
   5739 
   5740                     case TgaPixelDepth.Bpp24:
   5741                         PixFormat = PixelFormat.Format24bppRgb;
   5742                         break;
   5743 
   5744                     case TgaPixelDepth.Bpp32:
   5745                         if (UseAlpha)
   5746                         {
   5747                             var f = Footer;
   5748                             if (ExtArea?.AttributesType == TgaAttrType.PreMultipliedAlpha)
   5749                                 PixFormat = PixelFormat.Format32bppPArgb;
   5750                             else
   5751                                 PixFormat = PixelFormat.Format32bppArgb;
   5752                         }
   5753                         else
   5754                             PixFormat = PixelFormat.Format32bppRgb;
   5755                         break;
   5756 
   5757                     default:
   5758                         PixFormat = PixelFormat.Undefined;
   5759                         break;
   5760                 }
   5761                 #endregion
   5762 
   5763                 ushort BMP_Width = (PostageStampImage ? ExtArea.PostageStampImage.Width : Width);
   5764                 ushort BMP_Height = (PostageStampImage ? ExtArea.PostageStampImage.Height : Height);
   5765                 Bitmap BMP = new Bitmap(BMP_Width, BMP_Height, PixFormat);
   5766 
   5767                 #region ColorMap and GrayPalette
   5768                 if (Header.ColorMapType == TgaColorMapType.ColorMap &&
   5769                    (Header.ImageType == TgaImageType.RLE_ColorMapped ||
   5770                     Header.ImageType == TgaImageType.Uncompressed_ColorMapped))
   5771                 {
   5772 
   5773                     ColorPalette ColorMap = BMP.Palette;
   5774                     Color[] CMapColors = ColorMap.Entries;
   5775 
   5776                     switch (Header.ColorMapSpec.ColorMapEntrySize)
   5777                     {
   5778                         case TgaColorMapEntrySize.X1R5G5B5:
   5779                         case TgaColorMapEntrySize.A1R5G5B5:
   5780                             const float To8Bit = 255f / 31f; // Scale value from 5 to 8 bits.
   5781                             for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
   5782                             {
   5783                                 ushort A1R5G5B5 = BitConverter.ToUInt16(ImageOrColorMapArea.ColorMapData, i * 2);
   5784                                 int A = (UseAlpha ? (A1R5G5B5 & 0x8000) >> 15 : 1) * 255; // (0 or 1) * 255
   5785                                 int R = (int)(((A1R5G5B5 & 0x7C00) >> 10) * To8Bit);
   5786                                 int G = (int)(((A1R5G5B5 & 0x3E0) >> 5) * To8Bit);
   5787                                 int B = (int)((A1R5G5B5 & 0x1F) * To8Bit);
   5788                                 CMapColors[i] = Color.FromArgb(A, R, G, B);
   5789                             }
   5790                             break;
   5791 
   5792                         case TgaColorMapEntrySize.R8G8B8:
   5793                             for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
   5794                             {
   5795                                 int Index = i * 3; //RGB = 3 bytes
   5796                                 int R = ImageOrColorMapArea.ColorMapData[Index + 2];
   5797                                 int G = ImageOrColorMapArea.ColorMapData[Index + 1];
   5798                                 int B = ImageOrColorMapArea.ColorMapData[Index];
   5799                                 CMapColors[i] = Color.FromArgb(R, G, B);
   5800                             }
   5801                             break;
   5802 
   5803                         case TgaColorMapEntrySize.A8R8G8B8:
   5804                             for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++)
   5805                             {
   5806                                 int ARGB = BitConverter.ToInt32(ImageOrColorMapArea.ColorMapData, i * 4);
   5807                                 CMapColors[i] = Color.FromArgb(UseAlpha ? ARGB | (0xFF << 24) : ARGB);
   5808                             }
   5809                             break;
   5810 
   5811                         default:
   5812                             ColorMap = null;
   5813                             break;
   5814                     }
   5815 
   5816                     if (ColorMap != null)
   5817                         BMP.Palette = ColorMap;
   5818                 }
   5819 
   5820                 if (PixFormat == PixelFormat.Format8bppIndexed && IsGrayImage)
   5821                 {
   5822                     ColorPalette GrayPalette = BMP.Palette;
   5823                     Color[] GrayColors = GrayPalette.Entries;
   5824                     for (int i = 0; i < GrayColors.Length; i++)
   5825                         GrayColors[i] = Color.FromArgb(i, i, i);
   5826                     BMP.Palette = GrayPalette;
   5827                 }
   5828                 #endregion
   5829 
   5830                 #region Bitmap width must by aligned (align value = 32 bits = 4 bytes)!
   5831                 byte[] ImageData;
   5832                 int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
   5833                 int StrideBytes = BMP.Width * BytesPerPixel;
   5834                 int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes;
   5835 
   5836                 if (PaddingBytes > 0) //Need bytes align
   5837                 {
   5838                     ImageData = new byte[(StrideBytes + PaddingBytes) * BMP.Height];
   5839                     for (int i = 0; i < BMP.Height; i++)
   5840                         Buffer.BlockCopy(PostageStampImage ? ExtArea.PostageStampImage.Data :
   5841                             ImageOrColorMapArea.ImageData, i * StrideBytes, ImageData,
   5842                             i * (StrideBytes + PaddingBytes), StrideBytes);
   5843                 }
   5844                 else
   5845                     ImageData = BitConverterExt.ToBytes(PostageStampImage ? ExtArea.PostageStampImage.Data :
   5846                         ImageOrColorMapArea.ImageData);
   5847 
   5848                 // Not official supported, but works (tested on 2 test images)!
   5849                 if (PixFormat == PixelFormat.Format16bppGrayScale)
   5850                 {
   5851                     for (long i = 0; i < ImageData.Length; i++)
   5852                         ImageData[i] ^= byte.MaxValue;
   5853                 }
   5854                 #endregion
   5855 
   5856                 Rectangle Re = new Rectangle(0, 0, BMP.Width, BMP.Height);
   5857                 BitmapData BmpData = BMP.LockBits(Re, ImageLockMode.WriteOnly, BMP.PixelFormat);
   5858                 Marshal.Copy(ImageData, 0, BmpData.Scan0, ImageData.Length);
   5859                 BMP.UnlockBits(BmpData);
   5860                 ImageData = null;
   5861                 BmpData = null;
   5862 
   5863                 if (ExtArea != null && ExtArea.KeyColor.ToInt() != 0)
   5864                     BMP.MakeTransparent(ExtArea.KeyColor.ToColor());
   5865 
   5866                 #region Flip Image
   5867                 switch (Header.ImageSpec.ImageDescriptor.ImageOrigin)
   5868                 {
   5869                     case TgaImgOrigin.BottomLeft:
   5870                         BMP.RotateFlip(RotateFlipType.RotateNoneFlipY);
   5871                         break;
   5872                     case TgaImgOrigin.BottomRight:
   5873                         BMP.RotateFlip(RotateFlipType.RotateNoneFlipXY);
   5874                         break;
   5875                     case TgaImgOrigin.TopLeft:
   5876                     default:
   5877                         break;
   5878                     case TgaImgOrigin.TopRight:
   5879                         BMP.RotateFlip(RotateFlipType.RotateNoneFlipX);
   5880                         break;
   5881                 }
   5882                 #endregion
   5883 
   5884                 return BMP;
   5885             }
   5886             catch
   5887             {
   5888                 return null;
   5889             }
   5890         }
   5891         #endregion
   5892 
   5893         #region Explicit
   5894         public static explicit operator Bitmap(TGA tga)
   5895         {
   5896             return tga.ToBitmap();
   5897         }
   5898 
   5899         public static explicit operator TGA(Bitmap bmp)
   5900         {
   5901             return FromBitmap(bmp);
   5902         }
   5903         #endregion
   5904 
   5905         #region PostageStamp Image
   5906         /// <summary>
   5907         /// Convert <see cref="TgaPostageStampImage"/> to <see cref="Bitmap"/>.
   5908         /// </summary>
   5909         /// <param name="ForceUseAlpha">Force use alpha channel.</param>
   5910         /// <returns>Bitmap or null.</returns>
   5911         public Bitmap GetPostageStampImage(bool ForceUseAlpha = false)
   5912         {
   5913             if (ExtArea == null || ExtArea.PostageStampImage == null || ExtArea.PostageStampImage.Data == null ||
   5914                 ExtArea.PostageStampImage.Width <= 0 || ExtArea.PostageStampImage.Height <= 0)
   5915                 return null;
   5916 
   5917             return ToBitmapFunc(ForceUseAlpha, true);
   5918         }
   5919 
   5920         /// <summary>
   5921         /// Update Postage Stamp Image or set it.
   5922         /// </summary>
   5923         public void UpdatePostageStampImage()
   5924         {
   5925             if (Header.ImageType == TgaImageType.NoImageData)
   5926             {
   5927                 if (ExtArea != null)
   5928                     ExtArea.PostageStampImage = null;
   5929                 return;
   5930             }
   5931 
   5932             ToNewFormat();
   5933             if (ExtArea.PostageStampImage == null)
   5934                 ExtArea.PostageStampImage = new TgaPostageStampImage();
   5935 
   5936             int PS_Width = Header.ImageSpec.ImageWidth;
   5937             int PS_Height = Header.ImageSpec.ImageHeight;
   5938 
   5939             if (Width > 64 || Height > 64)
   5940             {
   5941                 float AspectRatio = Width / (float)Height;
   5942                 PS_Width = (byte)(64f * (AspectRatio < 1f ? AspectRatio : 1f));
   5943                 PS_Height = (byte)(64f / (AspectRatio > 1f ? AspectRatio : 1f));
   5944             }
   5945             PS_Width = Math.Max(PS_Width, 4);
   5946             PS_Height = Math.Max(PS_Height, 4);
   5947 
   5948             ExtArea.PostageStampImage.Width = (byte)PS_Width;
   5949             ExtArea.PostageStampImage.Height = (byte)PS_Height;
   5950 
   5951             int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0);
   5952             ExtArea.PostageStampImage.Data = new byte[PS_Width * PS_Height * BytesPerPixel];
   5953 
   5954             float WidthCoef = Width / (float)PS_Width;
   5955             float HeightCoef = Height / (float)PS_Height;
   5956 
   5957             for (int y = 0; y < PS_Height; y++)
   5958             {
   5959                 int Y_Offset = (int)(y * HeightCoef) * Width * BytesPerPixel;
   5960                 int y_Offset = y * PS_Width * BytesPerPixel;
   5961 
   5962                 for (int x = 0; x < PS_Width; x++)
   5963                 {
   5964                     Buffer.BlockCopy(ImageOrColorMapArea.ImageData, Y_Offset + (int)(x * WidthCoef) * BytesPerPixel,
   5965                         ExtArea.PostageStampImage.Data, y_Offset + x * BytesPerPixel, BytesPerPixel);
   5966                 }
   5967             }
   5968         }
   5969 
   5970         public void DeletePostageStampImage()
   5971         {
   5972             if (ExtArea != null)
   5973                 ExtArea.PostageStampImage = null;
   5974         }
   5975         #endregion
   5976     }
   5977 }