diff options
-rw-r--r-- | win32/PlatWin.cxx | 884 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 118 |
2 files changed, 627 insertions, 375 deletions
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 6a2f103ba..2ba4ed374 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 @@ -264,7 +290,51 @@ FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool 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_; + HRESULT hr = pIDWriteFactory->CreateTextFormat(wszFace, NULL, + bold_ ? DWRITE_FONT_WEIGHT_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; } @@ -279,8 +349,7 @@ 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; } @@ -338,24 +407,13 @@ Font::~Font() { void Font::Create(const char *faceName, int characterSet, int 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 +425,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 +454,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_); @@ -441,14 +507,12 @@ 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) { // 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 +521,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 +539,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 +651,156 @@ 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; } 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(rc.left + 0.5, rc.top+0.5, 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(rc.left, rc.top, 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); - } - - BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + 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); - 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 +808,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 +820,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. @@ -794,62 +905,48 @@ public: }; typedef VarBuffer<int, 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_, int 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); + } 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, 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, 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, @@ -857,44 +954,63 @@ void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, con // 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) { + 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) { 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); } } + 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 +1037,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])) { @@ -971,76 +1065,137 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi } int 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_) { + 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_) { + 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; +int SurfaceImpl::InternalLeading(Font &) { + return 0; } -int SurfaceImpl::ExternalLeading(Font &font_) { - SetFont(font_); - TEXTMETRIC tm; - ::GetTextMetrics(hdc, &tm); - return tm.tmExternalLeading; +int SurfaceImpl::ExternalLeading(Font &) { + return 1; } int 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_) { + 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 +1806,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 +1815,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; } |