diff options
-rw-r--r-- | cocoa/PlatCocoa.h | 24 | ||||
-rw-r--r-- | cocoa/PlatCocoa.mm | 40 | ||||
-rw-r--r-- | cocoa/ScintillaTest/AppController.mm | 2 | ||||
-rw-r--r-- | include/Platform.h | 51 | ||||
-rw-r--r-- | src/Editor.cxx | 10 | ||||
-rw-r--r-- | src/PositionCache.cxx | 14 | ||||
-rw-r--r-- | src/PositionCache.h | 10 | ||||
-rw-r--r-- | win32/PlatWin.cxx | 954 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 118 |
9 files changed, 747 insertions, 476 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index 0e3c5f2e2..7f6a59705 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -96,20 +96,20 @@ public: void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage); void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back); void Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource); - void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, + void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION 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, + void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION 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 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(Scintilla::Palette *pal, bool inBackGround); void SetClip(PRectangle rc); diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index 75803e33b..1972fa7a9 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -156,7 +156,7 @@ static int FontCharacterSet(Font &f) { /** * Creates a CTFontRef with the given properties. */ -void Font::Create(const char *faceName, int characterSet, int size, bool bold, bool italic, +void Font::Create(const char *faceName, int characterSet, float size, bool bold, bool italic, int /* extraFontFlag */) { Release(); @@ -507,6 +507,9 @@ void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) if (gc) { FillColour(back); + // Snap rectangle boundaries to nearest int + rc.left = lround(rc.left); + rc.right = lround(rc.right); CGRect rect = PRectangleToCGRect(rc); CGContextFillRect(gc, rect); } @@ -641,7 +644,10 @@ void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int /*cornerSize*/, C { if ( gc ) { ColourDesired colour( fill.AsLong() ); - + + // Snap rectangle boundaries to nearest int + rc.left = lround(rc.left); + rc.right = lround(rc.right); // Set the Fill color to match CGContextSetRGBFillColor( gc, colour.GetRed() / 255.0, colour.GetGreen() / 255.0, colour.GetBlue() / 255.0, alphaFill / 255.0 ); CGRect rect = PRectangleToCGRect( rc ); @@ -836,7 +842,7 @@ void SurfaceImpl::Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSou //-------------------------------------------------------------------------------------------------- -void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back) { FillRectangle(rc, back); @@ -845,7 +851,7 @@ void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const ch //-------------------------------------------------------------------------------------------------- -void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back) { CGContextSaveGState(gc); @@ -913,7 +919,7 @@ CFStringEncoding EncodingFromCharacterSet(bool unicode, int characterSet) } } -void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore) { CFStringEncoding encoding = EncodingFromCharacterSet(unicodeMode, FontCharacterSet(font_)); @@ -943,7 +949,7 @@ static size_t utf8LengthFromLead(unsigned char uch) { //-------------------------------------------------------------------------------------------------- -void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) +void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, XYPOSITION *positions) { CFStringEncoding encoding = EncodingFromCharacterSet(unicodeMode, FontCharacterSet(font_)); textLayout->setText (reinterpret_cast<const UInt8*>(s), len, encoding, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); @@ -962,7 +968,7 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi size_t codeUnits = (lenChar < 4) ? 1 : 2; CGFloat xPosition = CTLineGetOffsetForStringIndex(mLine, ui+1, NULL); for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) { - positions[i++] = static_cast<int>(lround(xPosition)); + positions[i++] = xPosition; } ui += codeUnits; } @@ -978,20 +984,20 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi size_t lenChar = Platform::IsDBCSLeadByte(codePage, s[i]) ? 2 : 1; CGFloat xPosition = CTLineGetOffsetForStringIndex(mLine, ui+1, NULL); for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) { - positions[i++] = static_cast<int>(lround(xPosition)); + positions[i++] = xPosition; } ui++; } } else { // Single byte encoding for (int i=0;i<len;i++) { CGFloat xPosition = CTLineGetOffsetForStringIndex(mLine, i+1, NULL); - positions[i] = static_cast<int>(lround(xPosition)); + positions[i] = xPosition; } } } -int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { +XYPOSITION SurfaceImpl::WidthText(Font &font_, const char *s, int len) { if (font_.GetID()) { CFStringEncoding encoding = EncodingFromCharacterSet(unicodeMode, FontCharacterSet(font_)); @@ -1002,7 +1008,7 @@ int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { return 1; } -int SurfaceImpl::WidthChar(Font &font_, char ch) { +XYPOSITION SurfaceImpl::WidthChar(Font &font_, char ch) { char str[2] = { ch, '\0' }; if (font_.GetID()) { @@ -1019,7 +1025,7 @@ int SurfaceImpl::WidthChar(Font &font_, char ch) { const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890" "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; -int SurfaceImpl::Ascent(Font &font_) { +XYPOSITION SurfaceImpl::Ascent(Font &font_) { if (!font_.GetID()) return 1; @@ -1028,7 +1034,7 @@ int SurfaceImpl::Ascent(Font &font_) { } -int SurfaceImpl::Descent(Font &font_) { +XYPOSITION SurfaceImpl::Descent(Font &font_) { if (!font_.GetID()) return 1; @@ -1037,11 +1043,11 @@ int SurfaceImpl::Descent(Font &font_) { } -int SurfaceImpl::InternalLeading(Font &) { +XYPOSITION SurfaceImpl::InternalLeading(Font &) { return 0; } -int SurfaceImpl::ExternalLeading(Font &font_) { +XYPOSITION SurfaceImpl::ExternalLeading(Font &font_) { if (!font_.GetID()) return 1; @@ -1050,13 +1056,13 @@ int SurfaceImpl::ExternalLeading(Font &font_) { } -int SurfaceImpl::Height(Font &font_) { +XYPOSITION SurfaceImpl::Height(Font &font_) { int ht = Ascent(font_) + Descent(font_); return ht; } -int SurfaceImpl::AverageCharWidth(Font &font_) { +XYPOSITION SurfaceImpl::AverageCharWidth(Font &font_) { if (!font_.GetID()) return 1; diff --git a/cocoa/ScintillaTest/AppController.mm b/cocoa/ScintillaTest/AppController.mm index 4c8801a33..fbcdad9a8 100644 --- a/cocoa/ScintillaTest/AppController.mm +++ b/cocoa/ScintillaTest/AppController.mm @@ -116,7 +116,7 @@ const char user_keywords[] = // Definition of own keywords, not used by MySQL. [mEditor setReferenceProperty: SCI_SETKEYWORDS parameter: 7 value: user_keywords]; // Colors and styles for various syntactic elements. First the default style. - [mEditor setStringProperty: SCI_STYLESETFONT parameter: STYLE_DEFAULT value: @"Andale Mono"]; + [mEditor setStringProperty: SCI_STYLESETFONT parameter: STYLE_DEFAULT value: @"Helvetica"]; // [mEditor setStringProperty: SCI_STYLESETFONT parameter: STYLE_DEFAULT value: @"Monospac821 BT"]; // Very pleasing programmer's font. [mEditor setGeneralProperty: SCI_STYLESETSIZE parameter: STYLE_DEFAULT value: 14]; [mEditor setColorProperty: SCI_STYLESETFORE parameter: STYLE_DEFAULT value: [NSColor blackColor]]; diff --git a/include/Platform.h b/include/Platform.h index b0f3de0dc..d4c6bbfec 100644 --- a/include/Platform.h +++ b/include/Platform.h @@ -9,6 +9,9 @@ #ifndef PLATFORM_H #define PLATFORM_H +#define XYPOSITION float +//#define XYPOSITION int + // PLAT_GTK = GTK+ on Linux or Win32 // PLAT_GTK_WIN32 is defined additionally when running PLAT_GTK under Win32 // PLAT_WIN = Win32 API on Win32 OS @@ -76,10 +79,10 @@ typedef void *IdlerID; */ class Point { public: - int x; - int y; + XYPOSITION x; + XYPOSITION y; - explicit Point(int x_=0, int y_=0) : x(x_), y(y_) { + explicit Point(XYPOSITION x_=0, XYPOSITION y_=0) : x(x_), y(y_) { } // Other automatically defined methods (assignment, copy constructor, destructor) are fine @@ -94,12 +97,12 @@ public: */ class PRectangle { public: - int left; - int top; - int right; - int bottom; + XYPOSITION left; + XYPOSITION top; + XYPOSITION right; + XYPOSITION bottom; - PRectangle(int left_=0, int top_=0, int right_=0, int bottom_ = 0) : + PRectangle(XYPOSITION left_=0, XYPOSITION top_=0, XYPOSITION right_=0, XYPOSITION bottom_ = 0) : left(left_), top(top_), right(right_), bottom(bottom_) { } @@ -121,14 +124,14 @@ public: return (right > other.left) && (left < other.right) && (bottom > other.top) && (top < other.bottom); } - void Move(int xDelta, int yDelta) { + void Move(XYPOSITION xDelta, XYPOSITION yDelta) { left += xDelta; top += yDelta; right += xDelta; bottom += yDelta; } - int Width() { return right - left; } - int Height() { return bottom - top; } + XYPOSITION Width() { return right - left; } + XYPOSITION Height() { return bottom - top; } bool Empty() { return (Height() <= 0) || (Width() <= 0); } @@ -300,7 +303,7 @@ public: Font(); virtual ~Font(); - virtual void Create(const char *faceName, int characterSet, int size, + virtual void Create(const char *faceName, int characterSet, float size, bool bold, bool italic, int extraFontFlag=0); virtual void Release(); @@ -349,18 +352,18 @@ public: virtual void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back)=0; virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0; - virtual void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back)=0; - virtual void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back)=0; - virtual void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore)=0; - virtual void MeasureWidths(Font &font_, const char *s, int len, int *positions)=0; - virtual int WidthText(Font &font_, const char *s, int len)=0; - virtual int WidthChar(Font &font_, char ch)=0; - virtual int Ascent(Font &font_)=0; - virtual int Descent(Font &font_)=0; - virtual int InternalLeading(Font &font_)=0; - virtual int ExternalLeading(Font &font_)=0; - virtual int Height(Font &font_)=0; - virtual int AverageCharWidth(Font &font_)=0; + virtual void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back)=0; + virtual void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back)=0; + virtual void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, ColourAllocated fore)=0; + virtual void MeasureWidths(Font &font_, const char *s, int len, XYPOSITION *positions)=0; + virtual XYPOSITION WidthText(Font &font_, const char *s, int len)=0; + virtual XYPOSITION WidthChar(Font &font_, char ch)=0; + virtual XYPOSITION Ascent(Font &font_)=0; + virtual XYPOSITION Descent(Font &font_)=0; + virtual XYPOSITION InternalLeading(Font &font_)=0; + virtual XYPOSITION ExternalLeading(Font &font_)=0; + virtual XYPOSITION Height(Font &font_)=0; + virtual XYPOSITION AverageCharWidth(Font &font_)=0; virtual int SetPalette(Palette *pal, bool inBackGround)=0; virtual void SetClip(PRectangle rc)=0; diff --git a/src/Editor.cxx b/src/Editor.cxx index 452666be7..f44279bbd 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -2216,7 +2216,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou // Layout the line, determining the position of each character, // with an extra element at the end for the end of the line. int startseg = 0; // Start of the current segment, in char. number - int startsegx = 0; // Start of the current segment, in pixels + XYPOSITION startsegx = 0; // Start of the current segment, in pixels ll->positions[0] = 0; unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars; bool lastSegItalics = false; @@ -2902,8 +2902,10 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis // draw strings that are completely past the right side of the window. if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { // Clip to line rectangle, since may have a huge position which will not work with some platforms - rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left); - rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right); + if (rcSegment.left < rcLine.left) + rcSegment.left = rcLine.left; + if (rcSegment.right > rcLine.right) + rcSegment.right = rcLine.right; int styleMain = ll->styles[i]; const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc); @@ -3379,7 +3381,7 @@ void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xS const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth); const int virtualOffset = posCaret.VirtualSpace() * spaceWidth; if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) { - int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)]; + XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)]; if (ll->wrapIndent != 0) { int lineStart = ll->LineStart(subLine); if (lineStart != 0) // Wrapped diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 2105c292f..614e6b8bf 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -86,7 +86,7 @@ void LineLayout::Resize(int maxLineLength_) { indicators = new char[maxLineLength_ + 1]; // Extra position allocated as sometimes the Windows // GetTextExtentExPoint API writes an extra element. - positions = new int[maxLineLength_ + 1 + 1]; + positions = new XYPOSITION[maxLineLength_ + 1 + 1]; maxLineLength = maxLineLength_; } } @@ -503,15 +503,15 @@ PositionCacheEntry::PositionCacheEntry() : } void PositionCacheEntry::Set(unsigned int styleNumber_, const char *s_, - unsigned int len_, int *positions_, unsigned int clock_) { + unsigned int len_, XYPOSITION *positions_, unsigned int clock_) { Clear(); styleNumber = styleNumber_; len = len_; clock = clock_; if (s_ && positions_) { - positions = new short[len + (len + 1) / 2]; + positions = new XYPOSITION[len + (len + 1) / 2]; for (unsigned int i=0; i<len; i++) { - positions[i] = static_cast<short>(positions_[i]); + positions[i] = static_cast<XYPOSITION>(positions_[i]); } memcpy(reinterpret_cast<char *>(positions + len), s_, len); } @@ -530,7 +530,7 @@ void PositionCacheEntry::Clear() { } bool PositionCacheEntry::Retrieve(unsigned int styleNumber_, const char *s_, - unsigned int len_, int *positions_) const { + unsigned int len_, XYPOSITION *positions_) const { if ((styleNumber == styleNumber_) && (len == len_) && (memcmp(reinterpret_cast<char *>(positions + len), s_, len)== 0)) { for (unsigned int i=0; i<len; i++) { @@ -595,7 +595,7 @@ void PositionCache::SetSize(size_t size_) { } void PositionCache::MeasureWidths(Surface *surface, ViewStyle &vstyle, unsigned int styleNumber, - const char *s, unsigned int len, int *positions, Document *pdoc) { + const char *s, unsigned int len, XYPOSITION *positions, Document *pdoc) { allClear = false; int probe = -1; @@ -621,7 +621,7 @@ void PositionCache::MeasureWidths(Surface *surface, ViewStyle &vstyle, unsigned if (len > BreakFinder::lengthStartSubdivision) { // Break up into segments unsigned int startSegment = 0; - int xStartSegment = 0; + XYPOSITION xStartSegment = 0; while (startSegment < len) { unsigned int lenSegment = pdoc->SafeSegment(s + startSegment, len - startSegment, BreakFinder::lengthEachSubdivision); surface->MeasureWidths(vstyle.styles[styleNumber].font, s + startSegment, lenSegment, positions + startSegment); diff --git a/src/PositionCache.h b/src/PositionCache.h index c6076ea20..280446627 100644 --- a/src/PositionCache.h +++ b/src/PositionCache.h @@ -41,7 +41,7 @@ public: unsigned char *styles; int styleBitsSet; char *indicators; - int *positions; + XYPOSITION *positions; char bracePreviousStyles[2]; // Hotspot support @@ -103,13 +103,13 @@ class PositionCacheEntry { unsigned int styleNumber:8; unsigned int len:8; unsigned int clock:16; - short *positions; + XYPOSITION *positions; public: PositionCacheEntry(); ~PositionCacheEntry(); - void Set(unsigned int styleNumber_, const char *s_, unsigned int len_, int *positions_, unsigned int clock); + void Set(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_, unsigned int clock); void Clear(); - bool Retrieve(unsigned int styleNumber_, const char *s_, unsigned int len_, int *positions_) const; + bool Retrieve(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_) const; static int Hash(unsigned int styleNumber_, const char *s, unsigned int len); bool NewerThan(const PositionCacheEntry &other) const; void ResetClock(); @@ -155,7 +155,7 @@ public: void SetSize(size_t size_); size_t GetSize() const { return size; } void MeasureWidths(Surface *surface, ViewStyle &vstyle, unsigned int styleNumber, - const char *s, unsigned int len, int *positions, Document *pdoc); + const char *s, unsigned int len, XYPOSITION *positions, Document *pdoc); }; inline bool IsSpaceOrTab(int ch) { diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 6a2f103ba..acbb5f62d 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,30 @@ void Palette::Allocate(Window &) { } } +static IDWriteFactory *pIDWriteFactory = 0; + +static void EnsureDWriteFactory() { + // Construct + if (!pIDWriteFactory) { + DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(&pIDWriteFactory)); + } +} + +struct FormatAndBaseline { + IDWriteTextFormat *pTextFormat; + FLOAT baseline; + FormatAndBaseline(IDWriteTextFormat *pTextFormat_, FLOAT baseline_) : + pTextFormat(pTextFormat_), baseline(baseline_) { + } + ~FormatAndBaseline() { + pTextFormat->Release(); + pTextFormat = 0; + baseline = 1; + } +}; + #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 #endif @@ -247,28 +273,74 @@ class FontCached : Font { int usage; LOGFONTA lf; int hash; - FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_); + FontCached(const char *faceName_, int characterSet_, float size_, bool bold_, bool italic_, int extraFontFlag_); ~FontCached() {} - bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_); + bool SameAs(const char *faceName_, int characterSet_, float size_, bool bold_, bool italic_, int extraFontFlag_); 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 char *faceName_, int characterSet_, float size_, bool bold_, bool italic_, int extraFontFlag_); static void ReleaseId(FontID fid_); }; FontCached *FontCached::first = 0; -FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_) : +FontCached::FontCached(const char *faceName_, int characterSet_, float 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); + fid = 0; + EnsureDWriteFactory(); + if (pIDWriteFactory) { +#ifdef OLD_CODE + HFONT fontSave = static_cast<HFONT>(::SelectObject(hdc, font_.GetID())); + DWORD sizeOLTM = ::GetOutlineTextMetrics(hdc, NULL, NULL); + std::vector<char> vOLTM(sizeOLTM); + LPOUTLINETEXTMETRIC potm = reinterpret_cast<LPOUTLINETEXTMETRIC>(&vOLTM[0]); + DWORD worked = ::GetOutlineTextMetrics(hdc, sizeOLTM, potm); + ::SelectObject(hdc, fontSave); + if (!worked) + return; + const WCHAR *pwcFamily = reinterpret_cast<WCHAR *>(&vOLTM[reinterpret_cast<size_t>(potm->otmpFamilyName)]); + //const WCHAR *pwcFace = reinterpret_cast<WCHAR *>(&vOLTM[reinterpret_cast<size_t>(potm->otmpFaceName)]); + FLOAT fHeight = potm->otmTextMetrics.tmHeight * 72.0f / 96.0f; + bool italics = potm->otmTextMetrics.tmItalic != 0; + bool bold = potm->otmTextMetrics.tmWeight >= FW_BOLD; +#endif + IDWriteTextFormat *pTextFormat; + const int faceSize = 200; + WCHAR wszFace[faceSize]; + UTF16FromUTF8(faceName_, strlen(faceName_)+1, wszFace, faceSize); + FLOAT fHeight = size_; + if (fHeight > 2000) + fHeight = fHeight / 1000.0f; + HRESULT hr = pIDWriteFactory->CreateTextFormat(wszFace, NULL, + bold_ ? DWRITE_FONT_WEIGHT_SEMI_BOLD : DWRITE_FONT_WEIGHT_REGULAR, + italic_ ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, fHeight, L"en-us", &pTextFormat); + if (SUCCEEDED(hr)) { + const int maxLines = 2; + DWRITE_LINE_METRICS lineMetrics[maxLines]; + UINT32 lineCount = 0; + FLOAT baseline = 1.0f; + IDWriteTextLayout *pTextLayout = 0; + HRESULT hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat, + 100.0f, 100.0f, &pTextLayout); + if (SUCCEEDED(hr)) { + hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount); + if (SUCCEEDED(hr)) { + baseline = lineMetrics[0].baseline; + } + pTextLayout->Release(); + } + fid = reinterpret_cast<void *>(new FormatAndBaseline(pTextFormat, baseline)); + } + } usage = 1; } -bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_) { +bool FontCached::SameAs(const char *faceName_, int characterSet_, float size_, bool bold_, bool italic_, int extraFontFlag_) { return (lf.lfHeight == -(abs(size_))) && (lf.lfWeight == (bold_ ? FW_BOLD : FW_NORMAL)) && @@ -279,12 +351,11 @@ bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, boo } void FontCached::Release() { - if (fid) - ::DeleteObject(fid); + delete reinterpret_cast<FormatAndBaseline *>(fid); fid = 0; } -FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_, int extraFontFlag_) { +FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, float size_, bool bold_, bool italic_, int extraFontFlag_) { FontID ret = 0; ::EnterCriticalSection(&crPlatformLock); int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_, extraFontFlag_); @@ -335,27 +406,16 @@ Font::~Font() { #define FONTS_CACHED -void Font::Create(const char *faceName, int characterSet, int size, +void Font::Create(const char *faceName, int characterSet, float size, bool bold, bool italic, int extraFontFlag) { 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 } void Font::Release() { -#ifndef FONTS_CACHED - if (fid) - ::DeleteObject(fid); -#else if (fid) FontCached::ReleaseId(fid); -#endif fid = 0; } @@ -367,23 +427,26 @@ class SurfaceImpl : public Surface { bool unicodeMode; HDC hdc; bool hdcOwned; - HPEN pen; - HPEN penOld; - HBRUSH brush; - HBRUSH brushOld; - HFONT font; - HFONT fontOld; - HBITMAP bitmap; - HBITMAP bitmapOld; - HPALETTE paletteOld; int maxWidthMeasure; int maxLenText; + int x, y; int codePage; // If 9x OS and current code page is same as ANSI code page. bool win9xACPSame; - void BrushColor(ColourAllocated back); + ID2D1RenderTarget *pRenderTarget; + bool ownRenderTarget; + bool needsEndDraw; + int clipsActive; + + IDWriteTextFormat *pTextFormat; + FLOAT baseline; + ID2D1SolidColorBrush *pBrush; + float dpiScaleX; + float dpiScaleY; + bool hasBegun; + void SetFont(Font &font_); // Private so SurfaceImpl objects can not be copied @@ -393,13 +456,18 @@ public: SurfaceImpl(); virtual ~SurfaceImpl(); + void SetDWrite(HDC hdc); 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_); @@ -415,19 +483,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,14 +509,13 @@ public: } //namespace Scintilla #endif +#pragma comment(lib, "d2d1.lib") +#pragma comment(lib, "dwrite.lib") + SurfaceImpl::SurfaceImpl() : unicodeMode(false), - hdc(0), hdcOwned(false), - pen(0), penOld(0), - brush(0), brushOld(0), - font(0), fontOld(0), - bitmap(0), bitmapOld(0), - paletteOld(0) { + hdc(0), hdcOwned(false), + x(0), y(0), { // Windows 9x has only a 16 bit coordinate system so break after 30000 pixels maxWidthMeasure = IsNT() ? INT_MAX : 30000; // There appears to be a 16 bit string length limit in GDI on NT and a limit of @@ -457,6 +524,17 @@ SurfaceImpl::SurfaceImpl() : codePage = 0; win9xACPSame = false; + + pRenderTarget = NULL; + ownRenderTarget = false; + needsEndDraw = false; + clipsActive = 0; + pTextFormat = NULL; + baseline = 1.0f; + pBrush = NULL; + dpiScaleX = 1.0; + dpiScaleY = 1.0; + hasBegun = false; } SurfaceImpl::~SurfaceImpl() { @@ -464,102 +542,107 @@ SurfaceImpl::~SurfaceImpl() { } void SurfaceImpl::Release() { - if (penOld) { - ::SelectObject(reinterpret_cast<HDC>(hdc), penOld); - ::DeleteObject(pen); - penOld = 0; - } - pen = 0; - if (brushOld) { - ::SelectObject(reinterpret_cast<HDC>(hdc), brushOld); - ::DeleteObject(brush); - brushOld = 0; - } - brush = 0; - if (fontOld) { - // Fonts are not deleted as they are owned by a Font object - ::SelectObject(reinterpret_cast<HDC>(hdc), fontOld); - fontOld = 0; - } - font = 0; - if (bitmapOld) { - ::SelectObject(reinterpret_cast<HDC>(hdc), bitmapOld); - ::DeleteObject(bitmap); - bitmapOld = 0; - } - bitmap = 0; - if (paletteOld) { - // Palettes are not deleted as they are owned by a Palette object - ::SelectPalette(reinterpret_cast<HDC>(hdc), - reinterpret_cast<HPALETTE>(paletteOld), TRUE); - paletteOld = 0; - } if (hdcOwned) { ::DeleteDC(reinterpret_cast<HDC>(hdc)); hdc = 0; hdcOwned = false; } + + if (pBrush) { + pBrush->Release(); + pBrush = 0; + } + if (pRenderTarget) { + while (clipsActive) { + pRenderTarget->PopAxisAlignedClip(); + clipsActive--; + } + if (ownRenderTarget) { + pRenderTarget->Release(); + } + pRenderTarget = 0; + } +} + +// Ensures have pIDWriteFactory, and pRenderTarget +// If any fail, pRenderTarget will be NULL +void SurfaceImpl::SetDWrite(HDC hdc) { + // Construct + EnsureDWriteFactory(); + dpiScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f; + dpiScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0f; } bool SurfaceImpl::Initialised() { - return hdc != 0; + return pRenderTarget != 0; +} + +HRESULT SurfaceImpl::FlushDrawing() { + return pRenderTarget->Flush(); } -void SurfaceImpl::Init(WindowID) { +void SurfaceImpl::Init(WindowID wid) { Release(); hdc = ::CreateCompatibleDC(NULL); hdcOwned = true; ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE); + RECT rc; + ::GetClientRect(reinterpret_cast<HWND>(wid), &rc); + SetDWrite(hdc); } void SurfaceImpl::Init(SurfaceID sid, WindowID) { Release(); - hdc = reinterpret_cast<HDC>(sid); + hdc = ::CreateCompatibleDC(NULL); + hdcOwned = true; + pRenderTarget = reinterpret_cast<ID2D1HwndRenderTarget *>(sid); ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE); + SetDWrite(hdc); } void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID) { Release(); - hdc = ::CreateCompatibleDC(static_cast<SurfaceImpl *>(surface_)->hdc); + hdc = ::CreateCompatibleDC(NULL); // Just for measurement hdcOwned = true; - bitmap = ::CreateCompatibleBitmap(static_cast<SurfaceImpl *>(surface_)->hdc, width, height); - bitmapOld = static_cast<HBITMAP>(::SelectObject(hdc, bitmap)); - ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE); -} - -void SurfaceImpl::PenColour(ColourAllocated fore) { - if (pen) { - ::SelectObject(hdc, penOld); - ::DeleteObject(pen); - pen = 0; - penOld = 0; + SurfaceImpl *psurfOther = static_cast<SurfaceImpl *>(surface_); + ID2D1BitmapRenderTarget *pCompatibleRenderTarget = NULL; + HRESULT hr = psurfOther->pRenderTarget->CreateCompatibleRenderTarget( + D2D1::SizeF(width, height), &pCompatibleRenderTarget); + if (SUCCEEDED(hr)) { + pRenderTarget = pCompatibleRenderTarget; + pRenderTarget->BeginDraw(); + ownRenderTarget = true; + needsEndDraw = true; } - pen = ::CreatePen(0,1,fore.AsLong()); - penOld = static_cast<HPEN>(::SelectObject(reinterpret_cast<HDC>(hdc), pen)); } -void SurfaceImpl::BrushColor(ColourAllocated back) { - if (brush) { - ::SelectObject(hdc, brushOld); - ::DeleteObject(brush); - brush = 0; - brushOld = 0; +void SurfaceImpl::PenColour(ColourAllocated fore) { + D2DPenColour(fore); +} + +void SurfaceImpl::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; + } + } } - // Only ever want pure, non-dithered brushes - ColourAllocated colourNearest = ::GetNearestColor(hdc, back.AsLong()); - brush = ::CreateSolidBrush(colourNearest.AsLong()); - brushOld = static_cast<HBRUSH>(::SelectObject(hdc, brush)); } void SurfaceImpl::SetFont(Font &font_) { - if (font_.GetID() != font) { - if (fontOld) { - ::SelectObject(hdc, font_.GetID()); - } else { - fontOld = static_cast<HFONT>(::SelectObject(hdc, font_.GetID())); - } - font = reinterpret_cast<HFONT>(font_.GetID()); - } + FormatAndBaseline *pfabl = reinterpret_cast<FormatAndBaseline *>(font_.GetID()); + pTextFormat = pfabl->pTextFormat; + baseline = pfabl->baseline; } int SurfaceImpl::LogPixelsY() { @@ -571,142 +654,160 @@ int SurfaceImpl::DeviceHeightFont(int points) { } void SurfaceImpl::MoveTo(int x_, int y_) { - ::MoveToEx(hdc, x_, y_, 0); + 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 SurfaceImpl::LineTo(int x_, int y_) { - ::LineTo(hdc, x_, 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 SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) { - PenColour(fore); - BrushColor(back); - ::Polygon(hdc, reinterpret_cast<POINT *>(pts), npts); + 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 SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) { - PenColour(fore); - BrushColor(back); - ::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); + 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 SurfaceImpl::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); - ::SetBkColor(hdc, back.AsLong()); - ::ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rcw, TEXT(""), 0, NULL); + 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 SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { - HBRUSH br; - if (static_cast<SurfaceImpl &>(surfacePattern).bitmap) - br = ::CreatePatternBrush(static_cast<SurfaceImpl &>(surfacePattern).bitmap); - else // Something is wrong so display in red - br = ::CreateSolidBrush(RGB(0xff, 0, 0)); - RECT rcw = RectFromPRectangle(rc); - ::FillRect(hdc, &rcw, br); - ::DeleteObject(br); + SurfaceImpl &surfOther = static_cast<SurfaceImpl &>(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 SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) { - PenColour(fore); - BrushColor(back); - ::RoundRect(hdc, - rc.left + 1, rc.top, - rc.right - 1, rc.bottom, - 8, 8); -} - -// Plot a point into a DWORD buffer symetrically to all 4 qudrants -static void AllFour(DWORD *pixels, int width, int height, int x, int y, DWORD val) { - pixels[y*width+x] = val; - pixels[y*width+width-1-x] = val; - pixels[(height-1-y)*width+x] = val; - pixels[(height-1-y)*width+width-1-x] = val; -} - -#ifndef AC_SRC_OVER -#define AC_SRC_OVER 0x00 -#endif -#ifndef AC_SRC_ALPHA -#define AC_SRC_ALPHA 0x01 -#endif + 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); -static DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) { - union { - byte pixVal[4]; - DWORD val; - } converter; - converter.pixVal[0] = b; - converter.pixVal[1] = g; - converter.pixVal[2] = r; - converter.pixVal[3] = a; - return converter.val; + 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 SurfaceImpl::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)); - int width = rc.Width(); - int height = rc.Height(); - // Ensure not distorted too much by corners when small - cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2); - BITMAPINFO bpih = {sizeof(BITMAPINFOHEADER), width, height, 1, 32, BI_RGB, 0, 0, 0, 0, 0}; - void *image = 0; - HBITMAP hbmMem = CreateDIBSection(reinterpret_cast<HDC>(hMemDC), &bpih, - DIB_RGB_COLORS, &image, NULL, 0); - - HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem); - - DWORD valEmpty = dwordFromBGRA(0,0,0,0); - DWORD valFill = dwordFromBGRA( - static_cast<byte>(GetBValue(fill.AsLong()) * alphaFill / 255), - static_cast<byte>(GetGValue(fill.AsLong()) * alphaFill / 255), - static_cast<byte>(GetRValue(fill.AsLong()) * alphaFill / 255), - static_cast<byte>(alphaFill)); - DWORD valOutline = dwordFromBGRA( - static_cast<byte>(GetBValue(outline.AsLong()) * alphaOutline / 255), - static_cast<byte>(GetGValue(outline.AsLong()) * alphaOutline / 255), - static_cast<byte>(GetRValue(outline.AsLong()) * alphaOutline / 255), - static_cast<byte>(alphaOutline)); - DWORD *pixels = reinterpret_cast<DWORD *>(image); - for (int y=0; y<height; y++) { - for (int x=0; x<width; x++) { - if ((x==0) || (x==width-1) || (y == 0) || (y == height-1)) { - pixels[y*width+x] = valOutline; - } else { - pixels[y*width+x] = valFill; - } - } - } - for (int c=0;c<cornerSize; c++) { - for (int x=0;x<c+1; x++) { - AllFour(pixels, width, height, x, c-x, valEmpty); - } - } - for (int x=1;x<cornerSize; x++) { - AllFour(pixels, width, height, x, cornerSize-x, valOutline); - } + 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); - BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - - AlphaBlendFn(reinterpret_cast<HDC>(hdc), rc.left, rc.top, width, height, hMemDC, 0, 0, width, height, merge); - - SelectBitmap(hMemDC, hbmOld); - ::DeleteObject(hbmMem); - ::DeleteDC(hMemDC); - } else { - BrushColor(outline); - RECT rcw = RectFromPRectangle(rc); - FrameRect(hdc, &rcw, brush); + 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 SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) { - if (AlphaBlendFn && rc.Width() > 0) { - HDC hMemDC = ::CreateCompatibleDC(reinterpret_cast<HDC>(hdc)); + if (pRenderTarget) { if (rc.Width() > width) rc.left += (rc.Width() - width) / 2; rc.right = rc.left + width; @@ -714,15 +815,10 @@ void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsi rc.top += (rc.Height() - height) / 2; rc.bottom = rc.top + height; - BITMAPINFO bpih = {sizeof(BITMAPINFOHEADER), width, height, 1, 32, BI_RGB, 0, 0, 0, 0, 0}; - unsigned char *image = 0; - HBITMAP hbmMem = CreateDIBSection(reinterpret_cast<HDC>(hMemDC), &bpih, - DIB_RGB_COLORS, reinterpret_cast<void **>(&image), NULL, 0); - HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem); - - for (int y=height-1; y>=0; y--) { + 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 + (y*width+x) * 4; + 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; @@ -731,28 +827,50 @@ void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsi pixel[3] = *pixelsImage++; } } - - BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; - - AlphaBlendFn(reinterpret_cast<HDC>(hdc), rc.left, rc.top, rc.Width(), rc.Height(), hMemDC, 0, 0, width, height, merge); - - SelectBitmap(hMemDC, hbmOld); - ::DeleteObject(hbmMem); - ::DeleteDC(hMemDC); + 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 SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) { - PenColour(fore); - BrushColor(back); - ::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); + 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 SurfaceImpl::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); + SurfaceImpl &surfOther = static_cast<SurfaceImpl &>(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(); + } } // Buffer to hold strings and string position arrays without always allocating on heap. @@ -792,109 +910,115 @@ public: } } }; -typedef VarBuffer<int, stackBufferLength> TextPositions; +typedef VarBuffer<XYPOSITION, stackBufferLength> TextPositions; -void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions) { +void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, XYPOSITION ybase, const char *s, int len, UINT) { SetFont(font_); RECT rcw = RectFromPRectangle(rc); - SIZE sz={0,0}; - int pos = 0; - int x = rc.left; - - // Text drawing may fail if the text is too big. - // If it does fail, slice up into segments and draw each segment. - const int maxSegmentLength = 0x200; - - if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) { - // Use ANSI calls - int lenDraw = Platform::Minimum(len, maxLenText); - if (!::ExtTextOutA(hdc, x, ybase, fuOptions, &rcw, s, lenDraw, NULL)) { - while (lenDraw > pos) { - int seglen = Platform::Minimum(maxSegmentLength, lenDraw - pos); - if (!::ExtTextOutA(hdc, x, ybase, fuOptions, &rcw, s+pos, seglen, NULL)) { - PLATFORM_ASSERT(false); - return; - } - ::GetTextExtentPoint32A(hdc, s+pos, seglen, &sz); - x += sz.cx; - pos += seglen; - } - } - } else { - // Use Unicode calls - const TextWide tbuf(s, len, unicodeMode, codePage); - if (!::ExtTextOutW(hdc, x, ybase, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, NULL)) { - while (tbuf.tlen > pos) { - int seglen = Platform::Minimum(maxSegmentLength, tbuf.tlen - pos); - if (!::ExtTextOutW(hdc, x, ybase, fuOptions, &rcw, tbuf.buffer+pos, seglen, NULL)) { - PLATFORM_ASSERT(false); - return; - } - ::GetTextExtentPoint32W(hdc, tbuf.buffer+pos, seglen, &sz); - x += sz.cx; - pos += seglen; - } + + // 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()+2, rc.Height(), &pTextLayout); + if (SUCCEEDED(hr)) { + D2D1_POINT_2F origin = {rc.left, ybase-baseline}; + 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-baseline) / 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 SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceImpl::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); + if (pRenderTarget) { + FillRectangle(rc, back); + D2DPenColour(fore); + DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE); + } } -void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, +void SurfaceImpl::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); + if (pRenderTarget) { + FillRectangle(rc, back); + D2DPenColour(fore); + 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 SurfaceImpl::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] != ' ') { - ::SetTextColor(hdc, fore.AsLong()); - ::SetBkMode(hdc, TRANSPARENT); - DrawTextCommon(rc, font_, ybase, s, len, 0); - ::SetBkMode(hdc, OPAQUE); + if (pRenderTarget) { + D2DPenColour(fore); + DrawTextCommon(rc, font_, ybase, s, len, 0); + } return; } } } -int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { +XYPOSITION SurfaceImpl::WidthText(Font &font_, const char *s, int len) { + FLOAT width = 1.0; SetFont(font_); - SIZE sz={0,0}; - if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) { - ::GetTextExtentPoint32A(hdc, s, Platform::Minimum(len, maxLenText), &sz); - } else { - const TextWide tbuf(s, len, unicodeMode, codePage); - ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz); + 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 sz.cx; + return int(width + 0.5); } -void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) { +void SurfaceImpl::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); - 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 - // So measure the character widths by measuring each initial substring - // Turns a linear operation into a qudratic but seems fast enough on test files - for (int widthSS=0; widthSS < tbuf.tlen; widthSS++) { - ::GetTextExtentPoint32W(hdc, tbuf.buffer, widthSS+1, &sz); - poses.buffer[widthSS] = sz.cx; + 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); @@ -921,39 +1045,17 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi while (i<len) { positions[i++] = lastPos; } - } else if (IsNT() || (codePage==0) || win9xACPSame) { - // Zero positions to avoid random behaviour on failure. - memset(positions, 0, len * sizeof(*positions)); - // len may be larger than platform supports so loop over segments small enough for platform - int startOffset = 0; - while (len > 0) { - int lenBlock = Platform::Minimum(len, maxLenText); - if (!::GetTextExtentExPointA(hdc, s, lenBlock, maxWidthMeasure, &fit, positions, &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; - } - startOffset = positions[lenBlock-1]; - len -= lenBlock; - positions += lenBlock; - s += lenBlock; + } 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 { - // Support Asian string display in 9x English - const TextWide tbuf(s, len, unicodeMode, codePage); - TextPositions poses(tbuf.tlen); - for (int widthSS=0; widthSS<tbuf.tlen; widthSS++) { - ::GetTextExtentPoint32W(hdc, tbuf.buffer, widthSS+1, &sz); - poses.buffer[widthSS] = sz.cx; - } + // May be more than one byte per position int ui = 0; for (int i=0;i<len;) { if (::IsDBCSLeadByteEx(codePage, s[i])) { @@ -970,77 +1072,138 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi } } -int SurfaceImpl::WidthChar(Font &font_, char ch) { +XYPOSITION SurfaceImpl::WidthChar(Font &font_, char ch) { + FLOAT width = 1.0; SetFont(font_); - SIZE sz; - ::GetTextExtentPoint32A(hdc, &ch, 1, &sz); - return sz.cx; + 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); } -int SurfaceImpl::Ascent(Font &font_) { +XYPOSITION SurfaceImpl::Ascent(Font &font_) { + FLOAT ascent = 1.0; SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmAscent; + if (pIDWriteFactory && pTextFormat) { + SetFont(font_); + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + HRESULT hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat, 1000.0, 1000.0, &pTextLayout); + if (SUCCEEDED(hr)) { + DWRITE_TEXT_METRICS textMetrics; + pTextLayout->GetMetrics(&textMetrics); + ascent = textMetrics.layoutHeight; + const int clusters = 20; + DWRITE_CLUSTER_METRICS clusterMetrics[clusters]; + UINT32 count = 0; + pTextLayout->GetClusterMetrics(clusterMetrics, clusters, &count); + //height = pTextLayout->GetMaxHeight(); + FLOAT minWidth = 0; + hr = pTextLayout->DetermineMinWidth(&minWidth); + const int maxLines = 2; + DWRITE_LINE_METRICS lineMetrics[maxLines]; + UINT32 lineCount = 0; + hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount); + if (SUCCEEDED(hr)) { + ascent = lineMetrics[0].baseline; + } + pTextLayout->Release(); + } + } + return int(ascent + 0.5); } -int SurfaceImpl::Descent(Font &font_) { +XYPOSITION SurfaceImpl::Descent(Font &font_) { + FLOAT descent = 1.0; SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmDescent; + if (pIDWriteFactory && pTextFormat) { + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + HRESULT hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat, 1000.0, 1000.0, &pTextLayout); + if (SUCCEEDED(hr)) { + const int maxLines = 2; + DWRITE_LINE_METRICS lineMetrics[maxLines]; + UINT32 lineCount = 0; + hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount); + if (SUCCEEDED(hr)) { + descent = lineMetrics[0].height - lineMetrics[0].baseline; + } + pTextLayout->Release(); + } + } + return int(descent + 0.5); } -int SurfaceImpl::InternalLeading(Font &font_) { - SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmInternalLeading; +XYPOSITION SurfaceImpl::InternalLeading(Font &) { + return 0; } -int SurfaceImpl::ExternalLeading(Font &font_) { - SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmExternalLeading; +XYPOSITION SurfaceImpl::ExternalLeading(Font &) { + return 1; } -int SurfaceImpl::Height(Font &font_) { +XYPOSITION SurfaceImpl::Height(Font &font_) { + FLOAT height = 1.0; SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmHeight; + if (pIDWriteFactory && pTextFormat) { + // Create a layout + IDWriteTextLayout *pTextLayout = 0; + HRESULT hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat, 1000.0, 1000.0, &pTextLayout); + if (SUCCEEDED(hr)) { + const int maxLines = 2; + DWRITE_LINE_METRICS lineMetrics[maxLines]; + UINT32 lineCount = 0; + hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount); + if (SUCCEEDED(hr)) { + height = lineMetrics[0].height; + } + pTextLayout->Release(); + } + } + // Truncating rather than rounding as otherwise too much space. + return int(height); } -int SurfaceImpl::AverageCharWidth(Font &font_) { +XYPOSITION SurfaceImpl::AverageCharWidth(Font &font_) { + FLOAT width = 1.0; SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmAveCharWidth; + 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 SurfaceImpl::SetPalette(Palette *pal, bool inBackGround) { - if (paletteOld) { - ::SelectPalette(hdc, paletteOld, TRUE); - } - paletteOld = 0; - int changes = 0; - if (pal->allowRealization) { - paletteOld = ::SelectPalette(hdc, - reinterpret_cast<HPALETTE>(pal->hpal), inBackGround); - changes = ::RealizePalette(hdc); - } - return changes; +int SurfaceImpl::SetPalette(Palette *, bool) { + return 0; } void SurfaceImpl::SetClip(PRectangle rc) { - ::IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); + if (pRenderTarget) { + D2D1_RECT_F rcClip = {rc.left, rc.top, rc.right, rc.bottom}; + pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED); + clipsActive++; + } } void SurfaceImpl::FlushCachedState() { - pen = 0; - brush = 0; - font = 0; } void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) { @@ -1651,6 +1814,8 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) { if (pimage) { Surface *surfaceItem = Surface::Allocate(); if (surfaceItem) { + // TODO: Need a DC RenderTarget here + /* surfaceItem->Init(pDrawItem->hDC, pDrawItem->hwndItem); int left = pDrawItem->rcItem.left + ItemInset.x + ImageInset.x; PRectangle rcImage(left, pDrawItem->rcItem.top, @@ -1658,6 +1823,7 @@ void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) { surfaceItem->DrawRGBAImage(rcImage, pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels()); delete surfaceItem; + */ ::SetTextAlign(pDrawItem->hDC, TA_TOP); } } diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index 9925a64bb..28e48596b 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -24,6 +24,9 @@ #include <richedit.h> #include <windowsx.h> +#include <d2d1.h> +#include <dwrite.h> + #include "Platform.h" #include "ILexer.h" @@ -197,6 +200,10 @@ class ScintillaWin : static HINSTANCE hInstance; + IDWriteFactory *pIDWriteFactory; + ID2D1Factory *pD2DFactory; + ID2D1HwndRenderTarget *pRenderTarget; + ScintillaWin(HWND hwnd); ScintillaWin(const ScintillaWin &); virtual ~ScintillaWin(); @@ -204,6 +211,8 @@ class ScintillaWin : virtual void Initialise(); virtual void Finalise(); + void EnsureRenderTarget(); + void DropRenderTarget(); HWND MainHWND(); static sptr_t DirectFunction( @@ -350,6 +359,10 @@ ScintillaWin::ScintillaWin(HWND hwnd) { sysCaretWidth = 0; sysCaretHeight = 0; + pIDWriteFactory = 0; + pD2DFactory = 0; + pRenderTarget = 0; + keysAlwaysUnicode = false; caret.period = ::GetCaretBlinkTime(); @@ -378,18 +391,69 @@ void ScintillaWin::Initialise() { ::GetProcAddress(commctrl32, "_TrackMouseEvent"); } } + + if (!pIDWriteFactory) { + DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(&pIDWriteFactory)); + } + if (!pD2DFactory) { + D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory); + } } void ScintillaWin::Finalise() { ScintillaBase::Finalise(); SetTicking(false); SetIdle(false); + DropRenderTarget(); + if (pIDWriteFactory) { + pIDWriteFactory->Release(); + pIDWriteFactory = 0; + } + if (pD2DFactory) { + pD2DFactory->Release(); + pD2DFactory = 0; + } ::RevokeDragDrop(MainHWND()); if (SUCCEEDED(hrOle)) { ::OleUninitialize(); } } +void ScintillaWin::EnsureRenderTarget() { + if (pD2DFactory && !pRenderTarget) { + RECT rc; + HWND hw = MainHWND(); + GetClientRect(hw, &rc); + + D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top); + + // Create a Direct2D render target. +#if 1 + pD2DFactory->CreateHwndRenderTarget( + D2D1::RenderTargetProperties(), + D2D1::HwndRenderTargetProperties(hw, size), + &pRenderTarget); +#else + pD2DFactory->CreateHwndRenderTarget( + D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT , + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), + 96.0f, 96.0f, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT), + D2D1::HwndRenderTargetProperties(hw, size), + &pRenderTarget); +#endif + } +} + +void ScintillaWin::DropRenderTarget() { + if (pRenderTarget) { + pRenderTarget->Release(); + pRenderTarget = 0; + } +} + HWND ScintillaWin::MainHWND() { return reinterpret_cast<HWND>(wMain.GetID()); } @@ -505,8 +569,11 @@ LRESULT ScintillaWin::WndPaint(uptr_t wParam) { pps = &ps; ::BeginPaint(MainHWND(), pps); } - AutoSurface surfaceWindow(pps->hdc, this); + //AutoSurface surfaceWindow(pps->hdc, this); + EnsureRenderTarget(); + AutoSurface surfaceWindow(pRenderTarget, this); if (surfaceWindow) { + pRenderTarget->BeginDraw(); rcPaint = PRectangle(pps->rcPaint.left, pps->rcPaint.top, pps->rcPaint.right, pps->rcPaint.bottom); PRectangle rcClient = GetClientRectangle(); paintingAllText = rcPaint.Contains(rcClient); @@ -517,6 +584,10 @@ LRESULT ScintillaWin::WndPaint(uptr_t wParam) { } Paint(surfaceWindow, rcPaint); surfaceWindow->Release(); + HRESULT hr = pRenderTarget->EndDraw(); + if (hr == D2DERR_RECREATE_TARGET) { + DropRenderTarget(); + } } if (hRgnUpdate) { ::DeleteRgn(hRgnUpdate); @@ -674,6 +745,7 @@ sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam break; case WM_SIZE: { + DropRenderTarget(); //Platform::DebugPrintf("Scintilla WM_SIZE %d %d\n", LoWord(lParam), HiWord(lParam)); ChangeSize(); } @@ -1189,11 +1261,12 @@ bool ScintillaWin::PaintContains(PRectangle rc) { return contains; } -void ScintillaWin::ScrollText(int linesToMove) { +void ScintillaWin::ScrollText(int /* linesToMove */) { //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove); - ::ScrollWindow(MainHWND(), 0, - vs.lineHeight * linesToMove, 0, 0); - ::UpdateWindow(MainHWND()); + //::ScrollWindow(MainHWND(), 0, + // vs.lineHeight * linesToMove, 0, 0); + //::UpdateWindow(MainHWND()); + Redraw(); } void ScintillaWin::UpdateSystemCaret() { @@ -2283,7 +2356,9 @@ void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) { HorizontalScrollTo(xPos); } -void ScintillaWin::RealizeWindowPalette(bool inBackGround) { +void ScintillaWin::RealizeWindowPalette(bool) { + // No support for palette with D2D +/* RefreshStyleData(); HDC hdc = ::GetDC(MainHWND()); // Select a stock font to prevent warnings from BoundsChecker @@ -2296,6 +2371,7 @@ void ScintillaWin::RealizeWindowPalette(bool inBackGround) { surfaceWindow->Release(); } ::ReleaseDC(MainHWND(), hdc); +*/ } /** @@ -2303,23 +2379,30 @@ void ScintillaWin::RealizeWindowPalette(bool inBackGround) { * This paint will not be abandoned. */ void ScintillaWin::FullPaint() { - HDC hdc = ::GetDC(MainHWND()); - FullPaintDC(hdc); - ::ReleaseDC(MainHWND(), hdc); + //HDC hdc = ::GetDC(MainHWND()); + //FullPaintDC(hdc); + //::ReleaseDC(MainHWND(), hdc); + FullPaintDC(0); } /** * Redraw all of text area on the specified DC. * This paint will not be abandoned. */ -void ScintillaWin::FullPaintDC(HDC hdc) { +void ScintillaWin::FullPaintDC(HDC) { paintState = painting; rcPaint = GetClientRectangle(); paintingAllText = true; - AutoSurface surfaceWindow(hdc, this); + EnsureRenderTarget(); + AutoSurface surfaceWindow(pRenderTarget, this); if (surfaceWindow) { + pRenderTarget->BeginDraw(); Paint(surfaceWindow, rcPaint); surfaceWindow->Release(); + HRESULT hr = pRenderTarget->EndDraw(); + if (hr == D2DERR_RECREATE_TARGET) { + DropRenderTarget(); + } } paintState = notPainting; } @@ -2693,10 +2776,21 @@ sptr_t PASCAL ScintillaWin::CTWndProc( ::BeginPaint(hWnd, &ps); Surface *surfaceWindow = Surface::Allocate(); if (surfaceWindow) { - surfaceWindow->Init(ps.hdc, hWnd); + ID2D1HwndRenderTarget *pCTRenderTarget = 0; + RECT rc; + GetClientRect(hWnd, &rc); + // Create a Direct2D render target. + sciThis->pD2DFactory->CreateHwndRenderTarget( + D2D1::RenderTargetProperties(), + D2D1::HwndRenderTargetProperties(hWnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)), + &pCTRenderTarget); + //surfaceWindow->Init(ps.hdc, hWnd); + surfaceWindow->Init(pCTRenderTarget, hWnd); surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == sciThis->ct.codePage); surfaceWindow->SetDBCSMode(sciThis->ct.codePage); + pCTRenderTarget->BeginDraw(); sciThis->ct.PaintCT(surfaceWindow); + pCTRenderTarget->EndDraw(); surfaceWindow->Release(); delete surfaceWindow; } |