bmpr.hpp 3.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
// Copyright (c) 2012-2017 VideoStitch SAS
// Copyright (c) 2018 stitchEm

#ifndef BMPR_HPP_
#define BMPR_HPP_

#include "io.hpp"

#include <cassert>
#include <fstream>
#include <ostream>
#include <vector>

typedef struct {
  char* filename;
  void* buffer;
} cache_t;

#define MAX_CACHED_FILES (8)

static cache_t filesCache[MAX_CACHED_FILES] = {
    {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
    {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr},
};

void* cachedFile(const char* filename) {
  cache_t* free_entry = nullptr;

  for (int i = 0; i < MAX_CACHED_FILES; i++) {
    if (filesCache[i].filename) {
      if (strcmp(filesCache[i].filename, filename) == 0) {
        return (filesCache[i].buffer);
      }
    } else if (free_entry == nullptr) {
      free_entry = filesCache + i;
    }
  }
  assert(free_entry);
  free_entry->filename = (char*)malloc(strlen(filename) + 1);
  strcpy(free_entry->filename, filename);
  FILE* fd = VideoStitch::Io::openFile(filename, "rb");
  assert(fd);
  fseek(fd, 0, SEEK_END);
  size_t len = ftell(fd);
  fseek(fd, 0, SEEK_SET);
  free_entry->buffer = malloc(len);
  assert(free_entry->buffer);
// we don't care about the return of read
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
  fread(free_entry->buffer, sizeof(char), len, fd);
#pragma GCC diagnostic pop
  fclose(fd);
  return (free_entry->buffer);
}

namespace VideoStitch {
namespace Input {
class BMPReader {
 public:
  BMPReader(const char* filename, std::ostream*) : width(0), height(0), cury(0), bottomup(0), pitch(0) {
    buffer = (char*)cachedFile(filename);
    assert(buffer);
    (void)readHeader();
  }

  ~BMPReader() {}

  unsigned getWidth() const { return width; }

  unsigned getHeight() const { return height; }

  bool ok() const { return true; }

  /**
   * Fill in the given buffer with the next row (RGBRGBRGB).
   * @data must be large enough to hold one row.
   */
  bool getNextRow(unsigned char* data) {
    char* src = buffer + bmf.bfOffBits;
    if (cury < height) {
      if (!bottomup) {
        src += (height - (cury + 1)) * pitch;
      } else {
        src += cury * pitch;
      }
      memcpy(data, src, width * 3);
      cury++;
      return true;
    }
    return false;
  }

 private:
  int readHeader() {
    memcpy(&bmf, buffer, sizeof(struct bmpfileheader));
    memcpy(&bmi, buffer + sizeof(struct bmpfileheader), sizeof(struct bmpinfoheader));
    if ((bmf.bfType != 0x4D42) || (bmf.bfReserved1) || (bmf.bfReserved2) ||
        (bmi.biSize != sizeof(struct bmpinfoheader)) || (bmi.biPlanes != 1) || (bmi.biBitCount != 24) ||
        (bmi.biCompression)) {
      return (-1);
    }
    width = bmi.biWidth;
    bottomup = (int)(bmi.biHeight >> 31);
    height = (bmi.biHeight ^ bottomup) - bottomup;
    pitch = (size_t)(unsigned int)((((width * bmi.biPlanes * bmi.biBitCount) + 31) >> 3) & -4);
    cury = 0;
    return (0);
  }

 private:
  char* buffer;
  unsigned int width;
  unsigned int height;
  unsigned int cury;
  int bottomup;
  size_t pitch;

  struct __attribute__((__packed__)) bmpfileheader {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffBits;
  } bmf;

  struct __attribute__((__packed__)) bmpinfoheader {
    uint32_t biSize;
    int32_t biWidth;
    int32_t biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int32_t biXPelsPerMeter;
    int32_t biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
  } bmi;
};
}  // namespace Input
}  // namespace VideoStitch

#endif