Pull to refresh

Пишем плагин чтения файла изображений для OpenSceneGraph

Reading time3 min
Views4.2K
В этой статье речь пойдет о написании плагина для OpenSceneGraph. Плагин добавляет возможность использования формата PCX фирмы ZSoft Corporation. Код упрощен до предела и включает в себя только функцию чтения, функцию записи предлагаю написать самим. Я понимаю, что на сайте www.openscenegraph.org можно скачать исходники плагинов и посмотреть, как все работает, но форматирование исходников меня несколько удивило и я решил разложить все по полочкам. И оставить для себя заметку, чтоб не забыть.

image

Главный класс


Во-первых, нам надо создать класс, наследник класса osgDB::ReaderWriter. Плагины 3D форматов тоже используют этот класс:

class ReaderWriterPCX : public osgDB::ReaderWriter
{
  public:
    ReaderWriterPCX();
    const char* className() const;
    ReadResult readObject(std::istream& fin, const Options* options = 0) const;
    ReadResult readObject(const std::string& file, const Options* options = 0) const;
    ReadResult readImage(std::istream& fin, const Options* options = 0) const;
    ReadResult readImage(const std::string& file, const Options* options = 0) const;
    WriteResult writeImage(const osg::Image& image, std::ostream& fout, const Options* = 0) const;
    WriteResult writeImage(const osg::Image& img, const std::string& fileName, const Options* options = 0) const;
  
  private:
    static ReadResult readPCXStream(std::istream& fin);
    
};

Как понятно из кода выше — это описание функций плагина для чтения и записи файла, а также установки имени класса и используемого расширения файла. Остальной код приводить не буду. Его вы сможете посмотреть по ссылке в конце статьи или в исходниках OpenSceneGraph для другого примера. В нем открытие файла, проверка на соответствие расширений, наличие файла и так далее.

ReaderWriterPCX::ReaderWriterPCX()
{
  supportsExtension("pcx","PCX Image format");
}

const char* ReaderWriterPCX::className() const 
{ 
  return "PCX Image Reader"; 
}

Главная функция


Хоть возвращаемый тип функций и osgDB::ReaderWriter::ReadResult, но в данном случае возвращается osg::Image. Нам, во-первых, необходимо узнать некоторые параметры изображения, такие как размеры изображения и сами данные пикселей. Альфа-канал данный формат не использует, а поддержку палитры сделаем только 256+. Данный формат использует супер пупер сжатие данных RLE. Цитата из Вики:
Алгоритм такого сжатия очень быстрый и занимает небольшой объём памяти, однако не очень эффективен, непрактичен для сжатия фотографий и более детальной компьютерной графики.

Используется сжатие без потерь. При сохранении изображения подряд идущие пиксели одинакового цвета объединяются и вместо указания цвета для каждого пикселя указывается цвет группы пикселей и их количество. Такой алгоритм хорошо сжимает изображения, в которых присутствуют области одного цвета.

Итак, открываем файл и считываем его сигнатуру. Если она правильная — идем дальше. Проверяем и остальные данные, считываем размеры, палитру (она находится в конце файла для 256 цветов).

fin.read((char*) &pcx->Identifier, sizeof(pcx->Identifier));
if (pcx->Identifier != 0x0a) {
  OSG_WARN << "Invalid PCX Identifier\n";
  return 0;
}

И непосредственно сам алгоритм распаковки:

for (int h = height_ret - 1; h >= 0; --h) { 
  for (int w = 0; w < width_ret; ++w) {
    if(!count) {
      if(!fin.read((char*) &tmp, sizeof(tmp))) {
        OSG_WARN << "file truncated\n";
        return 0;        
      }
        
      if( (tmp & 0xc0) == 0xc0) {
        count = tmp & 0x3f;
        if(!fin.read((char*) &tmp, sizeof(tmp))) {
          OSG_WARN << "file truncated\n";
          return 0;
        }
      } else {
        count = 1;
      }
    }
      
    index = h * width_ret + w;
    imageBuffer[index].red = colorPalette[tmp].red;
    imageBuffer[index].green = colorPalette[tmp].green;
    imageBuffer[index].blue = colorPalette[tmp].blue;
    --count;
  }
}

Массив imageBuffer и есть наши данные. Он состоит трех цветов, помноженных на количество пикселей. То есть это и есть наши распакованные данные. Именно они будут использоваться при установке данных изображения, а формат и размер будет выбран GL_RGB и 3 байта соответственно.

Плагин нужно скопировать в папку с плагинами. У меня это /usr/lib/osgPlugins-3.2.0/ (я использую Linux). Проверить его можно открыв с помощью osgmovie, который есть в примерах. В конце файла должен быть указан код, подключающий плагин:

REGISTER_OSGPLUGIN(pcx, ReaderWriterPCX)

Эпилог


Наличие плагинов дает возможность использовать OpenSceneGraph для написания движков под старые игры с новой графикой или использовать свои форматы хранения данных в играх. В следующей статье планируется написать плагин для чтения 3D формата.

Исходники: bitbucket.org/darkprof/pcx/src
Tags:
Hubs:
Total votes 9: ↑9 and ↓0+9
Comments0

Articles