他人の空似自作物置場

cv2converter.zip/main.cpp


#define _SCL_SECURE_NO_WARNINGS 1

#include <iostream>
#include <vector>
#include <algorithm>
#include <array>
#include <string>

#include <png.h>
#include <zlib.h>

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
#include <boost/range.hpp>
#include <boost/range/irange.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

#include <Windows.h>

struct Color {
public:
   Color() {
   }
   Color(unsigned char r, unsigned char g, unsigned char b, unsigned char alpha) :
      r(r), g(g), b(b), alpha(alpha)
   {
   }
   unsigned char r;
   unsigned char g;
   unsigned char b;
   unsigned char alpha;
};

class UCharVectorO : boost::noncopyable {
private:
   std::vector<unsigned char> &result;
public:
   UCharVectorO(std::vector<unsigned char> &result) : result(result) {}
   static void Write(png_struct *png, png_byte *data, png_size_t size) {
      UCharVectorO * const out = reinterpret_cast<UCharVectorO *>(::png_get_io_ptr(png));
      BOOST_ASSERT(out != NULL);
      const unsigned int cur = out->result.size();
      out->result.resize(cur + size);
      std::copy(&data[0], &data[size], &out->result[cur]);
   }
   static void Flush(png_struct *png) {
   }
};

class UCharVectorI : boost::noncopyable {
private:
   const std::vector<unsigned char> &result;
   unsigned int readIndex;
public:
   UCharVectorI(const std::vector<unsigned char> &result) : result(result), readIndex(0) {}
   static void Read(png_struct * const png, png_byte * const data, png_size_t size) {
      UCharVectorI * const out = reinterpret_cast<UCharVectorI *>(::png_get_io_ptr(png));
      if (out->readIndex + size > out->result.size()) {
         size = out->result.size() - out->readIndex;
      }
      std::copy(out->result.begin() + out->readIndex, out->result.begin() + out->readIndex + size, data);
      out->readIndex += size;
   }
};

void png_write_struct_free(png_struct *png) {
   ::png_destroy_write_struct(&png, NULL);
}

void png_info_free(png_struct * const png, png_info *info) {
   ::png_destroy_info_struct(png, &info);
}

void png_read_struct_free(png_struct *png) {
   ::png_destroy_read_struct(&png, NULL, NULL);
}

bool toPng(unsigned int width, unsigned int height, const std::vector<Color> &data, std::vector<unsigned char> &result) {
   const boost::shared_ptr<png_struct> png(::png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL), png_write_struct_free);
   if (!png) {
      return false;
   }
   const boost::shared_ptr<png_info> info(::png_create_info_struct(png.get()), boost::bind(png_info_free, png.get(), _1));
   if (!info) {
      return false;
   }
   result.clear();
   UCharVectorO io(result);
   ::png_set_write_fn(png.get(), &io, UCharVectorO::Write, UCharVectorO::Flush);
   ::png_set_IHDR(png.get(), info.get(), width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE);
   ::png_write_info(png.get(), info.get());
   std::vector<const png_byte *> rows(height);
   for (unsigned int i = 0; i < height; i++) {
      rows[i] = reinterpret_cast<const png_byte *>(&data[i * width]);
   }
   ::png_write_image(png.get(), const_cast<png_byte **>(&rows.front()));
   ::png_write_end(png.get(), info.get());
   return true;
}

bool fromPng(const std::vector<unsigned char> &data, std::vector<Color> &pixels, unsigned int &width, unsigned int &height) {
   const std::unique_ptr<png_struct, void(*)(png_struct *)> png(::png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL), png_read_struct_free);
   const std::unique_ptr<png_info, boost::function<void(png_info *)> > info(::png_create_info_struct(png.get()), boost::bind(png_info_free, png.get(), _1));
   const std::unique_ptr<png_info, boost::function<void(png_info *)> > end(::png_create_info_struct(png.get()), boost::bind(png_info_free, png.get(), _1));
   UCharVectorI io(data);
   ::png_set_read_fn(png.get(), &io, UCharVectorI::Read);
   unsigned char signature[8];
   UCharVectorI::Read(png.get(), signature, 8);
   if (::png_sig_cmp(signature, 0, 8) != 0) {
      return false;
   }
   ::png_set_sig_bytes(png.get(), 8);
   ::png_read_png(png.get(), info.get(), PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_16, NULL);
   width = ::png_get_image_width(png.get(), info.get());
   height = ::png_get_image_height(png.get(), info.get());
   const Color * const * const rows = reinterpret_cast<const Color * const *>(::png_get_rows(png.get(), info.get()));
   pixels.resize(width * height);
   for (unsigned int h : boost::irange(0U, height)) {
      for (unsigned int w : boost::irange(0U, width)) {
         pixels[h * width + w] = rows[h][w];
      }
   }
   return true;
}

bool cv2ToPng(const std::vector<unsigned char> &data, std::vector<unsigned char> &result) {
   const unsigned char cn = data[0];
   const unsigned int w2 = *reinterpret_cast<const unsigned int *>(&data[1]);
   const unsigned int h = *reinterpret_cast<const unsigned int *>(&data[5]);
   const unsigned int w = *reinterpret_cast<const unsigned int *>(&data[9]);
   if (h == 0 || w2 == 0) {
      std::vector<Color> pixels(w2 * h);
      return toPng(w2, h, pixels, result);
   }
   if (cn == 24 || cn == 32) {
      std::vector<Color> pixels(w2 * h);
      const unsigned char *p = &data[17];
      for (unsigned int i = 0; i < w2 * h; i++) {
         pixels[i].b = p[0];
         pixels[i].g = p[1];
         pixels[i].r = p[2];
         pixels[i].alpha = p[3];
         p += 4;
         if (((i + 1) % w2) == 0) {
            p += 4 * (w - w2);
         }
      }
      return toPng(w2, h, pixels, result);
   }
   if (cn == 8) {
      std::wcout << boost::wformat(L"palette型cv2は未対応です、空の画像で出力されます。\n");
      std::vector<Color> palette;
      // loadPalette(palette); TODO
      std::vector<Color> pixels(h * w2);
      const unsigned char *p = &data[17];
      for (unsigned int i = 0; i < h*w2; i += w2, p+= w) {
         for (unsigned int j : boost::irange(0U, w2)) {
            pixels[i * w2 + j] = palette[*p];
         }
      }
      return toPng(w2, h, pixels, result);
   }
   std::wcout << boost::wformat(L"未知の色数です、出力されません。\n");
   return false;
}

bool pngToCv2(const std::vector<unsigned char> &data, std::vector<unsigned char> &result) {
   std::vector<Color> pixels;
   unsigned int width;
   unsigned int height;
   if (!fromPng(data, pixels, width, height)) {
      return false;
   }
   result.resize(1 + 4 + 4 + 4 + 4 + (4 * pixels.size()));
   result[0] = 32;
   *reinterpret_cast<unsigned int *>(&result[1]) = width;
   *reinterpret_cast<unsigned int *>(&result[5]) = height;
   *reinterpret_cast<unsigned int *>(&result[9]) = width;
   *reinterpret_cast<unsigned int *>(&result[13]) = 0;
   std::vector<unsigned char>::iterator it = result.begin() + 17;
   for (const Color &color : pixels) {
      *it = color.b; it++;
      *it = color.g; it++;
      *it = color.r; it++;
      *it = color.alpha; it++;
   }
   return true;
}

int main() {
   std::locale loc = std::locale("japanese").combine<std::numpunct<char> >(std::locale::classic()).combine<std::numpunct<wchar_t> >(std::locale::classic());
   std::locale::global(loc);
   std::wcout.imbue(loc);
   std::cout.imbue(loc);
   int argc;
   const wchar_t * const * const argv = ::CommandLineToArgvW(GetCommandLineW(), &argc);
   for (const unsigned int i : boost::irange(1U, static_cast<unsigned int>(argc))) {
      const boost::filesystem::path path = argv[i];
      if (!boost::filesystem::is_regular_file(path)) {
         std::wcout << boost::wformat(L"%sはファイルではありません。\n") % path;
         continue;
      }
      const unsigned long long int fileSize = boost::filesystem::file_size(path);
      boost::filesystem::ifstream ifs(path, boost::filesystem::ifstream::binary);
      if (!ifs.is_open()) {
         std::wcout << boost::wformat(L"%sを開けませんでした。\n") % path;
         continue;
      }
      std::vector<unsigned char> in(fileSize);
      ifs.read(reinterpret_cast<char *>(&in.front()), in.size());
      ifs.close();
      bool result = false;
      std::vector<unsigned char> out;
      boost::filesystem::path outPath = path;
      if (path.extension() == L".cv2") {
         result = cv2ToPng(in, out);
         outPath.replace_extension(".png");
      } else if (path.extension() == L".png") {
         result = pngToCv2(in, out);
         outPath.replace_extension(".cv2");
      }
      if (!result) {
         std::wcout << boost::wformat(L"%sの変換に失敗しました。\n") % path;
         continue;
      }
      boost::filesystem::ofstream ofs(outPath, boost::filesystem::ofstream::binary);
      ofs.write(reinterpret_cast<const char *>(&out.front()), out.size());
      ofs.close();
   }
   std::wcout << boost::wformat(L"全ての処理が終了しました。\n");
   std::wcin.get();
   return 0;
}