C言語で作る Windows DICOM Viewer

![2024-03-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/941038/a0fcb684-098b-3ede-8cb0-d2d95f2c178d.png)

### Visual Studio の設定箇所
・C++で空のプロジェクトを作る

・一点だけ注意があります。
デフォルトだと、コンソールアプリを作ろうとするので、
[プロジェクト]-[プロパティ]-[構成のプロパティ]-[リンカー]-[システム]の一番上の行の[サブシステム]で、リンカエラーが出ます。Windows (/SUBSYSTEM:WINDOWS)を選ぶと、GUIのWindowsアプリケーションをビルドする準備を整えてくれるので、そこだけ注意してください。

エキシプリシット・リトル・エンディアンのファイルなら大丈夫なはずです。
プラナーインプリメンテーションは、RGBの標準配列に対応しています。
RRRRRGGGGGBBBBBとかはダメです。

```C:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#pragma warning(disable:4996)

PSTR filename;

int get_file_size(const char* file_name)
{
	FILE* fp = fopen(file_name, "rb");
	if (fp == NULL) {
		return -1LL;
	}

	if (fseek(fp, 0L, SEEK_END) == 0) {
		fpos_t pos;

		if (fgetpos(fp, &pos) == 0) {
			fclose(fp);
			return (int)pos;
		}
	}

	fclose(fp);
	return -1LL;
}


void func(HDC hdc)
{
	int k = 0;

	k = get_file_size(filename);

	unsigned char* data = (unsigned char*)malloc(k);

	FILE* fp = fopen(filename, "rb");

	fread(data, 1, k, fp);

	int s = 0;

	int rows = 0;
	int columns = 0;
	int w_center = 0;
	int w_width = 0;

	for (int i = 0; i < k; i++)
	{
		if (data[i] == 0x28 && data[i + 1] == 0x00 && data[i + 2] == 0x10 && data[i + 3] == 0x00)
		{
			rows = data[i + 8];
			rows += data[i + 9] * 256;
			continue;
		}

		if (data[i] == 0x28 && data[i + 1] == 0x00 && data[i + 2] == 0x11 && data[i + 3] == 0x00)
		{
			columns = data[i + 8];
			columns += data[i + 9] * 256;
			continue;
		}

		if (data[i] == 0x28 && data[i + 1] == 0x00 && data[i + 2] == 0x50 && data[i + 3] == 0x10)
		{
			int array[4];
			array[0] = data[i + 8] - (3 * 16);
			array[1] = data[i + 8] - (3 * 16);
			array[2] = data[i + 8] - (3 * 16);
			array[3] = data[i + 8] - (3 * 16);
			w_center = (array[0] * 1000) + (array[1] * 100) + (array[2] * 10) + (array[3]);
			continue;
		}

		if (data[i] == 0x28 && data[i + 1] == 0x00 && data[i + 2] == 0x51 && data[i + 3] == 0x10)
		{
			int array[4];
			array[0] = data[i + 8] - (3 * 16);
			array[1] = data[i + 8] - (3 * 16);
			array[2] = data[i + 8] - (3 * 16);
			array[3] = data[i + 8] - (3 * 16);
			w_width = (array[0] * 1000) + (array[1] * 100) + (array[2] * 10) + (array[3]);
			continue;
		}

		if (data[i] == 0xE0 && data[i + 1] == 0x7F && data[i + 2] == 0x10 && data[i + 3] == 0x00)
		{
			s = i;
			s += 12;
			break;
		}
	}

	fclose(fp);
 
	int height = rows;
	int width = columns;
	int window_width = w_width;
	int window_center = w_center;

	unsigned char rgb[3];

	for (int i = 0; i < width * height; i++)
	{
		int value = data[2 * i + s] + data[2 * i + 1 + s] * 256;

		double val = value;

		if (value >= window_width)
		{
			val = 255;
		}
		else if (value < 0)
		{
			val = 0;
		}
		else
		{
			val = val / window_width;
			val = val * 256;
		}

		value = (int)val;

		rgb[0] = (unsigned char)value;
		rgb[1] = (unsigned char)value;
		rgb[2] = (unsigned char)value;

		long RGB = rgb[0] * 65536 + rgb[1] * 256 + rgb[2];

        //５で割ってるのは、Rows,Columnsが3000のファイルがあったので、画面に収まらなかったために圧縮しました。通常のサイズで表示したいときは、/ 5を削除してください。
		SetPixel(hdc, (i % width) / 5, (int)(i / width) / 5, RGB);
	}
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;

	int iCount;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		func(hdc);
		EndPaint(hwnd, &ps);
		return 0;
	}

	return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR lpCmdLine, int nCmdShow) {

	filename = lpCmdLine;

	HWND hwnd;
	MSG msg;
	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("konecom");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
		TEXT("konecom"), TEXT("konecom"),
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL
	);


	if (hwnd == NULL) return -1;

	while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
	return msg.wParam;
}

```

ビルドを通したら、
```:
xxxx.exe <読影したいdcmファイル>
```
で、実行してください。