Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

732 lines
23 KiB
C

/*H********************************************************************************/
/*!
\File imageconv.c
\Description
Test DirtyGraph image conversion routines.
\Copyright
Copyright (c) 2006 Electronic Arts Inc.
\Version 02/23/2006 (jbrookes) First Version
*/
/********************************************************************************H*/
/*** Include files ****************************************************************/
#include <windows.h>
#include <string.h>
#include "DirtySDK/platform.h"
#include "DirtySDK/graph/dirtygraph.h"
#include "DirtySDK/graph/dirtygif.h"
#include "DirtySDK/graph/dirtyjpg.h"
#include "DirtySDK/graph/dirtypng.h"
#include "libsample/zfile.h"
#include "libsample/zlib.h"
#include "libsample/zmem.h"
#include "testermodules.h"
#define RUNLIBJPEG (FALSE)
#if RUNLIBJPEG
extern BITMAPINFO *jpeg_read_dibitmap(char *fname, char *errbuf, long errlen, long *cmpsize);
#endif
/*** Defines **********************************************************************/
/*** Type Definitions *************************************************************/
/*** Function Prototypes **********************************************************/
/*** Variables ********************************************************************/
static DirtyGraphRefT *pDirtyGraph;
/*** Private Functions ************************************************************/
/*F********************************************************************************/
/*!
\Function _CmdImgConvParseHeader
\Description
Read input image and parse it
\Input *pInputFile - input file data
\Input uInputSize - size of input file data
\Input *pImageInfo - [out] storage for image info
\Output
DirtyGraphInfoT * - pointer to parsed image info, or NULL
\Version 01/17/2020 (jbrookes) Split from _CmdImgConvDecodeImage
*/
/********************************************************************************F*/
static DirtyGraphInfoT *_CmdImgConvParseHeader(const uint8_t *pInputFile, uint32_t uInputSize, DirtyGraphInfoT *pImageInfo)
{
int32_t iError;
const char* _strImageTypes[] = { "unknown", "gif", "jpg", "png" };
// create module state
if (pDirtyGraph == NULL)
{
pDirtyGraph = DirtyGraphCreate();
}
// parse the image header
if ((iError = DirtyGraphDecodeHeader(pDirtyGraph, pImageInfo, pInputFile, uInputSize)) < 0)
{
ZPrintf("imgconv: error %d trying to parse image\n", iError);
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
return(NULL);
}
// identify image type
ZPrintf("imgconv: parsed %s image\n", _strImageTypes[pImageInfo->uType]);
return(pImageInfo);
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvDecodeImage
\Description
Read input image, and decode it to a 32bit ARGB file using DirtyGraph
\Input *pInputFile - input file data
\Input uInputSize - size of input file data
\Input pImageInfo - image info
\Input bMultiImage - multi-image decoding enabled
\Output
uint8_t * - pointer to output 32bit ARGB image, or NULL
\Version 02/23/2006 (jbrookes)
*/
/********************************************************************************F*/
static uint8_t *_CmdImgConvDecodeImage(const uint8_t *pInputFile, uint32_t uInputSize, DirtyGraphInfoT *pImageInfo, uint8_t bMultiImage)
{
uint8_t *p32Bit;
int32_t iError, iNumFrames = bMultiImage ? pImageInfo->uNumFrames : 1;
// allocate space for 32bit raw image
if ((p32Bit = ZMemAlloc(pImageInfo->uNumFrames*pImageInfo->iWidth*pImageInfo->iHeight*4)) == NULL)
{
ZPrintf("imgconv: could not allocate memory for decoded image\n");
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
return(NULL);
}
// decode the image
if (iNumFrames == 1)
{
if ((iError = DirtyGraphDecodeImage(pDirtyGraph, pImageInfo, p32Bit, pImageInfo->iWidth, pImageInfo->iHeight)) < 0)
{
ZPrintf("imgconv: error %d trying to decode image\n", iError);
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
ZMemFree(p32Bit);
return(NULL);
}
}
else
{
// get animation info
int32_t *pAnimInfo = ZMemAlloc(pImageInfo->uNumFrames*sizeof(int32_t));
int32_t iFrame, iFrames = DirtyGraphGetImageInfo(pDirtyGraph, pImageInfo, 'anim', pAnimInfo, pImageInfo->uNumFrames*sizeof(int32_t));
ZPrintf("imgconv: %d frames with animation delays of ");
for (iFrame = 0; iFrame < iFrames; iFrame += 1)
{
ZPrintf("%d,", pAnimInfo[iFrame]);
}
ZPrintf("\n");
ZMemFree(pAnimInfo);
// decode the multiframe inmage
if ((iError = DirtyGraphDecodeImageMulti(pDirtyGraph, pImageInfo, p32Bit, pImageInfo->iWidth, pImageInfo->iHeight)) < 0)
{
ZPrintf("imgconv: error %d trying to decode image\n", iError);
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
ZMemFree(p32Bit);
return(NULL);
}
}
// return 32bit image buffer
return(p32Bit);
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvSwizzleLineBMP
\Description
Swizzle scanline in place.
\Input *pScan - scanline to swizzled
\Input iWidth - width of scanline
\Output
None
\Version 03/07/2007 (jbrookes)
*/
/********************************************************************************F*/
static void _CmdImgConvSwizzleLineBMP(uint8_t *pScan, int32_t iWidth)
{
int32_t iCurW;
uint8_t uTmp;
// do the swap and swizzle in one pass
for (iCurW = 0; iCurW < iWidth; iCurW += 1, pScan += 4)
{
// swap a and b
uTmp = pScan[0]; // save a
pScan[0] = pScan[3]; // b->a
pScan[3] = uTmp; // a->b
// swap r and g
uTmp = pScan[1]; // save r
pScan[1] = pScan[2]; // g->r
pScan[2] = uTmp; // r->g
}
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvSwapAndSwizzleLineBMP
\Description
Swap scanlines pointed to by pScanLo and pScanHi and swizzle in-place.
\Input *pScanLo - lo scanline to swap&swizzle
\Input *pScanHi - hi scanline to swap&swizzle
\Input iWidth - width of scanline
\Output
None
\Version 03/07/2007 (jbrookes)
*/
/********************************************************************************F*/
static void _CmdImgConvSwapAndSwizzleLineBMP(uint8_t *pScanLo, uint8_t *pScanHi, int32_t iWidth)
{
int32_t iCurW;
uint8_t uTmp;
// do the swap and swizzle in one pass
for (iCurW = 0; iCurW < iWidth; iCurW += 1, pScanLo += 4, pScanHi += 4)
{
// swap a(hi) and b(lo)
uTmp = pScanHi[0]; // save a(hi)
pScanHi[0] = pScanLo[3]; // b(lo)->a(hi)
pScanLo[3] = uTmp; // a(hi)->b(lo)
// swap b(hi) and a(lo)
uTmp = pScanHi[3]; // save b(hi)
pScanHi[3] = pScanLo[0]; // a(lo)->b(hi)
pScanLo[0] = uTmp; // b(hi)->a(lo)
// swap r(hi) and g(lo)
uTmp = pScanHi[1]; // save r(hi)
pScanHi[1] = pScanLo[2]; // g(lo)->r(hi)
pScanLo[2] = uTmp; // r(hi)->g(lo)
// swap g(hi) and r(lo)
uTmp = pScanHi[2]; // save g(hi)
pScanHi[2] = pScanLo[1]; // r(lo)->g(hi)
pScanLo[1] = uTmp; // g(hi)->r(lo)
}
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvSaveBMP
\Description
Save input 32bit ARGB image as a 32bit BMP file
\Input *pFilename - filename of file to save
\Input *pImageData - input image data
\Input iWidth - width of input image
\Input iHeight - height of input image
\Output
int32_t - ZFileClose() result
\Version 02/23/2006 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdImgConvSaveBMP(const char *pFileName, const uint8_t *pImageData, int32_t iWidth, int32_t iHeight)
{
BITMAPFILEHEADER BitmapFileHeader;
BITMAPINFOHEADER BitmapInfoHeader;
uint8_t *pScanHi, *pScanLo, *pBMPData;
ZFileT iFileId;
int32_t iCurH;
// open the file for writing
if ((iFileId = ZFileOpen(pFileName, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_CREATE|ZFILE_OPENFLAG_BINARY)) < 0)
{
return(-1);
}
// make a temp copy for conversion to bmp format
if ((pBMPData = ZMemAlloc(iWidth*iHeight*4)) == NULL)
{
return(-2);
}
memcpy(pBMPData, pImageData, iWidth*iHeight*4);
// vflip image and convert from ARGB to BGRA in one pass
for (iCurH = 0; iCurH < (iHeight/2); iCurH++)
{
// ref scanlines to swap and swizzle
pScanLo = pBMPData + (iCurH*iWidth*4);
pScanHi = pBMPData + ((iHeight-iCurH-1)*iWidth*4);
// do the swap and swizzle
_CmdImgConvSwapAndSwizzleLineBMP(pScanLo, pScanHi, iWidth);
}
// if height is odd, swizzle the center scanline
if (iHeight & 1)
{
// ref scanlines to swap and swizzle
pScanLo = pBMPData + (iCurH*iWidth*4);
// do the swap and swizzle
_CmdImgConvSwizzleLineBMP(pScanLo, iWidth);
}
// format bitmap header
ds_memclr(&BitmapFileHeader, sizeof(BitmapFileHeader));
BitmapFileHeader.bfType = 'MB';
BitmapFileHeader.bfSize = sizeof(BitmapFileHeader)+sizeof(BitmapInfoHeader)+(iWidth*iHeight*4);
BitmapFileHeader.bfOffBits = sizeof(BitmapFileHeader)+sizeof(BitmapInfoHeader);
// write fileheader to output file
if (ZFileWrite(iFileId, &BitmapFileHeader, sizeof(BitmapFileHeader)) < 0)
{
ZFileClose(iFileId);
return(-1);
}
// format bitmapinfo header
ds_memclr(&BitmapInfoHeader, sizeof(BitmapInfoHeader));
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoHeader.biWidth = iWidth;
BitmapInfoHeader.biHeight = iHeight;
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biBitCount = 32;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biSizeImage = iWidth*iHeight*4;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;
BitmapInfoHeader.biClrUsed = 0;
BitmapInfoHeader.biClrImportant = 0;
// write infoheader to output file
if (ZFileWrite(iFileId, &BitmapInfoHeader, sizeof(BitmapInfoHeader)) < 0)
{
ZFileClose(iFileId);
return(-1);
}
// write pixel data to output file
if (ZFileWrite(iFileId, pBMPData, iWidth*iHeight*4) < 0)
{
ZFileClose(iFileId);
return(-1);
}
// free pixel data buffer
ZMemFree(pBMPData);
// close the file
return(ZFileClose(iFileId));
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvSaveRAW
\Description
Save input 32bit ARGB image as a 24bit RAW image.
\Input *pFilename - filename of file to save
\Input *pImageData - input image data
\Input iWidth - width of input image
\Input iHeight - height of input image
\Output
int32_t - ZFileSave() result
\Version 02/23/2006 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdImgConvSaveRAW(const char *pFileName, uint8_t *pImageData, int32_t iWidth, int32_t iHeight)
{
uint8_t *pSrc, *pDst;
int32_t iW, iH;
// convert 32bit ARGB image to 24bit RGB image (inline)
for (pSrc=pImageData, pDst=pImageData, iH=0; iH < iHeight; iH++)
{
for (iW = 0; iW < iWidth; iW++)
{
pDst[0] = pSrc[1];
pDst[1] = pSrc[2];
pDst[2] = pSrc[3];
pSrc += 4;
pDst += 3;
}
}
// write the file
return(ZFileSave(pFileName, (const char *)pImageData, iWidth*iHeight*3, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_CREATE|ZFILE_OPENFLAG_BINARY));
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvDecodeAndSave
\Description
Decode input to 32bit ARGB and save as raw or bmp.
\Input *pFilename - filename of file to save
\Input *pInputFile - input file
\Input iInputSize - input file size
\Input *pImageInfo - image info
\Output
int32_t - ZFileSave() result
\Version 01/17/2020 (jbrookes) Split from CmdImgConv()
*/
/********************************************************************************F*/
static int32_t _CmdImgConvDecodeAndSave(const char *pFileName, uint8_t *pInputFile, int32_t iInputSize, DirtyGraphInfoT *pImageInfo)
{
uint8_t *p32BitImage;
int32_t iResult;
// convert to 32bit raw image
if ((p32BitImage = _CmdImgConvDecodeImage(pInputFile, iInputSize, pImageInfo, FALSE)) == NULL)
{
return(0);
}
// save output image based on type
if (ds_stristr(pFileName, ".raw"))
{
iResult = _CmdImgConvSaveRAW(pFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight);
}
else if (ds_stristr(pFileName, ".bmp"))
{
iResult = _CmdImgConvSaveBMP(pFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight);
}
else
{
ZPrintf("imgconv: output filetype unrecognized\n");
iResult = -1;
}
// success?
if (iResult >= 0)
{
ZPrintf("imgconv: saved output image %s\n", pFileName);
}
else
{
ZPrintf("imgconv: error writing output file '%s'\n", pFileName);
}
// dispose of buffer and return result to caller
ZMemFree(p32BitImage);
return(iResult);
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvDecodeAndSaveMulti
\Description
Decode input multi-frame file to 32bit ARGB and save as raw or bmp files.
A numerical extension is appended to differentiate the frames. Uses
DirtyGraphDecodeImage32Multi()
\Input *pFilename - filename of file to save
\Input *pInputFile - input file
\Input iInputSize - input file size
\Input *pImageInfo - image info
\Output
int32_t - ZFileSave() result
\Version 01/17/2020 (jbrookes) Split from CmdImgConv()
*/
/********************************************************************************F*/
static int32_t _CmdImgConvDecodeAndSaveMulti(const char *pFileName, uint8_t *pInputFile, int32_t iInputSize, DirtyGraphInfoT *pImageInfo)
{
char strOutputFileName[1024], *pExt;
uint8_t *p32BitImage;
int32_t iResult, iFrame, iFrameSize;
uint8_t bBmp;
// convert to 32bit raw image
if ((p32BitImage = _CmdImgConvDecodeImage(pInputFile, iInputSize, pImageInfo, TRUE)) == NULL)
{
return(0);
}
// copy filename
ds_strnzcpy(strOutputFileName, pFileName, sizeof(strOutputFileName));
// save output image based on type
if ((pExt = ds_stristr(strOutputFileName, ".raw")) != NULL)
{
bBmp = FALSE;
}
else if ((pExt = ds_stristr(strOutputFileName, ".bmp")) != NULL)
{
bBmp = TRUE;
}
else
{
ZPrintf("imgconv: output filetype unrecognized\n");
return(0);
}
// set up filename for writing multiple frames; first truncate the extension
*pExt = '\0';
// write out frame data as successive images
for (iFrame = 0, iFrameSize = pImageInfo->iWidth*pImageInfo->iHeight*4, iResult = 0; (iFrame < pImageInfo->uNumFrames) && (iResult == 0); iFrame += 1)
{
ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-%02d%s", iFrame, bBmp ? ".bmp" : ".raw");
iResult = bBmp ? _CmdImgConvSaveBMP(strOutputFileName, p32BitImage+(iFrame*iFrameSize), pImageInfo->iWidth, pImageInfo->iHeight) : _CmdImgConvSaveRAW(strOutputFileName, p32BitImage+(iFrame * iFrameSize), pImageInfo->iWidth, pImageInfo->iHeight);
}
// success?
if (iResult >= 0)
{
ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-XX%s", bBmp ? ".bmp" : ".raw");
ZPrintf("imgconv: saved %d output images %s\n", iFrame, strOutputFileName);
}
else
{
ZPrintf("imgconv: error writing output file '%s'\n", strOutputFileName);
}
// dispose of buffer and return result to caller
ZMemFree(p32BitImage);
return(iResult);
}
/*F********************************************************************************/
/*!
\Function _CmdImgConvDecodeAndSaveMulti2
\Description
Decode input multi-frame file to 32bit ARGB and save as raw or bmp files.
A numerical extension is appended to differentiate the frames. Uses
DirtyGraphDecodeImageFrame().
\Input *pFilename - filename of file to save
\Input *pInputFile - input file
\Input iInputSize - input file size
\Input *pImageInfo - image info
\Output
int32_t - ZFileSave() result
\Version 02/06/2020 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdImgConvDecodeAndSaveMulti2(const char *pFileName, uint8_t *pInputFile, int32_t iInputSize, DirtyGraphInfoT *pImageInfo)
{
char strOutputFileName[1024], *pExt;
uint8_t *p32BitImage;
int32_t iResult, iFrame, iError;
uint8_t bBmp;
// allocate space for 32bit raw image
if ((p32BitImage = ZMemAlloc(pImageInfo->iWidth*pImageInfo->iHeight*4)) == NULL)
{
ZPrintf("imgconv: could not allocate memory for decoded image\n");
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
return(0);
}
// copy filename
ds_strnzcpy(strOutputFileName, pFileName, sizeof(strOutputFileName));
// save output image based on type
if ((pExt = ds_stristr(strOutputFileName, ".raw")) != NULL)
{
bBmp = FALSE;
}
else if ((pExt = ds_stristr(strOutputFileName, ".bmp")) != NULL)
{
bBmp = TRUE;
}
else
{
ZPrintf("imgconv: output filetype unrecognized\n");
return(0);
}
// set up filename for writing multiple frames; first truncate the extension
*pExt = '\0';
// write out frame data as successive images
for (iFrame = 0, iResult = 0; (iFrame < pImageInfo->uNumFrames) && (iResult == 0); iFrame += 1)
{
// decode the multiframe inmage
uint64_t uTick = NetTickUsec();
if ((iError = DirtyGraphDecodeImageFrame(pDirtyGraph, pImageInfo, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight, iFrame)) < 0)
{
ZPrintf("imgconv: error %d trying to decode image\n", iError);
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
ZMemFree(p32BitImage);
return(0);
}
ZPrintf("imgconv: %dus for decode\n", NetTickDiff(NetTickUsec(), uTick));
// create filename
ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-%02d%s", iFrame, bBmp ? ".bmp" : ".raw");
// save the image
iResult = bBmp ? _CmdImgConvSaveBMP(strOutputFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight) : _CmdImgConvSaveRAW(strOutputFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight);
}
// success?
if (iResult >= 0)
{
ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-XX%s", bBmp ? ".bmp" : ".raw");
ZPrintf("imgconv: saved %d output images %s\n", iFrame, strOutputFileName);
}
else
{
ZPrintf("imgconv: error writing output file '%s'\n", strOutputFileName);
}
// dispose of buffer and return result to caller
ZMemFree(p32BitImage);
return(iResult);
}
/*** Public functions *************************************************************/
/*F********************************************************************************/
/*!
\Function CmdStream
\Description
Create the Module module.
\Input *argz - environment
\Input argc - number of args
\Input *argv[] - argument list
\Output int32_t - standard return code
\Version 02/23/2006 (jbrookes)
*/
/********************************************************************************F*/
int32_t CmdImgConv(ZContext *argz, int32_t argc, char *argv[])
{
int32_t iInputSize, iResult, iArg = 1;
uint8_t *pInputFile;
const char *pInputName;
DirtyGraphInfoT ImageInfo;
uint8_t bMultiFrame = FALSE, bMultiFrame2 = FALSE;
// check for module state destroy (no arguments)
if ((argc == 1) && (pDirtyGraph != NULL))
{
// destroy state
DirtyGraphDestroy(pDirtyGraph);
pDirtyGraph = NULL;
ZPrintf("%s: graph instance destroyed\n", argv[0]);
return(0);
}
// check for multiframe argument
if ((argc > 1) && !strcmp(argv[iArg], "-m"))
{
iArg += 1;
bMultiFrame = TRUE;
}
// check for multiframe flavor #2
if ((argc > 1) && !strcmp(argv[iArg], "-m2"))
{
iArg += 1;
bMultiFrame = TRUE;
bMultiFrame2 = TRUE;
}
// usage
if ((argc == iArg) || (argc > iArg+2))
{
ZPrintf("usage: %s [-m] <inputfile> <outputfile>\n", argv[0]);
return(0);
}
pInputName = argv[iArg];
// open input file for reading
if ((pInputFile = (uint8_t *)ZFileLoad(pInputName, &iInputSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) == NULL)
{
ZPrintf("%s: unable to open input file '%s'\n", argv[0], pInputName);
return(0);
}
#if RUNLIBJPEG
// first read it with libjpeg for comparison
if (ds_stristr(pInputName, ".jpg"))
{
char strError[256];
jpeg_read_dibitmap((char *)pInputName, strError, sizeof(strError), NULL);
}
#endif
// parse image info
if (_CmdImgConvParseHeader(pInputFile, iInputSize, &ImageInfo) == NULL)
{
return(0);
}
// output?
if (argc == iArg+2)
{
if ((ImageInfo.uNumFrames == 1) || !bMultiFrame)
{
iResult = _CmdImgConvDecodeAndSave(argv[iArg+1], pInputFile, iInputSize, &ImageInfo);
}
else if (!bMultiFrame2)
{
iResult = _CmdImgConvDecodeAndSaveMulti(argv[iArg+1], pInputFile, iInputSize, &ImageInfo);
}
else
{
iResult = _CmdImgConvDecodeAndSaveMulti2(argv[iArg+1], pInputFile, iInputSize, &ImageInfo);
}
}
// dispose of source image
ZMemFree(pInputFile);
return(0);
}