/*------------------------------------ * VisualPng.C -- Shows a PNG image *------------------------------------ * * Copyright 2000, Willem van Schaik. * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ /* switches */ /* defines */ #define PROGNAME "VisualPng" #define LONGNAME "Win32 Viewer for PNG-files" #define VERSION "1.0 of 2000 June 07" /* constants */ #define MARGIN 8 /* standard includes */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <zlib.h> /* application includes */ #include "png.h" #include "pngfile.h" #include "resource.h" /* macros */ /* function prototypes */ LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CenterAbout (HWND hwndChild, HWND hwndParent); BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount, int *pFileIndex); BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex, PTSTR pstrPrevName, PTSTR pstrNextName); BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName, png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels, png_color *pBkgColor); BOOL DisplayImage (HWND hwnd, BYTE **ppDib, BYTE **ppDiData, int cxWinSize, int cyWinSize, BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, BOOL bStretched); BOOL InitBitmap ( BYTE *pDiData, int cxWinSize, int cyWinSize); BOOL FillBitmap ( BYTE *pDiData, int cxWinSize, int cyWinSize, BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, BOOL bStretched); /* a few global variables */ static char *szProgName = PROGNAME; static char *szAppName = LONGNAME; static char *szIconName = PROGNAME; static char szCmdFileName [MAX_PATH]; /* MAIN routine */ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HACCEL hAccel; HWND hwnd; MSG msg; WNDCLASS wndclass; int ixBorders, iyBorders; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon (hInstance, szIconName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */ wndclass.lpszMenuName = szProgName; wndclass.lpszClassName = szProgName; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"), szProgName, MB_ICONERROR); return 0; } /* if filename given on commandline, store it */ if ((szCmdLine != NULL) && (*szCmdLine != '\0')) if (szCmdLine[0] == '"') strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2); else strcpy (szCmdFileName, szCmdLine); else strcpy (szCmdFileName, ""); /* calculate size of window-borders */ ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) + GetSystemMetrics (SM_CXDLGFRAME)); iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) + GetSystemMetrics (SM_CYDLGFRAME)) + GetSystemMetrics (SM_CYCAPTION) + GetSystemMetrics (SM_CYMENUSIZE) + 1; /* WvS: don't ask me why? */ hwnd = CreateWindow (szProgName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders, /* CW_USEDEFAULT, CW_USEDEFAULT, */ NULL, NULL, hInstance, NULL); ShowWindow (hwnd, iCmdShow); UpdateWindow (hwnd); hAccel = LoadAccelerators (hInstance, szProgName); while (GetMessage (&msg, NULL, 0, 0)) { if (!TranslateAccelerator (hwnd, hAccel, &msg)) { TranslateMessage (&msg); DispatchMessage (&msg); } } return msg.wParam; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HINSTANCE hInstance ; static HDC hdc; static PAINTSTRUCT ps; static HMENU hMenu; static BITMAPFILEHEADER *pbmfh; static BITMAPINFOHEADER *pbmih; static BYTE *pbImage; static int cxWinSize, cyWinSize; static int cxImgSize, cyImgSize; static int cImgChannels; static png_color bkgColor = {127, 127, 127}; static BOOL bStretched = TRUE; static BYTE *pDib = NULL; static BYTE *pDiData = NULL; static TCHAR szImgPathName [MAX_PATH]; static TCHAR szTitleName [MAX_PATH]; static TCHAR *pPngFileList = NULL; static int iPngFileCount; static int iPngFileIndex; BOOL bOk; switch (message) { case WM_CREATE: hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; PngFileInitialize (hwnd); strcpy (szImgPathName, ""); /* in case we process file given on command-line */ if (szCmdFileName[0] != '\0') { strcpy (szImgPathName, szCmdFileName); /* read the other png-files in the directory for later */ /* next/previous commands */ BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount, &iPngFileIndex); /* load the image from file */ if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) return 0; /* invalidate the client area for later update */ InvalidateRect (hwnd, NULL, TRUE); /* display the PNG into the DIBitmap */ DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); } return 0; case WM_SIZE: cxWinSize = LOWORD (lParam); cyWinSize = HIWORD (lParam); /* invalidate the client area for later update */ InvalidateRect (hwnd, NULL, TRUE); /* display the PNG into the DIBitmap */ DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); return 0; case WM_INITMENUPOPUP: hMenu = GetMenu (hwnd); if (pbImage) EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED); else EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED); return 0; case WM_COMMAND: hMenu = GetMenu (hwnd); switch (LOWORD (wParam)) { case IDM_FILE_OPEN: /* show the File Open dialog box */ if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName)) return 0; /* read the other png-files in the directory for later */ /* next/previous commands */ BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount, &iPngFileIndex); /* load the image from file */ if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) return 0; /* invalidate the client area for later update */ InvalidateRect (hwnd, NULL, TRUE); /* display the PNG into the DIBitmap */ DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); return 0; case IDM_FILE_SAVE: /* show the File Save dialog box */ if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName)) return 0; /* save the PNG to a disk file */ SetCursor (LoadCursor (NULL, IDC_WAIT)); ShowCursor (TRUE); bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize, bkgColor); ShowCursor (FALSE); SetCursor (LoadCursor (NULL, IDC_ARROW)); if (!bOk) MessageBox (hwnd, TEXT ("Error in saving the PNG image"), szProgName, MB_ICONEXCLAMATION | MB_OK); return 0; case IDM_FILE_NEXT: /* read next entry in the directory */ if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex, NULL, szImgPathName)) { if (strcmp (szImgPathName, "") == 0) return 0; /* load the image from file */ if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) return 0; /* invalidate the client area for later update */ InvalidateRect (hwnd, NULL, TRUE); /* display the PNG into the DIBitmap */ DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); } return 0; case IDM_FILE_PREVIOUS: /* read previous entry in the directory */ if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex, szImgPathName, NULL)) { if (strcmp (szImgPathName, "") == 0) return 0; /* load the image from file */ if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) return 0; /* invalidate the client area for later update */ InvalidateRect (hwnd, NULL, TRUE); /* display the PNG into the DIBitmap */ DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); } return 0; case IDM_FILE_EXIT: /* more cleanup needed... */ /* free image buffer */ if (pDib != NULL) { free (pDib); pDib = NULL; } /* free file-list */ if (pPngFileList != NULL) { free (pPngFileList); pPngFileList = NULL; } /* let's go ... */ exit (0); return 0; case IDM_OPTIONS_STRETCH: bStretched = !bStretched; if (bStretched) CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED); else CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED); /* invalidate the client area for later update */ InvalidateRect (hwnd, NULL, TRUE); /* display the PNG into the DIBitmap */ DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); return 0; case IDM_HELP_ABOUT: DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ; return 0; } /* end switch */ break; case WM_PAINT: hdc = BeginPaint (hwnd, &ps); if (pDib) SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0, 0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS); EndPaint (hwnd, &ps); return 0; case WM_DESTROY: if (pbmfh) { free (pbmfh); pbmfh = NULL; } PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, message, wParam, lParam); } BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG : ShowWindow (hDlg, SW_HIDE); CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER)); ShowWindow (hDlg, SW_SHOW); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } /*--------------- * CenterAbout *--------------- */ BOOL CenterAbout (HWND hwndChild, HWND hwndParent) { RECT rChild, rParent, rWorkArea; int wChild, hChild, wParent, hParent; int xNew, yNew; BOOL bResult; /* Get the Height and Width of the child window */ GetWindowRect (hwndChild, &rChild); wChild = rChild.right - rChild.left; hChild = rChild.bottom - rChild.top; /* Get the Height and Width of the parent window */ GetWindowRect (hwndParent, &rParent); wParent = rParent.right - rParent.left; hParent = rParent.bottom - rParent.top; /* Get the limits of the 'workarea' */ bResult = SystemParametersInfo( SPI_GETWORKAREA, /* system parameter to query or set */ sizeof(RECT), &rWorkArea, 0); if (!bResult) { rWorkArea.left = rWorkArea.top = 0; rWorkArea.right = GetSystemMetrics(SM_CXSCREEN); rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN); } /* Calculate new X position, then adjust for workarea */ xNew = rParent.left + ((wParent - wChild) /2); if (xNew < rWorkArea.left) { xNew = rWorkArea.left; } else if ((xNew+wChild) > rWorkArea.right) { xNew = rWorkArea.right - wChild; } /* Calculate new Y position, then adjust for workarea */ yNew = rParent.top + ((hParent - hChild) /2); if (yNew < rWorkArea.top) { yNew = rWorkArea.top; } else if ((yNew+hChild) > rWorkArea.bottom) { yNew = rWorkArea.bottom - hChild; } /* Set it, and return */ return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } /*---------------- * BuildPngList *---------------- */ BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount, int *pFileIndex) { static TCHAR szImgPathName [MAX_PATH]; static TCHAR szImgFileName [MAX_PATH]; static TCHAR szImgFindName [MAX_PATH]; WIN32_FIND_DATA finddata; HANDLE hFind; static TCHAR szTmp [MAX_PATH]; BOOL bOk; int i, ii; int j, jj; /* free previous file-list */ if (*ppFileList != NULL) { free (*ppFileList); *ppFileList = NULL; } /* extract foldername, filename and search-name */ strcpy (szImgPathName, pstrPathName); strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1); strcpy (szImgFindName, szImgPathName); *(strrchr (szImgFindName, '\\') + 1) = '\0'; strcat (szImgFindName, "*.png"); /* first cycle: count number of files in directory for memory allocation */ *pFileCount = 0; hFind = FindFirstFile(szImgFindName, &finddata); bOk = (hFind != (HANDLE) -1); while (bOk) { *pFileCount += 1; bOk = FindNextFile(hFind, &finddata); } FindClose(hFind); /* allocation memory for file-list */ *ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH); /* second cycle: read directory and store filenames in file-list */ hFind = FindFirstFile(szImgFindName, &finddata); bOk = (hFind != (HANDLE) -1); i = 0; ii = 0; while (bOk) { strcpy (*ppFileList + ii, szImgPathName); strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName); if (strcmp(pstrPathName, *ppFileList + ii) == 0) *pFileIndex = i; ii += MAX_PATH; i++; bOk = FindNextFile(hFind, &finddata); } FindClose(hFind); /* finally we must sort the file-list */ for (i = 0; i < *pFileCount - 1; i++) { ii = i * MAX_PATH; for (j = i+1; j < *pFileCount; j++) { jj = j * MAX_PATH; if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0) { strcpy (szTmp, *ppFileList + jj); strcpy (*ppFileList + jj, *ppFileList + ii); strcpy (*ppFileList + ii, szTmp); /* check if this was the current image that we moved */ if (*pFileIndex == i) *pFileIndex = j; else if (*pFileIndex == j) *pFileIndex = i; } } } return TRUE; } /*---------------- * SearchPngList *---------------- */ BOOL SearchPngList ( TCHAR *pFileList, int FileCount, int *pFileIndex, PTSTR pstrPrevName, PTSTR pstrNextName) { if (FileCount > 0) { /* get previous entry */ if (pstrPrevName != NULL) { if (*pFileIndex > 0) *pFileIndex -= 1; else *pFileIndex = FileCount - 1; strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH)); } /* get next entry */ if (pstrNextName != NULL) { if (*pFileIndex < FileCount - 1) *pFileIndex += 1; else *pFileIndex = 0; strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH)); } return TRUE; } else { return FALSE; } } /*----------------- * LoadImageFile *----------------- */ BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName, png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels, png_color *pBkgColor) { static TCHAR szTmp [MAX_PATH]; /* if there's an existing PNG, free the memory */ if (*ppbImage) { free (*ppbImage); *ppbImage = NULL; } /* Load the entire PNG into memory */ SetCursor (LoadCursor (NULL, IDC_WAIT)); ShowCursor (TRUE); PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, pBkgColor); ShowCursor (FALSE); SetCursor (LoadCursor (NULL, IDC_ARROW)); if (*ppbImage != NULL) { sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); SetWindowText (hwnd, szTmp); } else { MessageBox (hwnd, TEXT ("Error in loading the PNG image"), szProgName, MB_ICONEXCLAMATION | MB_OK); return FALSE; } return TRUE; } /*---------------- * DisplayImage *---------------- */ BOOL DisplayImage (HWND hwnd, BYTE **ppDib, BYTE **ppDiData, int cxWinSize, int cyWinSize, BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, BOOL bStretched) { BYTE *pDib = *ppDib; BYTE *pDiData = *ppDiData; /* BITMAPFILEHEADER *pbmfh; */ BITMAPINFOHEADER *pbmih; WORD wDIRowBytes; png_color bkgBlack = {0, 0, 0}; png_color bkgGray = {127, 127, 127}; png_color bkgWhite = {255, 255, 255}; /* allocate memory for the Device Independant bitmap */ wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2; if (pDib) { free (pDib); pDib = NULL; } if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) + wDIRowBytes * cyWinSize))) { MessageBox (hwnd, TEXT ("Error in displaying the PNG image"), szProgName, MB_ICONEXCLAMATION | MB_OK); *ppDib = pDib = NULL; return FALSE; } *ppDib = pDib; memset (pDib, 0, sizeof(BITMAPINFOHEADER)); /* initialize the dib-structure */ pbmih = (BITMAPINFOHEADER *) pDib; pbmih->biSize = sizeof(BITMAPINFOHEADER); pbmih->biWidth = cxWinSize; pbmih->biHeight = -((long) cyWinSize); pbmih->biPlanes = 1; pbmih->biBitCount = 24; pbmih->biCompression = 0; pDiData = pDib + sizeof(BITMAPINFOHEADER); *ppDiData = pDiData; /* first fill bitmap with gray and image border */ InitBitmap (pDiData, cxWinSize, cyWinSize); /* then fill bitmap with image */ if (pbImage) { FillBitmap ( pDiData, cxWinSize, cyWinSize, pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); } return TRUE; } /*-------------- * InitBitmap *-------------- */ BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize) { BYTE *dst; int x, y, col; /* initialize the background with gray */ dst = pDiData; for (y = 0; y < cyWinSize; y++) { col = 0; for (x = 0; x < cxWinSize; x++) { /* fill with GRAY */ *dst++ = 127; *dst++ = 127; *dst++ = 127; col += 3; } /* rows start on 4 byte boundaries */ while ((col % 4) != 0) { dst++; col++; } } return TRUE; } /*-------------- * FillBitmap *-------------- */ BOOL FillBitmap ( BYTE *pDiData, int cxWinSize, int cyWinSize, BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, BOOL bStretched) { BYTE *pStretchedImage; BYTE *pImg; BYTE *src, *dst; BYTE r, g, b, a; const int cDIChannels = 3; WORD wImgRowBytes; WORD wDIRowBytes; int cxNewSize, cyNewSize; int cxImgPos, cyImgPos; int xImg, yImg; int xWin, yWin; int xOld, yOld; int xNew, yNew; if (bStretched) { cxNewSize = cxWinSize - 2 * MARGIN; cyNewSize = cyWinSize - 2 * MARGIN; /* stretch the image to it's window determined size */ /* the following two are mathematically the same, but the first * has side-effects because of rounding */ /* if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) */ if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize)) { cyNewSize = cxNewSize * cyImgSize / cxImgSize; cxImgPos = MARGIN; cyImgPos = (cyWinSize - cyNewSize) / 2; } else { cxNewSize = cyNewSize * cxImgSize / cyImgSize; cyImgPos = MARGIN; cxImgPos = (cxWinSize - cxNewSize) / 2; } pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize); pImg = pStretchedImage; for (yNew = 0; yNew < cyNewSize; yNew++) { yOld = yNew * cyImgSize / cyNewSize; for (xNew = 0; xNew < cxNewSize; xNew++) { xOld = xNew * cxImgSize / cxNewSize; r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0); g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1); b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2); *pImg++ = r; *pImg++ = g; *pImg++ = b; if (cImgChannels == 4) { a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 3); *pImg++ = a; } } } /* calculate row-bytes */ wImgRowBytes = cImgChannels * cxNewSize; wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2; /* copy image to screen */ for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++) { if (yWin >= cyWinSize - cyImgPos) break; src = pStretchedImage + yImg * wImgRowBytes; dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels; for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++) { if (xWin >= cxWinSize - cxImgPos) break; r = *src++; g = *src++; b = *src++; *dst++ = b; /* note the reverse order */ *dst++ = g; *dst++ = r; if (cImgChannels == 4) { a = *src++; } } } /* free memory */ if (pStretchedImage != NULL) { free (pStretchedImage); pStretchedImage = NULL; } } /* process the image not-stretched */ else { /* calculate the central position */ cxImgPos = (cxWinSize - cxImgSize) / 2; cyImgPos = (cyWinSize - cyImgSize) / 2; /* check for image larger than window */ if (cxImgPos < MARGIN) cxImgPos = MARGIN; if (cyImgPos < MARGIN) cyImgPos = MARGIN; /* calculate both row-bytes */ wImgRowBytes = cImgChannels * cxImgSize; wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2; /* copy image to screen */ for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++) { if (yWin >= cyWinSize - MARGIN) break; src = pbImage + yImg * wImgRowBytes; dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels; for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++) { if (xWin >= cxWinSize - MARGIN) break; r = *src++; g = *src++; b = *src++; *dst++ = b; /* note the reverse order */ *dst++ = g; *dst++ = r; if (cImgChannels == 4) { a = *src++; } } } } return TRUE; } /*----------------- * end of source *----------------- */