stbライブラリ

# 前置き(注意)


ど素人がとりあえず動けばいい精神で作ってます。ご注意ください。
間違っていて沼っても責任は取りません。
要するにこの記事片手にヘッダファイルの先頭の取説(英語)読め




# やりたいこと　
C言語をつかって画像(png)を読み込んでリサイズして書き出したい

~~Python使え？めんどくせえ~~


# つかったもの　
- Visual Studio 2022
- stbライブラリ([github](https://github.com/nothings/stb)からもらってこよう)
- C言語の知識(ポインタとファイルの読み書き、配列)

# サンプルコード(解説はこれの下)
```C
//ライブラリ関係のマクロ定義
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION

//お節介なVisual Studioのためのマクロ定義
#define _CRT_SECURE_NO_WARNINGS



#include<stb_image_write.h>
#include<stb_image.h>
#include<stb_image_resize.h>
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>



int main(int argc, char* argv[]) {


	unsigned char* pixel = NULL;//読み込んだ画像データ格納
	unsigned char* pixel_resize =NULL;//リサイズ後の画像データ格納
	int width = 0, height = 0, bpp = 0;//読み込んだファイルのサイズを格納
	int re_width = 1280, re_height = 720;//リサイズするサイズを定義

	

	//ドラッグされなかった時の処理
	if (argc < 2) {
		printf("ファイルをドラッグして起動してください\n");
		Sleep(5000);
		return 0;
	}
	

	//メイン処理
	for (int i = 1; i < argc; i++) {

		//画像をロード
		pixel = stbi_load(argv[i], &width, &height, &bpp, 4);

		//NULL処理
		if (pixel == NULL) {

			printf("%d枚目の画像の読み込みに失敗しました。\n", i);
			Sleep(5000);
			return -1;

		}
		
        //メモリを確保
		pixel_resize = (unsigned char*)malloc((size_t)re_width * re_height * 4);
		

		//リサイズ
		stbir_resize_uint8(pixel, width, height, 0, pixel_resize, re_width, re_height, 0, bpp);


		//出力
		stbi_write_png(argv[i],re_width, re_height, bpp, pixel_resize,  0);

		//ファイルポインタ解放
		stbi_image_free(pixel);
		free(pixel_resize);

		printf("%d枚目の画像を処理しました。\n", i);

	}
	return 0;
}
```

# 解説
## 下準備
* [github](https://github.com/nothings/stb)からライブラリもらってくる
* 適当に解凍
* Visual Studioのプロジェクトのプロパティを開いて追加のインクルードディレクトリを設定![resize_1280_720 - Microsoft Visual Studio 2023_05_01 22_03_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2944355/8f8d89e3-42c1-85a3-e08b-1c2beed6211b.png)
* この記事を片手にヘッダファイルの取説をGoogle翻訳の力を借りて読む


## #define
``` C
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION
```

ライブラリを使うために必要。なんでかは知らん。
それぞれのヘッダファイルの初めのほうに必要なマクロ定義が説明されているので要確認。(知らなくて沼った)

## #include
```C
#include<stb_image_write.h>
#include<stb_image.h>
#include<stb_image_resize.h>
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
```
上から順に
画像を書き出すライブラリ
画像を読み込むライブラリ
画像をリサイズするライブラリ
printf()とかが定義されてるやつ
malloc()とかが定義されているやつ
sleep()を使うためだけに入れたやつ(ぶっちゃけ要らん

## ライブラリについて

注)サンプルコード基準で説明します。変数名が分かりにくくてすまん。

### 読み込み

```C
pixel = stbi_load(argv[i], &width, &height, &bpp, 4);
```
**pixel**
読み込んだ画像データのポインタが突っ込まれる。
unsigned char*型で宣言すること。
読み込みに失敗したらNULLを入れるのでエラー処理しよう。

**argv[i]**
読み込む画像の名前を文字列として入れる。
フルパス指定もいける。相対パスは知らん。

**&width**
読み込んだ画像の幅が指定したポインタに格納される。
int型で宣言。

**&height**
読み込んだ画像の高さを(ry

**&bpp**
読み込んだ画像が1ピクセルあたりunsigned charの変数いくつで表現されているかを格納。
RGBで表現される場合は3、RGBAの場合は4を返すっぽい。
詳しくはヘッダファイル読め。

**4**
0以外を指定すると読み込んだ画像をunsigned charの変数いくつで表現するかを強制的に指定。
詳しくはヘッダファイル読め。
ちなみに、例えば4を指定したときにRGBの画像を読み込んだ場合はAの値が常に255になる
~~これ常に4を指定してRGBA形式を前提としたコード書くのが安定な気がしてきた~~

**蛇足**
png以外にもいろいろ読めるっぽい
ヘッダファイルを(ry


## メモリ解放
```C
stbi_image_free(pixel);
```
**pixel**
使い方はfree()と同じ。
主にstbi_image_load()で読み込んだファイルを解放する。
ちゃんと解放しようね。

### リサイズ
```C
stbir_resize_uint8(pixel, width, height, 0, pixel_resize, re_width, re_height, 0, bpp);
```
**pixel**
リサイズしたい画像データの配列　
unsigned char*型
stbi_load()で読み込んだ画像データを使うのがベターか　

**width**
リサイズしたい画像の幅　

**height** 
リサイズしたい画像の高さ　

**0** 
なんか指定するっぽいが0を入れるとライブラリ側で勝手にいい感じの値を入れてくれるのでライブラリにお任せするのが安定。変にいじると現代アートが生成される。(された)
詳しいことはヘッダファイル読め

**pixel_resize**
リサイズした後のデータを入れる配列。
もちろんunsigned char*型。
あらかじめmalloc()あたりでメモリ確保しておくこと。忘れるとライブラリ内で領域外アクセスして落ちるよ!(沼った)
~~ライブラリ側でやってくれよ~~

**re_width**
リサイズ後の画像の幅　

**height** 
リサイズ後の画像の高さ　

**0** 
なんか指定するっぽいが0を入れるとライブラリ側で勝手にいい感じの(ry

**bpp**
読み込んだ画像が1ピクセルあたりunsigned charの変数いくつで表現されているかを入れる。
RGBで表現される場合は3、RGBAの場合は4。
詳しくはヘッダファイル読め。

### 書き出し
```C
stbi_write_png(argv[i],re_width, re_height, bpp, pixel_resize,  0);
```
**argv[i]**
書き出す画像の名前。
フルパス指定もいける。相対パスは知らん。
読み込んだ画像の名前を使うと確認無しで上書きするので注意。
デバッグ用の生贄は必ずコピー取っとこうね!

<追記>
なぜかビデオフォルダ等の特殊ディレクトリに書き出せなくなりました。
そのようなパスが指定されたときはパスの場所を他のディレクトリに置き換えましょう。
以下その場しのぎのクソコード(ファイル名をクソ長くすると配列がオーバーフローする)
```C
//指定されたパスが特殊ディレクトリ(ビデオフォルダ)を指す場合はパスを変更
if (strncmp(data->argv, "C:\\Users\\Videos\\Captures", strlen("C:\\Users\\Videos\\Captures")) == 0) {
		char moziretsu[300]= "C:\\Users\\hogehoge";
		strcat(moziretsu, &data->argv[strlen("C:\\Users\\Videos\\Captures")]);
		output_result = stbi_write_png(moziretsu, re_width, re_height, 4, pixel_re, 0);
	}
	else {
		output_result = stbi_write_png(data->argv, re_width, re_height, 4, pixel_re, 0);
	}
```
**re_width**
書き出したい画像の幅　

**re_height** 
書き出したい画像の高さ

**bpp**
書き出したい画像が1ピクセルあたりunsigned charの変数いくつで表現されているかを入れる。
RGBで表現される場合は3、RGBAの場合は4。
詳しくはヘッダファイル(ry

**pixel_resize**
書き出したい画像データ

**0** 
なんか指定するっぽいが0を入れるとライブラリ側で勝手にいい感じの(ry

**蛇足1**
使う関数を変えればpng以外にもいろいろ書き出せるっぽい

**蛇足2**
stbi_write_png()がけっこう重いので複数の画像を処理するならマルチスレッドなりで並列化したほうが良さげ
~~やらないのかって？めんどい。まあ適当に関数ポインタ作ってwinAPIにぶちこみゃいけるっしょ。~~





# 参考資料

[ぬの部屋（仮)](https://suzulang.com/stb-library-image-read/)
[mieki256's diary](http://blawat2015.no-ip.com/~mieki256/diary/202207171.html)
[マクロ定義で沼った人](https://teratail.com/questions/227963)