1 // Copyright 2008-present Contributors to the OpenImageIO project. 2 // SPDX-License-Identifier: BSD-3-Clause 3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md 4 5 #pragma once 6 7 // Format reference: ftp://ftp.sgi.com/graphics/SGIIMAGESPEC 8 9 #include <cstdio> 10 11 #include <OpenImageIO/filesystem.h> 12 #include <OpenImageIO/fmath.h> 13 #include <OpenImageIO/imageio.h> 14 15 OIIO_PLUGIN_NAMESPACE_BEGIN 16 17 namespace sgi_pvt { 18 19 // magic number identifying SGI file 20 const short SGI_MAGIC = 0x01DA; 21 22 // SGI file header - all fields are written in big-endian to file 23 struct SgiHeader { 24 int16_t magic; // must be 0xDA01 (big-endian) 25 int8_t storage; // compression used, see StorageFormat enum 26 int8_t bpc; // number of bytes per pixel channel 27 uint16_t dimension; // dimension of he image, see Dimension 28 uint16_t xsize; // width in pixels 29 uint16_t ysize; // height in pixels 30 uint16_t zsize; // number of channels: 1(B/W), 3(RGB) or 4(RGBA) 31 int32_t pixmin; // minimum pixel value 32 int32_t pixmax; // maximum pixel value 33 int32_t dummy; // unused, should be set to 0 34 char imagename[80]; // null terminated ASCII string 35 int32_t colormap; // how pixels should be interpreted 36 // see ColorMap enum 37 }; 38 39 // size of the header with all dummy bytes 40 const int SGI_HEADER_LEN = 512; 41 42 enum StorageFormat { 43 VERBATIM = 0, // uncompressed 44 RLE // RLE compressed 45 }; 46 47 enum Dimension { 48 ONE_SCANLINE_ONE_CHANNEL = 1, // single scanline and single channel 49 MULTI_SCANLINE_ONE_CHANNEL, // multiscanline, single channel 50 MULTI_SCANLINE_MULTI_CHANNEL // multiscanline, multichannel 51 }; 52 53 enum ColorMap { 54 NORMAL = 0, // B/W image for 1 channel, RGB for 3 channels and RGBA for 4 55 DITHERED, // only one channel of data, RGB values are packed int one byte: 56 // red and green - 3 bits, blue - 2 bits; obsolete 57 SCREEN, // obsolete 58 COLORMAP // TODO: what is this? 59 }; 60 61 } // namespace sgi_pvt 62 63 64 65 class SgiInput final : public ImageInput { 66 public: SgiInput()67 SgiInput() { init(); } ~SgiInput()68 virtual ~SgiInput() { close(); } format_name(void)69 virtual const char* format_name(void) const override { return "sgi"; } 70 virtual bool valid_file(const std::string& filename) const override; 71 virtual bool open(const std::string& name, ImageSpec& spec) override; 72 virtual bool close(void) override; 73 virtual bool read_native_scanline(int subimage, int miplevel, int y, int z, 74 void* data) override; 75 76 private: 77 FILE* m_fd = nullptr; 78 std::string m_filename; 79 sgi_pvt::SgiHeader m_sgi_header; 80 std::vector<uint32_t> start_tab; 81 std::vector<uint32_t> length_tab; 82 init()83 void init() 84 { 85 m_fd = nullptr; 86 memset(&m_sgi_header, 0, sizeof(m_sgi_header)); 87 } 88 89 // reads SGI file header (512 bytes) into m_sgi_header 90 // Return true if ok, false if there was a read error. 91 bool read_header(); 92 93 // reads RLE scanline start offset and RLE scanline length tables 94 // RLE scanline start offset is stored in start_tab 95 // RLE scanline length is stored in length_tab 96 // Return true if ok, false if there was a read error. 97 bool read_offset_tables(); 98 99 // read channel scanline data from file, uncompress it and save the data to 100 // 'out' buffer; 'out' should be allocate before call to this method. 101 // Return true if ok, false if there was a read error. 102 bool uncompress_rle_channel(int scanline_off, int scanline_len, 103 unsigned char* out); 104 105 /// Helper: read, with error detection 106 /// fread(void * buf,size_t itemsize,size_t nitems)107 bool fread(void* buf, size_t itemsize, size_t nitems) 108 { 109 size_t n = ::fread(buf, itemsize, nitems, m_fd); 110 if (n != nitems) 111 errorf("Read error"); 112 return n == nitems; 113 } 114 }; 115 116 117 118 class SgiOutput final : public ImageOutput { 119 public: SgiOutput()120 SgiOutput() {} ~SgiOutput()121 virtual ~SgiOutput() { close(); } format_name(void)122 virtual const char* format_name(void) const override { return "sgi"; } 123 virtual int supports(string_view feature) const override; 124 virtual bool open(const std::string& name, const ImageSpec& spec, 125 OpenMode mode = Create) override; 126 virtual bool close(void) override; 127 virtual bool write_scanline(int y, int z, TypeDesc format, const void* data, 128 stride_t xstride) override; 129 virtual bool write_tile(int x, int y, int z, TypeDesc format, 130 const void* data, stride_t xstride, 131 stride_t ystride, stride_t zstride) override; 132 133 private: 134 FILE* m_fd = nullptr; 135 std::string m_filename; 136 std::vector<unsigned char> m_scratch; 137 unsigned int m_dither; 138 std::vector<unsigned char> m_tilebuffer; 139 init()140 void init() { m_fd = NULL; } 141 142 bool create_and_write_header(); 143 144 /// Helper - write, with error detection 145 template<class T> 146 bool fwrite(const T* buf, size_t itemsize = sizeof(T), size_t nitems = 1) 147 { 148 size_t n = std::fwrite(buf, itemsize, nitems, m_fd); 149 if (n != nitems) 150 errorf("Error writing \"%s\" (wrote %d/%d records)", m_filename, 151 (int)n, (int)nitems); 152 return n == nitems; 153 } 154 }; 155 156 157 OIIO_PLUGIN_NAMESPACE_END 158