Initial commit.
This commit is contained in:
985
tools/ModView/image.cpp
Normal file
985
tools/ModView/image.cpp
Normal file
@@ -0,0 +1,985 @@
|
||||
// Filename:- image.cpp
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "includes.h"
|
||||
#include "modview.h"
|
||||
#include "textures.h"
|
||||
//
|
||||
#include "image.h"
|
||||
|
||||
#ifndef DWORD
|
||||
#define DWORD unsigned long
|
||||
#endif
|
||||
|
||||
|
||||
#define iDSTAMP_CELL_PIXDIM 16 // adjust this, higher = more secure but less stamps on picture
|
||||
#define iDSTAMP_CELL_PIXELS (iDSTAMP_CELL_PIXDIM * iDSTAMP_CELL_PIXDIM)
|
||||
#define iDSTAMP_INSTANCE_CELLDIM 8 // leave this alone at all times!
|
||||
#define iDSTAMP_INSTANCE_PIXDIM (iDSTAMP_INSTANCE_CELLDIM * iDSTAMP_CELL_PIXDIM)
|
||||
#define iDSTAMP_CHAR_BITS 7
|
||||
|
||||
|
||||
|
||||
#define iHIDDENBIT 1//32 // 1,2,4,8,16,32,64, or 128
|
||||
#define iHIDDENBITSHIFT 0//5 // 0,1,2,3, 4 5 6 7
|
||||
|
||||
#pragma pack(push,1)
|
||||
typedef struct
|
||||
{
|
||||
char sHDR[3]; // "HDR", no trailing zero
|
||||
char sDDMMYY[6]; // date/month/year, no trailing zero
|
||||
char sText[14]; // text, no end trailing zero, but maybe early one
|
||||
} WatermarkData_t;
|
||||
typedef struct
|
||||
{
|
||||
WatermarkData_t Data;
|
||||
DWORD dwCRC;
|
||||
} Watermark_t;
|
||||
typedef struct
|
||||
{
|
||||
byte R,G,B,A;
|
||||
}Pixel_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
byte *pPixels;
|
||||
int iWidth;
|
||||
int iHeight;
|
||||
int iPlanes;
|
||||
} DStampImage_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DStampImage_t *pImage;
|
||||
Watermark_t *pWatermark;
|
||||
} DStampData_t;
|
||||
|
||||
Watermark_t TheWatermark;
|
||||
|
||||
#define PixelSetR(p,r) p.R=r
|
||||
#define PixelSetG(p,g) p.G=g
|
||||
#define PixelSetB(p,b) p.B=b
|
||||
#define PixelSetA(p,a) p.A=a
|
||||
|
||||
#define PixelGetR(p) p.R
|
||||
#define PixelGetG(p) p.G
|
||||
#define PixelGetB(p) p.B
|
||||
#define PixelGetA(p) p.A
|
||||
|
||||
#define PixelInit(p) memset(&p,0,sizeof(p)); // later on I'll adapt this for initing default alpha etc, but for now
|
||||
|
||||
|
||||
|
||||
#define PROGRESS_INIT \
|
||||
CProgressCtrl *pProgress = NULL; \
|
||||
if (((CModViewApp*)AfxGetApp())->m_pMainWnd) \
|
||||
{ \
|
||||
pProgress = new CProgressCtrl; \
|
||||
bool bOK = !!pProgress->Create( WS_CHILD|WS_VISIBLE|PBS_SMOOTH, \
|
||||
CRect(100,100,200,200), \
|
||||
((CModViewApp*)AfxGetApp())->m_pMainWnd, \
|
||||
1 \
|
||||
); \
|
||||
if (!bOK) \
|
||||
{ \
|
||||
delete pProgress; \
|
||||
pProgress = NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define PROGRESS_SETRANGE(range) \
|
||||
if (pProgress) \
|
||||
{ \
|
||||
pProgress->SetRange(0,range); \
|
||||
}
|
||||
|
||||
#define PROGRESS_SETPOS(position) \
|
||||
if (pProgress) \
|
||||
{ \
|
||||
pProgress->SetPos(position);\
|
||||
}
|
||||
|
||||
#define PROGRESS_CLOSE \
|
||||
if (pProgress) \
|
||||
{ \
|
||||
delete pProgress; \
|
||||
pProgress = 0; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// return NULL = legal, else string saying why not...
|
||||
//
|
||||
static LPCSTR DStamp_TextIsLegal(LPCSTR psText)
|
||||
{
|
||||
int iLen = strlen(psText);
|
||||
if (iLen > sizeof(TheWatermark.Data.sText))
|
||||
{
|
||||
return va("\"%s\" is too long by %d chars",psText, sizeof(TheWatermark.Data.sText) - iLen);
|
||||
}
|
||||
|
||||
for (int i=0; i<iLen; i++)
|
||||
{
|
||||
if (psText[i] > 127)
|
||||
{
|
||||
return va("\"%s\" contains hi-ascii char '%c', only 7-bit ascii allowed",psText, psText[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void DStamp_EncryptData(void *pvData, int iByteLength)
|
||||
{
|
||||
/* don't use this method since we need to keep as 7-bit ascii
|
||||
byte *pData = (byte*) pvData;
|
||||
|
||||
for (int i=0; i<iByteLength; i++)
|
||||
{
|
||||
*pData = *pData ^ ('#'+i);
|
||||
pData++;
|
||||
}
|
||||
*/
|
||||
}
|
||||
// same as encrypt at the moment, because of XOR, but may change...
|
||||
//
|
||||
static void DStamp_DecryptData(void *pvData, int iByteLength)
|
||||
{
|
||||
DStamp_EncryptData(pvData,iByteLength);
|
||||
}
|
||||
|
||||
static DWORD DStamp_UpdateCRC(DWORD dwCRC, byte b)
|
||||
{
|
||||
dwCRC += (DWORD) b;
|
||||
|
||||
if (dwCRC & (1<<31))
|
||||
{
|
||||
dwCRC<<=1;
|
||||
dwCRC+=2; // add 1, plus carry
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCRC<<=1;
|
||||
dwCRC++; // add 1, no carry
|
||||
}
|
||||
|
||||
return dwCRC;
|
||||
}
|
||||
|
||||
static DWORD DStamp_CalcBlockCRC(void *pvData, int iByteLength)
|
||||
{
|
||||
byte *pData = (byte*) pvData;
|
||||
|
||||
DWORD dwCRC = 0;
|
||||
|
||||
for (int i=0; i<iByteLength; i++)
|
||||
{
|
||||
dwCRC = DStamp_UpdateCRC(dwCRC, pData[i]);
|
||||
}
|
||||
|
||||
dwCRC &= 0x7F7F7F7F; // lose 7th bit from each byte
|
||||
return dwCRC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static LPCSTR DStamp_GetYear(void)
|
||||
{
|
||||
static char sTemp[20];
|
||||
time_t ltime;
|
||||
|
||||
time( <ime );
|
||||
|
||||
struct tm *today = localtime( <ime );
|
||||
|
||||
strftime( sTemp, sizeof(sTemp), "%d%m%y", today );
|
||||
|
||||
return &sTemp[0];
|
||||
}
|
||||
|
||||
|
||||
static Watermark_t *DStamp_InitWatermark(LPCSTR psText)
|
||||
{
|
||||
Watermark_t *pWatermark = &TheWatermark;
|
||||
|
||||
strncpy(pWatermark->Data.sHDR, "HDR", sizeof(pWatermark->Data.sHDR));
|
||||
strncpy(pWatermark->Data.sText, psText, sizeof(pWatermark->Data.sText));
|
||||
strncpy(pWatermark->Data.sDDMMYY, DStamp_GetYear(), sizeof(pWatermark->Data.sDDMMYY));
|
||||
|
||||
DStamp_EncryptData(&pWatermark->Data, sizeof(pWatermark->Data));
|
||||
pWatermark->dwCRC = DStamp_CalcBlockCRC(&pWatermark->Data, sizeof(pWatermark->Data));
|
||||
|
||||
return pWatermark;
|
||||
}
|
||||
|
||||
|
||||
static Pixel_t *DStamp_GetImagePixel(DStampImage_t *pImage, int iXpos, int iYpos)
|
||||
{
|
||||
int iBPP = (pImage->iPlanes == 24)?3:4;
|
||||
|
||||
byte *pPixels = pImage->pPixels + (iYpos * pImage->iWidth * iBPP) + (iXpos * iBPP);
|
||||
|
||||
static Pixel_t Pixel;
|
||||
memcpy(&Pixel, pPixels, iBPP);
|
||||
if (iBPP == 3)
|
||||
{
|
||||
Pixel.A = 255; // actually, this can be ignored
|
||||
}
|
||||
|
||||
return &Pixel;
|
||||
}
|
||||
|
||||
static void DStamp_SetImagePixel(DStampImage_t *pImage, int iXpos, int iYpos, Pixel_t *pPixel)
|
||||
{
|
||||
int iBPP = (pImage->iPlanes == 24)?3:4;
|
||||
|
||||
byte *pPixels = pImage->pPixels + (iYpos * pImage->iWidth * iBPP) + (iXpos * iBPP);
|
||||
|
||||
memcpy(pPixels, pPixel, iBPP);
|
||||
}
|
||||
|
||||
// returns either 0 or 1 for bits, or 2 for "finished"
|
||||
//
|
||||
static int DStamp_GetWaterMarkBit(int *piSourceIndex_Byte, int *piSourceIndex_Bit, void *pvBytes, int iBytesLen)
|
||||
{
|
||||
byte *pBytes = (byte *) pvBytes;
|
||||
// if (*piSourceIndex_Byte==(int)strlen(psWatermarkString)+1) // trying to get bits from beyond trailing zero?
|
||||
// return 2;
|
||||
if (*piSourceIndex_Byte==iBytesLen) // trying to get bits from beyond trailing zero?
|
||||
return 2;
|
||||
|
||||
char c = pBytes[*piSourceIndex_Byte];
|
||||
|
||||
int iBitReturn = (c>>*piSourceIndex_Bit)&1;
|
||||
*piSourceIndex_Bit+=1; // *not* ++!
|
||||
if (*piSourceIndex_Bit == iDSTAMP_CHAR_BITS)
|
||||
{
|
||||
*piSourceIndex_Bit = 0;
|
||||
*piSourceIndex_Byte+=1; // *not* ++!
|
||||
}
|
||||
|
||||
return iBitReturn;
|
||||
}
|
||||
|
||||
|
||||
static void DStamp_ReadCell(DStampData_t *pData, int iCellXpos, int iCellYpos, byte *b1, byte *b2, byte *b3)
|
||||
{
|
||||
int iWBitR = 0,
|
||||
iWBitG = 0,
|
||||
iWBitB = 0;
|
||||
|
||||
for (int iPixelY=0; iPixelY<iDSTAMP_CELL_PIXDIM; iPixelY++)
|
||||
{
|
||||
for (int iPixelX=0; iPixelX<iDSTAMP_CELL_PIXDIM; iPixelX++)
|
||||
{
|
||||
Pixel_t Pixel = *DStamp_GetImagePixel(pData->pImage, iPixelX + iCellXpos, iPixelY + iCellYpos);
|
||||
|
||||
iWBitR += (PixelGetR(Pixel)&iHIDDENBIT)?1:0;
|
||||
iWBitG += (PixelGetG(Pixel)&iHIDDENBIT)?1:0;
|
||||
iWBitB += (PixelGetB(Pixel)&iHIDDENBIT)?1:0;
|
||||
}
|
||||
}
|
||||
|
||||
// work out pixel consensuses... (consensi?)
|
||||
//
|
||||
// if > half the pixels are true, accept that as true...
|
||||
//
|
||||
*b1 = (iWBitR > iDSTAMP_CELL_PIXELS/2)?1:0;
|
||||
*b2 = (iWBitG > iDSTAMP_CELL_PIXELS/2)?1:0;
|
||||
*b3 = (iWBitB > iDSTAMP_CELL_PIXELS/2)?1:0;
|
||||
}
|
||||
|
||||
static void DStamp_MarkCell(DStampData_t *pData, int iCellXpos, int iCellYpos, int *piTxtByte, int *piTxtBit)
|
||||
{
|
||||
byte R = DStamp_GetWaterMarkBit(piTxtByte, piTxtBit, pData->pWatermark, sizeof(*pData->pWatermark));
|
||||
byte G = DStamp_GetWaterMarkBit(piTxtByte, piTxtBit, pData->pWatermark, sizeof(*pData->pWatermark));
|
||||
byte B = DStamp_GetWaterMarkBit(piTxtByte, piTxtBit, pData->pWatermark, sizeof(*pData->pWatermark));
|
||||
|
||||
for (int iPixelY=0; iPixelY<iDSTAMP_CELL_PIXDIM; iPixelY++)
|
||||
{
|
||||
for (int iPixelX=0; iPixelX<iDSTAMP_CELL_PIXDIM; iPixelX++)
|
||||
{
|
||||
Pixel_t Pixel = *DStamp_GetImagePixel(pData->pImage, iPixelX + iCellXpos, iPixelY + iCellYpos);
|
||||
|
||||
PixelSetR(Pixel,( (PixelGetR(Pixel)&~iHIDDENBIT) | (((R==2)?0:R)<<iHIDDENBITSHIFT) ));
|
||||
PixelSetG(Pixel,( (PixelGetG(Pixel)&~iHIDDENBIT) | (((G==2)?0:G)<<iHIDDENBITSHIFT) ));
|
||||
PixelSetB(Pixel,( (PixelGetB(Pixel)&~iHIDDENBIT) | (((B==2)?0:B)<<iHIDDENBITSHIFT) ));
|
||||
|
||||
DStamp_SetImagePixel(pData->pImage, iPixelX + iCellXpos, iPixelY + iCellYpos, &Pixel);
|
||||
}
|
||||
}
|
||||
|
||||
/* byte R2,G2,B2;
|
||||
DStamp_ReadCell(pData, iCellXpos, iCellYpos, &R2,&G2,&B2);
|
||||
|
||||
assert( (R2==((R==2)?0:R)) &&
|
||||
(G2==((G==2)?0:G)) &&
|
||||
(B2==((B==2)?0:B))
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
// apply one watermark-instance to the image...
|
||||
//
|
||||
static void DStamp_MarkInstance(DStampData_t *pData, int iInstancePixelX, int iInstancePixelY)
|
||||
{
|
||||
int iTxtByte = 0;
|
||||
int iTxtBit = 0;
|
||||
|
||||
for (int iYCell = 0; iYCell < iDSTAMP_INSTANCE_CELLDIM; iYCell++)
|
||||
{
|
||||
for (int iXCell = 0; iXCell < iDSTAMP_INSTANCE_CELLDIM; iXCell++)
|
||||
{
|
||||
int iCellXpos = iInstancePixelX + (iXCell * iDSTAMP_CELL_PIXDIM);
|
||||
int iCellYpos = iInstancePixelY + (iYCell * iDSTAMP_CELL_PIXDIM);
|
||||
DStamp_MarkCell(pData, iCellXpos, iCellYpos, &iTxtByte, &iTxtBit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// see if we can read some watermark data, else return NULL...
|
||||
//
|
||||
static LPCSTR DStamp_ReadInstance(DStampData_t *pData, int iInstancePixelX, int iInstancePixelY)
|
||||
{
|
||||
LPCSTR psMessage = NULL;
|
||||
|
||||
int iTxtByte = 0;
|
||||
int iTxtBit = 0;
|
||||
|
||||
byte *pbOut = (byte*) pData->pWatermark;
|
||||
memset(pbOut,0,sizeof(*pData->pWatermark));
|
||||
|
||||
for (int iYCell = 0; iYCell < iDSTAMP_INSTANCE_CELLDIM; iYCell++)
|
||||
{
|
||||
for (int iXCell = 0; iXCell < iDSTAMP_INSTANCE_CELLDIM; iXCell++)
|
||||
{
|
||||
int iCellXpos = iInstancePixelX + (iXCell * iDSTAMP_CELL_PIXDIM);
|
||||
int iCellYpos = iInstancePixelY + (iYCell * iDSTAMP_CELL_PIXDIM);
|
||||
|
||||
byte b1,b2,b3;
|
||||
DStamp_ReadCell(pData, iCellXpos, iCellYpos, &b1,&b2,&b3);
|
||||
|
||||
#define DECODEBIT(DestString,bit) \
|
||||
DestString[iTxtByte] |= bit<<iTxtBit++; \
|
||||
if (iTxtBit==iDSTAMP_CHAR_BITS) \
|
||||
{ \
|
||||
iTxtBit=0; \
|
||||
iTxtByte++; \
|
||||
}
|
||||
|
||||
DECODEBIT(pbOut,b1);
|
||||
DECODEBIT(pbOut,b2);
|
||||
DECODEBIT(pbOut,b3);
|
||||
|
||||
if (iTxtByte>=3) // huge speed opt, check for header, if not found, give up on this cell...
|
||||
{
|
||||
if (strncmp((char*)pbOut,"HDR",3))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncmp(pData->pWatermark->Data.sHDR,"HDR",3))
|
||||
{
|
||||
DWORD dwCRC = DStamp_CalcBlockCRC(&pData->pWatermark->Data, sizeof(pData->pWatermark->Data));
|
||||
|
||||
char sString[100];
|
||||
char sDate[100];
|
||||
strncpy(sString,pData->pWatermark->Data.sText,sizeof(pData->pWatermark->Data.sText));
|
||||
sString[sizeof(pData->pWatermark->Data.sText)] = '\0';
|
||||
strncpy(sDate,pData->pWatermark->Data.sDDMMYY,sizeof(pData->pWatermark->Data.sDDMMYY));
|
||||
sDate[sizeof(pData->pWatermark->Data.sDDMMYY)] = '\0';
|
||||
static char sOutput[1024];
|
||||
sprintf(sOutput,"SentTo: \"%s\", Date(DD/MM/YY) = %s",sString,sDate);
|
||||
|
||||
if (dwCRC == pData->pWatermark->dwCRC)
|
||||
{
|
||||
OutputDebugString(sOutput);
|
||||
psMessage = &sOutput[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugString(va("Skipping non-CRC HDR-match: %s\n",sOutput));
|
||||
}
|
||||
}
|
||||
|
||||
return psMessage;
|
||||
}
|
||||
|
||||
// return is NULL for ok, else error string...
|
||||
//
|
||||
LPCSTR DStamp_MarkImage(byte *pPixels, int iWidth, int iHeight, int iPlanes, LPCSTR psText)
|
||||
{
|
||||
LPCSTR psError = NULL;
|
||||
|
||||
if (iPlanes == 24 || iPlanes == 32)
|
||||
{
|
||||
psError = DStamp_TextIsLegal(psText);
|
||||
|
||||
if (!psError)
|
||||
{
|
||||
DStampImage_t Image;
|
||||
Image.pPixels = pPixels;
|
||||
Image.iWidth = iWidth;
|
||||
Image.iHeight = iHeight;
|
||||
Image.iPlanes = iPlanes;
|
||||
|
||||
DStampData_t Data;
|
||||
Data.pImage = &Image;
|
||||
Data.pWatermark = DStamp_InitWatermark(psText);
|
||||
|
||||
int iInstances_Across = Data.pImage->iWidth / iDSTAMP_INSTANCE_PIXDIM;
|
||||
int iInstances_Down = Data.pImage->iHeight / iDSTAMP_INSTANCE_PIXDIM;
|
||||
|
||||
int iInstancesTotal = iInstances_Across * iInstances_Down;
|
||||
#ifdef _DEBUG
|
||||
int iDebug_WaterMarkSize = sizeof(Watermark_t);
|
||||
|
||||
OutputDebugString(va("%d stamp instances on screen\n",iInstancesTotal));
|
||||
#endif
|
||||
|
||||
if (iInstancesTotal)
|
||||
{
|
||||
// center the stamp grid within the image...
|
||||
//
|
||||
int iYStart = (Data.pImage->iHeight - (iInstances_Down * iDSTAMP_INSTANCE_PIXDIM))/2;
|
||||
int iXStart = (Data.pImage->iWidth - (iInstances_Across * iDSTAMP_INSTANCE_PIXDIM))/2;
|
||||
|
||||
for (int iInstanceY = 0; iInstanceY < iInstances_Down; iInstanceY++)
|
||||
{
|
||||
for (int iInstanceX = 0; iInstanceX < iInstances_Across; iInstanceX++)
|
||||
{
|
||||
int iInstancePixelX = (iInstanceX * iDSTAMP_INSTANCE_PIXDIM) + iXStart;
|
||||
int iInstancePixelY = (iInstanceY * iDSTAMP_INSTANCE_PIXDIM) + iYStart;
|
||||
|
||||
DStamp_MarkInstance(&Data, iInstancePixelX, iInstancePixelY);
|
||||
// LPCSTR psMessage = DStamp_ReadInstance(&Data, iInstancePixelX, iInstancePixelY);
|
||||
// if (psMessage)
|
||||
// {
|
||||
// OutputDebugString(va("Marked Instance with message \"%s\"\n",psMessage));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// int z=1;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
psError = va("DStamp_MarkImage(): Unable to fit watermark on screen using iDSTAMP_CELL_PIXDIM of %d!",iDSTAMP_CELL_PIXDIM);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
psError = "DStamp_MarkImage(): Supplied image must be 24 or 32 bit";
|
||||
}
|
||||
|
||||
return psError;
|
||||
}
|
||||
|
||||
bool DStamp_MarkImage(Texture_t *pTexture, LPCSTR psText)
|
||||
{
|
||||
LPCSTR psError = DStamp_MarkImage(pTexture->pPixels, pTexture->iWidth, pTexture->iHeight, 32, psText);
|
||||
|
||||
if (!psError)
|
||||
return true;
|
||||
|
||||
ErrorBox(psError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void DStamp_AnalyseImage(byte *pPixels, int iWidth, int iHeight, int iPlanes)
|
||||
{
|
||||
CWaitCursor wait;
|
||||
|
||||
const int iBlockSampleSize = 8;
|
||||
|
||||
PROGRESS_INIT;
|
||||
PROGRESS_SETRANGE((iHeight/iBlockSampleSize));
|
||||
|
||||
DStampImage_t Image;
|
||||
Image.pPixels = pPixels;
|
||||
Image.iWidth = iWidth;
|
||||
Image.iHeight = iHeight;
|
||||
Image.iPlanes = iPlanes;
|
||||
|
||||
DStampImage_t ImageOut;
|
||||
ImageOut.pPixels = (byte *) malloc (iWidth * iHeight * ((iPlanes == 24)?3:4));
|
||||
ImageOut.iWidth = iWidth;
|
||||
ImageOut.iHeight = iHeight;
|
||||
ImageOut.iPlanes = iPlanes;
|
||||
|
||||
|
||||
/*
|
||||
// new line dup code...
|
||||
//
|
||||
for (int y=0; y<iHeight-3; y+=30)
|
||||
{
|
||||
for (int x=0; x<iWidth; x++)
|
||||
{
|
||||
Pixel_t *pPixel = DStamp_GetImagePixel(&Image, x, y);
|
||||
DStamp_SetImagePixel(&Image, x, y+1, pPixel);
|
||||
DStamp_SetImagePixel(&Image, x, y+2, pPixel);
|
||||
DStamp_SetImagePixel(&Image, x, y+3, pPixel);
|
||||
}
|
||||
}
|
||||
*/
|
||||
#if 1
|
||||
for (int y = 0; y+iBlockSampleSize < iHeight; y += iBlockSampleSize)
|
||||
{
|
||||
PROGRESS_SETPOS(y);
|
||||
for (int x = 0; x+iBlockSampleSize < iWidth; x += iBlockSampleSize)
|
||||
{
|
||||
unsigned int r=0,g=0,b=0;
|
||||
unsigned int rh=0,gh=0,bh=0;
|
||||
unsigned int rl=INT_MAX,gl=INT_MAX,bl=INT_MAX;
|
||||
|
||||
if (x==120 && y==232)
|
||||
{
|
||||
int z=1;
|
||||
}
|
||||
|
||||
int iNoisyPixels = 0;
|
||||
int iMassivelyDeviantPixels = 0;
|
||||
for (int iBlockPass=0; iBlockPass<3; iBlockPass++)
|
||||
{
|
||||
// pass 0 = read pixels, average out channels
|
||||
// pass 1 = count noisy pixels
|
||||
// pass 2 = colour in block as 0 or 255 to show noise if > half blocks noisy
|
||||
//
|
||||
for (int by=0; by < iBlockSampleSize; by++)
|
||||
{
|
||||
for (int bx=0; bx < iBlockSampleSize; bx++)
|
||||
{
|
||||
int px = x+bx;
|
||||
int py = y+by;
|
||||
Pixel_t *pPixel = DStamp_GetImagePixel(&Image, px, py);
|
||||
|
||||
switch (iBlockPass)
|
||||
{
|
||||
case 0: // read/accumulate pixel values...
|
||||
{
|
||||
r += pPixel->R;
|
||||
g += pPixel->G;
|
||||
b += pPixel->B;
|
||||
|
||||
rh = max(rh,pPixel->R);
|
||||
gh = max(gh,pPixel->G);
|
||||
bh = max(bh,pPixel->B);
|
||||
|
||||
rl = min(rl,pPixel->R);
|
||||
gl = min(gl,pPixel->G);
|
||||
bl = min(bl,pPixel->B);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // analyse...
|
||||
{
|
||||
|
||||
if (rh-rl>90 ||
|
||||
gh-gl>90 ||
|
||||
bh-bl>90
|
||||
)
|
||||
{
|
||||
iMassivelyDeviantPixels++;
|
||||
}
|
||||
|
||||
if (px>0 && px<iWidth-1 && py>0 && py<iHeight-1)
|
||||
{
|
||||
int this_r = pPixel->R;
|
||||
int this_g = pPixel->G;
|
||||
int this_b = pPixel->B;
|
||||
|
||||
bool bPixelIsNoisy = false;
|
||||
|
||||
if (px==123 && py==235)
|
||||
{
|
||||
int z=1;
|
||||
}
|
||||
|
||||
for (int y2=py-1; y2<py+2 && !bPixelIsNoisy; y2++)
|
||||
{
|
||||
for (int x2=px-1; x2<px+2 && !bPixelIsNoisy; x2++)
|
||||
{
|
||||
if (x2!=px&&y2!=py)
|
||||
{
|
||||
pPixel = DStamp_GetImagePixel(&Image, x2, y2);
|
||||
|
||||
int rdiff = abs(pPixel->R - this_r);
|
||||
int gdiff = abs(pPixel->G - this_g);
|
||||
int bdiff = abs(pPixel->B - this_b);
|
||||
|
||||
const int iMinTolerance = 7;
|
||||
const int iMaxTolerance = 50;
|
||||
|
||||
if ((rdiff > iMaxTolerance) ||
|
||||
(gdiff > iMaxTolerance) ||
|
||||
(bdiff > iMaxTolerance) ||
|
||||
abs(pPixel->R - r) > iMaxTolerance ||
|
||||
abs(pPixel->G - g) > iMaxTolerance ||
|
||||
abs(pPixel->B - b) > iMaxTolerance
|
||||
)
|
||||
{
|
||||
//iMassivelyDeviantPixels++;
|
||||
iNoisyPixels-=5000; // force not to be noisy for blocks with sharp contrasts
|
||||
}
|
||||
else
|
||||
if ((rdiff > iMinTolerance && rdiff < iMaxTolerance) ||
|
||||
(gdiff > iMinTolerance && gdiff < iMaxTolerance) ||
|
||||
(bdiff > iMinTolerance && bdiff < iMaxTolerance)
|
||||
)
|
||||
{
|
||||
bPixelIsNoisy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bPixelIsNoisy)
|
||||
{
|
||||
iNoisyPixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // paint...
|
||||
{
|
||||
pPixel->R = pPixel->G = pPixel->B = (iNoisyPixels>((iBlockSampleSize*iBlockSampleSize)/2))?255:0;
|
||||
|
||||
DStamp_SetImagePixel(&ImageOut, x+bx, y+by, pPixel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (iBlockPass)
|
||||
{
|
||||
case 0:
|
||||
r /= iBlockSampleSize*iBlockSampleSize;
|
||||
g /= iBlockSampleSize*iBlockSampleSize;
|
||||
b /= iBlockSampleSize*iBlockSampleSize;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (iMassivelyDeviantPixels > (iBlockSampleSize*iBlockSampleSize)/8)
|
||||
{
|
||||
// if > 1/8th of the pixels are massively deviant, then set this block so we can't use it.
|
||||
iNoisyPixels-=5000; // force not to be noisy for blocks with sharp contrasts
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
// copy analysis results over input picture for saving on return...
|
||||
//
|
||||
DStampImage_t ImageTmp;
|
||||
ImageTmp.pPixels = (byte *) malloc (iWidth * iHeight * ((iPlanes == 24)?3:4));
|
||||
ImageTmp.iWidth = iWidth;
|
||||
ImageTmp.iHeight = iHeight;
|
||||
ImageTmp.iPlanes = iPlanes;
|
||||
|
||||
memcpy(ImageTmp.pPixels,ImageOut.pPixels, iWidth * iHeight * ((iPlanes == 24)?3:4));
|
||||
|
||||
// now eliminate any single white or black sampleblocks...
|
||||
//
|
||||
// for (int iEliminate = 0; iEliminate<2; iEliminate++)
|
||||
{
|
||||
// start one-in from all edges, so we can check 8 surrounding squares safely...
|
||||
//
|
||||
for (y = 0+iBlockSampleSize; y + (2*iBlockSampleSize) < iHeight; y += iBlockSampleSize)
|
||||
{
|
||||
for (int x = 0+iBlockSampleSize; x + (2*iBlockSampleSize) < iWidth; x+= iBlockSampleSize)
|
||||
{
|
||||
unsigned int r=0,g=0,b=0;
|
||||
|
||||
Pixel_t *pPixel = DStamp_GetImagePixel(&ImageTmp, x, y);
|
||||
|
||||
bool bThisPixelWasBlack = !(pPixel->R); // all channels same, so just checking R is fine
|
||||
|
||||
//if (bThisPixelWasBlack)
|
||||
{
|
||||
// now check neighbours, all 8 surrounding blocks must be same colour, or bye-bye...
|
||||
//
|
||||
bool bSurroundedByOpposite = true;
|
||||
for (int y2=y-iBlockSampleSize; y2<y+(2*iBlockSampleSize) && bSurroundedByOpposite; y2+=iBlockSampleSize)
|
||||
{
|
||||
for (int x2=x-iBlockSampleSize; x2<x+(2*iBlockSampleSize) && bSurroundedByOpposite; x2+=iBlockSampleSize)
|
||||
{
|
||||
if (x2!=x && y2!=y)
|
||||
{
|
||||
Pixel_t *pPixel = DStamp_GetImagePixel(&ImageTmp, x2, y2);
|
||||
|
||||
bool bScanPixelWasBlack = !(pPixel->R);
|
||||
|
||||
if (bScanPixelWasBlack == bThisPixelWasBlack)
|
||||
{
|
||||
bSurroundedByOpposite = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bSurroundedByOpposite)
|
||||
{
|
||||
// blot this pixel out by toggling it to the other colour...
|
||||
//
|
||||
pPixel->R = pPixel->G = pPixel->B = bThisPixelWasBlack?255:0;
|
||||
|
||||
for (int py=y; py<y+iBlockSampleSize; py++)
|
||||
{
|
||||
for (int px=x; px<x+iBlockSampleSize; px++)
|
||||
{
|
||||
DStamp_SetImagePixel(&ImageOut, px, py, pPixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(ImageTmp.pPixels,ImageOut.pPixels, iWidth * iHeight * ((iPlanes == 24)?3:4));
|
||||
#endif
|
||||
|
||||
// now highlight original pic with differences...
|
||||
//
|
||||
for (y=0; y+iBlockSampleSize < iHeight; y+=iBlockSampleSize)
|
||||
{
|
||||
for (int x=0; x+iBlockSampleSize < iWidth; x+=iBlockSampleSize)
|
||||
{
|
||||
Pixel_t *pPixel = DStamp_GetImagePixel(&ImageTmp, x, y);
|
||||
|
||||
bool bThisPixelWasBlack = !(pPixel->R); // all channels same, so just checking R is fine
|
||||
|
||||
// bool bPrevPixelWasBlack = true;
|
||||
// if (x>=iBlockSampleSize)
|
||||
// {
|
||||
// pPixel = DStamp_GetImagePixel(&ImageTmp, x-iBlockSampleSize, y);
|
||||
// bPrevPixelWasBlack = !(pPixel->R); // all channels same, so just checking R is fine
|
||||
// }
|
||||
|
||||
if (!bThisPixelWasBlack )//&& !bPrevPixelWasBlack)
|
||||
{
|
||||
unsigned int r=0,g=0,b=0;
|
||||
for (int iPass=0; iPass<2; iPass++)
|
||||
{
|
||||
// highlight this sample square of the original pic...
|
||||
//
|
||||
for (int y2=y; y2<y+iBlockSampleSize; y2++)
|
||||
{
|
||||
for (int x2=x; x2<x+iBlockSampleSize; x2++)
|
||||
{
|
||||
if (!iPass)
|
||||
{
|
||||
pPixel = DStamp_GetImagePixel(&Image, x2, y2);
|
||||
r+= pPixel->R;
|
||||
g+= pPixel->G;
|
||||
b+= pPixel->B;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 1
|
||||
// highlight...
|
||||
//
|
||||
pPixel = DStamp_GetImagePixel(&Image, x2/*-iBlockSampleSize*/, y2);
|
||||
|
||||
pPixel->R |= 128;
|
||||
pPixel->G &=~128;
|
||||
pPixel->B &=~128;
|
||||
#else
|
||||
pPixel = DStamp_GetImagePixel(&Image, x2, y2);
|
||||
|
||||
pPixel->R = (r + pPixel->R)/2;
|
||||
pPixel->G = (g + pPixel->G)/2;
|
||||
pPixel->B = (b + pPixel->B)/2;
|
||||
#endif
|
||||
DStamp_SetImagePixel(&Image, x2, y2, pPixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!iPass)
|
||||
{
|
||||
pPixel->R = r = r/(iBlockSampleSize*iBlockSampleSize);
|
||||
pPixel->G = g = g/(iBlockSampleSize*iBlockSampleSize);
|
||||
pPixel->B = b = b/(iBlockSampleSize*iBlockSampleSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PROGRESS_CLOSE;
|
||||
|
||||
free(ImageTmp.pPixels);
|
||||
free(ImageOut.pPixels);
|
||||
}
|
||||
|
||||
void DStamp_AnalyseImage(Texture_t *pTexture)
|
||||
{
|
||||
DStamp_AnalyseImage(pTexture->pPixels, pTexture->iWidth, pTexture->iHeight, 32);
|
||||
}
|
||||
|
||||
LPCSTR DStamp_ReadImage(byte *pPixels, int iWidth, int iHeight, int iPlanes)
|
||||
{
|
||||
CWaitCursor wait;
|
||||
|
||||
LPCSTR psMessage = NULL;
|
||||
|
||||
if (iPlanes == 24 || iPlanes == 32)
|
||||
{
|
||||
DStampImage_t Image;
|
||||
Image.pPixels = pPixels;
|
||||
Image.iWidth = iWidth;
|
||||
Image.iHeight = iHeight;
|
||||
Image.iPlanes = iPlanes;
|
||||
|
||||
DStampData_t Data;
|
||||
Data.pImage = &Image;
|
||||
Data.pWatermark = &TheWatermark;
|
||||
|
||||
if (1)
|
||||
{
|
||||
|
||||
int iInstances_Across = Data.pImage->iWidth / iDSTAMP_INSTANCE_PIXDIM;
|
||||
int iInstances_Down = Data.pImage->iHeight / iDSTAMP_INSTANCE_PIXDIM;
|
||||
|
||||
// #ifdef _DEBUG
|
||||
// int iDebug_WaterMarkSize = sizeof(Watermark_t);
|
||||
// int iDebug_InstancesTotal = iInstances_Across * iInstances_Down;
|
||||
// #endif
|
||||
/*
|
||||
#define iDSTAMP_CELL_PIXDIM 16 // adjust this, higher = more secure but less stamps on picture
|
||||
#define iDSTAMP_CELL_PIXELS (iDSTAMP_CELL_PIXDIM * iDSTAMP_CELL_PIXDIM)
|
||||
#define iDSTAMP_INSTANCE_CELLDIM 8 // leave this alone at all times!
|
||||
#define iDSTAMP_INSTANCE_PIXDIM (iDSTAMP_INSTANCE_CELLDIM * iDSTAMP_CELL_PIXDIM)
|
||||
#define iDSTAMP_CHAR_BITS 7
|
||||
|
||||
*/
|
||||
CProgressCtrl *pProgress = NULL;
|
||||
|
||||
if (((CModViewApp*)AfxGetApp())->m_pMainWnd)
|
||||
{
|
||||
pProgress = new CProgressCtrl;
|
||||
bool bOK = !!pProgress->Create( WS_CHILD|WS_VISIBLE|PBS_SMOOTH, // DWORD dwStyle,
|
||||
CRect(100,100,200,200), // const RECT& rect,
|
||||
((CModViewApp*)AfxGetApp())->m_pMainWnd, // CWnd* pParentWnd,
|
||||
1 // UINT nID
|
||||
);
|
||||
if (!bOK)
|
||||
{
|
||||
delete pProgress;
|
||||
pProgress = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// the image may have been cropped, so we need to hunt for the signature at every pixel position...
|
||||
//
|
||||
int iYScans = 0;
|
||||
for (int iLazy = 0; iLazy<2; iLazy++)
|
||||
{
|
||||
for (int iYStart = 0; iYStart < iDSTAMP_INSTANCE_PIXDIM && !psMessage; iYStart++)
|
||||
{
|
||||
if (iLazy)
|
||||
{
|
||||
if (pProgress)
|
||||
{
|
||||
pProgress->SetRange(0,iYScans);
|
||||
}
|
||||
OutputDebugString(va("iYStart %%%d\n",(iYStart*100)/iDSTAMP_INSTANCE_PIXDIM));
|
||||
if (pProgress)
|
||||
{
|
||||
pProgress->SetPos(iYStart);
|
||||
// wait.Restore();
|
||||
}
|
||||
|
||||
for (int iXStart = 0; iXStart < iDSTAMP_INSTANCE_PIXDIM && !psMessage; iXStart++)
|
||||
{
|
||||
// look for some stamps here...
|
||||
//
|
||||
for (int iInstanceY = 0; iInstanceY < iInstances_Down && !psMessage; iInstanceY++)
|
||||
{
|
||||
for (int iInstanceX = 0; iInstanceX < iInstances_Across && !psMessage; iInstanceX++)
|
||||
{
|
||||
int iInstancePixelX = (iInstanceX * iDSTAMP_INSTANCE_PIXDIM) + iXStart;
|
||||
int iInstancePixelY = (iInstanceY * iDSTAMP_INSTANCE_PIXDIM) + iYStart;
|
||||
|
||||
if (iInstancePixelX + iDSTAMP_INSTANCE_PIXDIM >= iWidth
|
||||
||
|
||||
iInstancePixelY + iDSTAMP_INSTANCE_PIXDIM >= iHeight
|
||||
)
|
||||
{
|
||||
// ... then skip this position while hunting for header start
|
||||
}
|
||||
else
|
||||
{
|
||||
psMessage = DStamp_ReadInstance(&Data, iInstancePixelX, iInstancePixelY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int iInstanceY = 0; iInstanceY < iInstances_Down; iInstanceY++)
|
||||
{
|
||||
int iInstancePixelY = (iInstanceY * iDSTAMP_INSTANCE_PIXDIM) + iYStart;
|
||||
|
||||
if (iInstancePixelY + iDSTAMP_INSTANCE_PIXDIM >= iHeight)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
iYScans++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pProgress)
|
||||
{
|
||||
delete pProgress;
|
||||
pProgress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorBox("DStamp_ReadImage(): Supplied image must be 24 or 32 bit");
|
||||
}
|
||||
|
||||
return psMessage;
|
||||
}
|
||||
|
||||
|
||||
////////////////// eof ////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user