James Tabor wrote:
Hi,
I finished a fast port of GetGlyphOutline from Wine. Not too bad, no
crashes.
Thanks,
James
Sorry here is the patch,
James
--- /rmt/ros/rossavold/reactos/subsys/win32k/objects/text.c 2006-01-27 20:26:03.000000000
+0000
+++ ./text.c 2006-01-31 07:22:58.000000000 +0000
@@ -29,6 +29,9 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/tttables.h>
+#include <freetype/fttrigon.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftoutln.h>
#define NDEBUG
#include <debug.h>
@@ -2117,6 +2120,44 @@
return 0;
}
+static
+void
+FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
+{
+ pt->x.value = vec->x >> 6;
+ pt->x.fract = (vec->x & 0x3f) << 10;
+ pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
+ pt->y.value = vec->y >> 6;
+ pt->y.fract = (vec->y & 0x3f) << 10;
+ pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
+ return;
+}
+
+/*
+ This function builds an FT_Fixed from a float. It puts the integer part
+ in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
+ It fails if the integer part of the float number is greater than SHORT_MAX.
+*/
+static inline FT_Fixed FT_FixedFromFloat(float f)
+{
+ short value = f;
+ unsigned short fract = (f - value) * 0xFFFF;
+ return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
+}
+
+/*
+ This function builds an FT_Fixed from a FIXED. It simply put f.value
+ in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
+*/
+static inline FT_Fixed FT_FixedFromFIXED(FIXED f)
+{
+ return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
+}
+
+/*
+ * Based on WineEngGetGlyphOutline
+ *
+ */
ULONG
APIENTRY
NtGdiGetGlyphOutline(
@@ -2129,8 +2170,565 @@
IN LPMAT2 pmat2,
IN BOOL bIgnoreRotation)
{
- UNIMPLEMENTED;
- return 0;
+ static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
+ PDC dc;
+ PTEXTOBJ TextObj;
+ PFONTGDI FontGDI;
+ HFONT hFont = 0;
+ NTSTATUS Status;
+ GLYPHMETRICS gm;
+ PVOID Buffer;
+ ULONG Size;
+ FT_Face ft_face;
+ FT_UInt glyph_index;
+ DWORD width, height, pitch, needed = 0;
+ FT_Bitmap ft_bitmap;
+ FT_Error error;
+ INT left, right, top = 0, bottom = 0;
+ FT_Angle angle = 0;
+ FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+ FLOAT eM11, widthRatio = 1.0;
+ FT_Matrix transMat = identityMat;
+ BOOL needsTransform = FALSE;
+ INT orientation;
+ LONG aveWidth;
+ INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
+ OUTLINETEXTMETRICW *potm;
+
+ DPRINT1("%p, %04x, %08x, %p, %08lx, %p, %p\n", hdc, wch, iFormat, pgm,
+ cjBuf, pvBuf, pmat2);
+
+ dc = DC_LockDc(hdc);
+ if (dc == NULL)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+ eM11 = dc->w.xformWorld2Vport.eM11;
+ hFont = dc->w.hFont;
+ TextObj = TEXTOBJ_LockText(hFont);
+ DC_UnlockDc(dc);
+ if (TextObj == NULL)
+ {
+ SetLastWin32Error(ERROR_INVALID_HANDLE);
+ return GDI_ERROR;
+ }
+ FontGDI = ObjToGDI(TextObj->Font, FONT);
+ ft_face = FontGDI->face;
+ aveWidth = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.lfWidth: 0;
+ orientation = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.lfOrientation: 0;
+ TEXTOBJ_UnlockText(TextObj);
+ Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
+ potm = ExAllocatePoolWithTag(PagedPool, Size, TAG_GDITEXT);
+ if (NULL == potm)
+ {
+ return GDI_ERROR;
+ }
+ IntGetOutlineTextMetrics(FontGDI, Size, potm);
+
+ IntLockFreeType;
+ /* Durring testing, I never saw this used. In here just incase.*/
+ if (ft_face->charmap == NULL)
+ {
+ DPRINT1("WARNING: No charmap selected!\n");
+ DPRINT1("This font face has %d charmaps\n", ft_face->num_charmaps);
+ int n;
+ FT_CharMap found = 0, charmap;
+
+ for (n = 0; n < ft_face->num_charmaps; n++)
+ {
+ charmap = ft_face->charmaps[n];
+ DPRINT1("found charmap encoding: %u\n", charmap->encoding);
+ if (charmap->encoding != 0)
+ {
+ found = charmap;
+ break;
+ }
+ }
+ if (!found)
+ {
+ DPRINT1("WARNING: Could not find desired charmap!\n");
+ }
+ error = FT_Set_Charmap(ft_face, found);
+ if (error)
+ {
+ DPRINT1("WARNING: Could not set the charmap!\n");
+ }
+ }
+
+ if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) ||
aveWidth || pmat2)
+ load_flags |= FT_LOAD_NO_BITMAP;
+
+ if (iFormat & GGO_GLYPH_INDEX)
+ {
+ glyph_index = wch;
+ iFormat &= ~GGO_GLYPH_INDEX;
+ }
+ else glyph_index = FT_Get_Char_Index(ft_face, wch);
+
+ error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
+ if (error)
+ {
+ DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n",
glyph_index);
+ IntUnLockFreeType;
+ ExFreePool(potm);
+ return GDI_ERROR;
+ }
+ IntUnLockFreeType;
+
+ if (aveWidth && potm)
+ {
+ widthRatio = (FLOAT)aveWidth * eM11 / (FLOAT)
potm->otmTextMetrics.tmAveCharWidth;
+ ExFreePool(potm); /* It looks like we are finished with potm ATM.*/
+ }
+
+ left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
+ right = (INT)((ft_face->glyph->metrics.horiBearingX +
ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
+
+ adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
+ lsb = left >> 6;
+ bbx = (right - left) >> 6;
+
+ /* Scaling transform */
+ if(aveWidth)
+ {
+ FT_Matrix scaleMat;
+ DPRINT1("Scaling Trans!\n");
+ scaleMat.xx = FT_FixedFromFloat(widthRatio);
+ scaleMat.xy = 0;
+ scaleMat.yx = 0;
+ scaleMat.yy = (1 << 16);
+
+ FT_Matrix_Multiply(&scaleMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ /* Rotation transform */
+ if(orientation)
+ {
+ FT_Matrix rotationMat;
+ FT_Vector vecAngle;
+ DPRINT1("Rotation Trans!\n");
+ angle = FT_FixedFromFloat((float)orientation / 10.0);
+ FT_Vector_Unit(&vecAngle, angle);
+ rotationMat.xx = vecAngle.x;
+ rotationMat.xy = -vecAngle.y;
+ rotationMat.yx = -rotationMat.xy;
+ rotationMat.yy = rotationMat.xx;
+
+ FT_Matrix_Multiply(&rotationMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ /* Extra transformation specified by caller */
+ if (pmat2)
+ {
+ FT_Matrix extraMat;
+ DPRINT1("MAT2 Matrix Trans!\n");
+ extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
+ extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
+ extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
+ extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
+ FT_Matrix_Multiply(&extraMat, &transMat);
+ needsTransform = TRUE;
+ }
+
+ if(!needsTransform)
+ {
+ top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
+ bottom = (ft_face->glyph->metrics.horiBearingY -
+ ft_face->glyph->metrics.height) & -64;
+ gm.gmCellIncX = adv;
+ gm.gmCellIncY = 0;
+ }
+ else
+ {
+ INT xc, yc;
+ FT_Vector vec;
+ for(xc = 0; xc < 2; xc++)
+ {
+ for(yc = 0; yc < 2; yc++)
+ {
+ vec.x = (ft_face->glyph->metrics.horiBearingX +
+ xc * ft_face->glyph->metrics.width);
+ vec.y = ft_face->glyph->metrics.horiBearingY -
+ yc * ft_face->glyph->metrics.height;
+ DPRINT1("Vec %ld,%ld\n", vec.x, vec.y);
+ FT_Vector_Transform(&vec, &transMat);
+ if(xc == 0 && yc == 0)
+ {
+ left = right = vec.x;
+ top = bottom = vec.y;
+ }
+ else
+ {
+ if(vec.x < left) left = vec.x;
+ else if(vec.x > right) right = vec.x;
+ if(vec.y < bottom) bottom = vec.y;
+ else if(vec.y > top) top = vec.y;
+ }
+ }
+ }
+ left = left & -64;
+ right = (right + 63) & -64;
+ bottom = bottom & -64;
+ top = (top + 63) & -64;
+
+ DPRINT1("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
+ vec.x = ft_face->glyph->metrics.horiAdvance;
+ vec.y = 0;
+ FT_Vector_Transform(&vec, &transMat);
+ gm.gmCellIncX = (vec.x+63) >> 6;
+ gm.gmCellIncY = -((vec.y+63) >> 6);
+ }
+ gm.gmBlackBoxX = (right - left) >> 6;
+ gm.gmBlackBoxY = (top - bottom) >> 6;
+ gm.gmptGlyphOrigin.x = left >> 6;
+ gm.gmptGlyphOrigin.y = top >> 6;
+
+ if (pgm)
+ {
+ Status = MmCopyToCaller(pgm, &gm, sizeof(GLYPHMETRICS));
+ if (! NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return GDI_ERROR;
+ }
+ }
+
+ if (iFormat == GGO_METRICS)
+ {
+ DPRINT1("GGO_METRICS Exit!\n");
+ return 1; /* FIXME */
+ }
+
+ if(ft_face->glyph->format != ft_glyph_format_outline && iFormat !=
GGO_BITMAP)
+ {
+ DPRINT1("loaded a bitmap\n");
+ return GDI_ERROR;
+ }
+
+ switch(iFormat)
+ {
+ case GGO_BITMAP:
+ width = gm.gmBlackBoxX;
+ height = gm.gmBlackBoxY;
+ pitch = ((width + 31) >> 5) << 2;
+ needed = pitch * height;
+
+ if(!pvBuf || !cjBuf) break;
+
+ Buffer = pvBuf;
+
+ switch(ft_face->glyph->format)
+ {
+ case ft_glyph_format_bitmap:
+ {
+ BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
+ INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
+ INT h = ft_face->glyph->bitmap.rows;
+ while(h--)
+ {
+ memcpy(dst, src, w);
+ src += ft_face->glyph->bitmap.pitch;
+ dst += pitch;
+ }
+ break;
+ }
+
+ case ft_glyph_format_outline:
+ ft_bitmap.width = width;
+ ft_bitmap.rows = height;
+ ft_bitmap.pitch = pitch;
+ ft_bitmap.pixel_mode = ft_pixel_mode_mono;
+ ft_bitmap.buffer = pvBuf;
+
+ IntLockFreeType;
+ if(needsTransform)
+ {
+ FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
+ }
+ FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
+ /* Note: FreeType will only set 'black' bits for us. */
+ memset(pvBuf, 0, needed);
+ FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
+ IntUnLockFreeType;
+ break;
+
+ default:
+ DPRINT1("loaded glyph format %x\n", ft_face->glyph->format);
+ return GDI_ERROR;
+ }
+ break;
+
+ case GGO_GRAY2_BITMAP:
+ case GGO_GRAY4_BITMAP:
+ case GGO_GRAY8_BITMAP:
+ {
+ unsigned int mult, row, col;
+ BYTE *start, *ptr;
+
+ width = gm.gmBlackBoxX;
+ height = gm.gmBlackBoxY;
+ pitch = (width + 3) / 4 * 4;
+ needed = pitch * height;
+
+ if(!pvBuf || !cjBuf) break;
+ ft_bitmap.width = width;
+ ft_bitmap.rows = height;
+ ft_bitmap.pitch = pitch;
+ ft_bitmap.pixel_mode = ft_pixel_mode_grays;
+ ft_bitmap.buffer = pvBuf;
+
+ IntLockFreeType;
+ if(needsTransform)
+ {
+ FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
+ }
+ FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
+ memset(ft_bitmap.buffer, 0, cjBuf);
+ FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
+ IntUnLockFreeType;
+
+ if(iFormat == GGO_GRAY2_BITMAP)
+ mult = 4;
+ else if(iFormat == GGO_GRAY4_BITMAP)
+ mult = 16;
+ else if(iFormat == GGO_GRAY8_BITMAP)
+ mult = 64;
+ else
+ {
+ assert(0);
+ break;
+ }
+
+ start = pvBuf;
+ for(row = 0; row < height; row++)
+ {
+ ptr = start;
+ for(col = 0; col < width; col++, ptr++)
+ {
+ *ptr = (((int)*ptr) * mult + 128) / 256;
+ }
+ start += pitch;
+ }
+ break;
+ }
+
+ case GGO_NATIVE:
+ {
+ int contour, point = 0, first_pt;
+ FT_Outline *outline = &ft_face->glyph->outline;
+ TTPOLYGONHEADER *pph;
+ TTPOLYCURVE *ppc;
+ DWORD pph_start, cpfx, type;
+
+ if(cjBuf == 0) pvBuf = NULL;
+
+ if (needsTransform && pvBuf)
+ {
+ IntLockFreeType;
+ FT_Outline_Transform(outline, &transMat);
+ IntUnLockFreeType;
+ }
+
+ for(contour = 0; contour < outline->n_contours; contour++)
+ {
+ pph_start = needed;
+ pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
+ first_pt = point;
+ if(pvBuf)
+ {
+ pph->dwType = TT_POLYGON_TYPE;
+ FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
+ }
+ needed += sizeof(*pph);
+ point++;
+ while(point <= outline->contours[contour])
+ {
+ ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
+ type = (outline->tags[point] & FT_Curve_Tag_On) ?
+ TT_PRIM_LINE : TT_PRIM_QSPLINE;
+ cpfx = 0;
+ do
+ {
+ if(pvBuf)
+ FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
+ cpfx++;
+ point++;
+ } while(point <= outline->contours[contour] &&
+ (outline->tags[point] & FT_Curve_Tag_On) ==
+ (outline->tags[point-1] & FT_Curve_Tag_On));
+ /* At the end of a contour Windows adds the start point, but
+ only for Beziers */
+ if(point > outline->contours[contour] &&
+ !(outline->tags[point-1] & FT_Curve_Tag_On))
+ {
+ if(pvBuf)
+ FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
+ cpfx++;
+ }
+ else
+ if(point <= outline->contours[contour] &&
+ outline->tags[point] & FT_Curve_Tag_On)
+ {
+ /* add closing pt for bezier */
+ if(pvBuf)
+ FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
+ cpfx++;
+ point++;
+ }
+ if(pvBuf)
+ {
+ ppc->wType = type;
+ ppc->cpfx = cpfx;
+ }
+ needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
+ }
+ if(pvBuf)
+ pph->cb = needed - pph_start;
+ }
+ break;
+ }
+ case GGO_BEZIER:
+ {
+ /* Convert the quadratic Beziers to cubic Beziers.
+ The parametric eqn for a cubic Bezier is, from PLRM:
+ r(t) = at^3 + bt^2 + ct + r0
+ with the control points:
+ r1 = r0 + c/3
+ r2 = r1 + (c + b)/3
+ r3 = r0 + c + b + a
+
+ A quadratic Beizer has the form:
+ p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
+
+ So equating powers of t leads to:
+ r1 = 2/3 p1 + 1/3 p0
+ r2 = 2/3 p1 + 1/3 p2
+ and of course r0 = p0, r3 = p2
+ */
+
+ int contour, point = 0, first_pt;
+ FT_Outline *outline = &ft_face->glyph->outline;
+ TTPOLYGONHEADER *pph;
+ TTPOLYCURVE *ppc;
+ DWORD pph_start, cpfx, type;
+ FT_Vector cubic_control[4];
+ if(cjBuf == 0) pvBuf = NULL;
+
+ if (needsTransform && pvBuf)
+ {
+ IntLockFreeType;
+ FT_Outline_Transform(outline, &transMat);
+ IntUnLockFreeType;
+ }
+
+ for(contour = 0; contour < outline->n_contours; contour++)
+ {
+ pph_start = needed;
+ pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
+ first_pt = point;
+ if(pvBuf)
+ {
+ pph->dwType = TT_POLYGON_TYPE;
+ FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
+ }
+ needed += sizeof(*pph);
+ point++;
+ while(point <= outline->contours[contour])
+ {
+ ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
+ type = (outline->tags[point] & FT_Curve_Tag_On) ?
+ TT_PRIM_LINE : TT_PRIM_CSPLINE;
+ cpfx = 0;
+ do
+ {
+ if(type == TT_PRIM_LINE)
+ {
+ if(pvBuf)
+ FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
+ cpfx++;
+ point++;
+ }
+ else
+ {
+ /* Unlike QSPLINEs, CSPLINEs always have their endpoint
+ so cpfx = 3n */
+
+ /* FIXME: Possible optimization in endpoint calculation
+ if there are two consecutive curves */
+ cubic_control[0] = outline->points[point-1];
+ if(!(outline->tags[point-1] & FT_Curve_Tag_On))
+ {
+ cubic_control[0].x += outline->points[point].x + 1;
+ cubic_control[0].y += outline->points[point].y + 1;
+ cubic_control[0].x >>= 1;
+ cubic_control[0].y >>= 1;
+ }
+ if(point+1 > outline->contours[contour])
+ cubic_control[3] = outline->points[first_pt];
+ else
+ {
+ cubic_control[3] = outline->points[point+1];
+ if(!(outline->tags[point+1] & FT_Curve_Tag_On))
+ {
+ cubic_control[3].x += outline->points[point].x + 1;
+ cubic_control[3].y += outline->points[point].y + 1;
+ cubic_control[3].x >>= 1;
+ cubic_control[3].y >>= 1;
+ }
+ }
+ /* r1 = 1/3 p0 + 2/3 p1
+ r2 = 1/3 p2 + 2/3 p1 */
+ cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
+ cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
+ cubic_control[2] = cubic_control[1];
+ cubic_control[1].x += (cubic_control[0].x + 1) / 3;
+ cubic_control[1].y += (cubic_control[0].y + 1) / 3;
+ cubic_control[2].x += (cubic_control[3].x + 1) / 3;
+ cubic_control[2].y += (cubic_control[3].y + 1) / 3;
+ if(pvBuf)
+ {
+ FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
+ FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
+ FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
+ }
+ cpfx += 3;
+ point++;
+ }
+ }
+ while(point <= outline->contours[contour] &&
+ (outline->tags[point] & FT_Curve_Tag_On) ==
+ (outline->tags[point-1] & FT_Curve_Tag_On));
+ /* At the end of a contour Windows adds the start point,
+ but only for Beziers and we've already done that.
+ */
+ if(point <= outline->contours[contour] &&
+ outline->tags[point] & FT_Curve_Tag_On)
+ {
+ /* This is the closing pt of a bezier, but we've already
+ added it, so just inc point and carry on */
+ point++;
+ }
+ if(pvBuf)
+ {
+ ppc->wType = type;
+ ppc->cpfx = cpfx;
+ }
+ needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
+ }
+ if(pvBuf)
+ pph->cb = needed - pph_start;
+ }
+ break;
+ }
+
+ default:
+ DPRINT1("Unsupported format %d\n", iFormat);
+ return GDI_ERROR;
+ }
+ return needed;
}
DWORD