WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/graphicstext.cpp
2020-09-30 16:53:49 +02:00

1259 lines
38 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1999-2000 Microsoft Corporation
*
* Module Name:
*
* GraphicsText.cpp
*
* Abstract:
*
* Text layout and display, Text measurement, Unicode to glyph mapping
*
* Notes:
*
* Provides support to allow apps to work with Unicode in logical order,
* hides the mapping from Unicode to glyphs.
*
* Created:
*
* 06/01/99 dbrown
*
\**************************************************************************/
#include "precomp.hpp"
const DOUBLE PI = 3.1415926535897932384626433832795;
///// Coordinate systems
//
// The following coordinate systems are used:
//
// World coordinates (REAL) - the coordinate system used by client
// applications and passed to most Graphics APIs (for example
// DrawLine). Text is always purely vertical or purely horizontal in
// world coordinates. Fonts constructed with emSize specified in
// alternate units are first converted to world units by calling
// GetScaleForAlternatePageUnit.
//
// Device coordinates (REAL) - Coordinates used on the device surface.
// World coordinates are transformed to device coordinates using the
// Graphics.Context.WorldToDevice.Transform function. REAL device
// coordinates may have non-integral values when addressing sub-pixels.
//
// Font nominal coordinates (INT) - (aka deign units) coordinates used to
// define a scalable font independant of scaled size.
// GpFontFace.GetDesignEmHeight is the emSize of a font in nominal units.
// Nominal coordinates are always a pure scale factor of world units with
// no shear. For horizontal text there there is no rotation between
// nominal and world coordinates. For vertical text, most non Far East
// script characters are rotated by 90 degrees.
//
// Ideal coordinates (INT) - world coordinates mapped to integers by
// a pure scale factor for use in Line Services, OpenType services and
// Uniscribe shaping engine interfaces. The scale factor is usually
// 2048 divided by the emSize of the default font in a text imager.
///// Transforms
//
// WorldToDevice - stored in a Graphics. May include scaling,
// shearing and/or translation.
//
// WorldToIdeal - stored in a text imager while the imager is attached
// to a Graphics. A pure scale factor, usually 2048 divided by the emSize
// of the imager default font.
//
// FontTransform - stored in a FaceRealization. Maps font nominal units
// to device coordinates, May include scaling, shearing and rotation, but
// not translation.
///// Common buffer parameters
//
// glyphAdvance - per-glyph advance widths stored in ideal units measured
// along the text baseline.
//
// glyphOffset - combining character offsets stored in ideal units
// measured along and perpendicular to the baseline. The glyphOffset
// buffer is required by Line Services, OpenType services and the
// complex script shaping engines, but may somethimes be bypassed for
// simple scripts.
//
// glyphOrigins - per glyph device coordinates of the glyph origin (the
// initial point on the baseline of the glyhps advance vector).
// Represented as PointF. Non integer values represent sub pixel
// positions.
///// Glyph positioning functions
//
//
// DrawPlacedGlyphs - Builds glyphPos array and passes it to the device driver.
// ALL text device output output eventually comes through here.
//
// GetDeviceGlyphOriginsNominal
// Used when there's no hinting to be accounted for.
// Places glyph on device using nominal metrics passed in glyphAdvances
// and glyphOffsets.
//
// GetDeviceGlyphOriginsAdjusted
// Used to adjust for the difference between nominal and hinted metrics
// Generates glyph origins in device units, and adjusts the width of spaces
// to achieve the totalRequiredIdealAdvance parameter.
// !!! Need to add support for kashida and inter-glyph justification.
//
// GetRealizedGlyphPlacement
// Used to obtain hinted advance metrics along the baseline.
// !!! Needs to be updated to call complex script shaping engines.
//
// GetFontTransformForAlternateResolution
// Used during XMF playback.
// Generates a font transform to match a font that was recorded at
// a different resolution.
//
// MeasureGlyphsAtAlternateResolution
// Used during XMF playback.
// Measures glyphs passed to DrawDriverString as if they were to be rendered
// at the original XMF recording resolution.
/**************************************************************************\
*
* GpGraphics::DrawString
*
* Draw plain, marked up or formatted text in a rectangle
*
* Arguments:
*
*
* Return Value:
*
* GDIPlus status
*
* Created:
*
* 06/25/99 dbrown
*
\**************************************************************************/
GpStatus
GpGraphics::DrawString(
const WCHAR *string,
INT length,
const GpFont *font,
const RectF *layoutRect,
const GpStringFormat *format,
const GpBrush *brush
)
{
ASSERT(string && font && brush);
GpStatus status = CheckTextMode();
if (status != Ok)
{
if (IsRecording())
SetValid(FALSE); // Prevent any more recording
return status;
}
// Check that the clipping rectangle, if any, is visible, at least in part.
if ( !IsRecording() // Metafile clipping happens at playback
&& layoutRect->Width
&& layoutRect->Height
&& ( !format
|| !(format->GetFormatFlags() & StringFormatFlagsNoClip)))
{
if ( layoutRect->Width < 0
|| layoutRect->Height < 0)
{
// Client has requested clipping to an empty rectangle, nothing
// will display.
return Ok;
}
// If client clipping rectangle is outside the visible clipping region -- were done.
GpRectF deviceClipRectFloat;
GpRect deviceClipRectPixel;
GpMatrix worldToDevice;
TransformBounds(
&Context->WorldToDevice,
layoutRect->X,
layoutRect->Y,
layoutRect->X + layoutRect->Width,
layoutRect->Y + layoutRect->Height,
&deviceClipRectFloat
);
status = BoundsFToRect(&deviceClipRectFloat, &deviceClipRectPixel);
if(status != Ok)
{
return status;
}
if (IsTotallyClipped(&deviceClipRectPixel))
{
// Since nothing will be visible, we need do no more.
return Ok;
}
}
REAL emSize = font->GetEmSize() * GetScaleForAlternatePageUnit(font->GetUnit());
if (IsRecording())
{
// Record Gdiplus metafile record
// first measure the text bounding rectangle
RectF boundingBox;
status = MeasureString(
string,
length,
font,
layoutRect,
format,
&boundingBox,
NULL,
NULL);
if (status != Ok)
{
SetValid(FALSE); // Prevent any more recording
return status;
}
GpRectF bounds;
TransformBounds(&(Context->WorldToDevice), boundingBox.X, boundingBox.Y,
boundingBox.GetRight(), boundingBox.GetBottom(), &bounds);
status = Metafile->RecordDrawString(
&bounds,
string,
length,
font,
layoutRect,
format,
brush
);
if (status != Ok)
{
SetValid(FALSE); // Prevent any more recording
return status;
}
if (!DownLevel)
{
return Ok;
}
// else we need to record down-level GDI EMF records as well
// Since we have recorded all the parameters to DrawString,
// we don't need to do anything more. For the downlevel case,
// we need to record the DrawString call as a sequence of
// ExtTextOut calls.
}
else
{
// Not recording a metafile, so it is safe to try using the fast text imager.
FastTextImager fastImager;
status = fastImager.Initialize(
this,
string,
length,
*layoutRect,
font->GetFamily(),
font->GetStyle(),
emSize,
format,
brush
);
if (status == Ok)
{
status = fastImager.DrawString();
}
// If the fast text imager couldn't handle this case, it returns
// NotImplemented, and we continue into the full text imager.
// Otherwise it either completed successfully or hit an error
// that we need to report.
if (status != NotImplemented)
{
return status;
}
}
// Draw text with the full text imager
GpTextImager *imager;
status = newTextImager( // Always creates a fulltextimager.
string,
length,
layoutRect->Width,
layoutRect->Height,
font->GetFamily(),
font->GetStyle(),
emSize,
format,
brush,
&imager,
TRUE // Fast way to set NoChange flag to allow simple imager
);
IF_NOT_OK_WARN_AND_RETURN(status);
imager->GetMetaFileRecordingFlag() = IsRecording();
EmfPlusDisabler disableEmfPlus(&Metafile);
status = imager->Draw(this, &PointF(layoutRect->X, layoutRect->Y));
delete imager;
return status;
}
/**************************************************************************\
*
* GpGraphics::MeasureString
*
* Measure plain, marked up or formatted text in a rectangle
*
* Arguments:
*
*
* Return Value:
*
* GDIPlus status
*
* Created:
*
* 10/26/99 dbrown
*
\**************************************************************************/
GpStatus
GpGraphics::MeasureString(
const WCHAR *string,
INT length,
const GpFont *font,
const RectF *layoutRect,
const GpStringFormat *format,
RectF *boundingBox,
INT *codepointsFitted,
INT *linesFilled
)
{
CalculateTextRenderingHintInternal();
ASSERT(string && font && boundingBox);
if (!string || !font || !boundingBox)
{
return InvalidParameter;
}
GpStatus status;
REAL emSize = font->GetEmSize() * GetScaleForAlternatePageUnit(font->GetUnit());
if (!IsRecording())
{
// Try using the fast imager
FastTextImager fastImager;
status = fastImager.Initialize(
this,
string,
length,
*layoutRect,
font->GetFamily(),
font->GetStyle(),
emSize,
format,
NULL
);
if (status == Ok)
{
status = fastImager.MeasureString(
boundingBox,
codepointsFitted,
linesFilled
);
}
// If the fast text imager couldn't handle this case, it returns
// NotImplemented, and we continue into the full text imager.
// Otherwise it either completed successfully or hit an error
// that we need to report.
if (status != NotImplemented)
{
return status;
}
}
// Measure text with the full text imager
GpTextImager *imager;
status = newTextImager(
string,
length,
layoutRect->Width,
layoutRect->Height,
font->GetFamily(),
font->GetStyle(),
emSize,
format,
NULL,
&imager,
TRUE // Enable use of simple formatter when no format passed
);
IF_NOT_OK_WARN_AND_RETURN(status);
*boundingBox = *layoutRect;
REAL nearGlyphEdge;
REAL farGlyphEdge;
REAL textDepth;
status = imager->Measure( // Returned edges exclude overhang
this,
&nearGlyphEdge,
&farGlyphEdge,
&textDepth,
codepointsFitted,
linesFilled
);
// Generate bounding box (excluding overhang) from near and far glyph edges
if (status == Ok)
{
// Fix up near/far glyph edges for empty box
if (nearGlyphEdge > farGlyphEdge)
{
nearGlyphEdge = 0;
farGlyphEdge = 0;
}
if ( format
&& format->GetFormatFlags() & StringFormatFlagsDirectionVertical)
{
boundingBox->Y = layoutRect->Y + nearGlyphEdge;
boundingBox->Height = farGlyphEdge - nearGlyphEdge;
if (format)
{
StringAlignment lineAlign = format->GetLineAlign();
REAL leadingOffset = 0.0; // positive offset to the leading side edge of the textbox
if (lineAlign == StringAlignmentCenter)
{
leadingOffset = (boundingBox->Width - textDepth)/2;
}
else if (lineAlign == StringAlignmentFar)
{
leadingOffset = boundingBox->Width - textDepth;
}
if (format->GetFormatFlags() & StringFormatFlagsDirectionRightToLeft)
{
boundingBox->X += (boundingBox->Width - textDepth - leadingOffset);
}
else
{
boundingBox->X += leadingOffset;
}
}
boundingBox->Width = textDepth;
}
else
{
boundingBox->X = layoutRect->X + nearGlyphEdge;
boundingBox->Width = farGlyphEdge - nearGlyphEdge;
if (format)
{
StringAlignment lineAlign = format->GetLineAlign();
if (lineAlign == StringAlignmentCenter)
{
boundingBox->Y += (boundingBox->Height - textDepth) / 2;
}
else if (lineAlign == StringAlignmentFar)
{
boundingBox->Y += boundingBox->Height - textDepth;
}
}
boundingBox->Height = textDepth;
}
if (!format
|| !(format->GetFormatFlags() & StringFormatFlagsNoClip))
{
// Make sure display bounding box never exceeds layout rectangle
// in case of clipping.
if ( layoutRect->Width > 0.0
&& boundingBox->Width > layoutRect->Width)
{
boundingBox->Width = layoutRect->Width;
boundingBox->X = layoutRect->X;
}
if ( layoutRect->Height > 0.0
&& boundingBox->Height > layoutRect->Height)
{
boundingBox->Height = layoutRect->Height;
boundingBox->Y = layoutRect->Y;
}
}
}
delete imager;
return status;
}
/**************************************************************************\
*
* GpGraphics::MeasureCharacterRanges
*
* Produce a bounding regions of all given character ranges in stringformat
*
* Arguments:
*
*
* Return Value:
*
* GDIPlus status
*
* Created:
*
* 10-9-2000 wchao
*
\**************************************************************************/
GpStatus
GpGraphics::MeasureCharacterRanges(
const WCHAR *string,
INT length,
const GpFont *font,
const RectF &layoutRect,
const GpStringFormat *format,
INT regionCount,
GpRegion **regions
)
{
CalculateTextRenderingHintInternal();
ASSERT(format && string && font && regions);
INT rangeCount = format->GetMeasurableCharacterRanges();
if (regionCount < rangeCount)
{
return InvalidParameter;
}
INT stringLength;
if (length == -1)
{
stringLength = 0;
while (string[stringLength])
{
stringLength++;
}
}
else
{
stringLength = length;
}
GpStatus status;
REAL emSize = font->GetEmSize() * GetScaleForAlternatePageUnit(font->GetUnit());
GpTextImager *imager;
status = newTextImager(
string,
stringLength,
layoutRect.Width,
layoutRect.Height,
font->GetFamily(),
font->GetStyle(),
emSize,
format,
NULL,
&imager,
TRUE // Enable use of simple formatter when no format passed
);
IF_NOT_OK_WARN_AND_RETURN(status);
imager->GetMetaFileRecordingFlag() = IsRecording();
PointF imagerOrigin(layoutRect.X , layoutRect.Y);
status = imager->MeasureRanges(
this,
&imagerOrigin,
regions
);
delete imager;
return status;
}
///// DrawPlacedGlyphs - Draw glyphs with arbitrary transform at device coordinates
//
//
GpStatus
GpGraphics::DrawPlacedGlyphs(
const GpFaceRealization *faceRealization,
const GpBrush *brush,
INT flags, // For DG_NOGDI
const WCHAR *string,
UINT stringLength,
BOOL rightToLeft,
const UINT16 *glyphs,
const UINT16 *glyphMap,
const PointF *glyphOrigins,
INT glyphCount,
ItemScript Script,
BOOL sideways // e.g. FE characters in vertical text
)
{
IF_NOT_OK_WARN_AND_RETURN(faceRealization->GetStatus());
INT i;
BOOL bNeedPath = FALSE;
GpFaceRealization cloneFaceRealization;
GpGlyphPos *glyphPositions = NULL;
GpGlyphPos *glyphPathPositions = NULL;
// Display glyphs for Bits. Handle as many as possible in one go.
INT glyphStart = 0; // start of this display run
INT glyphsProcessed; // Number of glyphs processed by this GetGlyphPos call
INT glyphPositionCount; // Number of glyphPositions generated by this GetGlyphPos call
// Display glyphs for path. Handle as many as possible in one go.
INT glyphPathStart = 0; // start of this display run
INT glyphsPathProcessed, glyphsPathProcessedTemp; // Number of glyphs processed by this GetGlyphPos call
INT glyphPathPositionCount, glyphPathPositionCountTemp; // Number of glyphPositions generated by this GetGlyphPos call
GpStatus status = Ok;
if (!glyphOrigins)
{
ASSERT(glyphOrigins);
return GenericError;
}
// For sideways text, we have been passed glyph origins at the
// top baseline, but we need to pass leftside baseline origins
// to DrvDrawGlyphs for the benefit of metafiles and GDI positioning.
AutoBuffer<PointF, 16> adjustedGlyphOrigins;
const PointF *leftsideGlyphOrigins = glyphOrigins;
if (sideways && Driver != Globals::MetaDriver)
{
adjustedGlyphOrigins.SetSize(glyphCount);
if (!adjustedGlyphOrigins)
{
status = OutOfMemory;
goto error;
}
status = faceRealization->GetGlyphStringVerticalOriginOffsets(
glyphs,
glyphCount,
adjustedGlyphOrigins.Get()
);
if (status != Ok)
{
goto error;
}
for (INT i=0; i<glyphCount; i++)
{
adjustedGlyphOrigins[i].X = glyphOrigins[i].X - adjustedGlyphOrigins[i].X;
adjustedGlyphOrigins[i].Y = glyphOrigins[i].Y - adjustedGlyphOrigins[i].Y;
}
leftsideGlyphOrigins = adjustedGlyphOrigins.Get();
}
glyphPositions = new GpGlyphPos[glyphCount];
if (!glyphPositions)
{
status = OutOfMemory;
goto error;
}
ASSERT(!faceRealization->IsPathFont() || Driver == Globals::MetaDriver);
if (Driver == Globals::MetaDriver)
{
INT minX = MAXLONG;
INT minY = MAXLONG;
INT maxX = MINLONG;
INT maxY = MINLONG;
INT glyphPositionCountTemp = 0;
while (glyphStart < glyphCount)
{
glyphPositionCount = faceRealization->GetGlyphPos(
glyphCount - glyphStart,
glyphs + glyphStart,
glyphPositions + glyphStart,
glyphOrigins + glyphStart,
&glyphsProcessed,
sideways
);
if (glyphPositionCount == 0 && ((glyphsProcessed + glyphStart) < glyphCount))
{
status = OutOfMemory;
goto error;
}
for (i = 0; i < glyphPositionCount; i++)
{
INT j = glyphPositionCountTemp + i;
if (glyphPositions[j].GetWidth() != 0 &&
glyphPositions[j].GetHeight() != 0)
{
minX = min(minX, glyphPositions[j].GetLeft());
minY = min(minY, glyphPositions[j].GetTop());
maxX = max(maxX, glyphPositions[j].GetLeft() + glyphPositions[j].GetWidth());
maxY = max(maxY, glyphPositions[j].GetTop() + glyphPositions[j].GetHeight());
}
if (glyphPositions[j].GetTempBits() != NULL)
{
GpFree(glyphPositions[j].GetTempBits());
glyphPositions[j].SetTempBits(0);
}
}
glyphStart += glyphsProcessed;
glyphPositionCountTemp += glyphPositionCount;
}
glyphPositionCount = glyphPositionCountTemp;
if (minX < maxX && minY < maxY)
{
// must grab the devlock before going into the driver.
Devlock devlock(Device);
GpRect drawBounds(minX, minY, maxX-minX, maxY-minY);
REAL edgeGlyphAdvance;
if (rightToLeft)
{
status = faceRealization->GetGlyphStringDeviceAdvanceVector(glyphs,
1,
FALSE,
&edgeGlyphAdvance);
}
else
{
status = faceRealization->GetGlyphStringDeviceAdvanceVector(&glyphs[glyphCount-1],
1,
FALSE,
&edgeGlyphAdvance);
}
if (status != Ok)
goto error;
if (sideways)
{
flags |= DG_SIDEWAY;
}
status = DrvDrawGlyphs(
&drawBounds,
glyphPositions,
NULL,
glyphPositionCount,
brush->GetDeviceBrush(),
faceRealization,
glyphs,
glyphMap,
leftsideGlyphOrigins,
glyphCount,
string,
stringLength,
Script,
GpRound(edgeGlyphAdvance),
rightToLeft,
flags
);
if (status != Ok)
goto error;
}
}
else
{
if (IsPrinter())
{
DriverPrint *pdriver = (DriverPrint*) Driver;
if (pdriver->DriverType == DriverPostscript)
{
if (brush->GetBrushType() != BrushTypeSolidColor)
{
// generate bitmap & path in glyphPos
bNeedPath = TRUE;
}
}
}
if (bNeedPath)
{
cloneFaceRealization.CloneFaceRealization(faceRealization, TRUE);
if (!cloneFaceRealization.IsValid())
{
status = OutOfMemory;
goto error;
}
ASSERT(cloneFaceRealization.IsPathFont());
}
if (bNeedPath)
{
glyphPathPositions = new GpGlyphPos[glyphCount];
if (!glyphPathPositions)
{
status = OutOfMemory;
goto error;
}
}
while (glyphStart < glyphCount)
{
glyphPositionCount = faceRealization->GetGlyphPos(
glyphCount - glyphStart,
glyphs + glyphStart,
glyphPositions,
glyphOrigins + glyphStart,
&glyphsProcessed,
sideways
);
// glyphPositionCount = number of entries added to glyphPositions array
// glyphsPositioned = number of glyph indices processed from glyph buffer
if (glyphPositionCount == 0 && ((glyphsProcessed + glyphStart) < glyphCount))
{
status = OutOfMemory;
goto error;
}
glyphsPathProcessed = 0;
glyphPathPositionCount = 0;
while (glyphsPathProcessed < glyphsProcessed)
{
INT minX = MAXLONG;
INT minY = MAXLONG;
INT maxX = MINLONG;
INT maxY = MINLONG;
if (bNeedPath)
{
glyphPathPositionCountTemp = cloneFaceRealization.GetGlyphPos(
glyphsProcessed - glyphsPathProcessed,
glyphs + glyphPathStart + glyphsPathProcessed,
glyphPathPositions,
glyphOrigins + glyphPathStart + glyphsPathProcessed,
&glyphsPathProcessedTemp,
sideways
);
glyphsPathProcessed += glyphsPathProcessedTemp;
if (glyphPathPositionCountTemp == 0 && (glyphsPathProcessed < glyphsProcessed))
{
ASSERT(glyphPathPositionCount != glyphPositionCount);
status = OutOfMemory;
goto error;
}
}
else
{
glyphsPathProcessed = glyphsProcessed;
glyphPathPositionCountTemp = glyphPositionCount;
}
for (i = 0; i < glyphPathPositionCountTemp; i++)
{
INT j = glyphPathPositionCount + i;
if (glyphPositions[j].GetWidth() != 0 &&
glyphPositions[j].GetHeight() != 0)
{
minX = min(minX, glyphPositions[j].GetLeft());
minY = min(minY, glyphPositions[j].GetTop());
maxX = max(maxX, glyphPositions[j].GetLeft() + glyphPositions[j].GetWidth());
maxY = max(maxY, glyphPositions[j].GetTop() + glyphPositions[j].GetHeight());
}
}
if (minX < maxX && minY < maxY)
{
// must grab the devlock before going into the driver.
Devlock devlock(Device);
GpRect drawBounds(minX, minY, maxX-minX, maxY-minY);
REAL edgeGlyphAdvance;
if (rightToLeft)
{
status = faceRealization->GetGlyphStringDeviceAdvanceVector(glyphs,
1,
FALSE,
&edgeGlyphAdvance);
}
else
{
status = faceRealization->GetGlyphStringDeviceAdvanceVector(&glyphs[glyphCount-1],
1,
FALSE,
&edgeGlyphAdvance);
}
if (status != Ok)
goto error;
status = DrvDrawGlyphs(
&drawBounds,
&glyphPositions[glyphPathPositionCount],
glyphPathPositions,
glyphPathPositionCountTemp,
brush->GetDeviceBrush(),
faceRealization,
glyphs + glyphPathStart,
glyphMap + glyphPathStart,
leftsideGlyphOrigins + glyphPathStart,
glyphsProcessed,
string,
stringLength,
Script,
GpRound(edgeGlyphAdvance),
rightToLeft,
flags
);
if (status != Ok)
goto error;
}
glyphPathPositionCount += glyphPathPositionCountTemp;
}
ASSERT (glyphsPathProcessed == glyphsProcessed);
ASSERT (glyphPathPositionCount == glyphPositionCount);
// Free any temporary bitmap buffers created by subpixelling
for (i=0; i<glyphPositionCount; i++)
{
if (glyphPositions[i].GetTempBits() != NULL)
{
GpFree(glyphPositions[i].GetTempBits());
glyphPositions[i].SetTempBits(0);
}
}
glyphStart += glyphsProcessed;
glyphPathStart += glyphsPathProcessed;
}
}
error:
// free memory allocated
if (glyphPositions)
delete [] glyphPositions;
if (glyphPathPositions)
delete [] glyphPathPositions;
return status;
}
// GpGraphics::CheckTextMode
// disallow ClearType text for CompositingModeSourceCopy
GpStatus GpGraphics::CheckTextMode()
{
CalculateTextRenderingHintInternal();
if (GetCompositingMode() == CompositingModeSourceCopy &&
GetTextRenderingHintInternal() == TextRenderingHintClearTypeGridFit)
{
ONCE(WARNING(("CompositingModeSourceCopy cannot be used with ClearType text")));
return InvalidParameter;
}
return Ok;
} // GpGraphics::CheckTextMode
void GpGraphics::CalculateTextRenderingHintInternal()
{
// this procedure is meant to be used by internal text routine and will convert TextRenderingHintSystemDefault
// to the current system mode
ASSERT(Context);
TextRenderingHint textMode = Context->TextRenderHint;
if (IsPrinter())
{
textMode = TextRenderingHintSingleBitPerPixelGridFit;
}
else if (textMode == TextRenderingHintSystemDefault)
{
if (Globals::CurrentSystemRenderingHintInvalid)
{
// Get the current text antialiazing mode from the system
DWORD bOldSF, dwOldSFT;
SystemParametersInfoA( SPI_GETFONTSMOOTHING, 0, (PVOID)&bOldSF, 0 );
if (bOldSF)
{
SystemParametersInfoA( SPI_GETFONTSMOOTHINGTYPE, 0, (PVOID)&dwOldSFT, 0 );
if( dwOldSFT & FE_FONTSMOOTHINGCLEARTYPE )
{
Globals::CurrentSystemRenderingHint = TextRenderingHintClearTypeGridFit;
} else
{
Globals::CurrentSystemRenderingHint = TextRenderingHintAntiAliasGridFit;
}
} else
{
Globals::CurrentSystemRenderingHint = TextRenderingHintSingleBitPerPixelGridFit;
}
}
textMode = Globals::CurrentSystemRenderingHint;
}
// Lead and PM decision to disable ClearType on downlevel system, we allow only on Windows NT 5.1 or later
if ((textMode == TextRenderingHintClearTypeGridFit) &&
(!Globals::IsNt ||
(Globals::OsVer.dwMajorVersion < 5) ||
((Globals::OsVer.dwMajorVersion == 5) && (Globals::OsVer.dwMinorVersion < 1))
)
)
{
textMode = TextRenderingHintSingleBitPerPixelGridFit;
}
if (textMode == TextRenderingHintClearTypeGridFit ||
textMode == TextRenderingHintAntiAlias ||
textMode == TextRenderingHintAntiAliasGridFit)
{
if (Surface &&
GetPixelFormatSize(Surface->PixelFormat) <= 8 &&
Surface->PixelFormat != PixelFormatMulti)
{
// disable AA & ClearType in 256 bit color mode and less
textMode = TextRenderingHintSingleBitPerPixelGridFit;
}
else if (Globals::IsTerminalServer)
{
// disable AA & ClearType for Terminal Server desktop surface
if (Surface && Surface->IsDesktopSurface())
{
textMode = TextRenderingHintSingleBitPerPixelGridFit;
}
}
}
if (textMode == TextRenderingHintClearTypeGridFit)
{
if (Globals::CurrentSystemRenderingHintInvalid)
{
// get ClearType orientation setting from the system
UpdateLCDOrientation();
}
}
Globals::CurrentSystemRenderingHintInvalid = FALSE;
TextRenderingHintInternal = textMode;
} // GpGraphics::CalculateTextRenderingHintInternal
///// DrawFontStyleLine
//
// Draw underline or strikethrough or both depending on what style is used
// in the font. Given points are in world coordinate.
//
// Make sure the line thickness is at least 1 pixel wide.
GpStatus GpGraphics::DrawFontStyleLine(
const PointF *baselineOrigin, // baseline origin
REAL baselineLength, // baseline length
const GpFontFace *face, // font face
const GpBrush *brush, // brush
BOOL vertical, // vertical text?
REAL emSize, // font EM size in world unit
INT style, // kind of lines to be drawn
const GpMatrix *matrix // additional transform
)
{
REAL fontToWorld = emSize / TOREAL(face->GetDesignEmHeight());
PointF drawingParams[2]; // X is offset from baseline, Y is device pen width
INT count = 0;
GpStatus status = Ok;
if (style & FontStyleUnderline)
{
// underlining metric
const REAL penPos = face->GetDesignUnderscorePosition() * fontToWorld;
REAL penWidth = face->GetDesignUnderscoreSize() * fontToWorld;
penWidth = GetDevicePenWidth(penWidth, matrix);
drawingParams[count].X = penPos;
drawingParams[count++].Y = penWidth;
}
if (style & FontStyleStrikeout)
{
// strikethrough metric
const REAL penPos = face->GetDesignStrikeoutPosition() * fontToWorld;
REAL penWidth = face->GetDesignStrikeoutSize() * fontToWorld;
penWidth = GetDevicePenWidth(penWidth, matrix);
drawingParams[count].X = penPos;
drawingParams[count++].Y = penWidth;
}
for (INT i = 0; i < count; i++)
{
PointF points[2];
points[0] = *baselineOrigin;
if (vertical)
{
points[0].X += drawingParams[i].X; // offset from baseline
points[1].X = points[0].X;
points[1].Y = points[0].Y + baselineLength;
}
else
{
points[0].Y -= drawingParams[i].X; // offset from baseline
points[1].Y = points[0].Y;
points[1].X = points[0].X + baselineLength;
}
if (matrix)
{
matrix->Transform(points, 2);
}
status = DrawLine(
&GpPen(
brush,
drawingParams[i].Y,
UnitPixel
),
points[0],
points[1]
);
IF_NOT_OK_WARN_AND_RETURN(status);
}
return status;
}
// fix up pen width for strikeout/underline/hotkey cases
// to avoid varying line width within the same paragraph
// return value is in pixel units
REAL GpGraphics::GetDevicePenWidth(
REAL widthInWorldUnits,
const GpMatrix *matrix
)
{
GpMatrix worldToDevice;
GetWorldToDeviceTransform(&worldToDevice);
if (matrix)
{
worldToDevice.Prepend(*matrix);
}
PointF underlineVector(widthInWorldUnits, 0.0f);
worldToDevice.VectorTransform(&underlineVector);
REAL penWidth = (REAL)GpRound(VectorLength(underlineVector));
if (penWidth < 1.0f)
penWidth = 1.0f;
return penWidth;
}
///// DriverString APIs
//
// Driver string APIs are in engine\text\DriverStringImager.cpp