gdi32: GetGlyphIndices doesn't substitute glyph incorrect

Aric Stewart aric at codeweavers.com
Wed May 15 07:35:04 CDT 2013


Hello Kusanagi-san,

On 5/15/13 6:03 AM, Kusanagi Kouichi wrote:
> On 2013-05-10 09:23:17 -0500, Aric Stewart wrote:
>> Hi there,
>>
>> I know I am a year late to the party here but I am digging deep into tategaki for a few Japanese programs that are having serious rendering issues and I believe that this patch is incorrect.
> 
> Can you explain the problem? How do I reproduce it? I know that
> tategaki has a minor issue, but it's not serious. Does reverting
> commit fecb1d8b6f81c2bfff7116e80380b1d4f7b7befc ("gdi32:
> GetGlyphIndices doesn't substitute glyph.") help?
> 

Just reverting the changes to GetGlyphIndices causes your original tests to fail. so I will have to investigate why and try to correct that. Did you have a specific test case that required this change?

>> I am attaching the test patch that proves it to me. Kusanagi-san, we should try to make sure that this does not break what it was that you where trying to fix with your original patch.
> 
> Your test passed here.
> 
>> -aric


I also thought that tategaki was mostly working, but I started to investigate and have found quite a number of serious issues.

I have attached my test program as well as png screen shots from win7 and wine to illustrate the issues I am trying to solve. Most interesting to me are the rotated glyph origin problem as well as the fact that @fonts rotate many of the characters by 90 degrees.

I have some work in progress that is fixing the origin, the incorrect GSUB substitution when using glyph indices and also the rotation.

I am finding the logic on how windows decides which glyphs get the 90 degree rotation and which do not very hard to figure out. Do you have any understanding on how that works?

thanks!
-aric
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tate-win7.png
Type: image/png
Size: 249098 bytes
Desc: not available
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20130515/312a84b0/attachment-0002.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tate-wine.png
Type: image/png
Size: 107407 bytes
Desc: not available
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20130515/312a84b0/attachment-0003.png>
-------------- next part --------------
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

HFONT font = NULL;
HFONT font2 = NULL;
HFONT font3 = NULL;
HFONT font4 = NULL;

HFONT fonta = NULL;

//static const char fontname[] = "@Ume UI Gothic";
//static const char fontname2[] = "Ume UI Gothic";
static const char fontname[] = "@MS UI Gothic";
static const char fontname2[] = "MS UI Gothic";

#define HEIGHT -46

static HFONT createTateFont(int rotation, int height)
{
    LOGFONTA lf;
    HFONT font;

    memset(&lf,0,sizeof(LOGFONTA));
    lf.lfCharSet = 128;
    lf.lfEscapement = rotation;
    lf.lfOrientation = rotation;
    strcpy(lf.lfFaceName,fontname);
    lf.lfHeight = height;
    lf.lfPitchAndFamily = 30;
    lf.lfOutPrecision = 4;
    font = CreateFontIndirectA(&lf);

    return font;
}

static LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE:
        {
            LOGFONTA lf;
            memset(&lf,0,sizeof(LOGFONTA));
            lf.lfCharSet = 128;
            lf.lfEscapement = 2700;
            lf.lfOrientation = 2700;
            strcpy(lf.lfFaceName,fontname);
            lf.lfHeight = HEIGHT;
            lf.lfPitchAndFamily = 30;
            lf.lfOutPrecision = 4;
            font = CreateFontIndirectA(&lf);

            memset(&lf,0,sizeof(LOGFONTA));
            lf.lfCharSet = 128;
            lf.lfEscapement = 2700;
            lf.lfOrientation = 2700;
            strcpy(lf.lfFaceName,fontname2);
            lf.lfHeight = HEIGHT;
            lf.lfPitchAndFamily = 30;
            lf.lfOutPrecision = 4;
            font2 = CreateFontIndirectA(&lf);

            memset(&lf,0,sizeof(LOGFONTA));
            lf.lfCharSet = 128;
            lf.lfEscapement = 0;
            lf.lfOrientation = 0;
            strcpy(lf.lfFaceName,fontname2);
            lf.lfHeight = HEIGHT;
            lf.lfPitchAndFamily = 30;
            lf.lfOutPrecision = 4;
            font3 = CreateFontIndirectA(&lf);

            memset(&lf,0,sizeof(LOGFONTA));
            lf.lfCharSet = 128;
            lf.lfEscapement = 0;
            lf.lfOrientation = 0;
            strcpy(lf.lfFaceName,fontname);
            lf.lfHeight = HEIGHT;
            lf.lfPitchAndFamily = 30;
            lf.lfOutPrecision = 4;
            font4 = CreateFontIndirectA(&lf);

            fonta = createTateFont(0,24);

            return 0;
        }
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HPEN hLinePen;
            COLORREF qLineColor;
            HPEN hPenOld;
            HFONT oldfont;
            RECT rect = {0,0,800,800};
            WCHAR str[] = {0x3300, 0xff5e,0x3042,0x3044,0xff31,'A',0};
            GLYPHMETRICS metrics;
            static const MAT2 mat = { {0,1},{0,0},{0,0},{0,1} };
            char mtx[2550];
            unsigned char buffer[10023];
            WORD index1[6],index2[6];

            FILE *f = fopen("./out","w");
            char output[300];
            int i,j;
            unsigned char* line;
            int pitch;
            int base_p = 40;


            HDC hdc = BeginPaint(hWnd, &ps);
            SetBkMode(hdc,OPAQUE);
            oldfont = (HFONT)SelectObject(hdc,font);

            qLineColor = RGB(255, 0, 0);
            hLinePen = CreatePen(PS_SOLID, 1, qLineColor);
            hPenOld = (HPEN)SelectObject(hdc, hLinePen);

            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, 600, base_p);
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, base_p, 600);
            ExtTextOutW(hdc, base_p, base_p, 0, &rect, str, 6, NULL);

            GetGlyphOutlineW(hdc, str[0], GGO_BITMAP, &metrics, sizeof(buffer), buffer,  &mat);
            sprintf(mtx,"%i,%i,  (%i,%i), %i,%i",
                metrics.gmBlackBoxX,
                metrics.gmBlackBoxY,
                metrics.gmptGlyphOrigin.x,
                metrics.gmptGlyphOrigin.y,
                metrics.gmCellIncX,
                metrics.gmCellIncY);

            //ExtTextOutA(hdc, base_p, base_p, 0, &rect, mtx, strlen(mtx), NULL);
            fprintf(f,"rotated '@': %s\n",mtx);
            pitch = ((metrics.gmBlackBoxX+ 31) / 32) * 4;
            fprintf(f,"----------------------------------------\n");
            for(i = 0; i < metrics.gmBlackBoxY; i++) {
                line = (unsigned char*) buffer + i * pitch;
                output[0] = '\0';
                for(j = 0; j < pitch * 8; j++) {
                        strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
                }
                fprintf(f,"%s\n", output);
                }
            fprintf(f,"----------------------------------------\n");


            SelectObject(hdc,font2);

            base_p += 60;
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, 600, base_p);
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, base_p, 600);

            ExtTextOutW(hdc, base_p, base_p, 0, &rect, str, 6, NULL);
            GetGlyphOutlineW(hdc, str[0], GGO_BITMAP, &metrics, sizeof(buffer), buffer,  &mat);
            sprintf(mtx,"%i,%i,  (%i,%i), %i,%i",
                metrics.gmBlackBoxX,
                metrics.gmBlackBoxY,
                metrics.gmptGlyphOrigin.x,
                metrics.gmptGlyphOrigin.y,
                metrics.gmCellIncX,
                metrics.gmCellIncY);

            //ExtTextOutA(hdc, base_p, base_p, 0, &rect, mtx, strlen(mtx), NULL);
            fprintf(f,"rotated no '@': %s\n",mtx);
            fprintf(f,"----------------------------------------\n");
            for(i = 0; i < metrics.gmBlackBoxY; i++) {
                line = (unsigned char*) buffer + i * pitch;
                output[0] = '\0';
                for(j = 0; j < pitch * 8; j++) {
                        strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
                }
                fprintf(f,"%s\n", output);
                }
            fprintf(f,"----------------------------------------\n");
            base_p += 10;

            SelectObject(hdc,font3);
            GetGlyphIndicesW(hdc, str, 6, index1, 0);
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, 600, base_p);
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, base_p, 600);
rect.left = 0;
rect.top = 160;
            ExtTextOutW(hdc, base_p, base_p, 0, &rect, str, 6, NULL);
            GetGlyphOutlineW(hdc, str[0], GGO_BITMAP, &metrics, sizeof(buffer), buffer,  &mat);
            sprintf(mtx,"%i,%i,  (%i,%i), %i,%i",
                metrics.gmBlackBoxX,
                metrics.gmBlackBoxY,
                metrics.gmptGlyphOrigin.x,
                metrics.gmptGlyphOrigin.y,
                metrics.gmCellIncX,
                metrics.gmCellIncY);

            //ExtTextOutA(hdc, base_p, base_p, 0, &rect, mtx, strlen(mtx), NULL);
            fprintf(f,"unrotated no '@' index (%x): %s\n",index1[0], mtx);
            fprintf(f,"----------------------------------------\n");
            for(i = 0; i < metrics.gmBlackBoxY; i++) {
                line = (unsigned char*) buffer + i * pitch;
                output[0] = '\0';
                for(j = 0; j < pitch * 8; j++) {
                        strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
                }
                fprintf(f,"%s\n", output);
                }
            fprintf(f,"----------------------------------------\n");

            SelectObject(hdc,font4);
            GetGlyphIndicesW(hdc, str, 6, index2, 0);
            base_p += 60;
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, 600, base_p);
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, base_p, 600);
            ExtTextOutW(hdc, base_p, base_p, 0, &rect, str, 6, NULL);
            GetGlyphOutlineW(hdc, str[0], GGO_BITMAP, &metrics, sizeof(buffer), buffer,  &mat);
            sprintf(mtx,"%i,%i,  (%i,%i), %i,%i",
                metrics.gmBlackBoxX,
                metrics.gmBlackBoxY,
                metrics.gmptGlyphOrigin.x,
                metrics.gmptGlyphOrigin.y,
                metrics.gmCellIncX,
                metrics.gmCellIncY);

//            ExtTextOutA(hdc, base_p, base_p, 0, &rect, mtx, strlen(mtx), NULL);
            fprintf(f,"unrotated '@': %s\n",mtx);

            pitch = ((metrics.gmBlackBoxX+ 31) / 32) * 4;
            fprintf(f,"----------------------------------------\n");
            for(i = 0; i < metrics.gmBlackBoxY; i++) {
                line = (unsigned char*) buffer + i * pitch;
                output[0] = '\0';
                for(j = 0; j < pitch * 8; j++) {
                        strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
                }
                fprintf(f,"%s\n", output);
                }
            fprintf(f,"----------------------------------------\n");
            fprintf(f,"\n");

            base_p += 60;
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, 600, base_p);
            MoveToEx(hdc, base_p, base_p, NULL);
            LineTo(hdc, base_p, 600);
            ExtTextOutW(hdc, base_p, base_p, ETO_GLYPH_INDEX, &rect, index1, 6, NULL);
            GetGlyphOutlineW(hdc, index1[0], GGO_BITMAP|GGO_GLYPH_INDEX, &metrics, sizeof(buffer), buffer,  &mat);
            sprintf(mtx,"%i,%i,  (%i,%i), %i,%i",
                metrics.gmBlackBoxX,
                metrics.gmBlackBoxY,
                metrics.gmptGlyphOrigin.x,
                metrics.gmptGlyphOrigin.y,
                metrics.gmCellIncX,
                metrics.gmCellIncY);
            fprintf(f,"unrotated '@' by index (%x): %s\n",index1[0],mtx);
            pitch = ((metrics.gmBlackBoxX+ 31) / 32) * 4;
            fprintf(f,"----------------------------------------\n");
            for(i = 0; i < metrics.gmBlackBoxY; i++) {
                line = (unsigned char*) buffer + i * pitch;
                output[0] = '\0';
                for(j = 0; j < pitch * 8; j++) {
                        strcat(output, (line[j / 8] & (1 << (7 - (j % 8)))) ? "#" : " ");
                }
                fprintf(f,"%s\n", output);
                }
            fprintf(f,"----------------------------------------\n");

            base_p += 100;
            SetBkMode(hdc,TRANSPARENT);
            for (i = 0; i < 3600; i+=450)
            {
                HFONT teh_font;
                teh_font = createTateFont(i, HEIGHT);

                SelectObject(hdc,teh_font);
                MoveToEx(hdc, base_p+300, base_p, NULL);
                LineTo(hdc, 700, base_p);
                MoveToEx(hdc, base_p+300, base_p, NULL);
                LineTo(hdc, base_p+300, 700);
                ExtTextOutW(hdc, base_p+300, base_p, 0, &rect, str, 6, NULL);
                SelectObject(hdc,oldfont);
                DeleteObject(teh_font);
            }

            SelectObject(hdc, hPenOld);
            DeleteObject(hLinePen);
            SelectObject(hdc, oldfont);

            EndPaint(hWnd, &ps);
            fclose(f);
            return 0;
        }
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProcA(hWnd, msg, wParam, lParam);
}

static LRESULT WINAPI MainProc2( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static int ix = 0x2010;
    static int xxx = 0x5517;

    switch (msg)
    {
        case WM_CREATE:
        {
            return 0;
        }
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HPEN hLinePen;
            COLORREF qLineColor;
            HPEN hPenOld;
            HFONT oldfont;
            RECT rect = {0,0,800,800};
            WCHAR str;
            GLYPHMETRICS metrics;
            static const MAT2 mat = { {0,1},{0,0},{0,0},{0,1} };
            char mtx[2550];
            unsigned char buffer[10023];
            WORD index1[5],index2[5];

            char output[300];
            int i,j;
            unsigned char* line;
            int pitch;
            int base_x = 40;
            int base_y = 40;
            int idx;

            HDC hdc = BeginPaint(hWnd, &ps);
            SetBkMode(hdc,TRANSPARENT);
            oldfont = (HFONT)SelectObject(hdc,fonta);

            GetClientRect(hWnd,&rect);

            idx = 0;
            index2[0] = 0;
            index2[1] = 0;
            while (base_y < rect.bottom)
            {
                str = ix + idx;
                GetGlyphIndicesW(hdc, &str, 1, index1, 0);
                //index1[0] = xxx;
                if (index2[0] == 0 && index1[0] != 0) index2[0] = index1[0];
                if (index1[0] != 0) index2[1] = index1[0];

                ExtTextOutW(hdc, base_x, base_y, ETO_GLYPH_INDEX, &rect, index1, 1, NULL);

                GetGlyphOutlineW(hdc, (WCHAR)index1[0], GGO_BITMAP|GGO_GLYPH_INDEX, &metrics, sizeof(buffer), buffer,  &mat);

                base_x += metrics.gmBlackBoxX+5;
                if (base_x >= rect.right)
                {
                    base_x = 40;
                    base_y += 40;
                }
                idx++;
            }
            str = idx;
            sprintf(mtx,"%x-%x %x=%x\n",ix,ix+idx,index2[0],index2[1]);
            SetWindowText(hWnd,mtx);

            SelectObject(hdc, hPenOld);
            DeleteObject(hLinePen);
            SelectObject(hdc, oldfont);

            EndPaint(hWnd, &ps);
            return 0;
        }
        case WM_KEYDOWN: ix +=200; InvalidateRect(hWnd,NULL,TRUE); break;
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProcA(hWnd, msg, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow)
{
    WNDCLASSA wc;
    HWND hWnd, hWnd2;
    MSG msg;

    wc.style = 0;
    wc.lpfnWndProc = MainProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor( 0, IDI_APPLICATION );
    wc.hbrBackground = (HBRUSH)GetStockObject( GRAY_BRUSH );
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "TATE";

    RegisterClass(&wc);

    wc.style = 0;
    wc.lpfnWndProc = MainProc2;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor( 0, IDI_APPLICATION );
    wc.hbrBackground = (HBRUSH)GetStockObject( GRAY_BRUSH );
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "TATE2";

    RegisterClass(&wc);

    hWnd = CreateWindowA("TATE","Test",WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        0, 0, hInst, NULL );

    hWnd2 = CreateWindowA("TATE2","Test",WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        0, 0, hInst, NULL );

    ShowWindow(hWnd, SW_NORMAL);
    UpdateWindow(hWnd);
    ShowWindow(hWnd2, SW_NORMAL);
    UpdateWindow(hWnd2);


    while( GetMessageA(&msg, 0, 0, 0) )
    {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }

    return 0;
}


More information about the wine-devel mailing list