[PATCH 4/4] gdiplus: Add DrawDriverString serialization.

Shawn M. Chapla schapla at codeweavers.com
Fri Jul 10 17:25:18 CDT 2020


Signed-off-by: Shawn M. Chapla <schapla at codeweavers.com>
---
 dlls/gdiplus/gdiplus_private.h |   3 +
 dlls/gdiplus/graphics.c        |   5 +-
 dlls/gdiplus/metafile.c        | 182 +++++++++++++++++++++++++++++++--
 dlls/gdiplus/tests/metafile.c  |  30 +++---
 4 files changed, 193 insertions(+), 27 deletions(-)

diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index 2ceaeade01..1b2de4e8f4 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -104,6 +104,9 @@ extern GpStatus METAFILE_DrawImagePointsRect(GpMetafile* metafile, GpImage *imag
 extern GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val) DECLSPEC_HIDDEN;
 extern GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path) DECLSPEC_HIDDEN;
 extern GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path) DECLSPEC_HIDDEN;
+extern GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
+    GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
+    GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix) DECLSPEC_HIDDEN;
 extern void METAFILE_Free(GpMetafile *metafile) DECLSPEC_HIDDEN;
 
 extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1,
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index 6c50d2d56c..383a6286ac 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -7427,7 +7427,10 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text,
     if (length == -1)
         length = lstrlenW(text);
 
-    if (graphics->hdc && !graphics->alpha_hdc &&
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+        return METAFILE_DrawDriverString((GpMetafile*)graphics->image, text, length, font,
+            format, brush, positions, flags, matrix);
+    else if (graphics->hdc && !graphics->alpha_hdc &&
         brush->bt == BrushTypeSolidColor &&
         (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
         stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c
index d25278d115..cedffb23ca 100644
--- a/dlls/gdiplus/metafile.c
+++ b/dlls/gdiplus/metafile.c
@@ -378,6 +378,17 @@ typedef struct EmfPlusImageAttributes
     DWORD Reserved2;
 } EmfPlusImageAttributes;
 
+typedef struct EmfPlusFont
+{
+    DWORD Version;
+    float EmSize;
+    DWORD SizeUnit;
+    DWORD FontStyleFlags;
+    DWORD Reserved;
+    DWORD Length;
+    WCHAR FamilyName[1];
+} EmfPlusFont;
+
 typedef struct EmfPlusObject
 {
     EmfPlusRecordHeader Header;
@@ -389,6 +400,7 @@ typedef struct EmfPlusObject
         EmfPlusRegion region;
         EmfPlusImage image;
         EmfPlusImageAttributes image_attributes;
+        EmfPlusFont font;
     } ObjectData;
 } EmfPlusObject;
 
@@ -537,17 +549,6 @@ typedef struct EmfPlusFillPie
     } RectData;
 } EmfPlusFillPie;
 
-typedef struct EmfPlusFont
-{
-    DWORD Version;
-    float EmSize;
-    DWORD SizeUnit;
-    DWORD FontStyleFlags;
-    DWORD Reserved;
-    DWORD Length;
-    WCHAR FamilyName[1];
-} EmfPlusFont;
-
 typedef struct EmfPlusDrawDriverString
 {
     EmfPlusRecordHeader Header;
@@ -4643,3 +4644,162 @@ GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path)
     METAFILE_WriteRecords(metafile);
     return Ok;
 }
+
+static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id)
+{
+    EmfPlusObject *object_record;
+    EmfPlusFont *font_record;
+    GpStatus stat;
+    INT fn_len;
+    INT style;
+
+    *id = -1;
+
+    if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
+            metafile->metafile_type != MetafileTypeEmfPlusDual)
+        return Ok;
+
+    /* The following cast is ugly, but GdipGetFontStyle does treat
+       its first parameter as const. */
+    stat = GdipGetFontStyle((GpFont*)font, &style);
+    if (stat != Ok)
+        return stat;
+
+    fn_len = lstrlenW(font->family->FamilyName);
+    stat = METAFILE_AllocateRecord(metafile,
+        FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]),
+        (void**)&object_record);
+    if (stat != Ok)
+        return stat;
+
+    *id = METAFILE_AddObjectId(metafile);
+
+    object_record->Header.Type = EmfPlusRecordTypeObject;
+    object_record->Header.Flags = *id | ObjectTypeFont << 8;
+
+    font_record = &object_record->ObjectData.font;
+    font_record->Version = VERSION_MAGIC2;
+    font_record->EmSize = font->emSize;
+    font_record->SizeUnit = font->unit;
+    font_record->FontStyleFlags = style;
+    font_record->Reserved = 0;
+    font_record->Length = fn_len;
+
+    memcpy(font_record->FamilyName, font->family->FamilyName,
+        fn_len * sizeof(*font->family->FamilyName));
+
+    return Ok;
+}
+
+GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length,
+    GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush,
+    GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix)
+{
+    DWORD brush_id;
+    DWORD font_id;
+    DWORD alloc_size;
+    GpStatus stat;
+    EmfPlusDrawDriverString *draw_string_record;
+    BYTE *cursor;
+    BOOL inline_color;
+    BOOL include_matrix = FALSE;
+
+    if (length <= 0)
+        return InvalidParameter;
+
+    if (metafile->metafile_type != MetafileTypeEmfPlusOnly &&
+            metafile->metafile_type != MetafileTypeEmfPlusDual)
+    {
+        FIXME("metafile type not supported: %i\n", metafile->metafile_type);
+        return NotImplemented;
+    }
+
+    stat = METAFILE_AddFontObject(metafile, font, &font_id);
+    if (stat != Ok)
+        return stat;
+
+    inline_color = (brush->bt == BrushTypeSolidColor);
+    if (!inline_color)
+    {
+        stat = METAFILE_AddBrushObject(metafile, brush, &brush_id);
+        if (stat != Ok)
+            return stat;
+    }
+
+    if (matrix)
+    {
+        BOOL identity;
+
+        stat = GdipIsMatrixIdentity(matrix, &identity);
+        if (stat != Ok)
+           return stat;
+
+        include_matrix = !identity;
+    }
+
+    alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) +
+        length * (sizeof(*text) + sizeof(*positions));
+
+    if (include_matrix)
+        alloc_size += sizeof(*matrix);
+
+    /* Pad record to DWORD alignment. */
+    alloc_size = (alloc_size + 3) & ~3;
+
+    stat = METAFILE_AllocateRecord(metafile, alloc_size, (void**)&draw_string_record);
+    if (stat != Ok)
+        return stat;
+
+    draw_string_record->Header.Type = EmfPlusRecordTypeDrawDriverString;
+    draw_string_record->Header.Flags = font_id;
+    draw_string_record->DriverStringOptionsFlags = flags;
+    draw_string_record->MatrixPresent = include_matrix;
+    draw_string_record->GlyphCount = length;
+
+    if (inline_color)
+    {
+        draw_string_record->Header.Flags |= 0x8000;
+        draw_string_record->brush.Color = ((GpSolidFill*)brush)->color;
+    }
+    else
+        draw_string_record->brush.BrushId = brush_id;
+
+    cursor = &draw_string_record->VariableData[0];
+
+    memcpy(cursor, text, length * sizeof(*text));
+    cursor += length * sizeof(*text);
+
+    if (flags & DriverStringOptionsRealizedAdvance)
+    {
+        static BOOL fixme_written = FALSE;
+
+        /* Native never writes DriverStringOptionsRealizedAdvance. Instead,
+           in the case of RealizedAdvance, each glyph position is computed
+           and serialized.
+
+           While native GDI+ is capable of playing back metafiles with this
+           flag set, it is possible that some application might rely on
+           metafiles produced from GDI+ not setting this flag. Ideally we
+           would also compute the position of each glyph here, serialize those
+           values, and not set DriverStringOptionsRealizedAdvance. */
+        if (!fixme_written)
+        {
+            fixme_written = TRUE;
+            FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n");
+        }
+
+        *((PointF*)cursor) = *positions;
+    }
+    else
+        memcpy(cursor, positions, length * sizeof(*positions));
+
+    if (include_matrix)
+    {
+        cursor += length * sizeof(*positions);
+        memcpy(cursor, matrix, sizeof(*matrix));
+    }
+
+    METAFILE_WriteRecords(metafile);
+
+    return Ok;
+}
diff --git a/dlls/gdiplus/tests/metafile.c b/dlls/gdiplus/tests/metafile.c
index 9e25da7561..cebbec1db7 100644
--- a/dlls/gdiplus/tests/metafile.c
+++ b/dlls/gdiplus/tests/metafile.c
@@ -2956,11 +2956,11 @@ static void test_restoredc(void)
 static const emfplus_record drawdriverstring_records[] = {
     { EMR_HEADER },
     { EmfPlusRecordTypeHeader },
-    { EmfPlusRecordTypeObject, ObjectTypeFont << 8, 1, 0 },
-    { EmfPlusRecordTypeDrawDriverString, 0x8000, 1, 0 },
-    { EmfPlusRecordTypeObject, (ObjectTypeFont << 8) | 1, 1 },
-    { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 2, 1, 1 },
-    { EmfPlusRecordTypeDrawDriverString, 0x1, 1, 1 },
+    { EmfPlusRecordTypeObject, ObjectTypeFont << 8 },
+    { EmfPlusRecordTypeDrawDriverString, 0x8000 },
+    { EmfPlusRecordTypeObject, (ObjectTypeFont << 8) | 1 },
+    { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 2 },
+    { EmfPlusRecordTypeDrawDriverString, 0x1 },
     { EmfPlusRecordTypeEndOfFile },
     { EMR_EOF },
     { 0 }
@@ -3023,14 +3023,14 @@ static void test_drawdriverstring(void)
 
     stat = GdipDrawDriverString(graphics, L"Test", 4, solidfont, solidbrush, solidpos,
         DriverStringOptionsCmapLookup, matrix);
-    todo_wine expect(Ok, stat);
+    expect(Ok, stat);
 
     stat = GdipSetMatrixElements(matrix, 1.5, 0.0, 0.0, 1.5, 0.0, 0.0);
     expect(Ok, stat);
 
     stat = GdipDrawDriverString(graphics, L"Test ", 5, hatchfont, hatchbrush, &hatchpos,
         DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, matrix);
-    todo_wine expect(Ok, stat);
+    expect(Ok, stat);
 
     stat = GdipDeleteGraphics(graphics);
     graphics = NULL;
@@ -3058,28 +3058,28 @@ static void test_drawdriverstring(void)
         "Expected RGB 0x%.8x with any non-zero alpha value, got 0x%.8x\n", expected, got)
 
     GdipBitmapGetPixel(bitmap, 12, 9, &color);
-    todo_wine fuzzy_expect(0xff, color);
+    fuzzy_expect(0xff, color);
 
     GdipBitmapGetPixel(bitmap, 21, 9, &color);
-    todo_wine fuzzy_expect(0xff, color);
+    fuzzy_expect(0xff, color);
 
     GdipBitmapGetPixel(bitmap, 31, 9, &color);
-    todo_wine fuzzy_expect(0xff, color);
+    fuzzy_expect(0xff, color);
 
     GdipBitmapGetPixel(bitmap, 41, 9, &color);
-    todo_wine fuzzy_expect(0xff, color);
+    fuzzy_expect(0xff, color);
 
     GdipBitmapGetPixel(bitmap, 14, 23, &color);
-    todo_wine fuzzy_expect(0xff0000, color);
+    fuzzy_expect(0xff0000, color);
 
     GdipBitmapGetPixel(bitmap, 23, 23, &color);
-    todo_wine fuzzy_expect(0xff0000, color);
+    fuzzy_expect(0xff0000, color);
 
     GdipBitmapGetPixel(bitmap, 28, 23, &color);
-    todo_wine fuzzy_expect(0xff0000, color);
+    fuzzy_expect(0xff0000, color);
 
     GdipBitmapGetPixel(bitmap, 32, 23, &color);
-    todo_wine fuzzy_expect(0xff0000, color);
+    fuzzy_expect(0xff0000, color);
 
 #undef fuzzy_expect
 
-- 
2.27.0




More information about the wine-devel mailing list