diff options
author | George Hazan <george.hazan@gmail.com> | 2024-03-20 22:06:26 +0300 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2024-03-20 22:06:26 +0300 |
commit | 335a494074e464e8f7f6e90f195bcd7339518a89 (patch) | |
tree | 6c45877f941ff83dea633a791c202f33edc87557 | |
parent | 33aa78b58cd86f9b8e38032ff157ff614c23d73c (diff) |
initial smiley support
-rw-r--r-- | plugins/ExternalAPI/m_NewStory.h | 2 | ||||
-rw-r--r-- | plugins/NewStory/NewStory.vcxproj | 4 | ||||
-rw-r--r-- | plugins/NewStory/NewStory.vcxproj.filters | 3 | ||||
-rw-r--r-- | plugins/NewStory/src/TxDIB.cpp | 1250 | ||||
-rw-r--r-- | plugins/NewStory/src/TxDIB.h | 319 | ||||
-rw-r--r-- | plugins/NewStory/src/history_control.cpp | 12 | ||||
-rw-r--r-- | plugins/NewStory/src/history_control.h | 3 | ||||
-rw-r--r-- | plugins/NewStory/src/main.cpp | 9 | ||||
-rw-r--r-- | plugins/NewStory/src/stdafx.h | 13 | ||||
-rw-r--r-- | plugins/NewStory/src/templates.cpp | 2 | ||||
-rw-r--r-- | plugins/NewStory/src/utils.cpp | 29 | ||||
-rw-r--r-- | plugins/NewStory/src/utils.h | 1 | ||||
-rw-r--r-- | plugins/NewStory/src/webpage.cpp | 41 |
13 files changed, 1680 insertions, 8 deletions
diff --git a/plugins/ExternalAPI/m_NewStory.h b/plugins/ExternalAPI/m_NewStory.h index 00bf7b6e44..c2690e313b 100644 --- a/plugins/ExternalAPI/m_NewStory.h +++ b/plugins/ExternalAPI/m_NewStory.h @@ -23,6 +23,8 @@ enum // NSM_SET_OPTIONS, // options were changed + NSM_IMAGE_LOADED, + NSM_LAST }; diff --git a/plugins/NewStory/NewStory.vcxproj b/plugins/NewStory/NewStory.vcxproj index 9df881d8b1..b34ca769e9 100644 --- a/plugins/NewStory/NewStory.vcxproj +++ b/plugins/NewStory/NewStory.vcxproj @@ -53,6 +53,7 @@ <ClCompile Include="src\history_log.cpp" /> <ClCompile Include="src\history_menus.cpp" /> <ClCompile Include="src\history_svc.cpp" /> + <ClCompile Include="src\TxDIB.cpp" /> <ClCompile Include="src\webpage.cpp" /> <ClCompile Include="src\main.cpp" /> <ClCompile Include="src\options.cpp" /> @@ -78,6 +79,9 @@ <ResourceCompile Include="res\Version.rc" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj"> + <Project>{5d14cff3-0d17-4528-99ea-de9dca47cc2e}</Project> + </ProjectReference> <ProjectReference Include="..\..\libs\litehtml\litehtml.vcxproj"> <Project>{51db004a-e160-47c7-b017-bec90fdc442d}</Project> </ProjectReference> diff --git a/plugins/NewStory/NewStory.vcxproj.filters b/plugins/NewStory/NewStory.vcxproj.filters index 83fc3b22e0..99df9f1851 100644 --- a/plugins/NewStory/NewStory.vcxproj.filters +++ b/plugins/NewStory/NewStory.vcxproj.filters @@ -59,6 +59,9 @@ <ClCompile Include="src\dib.cpp"> <Filter>Source Files\Litehtml</Filter> </ClCompile> + <ClCompile Include="src\TxDIB.cpp"> + <Filter>Source Files\Litehtml</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="src\calendartool.h"> diff --git a/plugins/NewStory/src/TxDIB.cpp b/plugins/NewStory/src/TxDIB.cpp new file mode 100644 index 0000000000..094fd4a2f2 --- /dev/null +++ b/plugins/NewStory/src/TxDIB.cpp @@ -0,0 +1,1250 @@ +#include "stdafx.h" +#include "TxDIB.h" +#include <emmintrin.h> +#include <mmintrin.h> + +CTxDIB::CTxDIB(void) +{ + m_maxAlpha = 255; + m_bits = NULL; + m_width = 0; + m_height = 0; +} + +CTxDIB::CTxDIB(const CTxDIB &val) +{ + m_bits = NULL; + m_width = 0; + m_height = 0; + m_maxAlpha = 255; + _copy(val); +} + +CTxDIB::CTxDIB(LPCWSTR fileName) +{ + m_bits = NULL; + m_width = 0; + m_height = 0; + m_maxAlpha = 255; + load(fileName); +} + +CTxDIB::~CTxDIB(void) +{ + destroy(); +} + +BOOL CTxDIB::load(LPCWSTR fileName) +{ + BOOL ret = FALSE; + + FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeU(fileName); + FIBITMAP *dib = FreeImage_LoadU(fif, fileName); + ret = attach(dib); + PreMultiplyWithAlpha(); + FreeImage_Unload(dib); + + return ret; +} + +BOOL CTxDIB::load(HRSRC hRes, HMODULE hModule /*= NULL*/) +{ + BOOL ret = FALSE; + DWORD rsize = SizeofResource(hModule, hRes); + HGLOBAL hMem = LoadResource(hModule, hRes); + if (hMem) { + LPBYTE lpData = (LPBYTE)LockResource(hMem); + ret = load(lpData, rsize); + } + return ret; +} + +BOOL CTxDIB::load(LPBYTE data, DWORD size) +{ + BOOL ret = FALSE; + + FIMEMORY *mem = FreeImage_OpenMemory(data, size); + if (mem) { + FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(mem, size); + FIBITMAP *dib = FreeImage_LoadFromMemory(fif, mem); + ret = attach(dib); + PreMultiplyWithAlpha(); + FreeImage_Unload(dib); + FreeImage_CloseMemory(mem); + } + + return ret; +} + +BOOL CTxDIB::destroy() +{ + if (m_bits) { + free(m_bits); + m_bits = NULL; + } + m_width = 0; + m_height = 0; + return TRUE; +} + +BOOL CTxDIB::draw(HDC hdc, int x, int y, int cx /*= -1*/, long cy /*= -1*/) +{ + if (!m_bits || !cx || !cy) { + return FALSE; + } + + if (cx > 0 && cx != m_width || cy > 0 && cy != m_height) { + CTxDIB newdib; + resample(cx, cy, &newdib); + return newdib.draw(hdc, x, y); + } + + HDC memDC = CreateCompatibleDC(hdc); + HBITMAP bmp = createBitmap(memDC); + HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, bmp); + + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.AlphaFormat = AC_SRC_ALPHA; + bf.SourceConstantAlpha = m_maxAlpha; + + AlphaBlend(hdc, x, y, + cx >= 0 ? cx : m_width, + cy >= 0 ? cy : m_height, + memDC, + 0, 0, + m_width, + m_height, + bf); + + SelectObject(memDC, oldBmp); + DeleteObject(bmp); + DeleteDC(memDC); + + return TRUE; +} + +void CTxDIB::setTransColor(COLORREF clr) +{ + RGBQUAD rgb; + rgb.rgbRed = GetBValue(clr); + rgb.rgbGreen = GetGValue(clr); + rgb.rgbBlue = GetBValue(clr); + if (m_bits) { + size_t cnt = m_width * m_height; + for (size_t i = 0; i < cnt; i++) { + if (m_bits[i].rgbRed == rgb.rgbRed && m_bits[i].rgbGreen == rgb.rgbGreen && m_bits[i].rgbBlue == rgb.rgbBlue) { + m_bits[i].rgbReserved = 0; + PreMulRGBA(m_bits[i]); + } + } + } +} + +void CTxDIB::resample(int newWidth, int newHeight, CTxDIB *dst) +{ + if (newHeight <= 0 || newWidth <= 0 || !isValid()) { + return; + } + if (newWidth < m_width && newHeight < m_height) { + QIShrink(newWidth, newHeight, dst); + } + else { + resample2(newWidth, newHeight, dst); + } +} + +RGBQUAD CTxDIB::GetPixelColorInterpolated(float x, float y) +{ + float sX, sY; //source location + + WORD wt1, wt2, wd, wb, wc, wa; + WORD wrr, wgg, wbb, waa; + int xi, yi; + + float t1, t2, d, b, c, a; + RGBQUAD rgb11, rgb21, rgb12, rgb22; + + RGBQUAD color; + + LPRGBQUAD pxptr; + + yi = (int)y; if (y < 0) yi--; + xi = (int)x; if (x < 0) xi--; + + wt2 = (WORD)((x - yi) * 256.0f); + if (xi < -1 || xi >= m_width || yi < -1 || yi >= m_height) { + //recalculate coordinates and use faster method later on + OverflowCoordinates(sX, sY); + xi = (int)sX; if (sX < 0) xi--; //sX and/or sY have changed ... recalculate xi and yi + yi = (int)sY; if (sY < 0) yi--; + wt2 = (WORD)((sY - yi) * 256.0f); + } + //get four neighbouring pixels + if ((xi + 1) < m_width && xi >= 0 && (yi + 1) < m_height && yi >= 0) { + //all pixels are inside RGB24 image... optimize reading (and use fixed point arithmetic) + wt1 = (WORD)((sX - xi) * 256.0f); + wd = wt1 * wt2 >> 8; + wb = wt1 - wd; + wc = wt2 - wd; + wa = 256 - wt1 - wc; + + pxptr = m_bits + yi * m_width + xi; + wbb = wa * pxptr->rgbBlue; + wgg = wa * pxptr->rgbGreen; + wrr = wa * pxptr->rgbRed; + waa = wa * pxptr->rgbReserved; + pxptr++; + wbb += wb * pxptr->rgbBlue; + wgg += wb * pxptr->rgbGreen; + wrr += wb * pxptr->rgbRed; + waa += wb * pxptr->rgbReserved; + pxptr += m_width - 1; + wbb += wc * pxptr->rgbBlue; + wgg += wc * pxptr->rgbGreen; + wrr += wc * pxptr->rgbRed; + waa += wc * pxptr->rgbReserved; + pxptr++; + wbb += wd * pxptr->rgbBlue; + wgg += wd * pxptr->rgbGreen; + wrr += wd * pxptr->rgbRed; + waa += wd * pxptr->rgbReserved; + + color.rgbRed = (BYTE)(wrr >> 8); + color.rgbGreen = (BYTE)(wgg >> 8); + color.rgbBlue = (BYTE)(wbb >> 8); + color.rgbReserved = (BYTE)(waa >> 8); + } + else { + //default (slower) way to get pixels (not RGB24 or some pixels out of borders) + t1 = sX - xi; + t2 = sY - yi; + d = t1 * t2; + b = t1 - d; + c = t2 - d; + a = 1 - t1 - c; + rgb11 = GetPixelColorWithOverflow(xi, yi); + rgb21 = GetPixelColorWithOverflow(xi + 1, yi); + rgb12 = GetPixelColorWithOverflow(xi, yi + 1); + rgb22 = GetPixelColorWithOverflow(xi + 1, yi + 1); + //calculate linear interpolation + color.rgbRed = (BYTE)(a * rgb11.rgbRed + b * rgb21.rgbRed + c * rgb12.rgbRed + d * rgb22.rgbRed); + color.rgbGreen = (BYTE)(a * rgb11.rgbGreen + b * rgb21.rgbGreen + c * rgb12.rgbGreen + d * rgb22.rgbGreen); + color.rgbBlue = (BYTE)(a * rgb11.rgbBlue + b * rgb21.rgbBlue + c * rgb12.rgbBlue + d * rgb22.rgbBlue); + color.rgbReserved = (BYTE)(a * rgb11.rgbReserved + b * rgb21.rgbReserved + c * rgb12.rgbReserved + d * rgb22.rgbReserved); + } + return color; +} + + + +void CTxDIB::PreMultiplyWithAlpha() +{ + if (!isValid()) { + return; + } + + int cnt = m_width * m_height; + for (int i = 0; i < cnt; i++) { + PreMulRGBA(m_bits[i]); + } +} + +void CTxDIB::_copy(const CTxDIB &val) +{ + _copy(val.m_bits, val.m_width, val.m_height, TRUE); + m_maxAlpha = val.m_maxAlpha; +} + +void CTxDIB::crop(int left, int top, int right, int bottom, CTxDIB *dst /*= NULL*/) +{ + if (!isValid()) { + return; + } + + left = std::max(0, std::min(left, m_width)); + top = std::max(0, std::min(top, m_height)); + right = std::max(0, std::min(right, m_width)); + bottom = std::max(0, std::min(bottom, m_height)); + + int newWidth = right - left; + int newHeight = bottom - top; + + if (newWidth <= 0 || newHeight <= 0) { + if (dst) { + dst->destroy(); + } + return; + } + + LPRGBQUAD newBits = (LPRGBQUAD)malloc(newWidth * newHeight * sizeof(RGBQUAD)); + + int startSrc = (m_height - bottom) * m_width + left; + int startDst = 0; + for (int i = 0; i < newHeight; i++, startSrc += m_width, startDst += newWidth) { + memcpy(newBits + startDst, m_bits + startSrc, newWidth * sizeof(RGBQUAD)); + } + + if (dst) { + dst->_copy(newBits, newWidth, newHeight); + dst->m_maxAlpha = m_maxAlpha; + } + else { + _copy(newBits, newWidth, newHeight); + } +} + +void CTxDIB::_copy(LPRGBQUAD newBits, int newWidth, int newHeight, BOOL copyBits) +{ + destroy(); + m_width = newWidth; + m_height = newHeight; + if (!copyBits) { + m_bits = newBits; + } + else { + if (newBits) { + size_t sz = m_width * m_height * sizeof(RGBQUAD); + m_bits = (LPRGBQUAD)malloc(sz); + memcpy(m_bits, newBits, sz); + } + else { + newBits = NULL; + } + } +} + +void CTxDIB::tile(HDC hdc, LPRECT rcDraw, LPRECT rcClip /*= NULL*/) +{ + if (!m_width || !m_height || !m_bits) { + return; + } + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.AlphaFormat = AC_SRC_ALPHA; + bf.SourceConstantAlpha = 255; + + int x = 0; + int y = 0; + + HBITMAP bmp = createBitmap(hdc); + HDC memDC = CreateCompatibleDC(hdc); + HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, bmp); + RECT rcDst; + RECT rcTemp; + + int drawWidth = 0; + int drawHeight = 0; + int imgX = 0; + int imgY = 0; + + for (int top = rcDraw->top; top <= rcDraw->bottom; top += m_height) { + for (int left = rcDraw->left; left <= rcDraw->right; left += m_width) { + rcDst.left = left; + rcDst.top = top; + rcDst.right = left + m_width; + rcDst.bottom = top + m_height; + if (!rcClip || rcClip && IntersectRect(&rcTemp, &rcDst, rcClip)) { + imgX = 0; + imgY = 0; + drawWidth = m_width; + drawHeight = m_height; + if (rcClip) { + imgY = rcTemp.top - rcDst.top; + imgX = rcTemp.left - rcDst.left; + drawWidth = rcTemp.right - rcTemp.left; + drawHeight = rcTemp.bottom - rcTemp.top; + rcDst = rcTemp; + } + drawWidth -= (rcDst.right - rcDraw->right) <= 0 ? 0 : (rcDst.right - rcDraw->right); + drawHeight -= (rcDst.bottom - rcDraw->bottom) <= 0 ? 0 : (rcDst.bottom - rcDraw->bottom); + + AlphaBlend(hdc, rcDst.left, rcDst.top, drawWidth, drawHeight, memDC, imgX, imgY, drawWidth, drawHeight, bf); + } + } + } + SelectObject(memDC, oldBmp); + DeleteObject(bmp); + DeleteDC(memDC); +} + +HBITMAP CTxDIB::createBitmap(HDC hdc) +{ + HBITMAP bmp = NULL; + + BITMAPINFO bmp_info = { 0 }; + bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmp_info.bmiHeader.biWidth = m_width; + bmp_info.bmiHeader.biHeight = m_height; + bmp_info.bmiHeader.biPlanes = 1; + bmp_info.bmiHeader.biBitCount = 32; + + HDC dc = hdc; + if (!dc) { + dc = GetDC(NULL); + } + + void *buf = 0; + bmp = ::CreateDIBSection( + hdc, + &bmp_info, + DIB_RGB_COLORS, + &buf, + 0, + 0 + ); + + if (buf) { + memcpy(buf, m_bits, m_width * m_height * sizeof(RGBQUAD)); + } + if (!hdc) { + ReleaseDC(NULL, dc); + } + return bmp; +} + +BOOL CTxDIB::createFromHBITMAP(HBITMAP bmp) +{ + FIBITMAP *dib = NULL; + if (bmp) { + BITMAP bm; + GetObject(bmp, sizeof(BITMAP), (LPSTR)&bm); + dib = FreeImage_Allocate(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel); + if (dib) { + int nColors = FreeImage_GetColorsUsed(dib); + HDC dc = GetDC(NULL); + int res = GetDIBits(dc, bmp, 0, FreeImage_GetHeight(dib), FreeImage_GetBits(dib), FreeImage_GetInfo(dib), DIB_RGB_COLORS); + ReleaseDC(NULL, dc); + FreeImage_GetInfoHeader(dib)->biClrUsed = nColors; + FreeImage_GetInfoHeader(dib)->biClrImportant = nColors; + attach(dib); + FreeImage_Unload(dib); + return TRUE; + } + } + return FALSE; +} + +BOOL CTxDIB::attach(LPVOID pdib) +{ + destroy(); + + FIBITMAP *dib = (FIBITMAP *)pdib; + if (dib) { + BOOL delDIB = FALSE;; + if (FreeImage_GetBPP(dib) != 32) { + FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib); + if (dib32) { + dib = dib32; + delDIB = TRUE; + } + else { + dib = NULL; + } + } + if (dib) { + BITMAPINFO *hdr = FreeImage_GetInfo(dib); + m_width = FreeImage_GetWidth(dib); + m_height = FreeImage_GetHeight(dib); + m_bits = (LPRGBQUAD)malloc(m_width * m_height * sizeof(RGBQUAD)); + memcpy(m_bits, FreeImage_GetBits(dib), m_width * m_height * sizeof(RGBQUAD)); + if (delDIB) { + FreeImage_Unload(dib); + } + return TRUE; + } + } + return FALSE; +} + +BOOL CTxDIB::createFromHICON(HICON ico, BOOL fixAlpha) +{ + destroy(); + BOOL ret = FALSE; + if (ico) { + ICONINFO iinfo; + GetIconInfo(ico, &iinfo); + if (iinfo.hbmColor) { + if (createFromHBITMAP(iinfo.hbmColor)) { + CTxDIB mask; + if (mask.createFromHBITMAP(iinfo.hbmMask)) { + int cnt = m_width * m_height; + for (int i = 0; i < cnt; i++) { + if (mask.m_bits[i].rgbBlue) { + m_bits[i].rgbReserved = 0; + m_bits[i].rgbRed = 0; + m_bits[i].rgbGreen = 0; + m_bits[i].rgbBlue = 0; + } + else if (!m_bits[i].rgbReserved && fixAlpha) { + m_bits[i].rgbReserved = 255; + } + PreMulRGBA(m_bits[i]); + } + } + ret = TRUE; + } + } + else if (iinfo.hbmMask) { + BITMAP bm; + GetObject(iinfo.hbmMask, sizeof(BITMAP), (LPSTR)&bm); + if (bm.bmWidth * 2 == bm.bmHeight) { + m_width = bm.bmWidth; + m_height = bm.bmHeight / 2; + m_bits = (LPRGBQUAD)malloc(m_width * m_height * sizeof(RGBQUAD)); + ZeroMemory(m_bits, m_width * m_height * sizeof(RGBQUAD)); + CTxDIB dib; + dib.createFromHBITMAP(iinfo.hbmMask); + dib.crop(0, m_width, m_width, m_height, NULL); + int cnt = m_width * m_height; + for (int i = 0; i < cnt; i++) { + if (dib.m_bits[i].rgbBlue) { + m_bits[i].rgbReserved = 255; + m_bits[i].rgbRed = 0; + m_bits[i].rgbGreen = 0; + m_bits[i].rgbBlue = 0; + } + else { + m_bits[i].rgbReserved = 0; + m_bits[i].rgbRed = 0; + m_bits[i].rgbGreen = 0; + m_bits[i].rgbBlue = 0; + } + } + ret = TRUE; + } + } + + if (iinfo.hbmColor) DeleteObject(iinfo.hbmColor); + if (iinfo.hbmMask) DeleteObject(iinfo.hbmMask); + } + return ret; +} + +void CTxDIB::colorize(COLORREF clr) +{ + if (m_bits) { + BYTE r = GetRValue(clr); + BYTE g = GetGValue(clr); + BYTE b = GetBValue(clr); + int cnt = m_width * m_height; + for (int i = 0; i < cnt; i++) { + if (m_bits[i].rgbReserved) { + m_bits[i].rgbRed = r; + m_bits[i].rgbGreen = g; + m_bits[i].rgbBlue = b; + PreMulRGBA(m_bits[i]); + } + } + } +} + +RGBQUAD CTxDIB::GetAreaColorInterpolated(float const xc, float const yc, float const w, float const h) +{ + RGBQUAD color; //calculated colour + + //area is wider and/or taller than one pixel: + CTxDibRect2 area(xc - w / 2.0f, yc - h / 2.0f, xc + w / 2.0f, yc + h / 2.0f); //area + int xi1 = (int)(area.botLeft.x + 0.49999999f); //low x + int yi1 = (int)(area.botLeft.y + 0.49999999f); //low y + + + int xi2 = (int)(area.topRight.x + 0.5f); //top x + int yi2 = (int)(area.topRight.y + 0.5f); //top y (for loops) + + float rr, gg, bb, aa; //red, green, blue and alpha components + rr = gg = bb = aa = 0; + int x, y; //loop counters + float s = 0; //surface of all pixels + float cps; //surface of current crosssection + if (h > 1 && w > 1) { + //width and height of area are greater than one pixel, so we can employ "ordinary" averaging + CTxDibRect2 intBL, intTR; //bottom left and top right intersection + intBL = area.CrossSection(CTxDibRect2(((float)xi1) - 0.5f, ((float)yi1) - 0.5f, ((float)xi1) + 0.5f, ((float)yi1) + 0.5f)); + intTR = area.CrossSection(CTxDibRect2(((float)xi2) - 0.5f, ((float)yi2) - 0.5f, ((float)xi2) + 0.5f, ((float)yi2) + 0.5f)); + float wBL, wTR, hBL, hTR; + wBL = intBL.Width(); //width of bottom left pixel-area intersection + hBL = intBL.Height(); //height of bottom left... + wTR = intTR.Width(); //width of top right... + hTR = intTR.Height(); //height of top right... + + AddAveragingCont(GetPixelColorWithOverflow(xi1, yi1), wBL * hBL, rr, gg, bb, aa); //bottom left pixel + AddAveragingCont(GetPixelColorWithOverflow(xi2, yi1), wTR * hBL, rr, gg, bb, aa); //bottom right pixel + AddAveragingCont(GetPixelColorWithOverflow(xi1, yi2), wBL * hTR, rr, gg, bb, aa); //top left pixel + AddAveragingCont(GetPixelColorWithOverflow(xi2, yi2), wTR * hTR, rr, gg, bb, aa); //top right pixel + //bottom and top row + for (x = xi1 + 1; x < xi2; x++) { + AddAveragingCont(GetPixelColorWithOverflow(x, yi1), hBL, rr, gg, bb, aa); //bottom row + AddAveragingCont(GetPixelColorWithOverflow(x, yi2), hTR, rr, gg, bb, aa); //top row + } + //leftmost and rightmost column + for (y = yi1 + 1; y < yi2; y++) { + AddAveragingCont(GetPixelColorWithOverflow(xi1, y), wBL, rr, gg, bb, aa); //left column + AddAveragingCont(GetPixelColorWithOverflow(xi2, y), wTR, rr, gg, bb, aa); //right column + } + for (y = yi1 + 1; y < yi2; y++) { + for (x = xi1 + 1; x < xi2; x++) { + color = GetPixelColorWithOverflow(x, y); + rr += color.rgbRed; + gg += color.rgbGreen; + bb += color.rgbBlue; + aa += color.rgbReserved; + }//for x + }//for y + } + else { + //width or height greater than one: + CTxDibRect2 intersect; //intersection with current pixel + CTxDibPoint2 center; + for (y = yi1; y <= yi2; y++) { + for (x = xi1; x <= xi2; x++) { + intersect = area.CrossSection(CTxDibRect2(((float)x) - 0.5f, ((float)y) - 0.5f, ((float)x) + 0.5f, ((float)y) + 0.5f)); + center = intersect.Center(); + color = GetPixelColorInterpolated(center.x, center.y); + cps = intersect.Surface(); + rr += color.rgbRed * cps; + gg += color.rgbGreen * cps; + bb += color.rgbBlue * cps; + aa += color.rgbReserved * cps; + }//for x + }//for y + }//if + + s = area.Surface(); + rr /= s; gg /= s; bb /= s; aa /= s; + if (rr > 255) rr = 255; if (rr < 0) rr = 0; color.rgbRed = (BYTE)rr; + if (gg > 255) gg = 255; if (gg < 0) gg = 0; color.rgbGreen = (BYTE)gg; + if (bb > 255) bb = 255; if (bb < 0) bb = 0; color.rgbBlue = (BYTE)bb; + if (aa > 255) aa = 255; if (aa < 0) aa = 0; color.rgbReserved = (BYTE)aa; + return color; +} + +typedef long fixed; // Our new fixed point type +#define itofx(x) ((x) << 8) // Integer to fixed point +#define ftofx(x) (long)((x) * 256) // Float to fixed point +#define dtofx(x) (long)((x) * 256) // Double to fixed point +#define fxtoi(x) ((x) >> 8) // Fixed point to integer +#define fxtof(x) ((float) (x) / 256) // Fixed point to float +#define fxtod(x) ((double)(x) / 256) // Fixed point to double +#define Mulfx(x,y) (((x) * (y)) >> 8) // Multiply a fixed by a fixed +#define Divfx(x,y) (((x) << 8) / (y)) // Divide a fixed by a fixed +#define _PIXEL DWORD // Pixel + +void CTxDIB::resample2(int newWidth, int newHeight, CTxDIB *dst /*= NULL */) +{ + // Check for valid bitmap + if (isValid()) { + // Calculate scaling params + long _width = std::max(1, newWidth); + long _height = std::max(1, newHeight); + float dx = (float)m_width / (float)_width; + float dy = (float)m_height / (float)_height; + fixed f_dx = ftofx(dx); + fixed f_dy = ftofx(dy); + fixed f_1 = itofx(1); + + LPRGBQUAD lpData = (LPRGBQUAD)malloc(_width * _height * sizeof(RGBQUAD)); + if (lpData) { + // Scale bitmap + DWORD dwDstHorizontalOffset; + DWORD dwDstVerticalOffset = 0; + DWORD dwDstTotalOffset; + LPRGBQUAD lpSrcData = m_bits; + DWORD dwSrcTotalOffset; + LPRGBQUAD lpDstData = lpData; + for (long i = 0; i < _height; i++) { + dwDstHorizontalOffset = 0; + for (long j = 0; j < _width; j++) { + // Update destination total offset + dwDstTotalOffset = dwDstVerticalOffset + dwDstHorizontalOffset; + + // Update bitmap + fixed f_i = itofx(i); + fixed f_j = itofx(j); + fixed f_a = Mulfx(f_i, f_dy); + fixed f_b = Mulfx(f_j, f_dx); + long m = fxtoi(f_a); + long n = fxtoi(f_b); + fixed f_f = f_a - itofx(m); + fixed f_g = f_b - itofx(n); + dwSrcTotalOffset = m * m_width + n; + DWORD dwSrcTopLeft = dwSrcTotalOffset; + DWORD dwSrcTopRight = dwSrcTotalOffset + 1; + if (n >= m_width - 1) + dwSrcTopRight = dwSrcTotalOffset; + DWORD dwSrcBottomLeft = dwSrcTotalOffset + m_width; + if (m >= m_height - 1) + dwSrcBottomLeft = dwSrcTotalOffset; + DWORD dwSrcBottomRight = dwSrcTotalOffset + m_width + 1; + if ((n >= m_width - 1) || (m >= m_height - 1)) + dwSrcBottomRight = dwSrcTotalOffset; + fixed f_w1 = Mulfx(f_1 - f_f, f_1 - f_g); + fixed f_w2 = Mulfx(f_1 - f_f, f_g); + fixed f_w3 = Mulfx(f_f, f_1 - f_g); + fixed f_w4 = Mulfx(f_f, f_g); + RGBQUAD pixel1 = lpSrcData[dwSrcTopLeft]; + RGBQUAD pixel2 = lpSrcData[dwSrcTopRight]; + RGBQUAD pixel3 = lpSrcData[dwSrcBottomLeft]; + RGBQUAD pixel4 = lpSrcData[dwSrcBottomRight]; + fixed f_r1 = itofx(pixel1.rgbRed); + fixed f_r2 = itofx(pixel2.rgbRed); + fixed f_r3 = itofx(pixel3.rgbRed); + fixed f_r4 = itofx(pixel4.rgbRed); + fixed f_g1 = itofx(pixel1.rgbGreen); + fixed f_g2 = itofx(pixel2.rgbGreen); + fixed f_g3 = itofx(pixel3.rgbGreen); + fixed f_g4 = itofx(pixel4.rgbGreen); + fixed f_b1 = itofx(pixel1.rgbBlue); + fixed f_b2 = itofx(pixel2.rgbBlue); + fixed f_b3 = itofx(pixel3.rgbBlue); + fixed f_b4 = itofx(pixel4.rgbBlue); + fixed f_a1 = itofx(pixel1.rgbReserved); + fixed f_a2 = itofx(pixel2.rgbReserved); + fixed f_a3 = itofx(pixel3.rgbReserved); + fixed f_a4 = itofx(pixel4.rgbReserved); + lpDstData[dwDstTotalOffset].rgbRed = (BYTE)fxtoi(Mulfx(f_w1, f_r1) + Mulfx(f_w2, f_r2) + Mulfx(f_w3, f_r3) + Mulfx(f_w4, f_r4)); + lpDstData[dwDstTotalOffset].rgbGreen = (BYTE)fxtoi(Mulfx(f_w1, f_g1) + Mulfx(f_w2, f_g2) + Mulfx(f_w3, f_g3) + Mulfx(f_w4, f_g4)); + lpDstData[dwDstTotalOffset].rgbBlue = (BYTE)fxtoi(Mulfx(f_w1, f_b1) + Mulfx(f_w2, f_b2) + Mulfx(f_w3, f_b3) + Mulfx(f_w4, f_b4)); + lpDstData[dwDstTotalOffset].rgbReserved = (BYTE)fxtoi(Mulfx(f_w1, f_a1) + Mulfx(f_w2, f_a2) + Mulfx(f_w3, f_a3) + Mulfx(f_w4, f_a4)); + + // Update destination horizontal offset + dwDstHorizontalOffset++; + } + + dwDstVerticalOffset += _width; + } + + if (dst) { + dst->_copy(lpData, newWidth, newHeight); + dst->m_maxAlpha = m_maxAlpha; + } + else { + _copy(lpData, newWidth, newHeight); + } + } + } +} + +bool CTxDIB::QIShrink(int newWidth, int newHeight, CTxDIB *dst /*= NULL */) +{ + if (!isValid()) return false; + + if (newWidth > m_width || newHeight > m_height) { + return false; + } + + if (newWidth == m_width && newHeight == m_height) { + if (dst) { + *dst = *this; + } + return true; + } + + LPRGBQUAD newBits = (LPRGBQUAD)malloc(newWidth * newHeight * sizeof(RGBQUAD)); + + const int oldx = m_width; + const int oldy = m_height; + + int accuCellSize = 5; + + unsigned int *accu = new unsigned int[newWidth * accuCellSize]; //array for summing pixels... one pixel for every destination column + unsigned int *accuPtr; //pointer for walking through accu + //each cell consists of blue, red, green component and count of pixels summed in this cell + memset(accu, 0, newWidth * accuCellSize * sizeof(unsigned int)); //clear accu + + //RGB24 version with pointers + LPRGBQUAD destPtr, srcPtr, destPtrS, srcPtrS; //destination and source pixel, and beginnings of current row + srcPtrS = m_bits; + destPtrS = newBits; + int ex = 0, ey = 0; //ex and ey replace division... + int dy = 0; + //(we just add pixels, until by adding newWidth or newHeight we get a number greater than old size... then + // it's time to move to next pixel) + + for (int y = 0; y < oldy; y++) { //for all source rows + ey += newHeight; + ex = 0; //restart with ex = 0 + accuPtr = accu; //restart from beginning of accu + srcPtr = srcPtrS; //and from new source line + + for (int x = 0; x < oldx; x++) { //for all source columns + ex += newWidth; + accuPtr[0] += srcPtr[x].rgbRed; //add current pixel to current accu slot + accuPtr[1] += srcPtr[x].rgbGreen; + accuPtr[2] += srcPtr[x].rgbBlue; + accuPtr[4] += srcPtr[x].rgbReserved; + accuPtr[3]++; + if (ex > oldx) { //when we reach oldx, it's time to move to new slot + accuPtr += accuCellSize; + ex -= oldx; //(substract oldx from ex and resume from there on) + }//if (ex overflow) + }//for x + + if (ey >= oldy) { //now when this happens + ey -= oldy; //it's time to move to new destination row + destPtr = destPtrS; //reset pointers to proper initial values + accuPtr = accu; + for (int k = 0; k < newWidth; k++) { //copy accu to destination row (divided by number of pixels in each slot) + destPtr[k].rgbRed = (BYTE)(accuPtr[0] / accuPtr[3]); + destPtr[k].rgbGreen = (BYTE)(accuPtr[1] / accuPtr[3]); + destPtr[k].rgbBlue = (BYTE)(accuPtr[2] / accuPtr[3]); + destPtr[k].rgbReserved = (BYTE)(accuPtr[4] / accuPtr[3]); + accuPtr += accuCellSize; + }//for k + memset(accu, 0, newWidth * accuCellSize * sizeof(unsigned int)); //clear accu + destPtrS += newWidth; + }//if (ey overflow) + + srcPtrS += m_width; //next round we start from new source row + }//for y + + delete[] accu; //delete helper array + + //copy new image to the destination + if (dst) { + dst->_copy(newBits, newWidth, newHeight); + dst->m_maxAlpha = m_maxAlpha; + } + else { + _copy(newBits, newWidth, newHeight); + } + return true; +} + +BOOL CTxDIB::savePNG(LPCWSTR fileName, int dpi) +{ + FIBITMAP *dib = FreeImage_Allocate(m_width, m_height, 32); + memcpy(FreeImage_GetBits(dib), m_bits, m_width * m_height * 4); + FreeImage_SetDotsPerMeterX(dib, (unsigned int)((double)dpi / 0.0254)); + FreeImage_SetDotsPerMeterY(dib, (unsigned int)((double)dpi / 0.0254)); + BOOL ret = FreeImage_SaveU(FIF_PNG, dib, fileName); + FreeImage_Unload(dib); + return ret; +} + +BOOL CTxDIB::saveJPG(LPCWSTR fileName, int quality, int dpi) +{ + FIBITMAP *dib = FreeImage_Allocate(m_width, m_height, 32); + memcpy(FreeImage_GetBits(dib), m_bits, m_width * m_height * 4); + FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib); + + int flags = JPEG_QUALITYGOOD; + switch (quality) { + case JPEG_QUALITY_SUPER: + flags = JPEG_QUALITYSUPERB; + break; + case JPEG_QUALITY_GOOD: + flags = JPEG_QUALITYGOOD; + break; + case JPEG_QUALITY_NORMAL: + flags = JPEG_QUALITYNORMAL; + break; + case JPEG_QUALITY_AVERAGE: + flags = JPEG_QUALITYAVERAGE; + break; + case JPEG_QUALITY_BAD: + flags = JPEG_QUALITYBAD; + break; + } + + FreeImage_SetDotsPerMeterX(dib24, (unsigned int)((double)dpi / 0.0254)); + FreeImage_SetDotsPerMeterY(dib24, (unsigned int)((double)dpi / 0.0254)); + BOOL ret = FreeImage_SaveU(FIF_JPEG, dib24, fileName, flags); + FreeImage_Unload(dib); + FreeImage_Unload(dib24); + return ret; +} + +BOOL CTxDIB::saveBMP(LPCWSTR fileName) +{ + FIBITMAP *dib = FreeImage_Allocate(m_width, m_height, 32); + memcpy(FreeImage_GetBits(dib), m_bits, m_width * m_height * 4); + BOOL ret = FreeImage_SaveU(FIF_BMP, dib, fileName, BMP_SAVE_RLE); + FreeImage_Unload(dib); + return ret; +} + +BOOL CTxDIB::calcAlpha(CTxDIB *imgWhite, CTxDIB *imgBlack) +{ + if (!imgBlack || !imgWhite) { + return FALSE; + } + + if (imgWhite->getWidth() != imgBlack->getWidth() || + imgWhite->getHeight() != imgBlack->getHeight()) { + return FALSE; + } + + destroy(); + + m_width = imgBlack->getWidth(); + m_height = imgBlack->getHeight(); + size_t cnt = m_width * m_height; + size_t sz = cnt * sizeof(RGBQUAD); + m_bits = (LPRGBQUAD)malloc(sz); + + int alphaR, alphaG, alphaB, resultR, resultG, resultB; + + for (size_t i = 0; i < cnt; i++) { + alphaR = imgBlack->m_bits[i].rgbRed - imgWhite->m_bits[i].rgbRed + 255; + alphaG = imgBlack->m_bits[i].rgbGreen - imgWhite->m_bits[i].rgbGreen + 255; + alphaB = imgBlack->m_bits[i].rgbBlue - imgWhite->m_bits[i].rgbBlue + 255; + + if (alphaG != 0) { + resultR = imgBlack->m_bits[i].rgbRed * 255 / alphaG; + resultG = imgBlack->m_bits[i].rgbGreen * 255 / alphaG; + resultB = imgBlack->m_bits[i].rgbBlue * 255 / alphaG; + } + else { + resultR = 0; + resultG = 0; + resultB = 0; + } + + m_bits[i].rgbReserved = (BYTE)alphaG; + m_bits[i].rgbRed = (BYTE)resultR; + m_bits[i].rgbGreen = (BYTE)resultG; + m_bits[i].rgbBlue = (BYTE)resultB; + PreMulRGBA(m_bits[i]); + } + + return TRUE; +} + +#define RBLOCK 96 + +void CTxDIB::rotateLeft(CTxDIB *dst /*= NULL*/) +{ + if (!isValid()) return; + + int width = getHeight(); + int height = getWidth(); + + size_t sz = width * height * sizeof(RGBQUAD); + LPRGBQUAD newBbits = (LPRGBQUAD)malloc(sz); + + int xs, ys; //x-segment and y-segment + long x, x2, y; + LPRGBQUAD srcPtr; + LPRGBQUAD dstPtr; + for (xs = 0; xs < width; xs += RBLOCK) { //for all image blocks of RBLOCK*RBLOCK pixels + for (ys = 0; ys < height; ys += RBLOCK) { + //RGB24 optimized pixel access: + for (x = xs; x < std::min(width, xs + RBLOCK); x++) { //do rotation + x2 = width - x - 1; + dstPtr = newBbits + ys * width + x; + srcPtr = m_bits + x2 * m_width + ys; + for (y = ys; y < std::min(height, ys + RBLOCK); y++) { + *dstPtr = *srcPtr; + srcPtr++; + dstPtr += width; + }//for y + }//for x + }//for ys + }//for xs + + if (dst) { + dst->_copy(newBbits, width, height, FALSE); + } + else { + _copy(newBbits, width, height, FALSE); + } +} + +void CTxDIB::rotateRight(CTxDIB *dst /*= NULL*/) +{ + if (!isValid()) return; + + int width = getHeight(); + int height = getWidth(); + + size_t sz = width * height * sizeof(RGBQUAD); + LPRGBQUAD newBbits = (LPRGBQUAD)malloc(sz); + + int xs, ys; //x-segment and y-segment + long x, y2, y; + LPRGBQUAD srcPtr; + LPRGBQUAD dstPtr; + + for (xs = 0; xs < width; xs += RBLOCK) { //for all image blocks of RBLOCK*RBLOCK pixels + for (ys = 0; ys < height; ys += RBLOCK) { + //RGB24 optimized pixel access: + for (y = ys; y < std::min(height, ys + RBLOCK); y++) { //do rotation + y2 = height - y - 1; + dstPtr = newBbits + y * width + xs; + srcPtr = m_bits + xs * m_width + y2; + for (x = xs; x < std::min(width, xs + RBLOCK); x++) { + *dstPtr = *srcPtr; + dstPtr++; + srcPtr += m_width; + }//for y + }//for x + }//for ys + }//for xs + + if (dst) { + dst->_copy(newBbits, width, height, FALSE); + } + else { + _copy(newBbits, width, height, FALSE); + } +} + +////////////////////////////////////////////////////////////////////////// + +CTxSkinDIB::CTxSkinDIB() +{ + ZeroMemory(&m_margins, sizeof(m_margins)); + m_tileX = FALSE; + m_tileY = FALSE; +} + +CTxSkinDIB::~CTxSkinDIB() +{ +} + +BOOL CTxSkinDIB::load(LPCWSTR fileName, MARGINS *mg, BOOL tileX, BOOL tileY) +{ + CTxDIB dib; + if (dib.load(fileName)) { + return load(&dib, mg, tileX, tileY); + } + return FALSE; +} + +BOOL CTxSkinDIB::load(CTxDIB *dib, MARGINS *mg, BOOL tileX, BOOL tileY) +{ + if (!dib) return FALSE; + + m_margins = *mg; + m_tileX = tileX; + m_tileY = tileY; + + if (m_margins.cxLeftWidth) { + if (m_margins.cyTopHeight) { + dib->crop(0, + 0, + m_margins.cxLeftWidth, + m_margins.cyTopHeight, + &m_dibLeftTop); + } + if (m_margins.cyBottomHeight) { + dib->crop(0, + dib->getHeight() - m_margins.cyBottomHeight, + m_margins.cxLeftWidth, + dib->getHeight(), + &m_dibLeftBottom); + } + dib->crop(0, + m_margins.cyTopHeight, + m_margins.cxLeftWidth, + dib->getHeight() - m_margins.cyBottomHeight, + &m_dibLeftCenter); + } + + if (m_margins.cxRightWidth) { + if (m_margins.cyTopHeight) { + dib->crop(dib->getWidth() - m_margins.cxRightWidth, + 0, + dib->getWidth(), + m_margins.cyTopHeight, + &m_dibRightTop); + } + if (m_margins.cyBottomHeight) { + dib->crop(dib->getWidth() - m_margins.cxRightWidth, + dib->getHeight() - m_margins.cyBottomHeight, + dib->getWidth(), + dib->getHeight(), + &m_dibRightBottom); + } + dib->crop(dib->getWidth() - m_margins.cxRightWidth, + m_margins.cyTopHeight, + dib->getWidth(), + dib->getHeight() - m_margins.cyBottomHeight, + &m_dibRightCenter); + } + + if (m_margins.cyTopHeight) { + dib->crop(m_margins.cxLeftWidth, + 0, + dib->getWidth() - m_margins.cxRightWidth, + m_margins.cyTopHeight, + &m_dibTop); + } + + if (m_margins.cyBottomHeight) { + dib->crop(m_margins.cxLeftWidth, + dib->getHeight() - m_margins.cyBottomHeight, + dib->getWidth() - m_margins.cxRightWidth, + dib->getHeight(), + &m_dibBottom); + } + + dib->crop(m_margins.cxLeftWidth, + m_margins.cyTopHeight, + dib->getWidth() - m_margins.cxRightWidth, + dib->getHeight() - m_margins.cyBottomHeight, + &m_dibCenter); + + return TRUE; +} + +void CTxSkinDIB::draw(HDC hdc, LPRECT rcDraw, LPRECT rcClip) +{ + RECT rcPart; + RECT rcTmp; + if (m_margins.cxLeftWidth) { + if (m_margins.cyTopHeight) { + rcPart.left = rcDraw->left; + rcPart.right = rcPart.left + m_margins.cxLeftWidth; + rcPart.top = rcDraw->top; + rcPart.bottom = rcPart.top + m_margins.cyTopHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + m_dibLeftTop.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + } + + if (m_margins.cyBottomHeight) { + rcPart.left = rcDraw->left; + rcPart.right = rcPart.left + m_margins.cxLeftWidth; + rcPart.top = rcDraw->bottom - m_margins.cyBottomHeight; + rcPart.bottom = rcPart.top + m_margins.cyBottomHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + m_dibLeftBottom.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + } + + rcPart.left = rcDraw->left; + rcPart.right = rcPart.left + m_margins.cxLeftWidth; + rcPart.top = rcDraw->top + m_margins.cyTopHeight; + rcPart.bottom = rcDraw->bottom - m_margins.cyBottomHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + if (!m_tileY) { + m_dibLeftCenter.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + else { + m_dibLeftCenter.tile(hdc, &rcPart, rcClip); + } + } + + } + + if (m_margins.cxRightWidth) { + if (m_margins.cyTopHeight) { + rcPart.left = rcDraw->right - m_margins.cxRightWidth; + rcPart.right = rcPart.left + m_margins.cxRightWidth; + rcPart.top = rcDraw->top; + rcPart.bottom = rcPart.top + m_margins.cyTopHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + m_dibRightTop.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + } + + if (m_margins.cyBottomHeight) { + rcPart.left = rcDraw->right - m_margins.cxRightWidth; + rcPart.right = rcPart.left + m_margins.cxRightWidth; + rcPart.top = rcDraw->bottom - m_margins.cyBottomHeight; + rcPart.bottom = rcPart.top + m_margins.cyBottomHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + m_dibRightBottom.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + } + + rcPart.left = rcDraw->right - m_margins.cxRightWidth; + rcPart.right = rcPart.left + m_margins.cxRightWidth; + rcPart.top = rcDraw->top + m_margins.cyTopHeight; + rcPart.bottom = rcDraw->bottom - m_margins.cyBottomHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + if (!m_tileY) { + m_dibRightCenter.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + else { + m_dibRightCenter.tile(hdc, &rcPart, rcClip); + } + } + + } + + if (m_margins.cyTopHeight) { + rcPart.left = rcDraw->left + m_margins.cxLeftWidth; + rcPart.right = rcDraw->right - m_margins.cxRightWidth; + rcPart.top = rcDraw->top; + rcPart.bottom = rcDraw->top + m_margins.cyTopHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + if (!m_tileX) { + m_dibTop.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + else { + m_dibTop.tile(hdc, &rcPart, rcClip); + } + } + } + + if (m_margins.cyBottomHeight) { + rcPart.left = rcDraw->left + m_margins.cxLeftWidth; + rcPart.right = rcDraw->right - m_margins.cxRightWidth; + rcPart.top = rcDraw->bottom - m_margins.cyBottomHeight; + rcPart.bottom = rcPart.top + m_margins.cyBottomHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + if (!m_tileX) { + m_dibBottom.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + else { + m_dibBottom.tile(hdc, &rcPart, rcClip); + } + } + } + + rcPart.left = rcDraw->left + m_margins.cxLeftWidth; + rcPart.right = rcDraw->right - m_margins.cxRightWidth; + rcPart.top = rcDraw->top + m_margins.cyTopHeight; + rcPart.bottom = rcDraw->bottom - m_margins.cyBottomHeight; + if (!rcClip || rcClip && IntersectRect(&rcTmp, rcClip, &rcPart)) { + if (!m_tileY && !m_tileX) { + m_dibCenter.draw(hdc, rcPart.left, rcPart.top, rcPart.right - rcPart.left, rcPart.bottom - rcPart.top); + } + else if (m_tileX && m_tileY) { + m_dibCenter.tile(hdc, &rcPart, rcClip); + } + else if (m_tileX && !m_tileY) { + CTxDIB dib; + m_dibCenter.resample(m_dibCenter.getWidth(), rcPart.bottom - rcPart.top, &dib); + dib.tile(hdc, &rcPart, rcClip); + } + else { + CTxDIB dib; + m_dibCenter.resample(rcPart.right - rcPart.left, m_dibCenter.getHeight(), &dib); + dib.tile(hdc, &rcPart, rcClip); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +CTxDibSet::CTxDibSet(CTxDIB *img, int rows, int cols) +{ + m_cols = cols; + m_rows = rows; + m_width = img->getWidth() / cols; + m_height = img->getHeight() / rows; + for (int row = 0; row < rows; row++) { + imgCols vCols; + for (int col = 0; col < cols; col++) { + CTxDIB *frame = new CTxDIB; + img->crop(col * m_width, row * m_height, (col + 1) * m_width, (row + 1) * m_height, frame); + vCols.push_back(frame); + } + m_items.push_back(vCols); + } +} + +CTxDibSet::~CTxDibSet() +{ + for (size_t row = 0; row < m_items.size(); row++) { + for (size_t col = 0; col < m_items[row].size(); col++) { + delete m_items[row][col]; + } + } +} diff --git a/plugins/NewStory/src/TxDIB.h b/plugins/NewStory/src/TxDIB.h new file mode 100644 index 0000000000..2b8212d228 --- /dev/null +++ b/plugins/NewStory/src/TxDIB.h @@ -0,0 +1,319 @@ +#pragma once + +#include <Uxtheme.h> +#include <math.h> +#include <vector> +#include <memory> +#include <algorithm> + +#define JPEG_QUALITY_SUPER 0 +#define JPEG_QUALITY_GOOD 1 +#define JPEG_QUALITY_NORMAL 2 +#define JPEG_QUALITY_AVERAGE 3 +#define JPEG_QUALITY_BAD 4 + +class CTxDIB +{ + LPRGBQUAD m_bits; + int m_width; + int m_height; + BYTE m_maxAlpha; +public: + CTxDIB(void); + CTxDIB(LPCWSTR fileName); + CTxDIB(const CTxDIB& val); + virtual ~CTxDIB(void); + + void operator=(const CTxDIB& val); + + BOOL load(LPCWSTR fileName); + BOOL load(HRSRC hRes, HMODULE hModule = NULL); + BOOL load(LPBYTE data, DWORD size); + BOOL savePNG(LPCWSTR fileName, int dpi = 96); + BOOL saveJPG(LPCWSTR fileName, int quality = JPEG_QUALITY_GOOD, int dpi = 96); + BOOL saveBMP(LPCWSTR fileName); + + BOOL destroy(); + BOOL draw(HDC hdc, int x, int y, int cx = -1, long cy = -1); + BOOL draw(HDC hdc, LPCRECT rcDraw); + BOOL createFromHBITMAP(HBITMAP bmp); + BOOL createFromHICON(HICON ico, BOOL fixAlpha); /* default fixAlpha=TRUE, for shell icons set is to FALSE */ + + HBITMAP createBitmap( HDC hdc = NULL ); + void setTransColor(COLORREF clr); + void resample( int newWidth, int newHeight, CTxDIB* dst = NULL ); + void tile(HDC hdc, LPRECT rcDraw, LPRECT rcClip = NULL); + int getWidth(); + int getHeight(); + void crop(int left, int top, int right, int bottom, CTxDIB* dst = NULL); + void crop(LPCRECT rcCrop, CTxDIB* dst = NULL); + BOOL isValid(); + void setMaxAlpha(BYTE alpha); + BYTE getMaxAlpha(); + void colorize(COLORREF clr); + BOOL calcAlpha(CTxDIB* imgWhite, CTxDIB* imgBlack); + LPRGBQUAD getBits() { return m_bits; } + void PreMulRGBA(RGBQUAD& color); + void rotateLeft(CTxDIB* dst = NULL); + void rotateRight(CTxDIB* dst = NULL); + void _copy( LPRGBQUAD newBits, int newWidth, int newHeight, BOOL copyBits = FALSE ); + BOOL attach( LPVOID dib ); + void PreMultiplyWithAlpha(); +private: + void OverflowCoordinates(float &x, float &y); + RGBQUAD GetPixelColorWithOverflow(long x, long y); + RGBQUAD GetAreaColorInterpolated(float const xc, float const yc, float const w, float const h); + void AddAveragingCont(RGBQUAD const &color, float const surf, float &rr, float &gg, float &bb, float &aa); + RGBQUAD GetPixelColorInterpolated(float x,float y); + void resample2( int newWidth, int newHeight, CTxDIB* dst = NULL ); + bool QIShrink( int newWidth, int newHeight, CTxDIB* dst = NULL ); + + void _copy( const CTxDIB& val ); +}; + +class CTxDibSet +{ + typedef std::vector<CTxDIB*> imgCols; + + std::vector<imgCols> m_items; + int m_width; + int m_height; + int m_cols; + int m_rows; +public: + CTxDibSet(CTxDIB* img, int rows, int cols); + ~CTxDibSet(); + CTxDIB* get(int col, int row) { return m_items[row][col]; } + + + int width() { return m_width; } + int height() { return m_height; } + int rows() { return m_rows; } + int cols() { return m_cols; } +}; + +class CTxSkinDIB +{ + CTxDIB m_dibLeftTop; + CTxDIB m_dibTop; + CTxDIB m_dibRightTop; + + CTxDIB m_dibLeftCenter; + CTxDIB m_dibCenter; + CTxDIB m_dibRightCenter; + + CTxDIB m_dibLeftBottom; + CTxDIB m_dibBottom; + CTxDIB m_dibRightBottom; + + MARGINS m_margins; + BOOL m_tileX; + BOOL m_tileY; +public: + CTxSkinDIB(); + virtual ~CTxSkinDIB(); + + BOOL load(LPCWSTR fileName, MARGINS* mg, BOOL tileX, BOOL tileY); + BOOL load(CTxDIB* dib, MARGINS* mg, BOOL tileX, BOOL tileY); + + void draw(HDC hdc, LPRECT rcDraw, LPRECT rcClip); +}; + +//***bd*** simple floating point point +class CTxDibPoint2 +{ +public: + CTxDibPoint2(); + CTxDibPoint2(float const x_, float const y_); + CTxDibPoint2(CTxDibPoint2 const &p); + + float Distance(CTxDibPoint2 const p2); + float Distance(float const x_, float const y_); + + float x,y; +}; + +//and simple rectangle +class CTxDibRect2 +{ +public: + CTxDibRect2(); + CTxDibRect2(float const x1_, float const y1_, float const x2_, float const y2_); + CTxDibRect2(CTxDibPoint2 const &bl, CTxDibPoint2 const &tr); + CTxDibRect2(CTxDibRect2 const &p); + + float Surface() const; + CTxDibRect2 CrossSection(CTxDibRect2 const &r2); + CTxDibPoint2 Center(); + float Width(); + float Height(); + + CTxDibPoint2 botLeft; + CTxDibPoint2 topRight; +}; + + +inline RGBQUAD CTxDIB::GetPixelColorWithOverflow(long x, long y) +{ + if (!(0 <= y && y < m_height && 0 <= x && x < m_width)) + { + x = std::max((int) x, 0); + x = std::min((int) x, m_width - 1); + y = std::max((int) y, 0); + y = std::min((int) y, m_height - 1); + } + return m_bits[y * m_width + x]; +} + +inline void CTxDIB::OverflowCoordinates( float &x, float &y ) +{ + if (x >= 0 && x < m_width && y >= 0 && y < m_height) + { + return; + } + x = std::max(x, 0.0f); + x = std::min(x, (float) m_width - 1); + y = std::max(y, 0.0f); + y = std::min(y, (float) m_height - 1); +} + +inline void CTxDIB::AddAveragingCont(RGBQUAD const &color, float const surf, float &rr, float &gg, float &bb, float &aa) +{ + rr += color.rgbRed * surf; + gg += color.rgbGreen * surf; + bb += color.rgbBlue * surf; + aa += color.rgbReserved * surf; +} + + +inline void CTxDIB::PreMulRGBA(RGBQUAD& color) +{ + color.rgbRed = (color.rgbRed * color.rgbReserved) / 255; + color.rgbGreen = (color.rgbGreen * color.rgbReserved) / 255; + color.rgbBlue = (color.rgbBlue * color.rgbReserved) / 255; +} + +inline int CTxDIB::getWidth() +{ + return m_width; +} + +inline int CTxDIB::getHeight() +{ + return m_height; +} + + +inline void CTxDIB::crop(LPCRECT rcCrop, CTxDIB* dst /*= NULL*/) +{ + crop(rcCrop->left, rcCrop->top, rcCrop->right, rcCrop->bottom, dst); +} + +inline BOOL CTxDIB::isValid() +{ + return m_bits ? TRUE : FALSE; +} + +inline void CTxDIB::setMaxAlpha(BYTE alpha) +{ + m_maxAlpha = alpha; +} + +inline BYTE CTxDIB::getMaxAlpha() +{ + return m_maxAlpha; +} + +inline BOOL CTxDIB::draw(HDC hdc, LPCRECT rcDraw) +{ + return draw(hdc, rcDraw->left, rcDraw->top, rcDraw->right - rcDraw->left, rcDraw->bottom - rcDraw->top); +} + +inline void CTxDIB::operator=(const CTxDIB& val) +{ + _copy(val); +} + + +inline CTxDibPoint2::CTxDibPoint2() +{ + x=y=0.0f; +} + +inline CTxDibPoint2::CTxDibPoint2(float const x_, float const y_) +{ + x=x_; + y=y_; +} + +inline CTxDibPoint2::CTxDibPoint2(CTxDibPoint2 const &p) +{ + x=p.x; + y=p.y; +} + +inline float CTxDibPoint2::Distance(CTxDibPoint2 const p2) +{ + return (float)sqrt((x-p2.x)*(x-p2.x)+(y-p2.y)*(y-p2.y)); +} + +inline float CTxDibPoint2::Distance(float const x_, float const y_) +{ + return (float)sqrt((x-x_)*(x-x_)+(y-y_)*(y-y_)); +} + +inline CTxDibRect2::CTxDibRect2() +{ +} + +inline CTxDibRect2::CTxDibRect2(float const x1_, float const y1_, float const x2_, float const y2_) +{ + botLeft.x=x1_; + botLeft.y=y1_; + topRight.x=x2_; + topRight.y=y2_; +} + +inline CTxDibRect2::CTxDibRect2(CTxDibRect2 const &p) +{ + botLeft=p.botLeft; + topRight=p.topRight; +} + +inline float CTxDibRect2::Surface() const +/* + * Returns the surface of rectangle. + */ +{ + return (topRight.x-botLeft.x)*(topRight.y-botLeft.y); +} + +inline CTxDibRect2 CTxDibRect2::CrossSection(CTxDibRect2 const &r2) +{ + CTxDibRect2 cs; + cs.botLeft.x =std::max(botLeft.x, r2.botLeft.x); + cs.botLeft.y = std::max(botLeft.y, r2.botLeft.y); + cs.topRight.x = std::min(topRight.x, r2.topRight.x); + cs.topRight.y = std::min(topRight.y, r2.topRight.y); + if (cs.botLeft.x<=cs.topRight.x && cs.botLeft.y<=cs.topRight.y) { + return cs; + } else { + return CTxDibRect2(0,0,0,0); + }//if +} + +inline CTxDibPoint2 CTxDibRect2::Center() +{ + return CTxDibPoint2((topRight.x+botLeft.x)/2.0f, (topRight.y+botLeft.y)/2.0f); +} + +inline float CTxDibRect2::Width() +{ + return topRight.x-botLeft.x; +} + +inline float CTxDibRect2::Height() +{ + return topRight.y-botLeft.y; +} + diff --git a/plugins/NewStory/src/history_control.cpp b/plugins/NewStory/src/history_control.cpp index 558fd4ca03..b5e323d412 100644 --- a/plugins/NewStory/src/history_control.cpp +++ b/plugins/NewStory/src/history_control.cpp @@ -1178,6 +1178,18 @@ LRESULT CALLBACK NewstoryListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM InvalidateRect(hwnd, 0, FALSE);
break;
+ case NSM_IMAGE_LOADED:
+ /* {
+ int eventCount = data->totalCount;
+ for (int i = 0; i < eventCount; i++) {
+ auto *p = data->GetItem(i);
+ if (p->m_doc)
+ p->m_doc->render(data->cachedWindowWidth);
+ }
+ }
+ InvalidateRect(hwnd, 0, FALSE);*/
+ break;
+
case UM_ADDEVENT:
if (data->pMsgDlg == nullptr)
data->AddEvent(wParam, lParam, 1);
diff --git a/plugins/NewStory/src/history_control.h b/plugins/NewStory/src/history_control.h index 188e39f037..3667a22d4e 100644 --- a/plugins/NewStory/src/history_control.h +++ b/plugins/NewStory/src/history_control.h @@ -8,6 +8,7 @@ struct NewstoryListData; class NSWebPage : public windows_container
{
NewstoryListData &ctrl;
+ cairo_images_cache m_images;
cairo_surface_t *get_image(const std::string &url) override;
@@ -20,6 +21,8 @@ class NSWebPage : public windows_container void set_clip(const litehtml::position &pos, const litehtml::border_radiuses &bdr_radius) override;
void set_cursor(const char *cursor) override;
+ void on_image_loaded(const wchar_t *file, const wchar_t *url, bool redraw_only);
+
public:
NSWebPage(NewstoryListData &_1) :
ctrl(_1)
diff --git a/plugins/NewStory/src/main.cpp b/plugins/NewStory/src/main.cpp index 6ce6d2134c..82c6b31edd 100644 --- a/plugins/NewStory/src/main.cpp +++ b/plugins/NewStory/src/main.cpp @@ -136,9 +136,18 @@ static int evtTopToolbar(WPARAM, LPARAM) return 0;
}
+static int evtModuleLoaded(WPARAM, LPARAM)
+{
+ g_plugin.bHasSmileys = ServiceExists(MS_SMILEYADD_BATCHPARSE);
+ return 0;
+}
+
static int evtModulesLoaded(WPARAM, LPARAM)
{
HookEvent(ME_TTB_MODULELOADED, evtTopToolbar);
+ HookEvent(ME_SYSTEM_MODULELOAD, evtModuleLoaded);
+ HookEvent(ME_SYSTEM_MODULEUNLOAD, evtModuleLoaded);
+ evtModuleLoaded(0, 0);
InitFonts();
InitNewstoryControl();
diff --git a/plugins/NewStory/src/stdafx.h b/plugins/NewStory/src/stdafx.h index dbdc97963e..ebab8c737f 100644 --- a/plugins/NewStory/src/stdafx.h +++ b/plugins/NewStory/src/stdafx.h @@ -65,10 +65,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "m_PluginUpdater.h"
#include "m_smileyadd.h"
-#include "../../Libs/litehtml/include/cairo.h"
-#include "../../Libs/litehtml/include/litehtml.h"
-#include "../../Libs/litehtml/containers/windows/cairo/cairo_font.h"
-#include "../../Libs/litehtml/containers/windows/cairo/windows_container.h"
+#include "../../Libs/freeimage/src/FreeImage.h"
+
+#include <../include/cairo.h>
+#include <../include/litehtml.h>
+#include <../containers/windows/cairo/cairo_font.h>
+#include <../containers/windows/cairo/windows_container.h>
+#include <../containers/cairo/cairo_images_cache.h>
#include "dib.h"
#include "resource.h"
@@ -106,7 +109,7 @@ struct CMPlugin : public PLUGIN<CMPlugin> // thesw options are a copy of static CMOption to keep performance high
bool bMsgGrouping, bDrawEdge, bHppCompat, bDisableDelete = false;
- bool bShowType, bShowDirection;
+ bool bShowType, bShowDirection, bHasSmileys;
COLORREF clCustom[5];
diff --git a/plugins/NewStory/src/templates.cpp b/plugins/NewStory/src/templates.cpp index 030866aaa2..9d73c35d1b 100644 --- a/plugins/NewStory/src/templates.cpp +++ b/plugins/NewStory/src/templates.cpp @@ -143,6 +143,8 @@ CMStringA ItemData::formatHtml(const wchar_t *pwszStr) CMStringA szBody;
AppendString(szBody, T2Utf((pwszStr) ? pwszStr : formatString()).get());
UrlAutodetect(szBody);
+ if (g_plugin.bHasSmileys)
+ ReplaceSmileys(hContact, szBody);
str += szBody;
str.Append("</body></html>");
diff --git a/plugins/NewStory/src/utils.cpp b/plugins/NewStory/src/utils.cpp index f24cd3bd4c..66ad836a9a 100644 --- a/plugins/NewStory/src/utils.cpp +++ b/plugins/NewStory/src/utils.cpp @@ -156,3 +156,32 @@ void UrlAutodetect(CMStringA &str) p = str.c_str() + pos + newText.GetLength();
}
}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void ReplaceSmileys(MCONTACT hContact, CMStringA &str)
+{
+ SMADD_BATCHPARSE sp;
+ sp.Protocolname = Proto_GetBaseAccountName(hContact);
+ sp.flag = SAFL_PATH;
+ sp.str.a = str.c_str();
+ sp.hContact = hContact;
+
+ if (auto *spRes = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp)) {
+ for (int i = (int)sp.numSmileys-1; i >= 0; i--) {
+ auto &smiley = spRes[i];
+ if (mir_wstrlen(smiley.filepath) > 0) {
+ CMStringA szText(str.Mid(smiley.startChar, smiley.size));
+ str.Delete(smiley.startChar, smiley.size);
+
+ CMStringA szNew;
+ if (sp.oflag & SAFL_UNICODE)
+ szNew.Format("<img class=\"img\" src=\"file://%s\" title=\"%s\" alt=\"%s\" />", T2Utf(smiley.filepath).get(), szText.c_str(), szText.c_str());
+ else
+ szNew.Format("<img class=\"img\" src=\"file://%s\" title=\"%s\" alt=\"%s\" />", (char *)smiley.filepath, szText.c_str(), szText.c_str());
+ str.Insert(smiley.startChar, szNew);
+ }
+ }
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spRes);
+ }
+}
diff --git a/plugins/NewStory/src/utils.h b/plugins/NewStory/src/utils.h index 35da06c39a..24b65beec1 100644 --- a/plugins/NewStory/src/utils.h +++ b/plugins/NewStory/src/utils.h @@ -9,5 +9,6 @@ bool NSMenu_Process(int iCommand, NewstoryListData *data); int GetFontHeight(const LOGFONTA &lf);
+void ReplaceSmileys(MCONTACT hContact, CMStringA &str);
void UrlAutodetect(CMStringA &str);
void RemoveBbcodes(CMStringW &pwszText);
diff --git a/plugins/NewStory/src/webpage.cpp b/plugins/NewStory/src/webpage.cpp index a959fce923..1c74ea683c 100644 --- a/plugins/NewStory/src/webpage.cpp +++ b/plugins/NewStory/src/webpage.cpp @@ -17,15 +17,47 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "stdafx.h" +#include "TxDIB.h" ///////////////////////////////////////////////////////////////////////////////////////// // Litehtml interface CRITICAL_SECTION cairo_font::m_sync; -cairo_surface_t* NSWebPage::get_image(const std::string &) +cairo_surface_t *dib_to_surface(CTxDIB &img) { - return 0; + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, img.getWidth(), img.getHeight()); + unsigned char *dst = cairo_image_surface_get_data(surface); + unsigned char *src = (unsigned char *)img.getBits(); + int line_size = img.getWidth() * 4; + int dst_offset = img.getWidth() * (img.getHeight() - 1) * 4; + int src_offset = 0; + for (int i = 0; i < img.getHeight(); i++, src_offset += line_size, dst_offset -= line_size) { + memcpy(dst + dst_offset, src + src_offset, line_size); + } + cairo_surface_mark_dirty(surface); + return surface; +} + +void NSWebPage::on_image_loaded(const wchar_t *file, const wchar_t *url, bool redraw_only) +{ + if (!mir_wstrncmp(file, L"file://", 7)) + file += 7; + + CTxDIB img; + if (img.load(file)) { + cairo_surface_t *surface = dib_to_surface(img); + m_images.add_image(T2Utf(url).get(), surface); + + PostMessage(ctrl.m_hwnd, NSM_IMAGE_LOADED, redraw_only, 0); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +cairo_surface_t* NSWebPage::get_image(const std::string &url) +{ + return m_images.get_image(url); } void NSWebPage::get_client_rect(litehtml::position &pos) const @@ -38,8 +70,11 @@ void NSWebPage::import_css(litehtml::string &, const litehtml::string &, litehtm { } -void NSWebPage::load_image(const char *, const char *, bool) +void NSWebPage::load_image(const char *src, const char */*baseUrl*/, bool redraw_on_ready) { + Utf2T wszUrl(src); + if (m_images.reserve(src)) + on_image_loaded(wszUrl, wszUrl, redraw_on_ready); } void NSWebPage::on_anchor_click(const char *pszUtl, const litehtml::element::ptr &) |