|
|
/**************************************************************************\
* * 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
|