text: /**
   * xrdp: A Remote Desktop Protocol server.
   *
   * Copyright (C) Laxmikant Rashinkar 2004-2012
   *
   * 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
   *
   *     http://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.
   */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include <fcntl.h>
  #include <stdint.h>
  #include "common.h"
  
  // multi byte values are stored in little endian
  
  struct bmp_magic
  {
      char magic[2];
  };
  
  struct bmp_hdr
  {
      uint32_t size;        // file size in bytes
      uint16_t reserved1;
      uint16_t reserved2;
      uint32_t offset;      // offset to image data, in bytes
  };
  
  struct dib_hdr
  {
      uint32_t  hdr_size;
      int32_t   width;
      int32_t   height;
      uint16_t  nplanes;
      uint16_t  bpp;
      uint32_t  compress_type;
      uint32_t  image_size;
      int32_t   hres;
      int32_t   vres;
      uint32_t  ncolors;
      uint32_t  nimpcolors;
  };
  
  // forward declarations
  int parse_bmp(char *filename, struct pic_info *pic_info);
  int parse_bmp_24(struct bmp_hdr *bmp_hdr, struct dib_hdr *dib_hdr, int fd, struct pic_info *pic_info);
  
  int parse_bmp(char *filename, struct pic_info *pic_info)
  {
      int  got_magic;
      int  fd;
      int  rval;
  
      struct bmp_magic   magic;
      struct bmp_hdr     bmp_hdr;
      struct dib_hdr     dib_hdr;
  
      if ((fd = open(filename, O_RDONLY)) < 0)
      {
          printf("error opeing %s\n", filename);
          return -1;
      }
  
      // read BMP magic...
      if ((rval = read(fd, magic.magic, 2)) != 2)
      {
          fprintf(stderr, "error reading BMP signature from file %s\n", filename);
          return -1;
      }
  
      got_magic = 0;
  
      // ...and confirm that this is indeed a BMP file
      if ((magic.magic[0] == 'B') && (magic.magic[1] == 'M'))
      {
          // BM – Windows 3.1x, 95, NT, ... etc
          got_magic = 1;
      }
      else if ((magic.magic[0] == 'B') && (magic.magic[1] == 'A'))
      {
          // BA – OS/2 struct Bitmap Array
          got_magic = 1;
      }
      else if ((magic.magic[0] == 'C') && (magic.magic[1] == 'I'))
      {
          // CI – OS/2 struct Color Icon
          got_magic = 1;
      }
      else if ((magic.magic[0] == 'C') && (magic.magic[1] == 'P'))
      {
          // CP – OS/2 const Color Pointer
          got_magic = 1;
      }
      else if ((magic.magic[0] == 'I') && (magic.magic[1] == 'C'))
      {
          // IC – OS/2 struct Icon
          got_magic = 1;
      }
      else if ((magic.magic[0] == 'P') && (magic.magic[1] == 'T'))
      {
          // PT – OS/2 Pointer
          got_magic = 1;
      }
  
      if (!got_magic)
      {
          fprintf(stderr, "%s is not a valid BMP file\n", filename);
          return -1;
      }
  
      // read BMP header
      if ((rval = read(fd, &bmp_hdr, sizeof(bmp_hdr))) < sizeof(bmp_hdr))
      {
          fprintf(stderr, "error BMP header from file %s\n", filename);
          return -1;
      }
  
      // read DIB header
      if ((rval = read(fd, &dib_hdr, sizeof(dib_hdr))) < sizeof(dib_hdr))
      {
          fprintf(stderr, "error reading DIB header from file %s\n", filename);
          return -1;
      }
  
  #if 0
      printf("header size:   %d\n", dib_hdr.hdr_size);
      printf("width:         %d\n", dib_hdr.width);
      printf("height:        %d\n", dib_hdr.height);
      printf("num planes:    %d\n", dib_hdr.nplanes);
      printf("bpp:           %d\n", dib_hdr.bpp);
      printf("comp type:     %d\n", dib_hdr.compress_type);
      printf("image size:    %d\n", dib_hdr.image_size);
      printf("hres:          %d\n", dib_hdr.hres);
      printf("vres:          %d\n", dib_hdr.vres);
      printf("ncolors:       %d\n", dib_hdr.ncolors);
      printf("nimpcolors:    %d\n", dib_hdr.nimpcolors);
  #endif
  
      if (dib_hdr.compress_type)
      {
          printf("TODO: compressed images not yet supported\n");
          return -1;
      }
  
      pic_info->width = dib_hdr.width;
      pic_info->height = dib_hdr.height;
  
      if (dib_hdr.bpp == 24)
      {
          rval = parse_bmp_24(&bmp_hdr, &dib_hdr, fd, pic_info);
      }
  
      close(fd);
      return rval;
  }
  
  /**
   * extract 24bit BMP data from image file
   *
   * @return 0 on success
   * @return -1 on failure
   */
  
  int parse_bmp_24(
      struct bmp_hdr  *bmp_hdr,
      struct dib_hdr  *dib_hdr,
      int             fd,
      struct pic_info *pic_info
  )
  {
      char *file_data;
      char *ptr_file_data;
      char *mem_data;
      char *ptr_mem_data;
      char *cptr;
  
      int  w = dib_hdr->width;    // picture width
      int  h = dib_hdr->height;   // picture height
      int  bpl;                   // bytes per line
      int  bytes;
      int  i;
      int  j;
  
      // bytes per image line = width x bytes_per_pixel + padding
      i = (w * 3) % 4;
      j = (i == 0) ? 0 : 4 - i;
      bpl = w * 3 + j;
  
      // 24 bit depth, no alpha channel
      file_data = (char *) malloc(h * bpl);
  
      // point to first line in image data, which is stored in reverse order
      ptr_file_data = (file_data + dib_hdr->image_size) - bpl;
  
      // 24 bit depth, with alpha channel
      mem_data  = (char *) malloc(w * h * 4);
      ptr_mem_data = mem_data;
  
      pic_info->pixel_data = ptr_mem_data;
  
      // seek to beginning of pixel data
      lseek(fd, bmp_hdr->offset, SEEK_SET);
  
      // read all pixel data
      bytes = read(fd, file_data, dib_hdr->image_size);
  
      // convert 24bit to 24 bit with alpha and store in reverse
      for (i = 0; i < h; i ++)
      {
          cptr = ptr_file_data;
  
          for (j = 0; j < w; j++)
          {
              *ptr_mem_data++ = *cptr++; // blue value
              *ptr_mem_data++ = *cptr++; // green value
              *ptr_mem_data++ = *cptr++; // red value
              *ptr_mem_data++ = 0;       // alpha channel
          }
  
          ptr_file_data -= bpl;
      }
  
      free(file_data);
      return 0;
  }
  