You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
16 KiB
645 lines
16 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 2000 Microsoft Corporation
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Contains ClearType scan operations
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 07/19/2000 mleonov
|
|
* Created it.
|
|
* 01/11/2001 mleonov
|
|
* Reengineered ClearType architecture to allow variable length scan records
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
static inline VOID
|
|
SetupGammaTables(ULONG ulGamma, const BYTE ** gamma, const BYTE ** gammaInv)
|
|
{
|
|
static BYTE const * const gammaTables[] =
|
|
{
|
|
Globals::TextContrastTableIdentity, Globals::TextContrastTableIdentity,
|
|
Globals::TextContrastTablesDir[0], Globals::TextContrastTablesInv[0],
|
|
Globals::TextContrastTablesDir[1], Globals::TextContrastTablesInv[1],
|
|
Globals::TextContrastTablesDir[2], Globals::TextContrastTablesInv[2],
|
|
Globals::TextContrastTablesDir[3], Globals::TextContrastTablesInv[3],
|
|
Globals::TextContrastTablesDir[4], Globals::TextContrastTablesInv[4],
|
|
Globals::TextContrastTablesDir[5], Globals::TextContrastTablesInv[5],
|
|
Globals::TextContrastTablesDir[6], Globals::TextContrastTablesInv[6],
|
|
Globals::TextContrastTablesDir[7], Globals::TextContrastTablesInv[7],
|
|
Globals::TextContrastTablesDir[8], Globals::TextContrastTablesInv[8],
|
|
Globals::TextContrastTablesDir[9], Globals::TextContrastTablesInv[9],
|
|
Globals::TextContrastTablesDir[10], Globals::TextContrastTablesInv[10],
|
|
Globals::TextContrastTablesDir[11], Globals::TextContrastTablesInv[11]
|
|
};
|
|
|
|
if (ulGamma > 12)
|
|
{
|
|
ASSERT(FALSE);
|
|
ulGamma = 12;
|
|
}
|
|
*gamma = gammaTables[2 * ulGamma];
|
|
*gammaInv = gammaTables[2 * ulGamma + 1];
|
|
} // SetupGammaTables
|
|
|
|
static __forceinline BYTE
|
|
BlendOneChannel(BYTE alphaCT, BYTE alphaBrush, BYTE foreground, BYTE background, const BYTE * gamma, const BYTE * gammaInv)
|
|
{
|
|
ASSERT(0 <= alphaCT && alphaCT <= CT_SAMPLE_F);
|
|
if (alphaCT == 0)
|
|
return background;
|
|
foreground = gamma[foreground];
|
|
background = gamma[background];
|
|
ULONG ulongRet = ULONG(0.5 + background + ((double)alphaBrush * (foreground - background) * alphaCT) / (255 * CT_SAMPLE_F));
|
|
ASSERT(ulongRet <= 255);
|
|
BYTE ret = (BYTE)ulongRet;
|
|
ret = gammaInv[ret];
|
|
return ret;
|
|
} // BlendOneChannel
|
|
|
|
namespace
|
|
{
|
|
|
|
class ClearTypeSolidBlend
|
|
{
|
|
const ARGB ArgbF;
|
|
const BYTE * ClearTypeBits;
|
|
|
|
public:
|
|
ClearTypeSolidBlend(const ScanOperation::OtherParams * otherParams)
|
|
: ClearTypeBits(otherParams->CTBuffer), ArgbF(otherParams->SolidColor)
|
|
{}
|
|
|
|
// always call IsCompletelyTransparent in the beginning of RMW operation
|
|
bool IsCompletelyTransparent() const
|
|
{
|
|
return GetAlpha() == 0;
|
|
}
|
|
BYTE GetCT() const
|
|
{
|
|
return *ClearTypeBits;
|
|
}
|
|
ARGB GetARGB() const
|
|
{
|
|
return ArgbF;
|
|
}
|
|
BYTE GetAlpha() const
|
|
{
|
|
return (BYTE)GpColor::GetAlphaARGB(ArgbF);
|
|
}
|
|
BYTE GetRed() const
|
|
{
|
|
return (BYTE)GpColor::GetRedARGB(ArgbF);
|
|
}
|
|
BYTE GetGreen() const
|
|
{
|
|
return (BYTE)GpColor::GetGreenARGB(ArgbF);
|
|
}
|
|
BYTE GetBlue() const
|
|
{
|
|
return (BYTE)GpColor::GetBlueARGB(ArgbF);
|
|
}
|
|
bool IsOpaque() const
|
|
{
|
|
return GetCT() == CT_LOOKUP - 1 && GetAlpha() == 255;
|
|
}
|
|
bool IsTransparent() const
|
|
{
|
|
// we took care of zero GetAlpha() in IsCompletelyTransparent()
|
|
return GetCT() == 0;
|
|
}
|
|
bool IsTranslucent() const
|
|
{
|
|
return !IsTransparent() && !IsOpaque();
|
|
}
|
|
void operator++()
|
|
{
|
|
++ClearTypeBits;
|
|
}
|
|
}; // class ClearTypeSolidBlend
|
|
|
|
class ClearTypeCARGBBlend
|
|
{
|
|
const ARGB * ArgbBrushBits;
|
|
const BYTE * ClearTypeBits;
|
|
public:
|
|
ClearTypeCARGBBlend(const ScanOperation::OtherParams * otherParams)
|
|
: ClearTypeBits(otherParams->CTBuffer),
|
|
ArgbBrushBits(static_cast<const ARGB*>(otherParams->BlendingScan))
|
|
{}
|
|
|
|
bool IsCompletelyTransparent() const
|
|
{
|
|
return false;
|
|
}
|
|
BYTE GetCT() const
|
|
{
|
|
return *ClearTypeBits;
|
|
}
|
|
ARGB GetARGB() const
|
|
{
|
|
return *ArgbBrushBits;
|
|
}
|
|
BYTE GetAlpha() const
|
|
{
|
|
return (BYTE)GpColor::GetAlphaARGB(*ArgbBrushBits);
|
|
}
|
|
BYTE GetRed() const
|
|
{
|
|
return (BYTE)GpColor::GetRedARGB(*ArgbBrushBits);
|
|
}
|
|
BYTE GetGreen() const
|
|
{
|
|
return (BYTE)GpColor::GetGreenARGB(*ArgbBrushBits);
|
|
}
|
|
BYTE GetBlue() const
|
|
{
|
|
return (BYTE)GpColor::GetBlueARGB(*ArgbBrushBits);
|
|
}
|
|
bool IsOpaque() const
|
|
{
|
|
return GetCT() == CT_LOOKUP - 1 && GetAlpha() == 255;
|
|
}
|
|
bool IsTransparent() const
|
|
{
|
|
return GetCT() == 0 || GetAlpha() == 0;
|
|
}
|
|
bool IsTranslucent() const
|
|
{
|
|
return !IsTransparent() && !IsOpaque();
|
|
}
|
|
void operator++()
|
|
{
|
|
++ClearTypeBits;
|
|
++ArgbBrushBits;
|
|
}
|
|
}; // class ClearTypeCARGBBlend
|
|
|
|
} // namespace
|
|
|
|
template <class BLENDTYPE>
|
|
static VOID ClearTypeBlend(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const ScanOperation::OtherParams *otherParams,
|
|
BLENDTYPE & bl
|
|
)
|
|
{
|
|
if (bl.IsCompletelyTransparent())
|
|
return;
|
|
|
|
DEFINE_POINTERS(ARGB, ARGB)
|
|
|
|
ASSERT(count > 0);
|
|
|
|
ULONG gammaValue = otherParams->TextContrast;
|
|
|
|
const BYTE * gamma, * gammaInv;
|
|
SetupGammaTables(gammaValue, &gamma, &gammaInv);
|
|
|
|
do {
|
|
if (bl.IsTransparent())
|
|
; // fully transparent case, nothing to do
|
|
else if (bl.IsOpaque())
|
|
{ // fully opaque case, copy the foreground color
|
|
*d = bl.GetARGB();
|
|
}
|
|
else
|
|
{
|
|
const BYTE blendIndex = bl.GetCT();
|
|
ASSERT(0 <= blendIndex && blendIndex <= CT_LOOKUP - 1);
|
|
|
|
const Globals::F_RGB blend = Globals::gaOutTable[blendIndex];
|
|
const ARGB source = *s;
|
|
const BYTE alphaBrush = bl.GetAlpha();
|
|
|
|
const BYTE dstRed = BlendOneChannel(
|
|
blend.kR,
|
|
alphaBrush,
|
|
bl.GetRed(),
|
|
(BYTE)GpColor::GetRedARGB(source),
|
|
gamma,
|
|
gammaInv);
|
|
|
|
const BYTE dstGre = BlendOneChannel(
|
|
blend.kG,
|
|
alphaBrush,
|
|
bl.GetGreen(),
|
|
(BYTE)GpColor::GetGreenARGB(source),
|
|
gamma,
|
|
gammaInv);
|
|
|
|
const BYTE dstBlu = BlendOneChannel(
|
|
blend.kB,
|
|
alphaBrush,
|
|
bl.GetBlue(),
|
|
(BYTE)GpColor::GetBlueARGB(source),
|
|
gamma,
|
|
gammaInv);
|
|
|
|
*d = GpColor::MakeARGB(255, dstRed, dstGre, dstBlu);
|
|
}
|
|
++bl;
|
|
++s;
|
|
++d;
|
|
} while (--count);
|
|
} // ClearTypeBlend
|
|
|
|
|
|
VOID FASTCALL
|
|
ScanOperation::CTBlendCARGB(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeCARGBBlend bl(otherParams);
|
|
ClearTypeBlend(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::CTBlendCARGB
|
|
|
|
VOID FASTCALL
|
|
ScanOperation::CTBlendSolid(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeSolidBlend bl(otherParams);
|
|
ClearTypeBlend(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::CTBlendSolid
|
|
|
|
template <class BLENDTYPE>
|
|
static VOID
|
|
CTReadRMW16(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const ScanOperation::OtherParams *otherParams,
|
|
BLENDTYPE & bl
|
|
)
|
|
{
|
|
if (bl.IsCompletelyTransparent())
|
|
return;
|
|
|
|
DEFINE_POINTERS(UINT16, UINT16)
|
|
|
|
// We want to get dword alignment for our copies, so handle the
|
|
// initial partial dword, if there is one:
|
|
|
|
if (((ULONG_PTR) s) & 0x2)
|
|
{
|
|
if (bl.IsTranslucent())
|
|
{
|
|
*(d) = *(s);
|
|
}
|
|
|
|
d++;
|
|
s++;
|
|
++bl;
|
|
count--;
|
|
}
|
|
|
|
// Now go through the aligned dword loop:
|
|
|
|
while ((count -= 2) >= 0)
|
|
{
|
|
if (bl.IsTranslucent())
|
|
{
|
|
++bl;
|
|
if (bl.IsTranslucent())
|
|
{
|
|
// Both pixels have partial alpha, so do a dword read:
|
|
|
|
*((UNALIGNED UINT32*) d) = *((UINT32*) s);
|
|
}
|
|
else
|
|
{
|
|
// Only the first pixel has partial alpha, so do a word read:
|
|
|
|
*(d) = *(s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++bl;
|
|
if (bl.IsTranslucent())
|
|
{
|
|
// Only the second pixel has partial alpha, so do a word read:
|
|
|
|
*(d + 1) = *(s + 1);
|
|
}
|
|
}
|
|
|
|
d += 2;
|
|
s += 2;
|
|
++bl;
|
|
}
|
|
|
|
// Handle the end alignment:
|
|
|
|
if (count & 1)
|
|
{
|
|
if (bl.IsTranslucent())
|
|
{
|
|
*(d) = *(s);
|
|
}
|
|
}
|
|
} // CTReadRMW16
|
|
|
|
VOID FASTCALL ScanOperation::ReadRMW_16_CT_CARGB(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeCARGBBlend bl(otherParams);
|
|
CTReadRMW16(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::ReadRMW_16_CT_CARGB
|
|
|
|
VOID FASTCALL ScanOperation::ReadRMW_16_CT_Solid(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeSolidBlend bl(otherParams);
|
|
CTReadRMW16(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::ReadRMW_16_CT_Solid
|
|
|
|
template <class BLENDTYPE>
|
|
static VOID
|
|
CTReadRMW24(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const ScanOperation::OtherParams *otherParams,
|
|
BLENDTYPE & bl
|
|
)
|
|
{
|
|
if (bl.IsCompletelyTransparent())
|
|
return;
|
|
|
|
DEFINE_POINTERS(BYTE, BYTE)
|
|
|
|
ULONG_PTR srcToDstDelta = (ULONG_PTR) d - (ULONG_PTR) s;
|
|
|
|
// Handle the initial partial read:
|
|
|
|
INT initialAlignment = (INT) ((ULONG_PTR) s & 3);
|
|
if (initialAlignment)
|
|
{
|
|
if (bl.IsTranslucent())
|
|
{
|
|
UINT32 *alignedSrc = (UINT32*) ((ULONG_PTR) s & ~3);
|
|
DWORD dwBuffer[2];
|
|
|
|
// Get pointer to start of pixel inside dwBuffer
|
|
BYTE *pByte = (BYTE*) dwBuffer + initialAlignment;
|
|
|
|
// Copy first aligned DWORDS from the source
|
|
dwBuffer[0] = *alignedSrc;
|
|
// Copy next one only if pixel is split between 2 aligned DWORDS
|
|
if (initialAlignment >= 2)
|
|
dwBuffer[1] = *(alignedSrc + 1);
|
|
|
|
// Copy 4 bytes to the destination
|
|
// This will cause an extra byte to have garbage in the
|
|
// destination buffer, but will be overwritten if next pixel
|
|
// is used.
|
|
*((DWORD*) d) = *((UNALIGNED DWORD*) pByte);
|
|
}
|
|
|
|
++bl;
|
|
s += 3;
|
|
if (--count == 0)
|
|
return;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
// Find the first pixel to copy
|
|
|
|
while (!bl.IsTranslucent())
|
|
{
|
|
++bl;
|
|
s += 3;
|
|
if (--count == 0)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
UINT32 *startSrc = (UINT32*) ((ULONG_PTR) (s) & ~3);
|
|
|
|
// Now find the first "don't copy" pixel after that:
|
|
|
|
while (bl.IsTranslucent())
|
|
{
|
|
++bl;
|
|
s += 3;
|
|
if (--count == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 'endSrc' is inclusive of the last pixel's last byte:
|
|
|
|
UINT32 *endSrc = (UINT32*) ((ULONG_PTR) (s + 2) & ~3);
|
|
UNALIGNED UINT32 *dstPtr = (UNALIGNED UINT32*) ((ULONG_PTR) startSrc + srcToDstDelta);
|
|
|
|
while (startSrc <= endSrc)
|
|
{
|
|
*dstPtr++ = *startSrc++;
|
|
}
|
|
|
|
if (count == 0)
|
|
return;
|
|
}
|
|
} // CTReadRMW24
|
|
|
|
VOID FASTCALL ScanOperation::ReadRMW_24_CT_CARGB(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeCARGBBlend bl(otherParams);
|
|
CTReadRMW24(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::ReadRMW_24_CT_CARGB
|
|
|
|
VOID FASTCALL ScanOperation::ReadRMW_24_CT_Solid(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeSolidBlend bl(otherParams);
|
|
CTReadRMW24(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::ReadRMW_24_CT_Solid
|
|
|
|
|
|
template <class BLENDTYPE>
|
|
static VOID
|
|
CTWriteRMW16(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const ScanOperation::OtherParams *otherParams,
|
|
BLENDTYPE & bl
|
|
)
|
|
{
|
|
if (bl.IsCompletelyTransparent())
|
|
return;
|
|
|
|
DEFINE_POINTERS(UINT16, UINT16)
|
|
|
|
// We want to get dword alignment for our copies, so handle the
|
|
// initial partial dword, if there is one:
|
|
|
|
if (((ULONG_PTR) d) & 0x2)
|
|
{
|
|
if (!bl.IsTransparent())
|
|
{
|
|
*(d) = *(s);
|
|
}
|
|
|
|
d++;
|
|
s++;
|
|
++bl;
|
|
count--;
|
|
}
|
|
|
|
// Now go through the aligned dword loop:
|
|
|
|
while ((count -= 2) >= 0)
|
|
{
|
|
if (!bl.IsTransparent())
|
|
{
|
|
++bl;
|
|
if (!bl.IsTransparent())
|
|
{
|
|
// Both pixels have partial bl, so do a dword read:
|
|
|
|
*((UINT32*) d) = *((UNALIGNED UINT32*) s);
|
|
}
|
|
else
|
|
{
|
|
// Only the first pixel has partial bl, so do a word read:
|
|
|
|
*(d) = *(s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++bl;
|
|
if (!bl.IsTransparent())
|
|
{
|
|
// Only the second pixel has partial bl, so do a word read:
|
|
|
|
*(d + 1) = *(s + 1);
|
|
}
|
|
}
|
|
|
|
d += 2;
|
|
s += 2;
|
|
++bl;
|
|
}
|
|
|
|
// Handle the end alignment:
|
|
|
|
if (count & 1)
|
|
{
|
|
if (!bl.IsTransparent())
|
|
{
|
|
*(d) = *(s);
|
|
}
|
|
}
|
|
} // CTWriteRMW16
|
|
|
|
VOID FASTCALL ScanOperation::WriteRMW_16_CT_CARGB(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeCARGBBlend bl(otherParams);
|
|
CTWriteRMW16(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::WriteRMW_16_CT_CARGB
|
|
|
|
VOID FASTCALL ScanOperation::WriteRMW_16_CT_Solid(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeSolidBlend bl(otherParams);
|
|
CTWriteRMW16(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::WriteRMW_16_CT_Solid
|
|
|
|
|
|
template <class BLENDTYPE>
|
|
static VOID
|
|
CTWriteRMW24(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const ScanOperation::OtherParams *otherParams,
|
|
BLENDTYPE & bl
|
|
)
|
|
{
|
|
DEFINE_POINTERS(BYTE, BYTE)
|
|
|
|
ASSERT(count>0);
|
|
|
|
do {
|
|
if (!bl.IsTransparent())
|
|
{
|
|
// Doing byte per byte writes are much faster than finding
|
|
// runs and doing DWORD copies.
|
|
*(d) = *(s);
|
|
*(d + 1) = *(s + 1);
|
|
*(d + 2) = *(s + 2);
|
|
}
|
|
d += 3;
|
|
s += 3;
|
|
++bl;
|
|
} while (--count != 0);
|
|
} // CTWriteRMW24
|
|
|
|
|
|
VOID FASTCALL ScanOperation::WriteRMW_24_CT_CARGB(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeCARGBBlend bl(otherParams);
|
|
CTWriteRMW24(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::WriteRMW_24_CT_CARGB
|
|
|
|
VOID FASTCALL ScanOperation::WriteRMW_24_CT_Solid(
|
|
VOID *dst,
|
|
const VOID *src,
|
|
INT count,
|
|
const OtherParams *otherParams
|
|
)
|
|
{
|
|
ClearTypeSolidBlend bl(otherParams);
|
|
CTWriteRMW24(dst, src, count, otherParams, bl);
|
|
} // ScanOperation::WriteRMW_24_CT_CARGB
|
|
|