986 lines
25 KiB
C++
986 lines
25 KiB
C++
// 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 ////////////////
|
|
|
|
|
|
|
|
|