/** * FreeRDP: A Remote Desktop Protocol Implementation * Color Conversion Routines * * Copyright 2010 Marc-Andre Moreau * Copyright 2016 Armin Novak * Copyright 2016 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #define TAG FREERDP_TAG("color") BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* data) { UINT32 x, y; const BYTE* srcp; BYTE* dstp; BYTE* dstData; UINT32 scanline; /* * converts a 1-bit-per-pixel glyph to a one-byte-per-pixel glyph: * this approach uses a little more memory, but provides faster * means of accessing individual pixels in blitting operations */ scanline = (width + 7) / 8; dstData = (BYTE*) _aligned_malloc(width * height, 16); if (!dstData) return NULL; ZeroMemory(dstData, width * height); dstp = dstData; for (y = 0; y < height; y++) { srcp = data + (y * scanline); for (x = 0; x < width; x++) { if ((*srcp & (0x80 >> (x % 8))) != 0) *dstp = 0xFF; dstp++; if (((x + 1) % 8 == 0) && x != 0) srcp++; } } return dstData; } BOOL freerdp_image_copy_from_monochrome(BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, const BYTE* pSrcData, UINT32 backColor, UINT32 foreColor, const UINT32* palette) { UINT32 x, y; BOOL vFlip; UINT32 srcFlip; UINT32 dstFlip; UINT32 nDstPad; UINT32 monoStep; UINT32 dstBitsPerPixel; UINT32 dstBytesPerPixel; dstBitsPerPixel = GetBitsPerPixel(DstFormat); dstBytesPerPixel = GetBytesPerPixel(DstFormat); dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat); if (nDstStep == 0) nDstStep = dstBytesPerPixel * nWidth; nDstPad = (nDstStep - (nWidth * dstBytesPerPixel)); srcFlip = FREERDP_PIXEL_FLIP_NONE; vFlip = (srcFlip != dstFlip) ? TRUE : FALSE; monoStep = (nWidth + 7) / 8; for (y = 0; y < nHeight; y++) { const BYTE* monoBits; BYTE* pDstLine = &pDstData[((nYDst + y) * nDstStep)]; UINT32 monoBit = 0x80; if (!vFlip) monoBits = &pSrcData[monoStep * y]; else monoBits = &pSrcData[monoStep * (nHeight - y - 1)]; for (x = 0; x < nWidth; x++) { BYTE* pDstPixel = &pDstLine[((nXDst + x) * GetBytesPerPixel(DstFormat))]; BOOL monoPixel = (*monoBits & monoBit) ? TRUE : FALSE; if (!(monoBit >>= 1)) { monoBits++; monoBit = 0x80; } if (monoPixel) WriteColor(pDstPixel, DstFormat, backColor); else WriteColor(pDstPixel, DstFormat, foreColor); } } return 1; } static INLINE UINT32 freerdp_image_inverted_pointer_color(UINT32 x, UINT32 y) { #if 1 /** * Inverted pointer colors (where individual pixels can change their * color to accommodate the background behind them) only seem to be * supported on Windows. * Using a static replacement color for these pixels (e.g. black) * might result in invisible pointers depending on the background. * This function returns either black or white, depending on the * pixel's position. */ return (x + y) & 1 ? 0xFF000000 : 0xFFFFFFFF; #else return 0xFF000000; #endif } /** * Drawing Monochrome Pointers: * http://msdn.microsoft.com/en-us/library/windows/hardware/ff556143/ * * Drawing Color Pointers: * http://msdn.microsoft.com/en-us/library/windows/hardware/ff556138/ */ BOOL freerdp_image_copy_from_pointer_data( BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, const BYTE* xorMask, UINT32 xorMaskLength, const BYTE* andMask, UINT32 andMaskLength, UINT32 xorBpp, const UINT32* palette) { UINT32 x, y; BOOL vFlip; UINT32 srcFlip; UINT32 dstFlip; UINT32 nDstPad; UINT32 xorStep; UINT32 andStep; UINT32 xorBit; UINT32 andBit; const BYTE* xorBits; const BYTE* andBits; UINT32 xorPixel; UINT32 andPixel; UINT32 dstBitsPerPixel; UINT32 dstBytesPerPixel; dstBitsPerPixel = GetBitsPerPixel(DstFormat); dstBytesPerPixel = GetBytesPerPixel(DstFormat); dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat); if (FREERDP_PIXEL_FORMAT_FLIP(DstFormat)) { WLog_ERR(TAG, "Format %s not supported!", GetColorFormatName(DstFormat)); } if (nDstStep < 0) nDstStep = dstBytesPerPixel * nWidth; nDstPad = (nDstStep - (nWidth * dstBytesPerPixel)); srcFlip = (xorBpp == 1) ? FREERDP_PIXEL_FLIP_NONE : FREERDP_PIXEL_FLIP_VERTICAL; vFlip = (srcFlip != dstFlip) ? TRUE : FALSE; andStep = (nWidth + 7) / 8; andStep += (andStep % 2); if (!xorMask || (xorMaskLength == 0)) return -1; if (xorBpp == 1) { if (!andMask || (andMaskLength == 0)) return -1; xorStep = (nWidth + 7) / 8; xorStep += (xorStep % 2); if (xorStep * nHeight > xorMaskLength) return -1; if (andStep * nHeight > andMaskLength) return -1; for (y = 0; y < nHeight; y++) { BYTE* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * GetBytesPerPixel(DstFormat))]; xorBit = andBit = 0x80; if (!vFlip) { xorBits = &xorMask[xorStep * y]; andBits = &andMask[andStep * y]; } else { xorBits = &xorMask[xorStep * (nHeight - y - 1)]; andBits = &andMask[andStep * (nHeight - y - 1)]; } for (x = 0; x < nWidth; x++) { UINT32 color; xorPixel = (*xorBits & xorBit) ? 1 : 0; if (!(xorBit >>= 1)) { xorBits++; xorBit = 0x80; } andPixel = (*andBits & andBit) ? 1 : 0; if (!(andBit >>= 1)) { andBits++; andBit = 0x80; } if (!andPixel && !xorPixel) color = 0xFF000000; /* black */ else if (!andPixel && xorPixel) color = 0xFFFFFFFF; /* white */ else if (andPixel && !xorPixel) color = 0x00000000; /* transparent */ else if (andPixel && xorPixel) color = freerdp_image_inverted_pointer_color(x, y); /* inverted */ color = ConvertColor(color, PIXEL_FORMAT_ABGR32, DstFormat, NULL); WriteColor(pDstPixel, DstFormat, color); pDstPixel += GetBytesPerPixel(DstFormat); } } return 1; } else if (xorBpp == 24 || xorBpp == 32 || xorBpp == 16 || xorBpp == 8) { UINT32 xorBytesPerPixel = xorBpp >> 3; xorStep = nWidth * xorBytesPerPixel; if (xorBpp == 8 && !palette) { WLog_ERR(TAG, "null palette in convertion from %d bpp to %d bpp", xorBpp, dstBitsPerPixel); return -1; } if (xorStep * nHeight > xorMaskLength) return -1; if (andMask) { if (andStep * nHeight > andMaskLength) return -1; } for (y = 0; y < nHeight; y++) { BYTE* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * GetBytesPerPixel(DstFormat))]; andBit = 0x80; if (!vFlip) { if (andMask) andBits = &andMask[andStep * y]; xorBits = &xorMask[xorStep * y]; } else { if (andMask) andBits = &andMask[andStep * (nHeight - y - 1)]; xorBits = &xorMask[xorStep * (nHeight - y - 1)]; } for (x = 0; x < nWidth; x++) { UINT32 color; BOOL ignoreAndMask = FALSE; if (xorBpp == 32) { xorPixel = *((UINT32*) xorBits); if (xorPixel & 0xFF000000) ignoreAndMask = TRUE; } else if (xorBpp == 16) { xorPixel = ConvertColor(*(UINT16*)xorBits, PIXEL_FORMAT_RGB16, PIXEL_FORMAT_ARGB32, NULL); } else if (xorBpp == 8) { xorPixel = 0xFF << 24 | ((UINT32*)palette)[xorBits[0]]; } else { xorPixel = xorBits[0] | xorBits[1] << 8 | xorBits[2] << 16 | 0xFF << 24; } xorBits += xorBytesPerPixel; andPixel = 0; if (andMask) { andPixel = (*andBits & andBit) ? 1 : 0; if (!(andBit >>= 1)) { andBits++; andBit = 0x80; } } /* Ignore the AND mask, if the color format already supplies alpha data. */ if (andPixel && !ignoreAndMask) { const UINT32 xorPixelMasked = xorPixel | 0xFF000000; if (xorPixelMasked == 0xFF000000) /* black -> transparent */ color = 0x00000000; else if (xorPixelMasked == 0xFFFFFFFF) /* white -> inverted */ color = freerdp_image_inverted_pointer_color(x, y); else color = xorPixel; color = ConvertColor(color, PIXEL_FORMAT_ARGB32, DstFormat, NULL); } else { color = ConvertColor(xorPixel, PIXEL_FORMAT_ARGB32, DstFormat, NULL); } WriteColor(pDstPixel, DstFormat, color); pDstPixel += GetBytesPerPixel(DstFormat); } } return 1; } WLog_ERR(TAG, "failed to convert from %d bpp to %d bpp", xorBpp, dstBitsPerPixel); return -1; } BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, INT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, const BYTE* pSrcData, DWORD SrcFormat, INT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const UINT32* palette) { const UINT32 dstByte = GetBytesPerPixel(DstFormat); const UINT32 srcByte = GetBytesPerPixel(SrcFormat); const UINT32 copyDstWidth = nWidth * dstByte; const UINT32 xSrcOffset = nXSrc * srcByte; const UINT32 xDstOffset = nXDst * dstByte; BOOL vSrcVFlip = FALSE; BOOL vDstVFlip = FALSE; BOOL vSrcHFlip = FALSE; BOOL vDstHFlip = FALSE; UINT32 srcVOffset = 0; INT32 srcVMultiplier = 1; UINT32 dstVOffset = 0; INT32 dstVMultiplier = 1; if (!pDstData || !pSrcData) return FALSE; switch (FREERDP_PIXEL_FORMAT_FLIP(SrcFormat)) { case FREERDP_PIXEL_FLIP_HORIZONTAL: vSrcHFlip = TRUE; return FALSE; case FREERDP_PIXEL_FLIP_VERTICAL: vSrcVFlip = TRUE; break; case FREERDP_PIXEL_FLIP_NONE: default: break; } switch (FREERDP_PIXEL_FORMAT_FLIP(DstFormat)) { case FREERDP_PIXEL_FLIP_HORIZONTAL: vDstHFlip = TRUE; return FALSE; case FREERDP_PIXEL_FLIP_VERTICAL: vDstVFlip = TRUE; break; case FREERDP_PIXEL_FLIP_NONE: default: break; } if (nDstStep < 0) nDstStep = nWidth * GetBytesPerPixel(DstFormat); if (nSrcStep < 0) nSrcStep = nWidth * GetBytesPerPixel(SrcFormat); if (vSrcVFlip) { srcVOffset = (nHeight - 1) * nSrcStep; srcVMultiplier = -1; } if (vDstVFlip) { dstVOffset = (nHeight - 1) * nDstStep; dstVMultiplier = -1; } if (vSrcHFlip || vDstHFlip) { WLog_ERR(TAG, "Horizontal flipping not supported! %s %s", GetColorFormatName(SrcFormat), GetColorFormatName(DstFormat)); return FALSE; } if (FREERDP_PIXEL_FORMAT_FLIP_MASKED(SrcFormat) == FREERDP_PIXEL_FORMAT_FLIP_MASKED(DstFormat)) { UINT32 y; for (y = 0; y < nHeight; y++) { const BYTE* srcLine = &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset]; BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset]; memcpy(&dstLine[xDstOffset], &srcLine[xSrcOffset], copyDstWidth); } } else { UINT32 x, y; for (y = 0; y < nHeight; y++) { const BYTE* srcLine = &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset]; BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset]; for (x = 0; x < nWidth; x++) { UINT32 dstColor; UINT32 color = ReadColor(&srcLine[(x + nXSrc) * srcByte], SrcFormat); dstColor = ConvertColor(color, SrcFormat, DstFormat, palette); WriteColor(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor); } } } return TRUE; } BOOL freerdp_image_fill(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 color) { UINT32 x, y; for (y = 0; y < nHeight; y++) { BYTE* pDstLine = &pDstData[(y + nYDst) * nDstStep]; for (x = 0; x < nWidth; x++) { BYTE* pDst = &pDstLine[(x + nXDst) * GetBytesPerPixel(DstFormat)]; WriteColor(pDst, DstFormat, color); } } return TRUE; }