#ifdef __EMSCRIPTEN__ //#include #include #else #include // for std::string #include // for std::ifstream #include // for std::vector #endif #include #include "nlohmann/json.hpp" // 라이브러리 경로에 맞게 수정 #include #include #include #include #include #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 //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> g_cachedFrames; uint8_t* g_cachedFrames = NULL; //std::vector 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 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 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 ; ifindAndGetOFString(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 ; igetUncompressedFrame(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; }