From c78cb0fb36db5ac84fdc6f012074a6c1cea68b1e Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Thu, 5 Jul 2012 12:16:42 +0000 Subject: AVS: folder structure change git-svn-id: http://svn.miranda-ng.org/main/trunk@769 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/AVS/src/image_utils.cpp | 753 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 753 insertions(+) create mode 100644 plugins/AVS/src/image_utils.cpp (limited to 'plugins/AVS/src/image_utils.cpp') diff --git a/plugins/AVS/src/image_utils.cpp b/plugins/AVS/src/image_utils.cpp new file mode 100644 index 0000000000..35cb33259a --- /dev/null +++ b/plugins/AVS/src/image_utils.cpp @@ -0,0 +1,753 @@ +#include "commonheaders.h" +#include "image_utils.h" + +#include +#include + +/* +Theese are the ones needed +#include +#include +#include +#include +#include +#include +*/ + +extern int _DebugTrace(const char *fmt, ...); +extern int _DebugTrace(HANDLE hContact, const char *fmt, ...); + + +#define GET_PIXEL(__P__, __X__, __Y__) ( __P__ + width * 4 * (__Y__) + 4 * (__X__)) + + +extern FI_INTERFACE *fei; + +// Make a bitmap all transparent, but only if it is a 32bpp +void MakeBmpTransparent(HBITMAP hBitmap) +{ + BITMAP bmp; + DWORD dwLen; + BYTE *p; + + GetObject(hBitmap, sizeof(bmp), &bmp); + + if (bmp.bmBitsPixel != 32) + return; + + dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); + p = (BYTE *)malloc(dwLen); + if (p == NULL) + return; + + memset(p, 0, dwLen); + SetBitmapBits(hBitmap, dwLen, p); + + free(p); +} + +// Resize ///////////////////////////////////////////////////////////////////////////////////////// + + +// Returns a copy of the bitmap with the size especified +// wParam = ResizeBitmap * +// lParam = NULL +INT_PTR BmpFilterResizeBitmap(WPARAM wParam,LPARAM lParam) +{ + // Call freeiamge service (is here only for backward compatibility) + return CallService(MS_IMG_RESIZE, wParam, lParam); +} + +HBITMAP CopyBitmapTo32(HBITMAP hBitmap) +{ + BITMAPINFO RGB32BitsBITMAPINFO; + BYTE * ptPixels; + HBITMAP hDirectBitmap; + + BITMAP bmp; + DWORD dwLen; + BYTE *p; + + GetObject(hBitmap, sizeof(bmp), &bmp); + + dwLen = bmp.bmWidth * bmp.bmHeight * 4; + p = (BYTE *)malloc(dwLen); + if (p == NULL) + return NULL; + + // Create bitmap + ZeroMemory(&RGB32BitsBITMAPINFO, sizeof(BITMAPINFO)); + RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + RGB32BitsBITMAPINFO.bmiHeader.biWidth = bmp.bmWidth; + RGB32BitsBITMAPINFO.bmiHeader.biHeight = bmp.bmHeight; + RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1; + RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32; + + hDirectBitmap = CreateDIBSection(NULL, + (BITMAPINFO *)&RGB32BitsBITMAPINFO, + DIB_RGB_COLORS, + (void **)&ptPixels, + NULL, 0); + + // Copy data + if (bmp.bmBitsPixel != 32) + { + HDC hdcOrig, hdcDest; + HBITMAP oldOrig, oldDest; + + hdcOrig = CreateCompatibleDC(NULL); + oldOrig = (HBITMAP) SelectObject(hdcOrig, hBitmap); + + hdcDest = CreateCompatibleDC(NULL); + oldDest = (HBITMAP) SelectObject(hdcDest, hDirectBitmap); + + BitBlt(hdcDest, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcOrig, 0, 0, SRCCOPY); + + SelectObject(hdcDest, oldDest); + DeleteObject(hdcDest); + SelectObject(hdcOrig, oldOrig); + DeleteObject(hdcOrig); + + // Set alpha + fei->FI_CorrectBitmap32Alpha(hDirectBitmap, FALSE); + } + else + { + GetBitmapBits(hBitmap, dwLen, p); + SetBitmapBits(hDirectBitmap, dwLen, p); + } + + free(p); + + return hDirectBitmap; +} + +HBITMAP CreateBitmap32(int cx, int cy) +{ + BITMAPINFO RGB32BitsBITMAPINFO; + UINT * ptPixels; + HBITMAP DirectBitmap; + + ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO)); + RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); + RGB32BitsBITMAPINFO.bmiHeader.biWidth=cx;//bm.bmWidth; + RGB32BitsBITMAPINFO.bmiHeader.biHeight=cy;//bm.bmHeight; + RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1; + RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32; + + DirectBitmap = CreateDIBSection(NULL, + (BITMAPINFO *)&RGB32BitsBITMAPINFO, + DIB_RGB_COLORS, + (void **)&ptPixels, + NULL, 0); + return DirectBitmap; +} + +// Set the color of points that are transparent +void SetTranspBkgColor(HBITMAP hBitmap, COLORREF color) +{ + BITMAP bmp; + DWORD dwLen; + BYTE *p; + int x, y; + + GetObject(hBitmap, sizeof(bmp), &bmp); + + if (bmp.bmBitsPixel != 32) + return; + + dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); + p = (BYTE *)malloc(dwLen); + if (p == NULL) + return; + memset(p, 0, dwLen); + + GetBitmapBits(hBitmap, dwLen, p); + + bool changed = false; + for (y = 0; y < bmp.bmHeight; ++y) { + BYTE *px = p + bmp.bmWidth * 4 * y; + + for (x = 0; x < bmp.bmWidth; ++x) + { + if (px[3] == 0) + { + px[0] = GetBValue(color); + px[1] = GetGValue(color); + px[2] = GetRValue(color); + changed = true; + } + px += 4; + } + } + + if (changed) + SetBitmapBits(hBitmap, dwLen, p); + + free(p); +} + + +#define HIMETRIC_INCH 2540 // HIMETRIC units per inch + +void SetHIMETRICtoDP(HDC hdc, SIZE* sz) +{ + POINT pt; + int nMapMode = GetMapMode(hdc); + if ( nMapMode < MM_ISOTROPIC && nMapMode != MM_TEXT ) + { + // when using a constrained map mode, map against physical inch + SetMapMode(hdc,MM_HIMETRIC); + pt.x = sz->cx; + pt.y = sz->cy; + LPtoDP(hdc,&pt,1); + sz->cx = pt.x; + sz->cy = pt.y; + SetMapMode(hdc, nMapMode); + } + else + { + // map against logical inch for non-constrained mapping modes + int cxPerInch, cyPerInch; + cxPerInch = GetDeviceCaps(hdc,LOGPIXELSX); + cyPerInch = GetDeviceCaps(hdc,LOGPIXELSY); + sz->cx = MulDiv(sz->cx, cxPerInch, HIMETRIC_INCH); + sz->cy = MulDiv(sz->cy, cyPerInch, HIMETRIC_INCH); + } + + pt.x = sz->cx; + pt.y = sz->cy; + DPtoLP(hdc,&pt,1); + sz->cx = pt.x; + sz->cy = pt.y; +} + +INT_PTR BmpFilterLoadBitmap32(WPARAM wParam,LPARAM lParam) +{ + FIBITMAP *dib32 = NULL; + + if(fei == NULL) + return 0; + + FIBITMAP *dib = (FIBITMAP *)CallService(MS_IMG_LOAD, lParam, IMGL_RETURNDIB|IMGL_TCHAR); + + if(dib == NULL) + return 0; + + if(fei->FI_GetBPP(dib) != 32) { + dib32 = fei->FI_ConvertTo32Bits(dib); + fei->FI_Unload(dib); + } + else + dib32 = dib; + + if(dib32) { + if(fei->FI_IsTransparent(dib32)) { + if(wParam) { + DWORD *dwTrans = (DWORD *)wParam; + *dwTrans = 1; + } + } + if(fei->FI_GetWidth(dib32) > 128 || fei->FI_GetHeight(dib32) > 128) { + FIBITMAP *dib_new = fei->FI_MakeThumbnail(dib32, 128, FALSE); + fei->FI_Unload(dib32); + if(dib_new == NULL) + return 0; + dib32 = dib_new; + } + + HBITMAP bitmap = fei->FI_CreateHBITMAPFromDIB(dib32); + + fei->FI_Unload(dib32); + fei->FI_CorrectBitmap32Alpha(bitmap, FALSE); + return (INT_PTR)bitmap; + } + return 0; +} + +static HWND hwndClui = 0; + +// +// Save /////////////////////////////////////////////////////////////////////////////////////////// +// PNG and BMP will be saved as 32bit images, jpg as 24bit with default quality (75) +// returns 1 on success, 0 on failure + +int BmpFilterSaveBitmap(HBITMAP hBmp, char *szFile, int flags) +{ + IMGSRVC_INFO i = {0}; + i.cbSize = sizeof(IMGSRVC_INFO); + i.szName = szFile; + i.hbm = hBmp; + i.dwMask = IMGI_HBITMAP; + i.fif = FIF_UNKNOWN; + + return !CallService(MS_IMG_SAVE, (WPARAM) &i, MAKELONG(0, flags)); +} + + +int BmpFilterSaveBitmapW(HBITMAP hBmp, wchar_t *wszFile, int flags) +{ + IMGSRVC_INFO i = {0}; + i.cbSize = sizeof(IMGSRVC_INFO); + i.wszName = wszFile; + i.hbm = hBmp; + i.dwMask = IMGI_HBITMAP; + i.fif = FIF_UNKNOWN; + + return !CallService(MS_IMG_SAVE, (WPARAM) &i, MAKELONG(IMGL_WCHAR, flags)); +} + +// Save an HBITMAP to an image +// wParam = HBITMAP +// lParam = filename +INT_PTR BmpFilterSaveBitmap(WPARAM wParam,LPARAM lParam) +{ + if ( fei == NULL ) + return -1; + + const char *szFile = (const char*)lParam; + char szFilename[MAX_PATH]; + if ( !CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)szFile, (LPARAM)szFilename)) + mir_snprintf(szFilename, SIZEOF(szFilename), "%s", szFile); + + int filenameLen = lstrlenA( szFilename ); + if ( filenameLen > 4 ) + return BmpFilterSaveBitmap(( HBITMAP )wParam, szFilename, 0); + + return -1; +} + +INT_PTR BmpFilterSaveBitmapW(WPARAM wParam,LPARAM lParam) +{ + if ( fei == NULL ) + return -1; + + const wchar_t *wszFile = (const wchar_t *)lParam; + wchar_t wszFilename[MAX_PATH]; + if ( !CallService(MS_UTILS_PATHTOABSOLUTEW, (WPARAM)wszFile, (LPARAM)wszFilename)) + mir_sntprintf(wszFilename, SIZEOF(wszFilename), _T("%s"), wszFile); + + int filenameLen = lstrlenW( wszFilename ); + if ( filenameLen > 4 ) + return BmpFilterSaveBitmapW(( HBITMAP )wParam, wszFilename, 0 ); + + return -1; +} + +// Returns != 0 if can save that type of image, = 0 if cant +// wParam = 0 +// lParam = PA_FORMAT_* // image format +// kept for compatibilty - with freeimage we can save all common formats + +INT_PTR BmpFilterCanSaveBitmap(WPARAM wParam,LPARAM lParam) +{ + return 1; +} + + +// Other utilities //////////////////////////////////////////////////////////////////////////////// + + +static BOOL ColorsAreTheSame(int colorDiff, BYTE *px1, BYTE *px2) +{ + return abs(px1[0] - px2[0]) <= colorDiff + && abs(px1[1] - px2[1]) <= colorDiff + && abs(px1[2] - px2[2]) <= colorDiff; +} + + +void AddToStack(int *stack, int *topPos, int x, int y) +{ + int i; + + // Already is in stack? + for(i = 0 ; i < *topPos ; i += 2) + { + if (stack[i] == x && stack[i+1] == y) + return; + } + + stack[*topPos] = x; + (*topPos)++; + + stack[*topPos] = y; + (*topPos)++; +} + + +BOOL GetColorForPoint(int colorDiff, BYTE *p, int width, int height, + int x0, int y0, int x1, int y1, int x2, int y2, BOOL *foundBkg, BYTE colors[][3]) +{ + BYTE *px1, *px2, *px3; + + px1 = GET_PIXEL(p, x0,y0); + px2 = GET_PIXEL(p, x1,y1); + px3 = GET_PIXEL(p, x2,y2); + + // If any of the corners have transparency, forget about it + // Not using != 255 because some MSN bmps have 254 in some positions + if (px1[3] < 253 || px2[3] < 253 || px3[3] < 253) + return FALSE; + + // See if is the same color + if (ColorsAreTheSame(colorDiff, px1, px2) && ColorsAreTheSame(colorDiff, px3, px2)) + { + *foundBkg = TRUE; + memmove(colors, px1, 3); + } + else + { + *foundBkg = FALSE; + } + + return TRUE; +} + + +DWORD GetImgHash(HBITMAP hBitmap) +{ + BITMAP bmp; + DWORD dwLen; + WORD *p; + + GetObject(hBitmap, sizeof(bmp), &bmp); + + dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); + p = (WORD *)malloc(dwLen); + if (p == NULL) + return 0; + memset(p, 0, dwLen); + + GetBitmapBits(hBitmap, dwLen, p); + + DWORD ret = 0; + for (DWORD i = 0 ; i < dwLen/2 ; i++) + ret += p[i]; + + free(p); + + return ret; +} + +/* + * Changes the handle to a grayscale image + */ +HBITMAP MakeGrayscale(HANDLE hContact, HBITMAP hBitmap) +{ + if(hBitmap) { + FIBITMAP *dib = fei->FI_CreateDIBFromHBITMAP(hBitmap); + + if(dib) { + FIBITMAP *dib_new = fei->FI_ConvertToGreyscale(dib); + fei->FI_Unload(dib); + if(dib_new) { + DeleteObject(hBitmap); + HBITMAP hbm_new = fei->FI_CreateHBITMAPFromDIB(dib_new); + fei->FI_Unload(dib_new); + return hbm_new; + } + } + } + return hBitmap; +} + +/* + * See if finds a transparent background in image, and set its transparency + * Return TRUE if found a transparent background + */ +BOOL MakeTransparentBkg(HANDLE hContact, HBITMAP *hBitmap) +{ + BYTE *p = NULL; + DWORD dwLen; + int width, height, i, j; + BITMAP bmp; + BYTE colors[8][3]; + BOOL foundBkg[8]; + BYTE *px1; + int count, maxCount, selectedColor; + HBITMAP hBmpTmp; + int colorDiff; + + GetObject(*hBitmap, sizeof(bmp), &bmp); + width = bmp.bmWidth; + height = bmp.bmHeight; + colorDiff = DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgColorDiff", + DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgColorDiff", 10)); + + // Min 5x5 to easy things in loop + if (width <= 4 || height <= 4) + return FALSE; + + dwLen = width * height * 4; + p = (BYTE *)malloc(dwLen); + if (p == NULL) + { + return FALSE; + } + + if (bmp.bmBitsPixel == 32) + { + hBmpTmp = *hBitmap; + } + else + { + // Convert to 32 bpp + hBmpTmp = CopyBitmapTo32(*hBitmap); + } + + GetBitmapBits(hBmpTmp, dwLen, p); + + // **** Get corner colors + + // Top left + if (!GetColorForPoint(colorDiff, p, width, height, + 0, 0, 0, 1, 1, 0, &foundBkg[0], &colors[0])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Top center + if (!GetColorForPoint(colorDiff, p, width, height, + width/2, 0, width/2-1, 0, width/2+1, 0, &foundBkg[1], &colors[1])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Top Right + if (!GetColorForPoint(colorDiff, p, width, height, + width-1, 0, width-1, 1, width-2, 0, &foundBkg[2], &colors[2])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Center left + if (!GetColorForPoint(colorDiff, p, width, height, + 0, height/2, 0, height/2-1, 0, height/2+1, &foundBkg[3], &colors[3])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Center left + if (!GetColorForPoint(colorDiff, p, width, height, + width-1, height/2, width-1, height/2-1, width-1, height/2+1, &foundBkg[4], &colors[4])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Bottom left + if (!GetColorForPoint(colorDiff, p, width, height, + 0, height-1, 0, height-2, 1, height-1, &foundBkg[5], &colors[5])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Bottom center + if (!GetColorForPoint(colorDiff, p, width, height, + width/2, height-1, width/2-1, height-1, width/2+1, height-1, &foundBkg[6], &colors[6])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Bottom Right + if (!GetColorForPoint(colorDiff, p, width, height, + width-1, height-1, width-1, height-2, width-2, height-1, &foundBkg[7], &colors[7])) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // **** X corners have to have the same color + + count = 0; + for (i = 0 ; i < 8 ; i++) + { + if (foundBkg[i]) + count++; + } + + if (count < DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgNumPoints", + DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5))) + { + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Ok, X corners at least have a color, lets compare then + maxCount = 0; + for (i = 0 ; i < 8 ; i++) + { + if (foundBkg[i]) + { + count = 0; + + for (j = 0 ; j < 8 ; j++) + { + if (foundBkg[j] && ColorsAreTheSame(colorDiff, (BYTE *) &colors[i], (BYTE *) &colors[j])) + count++; + } + + if (count > maxCount) + { + maxCount = count; + selectedColor = i; + } + } + } + + if (maxCount < DBGetContactSettingWord(hContact, "ContactPhoto", "TranspBkgNumPoints", + DBGetContactSettingWord(0, AVS_MODULE, "TranspBkgNumPoints", 5))) + { + // Not enought corners with the same color + if (hBmpTmp != *hBitmap) DeleteObject(hBmpTmp); + free(p); + return FALSE; + } + + // Get bkg color as mean of colors + { + int bkgColor[3]; + + bkgColor[0] = 0; + bkgColor[1] = 0; + bkgColor[2] = 0; + for (i = 0 ; i < 8 ; i++) + { + if (foundBkg[i] && ColorsAreTheSame(colorDiff, (BYTE *) &colors[i], (BYTE *) &colors[selectedColor])) + { + bkgColor[0] += colors[i][0]; + bkgColor[1] += colors[i][1]; + bkgColor[2] += colors[i][2]; + } + } + bkgColor[0] /= maxCount; + bkgColor[1] /= maxCount; + bkgColor[2] /= maxCount; + + colors[selectedColor][0] = bkgColor[0]; + colors[selectedColor][1] = bkgColor[1]; + colors[selectedColor][2] = bkgColor[2]; + } + + // **** Set alpha for the background color, from the borders + + if (hBmpTmp != *hBitmap) + { + DeleteObject(*hBitmap); + + *hBitmap = hBmpTmp; + + GetObject(*hBitmap, sizeof(bmp), &bmp); + GetBitmapBits(*hBitmap, dwLen, p); + } + + { + // Set alpha from borders + int x, y; + int topPos = 0; + int curPos = 0; + int *stack = (int *)malloc(width * height * 2 * sizeof(int)); + bool transpProportional = (DBGetContactSettingByte(NULL, AVS_MODULE, "MakeTransparencyProportionalToColorDiff", 0) != 0); + + if (stack == NULL) + { + free(p); + return FALSE; + } + + // Put four corners + AddToStack(stack, &topPos, 0, 0); + AddToStack(stack, &topPos, width/2, 0); + AddToStack(stack, &topPos, width-1, 0); + AddToStack(stack, &topPos, 0, height/2); + AddToStack(stack, &topPos, width-1, height/2); + AddToStack(stack, &topPos, 0, height-1); + AddToStack(stack, &topPos, width/2, height-1); + AddToStack(stack, &topPos, width-1, height-1); + + while(curPos < topPos) + { + // Get pos + x = stack[curPos]; curPos++; + y = stack[curPos]; curPos++; + + // Get pixel + px1 = GET_PIXEL(p, x, y); + + // It won't change the transparency if one exists + // (This avoid an endless loop too) + // Not using == 255 because some MSN bmps have 254 in some positions + if (px1[3] >= 253) + { + if (ColorsAreTheSame(colorDiff, px1, (BYTE *) &colors[selectedColor])) + { + if (transpProportional) + { + px1[3] = min(252, + (abs(px1[0] - colors[selectedColor][0]) + + abs(px1[1] - colors[selectedColor][1]) + + abs(px1[2] - colors[selectedColor][2])) / 3); + } + else + { + px1[3] = 0; + } + + // Add 4 neighbours + + if (x + 1 < width) + AddToStack(stack, &topPos, x + 1, y); + + if (x - 1 >= 0) + AddToStack(stack, &topPos, x - 1, y); + + if (y + 1 < height) + AddToStack(stack, &topPos, x, y + 1); + + if (y - 1 >= 0) + AddToStack(stack, &topPos, x, y - 1); + } + } + } + + free(stack); + } + + dwLen = SetBitmapBits(*hBitmap, dwLen, p); + free(p); + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Other utils + +int SaveAvatar( const char* protocol, const TCHAR* tszFileName ) +{ + int result = CallProtoService(protocol, PS_SETMYAVATART, 0, ( LPARAM )tszFileName); + if ( result == CALLSERVICE_NOTFOUND ) { + if ( tszFileName != NULL ) { + char szFileName[ MAX_PATH ]; + WideCharToMultiByte( CP_ACP, 0, tszFileName, -1, szFileName, SIZEOF(szFileName), 0, 0 ); + result = CallProtoService(protocol, PS_SETMYAVATAR, 0, ( LPARAM )szFileName); + } + else result = CallProtoService(protocol, PS_SETMYAVATAR, 0, 0); + } + + return result; +} -- cgit v1.2.3