> I think this is impossible to do. Having a stroked object,
> transformation *must* change the outline strokes, too, or you get
> unstable results. Just think of a square rotated by 45 degrees that
> gets scaled by (100,1) to make it extremely broad: Following your
> idea, the strokes would no longer be properly connected in the top and
> bottom corners because the angle between the square edges changes a
> lot (from 90 degrees to almost 180 degrees).
>
> In other words, strokes must always be applied to the final shape. A
> transformation matrix must be applied to the object before the
> stroking process starts.
>
> What about using `FT_Stroker_ParseOutline' after transformation of the
> outline?
I've tried that of course but it doesn't make a difference and I don't
understand
why. The shape looks exactly the same if FT_Outline_Transform() is called
before FT_Stroker_ParseOutline(). See the attached example code. I've modified
the example code to dump the shape into a BMP image so you can immediately see
the
results. In the image you can see that the stroke size is 8 pixels on the x-axis
but only 4 pixels on the y-axis although the transformation matrix is applied
before calling FT_Stroker_ParseOutline().
Marco
#include <stdio.h>
#include <math.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_SYNTHESIS_H
#include FT_STROKER_H
static FT_Library freetype_library = NULL;
#define WIDTH 320
#define HEIGHT 240
// desired stroke thickness in pixels
#define STROKE_THICKNESS 8
#define setvector(pv, px, py) (pv).x = ((int) (px)) << 6; (pv).y = ((int) (py))
<< 6;
#define Float2Fixed(fl) ((FT_Fixed)((fl)*65536.0f))
#define writew(fp, w) tmpword = (w); fwrite(&tmpword, 2, 1, (fp));
#define writel(fp, l) tmplong = (l); fwrite(&tmplong, 4, 1, (fp));
int main(int argc, char *argv[])
{
FT_Stroker stroker;
FT_UInt points, contours;
FT_Outline outline;
FT_BBox bbox;
FT_Bitmap bm;
FT_Vector v;
FT_Matrix m;
int x, y, xmin, ymin, xmax, ymax, pixelwidth, pixelheight, bytewidth;
unsigned char *buf, *ptr;
unsigned char *linebuf;
unsigned short tmpword;
unsigned long tmplong;
FILE *fp;
FT_Init_FreeType(&freetype_library);
FT_Stroker_New(freetype_library, &stroker);
FT_Stroker_Set(stroker, (int) ((double) STROKE_THICKNESS * 32.0),
FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
setvector(v, 0, 0);
FT_Stroker_BeginSubPath(stroker, &v, 1);
FT_Stroker_LineTo(stroker, &v);
setvector(v, 0, 0);
FT_Stroker_LineTo(stroker, &v);
setvector(v, 0, HEIGHT);
FT_Stroker_LineTo(stroker, &v);
setvector(v, WIDTH, HEIGHT);
FT_Stroker_LineTo(stroker, &v);
setvector(v, WIDTH, 0);
FT_Stroker_LineTo(stroker, &v);
setvector(v, 0, 0);
FT_Stroker_LineTo(stroker, &v);
FT_Stroker_EndSubPath(stroker);
FT_Stroker_GetBorderCounts(stroker, FT_STROKER_BORDER_LEFT, &points,
&contours);
memset(&outline, 0, sizeof(FT_Outline));
FT_Outline_New(freetype_library, 1024, 512, &outline);
outline.n_points = 0;
outline.n_contours = 0;
FT_Stroker_Export(stroker, &outline);
#if 1
// this transformation will reduce STROKE_THICKNESS on the y-axis to
STROKE_THICKNESS*0.5
// but I don't want this! STROKE_THICKNESS should always be the
constant value that has
// been set in FT_Stroker_Set() --- no matter which transformation
matrix has been applied!
m.xx = Float2Fixed(1.0);
m.xy = Float2Fixed(0.0);
m.yx = Float2Fixed(0.0);
m.yy = Float2Fixed(0.5);
FT_Outline_Transform(&outline, &m);
#endif
FT_Stroker_ParseOutline(stroker, &outline, 0);
FT_Stroker_GetCounts(stroker, &points, &contours);
#if 0
// this transformation will reduce STROKE_THICKNESS on the y-axis to
STROKE_THICKNESS*0.5
// but I don't want this! STROKE_THICKNESS should always be the
constant value that has
// been set in FT_Stroker_Set() --- no matter which transformation
matrix has been applied!
m.xx = Float2Fixed(1.0);
m.xy = Float2Fixed(0.0);
m.yx = Float2Fixed(0.0);
m.yy = Float2Fixed(0.5);
FT_Outline_Transform(&outline, &m);
#endif
FT_Stroker_Done(stroker);
FT_Outline_Get_BBox(&outline, &bbox);
FT_Outline_Translate(&outline, -bbox.xMin, -bbox.yMin);
FT_Outline_Get_BBox(&outline, &bbox);
xmin = bbox.xMin >> 6;
ymin = bbox.yMin >> 6;
xmax = bbox.xMax >> 6;
ymax = bbox.yMax >> 6;
if(bbox.xMax & 0x3f) xmax++;
if(bbox.yMax & 0x3f) ymax++;
pixelwidth = xmax - xmin;
pixelheight = ymax - ymin;
buf = calloc(pixelwidth * pixelheight, 1);
memset(&bm, 0, sizeof(FT_Bitmap));
bm.rows = pixelheight;
bm.width = pixelwidth;
bm.pitch = pixelwidth;
bm.buffer = buf;
bm.num_grays = 256;
bm.pixel_mode = FT_PIXEL_MODE_GRAY;
FT_Outline_Get_Bitmap(freetype_library, &outline, &bm);
fp = fopen("dump.bmp", "wb");
bytewidth = pixelwidth * 3 + pixelwidth % 4;
linebuf = malloc(bytewidth);
writew(fp, 0x4D42);
writel(fp, bytewidth * pixelheight + 54);
writel(fp, 0);
writel(fp, 0x36);
writel(fp, 0x28);
writel(fp, pixelwidth);
writel(fp, pixelheight);
writew(fp, 1);
writew(fp, 24);
writel(fp, 0);
writel(fp, 0);
writel(fp, 0);
writel(fp, 0);
writel(fp, 0);
writel(fp, 0);
ptr = buf + pixelheight * pixelwidth;
for(y = 0; y < pixelheight; y++) {
unsigned char *l = linebuf;
ptr -= pixelwidth;
for(x = 0; x < pixelwidth; x++) {
unsigned long rgb = ptr[x] * 65793;
*l++ = (unsigned char) (rgb & 0x0000ff);
*l++ = (unsigned char) ((rgb & 0x00ff00) >> 8);
*l++ = (unsigned char) ((rgb & 0xff0000) >> 16);
}
fwrite(linebuf, bytewidth, 1, fp);
}
fclose(fp);
free(linebuf);
free(buf);
FT_Outline_Done(freetype_library, &outline);
FT_Done_FreeType(freetype_library);
return 0;
}
_______________________________________________
Freetype mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/freetype