Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
8a4db9fd
Commit
8a4db9fd
authored
Feb 08, 2016
by
Nikolay Sivov
Committed by
Alexandre Julliard
Feb 08, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dwrite: Implement color glyph run decomposition.
Signed-off-by:
Nikolay Sivov
<
nsivov@codeweavers.com
>
Signed-off-by:
Alexandre Julliard
<
julliard@winehq.org
>
parent
2e927af3
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
334 additions
and
17 deletions
+334
-17
dwrite_private.h
dlls/dwrite/dwrite_private.h
+13
-0
font.c
dlls/dwrite/font.c
+236
-13
opentype.c
dlls/dwrite/opentype.c
+84
-0
font.c
dlls/dwrite/tests/font.c
+1
-4
No files found.
dlls/dwrite/dwrite_private.h
View file @
8a4db9fd
...
...
@@ -165,6 +165,19 @@ extern UINT32 opentype_get_cpal_palettecount(const void*) DECLSPEC_HIDDEN;
extern
UINT32
opentype_get_cpal_paletteentrycount
(
const
void
*
)
DECLSPEC_HIDDEN
;
extern
HRESULT
opentype_get_cpal_entries
(
const
void
*
,
UINT32
,
UINT32
,
UINT32
,
DWRITE_COLOR_F
*
)
DECLSPEC_HIDDEN
;
struct
dwrite_colorglyph
{
USHORT
layer
;
/* [0, num_layers) index indicating current layer */
/* base glyph record data, set once on initialization */
USHORT
first_layer
;
USHORT
num_layers
;
/* current layer record data, updated every time glyph is switched to next layer */
UINT16
glyph
;
UINT16
palette_index
;
};
extern
HRESULT
opentype_get_colr_glyph
(
const
void
*
,
UINT16
,
struct
dwrite_colorglyph
*
)
DECLSPEC_HIDDEN
;
extern
void
opentype_colr_next_glyph
(
const
void
*
,
struct
dwrite_colorglyph
*
)
DECLSPEC_HIDDEN
;
enum
gasp_flags
{
GASP_GRIDFIT
=
0x0001
,
GASP_DOGRAY
=
0x0002
,
...
...
dlls/dwrite/font.c
View file @
8a4db9fd
...
...
@@ -181,6 +181,22 @@ struct dwrite_glyphrunanalysis {
struct
dwrite_colorglyphenum
{
IDWriteColorGlyphRunEnumerator
IDWriteColorGlyphRunEnumerator_iface
;
LONG
ref
;
FLOAT
origin_x
;
/* original run origin */
FLOAT
origin_y
;
IDWriteFontFace2
*
fontface
;
/* for convenience */
DWRITE_COLOR_GLYPH_RUN
colorrun
;
/* returned with GetCurrentRun() */
DWRITE_GLYPH_RUN
run
;
/* base run */
UINT32
palette
;
/* palette index to get layer color from */
FLOAT
*
advances
;
/* original or measured advances for base glyphs */
FLOAT
*
color_advances
;
/* returned color run points to this */
UINT16
*
glyphindices
;
/* returned color run points to this */
struct
dwrite_colorglyph
*
glyphs
;
/* current glyph color info */
BOOL
has_regular_glyphs
;
/* TRUE if there's any glyph without a color */
UINT16
current_layer
;
/* enumerator position, updated with MoveNext */
UINT16
max_layer_num
;
/* max number of layers for this run */
struct
dwrite_fonttable
colr
;
/* used to access layers */
};
#define GLYPH_BLOCK_SHIFT 8
...
...
@@ -4610,24 +4626,147 @@ static ULONG WINAPI colorglyphenum_Release(IDWriteColorGlyphRunEnumerator *iface
TRACE
(
"(%p)->(%u)
\n
"
,
This
,
ref
);
if
(
!
ref
)
if
(
!
ref
)
{
heap_free
(
This
->
advances
);
heap_free
(
This
->
color_advances
);
heap_free
(
This
->
glyphindices
);
heap_free
(
This
->
glyphs
);
if
(
This
->
colr
.
context
)
IDWriteFontFace2_ReleaseFontTable
(
This
->
fontface
,
This
->
colr
.
context
);
IDWriteFontFace2_Release
(
This
->
fontface
);
heap_free
(
This
);
}
return
ref
;
}
static
FLOAT
get_glyph_origin
(
const
struct
dwrite_colorglyphenum
*
glyphenum
,
UINT32
g
)
{
BOOL
is_rtl
=
glyphenum
->
run
.
bidiLevel
&
1
;
FLOAT
origin
=
0
.
0
f
;
if
(
g
==
0
)
return
0
.
0
f
;
while
(
g
--
)
origin
+=
is_rtl
?
-
glyphenum
->
advances
[
g
]
:
glyphenum
->
advances
[
g
];
return
origin
;
}
static
BOOL
colorglyphenum_build_color_run
(
struct
dwrite_colorglyphenum
*
glyphenum
)
{
DWRITE_COLOR_GLYPH_RUN
*
colorrun
=
&
glyphenum
->
colorrun
;
FLOAT
advance_adj
=
0
.
0
f
;
BOOL
got_palette_index
;
UINT32
g
;
/* start with regular glyphs */
if
(
glyphenum
->
current_layer
==
0
&&
glyphenum
->
has_regular_glyphs
)
{
UINT32
first_glyph
=
0
;
for
(
g
=
0
;
g
<
glyphenum
->
run
.
glyphCount
;
g
++
)
{
if
(
glyphenum
->
glyphs
[
g
].
num_layers
==
0
)
{
glyphenum
->
glyphindices
[
g
]
=
glyphenum
->
glyphs
[
g
].
glyph
;
first_glyph
=
min
(
first_glyph
,
g
);
}
else
glyphenum
->
glyphindices
[
g
]
=
1
;
glyphenum
->
color_advances
[
g
]
=
glyphenum
->
advances
[
g
];
}
colorrun
->
baselineOriginX
=
glyphenum
->
origin_x
+
get_glyph_origin
(
glyphenum
,
first_glyph
);
colorrun
->
baselineOriginY
=
glyphenum
->
origin_y
;
colorrun
->
glyphRun
.
glyphCount
=
glyphenum
->
run
.
glyphCount
;
colorrun
->
paletteIndex
=
0xffff
;
memset
(
&
colorrun
->
runColor
,
0
,
sizeof
(
colorrun
->
runColor
));
glyphenum
->
has_regular_glyphs
=
FALSE
;
return
TRUE
;
}
else
{
colorrun
->
glyphRun
.
glyphCount
=
0
;
got_palette_index
=
FALSE
;
}
advance_adj
=
0
.
0
f
;
for
(
g
=
0
;
g
<
glyphenum
->
run
.
glyphCount
;
g
++
)
{
glyphenum
->
glyphindices
[
g
]
=
1
;
/* all glyph layers were returned */
if
(
glyphenum
->
glyphs
[
g
].
layer
==
glyphenum
->
glyphs
[
g
].
num_layers
)
{
advance_adj
+=
glyphenum
->
advances
[
g
];
continue
;
}
if
(
glyphenum
->
current_layer
==
glyphenum
->
glyphs
[
g
].
layer
&&
(
!
got_palette_index
||
colorrun
->
paletteIndex
==
glyphenum
->
glyphs
[
g
].
palette_index
))
{
UINT32
index
=
colorrun
->
glyphRun
.
glyphCount
;
if
(
!
got_palette_index
)
{
colorrun
->
paletteIndex
=
glyphenum
->
glyphs
[
g
].
palette_index
;
/* use foreground color or request one from the font */
if
(
colorrun
->
paletteIndex
==
0xffff
)
memset
(
&
colorrun
->
runColor
,
0
,
sizeof
(
colorrun
->
runColor
));
else
IDWriteFontFace2_GetPaletteEntries
(
glyphenum
->
fontface
,
glyphenum
->
palette
,
colorrun
->
paletteIndex
,
1
,
&
colorrun
->
runColor
);
/* found a glyph position new color run starts from, origin is "original origin + distance to this glyph" */
colorrun
->
baselineOriginX
=
glyphenum
->
origin_x
+
get_glyph_origin
(
glyphenum
,
g
);
colorrun
->
baselineOriginY
=
glyphenum
->
origin_y
;
glyphenum
->
color_advances
[
index
]
=
glyphenum
->
advances
[
g
];
got_palette_index
=
TRUE
;
}
glyphenum
->
glyphindices
[
index
]
=
glyphenum
->
glyphs
[
g
].
glyph
;
opentype_colr_next_glyph
(
glyphenum
->
colr
.
data
,
glyphenum
->
glyphs
+
g
);
if
(
index
)
glyphenum
->
color_advances
[
index
-
1
]
+=
advance_adj
;
colorrun
->
glyphRun
.
glyphCount
++
;
advance_adj
=
0
.
0
f
;
}
else
advance_adj
+=
glyphenum
->
advances
[
g
];
}
/* reset last advance */
if
(
colorrun
->
glyphRun
.
glyphCount
)
glyphenum
->
color_advances
[
colorrun
->
glyphRun
.
glyphCount
-
1
]
=
0
.
0
f
;
return
colorrun
->
glyphRun
.
glyphCount
>
0
;
}
static
HRESULT
WINAPI
colorglyphenum_MoveNext
(
IDWriteColorGlyphRunEnumerator
*
iface
,
BOOL
*
has_run
)
{
struct
dwrite_colorglyphenum
*
This
=
impl_from_IDWriteColorGlyphRunEnumerator
(
iface
);
FIXME
(
"(%p)->(%p): stub
\n
"
,
This
,
has_run
);
return
E_NOTIMPL
;
TRACE
(
"(%p)->(%p)
\n
"
,
This
,
has_run
);
*
has_run
=
FALSE
;
This
->
colorrun
.
glyphRun
.
glyphCount
=
0
;
while
(
This
->
current_layer
<
This
->
max_layer_num
)
{
if
(
colorglyphenum_build_color_run
(
This
))
break
;
else
This
->
current_layer
++
;
}
*
has_run
=
This
->
colorrun
.
glyphRun
.
glyphCount
>
0
;
return
S_OK
;
}
static
HRESULT
WINAPI
colorglyphenum_GetCurrentRun
(
IDWriteColorGlyphRunEnumerator
*
iface
,
DWRITE_COLOR_GLYPH_RUN
const
**
run
)
{
struct
dwrite_colorglyphenum
*
This
=
impl_from_IDWriteColorGlyphRunEnumerator
(
iface
);
FIXME
(
"(%p)->(%p): stub
\n
"
,
This
,
run
);
return
E_NOTIMPL
;
TRACE
(
"(%p)->(%p)
\n
"
,
This
,
run
);
if
(
This
->
colorrun
.
glyphRun
.
glyphCount
==
0
)
{
*
run
=
NULL
;
return
E_NOT_VALID_STATE
;
}
*
run
=
&
This
->
colorrun
;
return
S_OK
;
}
static
const
IDWriteColorGlyphRunEnumeratorVtbl
colorglyphenumvtbl
=
{
...
...
@@ -4639,12 +4778,13 @@ static const IDWriteColorGlyphRunEnumeratorVtbl colorglyphenumvtbl = {
};
HRESULT
create_colorglyphenum
(
FLOAT
originX
,
FLOAT
originY
,
const
DWRITE_GLYPH_RUN
*
run
,
const
DWRITE_GLYPH_RUN_DESCRIPTION
*
rundescr
,
DWRITE_MEASURING_MODE
mode
,
const
DWRITE_MATRIX
*
transform
,
UINT32
palette
,
IDWriteColorGlyphRunEnumerator
**
ret
)
DWRITE_MEASURING_MODE
m
easuring_m
ode
,
const
DWRITE_MATRIX
*
transform
,
UINT32
palette
,
IDWriteColorGlyphRunEnumerator
**
ret
)
{
struct
dwrite_colorglyphenum
*
colorglyphenum
;
BOOL
colorfont
,
has_colored_glyph
;
IDWriteFontFace2
*
fontface2
;
BOOL
colorfont
;
HRESULT
hr
;
UINT32
i
;
*
ret
=
NULL
;
...
...
@@ -4655,17 +4795,100 @@ HRESULT create_colorglyphenum(FLOAT originX, FLOAT originY, const DWRITE_GLYPH_R
}
colorfont
=
IDWriteFontFace2_IsColorFont
(
fontface2
)
&&
IDWriteFontFace2_GetColorPaletteCount
(
fontface2
)
>
palette
;
IDWriteFontFace2_Release
(
fontface2
);
if
(
!
colorfont
)
return
DWRITE_E_NOCOLOR
;
if
(
!
colorfont
)
{
hr
=
DWRITE_E_NOCOLOR
;
goto
failed
;
}
colorglyphenum
=
heap_alloc
(
sizeof
(
*
colorglyphenum
));
if
(
!
colorglyphenum
)
return
E_OUTOFMEMORY
;
colorglyphenum
=
heap_alloc_zero
(
sizeof
(
*
colorglyphenum
));
if
(
!
colorglyphenum
)
{
hr
=
E_OUTOFMEMORY
;
goto
failed
;
}
colorglyphenum
->
IDWriteColorGlyphRunEnumerator_iface
.
lpVtbl
=
&
colorglyphenumvtbl
;
colorglyphenum
->
ref
=
1
;
colorglyphenum
->
origin_x
=
originX
;
colorglyphenum
->
origin_y
=
originY
;
colorglyphenum
->
fontface
=
fontface2
;
colorglyphenum
->
glyphs
=
NULL
;
colorglyphenum
->
run
=
*
run
;
colorglyphenum
->
run
.
glyphIndices
=
NULL
;
colorglyphenum
->
run
.
glyphAdvances
=
NULL
;
colorglyphenum
->
run
.
glyphOffsets
=
NULL
;
colorglyphenum
->
palette
=
palette
;
memset
(
&
colorglyphenum
->
colr
,
0
,
sizeof
(
colorglyphenum
->
colr
));
colorglyphenum
->
colr
.
exists
=
TRUE
;
get_fontface_table
(
fontface2
,
MS_COLR_TAG
,
&
colorglyphenum
->
colr
);
colorglyphenum
->
current_layer
=
0
;
colorglyphenum
->
max_layer_num
=
0
;
colorglyphenum
->
glyphs
=
heap_alloc_zero
(
run
->
glyphCount
*
sizeof
(
*
colorglyphenum
->
glyphs
));
has_colored_glyph
=
FALSE
;
colorglyphenum
->
has_regular_glyphs
=
FALSE
;
for
(
i
=
0
;
i
<
run
->
glyphCount
;
i
++
)
{
if
(
opentype_get_colr_glyph
(
colorglyphenum
->
colr
.
data
,
run
->
glyphIndices
[
i
],
colorglyphenum
->
glyphs
+
i
)
==
S_OK
)
{
colorglyphenum
->
max_layer_num
=
max
(
colorglyphenum
->
max_layer_num
,
colorglyphenum
->
glyphs
[
i
].
num_layers
);
has_colored_glyph
=
TRUE
;
}
if
(
colorglyphenum
->
glyphs
[
i
].
num_layers
==
0
)
colorglyphenum
->
has_regular_glyphs
=
TRUE
;
}
/* It's acceptable to have a subset of glyphs mapped to color layers, for regular runs client
is supposed to proceed normally, like if font had no color info at all. */
if
(
!
has_colored_glyph
)
{
IDWriteColorGlyphRunEnumerator_Release
(
&
colorglyphenum
->
IDWriteColorGlyphRunEnumerator_iface
);
return
DWRITE_E_NOCOLOR
;
}
colorglyphenum
->
advances
=
heap_alloc
(
run
->
glyphCount
*
sizeof
(
FLOAT
));
colorglyphenum
->
color_advances
=
heap_alloc
(
run
->
glyphCount
*
sizeof
(
FLOAT
));
colorglyphenum
->
glyphindices
=
heap_alloc
(
run
->
glyphCount
*
sizeof
(
UINT16
));
colorglyphenum
->
colorrun
.
glyphRun
.
glyphIndices
=
colorglyphenum
->
glyphindices
;
colorglyphenum
->
colorrun
.
glyphRun
.
glyphAdvances
=
colorglyphenum
->
color_advances
;
colorglyphenum
->
colorrun
.
glyphRun
.
glyphOffsets
=
NULL
;
/* FIXME */
colorglyphenum
->
colorrun
.
glyphRunDescription
=
NULL
;
/* FIXME */
if
(
run
->
glyphAdvances
)
memcpy
(
colorglyphenum
->
advances
,
run
->
glyphAdvances
,
run
->
glyphCount
*
sizeof
(
FLOAT
));
else
{
DWRITE_FONT_METRICS
metrics
;
IDWriteFontFace_GetMetrics
(
run
->
fontFace
,
&
metrics
);
for
(
i
=
0
;
i
<
run
->
glyphCount
;
i
++
)
{
HRESULT
hr
;
INT32
a
;
switch
(
measuring_mode
)
{
case
DWRITE_MEASURING_MODE_NATURAL
:
hr
=
IDWriteFontFace2_GetDesignGlyphAdvances
(
fontface2
,
1
,
run
->
glyphIndices
+
i
,
&
a
,
run
->
isSideways
);
if
(
FAILED
(
hr
))
a
=
0
;
colorglyphenum
->
advances
[
i
]
=
get_scaled_advance_width
(
a
,
run
->
fontEmSize
,
&
metrics
);
break
;
case
DWRITE_MEASURING_MODE_GDI_CLASSIC
:
case
DWRITE_MEASURING_MODE_GDI_NATURAL
:
hr
=
IDWriteFontFace2_GetGdiCompatibleGlyphAdvances
(
fontface2
,
run
->
fontEmSize
,
1
.
0
f
,
transform
,
measuring_mode
==
DWRITE_MEASURING_MODE_GDI_NATURAL
,
run
->
isSideways
,
1
,
run
->
glyphIndices
+
i
,
&
a
);
if
(
FAILED
(
hr
))
colorglyphenum
->
advances
[
i
]
=
0
.
0
f
;
else
colorglyphenum
->
advances
[
i
]
=
floorf
(
a
*
run
->
fontEmSize
/
metrics
.
designUnitsPerEm
+
0
.
5
f
);
break
;
default:
;
}
}
}
*
ret
=
&
colorglyphenum
->
IDWriteColorGlyphRunEnumerator_iface
;
return
S_OK
;
failed:
IDWriteFontFace2_Release
(
fontface2
);
return
hr
;
}
dlls/dwrite/opentype.c
View file @
8a4db9fd
...
...
@@ -707,6 +707,29 @@ struct CPAL_ColorRecord
BYTE
alpha
;
};
/* COLR table */
struct
COLR_Header
{
USHORT
version
;
USHORT
numBaseGlyphRecords
;
ULONG
offsetBaseGlyphRecord
;
ULONG
offsetLayerRecord
;
USHORT
numLayerRecords
;
};
struct
COLR_BaseGlyphRecord
{
USHORT
GID
;
USHORT
firstLayerIndex
;
USHORT
numLayers
;
};
struct
COLR_LayerRecord
{
USHORT
GID
;
USHORT
paletteIndex
;
};
BOOL
is_face_type_supported
(
DWRITE_FONT_FACE_TYPE
type
)
{
return
(
type
==
DWRITE_FONT_FACE_TYPE_CFF
)
||
...
...
@@ -1692,3 +1715,64 @@ HRESULT opentype_get_cpal_entries(const void *cpal, UINT32 palette, UINT32 first
return
S_OK
;
}
static
int
colr_compare_gid
(
const
void
*
g
,
const
void
*
r
)
{
const
struct
COLR_BaseGlyphRecord
*
record
=
r
;
UINT16
glyph
=
*
(
UINT16
*
)
g
,
GID
=
GET_BE_WORD
(
record
->
GID
);
int
ret
=
0
;
if
(
glyph
>
GID
)
ret
=
1
;
else
if
(
glyph
<
GID
)
ret
=
-
1
;
return
ret
;
}
HRESULT
opentype_get_colr_glyph
(
const
void
*
colr
,
UINT16
glyph
,
struct
dwrite_colorglyph
*
ret
)
{
const
struct
COLR_BaseGlyphRecord
*
record
;
const
struct
COLR_Header
*
header
=
colr
;
const
struct
COLR_LayerRecord
*
layer
;
DWORD
layerrecordoffset
=
GET_BE_DWORD
(
header
->
offsetLayerRecord
);
DWORD
baserecordoffset
=
GET_BE_DWORD
(
header
->
offsetBaseGlyphRecord
);
WORD
numbaserecords
=
GET_BE_WORD
(
header
->
numBaseGlyphRecords
);
record
=
bsearch
(
&
glyph
,
(
BYTE
*
)
colr
+
baserecordoffset
,
numbaserecords
,
sizeof
(
struct
COLR_BaseGlyphRecord
),
colr_compare_gid
);
if
(
!
record
)
{
ret
->
layer
=
0
;
ret
->
first_layer
=
0
;
ret
->
num_layers
=
0
;
ret
->
glyph
=
glyph
;
ret
->
palette_index
=
0xffff
;
return
S_FALSE
;
}
ret
->
layer
=
0
;
ret
->
first_layer
=
GET_BE_WORD
(
record
->
firstLayerIndex
);
ret
->
num_layers
=
GET_BE_WORD
(
record
->
numLayers
);
layer
=
(
struct
COLR_LayerRecord
*
)((
BYTE
*
)
colr
+
layerrecordoffset
)
+
ret
->
first_layer
+
ret
->
layer
;
ret
->
glyph
=
GET_BE_WORD
(
layer
->
GID
);
ret
->
palette_index
=
GET_BE_WORD
(
layer
->
paletteIndex
);
return
S_OK
;
}
void
opentype_colr_next_glyph
(
const
void
*
colr
,
struct
dwrite_colorglyph
*
glyph
)
{
const
struct
COLR_Header
*
header
=
colr
;
const
struct
COLR_LayerRecord
*
layer
;
DWORD
layerrecordoffset
=
GET_BE_DWORD
(
header
->
offsetLayerRecord
);
/* iterated all the way through */
if
(
glyph
->
layer
==
glyph
->
num_layers
)
return
;
glyph
->
layer
++
;
layer
=
(
struct
COLR_LayerRecord
*
)((
BYTE
*
)
colr
+
layerrecordoffset
)
+
glyph
->
first_layer
+
glyph
->
layer
;
glyph
->
glyph
=
GET_BE_WORD
(
layer
->
GID
);
glyph
->
palette_index
=
GET_BE_WORD
(
layer
->
paletteIndex
);
}
dlls/dwrite/tests/font.c
View file @
8a4db9fd
...
...
@@ -5207,7 +5207,6 @@ static void test_TranslateColorGlyphRun(void)
while
(
1
)
{
hasrun
=
FALSE
;
hr
=
IDWriteColorGlyphRunEnumerator_MoveNext
(
layers
,
&
hasrun
);
todo_wine
ok
(
hr
==
S_OK
,
"got 0x%08x
\n
"
,
hr
);
if
(
!
hasrun
)
...
...
@@ -5216,7 +5215,6 @@ static void test_TranslateColorGlyphRun(void)
/* iterated all way through */
hr
=
IDWriteColorGlyphRunEnumerator_GetCurrentRun
(
layers
,
&
colorrun
);
todo_wine
ok
(
hr
==
E_NOT_VALID_STATE
,
"got 0x%08x
\n
"
,
hr
);
IDWriteColorGlyphRunEnumerator_Release
(
layers
);
...
...
@@ -5247,10 +5245,9 @@ todo_wine
layers
=
(
void
*
)
0xdeadbeef
;
hr
=
IDWriteFactory2_TranslateColorGlyphRun
(
factory2
,
0
.
0
,
0
.
0
,
&
run
,
NULL
,
DWRITE_MEASURING_MODE_NATURAL
,
NULL
,
0
,
&
layers
);
todo_wine
{
ok
(
hr
==
DWRITE_E_NOCOLOR
,
"got 0x%08x
\n
"
,
hr
);
ok
(
layers
==
NULL
,
"got %p
\n
"
,
layers
);
}
/* one glyph with, one without */
codepoints
[
0
]
=
'A'
;
codepoints
[
1
]
=
0x26c4
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment