mirror of
https://github.com/reactos/reactos.git
synced 2024-09-28 21:44:31 +00:00
[WIN32SS][FREETYPE] Fix performance regression FreeBASIC console output CORE-16177
By restoring historic state of GreExtTextOutW() / IntExtTextOutW() This reverts guilty rev 0.4.12-dev-190-gd6cfeaef51
as well as some follow ups that dealt with new errors popping up afterwards. The revert got ack of Katayama Hirofumi MZ. Test VBox on master: https://reactos.org/testman/compare.php?ids=68471,68475 Test KVM on master: https://reactos.org/testman/compare.php?ids=68472,68476 Analog revert was applied before to 0.4.12-RC-42-g021f498cb2
This commit is contained in:
parent
019e3ffe5f
commit
b5478a7873
|
@ -5618,7 +5618,7 @@ IntExtTextOutW(
|
||||||
FT_GlyphSlot glyph;
|
FT_GlyphSlot glyph;
|
||||||
FT_BitmapGlyph realglyph;
|
FT_BitmapGlyph realglyph;
|
||||||
LONGLONG TextLeft, RealXStart;
|
LONGLONG TextLeft, RealXStart;
|
||||||
ULONG TextTop, previous;
|
ULONG TextTop, previous, BackgroundLeft;
|
||||||
FT_Bool use_kerning;
|
FT_Bool use_kerning;
|
||||||
RECTL DestRect, MaskRect;
|
RECTL DestRect, MaskRect;
|
||||||
POINTL SourcePoint, BrushOrigin;
|
POINTL SourcePoint, BrushOrigin;
|
||||||
|
@ -5642,7 +5642,6 @@ IntExtTextOutW(
|
||||||
BOOL EmuBold, EmuItalic;
|
BOOL EmuBold, EmuItalic;
|
||||||
int thickness;
|
int thickness;
|
||||||
BOOL bResult;
|
BOOL bResult;
|
||||||
ULONGLONG TextWidth;
|
|
||||||
|
|
||||||
/* Check if String is valid */
|
/* Check if String is valid */
|
||||||
if ((Count > 0xFFFF) || (Count > 0 && String == NULL))
|
if ((Count > 0xFFFF) || (Count > 0 && String == NULL))
|
||||||
|
@ -5812,95 +5811,107 @@ IntExtTextOutW(
|
||||||
yoff = fixAscender >> 6;
|
yoff = fixAscender >> 6;
|
||||||
#undef VALIGN_MASK
|
#undef VALIGN_MASK
|
||||||
|
|
||||||
|
use_kerning = FT_HAS_KERNING(face);
|
||||||
|
previous = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the horizontal alignment and modify XStart accordingly.
|
||||||
|
*/
|
||||||
|
DxShift = fuOptions & ETO_PDY ? 1 : 0;
|
||||||
|
if (pdcattr->lTextAlign & (TA_RIGHT | TA_CENTER))
|
||||||
|
{
|
||||||
|
ULONGLONG TextWidth = 0;
|
||||||
|
LPCWSTR TempText = String;
|
||||||
|
int iStart;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate width of the text.
|
* Calculate width of the text.
|
||||||
*/
|
*/
|
||||||
TextWidth = 0;
|
|
||||||
DxShift = fuOptions & ETO_PDY ? 1 : 0;
|
|
||||||
use_kerning = FT_HAS_KERNING(face);
|
|
||||||
previous = 0;
|
|
||||||
if ((fuOptions & ETO_OPAQUE) ||
|
|
||||||
(pdcattr->lTextAlign & (TA_CENTER | TA_RIGHT)))
|
|
||||||
{
|
|
||||||
TextLeft = RealXStart;
|
|
||||||
TextTop = YStart;
|
|
||||||
for (i = 0; i < Count; ++i)
|
|
||||||
{
|
|
||||||
glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions);
|
|
||||||
|
|
||||||
// FIXME: Use FT_LOAD_BITMAP_METRICS_ONLY or cache.
|
if (NULL != Dx)
|
||||||
|
{
|
||||||
|
iStart = Count < 2 ? 0 : Count - 2;
|
||||||
|
TextWidth = Count < 2 ? 0 : (Dx[(Count-2)<<DxShift] << 6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iStart = 0;
|
||||||
|
}
|
||||||
|
TempText = String + iStart;
|
||||||
|
|
||||||
|
for (i = iStart; i < Count; i++)
|
||||||
|
{
|
||||||
|
glyph_index = get_glyph_index_flagged(face, *TempText, ETO_GLYPH_INDEX, fuOptions);
|
||||||
|
|
||||||
|
if (EmuBold || EmuItalic)
|
||||||
|
realglyph = NULL;
|
||||||
|
else
|
||||||
|
realglyph = ftGdiGlyphCacheGet(face, glyph_index, plf->lfHeight,
|
||||||
|
RenderMode, pmxWorldToDevice);
|
||||||
|
if (!realglyph)
|
||||||
|
{
|
||||||
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
|
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
DPRINT1("Failed to load glyph! [index: %d]\n", glyph_index);
|
DPRINT1("WARNING: Failed to load and render glyph! [index: %d]\n", glyph_index);
|
||||||
IntUnLockFreeType();
|
|
||||||
bResult = FALSE;
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glyph = face->glyph;
|
glyph = face->glyph;
|
||||||
|
if (EmuBold || EmuItalic)
|
||||||
|
{
|
||||||
if (EmuBold)
|
if (EmuBold)
|
||||||
FT_GlyphSlot_Embolden(glyph);
|
FT_GlyphSlot_Embolden(glyph);
|
||||||
if (EmuItalic)
|
if (EmuItalic)
|
||||||
FT_GlyphSlot_Oblique(glyph);
|
FT_GlyphSlot_Oblique(glyph);
|
||||||
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
|
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
realglyph = ftGdiGlyphCacheSet(face,
|
||||||
|
glyph_index,
|
||||||
|
plf->lfHeight,
|
||||||
|
pmxWorldToDevice,
|
||||||
|
glyph,
|
||||||
|
RenderMode);
|
||||||
|
}
|
||||||
if (!realglyph)
|
if (!realglyph)
|
||||||
{
|
{
|
||||||
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
|
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
|
||||||
IntUnLockFreeType();
|
IntUnLockFreeType();
|
||||||
bResult = FALSE;
|
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve kerning distance and move pen position */
|
}
|
||||||
if (use_kerning && previous && glyph_index && Dx == NULL)
|
/* Retrieve kerning distance */
|
||||||
|
if (use_kerning && previous && glyph_index)
|
||||||
{
|
{
|
||||||
FT_Vector delta;
|
FT_Vector delta;
|
||||||
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
|
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
|
||||||
TextLeft += delta.x;
|
TextWidth += delta.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Dx == NULL)
|
TextWidth += realglyph->root.advance.x >> 10;
|
||||||
{
|
|
||||||
TextLeft += realglyph->root.advance.x >> 10;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// FIXME this should probably be a matrix transform with TextTop as well.
|
|
||||||
Scale = pdcattr->mxWorldToDevice.efM11;
|
|
||||||
if (FLOATOBJ_Equal0(&Scale))
|
|
||||||
FLOATOBJ_Set1(&Scale);
|
|
||||||
|
|
||||||
/* do the shift before multiplying to preserve precision */
|
if (EmuBold || EmuItalic)
|
||||||
FLOATOBJ_MulLong(&Scale, Dx[i << DxShift] << 6);
|
|
||||||
TextLeft += FLOATOBJ_GetLong(&Scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DxShift)
|
|
||||||
{
|
{
|
||||||
TextTop -= Dx[2 * i + 1] << 6;
|
FT_Done_Glyph((FT_Glyph)realglyph);
|
||||||
|
realglyph = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
previous = glyph_index;
|
previous = glyph_index;
|
||||||
|
TempText++;
|
||||||
FT_Done_Glyph((FT_Glyph)realglyph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWidth = TextLeft - RealXStart;
|
previous = 0;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Process the horizontal alignment and modify XStart accordingly.
|
|
||||||
*/
|
|
||||||
if ((pdcattr->lTextAlign & TA_CENTER) == TA_CENTER)
|
if ((pdcattr->lTextAlign & TA_CENTER) == TA_CENTER)
|
||||||
{
|
{
|
||||||
RealXStart -= TextWidth / 2;
|
RealXStart -= TextWidth / 2;
|
||||||
}
|
}
|
||||||
else if ((pdcattr->lTextAlign & TA_RIGHT) == TA_RIGHT)
|
else
|
||||||
{
|
{
|
||||||
RealXStart -= TextWidth;
|
RealXStart -= TextWidth;
|
||||||
if (((RealXStart + TextWidth + 32) >> 6) <= Start.x + dc->ptlDCOrig.x)
|
}
|
||||||
RealXStart += 1 << 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
psurf = dc->dclevel.pSurface;
|
psurf = dc->dclevel.pSurface;
|
||||||
|
@ -5924,45 +5935,113 @@ IntExtTextOutW(
|
||||||
thickness = 1;
|
thickness = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fuOptions & ETO_OPAQUE)
|
if ((fuOptions & ETO_OPAQUE) && plf->lfItalic)
|
||||||
{
|
{
|
||||||
/* Draw background */
|
/* Draw background */
|
||||||
RECTL Rect;
|
TextLeft = RealXStart;
|
||||||
|
TextTop = YStart;
|
||||||
Rect.left = (RealXStart + 32) >> 6;
|
BackgroundLeft = (RealXStart + 32) >> 6;
|
||||||
Rect.top = TextTop + yoff - ((fixAscender + 32) >> 6);
|
for (i = 0; i < Count; ++i)
|
||||||
Rect.right = (RealXStart + TextWidth + 32) >> 6;
|
|
||||||
Rect.bottom = Rect.top + ((fixAscender + fixDescender) >> 6);
|
|
||||||
|
|
||||||
if (dc->fs & (DC_ACCUM_APP | DC_ACCUM_WMGR))
|
|
||||||
{
|
{
|
||||||
IntUpdateBoundsRect(dc, &Rect);
|
glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions);
|
||||||
|
|
||||||
|
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
DPRINT1("Failed to load and render glyph! [index: %d]\n", glyph_index);
|
||||||
|
IntUnLockFreeType();
|
||||||
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdcattr->ulDirty_ & DIRTY_BACKGROUND)
|
glyph = face->glyph;
|
||||||
DC_vUpdateBackgroundBrush(dc);
|
if (EmuBold)
|
||||||
if (dc->dctype == DCTYPE_DIRECT)
|
FT_GlyphSlot_Embolden(glyph);
|
||||||
MouseSafetyOnDrawStart(dc->ppdev, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
if (EmuItalic)
|
||||||
|
FT_GlyphSlot_Oblique(glyph);
|
||||||
|
realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
|
||||||
|
if (!realglyph)
|
||||||
|
{
|
||||||
|
DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
|
||||||
|
IntUnLockFreeType();
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
SourcePoint.x = SourcePoint.y = 0;
|
/* retrieve kerning distance and move pen position */
|
||||||
BrushOrigin.x = BrushOrigin.y = 0;
|
if (use_kerning && previous && glyph_index && NULL == Dx)
|
||||||
|
{
|
||||||
|
FT_Vector delta;
|
||||||
|
FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
|
||||||
|
TextLeft += delta.x;
|
||||||
|
}
|
||||||
|
DPRINT("TextLeft: %I64d\n", TextLeft);
|
||||||
|
DPRINT("TextTop: %lu\n", TextTop);
|
||||||
|
DPRINT("Advance: %d\n", realglyph->root.advance.x);
|
||||||
|
|
||||||
psurf = dc->dclevel.pSurface;
|
DestRect.left = BackgroundLeft;
|
||||||
|
DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6;
|
||||||
|
DestRect.top = TextTop + yoff - ((fixAscender + 32) >> 6);
|
||||||
|
DestRect.bottom = DestRect.top + ((fixAscender + fixDescender) >> 6);
|
||||||
|
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
|
||||||
|
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
|
||||||
|
{
|
||||||
|
IntUpdateBoundsRect(dc, &DestRect);
|
||||||
|
}
|
||||||
IntEngBitBlt(
|
IntEngBitBlt(
|
||||||
&psurf->SurfObj,
|
&psurf->SurfObj,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
(CLIPOBJ *)&dc->co,
|
(CLIPOBJ *)&dc->co,
|
||||||
NULL,
|
NULL,
|
||||||
&Rect,
|
&DestRect,
|
||||||
&SourcePoint,
|
&SourcePoint,
|
||||||
&SourcePoint,
|
&SourcePoint,
|
||||||
&dc->eboBackground.BrushObject,
|
&dc->eboBackground.BrushObject,
|
||||||
&BrushOrigin,
|
&BrushOrigin,
|
||||||
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
|
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
|
||||||
|
|
||||||
if (dc->dctype == DCTYPE_DIRECT)
|
|
||||||
MouseSafetyOnDrawEnd(dc->ppdev);
|
MouseSafetyOnDrawEnd(dc->ppdev);
|
||||||
|
BackgroundLeft = DestRect.right;
|
||||||
|
|
||||||
|
DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left;
|
||||||
|
DestRect.right = DestRect.left + realglyph->bitmap.width;
|
||||||
|
DestRect.top = TextTop + yoff - realglyph->top;
|
||||||
|
DestRect.bottom = DestRect.top + realglyph->bitmap.rows;
|
||||||
|
|
||||||
|
bitSize.cx = realglyph->bitmap.width;
|
||||||
|
bitSize.cy = realglyph->bitmap.rows;
|
||||||
|
MaskRect.right = realglyph->bitmap.width;
|
||||||
|
MaskRect.bottom = realglyph->bitmap.rows;
|
||||||
|
|
||||||
|
if (NULL == Dx)
|
||||||
|
{
|
||||||
|
TextLeft += realglyph->root.advance.x >> 10;
|
||||||
|
DPRINT("New TextLeft: %I64d\n", TextLeft);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME this should probably be a matrix transform with TextTop as well.
|
||||||
|
Scale = pdcattr->mxWorldToDevice.efM11;
|
||||||
|
if (FLOATOBJ_Equal0(&Scale))
|
||||||
|
FLOATOBJ_Set1(&Scale);
|
||||||
|
|
||||||
|
/* do the shift before multiplying to preserve precision */
|
||||||
|
FLOATOBJ_MulLong(&Scale, Dx[i<<DxShift] << 6);
|
||||||
|
TextLeft += FLOATOBJ_GetLong(&Scale);
|
||||||
|
DPRINT("New TextLeft2: %I64d\n", TextLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DxShift)
|
||||||
|
{
|
||||||
|
TextTop -= Dx[2 * i + 1] << 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = glyph_index;
|
||||||
|
|
||||||
|
if (EmuBold || EmuItalic)
|
||||||
|
{
|
||||||
|
FT_Done_Glyph((FT_Glyph)realglyph);
|
||||||
|
realglyph = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EXLATEOBJ_vInitialize(&exloRGB2Dst, &gpalRGB, psurf->ppal, 0, 0, 0);
|
EXLATEOBJ_vInitialize(&exloRGB2Dst, &gpalRGB, psurf->ppal, 0, 0, 0);
|
||||||
|
@ -5976,7 +6055,7 @@ IntExtTextOutW(
|
||||||
*/
|
*/
|
||||||
TextLeft = RealXStart;
|
TextLeft = RealXStart;
|
||||||
TextTop = YStart;
|
TextTop = YStart;
|
||||||
previous = 0;
|
BackgroundLeft = (RealXStart + 32) >> 6;
|
||||||
for (i = 0; i < Count; ++i)
|
for (i = 0; i < Count; ++i)
|
||||||
{
|
{
|
||||||
glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions);
|
glyph_index = get_glyph_index_flagged(face, String[i], ETO_GLYPH_INDEX, fuOptions);
|
||||||
|
@ -6033,6 +6112,39 @@ IntExtTextOutW(
|
||||||
DPRINT("TextTop: %lu\n", TextTop);
|
DPRINT("TextTop: %lu\n", TextTop);
|
||||||
DPRINT("Advance: %d\n", realglyph->root.advance.x);
|
DPRINT("Advance: %d\n", realglyph->root.advance.x);
|
||||||
|
|
||||||
|
if ((fuOptions & ETO_OPAQUE) && !plf->lfItalic)
|
||||||
|
{
|
||||||
|
DestRect.left = BackgroundLeft;
|
||||||
|
DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6;
|
||||||
|
DestRect.top = TextTop + yoff - ((fixAscender + 32) >> 6);
|
||||||
|
DestRect.bottom = DestRect.top + ((fixAscender + fixDescender) >> 6);
|
||||||
|
|
||||||
|
if (dc->dctype == DCTYPE_DIRECT)
|
||||||
|
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
|
||||||
|
|
||||||
|
if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR))
|
||||||
|
{
|
||||||
|
IntUpdateBoundsRect(dc, &DestRect);
|
||||||
|
}
|
||||||
|
IntEngBitBlt(
|
||||||
|
&psurf->SurfObj,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
(CLIPOBJ *)&dc->co,
|
||||||
|
NULL,
|
||||||
|
&DestRect,
|
||||||
|
&SourcePoint,
|
||||||
|
&SourcePoint,
|
||||||
|
&dc->eboBackground.BrushObject,
|
||||||
|
&BrushOrigin,
|
||||||
|
ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
|
||||||
|
|
||||||
|
if (dc->dctype == DCTYPE_DIRECT)
|
||||||
|
MouseSafetyOnDrawEnd(dc->ppdev);
|
||||||
|
|
||||||
|
BackgroundLeft = DestRect.right;
|
||||||
|
}
|
||||||
|
|
||||||
DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left;
|
DestRect.left = ((TextLeft + 32) >> 6) + realglyph->left;
|
||||||
DestRect.right = DestRect.left + realglyph->bitmap.width;
|
DestRect.right = DestRect.left + realglyph->bitmap.width;
|
||||||
DestRect.top = TextTop + yoff - realglyph->top;
|
DestRect.top = TextTop + yoff - realglyph->top;
|
||||||
|
@ -6057,12 +6169,8 @@ IntExtTextOutW(
|
||||||
if ( !HSourceGlyph )
|
if ( !HSourceGlyph )
|
||||||
{
|
{
|
||||||
DPRINT1("WARNING: EngCreateBitmap() failed!\n");
|
DPRINT1("WARNING: EngCreateBitmap() failed!\n");
|
||||||
|
// FT_Done_Glyph(realglyph);
|
||||||
bResult = FALSE;
|
bResult = FALSE;
|
||||||
if (EmuBold || EmuItalic)
|
|
||||||
{
|
|
||||||
FT_Done_Glyph((FT_Glyph)realglyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
|
SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
|
||||||
|
@ -6071,11 +6179,6 @@ IntExtTextOutW(
|
||||||
EngDeleteSurface((HSURF)HSourceGlyph);
|
EngDeleteSurface((HSURF)HSourceGlyph);
|
||||||
DPRINT1("WARNING: EngLockSurface() failed!\n");
|
DPRINT1("WARNING: EngLockSurface() failed!\n");
|
||||||
bResult = FALSE;
|
bResult = FALSE;
|
||||||
if (EmuBold || EmuItalic)
|
|
||||||
{
|
|
||||||
FT_Done_Glyph((FT_Glyph)realglyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6124,11 +6227,6 @@ IntExtTextOutW(
|
||||||
|
|
||||||
if (DoBreak)
|
if (DoBreak)
|
||||||
{
|
{
|
||||||
if (EmuBold || EmuItalic)
|
|
||||||
{
|
|
||||||
FT_Done_Glyph((FT_Glyph)realglyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6199,10 +6297,10 @@ IntExtTextOutW(
|
||||||
|
|
||||||
previous = glyph_index;
|
previous = glyph_index;
|
||||||
|
|
||||||
/* No cache, so clean up */
|
|
||||||
if (EmuBold || EmuItalic)
|
if (EmuBold || EmuItalic)
|
||||||
{
|
{
|
||||||
FT_Done_Glyph((FT_Glyph)realglyph);
|
FT_Done_Glyph((FT_Glyph)realglyph);
|
||||||
|
realglyph = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue