2017-02-22 11:49:55 -06:00
// 12 february 2017
# include "uipriv_windows.hpp"
# include "draw.hpp"
2017-05-17 15:15:54 -05:00
// TODO this whole file needs cleanup
2017-02-22 14:19:11 -06:00
// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect()
// we also need to collect all the OpenType features and background blocks and add them all at once
2017-05-17 15:15:54 -05:00
// TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly
2017-02-22 11:49:55 -06:00
// TODO contextual alternates override ligatures?
// TODO rename this struct to something that isn't exclusively foreach-ing?
struct foreachParams {
const uint16_t * s ;
IDWriteTextLayout * layout ;
2017-02-22 14:19:11 -06:00
std : : map < size_t , textDrawingEffect * > * effects ;
2017-02-22 11:49:55 -06:00
std : : map < size_t , IDWriteTypography * > * features ;
2017-02-22 18:13:36 -06:00
std : : vector < backgroundFunc > * backgroundFuncs ;
2017-02-22 11:49:55 -06:00
} ;
# define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000))
2017-02-22 14:19:11 -06:00
static void ensureEffectsInRange ( struct foreachParams * p , size_t start , size_t end , std : : function < void ( textDrawingEffect * ) > f )
{
size_t i ;
size_t * key ;
textDrawingEffect * t ;
for ( i = start ; i < end ; i + + ) {
// don't create redundant entries; see below
if ( ! isCodepointStart ( p - > s [ i ] ) )
continue ;
t = ( * ( p - > effects ) ) [ i ] ;
if ( t ! = NULL ) {
f ( t ) ;
continue ;
}
t = new textDrawingEffect ;
f ( t ) ;
( * ( p - > effects ) ) [ i ] = t ;
}
}
2017-05-17 15:15:54 -05:00
static void setFeaturesInRange ( struct foreachParams * p , size_t start , size_t end , uiOpenTypeFeatures * otf )
2017-02-22 11:49:55 -06:00
{
2017-05-17 15:15:54 -05:00
IDWriteTypography * dt ;
2017-02-22 11:49:55 -06:00
size_t i ;
2017-05-17 15:15:54 -05:00
dt = otfToDirectWrite ( otf ) ;
2017-02-22 11:49:55 -06:00
for ( i = start ; i < end ; i + + ) {
// don't create redundant entries; see below
2017-05-17 15:15:54 -05:00
// TODO explain this more clearly (surrogate pairs)
2017-02-22 11:49:55 -06:00
if ( ! isCodepointStart ( p - > s [ i ] ) )
continue ;
2017-05-17 15:15:54 -05:00
dt - > AddRef ( ) ;
( * ( p - > features ) ) [ i ] = dt ;
2017-02-22 11:49:55 -06:00
}
2017-05-17 15:15:54 -05:00
// and release the initial reference
dt - > Release ( ) ;
2017-02-22 11:49:55 -06:00
}
2017-02-22 18:13:36 -06:00
static backgroundFunc mkBackgroundFunc ( size_t start , size_t end , double r , double g , double b , double a )
2017-02-22 11:49:55 -06:00
{
2017-02-22 18:13:36 -06:00
return [ = ] ( uiDrawContext * c , uiDrawTextLayout * layout , double x , double y ) {
uiDrawBrush brush ;
2017-02-22 11:49:55 -06:00
2017-02-22 18:13:36 -06:00
brush . Type = uiDrawBrushTypeSolid ;
brush . R = r ;
brush . G = g ;
brush . B = b ;
brush . A = a ;
drawTextBackground ( c , x , y , layout , start , end , & brush , 0 ) ;
} ;
2017-02-22 11:49:55 -06:00
}
static int processAttribute ( uiAttributedString * s , uiAttributeSpec * spec , size_t start , size_t end , void * data )
{
struct foreachParams * p = ( struct foreachParams * ) data ;
DWRITE_TEXT_RANGE range ;
WCHAR * wfamily ;
2017-02-22 20:46:15 -06:00
size_t ostart , oend ;
2017-02-22 18:13:36 -06:00
BOOL hasUnderline ;
2017-02-22 11:49:55 -06:00
HRESULT hr ;
2017-02-22 20:46:15 -06:00
ostart = start ;
oend = end ;
2017-02-22 11:49:55 -06:00
start = attrstrUTF8ToUTF16 ( s , start ) ;
end = attrstrUTF8ToUTF16 ( s , end ) ;
range . startPosition = start ;
range . length = end - start ;
switch ( spec - > Type ) {
case uiAttributeFamily :
wfamily = toUTF16 ( ( char * ) ( spec - > Value ) ) ;
hr = p - > layout - > SetFontFamilyName ( wfamily , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying family name attribute " , hr ) ;
uiFree ( wfamily ) ;
break ;
case uiAttributeSize :
hr = p - > layout - > SetFontSize (
// TODO unify with drawtext.cpp
# define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
pointSizeToDWriteSize ( spec - > Double ) ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying size attribute " , hr ) ;
break ;
case uiAttributeWeight :
2017-02-22 14:19:11 -06:00
// TODO reverse the misalignment from drawtext.cpp if it is corrected
hr = p - > layout - > SetFontWeight (
( DWRITE_FONT_WEIGHT ) ( spec - > Value ) ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying weight attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeItalic :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontStyle (
dwriteItalics [ ( uiDrawTextItalic ) ( spec - > Value ) ] ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying italic attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeStretch :
2017-02-22 14:19:11 -06:00
hr = p - > layout - > SetFontStretch (
dwriteStretches [ ( uiDrawTextStretch ) ( spec - > Value ) ] ,
range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying stretch attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeColor :
2017-02-22 14:19:11 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasColor = true ;
t - > r = spec - > R ;
t - > g = spec - > G ;
t - > b = spec - > B ;
t - > a = spec - > A ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeBackground :
2017-02-22 18:13:36 -06:00
p - > backgroundFuncs - > push_back (
2017-02-22 20:46:15 -06:00
mkBackgroundFunc ( ostart , oend ,
2017-02-22 18:13:36 -06:00
spec - > R , spec - > G , spec - > B , spec - > A ) ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeUnderline :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderline = true ;
t - > u = ( uiDrawUnderlineStyle ) ( spec - > Value ) ;
} ) ;
// mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method
hasUnderline = FALSE ;
if ( ( uiDrawUnderlineStyle ) ( spec - > Value ) ! = uiDrawUnderlineStyleNone )
hasUnderline = TRUE ;
hr = p - > layout - > SetUnderline ( hasUnderline , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying underline attribute " , hr ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiAttributeUnderlineColor :
switch ( spec - > Value ) {
case uiDrawUnderlineColorCustom :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = spec - > R ;
t - > ug = spec - > G ;
t - > ub = spec - > B ;
t - > ua = spec - > A ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
2017-02-22 18:13:36 -06:00
// TODO see if Microsoft has any standard colors for this
2017-02-22 11:49:55 -06:00
case uiDrawUnderlineColorSpelling :
// TODO GtkTextView style property error-underline-color
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = 1.0 ;
t - > ug = 0.0 ;
t - > ub = 0.0 ;
t - > ua = 1.0 ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiDrawUnderlineColorGrammar :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = 0.0 ;
t - > ug = 1.0 ;
t - > ub = 0.0 ;
t - > ua = 1.0 ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
case uiDrawUnderlineColorAuxiliary :
2017-02-22 18:13:36 -06:00
ensureEffectsInRange ( p , start , end , [ = ] ( textDrawingEffect * t ) {
t - > hasUnderlineColor = true ;
t - > ur = 0.0 ;
t - > ug = 0.0 ;
t - > ub = 1.0 ;
t - > ua = 1.0 ;
} ) ;
2017-02-22 11:49:55 -06:00
break ;
}
break ;
2017-05-17 18:21:27 -05:00
case uiAttributeFeatures :
2017-05-17 15:15:54 -05:00
setFeaturesInRange ( p , start , end , ( uiOpenTypeFeatures * ) ( spec - > Value ) ) ;
2017-02-22 11:49:55 -06:00
break ;
2017-05-17 15:15:54 -05:00
default :
// TODO complain
;
2017-02-22 11:49:55 -06:00
}
return 0 ;
}
2017-02-22 14:19:11 -06:00
static void applyAndFreeEffectsAttributes ( struct foreachParams * p )
{
DWRITE_TEXT_RANGE range ;
HRESULT hr ;
for ( auto iter = p - > effects - > begin ( ) ; iter ! = p - > effects - > end ( ) ; iter + + ) {
// make sure we cover an entire code point
range . startPosition = iter - > first ;
range . length = 1 ;
if ( ! isCodepointStart ( p - > s [ iter - > first ] ) )
range . length = 2 ;
hr = p - > layout - > SetDrawingEffect ( iter - > second , range ) ;
if ( hr ! = S_OK )
logHRESULT ( L " error applying drawing effects attributes " , hr ) ;
iter - > second - > Release ( ) ;
}
delete p - > effects ;
}
2017-02-22 11:49:55 -06:00
static void applyAndFreeFeatureAttributes ( struct foreachParams * p )
{
DWRITE_TEXT_RANGE range ;
HRESULT hr ;
for ( auto iter = p - > features - > begin ( ) ; iter ! = p - > features - > end ( ) ; iter + + ) {
// make sure we cover an entire code point
range . startPosition = iter - > first ;
range . length = 1 ;
if ( ! isCodepointStart ( p - > s [ iter - > first ] ) )
range . length = 2 ;
hr = p - > layout - > SetTypography ( iter - > second , range ) ;
if ( hr ! = S_OK )
2017-02-22 14:19:11 -06:00
logHRESULT ( L " error applying typographic features attributes " , hr ) ;
2017-02-22 11:49:55 -06:00
iter - > second - > Release ( ) ;
}
delete p - > features ;
}
2017-02-22 18:13:36 -06:00
void attrstrToIDWriteTextLayoutAttrs ( uiDrawTextLayoutParams * p , IDWriteTextLayout * layout , std : : vector < backgroundFunc > * * backgroundFuncs )
2017-02-22 11:49:55 -06:00
{
struct foreachParams fep ;
fep . s = attrstrUTF16 ( p - > String ) ;
fep . layout = layout ;
2017-02-22 14:19:11 -06:00
fep . effects = new std : : map < size_t , textDrawingEffect * > ;
2017-02-22 11:49:55 -06:00
fep . features = new std : : map < size_t , IDWriteTypography * > ;
2017-02-22 18:13:36 -06:00
fep . backgroundFuncs = new std : : vector < backgroundFunc > ;
2017-02-22 11:49:55 -06:00
uiAttributedStringForEachAttribute ( p - > String , processAttribute , & fep ) ;
2017-02-22 14:19:11 -06:00
applyAndFreeEffectsAttributes ( & fep ) ;
2017-02-22 11:49:55 -06:00
applyAndFreeFeatureAttributes ( & fep ) ;
2017-02-22 18:13:36 -06:00
* backgroundFuncs = fep . backgroundFuncs ;
2017-02-22 11:49:55 -06:00
}