#include "commonheaders.h" #include "image_utils.h" extern int _DebugTrace(const char *fmt, ...); extern int _DebugTrace(MCONTACT hContact, const char *fmt, ...); #define GET_PIXEL(__P__, __X__, __Y__) ( __P__ + width * 4 * (__Y__) + 4 * (__X__)) // Make a bitmap all transparent, but only if it is a 32bpp void MakeBmpTransparent(HBITMAP hBitmap) { BITMAP bmp; GetObject(hBitmap, sizeof(bmp), &bmp); if (bmp.bmBitsPixel != 32) return; DWORD dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); BYTE *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 ( !PathToAbsolute(szFile, 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 ( !PathToAbsoluteW(wszFile, 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(MCONTACT 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(MCONTACT 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 = db_get_w(hContact, "ContactPhoto", "TranspBkgColorDiff", db_get_w(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 < db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", db_get_w(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 < db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", db_get_w(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 = (db_get_b(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_PTR result = CallProtoService(protocol, PS_SETMYAVATART, 0, ( LPARAM )tszFileName); if ( result == CALLSERVICE_NOTFOUND ) result = CallProtoService(protocol, PS_SETMYAVATAR, 0, (LPARAM)(char*)_T2A(tszFileName)); return result; }