//////////////////////////////////////////////////////////////////////////
//
// Tga image translucency fixer
// Created by Wernaeh
//
//////////////////////////////////////////////////////////////////////////
// Windows stuff
#include "Windows.h"
// Files
#include <fstream>
// For shortening stuff, directly include
// the std namespace
using namespace std;
// The global exit flag for the message loop
bool bExit = false;
// The main programm window
HWND hMainWin = NULL;
// The name of the current targa file
char *pFileName = NULL;
// The current targa file data
char *pPixels = NULL;
int iResX = 0;
int iResY = 0;
char cBypp = 0;
// The default (supported) targa header
const char cTgaHeader[12] =
{ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// Present a message box with the specified text
// indicating that some problem occured.
void MsgBox(const char *text)
{
MessageBox(hMainWin, text, "Problem:",
MB_OK | MB_ICONEXCLAMATION);
}
// Load a targa file with the given file name
// This method returns false if the targa file can not
// be modified, but the error is ignorable. This includes
// invalid file types, or tga's with corrupt headers.
// If everything is ok with the file, the global image data
// vars are filled.
bool LoadTga()
{
ifstream file;
// Open the targa file
file.open(pFileName, ios::in | ios::binary | ios::_Nocreate);
if (!file)
{
MsgBox("Failed to open file for reading");
return false;
}
// Extract the file header
char header[12];
if (!file.read(header, sizeof(char) * 12))
{
MsgBox("Corrupt tga header");
return false;
}
// Only support uncompressed targas
if (memcmp(header, cTgaHeader, 12) != 0)
{
MsgBox("Unsupported targa header");
return false;
}
// Extract file resolution
short resx;
short resy;
if (!file.read((char*)&resx, sizeof(short)) ||
!file.read((char*)&resy, sizeof(short)))
{
MsgBox("Corrupt targa resolution");
return false;
}
// Check for valid resolution
if (resx <= 0 || resx > 2048 ||
resy <= 0 || resy > 2048)
{
MsgBox("Targa resolution not supported");
return false;
}
// Extract bitdepth
char bitdepth;
if (!file.read(&bitdepth, sizeof(char)))
{
MsgBox("Corrupt targa bitdepth");
return false;
}
// Only open targas with 24 or 32 bits per pixel
if (bitdepth != 32 && bitdepth != 24)
{
MsgBox("Targa bitdepth not supported");
return false;
}
// Jump over last part of header
file.seekg(sizeof(char), ios::cur);
// Apply image sizing and depth information
iResX = resx;
iResY = resy;
cBypp = bitdepth / 8;
// Reserve image memory
pPixels =
(char*)malloc(iResX * iResY * cBypp * sizeof(char));
if (!pPixels)
{
throw("Failed to allocate pixel memory");
}
// Load actual image from file
if (!file.read(pPixels, iResX * iResY * cBypp * sizeof(char)))
{
MsgBox("Unexpected end in image");
return false;
}
// Close file again
file.close();
return true;
}
// Process the targa image in memory
void ProcessImage()
{
// No need to process targa files without
// alpha channel
if (cBypp != 4)
{
return;
}
// Create process map
char *map =
(char*)malloc(iResX * iResY * sizeof(char));
if (!map)
{
throw("Failed to alloc temporary memory");
}
// Initialize process map
int pixel = 3;
for (int i = 0;
i < iResX * iResY;
++i)
{
if (pPixels[pixel] != 0)
{
map[i] = 0;
}
else
{
map[i] = 2;
}
pixel += 4;
}
// Correct pixels until each one has been
// treated.
bool anyfound;
do
{
anyfound = false;
// Set all pixels neighbouring to at least
// a single treated pixel to the average of
// all treated neighbours.
int index = 0;
for (int i = 0;
i < iResY;
++i)
{
for (int j = 0;
j < iResX;
++j)
{
// Only look at untreated pixels
if (map[index] == 2)
{
// Build coords for neighbor pixels
int xnext = j + 1;
if (xnext >= iResX)
{
xnext -= iResX;
}
int xprev = j - 1;
if (xprev < 0)
{
xprev += iResX;
}
// Y coords for neighbors
int ynext = i + 1;
if (ynext >= iResY)
{
ynext -= iResY;
}
int yprev = i - 1;
if (yprev < 0)
{
yprev += iResY;
}
// Build neighbour indices
int indices[8] =
{ ynext * iResX + xnext,
ynext * iResX + j,
ynext * iResX + xprev,
i * iResX + xnext,
i * iResX + xprev,
yprev * iResX + xnext,
yprev * iResX + j,
yprev * iResX + xprev };
// Temporary accumulators
int accumr = 0;
int accumg = 0;
int accumb = 0;
int accnt = 0;
// Accumulate all completed neighbors
for (int k = 0;
k < 8;
++k)
{
if (map[indices[k]] == 0)
{
accumr += pPixels[indices[k] * 4];
accumg += pPixels[indices[k] * 4 + 1];
accumb += pPixels[indices[k] * 4 + 2];
++accnt;
}
}
// Assign result of accumulation and update
// the process map
if (accnt != 0)
{
pPixels[index * 4] = accumr / accnt;
pPixels[index * 4 + 1] = accumg / accnt;
pPixels[index * 4 + 2] = accumb / accnt;
map[index] = 1;
}
}
++index;
}
}
// Mark all treated pixels as completed
// Also raise anyfound flag if any pixel
// was treated.
for (int i = 0;
i < iResX * iResY;
++i)
{
if (map[i] == 1)
{
map[i] = 0;
anyfound = true;
}
}
}
while (anyfound);
// Release process map
free(map);
}
// Write the targa file back to disk
void SaveTga()
{
ofstream file;
// Open the file for output
file.open(pFileName, ios::out | ios::binary | ios::trunc);
if (!file)
{
MsgBox("Failed to open file for saving");
return;
}
// Create some temporaries
char bitdepth = cBypp * 8;
char bituse = 0;
// Write out all targa image data
file.write(cTgaHeader, 12 * sizeof(char));
file.write((char*)&iResX, sizeof(short));
file.write((char*)&iResY, sizeof(short));
file.write(&bitdepth, sizeof(char));
file.write(&bituse, sizeof(char));
file.write(pPixels, iResX * iResY * cBypp * sizeof(char));
// Close file again
file.close();
}
// Release all image data
void ReleaseImage()
{
iResX = 0;
iResY = 0;
cBypp = 0;
if (pPixels)
{
free(pPixels);
pPixels = NULL;
}
}
// Process a single file dropped into the main window
void ProcessFile()
{
// Load the targa file
if (LoadTga())
{
// Fix the image data
ProcessImage();
// Write it back into the file
SaveTga();
}
// Release all image data again
ReleaseImage();
}
// Process a number of files dropped into the window
void ProcessDrop(HDROP drop)
{
// Retrieve file count dropped
int fcount =
DragQueryFile(drop, 0xffffffff, NULL, 0);
for (int i = 0;
i < fcount;
++i)
{
// Allocate buffer for file name
int namlen =
DragQueryFile(drop, i, NULL, 0) + 1;
pFileName =
(char*)malloc(sizeof(char) * namlen);
if (!pFileName)
{
throw("Failed to allocate memory");
}
// Retrieve file name
DragQueryFile(drop, i, pFileName, namlen);
// Proces the specified file
ProcessFile();
// Release name buffer
free(pFileName);
}
// Clean up memory occupied by the drop
DragFinish(drop);
// Bring up a message box to indicate
// that we finished
MessageBox(hMainWin, "Files fixed",
"Finished", MB_OK);
}
// The window procedure
LRESULT CALLBACK WndProc
(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Handle incoming message
switch (uMsg)
{
case WM_CLOSE:
// Force program to terminate
bExit = true;
return 0;
case WM_DROPFILES:
// Some files were dropped, so process them
ProcessDrop((HDROP)wParam);
return 0;
}
// Let default window procedure handle all
// unhandled messages.
return
DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// Run the actual program
void Run()
{
// Repeat main loop until exit flag is raised
while (!bExit)
{
// Handle all incoming windows messages
MSG msg;
while (PeekMessage(&msg, hMainWin, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
// The programm creation method
void Create()
{
// Create program window class
WNDCLASS wndcls =
{ 0, &WndProc, 0, 0, GetModuleHandle(NULL), NULL,
LoadCursor(NULL, IDC_ARROW), GetSysColorBrush(COLOR_3DFACE),
NULL, "TgaFixWindow" };
if (RegisterClass(&wndcls) == 0)
{
throw("Failed to register window class");
}
// Create program window
hMainWin = CreateWindowEx
(WS_EX_APPWINDOW | WS_EX_ACCEPTFILES | WS_EX_TOPMOST |
WS_EX_CLIENTEDGE, "TgaFixWindow", "TgaFix",
WS_CAPTION | WS_BORDER | WS_POPUP | WS_SYSMENU,
50, 50, 150, 100, NULL, NULL, GetModuleHandle(NULL), NULL);
if (hMainWin == NULL)
{
throw("Failed to create window");
}
// Create the static "drag items here" text
HWND tempwin = CreateWindow
("STATIC", "Drag TGA files here", WS_CHILD | WS_VISIBLE,
22, 20, 100, 100, hMainWin, NULL, GetModuleHandle(NULL), NULL);
if (tempwin == NULL)
{
throw("Failed to create static window text");
}
// Make window visible
ShowWindow(hMainWin, SW_SHOW);
}
// Programm shutdown method
void Destroy()
{
// Release image data in the case of an exceptional
// shutdown.
ReleaseImage();
// Destroy programm window
if (hMainWin)
{
DestroyWindow(hMainWin);
}
// Unregister window class
UnregisterClass("TgaFixWindow", GetModuleHandle(NULL));
}
// This programm is intended to fix the problems occuring with
// alpha channel images on certain imaging applications. Photoshop
// for example sets all completely translucent pixels to white, which
// later on gives problems with mipmapping. This tga fixer allows the
// user to specifiy a number of files which then are all repaired.
// Repair is performed in that all fully transparent pixels are recolored
// with the average color of their (non translucent) neighbours.
// This process continues iteratively until all translucent pixels have
// been recolored appropriately. Note since this is only a tool, the
// code ain't as clean as it should perhaps be.
int CALLBACK WinMain
(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance,
IN LPSTR lpCmdLine, IN int nShowCmd)
{
try
{
// Setup out program on startup
Create();
try
{
// Run program
Run();
}
catch (const char *except)
{
MsgBox(except);
}
// Clean up after we finished
Destroy();
}
catch (const char *except)
{
MsgBox(except);
}
} |