I'm workin at very simple program that animate shape movement. Animation can be switched to on/off mode using right mouse button. I'm creating timer in window procedure and waiting for WM_TIMER event. But after creating timer (return value of SetTimer is 1) WM_TIMER case never reaching (i'm set break point at next line to detect it).
My window procedure:
fn CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT {
PAINTSTRUCT ps;
RECT win;
GetClientRect(hWnd, &win);
int w = win.right - win.left;
int h = win.bottom - win.top;
static COLORREF shape_color = RGB(0, 255, 255);
static int x = 50, y = 50;
static int r = 40;
int step = 50;
static bool animation = false;
switch (message) {
case WM_LBUTTONDOWN:
GetColorDlg(hWnd, shape_color);
break;
case WM_RBUTTONDOWN:
animation = !animation;
if (animation) {
timer_id = SetTimer(hWnd, 1, 500, NULL);
}
else {
KillTimer(hWnd, timer_id);
}
break;
case WM_TIMER:
++x_offset;
y_offset = sin(x_offset) * 20;
case WM_KEYDOWN: {
if (!animation) {
switch (wParam) {
case VK_UP:
y -= step;
break;
case VK_DOWN:
y += step;
break;
case VK_LEFT:
x -= step;
break;
case VK_RIGHT:
x += step;
break;
}
}
y += y_offset;
x += x_offset;
x = x + r > w ? w - r : x - r < 0 ? r : x;
y = y + r > h ? h - r : y - r < 0 ? r : y;
RECT invalid_area;
invalid_area.top = y + r;
invalid_area.left = x - r;
invalid_area.bottom = y - r;
invalid_area.right = x + r;
InvalidateRect(hWnd, &invalid_area, true);
break;
}
case WM_SIZE: {
if (x + r > w)
x = w - r;
if (y + r > h)
y = h - r;
break;
}
case WM_PAINT: {
//HDC hdc = BeginPaint(hWnd, &ps);
HDC hdc = GetDC(hWnd);
HBRUSH shape_brush = CreateSolidBrush(shape_color);
HPEN shape_pen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
SelectObject(hdc, shape_pen);
SelectObject(hdc, shape_brush);
Circle(hdc, x, y, r);
DeleteObject(shape_pen);
DeleteObject(shape_brush);
DeleteObject(hdc);
ReleaseDC(hWnd, hdc);
//EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
In your WM_PAINT handler you're not calling BeginPaint / EndPaint, which means your window will always remain marked as invalid. This will generate a continuous stream of WM_PAINT messages, and this stops your timer from working because WM_TIMER messages are only generated when the message queue is empty.
See If my WM_TIMER handler takes longer than the timer period, will my queue fill up with WM_TIMER messages? for the basic algorithm that controls when WM_TIMER messages are generated.
Related
I want to create an image from the desktop and set it to gray using BITMAPINFO, but it keeps showing me the warning, "Warning C6255 _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead".
I would appreciate any advice.
HBITMAP CreateGreyscaleBitmap(int cx, int cy)
{
BITMAPINFO* pbmi = (BITMAPINFO*)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = cx;
pbmi->bmiHeader.biHeight = cy;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 8;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = 0;
pbmi->bmiHeader.biXPelsPerMeter = 14173;
pbmi->bmiHeader.biYPelsPerMeter = 14173;
pbmi->bmiHeader.biClrUsed = 0;
pbmi->bmiHeader.biClrImportant = 0;
for (int i = 0; i < 256; i++)
{
pbmi->bmiColors[i].rgbRed = i;
pbmi->bmiColors[i].rgbGreen = i;
pbmi->bmiColors[i].rgbBlue = i;
pbmi->bmiColors[i].rgbReserved = 0;
}
PVOID pv;
return CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, &pv, NULL, 0);
}
I'm using the following code in order to convert my ImageMagick image to 32-bit HBITMAP:
BITMAP bitmap;
std::memset(&bitmap, 0, sizeof(bitmap));
bitmap.bmType = 0;
bitmap.bmWidth = image->image()->columns;
bitmap.bmHeight = image->image()->rows;
bitmap.bmWidthBytes = 4 * bitmap.bmWidth;
bitmap.bmPlanes = 1;
bitmap.bmBitsPixel = 32;
bitmap.bmBits = NULL;
const size_t size = bitmap.bmWidthBytes * bitmap.bmHeight;
auto buffer = (HANDLE)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);
RGBQUAD *bitmap_bits = (RGBQUAD *) GlobalLock((HGLOBAL) buffer);
register RGBQUAD *q = bitmap_bits;
for (size_t y = 0; y < image->image()->rows; y++)
{
register auto p = GetVirtualPixels(image->image(), 0, y, image->image()->columns, 1, exception);
if (!p) break;
for (size_t x = 0; x < image->image()->columns; x++)
{
q->rgbRed = ScaleQuantumToChar(GetPixelRed(image->image(), p));
q->rgbGreen = ScaleQuantumToChar(GetPixelGreen(image->image(), p));
q->rgbBlue = ScaleQuantumToChar(GetPixelBlue(image->image(), p));
q->rgbReserved = 0;
p += GetPixelChannels(image->image());
q++;
}
}
bitmap.bmBits = bitmap_bits;
HBITMAP hbmp = CreateBitmapIndirect(&bitmap);
It works well, but I'd like to save some memory by using images with lower depth. Unfortunately I'm not even able to make it work with 24-bit images. I modified my code to look like this:
BITMAP bitmap;
std::memset(&bitmap, 0, sizeof(bitmap));
bitmap.bmType = 0;
bitmap.bmWidth = image->image()->columns;
bitmap.bmHeight = image->image()->rows;
bitmap.bmWidthBytes = ((bitmap.bmWidth * 24 + 31) / 32) * 4;
bitmap.bmPlanes = 1;
bitmap.bmBitsPixel = 24;
bitmap.bmBits = NULL;
const size_t length = bitmap.bmWidthBytes * bitmap.bmHeight;
auto buffer = (HANDLE)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, length);
RGBTRIPLE *bitmap_bits = (RGBTRIPLE *) GlobalLock((HGLOBAL) buffer);
register RGBTRIPLE *q = bitmap_bits;
for (size_t y = 0; y < image->image()->rows; y++)
{
register auto p = GetVirtualPixels(image->image(), 0, y, image->image()->columns, 1, exception);
if (!p) break;
for (size_t x = 0; x < image->image()->columns; x++)
{
q->rgbtRed = ScaleQuantumToChar(GetPixelRed(image->image(), p));
q->rgbtGreen = ScaleQuantumToChar(GetPixelGreen(image->image(), p));
q->rgbtBlue = ScaleQuantumToChar(GetPixelBlue(image->image(), p));
p += GetPixelChannels(image->image());
q++;
}
}
bitmap.bmBits = bitmap_bits;
HBITMAP hbmp = CreateBitmapIndirect(&bitmap);
But it seems that this code cannot produce valid bitmap. What am I doing wrong?
You are not taking the stride/alignment into account. Each row needs to be DWORD aligned.
Calculating Surface Stride
In an uncompressed bitmap, the stride is the number of bytes needed to go from the start of one row of pixels to the start of the next row. The image format defines a minimum stride for an image. In addition, the graphics hardware might require a larger stride for the surface that contains the image.
For uncompressed RGB formats, the minimum stride is always the image width in bytes, rounded up to the nearest DWORD. You can use the following formula to calculate the stride:
stride = ((((biWidth * biBitCount) + 31) & ~31) >> 3)
You need to fix the way you access the RGBTRIPLEs in the buffer.
Before the "x loop" you should do something like q = (RGBTRIPLE*) (((char*)bitmap_bits) + (y * bitmap.bmWidthBytes));
CreateBitmapIndirect creates a DDB which is perhaps not the best choice, create a DIB instead:
#define CalcStride(w, bpp) ( ((((w) * (bpp)) + 31) & ~31) >> 3 )
static void SetPixel24(UINT w, void*bits, UINT x, UINT y, COLORREF cr)
{
RGBTRIPLE*p = ((RGBTRIPLE*) ( ((char*)bits) + (y * CalcStride(w, 24)) )) + x;
p->rgbtRed = GetRValue(cr);
p->rgbtGreen = GetGValue(cr);
p->rgbtBlue = GetBValue(cr);
}
void Silly24BPPExample()
{
HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, WC_STATIC, 0, WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_OVERLAPPEDWINDOW|SS_BITMAP|SS_REALSIZECONTROL, 0, 0, 99, 99, 0, 0, 0, 0);
const INT w = 4, h = 4, bpp = 24;
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(bi));
BITMAPINFOHEADER&bih = bi.bmiHeader;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = w, bih.biHeight = -h;
bih.biPlanes = 1, bih.biBitCount = bpp;
bih.biCompression = BI_RGB;
void*bits;
HBITMAP hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
for (UINT x = 0; x < w; ++x)
for (UINT y = 0; y < h; ++y)
SetPixel24(w, bits, x, y, RGB(255, 0, 0)); // All red
SetPixel24(w, bits, 0, 0, RGB(0, 0, 255)); // except one blue
SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hBmp);
for (MSG msg; IsWindow(hWnd) && GetMessage(&msg, 0, 0, 0); ) DispatchMessage(&msg);
// DeleteObject(...)
}
Intro: Code does not work in Release mode. Works in Debug.
This shows on one of my breakpoints:
Code spot is:
void Font::operator<<(std::string s)
{
this->printf(s); // this line
}
This method is called like this:
float oneframe;
oneframe = (double)elapsed/ 1000000.0;
float ffps =1. / oneframe;
int fps = ffps;
char txt[200];
sprintf(txt, "%d FPS", fps);
font << txt; // displays text
Printf is:
void Font::printf(std::string s)
{
UINT lines = 0;
std::vector<float> offsetX;
float offsetY=0;
UINT length = s.size();
XMMATRIX M = XMMatrixScaling(m_scaling.x, m_scaling.y, m_scaling.z)*
XMMatrixTranslation(m_translation.x, m_translation.y, m_translation.z);
float fontLength=0;
float fontHeight = 60.0f / windowHeight;
float fontWidth = 60.0f / windowWidth * 0.6f;
m_deviceContext->VSSetShader(m_vertexShader, 0, 0);
m_deviceContext->IASetInputLayout(m_inputLayout);
m_deviceContext->PSSetShader(m_pixelShader, 0, 0);
m_deviceContext->PSSetShaderResources(0, 1, &m_texture);
m_deviceContext->PSSetSamplers(0, 1, &m_sampler);
m_deviceContext->OMSetDepthStencilState(m_dsOff, 1);
if (m_anchor != TOP_LEFT)
{
float offset = 0;
for (int i = 0; i < length; i++)
{
offset += m_kerning*widthMap[s[i]];
if (s[i] == '\n' || s[i] == '\r' || i == length - 1)
{
offsetX.push_back(offset);
offset = 0;
}
}
}
for (int i = 0; i < length; i++)
{
XMFLOAT3 TL(-1, 1, 0), BR(1, -1, 0);
XMVECTOR vTL, vBR;
if (s[i] == '\n' || s[i] == '\r')
{
fontLength = 0;
lines++;
continue;
}
switch (m_anchor)
{
default:
case TOP_LEFT:
vTL = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontLength, -m_leading*lines, 0)), M);
vBR = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontWidth + fontLength, -m_leading*lines - fontHeight, 0)), M);
break;
case TOP_RIGHT:
vTL = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontLength - offsetX[lines], -m_leading*lines, 0)), M);
vBR = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontWidth + fontLength - offsetX[lines], -m_leading*lines - fontHeight, 0)), M);
break;
case BOTTOM_LEFT:
offsetY = m_leading*offsetX.size();
vTL = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontLength, -m_leading*lines+ offsetY, 0)), M);
vBR = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontWidth + fontLength, -m_leading*lines - fontHeight+ offsetY, 0)), M);
break;
case BOTTOM_RIGHT:
offsetY = m_leading*offsetX.size();
vTL = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontLength - offsetX[lines], -m_leading*lines + offsetY, 0)), M);
vBR = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontWidth + fontLength - offsetX[lines], -m_leading*lines - fontHeight + offsetY, 0)), M);
break;
case CENTER:
{
offsetY = m_leading*offsetX.size() / 2;
float halfOffsetx = offsetX[lines] / 2;
vTL = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontLength - halfOffsetx, -m_leading*lines + offsetY, 0)), M);
vBR = XMVector3TransformCoord(XMLoadFloat3(&XMFLOAT3(fontWidth + fontLength - halfOffsetx, -m_leading*lines - fontHeight + offsetY, 0)), M);
break;
}
}
XMStoreFloat3(&TL, vTL);
XMStoreFloat3(&BR, vBR);
assert(updateBuffer(TL, BR, fontMap[s[i]]));
UINT stride, offset;
stride = sizeof(SimpleVertex);
offset = 0;
m_deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
m_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_deviceContext->Draw(6, 0);
fontLength += m_kerning*widthMap[s[i]];
}
m_deviceContext->OMSetDepthStencilState(m_dsOn, 1);
}
Here's where it gets weird. I get this notification while in Release mode. The code above completely works, in Debug. For whatever reason, it does not work when I switch to Release. I've checked the txtvariable that is being sent to the function, it has text. To me, it seems as if the code has somehow been optimized out or something.
I have also deleted everything in the Debug and Release folders and did a fresh Build. Nothing. I've tested the executables without running from VS, same result. Debugging in Release shows that there is text in txt but I can't determine what happens as the debugger seems to float right over font << txt.
I did find out the answer. There is an assert used in printf near the end. Asserts do not run in Release. Only in debug.
After i draw many circle i want handle collisions between a circle but i don't know how to do it . And I want the color of the circle is random . I use function RGB() but it doesn't work . Please help me ! . Thanks .
My code
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
HBRUSH brush;
HBRUSH hBrush;
HPEN hPen;
static int dX[10] , dY[10] ;
static int x[10], y[10], oldX[10], oldY[10];
switch (message)
{
case WM_CREATE:
SetTimer(hWnd, 1, 3, NULL);
Beep(750, 300);
for (int i = 0; i < 10; i++){
dX[i] = rand() % 15 + 0;
dY[i] = rand() % 15 + 0;
x[i] = rand() % 15 + 0;
y[i] = rand() % 15 + 0;
oldX[i] = x[i];
oldY[i] = y[i];
}
break;
case WM_TIMER:
hdc = GetDC(hWnd);
brush = (HBRUSH)SelectObject(hdc, GetStockObject(WHITE_BRUSH));
RECT temp[10];
RECT rect;
GetClientRect(hWnd, &rect);
brush = (HBRUSH)SelectObject(hdc, GetStockObject(RGB(rand() % 255 + 0, rand() % 255 + 0, rand() % 255 + 0)));
for (int i = 0; i <10; i++){
temp[i].left = oldX[i];
temp[i].top = oldY[i];
temp[i].right = oldX[i] + 30;
temp[i].bottom = oldY[i] + 30;
FillRect(hdc, &temp[i], brush);
Ellipse(hdc, x[i], y[i], 30 + x[i], 30 + y[i]);
oldX[i] = x[i];
oldY[i] = y[i];
x[i] += dX[i];
y[i] += dY[i];
if (x[i] + 30 > rect.right || x[i] < 0)
{
dX[i] = -dX[i];
}
if (y[i] + 30 > rect.bottom || y[i] < 0)
{
dY[i] = -dY[i];
}
}
SelectObject(hdc, brush);
ReleaseDC(hWnd, hdc);
break;
My picture Demo here .
http://www.upsieutoc.com/images/2014/07/14/289ccd.jpg
The question is simple. How to draw following text into TStringGrid cell?
Operating system is Windows XP (or Windows Vista or Windows 7). Preferred development environment is C++ Builder 6, but I will accept also solutions for C++ Builder XE of Delphi. Preferred API function is DrawText, but if better function exists than this no problem. Font name is Times New Roman, font size is 11. Currently I am using this method to render cell content (simplified):
void __fastcall TForm_Main::StringGrid_DrawCell(TObject *Sender,
int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
TStringGrid *grid = (TStringGrid*)Sender;
if (grid == NULL) return;
// 1. BACKGROUND
grid->Canvas->Brush->Color = grid->Color;
grid->Canvas->FillRect(Rect);
// 2. TEXT
grid->Canvas->Font->Assign(grid->Font); // Times New Roman, 11pt
// horizontal centering
RECT RText = static_cast<RECT>(Rect);
AnsiString text = grid->Cells[ACol][ARow];
if (!text.IsEmpty()) {
int text_len = strlen(text.c_str());
SIZE size;
memset(&size, 0, sizeof(SIZE));
GetTextExtentPoint32(grid->Canvas->Handle, text.c_str(), text_len, &size);
int offset_x = (Rect.Width() - size.cx) >> 1;
RText.left += offset_x; RText.right += offset_x;
// rendering
DrawText(grid->Canvas->Handle, text.c_str(), text_len, &RText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}
}
Some characters have subscript glyph as special unicode character (for example code 0x2081 is assigned to subscript one) but unfortunately this is not case for capital letter U. Also these characters are not supported by all fonts (for example Times New Roman font doesnt support code range 0x2070 - 209F), see this Wikipedia article. I am searching for a more general solution like those implemented by Microsoft Word. MS Word doesn't have problem to render capital letter U as subscript using Times New Roman font.
If you want some char to render as superscript you must prefix it with ^. Likewise subscript characters must be prefixed with _.
void __fastcall TForm_Main::StringGrid_DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
TStringGrid *grid = (TStringGrid*)Sender;
if (grid == NULL)
{
return;
}
WideString wtext = L"φ_U = 120";
if (wtext.IsEmpty()) return;
// layout
SIZE size;
memset(&size, 0, sizeof(SIZE));
SSGetTextExtentPoint(grid->Canvas, wtext, size);
int offset_x = (Rect.Width() - size.cx + 1) >> 1; // horizontal centering
RECT RText = static_cast<RECT>(Rect);
RText.left += offset_x;
RText.right += offset_x;
// rendering
SetBkMode(grid->Canvas->Handle, TRANSPARENT);
SSDrawText(grid->Canvas, wtext, RText, DT_LEFT);
}
int TForm_Main::SSGetTextExtentPoint(TCanvas *canvas, WideString text, SIZE &size)
{
// Source: http://www.codeproject.com/Articles/12660/Using-Subscripts-and-Superscripts-When-Showing-Tex
SaveDC(canvas->Handle);
SIZE sz;
RECT outRect =
{0, 0, 0, 0};
HFONT oldFont;
LOGFONT lf;
GetObject(canvas->Font->Handle, sizeof(LOGFONT), &lf);
POINT sub, sup, subofs, supofs;
// Calculate subscript/superscript size and offsets
bool use_pixel_unit = false;
if (lf.lfHeight < 0)
{
lf.lfHeight = MulDiv(-lf.lfHeight, 72, GetDeviceCaps(canvas->Handle, LOGPIXELSY));
use_pixel_unit = true;
}
sub.x = lf.lfWidth / 2;
sup.x = lf.lfWidth / 2;
sub.y = lf.lfHeight / 3 * 2;
sup.y = lf.lfHeight / 3 * 2;
subofs.x = lf.lfWidth / 2;
supofs.x = lf.lfWidth / 2;
subofs.y = lf.lfHeight / 6;
supofs.y = lf.lfHeight / 3;
lf.lfWidth = sub.x;
lf.lfHeight = sub.y;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
HFONT SubFont;
SubFont = CreateFontIndirect(&lf);
lf.lfWidth = sup.x;
lf.lfHeight = sup.y;
HFONT SupFont;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
SupFont = CreateFontIndirect(&lf);
WideString temp = text;
TCHAR c;
// Calculate the size of the text that needs to be displayed
do
{
int x1 = 1, x2 = 1, x = 1;
WideString s = "";
c = ' ';
bool bFind = true;
// Find the first "^" or "_", indicating the sub- or superscript
while (bFind)
{
x1 = text.Pos(L"^");
x2 = text.Pos(L"_");
if ((x1 == 0) && (x2 == 0))
{
x = 0;
}
else if ((x1 > 0) && (x2 > 0))
{
x = min(x1, x2);
}
else if (x1 > 0)
{
x = x1;
}
else
{
x = x2;
}
if (x == 0)
{
bFind = false;
x = text.Length() + 1;
}
else if (x == text.Length())
{
bFind = false;
}
else if (text[x] != text[x + 1])
{
bFind = false;
c = text[x];
}
else
{
x++;
}
s = s + text.SubString(1, x - 1);
text.Delete(1, min(x, text.Length()));
}
sz = canvas->TextExtent(s);
outRect.right += sz.cx;
if ((outRect.bottom - outRect.top) < sz.cy)
{
outRect.top = outRect.bottom - sz.cy;
}
switch (c)
{
case '^':
oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + supofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
case '_':
oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + subofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
}
}
while (c != ' ');
// Adjust text position
outRect.bottom += subofs.y;
outRect.top -= subofs.x;
size.cx = outRect.right - outRect.left;
size.cy = outRect.bottom - outRect.top;
DeleteObject(SubFont);
DeleteObject(SupFont);
// Done, restoring the device context
RestoreDC(canvas->Handle, -1);
return 0;
}
// ---------------------------------------------------------------------------
int TForm_Main::SSDrawText(TCanvas *canvas, WideString text, RECT &drawRect, int justification)
{
// Source: http://www.codeproject.com/Articles/12660/Using-Subscripts-and-Superscripts-When-Showing-Tex
SaveDC(canvas->Handle);
SIZE sz;
RECT outRect =
{0, 0, 0, 0};
HFONT oldFont;
LOGFONT lf;
GetObject(canvas->Font->Handle, sizeof(LOGFONT), &lf);
POINT sub, sup, subofs, supofs;
bool contains_subscript = false;
bool contains_superscript = false;
// Calculate subscript/superscript size and offsets
bool use_pixel_unit = false;
if (lf.lfHeight < 0)
{
lf.lfHeight = MulDiv(-lf.lfHeight, 72, GetDeviceCaps(canvas->Handle, LOGPIXELSY));
use_pixel_unit = true;
}
sub.x = (lf.lfWidth + 1) >> 1;
sup.x = (lf.lfWidth + 1) >> 1;
sub.y = (lf.lfHeight << 1) / 3;
sup.y = (lf.lfHeight << 1) / 3;
if (lf.lfHeight == 10)
{
sub.y++; // make subscript a little larger
}
subofs.x = (lf.lfWidth + 1) >> 1;
supofs.x = (lf.lfWidth + 1) >> 1;
subofs.y = (lf.lfHeight + 3) / 6;
supofs.y = (lf.lfHeight) / 3;
long sub_shift_down = lf.lfHeight - sub.y;
lf.lfWidth = sub.x;
lf.lfHeight = sub.y;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
HFONT SubFont;
SubFont = CreateFontIndirect(&lf);
lf.lfWidth = sup.x;
lf.lfHeight = sup.y;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
HFONT SupFont;
SupFont = CreateFontIndirect(&lf);
WideString temp = text;
TCHAR c;
// Calculate the size of the text that needs to be displayed
do
{
int x1 = 1, x2 = 1, x = 1;
WideString s = "";
c = ' ';
bool bFind = true;
// Find the first "^" or "_", indicating the sub- or superscript
while (bFind)
{
x1 = text.Pos(L"^");
x2 = text.Pos(L"_");
if ((x1 == 0) && (x2 == 0))
{
x = 0;
}
else if ((x1 > 0) && (x2 > 0))
{
x = min(x1, x2);
}
else if (x1 > 0)
{
x = x1;
}
else
{
x = x2;
}
if (x == 0)
{
bFind = false;
x = text.Length() + 1;
}
else if (x == text.Length())
{
bFind = false;
}
else if (text[x] != text[x + 1])
{
bFind = false;
c = text[x];
}
else
{
x++;
}
s = s + text.SubString(1, x - 1);
text.Delete(1, min(x, text.Length()));
}
sz = canvas->TextExtent(s);
outRect.right += sz.cx;
if ((outRect.bottom - outRect.top) < sz.cy)
{
outRect.top = outRect.bottom - sz.cy;
}
switch (c)
{
case '^':
oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + supofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
contains_superscript = true;
break;
case '_':
oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + subofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
contains_subscript = true;
break;
}
}
while (c != ' ');
// Adjust text position
if (contains_subscript)
{
outRect.bottom += subofs.y;
}
if (contains_superscript)
{
outRect.top -= supofs.y;
}
POINT Origin;
Origin.y = drawRect.top + (((drawRect.bottom - drawRect.top) - (outRect.bottom - outRect.top) + 1) >> 1);
switch (justification)
{
case DT_CENTER:
Origin.x = (drawRect.right - drawRect.left) / 2 - (outRect.right - outRect.left) / 2 + drawRect.left;
break;
case DT_LEFT:
Origin.x = drawRect.left;
break;
case DT_RIGHT:
Origin.x = drawRect.right - (outRect.right - outRect.left);
}
POINT pnt = Origin;
text = temp;
// Draw text
do
{
int x1 = 1, x2 = 1, x = 1;
WideString s = "";
c = ' ';
bool bFind = true;
// Find the first "^" or "_", indicating the sub- or superscript
while (bFind)
{
x = text.Pos(L"^_");
x1 = text.Pos(L"^");
x2 = text.Pos(L"_");
if ((x1 == 0) && (x2 == 0))
{
x = 0;
}
else if ((x1 > 0) && (x2 > 0))
{
x = min(x1, x2);
}
else if (x1 > 0)
{
x = x1;
}
else
{
x = x2;
}
if (x == 0)
{
bFind = false;
x = text.Length() + 1;
}
else if (x == text.Length())
{
bFind = false;
}
else if (text[x] != text[x + 1])
{
bFind = false;
c = text[x];
}
else
{
x++;
}
s = s + text.SubString(1, x - 1);
text.Delete(1, min(x, text.Length()));
}
// Draw main text
ExtTextOutW(canvas->Handle, pnt.x, pnt.y, 0, &drawRect, s.c_bstr(), s.Length(), NULL);
GetTextExtentPoint32W(canvas->Handle, s.c_bstr(), s.Length(), &sz);
pnt.x += sz.cx;
// Draw subscript or superscript
switch (c)
{
case '^':
oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
ExtTextOutW(canvas->Handle, pnt.x + supofs.x, pnt.y - supofs.y, 0, &drawRect, text.c_bstr(), 1, NULL);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
pnt.x += sz.cx + supofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
case '_':
oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
ExtTextOutW(canvas->Handle, pnt.x + subofs.x, pnt.y + subofs.y + sub_shift_down, 0, &drawRect, text.c_bstr(), 1, NULL);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
pnt.x += sz.cx + subofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
}
}
while (c != ' ');
DeleteObject(SubFont);
DeleteObject(SupFont);
// Done, restoring the device context
RestoreDC(canvas->Handle, -1);
return 0;
}
`
function SSTextLeft(ACanvas: TCanvas; ARect: TRect; const S: string): integer;
var
i,
h,
h3: integer;
sup,
sub: boolean;
begin
with ACanvas, ARect do begin
h:= textHeight('H');
h3:= h div 3;
brush.Style:= bsClear;
moveTo(Left +1, Top +1);
i:= 1;
while i < Length(s) +1 do begin
if s[i] = '^' then
begin
Inc(i);
font.Height:= MulDiv(font.Height, 8, 10);
textOut(penPos.X, penPos.Y -h3, s[i]);
moveTo(penPos.X +1, penPos.Y +h3);
font.Height:= h;
Inc(i);
end
else if s[i] = '_' then
begin
Inc(i);
font.Height:= MulDiv(font.Height, 8, 10);
textOut(penPos.X, penPos.Y +h3, s[i]);
moveTo(penPos.X +1, penPos.Y -h3);
font.Height:= h;
Inc(i);
end
else
begin
textOut(penPos.X, penPos.Y, s[i]);
Inc(i);
moveTo(penPos.X +1, penPos.Y);
end;
end;// while
end;
sup:= Pos('^', S) > 0;
sub:= Pos('_', S) > 0;
if sup and sub then
result:= MulDiv(h, 5, 3)
else if sup or sub then
result:= h +h3
else
result:= h;
end;// SSTextLeft
`