gdiplus: Store graphics clipping region in device coordinates.
This commit is contained in:
parent
c8ebd4ade0
commit
14f34c15d1
|
@ -166,7 +166,7 @@ struct GpGraphics{
|
|||
REAL xres, yres;
|
||||
GpMatrix worldtrans; /* world transform */
|
||||
BOOL busy; /* hdc handle obtained by GdipGetDC */
|
||||
GpRegion *clip;
|
||||
GpRegion *clip; /* in device coords */
|
||||
UINT textcontrast; /* not used yet. get/set only */
|
||||
struct list containers;
|
||||
GraphicsContainer contid; /* last-issued container ID */
|
||||
|
|
|
@ -51,6 +51,29 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text,
|
|||
GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
|
||||
INT flags, GDIPCONST GpMatrix *matrix);
|
||||
|
||||
static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
||||
GpCoordinateSpace src_space, GpMatrix *matrix);
|
||||
|
||||
static void transform_rectf(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
||||
GpCoordinateSpace src_space, GpRectF *rect)
|
||||
{
|
||||
GpPointF pt[3];
|
||||
|
||||
pt[0].X = rect->X;
|
||||
pt[0].Y = rect->Y;
|
||||
pt[1].X = rect->X + rect->Width;
|
||||
pt[1].Y = rect->Y;
|
||||
pt[2].X = rect->X;
|
||||
pt[2].Y = rect->Y + rect->Height;
|
||||
GdipTransformPoints(graphics, dst_space, src_space, pt, 3);
|
||||
rect->X = pt[0].X;
|
||||
rect->Y = pt[0].Y;
|
||||
rect->Width = sqrt((pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y) +
|
||||
(pt[1].X - pt[0].X) * (pt[1].X - pt[0].X));
|
||||
rect->Height = sqrt((pt[2].Y - pt[0].Y) * (pt[2].Y - pt[0].Y) +
|
||||
(pt[2].X - pt[0].X) * (pt[2].X - pt[0].X));
|
||||
}
|
||||
|
||||
/* Converts from gdiplus path point type to gdi path point type. */
|
||||
static BYTE convert_path_point_type(BYTE type)
|
||||
{
|
||||
|
@ -283,9 +306,6 @@ static void restore_dc(GpGraphics *graphics, INT state)
|
|||
RestoreDC(graphics->hdc, state);
|
||||
}
|
||||
|
||||
static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
||||
GpCoordinateSpace src_space, GpMatrix *matrix);
|
||||
|
||||
/* This helper applies all the changes that the points listed in ptf need in
|
||||
* order to be drawn on the device context. In the end, this should include at
|
||||
* least:
|
||||
|
@ -349,7 +369,8 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_
|
|||
|
||||
static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
|
||||
{
|
||||
return GdipGetRegionHRgn(graphics->clip, graphics, hrgn);
|
||||
/* clipping region is in device coords */
|
||||
return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
|
||||
}
|
||||
|
||||
/* Draw non-premultiplied ARGB data to the given graphics object */
|
||||
|
@ -4046,6 +4067,8 @@ GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
|
|||
*/
|
||||
GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
|
||||
{
|
||||
GpStatus status;
|
||||
|
||||
TRACE("(%p, %p)\n", graphics, rect);
|
||||
|
||||
if(!graphics)
|
||||
|
@ -4054,7 +4077,11 @@ GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
|
|||
if(graphics->busy)
|
||||
return ObjectBusy;
|
||||
|
||||
return GdipGetRegionBounds(graphics->clip, graphics, rect);
|
||||
status = GdipGetRegionBounds(graphics->clip, graphics, rect);
|
||||
if (status == Ok)
|
||||
transform_rectf(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, rect);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -5397,11 +5424,15 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
|
|||
if(!graphics)
|
||||
return InvalidParameter;
|
||||
|
||||
if(graphics->busy)
|
||||
return ObjectBusy;
|
||||
|
||||
/* hrgn is already in device units */
|
||||
status = GdipCreateRegionHrgn(hrgn, ®ion);
|
||||
if(status != Ok)
|
||||
return status;
|
||||
|
||||
status = GdipSetClipRegion(graphics, region, mode);
|
||||
status = GdipCombineRegionRegion(graphics->clip, region, mode);
|
||||
|
||||
GdipDeleteRegion(region);
|
||||
return status;
|
||||
|
@ -5409,6 +5440,9 @@ GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode
|
|||
|
||||
GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
|
||||
{
|
||||
GpStatus status;
|
||||
GpPath *clip_path;
|
||||
|
||||
TRACE("(%p, %p, %d)\n", graphics, path, mode);
|
||||
|
||||
if(!graphics)
|
||||
|
@ -5417,7 +5451,20 @@ GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineM
|
|||
if(graphics->busy)
|
||||
return ObjectBusy;
|
||||
|
||||
return GdipCombineRegionPath(graphics->clip, path, mode);
|
||||
status = GdipClonePath(path, &clip_path);
|
||||
if (status == Ok)
|
||||
{
|
||||
GpMatrix world_to_device;
|
||||
|
||||
get_graphics_transform(graphics, CoordinateSpaceDevice,
|
||||
CoordinateSpaceWorld, &world_to_device);
|
||||
status = GdipTransformPath(clip_path, &world_to_device);
|
||||
if (status == Ok)
|
||||
GdipCombineRegionPath(graphics->clip, clip_path, mode);
|
||||
|
||||
GdipDeletePath(clip_path);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
|
||||
|
@ -5438,6 +5485,7 @@ GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
|
|||
rect.Y = y;
|
||||
rect.Width = width;
|
||||
rect.Height = height;
|
||||
transform_rectf(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &rect);
|
||||
|
||||
return GdipCombineRegionRect(graphics->clip, &rect, mode);
|
||||
}
|
||||
|
@ -5460,6 +5508,9 @@ GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
|
|||
GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
|
||||
CombineMode mode)
|
||||
{
|
||||
GpStatus status;
|
||||
GpRegion *clip;
|
||||
|
||||
TRACE("(%p, %p, %d)\n", graphics, region, mode);
|
||||
|
||||
if(!graphics || !region)
|
||||
|
@ -5468,7 +5519,19 @@ GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
|
|||
if(graphics->busy)
|
||||
return ObjectBusy;
|
||||
|
||||
return GdipCombineRegionRegion(graphics->clip, region, mode);
|
||||
status = GdipCloneRegion(region, &clip);
|
||||
if (status == Ok)
|
||||
{
|
||||
GpMatrix world_to_device;
|
||||
|
||||
get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
|
||||
status = GdipTransformRegion(clip, &world_to_device);
|
||||
if (status == Ok)
|
||||
status = GdipCombineRegionRegion(graphics->clip, clip, mode);
|
||||
|
||||
GdipDeleteRegion(clip);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
|
||||
|
@ -5726,6 +5789,7 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
|
|||
{
|
||||
GpRegion *clip;
|
||||
GpStatus status;
|
||||
GpMatrix device_to_world;
|
||||
|
||||
TRACE("(%p, %p)\n", graphics, region);
|
||||
|
||||
|
@ -5738,6 +5802,14 @@ GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
|
|||
if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
|
||||
return status;
|
||||
|
||||
get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
|
||||
status = GdipTransformRegion(clip, &device_to_world);
|
||||
if (status != Ok)
|
||||
{
|
||||
GdipDeleteRegion(clip);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* free everything except root node and header */
|
||||
delete_element(®ion->node);
|
||||
memcpy(region, clip, sizeof(GpRegion));
|
||||
|
|
|
@ -4594,7 +4594,6 @@ static void test_clipping(void)
|
|||
|
||||
status = GdipGetClipBounds(graphics, &rect);
|
||||
expect(Ok, status);
|
||||
todo_wine
|
||||
ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
|
||||
"expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
|
@ -4604,7 +4603,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
status = GdipGetRegionBounds(region, graphics, &rect);
|
||||
expect(Ok, status);
|
||||
todo_wine
|
||||
ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
|
||||
"expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
|
@ -4617,7 +4615,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 45 && rc.top == 20 && rc.right == 95 && rc.bottom == 45,
|
||||
"expected 45,20-95,45, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -4626,7 +4623,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -4670,7 +4666,6 @@ todo_wine
|
|||
|
||||
status = GdipGetClipBounds(graphics, &rect);
|
||||
expect(Ok, status);
|
||||
todo_wine
|
||||
ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
|
||||
"expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
|
@ -4680,7 +4675,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
status = GdipGetRegionBounds(region, graphics, &rect);
|
||||
expect(Ok, status);
|
||||
todo_wine
|
||||
ok(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0,
|
||||
"expected 45.0,20.0-50.0,25.0, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
|
@ -4693,7 +4687,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 45 && rc.top == 20 && rc.right == 95 && rc.bottom == 45,
|
||||
"expected 45,20-95,45, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -4702,7 +4695,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -4746,7 +4738,6 @@ todo_wine
|
|||
|
||||
status = GdipGetClipBounds(graphics, &rect);
|
||||
expect(Ok, status);
|
||||
todo_wine
|
||||
ok((rect.X == 13.75 && rect.Y == 4.375 && rect.Width == 18.75 && rect.Height == 9.375) ||
|
||||
broken(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0) /* before Win7 */,
|
||||
"expected 13.75,4.375-18.75,9.375, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
@ -4757,8 +4748,9 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
status = GdipGetRegionBounds(region, graphics, &rect);
|
||||
expect(Ok, status);
|
||||
todo_wine
|
||||
ok((rect.X == 13.75 && rect.Y == 4.375 && rect.Width == 18.75 && rect.Height == 9.375) ||
|
||||
/* rounding under Wine is slightly different */
|
||||
(rect.X == 14.0 && rect.Y == 4.0 && rect.Width == 19.0 && rect.Height == 10.0) /* Wine */ ||
|
||||
broken(rect.X == 45.0 && rect.Y == 20.0 && rect.Width == 50.0 && rect.Height == 25.0) /* before Win7 */,
|
||||
"expected 13.75,4.375-18.75,9.375, got %.2f,%.2f-%.2f,%.2f\n", rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
|
@ -4771,8 +4763,9 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 14 && rc.top == 5 && rc.right == 33 && rc.bottom == 14) ||
|
||||
/* rounding under Wine is slightly different */
|
||||
(rc.left == 14 && rc.top == 4 && rc.right == 33 && rc.bottom == 14) /* Wine */ ||
|
||||
broken(rc.left == 45 && rc.top == 20 && rc.right == 95 && rc.bottom == 45) /* before Win7 */,
|
||||
"expected 14,5-33,14, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -4781,7 +4774,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) ||
|
||||
broken(rc.left == 267 && rc.top == 267 && rc.right == 534 && rc.bottom == 534) /* before Win7 */,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
@ -4962,7 +4954,6 @@ static void test_clipping_2(void)
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 7200 && rc.top == 7200 && rc.right == 14400 && rc.bottom == 14400) ||
|
||||
broken(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) /* before Win7 */,
|
||||
"expected 7200,7200-14400,14400, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
@ -4971,7 +4962,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 9600 && rc.top == 9600 && rc.right == 19200 && rc.bottom == 19200) ||
|
||||
broken(rc.left == 134 && rc.top == 134 && rc.right == 267 && rc.bottom == 267) /* before Win7 */,
|
||||
"expected 9600,9600-19200,19200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
@ -5053,7 +5043,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 75 && rc.top == 75 && rc.right == 150 && rc.bottom == 150) ||
|
||||
broken(rc.left == 2 && rc.top == 2 && rc.right == 3 && rc.bottom == 3) /* before Win7 */,
|
||||
"expected 75,75-150,150, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
@ -5062,7 +5051,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) ||
|
||||
broken(rc.left == 2 && rc.top == 2 && rc.right == 3 && rc.bottom == 3) /* before Win7 */,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
@ -5099,7 +5087,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 65 && rc.top == 65 && rc.right == 140 && rc.bottom == 140,
|
||||
"expected 65,65-140,140, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -5107,7 +5094,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -5137,7 +5123,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 300 && rc.top == 150 && rc.right == 600 && rc.bottom == 300,
|
||||
"expected 300,150-600,300, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -5145,7 +5130,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok(rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
DeleteObject(hrgn);
|
||||
|
@ -5170,7 +5154,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 150 && rc.top == 75 && rc.right == 300 && rc.bottom == 150) ||
|
||||
broken(rc.left == 300 && rc.top == 150 && rc.right == 600 && rc.bottom == 300) /* before Win7 */,
|
||||
"expected 150,75-300,150, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
@ -5179,7 +5162,6 @@ todo_wine
|
|||
expect(Ok, status);
|
||||
ret = GetRgnBox(hrgn, &rc);
|
||||
ok(ret == SIMPLEREGION, "expected SIMPLEREGION, got %d\n", ret);
|
||||
todo_wine
|
||||
ok((rc.left == 100 && rc.top == 100 && rc.right == 200 && rc.bottom == 200) ||
|
||||
broken(rc.left == 200 && rc.top == 200 && rc.right == 400 && rc.bottom == 400) /* before Win7 */,
|
||||
"expected 100,100-200,200, got %d,%d-%d,%d\n", rc.left, rc.top, rc.right, rc.bottom);
|
||||
|
|
Loading…
Reference in New Issue