content: /*
   * Copyright 2002-2019 Drew Noakes and contributors
   *
   *    Licensed under the Apache License, Version 2.0 (the "License");
   *    you may not use this file except in compliance with the License.
   *    You may obtain a copy of the License at
   *
   *        https://www.apache.org/licenses/LICENSE-2.0
   *
   *    Unless required by applicable law or agreed to in writing, software
   *    distributed under the License is distributed on an "AS IS" BASIS,
   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *    See the License for the specific language governing permissions and
   *    limitations under the License.
   *
   * More information about this project is available at:
   *
   *    https://drewnoakes.com/code/exif/
   *    https://github.com/drewnoakes/metadata-extractor
   */
  package com.drew.metadata.bmp;
  
  import com.drew.lang.annotations.NotNull;
  import com.drew.lang.annotations.Nullable;
  import com.drew.metadata.Directory;
  
  import java.util.HashMap;
  
  /**
   * @author Drew Noakes https://drewnoakes.com
   * @author Nadahar
   */
  @SuppressWarnings("WeakerAccess")
  public class BmpHeaderDirectory extends Directory
  {
      public static final int TAG_BITMAP_TYPE = -2;
      public static final int TAG_HEADER_SIZE = -1;
  
      public static final int TAG_IMAGE_HEIGHT = 1;
      public static final int TAG_IMAGE_WIDTH = 2;
      public static final int TAG_COLOUR_PLANES = 3;
      public static final int TAG_BITS_PER_PIXEL = 4;
      public static final int TAG_COMPRESSION = 5;
      public static final int TAG_X_PIXELS_PER_METER = 6;
      public static final int TAG_Y_PIXELS_PER_METER = 7;
      public static final int TAG_PALETTE_COLOUR_COUNT = 8;
      public static final int TAG_IMPORTANT_COLOUR_COUNT = 9;
      public static final int TAG_RENDERING = 10;
      public static final int TAG_COLOR_ENCODING = 11;
      public static final int TAG_RED_MASK = 12;
      public static final int TAG_GREEN_MASK = 13;
      public static final int TAG_BLUE_MASK = 14;
      public static final int TAG_ALPHA_MASK = 15;
      public static final int TAG_COLOR_SPACE_TYPE = 16;
      public static final int TAG_GAMMA_RED = 17;
      public static final int TAG_GAMMA_GREEN = 18;
      public static final int TAG_GAMMA_BLUE = 19;
      public static final int TAG_INTENT = 20;
      public static final int TAG_LINKED_PROFILE = 21;
  
      @NotNull
      private static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
  
      static {
          _tagNameMap.put(TAG_BITMAP_TYPE, "Bitmap type");
          _tagNameMap.put(TAG_HEADER_SIZE, "Header Size");
  
          _tagNameMap.put(TAG_IMAGE_HEIGHT, "Image Height");
          _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
          _tagNameMap.put(TAG_COLOUR_PLANES, "Planes");
          _tagNameMap.put(TAG_BITS_PER_PIXEL, "Bits Per Pixel");
          _tagNameMap.put(TAG_COMPRESSION, "Compression");
          _tagNameMap.put(TAG_X_PIXELS_PER_METER, "X Pixels per Meter");
          _tagNameMap.put(TAG_Y_PIXELS_PER_METER, "Y Pixels per Meter");
          _tagNameMap.put(TAG_PALETTE_COLOUR_COUNT, "Palette Colour Count");
          _tagNameMap.put(TAG_IMPORTANT_COLOUR_COUNT, "Important Colour Count");
          _tagNameMap.put(TAG_RENDERING, "Rendering");
          _tagNameMap.put(TAG_COLOR_ENCODING, "Color Encoding");
          _tagNameMap.put(TAG_RED_MASK, "Red Mask");
          _tagNameMap.put(TAG_GREEN_MASK, "Green Mask");
          _tagNameMap.put(TAG_BLUE_MASK, "Blue Mask");
          _tagNameMap.put(TAG_ALPHA_MASK, "Alpha Mask");
          _tagNameMap.put(TAG_COLOR_SPACE_TYPE, "Color Space Type");
          _tagNameMap.put(TAG_GAMMA_RED, "Red Gamma Curve");
          _tagNameMap.put(TAG_GAMMA_GREEN, "Green Gamma Curve");
          _tagNameMap.put(TAG_GAMMA_BLUE, "Blue Gamma Curve");
          _tagNameMap.put(TAG_INTENT, "Rendering Intent");
          _tagNameMap.put(TAG_LINKED_PROFILE, "Linked Profile File Name");
      }
  
      public BmpHeaderDirectory()
      {
          this.setDescriptor(new BmpHeaderDescriptor(this));
      }
  
      @Nullable
      public BitmapType getBitmapType() {
          Integer value = getInteger(TAG_BITMAP_TYPE);
          return value == null ? null : BitmapType.typeOf(value.intValue());
      }
  
      @Nullable
      public Compression getCompression() {
          return Compression.typeOf(this);
      }
  
      @Nullable
      public RenderingHalftoningAlgorithm getRendering() {
          Integer value = getInteger(TAG_RENDERING);
          return value == null ? null : RenderingHalftoningAlgorithm.typeOf(value.intValue());
      }
  
      @Nullable
      public ColorEncoding getColorEncoding() {
          Integer value = getInteger(TAG_COLOR_ENCODING);
          return value == null ? null : ColorEncoding.typeOf(value.intValue());
      }
  
      @Nullable
      public ColorSpaceType getColorSpaceType() {
          Long value = getLongObject(TAG_COLOR_SPACE_TYPE);
          return value == null ? null : ColorSpaceType.typeOf(value.longValue());
      }
  
      @Nullable
      public RenderingIntent getRenderingIntent() {
          Integer value = getInteger(TAG_INTENT);
          return value == null ? null : RenderingIntent.typeOf(value.intValue());
      }
  
      @Override
      @NotNull
      public String getName()
      {
          return "BMP Header";
      }
  
      @Override
      @NotNull
      protected HashMap<Integer, String> getTagNameMap()
      {
          return _tagNameMap;
      }
  
      public enum BitmapType {
  
             /** "BM" - Windows or OS/2 bitmap */
          BITMAP(0x4D42),
  
          /** "BA" - OS/2 Bitmap array (multiple bitmaps) */
          OS2_BITMAP_ARRAY(0x4142),
  
              /** "IC" - OS/2 Icon */
          OS2_ICON(0x4349),
  
              /** "CI" - OS/2 Color icon */
          OS2_COLOR_ICON(0x4943),
  
          /** "CP" - OS/2 Color pointer */
          OS2_COLOR_POINTER(0x5043),
  
              /** "PT" - OS/2 Pointer */
          OS2_POINTER(0x5450);
  
          private final int value;
  
          private BitmapType(int value) {
              this.value = value;
          }
  
          public int getValue() {
              return value;
          }
  
          @Nullable
          public static BitmapType typeOf(int value) {
              for (BitmapType bitmapType : BitmapType.values())
              {
                  if (bitmapType.value == value)
                  {
                      return bitmapType;
                  }
              }
              return null;
          }
  
          @Override
          @NotNull
          public String toString() {
              switch (this) {
                  case BITMAP: return "Standard";
                  case OS2_BITMAP_ARRAY: return "Bitmap Array";
                  case OS2_COLOR_ICON: return "Color Icon";
                  case OS2_COLOR_POINTER: return "Color Pointer";
                  case OS2_ICON: return "Monochrome Icon";
                  case OS2_POINTER: return "Monochrome Pointer";
                  default:
                      throw new IllegalStateException("Unimplemented bitmap type " + super.toString());
              }
          }
      }
  
      public enum Compression {
  
          /** 0 = None */
          BI_RGB(0),
  
          /** 1 = RLE 8-bit/pixel */
          BI_RLE8(1),
  
          /** 2 = RLE 4-bit/pixel */
          BI_RLE4(2),
  
          /** 3 = Bit fields (not OS22XBITMAPHEADER (size 64)) */
          BI_BITFIELDS(3),
  
          /** 3 = Huffman 1D (if OS22XBITMAPHEADER (size 64)) */
          BI_HUFFMAN_1D(3),
  
          /** 4 = JPEG (not OS22XBITMAPHEADER (size 64)) */
          BI_JPEG(4),
  
          /** 4 = RLE 24-bit/pixel (if OS22XBITMAPHEADER (size 64)) */
          BI_RLE24(4),
  
          /** 5 = PNG */
          BI_PNG(5),
  
          /** 6 = RGBA bit fields */
          BI_ALPHABITFIELDS(6),
  
          /** 11 = CMYK */
          BI_CMYK(11),
  
          /** 12 = CMYK RLE-8 */
          BI_CMYKRLE8(12),
  
          /** 13 = CMYK RLE-4 */
          BI_CMYKRLE4(13);
  
          private final int value;
  
          private Compression(int value) {
              this.value = value;
          }
  
          public int getValue() {
              return value;
          }
  
          @Nullable
          public static Compression typeOf(@NotNull BmpHeaderDirectory directory) {
              Integer value = directory.getInteger(TAG_COMPRESSION);
              if (value == null)
                  return null;
              Integer headerSize = directory.getInteger(TAG_HEADER_SIZE);
              if (headerSize == null)
                  return null;
  
              return typeOf(value, headerSize);
          }
  
          @Nullable
          public static Compression typeOf(int value, int headerSize) {
              switch (value) {
                  case 0:  return BI_RGB;
                  case 1:  return BI_RLE8;
                  case 2:  return BI_RLE4;
                  case 3:  return headerSize == 64 ? BI_HUFFMAN_1D : BI_BITFIELDS;
                  case 4:  return headerSize == 64 ? BI_RLE24 : BI_JPEG;
                  case 5:  return BI_PNG;
                  case 6:  return BI_ALPHABITFIELDS;
                  case 11: return BI_CMYK;
                  case 12: return BI_CMYKRLE8;
                  case 13: return BI_CMYKRLE4;
                  default: return null;
              }
          }
  
          @Override
          @NotNull
          public String toString() {
              switch (this) {
                  case BI_RGB:            return "None";
                  case BI_RLE8:           return "RLE 8-bit/pixel";
                  case BI_RLE4:           return "RLE 4-bit/pixel";
                  case BI_BITFIELDS:      return "Bit Fields";
                  case BI_HUFFMAN_1D:     return "Huffman 1D";
                  case BI_JPEG:           return "JPEG";
                  case BI_RLE24:          return "RLE 24-bit/pixel";
                  case BI_PNG:            return "PNG";
                  case BI_ALPHABITFIELDS: return "RGBA Bit Fields";
                  case BI_CMYK:           return "CMYK Uncompressed";
                  case BI_CMYKRLE8:       return "CMYK RLE-8";
                  case BI_CMYKRLE4:       return "CMYK RLE-4";
                  default:
                      throw new IllegalStateException("Unimplemented compression type " + super.toString());
              }
          }
      }
  
      public enum RenderingHalftoningAlgorithm {
  
          /** No halftoning algorithm */
          NONE(0),
  
          /** Error Diffusion Halftoning */
          ERROR_DIFFUSION(1),
  
          /** Processing Algorithm for Noncoded Document Acquisition */
          PANDA(2),
  
          /** Super-circle Halftoning */
          SUPER_CIRCLE(3);
  
          private final int value;
  
          private RenderingHalftoningAlgorithm(int value) {
              this.value = value;
          }
  
          public int getValue() {
              return value;
          }
  
          @Nullable
          public static RenderingHalftoningAlgorithm typeOf(int value) {
              for (RenderingHalftoningAlgorithm renderingHalftoningAlgorithm : RenderingHalftoningAlgorithm.values())
              {
                  if (renderingHalftoningAlgorithm.value == value)
                  {
                      return renderingHalftoningAlgorithm;
                  }
              }
              return null;
          }
  
          @Override
          @NotNull
          public String toString() {
              switch (this) {
                  case NONE:
                      return "No Halftoning Algorithm";
                  case ERROR_DIFFUSION:
                      return "Error Diffusion Halftoning";
                  case PANDA:
                      return "Processing Algorithm for Noncoded Document Acquisition";
                  case SUPER_CIRCLE:
                      return "Super-circle Halftoning";
                  default:
                      throw new IllegalStateException("Unimplemented rendering halftoning algorithm type " + super.toString());
              }
          }
      }
  
      public enum ColorEncoding {
          RGB(0);
  
          private final int value;
  
          private ColorEncoding(int value) {
              this.value = value;
          }
  
          public int getValue() {
              return value;
          }
  
          @Nullable
          public static ColorEncoding typeOf(int value) {
              return value == 0 ? RGB : null;
          }
      }
  
      public enum ColorSpaceType {
  
          /** 0 = Calibrated RGB */
          LCS_CALIBRATED_RGB(0L),
  
          /** "sRGB" = sRGB Color Space */
          LCS_sRGB(0x73524742L),
  
          /** "Win " = System Default Color Space, sRGB */
          LCS_WINDOWS_COLOR_SPACE(0x57696E20L),
  
          /** "LINK" = Linked Profile */
          PROFILE_LINKED(0x4C494E4BL),
  
          /** "MBED" = Embedded Profile */
          PROFILE_EMBEDDED(0x4D424544L);
  
          private final long value;
  
          private ColorSpaceType(long value) {
              this.value = value;
          }
  
          public long getValue() {
              return value;
          }
  
          @Nullable
          public static ColorSpaceType typeOf(long value) {
              for (ColorSpaceType colorSpaceType : ColorSpaceType.values())
              {
                  if (colorSpaceType.value == value)
                  {
                      return colorSpaceType;
                  }
              }
              return null;
          }
  
          @Override
          @NotNull
          public String toString() {
              switch (this) {
                  case LCS_CALIBRATED_RGB:
                      return "Calibrated RGB";
                  case LCS_sRGB:
                      return "sRGB Color Space";
                  case LCS_WINDOWS_COLOR_SPACE:
                      return "System Default Color Space, sRGB";
                  case PROFILE_LINKED:
                      return "Linked Profile";
                  case PROFILE_EMBEDDED:
                      return "Embedded Profile";
                  default:
                      throw new IllegalStateException("Unimplemented color space type " + super.toString());
              }
          }
      }
  
      public enum RenderingIntent {
  
          /** Graphic, Saturation */
          LCS_GM_BUSINESS(1),
  
          /** Proof, Relative Colorimetric */
          LCS_GM_GRAPHICS(2),
  
          /** Picture, Perceptual */
          LCS_GM_IMAGES(4),
  
          /** Match, Absolute Colorimetric */
          LCS_GM_ABS_COLORIMETRIC(8);
  
          private final int value;
  
          private RenderingIntent(int value) {
              this.value = value;
          }
  
          public int getValue() {
              return value;
          }
  
          @Nullable
          public static RenderingIntent typeOf(long value) {
              for (RenderingIntent renderingIntent : RenderingIntent.values())
              {
                  if (renderingIntent.value == value)
                  {
                      return renderingIntent;
                  }
              }
              return null;
          }
  
          @Override
          @NotNull
          public String toString() {
              switch (this) {
                  case LCS_GM_BUSINESS:
                      return "Graphic, Saturation";
                  case LCS_GM_GRAPHICS:
                      return "Proof, Relative Colorimetric";
                  case LCS_GM_IMAGES:
                      return "Picture, Perceptual";
                  case LCS_GM_ABS_COLORIMETRIC:
                      return "Match, Absolute Colorimetric";
                  default:
                      throw new IllegalStateException("Unimplemented rendering intent " + super.toString());
              }
          }
      }
  }
  