import bpy from mathutils import Vector surface_data = bpy.data.curves.new('wook', 'SURFACE') surface_data.dimensions = '3D' # 16 coordinates, set points per segments (U * V) points = [ Vector((-1.5, -1.5, 0.0, 1.0)), Vector((-1.5, -0.5, 0.0, 1.0)), # 0, 0, 1 / 0, 1, 0 Vector((-1.5, 0.5, 0.0, 1.0)), Vector((-1.5, 1.5, 0.0, 1.0)), # 0, 1, 1 / 1, 0, 0 Vector((-0.5, -1.5, 0.0, 1.0)), Vector((-0.5, -0.5, 1.0, 1.0)), # 1, 0, 1 / 1, 1, 0 Vector((-0.5, 0.5, 1.0, 1.0)), Vector((-0.5, 1.5, 0.0, 1.0)), # 1, 1, 1 / 0, 0, 0 Vector((0.5, -1.5, 0.0, 1.0)), Vector((0.5, -0.5, 1.0, 1.0)), # 0.0, 0,0, 0.5 / 0.0, 0,5, 0.0 Vector((0.5, 0.5, 1.0, 1.0)), Vector((0.5, 1.5, 0.0, 1.0)), # 0.0, 0.5, 0.5 / 0.5, 0.0, 0.0 Vector((1.5, -1.5, 0.0, 1.0)), Vector((1.5, -0.5, 0.0, 1.0)), # 0.5, 0.0, 0.5 / 0.5, 0.5, 0.0 Vector((1.5, 0.5, 0.0, 1.0)), Vector((1.5, 1.5, 0.0, 1.0)) # 0.5, 0.5, 0.5 / 1.0, 0.2, 0.2 ] for i in range(0, 16, 4): spline = surface_data.splines.new(type='NURBS') spline.points.add(3) # already has a default zero vector for p, new_co in zip(spline.points, points[i:i+4]): p.co = new_co surface_object = bpy.data.objects.new('NURBS_OBJ', surface_data) splines = surface_object.data.splines for s in splines: for p in s.points: p.select = True ####################################### ####################################### # 新しいCollrectionを作成 newCol = bpy.data.collections.new('Collection 1') # 現在のシーンにコレクションをリンク bpy.context.scene.collection.children.link(newCol) # コレクションにオブジェクトをリンク newCol.objects.link(surface_object) ####################################### ####################################### bpy.context.view_layer.objects.active = surface_object bpy.ops.object.mode_set(mode='EDIT') bpy.ops.curve.make_segment() #https://blender.stackexchange.com/questions/126577/blender-2-8-api-python-set-active-object
https://blender.stackexchange.com/questions/126577/blender-2-8-api-python-set-active-object
import bpy
curvedata = bpy.data.curves.new("my curve", type='CURVE')
curvedata.dimensions = '3D'
polyline = curvedata.splines.new('BEZIER')
polyline.bezier_points.add(2) # 制御点数ー1個
polyline.bezier_points[0].co = 0,0,0
polyline.bezier_points[0].handle_left = -1,0,0
polyline.bezier_points[0].handle_right = 1,0,0
polyline.bezier_points[1].co = 2,0,1
polyline.bezier_points[1].handle_left = 1,0,1
polyline.bezier_points[1].handle_right = 3,0,1
polyline.bezier_points[2].co = 4,0,0
polyline.bezier_points[2].handle_left = 3,0,0
polyline.bezier_points[2].handle_right = 5,0,0
obj = bpy.data.objects.new("my bezier", curvedata)
# 新しいCollrectionを作成
newCol = bpy.data.collections.new('Collection 1')
# 現在のシーンにコレクションをリンク
bpy.context.scene.collection.children.link(newCol)
# コレクションにオブジェクトをリンク
newCol.objects.link(obj)
import bpy
#############################################################
#############################################################
# メタボール(一つ目)
# add metaball object
mball = bpy.data.metaballs.new("MyBall")
mball.resolution = 0.15 # View resolution
#mball.render_resolution
ele = mball.elements.new()
ele.co = (0.0, 0.0, 0.0)
ele.use_negative = False
ele.radius = 2.0
# create meta ball
obj1 = bpy.data.objects.new("MyBallObjectK", mball)
#############################################################
#############################################################
# メタボール(二つ目)
mball = bpy.data.metaballs.new("MyBall") mball.resolution = 0.15 # View resolution ele = mball.elements.new() ele.co = (1.0, 1.0, 1.0) ele.use_negative = False ele.radius = 2.0 # create meta ball obj2 = bpy.data.objects.new("MyBallObjectK", mball)# 名前を同じにすることで MyBallObjectK.001 のような名前にする
#############################################################
#############################################################
# 新しいCollrectionを作成
newCol = bpy.data.collections.new('Collection 1')
# 現在のシーンにコレクションをリンク
bpy.context.scene.collection.children.link(newCol)
newCol.objects.link(obj1)
newCol.objects.link(obj2)
#https://developer.blender.org/T33455
Blender Pythonでメタボールを作ろうとして、以下の問題に遭遇した。
つまり、メタボールのオブジェクトを二つ作ったが中身が表示されなかった。

メタボールの挙動は、オブジェクト名に依存する。
メタボールは、オブジェクト名が数字で終わらないものをmotherとし、それ以外の、同じ名前 + . + 数字のオブジェクト同士が結合する
・Kball / Mball ... 名前が違うと独立する
・Mball2 ... 名前の最後が数字だと表示されない
・Kball / Kball2 ... 名前が同じでも、単純に後ろに数字を付けるだけでは表示されない
・Kball / Kball.001 ... 同じ名前.数字 の組み合わせで初めてくっつく
libpngで8bit,16bit以外のビット深度のグレイスケール画像を読み込むと、メモリ上には1bit,2bit,4bitなどごとにデータが格納されているので、1byteごとデータを取り出すような簡単な処理で取得できない。
ちょっと調べた限りでは画素値を素直に取り出す関数などが見当たらなかった(あるかもしれない)のでとりあえず適当に作ることにした。
例えば、bit depth==4で
10101011 , 01001110 , 10000101 というデータがあったら、
1010,1011,0100,1110,1000,0101
と分割する
対象が1bit,2bit,4bitの時は、以下のような感じで、該当部分のビット列だけを抜き出して右にシフトする。
対象が8bitの時はデータをそのままunsigned char型の配列として扱う。
対象が16bitの時はデータをそのままunsigned short型の配列として扱い、取得した2byteの前後をswapする。
//! @file CGetGrayBits.hpp //! @brief 1bit,2bit,4bit,8bit,16bitごとに納められたデータを取得する #pragma once //! @class CGetGrayBits //! @brief 配列上のx番目の画素値を取得するクラス class CGetGrayBits { unsigned char* m_p; //!< 画素へのポインタ int m_depth; //!< ビット深度 unsigned char m_mask = 0; //!< depth8未満の時のビットマスク public:
//! @brief コンストラクタ //! @param [in] p 画素配列へのポインタ //! @param [in] depth ビット深度。1画素が何ビットで表現されているか CGetGrayBits( const void* const p, const int depth) : m_p(static_cast<unsigned char*>(const_cast<void*>(p))), m_depth(depth) { //ビット深度が8未満の時に使用するマスクを作成 if (m_depth < 8) { for (int i = 0; i < m_depth; i++) m_mask = (m_mask << 1) | 1; } }
//! @brief 指定した番号の画素を取得 //! @param [in] x 画素番号 //! @return 画素値 int operator[](const std::size_t x) { switch (m_depth) { case 1: case 2: case 4: return getgrayLess8(x); break; case 8: return getgray8(x); break; case 16: return getgray16(x); break; } return -1; }
//! @brief 指定した番号の画素を取得(ビット深度==8の時) //! @param [in] x 画素番号 //! @return 画素値をintで表現したもの inline int getgray8(const std::size_t x)const { return static_cast<int>(m_p[x]); }
//! @brief 指定した番号の画素を取得(ビット深度==16の時) //! @param [in] x 画素番号 //! @return 画素値をintで表現したもの inline int getgray16(const std::size_t x)const { unsigned short t = reinterpret_cast<unsigned short*>(m_p)[x]; std::swap( reinterpret_cast<unsigned char*>(&t)[0], reinterpret_cast<unsigned char*>(&t)[1]); return t; }
//! @brief 指定した番号の画素を取得(ビット深度<8の時) //! @param [in] x 画素番号 //! @return 画素値をintで表現したもの inline int getgrayLess8(const std::size_t x)const { // x ドットが何バイト目かを算出 (0開始) std::size_t byte = x / (8 / m_depth); unsigned char c = m_p[byte]; std::size_t pos = x % (8 / m_depth); int shift = 8 - m_depth - pos * m_depth; unsigned char mask1 = m_mask << shift; unsigned int val = (c & mask1) >> shift; return val; }
};
#pragma once #include <cstdlib> #include <iostream> #include <vector> #include <bitset> #include <png.h> #include <algorithm> #include "CGetGrayBits.hpp" #pragma comment(lib,"libpng16.lib") void out_to_ppm( const char* outpath, const int width, const int height, const std::vector<int>& pixels );
//エラーの時強制終了 void abort_(const char* c) { printf(c); abort(); } //! @brief pngファイル読み込み関数 //! @param [in] file_name ファイル名 //! @param [out] width 画像幅(ピクセル) //! @param [out] height 画像高さ(ピクセル) //! @param [out] color_type RGBかRGBAか...等 //! @param [out] bit_depth チャンネルのビット数 //! @param [out] row_pointers 画像データへのポインタのポインタ //! @param [out] palette パレット //! @param [out] palette_num パレット色の個数 void read_png( const char* file_name, int* width, int* height, png_byte* color_type, png_byte* bit_depth, png_bytep** row_pointers, png_colorp* palette, int* palette_num ) { png_byte header[8]; // 8 is the maximum size that can be checked FILE* fp = fopen(file_name, "rb"); if (!fp) { abort_("[read_png_file] File could not be opened for reading"); } fread(header, 1, 8, fp); if (png_sig_cmp(header, 0, 8)) { abort_("[read_png_file] File is not recognized as a PNG file"); } png_structp png_ptr; png_infop info_ptr; /* initialize stuff */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) abort_("[read_png_file] png_create_read_struct failed"); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) abort_("[read_png_file] png_create_info_struct failed"); if (setjmp(png_jmpbuf(png_ptr))) abort_("[read_png_file] Error during init_io"); png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); ///////////////////////////////////////// // 画像情報の取得 *width = png_get_image_width(png_ptr, info_ptr); *height = png_get_image_height(png_ptr, info_ptr); *color_type = png_get_color_type(png_ptr, info_ptr); *bit_depth = png_get_bit_depth(png_ptr, info_ptr); // ///////////////////////////////////////// ///////////////////////////////////////// // パレットの取得 if (*color_type & PNG_COLOR_TYPE_PALETTE) { png_get_PLTE(png_ptr, info_ptr, palette, palette_num); } // ///////////////////////////////////////// int number_of_passes; number_of_passes = png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); /* read file */ if (setjmp(png_jmpbuf(png_ptr))) abort_("[read_png_file] Error during read_image"); ///////////////////////////////////////// // 画像の読み込み *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * *height); for (int y = 0; y < *height; y++) (*row_pointers)[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, info_ptr)); png_read_image(png_ptr, *row_pointers); // ///////////////////////////////////////// fclose(fp); }
int main() { int width; int height; png_byte color_type; png_byte bit_depth; png_bytep* row_pointers; png_colorp palette; int palette_num; //////////////////////////////// // 画像読み込み read_png( R"(c:\test\data\snail2.png)", &width, &height, &color_type, &bit_depth, &row_pointers, &palette, &palette_num ); // //////////////////////////////// std::cout << "width : " << width << std::endl; std::cout << "height: " << height << std::endl; std::cout << "colortype: " << (int)color_type << std::endl; std::cout << "bitdepth: " << (int)bit_depth << std::endl; //////////////////////////////// //グレイスケール画像だけを対象とする if (color_type != (PNG_COLOR_TYPE_GRAY )){ std::cout << "not support" << std::endl; int i; std::cin >> i; return -1; } // ////////////////////////////////
//////////////////////////////// // 内容を取得 std::vector<int> imgvec; for (int y = 0; y < height; y++) { png_bytep yhead = row_pointers[y]; // 画像の横一列の先頭を取得 CGetGrayBits g(yhead, bit_depth); // 画素取得の準備 for (int x = 0; x < width; x++) { int index = g[x]; // x番目の画素値をintで取得 imgvec.push_back( index ); // 画素値を保存 } } // ///////////////////////////////////////////////
/////////////////////////////////////////////// // 画像として保存 char outpath[1024]; sprintf(outpath, R"(C:\test\out-%d-palette.ppm)",bit_depth); out_to_ppm(outpath, width, height, imgvec); // /////////////////////////////////////////////// /////////////////////////////////////////////// // メモリの解放 for (size_t i = 0; i < (size_t)height; i++) { png_bytep yhead = row_pointers[i]; free(yhead); } free(row_pointers); // /////////////////////////////////////////////// int i; std::cin >> i; } void out_to_ppm(const char* outpath,const int width, const int height, const std::vector<int>& pixels) { int pixmax = *std::max_element(pixels.begin(), pixels.end()); FILE* fp = fopen(outpath, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); for (size_t y = 0; y < (size_t)height; y++) { for (size_t x = 0; x < (size_t)width; x++) { int index = y * width + x; unsigned char c = pixels[index] / (double)pixmax * 255; fprintf(fp, "%d %d %d ", c, c, c); } fprintf(fp, "\n"); } fclose(fp); }
前回ImageMagickで作成したデータを用いる。
libpng関係でグレースケール画像がほしい。
それもただのグレースケール画像では無く、bit-depthがそれぞれ1bit,2bit,4bit,8bit,16bitのものがほしい。
諸事情により自前で調達することにした。
ImageMagickはコマンドラインから画像を操作するソフトウェア。
ダウンロードは以下:
https://imagemagick.org/script/download.php
Windowsの場合、Q16とついているものを選んだ方がよりよい。
以下の要領でコマンドを実行する
例:
ちなみに、-depthを3や6といった数値にしても、png時のbit depthは勝手に8になる。
以前にも、以下の記事あたりで使ったのだが、メモリデバイスコンテキストをクラス化する話。
//! @file MdcCanvas.hpp //! @brief メモリデバイスコンテキストのカプセル化 //! @date 2020/04/22 別コードから移植 //! @date 2020/04/25 コメント整理 #pragma once #include <cassert> #include <Windows.h> namespace szl { struct cbit24_t; struct cbit32_t; //! @brief メモリデバイスコンテキストのカプセル化 //! @tparam PIXEL_BITS 1画素のデータ型。
template<typename PIXEL_BITS> class CMdcCanvas { //CreateDIBSection , DDB , DIB 関連 BITMAPINFO m_bmpInfo; //!< CreateDIBSectionに渡す構造体 LPDWORD m_lpPixel; //!< 画素へのポインタ HBITMAP m_hBitmap; //!< 作成したMDCのビットマップハンドル HDC m_hMemDC; //!< 作成したMDCのハンドル int m_imagesizemax; //!< 合計画素数(width*height) int m_width; //!< 画像の幅(ピクセル,自然数) int m_height; //!< 画像の高さ(ピクセル,自然数) public: //! @brief 新しい画像を作成。すでにあるなら現在のものを破棄して再作成 //! @param [in] width 画像の幅。( > 0) //! @param [in] height 画像の高さ。負数の時は左下原点になる //! @param [in] hdc CreateCompatibleDCに渡すDCのハンドル。指定しない場合はCreateDCで自動作成 //! @retval true 作成した //! @retval false 作成しなかった bool create_screen(int width, int height, HDC hdc=nullptr); //! @brief 現在使用している画像を削除。削除済みか作成されていないなら何もしない //! @return なし void delete_screen(); //! @brief 仮想画面の幅を取得 //! @return 画像幅(自然数) int get_width()const { return m_width; } //! @brief 仮想画面の高さを取得 //! @return 画像高さ(自然数) int get_height()const { return m_height; } //! @brief メモリデバイスコンテキストを取得 (const) //! @return メモリデバイスコンテキスト const HDC get_mem_dc()const { return m_hMemDC; } //! @brief メモリデバイスコンテキストを取得 //! @return メモリデバイスコンテキスト HDC get_mem_dc() { return m_hMemDC; } //! @brief BITMAPINFO構造体を取得 //! @return BITMAPINFO構造体のコピー BITMAPINFO get_BITMAPINFO()const { return m_bmpInfo; } //! @brief HBITMAPを取得 //! @return HBITMAPのコピー HBITMAP get_HBITMAP()const { return m_hBitmap; } //! @brief 書き込み用のメモリ領域へのアドレスを取得 PIXEL_BITS* get_mem_pixel() { return reinterpret_cast<PIXEL_BITS * >(m_lpPixel); } //! @brief 書き込み用のメモリ領域へのアドレスを取得 const PIXEL_BITS* get_mem_pixel()const { return reinterpret_cast<PIXEL_BITS*>(m_lpPixel); } //! @brief 画面に転送を行う //! @param [in] hdc 転送先のデバイスコンテキスト //! @param [in] rop 転送モード //! @return なし void bitblt_to(const HDC hdc,const DWORD rop = SRCCOPY)const; //! @brief 仮想画面が確保されているか //! @retval true 使用可能 //! @retval false 使用不可 bool is_valid()const { return (m_hBitmap == nullptr) ? false : true; } //! @brief 一次元配列として画素へアクセスする //! @param [in] index 要素番号 //! @return 画素への参照 //! @note indexが不正な場合assertする PIXEL_BITS& operator[](const int index) { assert(index >= 0 && index < m_imagesizemax); return get_mem_pixel()[index]; } //! @brief 一次元配列として画素へアクセスする(const) //! @param [in] index 要素番号 //! @return 画素への参照 //! @note indexが不正な場合assertする const PIXEL_BITS& operator[](const int index)const { assert(index >= 0 && index < m_imagesizemax); return get_mem_pixel()[index]; } //! @brief 二次元配列として画素へアクセスする //! @param [in] x X座標 //! @param [in] y Y座標 //! @return 画素への参照 //! @note 座標が不正な場合assertする PIXEL_BITS& operator()(const int x, const int y) { assert(x >= 0 && y >= 0); assert(x < get_width() && y < get_height()); assert((x + y * get_width()) < m_imagesizemax); return get_mem_pixel()[x + y * get_width()]; } //! @brief 二次元配列として画素へアクセスする //! @param [in] x X座標 //! @param [in] y Y座標 //! @return 画素への参照 //! @note 座標が不正な場合assertする const PIXEL_BITS& operator()(const int x, const int y)const { assert(x >= 0 && y >= 0); assert(x < get_width() && y < get_height()); assert((x + y * get_width()) < m_imagesizemax); return get_mem_pixel()[x + y * get_width()]; } //! @brief 初期化を行う CMdcCanvas(void); //! @brief 画像を破棄する ~CMdcCanvas(void); };
//! @struct cbit24_t //! @brief 1画素 3Byte の場合のデータ型 struct cbit24_t { unsigned char v[3]; //!< データ3Byte cbit24_t() {} cbit24_t(unsigned char r, unsigned char g, unsigned char b) :v{ b,g,r } {} unsigned char& r() { return v[2]; } //!< 赤を取得 unsigned char& g() { return v[1]; } //!< 緑を取得 unsigned char& b() { return v[0]; } //!< 青を取得 }; //! @struct cbit32_t //! @brief 1画素 4Byte の場合のデータ型 struct cbit32_t { unsigned char v[4]; //!< データ3Byte cbit32_t() {} cbit32_t(unsigned char r, unsigned char g, unsigned char b, unsigned char u = 0) :v{ b,g,r,u } {} unsigned char& u() { return v[3]; } //!< 未使用領域を取得 unsigned char& r() { return v[2]; } //!< 赤を取得 unsigned char& g() { return v[1]; } //!< 緑を取得 unsigned char& b() { return v[0]; } //!< 青を取得 }; //! @brief データ型のビット数を返す //! @tparam T データ型 //! @return sizeofの8倍 template<typename T> inline constexpr int bit_count() { return sizeof(T)*8; } //! @brief データ型のビット数を返す(自分定義3byte用) //! @return 24 template<>inline constexpr int bit_count<cbit24_t>() { return 24; } //! @brief データ型のビット数を返す(自分定義4byte用) //! @return 32 template<>inline constexpr int bit_count<cbit32_t>() { return 32; } template<typename PIXEL_BITS> CMdcCanvas<PIXEL_BITS>::CMdcCanvas(void) { m_hBitmap = nullptr; m_hMemDC = nullptr; m_lpPixel = nullptr; m_width = 0; m_height = 0; m_imagesizemax = 0; } template<typename PIXEL_BITS> CMdcCanvas<PIXEL_BITS>::~CMdcCanvas(void) { delete_screen(); } template<typename PIXEL_BITS> bool CMdcCanvas<PIXEL_BITS>::create_screen(int width, int height, const HDC hdc) { //指定がおかしければ削除される if (width <= 0 || abs(height) <= 0) { delete_screen(); return false; } //既にあるなら削除する if (is_valid()) { delete_screen(); } m_width = width; m_height = abs(height); //デバイスコンテキストのハンドルの設定 HDC hdcd; if (hdc == nullptr) { hdcd = CreateDCA("DISPLAY", 0, 0, 0); } else { hdcd = hdc; } //DIBの情報を設定する m_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); m_bmpInfo.bmiHeader.biWidth = width; m_bmpInfo.bmiHeader.biHeight = -height; //-を指定しないと上下逆になる m_bmpInfo.bmiHeader.biPlanes = 1; m_bmpInfo.bmiHeader.biBitCount = bit_count<PIXEL_BITS>(); m_bmpInfo.bmiHeader.biCompression = BI_RGB; m_hBitmap = CreateDIBSection(hdcd, &m_bmpInfo, DIB_RGB_COLORS, (void**)&m_lpPixel, nullptr, 0); m_hMemDC = CreateCompatibleDC(hdcd); if (hdc == nullptr) { DeleteDC(hdcd); } if (m_hBitmap == nullptr || m_hMemDC == nullptr) { delete_screen(); return false; } SelectObject(m_hMemDC, m_hBitmap); m_imagesizemax = m_width * m_height; return true; } template<typename PIXEL_BITS> void CMdcCanvas<PIXEL_BITS>::delete_screen() { if (m_hBitmap == nullptr) return; DeleteDC(m_hMemDC); DeleteObject(m_hBitmap); m_lpPixel = nullptr; m_hBitmap = nullptr; m_hMemDC = nullptr; m_width = 0; m_height = 0; m_imagesizemax = 0; } template<typename PIXEL_BITS> void CMdcCanvas<PIXEL_BITS>::bitblt_to(const HDC hdc,const DWORD rop)const { BitBlt(hdc, 0, 0, get_width(), get_height(), m_hMemDC, 0, 0, rop); } using CMdcCanvas24 = CMdcCanvas<cbit24_t>; using CMdcCanvas32 = CMdcCanvas<cbit32_t>; }
#include<windows.h> #include "MdcCanvas.hpp" //メモリデバイスコンテキスト作成 szl::CMdcCanvas24 vscr;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; switch (msg) { case WM_SIZE: InvalidateRect(hwnd, nullptr, TRUE); break; case WM_PAINT: PAINTSTRUCT ps; hdc = BeginPaint(hwnd, &ps); vscr.bitblt_to(hdc);//画面に表示 EndPaint(hwnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("DC_TEST"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("DC_TEST"), TEXT("mem dc test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL ); //画像作成 HDC dc = GetDC(hwnd); vscr.create_screen(200, 100,dc); ReleaseDC(hwnd, dc); int i = 0; //メモリに直接アクセス for (int i = 0; i < 200 * 100; i++) { vscr.get_mem_pixel()[i] = szl::cbit24_t(100, 255, 100); } //GDIオブジェクトとして扱う HPEN hp = CreatePen(PS_SOLID, 5, RGB(255, 0, 0)); HPEN oldp = (HPEN)SelectObject(vscr.get_mem_dc(), hp); MoveToEx(vscr.get_mem_dc(), -10, -10,nullptr); LineTo(vscr.get_mem_dc(), 50, 80); SelectObject(vscr.get_mem_dc(), oldp); DeleteObject(hp); //二次元配列の形でアクセス vscr(0, 0) = szl::cbit24_t(255, 0, 0); vscr(0, 1) = szl::cbit24_t(255, 0, 0); vscr(0, 2) = szl::cbit24_t(0, 255, 0); vscr(0, 3) = szl::cbit24_t(0, 0, 255); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
世の中にはたくさんの「簡単に使えるようにクラスにしてまとめました」があるが、そのほとんどは使い物にならないと私は考えている。もちろん自分で作ったものも含めて。
というのも、クラス化というのはある程度の目的や明確な用途があって初めて効果を発揮するものなので、普段自分が使う部分だけが使いやすくなるように作って使いやすくなったように見えてもそれは自分にとってだけなのだ。むしろ提示されたほうは学習コストが無駄にかかる。
さて、薄々気づいている人もいるかと思うが、著者は今ライザのアトリエをやっていてブログを書く暇がガチでない。というわけで、もう暫くできるだけ楽に更新できる内容で繋いでいきたい。
Windowsプログラミングはエントリポイントが原則としてWinMainになる。
とはいえmainからは動かないかというとそんなことはない。
ウィンドウを開くために必要なhInstanceはGetModuleHandle関数に0を渡すことで取得できる。
#include <Windows.h> #include <cstdio> LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
int main() { //インスタンスハンドルの取得 HINSTANCE hInstance = GetModuleHandle(0); HWND hwnd; WNDCLASS winc; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("WITHOUT_WINMAIN"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("WITHOUT_WINMAIN"), TEXT("window without WinMain"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; printf("success\n"); // これを入れるとコンソールに表示される FreeConsole(); // これを入れるとコンソール自体が消える MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return msg.wParam; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_LBUTTONDOWN: MessageBox(hwnd, L"hello", 0, 0); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
これだけだとあまりにも記事として寂しいので、ウィンドウ生成をDLL内に入れることを考える。
これを発展させればクロスプラットフォームなGUIライブラリを作れる(多分)
#pragma once #ifdef DLL_EXPORT_DO /* DLLを作る場合 */ #define __DLL_PORT extern "C" __declspec(dllexport) #else /* DLLを使う場合 */ #define __DLL_PORT extern "C" __declspec(dllimport) #endif
#pragma once #include<Windows.h> #include "../dllport.h" LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved); __DLL_PORT int open_window();
#include "winshow.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_LBUTTONDOWN: MessageBox(hwnd, L"hello in dll", 0, 0); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
int open_window() { //インスタンスハンドルの取得 HINSTANCE hInstance = GetModuleHandle(0); HWND hwnd; WNDCLASS winc; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("WITHOUT_WINMAIN"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("WITHOUT_WINMAIN"), TEXT("window without WinMain"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return msg.wParam; }
#include <Windows.h> #include <cstdio> #include "../winshow/winshow.h" #pragma comment(lib,"winshow.lib") int main() { return open_window(); }
16bit Grayscale画像の場合、以下のコードのように、2byte値の前後を交換して与えなければいけない。
#include <cstdlib> #include<algorithm> #include <png.h> #pragma comment(lib,"libpng16d.lib") //エラーの時強制終了 void abort_(const char* c) { printf(c); abort(); } int main() { int width; int height; png_byte color_type = PNG_COLOR_TYPE_GRAY;// グレイスケール png_byte bit_depth = 16;// 2バイト png_bytep* row_pointers;
//////////////////////////////// // 画像作成 height = 1; width = 4; row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); row_pointers[0] = (png_byte*)malloc(4*2); png_byte* xhead; xhead = row_pointers[0]; unsigned short* p0 = (unsigned short*)&xhead[0];// 1byte置きに画素を配置する unsigned short* p1 = (unsigned short*)&xhead[2]; unsigned short* p2 = (unsigned short*)&xhead[4]; unsigned short* p3 = (unsigned short*)&xhead[6]; unsigned short v; *p0 = 0; v = USHRT_MAX / 4;// 画素値を設定 std::swap( ((unsigned char*)(&v))[0], ((unsigned char*)(&v))[1]);// 2byte画素値の前後を入れ替える *p1 = v; v = USHRT_MAX/2;// 画素値を設定 std::swap(((unsigned char*)(&v))[0], ((unsigned char*)(&v))[1]);// 2byte画素値の前後を入れ替える *p2 = v; v = USHRT_MAX;// 画素値を設定 std::swap(((unsigned char*)(&v))[0], ((unsigned char*)(&v))[1]);// 2byte画素値の前後を入れ替える *p3 = v; // ////////////////////////////////
const char* file_name = R"(c:\test\test_16bit_1x4.png)"; /* create file */ FILE* fp = fopen(file_name, "wb"); if (!fp) abort_("[write_png_file] File could not be opened for writing"); png_structp png_ptr; /* initialize stuff */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) abort_("[write_png_file] png_create_write_struct failed"); png_infop info_ptr; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) abort_("[write_png_file] png_create_info_struct failed"); if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error during init_io"); png_init_io(png_ptr, fp); ///////////////////////////////////// //ヘッダの設定 if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error during writing header"); png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); // ///////////////////////////////////// ///////////////////////////////////// // 画像のファイル出力 if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error during writing bytes"); png_write_image(png_ptr, row_pointers); // end write if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error during end of write"); png_write_end(png_ptr, NULL); // ///////////////////////////////////// fclose(fp); /////////////////////////////////////////////// // メモリの解放 for (size_t i = 0; i < (size_t)height; i++) { png_bytep yhead = row_pointers[i]; free(yhead); } free(row_pointers); // /////////////////////////////////////////////// }
libpng 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み
前記事で出力した16bit grayscale画像を読み込む。
この場合も入力画像の一画素の前後を反転させなければならない。
#pragma once #include <cstdlib> #include <iostream> #include <bitset> #include <png.h> #pragma comment(lib,"libpng16d.lib") //エラーの時強制終了 void abort_(const char* c) { printf(c); abort(); } //! @brief pngファイル読み込み関数 //! @param [in] file_name ファイル名 //! @param [out] width 画像幅(ピクセル) //! @param [out] height 画像高さ(ピクセル) //! @param [out] color_type RGBかRGBAか...等 //! @param [out] bit_depth チャンネルのビット数 //! @param [out] row_pointers 画像データへのポインタのポインタ void read_png( const char* file_name, int* width, int* height, png_byte* color_type, png_byte* bit_depth, png_bytep** row_pointers ) { png_byte header[8]; // 8 is the maximum size that can be checked FILE* fp = fopen(file_name, "rb"); if (!fp) { abort_("[read_png_file] File could not be opened for reading"); } fread(header, 1, 8, fp); if (png_sig_cmp(header, 0, 8)) { abort_("[read_png_file] File is not recognized as a PNG file"); } png_structp png_ptr; png_infop info_ptr; /* initialize stuff */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) abort_("[read_png_file] png_create_read_struct failed"); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) abort_("[read_png_file] png_create_info_struct failed"); if (setjmp(png_jmpbuf(png_ptr))) abort_("[read_png_file] Error during init_io"); png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); ///////////////////////////////////////// // 画像情報の取得 *width = png_get_image_width(png_ptr, info_ptr); *height = png_get_image_height(png_ptr, info_ptr); *color_type = png_get_color_type(png_ptr, info_ptr); *bit_depth = png_get_bit_depth(png_ptr, info_ptr); // ///////////////////////////////////////// int number_of_passes; number_of_passes = png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); /* read file */ if (setjmp(png_jmpbuf(png_ptr))) abort_("[read_png_file] Error during read_image"); ///////////////////////////////////////// // 画像の読み込み *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * *height); for (int y = 0; y < *height; y++) (*row_pointers)[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, info_ptr)); png_read_image(png_ptr, *row_pointers); // ///////////////////////////////////////// fclose(fp); } int main() { int width; int height; png_byte color_type; png_byte bit_depth; png_bytep* row_pointers; //////////////////////////////// // 画像読み込み read_png( R"(c:\test\test_16bit_1x4.png)", &width, &height, &color_type, &bit_depth, &row_pointers ); // //////////////////////////////// std::cout << "width : " << width << std::endl; std::cout << "height: " << height << std::endl; std::cout << "colortype: " << (int)color_type << std::endl; std::cout << "bitdepth: " << (int)bit_depth << std::endl; //////////////////////////////// //今回は1ビットグレイスケール画像だけを対象にする if ((color_type != PNG_COLOR_TYPE_GRAY) || (bit_depth != 16) ) { return -1; } // ////////////////////////////////
//////////////////////////////// // 内容を表示 png_bytep yhead = row_pointers[0]; png_bytep xpix = yhead; unsigned short* pp = (unsigned short*)xpix; std::swap(((unsigned char*)(&pp[0]))[0], ((unsigned char*)(&pp[0]))[1]); std::swap(((unsigned char*)(&pp[1]))[0], ((unsigned char*)(&pp[1]))[1]); std::swap(((unsigned char*)(&pp[2]))[0], ((unsigned char*)(&pp[2]))[1]); std::swap(((unsigned char*)(&pp[3]))[0], ((unsigned char*)(&pp[3]))[1]); std::cout << "[0]" << std::bitset<16>(pp[0]) << std::endl; std::cout << "[1]" << std::bitset<16>(pp[1]) << std::endl; std::cout << "[2]" << std::bitset<16>(pp[2]) << std::endl; std::cout << "[3]" << std::bitset<16>(pp[3]) << std::endl; // ///////////////////////////////////////////////
/////////////////////////////////////////////// // メモリの解放 for (size_t i = 0; i < (size_t)height; i++) { png_bytep yhead = row_pointers[i]; free(yhead); } free(row_pointers); // /////////////////////////////////////////////// int i; std::cin >> i; }
libpng 1bit / 4bit PNG_COLOR_TYPE_GRAY 出力
libpng PNG_COLOR_TYPE_PALETTEでパレットを使った出力
libpng パレット色(PNG_COLOR_TYPE_PALETTE)画像の読み込み
libpng 16bit PNG_COLOR_TYPE_GRAY 読み込み