/* * (c) Copyright Ascensio System SIA 2010-2023 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ #include "MetaFileUtils.h" #include "../../ImageFileFormatChecker.h" #include "../../BgraFrame.h" #include #include #include #include #include #ifndef DIB_RGB_COLORS #define DIB_RGB_COLORS 0x00 #endif #define MINACCURACY 5 #define MAXACCURACY 10 namespace MetaFile { bool Equals(double dFirst, double dSecond, double dEpsilon) { return std::abs(dFirst - dSecond) <= dEpsilon; } unsigned char GetLowestBit(unsigned int ulValue) { if (0 == ulValue) return 0; unsigned char unOffset = 0; unsigned int ulBit = 1; while (!(ulValue & ulBit)) { ulBit = ulBit << 1; unOffset++; // ограничиваемся 32-битами if (ulBit & 0x80000000) return 0; } return unOffset; } bool ReadImageCoreHeader(BYTE* pHeaderBuffer, unsigned int ulHeaderBufferLen, BYTE* pImageBuffer, unsigned int ulImageBufferLen, BYTE** ppDstBuffer, unsigned int* pulWidth, unsigned int* pulHeight) { CDataStream oHeaderStream; oHeaderStream.SetStream(pHeaderBuffer, ulHeaderBufferLen); unsigned short ushWidth; unsigned short ushHeight; unsigned short ushPlanes; unsigned short ushBitCount; oHeaderStream >> ushWidth; oHeaderStream >> ushHeight; oHeaderStream >> ushPlanes; oHeaderStream >> ushBitCount; if (0x0001 != ushPlanes) return false; return false; } bool ReadImageInfoHeader(BYTE* pHeaderBuffer, unsigned int ulHeaderBufferLen, BYTE* pImageBuffer, unsigned int ulImageBufferLen, BYTE** ppDstBuffer, unsigned int* pulWidth, unsigned int* pulHeight, unsigned int& unColorUsed) { CDataStream oHeaderStream; oHeaderStream.SetStream(pHeaderBuffer, ulHeaderBufferLen); int nWidth; int nHeight; unsigned short ushPlanes; unsigned short ushBitCount; unsigned int unCompression; unsigned int unImageSize; unsigned int unXPelsPerMeter; unsigned int unYPelsPerMeter; unsigned int unColorImportant; oHeaderStream >> nWidth; oHeaderStream >> nHeight; oHeaderStream >> ushPlanes; oHeaderStream >> ushBitCount; oHeaderStream >> unCompression; oHeaderStream >> unImageSize; oHeaderStream >> unXPelsPerMeter; oHeaderStream >> unYPelsPerMeter; oHeaderStream >> unColorUsed; oHeaderStream >> unColorImportant; if (0x0001 != ushPlanes) return false; if (nHeight < 0x00000000 && (BI_RGB != unCompression)) return false; if (nWidth < 0) return false; BYTE* pBgraBuffer = NULL; unsigned int ulWidth = 0; unsigned int ulHeight = 0; BYTE* pBuffer = pImageBuffer; int lBufLen = ulImageBufferLen; *ppDstBuffer = NULL; *pulWidth = 0; *pulHeight = 0; if (BI_BITCOUNT_0 == ushBitCount) // Значит компрессия либо PNG, либо JPEG { if (BI_JPEG != unCompression || BI_PNG != unCompression) return false; #ifdef METAFILE_DISABLE_FILESYSTEM return false; #endif std::wstring wsTempFileName = GetTempFilename(); if (wsTempFileName.empty()) return false; NSFile::CFileBinary oFile; if (!oFile.CreateFileW(wsTempFileName)) return false; oFile.WriteFile(pBuffer, (DWORD)unImageSize); oFile.CloseFile(); CBgraFrame oFrame; oFrame.OpenFile(wsTempFileName); // TODO: Как будут файлы сделать чтение. NSFile::CFileBinary::Remove(wsTempFileName); return false; } else if (BI_BITCOUNT_1 == ushBitCount) { // Двуцветная картинка, значит палитра состоит из 2-х цветов TRgbQuad oColor1, oColor2; if (oHeaderStream.CanRead() >= 8) { oHeaderStream >> oColor1 >> oColor2; } else { oColor1.r = oColor1.g = oColor1.b = 0; oColor2.r = oColor2.g = oColor2.b = 255; } // Считываем саму картинку int lCalcLen = (((nWidth * ushPlanes * ushBitCount + 31) & ~31) / 8) * abs(nHeight); if (lCalcLen > lBufLen) return false; pBgraBuffer = new BYTE[nWidth * abs(nHeight) * 4 * sizeof(BYTE)]; if (NULL == pBgraBuffer) return false; ulHeight = (unsigned short)abs(nHeight); ulWidth = (unsigned short)nWidth; int nWidthBytes = (nWidth + 7) / 8; int nAdditBytes = 4 - div_t(div(((nWidth + 7) / 8), 4)).rem; if (4 == nAdditBytes) nAdditBytes = 0; int nLastBitCount = div_t(div(nWidth, 8)).rem - 1; if (-1 == nLastBitCount) nLastBitCount = 7; nLastBitCount = (int)pow((double)2, (double)nLastBitCount); if (nHeight < 0) { for (int nY = 0; nY < abs(nHeight); nY++) { int nIndex = 4 * nWidth * nY; for (int nX = 0; nX < nWidthBytes; nX++) { int nByte = *pBuffer; pBuffer++; lBufLen--; int nBitCount = 128; int nAlpha = 255; if (nX == nWidthBytes - 1) { // Не до конца заполненный байт иногда заполняется странным цветом, поэтому мы делаем его прозрачным nBitCount = nLastBitCount; nAlpha = 0; } for (int nBitIndex = nBitCount; nBitIndex > 0; nBitIndex /= 2) { int nBit = (nByte & nBitIndex); TRgbQuad* pColor = (nBit ? &oColor2 : &oColor1); pBgraBuffer[nIndex + 0] = pColor->b; pBgraBuffer[nIndex + 1] = pColor->g; pBgraBuffer[nIndex + 2] = pColor->r; pBgraBuffer[nIndex + 3] = nAlpha; nIndex += 4; } } for (int nAddIndex = 0; nAddIndex < nAdditBytes; nAddIndex++) { // int nByte = *pBuffer; ++pBuffer; --lBufLen; } } } else { for (int nY = abs(nHeight) - 1; nY >= 0; nY--) { int nIndex = 4 * nWidth * nY; for (int nX = 0; nX < nWidthBytes; nX++) { int nByte = *pBuffer; pBuffer++; lBufLen--; int nBitCount = 128; int nAlpha = 255; if (nX == nWidthBytes - 1) { // Не до конца заполненный байт иногда заполняется странным цветом, поэтому мы делаем его прозрачным nBitCount = nLastBitCount; //nAlpha = 0; } for (int nBitIndex = nBitCount; nBitIndex > 0; nBitIndex /= 2) { int nBit = (nByte & nBitIndex); TRgbQuad* pColor = (nBit ? &oColor2 : &oColor1); pBgraBuffer[nIndex + 0] = pColor->b; pBgraBuffer[nIndex + 1] = pColor->g; pBgraBuffer[nIndex + 2] = pColor->r; pBgraBuffer[nIndex + 3] = nAlpha; nIndex += 4; } } for (int nAddIndex = 0; nAddIndex < nAdditBytes; nAddIndex++) { // int nByte = *pBuffer; ++pBuffer; --lBufLen; } } } *ppDstBuffer = pBgraBuffer; *pulWidth = ulWidth; *pulHeight = ulHeight; return true; } else if (BI_BITCOUNT_2 == ushBitCount) { unsigned char unColorTableLen = 16; if (0 != unColorUsed) unColorTableLen = (std::min)((unsigned char)16, (unsigned char)unColorUsed); TRgbQuad oColorTable[16]; if (oHeaderStream.CanRead() < unColorTableLen * 4) return false; // Считываем палитру for (unsigned short ushIndex = 0; ushIndex < unColorTableLen; ushIndex++) { oHeaderStream >> oColorTable[ushIndex]; } // 4 бита - 1 пиксел // Считываем саму картинку int lCalcLen = (((nWidth * ushPlanes * ushBitCount + 31) & ~31) / 8) * abs(nHeight); if (lCalcLen != lBufLen) return false; // Ширина в байтах должна быть кратна 4, значит сама ширина должна быть кратна 8 int nAdd = 0; while (0 != div_t(div(nWidth + nAdd, 8)).rem) { nAdd++; } int nScanLineBytes = (nWidth + nAdd) / 2; if (lBufLen < (nScanLineBytes * abs(nHeight))) return false; pBgraBuffer = new BYTE[nWidth * abs(nHeight) * 4 * sizeof(BYTE)]; if (NULL == pBgraBuffer) return false; ulHeight = (unsigned short)abs(nHeight); ulWidth = (unsigned short)nWidth; if (nHeight < 0) { for (int nY = 0; nY < abs(nHeight); nY++) { for (int nLineIndex = 0; nLineIndex < nScanLineBytes; nLineIndex++) { BYTE nByte = *pBuffer; pBuffer++; int nX = nLineIndex * 2; int nIndex = 4 * (nWidth * nY + nX); BYTE nColorIndex = (nByte & 0xf0) >> 4; if (nX < nWidth) { pBgraBuffer[nIndex + 0] = oColorTable[nColorIndex].b; pBgraBuffer[nIndex + 1] = oColorTable[nColorIndex].g; pBgraBuffer[nIndex + 2] = oColorTable[nColorIndex].r; pBgraBuffer[nIndex + 3] = 255; } nX = nLineIndex * 2 + 1; nIndex = 4 * (nWidth * nY + nX); nColorIndex = nByte & 0x0f; if (nX < nWidth) { pBgraBuffer[nIndex + 0] = oColorTable[nColorIndex].b; pBgraBuffer[nIndex + 1] = oColorTable[nColorIndex].g; pBgraBuffer[nIndex + 2] = oColorTable[nColorIndex].r; pBgraBuffer[nIndex + 3] = 255; } } } } else { for (int nY = abs(nHeight) - 1; nY >= 0; nY--) { for (int nLineIndex = 0; nLineIndex < nScanLineBytes; nLineIndex++) { BYTE nByte = *pBuffer; pBuffer++; int nX = nLineIndex * 2; int nIndex = 4 * (nWidth * nY + nX); BYTE nColorIndex = (nByte & 0xf0) >> 4; if (nX < nWidth) { pBgraBuffer[nIndex + 0] = oColorTable[nColorIndex].b; pBgraBuffer[nIndex + 1] = oColorTable[nColorIndex].g; pBgraBuffer[nIndex + 2] = oColorTable[nColorIndex].r; pBgraBuffer[nIndex + 3] = 255; } nX = nLineIndex * 2 + 1; nIndex = 4 * (nWidth * nY + nX); nColorIndex = nByte & 0x0f; if (nX < nWidth) { pBgraBuffer[nIndex + 0] = oColorTable[nColorIndex].b; pBgraBuffer[nIndex + 1] = oColorTable[nColorIndex].g; pBgraBuffer[nIndex + 2] = oColorTable[nColorIndex].r; pBgraBuffer[nIndex + 3] = 255; } } } } *ppDstBuffer = pBgraBuffer; *pulWidth = ulWidth; *pulHeight = ulHeight; return true; } else if (BI_BITCOUNT_3 == ushBitCount) { unsigned short ushColorTableLen = 256; if (0 != unColorUsed) ushColorTableLen = (std::min)((unsigned short)256, (unsigned short)unColorUsed); TRgbQuad oColorTable[256]; if (oHeaderStream.CanRead() < ushColorTableLen * 4) return false; // Считываем палитру for (unsigned short ushIndex = 0; ushIndex < ushColorTableLen; ushIndex++) { oHeaderStream >> oColorTable[ushIndex]; } // 1 байт - 1 пиксел // Ширина должна быть кратна 4. int nAdd = 0; while (0 != div_t(div(nWidth + nAdd, 4)).rem) { nAdd++; } BYTE* pUncompressedBuffer = NULL; if (BI_RLE8 == unCompression) { int nStride = nWidth + nAdd; int lUncompressedLen = nStride * abs(nHeight); pUncompressedBuffer = new BYTE[lUncompressedLen]; if (!pUncompressedBuffer) return false; for (int nPos = 0, nUncompressedPos = 0, nLinePos = 0; nPos < lBufLen; nPos += 2) { BYTE nCount = pBuffer[nPos]; BYTE nColor = pBuffer[nPos + 1]; if (nCount == 0) { // End of line. if (0 == nColor) { if (nLinePos < nStride) { int nAdditionalSpace = nStride - nLinePos; if (nUncompressedPos + nAdditionalSpace > lUncompressedLen) break; ::memset(pUncompressedBuffer + nUncompressedPos, 0, nAdditionalSpace); nUncompressedPos += nAdditionalSpace; } nLinePos = 0; continue; } // End of bitmap. else if (1 == nColor) { break; } else { // TODO: 2 break; } } nLinePos += nCount; while (nLinePos >= nStride) { nLinePos -= nStride; } if (nUncompressedPos + nCount > lUncompressedLen) break; for (int i = 0; i < nCount; ++i) pUncompressedBuffer[nUncompressedPos + i] = nColor; ::memset(pUncompressedBuffer + nUncompressedPos, nColor, nCount); nUncompressedPos += nCount; } pBuffer = pUncompressedBuffer; lBufLen = lUncompressedLen; } if (lBufLen < (nWidth + nAdd) * abs(nHeight)) { if (pUncompressedBuffer) delete[] pUncompressedBuffer; return false; } pBgraBuffer = new BYTE[nWidth * abs(nHeight) * 4 * sizeof(BYTE)]; if (NULL == pBgraBuffer) return false; ulHeight = (unsigned short)abs(nHeight); ulWidth = (unsigned short)nWidth; if (nHeight < 0) { for (int nY = 0; nY < abs(nHeight); nY++) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * (nWidth * nY + nX); BYTE nByte = *pBuffer; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 0] = oColorTable[nByte].b; pBgraBuffer[nIndex + 1] = oColorTable[nByte].g; pBgraBuffer[nIndex + 2] = oColorTable[nByte].r; pBgraBuffer[nIndex + 3] = 255; } pBuffer += nAdd; lBufLen -= nAdd; } } else { for (int nY = abs(nHeight) - 1; nY >= 0; nY--) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * (nWidth * nY + nX); BYTE nByte = *pBuffer; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 0] = oColorTable[nByte].b; pBgraBuffer[nIndex + 1] = oColorTable[nByte].g; pBgraBuffer[nIndex + 2] = oColorTable[nByte].r; pBgraBuffer[nIndex + 3] = 255; } pBuffer += nAdd; lBufLen -= nAdd; } } *ppDstBuffer = pBgraBuffer; *pulWidth = ulWidth; *pulHeight = ulHeight; if (pUncompressedBuffer) delete[] pUncompressedBuffer; return true; } else if (BI_BITCOUNT_4 == ushBitCount) { unsigned int ulMaskR = 0x1f, ulMaskB = 0x7C00, ulMaskG = 0x3E0; unsigned int ulShiftR = 0, ulShiftB = 10, ulShiftG = 5; double dKoefR = 255 / 31.0, dKoefB = 255 / 31.0, dKoefG = 255 / 31.0; if (BI_RGB == unCompression) { // Маски, сдвиги и коэффициенты уже заполнены стандартными значениями для масок // 000000000011111 - Red // 000001111100000 - Green // 111110000000000 - Blue } else if (BI_BITFIELDS == unCompression) { oHeaderStream >> ulMaskB; oHeaderStream >> ulMaskG; oHeaderStream >> ulMaskR; ulShiftR = GetLowestBit(ulMaskR); ulShiftB = GetLowestBit(ulMaskB); ulShiftG = GetLowestBit(ulMaskG); dKoefR = 255.0 / (ulMaskR >> ulShiftR); dKoefG = 255.0 / (ulMaskG >> ulShiftG); dKoefB = 255.0 / (ulMaskB >> ulShiftB); } else return false; // Считываем саму картинку int lCalcLen = (((nWidth * ushPlanes * ushBitCount + 31) & ~31) / 8) * abs(nHeight); if (lCalcLen != lBufLen) return false; // 2 байт на все каналы канал // (Ширина * 3) должна быть кратна 4. int nAdd = 0; while (0 != div_t(div(2 * nWidth + nAdd, 4)).rem) { nAdd++; } pBgraBuffer = new BYTE[nWidth * abs(nHeight) * 4 * sizeof(BYTE)]; if (NULL == pBgraBuffer) return false; ulHeight = (unsigned short)abs(nHeight); ulWidth = (unsigned short)nWidth; if (nHeight < 0) { for (int nY = 0; nY < abs(nHeight); nY++) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * (nWidth * nY + nX); unsigned short ushValue = ((pBuffer[1] << 8) | pBuffer[0]) & 0xFFFF; pBuffer += 2; lBufLen -= 2; unsigned char unR = (ushValue & ulMaskR) >> ulShiftR; unsigned char unG = (ushValue & ulMaskG) >> ulShiftG; unsigned char unB = (ushValue & ulMaskB) >> ulShiftB; pBgraBuffer[nIndex + 0] = (unsigned char)(unR * dKoefR); pBgraBuffer[nIndex + 1] = (unsigned char)(unG * dKoefG); pBgraBuffer[nIndex + 2] = (unsigned char)(unB * dKoefB); pBgraBuffer[nIndex + 3] = 255; } pBuffer += nAdd; lBufLen -= nAdd; } } else { for (int nY = abs(nHeight) - 1; nY >= 0; nY--) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * (nWidth * nY + nX); unsigned short ushValue = ((pBuffer[1] << 8) | pBuffer[0]) & 0xFFFF; pBuffer += 2; lBufLen -= 2; unsigned char unR = (ushValue & ulMaskR) >> ulShiftR; unsigned char unG = (ushValue & ulMaskG) >> ulShiftG; unsigned char unB = (ushValue & ulMaskB) >> ulShiftB; pBgraBuffer[nIndex + 0] = (unsigned char)(unR * dKoefR); pBgraBuffer[nIndex + 1] = (unsigned char)(unG * dKoefG); pBgraBuffer[nIndex + 2] = (unsigned char)(unB * dKoefB); pBgraBuffer[nIndex + 3] = 255; } pBuffer += nAdd; lBufLen -= nAdd; } } *ppDstBuffer = pBgraBuffer; *pulWidth = ulWidth; *pulHeight = ulHeight; return true; } else if (BI_BITCOUNT_5 == ushBitCount) { if (BI_RGB != unCompression) return false; // TODO: Сделать данный вариант, как только будет файлы с данным типом // Считываем саму картинку int lCalcLen = (((nWidth * ushPlanes * ushBitCount + 31) & ~31) / 8) * abs(nHeight); if (lCalcLen != lBufLen) return false; // 1 байт на каждый канал // (Ширина * 3) должна быть кратна 4. int nAdd = 0; while (0 != div_t(div(3 * nWidth + nAdd, 4)).rem) { nAdd++; } pBgraBuffer = new BYTE[nWidth * abs(nHeight) * 4 * sizeof(BYTE)]; if (NULL == pBgraBuffer) return false; ulHeight = (unsigned short)abs(nHeight); ulWidth = (unsigned short)nWidth; if (nHeight < 0) { for (int nY = 0; nY < abs(nHeight); nY++) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * (nWidth * nY + nX); pBgraBuffer[nIndex + 0] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 1] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 2] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 3] = 255; } pBuffer += nAdd; lBufLen -= nAdd; } } else { for (int nY = abs(nHeight) - 1; nY >= 0; nY--) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * (nWidth * nY + nX); pBgraBuffer[nIndex + 0] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 1] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 2] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 3] = 255; } pBuffer += nAdd; lBufLen -= nAdd; } } *ppDstBuffer = pBgraBuffer; *pulWidth = ulWidth; *pulHeight = ulHeight; return true; } else if (BI_BITCOUNT_6 == ushBitCount) { unsigned int ulMaskR = 0xff000000, ulMaskB = 0x00ff0000, ulMaskG = 0x0000ff00; if (BI_RGB == unCompression) { // Маски, сдвиги и коэффициенты уже заполнены стандартными значениями для масок } else if (BI_BITFIELDS == unCompression) { if (oHeaderStream.CanRead() < 12) return false; oHeaderStream >> ulMaskB; oHeaderStream >> ulMaskG; oHeaderStream >> ulMaskR; } else return false; // Считываем саму картинку int lCalcLen = (((nWidth * ushPlanes * ushBitCount + 31) & ~31) / 8) * abs(nHeight); if (lCalcLen != lBufLen) return false; // 1 байт на каждый канал // Ширина должна быть кратна 4. int nAdd = 0; while (0 != div_t(div(nWidth + nAdd, 4)).rem) { nAdd++; } pBgraBuffer = new BYTE[(nWidth + nAdd) * abs(nHeight) * 4 * sizeof(BYTE)]; if (NULL == pBgraBuffer) return false; ulHeight = (unsigned short)abs(nHeight); ulWidth = (unsigned short)(nWidth + nAdd); if (nHeight < 0) { for (int nY = 0; nY < abs(nHeight); nY++) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * ((nWidth + nAdd) * nY + nX); pBgraBuffer[nIndex + 0] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 1] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 2] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 3] = pBuffer[0]; pBuffer++; lBufLen--; // Если брать значение из картинки, тогда она получается всегда прозрачной } for (int nX = nWidth; nX < nWidth + nAdd; nX++) { int nIndex = 4 * ((nWidth + nAdd) * nY + nX); pBgraBuffer[nIndex + 0] = 255; pBgraBuffer[nIndex + 1] = 255; pBgraBuffer[nIndex + 2] = 255; pBgraBuffer[nIndex + 3] = 0; } } } else { for (int nY = abs(nHeight) - 1; nY >= 0; nY--) { for (int nX = 0; nX < nWidth; nX++) { int nIndex = 4 * ((nWidth + nAdd) * nY + nX); pBgraBuffer[nIndex + 0] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 1] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 2] = pBuffer[0]; pBuffer++; lBufLen--; pBgraBuffer[nIndex + 3] = pBuffer[0]; pBuffer++; lBufLen--; // Если брать значение из картинки, тогда она получается всегда прозрачной } for (int nX = nWidth; nX < nWidth + nAdd; nX++) { int nIndex = 4 * ((nWidth + nAdd) * nY + nX); pBgraBuffer[nIndex + 0] = 255; pBgraBuffer[nIndex + 1] = 255; pBgraBuffer[nIndex + 2] = 255; pBgraBuffer[nIndex + 3] = 0; } } } *ppDstBuffer = pBgraBuffer; *pulWidth = ulWidth; *pulHeight = ulHeight; return true; } return false; } void ReadImage(BYTE* pHeaderBuffer, unsigned int ulHeaderBufferLen, BYTE* pImageBuffer, unsigned int ulImageBufferLen, BYTE** ppDstBuffer, unsigned int* pulWidth, unsigned int* pulHeight, unsigned int& unColorUsed) { if (ulHeaderBufferLen <= 0 || NULL == pHeaderBuffer || NULL == pImageBuffer || ulImageBufferLen < 0) return; CDataStream oHeaderStream; oHeaderStream.SetStream(pHeaderBuffer, ulHeaderBufferLen); // Считываем заголовок unsigned int ulHeaderSize; oHeaderStream >> ulHeaderSize; if (ulHeaderSize < 0x0000000C) return; else if (0x0000000C == ulHeaderSize) // BitmapCoreHeader ReadImageCoreHeader(pHeaderBuffer + 4, ulHeaderBufferLen - 4, pImageBuffer, ulImageBufferLen, ppDstBuffer, pulWidth, pulHeight); else // BitmapInfoHeader ReadImageInfoHeader(pHeaderBuffer + 4, ulHeaderBufferLen - 4, pImageBuffer, ulImageBufferLen, ppDstBuffer, pulWidth, pulHeight, unColorUsed); } void ReadImage(BYTE* pImageBuffer, unsigned int unBufferLen, unsigned int unColorUsage, BYTE** ppDstBuffer, unsigned int* punWidth, unsigned int* punHeight, unsigned int& unColorUsed) { if (unBufferLen <= 0 || NULL == pImageBuffer) return; CDataStream oHeaderStream; oHeaderStream.SetStream(pImageBuffer, unBufferLen); // Считываем заголовок unsigned int unHeaderSize; oHeaderStream >> unHeaderSize; if (unHeaderSize > unBufferLen) return; else if (unHeaderSize < 0x0000000C) return; else if (0x0000000C == unHeaderSize) // BitmapCoreHeader ReadImageCoreHeader(pImageBuffer + 4, unHeaderSize - 4, pImageBuffer + unHeaderSize, unBufferLen - unHeaderSize, ppDstBuffer, punWidth, punHeight); else // BitmapInfoHeader { int nWidth; int nHeight; unsigned short ushPlanes; unsigned short ushBitCount; unsigned int unCompression; unsigned int unImageSize; unsigned int unXPelsPerMeter; unsigned int unYPelsPerMeter; // unsigned int unColorUsed; unsigned int unColorImportant; oHeaderStream >> nWidth; oHeaderStream >> nHeight; oHeaderStream >> ushPlanes; oHeaderStream >> ushBitCount; oHeaderStream >> unCompression; oHeaderStream >> unImageSize; oHeaderStream >> unXPelsPerMeter; oHeaderStream >> unYPelsPerMeter; oHeaderStream >> unColorUsed; oHeaderStream >> unColorImportant; if (DIB_RGB_COLORS == unColorUsage) { int lCalcLen = (((nWidth * ushPlanes * ushBitCount + 31) & ~31) / 8) * abs(nHeight); if (unCompression == 0) { int nAvailableLen = (unBufferLen - unHeaderSize) - lCalcLen - unColorUsed * 4; if (nAvailableLen < 0) return; if (0 == unColorUsed && BI_BITCOUNT_1 == ushBitCount && nAvailableLen >= 8) unColorUsed = 2; else if (0 == unColorUsed && BI_BITCOUNT_3 == ushBitCount && nAvailableLen >= 1024) unColorUsed = 256; } unHeaderSize += 4 * unColorUsed; // RGBQuad ReadImageInfoHeader(pImageBuffer + 4, unHeaderSize - 4, pImageBuffer + unHeaderSize, unBufferLen - unHeaderSize, ppDstBuffer, punWidth, punHeight, unColorUsed); } else { // TODO: реализовать другие типы цветов } } } double GetEllipseAngle(int nL, int nT, int nR, int nB, int nX, int nY) { double dX0 = (nL + nR) / 2.0; double dY0 = (nT + nB) / 2.0; // Определим квадрант int nQuarter = -1; if (nX >= dX0) { if (nY <= dY0) nQuarter = 3; else nQuarter = 0; } else { if (nY <= dY0) nQuarter = 2; else nQuarter = 1; } double dDist = sqrt((double)(nX - dX0) * (nX - dX0) + (nY - dY0) * (nY - dY0)); double dRadAngle = asin(fabs(nY - dY0) / dDist); double dAngle = dRadAngle * 180 / 3.14159265358979323846; switch (nQuarter) { case 1: dAngle = 180 - dAngle; break; case 2: dAngle = 180 + dAngle; break; case 3: dAngle = 360 - dAngle; break; } return dAngle; } void ProcessRasterOperation(unsigned int unRasterOperation, BYTE** ppBgra, unsigned int unWidth, unsigned int unHeight) { BYTE* pBgra = *ppBgra; if (NULL == pBgra) return; // Для битовых операций SRCPAINT и SRCAND сделаем, как будто фон чисто белый. if (0x008800C6 == unRasterOperation) // SRCPAINT { BYTE* pCur = pBgra; for (unsigned int unY = 0; unY < unHeight; unY++) { for (unsigned int unX = 0; unX < unWidth; unX++) { unsigned int unIndex = (unY * unWidth + unX) * 4; if (0xff == pCur[unIndex + 0] && 0xff == pCur[unIndex + 1] && 0xff == pCur[unIndex + 2]) pCur[unIndex + 3] = 0; } } } else if (0x00EE0086 == unRasterOperation) // SRCAND { BYTE* pCur = pBgra; for (unsigned int unY = 0; unY < unHeight; unY++) { for (unsigned int unX = 0; unX < unWidth; unX++) { unsigned int unIndex = (unY * unWidth + unX) * 4; if (0 == pCur[unIndex + 0] && 0 == pCur[unIndex + 1] && 0 == pCur[unIndex + 2]) pCur[unIndex + 3] = 0; } } } //Точная копия картинки, поэтому делаем alpha канал = 0xff, чтобы нейтрализовать его else if (0x00CC0020 == unRasterOperation) //SRCCOPY { for (unsigned int unIndex = 3; unIndex < unWidth * 4 * unHeight; unIndex += 4) pBgra[unIndex] = 0xff; } } std::wstring GetTempFilename(const std::wstring& sFolder) { std::wstring sTmpFile = NSFile::CFileBinary::CreateTempFileWithUniqueName(sFolder.empty() ? NSFile::CFileBinary::GetTempPath() : sFolder, L"wmf"); if (sTmpFile.empty()) return sTmpFile; if (NSFile::CFileBinary::Exists(sTmpFile)) NSFile::CFileBinary::Remove(sTmpFile); return sTmpFile; } std::wstring StringNormalization(const std::wstring& wsString) { std::wstring wsText; for (const wchar_t& wChar : wsString) { if (wChar == L'<') wsText += L"<"; else if (wChar == L'>') wsText += L">"; else if (wChar == L'&') wsText += L"&"; else if (wChar == L'\'') wsText += L"'"; else if (wChar == L'"') wsText += L"""; else if (wChar == L'\r' || (wChar >= 0x00 && wChar <=0x1F)) continue; else wsText += wChar; } return wsText; } bool StringEquals(const std::wstring& wsFirst, const std::wstring& wsSecond) { #if 0 // since c++14! return std::equal(wsFirst.begin(), wsFirst.end(), wsSecond.begin(), wsSecond.end(), [](wchar_t wchFirst, wchar_t wchSecond) { return tolower(wchFirst) == tolower(wchSecond); }); #else size_t sizeFirst = wsFirst.length(); size_t sizeSecond = wsSecond.length(); if (sizeFirst != sizeSecond) return false; for (size_t i = 0; i < sizeFirst; ++i) { if (tolower(wsFirst[i]) != tolower(wsSecond[i])) return false; } return true; #endif } std::wstring ConvertToWString(double dValue, int nAccuracy) { const double dAbsValue {std::abs(dValue)}; const double dRemainder{dAbsValue - std::floor(dAbsValue)}; if (Equals(0., dRemainder)) return std::to_wstring(static_cast(dValue)); if (nAccuracy < 0) { if (dAbsValue < 1.) nAccuracy = MAXACCURACY; else nAccuracy = MINACCURACY; } std::wstringstream owsStream; owsStream << std::fixed << std::setprecision(nAccuracy) << dValue; std::wstring wsValue{owsStream.str()}; const size_t unDotPosition{wsValue.find_first_of(L'.')}; if (std::wstring::npos == unDotPosition) return wsValue; const size_t unFirstPosition{wsValue.find_first_not_of(L'0', unDotPosition + 1)}; if (std::wstring::npos == unFirstPosition) return wsValue.substr(0, unDotPosition); const size_t unLastPosition = wsValue.find_last_not_of(L'0', unFirstPosition + MINACCURACY - 1); return wsValue.substr(0, unLastPosition + 1); } std::wstring ConvertToWString(const std::vector& arValues, int nAccuracy) { std::wstring wsValue; for (double dValue : arValues) wsValue += ConvertToWString(dValue, nAccuracy) + L' '; wsValue.pop_back(); return wsValue; } std::wstring ConvertToUnicode(const unsigned char* pText, unsigned long unLength, unsigned short uchCharSet) { NSStringExt::CConverter::ESingleByteEncoding eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_DEFAULT;; // Соответствие Charset -> Codepage: http://support.microsoft.com/kb/165478 // http://msdn.microsoft.com/en-us/library/cc194829.aspx // Charset Name Charset Value(hex) Codepage number // ------------------------------------------------------ // // DEFAULT_CHARSET 1 (x01) // SYMBOL_CHARSET 2 (x02) // OEM_CHARSET 255 (xFF) // ANSI_CHARSET 0 (x00) 1252 // RUSSIAN_CHARSET 204 (xCC) 1251 // EASTEUROPE_CHARSET 238 (xEE) 1250 // GREEK_CHARSET 161 (xA1) 1253 // TURKISH_CHARSET 162 (xA2) 1254 // BALTIC_CHARSET 186 (xBA) 1257 // HEBREW_CHARSET 177 (xB1) 1255 // ARABIC _CHARSET 178 (xB2) 1256 // SHIFTJIS_CHARSET 128 (x80) 932 // HANGEUL_CHARSET 129 (x81) 949 // GB2313_CHARSET 134 (x86) 936 // CHINESEBIG5_CHARSET 136 (x88) 950 // THAI_CHARSET 222 (xDE) 874 // JOHAB_CHARSET 130 (x82) 1361 // VIETNAMESE_CHARSET 163 (xA3) 1258 switch (uchCharSet) { default: case DEFAULT_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_DEFAULT; break; case SYMBOL_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_DEFAULT; break; case ANSI_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1252; break; case RUSSIAN_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1251; break; case EASTEUROPE_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1250; break; case GREEK_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1253; break; case TURKISH_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1254; break; case BALTIC_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1257; break; case HEBREW_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1255; break; case ARABIC_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1256; break; case SHIFTJIS_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP932; break; case HANGEUL_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP949; break; case 134/*GB2313_CHARSET*/: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP936; break; case CHINESEBIG5_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP950; break; case THAI_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP874; break; case JOHAB_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1361; break; case VIETNAMESE_CHARSET: eCharSet = NSStringExt::CConverter::ESingleByteEncoding::SINGLE_BYTE_ENCODING_CP1258; break; } return NSStringExt::CConverter::GetUnicodeFromSingleByteString(pText, unLength, eCharSet); } }