SDcmWeb/webassembly/test_v3.cc
2025-10-12 00:17:30 +09:00

2552 lines
82 KiB
C++

#ifdef __EMSCRIPTEN__
//#include <emscripten.h>
#include <emscripten/emscripten.h>
#else
#include <string> // for std::string
#include <fstream> // for std::ifstream
#include <vector> // for std::vector
#endif
#include <cstring>
#include "nlohmann/json.hpp" // 라이브러리 경로에 맞게 수정
#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengles2.h>
#include <SDL2/SDL_opengles2_gl2ext.h>
#include <dcmtk/ofstd/ofstring.h>
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcuid.h"
#include "dcmtk/dcmdata/dcmetinf.h"
#include "dcmtk/dcmdata/dcdict.h"
#include "dcmtk/dcmdata/dcdicent.h"
#include "dcmtk/dcmdata/dcxfer.h"
#include "dcmtk/dcmjpeg/djdecode.h" /* for JPEG decoders */
#include "dcmtk/dcmjpeg/djencode.h" /* for JPEG encoders */
#include "dcmtk/dcmjpls/djdecode.h" /* for JPEG-LS decoders */
#include "dcmtk/dcmjpls/djencode.h" /* for JPEG-LS encoders */
#include "dcmtk/dcmdata/dcrledrg.h" /* for RLE decoder */
#include "dcmtk/dcmdata/dcrleerg.h" /* for RLE encoder */
#include "dcmtk/dcmjpeg/dipijpeg.h" /* for dcmimage JPEG plugin */
#include "dcmtk/dcmjpeg/djrplol.h"
#include "dcmtk/dcmimage/diregist.h"
#include "dcmtk/dcmdata/dcpixseq.h"
#include "dcmtk/dcmjpeg/djcparam.h"
#include "dcmtk/dcmjpeg/djeijg8.h"
#include "dcmtk/dcmjpeg/djcodece.h"
#include "dcmtk/dcmdata/dcchrstr.h"
#include "dcmtk/ofstd/ofchrenc.h"
#include "dcmtk/dcmdata/dcvrui.h"
#include "dcmtk/dcmdata/dcvrsh.h"
#include "dcmtk/dcmdata/dcistrmb.h"
#include "dcmtk/oflog/loglevel.h"
#include "events.h"
#include "unistd.h"
#include <vector>
//emscripten::val json;
int g_currentMode = (int)InteractionMode::MODE_PAN; // 기본 모드는 Pan
typedef void (*callback_UpdateDcmImage) (int nRet);
callback_UpdateDcmImage callback_UpdateDcmImageComplete = NULL;
typedef void (*callback_UpdateWasmInitGeometry) (const char* pStr);
callback_UpdateWasmInitGeometry callback_UpdateWasmInitGeometryComplete = NULL;
EventHandler* g_pEventHandler = NULL;
int g_nHandleWget = 0;
bool g_bCancelLoad = false;
bool g_bLoadImage = false;
bool g_bVisible = false;
DcmDataset* g_pDcmDataset = NULL;
DcmElement* g_pixelDataElement = NULL;
bool g_bChange = false;
GLuint g_nTextureID = 0;
GLuint g_nTextureGray8ID = 0;
GLuint g_nTextureGray16ID = 0;
GLuint g_nTextureGray8PaletteID = 0;
GLuint g_nPaletteTextureID = 0;
GLuint g_shaderProgramGray8 = 0;
GLuint g_shaderProgramGray8Palette = 0;
GLuint g_shaderProgramGray16 = 0;
GLuint g_shaderProgramRGB = 0;
// Vertex shader
GLint g_shaderPanGray16, g_shaderZoomGray16, g_shaderAspectGray16, g_shaderWindowCenterGray16, g_shaderWindowWidthGray16;
GLint g_shaderPanGray8, g_shaderZoomGray8, g_shaderAspectGray8, g_shaderWindowCenterGray8, g_shaderWindowWidthGray8;
GLint g_shaderPanGray8Palette, g_shaderZoomGray8Palette, g_shaderAspectGray8Palette, g_shaderWindowCenterGray8Palette, g_shaderWindowWidthGray8Palette;
GLint g_shaderPanRGB, g_shaderZoomRGB, g_shaderAspectRGB, g_shaderWindowCenterRGB, g_shaderWindowWidthRGB;
int g_nTotalBytes = 0;
int g_nReadBytes = 0;
int g_nWindowWidth = 0;
int g_nWindowCenter = 0;
int g_nPrevWindowWidth = 0;
int g_nPrevWindowCenter = 0;
int g_nDefaultWindowWidth = 0;
int g_nDefaultWindowCenter = 0;
int g_nColorType = 0;
Uint32 g_nCurrentFrame = 0;
int g_nTotalFrames = 0;
int g_nFrameTextureWidth = 0;
int g_nFrameTextureHeight = 0;
int g_nSamplesPerPixel = 0;
int g_nBitsAllocated = 0;
int g_nFrameSizeUncompressed = 0;
int g_nWindowSizeWidth = 0;
int g_nWindowSizeHeight = 0;
GLuint g_nUsePaletteIndex = 0; // 팔레트 모드 사용 여부
GLuint g_nPaletteTexLocationIndex = 0; // 팔레트 텍스처 유니폼 변수
float g_fFrameTime = 0;
float g_fFrameDelay = 0;
int g_nPaletteEntries = 0;
std::string g_photometricInterpretation;
//char* g_pBuf = NULL;
GLuint g_vbo = 0;
bool g_bUseFirst = false;
Uint32 g_lastUpdateTime;
std::string strRet;
int g_nViewerID = -1;
//std::vector<std::vector<char>> g_cachedFrames;
uint8_t* g_cachedFrames = NULL;
//std::vector<unsigned char> g_paletteBuffer;
uint8_t* g_paletteBuffer = NULL;
const GLchar* vertexSourceGray8 =
"uniform vec2 pan; \n"
"uniform float zoom; \n"
"uniform float aspect; \n"
"attribute vec4 position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(position.xyz, 1.0); \n"
" gl_Position.xy += pan; \n"
" gl_Position.xy *= zoom; \n"
" texCoord = a_texCoord; \n"
" gl_Position.y *= aspect; \n"
"} \n";
// Fragment/pixel shader
const GLchar* fragmentSourceGray8 =
"precision mediump float; \n"
"varying vec2 texCoord; \n"
"uniform sampler2D texSampler; \n"
"uniform float fWindowCenter; \n"
"uniform float fWindowWidth; \n"
"\n"
"void main() \n"
"{ \n"
" // 1. 텍스처에서 8비트 회색조 값을 읽어옵니다. \n"
" // GL_LUMINANCE 포맷 텍스처의 경우, r, g, b 채널에 모두 동일한 값이 들어있습니다. \n"
" // texture2D 함수는 이 값을 0.0 ~ 1.0 범위의 실수로 반환합니다. \n"
" float normalizedPixel = texture2D(texSampler, texCoord).r;\n"
"\n"
" // 2. 0.0~1.0 범위의 값을 원래의 8비트 값(0~255) 범위로 되돌립니다. \n"
" float originalValue = normalizedPixel * 255.0; \n"
"\n"
" // 3. Window Level/Width를 적용하여 최종 밝기(0.0 ~ 1.0)를 계산합니다. \n"
" float windowMin = fWindowCenter - fWindowWidth / 2.0; \n"
" float finalValue = (originalValue - windowMin) / fWindowWidth; \n"
"\n"
" // 4. 계산된 값을 0.0 ~ 1.0 범위로 제한(clamp)하여 색상 값으로 사용합니다. \n"
" finalValue = clamp(finalValue, 0.0, 1.0); \n"
"\n"
" // 5. 최종 색상(회색조)을 모든 채널(R, G, B)에 동일하게 적용하여 출력합니다. \n"
" gl_FragColor = vec4(finalValue, finalValue, finalValue, 1.0); \n"
"} \n";
// Palette Color용 프래그먼트 셰이더
const GLchar* fragmentSourceGray8Palette =
"precision mediump float; \n"
"varying vec2 texCoord; \n"
"uniform sampler2D texSampler; \n"
"uniform sampler2D u_paletteTexture; \n"
"uniform float fWindowCenter; \n"
"uniform float fWindowWidth; \n"
"uniform bool u_usePalette; \n"
"\n"
"void main() \n"
"{ \n"
" if (u_usePalette) \n"
" { \n"
" // === 팔레트 컬러 디스플레이 모드 === \n"
"\n"
" // 1. texSampler에서 현재 픽셀의 '인덱스' 값을 읽어옵니다. \n"
" float index = texture2D(texSampler, texCoord).r; \n"
"\n"
" // 2. 이 인덱스를 좌표로 사용하여 u_paletteTexture에서 최종 색상을 '조회'합니다. \n"
" vec4 finalColor = texture2D(u_paletteTexture, vec2(index, 0.5)); \n"
"\n"
" // 3. 조회된 팔레트 색상을 그대로 화면에 출력합니다. \n"
" gl_FragColor = finalColor; \n"
" } \n"
" else \n"
" { \n"
" // === 그레이스케일 + Window/Level 디스플레이 모드 === \n"
"\n"
" // 1. texSampler에서 현재 픽셀의 '회색조 값'을 읽어옵니다. \n"
" float normalizedPixel = texture2D(texSampler, texCoord).r;\n"
"\n"
" // 2. 0.0~1.0 범위의 값을 원래의 0~255 범위로 되돌립니다. \n"
" float originalValue = normalizedPixel * 255.0; \n"
"\n"
" // 3. Window/Level을 적용하여 최종 밝기를 계산합니다. \n"
" float windowMin = fWindowCenter - fWindowWidth / 2.0; \n"
" float finalValue = (originalValue - windowMin) / fWindowWidth; \n"
"\n"
" // 4. 계산된 값을 0.0 ~ 1.0 범위로 제한합니다. \n"
" finalValue = clamp(finalValue, 0.0, 1.0); \n"
"\n"
" // 5. 최종 계산된 회색조 색상을 출력합니다. \n"
" gl_FragColor = vec4(finalValue, finalValue, finalValue, 1.0); \n"
" } \n"
"} \n";
// Palette Color용 프래그먼트 셰이더
const GLchar* fragmentSourceGray8Palette_v1 =
"precision mediump float; \n"
"varying vec2 texCoord; \n"
"uniform sampler2D texSampler; \n"
"uniform sampler2D u_paletteTexture; \n"
"uniform float fWindowCenter; \n"
"uniform float fWindowWidth; \n"
"uniform bool u_usePalette; \n"
"\n"
"void main() \n"
"{ \n"
" // 1. 텍스처에서 8비트 회색조 값을 읽어옵니다. \n"
" // GL_LUMINANCE 포맷 텍스처의 경우, r, g, b 채널에 모두 동일한 값이 들어있습니다. \n"
" // texture2D 함수는 이 값을 0.0 ~ 1.0 범위의 실수로 반환합니다. \n"
" float normalizedPixel = texture2D(texSampler, texCoord).r;\n"
"\n"
" // 2. 0.0~1.0 범위의 값을 원래의 8비트 값(0~255) 범위로 되돌립니다. \n"
" float originalValue = normalizedPixel * 255.0; \n"
"\n"
" // 3. Window Level/Width를 적용하여 최종 밝기(0.0 ~ 1.0)를 계산합니다. \n"
" float windowMin = fWindowCenter - fWindowWidth / 2.0; \n"
" float finalValue = (originalValue - windowMin) / fWindowWidth; \n"
"\n"
" // 4. 계산된 값을 0.0 ~ 1.0 범위로 제한(clamp)하여 색상 값으로 사용합니다. \n"
" finalValue = clamp(finalValue, 0.0, 1.0); \n"
"\n"
" // 5. 최종 색상(회색조)을 모든 채널(R, G, B)에 동일하게 적용하여 출력합니다. \n"
" gl_FragColor = vec4(finalValue, finalValue, finalValue, 1.0); \n"
" if (u_usePalette) \n"
" { \n"
" // === 팔레트 컬러 디스플레이 모드 === \n"
"\n"
" // 1. texSampler에서 현재 픽셀의 '인덱스' 값을 읽어옵니다. \n"
" float index = texture2D(texSampler, texCoord).r; \n"
"\n"
" // 2. 이 인덱스를 좌표로 사용하여 u_paletteTexture에서 최종 색상을 '조회'합니다. \n"
" vec4 finalColor = texture2D(u_paletteTexture, vec2(index, 0.5)); \n"
"\n"
" // 3. 조회된 팔레트 색상을 그대로 화면에 출력합니다. \n"
" gl_FragColor = finalColor; \n"
" } \n"
"} \n";
const GLchar* vertexSourceGray16 =
"uniform vec2 pan; \n"
"uniform float zoom; \n"
"uniform float aspect; \n"
"attribute vec4 position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(position.xyz, 1.0); \n"
" gl_Position.xy += pan; \n"
" gl_Position.xy *= zoom; \n"
" texCoord = a_texCoord; \n"
" gl_Position.y *= aspect; \n"
"} \n";
// 16비트 LUMINANCE_ALPHA 텍스처를 올바르게 처리하도록 수정된 프래그먼트 셰이더
const GLchar* fragmentSourceGray16_test =
"precision mediump float; \n"
"varying vec2 texCoord; \n"
"uniform sampler2D texSampler; \n"
"uniform float fWindowCenter; \n"
"uniform float fWindowWidth; \n"
"\n"
"void main() \n"
"{ \n"
" // 1. 텍스처에서 두 개의 8비트 채널(Luminance, Alpha) 값을 읽어옵니다. \n"
" vec4 colorOut = texture2D(texSampler, texCoord); \n"
" gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); \n"
"} \n";
// 16비트 LUMINANCE_ALPHA 텍스처를 올바르게 처리하도록 수정된 프래그먼트 셰이더
const GLchar* fragmentSourceGray16 =
"precision mediump float; \n"
"varying vec2 texCoord; \n"
"uniform sampler2D texSampler; \n"
"uniform float fWindowCenter; \n"
"uniform float fWindowWidth; \n"
"\n"
"void main() \n"
"{ \n"
" // 1. 텍스처에서 두 개의 8비트 채널(Luminance, Alpha) 값을 읽어옵니다. \n"
" vec4 colorOut = texture2D(texSampler, texCoord); \n"
"\n"
" // 2. 두 채널을 조합하여 원래의 16비트 픽셀 값을 복원합니다. \n"
" // colorOut.r (Luminance)가 하위 8비트, colorOut.a (Alpha)가 상위 8비트입니다. \n"
" float highByte = floor(colorOut.a * 255.0); \n"
" float lowByte = floor(colorOut.r * 255.0); \n"
" float originalValue = highByte * 256.0 + lowByte; \n"
"\n"
" // 3. Window/Level을 적용하여 최종 밝기(0.0 ~ 1.0)를 계산합니다. \n"
" float windowMin = fWindowCenter - fWindowWidth / 2.0; \n"
" float normalizedValue = (originalValue - windowMin) / fWindowWidth; \n"
"\n"
" // 4. 계산된 값을 0.0 ~ 1.0 범위로 제한(clamp)합니다. \n"
" normalizedValue = clamp(normalizedValue, 0.0, 1.0); \n"
"\n"
" // 5. 최종 색상(회색조)을 출력합니다. \n"
" gl_FragColor = vec4(normalizedValue, normalizedValue, normalizedValue, 1.0); \n"
"} \n";
const GLchar* vertexSourceRGB =
"uniform vec2 panRGB; \n"
"uniform float zoomRGB; \n"
"uniform float aspectRGB; \n"
"attribute vec4 position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(position.xyz, 1.0); \n"
" gl_Position.xy += panRGB; \n"
" gl_Position.xy *= zoomRGB; \n"
" texCoord = a_texCoord; \n"
" gl_Position.y *= aspectRGB; \n"
"} \n";
// Fragment/pixel shader
const GLchar* fragmentSourceRGB =
"precision mediump float; \n"
"varying vec2 texCoord; \n"
"uniform sampler2D texSampler; \n"
"uniform float fWindowCenterRGB; \n"
"uniform float fWindowWidthRGB; \n"
"void main() \n"
"{ \n"
" vec4 colorOut = texture2D(texSampler, texCoord); \n"
" float fMin = fWindowCenterRGB - fWindowWidthRGB/2.0;\n"
" float fMax = fWindowCenterRGB + fWindowWidthRGB/2.0;\n"
" float fTestValue = (fMax - fMin);\n"
" float fR = (colorOut.r*256.0 - fMin) / (fMax-fMin);\n"
" float fG = (colorOut.g*256.0 - fMin) / (fMax-fMin);\n"
" float fB = (colorOut.b*256.0 - fMin) / (fMax-fMin);\n"
" gl_FragColor = vec4(fR, fG, fB, 1.0); \n"
//" gl_FragColor = vec4(colorOut.r/fTestValue, 1.0, 1.0, 1.0); \n"
"} \n";
/*
void ClearValue()
{
g_nTotalFrames = 0;
g_nFrameTextureWidth = 0;
g_nFrameTextureHeight = 0;
g_nSamplesPerPixel = 0;
g_nBitsAllocated = 0;
g_nCurrentFrame = 0;
g_nDefaultWindowWidth = 0;
g_nDefaultWindowCenter = 0;
g_nWindowWidth = 0;
g_nWindowCenter = 0;
g_nPrevWindowWidth = 0;
g_nPrevWindowCenter = 0;
g_nColorType = -1;
g_nFrameSizeUncompressed = 0;
g_nUsePaletteIndex = 0;
g_nPaletteTexLocationIndex = 0;
g_fFrameTime = 0;
g_fFrameDelay = 0;
g_nPaletteEntries = 0;
g_photometricInterpretation.clear();
g_paletteBuffer.clear();
if(g_pDcmDataset!=NULL)
{
delete g_pDcmDataset;
g_pDcmDataset = NULL;
}
if(g_pBuf!=NULL)
{
delete[] g_pBuf;
g_pBuf = NULL;
}
g_bChange = false;
}
void ClearTextures()
{
if(g_nTextureID>0)
{
glDeleteTextures(1, &g_nTextureID);
g_nTextureID = 0;
}
if(g_nTextureGray8ID>0)
{
glDeleteTextures(1, &g_nTextureGray8ID);
g_nTextureGray8ID = 0;
}
if(g_nTextureGray8PaletteID>0)
{
glDeleteTextures(1, &g_nTextureGray8PaletteID);
g_nTextureGray8PaletteID = 0;
}
if(g_nTextureGray16ID>0)
{
glDeleteTextures(1, &g_nTextureGray16ID);
g_nTextureGray16ID = 0;
}
if(g_nPaletteTextureID>0)
{
glDeleteTextures(1, &g_nPaletteTextureID);
g_nPaletteTextureID = 0;
}
}
*/
void PrintError()
{
int nError = 0;
nError = glGetError();
if(nError>0)
{
printf("GL Error Code: %d\n", nError);
int a=0;
}
}
// 셰이더 컴파일 성공 여부를 확인하고 에러 로그를 출력하는 함수
bool checkShaderCompilation(GLuint shader, const char* shaderName) {
GLint status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLint logLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
std::vector<char> errorLog(logLength);
glGetShaderInfoLog(shader, logLength, &logLength, &errorLog[0]);
fprintf(stderr, "셰이더 컴파일 에러 (%s): %s\n", shaderName, &errorLog[0]);
return false;
} else {
//printf("셰이더 컴파일 성공: %s\n", shaderName);
}
return true;
}
// 셰이더 프로그램 링크 성공 여부를 확인하고 에러 로그를 출력하는 함수
bool checkProgramLinking(GLuint program, const char* programName) {
GLint status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLint logLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
std::vector<char> errorLog(logLength);
glGetProgramInfoLog(program, logLength, &logLength, &errorLog[0]);
fprintf(stderr, "셰이더 프로그램 링크 에러 (%s): %s\n", programName, &errorLog[0]);
return false;
} else {
//printf("셰이더 프로그램 링크 성공: %s\n", programName);
}
return true;
}
void updateShader(EventHandler& eventHandler)
{
Camera& camera = eventHandler.camera();
//printf("updateShader: g_nColorType=%d\n", g_nColorType);
if(g_nColorType==0)
{
if(g_nBitsAllocated>8)
{
glUseProgram(g_shaderProgramGray16);
glUniform2fv(g_shaderPanGray16, 1, camera.pan());
glUniform1f(g_shaderZoomGray16, camera.zoom());
glUniform1f(g_shaderAspectGray16, camera.aspect());
}
else if(g_nBitsAllocated==8)
{
if(g_nPaletteEntries>0)
{
glUseProgram(g_shaderProgramGray8Palette);
// 팔레트 모드 사용 여부를 유니폼 변수로 전달
GLint usePaletteLocation = glGetUniformLocation(g_shaderProgramGray8Palette, "u_usePalette");
glUniform1i(usePaletteLocation, 1); // 팔레트 모드 활성화
glUniform2fv(g_shaderPanGray8Palette, 1, camera.pan());
glUniform1f(g_shaderZoomGray8Palette, camera.zoom());
glUniform1f(g_shaderAspectGray8Palette, camera.aspect());
}
else
{
glUseProgram(g_shaderProgramGray8);
// 팔레트 모드 비활성화
glUniform2fv(g_shaderPanGray8, 1, camera.pan());
glUniform1f(g_shaderZoomGray8, camera.zoom());
glUniform1f(g_shaderAspectGray8, camera.aspect());
}
}
}
else if(g_nColorType==1)
{
glUseProgram(g_shaderProgramRGB);
glUniform2fv(g_shaderPanRGB, 1, camera.pan());
glUniform1f(g_shaderZoomRGB, camera.zoom());
glUniform1f(g_shaderAspectRGB, camera.aspect());
}
}
void updateTextureGray8(int nWidth, int nHeight, void* pData)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_nTextureGray8ID); // 8비트 인덱스 데이터 텍스처
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
PrintError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nWidth, nHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pData);
PrintError();
//glBindTexture(GL_TEXTURE_2D, 0); // 텍스처 바인딩 해제
GLint nTextureID = glGetUniformLocation(g_shaderProgramGray8, "texSampler");
glUniform1i(nTextureID, 0);
}
//Uint8* g_pBufTest = NULL;
void updateTextureGray8Palette(int nWidth, int nHeight, void* pData)
{
//updateTextureGray8(nWidth, nHeight, pData);
//return;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_nTextureGray8PaletteID); // 8비트 인덱스 데이터 텍스처
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
PrintError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, nWidth, nHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pData);
PrintError();
//glBindTexture(GL_TEXTURE_2D, 0); // 텍스처 바인딩 해제
GLint nTextureID = glGetUniformLocation(g_shaderProgramGray8, "texSampler");
glUniform1i(nTextureID, 0);
GLint usePaletteLocation = glGetUniformLocation(g_shaderProgramGray8Palette, "u_usePalette");
//glUniform1i(usePaletteLocation, 0); // 팔레트 모드 활성화
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, g_nPaletteTextureID);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
PrintError();
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_nPaletteEntries, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, g_paletteBuffer.data());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_nPaletteEntries, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, g_paletteBuffer);
PrintError();
GLint nPaletteTextureID = glGetUniformLocation(g_shaderProgramGray8Palette, "u_paletteTexture");
glUniform1i(nPaletteTextureID, 1);
//usePaletteLocation = glGetUniformLocation(g_shaderProgramGray8Palette, "u_usePalette");
glUniform1i(usePaletteLocation, 1); // 팔레트 모드 활성화
}
void updateTextureGray16(int nWidth, int nHeight, void* pData)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_nTextureGray16ID); // 16비트 인덱스 데이터 텍스처
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
PrintError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, nWidth, nHeight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pData);
PrintError();
GLint nTextureID = glGetUniformLocation(g_shaderProgramGray16, "texSampler");
glUniform1i(nTextureID, 0);
}
void updateTextureRGB(int nWidth, int nHeight, void* pData)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_nTextureID); // 8비트 인덱스 데이터 텍스처
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
PrintError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nWidth, nHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
PrintError();
//glBindTexture(GL_TEXTURE_2D, 0); // 텍스처 바인딩 해제
GLint nTextureID = glGetUniformLocation(g_shaderProgramRGB, "texSampler");
glUniform1i(nTextureID, 0);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, nWidth, nHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
}
void updateWindowWidthLevelGray8(int nWidth, int nLevel)
{
glUniform1f(g_shaderWindowCenterGray8, (float)nLevel);
glUniform1f(g_shaderWindowWidthGray8, (float)nWidth);
}
void updateWindowWidthLevelGray8Palette(int nWidth, int nLevel)
{
glUniform1f(g_shaderWindowCenterGray8Palette, (float)nLevel);
glUniform1f(g_shaderWindowWidthGray8Palette, (float)nWidth);
}
void updateWindowWidthLevelGray16(int nWidth, int nLevel)
{
glUniform1f(g_shaderWindowCenterGray16, (float)nLevel);
glUniform1f(g_shaderWindowWidthGray16, (float)nWidth);
}
void updateWindowWidthLevelRGB(int nWidth, int nLevel)
{
glUniform1f(g_shaderWindowCenterRGB, (float)nLevel);
glUniform1f(g_shaderWindowWidthRGB, (float)nWidth);
}
void initShaderGray8()
{
// Create and compile vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSourceGray8, NULL);
glCompileShader(vertexShader);
checkShaderCompilation(vertexShader, "Gray8 Vertex"); // 에러 확인 추가
// Create and compile fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSourceGray8, NULL);
glCompileShader(fragmentShader);
checkShaderCompilation(fragmentShader, "Gray8 Fragment"); // 에러 확인 추가
// Link vertex and fragment shader into shader program and use it
g_shaderProgramGray8 = glCreateProgram();
glAttachShader(g_shaderProgramGray8, vertexShader);
glAttachShader(g_shaderProgramGray8, fragmentShader);
glLinkProgram(g_shaderProgramGray8);
checkProgramLinking(g_shaderProgramGray8, "Gray8 Program"); // 에러 확인 추가
// Get shader variables and initialize them
g_shaderPanGray8 = glGetUniformLocation(g_shaderProgramGray8, "pan");
g_shaderZoomGray8 = glGetUniformLocation(g_shaderProgramGray8, "zoom");
g_shaderAspectGray8 = glGetUniformLocation(g_shaderProgramGray8, "aspect");
g_shaderWindowCenterGray8 = glGetUniformLocation(g_shaderProgramGray8, "fWindowCenter");
g_shaderWindowWidthGray8 = glGetUniformLocation(g_shaderProgramGray8, "fWindowWidth");
glUniform1f(g_shaderWindowCenterGray8, 128.0f);
glUniform1f(g_shaderWindowWidthGray8, 255.0f);
////printf("pan:%d, zoom:%d, aspect:%d, g_shaderWindowCenterGray8:%d, g_shaderWindowWidthGray8:%d\n", g_shaderPanGray8, g_shaderZoomGray8, g_shaderAspectGray8, g_shaderWindowCenterGray8, g_shaderWindowWidthGray8);
}
void initShaderGray8Palette()
{
// Create and compile vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSourceGray8, NULL);
glCompileShader(vertexShader);
checkShaderCompilation(vertexShader, "Gray8Palette Vertex"); // 에러 확인 추가
// Create and compile fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSourceGray8Palette, NULL);
glCompileShader(fragmentShader);
checkShaderCompilation(fragmentShader, "Gray8Palette Fragment"); // 에러 확인 추가
// Link vertex and fragment shader into shader program and use it
g_shaderProgramGray8Palette = glCreateProgram();
glAttachShader(g_shaderProgramGray8Palette, vertexShader);
glAttachShader(g_shaderProgramGray8Palette, fragmentShader);
glLinkProgram(g_shaderProgramGray8Palette);
checkProgramLinking(g_shaderProgramGray8Palette, "Gray8Palette Program"); // 에러 확인 추가
// Get shader variables and initialize them
g_shaderPanGray8Palette = glGetUniformLocation(g_shaderProgramGray8Palette, "pan");
g_shaderZoomGray8Palette = glGetUniformLocation(g_shaderProgramGray8Palette, "zoom");
g_shaderAspectGray8Palette = glGetUniformLocation(g_shaderProgramGray8Palette, "aspect");
g_shaderWindowCenterGray8Palette = glGetUniformLocation(g_shaderProgramGray8Palette, "fWindowCenter");
g_shaderWindowWidthGray8Palette = glGetUniformLocation(g_shaderProgramGray8Palette, "fWindowWidth");
// 팔레트 텍스처 유니폼 변수
g_nPaletteTexLocationIndex = glGetUniformLocation(g_shaderProgramGray8Palette, "u_paletteTexture");
g_nUsePaletteIndex = glGetUniformLocation(g_shaderProgramGray8Palette, "u_usePalette");
glUniform1f(g_shaderWindowCenterGray8Palette, 128.0f);
glUniform1f(g_shaderWindowWidthGray8Palette, 255.0f);
////printf("pan:%d, zoom:%d, aspect:%d, g_shaderWindowCenterGray8:%d, g_shaderWindowWidthGray8:%d\n", g_shaderPanGray8Palette, g_shaderZoomGray8Palette, g_shaderAspectGray8Palette, g_shaderWindowCenterGray8Palette, g_shaderWindowWidthGray8Palette);
}
void initShaderGray16()
{
// Create and compile vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSourceGray16, NULL);
glCompileShader(vertexShader);
checkShaderCompilation(vertexShader, "Gray16 Vertex"); // 에러 확인 추가
// Create and compile fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSourceGray16, NULL);
glCompileShader(fragmentShader);
checkShaderCompilation(fragmentShader, "Gray16 Fragment"); // 에러 확인 추가
// Link vertex and fragment shader into shader program and use it
g_shaderProgramGray16 = glCreateProgram();
glAttachShader(g_shaderProgramGray16, vertexShader);
glAttachShader(g_shaderProgramGray16, fragmentShader);
glLinkProgram(g_shaderProgramGray16);
checkProgramLinking(g_shaderProgramGray16, "Gray16 Program"); // 에러 확인 추가
// Get shader variables and initialize them
g_shaderPanGray16 = glGetUniformLocation(g_shaderProgramGray16, "pan");
g_shaderZoomGray16 = glGetUniformLocation(g_shaderProgramGray16, "zoom");
g_shaderAspectGray16 = glGetUniformLocation(g_shaderProgramGray16, "aspect");
g_shaderWindowCenterGray16 = glGetUniformLocation(g_shaderProgramGray16, "fWindowCenter");
g_shaderWindowWidthGray16 = glGetUniformLocation(g_shaderProgramGray16, "fWindowWidth");
glUniform1f(g_shaderWindowCenterGray16, 128.0f);
glUniform1f(g_shaderWindowWidthGray16, 255.0f);
////printf("pan:%d, zoom:%d, aspect:%d, g_shaderWindowCenterGray16:%d, g_shaderWindowWidthGray16:%d\n", g_shaderPanGray16, g_shaderZoomGray16, g_shaderAspectGray16, g_shaderWindowCenterGray16, g_shaderWindowWidthGray16);
}
void initShaderRGB()
{
// Create and compile vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSourceRGB, NULL);
glCompileShader(vertexShader);
checkShaderCompilation(vertexShader, "RGB Vertex"); // 에러 확인 추가
// Create and compile fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSourceRGB, NULL);
glCompileShader(fragmentShader);
checkShaderCompilation(fragmentShader, "RGB Fragment"); // 에러 확인 추가
// Link vertex and fragment shader into shader program and use it
g_shaderProgramRGB = glCreateProgram();
////printf("g_shaderProgramRGB: %d\n", g_shaderProgramRGB);
glAttachShader(g_shaderProgramRGB, vertexShader);
glAttachShader(g_shaderProgramRGB, fragmentShader);
glLinkProgram(g_shaderProgramRGB);
checkProgramLinking(g_shaderProgramRGB, "RGB Program"); // 에러 확인 추가
//glUseProgram(g_shaderProgramRGB);
// Get shader variables and initialize them
g_shaderPanRGB = glGetUniformLocation(g_shaderProgramRGB, "panRGB");
g_shaderZoomRGB = glGetUniformLocation(g_shaderProgramRGB, "zoomRGB");
g_shaderAspectRGB = glGetUniformLocation(g_shaderProgramRGB, "aspectRGB");
g_shaderWindowCenterRGB = glGetUniformLocation(g_shaderProgramRGB, "fWindowCenterRGB");
g_shaderWindowWidthRGB = glGetUniformLocation(g_shaderProgramRGB, "fWindowWidthRGB");
glUniform1f(g_shaderWindowCenterRGB, 128.0f);
glUniform1f(g_shaderWindowWidthRGB, 255.0f);
////printf("pan:%d, zoom:%d, aspect:%d, g_shaderWindowCenterRGB:%d, g_shaderWindowWidthRGB:%d\n", g_shaderPanRGB, g_shaderZoomRGB, g_shaderAspectRGB, g_shaderWindowCenterRGB, g_shaderWindowWidthRGB);
}
GLuint initShader(EventHandler& eventHandler)
{
initShaderGray8Palette();
initShaderGray8();
initShaderGray16();
initShaderRGB();
//updateShader(eventHandler);
g_pEventHandler = &eventHandler;
return g_shaderProgramGray16;
}
void initGeometry(GLuint shaderProgram)
{
// Create vertex buffer object and copy vertex data into it
printf("initGeometry g_vbo: %d, %d\n", g_vbo, shaderProgram);
g_nWindowSizeWidth = (int)g_pEventHandler->camera().windowSize().width;
g_nWindowSizeHeight = (int)g_pEventHandler->camera().windowSize().height;
float fWindowWidth = (float)g_pEventHandler->camera().windowSize().width;
float fWindowHeight = (float)g_pEventHandler->camera().windowSize().height;
float fWindowRatio = fWindowWidth / fWindowHeight;
float fImageRatio = (float)g_nFrameTextureWidth / (float)g_nFrameTextureHeight;
float fRatioX = 1.0f;
float fRatioY = 1.0f;
float fRefWidthRatio = (float)g_nFrameTextureWidth / fWindowWidth;
float fRefHeightRatio = (float)g_nFrameTextureHeight / fWindowHeight;
printf("initGeometry Texture(%d, %d), Window(%f, %f)\n", g_nFrameTextureWidth, g_nFrameTextureHeight,
fWindowWidth, fWindowHeight);
if(fWindowRatio > fImageRatio)
{
fRatioY = 1.0f;
fRatioX = fImageRatio / fWindowRatio;
}
else
{
fRatioX = 1.0f;
fRatioY = fWindowRatio / fImageRatio;
}
if(g_vbo==0)
{
glGenBuffers(1, &g_vbo);
printf("glGenBuffer: %d\n", g_vbo);
}
glBindBuffer(GL_ARRAY_BUFFER, g_vbo);
GLfloat vertices[] =
{
-1.0f*fRatioX, -1.0f*fRatioY, 0.0f, 0.0f, 1.0f,
1.0f*fRatioX, -1.0f*fRatioY, 0.0f, 1.0f, 1.0f,
-1.0f*fRatioX, 1.0f*fRatioY, 0.0f, 0.0f, 0.0f,
1.0f*fRatioX, 1.0f*fRatioY, 0.0f, 1.0f, 0.0f,
-1.0f*fRatioX, 1.0f*fRatioY, 0.0f, 0.0f, 0.0f,
1.0f*fRatioX, -1.0f*fRatioY, 0.0f, 1.0f, 1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Specify the layout of the shader vertex data (positions only, 3 floats)
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
// glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid*)0);
// glEnableVertexAttribArray(0);
GLint texAttrib = glGetAttribLocation(shaderProgram, "a_texCoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
//glEnableVertexAttribArray(2);
//glBindBuffer(GL_ARRAY_BUFFER, 0);
////printf("initGeometry(%d, %d)\n", posAttrib, texAttrib);
if(callback_UpdateWasmInitGeometryComplete!=NULL)
{
strRet.clear();
strRet = "complete initGeometry";
callback_UpdateWasmInitGeometryComplete((const char*)strRet.c_str());
}
}
/*
void initGeometry(GLuint shaderProgram)
{
// Create vertex buffer object and copy vertex data into it
printf("initGeometry g_vbo: %d, %d\n", g_vbo, shaderProgram);
g_nWindowSizeWidth = (int)g_pEventHandler->camera().windowSize().width;
g_nWindowSizeHeight = (int)g_pEventHandler->camera().windowSize().height;
float fWindowWidth = (float)g_pEventHandler->camera().windowSize().width;
float fWindowHeight = (float)g_pEventHandler->camera().windowSize().height;
float fWindowRatio = fWindowWidth / fWindowHeight;
float fFrameTextureRatio = (float)g_nFrameTextureWidth / (float)g_nFrameTextureHeight;
float fRatioX = 1.0f;
float fRatioY = 1.0f;
float fRefWidthRatio = (float)g_nFrameTextureWidth / fWindowWidth;
float fRefHeightRatio = (float)g_nFrameTextureHeight / fWindowHeight;
printf("initGeometry Texture(%d, %d), Window(%f, %f)\n", g_nFrameTextureWidth, g_nFrameTextureHeight,
fWindowWidth, fWindowHeight);
if(fRefWidthRatio>fRefHeightRatio)
{
fRatioX = 1.0f;
fRatioY = (fWindowWidth*((float)g_nFrameTextureHeight/(float)g_nFrameTextureWidth)) / fWindowHeight;
}
else
{
fRatioY = 1.0f;
fRatioX = (fWindowHeight*((float)g_nFrameTextureWidth/(float)g_nFrameTextureHeight)) / fWindowWidth;
}
if(g_vbo==0)
{
glGenBuffers(1, &g_vbo);
printf("glGenBuffer: %d\n", g_vbo);
}
glBindBuffer(GL_ARRAY_BUFFER, g_vbo);
GLfloat vertices[] =
{
-1.0f*fRatioX, -1.0f*fRatioY, 0.0f, 0.0f, 1.0f,
1.0f*fRatioX, -1.0f*fRatioY, 0.0f, 1.0f, 1.0f,
-1.0f*fRatioX, 1.0f*fRatioY, 0.0f, 0.0f, 0.0f,
1.0f*fRatioX, 1.0f*fRatioY, 0.0f, 1.0f, 0.0f,
-1.0f*fRatioX, 1.0f*fRatioY, 0.0f, 0.0f, 0.0f,
1.0f*fRatioX, -1.0f*fRatioY, 0.0f, 1.0f, 1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Specify the layout of the shader vertex data (positions only, 3 floats)
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
// glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid*)0);
// glEnableVertexAttribArray(0);
GLint texAttrib = glGetAttribLocation(shaderProgram, "a_texCoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
//glEnableVertexAttribArray(2);
//glBindBuffer(GL_ARRAY_BUFFER, 0);
////printf("initGeometry(%d, %d)\n", posAttrib, texAttrib);
}
*/
void onErrorData(void* arg)
{
//printf("onErrorData %d\n", (int)arg);
}
int onLoadedDataInternal(void* arg, void* buffer, int nSize)
{
printf("onLoadedData %p, %d\n", buffer, nSize);
//ClearValue();
//g_paletteBuffer.clear();
if(g_paletteBuffer!=NULL)
{
delete[] g_paletteBuffer;
g_paletteBuffer = NULL;
}
g_nPaletteEntries = 0;
OFCondition error;
E_TransferSyntax opt_ixfer = EXS_Unknown;
E_FileReadMode opt_readMode = ERM_autoDetect;
Uint8* pData = (Uint8*)buffer;
g_nCurrentFrame = 0;
int nFindIndex = 0;
bool bFind = false;
int i=0;
Uint8* pDataTmp = (Uint8*)buffer;
for(i=0 ; i<nSize-4 && bFind==false ; i++)
{
if(pDataTmp[i]=='D' && pDataTmp[i+1]=='I' && pDataTmp[i+2]=='C' && pDataTmp[i+3]=='M')
{
nFindIndex = i;
bFind = true;
break;
}
}
if(bFind==false)
{
if(callback_UpdateDcmImageComplete!=NULL)
{
printf("c++ callback_UpdateDcmImageComplete\n");
callback_UpdateDcmImageComplete(-1);
}
return -1;
}
//memset(pData, 0x00, nFindIndex-1);
g_fFrameTime = 0;
g_fFrameDelay = 0;
// //printf("nFindIndex = 0x%x", nFindIndex);
if(g_pDcmDataset!=NULL)
{
delete g_pDcmDataset;
}
DcmInputBufferStream dcmBuffer;
dcmBuffer.setBuffer(pData, nSize);
dcmBuffer.setEos();
DcmFileFormat dcmff;
error = dcmff.read(dcmBuffer);
if(error.bad())
{
printf("Error reading DICOM file: %s\n", error.text());
if(callback_UpdateDcmImageComplete!=NULL)
{
printf("c++ callback_UpdateDcmImageComplete\n");
callback_UpdateDcmImageComplete(-2);
}
return -2;
}
error = dcmff.loadAllDataIntoMemory();
g_pDcmDataset = dcmff.getAndRemoveDataset();
OFString strTransferSyntaxUID;
error = dcmff.getMetaInfo()->findAndGetOFString(DCM_TransferSyntaxUID, strTransferSyntaxUID);
DcmXfer dcmXfer(strTransferSyntaxUID.c_str());
g_pDcmDataset->transferInit();
//error = g_pDcmDataset->read(dcmBuffer, EXS_JPEGProcess14SV1);
error = g_pDcmDataset->read(dcmBuffer, dcmXfer.getXfer());
if(error.bad())
{
printf("Dataset State: %u(%s)\n", g_pDcmDataset->transferState(), error.text());
if(callback_UpdateDcmImageComplete!=NULL)
{
printf("c++ callback_UpdateDcmImageComplete\n");
callback_UpdateDcmImageComplete(-3);
}
return -3;
}
g_pDcmDataset->transferEnd();
dcmBuffer.releaseBuffer();
g_nTotalFrames = 1;
g_nFrameTextureWidth = 0;
g_nFrameTextureHeight = 0;
g_nSamplesPerPixel = 1;
g_nBitsAllocated = 8;
// Photometric Interpretation 태그를 읽어 이미지 종류를 확인합니다.
OFString photometricInterpretation;
g_pDcmDataset->findAndGetOFString(DCM_PhotometricInterpretation, photometricInterpretation);
g_photometricInterpretation = photometricInterpretation.c_str();
Uint16 val = 0;
g_pDcmDataset->findAndGetUint16(DCM_Rows, val);
g_nFrameTextureHeight = val;
g_pDcmDataset->findAndGetUint16(DCM_Columns, val);
g_nFrameTextureWidth = val;
g_pDcmDataset->findAndGetUint16(DCM_SamplesPerPixel, val);
g_nSamplesPerPixel = val;
g_pDcmDataset->findAndGetUint16(DCM_BitsAllocated, val);
g_nBitsAllocated = val;
g_nFrameSizeUncompressed = g_nFrameTextureWidth * g_nFrameTextureHeight * g_nSamplesPerPixel * (g_nBitsAllocated / 8);
//g_pBuf = new char[g_nFrameSizeUncompressed];
int nMaxValue = pow(2, g_nBitsAllocated) - 1;
int nMinValue = 0;
int nDefaultWindowWidth = nMaxValue - nMinValue;
int nDefaultWindowCenter = (nMaxValue + nMinValue)/2;
Sint32 nFrames = 0;
OFString strTmp;
OFString strFrames;
// g_pDcmDataset->findAndGetSint32(DCM_NumberOfFrames, nFrames);
g_pDcmDataset->findAndGetOFString(DCM_NumberOfFrames, strFrames);
//usleep(1000*1000);
nFrames = atoi(strFrames.c_str());
if(nFrames<=0)
{
nFrames = 1;
}
else
{
strTmp = "";
g_pDcmDataset->findAndGetOFString(DCM_FrameTime, strTmp);
printf("DCM_Frametime: %s\n", strTmp.c_str());
g_fFrameTime = atof(strTmp.c_str());
strTmp = "";
g_pDcmDataset->findAndGetOFString(DCM_FrameDelay, strTmp);
printf("DCM_FrameDelay: %s\n", strTmp.c_str());
g_fFrameDelay = atof(strTmp.c_str());
}
g_nTotalFrames = nFrames;
strTmp = "";
g_pDcmDataset->findAndGetOFString(DCM_WindowCenter, strTmp);
g_nDefaultWindowCenter = atoi(strTmp.c_str());
if(g_nDefaultWindowCenter==0)
{
g_nDefaultWindowCenter = nDefaultWindowCenter;
}
if(g_nWindowCenter<=0 && g_nDefaultWindowCenter)
{
g_nWindowCenter = g_nDefaultWindowCenter;
}
strTmp = "";
g_pDcmDataset->findAndGetOFString(DCM_WindowWidth, strTmp);
g_nDefaultWindowWidth = atoi(strTmp.c_str());
if(g_nDefaultWindowWidth==0)
{
g_nDefaultWindowWidth = nDefaultWindowWidth;
}
if(g_nWindowWidth<=0 && g_nDefaultWindowWidth>0)
{
g_nWindowWidth = g_nDefaultWindowWidth;
}
g_nPrevWindowCenter = g_nWindowCenter;
g_nPrevWindowWidth = g_nWindowWidth;
if(g_nSamplesPerPixel==3 && g_nBitsAllocated==8)
{
g_nColorType = 1;
}
else
{
g_nColorType = 0;
}
if(g_nFrameTextureWidth==0)
{
DcmElement* pElement = NULL;
g_pDcmDataset->findAndGetElement(DCM_Columns, pElement);
//printf("pElement: %x\n", pElement);
}
// DcmElement* pixelDataElement = NULL;
// error = g_pDcmDataset->findAndGetElement(DCM_PixelData, pixelDataElement);
g_pixelDataElement = NULL;
g_pDcmDataset->findAndGetElement(DCM_PixelData, g_pixelDataElement);
if(g_cachedFrames!=NULL)
{
delete[] g_cachedFrames;
g_cachedFrames = NULL;
}
if(g_pixelDataElement!=NULL)
{
OFCondition ofTest;
OFString decompressedColorModel;
Uint32 nDisplayFrameIndex = g_nCurrentFrame;
//g_pBuf = (char*)malloc(g_nFrameSizeUncompressed);
Uint32 startFragment = 0;
//nDisplayFrameIndex = 1;
//g_cachedFrames.clear();
//g_cachedFrames.resize(g_nTotalFrames);
g_cachedFrames = (uint8_t*)new uint8_t[g_nFrameSizeUncompressed*g_nTotalFrames];
memset(g_cachedFrames, 0, g_nFrameSizeUncompressed*g_nTotalFrames);
int i=0;
for(i=0 ; i<g_nTotalFrames ; i++)
{
//g_cachedFrames[i].resize(g_nFrameSizeUncompressed);
//ofTest = g_pixelDataElement->getUncompressedFrame(g_pDcmDataset, i, startFragment, g_cachedFrames[i].data(), g_nFrameSizeUncompressed, decompressedColorModel);
uint8_t* pTmpData = (uint8_t*)&g_cachedFrames[i*g_nFrameSizeUncompressed];
ofTest = g_pixelDataElement->getUncompressedFrame(g_pDcmDataset, i, startFragment, pTmpData, g_nFrameSizeUncompressed, decompressedColorModel);
}
}
if(g_nBitsAllocated==8 && g_nSamplesPerPixel==3)
{
g_nColorType = 1;
const Uint8* pImageData = NULL;
error = g_pDcmDataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
if(error.bad())
{
//printf("chooseRepresentation: %s\n", error.text());
}
else
{
g_pDcmDataset->findAndGetUint8Array(DCM_PixelData, pImageData);
}
}
else if(g_nBitsAllocated==16 && g_nSamplesPerPixel==1)
{
g_nColorType = 0;
const Uint16* pImageData16 = NULL;
error = g_pDcmDataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
if(error.bad())
{
//printf("chooseRepresentation: %s\n", error.text());
}
else
{
g_pDcmDataset->findAndGetUint16Array(DCM_PixelData, pImageData16);
}
}
else if(g_nBitsAllocated==8 && g_nSamplesPerPixel==1)
{
g_nColorType = 0;
const Uint8* pImageData = NULL;
error = g_pDcmDataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
if(error.bad())
{
printf("chooseRepresentation: %s\n", error.text());
}
else
{
g_pDcmDataset->findAndGetUint8Array(DCM_PixelData, pImageData);
}
}
else
{
g_nColorType = 0;
}
if (photometricInterpretation == "PALETTE COLOR")
{
std::cout << "형식: 8-bit Palette Color 이미지" << std::endl;
// --- 1. 인덱스 데이터 읽기 (8비트 픽셀 데이터) ---
const Uint8* indexData = NULL;
unsigned long count = 0;
g_pDcmDataset->findAndGetUint8Array(DCM_PixelData, indexData, &count);
if (indexData) {
// 이 indexData를 GL_LUMINANCE 포맷을 사용하여 첫 번째 텍스처(인덱스 텍스처)로 업로드합니다.
// setupTexture(indexData, GL_LUMINANCE, GL_UNSIGNED_BYTE);
}
// --- 2. 컬러 팔레트 데이터 읽기 ---
// DICOM에서 팔레트는 R, G, B 채널별로 따로 저장되어 있습니다.
const Uint16* redPalette = NULL;
const Uint16* greenPalette = NULL;
const Uint16* bluePalette = NULL;
unsigned long paletteEntries = 0; // 팔레트의 색상 수 (보통 256)
g_pDcmDataset->findAndGetUint16Array(DCM_RedPaletteColorLookupTableData, redPalette, &paletteEntries);
g_pDcmDataset->findAndGetUint16Array(DCM_GreenPaletteColorLookupTableData, greenPalette);
g_pDcmDataset->findAndGetUint16Array(DCM_BluePaletteColorLookupTableData, bluePalette);
if (redPalette && greenPalette && bluePalette)
{
g_nPaletteEntries = paletteEntries;
std::cout << "컬러 팔레트 로드 성공. 항목 수: " << paletteEntries << std::endl;
// --- 3. 팔레트 텍스처 생성 ---
// 256개의 RGB 색상값을 담을 버퍼를 생성합니다.
//g_paletteBuffer.reserve(paletteEntries * 3);
//g_paletteBuffer.resize(paletteEntries * 3);
g_paletteBuffer = new uint8_t[paletteEntries*3];
for (unsigned int i = 0; i < paletteEntries; ++i)
{
// DICOM 팔레트 데이터는 보통 16비트이지만, 화면 표시는 8비트로 충분하므로 상위 8비트를 사용합니다.
g_paletteBuffer[i * 3 + 0] = redPalette[i] >> 8; // Red
g_paletteBuffer[i * 3 + 1] = greenPalette[i] >> 8; // Green
g_paletteBuffer[i * 3 + 2] = bluePalette[i] >> 8; // Blue
}
// 이 paletteBuffer를 GL_RGB 포맷을 사용하여 두 번째 텍스처(팔레트 텍스처)로 업로드합니다.
// GLuint paletteTextureID;
// glGenTextures(1, &paletteTextureID);
// glBindTexture(GL_TEXTURE_2D, paletteTextureID);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_nPaletteEntries, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, g_paletteBuffer.data());
// ... (텍스처 파라미터 설정)
}
}
if(callback_UpdateDcmImageComplete!=NULL)
{
printf("c++ callback_UpdateDcmImageComplete\n");
callback_UpdateDcmImageComplete(1);
}
//ResetWindowWidthLevel();
g_nWindowWidth = g_nDefaultWindowWidth;
g_nWindowCenter = g_nDefaultWindowCenter;
g_nPrevWindowWidth = g_nDefaultWindowWidth;
g_nPrevWindowCenter = g_nDefaultWindowCenter;
return 1;
//g_bChange = false;
}
int onLoadedData(void* arg, void* buffer, int nSize)
{
int nRet = onLoadedDataInternal(arg, buffer, nSize);
if(nRet)
{
g_bChange = true;
printf("load OK!\n");
}
free(buffer);
return nRet;
}
void onErrorData2(unsigned nHandle, void* arg, int nHTTPCode, const char* strDescription)
{
printf("onErrorData2 %d, %p, %d, %s\n", nHandle, arg, nHTTPCode, strDescription);
}
void onLoadedData2(unsigned nHandle, void* arg, void* buffer, unsigned nSize)
{
if(g_bCancelLoad==true)
{
return;
}
printf("onLoadedData2: %d(%d), %p, %p, %d\n", g_nHandleWget, nHandle, arg, buffer, nSize);
if(g_nHandleWget==nHandle)
{
onLoadedData(arg, buffer, nSize);
}
}
void onProgressData2(unsigned nHandle, void* arg, int nLoadedBytes, int nTotalBytes)
{
//printf("onProgressData2: %d, %d, %d, %d\n", nHandle, (int)arg, nLoadedBytes, nTotalBytes);
g_nReadBytes = nLoadedBytes;
g_nTotalBytes = nTotalBytes;
}
void initTextureGray8()
{
int w = g_nFrameTextureWidth;
int h = g_nFrameTextureHeight;
//const Uint8* pImageData = NULL;
//g_pDcmDataset->findAndGetUint8Array(DCM_PixelData, pImageData);
{
glGenTextures(1, &g_nTextureGray8ID);
//updateTextureGray8(w, h, (void*)pImageData);
//updateTextureGray8(w, h, (void*)g_cachedFrames[0].data());
updateTextureGray8(w, h, (void*)&g_cachedFrames[0]);
}
}
void initTextureGray8Palette()
{
//initTextureGray8();
//return;
int w = g_nFrameTextureWidth;
int h = g_nFrameTextureHeight;
//const Uint8* pImageData = NULL;
//g_pDcmDataset->findAndGetUint8Array(DCM_PixelData, pImageData);
{
glGenTextures(1, &g_nTextureGray8PaletteID);
glGenTextures(1, &g_nPaletteTextureID);
//printf("glGenTexture: g_nTextureGray8PaletteID(%d), g_nPaletteTextureID(%d), pImageData(%p)\n", g_nTextureGray8PaletteID, g_nPaletteTextureID, pImageData);
//updateTextureGray8Palette(w, h, (void*)g_cachedFrames[0].data());
updateTextureGray8Palette(w, h, (void*)&g_cachedFrames[0]);
}
}
void initTextureGray16()
{
int w = g_nFrameTextureWidth;
int h = g_nFrameTextureHeight;
//const Uint16* pImageData = NULL;
{
//printf("updateTextureGray16: %p\n", pImageData);
glGenTextures(1, &g_nTextureGray16ID);
//g_pDcmDataset->findAndGetUint16Array(DCM_PixelData, pImageData);
//updateTextureGray16(w, h, (void*)g_cachedFrames[0].data());
updateTextureGray16(w, h, (void*)&g_cachedFrames[0]);
}
}
void initTextureRGB()
{
int w = g_nFrameTextureWidth;
int h = g_nFrameTextureHeight;
//const Uint8* pImageData = NULL;
//g_pDcmDataset->findAndGetUint8Array(DCM_PixelData, pImageData);
{
glGenTextures(1, &g_nTextureID);
//updateTextureRGB(w, h, (void*)g_cachedFrames[0].data());
updateTextureRGB(w, h, (void*)&g_cachedFrames[0]);
}
}
#ifdef __EMSCRIPTEN__
#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN
#endif
/**
* Svelte의 on:mousemove 이벤트로부터 마우스의 상대적 이동량을 받아 처리합니다.
* @param deltaX 이전 프레임으로부터의 X축 이동량 (event.movementX)
* @param deltaY 이전 프레임으로부터의 Y축 이동량 (event.movementY)
*/
EXTERN EMSCRIPTEN_KEEPALIVE void DoMouseDown(int x, int y)
{
// EventHandler 인스턴스가 유효한지 확인합니다.
if (!g_pEventHandler) {
return;
}
g_pEventHandler->SetMouseButtonDownPosition(x, y);
printf("DoMouseDown (%d, %d)\n", x, y);
return;
}
EXTERN EMSCRIPTEN_KEEPALIVE void DoMouseUp(int x, int y)
{
// EventHandler 인스턴스가 유효한지 확인합니다.
if (!g_pEventHandler) {
return;
}
g_pEventHandler->SetMouseButtonUpPosition(x, y);
printf("DoMouseUp (%d, %d)\n", x, y);
return;
}
EXTERN EMSCRIPTEN_KEEPALIVE void DoMouseMove(int x, int y)
{
// EventHandler 인스턴스가 유효한지 확인합니다.
if (!g_pEventHandler) {
return;
}
g_pEventHandler->panEventMouse(x, y);
printf("Pan 모드에서 마우스 이동: (%d, %d)\n", x, y);
return;
}
EXTERN EMSCRIPTEN_KEEPALIVE void DoMouseWheelZoom(float fDelta)
{
// EventHandler 인스턴스가 유효한지 확인합니다.
if (!g_pEventHandler) {
return;
}
g_pEventHandler->zoom(fDelta);
printf("zoom (%f)\n", fDelta);
return;
}
EXTERN EMSCRIPTEN_KEEPALIVE void DoPinchZoom(float scale, float pinchX, float pinchY) {
if (!g_pEventHandler) {
return;
}
Camera& camera = g_pEventHandler->camera();
// 1. 줌을 적용하기 전, 핀치 중심점의 월드(World) 좌표를 계산합니다.
float preZoomWorldX, preZoomWorldY;
camera.windowToWorldCoords((int)pinchX, (int)pinchY, preZoomWorldX, preZoomWorldY);
// 2. 전달받은 비율(scale)을 사용하여 줌 레벨을 업데이트합니다.
// camera.cpp에 setZoomDelta가 있으므로, 현재 줌과의 차이를 계산하여 전달합니다.
float currentZoom = camera.zoom();
float newZoom = currentZoom * scale;
float zoomDelta = newZoom - currentZoom;
camera.setZoomDelta(zoomDelta); // 기존 setZoomDelta 함수를 활용
// 3. 줌을 적용한 후, 동일한 화면 좌표가 어떤 월드 좌표를 가리키는지 다시 계산합니다.
float postZoomWorldX, postZoomWorldY;
camera.windowToWorldCoords((int)pinchX, (int)pinchY, postZoomWorldX, postZoomWorldY);
// 4. 두 월드 좌표의 차이만큼 화면을 이동(pan)시켜 줍니다.
// 이렇게 하면 핀치 중심점은 줌 전후에 같은 위치에 머무르게 됩니다.
Vec2 deltaWorld = { postZoomWorldX - preZoomWorldX, postZoomWorldY - preZoomWorldY };
camera.setPanDelta(deltaWorld); // 기존 setPanDelta 함수를 활용
}
/*
EXTERN EMSCRIPTEN_KEEPALIVE void DoMouseMove(float deltaX, float deltaY)
{
// EventHandler 인스턴스가 유효한지 확인합니다.
if (!g_pEventHandler) {
return;
}
// 현재 활성화된 모드에 따라 다른 동작을 수행합니다.
if (g_currentMode & MODE_PAN) {
// --- 이동 (Pan) 모드 로직 ---
// Camera 객체에 접근합니다.
Camera& camera = g_pEventHandler->camera();
// 전달받은 delta 값을 화면 크기로 정규화하면 너무 민감할 수 있으므로,
// 줌 레벨을 고려하여 이동량을 계산합니다.
// 이 계산 방식은 기존 panEventMouse와 유사하게 동작합니다.
float panDeltaX = deltaX / camera.zoom();
float panDeltaY = deltaY / camera.zoom();
// Camera의 pan 값을 업데이트합니다. Y축은 보통 반전시켜줍니다.
camera.setPanDelta({ panDeltaX, -panDeltaY });
printf("Pan 모드에서 마우스 이동: dX=%.2f, dY=%.2f\n", deltaX, deltaY);
} else if (g_currentMode & MODE_WIDTHLEVEL) {
// --- Window/Level 모드 로직 ---
// 이전에 사용하시던 m_fDeltaX, m_fDeltaY와 유사하게
// deltaX와 deltaY를 사용하여 Window Width와 Level을 조절하는 로직을 여기에 구현합니다.
// 예를 들어, g_pEventHandler에 관련 값을 업데이트하는 함수를 만들 수 있습니다.
// 예시:
// g_pEventHandler->UpdateWindowLevel(deltaX, deltaY);
printf("Window/Level 모드에서 마우스 이동: dX=%.2f, dY=%.2f\n", deltaX, deltaY);
}
}
*/
EXTERN EMSCRIPTEN_KEEPALIVE int CancelLoadImage()
{
printf("c++ CancelLoadImage: %d\n", g_nHandleWget);
g_bCancelLoad = true;
emscripten_async_wget2_abort(g_nHandleWget);
printf("c++ Cancel Complete\n");
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int UpdateDcmImage(std::string strFile)
{
g_nTotalBytes = 1000000000;
g_nReadBytes = 0;
g_bCancelLoad = false;
g_bLoadImage = true;
//emscripten_async_wget_data(strFile.c_str(), (void*)135, onLoadedData, onErrorData);
g_nHandleWget = emscripten_async_wget2_data(strFile.c_str(), "GET", "", (void*)135, false, onLoadedData2, onErrorData2, onProgressData2);
printf("c++ UpdateDcmImage: %d(%s)\n", g_nHandleWget, strFile.c_str());
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int ParseDicomToPixelData(char* buffer, int nDataSize)
{
onLoadedDataInternal(NULL, (void*)buffer, nDataSize);
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE void Shutdown() {
printf("--- C++ Shutdown() called. Terminating main loop and cleaning up. ---\n");
// 1. Emscripten의 메인 루프를 취소합니다.
emscripten_cancel_main_loop();
// 2. 생성했던 모든 GL 리소스를 삭제합니다.
if (g_vbo != 0) {
glDeleteBuffers(1, &g_vbo);
g_vbo = 0;
}
if (g_nTextureID != 0) {
glDeleteTextures(1, &g_nTextureID);
g_nTextureID = 0;
}
if (g_nTextureGray8ID != 0) {
glDeleteTextures(1, &g_nTextureGray8ID);
g_nTextureGray8ID = 0;
}
if (g_nTextureGray16ID != 0) {
glDeleteTextures(1, &g_nTextureGray16ID);
g_nTextureGray16ID = 0;
}
if (g_nTextureGray8PaletteID != 0) {
glDeleteTextures(1, &g_nTextureGray8PaletteID);
g_nTextureGray8PaletteID = 0;
}
if (g_nPaletteTextureID != 0) {
glDeleteTextures(1, &g_nPaletteTextureID);
g_nPaletteTextureID = 0;
}
if (g_shaderProgramGray8 != 0) {
glDeleteProgram(g_shaderProgramGray8);
g_shaderProgramGray8 = 0;
}
if (g_shaderProgramGray8Palette != 0) {
glDeleteProgram(g_shaderProgramGray8Palette);
g_shaderProgramGray8Palette = 0;
}
if (g_shaderProgramGray16 != 0) {
glDeleteProgram(g_shaderProgramGray16);
g_shaderProgramGray16 = 0;
}
if (g_shaderProgramRGB != 0) {
glDeleteProgram(g_shaderProgramRGB);
g_shaderProgramRGB = 0;
}
if(g_pDcmDataset!=NULL)
{
delete g_pDcmDataset;
g_pDcmDataset = NULL;
}
//g_paletteBuffer.clear();
if(g_paletteBuffer!=NULL)
{
delete[] g_paletteBuffer;
g_paletteBuffer = NULL;
}
//g_cachedFrames.clear();
if(g_cachedFrames!=NULL)
{
delete[] g_cachedFrames;
g_cachedFrames = NULL;
}
// ... 다른 모든 셰이더 프로그램, 텍스처 등도 동일하게 정리합니다. ...
}
EXTERN EMSCRIPTEN_KEEPALIVE int ProcessDCMFile(char* buffer, int nDataSize)
{
//ClearValue();
onLoadedData(NULL, (void*)buffer, nDataSize);
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int SetWindowWidthLevel(int nWindowCenter, int nWindowWidth)
{
//printf("New nWindowCenter:%d, nWindowWidth:%d\n", nWindowCenter, nWindowWidth);
g_nWindowCenter = nWindowCenter;
g_nWindowWidth = nWindowWidth;
g_nPrevWindowCenter = nWindowCenter;
g_nPrevWindowWidth = nWindowWidth;
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int SetDisplayFrameIndex(int nIndex)
{
g_nCurrentFrame = nIndex;
if(g_nCurrentFrame>=g_nTotalFrames)
{
g_nCurrentFrame = g_nTotalFrames-1;
}
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int SetResizeFrame(int nWidth, int nHeight)
{
printf("SetResizeFrame: (%d, %d)\n", nWidth, nHeight);
//g_pEventHandler->camera().setWindowSize(nWidth, nHeight);
g_nWindowSizeWidth = nWidth;
g_nWindowSizeHeight = nHeight;
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int ResetView()
{
g_pEventHandler->camera().reset();
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int ResetWindowWidthLevel()
{
g_nWindowWidth = g_nDefaultWindowWidth;
g_nWindowCenter = g_nDefaultWindowCenter;
g_nPrevWindowWidth = g_nDefaultWindowWidth;
g_nPrevWindowCenter = g_nDefaultWindowCenter;
return 0;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetTotalBytes()
{
return g_nTotalBytes;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetReadBytes()
{
return g_nReadBytes;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetWindowWidth()
{
return g_nWindowWidth;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetWindowCenter()
{
return g_nWindowCenter;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetTotalFrames()
{
return g_nTotalFrames;
}
EXTERN EMSCRIPTEN_KEEPALIVE float GetFrameUpdateTimeDelay()
{
return (g_fFrameDelay + g_fFrameTime);
}
EXTERN EMSCRIPTEN_KEEPALIVE char* getDumpJson()
{
printf("--- GetDumpJson() called ---\n");
nlohmann::json j;
j["WindowWidthLevel"]["WindowWidth"] = g_nPrevWindowWidth;
j["WindowWidthLevel"]["WindowCenter"] = g_nPrevWindowCenter;
j["visible"] = g_bVisible;
j["viewerID"] = g_nViewerID;
j["Width"] = g_nWindowSizeWidth;
j["Height"] = g_nWindowSizeHeight;
g_pEventHandler->camera().toJson(j);
strRet.clear();
strRet = j.dump(2);
return (char*)strRet.c_str();
}
EXTERN EMSCRIPTEN_KEEPALIVE void setDumpJson(const char* pStrDumpJson)
{
printf("--- setDumpJson(%s) called ---\n", pStrDumpJson);
g_pEventHandler->camera().fromString(pStrDumpJson);
printf("Camera::fromString enter!%s\n", pStrDumpJson);
try
{
nlohmann::json json_obj = nlohmann::json::parse(pStrDumpJson);
const auto& WidthLevel_data = json_obj["WindowWidthLevel"];
g_nWindowWidth = WidthLevel_data["WindowWidth"];
g_nWindowCenter = WidthLevel_data["WindowCenter"];
g_nPrevWindowWidth = g_nWindowWidth;
g_nPrevWindowCenter = g_nWindowCenter;
if (json_obj.contains("visible")) {
// 2. 키가 존재하면, 값을 가져와 g_bVisible에 할당합니다.
g_bVisible = json_obj["visible"];
printf("Parsed 'visible': %s\n", g_bVisible ? "true" : "false");
} else {
// 3. 키가 존재하지 않으면, 기본값을 설정하여 예기치 않은 동작을 방지합니다.
printf("'visible' key not found in JSON. Using default value (true).\n");
g_bVisible = true;
}
if(json_obj.contains("viewerID"))
{
g_nViewerID = json_obj["viewerID"];
}
else
{
printf("can not find viewerID\n");
}
}
catch (const nlohmann::json::exception& e)
{
// It's good practice to catch potential errors
printf("JSON parsing error: %s\n", e.what());
}
}
EXTERN EMSCRIPTEN_KEEPALIVE void SetInteractionMode(int mode) {
if (mode & InteractionMode::MODE_PAN ) {
printf("C++: Interaction mode set to PAN\n");
}
if (mode & InteractionMode::MODE_WIDTHLEVEL ) {
printf("C++: Interaction mode set to WIDTHLEVEL\n");
}
if (mode & InteractionMode::MODE_ZOOM ) {
printf("C++: Interaction mode set to ZOOM\n");
}
if (mode & InteractionMode::MODE_SCROLL ) {
printf("C++: Interaction mode set to SCROLL\n");
}
g_currentMode = mode;
g_pEventHandler->SetInteractionMode(g_currentMode);
}
EXTERN EMSCRIPTEN_KEEPALIVE bool SetCallbackUpdateDcmImageComplete(callback_UpdateDcmImage callback_)
{
//printf("SetCallbackUpdateDcmImage: %x\n", callback_);
callback_UpdateDcmImageComplete = callback_;
return true;
}
EXTERN EMSCRIPTEN_KEEPALIVE bool SetCallbackUpdateWasmInitGeometryComplete(callback_UpdateWasmInitGeometry callback_)
{
//printf("SetCallbackUpdateDcmImage: %x\n", callback_);
callback_UpdateWasmInitGeometryComplete = callback_;
return true;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetFrameWidth()
{
return g_nFrameTextureWidth;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetFrameHeight()
{
return g_nFrameTextureHeight;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetFrameSizeUncompressed()
{
return g_nFrameSizeUncompressed;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetPaletteEntries()
{
return g_nPaletteEntries;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetDefaultWindowWidth()
{
return g_nDefaultWindowWidth;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetDefaultWindowCenter()
{
return g_nDefaultWindowCenter;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetBitsAllocated()
{
return g_nBitsAllocated;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetSamplesPerPixel()
{
return g_nSamplesPerPixel;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetColorType()
{
return g_nColorType;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetWidth()
{
return g_nWindowSizeWidth;
}
EXTERN EMSCRIPTEN_KEEPALIVE int GetHeight()
{
return g_nWindowSizeHeight;
}
EXTERN EMSCRIPTEN_KEEPALIVE uint8_t* GetPixelData()
{
printf("--- GetPixelData() called ---\n");
return g_cachedFrames;
}
EXTERN EMSCRIPTEN_KEEPALIVE uint8_t* GetPaletteData()
{
printf("--- GetPixelData() called ---\n");
return g_paletteBuffer;
}
EXTERN EMSCRIPTEN_KEEPALIVE void SetDicomData(uint8_t* pPixelData, uint8_t* pPaletteData,
int nFrameWidth, int nFrameHeight, int nBitsAllocated, int nSamplesPerPixel,
int nDefaultWindowWidth, int nDefaultWindowCenter, int nPaletteEntries, int nColorType,
int nTotalFrames)
{
if(g_paletteBuffer!=NULL)
{
delete[] g_paletteBuffer;
g_paletteBuffer = NULL;
}
if(g_cachedFrames!=NULL)
{
delete[] g_cachedFrames;
g_cachedFrames = NULL;
}
g_nDefaultWindowWidth = nDefaultWindowWidth;
g_nDefaultWindowCenter = nDefaultWindowCenter;
g_nWindowWidth = g_nDefaultWindowWidth;
g_nWindowCenter = g_nDefaultWindowCenter;
g_nPrevWindowWidth = g_nDefaultWindowWidth;
g_nPrevWindowCenter = g_nDefaultWindowCenter;
g_nBitsAllocated = nBitsAllocated;
g_nSamplesPerPixel = nSamplesPerPixel;
g_nFrameTextureWidth = nFrameWidth;
g_nFrameTextureHeight = nFrameHeight;
g_cachedFrames = pPixelData;
g_paletteBuffer = pPaletteData;
g_nPaletteEntries = nPaletteEntries;
g_nColorType = nColorType;
g_nTotalFrames = nTotalFrames;
g_bChange = true;
printf("SetDicomData c++(int nFrameWidth:%d, int nFrameHeight:%d, int nBitsAllocated:%d, int nSamplesPerPixel:%d, \
int nDefaultWindowWidth:%d, int nDefaultWindowCenter:%d, int nPaletteEntries:%d, int nColorType:%d \
int nTotalFrames:%d)\n",
nFrameWidth, nFrameHeight, nBitsAllocated, nSamplesPerPixel,
nDefaultWindowWidth, nDefaultWindowCenter, nPaletteEntries, nColorType,
nTotalFrames);
}
#endif
void UpdateFrame()
{
OFCondition ofTest;
OFString decompressedColorModel;
Uint32 nDisplayFrameIndex = g_nCurrentFrame;
//g_pBuf = (char*)malloc(g_nFrameSizeUncompressed);
Uint32 startFragment = 0;
//nDisplayFrameIndex = 1;
Uint32 nDisplay = nDisplayFrameIndex/1;
//ofTest = g_pixelDataElement->getUncompressedFrame(g_pDcmDataset, nDisplay, startFragment, g_pBuf, g_nFrameSizeUncompressed, decompressedColorModel);
if(ofTest.bad())
{
//printf("getUncompressedFrame error: %s\n", ofTest.text());
}
//printf("nDisplay: %d\n", nDisplay);
nDisplayFrameIndex++;
g_nCurrentFrame = nDisplayFrameIndex;
if(g_nCurrentFrame>=g_nTotalFrames*1)
{
g_nCurrentFrame = 0;
}
if(g_nColorType==0)
{
if(g_nBitsAllocated==8)
{
//printf("updateTextureGray8: %d, %d, %d, %d, %s\nerror: %s\n", g_nFrameTextureWidth, g_nFrameTextureHeight, g_nCurrentFrame, g_nFrameSizeUncompressed, decompressedColorModel.c_str(), ofTest.text());
if(g_nPaletteEntries>0)
{
//printf("updateTextureGray8Palette: %d, %d, %d, %d, %s\nerror: %s\n", g_nFrameTextureWidth, g_nFrameTextureHeight, g_nCurrentFrame, g_nFrameSizeUncompressed, decompressedColorModel.c_str(), ofTest.text());
//updateTextureGray8Palette(g_nFrameTextureWidth, g_nFrameTextureHeight, g_cachedFrames[nDisplay].data());
updateTextureGray8Palette(g_nFrameTextureWidth, g_nFrameTextureHeight, (void*)&g_cachedFrames[nDisplay*g_nFrameSizeUncompressed]);
}
else
{
//updateTextureGray8(g_nFrameTextureWidth, g_nFrameTextureHeight, g_cachedFrames[nDisplay].data());
updateTextureGray8(g_nFrameTextureWidth, g_nFrameTextureHeight, (void*)&g_cachedFrames[nDisplay*g_nFrameSizeUncompressed]);
}
}
else if(g_nBitsAllocated>8)
{
//printf("updateTextureGray16: %d, %d, %d, %d, %s\nerror: %s\n", g_nFrameTextureWidth, g_nFrameTextureHeight, g_nCurrentFrame, g_nFrameSizeUncompressed, decompressedColorModel.c_str(), ofTest.text());
//updateTextureGray16(g_nFrameTextureWidth, g_nFrameTextureHeight, g_cachedFrames[nDisplay].data());
updateTextureGray16(g_nFrameTextureWidth, g_nFrameTextureHeight, (void*)&g_cachedFrames[nDisplay*g_nFrameSizeUncompressed]);
}
}
else if(g_nColorType==1)
{
////printf("updateTextureRGB: %d, %d, %d, %d, %s\nerror: %s\n", g_nFrameTextureWidth, g_nFrameTextureHeight, g_nCurrentFrame, g_nFrameSizeUncompressed, decompressedColorModel.c_str(), ofTest.text());
//updateTextureRGB(g_nFrameTextureWidth, g_nFrameTextureHeight, g_cachedFrames[nDisplay].data());
updateTextureRGB(g_nFrameTextureWidth, g_nFrameTextureHeight, (void*)&g_cachedFrames[nDisplay*g_nFrameSizeUncompressed]);
}
}
void redraw(EventHandler& eventHandler)
{
// 1. 그리기 전, 화면을 깨끗이 지웁니다.
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
int nWindowSizeWidth = (int)g_pEventHandler->camera().windowSize().width;
int nWindowSizeHeight = (int)g_pEventHandler->camera().windowSize().height;
bool bSizeChange = false;
if(nWindowSizeWidth != g_nWindowSizeWidth ||
nWindowSizeHeight != g_nWindowSizeHeight)
{
bSizeChange = true;
printf("redraw: width(%d), height(%d) --> width(%d), height(%d)\n",
nWindowSizeWidth, nWindowSizeHeight, g_nWindowSizeWidth, g_nWindowSizeHeight);
g_pEventHandler->camera().setWindowSize(g_nWindowSizeWidth, g_nWindowSizeHeight);
glViewport(0, 0, g_nWindowSizeWidth, g_nWindowSizeHeight);
}
if(g_bChange==true)
{
//ClearTextures();
printf("g_bChange!!\n");
if(g_nColorType==0)
{
printf("ColorType==0\n");
if(g_nBitsAllocated==8)
{
printf("redraw: initTextureGray8\n");
if(g_nPaletteEntries>0)
{
printf("redraw: g_nPaletteEntries>0 and shader(%d)\n", g_shaderProgramGray8Palette);
initGeometry(g_shaderProgramGray8Palette);
glUseProgram(g_shaderProgramGray8Palette);
initTextureGray8Palette();
}
else
{
initGeometry(g_shaderProgramGray8);
glUseProgram(g_shaderProgramGray8);
initTextureGray8();
}
}
else if(g_nBitsAllocated>8)
{
printf("redraw: initTextureGray16\n");
initGeometry(g_shaderProgramGray16);
glUseProgram(g_shaderProgramGray16);
initTextureGray16();
}
else
{
printf("redraw: initTextureGray8\n");
initGeometry(g_shaderProgramGray8);
glUseProgram(g_shaderProgramGray8);
initTextureGray8();
}
updateShader(*g_pEventHandler);
}
else if(g_nColorType==1)
{
printf("redraw: initTextureRGB\n");
initGeometry(g_shaderProgramRGB);
glUseProgram(g_shaderProgramRGB);
initTextureRGB();
updateShader(*g_pEventHandler);
}
g_bChange = false;
g_bUseFirst = true;
}
if(bSizeChange==true)
{
printf("bSizeChange==true\n");
if(g_nColorType==0)
{
printf("redraw: SizeChange initTextureGray16\n");
if(g_nBitsAllocated==8)
{
if(g_nPaletteEntries>0)
{
initGeometry(g_shaderProgramGray8Palette);
}
else
{
initGeometry(g_shaderProgramGray8);
}
//initGeometry(g_shaderProgramGray8);
}
else if(g_nBitsAllocated>8)
{
printf("redraw: initGeometry(g_shaderProgramGray16(%d)\n", g_shaderProgramGray16);
initGeometry(g_shaderProgramGray16);
}
}
else if(g_nColorType==1)
{
printf("redraw: SizeChange initTextureRGB\n");
initGeometry(g_shaderProgramRGB);
}
g_nWindowSizeWidth = (int)g_pEventHandler->camera().windowSize().width;
g_nWindowSizeHeight = (int)g_pEventHandler->camera().windowSize().height;
printf("redraw: SizeChanged (%d, %d)\n", g_nWindowSizeWidth, g_nWindowSizeHeight);
}
if(g_nTotalFrames>1)
{
//printf("redraw: g_nTotalFrames(%d)\n", g_nTotalFrames);
Uint32 lastUpdateTime = SDL_GetTicks();
if(lastUpdateTime - g_lastUpdateTime > g_fFrameTime)
{
UpdateFrame();
g_lastUpdateTime = lastUpdateTime;
}
}
else
{
if(g_nColorType==0)
{
if(g_nBitsAllocated>8)
{
}
}
}
bool bPrint = false;
if(g_nColorType==0)
{
if(g_nBitsAllocated>8)
{
if(bPrint)
printf("%d: updateWindowWidthLevelGray16(%d, %d)\n", g_nViewerID, g_nPrevWindowWidth, g_nPrevWindowCenter);
updateWindowWidthLevelGray16(g_nPrevWindowWidth, g_nPrevWindowCenter);
}
else if(g_nBitsAllocated==8)
{
if(g_nPaletteEntries>0)
{
if(bPrint)
printf("%d: updateWindowWidthLevelGray8Palette:%d, %d\n", g_nViewerID, g_nPrevWindowWidth, g_nPrevWindowCenter);
updateWindowWidthLevelGray8Palette(g_nPrevWindowWidth, g_nPrevWindowCenter);
}
else
{
if(bPrint)
printf("%d: updateWindowWidthLevelGray8:%d, %d\n", g_nViewerID, g_nPrevWindowWidth, g_nPrevWindowCenter);
updateWindowWidthLevelGray8(g_nPrevWindowWidth, g_nPrevWindowCenter);
}
}
}
else if(g_nColorType==1)
{
if(bPrint)
printf("%d: updateWindowWidthLevelRGB:%d, %d\n", g_nViewerID, g_nPrevWindowWidth, g_nPrevWindowCenter);
updateWindowWidthLevelRGB(g_nPrevWindowWidth, g_nPrevWindowCenter);
}
// Clear screen
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//glUseProgram(g_shaderProgramGray16);
// glActiveTexture(GL_TEXTURE0);
// glBindTexture(GL_TEXTURE_2D, g_nTextureID);
//glBindBuffer(GL_ARRAY_BUFFER, g_vbo);
// Draw the vertex buffer
glDrawArrays(GL_TRIANGLES, 0, 6);
// Swap front/back framebuffers
eventHandler.swapWindow();
//printf("redraw\n");
}
void mainLoop(void* mainLoopArg)
{
if(g_bVisible==false)
{
return;
}
EventHandler& eventHandler = *((EventHandler*)mainLoopArg);
eventHandler.processEvents();
int nType = eventHandler.GetEventType();
if(g_currentMode&InteractionMode::MODE_WIDTHLEVEL)
{
//printf("nType: %d\n", nType);
if(nType==1 || nType==2)
{
float fDeltaX = eventHandler.GetDeltaX();
float fDeltaY = eventHandler.GetDeltaY();
g_nPrevWindowCenter = g_nWindowCenter+fDeltaY;
g_nPrevWindowWidth = g_nWindowWidth + fDeltaX;
}
else if(nType==3 || nType==4)
{
g_nWindowCenter = g_nPrevWindowCenter;
g_nWindowWidth = g_nPrevWindowWidth;
}
}
// Update shader if camera changed
if (eventHandler.camera().updated())
updateShader(eventHandler);
if(g_nTotalFrames==0)
{
//redraw(eventHandler);
SDL_Renderer* renderer = eventHandler.GetRenderer();
// 시간에 따라 계속 변하는 색상 값을 만듭니다 (0 ~ 255).
// SDL_GetTicks()는 SDL 초기화 후 경과된 시간을 밀리초 단위로 반환합니다.
Uint8 red_value = (SDL_GetTicks() / 10) % 255;
red_value = 64;
// 렌더링 색상을 설정합니다 (R, G, B, A).
SDL_SetRenderDrawColor(renderer, red_value, 64, 64, 255);
// 설정된 색상으로 화면 전체를 지웁니다.
SDL_RenderClear(renderer);
// 지금까지의 렌더링 명령을 화면에 실제로 표시합니다.
SDL_RenderPresent(renderer);
}
else
{
redraw(eventHandler);
}
}
/**
* 이 함수는 Emscripten에 의해 매 프레임마다 호출될 메인 루프입니다.
*/
void main_loop(void* pMainLoopArg) {
EventHandler* pEventHandler = (EventHandler*)pMainLoopArg;
SDL_Renderer* renderer = pEventHandler->GetRenderer();
// 시간에 따라 계속 변하는 색상 값을 만듭니다 (0 ~ 255).
// SDL_GetTicks()는 SDL 초기화 후 경과된 시간을 밀리초 단위로 반환합니다.
Uint8 red_value = (SDL_GetTicks() / 10) % 255;
// 렌더링 색상을 설정합니다 (R, G, B, A).
SDL_SetRenderDrawColor(renderer, red_value, 64, 64, 255);
// 설정된 색상으로 화면 전체를 지웁니다.
SDL_RenderClear(renderer);
// 지금까지의 렌더링 명령을 화면에 실제로 표시합니다.
SDL_RenderPresent(renderer);
}
/**
* 애플리케이션의 시작점입니다.
*/
int main(int argc, char* argv[]) {
printf("C++ main 함수 시작!\n");
g_nTotalFrames = 0;
g_bVisible = true;
OFLog::configure(OFLogger::OFF_LOG_LEVEL);
EventHandler eventHandler("dcm image viewer");
DJDecoderRegistration::registerCodecs();
DJLSDecoderRegistration::registerCodecs();
DcmRLEDecoderRegistration::registerCodecs();
std::string strLoadFile = "http://192.168.1.21:3001/0010.dcm";
//emscripten_async_wget_data(strLoadFile.c_str(), (void*)135, onLoadedData, onErrorData);
initShader(eventHandler);
printf("SDL 초기화 성공, 메인 루프 설정 시도...\n");
int fps = -1;
// Emscripten에게 main_loop 함수를 브라우저의 렌더링 주기에 맞춰 계속 호출하도록 등록합니다.
// 이 함수를 등록한 후, main 함수는 정상적으로 종료되어야 합니다.
//emscripten_set_main_loop(main_loop, &eventHandler, 1);
//emscripten_set_main_loop_arg(main_loop, (void*)&eventHandler, fps, true);
emscripten_set_main_loop_arg(mainLoop, (void*)&eventHandler, fps, true);
// main 함수는 여기서 종료되지만, 등록된 main_loop 덕분에 프로그램은 계속 실행됩니다.
// 이 시점 이후에 자바스크립트의 onRuntimeInitialized 콜백이 호출됩니다.
DJDecoderRegistration::cleanup();
DJLSDecoderRegistration::cleanup();
DcmRLEDecoderRegistration::cleanup();
return 0;
}