X-Git-Url: https://sfsrealm.hopto.org/projects/gitweb.cgi?a=blobdiff_plain;f=grpapi%2Fgrpapi.cpp;h=6c96c561290fac94dfba899aa2deb14a2561daef;hb=72348a2de786fe37fccc3600d9c4d3cd0b4f63e9;hp=84420e65a93575e451fa643829dd229ffb6b142e;hpb=afcd1861b390cdcd023cf6e7dfe28c44ba6e2cca;p=grpapi.git diff --git a/grpapi/grpapi.cpp b/grpapi/grpapi.cpp index 84420e6..6c96c56 100644 --- a/grpapi/grpapi.cpp +++ b/grpapi/grpapi.cpp @@ -34,10 +34,18 @@ typedef struct { DWORD Offset; } FRAMEHEADER; +typedef struct { + WORD *lpRowOffsets; + WORD *lpRowSizes; + LPBYTE *lpRowData; +} FRAMEDATA; + GETPIXELPROC MyGetPixel = GetPixel; SETPIXELPROC MySetPixel = (SETPIXELPROC)SetPixelV; void __inline SetPix(HDC hDC, int X, int Y, COLORREF clrColor, DWORD *dwPalette, DWORD dwFlags, DWORD dwAlpha); +void EncodeFrameData(signed short *lpImageData, WORD nFrame, GRPHEADER *lpGrpHeader, FRAMEHEADER *lpFrameHeader, FRAMEDATA *lpFrameData); +BOOL VerifyRow(signed short *lpRawRow, int nWidth, LPBYTE lpEncRow, int nSize); extern HINSTANCE hStorm; @@ -71,6 +79,23 @@ void GRPAPI WINAPI FreeGrpApi() { } +/* +HANDLE hProcessHeap = NULL; + +void * SFAlloc(size_t nSize) +{ + if (!hProcessHeap) hProcessHeap = GetProcessHeap(); + if (hProcessHeap) return HeapAlloc(hProcessHeap, 0, nSize); + else return NULL; +} + +void SFFree(void *lpMem) +{ + if (!hProcessHeap) hProcessHeap = GetProcessHeap(); + if (hProcessHeap) HeapFree(hProcessHeap, 0, lpMem); +} +*/ + BOOL GRPAPI WINAPI SetMpqDll(LPCSTR lpDllFileName) { if (LoadStorm((char *)lpDllFileName)) return TRUE; @@ -183,7 +208,7 @@ BOOL GRPAPI WINAPI DestroyGrp(HANDLE hGrp) BOOL GRPAPI WINAPI DrawGrp(HANDLE hGrp, HDC hdcDest, int nXDest, int nYDest, WORD nFrame, DWORD *dwPalette, DWORD dwFlags, DWORD dwAlpha) { - if (!hGrp || hGrp==INVALID_HANDLE_VALUE || hdcDest==0 || !dwPalette) return FALSE; + if (!hGrp || hGrp==INVALID_HANDLE_VALUE || hdcDest==0 || (!dwPalette && !(dwFlags&USE_INDEX))) return FALSE; GRPHEADER *GrpFile = (GRPHEADER *)hGrp; nFrame %= GrpFile->nFrames; FRAMEHEADER *GrpFrame = &((FRAMEHEADER *)(((char *)GrpFile)+6))[nFrame]; @@ -430,3 +455,283 @@ void __inline SetPix(HDC hDC, int X, int Y, COLORREF clrColor, DWORD *dwPalette, } MySetPixel(hDC,X,Y,clrColor); } + +HANDLE GRPAPI WINAPI CreateGrp(signed short *lpImageData, WORD nFrames, WORD wMaxWidth, WORD wMaxHeight, BOOL bNoCompress, DWORD *nGrpSize) +{ + GRPHEADER GrpHeader; + FRAMEHEADER *lpFrameHeaders; + FRAMEDATA *lpFrameData; + LPBYTE lpGrpData; + int i, j, x, y, x1, x2, y1, y2; + DWORD nLastOffset; + + if (!lpImageData || !nGrpSize) return (HANDLE)-1; + + GrpHeader.nFrames = nFrames; + GrpHeader.wMaxWidth = wMaxWidth; + GrpHeader.wMaxHeight = wMaxHeight; + lpFrameHeaders = (FRAMEHEADER *)malloc(nFrames * sizeof(FRAMEHEADER)); + lpFrameData = (FRAMEDATA *)malloc(nFrames * sizeof(FRAMEDATA)); + nLastOffset = sizeof(GRPHEADER) + nFrames * sizeof(FRAMEHEADER); + + for (i = 0; i < nFrames; i++) { + // Search for duplicate frames + for (j = 0; j < i; j++) { + if (memcmp(&lpImageData[i * wMaxWidth * wMaxHeight], + &lpImageData[j * wMaxWidth * wMaxHeight], + wMaxWidth * wMaxHeight * sizeof(short)) == 0) + break; + } + + if (j < i) { + memcpy(&lpFrameHeaders[i], &lpFrameHeaders[j], sizeof(FRAMEHEADER)); + lpFrameData[i].lpRowOffsets = 0; + continue; + } + + if (i == 0) { + lpFrameHeaders[i].Offset = sizeof(GRPHEADER) + nFrames * sizeof(FRAMEHEADER); + } + else { + lpFrameHeaders[i].Offset = nLastOffset; + } + + // Scan frame to find dimensions of used part + x1 = y1 = 0x10000; + x2 = y2 = -1; + for (y = 0; y < wMaxHeight; y++) { + for (x = 0; x < wMaxWidth; x++) { + if (lpImageData[i * wMaxWidth * wMaxHeight + y * wMaxWidth + x] >= 0) { + if (x < x1) x1 = x; + if (x > x2) x2 = x; + if (y < y1) y1 = y; + if (y > y2) y2 = y; + } + } + } + x2 = x2 - x1 + 1; + y2 = y2 - y1 + 1; + if ((WORD)x1 > 255) x1 = 255; + if ((WORD)y1 > 255) y1 = 255; + if ((WORD)x2 > 255) x2 = 255; + if ((WORD)y2 > 255) y2 = 255; + lpFrameHeaders[i].Left = x1; + lpFrameHeaders[i].Top = y1; + lpFrameHeaders[i].Width = x2; + lpFrameHeaders[i].Height = y2; + + if (!bNoCompress) { + EncodeFrameData(lpImageData, i, &GrpHeader, &lpFrameHeaders[i], &lpFrameData[i]); + + y = lpFrameHeaders[i].Height; + if (y > 0) { + y--; + nLastOffset = lpFrameHeaders[i].Offset + lpFrameData[i].lpRowOffsets[y] + lpFrameData[i].lpRowSizes[y]; + } + else { + nLastOffset = lpFrameHeaders[i].Offset; + } + } + else { + nLastOffset = lpFrameHeaders[i].Offset + lpFrameHeaders[i].Width * lpFrameHeaders[i].Height; + } + } + + lpGrpData = (LPBYTE)malloc(nLastOffset); + + // Write completed GRP to buffer + memcpy(lpGrpData, &GrpHeader, sizeof(GRPHEADER)); + memcpy(lpGrpData + sizeof(GRPHEADER), lpFrameHeaders, nFrames * sizeof(FRAMEHEADER)); + + for (i = 0; i < nFrames; i++) { + if (lpFrameData[i].lpRowOffsets) { + if (!bNoCompress) { + memcpy(lpGrpData + lpFrameHeaders[i].Offset, lpFrameData[i].lpRowOffsets, lpFrameHeaders[i].Height * sizeof(WORD)); + + for (y = 0; y < lpFrameHeaders[i].Height; y++) { + if (lpFrameData[i].lpRowData[y]) { + memcpy(lpGrpData + lpFrameHeaders[i].Offset + lpFrameData[i].lpRowOffsets[y], lpFrameData[i].lpRowData[y], lpFrameData[i].lpRowSizes[y]); + free(lpFrameData[i].lpRowData[y]); + } + } + + free(lpFrameData[i].lpRowOffsets); + free(lpFrameData[i].lpRowSizes); + free(lpFrameData[i].lpRowData); + } + else { + for (y = 0; y < lpFrameHeaders[i].Height; y++) { + for (x = 0; x < lpFrameHeaders[i].Width; x++) { + lpGrpData[lpFrameHeaders[i].Offset + y * lpFrameHeaders[i].Width + x] = + (BYTE)lpImageData[i * wMaxWidth * wMaxHeight + (lpFrameHeaders[i].Top + y) * wMaxWidth + lpFrameHeaders[i].Left + x]; + } + } + } + } + } + + free(lpFrameHeaders); + free(lpFrameData); + + *nGrpSize = nLastOffset; + return (HANDLE)lpGrpData; +} + +void EncodeFrameData(signed short *lpImageData, WORD nFrame, GRPHEADER *lpGrpHeader, FRAMEHEADER *lpFrameHeader, FRAMEDATA *lpFrameData) +{ + int x, y, i, nBufPos, nRepeat; + LPBYTE lpRowBuf; + WORD nLastOffset; + + lpFrameData->lpRowOffsets = (WORD *)malloc(lpFrameHeader->Height * sizeof(WORD)); + lpFrameData->lpRowSizes = (WORD *)malloc(lpFrameHeader->Height * sizeof(WORD)); + lpFrameData->lpRowData = (LPBYTE *)malloc(lpFrameHeader->Height * sizeof(LPBYTE)); + lpRowBuf = (LPBYTE)malloc(lpFrameHeader->Width * 2); + nLastOffset = lpFrameHeader->Height * sizeof(WORD); + + for (y = 0; y < lpFrameHeader->Height; y++) { + i = nFrame * lpGrpHeader->wMaxWidth * lpGrpHeader->wMaxHeight + (lpFrameHeader->Top + y) * lpGrpHeader->wMaxWidth; + + /* Fails verification when using this. Bug in encoder or decoder? Doesn't provide much benefit. + // Search for duplicate rows (experimental) + for (x = 0; x < y; x++) { + j = nFrame * lpGrpHeader->wMaxWidth * lpGrpHeader->wMaxHeight + (lpFrameHeader->Top + x) * lpGrpHeader->wMaxWidth; + if (memcmp(&lpImageData[i+lpFrameHeader->Left], + &lpImageData[j+lpFrameHeader->Left], + lpGrpHeader->wMaxWidth * sizeof(short)) == 0) + break; + } + + if (x < y) { + lpFrameData->lpRowOffsets[y] = lpFrameData->lpRowOffsets[x]; + lpFrameData->lpRowSizes[y] = 0; + lpFrameData->lpRowData[y] = 0; + +#ifdef _DEBUG + if (!VerifyRow(&lpImageData[i+lpFrameHeader->Left], lpFrameHeader->Width, lpFrameData->lpRowData[x], lpFrameData->lpRowSizes[x])) { + nBufPos = nBufPos; + } +#endif + + continue; + } + */ + + nBufPos = 0; + if (lpFrameHeader->Width > 0) { + for (x = lpFrameHeader->Left; x < lpFrameHeader->Left + lpFrameHeader->Width; x++) { + if (x < lpFrameHeader->Left + lpFrameHeader->Width - 1) { + if (lpImageData[i+x] < 0) { + lpRowBuf[nBufPos] = 0x80; + for (; lpImageData[i+x] < 0 && x < lpFrameHeader->Left + lpFrameHeader->Width && lpRowBuf[nBufPos] < 0xFF; x++) { + lpRowBuf[nBufPos]++; + } + x--; + if (nLastOffset + nBufPos + 1 <= 0xFFFF) + nBufPos++; + continue; + } + + // Count repeating pixels, nRepeat = number of pixels - 1, ignore if there are less than 4 duplicates + for (nRepeat = 0; lpImageData[i+x+nRepeat] == lpImageData[i+x+nRepeat+1] && x+nRepeat < lpFrameHeader->Left + lpFrameHeader->Width - 1 && nRepeat < 0x3E; nRepeat++) {} + + if (nRepeat > 2) { + lpRowBuf[nBufPos] = 0x41 + nRepeat; + lpRowBuf[nBufPos+1] = (BYTE)lpImageData[i+x]; + x += nRepeat; + if (nLastOffset + nBufPos + 2 <= 0xFFFF) + nBufPos += 2; + } + else { + lpRowBuf[nBufPos] = 0; + for (; lpImageData[i+x] >= 0 && x < lpFrameHeader->Left + lpFrameHeader->Width && lpRowBuf[nBufPos] < 0x3F; x++) { + // Count repeating pixels, ignore if there are less than 4 duplicates + for (nRepeat = 0; lpImageData[i+x+nRepeat] == lpImageData[i+x+nRepeat+1] && x+nRepeat < lpFrameHeader->Left + lpFrameHeader->Width - 1 && nRepeat < 3; nRepeat++) {} + if (nRepeat > 2) break; + + lpRowBuf[nBufPos]++; + lpRowBuf[nBufPos+lpRowBuf[nBufPos]] = (BYTE)lpImageData[i+x]; + } + if (lpImageData[i+x] >= 0 && x == lpFrameHeader->Left + lpFrameHeader->Width - 1 && lpRowBuf[nBufPos] < 0x3F) { + lpRowBuf[nBufPos]++; + lpRowBuf[nBufPos+lpRowBuf[nBufPos]] = (BYTE)lpImageData[i+x]; + } + x--; + if (nLastOffset + nBufPos + 1 + lpRowBuf[nBufPos] <= 0xFFFF) + nBufPos += 1 + lpRowBuf[nBufPos]; + } + } + else { + if (lpImageData[i+x] < 0) { + lpRowBuf[nBufPos] = 0x81; + if (nLastOffset + nBufPos + 1 <= 0xFFFF) + nBufPos++; + } + else { + lpRowBuf[nBufPos] = 1; + lpRowBuf[nBufPos+1] = (BYTE)lpImageData[i+x]; + if (nLastOffset + nBufPos + 2 <= 0xFFFF) + nBufPos += 2; + } + } + } + } + +#ifdef _DEBUG + if (!VerifyRow(&lpImageData[i+lpFrameHeader->Left], lpFrameHeader->Width, lpRowBuf, nBufPos)) { + nBufPos = nBufPos; + } +#endif + + if (y == 0) { + lpFrameData->lpRowOffsets[y] = lpFrameHeader->Height * sizeof(WORD); + } + else { + lpFrameData->lpRowOffsets[y] = nLastOffset; + } + + nLastOffset = lpFrameData->lpRowOffsets[y] + nBufPos; + + lpFrameData->lpRowSizes[y] = nBufPos; + lpFrameData->lpRowData[y] = (LPBYTE)malloc(nBufPos); + memcpy(lpFrameData->lpRowData[y], lpRowBuf, nBufPos); + } + + free(lpRowBuf); +} + +#ifdef _DEBUG +BOOL VerifyRow(signed short *lpRawRow, int nWidth, LPBYTE lpEncRow, int nSize) +{ + int i,x=0,ofs=0; + while (x < nWidth && ofs < nSize) { + if (!(lpEncRow[ofs] & 0x80)) { + if (!(lpEncRow[ofs] & 0x40)) { + for (i=1;i<=lpEncRow[ofs] && x= 0) return FALSE; + } + x+=lpEncRow[ofs]-128; + ofs++; + } + } + + if (x != nWidth || ofs != nSize) return FALSE; + + return TRUE; +} +#endif