2015-09-16 22:15:42 -05:00
// 7 september 2015
2016-04-23 11:47:15 -05:00
# include "uipriv_windows.hpp"
2015-09-16 22:15:42 -05:00
2016-05-05 19:23:52 -05:00
ID2D1Factory * d2dfactory = NULL ;
2015-09-16 22:15:42 -05:00
HRESULT initDraw ( void )
{
D2D1_FACTORY_OPTIONS opts ;
ZeroMemory ( & opts , sizeof ( D2D1_FACTORY_OPTIONS ) ) ;
// TODO make this an option
opts . debugLevel = D2D1_DEBUG_LEVEL_NONE ;
return D2D1CreateFactory ( D2D1_FACTORY_TYPE_SINGLE_THREADED ,
2016-04-23 21:15:33 -05:00
IID_ID2D1Factory ,
2015-09-16 22:15:42 -05:00
& opts ,
2015-09-17 09:31:22 -05:00
( void * * ) ( & d2dfactory ) ) ;
2015-09-16 22:15:42 -05:00
}
void uninitDraw ( void )
{
2016-04-23 11:47:15 -05:00
d2dfactory - > Release ( ) ;
2015-09-16 22:15:42 -05:00
}
2015-09-16 22:42:24 -05:00
ID2D1HwndRenderTarget * makeHWNDRenderTarget ( HWND hwnd )
{
D2D1_RENDER_TARGET_PROPERTIES props ;
D2D1_HWND_RENDER_TARGET_PROPERTIES hprops ;
HDC dc ;
RECT r ;
ID2D1HwndRenderTarget * rt ;
2015-09-17 09:31:22 -05:00
HRESULT hr ;
2015-09-16 22:42:24 -05:00
// we need a DC for the DPI
// we *could* just use the screen DPI but why when we have a window handle and its DC has a DPI
dc = GetDC ( hwnd ) ;
if ( dc = = NULL )
2016-04-23 11:47:15 -05:00
logLastError ( L " error getting DC to find DPI " ) ;
2015-09-16 22:42:24 -05:00
ZeroMemory ( & props , sizeof ( D2D1_RENDER_TARGET_PROPERTIES ) ) ;
props . type = D2D1_RENDER_TARGET_TYPE_DEFAULT ;
props . pixelFormat . format = DXGI_FORMAT_UNKNOWN ;
props . pixelFormat . alphaMode = D2D1_ALPHA_MODE_UNKNOWN ;
props . dpiX = GetDeviceCaps ( dc , LOGPIXELSX ) ;
props . dpiY = GetDeviceCaps ( dc , LOGPIXELSY ) ;
props . usage = D2D1_RENDER_TARGET_USAGE_NONE ;
props . minLevel = D2D1_FEATURE_LEVEL_DEFAULT ;
if ( ReleaseDC ( hwnd , dc ) = = 0 )
2016-04-23 11:47:15 -05:00
logLastError ( L " error releasing DC for finding DPI " ) ;
2015-09-16 22:42:24 -05:00
2016-04-29 16:08:31 -05:00
uiWindowsEnsureGetClientRect ( hwnd , & r ) ;
2015-09-16 22:42:24 -05:00
ZeroMemory ( & hprops , sizeof ( D2D1_HWND_RENDER_TARGET_PROPERTIES ) ) ;
hprops . hwnd = hwnd ;
hprops . pixelSize . width = r . right - r . left ;
hprops . pixelSize . height = r . bottom - r . top ;
2015-12-04 19:45:31 -06:00
// according to Rick Brewster, some drivers will misbehave if we don't specify this (see http://stackoverflow.com/a/33222983/3408572)
hprops . presentOptions = D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS ;
2015-09-16 22:42:24 -05:00
2016-04-23 11:47:15 -05:00
hr = d2dfactory - > CreateHwndRenderTarget (
2015-09-16 22:42:24 -05:00
& props ,
& hprops ,
& rt ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating area HWND render target " , hr ) ;
2015-09-16 22:42:24 -05:00
return rt ;
}
2015-10-11 11:36:48 -05:00
static void resetTarget ( ID2D1RenderTarget * rt )
{
D2D1_MATRIX_3X2_F dm ;
// transformations persist
// reset to the identity matrix
ZeroMemory ( & dm , sizeof ( D2D1_MATRIX_3X2_F ) ) ;
dm . _11 = 1 ;
dm . _22 = 1 ;
2016-04-23 11:47:15 -05:00
rt - > SetTransform ( & dm ) ;
2015-10-11 11:36:48 -05:00
}
2015-10-07 09:47:38 -05:00
uiDrawContext * newContext ( ID2D1RenderTarget * rt )
{
uiDrawContext * c ;
2015-10-09 10:49:57 -05:00
c = uiNew ( uiDrawContext ) ;
2015-10-07 09:47:38 -05:00
c - > rt = rt ;
2016-05-05 19:23:52 -05:00
c - > states = new std : : vector < struct drawState > ;
2015-10-11 11:36:48 -05:00
resetTarget ( c - > rt ) ;
2015-10-07 09:47:38 -05:00
return c ;
}
2015-10-09 11:41:01 -05:00
void freeContext ( uiDrawContext * c )
{
2015-10-13 12:20:25 -05:00
if ( c - > currentClip ! = NULL )
2016-04-23 11:47:15 -05:00
c - > currentClip - > Release ( ) ;
2016-05-05 19:23:52 -05:00
if ( c - > states - > size ( ) ! = 0 )
// TODO userbug()
complain ( " unbalanced save/restore " ) ;
delete c - > states ;
2015-10-09 11:41:01 -05:00
uiFree ( c ) ;
}
2015-10-07 09:47:38 -05:00
static ID2D1Brush * makeSolidBrush ( uiDrawBrush * b , ID2D1RenderTarget * rt , D2D1_BRUSH_PROPERTIES * props )
2015-09-16 22:15:42 -05:00
{
2015-09-17 12:10:38 -05:00
D2D1_COLOR_F color ;
ID2D1SolidColorBrush * brush ;
HRESULT hr ;
2015-09-16 22:15:42 -05:00
2015-10-07 09:47:38 -05:00
color . r = b - > R ;
color . g = b - > G ;
color . b = b - > B ;
color . a = b - > A ;
2016-04-23 11:47:15 -05:00
hr = rt - > CreateSolidColorBrush (
2015-10-07 09:47:38 -05:00
& color ,
2015-10-07 10:12:18 -05:00
props ,
2015-10-07 09:47:38 -05:00
& brush ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating solid brush " , hr ) ;
return brush ;
2015-10-07 09:47:38 -05:00
}
2015-10-07 15:54:56 -05:00
static ID2D1GradientStopCollection * mkstops ( uiDrawBrush * b , ID2D1RenderTarget * rt )
{
ID2D1GradientStopCollection * s ;
D2D1_GRADIENT_STOP * stops ;
size_t i ;
HRESULT hr ;
2016-04-23 21:15:33 -05:00
stops = ( D2D1_GRADIENT_STOP * ) uiAlloc ( b - > NumStops * sizeof ( D2D1_GRADIENT_STOP ) , " D2D1_GRADIENT_STOP[] " ) ;
2015-10-07 15:54:56 -05:00
for ( i = 0 ; i < b - > NumStops ; i + + ) {
stops [ i ] . position = b - > Stops [ i ] . Pos ;
stops [ i ] . color . r = b - > Stops [ i ] . R ;
stops [ i ] . color . g = b - > Stops [ i ] . G ;
stops [ i ] . color . b = b - > Stops [ i ] . B ;
stops [ i ] . color . a = b - > Stops [ i ] . A ;
}
2016-04-23 11:47:15 -05:00
hr = rt - > CreateGradientStopCollection (
2015-10-07 15:54:56 -05:00
stops ,
b - > NumStops ,
D2D1_GAMMA_2_2 , // this is the default for the C++-only overload of ID2D1RenderTarget::GradientStopCollection()
D2D1_EXTEND_MODE_CLAMP ,
& s ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating stop collection " , hr ) ;
2015-10-07 15:54:56 -05:00
2015-10-09 10:49:57 -05:00
uiFree ( stops ) ;
2015-10-07 15:54:56 -05:00
return s ;
}
2015-10-07 09:47:38 -05:00
static ID2D1Brush * makeLinearBrush ( uiDrawBrush * b , ID2D1RenderTarget * rt , D2D1_BRUSH_PROPERTIES * props )
{
2015-10-07 15:54:56 -05:00
ID2D1LinearGradientBrush * brush ;
D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES gprops ;
ID2D1GradientStopCollection * stops ;
HRESULT hr ;
ZeroMemory ( & gprops , sizeof ( D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES ) ) ;
gprops . startPoint . x = b - > X0 ;
gprops . startPoint . y = b - > Y0 ;
gprops . endPoint . x = b - > X1 ;
gprops . endPoint . y = b - > Y1 ;
stops = mkstops ( b , rt ) ;
2016-04-23 11:47:15 -05:00
hr = rt - > CreateLinearGradientBrush (
2015-10-07 15:54:56 -05:00
& gprops ,
props ,
stops ,
& brush ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating gradient brush " , hr ) ;
2015-10-07 15:54:56 -05:00
// the example at https://msdn.microsoft.com/en-us/library/windows/desktop/dd756682%28v=vs.85%29.aspx says this is safe to do now
2016-04-23 11:47:15 -05:00
stops - > Release ( ) ;
return brush ;
2015-10-07 09:47:38 -05:00
}
static ID2D1Brush * makeRadialBrush ( uiDrawBrush * b , ID2D1RenderTarget * rt , D2D1_BRUSH_PROPERTIES * props )
{
2015-10-07 17:32:55 -05:00
ID2D1RadialGradientBrush * brush ;
D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES gprops ;
ID2D1GradientStopCollection * stops ;
HRESULT hr ;
ZeroMemory ( & gprops , sizeof ( D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES ) ) ;
gprops . gradientOriginOffset . x = b - > X0 - b - > X1 ;
gprops . gradientOriginOffset . y = b - > Y0 - b - > Y1 ;
gprops . center . x = b - > X1 ;
gprops . center . y = b - > Y1 ;
gprops . radiusX = b - > OuterRadius ;
gprops . radiusY = b - > OuterRadius ;
stops = mkstops ( b , rt ) ;
2016-04-23 11:47:15 -05:00
hr = rt - > CreateRadialGradientBrush (
2015-10-07 17:32:55 -05:00
& gprops ,
props ,
stops ,
& brush ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating gradient brush " , hr ) ;
2015-10-07 17:32:55 -05:00
2016-04-23 11:47:15 -05:00
stops - > Release ( ) ;
return brush ;
2015-10-07 09:47:38 -05:00
}
static ID2D1Brush * makeBrush ( uiDrawBrush * b , ID2D1RenderTarget * rt )
{
D2D1_BRUSH_PROPERTIES props ;
2015-09-16 22:15:42 -05:00
2015-09-17 12:10:38 -05:00
ZeroMemory ( & props , sizeof ( D2D1_BRUSH_PROPERTIES ) ) ;
props . opacity = 1.0 ;
// identity matrix
props . transform . _11 = 1 ;
props . transform . _22 = 1 ;
2015-09-16 22:15:42 -05:00
2015-10-07 09:47:38 -05:00
switch ( b - > Type ) {
case uiDrawBrushTypeSolid :
return makeSolidBrush ( b , rt , & props ) ;
2015-10-07 15:54:56 -05:00
case uiDrawBrushTypeLinearGradient :
return makeLinearBrush ( b , rt , & props ) ;
2015-10-07 17:32:55 -05:00
case uiDrawBrushTypeRadialGradient :
return makeRadialBrush ( b , rt , & props ) ;
2015-10-07 09:47:38 -05:00
// case uiDrawBrushTypeImage:
// TODO
}
2015-10-09 10:49:57 -05:00
complain ( " invalid brush type %d in makeBrush() " , b - > Type ) ;
2015-10-07 09:47:38 -05:00
return NULL ; // make compiler happy
2015-09-16 22:15:42 -05:00
}
2015-10-13 12:20:25 -05:00
// how clipping works:
// every fill and stroke is done on a temporary layer with the clip geometry applied to it
// this is really the only way to clip in Direct2D that doesn't involve opacity images
// reference counting:
// - initially the clip is NULL, which means do not use a layer
// - the first time uiDrawClip() is called, we take a reference on the path passed in (this is also why uiPathEnd() is needed)
// - every successive time, we create a new PathGeometry and merge the current clip with the new path, releasing the reference we took earlier and taking a reference to the new one
// - in Save, we take another reference; in Restore we drop the refernece to the existing path geometry and transfer that saved ref to the new path geometry over to the context
// uiDrawFreePath() doesn't destroy the path geometry, it just drops the reference count, so a clip can exist independent of its path
static ID2D1Layer * applyClip ( uiDrawContext * c )
{
ID2D1Layer * layer ;
D2D1_LAYER_PARAMETERS params ;
HRESULT hr ;
// if no clip, don't do anything
if ( c - > currentClip = = NULL )
return NULL ;
// create a layer for clipping
// we have to explicitly make the layer because we're still targeting Windows 7
2016-04-23 11:47:15 -05:00
hr = c - > rt - > CreateLayer ( NULL , & layer ) ;
2015-10-13 12:20:25 -05:00
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating clip layer " , hr ) ;
2015-10-13 12:20:25 -05:00
// apply it as the clip
ZeroMemory ( & params , sizeof ( D2D1_LAYER_PARAMETERS ) ) ;
// this is the equivalent of InfiniteRect() in d2d1helper.h
params . contentBounds . left = - FLT_MAX ;
params . contentBounds . top = - FLT_MAX ;
params . contentBounds . right = FLT_MAX ;
params . contentBounds . bottom = FLT_MAX ;
params . geometricMask = ( ID2D1Geometry * ) ( c - > currentClip ) ;
// TODO is this correct?
2016-04-23 21:15:33 -05:00
params . maskAntialiasMode = c - > rt - > GetAntialiasMode ( ) ;
2015-10-13 12:20:25 -05:00
// identity matrix
params . maskTransform . _11 = 1 ;
params . maskTransform . _22 = 1 ;
params . opacity = 1.0 ;
params . opacityBrush = NULL ;
params . layerOptions = D2D1_LAYER_OPTIONS_NONE ;
// TODO is this correct?
2016-04-23 11:47:15 -05:00
if ( c - > rt - > GetTextAntialiasMode ( ) = = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE )
2015-10-13 12:20:25 -05:00
params . layerOptions = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE ;
2016-04-23 11:47:15 -05:00
c - > rt - > PushLayer ( & params , layer ) ;
2015-10-13 12:20:25 -05:00
// return the layer so it can be freed later
return layer ;
}
static void unapplyClip ( uiDrawContext * c , ID2D1Layer * layer )
{
if ( layer = = NULL )
return ;
2016-04-23 11:47:15 -05:00
c - > rt - > PopLayer ( ) ;
layer - > Release ( ) ;
2015-10-13 12:20:25 -05:00
}
2015-10-07 09:47:38 -05:00
void uiDrawStroke ( uiDrawContext * c , uiDrawPath * p , uiDrawBrush * b , uiDrawStrokeParams * sp )
2015-09-16 22:15:42 -05:00
{
2015-10-07 09:47:38 -05:00
ID2D1Brush * brush ;
2015-10-07 12:05:48 -05:00
ID2D1StrokeStyle * style ;
D2D1_STROKE_STYLE_PROPERTIES dsp ;
2015-10-16 09:46:26 -05:00
FLOAT * dashes ;
size_t i ;
2015-10-13 12:20:25 -05:00
ID2D1Layer * cliplayer ;
2015-10-07 12:05:48 -05:00
HRESULT hr ;
2015-09-16 22:15:42 -05:00
2015-10-07 09:47:38 -05:00
brush = makeBrush ( b , c - > rt ) ;
2015-10-07 12:05:48 -05:00
ZeroMemory ( & dsp , sizeof ( D2D1_STROKE_STYLE_PROPERTIES ) ) ;
switch ( sp - > Cap ) {
case uiDrawLineCapFlat :
dsp . startCap = D2D1_CAP_STYLE_FLAT ;
dsp . endCap = D2D1_CAP_STYLE_FLAT ;
dsp . dashCap = D2D1_CAP_STYLE_FLAT ;
break ;
case uiDrawLineCapRound :
dsp . startCap = D2D1_CAP_STYLE_ROUND ;
dsp . endCap = D2D1_CAP_STYLE_ROUND ;
dsp . dashCap = D2D1_CAP_STYLE_ROUND ;
break ;
case uiDrawLineCapSquare :
dsp . startCap = D2D1_CAP_STYLE_SQUARE ;
dsp . endCap = D2D1_CAP_STYLE_SQUARE ;
dsp . dashCap = D2D1_CAP_STYLE_SQUARE ;
break ;
}
switch ( sp - > Join ) {
case uiDrawLineJoinMiter :
dsp . lineJoin = D2D1_LINE_JOIN_MITER_OR_BEVEL ;
dsp . miterLimit = sp - > MiterLimit ;
break ;
case uiDrawLineJoinRound :
dsp . lineJoin = D2D1_LINE_JOIN_ROUND ;
break ;
case uiDrawLineJoinBevel :
dsp . lineJoin = D2D1_LINE_JOIN_BEVEL ;
break ;
}
dsp . dashStyle = D2D1_DASH_STYLE_SOLID ;
2015-10-16 09:46:26 -05:00
dashes = NULL ;
// note that dash widths and the dash phase are scaled up by the thickness by Direct2D
// TODO be sure to formally document this
if ( sp - > NumDashes ! = 0 ) {
dsp . dashStyle = D2D1_DASH_STYLE_CUSTOM ;
dashes = ( FLOAT * ) uiAlloc ( sp - > NumDashes * sizeof ( FLOAT ) , " FLOAT[] " ) ;
for ( i = 0 ; i < sp - > NumDashes ; i + + )
dashes [ i ] = sp - > Dashes [ i ] / sp - > Thickness ;
}
dsp . dashOffset = sp - > DashPhase / sp - > Thickness ;
2016-04-23 11:47:15 -05:00
hr = d2dfactory - > CreateStrokeStyle (
2015-10-07 12:05:48 -05:00
& dsp ,
2015-10-16 09:46:26 -05:00
dashes ,
sp - > NumDashes ,
2015-10-07 12:05:48 -05:00
& style ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating stroke style " , hr ) ;
2015-10-16 10:05:08 -05:00
if ( sp - > NumDashes ! = 0 )
2015-10-16 09:46:26 -05:00
uiFree ( dashes ) ;
2015-10-07 12:05:48 -05:00
2015-10-13 12:20:25 -05:00
cliplayer = applyClip ( c ) ;
2016-04-23 11:47:15 -05:00
c - > rt - > DrawGeometry (
2016-05-05 19:23:52 -05:00
pathGeometry ( p ) ,
2015-10-07 09:47:38 -05:00
brush ,
sp - > Thickness ,
2015-10-07 12:05:48 -05:00
style ) ;
2015-10-13 12:20:25 -05:00
unapplyClip ( c , cliplayer ) ;
2015-10-07 12:05:48 -05:00
2016-04-23 11:47:15 -05:00
style - > Release ( ) ;
brush - > Release ( ) ;
2015-09-16 22:15:42 -05:00
}
2015-10-07 09:47:38 -05:00
void uiDrawFill ( uiDrawContext * c , uiDrawPath * p , uiDrawBrush * b )
2015-09-16 22:15:42 -05:00
{
2015-10-07 09:47:38 -05:00
ID2D1Brush * brush ;
2015-10-13 12:20:25 -05:00
ID2D1Layer * cliplayer ;
2015-09-17 12:10:38 -05:00
2015-10-07 09:47:38 -05:00
brush = makeBrush ( b , c - > rt ) ;
2015-10-13 12:20:25 -05:00
cliplayer = applyClip ( c ) ;
2016-04-23 11:47:15 -05:00
c - > rt - > FillGeometry (
2016-05-05 19:23:52 -05:00
pathGeometry ( p ) ,
2015-10-07 09:47:38 -05:00
brush ,
2015-09-17 12:10:38 -05:00
NULL ) ;
2015-10-13 12:20:25 -05:00
unapplyClip ( c , cliplayer ) ;
2016-04-23 11:47:15 -05:00
brush - > Release ( ) ;
2015-09-16 22:15:42 -05:00
}
2015-10-11 11:36:48 -05:00
2015-10-13 12:20:25 -05:00
void uiDrawClip ( uiDrawContext * c , uiDrawPath * path )
{
ID2D1PathGeometry * newPath ;
ID2D1GeometrySink * newSink ;
HRESULT hr ;
// if there's no current clip, borrow the path
if ( c - > currentClip = = NULL ) {
2016-05-05 19:23:52 -05:00
c - > currentClip = pathGeometry ( path ) ;
2015-10-13 12:20:25 -05:00
// we have to take our own reference to that clip
2016-04-23 11:47:15 -05:00
c - > currentClip - > AddRef ( ) ;
2015-10-13 12:20:25 -05:00
return ;
}
// otherwise we have to intersect the current path with the new one
// we do that into a new path, and then replace c->currentClip with that new path
2016-04-23 21:15:33 -05:00
hr = d2dfactory - > CreatePathGeometry ( & newPath ) ;
2015-10-13 12:20:25 -05:00
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating new path " , hr ) ;
hr = newPath - > Open ( & newSink ) ;
2015-10-13 12:20:25 -05:00
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error opening new path " , hr ) ;
hr = c - > currentClip - > CombineWithGeometry (
2016-05-05 19:23:52 -05:00
pathGeometry ( path ) ,
2015-10-13 12:20:25 -05:00
D2D1_COMBINE_MODE_INTERSECT ,
NULL ,
// TODO is this correct or can this be set per target?
D2D1_DEFAULT_FLATTENING_TOLERANCE ,
2016-04-23 11:47:15 -05:00
newSink ) ;
2015-10-13 12:20:25 -05:00
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error intersecting old path with new path " , hr ) ;
hr = newSink - > Close ( ) ;
2015-10-13 12:20:25 -05:00
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error closing new path " , hr ) ;
newSink - > Release ( ) ;
2015-10-13 12:20:25 -05:00
// okay we have the new clip; we just need to replace the old one with it
2016-04-23 11:47:15 -05:00
c - > currentClip - > Release ( ) ;
2015-10-13 12:20:25 -05:00
c - > currentClip = newPath ;
// we have a reference already; no need for another
}
2015-10-13 09:47:54 -05:00
struct state {
ID2D1DrawingStateBlock * dsb ;
2015-10-13 12:20:25 -05:00
ID2D1PathGeometry * clip ;
2015-10-13 09:47:54 -05:00
} ;
2015-10-11 11:36:48 -05:00
void uiDrawSave ( uiDrawContext * c )
{
2015-11-27 19:38:21 -06:00
struct state * state ;
2015-10-12 09:30:36 -05:00
ID2D1DrawingStateBlock * dsb ;
HRESULT hr ;
2016-04-23 11:47:15 -05:00
hr = d2dfactory - > CreateDrawingStateBlock (
2015-10-12 09:30:36 -05:00
// TODO verify that these are correct
NULL ,
NULL ,
& dsb ) ;
if ( hr ! = S_OK )
2016-04-23 11:47:15 -05:00
logHRESULT ( L " error creating drawing state block " , hr ) ;
c - > rt - > SaveDrawingState ( dsb ) ;
2015-10-13 09:47:54 -05:00
2015-10-13 12:20:25 -05:00
// if we have a clip, we need to hold another reference to it
if ( c - > currentClip ! = NULL )
2016-04-23 11:47:15 -05:00
c - > currentClip - > AddRef ( ) ;
2015-10-13 12:20:25 -05:00
2015-11-27 19:38:21 -06:00
state = uiNew ( struct state ) ;
state - > dsb = dsb ;
state - > clip = c - > currentClip ;
ptrArrayAppend ( c - > states , state ) ;
2015-10-11 11:36:48 -05:00
}
void uiDrawRestore ( uiDrawContext * c )
{
2015-10-13 09:47:54 -05:00
struct state * state ;
2015-11-27 19:38:21 -06:00
state = ptrArrayIndex ( c - > states , struct state * , c - > states - > len - 1 ) ;
2015-10-13 09:47:54 -05:00
2016-04-23 11:47:15 -05:00
c - > rt - > RestoreDrawingState ( state - > dsb ) ;
state - > dsb - > Release ( ) ;
2015-10-12 09:30:36 -05:00
2015-10-13 12:20:25 -05:00
// if we have a current clip, we need to drop it
if ( c - > currentClip ! = NULL )
2016-04-23 11:47:15 -05:00
c - > currentClip - > Release ( ) ;
2015-10-13 12:20:25 -05:00
// no need to explicitly addref or release; just transfer the ref
c - > currentClip = state - > clip ;
2015-11-27 19:38:21 -06:00
uiFree ( state ) ;
ptrArrayDelete ( c - > states , c - > states - > len - 1 ) ;
2015-10-11 11:36:48 -05:00
}
2016-01-09 00:07:48 -06:00
2016-04-23 11:47:15 -05:00
// TODO C++-ize the rest of the file
2016-01-09 00:07:48 -06:00
// TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms
2016-01-09 01:23:01 -06:00
void uiDrawText ( uiDrawContext * c , double x , double y , uiDrawTextLayout * layout )
2016-01-09 00:07:48 -06:00
{
uiDrawBrush brush ;
ID2D1Brush * black ;
ZeroMemory ( & brush , sizeof ( uiDrawBrush ) ) ;
brush . Type = uiDrawBrushTypeSolid ;
brush . R = 0.0 ;
brush . G = 0.0 ;
brush . B = 0.0 ;
brush . A = 1.0 ;
black = makeBrush ( & brush , c - > rt ) ;
doDrawText ( c - > rt , black , x , y , layout ) ;
2016-04-23 11:47:15 -05:00
black - > Release ( ) ;
2016-01-09 00:07:48 -06:00
}
2016-04-19 17:45:16 -05:00
// TODO this is a mess
ID2D1Brush * createSolidColorBrushInternal ( ID2D1RenderTarget * rt , double r , double g , double b , double a )
{
uiDrawBrush brush ;
ZeroMemory ( & brush , sizeof ( uiDrawBrush ) ) ;
brush . Type = uiDrawBrushTypeSolid ;
brush . R = r ;
brush . G = g ;
brush . B = b ;
brush . A = a ;
return makeBrush ( & brush , rt ) ;
}