1-
21#include " StdAfx.h"
32#include " ImageLoadThread.h"
43#include < gdiplus.h>
2221#include " QOIWrapper.h"
2322#include " PSDWrapper.h"
2423#include " MaxImageDef.h"
25-
24+ # include " lepton_wrapper.h "
2625
2726using namespace Gdiplus ;
2827
@@ -62,6 +61,8 @@ static EImageFormat GetImageFormat(LPCTSTR sFileName) {
6261 } else if ((header[0 ] == 0xff && header[1 ] == 0x0a ) ||
6362 memcmp (header, " \x00\x00\x00\x0c JXL\x20\x0d\x0a\x87\x0a " , 12 ) == 0 ) {
6463 return IF_JXL;
64+ } else if (header[0 ] == 0xcf && header[1 ] == 0x84 ) {
65+ return IF_Lepton;
6566 } else if (!memcmp (header+4 , " ftyp" , 4 )) {
6667 // https://github.com/strukturag/libheif/issues/83
6768 // https://github.com/strukturag/libheif/blob/ce1e4586b6222588c5afcd60c7ba9caa86bcc58c/libheif/heif.h#L602-L805
@@ -117,7 +118,7 @@ static EImageFormat GetBitmapFormat(Gdiplus::Bitmap * pBitmap) {
117118 }
118119}
119120
120- static CJPEGImage* ConvertGDIPlusBitmapToJPEGImage (Gdiplus::Bitmap* pBitmap, int nFrameIndex, void * pEXIFData,
121+ static CJPEGImage* ConvertGDIPlusBitmapToJPEGImage (Gdiplus::Bitmap* pBitmap, int nFrameIndex, void * pEXIFData,
121122 __int64 nJPEGHash, bool &isOutOfMemory, bool &isAnimatedGIF) {
122123
123124 isOutOfMemory = false ;
@@ -289,7 +290,7 @@ void CImageLoadThread::ProcessRequest(CRequestBase& request) {
289290 }
290291
291292 CRequest& rq = (CRequest&)request;
292- double dStartTime = Helpers::GetExactTickCount ();
293+ double dStartTime = Helpers::GetExactTickCount ();
293294 // Get image format and read the image
294295 switch (GetImageFormat (rq.FileName )) {
295296 case IF_JPEG :
@@ -370,6 +371,10 @@ void CImageLoadThread::ProcessRequest(CRequestBase& request) {
370371 ProcessReadRAWRequest (&rq);
371372 break ;
372373#endif
374+ case IF_Lepton:
375+ DeleteCachedGDIBitmap ();
376+ ProcessReadLeptonRequest (&rq);
377+ break ;
373378 case IF_QOI:
374379 DeleteCachedGDIBitmap ();
375380 DeleteCachedWebpDecoder ();
@@ -397,7 +402,7 @@ void CImageLoadThread::ProcessRequest(CRequestBase& request) {
397402 }
398403 // then process the image if read was successful
399404 if (rq.Image != NULL ) {
400- rq.Image ->SetLoadTickCount (Helpers::GetExactTickCount () - dStartTime);
405+ rq.Image ->SetLoadTickCount (Helpers::GetExactTickCount () - dStartTime);
401406 if (!ProcessImageAfterLoad (&rq)) {
402407 delete rq.Image ;
403408 rq.Image = NULL ;
@@ -471,88 +476,78 @@ void CImageLoadThread::DeleteCachedAvifDecoder() {
471476#endif
472477}
473478
474- void CImageLoadThread::ProcessReadJPEGRequest (CRequest * request) {
475- HANDLE hFile = ::CreateFile (request->FileName , GENERIC_READ, FILE_SHARE_READ, NULL , OPEN_EXISTING, 0 , NULL );
476- if (hFile == INVALID_HANDLE_VALUE) {
477- return ;
478- }
479-
480- HGLOBAL hFileBuffer = NULL ;
481- void * pBuffer = NULL ;
479+ void CImageLoadThread::ProcessReadJPEGRequest (CRequest* request, const uint8_t * buffer, UINT size) {
482480 try {
483- // Don't read too huge files
484- long long nFileSize = Helpers::GetFileSize (hFile);
485- if (nFileSize > MAX_JPEG_FILE_SIZE) {
486- request->OutOfMemory = true ;
487- ::CloseHandle (hFile);
488- return ;
489- }
490- hFileBuffer = ::GlobalAlloc (GMEM_MOVEABLE, nFileSize);
491- pBuffer = (hFileBuffer == NULL ) ? NULL : ::GlobalLock (hFileBuffer);
492- if (pBuffer == NULL ) {
493- if (hFileBuffer) ::GlobalFree (hFileBuffer);
494- request->OutOfMemory = true ;
495- ::CloseHandle (hFile);
496- return ;
497- }
498- unsigned int nNumBytesRead;
499- if (::ReadFile (hFile, pBuffer, nFileSize, (LPDWORD) &nNumBytesRead, NULL ) && nNumBytesRead == nFileSize) {
500- bool bUseGDIPlus = CSettingsProvider::This ().ForceGDIPlus () || CSettingsProvider::This ().UseEmbeddedColorProfiles ();
501- if (bUseGDIPlus) {
502- IStream* pStream = NULL ;
503- if (::CreateStreamOnHGlobal (hFileBuffer, FALSE , &pStream) == S_OK) {
504- Gdiplus::Bitmap* pBitmap = Gdiplus::Bitmap::FromStream (pStream, CSettingsProvider::This ().UseEmbeddedColorProfiles ());
505- bool isOutOfMemory, isAnimatedGIF;
506- request->Image = ConvertGDIPlusBitmapToJPEGImage (pBitmap, 0 , Helpers::FindEXIFBlock (pBuffer, nFileSize),
507- Helpers::CalculateJPEGFileHash (pBuffer, nFileSize), isOutOfMemory, isAnimatedGIF);
508- request->OutOfMemory = request->Image == NULL && isOutOfMemory;
509- if (request->Image != NULL ) {
510- request->Image ->SetJPEGComment (Helpers::GetJPEGComment (pBuffer, nFileSize));
511- }
512- pStream->Release ();
513- delete pBitmap;
514- } else {
515- request->OutOfMemory = true ;
481+ bool bUseGDIPlus = CSettingsProvider::This ().ForceGDIPlus () || CSettingsProvider::This ().UseEmbeddedColorProfiles ();
482+ if (bUseGDIPlus) {
483+ CComPtr<IStream> pStream (::SHCreateMemStream (buffer, size));
484+ if (pStream) {
485+ std::shared_ptr<Gdiplus::Bitmap> pBitmap (Gdiplus::Bitmap::FromStream (pStream, CSettingsProvider::This ().UseEmbeddedColorProfiles ()));
486+ bool isOutOfMemory, isAnimatedGIF;
487+ request->Image = ConvertGDIPlusBitmapToJPEGImage (pBitmap.get (), 0 , Helpers::FindEXIFBlock ((void *)buffer, size),
488+ Helpers::CalculateJPEGFileHash ((void *)buffer, size), isOutOfMemory, isAnimatedGIF);
489+ request->OutOfMemory = request->Image == NULL && isOutOfMemory;
490+ if (request->Image != NULL ) {
491+ request->Image ->SetJPEGComment (Helpers::GetJPEGComment ((void *)buffer, size));
516492 }
517493 }
518- if (!bUseGDIPlus || request->OutOfMemory ) {
519- int nWidth, nHeight, nBPP;
520- TJSAMP eChromoSubSampling;
521- bool bOutOfMemory;
522- // int nTicks = ::GetTickCount();
523-
524- void * pPixelData = TurboJpeg::ReadImage (nWidth, nHeight, nBPP, eChromoSubSampling, bOutOfMemory, pBuffer, nFileSize);
525-
526- /*
527- TCHAR buffer[20];
528- _stprintf_s(buffer, 20, _T("%d"), ::GetTickCount() - nTicks);
529- ::MessageBox(NULL, CString(_T("Elapsed ticks: ")) + buffer, _T("Time"), MB_OK);
530- */
531-
532- // Color and b/w JPEG is supported
533- if (pPixelData != NULL && (nBPP == 3 || nBPP == 1 )) {
534- request->Image = new CJPEGImage (nWidth, nHeight, pPixelData,
535- Helpers::FindEXIFBlock (pBuffer, nFileSize), nBPP,
536- Helpers::CalculateJPEGFileHash (pBuffer, nFileSize), IF_JPEG, false , 0 , 1 , 0 );
537- request->Image ->SetJPEGComment (Helpers::GetJPEGComment (pBuffer, nFileSize));
538- request->Image ->SetJPEGChromoSampling (eChromoSubSampling);
539- } else if (bOutOfMemory) {
540- request->OutOfMemory = true ;
541- } else {
542- // failed, try GDI+
543- delete[] pPixelData;
544- ProcessReadGDIPlusRequest (request);
545- }
494+ else {
495+ request->OutOfMemory = true ;
546496 }
547497 }
548- } catch (...) {
549- delete request->Image ;
498+ if (!bUseGDIPlus || request->OutOfMemory ) {
499+ int nWidth, nHeight, nBPP;
500+ TJSAMP eChromoSubSampling;
501+ bool bOutOfMemory;
502+
503+ void * pPixelData = TurboJpeg::ReadImage (nWidth, nHeight, nBPP, eChromoSubSampling, bOutOfMemory, (const void *)buffer, size);
504+
505+ // Color and b/w JPEG is supported
506+ if (pPixelData != NULL && (nBPP == 3 || nBPP == 1 )) {
507+ request->Image = new CJPEGImage (nWidth, nHeight, pPixelData,
508+ Helpers::FindEXIFBlock ((void *)buffer, size), nBPP,
509+ Helpers::CalculateJPEGFileHash ((void *)buffer, size), IF_JPEG, false , 0 , 1 , 0 );
510+ request->Image ->SetJPEGComment (Helpers::GetJPEGComment ((void *)buffer, size));
511+ request->Image ->SetJPEGChromoSampling (eChromoSubSampling);
512+ }
513+ else if (bOutOfMemory) {
514+ request->OutOfMemory = true ;
515+ }
516+ else {
517+ // failed, try GDI+
518+ delete[] pPixelData;
519+ ProcessReadGDIPlusRequest (request);
520+ }
521+ }
522+ }
523+ catch (...) {
550524 request->Image = NULL ;
551525 request->ExceptionError = true ;
552526 }
553- ::CloseHandle (hFile);
554- if (pBuffer) ::GlobalUnlock (hFileBuffer);
555- if (hFileBuffer) ::GlobalFree (hFileBuffer);
527+ }
528+
529+ void CImageLoadThread::ProcessReadJPEGRequest (CRequest* request) {
530+ ATL::CAtlFile file;
531+ if (FAILED (file.Create (request->FileName , GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING))) {
532+ return ;
533+ }
534+
535+ ULONGLONG size = 0 ;
536+ if (FAILED (file.GetSize (size))) {
537+ return ;
538+ }
539+
540+ if (size > MAX_JPEG_FILE_SIZE) {
541+ request->OutOfMemory = true ;
542+ return ;
543+ }
544+
545+ CAtlFileMapping<uint8_t > file_map;
546+ if (FAILED (file_map.MapFile (file, 0 , 0 , PAGE_READONLY, FILE_MAP_READ))) {
547+ return ;
548+ }
549+
550+ ProcessReadJPEGRequest (request, file_map, (UINT)size);
556551}
557552
558553
@@ -715,7 +710,7 @@ void CImageLoadThread::ProcessReadPNGRequest(CRequest* request) {
715710 request->Image = new CJPEGImage (nWidth, nHeight, pPixelData, pEXIFData, 4 , 0 , IF_PNG, bHasAnimation, request->FrameIndex , nFrameCount, nFrameTimeMs);
716711 } else {
717712 DeleteCachedPngDecoder ();
718-
713+
719714 IStream* pStream = NULL ;
720715 if (::CreateStreamOnHGlobal (hFileBuffer, FALSE , &pStream) == S_OK) {
721716 Gdiplus::Bitmap* pBitmap = Gdiplus::Bitmap::FromStream (pStream, CSettingsProvider::This ().UseEmbeddedColorProfiles ());
@@ -861,7 +856,7 @@ void CImageLoadThread::ProcessReadAVIFRequest(CRequest* request) {
861856 int nWidth, nHeight, nBPP, nFrameCount, nFrameTimeMs;
862857 bool bHasAnimation;
863858 void * pEXIFData;
864- uint8* pPixelData = (uint8*)AvifReader::ReadImage (nWidth, nHeight, nBPP, bHasAnimation, request->FrameIndex ,
859+ uint8* pPixelData = (uint8*)AvifReader::ReadImage (nWidth, nHeight, nBPP, bHasAnimation, request->FrameIndex ,
865860 nFrameCount, nFrameTimeMs, pEXIFData, request->OutOfMemory , pBuffer, nFileSize);
866861 if (pPixelData != NULL ) {
867862 if (bHasAnimation)
@@ -1062,6 +1057,41 @@ void CImageLoadThread::ProcessReadGDIPlusRequest(CRequest * request) {
10621057 }
10631058}
10641059
1060+ void CImageLoadThread::ProcessReadLeptonRequest (CRequest* request) {
1061+ ATL::CAtlFile file;
1062+ if (FAILED (file.Create (request->FileName , GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING))) {
1063+ return ;
1064+ }
1065+
1066+ ULONGLONG size = 0 ;
1067+ if (FAILED (file.GetSize (size)))
1068+ {
1069+ return ;
1070+ }
1071+
1072+ CAtlFileMapping<uint8_t > file_map;
1073+ if (FAILED (file_map.MapFile (file, 0 , 0 , PAGE_READONLY, FILE_MAP_READ)))
1074+ {
1075+ return ;
1076+ }
1077+
1078+ // Get the lepton library.
1079+ auto & lib = lepton_wrapper::lib_lepton::get ();
1080+
1081+ // As we don't know how big the buffer should be for storing a resulting JPEG image,
1082+ // allocating it twice bigger than the input LEPTON file size.
1083+ // It should be enough for most cases.
1084+ auto buffer_size = size * 2 ;
1085+
1086+ // Extract a JPEG data from the lepton file and pass it to JPEG handler for further decoding.
1087+ std::vector<uint8_t > buffer (buffer_size);
1088+ uint8_t * pBuffer = &buffer[0 ];
1089+ uint64_t jpeg_size = {};
1090+ if (0 == lib.WrapperDecompressImage (file_map, size, pBuffer, buffer_size, 1 , &jpeg_size) && jpeg_size > 0 ) {
1091+ ProcessReadJPEGRequest (request, pBuffer, (UINT)jpeg_size);
1092+ }
1093+ }
1094+
10651095static unsigned char * alloc (int sizeInBytes) {
10661096 return new (std::nothrow) unsigned char [sizeInBytes];
10671097}
@@ -1111,7 +1141,7 @@ bool CImageLoadThread::ProcessImageAfterLoad(CRequest * request) {
11111141 double dZoom = request->ProcessParams .Zoom ;
11121142 CSize newSize;
11131143 if (dZoom < 0.0 ) {
1114- newSize = Helpers::GetImageRect (nWidth, nHeight,
1144+ newSize = Helpers::GetImageRect (nWidth, nHeight,
11151145 request->ProcessParams .TargetWidth , request->ProcessParams .TargetHeight , request->ProcessParams .AutoZoomMode , dZoom);
11161146 } else {
11171147 newSize = CSize ((int )(nWidth*dZoom + 0.5 ), (int )(nHeight*dZoom + 0.5 ));
@@ -1121,7 +1151,7 @@ bool CImageLoadThread::ProcessImageAfterLoad(CRequest * request) {
11211151 newSize.cy = max (1 , min (65535 , newSize.cy )); // max size must not be bigger than this after zoom
11221152
11231153 // clip to target rectangle
1124- CSize clippedSize (min (request->ProcessParams .TargetWidth , newSize.cx ),
1154+ CSize clippedSize (min (request->ProcessParams .TargetWidth , newSize.cx ),
11251155 min (request->ProcessParams .TargetHeight , newSize.cy ));
11261156
11271157 LimitOffsets (request->ProcessParams .Offsets , CSize (request->ProcessParams .TargetWidth , request->ProcessParams .TargetHeight ), newSize);
0 commit comments