diff options
Diffstat (limited to 'win32/PlatWin.cxx')
-rw-r--r-- | win32/PlatWin.cxx | 1214 |
1 files changed, 1030 insertions, 184 deletions
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 0ccc8aa13..00e0bf576 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -26,6 +26,8 @@ #include <commctrl.h> #include <richedit.h> #include <windowsx.h> +#include <d2d1.h> +#include <dwrite.h> #include "Platform.h" #include "UniConversion.h" @@ -195,6 +197,88 @@ void Palette::Allocate(Window &) { } } +IDWriteFactory *pIDWriteFactory = 0; +ID2D1Factory *pD2DFactory = 0; + +bool LoadD2D() { + static bool triedLoadingD2D = false; + static HMODULE hDLLD2D = 0; + static HMODULE hDLLDWrite = 0; + if (!triedLoadingD2D) { + typedef HRESULT (WINAPI *D2D1CFSig)(D2D1_FACTORY_TYPE factoryType, REFIID riid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, IUnknown **factory); + typedef HRESULT (WINAPI *DWriteCFSig)(DWRITE_FACTORY_TYPE factoryType, REFIID iid, + IUnknown **factory); + + hDLLD2D = ::LoadLibrary(TEXT("D2D1.DLL")); + if (hDLLD2D) { + D2D1CFSig fnD2DCF = (D2D1CFSig)::GetProcAddress(hDLLD2D, "D2D1CreateFactory"); + if (fnD2DCF) { + // A single threaded factory as Scintilla always draw on the GUI thread + fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + 0, + reinterpret_cast<IUnknown**>(&pD2DFactory)); + } + } + hDLLDWrite = ::LoadLibrary(TEXT("DWRITE.DLL")); + if (hDLLDWrite) { + DWriteCFSig fnDWCF = (DWriteCFSig)::GetProcAddress(hDLLDWrite, "DWriteCreateFactory"); + if (fnDWCF) { + fnDWCF(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(&pIDWriteFactory)); + } + } + } + triedLoadingD2D = true; + return pIDWriteFactory && pD2DFactory; +} + +struct FormatAndMetrics { + int technology; + HFONT hfont; + IDWriteTextFormat *pTextFormat; + int extraFontFlag; + FLOAT yAscent; + FLOAT yDescent; + FormatAndMetrics(HFONT hfont_, int extraFontFlag_) : + technology(SCWIN_TECH_GDI), hfont(hfont_), pTextFormat(0), extraFontFlag(extraFontFlag_), yAscent(2), yDescent(1) { + } + FormatAndMetrics(IDWriteTextFormat *pTextFormat_, int extraFontFlag_, FLOAT yAscent_, FLOAT yDescent_) : + technology(SCWIN_TECH_DIRECTWRITE), hfont(0), pTextFormat(pTextFormat_), extraFontFlag(extraFontFlag_), yAscent(yAscent_), yDescent(yDescent_) { + } + ~FormatAndMetrics() { + if (hfont) + ::DeleteObject(hfont); + if (pTextFormat) + pTextFormat->Release(); + pTextFormat = 0; + extraFontFlag = 0; + yAscent = 2; + yDescent = 1; + } + HFONT HFont(); +}; + +HFONT FormatAndMetrics::HFont() { + if (technology == SCWIN_TECH_GDI) { + return hfont; + } else { + LOGFONTW lf; + memset(&lf, 0, sizeof(lf)); + + HRESULT hr = pTextFormat->GetFontFamilyName(lf.lfFaceName, LF_FACESIZE); + if (SUCCEEDED(hr)) { + lf.lfWeight = pTextFormat->GetFontWeight(); + lf.lfItalic = pTextFormat->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC; + lf.lfHeight = -static_cast<int>(pTextFormat->GetFontSize()); + return ::CreateFontIndirectW(&lf); + } + } + return 0; +} + #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 #endif @@ -216,11 +300,28 @@ static BYTE Win32MapFontQuality(int extraFontFlag) { } } -static void SetLogFont(LOGFONTA &lf, const char *faceName, int characterSet, int size, bool bold, bool italic, int extraFontFlag) { +static D2D1_TEXT_ANTIALIAS_MODE DWriteMapFontQuality(int extraFontFlag) { + switch (extraFontFlag & SC_EFF_QUALITY_MASK) { + + case SC_EFF_QUALITY_NON_ANTIALIASED: + return D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + + case SC_EFF_QUALITY_ANTIALIASED: + return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + + case SC_EFF_QUALITY_LCD_OPTIMIZED: + return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + + default: + return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + } +} + +static void SetLogFont(LOGFONTA &lf, const char *faceName, int characterSet, float size, int weight, bool italic, int extraFontFlag) { memset(&lf, 0, sizeof(lf)); // The negative is to allow for leading - lf.lfHeight = -(abs(size)); - lf.lfWeight = bold ? FW_BOLD : FW_NORMAL; + lf.lfHeight = -(abs(static_cast<int>(size + 0.5))); + lf.lfWeight = weight; lf.lfItalic = static_cast<BYTE>(italic ? 1 : 0); lf.lfCharSet = static_cast<BYTE>(characterSet); lf.lfQuality = Win32MapFontQuality(extraFontFlag); @@ -232,71 +333,111 @@ static void SetLogFont(LOGFONTA &lf, const char *faceName, int characterSet, int * If one font is the same as another, its hash will be the same, but if the hash is the * same then they may still be different. */ -static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic, int extraFontFlag) { +static int HashFont(const FontParameters &fp) { return - size ^ - (characterSet << 10) ^ - ((extraFontFlag & SC_EFF_QUALITY_MASK) << 9) ^ - (bold ? 0x10000000 : 0) ^ - (italic ? 0x20000000 : 0) ^ - faceName[0]; + static_cast<int>(fp.size) ^ + (fp.characterSet << 10) ^ + ((fp.extraFontFlag & SC_EFF_QUALITY_MASK) << 9) ^ + ((fp.weight/100) << 12) ^ + (fp.italic ? 0x20000000 : 0) ^ + (fp.technology << 15) ^ + fp.faceName[0]; } class FontCached : Font { FontCached *next; int usage; + float size; LOGFONTA lf; + int technology; int hash; - FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_); + FontCached(const FontParameters &fp); ~FontCached() {} - bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_); + bool SameAs(const FontParameters &fp); virtual void Release(); static FontCached *first; public: - static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_); + static FontID FindOrCreate(const FontParameters &fp); static void ReleaseId(FontID fid_); }; FontCached *FontCached::first = 0; -FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_) : - next(0), usage(0), hash(0) { - SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_, extraFontFlag_); - hash = HashFont(faceName_, characterSet_, size_, bold_, italic_, extraFontFlag_); - fid = ::CreateFontIndirectA(&lf); +FontCached::FontCached(const FontParameters &fp) : + next(0), usage(0), size(1.0), hash(0) { + SetLogFont(lf, fp.faceName, fp.characterSet, fp.size, fp.weight, fp.italic, fp.extraFontFlag); + technology = fp.technology; + hash = HashFont(fp); + fid = 0; + if (technology == SCWIN_TECH_GDI) { + HFONT hfont = ::CreateFontIndirectA(&lf); + fid = reinterpret_cast<void *>(new FormatAndMetrics(hfont, fp.extraFontFlag)); + } else { + IDWriteTextFormat *pTextFormat; + const int faceSize = 200; + WCHAR wszFace[faceSize]; + UTF16FromUTF8(fp.faceName, strlen(fp.faceName)+1, wszFace, faceSize); + FLOAT fHeight = fp.size; + DWRITE_FONT_STYLE style = fp.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; + HRESULT hr = pIDWriteFactory->CreateTextFormat(wszFace, NULL, + static_cast<DWRITE_FONT_WEIGHT>(fp.weight), + style, + DWRITE_FONT_STRETCH_NORMAL, fHeight, L"en-us", &pTextFormat); + if (SUCCEEDED(hr)) { + pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); + + const int maxLines = 2; + DWRITE_LINE_METRICS lineMetrics[maxLines]; + UINT32 lineCount = 0; + FLOAT yAscent = 1.0f; + FLOAT yDescent = 1.0f; + IDWriteTextLayout *pTextLayout = 0; + hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat, + 100.0f, 100.0f, &pTextLayout); + if (SUCCEEDED(hr)) { + hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount); + if (SUCCEEDED(hr)) { + yAscent = lineMetrics[0].baseline; + yDescent = lineMetrics[0].height - lineMetrics[0].baseline; + } + pTextLayout->Release(); + } + fid = reinterpret_cast<void *>(new FormatAndMetrics(pTextFormat, fp.extraFontFlag, yAscent, yDescent)); + } + } usage = 1; } -bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_) { +bool FontCached::SameAs(const FontParameters &fp) { return - (lf.lfHeight == -(abs(size_))) && - (lf.lfWeight == (bold_ ? FW_BOLD : FW_NORMAL)) && - (lf.lfItalic == static_cast<BYTE>(italic_ ? 1 : 0)) && - (lf.lfCharSet == characterSet_) && - (lf.lfQuality == Win32MapFontQuality(extraFontFlag_)) && - 0 == strcmp(lf.lfFaceName,faceName_); + (size == fp.size) && + (lf.lfWeight == fp.weight) && + (lf.lfItalic == static_cast<BYTE>(fp.italic ? 1 : 0)) && + (lf.lfCharSet == fp.characterSet) && + (lf.lfQuality == Win32MapFontQuality(fp.extraFontFlag)) && + (technology == fp.technology) && + 0 == strcmp(lf.lfFaceName,fp.faceName); } void FontCached::Release() { - if (fid) - ::DeleteObject(fid); + delete reinterpret_cast<FormatAndMetrics *>(fid); fid = 0; } -FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_) { +FontID FontCached::FindOrCreate(const FontParameters &fp) { FontID ret = 0; ::EnterCriticalSection(&crPlatformLock); - int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_, extraFontFlag_); + int hashFind = HashFont(fp); for (FontCached *cur=first; cur; cur=cur->next) { if ((cur->hash == hashFind) && - cur->SameAs(faceName_, characterSet_, size_, bold_, italic_, extraFontFlag_)) { + cur->SameAs(fp)) { cur->usage++; ret = cur->fid; } } if (ret == 0) { - FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_, extraFontFlag_); + FontCached *fc = new FontCached(fp); if (fc) { fc->next = first; first = fc; @@ -335,35 +476,62 @@ Font::~Font() { #define FONTS_CACHED -void Font::Create(const char *faceName, int characterSet, int size, - bool bold, bool italic, int extraFontFlag) { +void Font::Create(const FontParameters &fp) { Release(); -#ifndef FONTS_CACHED - LOGFONT lf; - SetLogFont(lf, faceName, characterSet, size, bold, italic, extraFontFlag); - fid = ::CreateFontIndirect(&lf); -#else - if (faceName) - fid = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic, extraFontFlag); -#endif + if (fp.faceName) + fid = FontCached::FindOrCreate(fp); } void Font::Release() { -#ifndef FONTS_CACHED - if (fid) - ::DeleteObject(fid); -#else if (fid) FontCached::ReleaseId(fid); -#endif fid = 0; } +// Buffer to hold strings and string position arrays without always allocating on heap. +// May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer +// when less than safe size otherwise allocate on heap and free automatically. +template<typename T, int lengthStandard> +class VarBuffer { + T bufferStandard[lengthStandard]; +public: + T *buffer; + VarBuffer(size_t length) : buffer(0) { + if (length > lengthStandard) { + buffer = new T[length]; + } else { + buffer = bufferStandard; + } + } + ~VarBuffer() { + if (buffer != bufferStandard) { + delete []buffer; + buffer = 0; + } + } +}; + +const int stackBufferLength = 10000; +class TextWide : public VarBuffer<wchar_t, stackBufferLength> { +public: + int tlen; + TextWide(const char *s, int len, bool unicodeMode, int codePage=0) : + VarBuffer<wchar_t, stackBufferLength>(len) { + if (unicodeMode) { + tlen = UTF16FromUTF8(s, len, buffer, len); + } else { + // Support Asian string display in 9x English + tlen = ::MultiByteToWideChar(codePage, 0, s, len, buffer, len); + } + } +}; +typedef VarBuffer<XYPOSITION, stackBufferLength> TextPositions; + #ifdef SCI_NAMESPACE namespace Scintilla { #endif -class SurfaceImpl : public Surface { +class SurfaceGDI : public Surface { bool unicodeMode; HDC hdc; bool hdcOwned; @@ -386,12 +554,12 @@ class SurfaceImpl : public Surface { void BrushColor(ColourAllocated back); void SetFont(Font &font_); - // Private so SurfaceImpl objects can not be copied - SurfaceImpl(const SurfaceImpl &); - SurfaceImpl &operator=(const SurfaceImpl &); + // Private so SurfaceGDI objects can not be copied + SurfaceGDI(const SurfaceGDI &); + SurfaceGDI &operator=(const SurfaceGDI &); public: - SurfaceImpl(); - virtual ~SurfaceImpl(); + SurfaceGDI(); + virtual ~SurfaceGDI(); void Init(WindowID wid); void Init(SurfaceID sid, WindowID wid); @@ -415,19 +583,19 @@ public: void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back); void Copy(PRectangle rc, Point from, Surface &surfaceSource); - void DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions); - void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); - void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); - void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore); - void MeasureWidths(Font &font_, const char *s, int len, int *positions); - int WidthText(Font &font_, const char *s, int len); - int WidthChar(Font &font_, char ch); - int Ascent(Font &font_); - int Descent(Font &font_); - int InternalLeading(Font &font_); - int ExternalLeading(Font &font_); - int Height(Font &font_); - int AverageCharWidth(Font &font_); + void DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, UINT fuOptions); + void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); + void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); + void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore); + void MeasureWidths(Font &font_, const char *s, int len, XYPOSITION *positions); + XYPOSITION WidthText(Font &font_, const char *s, int len); + XYPOSITION WidthChar(Font &font_, char ch); + XYPOSITION Ascent(Font &font_); + XYPOSITION Descent(Font &font_); + XYPOSITION InternalLeading(Font &font_); + XYPOSITION ExternalLeading(Font &font_); + XYPOSITION Height(Font &font_); + XYPOSITION AverageCharWidth(Font &font_); int SetPalette(Palette *pal, bool inBackGround); void SetClip(PRectangle rc); @@ -441,7 +609,7 @@ public: } //namespace Scintilla #endif -SurfaceImpl::SurfaceImpl() : +SurfaceGDI::SurfaceGDI() : unicodeMode(false), hdc(0), hdcOwned(false), pen(0), penOld(0), @@ -459,11 +627,11 @@ SurfaceImpl::SurfaceImpl() : win9xACPSame = false; } -SurfaceImpl::~SurfaceImpl() { +SurfaceGDI::~SurfaceGDI() { Release(); } -void SurfaceImpl::Release() { +void SurfaceGDI::Release() { if (penOld) { ::SelectObject(reinterpret_cast<HDC>(hdc), penOld); ::DeleteObject(pen); @@ -501,33 +669,33 @@ void SurfaceImpl::Release() { } } -bool SurfaceImpl::Initialised() { +bool SurfaceGDI::Initialised() { return hdc != 0; } -void SurfaceImpl::Init(WindowID) { +void SurfaceGDI::Init(WindowID) { Release(); hdc = ::CreateCompatibleDC(NULL); hdcOwned = true; ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE); } -void SurfaceImpl::Init(SurfaceID sid, WindowID) { +void SurfaceGDI::Init(SurfaceID sid, WindowID) { Release(); hdc = reinterpret_cast<HDC>(sid); ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE); } -void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID) { +void SurfaceGDI::InitPixMap(int width, int height, Surface *surface_, WindowID) { Release(); - hdc = ::CreateCompatibleDC(static_cast<SurfaceImpl *>(surface_)->hdc); + hdc = ::CreateCompatibleDC(static_cast<SurfaceGDI *>(surface_)->hdc); hdcOwned = true; - bitmap = ::CreateCompatibleBitmap(static_cast<SurfaceImpl *>(surface_)->hdc, width, height); + bitmap = ::CreateCompatibleBitmap(static_cast<SurfaceGDI *>(surface_)->hdc, width, height); bitmapOld = static_cast<HBITMAP>(::SelectObject(hdc, bitmap)); ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE); } -void SurfaceImpl::PenColour(ColourAllocated fore) { +void SurfaceGDI::PenColour(ColourAllocated fore) { if (pen) { ::SelectObject(hdc, penOld); ::DeleteObject(pen); @@ -538,7 +706,7 @@ void SurfaceImpl::PenColour(ColourAllocated fore) { penOld = static_cast<HPEN>(::SelectObject(reinterpret_cast<HDC>(hdc), pen)); } -void SurfaceImpl::BrushColor(ColourAllocated back) { +void SurfaceGDI::BrushColor(ColourAllocated back) { if (brush) { ::SelectObject(hdc, brushOld); ::DeleteObject(brush); @@ -551,46 +719,48 @@ void SurfaceImpl::BrushColor(ColourAllocated back) { brushOld = static_cast<HBRUSH>(::SelectObject(hdc, brush)); } -void SurfaceImpl::SetFont(Font &font_) { +void SurfaceGDI::SetFont(Font &font_) { if (font_.GetID() != font) { + FormatAndMetrics *pfm = reinterpret_cast<FormatAndMetrics *>(font_.GetID()); + PLATFORM_ASSERT(pfm->technology == SCWIN_TECH_GDI); if (fontOld) { - ::SelectObject(hdc, font_.GetID()); + ::SelectObject(hdc, pfm->hfont); } else { - fontOld = static_cast<HFONT>(::SelectObject(hdc, font_.GetID())); + fontOld = static_cast<HFONT>(::SelectObject(hdc, pfm->hfont)); } - font = reinterpret_cast<HFONT>(font_.GetID()); + font = reinterpret_cast<HFONT>(pfm->hfont); } } -int SurfaceImpl::LogPixelsY() { +int SurfaceGDI::LogPixelsY() { return ::GetDeviceCaps(hdc, LOGPIXELSY); } -int SurfaceImpl::DeviceHeightFont(int points) { +int SurfaceGDI::DeviceHeightFont(int points) { return ::MulDiv(points, LogPixelsY(), 72); } -void SurfaceImpl::MoveTo(int x_, int y_) { +void SurfaceGDI::MoveTo(int x_, int y_) { ::MoveToEx(hdc, x_, y_, 0); } -void SurfaceImpl::LineTo(int x_, int y_) { +void SurfaceGDI::LineTo(int x_, int y_) { ::LineTo(hdc, x_, y_); } -void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) { +void SurfaceGDI::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) { PenColour(fore); BrushColor(back); ::Polygon(hdc, reinterpret_cast<POINT *>(pts), npts); } -void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) { +void SurfaceGDI::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) { PenColour(fore); BrushColor(back); ::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); } -void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) { +void SurfaceGDI::FillRectangle(PRectangle rc, ColourAllocated back) { // Using ExtTextOut rather than a FillRect ensures that no dithering occurs. // There is no need to allocate a brush either. RECT rcw = RectFromPRectangle(rc); @@ -598,10 +768,10 @@ void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) { ::ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rcw, TEXT(""), 0, NULL); } -void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { +void SurfaceGDI::FillRectangle(PRectangle rc, Surface &surfacePattern) { HBRUSH br; - if (static_cast<SurfaceImpl &>(surfacePattern).bitmap) - br = ::CreatePatternBrush(static_cast<SurfaceImpl &>(surfacePattern).bitmap); + if (static_cast<SurfaceGDI &>(surfacePattern).bitmap) + br = ::CreatePatternBrush(static_cast<SurfaceGDI &>(surfacePattern).bitmap); else // Something is wrong so display in red br = ::CreateSolidBrush(RGB(0xff, 0, 0)); RECT rcw = RectFromPRectangle(rc); @@ -609,7 +779,7 @@ void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { ::DeleteObject(br); } -void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) { +void SurfaceGDI::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) { PenColour(fore); BrushColor(back); ::RoundRect(hdc, @@ -645,7 +815,7 @@ static DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) { return converter.val; } -void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill, +void SurfaceGDI::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill, ColourAllocated outline, int alphaOutline, int /* flags*/ ) { if (AlphaBlendFn && rc.Width() > 0) { HDC hMemDC = ::CreateCompatibleDC(reinterpret_cast<HDC>(hdc)); @@ -704,7 +874,7 @@ void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated } } -void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) { +void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) { if (AlphaBlendFn && rc.Width() > 0) { HDC hMemDC = ::CreateCompatibleDC(reinterpret_cast<HDC>(hdc)); if (rc.Width() > width) @@ -743,58 +913,21 @@ void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsi } } -void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) { +void SurfaceGDI::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) { PenColour(fore); BrushColor(back); ::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); } -void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) { +void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) { ::BitBlt(hdc, rc.left, rc.top, rc.Width(), rc.Height(), - static_cast<SurfaceImpl &>(surfaceSource).hdc, from.x, from.y, SRCCOPY); + static_cast<SurfaceGDI &>(surfaceSource).hdc, from.x, from.y, SRCCOPY); } -// Buffer to hold strings and string position arrays without always allocating on heap. -// May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer -// when less than safe size otherwise allocate on heap and free automatically. -template<typename T, int lengthStandard> -class VarBuffer { - T bufferStandard[lengthStandard]; -public: - T *buffer; - VarBuffer(size_t length) : buffer(0) { - if (length > lengthStandard) { - buffer = new T[length]; - } else { - buffer = bufferStandard; - } - } - ~VarBuffer() { - if (buffer != bufferStandard) { - delete []buffer; - buffer = 0; - } - } -}; +typedef VarBuffer<int, stackBufferLength> TextPositionsI; -const int stackBufferLength = 10000; -class TextWide : public VarBuffer<wchar_t, stackBufferLength> { -public: - int tlen; - TextWide(const char *s, int len, bool unicodeMode, int codePage=0) : - VarBuffer<wchar_t, stackBufferLength>(len) { - if (unicodeMode) { - tlen = UTF16FromUTF8(s, len, buffer, len); - } else { - // Support Asian string display in 9x English - tlen = ::MultiByteToWideChar(codePage, 0, s, len, buffer, len); - } - } -}; -typedef VarBuffer<int, stackBufferLength> TextPositions; - -void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions) { +void SurfaceGDI::DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, UINT fuOptions) { SetFont(font_); RECT rcw = RectFromPRectangle(rc); SIZE sz={0,0}; @@ -838,21 +971,21 @@ void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, int ybase, const ch } } -void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceGDI::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back) { ::SetTextColor(hdc, fore.AsLong()); ::SetBkColor(hdc, back.AsLong()); DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE); } -void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceGDI::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back) { ::SetTextColor(hdc, fore.AsLong()); ::SetBkColor(hdc, back.AsLong()); DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE | ETO_CLIPPED); } -void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceGDI::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore) { // Avoid drawing spaces in transparent mode for (int i=0;i<len;i++) { @@ -866,7 +999,7 @@ void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, con } } -int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { +XYPOSITION SurfaceGDI::WidthText(Font &font_, const char *s, int len) { SetFont(font_); SIZE sz={0,0}; if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) { @@ -878,13 +1011,13 @@ int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { return sz.cx; } -void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) { +void SurfaceGDI::MeasureWidths(Font &font_, const char *s, int len, XYPOSITION *positions) { SetFont(font_); SIZE sz={0,0}; int fit = 0; if (unicodeMode) { const TextWide tbuf(s, len, unicodeMode, codePage); - TextPositions poses(tbuf.tlen); + TextPositionsI poses(tbuf.tlen); fit = tbuf.tlen; if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) { // Likely to have failed because on Windows 9x where function not available @@ -928,19 +1061,19 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi int startOffset = 0; while (len > 0) { int lenBlock = Platform::Minimum(len, maxLenText); - if (!::GetTextExtentExPointA(hdc, s, lenBlock, maxWidthMeasure, &fit, positions, &sz)) { + TextPositionsI poses(len); + if (!::GetTextExtentExPointA(hdc, s, lenBlock, maxWidthMeasure, &fit, poses.buffer, &sz)) { // Eeek - a NULL DC or other foolishness could cause this. return; } else if (fit < lenBlock) { // For some reason, such as an incomplete DBCS character // Not all the positions are filled in so make them equal to end. - for (int i=fit;i<lenBlock;i++) - positions[i] = positions[fit-1]; - } else if (startOffset > 0) { - for (int i=0;i<lenBlock;i++) - positions[i] += startOffset; + for (int i = fit;i<lenBlock;i++) + poses.buffer[i] = poses.buffer[fit-1]; } - startOffset = positions[lenBlock-1]; + for (int i=0;i<lenBlock;i++) + positions[i] = poses.buffer[i] + startOffset; + startOffset = poses.buffer[lenBlock-1]; len -= lenBlock; positions += lenBlock; s += lenBlock; @@ -948,7 +1081,7 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi } else { // Support Asian string display in 9x English const TextWide tbuf(s, len, unicodeMode, codePage); - TextPositions poses(tbuf.tlen); + TextPositionsI poses(tbuf.tlen); for (int widthSS=0; widthSS<tbuf.tlen; widthSS++) { ::GetTextExtentPoint32W(hdc, tbuf.buffer, widthSS+1, &sz); poses.buffer[widthSS] = sz.cx; @@ -970,56 +1103,56 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi } } -int SurfaceImpl::WidthChar(Font &font_, char ch) { +XYPOSITION SurfaceGDI::WidthChar(Font &font_, char ch) { SetFont(font_); SIZE sz; ::GetTextExtentPoint32A(hdc, &ch, 1, &sz); return sz.cx; } -int SurfaceImpl::Ascent(Font &font_) { +XYPOSITION SurfaceGDI::Ascent(Font &font_) { SetFont(font_); TEXTMETRIC tm; ::GetTextMetrics(hdc, &tm); return tm.tmAscent; } -int SurfaceImpl::Descent(Font &font_) { +XYPOSITION SurfaceGDI::Descent(Font &font_) { SetFont(font_); TEXTMETRIC tm; ::GetTextMetrics(hdc, &tm); return tm.tmDescent; } -int SurfaceImpl::InternalLeading(Font &font_) { +XYPOSITION SurfaceGDI::InternalLeading(Font &font_) { SetFont(font_); TEXTMETRIC tm; ::GetTextMetrics(hdc, &tm); return tm.tmInternalLeading; } -int SurfaceImpl::ExternalLeading(Font &font_) { +XYPOSITION SurfaceGDI::ExternalLeading(Font &font_) { SetFont(font_); TEXTMETRIC tm; ::GetTextMetrics(hdc, &tm); return tm.tmExternalLeading; } -int SurfaceImpl::Height(Font &font_) { +XYPOSITION SurfaceGDI::Height(Font &font_) { SetFont(font_); TEXTMETRIC tm; ::GetTextMetrics(hdc, &tm); return tm.tmHeight; } -int SurfaceImpl::AverageCharWidth(Font &font_) { +XYPOSITION SurfaceGDI::AverageCharWidth(Font &font_) { SetFont(font_); TEXTMETRIC tm; ::GetTextMetrics(hdc, &tm); return tm.tmAveCharWidth; } -int SurfaceImpl::SetPalette(Palette *pal, bool inBackGround) { +int SurfaceGDI::SetPalette(Palette *pal, bool inBackGround) { if (paletteOld) { ::SelectPalette(hdc, paletteOld, TRUE); } @@ -1033,28 +1166,705 @@ int SurfaceImpl::SetPalette(Palette *pal, bool inBackGround) { return changes; } -void SurfaceImpl::SetClip(PRectangle rc) { +void SurfaceGDI::SetClip(PRectangle rc) { ::IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); } -void SurfaceImpl::FlushCachedState() { +void SurfaceGDI::FlushCachedState() { pen = 0; brush = 0; font = 0; } -void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) { +void SurfaceGDI::SetUnicodeMode(bool unicodeMode_) { unicodeMode=unicodeMode_; } -void SurfaceImpl::SetDBCSMode(int codePage_) { +void SurfaceGDI::SetDBCSMode(int codePage_) { // No action on window as automatically handled by system. codePage = codePage_; win9xACPSame = !IsNT() && ((unsigned int)codePage == ::GetACP()); } -Surface *Surface::Allocate() { - return new SurfaceImpl; +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class SurfaceD2D : public Surface { + bool unicodeMode; + int x, y; + + int codePage; + + ID2D1RenderTarget *pRenderTarget; + bool ownRenderTarget; + int clipsActive; + + IDWriteTextFormat *pTextFormat; + FLOAT yAscent; + FLOAT yDescent; + + ID2D1SolidColorBrush *pBrush; + + int logPixelsY; + float dpiScaleX; + float dpiScaleY; + + void SetFont(Font &font_); + + // Private so SurfaceD2D objects can not be copied + SurfaceD2D(const SurfaceD2D &); + SurfaceD2D &operator=(const SurfaceD2D &); +public: + SurfaceD2D(); + virtual ~SurfaceD2D(); + + void SetScale(); + void Init(WindowID wid); + void Init(SurfaceID sid, WindowID wid); + void InitPixMap(int width, int height, Surface *surface_, WindowID wid); + + void Release(); + bool Initialised(); + + HRESULT FlushDrawing(); + + void PenColour(ColourAllocated fore); + void D2DPenColour(ColourAllocated fore, int alpha=255); + int LogPixelsY(); + int DeviceHeightFont(int points); + void MoveTo(int x_, int y_); + void LineTo(int x_, int y_); + void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back); + void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back); + void FillRectangle(PRectangle rc, ColourAllocated back); + void FillRectangle(PRectangle rc, Surface &surfacePattern); + void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back); + void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill, + ColourAllocated outline, int alphaOutline, int flags); + void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage); + void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back); + void Copy(PRectangle rc, Point from, Surface &surfaceSource); + + void DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, UINT fuOptions); + void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); + void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); + void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore); + void MeasureWidths(Font &font_, const char *s, int len, XYPOSITION *positions); + XYPOSITION WidthText(Font &font_, const char *s, int len); + XYPOSITION WidthChar(Font &font_, char ch); + XYPOSITION Ascent(Font &font_); + XYPOSITION Descent(Font &font_); + XYPOSITION InternalLeading(Font &font_); + XYPOSITION ExternalLeading(Font &font_); + XYPOSITION Height(Font &font_); + XYPOSITION AverageCharWidth(Font &font_); + + int SetPalette(Palette *pal, bool inBackGround); + void SetClip(PRectangle rc); + void FlushCachedState(); + + void SetUnicodeMode(bool unicodeMode_); + void SetDBCSMode(int codePage_); +}; + +#ifdef SCI_NAMESPACE +} //namespace Scintilla +#endif + +SurfaceD2D::SurfaceD2D() : + unicodeMode(false), + x(0), y(0) { + + codePage = 0; + + pRenderTarget = NULL; + ownRenderTarget = false; + clipsActive = 0; + + // From selected font + pTextFormat = NULL; + yAscent = 2; + yDescent = 1; + + pBrush = NULL; + + logPixelsY = 72; + dpiScaleX = 1.0; + dpiScaleY = 1.0; +} + +SurfaceD2D::~SurfaceD2D() { + Release(); +} + +void SurfaceD2D::Release() { + if (pBrush) { + pBrush->Release(); + pBrush = 0; + } + if (pRenderTarget) { + while (clipsActive) { + pRenderTarget->PopAxisAlignedClip(); + clipsActive--; + } + if (ownRenderTarget) { + pRenderTarget->Release(); + } + pRenderTarget = 0; + } +} + +void SurfaceD2D::SetScale() { + HDC hdcMeasure = ::CreateCompatibleDC(NULL); + logPixelsY = ::GetDeviceCaps(hdcMeasure, LOGPIXELSY); + dpiScaleX = ::GetDeviceCaps(hdcMeasure, LOGPIXELSX) / 96.0f; + dpiScaleY = logPixelsY / 96.0f; + ::DeleteDC(hdcMeasure); +} + +bool SurfaceD2D::Initialised() { + return pRenderTarget != 0; +} + +HRESULT SurfaceD2D::FlushDrawing() { + return pRenderTarget->Flush(); +} + +void SurfaceD2D::Init(WindowID /* wid */) { + Release(); + SetScale(); +} + +void SurfaceD2D::Init(SurfaceID sid, WindowID) { + Release(); + SetScale(); + pRenderTarget = reinterpret_cast<ID2D1HwndRenderTarget *>(sid); +} + +void SurfaceD2D::InitPixMap(int width, int height, Surface *surface_, WindowID) { + Release(); + SetScale(); + SurfaceD2D *psurfOther = static_cast<SurfaceD2D *>(surface_); + ID2D1BitmapRenderTarget *pCompatibleRenderTarget = NULL; + HRESULT hr = psurfOther->pRenderTarget->CreateCompatibleRenderTarget( + D2D1::SizeF(width, height), &pCompatibleRenderTarget); + if (SUCCEEDED(hr)) { + pRenderTarget = pCompatibleRenderTarget; + pRenderTarget->BeginDraw(); + ownRenderTarget = true; + } +} + +void SurfaceD2D::PenColour(ColourAllocated fore) { + D2DPenColour(fore); +} + +void SurfaceD2D::D2DPenColour(ColourAllocated fore, int alpha) { + if (pRenderTarget) { + D2D_COLOR_F col; + col.r = (fore.AsLong() & 0xff) / 255.0; + col.g = ((fore.AsLong() & 0xff00) >> 8) / 255.0; + col.b = (fore.AsLong() >> 16) / 255.0; + col.a = alpha / 255.0; + if (pBrush) { + pBrush->SetColor(col); + } else { + HRESULT hr = pRenderTarget->CreateSolidColorBrush(col, &pBrush); + if (!SUCCEEDED(hr) && pBrush) { + pBrush->Release(); + pBrush = 0; + } + } + } +} + +void SurfaceD2D::SetFont(Font &font_) { + FormatAndMetrics *pfm = reinterpret_cast<FormatAndMetrics *>(font_.GetID()); + PLATFORM_ASSERT(pfm->technology == SCWIN_TECH_DIRECTWRITE); + pTextFormat = pfm->pTextFormat; + yAscent = pfm->yAscent; + yDescent = pfm->yDescent; + if (pRenderTarget) { + pRenderTarget->SetTextAntialiasMode(DWriteMapFontQuality(pfm->extraFontFlag)); + } +} + +int SurfaceD2D::LogPixelsY() { + return logPixelsY; +} + +int SurfaceD2D::DeviceHeightFont(int points) { + return ::MulDiv(points, LogPixelsY(), 72); +} + +void SurfaceD2D::MoveTo(int x_, int y_) { + x = x_; + y = y_; +} + +static int Delta(int difference) { + if (difference < 0) + return -1; + else if (difference > 0) + return 1; + else + return 0; +} + +static int RoundFloat(float f) { + return int(f+0.5); +} + +void SurfaceD2D::LineTo(int x_, int y_) { + if (pRenderTarget) { + int xDiff = x_ - x; + int xDelta = Delta(xDiff); + int yDiff = y_ - y; + int yDelta = Delta(yDiff); + if ((xDiff == 0) || (yDiff == 0)) { + // Horizontal or vertical lines can be more precisely drawn as a filled rectangle + int xEnd = x_ - xDelta; + int left = Platform::Minimum(x, xEnd); + int width = abs(x - xEnd) + 1; + int yEnd = y_ - yDelta; + int top = Platform::Minimum(y, yEnd); + int height = abs(y - yEnd) + 1; + D2D1_RECT_F rectangle1 = D2D1::RectF(left, top, left+width, top+height); + pRenderTarget->FillRectangle(&rectangle1, pBrush); + } else if ((abs(xDiff) == abs(yDiff))) { + // 45 degree slope + pRenderTarget->DrawLine(D2D1::Point2F(x + 0.5, y + 0.5), + D2D1::Point2F(x_ + 0.5 - xDelta, y_ + 0.5 - yDelta), pBrush); + } else { + // Line has a different slope so difficult to avoid last pixel + pRenderTarget->DrawLine(D2D1::Point2F(x + 0.5, y + 0.5), + D2D1::Point2F(x_ + 0.5, y_ + 0.5), pBrush); + } + x = x_; + y = y_; + } +} + +void SurfaceD2D::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) { + if (pRenderTarget) { + ID2D1Factory *pFactory = 0; + pRenderTarget->GetFactory(&pFactory); + ID2D1PathGeometry *geometry=0; + HRESULT hr = pFactory->CreatePathGeometry(&geometry); + if (SUCCEEDED(hr)) { + ID2D1GeometrySink *sink = 0; + hr = geometry->Open(&sink); + if (SUCCEEDED(hr)) { + sink->BeginFigure(D2D1::Point2F(pts[0].x + 0.5f, pts[0].y + 0.5f), D2D1_FIGURE_BEGIN_FILLED); + for (size_t i=1; i<static_cast<size_t>(npts); i++) { + sink->AddLine(D2D1::Point2F(pts[i].x + 0.5f, pts[i].y + 0.5f)); + } + sink->EndFigure(D2D1_FIGURE_END_CLOSED); + sink->Close(); + sink->Release(); + + D2DPenColour(back); + pRenderTarget->FillGeometry(geometry,pBrush); + D2DPenColour(fore); + pRenderTarget->DrawGeometry(geometry,pBrush); + } + + geometry->Release(); + } + } +} + +void SurfaceD2D::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) { + if (pRenderTarget) { + D2DPenColour(back); + D2D1_RECT_F rectangle1 = D2D1::RectF(RoundFloat(rc.left) + 0.5, rc.top+0.5, RoundFloat(rc.right) - 0.5, rc.bottom-0.5); + D2DPenColour(back); + pRenderTarget->FillRectangle(&rectangle1, pBrush); + D2DPenColour(fore); + pRenderTarget->DrawRectangle(&rectangle1, pBrush); + } +} + +void SurfaceD2D::FillRectangle(PRectangle rc, ColourAllocated back) { + if (pRenderTarget) { + D2DPenColour(back); + D2D1_RECT_F rectangle1 = D2D1::RectF(RoundFloat(rc.left), rc.top, RoundFloat(rc.right), rc.bottom); + pRenderTarget->FillRectangle(&rectangle1, pBrush); + } +} + +void SurfaceD2D::FillRectangle(PRectangle rc, Surface &surfacePattern) { + SurfaceD2D &surfOther = static_cast<SurfaceD2D &>(surfacePattern); + surfOther.FlushDrawing(); + ID2D1Bitmap *pBitmap = NULL; + ID2D1BitmapRenderTarget *pCompatibleRenderTarget = reinterpret_cast<ID2D1BitmapRenderTarget *>( + surfOther.pRenderTarget); + HRESULT hr = pCompatibleRenderTarget->GetBitmap(&pBitmap); + if (SUCCEEDED(hr)) { + ID2D1BitmapBrush *pBitmapBrush = NULL; + D2D1_BITMAP_BRUSH_PROPERTIES brushProperties = + D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP, + D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); + // Create the bitmap brush. + hr = pRenderTarget->CreateBitmapBrush(pBitmap, brushProperties, &pBitmapBrush); + pBitmap->Release(); + if (SUCCEEDED(hr)) { + pRenderTarget->FillRectangle( + D2D1::RectF(rc.left, rc.top, rc.right, rc.bottom), + pBitmapBrush); + pBitmapBrush->Release(); + } + } +} + +void SurfaceD2D::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) { + if (pRenderTarget) { + D2D1_ROUNDED_RECT roundedRectFill = D2D1::RoundedRect( + D2D1::RectF(rc.left+1.0, rc.top+1.0, rc.right-1.0, rc.bottom-1.0), + 8, 8); + D2DPenColour(back); + pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush); + + D2D1_ROUNDED_RECT roundedRect = D2D1::RoundedRect( + D2D1::RectF(rc.left + 0.5, rc.top+0.5, rc.right - 0.5, rc.bottom-0.5), + 8, 8); + D2DPenColour(fore); + pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush); + } +} + +void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill, + ColourAllocated outline, int alphaOutline, int /* flags*/ ) { + if (pRenderTarget) { + D2D1_ROUNDED_RECT roundedRectFill = D2D1::RoundedRect( + D2D1::RectF(rc.left+1.0, rc.top+1.0, rc.right-1.0, rc.bottom-1.0), + cornerSize, cornerSize); + D2DPenColour(fill, alphaFill); + pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush); + + D2D1_ROUNDED_RECT roundedRect = D2D1::RoundedRect( + D2D1::RectF(rc.left + 0.5, rc.top+0.5, rc.right - 0.5, rc.bottom-0.5), + cornerSize, cornerSize); + D2DPenColour(outline, alphaOutline); + pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush); + } +} + +void SurfaceD2D::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) { + if (pRenderTarget) { + if (rc.Width() > width) + rc.left += (rc.Width() - width) / 2; + rc.right = rc.left + width; + if (rc.Height() > height) + rc.top += (rc.Height() - height) / 2; + rc.bottom = rc.top + height; + + std::vector<unsigned char> image(height * width * 4); + for (int y=0; y<height; y++) { + for (int x=0; x<width; x++) { + unsigned char *pixel = &image[0] + (y*width+x) * 4; + unsigned char alpha = pixelsImage[3]; + // Input is RGBA, output is BGRA with premultiplied alpha + pixel[2] = (*pixelsImage++) * alpha / 255; + pixel[1] = (*pixelsImage++) * alpha / 255; + pixel[0] = (*pixelsImage++) * alpha / 255; + pixel[3] = *pixelsImage++; + } + } + + ID2D1Bitmap *bitmap = 0; + D2D1_SIZE_U size = D2D1::SizeU(width, height); + D2D1_BITMAP_PROPERTIES props = {{DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED}, 72.0, 72.0}; + HRESULT hr = pRenderTarget->CreateBitmap(size, &image[0], + width * 4, &props, &bitmap); + if (SUCCEEDED(hr)) { + D2D1_RECT_F rcDestination = {rc.left, rc.top, rc.right, rc.bottom}; + pRenderTarget->DrawBitmap(bitmap, rcDestination); + } + bitmap->Release(); + } +} + +void SurfaceD2D::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) { + if (pRenderTarget) { + FLOAT radius = rc.Width() / 2.0f - 1.0f; + D2D1_ELLIPSE ellipse = D2D1::Ellipse( + D2D1::Point2F((rc.left + rc.right) / 2.0f, (rc.top + rc.bottom) / 2.0f), + radius,radius); + + PenColour(back); + pRenderTarget->FillEllipse(ellipse, pBrush); + PenColour(fore); + pRenderTarget->DrawEllipse(ellipse, pBrush); + } +} + +void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) { + SurfaceD2D &surfOther = static_cast<SurfaceD2D &>(surfaceSource); + surfOther.FlushDrawing(); + ID2D1BitmapRenderTarget *pCompatibleRenderTarget = reinterpret_cast<ID2D1BitmapRenderTarget *>( + surfOther.pRenderTarget); + ID2D1Bitmap *pBitmap = NULL; + HRESULT hr = pCompatibleRenderTarget->GetBitmap(&pBitmap); + if (SUCCEEDED(hr)) { + D2D1_RECT_F rcDestination = {rc.left, rc.top, rc.right, rc.bottom}; + D2D1_RECT_F rcSource = {from.x, from.y, from.x + rc.Width(), from.y + rc.Height()}; + pRenderTarget->DrawBitmap(pBitmap, rcDestination, 1.0f, + D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, rcSource); + pRenderTarget->Flush(); + pBitmap->Release(); + } +} + +void SurfaceD2D::DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, UINT) { + SetFont(font_); + RECT rcw = RectFromPRectangle(rc); + + // Use Unicode calls + const TextWide tbuf(s, len, unicodeMode, codePage); + if (pRenderTarget && pTextFormat && pBrush) { + + // Explicitly creating a text layout appears a little faster + IDWriteTextLayout *pTextLayout; + HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, + rc.Width(), rc.Height(), &pTextLayout); + if (SUCCEEDED(hr)) { + D2D1_POINT_2F origin = {rc.left, ybase-yAscent}; + pRenderTarget->DrawTextLayout(origin, pTextLayout, pBrush, D2D1_DRAW_TEXT_OPTIONS_NONE); + } else { + D2D1_RECT_F layoutRect = D2D1::RectF( + static_cast<FLOAT>(rcw.left) / dpiScaleX, + static_cast<FLOAT>(ybase-yAscent) / dpiScaleY, + static_cast<FLOAT>(rcw.right + 1) / dpiScaleX, + static_cast<FLOAT>(rcw.bottom) / dpiScaleY); + pRenderTarget->DrawText(tbuf.buffer, tbuf.tlen, pTextFormat, layoutRect, pBrush, D2D1_DRAW_TEXT_OPTIONS_NONE); + } + } +} + +void SurfaceD2D::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, + ColourAllocated fore, ColourAllocated back) { + if (pRenderTarget) { + FillRectangle(rc, back); + D2DPenColour(fore); + DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE); + } +} + +void SurfaceD2D::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, + ColourAllocated fore, ColourAllocated back) { + if (pRenderTarget) { + FillRectangle(rc, back); + D2DPenColour(fore); + DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE | ETO_CLIPPED); + } +} + +void SurfaceD2D::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, + ColourAllocated fore) { + // Avoid drawing spaces in transparent mode + for (int i=0;i<len;i++) { + if (s[i] != ' ') { + if (pRenderTarget) { + D2DPenColour(fore); + DrawTextCommon(rc, font_, ybase, s, len, 0); + } + return; + } + } +} + +XYPOSITION SurfaceD2D::WidthText(Font &font_, const char *s, int len) { + FLOAT width = 1.0; + SetFont(font_); + const TextWide tbuf(s, len, unicodeMode, codePage); + if (pIDWriteFactory && pTextFormat) { + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout); + if (SUCCEEDED(hr)) { + DWRITE_TEXT_METRICS textMetrics; + pTextLayout->GetMetrics(&textMetrics); + width = textMetrics.widthIncludingTrailingWhitespace; + pTextLayout->Release(); + } + } + return int(width + 0.5); +} + +void SurfaceD2D::MeasureWidths(Font &font_, const char *s, int len, XYPOSITION *positions) { + SetFont(font_); + int fit = 0; + const TextWide tbuf(s, len, unicodeMode, codePage); + TextPositions poses(tbuf.tlen); + fit = tbuf.tlen; + const int clusters = 1000; + DWRITE_CLUSTER_METRICS clusterMetrics[clusters]; + UINT32 count = 0; + if (pIDWriteFactory && pTextFormat) { + SetFont(font_); + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 10000.0, 1000.0, &pTextLayout); + if (!SUCCEEDED(hr)) + return; + // For now, assuming WCHAR == cluster + pTextLayout->GetClusterMetrics(clusterMetrics, clusters, &count); + FLOAT position = 0.0f; + size_t ti=0; + for (size_t ci=0;ci<count;ci++) { + position += clusterMetrics[ci].width; + for (size_t inCluster=0; inCluster<clusterMetrics[ci].length; inCluster++) { + //poses.buffer[ti++] = int(position + 0.5); + poses.buffer[ti++] = position; + } + } + PLATFORM_ASSERT(ti == static_cast<size_t>(tbuf.tlen)); + pTextLayout->Release(); + } + if (unicodeMode) { + // Map the widths given for UTF-16 characters back onto the UTF-8 input string + int ui=0; + const unsigned char *us = reinterpret_cast<const unsigned char *>(s); + int i=0; + while (ui<fit) { + unsigned char uch = us[i]; + unsigned int lenChar = 1; + if (uch >= (0x80 + 0x40 + 0x20 + 0x10)) { + lenChar = 4; + ui++; + } else if (uch >= (0x80 + 0x40 + 0x20)) { + lenChar = 3; + } else if (uch >= (0x80)) { + lenChar = 2; + } + for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) { + positions[i++] = poses.buffer[ui]; + } + ui++; + } + int lastPos = 0; + if (i > 0) + lastPos = positions[i-1]; + while (i<len) { + positions[i++] = lastPos; + } + } else if (codePage == 0) { + + // One character per position + PLATFORM_ASSERT(len == tbuf.tlen); + for (size_t kk=0;kk<static_cast<size_t>(len);kk++) { + positions[kk] = poses.buffer[kk]; + } + + } else { + + // May be more than one byte per position + int ui = 0; + for (int i=0;i<len;) { + if (::IsDBCSLeadByteEx(codePage, s[i])) { + positions[i] = poses.buffer[ui]; + positions[i+1] = poses.buffer[ui]; + i += 2; + } else { + positions[i] = poses.buffer[ui]; + i++; + } + + ui++; + } + } +} + +XYPOSITION SurfaceD2D::WidthChar(Font &font_, char ch) { + FLOAT width = 1.0; + SetFont(font_); + if (pIDWriteFactory && pTextFormat) { + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + const WCHAR wch = ch; + HRESULT hr = pIDWriteFactory->CreateTextLayout(&wch, 1, pTextFormat, 1000.0, 1000.0, &pTextLayout); + if (SUCCEEDED(hr)) { + DWRITE_TEXT_METRICS textMetrics; + pTextLayout->GetMetrics(&textMetrics); + width = textMetrics.widthIncludingTrailingWhitespace; + pTextLayout->Release(); + } + } + return int(width + 0.5); +} + +XYPOSITION SurfaceD2D::Ascent(Font &font_) { + SetFont(font_); + return ceil(yAscent); +} + +XYPOSITION SurfaceD2D::Descent(Font &font_) { + SetFont(font_); + return ceil(yDescent); +} + +XYPOSITION SurfaceD2D::InternalLeading(Font &) { + return 0; +} + +XYPOSITION SurfaceD2D::ExternalLeading(Font &) { + return 1; +} + +XYPOSITION SurfaceD2D::Height(Font &font_) { + return Ascent(font_) + Descent(font_); +} + +XYPOSITION SurfaceD2D::AverageCharWidth(Font &font_) { + FLOAT width = 1.0; + SetFont(font_); + if (pIDWriteFactory && pTextFormat) { + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + const WCHAR wszAllAlpha[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + HRESULT hr = pIDWriteFactory->CreateTextLayout(wszAllAlpha, wcslen(wszAllAlpha), pTextFormat, 1000.0, 1000.0, &pTextLayout); + if (SUCCEEDED(hr)) { + DWRITE_TEXT_METRICS textMetrics; + pTextLayout->GetMetrics(&textMetrics); + width = textMetrics.width / wcslen(wszAllAlpha); + pTextLayout->Release(); + } + } + return int(width + 0.5); +} + +int SurfaceD2D::SetPalette(Palette *, bool) { + return 0; +} + +void SurfaceD2D::SetClip(PRectangle rc) { + if (pRenderTarget) { + D2D1_RECT_F rcClip = {rc.left, rc.top, rc.right, rc.bottom}; + pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED); + clipsActive++; + } +} + +void SurfaceD2D::FlushCachedState() { +} + +void SurfaceD2D::SetUnicodeMode(bool unicodeMode_) { + unicodeMode=unicodeMode_; +} + +void SurfaceD2D::SetDBCSMode(int codePage_) { + // No action on window as automatically handled by system. + codePage = codePage_; +} + +Surface *Surface::Allocate(int technology) { + if (technology == SCWIN_TECH_GDI) + return new SurfaceGDI; + else + return new SurfaceD2D; } Window::~Window() { @@ -1092,8 +1902,7 @@ void Window::SetPositionRelative(PRectangle rc, Window w) { #ifdef MONITOR_DEFAULTTONULL // We're using the stub functionality of MultiMon.h to decay gracefully on machines // (ie, pre Win2000, Win95) that do not support the newer functions. - RECT rcMonitor; - memcpy(&rcMonitor, &rc, sizeof(rcMonitor)); // RECT and Rectangle are the same really. + RECT rcMonitor = {rc.left, rc.top, rc.right, rc.bottom}; MONITORINFO mi = {0}; mi.cbSize = sizeof(mi); @@ -1365,6 +2174,7 @@ ListBox::~ListBox() { class ListBoxX : public ListBox { int lineHeight; FontID fontCopy; + int technology; RGBAImageSet images; LineToItem lti; HWND lb; @@ -1407,7 +2217,7 @@ class ListBoxX : public ListBox { static const Point ImageInset; // Padding around image public: - ListBoxX() : lineHeight(10), fontCopy(0), lb(0), unicodeMode(false), + ListBoxX() : lineHeight(10), fontCopy(0), technology(0), lb(0), unicodeMode(false), desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8), parent(NULL), ctrlID(0), doubleClickAction(NULL), doubleClickActionData(NULL), widestItem(NULL), maxCharWidth(1), resizeHit(0), wheelDelta(0) { @@ -1419,7 +2229,7 @@ public: } } virtual void SetFont(Font &font); - virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_); + virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_, int technology_); virtual void SetAverageCharWidth(int width); virtual void SetVisibleRows(int rows); virtual int GetVisibleRows() const; @@ -1454,12 +2264,13 @@ ListBox *ListBox::Allocate() { return lb; } -void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_) { +void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, int technology_) { parent = &parent_; ctrlID = ctrlID_; location = location_; lineHeight = lineHeight_; unicodeMode = unicodeMode_; + technology = technology_; HWND hwndParent = reinterpret_cast<HWND>(parent->GetID()); HINSTANCE hinstanceParent = GetWindowInstance(hwndParent); // Window created as popup so not clipped within parent client area @@ -1471,17 +2282,19 @@ void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHei hinstanceParent, this); - ::MapWindowPoints(hwndParent, NULL, reinterpret_cast<POINT*>(&location), 1); + POINT locationw = {location.x, location.y}; + ::MapWindowPoints(hwndParent, NULL, &locationw, 1); + location = Point(locationw.x, locationw.y); } void ListBoxX::SetFont(Font &font) { - LOGFONT lf; - if (0 != ::GetObject(font.GetID(), sizeof(lf), &lf)) { + if (font.GetID()) { if (fontCopy) { ::DeleteObject(fontCopy); fontCopy = 0; } - fontCopy = ::CreateFontIndirect(&lf); + FormatAndMetrics *pfm = reinterpret_cast<FormatAndMetrics *>(font.GetID()); + fontCopy = pfm->HFont(); ::SendMessage(lb, WM_SETFONT, reinterpret_cast<WPARAM>(fontCopy), 0); } } @@ -1650,16 +2463,46 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) { // Draw the image, if any RGBAImage *pimage = images.Get(pixId); if (pimage) { - Surface *surfaceItem = Surface::Allocate(); + Surface *surfaceItem = Surface::Allocate(technology); if (surfaceItem) { - surfaceItem->Init(pDrawItem->hDC, pDrawItem->hwndItem); - int left = pDrawItem->rcItem.left + ItemInset.x + ImageInset.x; - PRectangle rcImage(left, pDrawItem->rcItem.top, - left + images.GetWidth(), pDrawItem->rcItem.bottom); - surfaceItem->DrawRGBAImage(rcImage, - pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels()); - delete surfaceItem; - ::SetTextAlign(pDrawItem->hDC, TA_TOP); + if (technology == SCWIN_TECH_GDI) { + surfaceItem->Init(pDrawItem->hDC, pDrawItem->hwndItem); + int left = pDrawItem->rcItem.left + ItemInset.x + ImageInset.x; + PRectangle rcImage(left, pDrawItem->rcItem.top, + left + images.GetWidth(), pDrawItem->rcItem.bottom); + surfaceItem->DrawRGBAImage(rcImage, + pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels()); + delete surfaceItem; + ::SetTextAlign(pDrawItem->hDC, TA_TOP); + } else { + D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_IGNORE), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT + ); + ID2D1DCRenderTarget *pDCRT = 0; + HRESULT hr = pD2DFactory->CreateDCRenderTarget(&props, &pDCRT); + RECT rcWindow; + GetClientRect(pDrawItem->hwndItem, &rcWindow); + hr = pDCRT->BindDC(pDrawItem->hDC, &rcWindow); + if (SUCCEEDED(hr)) { + surfaceItem->Init(pDCRT, pDrawItem->hwndItem); + pDCRT->BeginDraw(); + int left = pDrawItem->rcItem.left + ItemInset.x + ImageInset.x; + PRectangle rcImage(left, pDrawItem->rcItem.top, + left + images.GetWidth(), pDrawItem->rcItem.bottom); + surfaceItem->DrawRGBAImage(rcImage, + pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels()); + delete surfaceItem; + pDCRT->EndDraw(); + pDCRT->Release(); + } + } } } } @@ -1726,7 +2569,9 @@ void ListBoxX::SetList(const char *list, char separator, char typesep) { } void ListBoxX::AdjustWindowRect(PRectangle *rc) const { - ::AdjustWindowRectEx(reinterpret_cast<RECT*>(rc), WS_THICKFRAME, false, WS_EX_WINDOWEDGE); + RECT rcw = {rc->left, rc->top, rc->right, rc->bottom }; + ::AdjustWindowRectEx(&rcw, WS_THICKFRAME, false, WS_EX_WINDOWEDGE); + *rc = PRectangle(rcw.left, rcw.top, rcw.right, rcw.bottom); } int ListBoxX::ItemHeight() const { @@ -1767,8 +2612,9 @@ void ListBoxX::SetRedraw(bool on) { void ListBoxX::ResizeToCursor() { PRectangle rc = GetPosition(); - Point pt; - ::GetCursorPos(reinterpret_cast<POINT*>(&pt)); + POINT ptw; + ::GetCursorPos(&ptw); + Point pt(ptw.x, ptw.y); pt.x += dragOffset.x; pt.y += dragOffset.y; |