From 818d1450682a3cea8f54d292c171eac19c120e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Thu, 6 Mar 2025 09:34:45 -0800 Subject: [PATCH] Add FITS to thumbnailer --- thumbnailer/CMakeLists.txt | 2 +- thumbnailer/Dll.cpp | 2 + thumbnailer/TenmonThumbnailProvider.cpp | 25 ++- thumbnailer/loadxisf.cpp | 242 +++++++++++++++++------- 4 files changed, 195 insertions(+), 76 deletions(-) diff --git a/thumbnailer/CMakeLists.txt b/thumbnailer/CMakeLists.txt index f88375e..38f7c02 100644 --- a/thumbnailer/CMakeLists.txt +++ b/thumbnailer/CMakeLists.txt @@ -12,7 +12,7 @@ if(BUILD_THUMBNAILER) target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT) target_include_directories(tenmonthumbnailer PRIVATE ../libXISF) - target_link_libraries(tenmonthumbnailer PRIVATE shlwapi ${LCMS2_LIB} XISF) + target_link_libraries(tenmonthumbnailer PRIVATE shlwapi ${LCMS2_LIB} ${FITS_LIB} XISF) target_link_options(tenmonthumbnailer PRIVATE "-static") else(WIN32) qt_add_executable(tenmonthumbnailer diff --git a/thumbnailer/Dll.cpp b/thumbnailer/Dll.cpp index bc9537e..57f1619 100644 --- a/thumbnailer/Dll.cpp +++ b/thumbnailer/Dll.cpp @@ -196,6 +196,8 @@ STDAPI DllRegisterServer() {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_TENMONTHUMBHANDLER L"\\InProcServer32", NULL, szModuleName}, {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_TENMONTHUMBHANDLER L"\\InProcServer32", L"ThreadingModel", L"Apartment"}, {HKEY_CURRENT_USER, L"Software\\Classes\\.xisf\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, SZ_CLSID_TENMONTHUMBHANDLER}, + {HKEY_CURRENT_USER, L"Software\\Classes\\.fits\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, SZ_CLSID_TENMONTHUMBHANDLER}, + {HKEY_CURRENT_USER, L"Software\\Classes\\.fit\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, SZ_CLSID_TENMONTHUMBHANDLER}, }; hr = S_OK; diff --git a/thumbnailer/TenmonThumbnailProvider.cpp b/thumbnailer/TenmonThumbnailProvider.cpp index 4bde21a..522d112 100644 --- a/thumbnailer/TenmonThumbnailProvider.cpp +++ b/thumbnailer/TenmonThumbnailProvider.cpp @@ -1,16 +1,10 @@ -// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -// PARTICULAR PURPOSE. -// -// Copyright (c) Microsoft Corporation. All rights reserved - #include #include // For IThumbnailProvider. #include #include "libxisf.h" bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize); +bool loadFITS(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize); class TenmonThumbProvider : public IInitializeWithStream, public IThumbnailProvider @@ -109,8 +103,19 @@ IFACEMETHODIMP TenmonThumbProvider::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_AL *pdwAlpha = WTSAT_RGB; data.resize(readSize); - if(loadXISF(data, phbmp, cx)) - return S_OK; + if(data[0] == 'X' && data[1] == 'I' && data[2] == 'S' && data[3] == 'F') + { + if(loadXISF(data, phbmp, cx)) + return S_OK; + else + return E_FAIL; + } else - return E_FAIL; + { + if(loadFITS(data, phbmp, cx)) + return S_OK; + else + return E_FAIL; + } + return E_FAIL; } diff --git a/thumbnailer/loadxisf.cpp b/thumbnailer/loadxisf.cpp index b6a8cd2..e79e73c 100644 --- a/thumbnailer/loadxisf.cpp +++ b/thumbnailer/loadxisf.cpp @@ -1,12 +1,80 @@ #include "libxisf.h" #include #include "../rawimage.h" +#include bool OpenGLES = false; +void RawImageToHTBITMAP(std::shared_ptr &rawImage, HBITMAP *hbmp, UINT thumbSize) +{ + rawImage->calcStats(); + if(rawImage->imageStats().m_median[0] < rawImage->norm() * 0.2f) + { + //OutputDebugStringA("Stretch image"); + MTFParam params = rawImage->calcMTFParams(); + rawImage->applySTF(params); + } + + UINT w = rawImage->width(); + UINT h = rawImage->height(); + + UINT cw = thumbSize; + UINT ch = thumbSize; + if (w > h) + ch = h * thumbSize / w; + else + cw = w * thumbSize / h; + + + rawImage->resize(cw, ch); + rawImage->convertToType(RawImage::UINT8); + + BITMAPINFO bmi = {}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = cw; + bmi.bmiHeader.biHeight = -static_cast(ch); + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + UINT lw = cw * 4; + + BYTE *pBits; + HBITMAP bmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, reinterpret_cast(&pBits), NULL, 0); + + const unsigned char *p = (const unsigned char*)rawImage->data(); + const unsigned short *ps = (const unsigned short*)rawImage->data(); + if(rawImage->channels() == 1) + { + for(UINT y = 0; y < ch; y++) + { + for(UINT x = 0; x < cw; x++) + { + pBits[(y * lw) + x * 4 + 0] = p[y * cw + x]; + pBits[(y * lw) + x * 4 + 1] = p[y * cw + x]; + pBits[(y * lw) + x * 4 + 2] = p[y * cw + x]; + pBits[(y * lw) + x * 4 + 3] = 255; + } + } + } + else + { + for(UINT y = 0; y < ch; y++) + { + for(UINT x = 0; x < cw; x++) + { + pBits[(y * lw) + x * 4 + 0] = p[y * cw * 4 + x * 4 + 2]; + pBits[(y * lw) + x * 4 + 1] = p[y * cw * 4 + x * 4 + 1]; + pBits[(y * lw) + x * 4 + 2] = p[y * cw * 4 + x * 4 + 0]; + pBits[(y * lw) + x * 4 + 3] = 255; + } + } + } + + *hbmp = bmp; +} + bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize) { - OutputDebugStringA("TENMON"); try { LibXISF::XISFReader xisf; @@ -25,11 +93,8 @@ bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize) default: break; } - LibXISF::Image tmpImage = xisfImage; tmpImage.convertPixelStorageTo(LibXISF::Image::Planar); - UINT w = tmpImage.width(); - UINT h = tmpImage.height(); std::shared_ptr rawImage; if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray) @@ -42,75 +107,122 @@ bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize) rawImage = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type); } - rawImage->calcStats(); - if(rawImage->imageStats().m_median[0] < rawImage->norm() * 0.2f) - { - OutputDebugStringA("Stretch image"); - MTFParam params = rawImage->calcMTFParams(); - rawImage->applySTF(params); - } + RawImageToHTBITMAP(rawImage, hbmp, thumbSize); - UINT cw = thumbSize; - UINT ch = thumbSize; - if (w > h) - ch = h * thumbSize / w; - else - cw = w * thumbSize / h; - - - rawImage->resize(cw, ch); - rawImage->convertToType(RawImage::UINT8); - - BITMAPINFO bmi = {}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = cw; - bmi.bmiHeader.biHeight = -static_cast(ch); - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - UINT lw = cw * 4; - - BYTE *pBits; - HBITMAP bmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, reinterpret_cast(&pBits), NULL, 0); - - const unsigned char *p = (const unsigned char*)rawImage->data(); - const unsigned short *ps = (const unsigned short*)rawImage->data(); - if(tmpImage.channelCount() == 1) - { - for(UINT y = 0; y < ch; y++) - { - for(UINT x = 0; x < cw; x++) - { - pBits[(y * lw) + x * 4 + 0] = p[y * cw + x]; - pBits[(y * lw) + x * 4 + 1] = p[y * cw + x]; - pBits[(y * lw) + x * 4 + 2] = p[y * cw + x]; - pBits[(y * lw) + x * 4 + 3] = 255; - } - } - } - else - { - for(UINT y = 0; y < ch; y++) - { - for(UINT x = 0; x < cw; x++) - { - pBits[(y * lw) + x * 4 + 0] = p[y * cw * 4 + x * 4 + 2]; - pBits[(y * lw) + x * 4 + 1] = p[y * cw * 4 + x * 4 + 1]; - pBits[(y * lw) + x * 4 + 2] = p[y * cw * 4 + x * 4 + 0]; - pBits[(y * lw) + x * 4 + 3] = 255; - } - } - } - - *hbmp = bmp; return true; } catch (LibXISF::Error &err) { char text[1024]; - sprintf(text, "Failed to open XISF image %s", err.what()); + sprintf_s(text, 1000, "Failed to open XISF image %s", err.what()); OutputDebugStringA(text); return false; } return false; } + +bool loadFITS(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize) +{ + fitsfile *file; + + int status = 0; + int type = -1; + int num = 0; + long naxes[3] = {0}; + + auto checkError = [&status]() + { + char err[100]; + char text[1000]; + fits_get_errstatus(status, err); + sprintf_s(text, 1000, "Failed to load FITS file %s", err); + OutputDebugStringA(text); + return false; + }; + + const void *dataPtr = data.data(); + size_t size = data.size(); + fits_open_memfile(&file, "file.fits", READONLY, (void**)&dataPtr, &size, 0, nullptr, &status); + if(status)return checkError(); + fits_get_num_hdus(file, &num, &status); + if(status)return checkError(); + + int imgtype; + int naxis; + for(int i=1; i <= num; i++) + { + fits_movabs_hdu(file, i, IMAGE_HDU, &status);if(status)return checkError(); + fits_get_hdu_type(file, &type, &status);if(status)return checkError(); + fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError(); + fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError(); + + if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0) + { + RawImage::DataType type; + int fitstype; + long fpixel[3] = {1,1,1}; + switch(imgtype) + { + case BYTE_IMG: + type = RawImage::UINT8; + fitstype = TBYTE; + break; + case SHORT_IMG: + type = RawImage::UINT16; + fitstype = TSHORT; + break; + case USHORT_IMG: + type = RawImage::UINT16; + fitstype = TUSHORT; + break; + case ULONG_IMG: + type = RawImage::UINT32; + fitstype = TUINT; + break; + case FLOAT_IMG: + type = RawImage::FLOAT32; + fitstype = TFLOAT; + break; + case DOUBLE_IMG: + type = RawImage::FLOAT64; + fitstype = TDOUBLE; + break; + default: + return false; + break; + } + + size_t size = naxes[0]*naxes[1]; + size_t w = naxes[0]; + size_t h = naxes[1]; + + RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type); + uint8_t *data = static_cast(img.data()); + for (int i=1; i==1 || i<=naxes[2]; i++) + { + fpixel[2] = i; + fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status); + if(status)return checkError(); + } + if(fitstype == TSHORT) + { + uint16_t *s = static_cast(img.data()); + size_t size = img.size() * img.channels(); + for(size_t i=0; i image; + if(img.channels() == 1) + image = std::make_shared(std::move(img)); + else + image = RawImage::fromPlanar(img); + + RawImageToHTBITMAP(image, hbmp, thumbSize); + return true; + } + } + + return false; +} +