Skip to content

Commit

Permalink
Bugfix: EXIF data lost on lossless JPEG transformations
Browse files Browse the repository at this point in the history
  • Loading branch information
sylikc committed Jun 21, 2024
2 parents dc058fa + 75d5df5 commit 8692ff5
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 33 deletions.
10 changes: 5 additions & 5 deletions src/JPEGView/JPEGLosslessTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,30 @@ CJPEGLosslessTransform::EResult CJPEGLosslessTransform::PerformCrop(LPCTSTR sInp
static CJPEGLosslessTransform::EResult _DoTransformation(LPCTSTR sInputFile, LPCTSTR sOutputFile, tjtransform &transform) {
CJPEGLosslessTransform::EResult eResult = CJPEGLosslessTransform::Success;

tjhandle hTransform = tjInitTransform();
tjhandle hTransform = tj3Init(TJINIT_TRANSFORM);

unsigned int nNumBytesInput;
unsigned char* pInputJPEGBytes = _ReadFile(sInputFile, nNumBytesInput);
if (pInputJPEGBytes != NULL) {
unsigned char* pOutputJPEGBytes = NULL;
unsigned long nNumBytesOutput = 0;
if (0 == tjTransform(hTransform, pInputJPEGBytes, nNumBytesInput, 1, &pOutputJPEGBytes, &nNumBytesOutput, &transform, 0) && pOutputJPEGBytes != NULL) {
size_t nNumBytesOutput = 0;
if (0 == tj3Transform(hTransform, pInputJPEGBytes, nNumBytesInput, 1, &pOutputJPEGBytes, &nNumBytesOutput, &transform) && pOutputJPEGBytes != NULL) {
if (!_WriteFile(sOutputFile, pOutputJPEGBytes, nNumBytesOutput)) {
eResult = CJPEGLosslessTransform::WriteFileFailed;
}
} else {
eResult = CJPEGLosslessTransform::TransformationFailed;
}
if (pOutputJPEGBytes != NULL) {
tjFree(pOutputJPEGBytes);
tj3Free(pOutputJPEGBytes);
}
} else {
eResult = CJPEGLosslessTransform::ReadFileFailed;
}

delete[] pInputJPEGBytes;

tjDestroy(hTransform);
tj3Destroy(hTransform);

return eResult;
}
Expand Down
13 changes: 6 additions & 7 deletions src/JPEGView/SaveImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "ParameterDB.h"
#include "EXIFReader.h"
#include "TJPEGWrapper.h"
#include "libjpeg-turbo\include\turbojpeg.h"
#include "WEBPWrapper.h"
#include "QOIWrapper.h"
#include <gdiplus.h>
Expand Down Expand Up @@ -150,7 +149,7 @@ static void* CompressAndSave(LPCTSTR sFileName, CJPEGImage * pImage,

FILE *fptr = _tfopen(sFileName, _T("wb"));
if (fptr == NULL) {
tjFree(pTargetStream);
TurboJpeg::Free(pTargetStream);
return NULL;
}

Expand Down Expand Up @@ -190,7 +189,7 @@ static void* CompressAndSave(LPCTSTR sFileName, CJPEGImage * pImage,

int nJFIFLength = GetJFIFBlockLength(pTargetStream);
memcpy(pNewStream + 2 + pImage->GetEXIFDataLength() + nEXIFBlockLenCorrection, pTargetStream + 2 + nJFIFLength, nJPEGStreamLen - 2 - nJFIFLength);
tjFree(pTargetStream);
TurboJpeg::Free(pTargetStream);
pTargetStream = pNewStream;
tjFreeNeeded = false;
nJPEGStreamLen = nJPEGStreamLen - nJFIFLength + pImage->GetEXIFDataLength() + nEXIFBlockLenCorrection;
Expand All @@ -201,13 +200,13 @@ static void* CompressAndSave(LPCTSTR sFileName, CJPEGImage * pImage,
if (sComment != NULL && sComment[0] != 0) {
uint8* pNewStream = RemoveExistingCommentSegment(pTargetStream, nJPEGStreamLen);
if (pNewStream != NULL) {
if (tjFreeNeeded) tjFree(pTargetStream); else delete[] pTargetStream;
if (tjFreeNeeded) TurboJpeg::Free(pTargetStream); else delete[] pTargetStream;
pTargetStream = pNewStream;
tjFreeNeeded = false;
}
pNewStream = InsertCommentBlock(pTargetStream, nJPEGStreamLen, sComment);
if (pNewStream != NULL) {
if (tjFreeNeeded) tjFree(pTargetStream); else delete[] pTargetStream;
if (tjFreeNeeded) TurboJpeg::Free(pTargetStream); else delete[] pTargetStream;
pTargetStream = pNewStream;
tjFreeNeeded = false;
}
Expand All @@ -218,7 +217,7 @@ static void* CompressAndSave(LPCTSTR sFileName, CJPEGImage * pImage,

// delete partial file if no success
if (!bSuccess) {
if (tjFreeNeeded) tjFree(pTargetStream); else delete[] pTargetStream;
if (tjFreeNeeded) TurboJpeg::Free(pTargetStream); else delete[] pTargetStream;
_tunlink(sFileName);
return NULL;
}
Expand Down Expand Up @@ -389,7 +388,7 @@ bool CSaveImage::SaveImage(LPCTSTR sFileName, CJPEGImage * pImage, const CImageP
if (bSuccess) {
nPixelHash = Helpers::CalculateJPEGFileHash(pCompressedJPEG, nJPEGStreamLen);
if (tjFreeNeeded) {
tjFree((unsigned char*)pCompressedJPEG);
TurboJpeg::Free((unsigned char*)pCompressedJPEG);
} else {
delete[] pCompressedJPEG;
}
Expand Down
45 changes: 25 additions & 20 deletions src/JPEGView/TJPEGWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ void * TurboJpeg::ReadImage(int &width,
nchannels = 3;
chromoSubsampling = TJSAMP_420;

tjhandle hDecoder = tjInitDecompress();
tjhandle hDecoder = tj3Init(TJINIT_DECOMPRESS);
if (hDecoder == NULL) {
return NULL;
}

unsigned char* pPixelData = NULL;
int nSubSampling;
int nResult = tjDecompressHeader2(hDecoder, (unsigned char*)buffer, sizebytes, &width, &height, &nSubSampling);
if (nResult == 0){
chromoSubsampling = (TJSAMP)nSubSampling;
int nResult = tj3DecompressHeader(hDecoder, (unsigned char*)buffer, sizebytes);
if (nResult == 0) {
width = tj3Get(hDecoder, TJPARAM_JPEGWIDTH);
height = tj3Get(hDecoder, TJPARAM_JPEGHEIGHT);
chromoSubsampling = (TJSAMP)tj3Get(hDecoder, TJPARAM_SUBSAMP);
if (abs((double)width * height) > MAX_IMAGE_PIXELS) {
outOfMemory = true;
} else if (width <= MAX_IMAGE_DIMENSION && height <= MAX_IMAGE_DIMENSION) {
} else if (width <= MAX_IMAGE_DIMENSION && height <= MAX_IMAGE_DIMENSION && chromoSubsampling != TJSAMP_UNKNOWN) {
pPixelData = new(std::nothrow) unsigned char[TJPAD(width * 3) * height];
if (pPixelData != NULL) {
nResult = tjDecompress2(hDecoder, (unsigned char*)buffer, sizebytes, pPixelData, width, TJPAD(width * 3), height,
TJPF_BGR, 0);
nResult = tj3Decompress8(hDecoder, (unsigned char*)buffer, sizebytes, pPixelData, TJPAD(width * 3), TJPF_BGR);
if (nResult != 0) {
delete[] pPixelData;
pPixelData = NULL;
Expand All @@ -44,7 +44,7 @@ void * TurboJpeg::ReadImage(int &width,
}
}

tjDestroy(hDecoder);
tj3Destroy(hDecoder);

return pPixelData;
}
Expand All @@ -58,27 +58,32 @@ void * TurboJpeg::Compress(const void *source,
{
outOfMemory = false;
len = 0;
tjhandle hEncoder = tjInitCompress();
tjhandle hEncoder = tj3Init(TJINIT_COMPRESS);
if (hEncoder == NULL) {
return NULL;
}

unsigned char* pJPEGCompressed = NULL;
unsigned long nCompressedLen = 0;
int nResult = tjCompress2(hEncoder, (unsigned char*)source, width, TJPAD(width * 3), height, TJPF_BGR,
&pJPEGCompressed, &nCompressedLen, TJSAMP_420, quality, 0);
if (nResult != 0) {
if (pJPEGCompressed != NULL) {
tjFree(pJPEGCompressed);
pJPEGCompressed = NULL;
} else {
size_t nCompressedLen = 0;
tj3Set(hEncoder, TJPARAM_SUBSAMP, TJSAMP_420);
tj3Set(hEncoder, TJPARAM_QUALITY, quality);
int nResult = tj3Compress8(hEncoder, (unsigned char*)source, width, TJPAD(width * 3), height, TJPF_BGR,
&pJPEGCompressed, &nCompressedLen);
if (nResult != 0 || nCompressedLen > INT_MAX) {
if (pJPEGCompressed == NULL) {
outOfMemory = true;
}
Free(pJPEGCompressed);
pJPEGCompressed = NULL;
}

len = nCompressedLen;

tjDestroy(hEncoder);
tj3Destroy(hEncoder);

return pJPEGCompressed;
}
}

void TurboJpeg::Free(void* buffer) {
tj3Free(buffer);
}
4 changes: 3 additions & 1 deletion src/JPEGView/TJPEGWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ class TurboJpeg
int sizebytes); // size of jpeg compressed data.

// Compress image data into JPEG stream, returns compressed data.
// The returned buffer must be freed with tjFree()!
// The returned buffer must be freed with Free()!
static void * Compress(const void *buffer, // address of image in memory, format must be 3 bytes per pixel BRGBGR with padding to 4 byte boundary
int width, // width of image in pixels
int height, // height of image in pixels.
int &len, // returns length of compressed data
bool &outOfMemory, // returns if out of memory
int quality=75); // image quality as a percentage

// Free buffer allocated by Compress
static void Free(void* buffer);
};

0 comments on commit 8692ff5

Please sign in to comment.