Files
wxWidgets/src/msw/textmeasure.cpp
Vadim Zeitlin 70768a33d2 Dramatically speed up measuring text extent in wxMSW
Skip correction for the under/overhang for non-italic fonts: it seems to
be pretty small for them and avoiding the calls to ::GetCharABCWidths()
makes GetTextExtent() 7-8 times faster, which seems to be a worthwhile
compensation.

If we decide to restore these calls in the future, we will need to
implement some kind of cache for them, as they're just too slow
otherwise.
2020-06-10 23:46:13 +02:00

197 lines
6.0 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/msw/textmeasure.cpp
// Purpose: wxTextMeasure implementation for wxMSW
// Author: Manuel Martin
// Created: 2012-10-05
// Copyright: (c) 1997-2012 wxWidgets team
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/msw/private.h"
#ifndef WX_PRECOMP
#include "wx/window.h"
#include "wx/font.h"
#endif //WX_PRECOMP
#include "wx/private/textmeasure.h"
#include "wx/msw/dc.h"
// ============================================================================
// wxTextMeasure implementation
// ============================================================================
void wxTextMeasure::Init()
{
m_hdc = NULL;
m_hfontOld = NULL;
if ( m_dc )
{
wxClassInfo* const ci = m_dc->GetImpl()->GetClassInfo();
if ( ci->IsKindOf(wxCLASSINFO(wxMSWDCImpl)))
{
m_useDCImpl = false;
}
}
}
void wxTextMeasure::BeginMeasuring()
{
if ( m_dc )
{
m_hdc = m_dc->GetHDC();
// Non-native wxDC subclasses should override their DoGetTextExtent()
// and other methods.
wxASSERT_MSG( m_hdc, wxS("Must not be used with non-native wxDCs") );
}
else if ( m_win )
{
m_hdc = ::GetDC(GetHwndOf(m_win));
}
// We need to set the font if it's explicitly specified, of course, but
// also if we're associated with a window because the window HDC created
// above has the default font selected into it and not the font of the
// window.
if ( m_font || m_win )
m_hfontOld = (HFONT)::SelectObject(m_hdc, GetHfontOf(GetFont()));
}
void wxTextMeasure::EndMeasuring()
{
if ( m_hfontOld )
{
::SelectObject(m_hdc, m_hfontOld);
m_hfontOld = NULL;
}
if ( m_win )
::ReleaseDC(GetHwndOf(m_win), m_hdc);
//else: our HDC belongs to m_dc, don't touch it
m_hdc = NULL;
}
// Notice we don't check here the font. It is supposed to be OK before the call.
void wxTextMeasure::DoGetTextExtent(const wxString& string,
wxCoord *width,
wxCoord *height,
wxCoord *descent,
wxCoord *externalLeading)
{
SIZE sizeRect;
const size_t len = string.length();
if ( !::GetTextExtentPoint32(m_hdc, string.t_str(), len, &sizeRect) )
{
wxLogLastError(wxT("GetTextExtentPoint32()"));
}
// the result computed by GetTextExtentPoint32() may be too small as it
// accounts for under/overhang of the first/last character while we want
// just the bounding rect for this string so adjust the width as needed
// when using italic fonts as the difference is really noticeable for them
// (it may still exist, but seems to be at most 1px for the other fonts,
// and calling GetCharABCWidths() is pretty slow and much slower than
// calling GetTextExtentPoint32() itself, so avoid its overhead unless it's
// really, really necessary).
if ( GetFont().GetStyle() != wxFONTSTYLE_NORMAL && len > 0 )
{
ABC widthABC;
const wxChar chFirst = *string.begin();
if ( ::GetCharABCWidths(m_hdc, chFirst, chFirst, &widthABC) )
{
if ( widthABC.abcA < 0 )
sizeRect.cx -= widthABC.abcA;
if ( len > 1 )
{
const wxChar chLast = *string.rbegin();
::GetCharABCWidths(m_hdc, chLast, chLast, &widthABC);
}
//else: we already have the width of the last character
if ( widthABC.abcC < 0 )
sizeRect.cx -= widthABC.abcC;
}
//else: GetCharABCWidths() failed, not a TrueType font?
}
*width = sizeRect.cx;
*height = sizeRect.cy;
if ( descent || externalLeading )
{
TEXTMETRIC tm;
::GetTextMetrics(m_hdc, &tm);
if ( descent )
*descent = tm.tmDescent;
if ( externalLeading )
*externalLeading = tm.tmExternalLeading;
}
}
bool wxTextMeasure::DoGetPartialTextExtents(const wxString& text,
wxArrayInt& widths,
double scaleX)
{
if ( !m_hdc )
return wxTextMeasureBase::DoGetPartialTextExtents(text, widths, scaleX);
int fit = 0;
SIZE sz = {0,0};
if ( !::GetTextExtentExPoint(m_hdc,
text.t_str(), // string to check
text.length(),
INT_MAX, // max allowable width
&fit, // [out] count of chars
// that will fit
&widths[0], // array to fill
&sz) )
{
wxLogLastError(wxT("GetTextExtentExPoint"));
return false;
}
// The width of \t determined by GetTextExtentExPoint is 0. Determine the
// actual width using DoGetTextExtent and update the widths accordingly.
int offset = 0;
int tabWidth = 0;
int tabHeight = 0;
int* widthPtr = &widths[0];
for ( wxString::const_iterator i = text.begin(); i != text.end(); ++i )
{
if ( *i == '\t' )
{
if ( tabWidth == 0 )
{
DoGetTextExtent("\t", &tabWidth, &tabHeight);
}
offset += tabWidth;
}
*widthPtr++ += offset;
}
return true;
}