2008-03-25 17:34:57 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 Google (Evan Stade)
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "wingdi.h"
|
|
|
|
#include "wine/unicode.h"
|
|
|
|
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "objbase.h"
|
|
|
|
#include "ocidl.h"
|
|
|
|
#include "olectl.h"
|
|
|
|
#include "ole2.h"
|
|
|
|
|
|
|
|
#include "winreg.h"
|
|
|
|
#include "shlwapi.h"
|
|
|
|
|
|
|
|
#include "gdiplus.h"
|
2013-12-23 18:33:45 +00:00
|
|
|
#include "gdiplus_private.h"
|
2018-03-09 12:09:03 +00:00
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "wine/list.h"
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-09-14 21:26:45 +00:00
|
|
|
/* Mike "tamlin" Nordell 2012-09-14 for ReactOS:
|
|
|
|
* NOTE: Wine uses per-GpGraphics id's ('contid' starting from zero in
|
|
|
|
* every GpGraphics). Windows seems to use process-global id's, or at
|
|
|
|
* least more unique id's.
|
|
|
|
* This have the following implications. It:
|
|
|
|
* 1. fails the current gdiplus test case.
|
|
|
|
* 2. is not what Windows does.
|
|
|
|
*
|
|
|
|
* We therefore "obfuscate" the 'contid' a little to more match Windows'
|
|
|
|
* behaviour. The observable behviour should still remain the same,
|
|
|
|
* except for handing out more "unique" id's.
|
|
|
|
*/
|
|
|
|
#define GDIP_CONTID_STEP 64
|
|
|
|
static volatile LONG g_priv_contid = GDIP_CONTID_STEP;
|
|
|
|
#define GDIP_GET_NEW_CONTID_FOR(pGpGraphics) \
|
|
|
|
(UINT)(InterlockedExchangeAdd(&g_priv_contid,GDIP_CONTID_STEP))
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
/* ReactOS FIXME: Inspect */
|
|
|
|
#define fmax max
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
/* looks-right constants */
|
|
|
|
#define ANCHOR_WIDTH (2.0)
|
|
|
|
#define MAX_ITERS (50)
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
|
|
|
|
GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
|
|
|
|
GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
|
|
|
|
INT flags, GDIPCONST GpMatrix *matrix);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
/* Converts from gdiplus path point type to gdi path point type. */
|
|
|
|
static BYTE convert_path_point_type(BYTE type)
|
|
|
|
{
|
|
|
|
BYTE ret;
|
|
|
|
|
|
|
|
switch(type & PathPointTypePathTypeMask){
|
|
|
|
case PathPointTypeBezier:
|
|
|
|
ret = PT_BEZIERTO;
|
|
|
|
break;
|
|
|
|
case PathPointTypeLine:
|
|
|
|
ret = PT_LINETO;
|
|
|
|
break;
|
|
|
|
case PathPointTypeStart:
|
|
|
|
ret = PT_MOVETO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERR("Bad point type\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(type & PathPointTypeCloseSubpath)
|
|
|
|
ret |= PT_CLOSEFIGURE;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
static COLORREF get_gdi_brush_color(const GpBrush *brush)
|
|
|
|
{
|
|
|
|
ARGB argb;
|
|
|
|
|
|
|
|
switch (brush->bt)
|
|
|
|
{
|
|
|
|
case BrushTypeSolidColor:
|
|
|
|
{
|
|
|
|
const GpSolidFill *sf = (const GpSolidFill *)brush;
|
|
|
|
argb = sf->color;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BrushTypeHatchFill:
|
|
|
|
{
|
|
|
|
const GpHatch *hatch = (const GpHatch *)brush;
|
|
|
|
argb = hatch->forecol;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BrushTypeLinearGradient:
|
|
|
|
{
|
|
|
|
const GpLineGradient *line = (const GpLineGradient *)brush;
|
|
|
|
argb = line->startcolor;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BrushTypePathGradient:
|
|
|
|
{
|
|
|
|
const GpPathGradient *grad = (const GpPathGradient *)brush;
|
|
|
|
argb = grad->centercolor;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
FIXME("unhandled brush type %d\n", brush->bt);
|
|
|
|
argb = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ARGB2COLORREF(argb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
|
|
|
|
{
|
|
|
|
HBITMAP hbmp;
|
|
|
|
BITMAPINFOHEADER bmih;
|
|
|
|
DWORD *bits;
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
bmih.biSize = sizeof(bmih);
|
|
|
|
bmih.biWidth = 8;
|
|
|
|
bmih.biHeight = 8;
|
|
|
|
bmih.biPlanes = 1;
|
|
|
|
bmih.biBitCount = 32;
|
|
|
|
bmih.biCompression = BI_RGB;
|
|
|
|
bmih.biSizeImage = 0;
|
|
|
|
|
|
|
|
hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
|
|
|
|
if (hbmp)
|
|
|
|
{
|
|
|
|
const char *hatch_data;
|
|
|
|
|
|
|
|
if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
|
|
|
|
{
|
|
|
|
for (y = 0; y < 8; y++)
|
|
|
|
{
|
|
|
|
for (x = 0; x < 8; x++)
|
|
|
|
{
|
|
|
|
if (hatch_data[y] & (0x80 >> x))
|
|
|
|
bits[y * 8 + x] = hatch->forecol;
|
|
|
|
else
|
|
|
|
bits[y * 8 + x] = hatch->backcol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
|
|
|
|
|
|
|
|
for (y = 0; y < 64; y++)
|
|
|
|
bits[y] = hatch->forecol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hbmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
|
|
|
|
{
|
|
|
|
switch (brush->bt)
|
|
|
|
{
|
|
|
|
case BrushTypeSolidColor:
|
|
|
|
{
|
|
|
|
const GpSolidFill *sf = (const GpSolidFill *)brush;
|
|
|
|
lb->lbStyle = BS_SOLID;
|
|
|
|
lb->lbColor = ARGB2COLORREF(sf->color);
|
|
|
|
lb->lbHatch = 0;
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
case BrushTypeHatchFill:
|
|
|
|
{
|
|
|
|
const GpHatch *hatch = (const GpHatch *)brush;
|
|
|
|
HBITMAP hbmp;
|
|
|
|
|
|
|
|
hbmp = create_hatch_bitmap(hatch);
|
|
|
|
if (!hbmp) return OutOfMemory;
|
|
|
|
|
|
|
|
lb->lbStyle = BS_PATTERN;
|
|
|
|
lb->lbColor = 0;
|
|
|
|
lb->lbHatch = (ULONG_PTR)hbmp;
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
FIXME("unhandled brush type %d\n", brush->bt);
|
|
|
|
lb->lbStyle = BS_SOLID;
|
|
|
|
lb->lbColor = get_gdi_brush_color(brush);
|
|
|
|
lb->lbHatch = 0;
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
|
|
|
|
{
|
|
|
|
switch (lb->lbStyle)
|
|
|
|
{
|
|
|
|
case BS_PATTERN:
|
|
|
|
DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HBRUSH create_gdi_brush(const GpBrush *brush)
|
|
|
|
{
|
|
|
|
LOGBRUSH lb;
|
|
|
|
HBRUSH gdibrush;
|
|
|
|
|
|
|
|
if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
|
|
|
|
|
|
|
|
gdibrush = CreateBrushIndirect(&lb);
|
|
|
|
free_gdi_logbrush(&lb);
|
|
|
|
|
|
|
|
return gdibrush;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
|
|
|
|
{
|
2012-05-19 10:03:40 +00:00
|
|
|
LOGBRUSH lb;
|
2008-03-25 17:34:57 +00:00
|
|
|
HPEN gdipen;
|
|
|
|
REAL width;
|
2010-11-20 11:24:17 +00:00
|
|
|
INT save_state, i, numdashes;
|
2008-03-25 17:34:57 +00:00
|
|
|
GpPointF pt[2];
|
|
|
|
DWORD dash_array[MAX_DASHLEN];
|
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
save_state = SaveDC(graphics->hdc);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
EndPath(graphics->hdc);
|
|
|
|
|
|
|
|
if(pen->unit == UnitPixel){
|
|
|
|
width = pen->width;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
/* Get an estimate for the amount the pen width is affected by the world
|
|
|
|
* transform. (This is similar to what some of the wine drivers do.) */
|
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 1.0;
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipTransformMatrixPoints(&graphics->worldtrans, pt, 2);
|
2008-03-25 17:34:57 +00:00
|
|
|
width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
|
|
|
|
(pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
width *= units_to_pixels(pen->width, pen->unit == UnitWorld ? graphics->unit : pen->unit, graphics->xres);
|
2015-07-19 13:31:15 +00:00
|
|
|
width *= graphics->scale;
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 1.0;
|
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, pt, 2);
|
|
|
|
width *= sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
|
|
|
|
(pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(pen->dash == DashStyleCustom){
|
|
|
|
numdashes = min(pen->numdashes, MAX_DASHLEN);
|
|
|
|
|
|
|
|
TRACE("dashes are: ");
|
|
|
|
for(i = 0; i < numdashes; i++){
|
2012-12-11 21:40:15 +00:00
|
|
|
dash_array[i] = gdip_round(width * pen->dashes[i]);
|
2008-03-25 17:34:57 +00:00
|
|
|
TRACE("%d, ", dash_array[i]);
|
|
|
|
}
|
|
|
|
TRACE("\n and the pen style is %x\n", pen->style);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
create_gdi_logbrush(pen->brush, &lb);
|
2012-12-11 21:40:15 +00:00
|
|
|
gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb,
|
2008-03-25 17:34:57 +00:00
|
|
|
numdashes, dash_array);
|
2012-05-19 10:03:40 +00:00
|
|
|
free_gdi_logbrush(&lb);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
else
|
2012-05-19 10:03:40 +00:00
|
|
|
{
|
|
|
|
create_gdi_logbrush(pen->brush, &lb);
|
2012-12-11 21:40:15 +00:00
|
|
|
gdipen = ExtCreatePen(pen->style, gdip_round(width), &lb, 0, NULL);
|
2012-05-19 10:03:40 +00:00
|
|
|
free_gdi_logbrush(&lb);
|
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
SelectObject(graphics->hdc, gdipen);
|
|
|
|
|
|
|
|
return save_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void restore_dc(GpGraphics *graphics, INT state)
|
|
|
|
{
|
|
|
|
DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
|
|
|
|
RestoreDC(graphics->hdc, state);
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
static void round_points(POINT *pti, GpPointF *ptf, INT count)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
2012-12-11 21:40:15 +00:00
|
|
|
pti[i].x = gdip_round(ptf[i].X);
|
|
|
|
pti[i].y = gdip_round(ptf[i].Y);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
|
|
|
|
HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
|
|
|
|
{
|
2017-05-26 08:31:10 +00:00
|
|
|
if (GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
|
|
|
|
GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
|
2012-05-19 10:03:40 +00:00
|
|
|
{
|
|
|
|
TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
|
|
|
|
|
|
|
|
StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
|
|
|
|
hdc, src_x, src_y, src_width, src_height, SRCCOPY);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BLENDFUNCTION bf;
|
|
|
|
|
|
|
|
bf.BlendOp = AC_SRC_OVER;
|
|
|
|
bf.BlendFlags = 0;
|
|
|
|
bf.SourceConstantAlpha = 255;
|
|
|
|
bf.AlphaFormat = AC_SRC_ALPHA;
|
|
|
|
|
|
|
|
GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
|
|
|
|
hdc, src_x, src_y, src_width, src_height, bf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
|
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
GpRegion *rgn;
|
|
|
|
GpMatrix transform;
|
|
|
|
GpStatus stat;
|
2018-05-27 03:10:39 +00:00
|
|
|
BOOL identity;
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceDevice, &transform);
|
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipIsMatrixIdentity(&transform, &identity);
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipCloneRegion(graphics->clip, &rgn);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2018-05-27 03:10:39 +00:00
|
|
|
if (!identity)
|
|
|
|
stat = GdipTransformRegion(rgn, &transform);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipGetRegionHRgn(rgn, NULL, hrgn);
|
|
|
|
|
|
|
|
GdipDeleteRegion(rgn);
|
|
|
|
}
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
if (stat == Ok && graphics->gdi_clip)
|
|
|
|
{
|
|
|
|
if (*hrgn)
|
|
|
|
CombineRgn(*hrgn, *hrgn, graphics->gdi_clip, RGN_AND);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*hrgn = CreateRectRgn(0,0,0,0);
|
|
|
|
CombineRgn(*hrgn, graphics->gdi_clip, graphics->gdi_clip, RGN_COPY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
return stat;
|
2012-12-11 21:40:15 +00:00
|
|
|
}
|
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
/* Draw ARGB data to the given graphics object */
|
2012-12-11 21:40:15 +00:00
|
|
|
static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
|
2015-07-19 13:31:15 +00:00
|
|
|
const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
|
|
|
|
INT x, y;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
for (y=0; y<src_height; y++)
|
2012-12-11 21:40:15 +00:00
|
|
|
{
|
2015-07-19 13:31:15 +00:00
|
|
|
for (x=0; x<src_width; x++)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
ARGB dst_color, src_color;
|
|
|
|
src_color = ((ARGB*)(src + src_stride * y))[x];
|
2015-07-19 13:31:15 +00:00
|
|
|
|
|
|
|
if (!(src_color & 0xff000000))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
|
|
|
|
if (fmt & PixelFormatPAlpha)
|
|
|
|
GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over_fgpremult(dst_color, src_color));
|
|
|
|
else
|
|
|
|
GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
|
2015-07-19 13:31:15 +00:00
|
|
|
const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
|
2012-12-11 21:40:15 +00:00
|
|
|
{
|
|
|
|
HDC hdc;
|
|
|
|
HBITMAP hbitmap;
|
|
|
|
BITMAPINFOHEADER bih;
|
|
|
|
BYTE *temp_bits;
|
|
|
|
|
|
|
|
hdc = CreateCompatibleDC(0);
|
|
|
|
|
|
|
|
bih.biSize = sizeof(BITMAPINFOHEADER);
|
|
|
|
bih.biWidth = src_width;
|
|
|
|
bih.biHeight = -src_height;
|
|
|
|
bih.biPlanes = 1;
|
|
|
|
bih.biBitCount = 32;
|
|
|
|
bih.biCompression = BI_RGB;
|
|
|
|
bih.biSizeImage = 0;
|
|
|
|
bih.biXPelsPerMeter = 0;
|
|
|
|
bih.biYPelsPerMeter = 0;
|
|
|
|
bih.biClrUsed = 0;
|
|
|
|
bih.biClrImportant = 0;
|
|
|
|
|
|
|
|
hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
|
|
|
|
(void**)&temp_bits, NULL, 0);
|
|
|
|
|
2017-05-26 08:31:10 +00:00
|
|
|
if ((GetDeviceCaps(graphics->hdc, TECHNOLOGY) == DT_RASPRINTER &&
|
|
|
|
GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE) ||
|
2015-07-19 13:31:15 +00:00
|
|
|
fmt & PixelFormatPAlpha)
|
2013-05-23 17:23:00 +00:00
|
|
|
memcpy(temp_bits, src, src_width * src_height * 4);
|
|
|
|
else
|
|
|
|
convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
|
|
|
|
4 * src_width, src, src_stride);
|
2012-12-11 21:40:15 +00:00
|
|
|
|
|
|
|
SelectObject(hdc, hbitmap);
|
|
|
|
gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
|
|
|
|
hdc, 0, 0, src_width, src_height);
|
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(hbitmap);
|
|
|
|
|
|
|
|
return Ok;
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
|
2015-07-19 13:31:15 +00:00
|
|
|
const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
|
2012-05-19 10:03:40 +00:00
|
|
|
{
|
|
|
|
GpStatus stat=Ok;
|
|
|
|
|
|
|
|
if (graphics->image && graphics->image->type == ImageTypeBitmap)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
DWORD i;
|
|
|
|
int size;
|
2012-05-19 10:03:40 +00:00
|
|
|
RGNDATA *rgndata;
|
|
|
|
RECT *rects;
|
2012-12-11 21:40:15 +00:00
|
|
|
HRGN hrgn, visible_rgn;
|
|
|
|
|
|
|
|
hrgn = CreateRectRgn(dst_x, dst_y, dst_x + src_width, dst_y + src_height);
|
|
|
|
if (!hrgn)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
stat = get_clip_hrgn(graphics, &visible_rgn);
|
|
|
|
if (stat != Ok)
|
|
|
|
{
|
|
|
|
DeleteObject(hrgn);
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (visible_rgn)
|
|
|
|
{
|
|
|
|
CombineRgn(hrgn, hrgn, visible_rgn, RGN_AND);
|
|
|
|
DeleteObject(visible_rgn);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hregion)
|
|
|
|
CombineRgn(hrgn, hrgn, hregion, RGN_AND);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
size = GetRegionData(hrgn, 0, NULL);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
rgndata = heap_alloc_zero(size);
|
2012-05-19 10:03:40 +00:00
|
|
|
if (!rgndata)
|
2012-12-11 21:40:15 +00:00
|
|
|
{
|
|
|
|
DeleteObject(hrgn);
|
2012-05-19 10:03:40 +00:00
|
|
|
return OutOfMemory;
|
2012-12-11 21:40:15 +00:00
|
|
|
}
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
GetRegionData(hrgn, size, rgndata);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
rects = (RECT*)rgndata->Buffer;
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
|
2012-05-19 10:03:40 +00:00
|
|
|
&src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
|
|
|
|
rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
|
2015-07-19 13:31:15 +00:00
|
|
|
src_stride, fmt);
|
2012-05-19 10:03:40 +00:00
|
|
|
}
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(rgndata);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
DeleteObject(hrgn);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
else if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
ERR("This should not be used for metafiles; fix caller\n");
|
|
|
|
return NotImplemented;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
HRGN hrgn;
|
2012-05-19 10:03:40 +00:00
|
|
|
int save;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = get_clip_hrgn(graphics, &hrgn);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
save = SaveDC(graphics->hdc);
|
|
|
|
|
2017-03-05 21:15:16 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
|
2012-12-11 21:40:15 +00:00
|
|
|
|
|
|
|
if (hregion)
|
|
|
|
ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
|
2015-07-19 13:31:15 +00:00
|
|
|
src_height, src_stride, fmt);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
RestoreDC(graphics->hdc, save);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
DeleteObject(hrgn);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
|
2015-07-19 13:31:15 +00:00
|
|
|
const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
|
2012-12-11 21:40:15 +00:00
|
|
|
{
|
2015-07-19 13:31:15 +00:00
|
|
|
return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
|
2012-12-11 21:40:15 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
/* NOTE: start and end pixels must be in pre-multiplied ARGB format */
|
|
|
|
static inline ARGB blend_colors_premult(ARGB start, ARGB end, REAL position)
|
|
|
|
{
|
|
|
|
UINT pos = position * 255.0f + 0.5f;
|
|
|
|
return
|
|
|
|
(((((start >> 24) ) << 8) + (((end >> 24) ) - ((start >> 24) )) * pos) >> 8) << 24 |
|
|
|
|
(((((start >> 16) & 0xff) << 8) + (((end >> 16) & 0xff) - ((start >> 16) & 0xff)) * pos) >> 8) << 16 |
|
|
|
|
(((((start >> 8) & 0xff) << 8) + (((end >> 8) & 0xff) - ((start >> 8) & 0xff)) * pos) >> 8) << 8 |
|
|
|
|
(((((start ) & 0xff) << 8) + (((end ) & 0xff) - ((start ) & 0xff)) * pos) >> 8);
|
|
|
|
}
|
|
|
|
|
2009-05-09 09:26:16 +00:00
|
|
|
static ARGB blend_colors(ARGB start, ARGB end, REAL position)
|
2009-04-06 14:58:12 +00:00
|
|
|
{
|
2015-07-19 13:31:15 +00:00
|
|
|
INT start_a, end_a, final_a;
|
|
|
|
INT pos;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
pos = (INT)(position * 255.0f + 0.5f);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
start_a = ((start >> 24) & 0xff) * (pos ^ 0xff);
|
|
|
|
end_a = ((end >> 24) & 0xff) * pos;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
final_a = start_a + end_a;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (final_a < 0xff) return 0;
|
|
|
|
|
|
|
|
return (final_a / 0xff) << 24 |
|
|
|
|
((((start >> 16) & 0xff) * start_a + (((end >> 16) & 0xff) * end_a)) / final_a) << 16 |
|
|
|
|
((((start >> 8) & 0xff) * start_a + (((end >> 8) & 0xff) * end_a)) / final_a) << 8 |
|
|
|
|
(((start & 0xff) * start_a + ((end & 0xff) * end_a)) / final_a);
|
2009-04-06 14:58:12 +00:00
|
|
|
}
|
|
|
|
|
2009-05-09 09:26:16 +00:00
|
|
|
static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
|
|
|
|
{
|
|
|
|
REAL blendfac;
|
|
|
|
|
|
|
|
/* clamp to between 0.0 and 1.0, using the wrap mode */
|
2017-09-17 12:34:15 +00:00
|
|
|
position = (position - brush->rect.X) / brush->rect.Width;
|
2009-05-09 09:26:16 +00:00
|
|
|
if (brush->wrap == WrapModeTile)
|
|
|
|
{
|
|
|
|
position = fmodf(position, 1.0f);
|
|
|
|
if (position < 0.0f) position += 1.0f;
|
|
|
|
}
|
|
|
|
else /* WrapModeFlip* */
|
|
|
|
{
|
|
|
|
position = fmodf(position, 2.0f);
|
|
|
|
if (position < 0.0f) position += 2.0f;
|
|
|
|
if (position > 1.0f) position = 2.0f - position;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (brush->blendcount == 1)
|
|
|
|
blendfac = position;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i=1;
|
|
|
|
REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
|
|
|
|
REAL range;
|
|
|
|
|
|
|
|
/* locate the blend positions surrounding this position */
|
|
|
|
while (position > brush->blendpos[i])
|
|
|
|
i++;
|
|
|
|
|
|
|
|
/* interpolate between the blend positions */
|
|
|
|
left_blendpos = brush->blendpos[i-1];
|
|
|
|
left_blendfac = brush->blendfac[i-1];
|
|
|
|
right_blendpos = brush->blendpos[i];
|
|
|
|
right_blendfac = brush->blendfac[i];
|
|
|
|
range = right_blendpos - left_blendpos;
|
|
|
|
blendfac = (left_blendfac * (right_blendpos - position) +
|
|
|
|
right_blendfac * (position - left_blendpos)) / range;
|
|
|
|
}
|
2009-11-28 15:26:02 +00:00
|
|
|
|
|
|
|
if (brush->pblendcount == 0)
|
|
|
|
return blend_colors(brush->startcolor, brush->endcolor, blendfac);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i=1;
|
|
|
|
ARGB left_blendcolor, right_blendcolor;
|
|
|
|
REAL left_blendpos, right_blendpos;
|
|
|
|
|
|
|
|
/* locate the blend colors surrounding this position */
|
|
|
|
while (blendfac > brush->pblendpos[i])
|
|
|
|
i++;
|
|
|
|
|
|
|
|
/* interpolate between the blend colors */
|
|
|
|
left_blendpos = brush->pblendpos[i-1];
|
|
|
|
left_blendcolor = brush->pblendcolor[i-1];
|
|
|
|
right_blendpos = brush->pblendpos[i];
|
|
|
|
right_blendcolor = brush->pblendcolor[i];
|
|
|
|
blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
|
|
|
|
return blend_colors(left_blendcolor, right_blendcolor, blendfac);
|
|
|
|
}
|
2009-05-09 09:26:16 +00:00
|
|
|
}
|
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
static BOOL round_color_matrix(const ColorMatrix *matrix, int values[5][5])
|
2009-04-06 14:58:12 +00:00
|
|
|
{
|
2014-09-27 12:59:00 +00:00
|
|
|
/* Convert floating point color matrix to int[5][5], return TRUE if it's an identity */
|
|
|
|
BOOL identity = TRUE;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i=0; i<4; i++)
|
|
|
|
for (j=0; j<5; j++)
|
|
|
|
{
|
|
|
|
if (matrix->m[j][i] != (i == j ? 1.0 : 0.0))
|
|
|
|
identity = FALSE;
|
|
|
|
values[j][i] = gdip_round(matrix->m[j][i] * 256.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return identity;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ARGB transform_color(ARGB color, int matrix[5][5])
|
|
|
|
{
|
|
|
|
int val[5], res[4];
|
2012-03-05 20:17:42 +00:00
|
|
|
int i, j;
|
|
|
|
unsigned char a, r, g, b;
|
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
val[0] = ((color >> 16) & 0xff); /* red */
|
|
|
|
val[1] = ((color >> 8) & 0xff); /* green */
|
|
|
|
val[2] = (color & 0xff); /* blue */
|
|
|
|
val[3] = ((color >> 24) & 0xff); /* alpha */
|
|
|
|
val[4] = 255; /* translation */
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
for (i=0; i<4; i++)
|
2009-04-06 14:58:12 +00:00
|
|
|
{
|
2014-09-27 12:59:00 +00:00
|
|
|
res[i] = 0;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
for (j=0; j<5; j++)
|
2014-09-27 12:59:00 +00:00
|
|
|
res[i] += matrix[j][i] * val[j];
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
a = min(max(res[3] / 256, 0), 255);
|
|
|
|
r = min(max(res[0] / 256, 0), 255);
|
|
|
|
g = min(max(res[1] / 256, 0), 255);
|
|
|
|
b = min(max(res[2] / 256, 0), 255);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
return (a << 24) | (r << 16) | (g << 8) | b;
|
|
|
|
}
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
static BOOL color_is_gray(ARGB color)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
unsigned char r, g, b;
|
|
|
|
|
|
|
|
r = (color >> 16) & 0xff;
|
|
|
|
g = (color >> 8) & 0xff;
|
|
|
|
b = color & 0xff;
|
|
|
|
|
|
|
|
return (r == g) && (g == b);
|
|
|
|
}
|
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
/* returns preferred pixel format for the applied attributes */
|
2016-08-19 09:28:13 +00:00
|
|
|
PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
|
2015-07-19 13:31:15 +00:00
|
|
|
UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
UINT x, y;
|
|
|
|
INT i;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
if ((attributes->noop[type] == IMAGEATTR_NOOP_UNDEFINED &&
|
|
|
|
attributes->noop[ColorAdjustTypeDefault] == IMAGEATTR_NOOP_SET) ||
|
|
|
|
(attributes->noop[type] == IMAGEATTR_NOOP_SET))
|
|
|
|
return fmt;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (attributes->colorkeys[type].enabled ||
|
|
|
|
attributes->colorkeys[ColorAdjustTypeDefault].enabled)
|
2009-04-06 14:58:12 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
const struct color_key *key;
|
|
|
|
BYTE min_blue, min_green, min_red;
|
|
|
|
BYTE max_blue, max_green, max_red;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (!data || fmt != PixelFormat32bppARGB)
|
|
|
|
return PixelFormat32bppARGB;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (attributes->colorkeys[type].enabled)
|
|
|
|
key = &attributes->colorkeys[type];
|
|
|
|
else
|
|
|
|
key = &attributes->colorkeys[ColorAdjustTypeDefault];
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
min_blue = key->low&0xff;
|
|
|
|
min_green = (key->low>>8)&0xff;
|
|
|
|
min_red = (key->low>>16)&0xff;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
max_blue = key->high&0xff;
|
|
|
|
max_green = (key->high>>8)&0xff;
|
|
|
|
max_red = (key->high>>16)&0xff;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
for (x=0; x<width; x++)
|
|
|
|
for (y=0; y<height; y++)
|
2009-04-06 14:58:12 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
ARGB *src_color;
|
|
|
|
BYTE blue, green, red;
|
|
|
|
src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
|
|
|
|
blue = *src_color&0xff;
|
|
|
|
green = (*src_color>>8)&0xff;
|
|
|
|
red = (*src_color>>16)&0xff;
|
|
|
|
if (blue >= min_blue && green >= min_green && red >= min_red &&
|
|
|
|
blue <= max_blue && green <= max_green && red <= max_red)
|
|
|
|
*src_color = 0x00000000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attributes->colorremaptables[type].enabled ||
|
|
|
|
attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
|
|
|
|
{
|
|
|
|
const struct color_remap_table *table;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (!data || fmt != PixelFormat32bppARGB)
|
|
|
|
return PixelFormat32bppARGB;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (attributes->colorremaptables[type].enabled)
|
|
|
|
table = &attributes->colorremaptables[type];
|
|
|
|
else
|
|
|
|
table = &attributes->colorremaptables[ColorAdjustTypeDefault];
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
for (x=0; x<width; x++)
|
|
|
|
for (y=0; y<height; y++)
|
|
|
|
{
|
|
|
|
ARGB *src_color;
|
|
|
|
src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
|
|
|
|
for (i=0; i<table->mapsize; i++)
|
2009-04-06 14:58:12 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
if (*src_color == table->colormap[i].oldColor.Argb)
|
|
|
|
{
|
|
|
|
*src_color = table->colormap[i].newColor.Argb;
|
|
|
|
break;
|
|
|
|
}
|
2009-04-06 14:58:12 +00:00
|
|
|
}
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attributes->colormatrices[type].enabled ||
|
|
|
|
attributes->colormatrices[ColorAdjustTypeDefault].enabled)
|
|
|
|
{
|
|
|
|
const struct color_matrix *colormatrices;
|
2014-09-27 12:59:00 +00:00
|
|
|
int color_matrix[5][5];
|
|
|
|
int gray_matrix[5][5];
|
|
|
|
BOOL identity;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (!data || fmt != PixelFormat32bppARGB)
|
|
|
|
return PixelFormat32bppARGB;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (attributes->colormatrices[type].enabled)
|
|
|
|
colormatrices = &attributes->colormatrices[type];
|
|
|
|
else
|
|
|
|
colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
|
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
identity = round_color_matrix(&colormatrices->colormatrix, color_matrix);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
if (colormatrices->flags == ColorMatrixFlagsAltGray)
|
|
|
|
identity = (round_color_matrix(&colormatrices->graymatrix, gray_matrix) && identity);
|
|
|
|
|
|
|
|
if (!identity)
|
|
|
|
{
|
|
|
|
for (x=0; x<width; x++)
|
|
|
|
{
|
|
|
|
for (y=0; y<height; y++)
|
2009-05-09 09:26:16 +00:00
|
|
|
{
|
2014-09-27 12:59:00 +00:00
|
|
|
ARGB *src_color;
|
|
|
|
src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
|
|
|
|
|
|
|
|
if (colormatrices->flags == ColorMatrixFlagsDefault ||
|
|
|
|
!color_is_gray(*src_color))
|
|
|
|
{
|
|
|
|
*src_color = transform_color(*src_color, color_matrix);
|
|
|
|
}
|
|
|
|
else if (colormatrices->flags == ColorMatrixFlagsAltGray)
|
|
|
|
{
|
|
|
|
*src_color = transform_color(*src_color, gray_matrix);
|
|
|
|
}
|
2009-05-09 09:26:16 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
2014-09-27 12:59:00 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attributes->gamma_enabled[type] ||
|
|
|
|
attributes->gamma_enabled[ColorAdjustTypeDefault])
|
|
|
|
{
|
|
|
|
REAL gamma;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (!data || fmt != PixelFormat32bppARGB)
|
|
|
|
return PixelFormat32bppARGB;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (attributes->gamma_enabled[type])
|
|
|
|
gamma = attributes->gamma[type];
|
|
|
|
else
|
|
|
|
gamma = attributes->gamma[ColorAdjustTypeDefault];
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
for (x=0; x<width; x++)
|
|
|
|
for (y=0; y<height; y++)
|
|
|
|
{
|
|
|
|
ARGB *src_color;
|
|
|
|
BYTE blue, green, red;
|
|
|
|
src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
|
|
|
|
|
|
|
|
blue = *src_color&0xff;
|
|
|
|
green = (*src_color>>8)&0xff;
|
|
|
|
red = (*src_color>>16)&0xff;
|
|
|
|
|
|
|
|
/* FIXME: We should probably use a table for this. */
|
|
|
|
blue = floorf(powf(blue / 255.0, gamma) * 255.0);
|
|
|
|
green = floorf(powf(green / 255.0, gamma) * 255.0);
|
|
|
|
red = floorf(powf(red / 255.0, gamma) * 255.0);
|
|
|
|
|
|
|
|
*src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
|
2009-04-06 14:58:12 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
2015-07-19 13:31:15 +00:00
|
|
|
|
|
|
|
return fmt;
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Given a bitmap and its source rectangle, find the smallest rectangle in the
|
|
|
|
* bitmap that contains all the pixels we may need to draw it. */
|
|
|
|
static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
|
|
|
|
GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
|
|
|
|
GpRect *rect)
|
|
|
|
{
|
|
|
|
INT left, top, right, bottom;
|
|
|
|
|
|
|
|
switch (interpolation)
|
|
|
|
{
|
|
|
|
case InterpolationModeHighQualityBilinear:
|
|
|
|
case InterpolationModeHighQualityBicubic:
|
|
|
|
/* FIXME: Include a greater range for the prefilter? */
|
|
|
|
case InterpolationModeBicubic:
|
|
|
|
case InterpolationModeBilinear:
|
|
|
|
left = (INT)(floorf(srcx));
|
|
|
|
top = (INT)(floorf(srcy));
|
|
|
|
right = (INT)(ceilf(srcx+srcwidth));
|
|
|
|
bottom = (INT)(ceilf(srcy+srcheight));
|
|
|
|
break;
|
|
|
|
case InterpolationModeNearestNeighbor:
|
|
|
|
default:
|
2012-12-11 21:40:15 +00:00
|
|
|
left = gdip_round(srcx);
|
|
|
|
top = gdip_round(srcy);
|
|
|
|
right = gdip_round(srcx+srcwidth);
|
|
|
|
bottom = gdip_round(srcy+srcheight);
|
2009-04-06 14:58:12 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (wrap == WrapModeClamp)
|
|
|
|
{
|
|
|
|
if (left < 0)
|
|
|
|
left = 0;
|
|
|
|
if (top < 0)
|
|
|
|
top = 0;
|
|
|
|
if (right >= bitmap->width)
|
|
|
|
right = bitmap->width-1;
|
|
|
|
if (bottom >= bitmap->height)
|
|
|
|
bottom = bitmap->height-1;
|
2015-03-22 18:33:30 +00:00
|
|
|
if (bottom < top || right < left)
|
|
|
|
/* entirely outside image, just sample a pixel so we don't have to
|
|
|
|
* special-case this later */
|
|
|
|
left = top = right = bottom = 0;
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* In some cases we can make the rectangle smaller here, but the logic
|
|
|
|
* is hard to get right, and tiling suggests we're likely to use the
|
|
|
|
* entire source image. */
|
|
|
|
if (left < 0 || right >= bitmap->width)
|
|
|
|
{
|
|
|
|
left = 0;
|
|
|
|
right = bitmap->width-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top < 0 || bottom >= bitmap->height)
|
|
|
|
{
|
|
|
|
top = 0;
|
|
|
|
bottom = bitmap->height-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rect->X = left;
|
|
|
|
rect->Y = top;
|
|
|
|
rect->Width = right - left + 1;
|
|
|
|
rect->Height = bottom - top + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
|
|
|
|
UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
|
|
|
|
{
|
|
|
|
if (attributes->wrap == WrapModeClamp)
|
|
|
|
{
|
|
|
|
if (x < 0 || y < 0 || x >= width || y >= height)
|
|
|
|
return attributes->outside_color;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
|
|
|
|
if (x < 0)
|
|
|
|
x = width*2 + x % (width * 2);
|
|
|
|
if (y < 0)
|
|
|
|
y = height*2 + y % (height * 2);
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (attributes->wrap & WrapModeTileFlipX)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
if ((x / width) % 2 == 0)
|
|
|
|
x = x % width;
|
|
|
|
else
|
|
|
|
x = width - 1 - x % width;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
x = x % width;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (attributes->wrap & WrapModeTileFlipY)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
if ((y / height) % 2 == 0)
|
|
|
|
y = y % height;
|
|
|
|
else
|
|
|
|
y = height - 1 - y % height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
y = y % height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
|
|
|
|
{
|
|
|
|
ERR("out of range pixel requested\n");
|
|
|
|
return 0xffcd0084;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
|
|
|
|
}
|
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
static inline int positive_ceilf(float f)
|
|
|
|
{
|
|
|
|
return f - (int)f > 0.0f ? f + 1.0f : f;
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
|
|
|
|
UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
|
2012-12-11 21:40:15 +00:00
|
|
|
InterpolationMode interpolation, PixelOffsetMode offset_mode)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
static int fixme;
|
|
|
|
|
|
|
|
switch (interpolation)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
if (!fixme++)
|
|
|
|
FIXME("Unimplemented interpolation %i\n", interpolation);
|
|
|
|
/* fall-through */
|
|
|
|
case InterpolationModeBilinear:
|
|
|
|
{
|
|
|
|
REAL leftxf, topyf;
|
|
|
|
INT leftx, rightx, topy, bottomy;
|
|
|
|
ARGB topleft, topright, bottomleft, bottomright;
|
|
|
|
ARGB top, bottom;
|
|
|
|
float x_offset;
|
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
leftx = (INT)point->X;
|
|
|
|
leftxf = (REAL)leftx;
|
|
|
|
rightx = positive_ceilf(point->X);
|
|
|
|
topy = (INT)point->Y;
|
|
|
|
topyf = (REAL)topy;
|
|
|
|
bottomy = positive_ceilf(point->Y);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (leftx == rightx && topy == bottomy)
|
|
|
|
return sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
leftx, topy, attributes);
|
|
|
|
|
|
|
|
topleft = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
leftx, topy, attributes);
|
|
|
|
topright = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
rightx, topy, attributes);
|
|
|
|
bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
leftx, bottomy, attributes);
|
|
|
|
bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
rightx, bottomy, attributes);
|
|
|
|
|
|
|
|
x_offset = point->X - leftxf;
|
|
|
|
top = blend_colors(topleft, topright, x_offset);
|
|
|
|
bottom = blend_colors(bottomleft, bottomright, x_offset);
|
|
|
|
|
|
|
|
return blend_colors(top, bottom, point->Y - topyf);
|
|
|
|
}
|
|
|
|
case InterpolationModeNearestNeighbor:
|
2012-12-11 21:40:15 +00:00
|
|
|
{
|
|
|
|
FLOAT pixel_offset;
|
|
|
|
switch (offset_mode)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case PixelOffsetModeNone:
|
|
|
|
case PixelOffsetModeHighSpeed:
|
|
|
|
pixel_offset = 0.5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PixelOffsetModeHalf:
|
|
|
|
case PixelOffsetModeHighQuality:
|
|
|
|
pixel_offset = 0.0;
|
|
|
|
break;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
return sample_bitmap_pixel(src_rect, bits, width, height,
|
2012-12-11 21:40:15 +00:00
|
|
|
floorf(point->X + pixel_offset), floorf(point->Y + pixel_offset), attributes);
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
static ARGB resample_bitmap_pixel_premult(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
|
|
|
|
UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
|
|
|
|
InterpolationMode interpolation, PixelOffsetMode offset_mode)
|
|
|
|
{
|
|
|
|
static int fixme;
|
|
|
|
|
|
|
|
switch (interpolation)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
if (!fixme++)
|
|
|
|
FIXME("Unimplemented interpolation %i\n", interpolation);
|
|
|
|
/* fall-through */
|
|
|
|
case InterpolationModeBilinear:
|
|
|
|
{
|
|
|
|
REAL leftxf, topyf;
|
|
|
|
INT leftx, rightx, topy, bottomy;
|
|
|
|
ARGB topleft, topright, bottomleft, bottomright;
|
|
|
|
ARGB top, bottom;
|
|
|
|
float x_offset;
|
|
|
|
|
|
|
|
leftx = (INT)point->X;
|
|
|
|
leftxf = (REAL)leftx;
|
|
|
|
rightx = positive_ceilf(point->X);
|
|
|
|
topy = (INT)point->Y;
|
|
|
|
topyf = (REAL)topy;
|
|
|
|
bottomy = positive_ceilf(point->Y);
|
|
|
|
|
|
|
|
if (leftx == rightx && topy == bottomy)
|
|
|
|
return sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
leftx, topy, attributes);
|
|
|
|
|
|
|
|
topleft = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
leftx, topy, attributes);
|
|
|
|
topright = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
rightx, topy, attributes);
|
|
|
|
bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
leftx, bottomy, attributes);
|
|
|
|
bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
rightx, bottomy, attributes);
|
|
|
|
|
|
|
|
x_offset = point->X - leftxf;
|
|
|
|
top = blend_colors_premult(topleft, topright, x_offset);
|
|
|
|
bottom = blend_colors_premult(bottomleft, bottomright, x_offset);
|
|
|
|
|
|
|
|
return blend_colors_premult(top, bottom, point->Y - topyf);
|
|
|
|
}
|
|
|
|
case InterpolationModeNearestNeighbor:
|
|
|
|
{
|
|
|
|
FLOAT pixel_offset;
|
|
|
|
switch (offset_mode)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case PixelOffsetModeNone:
|
|
|
|
case PixelOffsetModeHighSpeed:
|
|
|
|
pixel_offset = 0.5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PixelOffsetModeHalf:
|
|
|
|
case PixelOffsetModeHighQuality:
|
|
|
|
pixel_offset = 0.0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return sample_bitmap_pixel(src_rect, bits, width, height,
|
|
|
|
floorf(point->X + pixel_offset), point->Y + pixel_offset, attributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
|
|
|
|
{
|
|
|
|
return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
|
|
|
|
}
|
|
|
|
|
2017-06-03 19:01:50 +00:00
|
|
|
/* is_fill is TRUE if filling regions, FALSE for drawing primitives */
|
|
|
|
static BOOL brush_can_fill_path(GpBrush *brush, BOOL is_fill)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
switch (brush->bt)
|
|
|
|
{
|
|
|
|
case BrushTypeSolidColor:
|
2017-06-03 19:01:50 +00:00
|
|
|
{
|
|
|
|
if (is_fill)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* cannot draw semi-transparent colors */
|
|
|
|
return (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000;
|
|
|
|
}
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
case BrushTypeHatchFill:
|
|
|
|
{
|
|
|
|
GpHatch *hatch = (GpHatch*)brush;
|
|
|
|
return ((hatch->forecol & 0xff000000) == 0xff000000) &&
|
|
|
|
((hatch->backcol & 0xff000000) == 0xff000000);
|
|
|
|
}
|
|
|
|
case BrushTypeLinearGradient:
|
|
|
|
case BrushTypeTextureFill:
|
|
|
|
/* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
|
|
|
|
default:
|
2014-04-22 15:42:03 +00:00
|
|
|
return FALSE;
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-01 11:50:25 +00:00
|
|
|
static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
2018-10-01 11:50:25 +00:00
|
|
|
GpStatus status = Ok;
|
2012-03-05 20:17:42 +00:00
|
|
|
switch (brush->bt)
|
|
|
|
{
|
2009-05-23 10:32:19 +00:00
|
|
|
case BrushTypeSolidColor:
|
|
|
|
{
|
|
|
|
GpSolidFill *fill = (GpSolidFill*)brush;
|
2012-05-19 10:03:40 +00:00
|
|
|
HBITMAP bmp = ARGB2BMP(fill->color);
|
|
|
|
|
|
|
|
if (bmp)
|
2009-05-23 10:32:19 +00:00
|
|
|
{
|
|
|
|
RECT rc;
|
|
|
|
/* partially transparent fill */
|
|
|
|
|
2018-10-01 11:50:25 +00:00
|
|
|
if (!SelectClipPath(graphics->hdc, RGN_AND))
|
|
|
|
{
|
|
|
|
status = GenericError;
|
|
|
|
DeleteObject(bmp);
|
|
|
|
break;
|
|
|
|
}
|
2009-05-23 10:32:19 +00:00
|
|
|
if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
|
|
|
|
{
|
|
|
|
HDC hdc = CreateCompatibleDC(NULL);
|
|
|
|
|
2018-10-01 11:50:25 +00:00
|
|
|
if (!hdc)
|
|
|
|
{
|
|
|
|
status = OutOfMemory;
|
|
|
|
DeleteObject(bmp);
|
|
|
|
break;
|
|
|
|
}
|
2009-05-23 10:32:19 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
SelectObject(hdc, bmp);
|
|
|
|
gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
|
|
|
|
hdc, 0, 0, 1, 1);
|
2009-05-23 10:32:19 +00:00
|
|
|
DeleteDC(hdc);
|
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
DeleteObject(bmp);
|
2009-05-23 10:32:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* else fall through */
|
|
|
|
}
|
2009-04-06 14:58:12 +00:00
|
|
|
default:
|
2012-05-19 10:03:40 +00:00
|
|
|
{
|
|
|
|
HBRUSH gdibrush, old_brush;
|
|
|
|
|
|
|
|
gdibrush = create_gdi_brush(brush);
|
2018-10-01 11:50:25 +00:00
|
|
|
if (!gdibrush)
|
|
|
|
{
|
|
|
|
status = OutOfMemory;
|
|
|
|
break;
|
|
|
|
}
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
old_brush = SelectObject(graphics->hdc, gdibrush);
|
2009-04-06 14:58:12 +00:00
|
|
|
FillPath(graphics->hdc);
|
2012-05-19 10:03:40 +00:00
|
|
|
SelectObject(graphics->hdc, old_brush);
|
|
|
|
DeleteObject(gdibrush);
|
2009-04-06 14:58:12 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-05-19 10:03:40 +00:00
|
|
|
}
|
2018-10-01 11:50:25 +00:00
|
|
|
|
|
|
|
return status;
|
2009-04-06 14:58:12 +00:00
|
|
|
}
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
static BOOL brush_can_fill_pixels(GpBrush *brush)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
switch (brush->bt)
|
|
|
|
{
|
|
|
|
case BrushTypeSolidColor:
|
|
|
|
case BrushTypeHatchFill:
|
|
|
|
case BrushTypeLinearGradient:
|
|
|
|
case BrushTypeTextureFill:
|
2012-05-19 10:03:40 +00:00
|
|
|
case BrushTypePathGradient:
|
2014-04-22 15:42:03 +00:00
|
|
|
return TRUE;
|
2012-03-05 20:17:42 +00:00
|
|
|
default:
|
2014-04-22 15:42:03 +00:00
|
|
|
return FALSE;
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
|
|
|
|
{
|
|
|
|
switch (brush->bt)
|
|
|
|
{
|
|
|
|
case BrushTypeSolidColor:
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
GpSolidFill *fill = (GpSolidFill*)brush;
|
|
|
|
for (x=0; x<fill_area->Width; x++)
|
|
|
|
for (y=0; y<fill_area->Height; y++)
|
|
|
|
argb_pixels[x + y*cdwStride] = fill->color;
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
case BrushTypeHatchFill:
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
GpHatch *fill = (GpHatch*)brush;
|
|
|
|
const char *hatch_data;
|
|
|
|
|
|
|
|
if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
|
|
|
|
return NotImplemented;
|
|
|
|
|
|
|
|
for (x=0; x<fill_area->Width; x++)
|
|
|
|
for (y=0; y<fill_area->Height; y++)
|
|
|
|
{
|
|
|
|
int hx, hy;
|
|
|
|
|
|
|
|
/* FIXME: Account for the rendering origin */
|
|
|
|
hx = (x + fill_area->X) % 8;
|
|
|
|
hy = (y + fill_area->Y) % 8;
|
|
|
|
|
|
|
|
if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
|
|
|
|
argb_pixels[x + y*cdwStride] = fill->forecol;
|
|
|
|
else
|
|
|
|
argb_pixels[x + y*cdwStride] = fill->backcol;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
case BrushTypeLinearGradient:
|
|
|
|
{
|
|
|
|
GpLineGradient *fill = (GpLineGradient*)brush;
|
2017-09-17 12:34:15 +00:00
|
|
|
GpPointF draw_points[3];
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
draw_points[0].X = fill_area->X;
|
|
|
|
draw_points[0].Y = fill_area->Y;
|
|
|
|
draw_points[1].X = fill_area->X+1;
|
|
|
|
draw_points[1].Y = fill_area->Y;
|
|
|
|
draw_points[2].X = fill_area->X;
|
|
|
|
draw_points[2].Y = fill_area->Y+1;
|
|
|
|
|
|
|
|
/* Transform the points to a co-ordinate space where X is the point's
|
|
|
|
* position in the gradient, 0.0 being the start point and 1.0 the
|
|
|
|
* end point. */
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
|
|
|
|
WineCoordinateSpaceGdiDevice, draw_points, 3);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
GpMatrix world_to_gradient = fill->transform;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = GdipInvertMatrix(&world_to_gradient);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = GdipTransformMatrixPoints(&world_to_gradient, draw_points, 3);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
REAL x_delta = draw_points[1].X - draw_points[0].X;
|
|
|
|
REAL y_delta = draw_points[2].X - draw_points[0].X;
|
|
|
|
|
|
|
|
for (y=0; y<fill_area->Height; y++)
|
|
|
|
{
|
|
|
|
for (x=0; x<fill_area->Width; x++)
|
|
|
|
{
|
|
|
|
REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
|
|
|
|
|
|
|
|
argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
case BrushTypeTextureFill:
|
|
|
|
{
|
|
|
|
GpTexture *fill = (GpTexture*)brush;
|
|
|
|
GpPointF draw_points[3];
|
|
|
|
GpStatus stat;
|
|
|
|
int x, y;
|
|
|
|
GpBitmap *bitmap;
|
|
|
|
int src_stride;
|
|
|
|
GpRect src_area;
|
|
|
|
|
|
|
|
if (fill->image->type != ImageTypeBitmap)
|
|
|
|
{
|
|
|
|
FIXME("metafile texture brushes not implemented\n");
|
|
|
|
return NotImplemented;
|
|
|
|
}
|
|
|
|
|
|
|
|
bitmap = (GpBitmap*)fill->image;
|
|
|
|
src_stride = sizeof(ARGB) * bitmap->width;
|
|
|
|
|
|
|
|
src_area.X = src_area.Y = 0;
|
|
|
|
src_area.Width = bitmap->width;
|
|
|
|
src_area.Height = bitmap->height;
|
|
|
|
|
|
|
|
draw_points[0].X = fill_area->X;
|
|
|
|
draw_points[0].Y = fill_area->Y;
|
|
|
|
draw_points[1].X = fill_area->X+1;
|
|
|
|
draw_points[1].Y = fill_area->Y;
|
|
|
|
draw_points[2].X = fill_area->X;
|
|
|
|
draw_points[2].Y = fill_area->Y+1;
|
|
|
|
|
|
|
|
/* Transform the points to the co-ordinate space of the bitmap. */
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = gdip_transform_points(graphics, CoordinateSpaceWorld,
|
|
|
|
WineCoordinateSpaceGdiDevice, draw_points, 3);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix world_to_texture = fill->transform;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = GdipInvertMatrix(&world_to_texture);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = GdipTransformMatrixPoints(&world_to_texture, draw_points, 3);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok && !fill->bitmap_bits)
|
|
|
|
{
|
|
|
|
BitmapData lockeddata;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
fill->bitmap_bits = heap_alloc_zero(sizeof(ARGB) * bitmap->width * bitmap->height);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!fill->bitmap_bits)
|
|
|
|
stat = OutOfMemory;
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
lockeddata.Width = bitmap->width;
|
|
|
|
lockeddata.Height = bitmap->height;
|
|
|
|
lockeddata.Stride = src_stride;
|
|
|
|
lockeddata.PixelFormat = PixelFormat32bppARGB;
|
|
|
|
lockeddata.Scan0 = fill->bitmap_bits;
|
|
|
|
|
|
|
|
stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
|
|
|
|
PixelFormat32bppARGB, &lockeddata);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
|
|
|
|
bitmap->width, bitmap->height,
|
2015-07-19 13:31:15 +00:00
|
|
|
src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(fill->bitmap_bits);
|
2012-03-05 20:17:42 +00:00
|
|
|
fill->bitmap_bits = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
REAL x_dx = draw_points[1].X - draw_points[0].X;
|
|
|
|
REAL x_dy = draw_points[1].Y - draw_points[0].Y;
|
|
|
|
REAL y_dx = draw_points[2].X - draw_points[0].X;
|
|
|
|
REAL y_dy = draw_points[2].Y - draw_points[0].Y;
|
|
|
|
|
|
|
|
for (y=0; y<fill_area->Height; y++)
|
|
|
|
{
|
|
|
|
for (x=0; x<fill_area->Width; x++)
|
|
|
|
{
|
|
|
|
GpPointF point;
|
|
|
|
point.X = draw_points[0].X + x * x_dx + y * y_dx;
|
|
|
|
point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
|
|
|
|
|
|
|
|
argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
|
|
|
|
&src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
|
2012-12-11 21:40:15 +00:00
|
|
|
&point, fill->imageattributes, graphics->interpolation,
|
|
|
|
graphics->pixeloffset);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
2012-05-19 10:03:40 +00:00
|
|
|
case BrushTypePathGradient:
|
|
|
|
{
|
|
|
|
GpPathGradient *fill = (GpPathGradient*)brush;
|
|
|
|
GpPath *flat_path;
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix world_to_device;
|
2012-05-19 10:03:40 +00:00
|
|
|
GpStatus stat;
|
|
|
|
int i, figure_start=0;
|
|
|
|
GpPointF start_point, end_point, center_point;
|
|
|
|
BYTE type;
|
|
|
|
REAL min_yf, max_yf, line1_xf, line2_xf;
|
|
|
|
INT min_y, max_y, min_x, max_x;
|
|
|
|
INT x, y;
|
|
|
|
ARGB outer_color;
|
2014-04-22 15:42:03 +00:00
|
|
|
static BOOL transform_fixme_once;
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
|
|
|
|
{
|
|
|
|
static int once;
|
|
|
|
if (!once++)
|
|
|
|
FIXME("path gradient focus not implemented\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fill->gamma)
|
|
|
|
{
|
|
|
|
static int once;
|
|
|
|
if (!once++)
|
|
|
|
FIXME("path gradient gamma correction not implemented\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fill->blendcount)
|
|
|
|
{
|
|
|
|
static int once;
|
|
|
|
if (!once++)
|
|
|
|
FIXME("path gradient blend not implemented\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fill->pblendcount)
|
|
|
|
{
|
|
|
|
static int once;
|
|
|
|
if (!once++)
|
|
|
|
FIXME("path gradient preset blend not implemented\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!transform_fixme_once)
|
|
|
|
{
|
|
|
|
BOOL is_identity=TRUE;
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipIsMatrixIdentity(&fill->transform, &is_identity);
|
2012-05-19 10:03:40 +00:00
|
|
|
if (!is_identity)
|
|
|
|
{
|
|
|
|
FIXME("path gradient transform not implemented\n");
|
2014-04-22 15:42:03 +00:00
|
|
|
transform_fixme_once = TRUE;
|
2012-05-19 10:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stat = GdipClonePath(fill->path, &flat_path);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
|
2012-05-19 10:03:40 +00:00
|
|
|
CoordinateSpaceWorld, &world_to_device);
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = GdipTransformPath(flat_path, &world_to_device);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
center_point = fill->center;
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = GdipTransformMatrixPoints(&world_to_device, ¢er_point, 1);
|
2012-05-19 10:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFlattenPath(flat_path, NULL, 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
{
|
|
|
|
GdipDeletePath(flat_path);
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i<flat_path->pathdata.Count; i++)
|
|
|
|
{
|
|
|
|
int start_center_line=0, end_center_line=0;
|
2014-04-22 15:42:03 +00:00
|
|
|
BOOL seen_start = FALSE, seen_end = FALSE, seen_center = FALSE;
|
2012-05-19 10:03:40 +00:00
|
|
|
REAL center_distance;
|
|
|
|
ARGB start_color, end_color;
|
|
|
|
REAL dy, dx;
|
|
|
|
|
|
|
|
type = flat_path->pathdata.Types[i];
|
|
|
|
|
|
|
|
if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
|
|
|
|
figure_start = i;
|
|
|
|
|
|
|
|
start_point = flat_path->pathdata.Points[i];
|
|
|
|
|
|
|
|
start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
|
|
|
|
|
|
|
|
if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
|
|
|
|
{
|
|
|
|
end_point = flat_path->pathdata.Points[figure_start];
|
|
|
|
end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
|
|
|
|
}
|
|
|
|
else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
|
|
|
|
{
|
|
|
|
end_point = flat_path->pathdata.Points[i+1];
|
|
|
|
end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
outer_color = start_color;
|
|
|
|
|
|
|
|
min_yf = center_point.Y;
|
|
|
|
if (min_yf > start_point.Y) min_yf = start_point.Y;
|
|
|
|
if (min_yf > end_point.Y) min_yf = end_point.Y;
|
|
|
|
|
|
|
|
if (min_yf < fill_area->Y)
|
|
|
|
min_y = fill_area->Y;
|
|
|
|
else
|
|
|
|
min_y = (INT)ceil(min_yf);
|
|
|
|
|
|
|
|
max_yf = center_point.Y;
|
|
|
|
if (max_yf < start_point.Y) max_yf = start_point.Y;
|
|
|
|
if (max_yf < end_point.Y) max_yf = end_point.Y;
|
|
|
|
|
|
|
|
if (max_yf > fill_area->Y + fill_area->Height)
|
|
|
|
max_y = fill_area->Y + fill_area->Height;
|
|
|
|
else
|
|
|
|
max_y = (INT)ceil(max_yf);
|
|
|
|
|
|
|
|
dy = end_point.Y - start_point.Y;
|
|
|
|
dx = end_point.X - start_point.X;
|
|
|
|
|
|
|
|
/* This is proportional to the distance from start-end line to center point. */
|
|
|
|
center_distance = dy * (start_point.X - center_point.X) +
|
|
|
|
dx * (center_point.Y - start_point.Y);
|
|
|
|
|
|
|
|
for (y=min_y; y<max_y; y++)
|
|
|
|
{
|
|
|
|
REAL yf = (REAL)y;
|
|
|
|
|
|
|
|
if (!seen_start && yf >= start_point.Y)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
seen_start = TRUE;
|
2012-05-19 10:03:40 +00:00
|
|
|
start_center_line ^= 1;
|
|
|
|
}
|
|
|
|
if (!seen_end && yf >= end_point.Y)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
seen_end = TRUE;
|
2012-05-19 10:03:40 +00:00
|
|
|
end_center_line ^= 1;
|
|
|
|
}
|
|
|
|
if (!seen_center && yf >= center_point.Y)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
seen_center = TRUE;
|
2012-05-19 10:03:40 +00:00
|
|
|
start_center_line ^= 1;
|
|
|
|
end_center_line ^= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start_center_line)
|
|
|
|
line1_xf = intersect_line_scanline(&start_point, ¢er_point, yf);
|
|
|
|
else
|
|
|
|
line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
|
|
|
|
|
|
|
|
if (end_center_line)
|
|
|
|
line2_xf = intersect_line_scanline(&end_point, ¢er_point, yf);
|
|
|
|
else
|
|
|
|
line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
|
|
|
|
|
|
|
|
if (line1_xf < line2_xf)
|
|
|
|
{
|
|
|
|
min_x = (INT)ceil(line1_xf);
|
|
|
|
max_x = (INT)ceil(line2_xf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
min_x = (INT)ceil(line2_xf);
|
|
|
|
max_x = (INT)ceil(line1_xf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (min_x < fill_area->X)
|
|
|
|
min_x = fill_area->X;
|
|
|
|
if (max_x > fill_area->X + fill_area->Width)
|
|
|
|
max_x = fill_area->X + fill_area->Width;
|
|
|
|
|
|
|
|
for (x=min_x; x<max_x; x++)
|
|
|
|
{
|
|
|
|
REAL xf = (REAL)x;
|
|
|
|
REAL distance;
|
|
|
|
|
|
|
|
if (start_color != end_color)
|
|
|
|
{
|
|
|
|
REAL blend_amount, pdy, pdx;
|
|
|
|
pdy = yf - center_point.Y;
|
|
|
|
pdx = xf - center_point.X;
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
if (fabs(pdx) <= 0.001 && fabs(pdy) <= 0.001)
|
|
|
|
{
|
|
|
|
/* Too close to center point, don't try to calculate outer color */
|
|
|
|
outer_color = start_color;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
|
|
|
|
outer_color = blend_colors(start_color, end_color, blend_amount);
|
|
|
|
}
|
2012-05-19 10:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
|
|
|
|
(end_point.X - start_point.X) * (yf - start_point.Y);
|
|
|
|
|
|
|
|
distance = distance / center_distance;
|
|
|
|
|
|
|
|
argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
|
|
|
|
blend_colors(outer_color, fill->centercolor, distance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GdipDeletePath(flat_path);
|
|
|
|
return stat;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
default:
|
|
|
|
return NotImplemented;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
/* Draws the linecap the specified color and size on the hdc. The linecap is in
|
|
|
|
* direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
|
|
|
|
* should not be called on an hdc that has a path you care about. */
|
|
|
|
static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
|
|
|
|
const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
|
|
|
|
{
|
|
|
|
HGDIOBJ oldbrush = NULL, oldpen = NULL;
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix matrix;
|
2008-03-25 17:34:57 +00:00
|
|
|
HBRUSH brush = NULL;
|
|
|
|
HPEN pen = NULL;
|
|
|
|
PointF ptf[4], *custptf = NULL;
|
|
|
|
POINT pt[4], *custpt = NULL;
|
|
|
|
BYTE *tp = NULL;
|
|
|
|
REAL theta, dsmall, dbig, dx, dy = 0.0;
|
|
|
|
INT i, count;
|
|
|
|
LOGBRUSH lb;
|
|
|
|
BOOL customstroke;
|
|
|
|
|
|
|
|
if((x1 == x2) && (y1 == y2))
|
|
|
|
return;
|
|
|
|
|
|
|
|
theta = gdiplus_atan2(y2 - y1, x2 - x1);
|
|
|
|
|
|
|
|
customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
|
|
|
|
if(!customstroke){
|
|
|
|
brush = CreateSolidBrush(color);
|
|
|
|
lb.lbStyle = BS_SOLID;
|
|
|
|
lb.lbColor = color;
|
|
|
|
lb.lbHatch = 0;
|
|
|
|
pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
|
|
|
|
PS_JOIN_MITER, 1, &lb, 0,
|
|
|
|
NULL);
|
|
|
|
oldbrush = SelectObject(graphics->hdc, brush);
|
|
|
|
oldpen = SelectObject(graphics->hdc, pen);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(cap){
|
|
|
|
case LineCapFlat:
|
|
|
|
break;
|
|
|
|
case LineCapSquare:
|
|
|
|
case LineCapSquareAnchor:
|
|
|
|
case LineCapDiamondAnchor:
|
|
|
|
size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
|
|
|
|
if(cap == LineCapDiamondAnchor){
|
|
|
|
dsmall = cos(theta + M_PI_2) * size;
|
|
|
|
dbig = sin(theta + M_PI_2) * size;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
dsmall = cos(theta + M_PI_4) * size;
|
|
|
|
dbig = sin(theta + M_PI_4) * size;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptf[0].X = x2 - dsmall;
|
|
|
|
ptf[1].X = x2 + dbig;
|
|
|
|
|
|
|
|
ptf[0].Y = y2 - dbig;
|
|
|
|
ptf[3].Y = y2 + dsmall;
|
|
|
|
|
|
|
|
ptf[1].Y = y2 - dsmall;
|
|
|
|
ptf[2].Y = y2 + dbig;
|
|
|
|
|
|
|
|
ptf[3].X = x2 - dbig;
|
|
|
|
ptf[2].X = x2 + dsmall;
|
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
round_points(pt, ptf, 4);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
Polygon(graphics->hdc, pt, 4);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LineCapArrowAnchor:
|
|
|
|
size = size * 4.0 / sqrt(3.0);
|
|
|
|
|
|
|
|
dx = cos(M_PI / 6.0 + theta) * size;
|
|
|
|
dy = sin(M_PI / 6.0 + theta) * size;
|
|
|
|
|
|
|
|
ptf[0].X = x2 - dx;
|
|
|
|
ptf[0].Y = y2 - dy;
|
|
|
|
|
|
|
|
dx = cos(- M_PI / 6.0 + theta) * size;
|
|
|
|
dy = sin(- M_PI / 6.0 + theta) * size;
|
|
|
|
|
|
|
|
ptf[1].X = x2 - dx;
|
|
|
|
ptf[1].Y = y2 - dy;
|
|
|
|
|
|
|
|
ptf[2].X = x2;
|
|
|
|
ptf[2].Y = y2;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
|
|
|
|
|
|
|
|
round_points(pt, ptf, 3);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
Polygon(graphics->hdc, pt, 3);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LineCapRoundAnchor:
|
|
|
|
dx = dy = ANCHOR_WIDTH * size / 2.0;
|
|
|
|
|
|
|
|
ptf[0].X = x2 - dx;
|
|
|
|
ptf[0].Y = y2 - dy;
|
|
|
|
ptf[1].X = x2 + dx;
|
|
|
|
ptf[1].Y = y2 + dy;
|
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 2);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
round_points(pt, ptf, 2);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LineCapTriangle:
|
|
|
|
size = size / 2.0;
|
|
|
|
dx = cos(M_PI_2 + theta) * size;
|
|
|
|
dy = sin(M_PI_2 + theta) * size;
|
|
|
|
|
|
|
|
ptf[0].X = x2 - dx;
|
|
|
|
ptf[0].Y = y2 - dy;
|
|
|
|
ptf[1].X = x2 + dx;
|
|
|
|
ptf[1].Y = y2 + dy;
|
|
|
|
|
|
|
|
dx = cos(theta) * size;
|
|
|
|
dy = sin(theta) * size;
|
|
|
|
|
|
|
|
ptf[2].X = x2 + dx;
|
|
|
|
ptf[2].Y = y2 + dy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 3);
|
|
|
|
|
|
|
|
round_points(pt, ptf, 3);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
Polygon(graphics->hdc, pt, 3);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LineCapRound:
|
|
|
|
dx = dy = size / 2.0;
|
|
|
|
|
|
|
|
ptf[0].X = x2 - dx;
|
|
|
|
ptf[0].Y = y2 - dy;
|
|
|
|
ptf[1].X = x2 + dx;
|
|
|
|
ptf[1].Y = y2 + dy;
|
|
|
|
|
|
|
|
dx = -cos(M_PI_2 + theta) * size;
|
|
|
|
dy = -sin(M_PI_2 + theta) * size;
|
|
|
|
|
|
|
|
ptf[2].X = x2 - dx;
|
|
|
|
ptf[2].Y = y2 - dy;
|
|
|
|
ptf[3].X = x2 + dx;
|
|
|
|
ptf[3].Y = y2 + dy;
|
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
round_points(pt, ptf, 4);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
|
|
|
|
pt[2].y, pt[3].x, pt[3].y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case LineCapCustom:
|
|
|
|
if(!custom)
|
|
|
|
break;
|
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
if (custom->type == CustomLineCapTypeAdjustableArrow)
|
|
|
|
{
|
|
|
|
GpAdjustableArrowCap *arrow = (GpAdjustableArrowCap *)custom;
|
|
|
|
if (arrow->cap.fill && arrow->height <= 0.0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
count = custom->pathdata.Count;
|
2015-11-22 10:10:02 +00:00
|
|
|
custptf = heap_alloc_zero(count * sizeof(PointF));
|
|
|
|
custpt = heap_alloc_zero(count * sizeof(POINT));
|
|
|
|
tp = heap_alloc_zero(count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
if(!custptf || !custpt || !tp)
|
2008-03-25 17:34:57 +00:00
|
|
|
goto custend;
|
|
|
|
|
|
|
|
memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipSetMatrixElements(&matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
|
|
|
GdipScaleMatrix(&matrix, size, size, MatrixOrderAppend);
|
|
|
|
GdipRotateMatrix(&matrix, (180.0 / M_PI) * (theta - M_PI_2),
|
2008-03-25 17:34:57 +00:00
|
|
|
MatrixOrderAppend);
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipTranslateMatrix(&matrix, x2, y2, MatrixOrderAppend);
|
|
|
|
GdipTransformMatrixPoints(&matrix, custptf, count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, custptf, count);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
2018-05-27 03:10:39 +00:00
|
|
|
round_points(custpt, custptf, count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
for(i = 0; i < count; i++)
|
|
|
|
tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
|
|
|
|
|
|
|
|
if(custom->fill){
|
|
|
|
BeginPath(graphics->hdc);
|
|
|
|
PolyDraw(graphics->hdc, custpt, tp, count);
|
|
|
|
EndPath(graphics->hdc);
|
|
|
|
StrokeAndFillPath(graphics->hdc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PolyDraw(graphics->hdc, custpt, tp, count);
|
|
|
|
|
|
|
|
custend:
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(custptf);
|
|
|
|
heap_free(custpt);
|
|
|
|
heap_free(tp);
|
2008-03-25 17:34:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!customstroke){
|
|
|
|
SelectObject(graphics->hdc, oldbrush);
|
|
|
|
SelectObject(graphics->hdc, oldpen);
|
|
|
|
DeleteObject(brush);
|
|
|
|
DeleteObject(pen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shortens the line by the given percent by changing x2, y2.
|
|
|
|
* If percent is > 1.0 then the line will change direction.
|
|
|
|
* If percent is negative it can lengthen the line. */
|
|
|
|
static void shorten_line_percent(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL percent)
|
|
|
|
{
|
|
|
|
REAL dist, theta, dx, dy;
|
|
|
|
|
|
|
|
if((y1 == *y2) && (x1 == *x2))
|
|
|
|
return;
|
|
|
|
|
|
|
|
dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
|
|
|
|
theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
|
|
|
|
dx = cos(theta) * dist;
|
|
|
|
dy = sin(theta) * dist;
|
|
|
|
|
|
|
|
*x2 = *x2 + dx;
|
|
|
|
*y2 = *y2 + dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Shortens the line by the given amount by changing x2, y2.
|
|
|
|
* If the amount is greater than the distance, the line will become length 0.
|
|
|
|
* If the amount is negative, it can lengthen the line. */
|
|
|
|
static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
|
|
|
|
{
|
|
|
|
REAL dx, dy, percent;
|
|
|
|
|
|
|
|
dx = *x2 - x1;
|
|
|
|
dy = *y2 - y1;
|
|
|
|
if(dx == 0 && dy == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
percent = amt / sqrt(dx * dx + dy * dy);
|
|
|
|
if(percent >= 1.0){
|
|
|
|
*x2 = x1;
|
|
|
|
*y2 = y1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
shorten_line_percent(x1, y1, x2, y2, percent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Conducts a linear search to find the bezier points that will back off
|
|
|
|
* the endpoint of the curve by a distance of amt. Linear search works
|
|
|
|
* better than binary in this case because there are multiple solutions,
|
|
|
|
* and binary searches often find a bad one. I don't think this is what
|
|
|
|
* Windows does but short of rendering the bezier without GDI's help it's
|
|
|
|
* the best we can do. If rev then work from the start of the passed points
|
|
|
|
* instead of the end. */
|
|
|
|
static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
|
|
|
|
{
|
|
|
|
GpPointF origpt[4];
|
|
|
|
REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
|
|
|
|
INT i, first = 0, second = 1, third = 2, fourth = 3;
|
|
|
|
|
|
|
|
if(rev){
|
|
|
|
first = 3;
|
|
|
|
second = 2;
|
|
|
|
third = 1;
|
|
|
|
fourth = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
origx = pt[fourth].X;
|
|
|
|
origy = pt[fourth].Y;
|
|
|
|
memcpy(origpt, pt, sizeof(GpPointF) * 4);
|
|
|
|
|
|
|
|
for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
|
|
|
|
/* reset bezier points to original values */
|
|
|
|
memcpy(pt, origpt, sizeof(GpPointF) * 4);
|
|
|
|
/* Perform magic on bezier points. Order is important here.*/
|
|
|
|
shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
|
|
|
|
shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
|
|
|
|
shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
|
|
|
|
shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
|
|
|
|
shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
|
|
|
|
shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
|
|
|
|
|
|
|
|
dx = pt[fourth].X - origx;
|
|
|
|
dy = pt[fourth].Y - origy;
|
|
|
|
|
|
|
|
diff = sqrt(dx * dx + dy * dy);
|
|
|
|
percent += 0.0005 * amt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draws a combination of bezier curves and lines between points. */
|
|
|
|
static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
|
|
|
|
GDIPCONST BYTE * types, INT count, BOOL caps)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
POINT *pti = heap_alloc_zero(count * sizeof(POINT));
|
|
|
|
BYTE *tp = heap_alloc_zero(count);
|
|
|
|
GpPointF *ptcopy = heap_alloc_zero(count * sizeof(GpPointF));
|
2008-03-25 17:34:57 +00:00
|
|
|
INT i, j;
|
|
|
|
GpStatus status = GenericError;
|
|
|
|
|
|
|
|
if(!count){
|
|
|
|
status = Ok;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if(!pti || !tp || !ptcopy){
|
|
|
|
status = OutOfMemory;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 1; i < count; i++){
|
|
|
|
if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
|
|
|
|
if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
|
2015-03-22 18:33:30 +00:00
|
|
|
|| !(types[i + 2] & PathPointTypeBezier)){
|
2008-03-25 17:34:57 +00:00
|
|
|
ERR("Bad bezier points\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
i += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(ptcopy, pt, count * sizeof(GpPointF));
|
|
|
|
|
|
|
|
/* If we are drawing caps, go through the points and adjust them accordingly,
|
|
|
|
* and draw the caps. */
|
|
|
|
if(caps){
|
|
|
|
switch(types[count - 1] & PathPointTypePathTypeMask){
|
|
|
|
case PathPointTypeBezier:
|
|
|
|
if(pen->endcap == LineCapArrowAnchor)
|
|
|
|
shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
|
|
|
|
else if((pen->endcap == LineCapCustom) && pen->customend)
|
|
|
|
shorten_bezier_amt(&ptcopy[count - 4],
|
|
|
|
pen->width * pen->customend->inset, FALSE);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
|
2008-03-25 17:34:57 +00:00
|
|
|
pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
|
|
|
|
pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
|
|
|
|
pt[count - 1].X, pt[count - 1].Y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case PathPointTypeLine:
|
|
|
|
if(pen->endcap == LineCapArrowAnchor)
|
|
|
|
shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
|
|
|
|
&ptcopy[count - 1].X, &ptcopy[count - 1].Y,
|
|
|
|
pen->width);
|
|
|
|
else if((pen->endcap == LineCapCustom) && pen->customend)
|
|
|
|
shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
|
|
|
|
&ptcopy[count - 1].X, &ptcopy[count - 1].Y,
|
|
|
|
pen->customend->inset * pen->width);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
|
2008-03-25 17:34:57 +00:00
|
|
|
pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
|
|
|
|
pt[count - 1].Y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERR("Bad path last point\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find start of points */
|
|
|
|
for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
|
|
|
|
== PathPointTypeStart); j++);
|
|
|
|
|
|
|
|
switch(types[j] & PathPointTypePathTypeMask){
|
|
|
|
case PathPointTypeBezier:
|
|
|
|
if(pen->startcap == LineCapArrowAnchor)
|
|
|
|
shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
|
|
|
|
else if((pen->startcap == LineCapCustom) && pen->customstart)
|
|
|
|
shorten_bezier_amt(&ptcopy[j - 1],
|
|
|
|
pen->width * pen->customstart->inset, TRUE);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
|
2008-03-25 17:34:57 +00:00
|
|
|
pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
|
|
|
|
pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
|
|
|
|
pt[j - 1].X, pt[j - 1].Y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case PathPointTypeLine:
|
|
|
|
if(pen->startcap == LineCapArrowAnchor)
|
|
|
|
shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
|
|
|
|
&ptcopy[j - 1].X, &ptcopy[j - 1].Y,
|
|
|
|
pen->width);
|
|
|
|
else if((pen->startcap == LineCapCustom) && pen->customstart)
|
|
|
|
shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
|
|
|
|
&ptcopy[j - 1].X, &ptcopy[j - 1].Y,
|
|
|
|
pen->customstart->inset * pen->width);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
|
2008-03-25 17:34:57 +00:00
|
|
|
pt[j].X, pt[j].Y, pt[j - 1].X,
|
|
|
|
pt[j - 1].Y);
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERR("Bad path points\n");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptcopy, count);
|
|
|
|
|
|
|
|
round_points(pti, ptcopy, count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
tp[i] = convert_path_point_type(types[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
PolyDraw(graphics->hdc, pti, tp, count);
|
|
|
|
|
|
|
|
status = Ok;
|
|
|
|
|
|
|
|
end:
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pti);
|
|
|
|
heap_free(ptcopy);
|
|
|
|
heap_free(tp);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
GpStatus trace_path(GpGraphics *graphics, GpPath *path)
|
|
|
|
{
|
|
|
|
GpStatus result;
|
|
|
|
|
|
|
|
BeginPath(graphics->hdc);
|
|
|
|
result = draw_poly(graphics, NULL, path->pathdata.Points,
|
|
|
|
path->pathdata.Types, path->pathdata.Count, FALSE);
|
|
|
|
EndPath(graphics->hdc);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
typedef enum GraphicsContainerType {
|
|
|
|
BEGIN_CONTAINER,
|
|
|
|
SAVE_GRAPHICS
|
|
|
|
} GraphicsContainerType;
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
typedef struct _GraphicsContainerItem {
|
|
|
|
struct list entry;
|
|
|
|
GraphicsContainer contid;
|
2016-11-17 22:35:44 +00:00
|
|
|
GraphicsContainerType type;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
SmoothingMode smoothing;
|
|
|
|
CompositingQuality compqual;
|
|
|
|
InterpolationMode interpolation;
|
|
|
|
CompositingMode compmode;
|
|
|
|
TextRenderingHint texthint;
|
|
|
|
REAL scale;
|
|
|
|
GpUnit unit;
|
|
|
|
PixelOffsetMode pixeloffset;
|
|
|
|
UINT textcontrast;
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix worldtrans;
|
2009-09-03 15:07:25 +00:00
|
|
|
GpRegion* clip;
|
2012-05-19 10:03:40 +00:00
|
|
|
INT origin_x, origin_y;
|
2009-09-03 15:07:25 +00:00
|
|
|
} GraphicsContainerItem;
|
|
|
|
|
|
|
|
static GpStatus init_container(GraphicsContainerItem** container,
|
2016-11-17 22:35:44 +00:00
|
|
|
GDIPCONST GpGraphics* graphics, GraphicsContainerType type){
|
2009-09-03 15:07:25 +00:00
|
|
|
GpStatus sts;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
*container = heap_alloc_zero(sizeof(GraphicsContainerItem));
|
2009-09-03 15:07:25 +00:00
|
|
|
if(!(*container))
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
(*container)->contid = graphics->contid + 1;
|
2016-11-17 22:35:44 +00:00
|
|
|
(*container)->type = type;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
(*container)->smoothing = graphics->smoothing;
|
|
|
|
(*container)->compqual = graphics->compqual;
|
|
|
|
(*container)->interpolation = graphics->interpolation;
|
|
|
|
(*container)->compmode = graphics->compmode;
|
|
|
|
(*container)->texthint = graphics->texthint;
|
|
|
|
(*container)->scale = graphics->scale;
|
|
|
|
(*container)->unit = graphics->unit;
|
|
|
|
(*container)->textcontrast = graphics->textcontrast;
|
|
|
|
(*container)->pixeloffset = graphics->pixeloffset;
|
2012-05-19 10:03:40 +00:00
|
|
|
(*container)->origin_x = graphics->origin_x;
|
|
|
|
(*container)->origin_y = graphics->origin_y;
|
2012-12-11 21:40:15 +00:00
|
|
|
(*container)->worldtrans = graphics->worldtrans;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
|
|
|
|
if(sts != Ok){
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(*container);
|
2009-09-03 15:07:25 +00:00
|
|
|
*container = NULL;
|
|
|
|
return sts;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
static void delete_container(GraphicsContainerItem* container)
|
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
GdipDeleteRegion(container->clip);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(container);
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus restore_container(GpGraphics* graphics,
|
|
|
|
GDIPCONST GraphicsContainerItem* container){
|
|
|
|
GpStatus sts;
|
|
|
|
GpRegion *newClip;
|
|
|
|
|
|
|
|
sts = GdipCloneRegion(container->clip, &newClip);
|
2012-12-11 21:40:15 +00:00
|
|
|
if(sts != Ok) return sts;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
graphics->worldtrans = container->worldtrans;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
GdipDeleteRegion(graphics->clip);
|
|
|
|
graphics->clip = newClip;
|
|
|
|
|
|
|
|
graphics->contid = container->contid - 1;
|
|
|
|
|
|
|
|
graphics->smoothing = container->smoothing;
|
|
|
|
graphics->compqual = container->compqual;
|
|
|
|
graphics->interpolation = container->interpolation;
|
|
|
|
graphics->compmode = container->compmode;
|
|
|
|
graphics->texthint = container->texthint;
|
|
|
|
graphics->scale = container->scale;
|
|
|
|
graphics->unit = container->unit;
|
|
|
|
graphics->textcontrast = container->textcontrast;
|
|
|
|
graphics->pixeloffset = container->pixeloffset;
|
2012-05-19 10:03:40 +00:00
|
|
|
graphics->origin_x = container->origin_x;
|
|
|
|
graphics->origin_y = container->origin_y;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
static GpStatus get_graphics_device_bounds(GpGraphics* graphics, GpRectF* rect)
|
2009-09-03 15:07:25 +00:00
|
|
|
{
|
|
|
|
RECT wnd_rect;
|
2010-11-20 11:24:17 +00:00
|
|
|
GpStatus stat=Ok;
|
|
|
|
GpUnit unit;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
if(graphics->hwnd) {
|
|
|
|
if(!GetClientRect(graphics->hwnd, &wnd_rect))
|
|
|
|
return GenericError;
|
|
|
|
|
|
|
|
rect->X = wnd_rect.left;
|
|
|
|
rect->Y = wnd_rect.top;
|
|
|
|
rect->Width = wnd_rect.right - wnd_rect.left;
|
|
|
|
rect->Height = wnd_rect.bottom - wnd_rect.top;
|
2010-11-20 11:24:17 +00:00
|
|
|
}else if (graphics->image){
|
|
|
|
stat = GdipGetImageBounds(graphics->image, rect, &unit);
|
|
|
|
if (stat == Ok && unit != UnitPixel)
|
|
|
|
FIXME("need to convert from unit %i\n", unit);
|
2012-12-11 21:40:15 +00:00
|
|
|
}else if (GetObjectType(graphics->hdc) == OBJ_MEMDC){
|
|
|
|
HBITMAP hbmp;
|
|
|
|
BITMAP bmp;
|
|
|
|
|
|
|
|
rect->X = 0;
|
|
|
|
rect->Y = 0;
|
|
|
|
|
|
|
|
hbmp = GetCurrentObject(graphics->hdc, OBJ_BITMAP);
|
|
|
|
if (hbmp && GetObjectW(hbmp, sizeof(bmp), &bmp))
|
|
|
|
{
|
|
|
|
rect->Width = bmp.bmWidth;
|
|
|
|
rect->Height = bmp.bmHeight;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* FIXME: ??? */
|
|
|
|
rect->Width = 1;
|
|
|
|
rect->Height = 1;
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
}else{
|
|
|
|
rect->X = 0;
|
|
|
|
rect->Y = 0;
|
|
|
|
rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
|
|
|
|
rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
|
|
|
|
{
|
|
|
|
GpStatus stat = get_graphics_device_bounds(graphics, rect);
|
|
|
|
|
|
|
|
if (stat == Ok && graphics->hdc)
|
2014-09-27 12:59:00 +00:00
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
GpPointF points[4], min_point, max_point;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
points[0].X = points[2].X = rect->X;
|
|
|
|
points[0].Y = points[1].Y = rect->Y;
|
|
|
|
points[1].X = points[3].X = rect->X + rect->Width;
|
|
|
|
points[2].Y = points[3].Y = rect->Y + rect->Height;
|
2014-09-27 12:59:00 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdip_transform_points(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, points, 4);
|
2014-09-27 12:59:00 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
min_point = max_point = points[0];
|
2014-09-27 12:59:00 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
for (i=1; i<4; i++)
|
|
|
|
{
|
|
|
|
if (points[i].X < min_point.X) min_point.X = points[i].X;
|
|
|
|
if (points[i].Y < min_point.Y) min_point.Y = points[i].Y;
|
|
|
|
if (points[i].X > max_point.X) max_point.X = points[i].X;
|
|
|
|
if (points[i].Y > max_point.Y) max_point.Y = points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
rect->X = min_point.X;
|
|
|
|
rect->Y = min_point.Y;
|
|
|
|
rect->Width = max_point.X - min_point.X;
|
|
|
|
rect->Height = max_point.Y - min_point.Y;
|
2014-09-27 12:59:00 +00:00
|
|
|
}
|
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
return stat;
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* on success, rgn will contain the region of the graphics object which
|
|
|
|
* is visible after clipping has been applied */
|
|
|
|
static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpRectF rectf;
|
|
|
|
GpRegion* tmp;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
/* Ignore graphics image bounds for metafiles */
|
|
|
|
if (graphics->image && graphics->image_type == ImageTypeMetafile)
|
|
|
|
return GdipCombineRegionRegion(rgn, graphics->clip, CombineModeReplace);
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = GdipCreateRegion(&tmp)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
|
|
|
|
|
|
|
|
end:
|
|
|
|
GdipDeleteRegion(tmp);
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
void get_log_fontW(const GpFont *font, GpGraphics *graphics, LOGFONTW *lf)
|
|
|
|
{
|
|
|
|
REAL height;
|
|
|
|
|
|
|
|
if (font->unit == UnitPixel)
|
|
|
|
{
|
|
|
|
height = units_to_pixels(font->emSize, graphics->unit, graphics->yres);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (graphics->unit == UnitDisplay || graphics->unit == UnitPixel)
|
|
|
|
height = units_to_pixels(font->emSize, font->unit, graphics->xres);
|
|
|
|
else
|
|
|
|
height = units_to_pixels(font->emSize, font->unit, graphics->yres);
|
|
|
|
}
|
|
|
|
|
|
|
|
lf->lfHeight = -(height + 0.5);
|
|
|
|
lf->lfWidth = 0;
|
|
|
|
lf->lfEscapement = 0;
|
|
|
|
lf->lfOrientation = 0;
|
|
|
|
lf->lfWeight = font->otm.otmTextMetrics.tmWeight;
|
|
|
|
lf->lfItalic = font->otm.otmTextMetrics.tmItalic ? 1 : 0;
|
|
|
|
lf->lfUnderline = font->otm.otmTextMetrics.tmUnderlined ? 1 : 0;
|
|
|
|
lf->lfStrikeOut = font->otm.otmTextMetrics.tmStruckOut ? 1 : 0;
|
|
|
|
lf->lfCharSet = font->otm.otmTextMetrics.tmCharSet;
|
|
|
|
lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
|
|
lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
|
|
lf->lfQuality = DEFAULT_QUALITY;
|
|
|
|
lf->lfPitchAndFamily = 0;
|
|
|
|
strcpyW(lf->lfFaceName, font->family->FamilyName);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font,
|
|
|
|
GDIPCONST GpStringFormat *format, HFONT *hfont,
|
|
|
|
GDIPCONST GpMatrix *matrix)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
HDC hdc = CreateCompatibleDC(0);
|
|
|
|
GpPointF pt[3];
|
2012-12-11 21:40:15 +00:00
|
|
|
REAL angle, rel_width, rel_height, font_height;
|
2012-03-05 20:17:42 +00:00
|
|
|
LOGFONTW lfw;
|
|
|
|
HFONT unscaled_font;
|
|
|
|
TEXTMETRICW textmet;
|
2008-09-07 10:32:49 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (font->unit == UnitPixel || font->unit == UnitWorld)
|
2012-12-11 21:40:15 +00:00
|
|
|
font_height = font->emSize;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
REAL unit_scale, res;
|
|
|
|
|
|
|
|
res = (graphics->unit == UnitDisplay || graphics->unit == UnitPixel) ? graphics->xres : graphics->yres;
|
|
|
|
unit_scale = units_scale(font->unit, graphics->unit, res);
|
|
|
|
|
|
|
|
font_height = font->emSize * unit_scale;
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 0.0;
|
|
|
|
pt[2].X = 0.0;
|
|
|
|
pt[2].Y = 1.0;
|
2012-12-11 21:40:15 +00:00
|
|
|
if (matrix)
|
|
|
|
{
|
|
|
|
GpMatrix xform = *matrix;
|
|
|
|
GdipTransformMatrixPoints(&xform, pt, 3);
|
|
|
|
}
|
2015-07-19 13:31:15 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
|
2012-03-05 20:17:42 +00:00
|
|
|
angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
|
|
|
|
rel_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));
|
|
|
|
rel_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));
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
get_log_fontW(font, graphics, &lfw);
|
2013-09-19 15:10:19 +00:00
|
|
|
lfw.lfHeight = -gdip_round(font_height * rel_height);
|
2012-03-05 20:17:42 +00:00
|
|
|
unscaled_font = CreateFontIndirectW(&lfw);
|
|
|
|
|
|
|
|
SelectObject(hdc, unscaled_font);
|
|
|
|
GetTextMetricsW(hdc, &textmet);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
lfw.lfWidth = gdip_round(textmet.tmAveCharWidth * rel_width / rel_height);
|
|
|
|
lfw.lfEscapement = lfw.lfOrientation = gdip_round((angle / M_PI) * 1800.0);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
*hfont = CreateFontIndirectW(&lfw);
|
|
|
|
|
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(unscaled_font);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", hdc, graphics);
|
|
|
|
|
|
|
|
return GdipCreateFromHDC2(hdc, NULL, graphics);
|
|
|
|
}
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
static void get_gdi_transform(GpGraphics *graphics, GpMatrix *matrix)
|
|
|
|
{
|
|
|
|
XFORM xform;
|
|
|
|
|
|
|
|
if (graphics->hdc == NULL)
|
|
|
|
{
|
|
|
|
GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetTransform(graphics->hdc, 0x204, &xform);
|
|
|
|
GdipSetMatrixElements(matrix, xform.eM11, xform.eM12, xform.eM21, xform.eM22, xform.eDx, xform.eDy);
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
|
|
|
|
{
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus retval;
|
2012-12-11 21:40:15 +00:00
|
|
|
HBITMAP hbitmap;
|
|
|
|
DIBSECTION dib;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
if(hDevice != NULL)
|
2009-09-03 15:07:25 +00:00
|
|
|
FIXME("Don't know how to handle parameter hDevice\n");
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
if(hdc == NULL)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
if(graphics == NULL)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
*graphics = heap_alloc_zero(sizeof(GpGraphics));
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!*graphics) return OutOfMemory;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(*graphics);
|
2008-09-07 10:32:49 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
|
|
|
|
if (hbitmap && GetObjectW(hbitmap, sizeof(dib), &dib) == sizeof(dib) &&
|
|
|
|
dib.dsBmih.biBitCount == 32 && dib.dsBmih.biCompression == BI_RGB)
|
|
|
|
{
|
|
|
|
(*graphics)->alpha_hdc = 1;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
(*graphics)->hdc = hdc;
|
2008-09-07 10:32:49 +00:00
|
|
|
(*graphics)->hwnd = WindowFromDC(hdc);
|
2009-05-09 09:26:16 +00:00
|
|
|
(*graphics)->owndc = FALSE;
|
2008-03-25 17:34:57 +00:00
|
|
|
(*graphics)->smoothing = SmoothingModeDefault;
|
|
|
|
(*graphics)->compqual = CompositingQualityDefault;
|
2012-03-05 20:17:42 +00:00
|
|
|
(*graphics)->interpolation = InterpolationModeBilinear;
|
2008-03-25 17:34:57 +00:00
|
|
|
(*graphics)->pixeloffset = PixelOffsetModeDefault;
|
|
|
|
(*graphics)->compmode = CompositingModeSourceOver;
|
|
|
|
(*graphics)->unit = UnitDisplay;
|
|
|
|
(*graphics)->scale = 1.0;
|
2012-12-11 21:40:15 +00:00
|
|
|
(*graphics)->xres = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
|
|
(*graphics)->yres = GetDeviceCaps(hdc, LOGPIXELSY);
|
2008-09-07 10:32:49 +00:00
|
|
|
(*graphics)->busy = FALSE;
|
2008-12-06 09:26:01 +00:00
|
|
|
(*graphics)->textcontrast = 4;
|
2009-09-03 15:07:25 +00:00
|
|
|
list_init(&(*graphics)->containers);
|
2018-01-18 23:17:41 +00:00
|
|
|
#ifdef __REACTOS__
|
2012-09-14 21:26:45 +00:00
|
|
|
(*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
|
2018-01-18 23:17:41 +00:00
|
|
|
#else
|
|
|
|
(*graphics)->contid = 0;
|
|
|
|
#endif
|
|
|
|
get_gdi_transform(*graphics, &(*graphics)->gdi_transform);
|
|
|
|
|
|
|
|
(*graphics)->gdi_clip = CreateRectRgn(0,0,0,0);
|
|
|
|
if (!GetClipRgn(hdc, (*graphics)->gdi_clip))
|
|
|
|
{
|
|
|
|
DeleteObject((*graphics)->gdi_clip);
|
|
|
|
(*graphics)->gdi_clip = NULL;
|
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-03-04 13:34:05 +00:00
|
|
|
TRACE("<-- %p\n", *graphics);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
|
|
|
|
{
|
|
|
|
GpStatus retval;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
*graphics = heap_alloc_zero(sizeof(GpGraphics));
|
2010-11-20 11:24:17 +00:00
|
|
|
if(!*graphics) return OutOfMemory;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipSetMatrixElements(&(*graphics)->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
2018-01-18 23:17:41 +00:00
|
|
|
GdipSetMatrixElements(&(*graphics)->gdi_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
2010-11-20 11:24:17 +00:00
|
|
|
|
|
|
|
if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(*graphics);
|
2010-11-20 11:24:17 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*graphics)->hdc = NULL;
|
|
|
|
(*graphics)->hwnd = NULL;
|
|
|
|
(*graphics)->owndc = FALSE;
|
|
|
|
(*graphics)->image = image;
|
2013-05-23 17:23:00 +00:00
|
|
|
/* We have to store the image type here because the image may be freed
|
|
|
|
* before GdipDeleteGraphics is called, and metafiles need special treatment. */
|
|
|
|
(*graphics)->image_type = image->type;
|
2010-11-20 11:24:17 +00:00
|
|
|
(*graphics)->smoothing = SmoothingModeDefault;
|
|
|
|
(*graphics)->compqual = CompositingQualityDefault;
|
2012-03-05 20:17:42 +00:00
|
|
|
(*graphics)->interpolation = InterpolationModeBilinear;
|
2010-11-20 11:24:17 +00:00
|
|
|
(*graphics)->pixeloffset = PixelOffsetModeDefault;
|
|
|
|
(*graphics)->compmode = CompositingModeSourceOver;
|
|
|
|
(*graphics)->unit = UnitDisplay;
|
|
|
|
(*graphics)->scale = 1.0;
|
2012-12-11 21:40:15 +00:00
|
|
|
(*graphics)->xres = image->xres;
|
|
|
|
(*graphics)->yres = image->yres;
|
2010-11-20 11:24:17 +00:00
|
|
|
(*graphics)->busy = FALSE;
|
|
|
|
(*graphics)->textcontrast = 4;
|
|
|
|
list_init(&(*graphics)->containers);
|
2018-01-18 23:17:41 +00:00
|
|
|
#ifdef __REACTOS__
|
2012-09-14 21:26:45 +00:00
|
|
|
(*graphics)->contid = GDIP_GET_NEW_CONTID_FOR(*graphics);
|
2018-01-18 23:17:41 +00:00
|
|
|
#else
|
|
|
|
(*graphics)->contid = 0;
|
|
|
|
#endif
|
2010-11-20 11:24:17 +00:00
|
|
|
|
|
|
|
TRACE("<-- %p\n", *graphics);
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
|
|
|
|
{
|
|
|
|
GpStatus ret;
|
2009-05-09 09:26:16 +00:00
|
|
|
HDC hdc;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", hwnd, graphics);
|
|
|
|
|
2009-05-09 09:26:16 +00:00
|
|
|
hdc = GetDC(hwnd);
|
|
|
|
|
|
|
|
if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
|
|
|
|
{
|
|
|
|
ReleaseDC(hwnd, hdc);
|
2008-03-25 17:34:57 +00:00
|
|
|
return ret;
|
2009-05-09 09:26:16 +00:00
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
(*graphics)->hwnd = hwnd;
|
2009-05-09 09:26:16 +00:00
|
|
|
(*graphics)->owndc = TRUE;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-06-01 13:14:02 +00:00
|
|
|
/* FIXME: no icm handling */
|
|
|
|
GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", hwnd, graphics);
|
|
|
|
|
2008-06-01 13:14:02 +00:00
|
|
|
return GdipCreateFromHWND(hwnd, graphics);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
|
|
|
|
UINT access, IStream **stream)
|
|
|
|
{
|
|
|
|
DWORD dwMode;
|
|
|
|
HRESULT ret;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!stream || !filename)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(access & GENERIC_WRITE)
|
|
|
|
dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
|
|
|
|
else if(access & GENERIC_READ)
|
|
|
|
dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
|
|
|
|
else
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
ret = SHCreateStreamOnFileW(filename, dwMode, stream);
|
|
|
|
|
|
|
|
return hresult_to_status(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
|
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
GraphicsContainerItem *cont, *next;
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p)\n", graphics);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics) return InvalidParameter;
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy) return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
if (graphics->image && graphics->image_type == ImageTypeMetafile)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2009-05-09 09:26:16 +00:00
|
|
|
if(graphics->owndc)
|
2008-03-25 17:34:57 +00:00
|
|
|
ReleaseDC(graphics->hwnd, graphics->hdc);
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
|
|
|
|
list_remove(&cont->entry);
|
|
|
|
delete_container(cont);
|
|
|
|
}
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
GdipDeleteRegion(graphics->clip);
|
2014-04-22 15:42:03 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
DeleteObject(graphics->gdi_clip);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
/* Native returns ObjectBusy on the second free, instead of crashing as we'd
|
|
|
|
* do otherwise, but we can't have that in the test suite because it means
|
|
|
|
* accessing freed memory. */
|
|
|
|
graphics->busy = TRUE;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(graphics);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
|
|
|
|
REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
|
|
|
|
width, height, startAngle, sweepAngle);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !pen || width <= 0 || height <= 0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathArc(path, x, y, width, height, startAngle, sweepAngle);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
|
|
|
|
INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
|
|
|
|
width, height, startAngle, sweepAngle);
|
|
|
|
|
2008-06-01 13:14:02 +00:00
|
|
|
return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
|
|
|
|
REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
|
|
|
|
{
|
|
|
|
GpPointF pt[4];
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
|
|
|
|
x2, y2, x3, y3, x4, y4);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !pen)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
pt[0].X = x1;
|
|
|
|
pt[0].Y = y1;
|
|
|
|
pt[1].X = x2;
|
|
|
|
pt[1].Y = y2;
|
|
|
|
pt[2].X = x3;
|
|
|
|
pt[2].Y = y3;
|
|
|
|
pt[3].X = x4;
|
|
|
|
pt[3].Y = y4;
|
2013-05-23 17:23:00 +00:00
|
|
|
return GdipDrawBeziers(graphics, pen, pt, 4);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
|
|
|
|
INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
|
|
|
|
x2, y2, x3, y3, x4, y4);
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
return GdipDrawBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-07-06 16:35:30 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPointF *points, INT count)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-07-06 16:35:30 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-07-06 16:35:30 +00:00
|
|
|
if(!graphics || !pen || !points || (count <= 0))
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-07-06 16:35:30 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathBeziers(path, points, count);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
|
|
|
|
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-07-06 16:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPoint *points, INT count)
|
|
|
|
{
|
|
|
|
GpPointF *pts;
|
|
|
|
GpStatus ret;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-07-06 16:35:30 +00:00
|
|
|
if(!graphics || !pen || !points || (count <= 0))
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
pts = heap_alloc_zero(sizeof(GpPointF) * count);
|
2008-07-06 16:35:30 +00:00
|
|
|
if(!pts)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
pts[i].X = (REAL)points[i].X;
|
|
|
|
pts[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipDrawBeziers(graphics,pen,pts,count);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pts);
|
2008-07-06 16:35:30 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-08-05 12:23:58 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPointF *points, INT count)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-08-05 12:23:58 +00:00
|
|
|
return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPoint *points, INT count)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-08-05 12:23:58 +00:00
|
|
|
return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPointF *points, INT count, REAL tension)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
GpPath *path;
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
2008-08-05 12:23:58 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
|
|
|
|
|
2008-08-05 12:23:58 +00:00
|
|
|
if(!graphics || !pen || !points || count <= 0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
2008-08-05 12:23:58 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-08-05 12:23:58 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathClosedCurve2(path, points, count, tension);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-09-07 10:32:49 +00:00
|
|
|
|
|
|
|
GdipDeletePath(path);
|
2008-08-05 12:23:58 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
return status;
|
2008-08-05 12:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPoint *points, INT count, REAL tension)
|
|
|
|
{
|
|
|
|
GpPointF *ptf;
|
|
|
|
GpStatus stat;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
|
|
|
|
|
2008-08-05 12:23:58 +00:00
|
|
|
if(!points || count <= 0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
ptf = heap_alloc_zero(sizeof(GpPointF)*count);
|
2008-08-05 12:23:58 +00:00
|
|
|
if(!ptf)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
ptf[i].X = (REAL)points[i].X;
|
|
|
|
ptf[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(ptf);
|
2008-08-05 12:23:58 +00:00
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPointF *points, INT count)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
return GdipDrawCurve2(graphics,pen,points,count,1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPoint *points, INT count)
|
|
|
|
{
|
|
|
|
GpPointF *pointsF;
|
|
|
|
GpStatus ret;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
if(!points)
|
2008-05-04 19:41:25 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!pointsF)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
pointsF[i].X = (REAL)points[i].X;
|
|
|
|
pointsF[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipDrawCurve(graphics,pen,pointsF,count);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pointsF);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
/* Approximates cardinal spline with Bezier curves. */
|
|
|
|
GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPointF *points, INT count, REAL tension)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpPath *path;
|
|
|
|
GpStatus status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !pen)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
if(count < 2)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathCurve2(path, points, count, tension);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPoint *points, INT count, REAL tension)
|
|
|
|
{
|
|
|
|
GpPointF *pointsF;
|
|
|
|
GpStatus ret;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
if(!points)
|
2008-05-04 19:41:25 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
pointsF = heap_alloc_zero(sizeof(GpPointF)*count);
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!pointsF)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
pointsF[i].X = (REAL)points[i].X;
|
|
|
|
pointsF[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pointsF);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
|
|
|
|
REAL tension)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
|
|
|
|
|
|
|
|
if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
|
|
|
|
return InvalidParameter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
|
|
|
|
REAL tension)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
|
|
|
|
|
|
|
|
if(count < 0){
|
|
|
|
return OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
|
|
|
|
return InvalidParameter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
|
|
|
|
}
|
|
|
|
|
2008-07-02 08:19:00 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
|
|
|
|
REAL y, REAL width, REAL height)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpPath *path;
|
|
|
|
GpStatus status;
|
2008-07-02 08:19:00 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
|
|
|
|
|
2008-07-02 08:19:00 +00:00
|
|
|
if(!graphics || !pen)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-07-02 08:19:00 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathEllipse(path, x, y, width, height);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-07-02 08:19:00 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-07-02 08:19:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
|
|
|
|
INT y, INT width, INT height)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
|
|
|
|
|
2008-07-02 08:19:00 +00:00
|
|
|
return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
|
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
UINT width, height;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
|
2008-09-07 10:32:49 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !image)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
GdipGetImageWidth(image, &width);
|
|
|
|
GdipGetImageHeight(image, &height);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
return GdipDrawImagePointRect(graphics, image, x, y,
|
|
|
|
0.0, 0.0, (REAL)width, (REAL)height, UnitPixel);
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
|
|
|
|
INT y)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
|
|
|
|
|
|
|
|
return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2009-06-07 07:59:56 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
|
|
|
|
REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
|
|
|
|
GpUnit srcUnit)
|
|
|
|
{
|
2009-11-28 15:26:02 +00:00
|
|
|
GpPointF points[3];
|
2012-12-11 21:40:15 +00:00
|
|
|
REAL scale_x, scale_y, width, height;
|
|
|
|
|
2009-11-28 15:26:02 +00:00
|
|
|
TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
if (!graphics || !image) return InvalidParameter;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
scale_x = units_scale(srcUnit, graphics->unit, graphics->xres);
|
|
|
|
scale_x *= graphics->xres / image->xres;
|
|
|
|
scale_y = units_scale(srcUnit, graphics->unit, graphics->yres);
|
|
|
|
scale_y *= graphics->yres / image->yres;
|
|
|
|
width = srcwidth * scale_x;
|
|
|
|
height = srcheight * scale_y;
|
|
|
|
|
2009-11-28 15:26:02 +00:00
|
|
|
points[0].X = points[2].X = x;
|
|
|
|
points[0].Y = points[1].Y = y;
|
2012-12-11 21:40:15 +00:00
|
|
|
points[1].X = x + width;
|
|
|
|
points[2].Y = y + height;
|
2009-11-28 15:26:02 +00:00
|
|
|
|
|
|
|
return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
|
|
|
|
srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
|
2009-06-07 07:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
|
|
|
|
INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
|
|
|
|
GpUnit srcUnit)
|
|
|
|
{
|
2009-11-28 15:26:02 +00:00
|
|
|
return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
|
2009-06-07 07:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
|
|
|
|
GDIPCONST GpPointF *dstpoints, INT count)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
UINT width, height;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
|
|
|
|
|
|
|
|
if(!image)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
GdipGetImageWidth(image, &width);
|
|
|
|
GdipGetImageHeight(image, &height);
|
|
|
|
|
|
|
|
return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
|
|
|
|
width, height, UnitPixel, NULL, NULL, NULL);
|
2009-06-07 07:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
|
|
|
|
GDIPCONST GpPoint *dstpoints, INT count)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpPointF ptf[3];
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
|
|
|
|
|
|
|
|
if (count != 3 || !dstpoints)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
ptf[0].X = (REAL)dstpoints[0].X;
|
|
|
|
ptf[0].Y = (REAL)dstpoints[0].Y;
|
|
|
|
ptf[1].X = (REAL)dstpoints[1].X;
|
|
|
|
ptf[1].Y = (REAL)dstpoints[1].Y;
|
|
|
|
ptf[2].X = (REAL)dstpoints[2].X;
|
|
|
|
ptf[2].Y = (REAL)dstpoints[2].Y;
|
|
|
|
|
|
|
|
return GdipDrawImagePoints(graphics, image, ptf, count);
|
2009-06-07 07:59:56 +00:00
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
|
|
|
|
unsigned int dataSize, const unsigned char *pStr, void *userdata)
|
|
|
|
{
|
|
|
|
GdipPlayMetafileRecord(userdata, record_type, flags, dataSize, pStr);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
|
|
|
|
GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
|
|
|
|
REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
|
|
|
|
DrawImageAbort callback, VOID * callbackData)
|
|
|
|
{
|
2010-04-20 08:30:10 +00:00
|
|
|
GpPointF ptf[4];
|
|
|
|
POINT pti[4];
|
|
|
|
GpStatus stat;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
|
|
|
|
count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
|
2008-03-25 17:34:57 +00:00
|
|
|
callbackData);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (count > 3)
|
|
|
|
return NotImplemented;
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!graphics || !image || !points || count != 3)
|
2008-03-25 17:34:57 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2010-03-04 13:34:05 +00:00
|
|
|
TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
|
|
|
|
debugstr_pointf(&points[2]));
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
return METAFILE_DrawImagePointsRect((GpMetafile*)graphics->image,
|
|
|
|
image, points, count, srcx, srcy, srcwidth, srcheight,
|
|
|
|
srcUnit, imageAttributes, callback, callbackData);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
memcpy(ptf, points, 3 * sizeof(GpPointF));
|
2014-04-22 15:42:03 +00:00
|
|
|
|
|
|
|
/* Ensure source width/height is positive */
|
|
|
|
if (srcwidth < 0)
|
|
|
|
{
|
|
|
|
GpPointF tmp = ptf[1];
|
|
|
|
srcx = srcx + srcwidth;
|
|
|
|
srcwidth = -srcwidth;
|
|
|
|
ptf[2].X = ptf[2].X + ptf[1].X - ptf[0].X;
|
|
|
|
ptf[2].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
|
|
|
|
ptf[1] = ptf[0];
|
|
|
|
ptf[0] = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srcheight < 0)
|
|
|
|
{
|
|
|
|
GpPointF tmp = ptf[2];
|
|
|
|
srcy = srcy + srcheight;
|
|
|
|
srcheight = -srcheight;
|
|
|
|
ptf[1].X = ptf[1].X + ptf[2].X - ptf[0].X;
|
|
|
|
ptf[1].Y = ptf[1].Y + ptf[2].Y - ptf[0].Y;
|
|
|
|
ptf[2] = ptf[0];
|
|
|
|
ptf[0] = tmp;
|
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
|
|
|
|
ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
|
2014-04-22 15:42:03 +00:00
|
|
|
if (!srcwidth || !srcheight || (ptf[3].X == ptf[0].X && ptf[3].Y == ptf[0].Y))
|
2012-03-05 20:17:42 +00:00
|
|
|
return Ok;
|
2017-09-17 12:34:15 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, ptf, 4);
|
|
|
|
round_points(pti, ptf, 4);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
TRACE("%s %s %s %s\n", wine_dbgstr_point(&pti[0]), wine_dbgstr_point(&pti[1]),
|
|
|
|
wine_dbgstr_point(&pti[2]), wine_dbgstr_point(&pti[3]));
|
|
|
|
|
|
|
|
srcx = units_to_pixels(srcx, srcUnit, image->xres);
|
|
|
|
srcy = units_to_pixels(srcy, srcUnit, image->yres);
|
|
|
|
srcwidth = units_to_pixels(srcwidth, srcUnit, image->xres);
|
|
|
|
srcheight = units_to_pixels(srcheight, srcUnit, image->yres);
|
|
|
|
TRACE("src pixels: %f,%f %fx%f\n", srcx, srcy, srcwidth, srcheight);
|
|
|
|
|
2016-06-05 19:08:07 +00:00
|
|
|
if (image->type == ImageTypeBitmap)
|
2009-09-03 15:07:25 +00:00
|
|
|
{
|
|
|
|
GpBitmap* bitmap = (GpBitmap*)image;
|
2014-09-27 12:59:00 +00:00
|
|
|
BOOL do_resampling = FALSE;
|
2014-04-22 15:42:03 +00:00
|
|
|
BOOL use_software = FALSE;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
TRACE("graphics: %.2fx%.2f dpi, fmt %#x, scale %f, image: %.2fx%.2f dpi, fmt %#x, color %08x\n",
|
|
|
|
graphics->xres, graphics->yres,
|
|
|
|
graphics->image && graphics->image->type == ImageTypeBitmap ? ((GpBitmap *)graphics->image)->format : 0,
|
|
|
|
graphics->scale, image->xres, image->yres, bitmap->format,
|
|
|
|
imageAttributes ? imageAttributes->outside_color : 0);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
if (ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
|
2012-03-05 20:17:42 +00:00
|
|
|
ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
|
|
|
|
srcx < 0 || srcy < 0 ||
|
|
|
|
srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
|
2014-09-27 12:59:00 +00:00
|
|
|
do_resampling = TRUE;
|
|
|
|
|
|
|
|
if (imageAttributes || graphics->alpha_hdc || do_resampling ||
|
|
|
|
(graphics->image && graphics->image->type == ImageTypeBitmap))
|
2014-04-22 15:42:03 +00:00
|
|
|
use_software = TRUE;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
if (use_software)
|
2009-09-03 15:07:25 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
RECT dst_area;
|
2014-04-22 15:42:03 +00:00
|
|
|
GpRectF graphics_bounds;
|
2012-03-05 20:17:42 +00:00
|
|
|
GpRect src_area;
|
|
|
|
int i, x, y, src_stride, dst_stride;
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix dst_to_src;
|
2010-04-20 08:30:10 +00:00
|
|
|
REAL m11, m12, m21, m22, mdx, mdy;
|
2014-09-27 12:59:00 +00:00
|
|
|
LPBYTE src_data, dst_data, dst_dyn_data=NULL;
|
2012-03-05 20:17:42 +00:00
|
|
|
BitmapData lockeddata;
|
|
|
|
InterpolationMode interpolation = graphics->interpolation;
|
2012-12-11 21:40:15 +00:00
|
|
|
PixelOffsetMode offset_mode = graphics->pixeloffset;
|
2012-03-05 20:17:42 +00:00
|
|
|
GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
|
|
|
|
REAL x_dx, x_dy, y_dx, y_dy;
|
|
|
|
static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!imageAttributes)
|
|
|
|
imageAttributes = &defaultImageAttributes;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
dst_area.left = dst_area.right = pti[0].x;
|
|
|
|
dst_area.top = dst_area.bottom = pti[0].y;
|
|
|
|
for (i=1; i<4; i++)
|
|
|
|
{
|
|
|
|
if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
|
|
|
|
if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
|
|
|
|
if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
|
|
|
|
if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = get_graphics_device_bounds(graphics, &graphics_bounds);
|
2014-04-22 15:42:03 +00:00
|
|
|
if (stat != Ok) return stat;
|
|
|
|
|
|
|
|
if (graphics_bounds.X > dst_area.left) dst_area.left = floorf(graphics_bounds.X);
|
|
|
|
if (graphics_bounds.Y > dst_area.top) dst_area.top = floorf(graphics_bounds.Y);
|
|
|
|
if (graphics_bounds.X + graphics_bounds.Width < dst_area.right) dst_area.right = ceilf(graphics_bounds.X + graphics_bounds.Width);
|
|
|
|
if (graphics_bounds.Y + graphics_bounds.Height < dst_area.bottom) dst_area.bottom = ceilf(graphics_bounds.Y + graphics_bounds.Height);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
TRACE("dst_area: %s\n", wine_dbgstr_rect(&dst_area));
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (IsRectEmpty(&dst_area)) return Ok;
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
m11 = (ptf[1].X - ptf[0].X) / srcwidth;
|
|
|
|
m21 = (ptf[2].X - ptf[0].X) / srcheight;
|
|
|
|
mdx = ptf[0].X - m11 * srcx - m21 * srcy;
|
|
|
|
m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
|
|
|
|
m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
|
|
|
|
mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
|
2010-03-04 13:34:05 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipSetMatrixElements(&dst_to_src, m11, m12, m21, m22, mdx, mdy);
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = GdipInvertMatrix(&dst_to_src);
|
|
|
|
if (stat != Ok) return stat;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
if (do_resampling)
|
|
|
|
{
|
|
|
|
get_bitmap_sample_size(interpolation, imageAttributes->wrap,
|
|
|
|
bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Make sure src_area is equal in size to dst_area. */
|
|
|
|
src_area.X = srcx + dst_area.left - pti[0].x;
|
|
|
|
src_area.Y = srcy + dst_area.top - pti[0].y;
|
|
|
|
src_area.Width = dst_area.right - dst_area.left;
|
|
|
|
src_area.Height = dst_area.bottom - dst_area.top;
|
|
|
|
}
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
TRACE("src_area: %d x %d\n", src_area.Width, src_area.Height);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
src_data = heap_alloc_zero(sizeof(ARGB) * src_area.Width * src_area.Height);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!src_data)
|
|
|
|
return OutOfMemory;
|
|
|
|
src_stride = sizeof(ARGB) * src_area.Width;
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
/* Read the bits we need from the source bitmap into a compatible buffer. */
|
2012-03-05 20:17:42 +00:00
|
|
|
lockeddata.Width = src_area.Width;
|
|
|
|
lockeddata.Height = src_area.Height;
|
|
|
|
lockeddata.Stride = src_stride;
|
|
|
|
lockeddata.Scan0 = src_data;
|
2015-07-19 13:31:15 +00:00
|
|
|
if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
|
|
|
|
lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
|
2018-03-09 12:09:03 +00:00
|
|
|
else if (imageAttributes != &defaultImageAttributes)
|
2018-01-18 23:17:41 +00:00
|
|
|
lockeddata.PixelFormat = PixelFormat32bppARGB;
|
2018-03-09 12:09:03 +00:00
|
|
|
else
|
|
|
|
lockeddata.PixelFormat = PixelFormat32bppPARGB;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
|
2015-07-19 13:31:15 +00:00
|
|
|
lockeddata.PixelFormat, &lockeddata);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat != Ok)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(src_data);
|
2012-12-11 21:40:15 +00:00
|
|
|
return stat;
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
apply_image_attributes(imageAttributes, src_data,
|
|
|
|
src_area.Width, src_area.Height,
|
2015-07-19 13:31:15 +00:00
|
|
|
src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
if (do_resampling)
|
|
|
|
{
|
2018-03-09 12:09:03 +00:00
|
|
|
REAL delta_xx, delta_xy, delta_yx, delta_yy;
|
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
/* Transform the bits as needed to the destination. */
|
2015-11-22 10:10:02 +00:00
|
|
|
dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
|
2014-09-27 12:59:00 +00:00
|
|
|
if (!dst_data)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(src_data);
|
2014-09-27 12:59:00 +00:00
|
|
|
return OutOfMemory;
|
|
|
|
}
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
GdipTransformMatrixPoints(&dst_to_src, dst_to_src_points, 3);
|
|
|
|
|
|
|
|
x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
|
|
|
|
x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
|
|
|
|
y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
|
|
|
|
y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
|
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
delta_yy = dst_area.top * y_dy;
|
|
|
|
delta_yx = dst_area.top * y_dx;
|
|
|
|
|
|
|
|
for (y=dst_area.top; y<dst_area.bottom; y++)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
2018-03-09 12:09:03 +00:00
|
|
|
delta_xx = dst_area.left * x_dx;
|
|
|
|
delta_xy = dst_area.left * x_dy;
|
|
|
|
|
|
|
|
for (x=dst_area.left; x<dst_area.right; x++)
|
2014-09-27 12:59:00 +00:00
|
|
|
{
|
|
|
|
GpPointF src_pointf;
|
|
|
|
ARGB *dst_color;
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
|
2018-03-09 12:09:03 +00:00
|
|
|
src_pointf.X = dst_to_src_points[0].X + delta_xx + delta_yx;
|
|
|
|
src_pointf.Y = dst_to_src_points[0].Y + delta_xy + delta_yy;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2014-09-27 12:59:00 +00:00
|
|
|
if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
|
2018-03-09 12:09:03 +00:00
|
|
|
{
|
|
|
|
if (lockeddata.PixelFormat != PixelFormat32bppPARGB)
|
|
|
|
*dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
|
|
|
|
imageAttributes, interpolation, offset_mode);
|
|
|
|
else
|
|
|
|
*dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf,
|
|
|
|
imageAttributes, interpolation, offset_mode);
|
|
|
|
}
|
2014-09-27 12:59:00 +00:00
|
|
|
else
|
|
|
|
*dst_color = 0;
|
2018-03-09 12:09:03 +00:00
|
|
|
|
|
|
|
delta_xx += x_dx;
|
|
|
|
delta_yx += y_dx;
|
2014-09-27 12:59:00 +00:00
|
|
|
}
|
2018-03-09 12:09:03 +00:00
|
|
|
|
|
|
|
delta_xy += x_dy;
|
|
|
|
delta_yy += y_dy;
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
2014-09-27 12:59:00 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
dst_data = src_data;
|
|
|
|
dst_stride = src_stride;
|
|
|
|
}
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
|
2015-07-19 13:31:15 +00:00
|
|
|
dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
|
|
|
|
lockeddata.PixelFormat);
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(src_data);
|
2014-09-27 12:59:00 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(dst_dyn_data);
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
return stat;
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-04-20 08:30:10 +00:00
|
|
|
HDC hdc;
|
2014-04-22 15:42:03 +00:00
|
|
|
BOOL temp_hdc = FALSE, temp_bitmap = FALSE;
|
2010-04-20 08:30:10 +00:00
|
|
|
HBITMAP hbitmap, old_hbm=NULL;
|
2016-11-17 22:35:44 +00:00
|
|
|
HRGN hrgn;
|
|
|
|
INT save_state;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
if (!(bitmap->format == PixelFormat16bppRGB555 ||
|
|
|
|
bitmap->format == PixelFormat24bppRGB ||
|
|
|
|
bitmap->format == PixelFormat32bppRGB ||
|
|
|
|
bitmap->format == PixelFormat32bppPARGB))
|
|
|
|
{
|
|
|
|
BITMAPINFOHEADER bih;
|
|
|
|
BYTE *temp_bits;
|
|
|
|
PixelFormat dst_format;
|
|
|
|
|
|
|
|
/* we can't draw a bitmap of this format directly */
|
|
|
|
hdc = CreateCompatibleDC(0);
|
2014-04-22 15:42:03 +00:00
|
|
|
temp_hdc = TRUE;
|
|
|
|
temp_bitmap = TRUE;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
bih.biSize = sizeof(BITMAPINFOHEADER);
|
|
|
|
bih.biWidth = bitmap->width;
|
|
|
|
bih.biHeight = -bitmap->height;
|
|
|
|
bih.biPlanes = 1;
|
|
|
|
bih.biBitCount = 32;
|
|
|
|
bih.biCompression = BI_RGB;
|
|
|
|
bih.biSizeImage = 0;
|
|
|
|
bih.biXPelsPerMeter = 0;
|
|
|
|
bih.biYPelsPerMeter = 0;
|
|
|
|
bih.biClrUsed = 0;
|
|
|
|
bih.biClrImportant = 0;
|
|
|
|
|
|
|
|
hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
|
|
|
|
(void**)&temp_bits, NULL, 0);
|
|
|
|
|
|
|
|
if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
|
|
|
|
dst_format = PixelFormat32bppPARGB;
|
|
|
|
else
|
|
|
|
dst_format = PixelFormat32bppRGB;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
convert_pixels(bitmap->width, bitmap->height,
|
|
|
|
bitmap->width*4, temp_bits, dst_format,
|
2012-12-11 21:40:15 +00:00
|
|
|
bitmap->stride, bitmap->bits, bitmap->format,
|
|
|
|
bitmap->image.palette);
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
if (bitmap->hbitmap)
|
|
|
|
hbitmap = bitmap->hbitmap;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
|
2014-04-22 15:42:03 +00:00
|
|
|
temp_bitmap = TRUE;
|
2012-12-11 21:40:15 +00:00
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
hdc = bitmap->hdc;
|
|
|
|
temp_hdc = (hdc == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_hdc)
|
|
|
|
{
|
|
|
|
if (!hdc) hdc = CreateCompatibleDC(0);
|
|
|
|
old_hbm = SelectObject(hdc, hbitmap);
|
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
save_state = SaveDC(graphics->hdc);
|
|
|
|
|
|
|
|
stat = get_clip_hrgn(graphics, &hrgn);
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
if (stat == Ok)
|
2016-11-17 22:35:44 +00:00
|
|
|
{
|
2018-01-18 23:17:41 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
|
2016-11-17 22:35:44 +00:00
|
|
|
DeleteObject(hrgn);
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
|
|
|
|
{
|
2012-05-19 10:03:40 +00:00
|
|
|
gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
|
|
|
|
hdc, srcx, srcy, srcwidth, srcheight);
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
|
2012-03-05 20:17:42 +00:00
|
|
|
hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
RestoreDC(graphics->hdc, save_state);
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if (temp_hdc)
|
|
|
|
{
|
|
|
|
SelectObject(hdc, old_hbm);
|
|
|
|
DeleteDC(hdc);
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if (temp_bitmap)
|
|
|
|
DeleteObject(hbitmap);
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
2012-12-11 21:40:15 +00:00
|
|
|
else if (image->type == ImageTypeMetafile && ((GpMetafile*)image)->hemf)
|
|
|
|
{
|
|
|
|
GpRectF rc;
|
|
|
|
|
|
|
|
rc.X = srcx;
|
|
|
|
rc.Y = srcy;
|
|
|
|
rc.Width = srcwidth;
|
|
|
|
rc.Height = srcheight;
|
|
|
|
|
|
|
|
return GdipEnumerateMetafileSrcRectDestPoints(graphics, (GpMetafile*)image,
|
|
|
|
points, count, &rc, srcUnit, play_metafile_proc, image, imageAttributes);
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
else
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
WARN("GpImage with nothing we can draw (metafile in wrong state?)\n");
|
|
|
|
return InvalidParameter;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
|
|
|
|
GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
|
|
|
|
INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
|
|
|
|
DrawImageAbort callback, VOID * callbackData)
|
|
|
|
{
|
|
|
|
GpPointF pointsF[3];
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
|
|
|
|
srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
|
|
|
|
callbackData);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!points || count!=3)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
pointsF[i].X = (REAL)points[i].X;
|
|
|
|
pointsF[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
|
|
|
|
(REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
|
|
|
|
callback, callbackData);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
|
|
|
|
REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
|
|
|
|
REAL srcwidth, REAL srcheight, GpUnit srcUnit,
|
|
|
|
GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
|
|
|
|
VOID * callbackData)
|
|
|
|
{
|
|
|
|
GpPointF points[3];
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
|
|
|
|
graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
|
|
|
|
srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
points[0].X = dstx;
|
|
|
|
points[0].Y = dsty;
|
|
|
|
points[1].X = dstx + dstwidth;
|
|
|
|
points[1].Y = dsty;
|
|
|
|
points[2].X = dstx;
|
|
|
|
points[2].Y = dsty + dstheight;
|
|
|
|
|
|
|
|
return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
|
|
|
|
srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
|
|
|
|
INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
|
|
|
|
INT srcwidth, INT srcheight, GpUnit srcUnit,
|
|
|
|
GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
|
|
|
|
VOID * callbackData)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
GpPointF points[3];
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
|
|
|
|
graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
|
|
|
|
srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
points[0].X = dstx;
|
|
|
|
points[0].Y = dsty;
|
|
|
|
points[1].X = dstx + dstwidth;
|
|
|
|
points[1].Y = dsty;
|
|
|
|
points[2].X = dstx;
|
|
|
|
points[2].Y = dsty + dstheight;
|
|
|
|
|
|
|
|
return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
|
|
|
|
srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
|
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
|
|
|
|
REAL x, REAL y, REAL width, REAL height)
|
|
|
|
{
|
|
|
|
RectF bounds;
|
|
|
|
GpUnit unit;
|
|
|
|
GpStatus ret;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!graphics || !image)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
ret = GdipGetImageBounds(image, &bounds, &unit);
|
|
|
|
if(ret != Ok)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return GdipDrawImageRectRect(graphics, image, x, y, width, height,
|
|
|
|
bounds.X, bounds.Y, bounds.Width, bounds.Height,
|
|
|
|
unit, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
|
|
|
|
INT x, INT y, INT width, INT height)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
|
|
|
|
REAL y1, REAL x2, REAL y2)
|
|
|
|
{
|
|
|
|
GpPointF pt[2];
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (!pen)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if (pen->unit == UnitPixel && pen->width <= 0.0)
|
|
|
|
return Ok;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
pt[0].X = x1;
|
|
|
|
pt[0].Y = y1;
|
|
|
|
pt[1].X = x2;
|
|
|
|
pt[1].Y = y2;
|
2013-05-23 17:23:00 +00:00
|
|
|
return GdipDrawLines(graphics, pen, pt, 2);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
|
|
|
|
INT y1, INT x2, INT y2)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
return GdipDrawLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
|
|
|
|
GpPointF *points, INT count)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!pen || !graphics || (count < 2))
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathLine2(path, points, count);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
|
|
|
|
GpPoint *points, INT count)
|
|
|
|
{
|
|
|
|
GpStatus retval;
|
2013-05-23 17:23:00 +00:00
|
|
|
GpPointF *ptf;
|
2008-03-25 17:34:57 +00:00
|
|
|
int i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
ptf = heap_alloc_zero(count * sizeof(GpPointF));
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!ptf) return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i ++){
|
|
|
|
ptf[i].X = (REAL) points[i].X;
|
|
|
|
ptf[i].Y = (REAL) points[i].Y;
|
|
|
|
}
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
retval = GdipDrawLines(graphics, pen, ptf, count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(ptf);
|
2008-03-25 17:34:57 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2017-03-05 21:15:16 +00:00
|
|
|
static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
|
|
|
INT save_state;
|
|
|
|
GpStatus retval;
|
2014-04-22 15:42:03 +00:00
|
|
|
HRGN hrgn=NULL;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
save_state = prepare_dc(graphics, pen);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
retval = get_clip_hrgn(graphics, &hrgn);
|
|
|
|
|
|
|
|
if (retval != Ok)
|
|
|
|
goto end;
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
|
2014-04-22 15:42:03 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
retval = draw_poly(graphics, pen, path->pathdata.Points,
|
|
|
|
path->pathdata.Types, path->pathdata.Count, TRUE);
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
end:
|
2008-03-25 17:34:57 +00:00
|
|
|
restore_dc(graphics, save_state);
|
2014-04-22 15:42:03 +00:00
|
|
|
DeleteObject(hrgn);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2017-03-05 21:15:16 +00:00
|
|
|
static GpStatus SOFTWARE_GdipDrawThinPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
|
2016-11-17 22:35:44 +00:00
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpPath* flat_path;
|
|
|
|
GpMatrix* transform;
|
|
|
|
GpRectF gp_bound_rect;
|
|
|
|
GpRect gp_output_area;
|
|
|
|
RECT output_area;
|
|
|
|
INT output_height, output_width;
|
|
|
|
DWORD *output_bits, *brush_bits=NULL;
|
|
|
|
int i;
|
|
|
|
static const BYTE static_dash_pattern[] = {1,1,1,0,1,0,1,0};
|
|
|
|
const BYTE *dash_pattern;
|
|
|
|
INT dash_pattern_size;
|
|
|
|
BYTE *dyn_dash_pattern = NULL;
|
|
|
|
|
|
|
|
stat = GdipClonePath(path, &flat_path);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
stat = GdipCreateMatrix(&transform);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
|
2016-11-17 22:35:44 +00:00
|
|
|
CoordinateSpaceWorld, transform);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFlattenPath(flat_path, transform, 1.0);
|
|
|
|
|
|
|
|
GdipDeleteMatrix(transform);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* estimate the output size in pixels, can be larger than necessary */
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
output_area.left = floorf(flat_path->pathdata.Points[0].X);
|
|
|
|
output_area.right = ceilf(flat_path->pathdata.Points[0].X);
|
|
|
|
output_area.top = floorf(flat_path->pathdata.Points[0].Y);
|
|
|
|
output_area.bottom = ceilf(flat_path->pathdata.Points[0].Y);
|
|
|
|
|
|
|
|
for (i=1; i<flat_path->pathdata.Count; i++)
|
|
|
|
{
|
|
|
|
REAL x, y;
|
|
|
|
x = flat_path->pathdata.Points[i].X;
|
|
|
|
y = flat_path->pathdata.Points[i].Y;
|
|
|
|
|
|
|
|
if (floorf(x) < output_area.left) output_area.left = floorf(x);
|
|
|
|
if (floorf(y) < output_area.top) output_area.top = floorf(y);
|
|
|
|
if (ceilf(x) > output_area.right) output_area.right = ceilf(x);
|
|
|
|
if (ceilf(y) > output_area.bottom) output_area.bottom = ceilf(y);
|
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = get_graphics_device_bounds(graphics, &gp_bound_rect);
|
2016-11-17 22:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
output_area.left = max(output_area.left, floorf(gp_bound_rect.X));
|
|
|
|
output_area.top = max(output_area.top, floorf(gp_bound_rect.Y));
|
|
|
|
output_area.right = min(output_area.right, ceilf(gp_bound_rect.X + gp_bound_rect.Width));
|
|
|
|
output_area.bottom = min(output_area.bottom, ceilf(gp_bound_rect.Y + gp_bound_rect.Height));
|
|
|
|
|
|
|
|
output_width = output_area.right - output_area.left + 1;
|
|
|
|
output_height = output_area.bottom - output_area.top + 1;
|
|
|
|
|
|
|
|
if (output_width <= 0 || output_height <= 0)
|
|
|
|
{
|
|
|
|
GdipDeletePath(flat_path);
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
gp_output_area.X = output_area.left;
|
|
|
|
gp_output_area.Y = output_area.top;
|
|
|
|
gp_output_area.Width = output_width;
|
|
|
|
gp_output_area.Height = output_height;
|
|
|
|
|
|
|
|
output_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
|
|
|
|
if (!output_bits)
|
|
|
|
stat = OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
if (pen->brush->bt != BrushTypeSolidColor)
|
|
|
|
{
|
|
|
|
/* allocate and draw brush output */
|
|
|
|
brush_bits = heap_alloc_zero(output_width * output_height * sizeof(DWORD));
|
|
|
|
|
|
|
|
if (brush_bits)
|
|
|
|
{
|
|
|
|
stat = brush_fill_pixels(graphics, pen->brush, brush_bits,
|
|
|
|
&gp_output_area, output_width);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
stat = OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
/* convert dash pattern to bool array */
|
|
|
|
switch (pen->dash)
|
|
|
|
{
|
|
|
|
case DashStyleCustom:
|
|
|
|
{
|
|
|
|
dash_pattern_size = 0;
|
|
|
|
|
|
|
|
for (i=0; i < pen->numdashes; i++)
|
|
|
|
dash_pattern_size += gdip_round(pen->dashes[i]);
|
|
|
|
|
|
|
|
if (dash_pattern_size != 0)
|
|
|
|
{
|
|
|
|
dash_pattern = dyn_dash_pattern = heap_alloc(dash_pattern_size);
|
|
|
|
|
|
|
|
if (dyn_dash_pattern)
|
|
|
|
{
|
|
|
|
int j=0;
|
|
|
|
for (i=0; i < pen->numdashes; i++)
|
|
|
|
{
|
|
|
|
int k;
|
|
|
|
for (k=0; k < gdip_round(pen->dashes[i]); k++)
|
|
|
|
dyn_dash_pattern[j++] = (i&1)^1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
stat = OutOfMemory;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* else fall through */
|
|
|
|
}
|
|
|
|
case DashStyleSolid:
|
|
|
|
default:
|
|
|
|
dash_pattern = static_dash_pattern;
|
|
|
|
dash_pattern_size = 1;
|
|
|
|
break;
|
|
|
|
case DashStyleDash:
|
|
|
|
dash_pattern = static_dash_pattern;
|
|
|
|
dash_pattern_size = 4;
|
|
|
|
break;
|
|
|
|
case DashStyleDot:
|
|
|
|
dash_pattern = &static_dash_pattern[4];
|
|
|
|
dash_pattern_size = 2;
|
|
|
|
break;
|
|
|
|
case DashStyleDashDot:
|
|
|
|
dash_pattern = static_dash_pattern;
|
|
|
|
dash_pattern_size = 6;
|
|
|
|
break;
|
|
|
|
case DashStyleDashDotDot:
|
|
|
|
dash_pattern = static_dash_pattern;
|
|
|
|
dash_pattern_size = 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
/* trace path */
|
|
|
|
GpPointF subpath_start = flat_path->pathdata.Points[0];
|
|
|
|
INT prev_x = INT_MAX, prev_y = INT_MAX;
|
|
|
|
int dash_pos = dash_pattern_size - 1;
|
|
|
|
|
|
|
|
for (i=0; i < flat_path->pathdata.Count; i++)
|
|
|
|
{
|
|
|
|
BYTE type, type2;
|
|
|
|
GpPointF start_point, end_point;
|
|
|
|
GpPoint start_pointi, end_pointi;
|
|
|
|
|
|
|
|
type = flat_path->pathdata.Types[i];
|
|
|
|
if (i+1 < flat_path->pathdata.Count)
|
|
|
|
type2 = flat_path->pathdata.Types[i+1];
|
|
|
|
else
|
|
|
|
type2 = PathPointTypeStart;
|
|
|
|
|
|
|
|
start_point = flat_path->pathdata.Points[i];
|
|
|
|
|
|
|
|
if ((type & PathPointTypePathTypeMask) == PathPointTypeStart)
|
|
|
|
subpath_start = start_point;
|
|
|
|
|
|
|
|
if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
|
|
|
|
end_point = subpath_start;
|
|
|
|
else if ((type2 & PathPointTypePathTypeMask) == PathPointTypeStart)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
end_point = flat_path->pathdata.Points[i+1];
|
|
|
|
|
|
|
|
start_pointi.X = floorf(start_point.X);
|
|
|
|
start_pointi.Y = floorf(start_point.Y);
|
|
|
|
end_pointi.X = floorf(end_point.X);
|
|
|
|
end_pointi.Y = floorf(end_point.Y);
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(start_pointi.X == end_pointi.X && start_pointi.Y == end_pointi.Y)
|
|
|
|
continue;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
/* draw line segment */
|
|
|
|
if (abs(start_pointi.Y - end_pointi.Y) > abs(start_pointi.X - end_pointi.X))
|
|
|
|
{
|
|
|
|
INT x, y, start_y, end_y, step;
|
|
|
|
|
|
|
|
if (start_pointi.Y < end_pointi.Y)
|
|
|
|
{
|
|
|
|
step = 1;
|
|
|
|
start_y = ceilf(start_point.Y) - output_area.top;
|
|
|
|
end_y = end_pointi.Y - output_area.top;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
step = -1;
|
|
|
|
start_y = start_point.Y - output_area.top;
|
|
|
|
end_y = ceilf(end_point.Y) - output_area.top;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (y=start_y; y != (end_y+step); y+=step)
|
|
|
|
{
|
|
|
|
x = gdip_round( start_point.X +
|
|
|
|
(end_point.X - start_point.X) * (y + output_area.top - start_point.Y) / (end_point.Y - start_point.Y) )
|
|
|
|
- output_area.left;
|
|
|
|
|
|
|
|
if (x == prev_x && y == prev_y)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
prev_x = x;
|
|
|
|
prev_y = y;
|
|
|
|
dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
|
|
|
|
|
|
|
|
if (!dash_pattern[dash_pos])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (x < 0 || x >= output_width || y < 0 || y >= output_height)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (brush_bits)
|
|
|
|
output_bits[x + y*output_width] = brush_bits[x + y*output_width];
|
|
|
|
else
|
|
|
|
output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
INT x, y, start_x, end_x, step;
|
|
|
|
|
|
|
|
if (start_pointi.X < end_pointi.X)
|
|
|
|
{
|
|
|
|
step = 1;
|
|
|
|
start_x = ceilf(start_point.X) - output_area.left;
|
|
|
|
end_x = end_pointi.X - output_area.left;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
step = -1;
|
|
|
|
start_x = start_point.X - output_area.left;
|
|
|
|
end_x = ceilf(end_point.X) - output_area.left;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (x=start_x; x != (end_x+step); x+=step)
|
|
|
|
{
|
|
|
|
y = gdip_round( start_point.Y +
|
|
|
|
(end_point.Y - start_point.Y) * (x + output_area.left - start_point.X) / (end_point.X - start_point.X) )
|
|
|
|
- output_area.top;
|
|
|
|
|
|
|
|
if (x == prev_x && y == prev_y)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
prev_x = x;
|
|
|
|
prev_y = y;
|
|
|
|
dash_pos = (dash_pos + 1 == dash_pattern_size) ? 0 : dash_pos + 1;
|
|
|
|
|
|
|
|
if (!dash_pattern[dash_pos])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (x < 0 || x >= output_width || y < 0 || y >= output_height)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (brush_bits)
|
|
|
|
output_bits[x + y*output_width] = brush_bits[x + y*output_width];
|
|
|
|
else
|
|
|
|
output_bits[x + y*output_width] = ((GpSolidFill*)pen->brush)->color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* draw output image */
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
stat = alpha_blend_pixels(graphics, output_area.left, output_area.top,
|
|
|
|
(BYTE*)output_bits, output_width, output_height, output_width * 4,
|
|
|
|
PixelFormat32bppARGB);
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
gdi_transform_release(graphics);
|
2016-11-17 22:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
heap_free(brush_bits);
|
|
|
|
heap_free(dyn_dash_pattern);
|
|
|
|
heap_free(output_bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
GdipDeletePath(flat_path);
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2017-03-05 21:15:16 +00:00
|
|
|
static GpStatus SOFTWARE_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
|
2016-11-17 22:35:44 +00:00
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpPath *wide_path;
|
|
|
|
GpMatrix *transform=NULL;
|
2017-09-17 12:34:15 +00:00
|
|
|
REAL flatness=1.0;
|
2016-11-17 22:35:44 +00:00
|
|
|
|
|
|
|
/* Check if the final pen thickness in pixels is too thin. */
|
|
|
|
if (pen->unit == UnitPixel)
|
|
|
|
{
|
|
|
|
if (pen->width < 1.415)
|
|
|
|
return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GpPointF points[3] = {{0,0}, {1,0}, {0,1}};
|
|
|
|
|
|
|
|
points[1].X = pen->width;
|
|
|
|
points[2].Y = pen->width;
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
stat = gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice,
|
2016-11-17 22:35:44 +00:00
|
|
|
CoordinateSpaceWorld, points, 3);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if (((points[1].X-points[0].X)*(points[1].X-points[0].X) +
|
|
|
|
(points[1].Y-points[0].Y)*(points[1].Y-points[0].Y) < 2.0001) &&
|
|
|
|
((points[2].X-points[0].X)*(points[2].X-points[0].X) +
|
|
|
|
(points[2].Y-points[0].Y)*(points[2].Y-points[0].Y) < 2.0001))
|
|
|
|
return SOFTWARE_GdipDrawThinPath(graphics, pen, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
stat = GdipClonePath(path, &wide_path);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if (pen->unit == UnitPixel)
|
|
|
|
{
|
|
|
|
/* We have to transform this to device coordinates to get the widths right. */
|
|
|
|
stat = GdipCreateMatrix(&transform);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
2018-01-18 23:17:41 +00:00
|
|
|
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
|
2016-11-17 22:35:44 +00:00
|
|
|
CoordinateSpaceWorld, transform);
|
|
|
|
}
|
2017-09-17 12:34:15 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set flatness based on the final coordinate space */
|
|
|
|
GpMatrix t;
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
|
2017-09-17 12:34:15 +00:00
|
|
|
CoordinateSpaceWorld, &t);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
flatness = 1.0/sqrt(fmax(
|
|
|
|
t.matrix[0] * t.matrix[0] + t.matrix[1] * t.matrix[1],
|
|
|
|
t.matrix[2] * t.matrix[2] + t.matrix[3] * t.matrix[3]));
|
|
|
|
}
|
2016-11-17 22:35:44 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = GdipWidenPath(wide_path, pen, transform, flatness);
|
2016-11-17 22:35:44 +00:00
|
|
|
|
|
|
|
if (pen->unit == UnitPixel)
|
|
|
|
{
|
|
|
|
/* Transform the path back to world coordinates */
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipInvertMatrix(transform);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipTransformPath(wide_path, transform);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Actually draw the path */
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFillPath(graphics, pen->brush, wide_path);
|
|
|
|
|
|
|
|
GdipDeleteMatrix(transform);
|
|
|
|
|
|
|
|
GdipDeletePath(wide_path);
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
|
|
|
|
{
|
|
|
|
GpStatus retval;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %p)\n", graphics, pen, path);
|
|
|
|
|
|
|
|
if(!pen || !graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
if (path->pathdata.Count == 0)
|
|
|
|
return Ok;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path);
|
|
|
|
else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE))
|
2016-11-17 22:35:44 +00:00
|
|
|
retval = SOFTWARE_GdipDrawPath(graphics, pen, path);
|
|
|
|
else
|
|
|
|
retval = GDI32_GdipDrawPath(graphics, pen, path);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
|
|
|
|
REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
|
|
|
|
width, height, startAngle, sweepAngle);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !pen)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
|
|
|
|
INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
|
|
|
|
width, height, startAngle, sweepAngle);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
|
|
|
|
REAL y, REAL width, REAL height)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!pen || !graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathRectangle(path, x, y, width, height);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
|
|
|
|
INT y, INT width, INT height)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpRectF* rects, INT count)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !pen || !rects || count < 1)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathRectangles(path, rects, count);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
|
|
|
|
GDIPCONST GpRect* rects, INT count)
|
|
|
|
{
|
|
|
|
GpRectF *rectsF;
|
|
|
|
GpStatus ret;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!rects || count<=0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
rectsF = heap_alloc_zero(sizeof(GpRectF) * count);
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!rectsF)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0;i < count;i++){
|
|
|
|
rectsF[i].X = (REAL)rects[i].X;
|
|
|
|
rectsF[i].Y = (REAL)rects[i].Y;
|
|
|
|
rectsF[i].Width = (REAL)rects[i].Width;
|
|
|
|
rectsF[i].Height = (REAL)rects[i].Height;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipDrawRectangles(graphics, pen, rectsF, count);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(rectsF);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
2010-04-20 08:30:10 +00:00
|
|
|
GpPath *path;
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
|
|
|
|
count, tension, fill);
|
2009-05-23 10:32:19 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if(!graphics || !brush || !points)
|
2008-03-25 17:34:57 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if(count == 1) /* Do nothing */
|
|
|
|
return Ok;
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(fill, &path);
|
|
|
|
if (status != Ok) return status;
|
2009-06-07 07:59:56 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathClosedCurve2(path, points, count, tension);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipFillPath(graphics, brush, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
GdipDeletePath(path);
|
2013-05-23 17:23:00 +00:00
|
|
|
return status;
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
|
|
|
|
{
|
|
|
|
GpPointF *ptf;
|
|
|
|
GpStatus stat;
|
|
|
|
INT i;
|
2009-11-28 15:26:02 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
|
|
|
|
count, tension, fill);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if(!points || count == 0)
|
2010-04-20 08:30:10 +00:00
|
|
|
return InvalidParameter;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if(count == 1) /* Do nothing */
|
|
|
|
return Ok;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
ptf = heap_alloc_zero(sizeof(GpPointF)*count);
|
2010-04-20 08:30:10 +00:00
|
|
|
if(!ptf)
|
|
|
|
return OutOfMemory;
|
2009-04-06 14:58:12 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
for(i = 0;i < count;i++){
|
|
|
|
ptf[i].X = (REAL)points[i].X;
|
|
|
|
ptf[i].Y = (REAL)points[i].Y;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(ptf);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
return stat;
|
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPointF *points, INT count)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
|
|
|
|
return GdipFillClosedCurve2(graphics, brush, points, count,
|
|
|
|
0.5f, FillModeAlternate);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPoint *points, INT count)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
|
|
|
|
return GdipFillClosedCurve2I(graphics, brush, points, count,
|
|
|
|
0.5f, FillModeAlternate);
|
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
|
|
|
|
REAL y, REAL width, REAL height)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if(!graphics || !brush)
|
|
|
|
return InvalidParameter;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GdipCreatePath(FillModeAlternate, &path);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
stat = GdipAddPathEllipse(path, x, y, width, height);
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFillPath(graphics, brush, path);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
}
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
2008-05-04 19:41:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
|
|
|
|
INT y, INT width, INT height)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
|
|
|
INT save_state;
|
|
|
|
GpStatus retval;
|
2014-04-22 15:42:03 +00:00
|
|
|
HRGN hrgn=NULL;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2017-06-03 19:01:50 +00:00
|
|
|
if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
|
2012-03-05 20:17:42 +00:00
|
|
|
return NotImplemented;
|
2010-11-20 11:24:17 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
save_state = SaveDC(graphics->hdc);
|
|
|
|
EndPath(graphics->hdc);
|
|
|
|
SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
|
|
|
|
: WINDING));
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
retval = get_clip_hrgn(graphics, &hrgn);
|
|
|
|
|
|
|
|
if (retval != Ok)
|
|
|
|
goto end;
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
|
2014-04-22 15:42:03 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
BeginPath(graphics->hdc);
|
|
|
|
retval = draw_poly(graphics, NULL, path->pathdata.Points,
|
|
|
|
path->pathdata.Types, path->pathdata.Count, FALSE);
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(retval == Ok)
|
|
|
|
{
|
|
|
|
EndPath(graphics->hdc);
|
2018-10-01 11:50:25 +00:00
|
|
|
retval = brush_fill_path(graphics, brush);
|
2017-09-17 12:34:15 +00:00
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_release(graphics);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
end:
|
|
|
|
RestoreDC(graphics->hdc, save_state);
|
2014-04-22 15:42:03 +00:00
|
|
|
DeleteObject(hrgn);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpRegion *rgn;
|
|
|
|
|
|
|
|
if (!brush_can_fill_pixels(brush))
|
|
|
|
return NotImplemented;
|
|
|
|
|
|
|
|
/* FIXME: This could probably be done more efficiently without regions. */
|
|
|
|
|
|
|
|
stat = GdipCreateRegionPath(path, &rgn);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
stat = GdipFillRegion(graphics, brush, rgn);
|
|
|
|
|
|
|
|
GdipDeleteRegion(rgn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
|
|
|
|
{
|
|
|
|
GpStatus stat = NotImplemented;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %p)\n", graphics, brush, path);
|
|
|
|
|
|
|
|
if(!brush || !graphics || !path)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2018-10-01 11:50:25 +00:00
|
|
|
if (!path->pathdata.Count)
|
|
|
|
return Ok;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
return METAFILE_FillPath((GpMetafile*)graphics->image, brush, path);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
if (!graphics->image && !graphics->alpha_hdc)
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GDI32_GdipFillPath(graphics, brush, path);
|
|
|
|
|
|
|
|
if (stat == NotImplemented)
|
|
|
|
stat = SOFTWARE_GdipFillPath(graphics, brush, path);
|
|
|
|
|
|
|
|
if (stat == NotImplemented)
|
|
|
|
{
|
|
|
|
FIXME("Not implemented for brushtype %i\n", brush->bt);
|
|
|
|
stat = Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
|
|
|
|
REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
|
|
|
|
graphics, brush, x, y, width, height, startAngle, sweepAngle);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !brush)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GdipCreatePath(FillModeAlternate, &path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFillPath(graphics, brush, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
|
|
|
|
INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
|
|
|
|
graphics, brush, x, y, width, height, startAngle, sweepAngle);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !brush || !points || !count)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GdipCreatePath(fillMode, &path);
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
{
|
|
|
|
stat = GdipAddPathPolygon(path, points, count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFillPath(graphics, brush, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GdipDeletePath(path);
|
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
|
|
|
GpPath *path;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !brush || !points || !count)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GdipCreatePath(fillMode, &path);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
2010-11-20 11:24:17 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GdipAddPathPolygonI(path, points, count);
|
2010-11-20 11:24:17 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipFillPath(graphics, brush, path);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GdipDeletePath(path);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPointF *points, INT count)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
|
|
|
|
}
|
2008-08-05 12:23:58 +00:00
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GDIPCONST GpPoint *points, INT count)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
|
|
|
|
|
2008-08-05 12:23:58 +00:00
|
|
|
return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
REAL x, REAL y, REAL width, REAL height)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
GpRectF rect;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
rect.X = x;
|
|
|
|
rect.Y = y;
|
|
|
|
rect.Width = width;
|
|
|
|
rect.Height = height;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
return GdipFillRectangles(graphics, brush, &rect, 1);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
INT x, INT y, INT width, INT height)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
GpRectF rect;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
rect.X = (REAL)x;
|
|
|
|
rect.Y = (REAL)y;
|
|
|
|
rect.Width = (REAL)width;
|
|
|
|
rect.Height = (REAL)height;
|
|
|
|
|
|
|
|
return GdipFillRectangles(graphics, brush, &rect, 1);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
|
|
|
|
INT count)
|
|
|
|
{
|
2013-05-23 17:23:00 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *path;
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if(!graphics || !brush || !rects || count <= 0)
|
2008-05-04 19:41:25 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
status = METAFILE_FillRectangles((GpMetafile*)graphics->image, brush, rects, count);
|
|
|
|
/* FIXME: Add gdi32 drawing. */
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
status = GdipAddPathRectangles(path, rects, count);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipFillPath(graphics, brush, path);
|
|
|
|
|
|
|
|
GdipDeletePath(path);
|
|
|
|
return status;
|
2008-05-04 19:41:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
|
|
|
|
INT count)
|
|
|
|
{
|
|
|
|
GpRectF *rectsF;
|
|
|
|
GpStatus ret;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!rects || count <= 0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
rectsF = heap_alloc_zero(sizeof(GpRectF)*count);
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!rectsF)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
rectsF[i].X = (REAL)rects[i].X;
|
|
|
|
rectsF[i].Y = (REAL)rects[i].Y;
|
2017-09-17 12:34:15 +00:00
|
|
|
rectsF[i].Width = (REAL)rects[i].Width;
|
2008-05-04 19:41:25 +00:00
|
|
|
rectsF[i].Height = (REAL)rects[i].Height;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipFillRectangles(graphics,brush,rectsF,count);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(rectsF);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
|
|
|
|
GpRegion* region)
|
2008-08-05 12:23:58 +00:00
|
|
|
{
|
2009-02-14 08:14:34 +00:00
|
|
|
INT save_state;
|
|
|
|
GpStatus status;
|
|
|
|
HRGN hrgn;
|
2009-09-03 15:07:25 +00:00
|
|
|
RECT rc;
|
2009-02-14 08:14:34 +00:00
|
|
|
|
2017-06-03 19:01:50 +00:00
|
|
|
if(!graphics->hdc || !brush_can_fill_path(brush, TRUE))
|
2012-03-05 20:17:42 +00:00
|
|
|
return NotImplemented;
|
2010-11-20 11:24:17 +00:00
|
|
|
|
2009-02-14 08:14:34 +00:00
|
|
|
save_state = SaveDC(graphics->hdc);
|
|
|
|
EndPath(graphics->hdc);
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
hrgn = NULL;
|
|
|
|
status = get_clip_hrgn(graphics, &hrgn);
|
|
|
|
if (status != Ok)
|
|
|
|
{
|
|
|
|
RestoreDC(graphics->hdc, save_state);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
|
|
|
|
DeleteObject(hrgn);
|
|
|
|
|
|
|
|
status = GdipGetRegionHRgn(region, graphics, &hrgn);
|
|
|
|
if (status != Ok)
|
2016-11-17 22:35:44 +00:00
|
|
|
{
|
2018-01-18 23:17:41 +00:00
|
|
|
RestoreDC(graphics->hdc, save_state);
|
|
|
|
return status;
|
2016-11-17 22:35:44 +00:00
|
|
|
}
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
|
|
|
|
DeleteObject(hrgn);
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
|
|
|
|
{
|
|
|
|
BeginPath(graphics->hdc);
|
|
|
|
Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
EndPath(graphics->hdc);
|
|
|
|
|
2018-10-01 11:50:25 +00:00
|
|
|
status = brush_fill_path(graphics, brush);
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
2009-02-14 08:14:34 +00:00
|
|
|
|
|
|
|
RestoreDC(graphics->hdc, save_state);
|
|
|
|
|
|
|
|
|
2018-10-01 11:50:25 +00:00
|
|
|
return status;
|
2008-08-05 12:23:58 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
|
|
|
|
GpRegion* region)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpRegion *temp_region;
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix world_to_device;
|
2012-03-05 20:17:42 +00:00
|
|
|
GpRectF graphics_bounds;
|
|
|
|
DWORD *pixel_data;
|
2012-05-19 10:03:40 +00:00
|
|
|
HRGN hregion;
|
|
|
|
RECT bound_rect;
|
|
|
|
GpRect gp_bound_rect;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (!brush_can_fill_pixels(brush))
|
|
|
|
return NotImplemented;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = gdi_transform_acquire(graphics);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = get_graphics_device_bounds(graphics, &graphics_bounds);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipCloneRegion(region, &temp_region);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = get_graphics_transform(graphics, WineCoordinateSpaceGdiDevice,
|
2012-03-05 20:17:42 +00:00
|
|
|
CoordinateSpaceWorld, &world_to_device);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = GdipTransformRegion(temp_region, &world_to_device);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
|
|
|
|
|
|
|
|
if (stat == Ok)
|
2012-05-19 10:03:40 +00:00
|
|
|
stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
GdipDeleteRegion(temp_region);
|
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
|
|
|
|
{
|
|
|
|
DeleteObject(hregion);
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_release(graphics);
|
2012-03-05 20:17:42 +00:00
|
|
|
return Ok;
|
2012-05-19 10:03:40 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
|
|
|
{
|
2012-05-19 10:03:40 +00:00
|
|
|
gp_bound_rect.X = bound_rect.left;
|
|
|
|
gp_bound_rect.Y = bound_rect.top;
|
|
|
|
gp_bound_rect.Width = bound_rect.right - bound_rect.left;
|
|
|
|
gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
pixel_data = heap_alloc_zero(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
|
2012-05-19 10:03:40 +00:00
|
|
|
if (!pixel_data)
|
|
|
|
stat = OutOfMemory;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (stat == Ok)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
2012-05-19 10:03:40 +00:00
|
|
|
stat = brush_fill_pixels(graphics, brush, pixel_data,
|
|
|
|
&gp_bound_rect, gp_bound_rect.Width);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (stat == Ok)
|
2012-05-19 10:03:40 +00:00
|
|
|
stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
|
|
|
|
gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
|
2015-07-19 13:31:15 +00:00
|
|
|
gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion,
|
|
|
|
PixelFormat32bppARGB);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pixel_data);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
DeleteObject(hregion);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* GdipFillRegion [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
|
|
|
|
GpRegion* region)
|
|
|
|
{
|
|
|
|
GpStatus stat = NotImplemented;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %p)\n", graphics, brush, region);
|
|
|
|
|
|
|
|
if (!(graphics && brush && region))
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
if (!graphics->image && !graphics->alpha_hdc)
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = GDI32_GdipFillRegion(graphics, brush, region);
|
|
|
|
|
|
|
|
if (stat == NotImplemented)
|
|
|
|
stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
|
|
|
|
|
|
|
|
if (stat == NotImplemented)
|
|
|
|
{
|
|
|
|
FIXME("not implemented for brushtype %i\n", brush->bt);
|
|
|
|
stat = Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-07-02 08:19:00 +00:00
|
|
|
GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
|
|
|
|
{
|
2010-03-04 13:34:05 +00:00
|
|
|
TRACE("(%p,%u)\n", graphics, intention);
|
|
|
|
|
2008-07-02 08:19:00 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
/* We have no internal operation queue, so there's no need to clear it. */
|
2008-07-02 08:19:00 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if (graphics->hdc)
|
|
|
|
GdiFlush();
|
|
|
|
|
|
|
|
return Ok;
|
2008-07-02 08:19:00 +00:00
|
|
|
}
|
|
|
|
|
2009-02-14 08:14:34 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* GdipGetClipBounds [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
|
|
|
|
{
|
2013-09-19 15:10:19 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpRegion *clip;
|
|
|
|
|
2009-02-14 08:14:34 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, rect);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-09-19 15:10:19 +00:00
|
|
|
status = GdipCreateRegion(&clip);
|
|
|
|
if (status != Ok) return status;
|
|
|
|
|
|
|
|
status = GdipGetClip(graphics, clip);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipGetRegionBounds(clip, graphics, rect);
|
|
|
|
|
|
|
|
GdipDeleteRegion(clip);
|
|
|
|
return status;
|
2009-02-14 08:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* GdipGetClipBoundsI [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", graphics, rect);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
/* FIXME: Compositing mode is not used anywhere except the getter/setter. */
|
|
|
|
GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
|
|
|
|
CompositingMode *mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !mode)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*mode = graphics->compmode;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Compositing quality is not used anywhere except the getter/setter. */
|
|
|
|
GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
|
|
|
|
CompositingQuality *quality)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, quality);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !quality)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*quality = graphics->compqual;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
|
|
|
|
GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
|
|
|
|
InterpolationMode *mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !mode)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*mode = graphics->interpolation;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2010-03-06 12:15:38 +00:00
|
|
|
/* FIXME: Need to handle color depths less than 24bpp */
|
2008-12-16 18:41:07 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
|
|
|
|
{
|
2010-03-06 12:15:38 +00:00
|
|
|
FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
|
2010-03-04 13:34:05 +00:00
|
|
|
|
2008-12-16 18:41:07 +00:00
|
|
|
if(!graphics || !argb)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2010-03-06 12:15:38 +00:00
|
|
|
return Ok;
|
2008-12-16 18:41:07 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, scale);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !scale)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*scale = graphics->scale;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, unit);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !unit)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*unit = graphics->unit;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
|
|
|
|
GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
|
|
|
|
*mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !mode)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*mode = graphics->pixeloffset;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
|
|
|
|
GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !mode)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*mode = graphics->smoothing;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", graphics, contrast);
|
|
|
|
|
|
|
|
if(!graphics || !contrast)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
*contrast = graphics->textcontrast;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
/* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
|
|
|
|
GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
|
|
|
|
TextRenderingHint *hint)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, hint);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !hint)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
*hint = graphics->texthint;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
|
|
|
|
{
|
|
|
|
GpRegion *clip_rgn;
|
|
|
|
GpStatus stat;
|
2016-08-19 09:28:13 +00:00
|
|
|
GpMatrix device_to_world;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %p)\n", graphics, rect);
|
|
|
|
|
|
|
|
if(!graphics || !rect)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
/* intersect window and graphics clipping regions */
|
|
|
|
if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
|
|
|
|
goto cleanup;
|
|
|
|
|
2016-08-19 09:28:13 +00:00
|
|
|
/* transform to world coordinates */
|
|
|
|
if((stat = get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world)) != Ok)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if((stat = GdipTransformRegion(clip_rgn, &device_to_world)) != Ok)
|
|
|
|
goto cleanup;
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
/* get bounds of the region */
|
|
|
|
stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
GdipDeleteRegion(clip_rgn);
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
|
|
|
|
{
|
|
|
|
GpRectF rectf;
|
|
|
|
GpStatus stat;
|
|
|
|
|
|
|
|
TRACE("(%p, %p)\n", graphics, rect);
|
|
|
|
|
|
|
|
if(!graphics || !rect)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
|
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
rect->X = gdip_round(rectf.X);
|
|
|
|
rect->Y = gdip_round(rectf.Y);
|
|
|
|
rect->Width = gdip_round(rectf.Width);
|
|
|
|
rect->Height = gdip_round(rectf.Height);
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, matrix);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !matrix)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
*matrix = graphics->worldtrans;
|
2008-03-25 17:34:57 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
|
|
|
|
{
|
|
|
|
GpSolidFill *brush;
|
|
|
|
GpStatus stat;
|
2009-09-03 15:07:25 +00:00
|
|
|
GpRectF wnd_rect;
|
2008-09-07 10:32:49 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %x)\n", graphics, color);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-08-19 09:28:13 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
return METAFILE_GraphicsClear((GpMetafile*)graphics->image, color);
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
2016-08-19 09:28:13 +00:00
|
|
|
if((stat = GdipGetVisibleClipBounds(graphics, &wnd_rect)) != Ok){
|
2009-09-03 15:07:25 +00:00
|
|
|
GdipDeleteBrush((GpBrush*)brush);
|
|
|
|
return stat;
|
2008-09-07 10:32:49 +00:00
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
|
|
|
|
wnd_rect.Width, wnd_rect.Height);
|
2008-09-07 10:32:49 +00:00
|
|
|
|
|
|
|
GdipDeleteBrush((GpBrush*)brush);
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", graphics, res);
|
|
|
|
|
|
|
|
if(!graphics || !res)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
return GdipIsEmptyRegion(graphics->clip, graphics, res);
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
|
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
GpStatus stat;
|
|
|
|
GpRegion* rgn;
|
|
|
|
GpPointF pt;
|
|
|
|
|
|
|
|
TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
|
2008-12-06 09:26:01 +00:00
|
|
|
|
|
|
|
if(!graphics || !result)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
pt.X = x;
|
|
|
|
pt.Y = y;
|
|
|
|
if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
|
|
|
|
CoordinateSpaceWorld, &pt, 1)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = GdipCreateRegion(&rgn)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
GdipDeleteRegion(rgn);
|
|
|
|
return stat;
|
2008-12-06 09:26:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
|
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpRegion* rgn;
|
|
|
|
GpPointF pts[2];
|
|
|
|
|
|
|
|
TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
|
2008-12-06 09:26:01 +00:00
|
|
|
|
|
|
|
if(!graphics || !result)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
pts[0].X = x;
|
|
|
|
pts[0].Y = y;
|
|
|
|
pts[1].X = x + width;
|
|
|
|
pts[1].Y = y + height;
|
|
|
|
|
|
|
|
if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
|
|
|
|
CoordinateSpaceWorld, pts, 2)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
pts[1].X -= pts[0].X;
|
|
|
|
pts[1].Y -= pts[0].Y;
|
|
|
|
|
|
|
|
if((stat = GdipCreateRegion(&rgn)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
GdipDeleteRegion(rgn);
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
|
|
|
|
{
|
|
|
|
return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
|
2008-12-06 09:26:01 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus gdip_format_string(HDC hdc,
|
2008-03-25 17:34:57 +00:00
|
|
|
GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
|
2013-05-23 17:23:00 +00:00
|
|
|
GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, int ignore_empty_clip,
|
2010-04-20 08:30:10 +00:00
|
|
|
gdip_format_string_callback callback, void *user_data)
|
2008-03-25 17:34:57 +00:00
|
|
|
{
|
|
|
|
WCHAR* stringdup;
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
|
2010-04-20 08:30:10 +00:00
|
|
|
nheight, lineend, lineno = 0;
|
|
|
|
RectF bounds;
|
|
|
|
StringAlignment halign;
|
|
|
|
GpStatus stat = Ok;
|
2008-03-25 17:34:57 +00:00
|
|
|
SIZE size;
|
2012-05-19 10:03:40 +00:00
|
|
|
HotkeyPrefix hkprefix;
|
|
|
|
INT *hotkeyprefix_offsets=NULL;
|
|
|
|
INT hotkeyprefix_count=0;
|
|
|
|
INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
|
2014-04-22 15:42:03 +00:00
|
|
|
BOOL seen_prefix = FALSE;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
|
|
|
if(length == -1) length = lstrlenW(string);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
stringdup = heap_alloc_zero((length + 1) * sizeof(WCHAR));
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!stringdup) return OutOfMemory;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (!format)
|
2017-06-03 19:01:50 +00:00
|
|
|
format = &default_drawstring_format;
|
2014-04-22 15:42:03 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
nwidth = rect->Width;
|
|
|
|
nheight = rect->Height;
|
2013-05-23 17:23:00 +00:00
|
|
|
if (ignore_empty_clip)
|
|
|
|
{
|
|
|
|
if (!nwidth) nwidth = INT_MAX;
|
|
|
|
if (!nheight) nheight = INT_MAX;
|
|
|
|
}
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
hkprefix = format->hkprefix;
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
if (hkprefix == HotkeyPrefixShow)
|
|
|
|
{
|
|
|
|
for (i=0; i<length; i++)
|
|
|
|
{
|
|
|
|
if (string[i] == '&')
|
|
|
|
hotkeyprefix_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hotkeyprefix_count)
|
2015-11-22 10:10:02 +00:00
|
|
|
hotkeyprefix_offsets = heap_alloc_zero(sizeof(INT) * hotkeyprefix_count);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
hotkeyprefix_count = 0;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
for(i = 0, j = 0; i < length; i++){
|
2010-04-20 08:30:10 +00:00
|
|
|
/* FIXME: This makes the indexes passed to callback inaccurate. */
|
2009-04-06 14:58:12 +00:00
|
|
|
if(!isprintW(string[i]) && (string[i] != '\n'))
|
2008-03-25 17:34:57 +00:00
|
|
|
continue;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
/* FIXME: tabs should be handled using tabstops from stringformat */
|
|
|
|
if (string[i] == '\t')
|
|
|
|
continue;
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
|
|
|
|
hotkeyprefix_offsets[hotkeyprefix_count++] = j;
|
|
|
|
else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
seen_prefix = TRUE;
|
2012-05-19 10:03:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
seen_prefix = FALSE;
|
2012-05-19 10:03:40 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
stringdup[j] = string[i];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = j;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
halign = format->align;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
while(sum < length){
|
2012-03-05 20:17:42 +00:00
|
|
|
GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
|
2008-03-25 17:34:57 +00:00
|
|
|
nwidth, &fit, NULL, &size);
|
|
|
|
fitcpy = fit;
|
|
|
|
|
|
|
|
if(fit == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
for(lret = 0; lret < fit; lret++)
|
|
|
|
if(*(stringdup + sum + lret) == '\n')
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Line break code (may look strange, but it imitates windows). */
|
|
|
|
if(lret < fit)
|
2009-09-03 15:07:25 +00:00
|
|
|
lineend = fit = lret; /* this is not an off-by-one error */
|
2008-03-25 17:34:57 +00:00
|
|
|
else if(fit < (length - sum)){
|
|
|
|
if(*(stringdup + sum + fit) == ' ')
|
|
|
|
while(*(stringdup + sum + fit) == ' ')
|
|
|
|
fit++;
|
|
|
|
else
|
|
|
|
while(*(stringdup + sum + fit - 1) != ' '){
|
|
|
|
fit--;
|
|
|
|
|
|
|
|
if(*(stringdup + sum + fit) == '\t')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(fit == 0){
|
|
|
|
fit = fitcpy;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
lineend = fit;
|
|
|
|
while(*(stringdup + sum + lineend - 1) == ' ' ||
|
|
|
|
*(stringdup + sum + lineend - 1) == '\t')
|
|
|
|
lineend--;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
else
|
|
|
|
lineend = fit;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GetTextExtentExPointW(hdc, stringdup + sum, lineend,
|
2008-03-25 17:34:57 +00:00
|
|
|
nwidth, &j, NULL, &size);
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
bounds.Width = size.cx;
|
|
|
|
|
|
|
|
if(height + size.cy > nheight)
|
2014-04-22 15:42:03 +00:00
|
|
|
{
|
|
|
|
if (format->attr & StringFormatFlagsLineLimit)
|
|
|
|
break;
|
2010-04-20 08:30:10 +00:00
|
|
|
bounds.Height = nheight - (height + size.cy);
|
2014-04-22 15:42:03 +00:00
|
|
|
}
|
2010-04-20 08:30:10 +00:00
|
|
|
else
|
|
|
|
bounds.Height = size.cy;
|
|
|
|
|
|
|
|
bounds.Y = rect->Y + height;
|
2008-12-06 09:26:01 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
switch (halign)
|
|
|
|
{
|
|
|
|
case StringAlignmentNear:
|
|
|
|
default:
|
|
|
|
bounds.X = rect->X;
|
|
|
|
break;
|
|
|
|
case StringAlignmentCenter:
|
|
|
|
bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
|
|
|
|
break;
|
|
|
|
case StringAlignmentFar:
|
|
|
|
bounds.X = rect->X + rect->Width - bounds.Width;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
|
|
|
|
if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
|
|
|
|
break;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
stat = callback(hdc, stringdup, sum, lineend,
|
2012-05-19 10:03:40 +00:00
|
|
|
font, rect, format, lineno, &bounds,
|
|
|
|
&hotkeyprefix_offsets[hotkeyprefix_pos],
|
|
|
|
hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sum += fit + (lret < fitcpy ? 1 : 0);
|
2008-03-25 17:34:57 +00:00
|
|
|
height += size.cy;
|
2010-04-20 08:30:10 +00:00
|
|
|
lineno++;
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
hotkeyprefix_pos = hotkeyprefix_end_pos;
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(height > nheight)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Stop if this was a linewrap (but not if it was a linebreak). */
|
2014-04-22 15:42:03 +00:00
|
|
|
if ((lret == fitcpy) && (format->attr & StringFormatFlagsNoWrap))
|
2008-03-25 17:34:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(stringdup);
|
|
|
|
heap_free(hotkeyprefix_offsets);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct measure_ranges_args {
|
|
|
|
GpRegion **regions;
|
2012-12-11 21:40:15 +00:00
|
|
|
REAL rel_width, rel_height;
|
2010-04-20 08:30:10 +00:00
|
|
|
};
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus measure_ranges_callback(HDC hdc,
|
2010-04-20 08:30:10 +00:00
|
|
|
GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
|
|
|
|
GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
|
2012-05-19 10:03:40 +00:00
|
|
|
INT lineno, const RectF *bounds, INT *underlined_indexes,
|
|
|
|
INT underlined_index_count, void *user_data)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
GpStatus stat = Ok;
|
|
|
|
struct measure_ranges_args *args = user_data;
|
|
|
|
|
|
|
|
for (i=0; i<format->range_count; i++)
|
|
|
|
{
|
|
|
|
INT range_start = max(index, format->character_ranges[i].First);
|
|
|
|
INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
|
|
|
|
if (range_start < range_end)
|
|
|
|
{
|
|
|
|
GpRectF range_rect;
|
|
|
|
SIZE range_size;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
range_rect.Y = bounds->Y / args->rel_height;
|
|
|
|
range_rect.Height = bounds->Height / args->rel_height;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GetTextExtentExPointW(hdc, string + index, range_start - index,
|
2010-04-20 08:30:10 +00:00
|
|
|
INT_MAX, NULL, NULL, &range_size);
|
2012-12-11 21:40:15 +00:00
|
|
|
range_rect.X = (bounds->X + range_size.cx) / args->rel_width;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GetTextExtentExPointW(hdc, string + index, range_end - index,
|
2010-04-20 08:30:10 +00:00
|
|
|
INT_MAX, NULL, NULL, &range_size);
|
2012-12-11 21:40:15 +00:00
|
|
|
range_rect.Width = (bounds->X + range_size.cx) / args->rel_width - range_rect.X;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
|
|
|
|
if (stat != Ok)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
|
|
|
|
GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
|
|
|
|
GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
|
|
|
|
INT regionCount, GpRegion** regions)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
int i;
|
2012-12-11 21:40:15 +00:00
|
|
|
HFONT gdifont, oldfont;
|
2010-04-20 08:30:10 +00:00
|
|
|
struct measure_ranges_args args;
|
2012-03-05 20:17:42 +00:00
|
|
|
HDC hdc, temp_hdc=NULL;
|
2012-12-11 21:40:15 +00:00
|
|
|
GpPointF pt[3];
|
|
|
|
RectF scaled_rect;
|
|
|
|
REAL margin_x;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2019-01-25 12:21:54 +00:00
|
|
|
TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_wn(string, length),
|
2010-04-20 08:30:10 +00:00
|
|
|
length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
|
|
|
|
|
|
|
|
if (!(graphics && string && font && layoutRect && stringFormat && regions))
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if (regionCount < stringFormat->range_count)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if(!graphics->hdc)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
hdc = temp_hdc = CreateCompatibleDC(0);
|
2010-11-20 11:24:17 +00:00
|
|
|
if (!temp_hdc) return OutOfMemory;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
else
|
|
|
|
hdc = graphics->hdc;
|
2010-11-20 11:24:17 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if (stringFormat->attr)
|
|
|
|
TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 0.0;
|
|
|
|
pt[2].X = 0.0;
|
|
|
|
pt[2].Y = 1.0;
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
|
2012-12-11 21:40:15 +00:00
|
|
|
args.rel_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));
|
|
|
|
args.rel_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));
|
|
|
|
|
|
|
|
margin_x = stringFormat->generic_typographic ? 0.0 : font->emSize / 6.0;
|
|
|
|
margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
|
|
|
|
|
|
|
|
scaled_rect.X = (layoutRect->X + margin_x) * args.rel_width;
|
|
|
|
scaled_rect.Y = layoutRect->Y * args.rel_height;
|
2013-05-23 17:23:00 +00:00
|
|
|
scaled_rect.Width = layoutRect->Width * args.rel_width;
|
|
|
|
scaled_rect.Height = layoutRect->Height * args.rel_height;
|
2012-12-11 21:40:15 +00:00
|
|
|
|
2015-07-19 13:31:15 +00:00
|
|
|
if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
|
|
|
|
if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
get_font_hfont(graphics, font, stringFormat, &gdifont, NULL);
|
|
|
|
oldfont = SelectObject(hdc, gdifont);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
for (i=0; i<stringFormat->range_count; i++)
|
|
|
|
{
|
|
|
|
stat = GdipSetEmpty(regions[i]);
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
args.regions = regions;
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = gdip_format_string(hdc, string, length, font, &scaled_rect, stringFormat,
|
2013-05-23 17:23:00 +00:00
|
|
|
(stringFormat->attr & StringFormatFlagsNoClip) != 0, measure_ranges_callback, &args);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
SelectObject(hdc, oldfont);
|
|
|
|
DeleteObject(gdifont);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if (temp_hdc)
|
|
|
|
DeleteDC(temp_hdc);
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct measure_string_args {
|
|
|
|
RectF *bounds;
|
|
|
|
INT *codepointsfitted;
|
|
|
|
INT *linesfilled;
|
2012-05-19 10:03:40 +00:00
|
|
|
REAL rel_width, rel_height;
|
2010-04-20 08:30:10 +00:00
|
|
|
};
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus measure_string_callback(HDC hdc,
|
2010-04-20 08:30:10 +00:00
|
|
|
GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
|
|
|
|
GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
|
2012-05-19 10:03:40 +00:00
|
|
|
INT lineno, const RectF *bounds, INT *underlined_indexes,
|
|
|
|
INT underlined_index_count, void *user_data)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
|
|
|
struct measure_string_args *args = user_data;
|
2012-05-19 10:03:40 +00:00
|
|
|
REAL new_width, new_height;
|
|
|
|
|
|
|
|
new_width = bounds->Width / args->rel_width;
|
2012-12-11 21:40:15 +00:00
|
|
|
new_height = (bounds->Height + bounds->Y) / args->rel_height - args->bounds->Y;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (new_width > args->bounds->Width)
|
|
|
|
args->bounds->Width = new_width;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (new_height > args->bounds->Height)
|
|
|
|
args->bounds->Height = new_height;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
if (args->codepointsfitted)
|
|
|
|
*args->codepointsfitted = index + length;
|
|
|
|
|
|
|
|
if (args->linesfilled)
|
|
|
|
(*args->linesfilled)++;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the smallest rectangle that bounds the text when it is printed in rect
|
|
|
|
* according to the format options listed in format. If rect has 0 width and
|
|
|
|
* height, then just find the smallest rectangle that bounds the text when it's
|
|
|
|
* printed at location (rect->X, rect-Y). */
|
|
|
|
GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
|
|
|
|
GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
|
|
|
|
GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
|
|
|
|
INT *codepointsfitted, INT *linesfilled)
|
|
|
|
{
|
2012-05-19 10:03:40 +00:00
|
|
|
HFONT oldfont, gdifont;
|
2010-04-20 08:30:10 +00:00
|
|
|
struct measure_string_args args;
|
2012-03-05 20:17:42 +00:00
|
|
|
HDC temp_hdc=NULL, hdc;
|
2012-05-19 10:03:40 +00:00
|
|
|
GpPointF pt[3];
|
2012-12-11 21:40:15 +00:00
|
|
|
RectF scaled_rect;
|
|
|
|
REAL margin_x;
|
2013-05-23 17:23:00 +00:00
|
|
|
INT lines, glyphs;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
|
|
|
|
debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
|
|
|
|
bounds, codepointsfitted, linesfilled);
|
|
|
|
|
|
|
|
if(!graphics || !string || !font || !rect || !bounds)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if(!graphics->hdc)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
hdc = temp_hdc = CreateCompatibleDC(0);
|
2010-11-20 11:24:17 +00:00
|
|
|
if (!temp_hdc) return OutOfMemory;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
else
|
|
|
|
hdc = graphics->hdc;
|
2010-11-20 11:24:17 +00:00
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if(linesfilled) *linesfilled = 0;
|
|
|
|
if(codepointsfitted) *codepointsfitted = 0;
|
|
|
|
|
|
|
|
if(format)
|
|
|
|
TRACE("may be ignoring some format flags: attr %x\n", format->attr);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 0.0;
|
|
|
|
pt[2].X = 0.0;
|
|
|
|
pt[2].Y = 1.0;
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
|
2012-05-19 10:03:40 +00:00
|
|
|
args.rel_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));
|
|
|
|
args.rel_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));
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
|
|
|
|
margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
|
|
|
|
|
|
|
|
scaled_rect.X = (rect->X + margin_x) * args.rel_width;
|
|
|
|
scaled_rect.Y = rect->Y * args.rel_height;
|
|
|
|
scaled_rect.Width = rect->Width * args.rel_width;
|
|
|
|
scaled_rect.Height = rect->Height * args.rel_height;
|
|
|
|
if (scaled_rect.Width >= 0.5)
|
|
|
|
{
|
|
|
|
scaled_rect.Width -= margin_x * 2.0 * args.rel_width;
|
|
|
|
if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
|
|
|
|
}
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
|
|
|
|
if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
|
2012-12-11 21:40:15 +00:00
|
|
|
|
|
|
|
get_font_hfont(graphics, font, format, &gdifont, NULL);
|
2012-05-19 10:03:40 +00:00
|
|
|
oldfont = SelectObject(hdc, gdifont);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
bounds->X = rect->X;
|
|
|
|
bounds->Y = rect->Y;
|
2010-04-20 08:30:10 +00:00
|
|
|
bounds->Width = 0.0;
|
|
|
|
bounds->Height = 0.0;
|
|
|
|
|
|
|
|
args.bounds = bounds;
|
2012-12-11 21:40:15 +00:00
|
|
|
args.codepointsfitted = &glyphs;
|
|
|
|
args.linesfilled = &lines;
|
|
|
|
lines = glyphs = 0;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
|
2010-04-20 08:30:10 +00:00
|
|
|
measure_string_callback, &args);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
if (linesfilled) *linesfilled = lines;
|
|
|
|
if (codepointsfitted) *codepointsfitted = glyphs;
|
|
|
|
|
|
|
|
if (lines)
|
|
|
|
bounds->Width += margin_x * 2.0;
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
SelectObject(hdc, oldfont);
|
|
|
|
DeleteObject(gdifont);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
if (temp_hdc)
|
|
|
|
DeleteDC(temp_hdc);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
struct draw_string_args {
|
2012-03-05 20:17:42 +00:00
|
|
|
GpGraphics *graphics;
|
|
|
|
GDIPCONST GpBrush *brush;
|
|
|
|
REAL x, y, rel_width, rel_height, ascent;
|
2010-04-20 08:30:10 +00:00
|
|
|
};
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus draw_string_callback(HDC hdc,
|
2010-04-20 08:30:10 +00:00
|
|
|
GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
|
|
|
|
GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
|
2012-05-19 10:03:40 +00:00
|
|
|
INT lineno, const RectF *bounds, INT *underlined_indexes,
|
|
|
|
INT underlined_index_count, void *user_data)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
|
|
|
struct draw_string_args *args = user_data;
|
2012-03-05 20:17:42 +00:00
|
|
|
PointF position;
|
2012-05-19 10:03:40 +00:00
|
|
|
GpStatus stat;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
position.X = args->x + bounds->X / args->rel_width;
|
|
|
|
position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
stat = draw_driver_string(args->graphics, &string[index], length, font, format,
|
2012-03-05 20:17:42 +00:00
|
|
|
args->brush, &position,
|
|
|
|
DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
if (stat == Ok && underlined_index_count)
|
|
|
|
{
|
|
|
|
OUTLINETEXTMETRICW otm;
|
|
|
|
REAL underline_y, underline_height;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
|
|
|
|
|
|
|
|
underline_height = otm.otmsUnderscoreSize / args->rel_height;
|
|
|
|
underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
|
|
|
|
|
|
|
|
for (i=0; i<underlined_index_count; i++)
|
|
|
|
{
|
|
|
|
REAL start_x, end_x;
|
|
|
|
SIZE text_size;
|
|
|
|
INT ofs = underlined_indexes[i] - index;
|
|
|
|
|
|
|
|
GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
|
|
|
|
start_x = text_size.cx / args->rel_width;
|
|
|
|
|
|
|
|
GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
|
|
|
|
end_x = text_size.cx / args->rel_width;
|
|
|
|
|
|
|
|
GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat;
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
|
|
|
|
INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
|
|
|
|
GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
|
|
|
|
{
|
|
|
|
HRGN rgn = NULL;
|
|
|
|
HFONT gdifont;
|
|
|
|
GpPointF pt[3], rectcpy[4];
|
|
|
|
POINT corners[4];
|
2012-12-11 21:40:15 +00:00
|
|
|
REAL rel_width, rel_height, margin_x;
|
|
|
|
INT save_state, format_flags = 0;
|
2012-03-05 20:17:42 +00:00
|
|
|
REAL offsety = 0.0;
|
2010-04-20 08:30:10 +00:00
|
|
|
struct draw_string_args args;
|
|
|
|
RectF scaled_rect;
|
2012-03-05 20:17:42 +00:00
|
|
|
HDC hdc, temp_hdc=NULL;
|
|
|
|
TEXTMETRICW textmetric;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
|
|
|
|
length, font, debugstr_rectf(rect), format, brush);
|
|
|
|
|
|
|
|
if(!graphics || !string || !font || !brush || !rect)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if(graphics->hdc)
|
|
|
|
{
|
|
|
|
hdc = graphics->hdc;
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
else
|
2010-11-20 11:24:17 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
hdc = temp_hdc = CreateCompatibleDC(0);
|
2010-11-20 11:24:17 +00:00
|
|
|
}
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
if(format){
|
|
|
|
TRACE("may be ignoring some format flags: attr %x\n", format->attr);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
format_flags = format->attr;
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
/* Should be no need to explicitly test for StringAlignmentNear as
|
|
|
|
* that is default behavior if no alignment is passed. */
|
2017-06-03 19:01:50 +00:00
|
|
|
if(format->line_align != StringAlignmentNear){
|
2012-12-11 21:40:15 +00:00
|
|
|
RectF bounds, in_rect = *rect;
|
|
|
|
in_rect.Height = 0.0; /* avoid height clipping */
|
|
|
|
GdipMeasureString(graphics, string, length, font, &in_rect, format, &bounds, 0, 0);
|
|
|
|
|
|
|
|
TRACE("bounds %s\n", debugstr_rectf(&bounds));
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2017-06-03 19:01:50 +00:00
|
|
|
if(format->line_align == StringAlignmentCenter)
|
2010-04-20 08:30:10 +00:00
|
|
|
offsety = (rect->Height - bounds.Height) / 2;
|
2017-06-03 19:01:50 +00:00
|
|
|
else if(format->line_align == StringAlignmentFar)
|
2010-04-20 08:30:10 +00:00
|
|
|
offsety = (rect->Height - bounds.Height);
|
|
|
|
}
|
2017-06-03 19:01:50 +00:00
|
|
|
TRACE("line align %d, offsety %f\n", format->line_align, offsety);
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
save_state = SaveDC(hdc);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 0.0;
|
|
|
|
pt[2].X = 0.0;
|
|
|
|
pt[2].Y = 1.0;
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
|
2010-04-20 08:30:10 +00:00
|
|
|
rel_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));
|
|
|
|
rel_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));
|
|
|
|
|
|
|
|
rectcpy[3].X = rectcpy[0].X = rect->X;
|
2012-12-11 21:40:15 +00:00
|
|
|
rectcpy[1].Y = rectcpy[0].Y = rect->Y;
|
2010-04-20 08:30:10 +00:00
|
|
|
rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
|
2012-12-11 21:40:15 +00:00
|
|
|
rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, rectcpy, 4);
|
|
|
|
round_points(corners, rectcpy, 4);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
margin_x = (format && format->generic_typographic) ? 0.0 : font->emSize / 6.0;
|
|
|
|
margin_x *= units_scale(font->unit, graphics->unit, graphics->xres);
|
|
|
|
|
|
|
|
scaled_rect.X = margin_x * rel_width;
|
2010-04-20 08:30:10 +00:00
|
|
|
scaled_rect.Y = 0.0;
|
|
|
|
scaled_rect.Width = rel_width * rect->Width;
|
|
|
|
scaled_rect.Height = rel_height * rect->Height;
|
2012-12-11 21:40:15 +00:00
|
|
|
if (scaled_rect.Width >= 0.5)
|
|
|
|
{
|
|
|
|
scaled_rect.Width -= margin_x * 2.0 * rel_width;
|
|
|
|
if (scaled_rect.Width < 0.5) return Ok; /* doesn't fit */
|
|
|
|
}
|
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
if (scaled_rect.Width >= 1 << 23) scaled_rect.Width = 1 << 23;
|
|
|
|
if (scaled_rect.Height >= 1 << 23) scaled_rect.Height = 1 << 23;
|
2012-12-11 21:40:15 +00:00
|
|
|
|
|
|
|
if (!(format_flags & StringFormatFlagsNoClip) &&
|
2013-09-19 15:10:19 +00:00
|
|
|
scaled_rect.Width != 1 << 23 && scaled_rect.Height != 1 << 23 &&
|
|
|
|
rect->Width > 0.0 && rect->Height > 0.0)
|
2010-04-20 08:30:10 +00:00
|
|
|
{
|
|
|
|
/* FIXME: If only the width or only the height is 0, we should probably still clip */
|
|
|
|
rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
|
2012-03-05 20:17:42 +00:00
|
|
|
SelectClipRgn(hdc, rgn);
|
2010-04-20 08:30:10 +00:00
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
get_font_hfont(graphics, font, format, &gdifont, NULL);
|
2012-03-05 20:17:42 +00:00
|
|
|
SelectObject(hdc, gdifont);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
args.graphics = graphics;
|
|
|
|
args.brush = brush;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
args.x = rect->X;
|
|
|
|
args.y = rect->Y + offsety;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
args.rel_width = rel_width;
|
|
|
|
args.rel_height = rel_height;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
GetTextMetricsW(hdc, &textmetric);
|
|
|
|
args.ascent = textmetric.tmAscent / rel_height;
|
2010-04-20 08:30:10 +00:00
|
|
|
|
2013-05-23 17:23:00 +00:00
|
|
|
gdip_format_string(hdc, string, length, font, &scaled_rect, format, TRUE,
|
2010-04-20 08:30:10 +00:00
|
|
|
draw_string_callback, &args);
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2010-04-20 08:30:10 +00:00
|
|
|
DeleteObject(rgn);
|
|
|
|
DeleteObject(gdifont);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
RestoreDC(hdc, save_state);
|
|
|
|
|
|
|
|
DeleteDC(temp_hdc);
|
2010-04-20 08:30:10 +00:00
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
|
|
|
|
{
|
|
|
|
TRACE("(%p)\n", graphics);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
return GdipSetInfinite(graphics->clip);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
|
|
|
|
{
|
2016-08-19 09:28:13 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p)\n", graphics);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-08-19 09:28:13 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
stat = METAFILE_ResetWorldTransform((GpMetafile*)graphics->image);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
return GdipSetMatrixElements(&graphics->worldtrans, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
2008-09-07 10:32:49 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
|
|
|
|
GpMatrixOrder order)
|
|
|
|
{
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
stat = METAFILE_RotateWorldTransform((GpMetafile*)graphics->image, angle, order);
|
2008-03-25 17:34:57 +00:00
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GdipRotateMatrix(&graphics->worldtrans, angle, order);
|
2008-12-06 09:26:01 +00:00
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
static GpStatus begin_container(GpGraphics *graphics,
|
|
|
|
GraphicsContainerType type, GraphicsContainer *state)
|
2008-12-06 09:26:01 +00:00
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
GraphicsContainerItem *container;
|
|
|
|
GpStatus sts;
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
if(!graphics || !state)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
sts = init_container(&container, graphics, type);
|
2009-09-03 15:07:25 +00:00
|
|
|
if(sts != Ok)
|
|
|
|
return sts;
|
|
|
|
|
|
|
|
list_add_head(&graphics->containers, &container->entry);
|
|
|
|
*state = graphics->contid = container->contid;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
if (type == BEGIN_CONTAINER)
|
|
|
|
METAFILE_BeginContainerNoParams((GpMetafile*)graphics->image, container->contid);
|
|
|
|
else
|
|
|
|
METAFILE_SaveGraphics((GpMetafile*)graphics->image, container->contid);
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", graphics, state);
|
|
|
|
return begin_container(graphics, SAVE_GRAPHICS, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
|
|
|
|
GraphicsContainer *state)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", graphics, state);
|
|
|
|
return begin_container(graphics, BEGIN_CONTAINER, state);
|
|
|
|
}
|
|
|
|
|
2009-06-07 07:59:56 +00:00
|
|
|
GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
|
|
|
|
{
|
2016-11-17 22:35:44 +00:00
|
|
|
GraphicsContainerItem *container;
|
|
|
|
GpMatrix transform;
|
|
|
|
GpStatus stat;
|
|
|
|
GpRectF scaled_srcrect;
|
|
|
|
REAL scale_x, scale_y;
|
|
|
|
|
|
|
|
TRACE("(%p, %s, %s, %d, %p)\n", graphics, debugstr_rectf(dstrect), debugstr_rectf(srcrect), unit, state);
|
|
|
|
|
|
|
|
if(!graphics || !dstrect || !srcrect || unit < UnitPixel || unit > UnitMillimeter || !state)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
stat = init_container(&container, graphics, BEGIN_CONTAINER);
|
|
|
|
if(stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
list_add_head(&graphics->containers, &container->entry);
|
|
|
|
*state = graphics->contid = container->contid;
|
|
|
|
|
|
|
|
scale_x = units_to_pixels(1.0, unit, graphics->xres);
|
|
|
|
scale_y = units_to_pixels(1.0, unit, graphics->yres);
|
|
|
|
|
|
|
|
scaled_srcrect.X = scale_x * srcrect->X;
|
|
|
|
scaled_srcrect.Y = scale_y * srcrect->Y;
|
|
|
|
scaled_srcrect.Width = scale_x * srcrect->Width;
|
|
|
|
scaled_srcrect.Height = scale_y * srcrect->Height;
|
|
|
|
|
|
|
|
transform.matrix[0] = dstrect->Width / scaled_srcrect.Width;
|
|
|
|
transform.matrix[1] = 0.0;
|
|
|
|
transform.matrix[2] = 0.0;
|
|
|
|
transform.matrix[3] = dstrect->Height / scaled_srcrect.Height;
|
|
|
|
transform.matrix[4] = dstrect->X - scaled_srcrect.X;
|
|
|
|
transform.matrix[5] = dstrect->Y - scaled_srcrect.Y;
|
|
|
|
|
|
|
|
GdipMultiplyMatrix(&graphics->worldtrans, &transform, MatrixOrderPrepend);
|
|
|
|
|
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
METAFILE_BeginContainer((GpMetafile*)graphics->image, dstrect, srcrect, unit, container->contid);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok;
|
2009-06-07 07:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
|
|
|
|
{
|
2016-11-17 22:35:44 +00:00
|
|
|
GpRectF dstrectf, srcrectf;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %p, %d, %p)\n", graphics, dstrect, srcrect, unit, state);
|
|
|
|
|
|
|
|
if (!dstrect || !srcrect)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
dstrectf.X = dstrect->X;
|
|
|
|
dstrectf.Y = dstrect->Y;
|
|
|
|
dstrectf.Width = dstrect->Width;
|
|
|
|
dstrectf.Height = dstrect->Height;
|
|
|
|
|
|
|
|
srcrectf.X = srcrect->X;
|
|
|
|
srcrectf.Y = srcrect->Y;
|
|
|
|
srcrectf.Width = srcrect->Width;
|
|
|
|
srcrectf.Height = srcrect->Height;
|
|
|
|
|
|
|
|
return GdipBeginContainer(graphics, &dstrectf, &srcrectf, unit, state);
|
2009-06-07 07:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
|
|
|
|
{
|
|
|
|
FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
|
|
|
|
return NotImplemented;
|
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
static GpStatus end_container(GpGraphics *graphics, GraphicsContainerType type,
|
|
|
|
GraphicsContainer state)
|
2008-12-06 09:26:01 +00:00
|
|
|
{
|
2009-09-03 15:07:25 +00:00
|
|
|
GpStatus sts;
|
|
|
|
GraphicsContainerItem *container, *container2;
|
2008-12-06 09:26:01 +00:00
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
if(!graphics)
|
2008-12-06 09:26:01 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
|
2016-11-17 22:35:44 +00:00
|
|
|
if(container->contid == state && container->type == type)
|
2009-09-03 15:07:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* did not find a matching container */
|
|
|
|
if(&container->entry == &graphics->containers)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
sts = restore_container(graphics, container);
|
|
|
|
if(sts != Ok)
|
|
|
|
return sts;
|
|
|
|
|
|
|
|
/* remove all of the containers on top of the found container */
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
|
|
|
|
if(container->contid == state)
|
|
|
|
break;
|
|
|
|
list_remove(&container->entry);
|
|
|
|
delete_container(container);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_remove(&container->entry);
|
|
|
|
delete_container(container);
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
if (type == BEGIN_CONTAINER)
|
|
|
|
METAFILE_EndContainer((GpMetafile*)graphics->image, state);
|
|
|
|
else
|
|
|
|
METAFILE_RestoreGraphics((GpMetafile*)graphics->image, state);
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
return Ok;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %x)\n", graphics, state);
|
|
|
|
return end_container(graphics, BEGIN_CONTAINER, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %x)\n", graphics, state);
|
|
|
|
return end_container(graphics, SAVE_GRAPHICS, state);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
|
|
|
|
REAL sy, GpMatrixOrder order)
|
|
|
|
{
|
2016-08-19 09:28:13 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-08-19 09:28:13 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
stat = METAFILE_ScaleWorldTransform((GpMetafile*)graphics->image, sx, sy, order);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
return GdipScaleMatrix(&graphics->worldtrans, sx, sy, order);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
|
|
|
|
CombineMode mode)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
|
|
|
|
|
|
|
|
if(!graphics || !srcgraphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
|
|
|
|
CompositingMode mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(graphics->compmode == mode)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
if(graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
|
|
|
|
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
|
|
|
|
EmfPlusRecordTypeSetCompositingMode, mode);
|
|
|
|
if(stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->compmode = mode;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
|
|
|
|
CompositingQuality quality)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, quality);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(graphics->compqual == quality)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
if(graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
|
|
|
|
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
|
|
|
|
EmfPlusRecordTypeSetCompositingQuality, quality);
|
|
|
|
if(stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->compqual = quality;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
|
|
|
|
InterpolationMode mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, mode);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
|
2008-03-25 17:34:57 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
|
|
|
|
mode = InterpolationModeBilinear;
|
|
|
|
|
|
|
|
if (mode == InterpolationModeHighQuality)
|
|
|
|
mode = InterpolationModeHighQualityBicubic;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (mode == graphics->interpolation)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
|
|
|
|
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
|
|
|
|
EmfPlusRecordTypeSetInterpolationMode, mode);
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->interpolation = mode;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %.2f)\n", graphics, scale);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || (scale <= 0.0))
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, graphics->unit, scale);
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->scale = scale;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, unit);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
if(unit == UnitWorld)
|
2008-03-25 17:34:57 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
stat = METAFILE_SetPageTransform((GpMetafile*)graphics->image, unit, graphics->scale);
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->unit = unit;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
|
|
|
|
mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(graphics->pixeloffset == mode)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
if(graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
|
|
|
|
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
|
|
|
|
EmfPlusRecordTypeSetPixelOffsetMode, mode);
|
|
|
|
if(stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->pixeloffset = mode;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2009-05-09 09:26:16 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
|
|
|
|
{
|
|
|
|
static int calls;
|
|
|
|
|
|
|
|
TRACE("(%p,%i,%i)\n", graphics, x, y);
|
|
|
|
|
|
|
|
if (!(calls++))
|
2012-05-19 10:03:40 +00:00
|
|
|
FIXME("value is unused in rendering\n");
|
2009-05-09 09:26:16 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
graphics->origin_x = x;
|
|
|
|
graphics->origin_y = y;
|
|
|
|
|
|
|
|
return Ok;
|
2009-05-09 09:26:16 +00:00
|
|
|
}
|
|
|
|
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
|
|
|
|
{
|
|
|
|
TRACE("(%p,%p,%p)\n", graphics, x, y);
|
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
if (!graphics || !x || !y)
|
|
|
|
return InvalidParameter;
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
*x = graphics->origin_x;
|
|
|
|
*y = graphics->origin_y;
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
|
2012-05-19 10:03:40 +00:00
|
|
|
return Ok;
|
Sync avifil, credui, crypt32, cryptdlg, cryptui, dnsapi, gdiplus, hhctrl, hnetcfg, iccvid, imaadp32, imm32, jscript, localspl, localui, mapi32, mciavi32, mcicda, mciqtz32, mciseq, mciwave, mshtml, msrle32, msvfw32, msvidc32, msxml3, oleacc, oleaut32 to Wine 1.2rc5 (Samuel Serapion, small changes by me)
Remove Esperanto and Walon languages from comctl32, comdlg32, mpr, msi, shlwapi, wininet
svn path=/trunk/; revision=47920
2010-07-01 11:09:47 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, mode);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(graphics->smoothing == mode)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
if(graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
GpStatus stat;
|
|
|
|
BOOL antialias = (mode != SmoothingModeDefault &&
|
|
|
|
mode != SmoothingModeNone && mode != SmoothingModeHighSpeed);
|
|
|
|
|
|
|
|
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
|
|
|
|
EmfPlusRecordTypeSetAntiAliasMode, (mode << 1) + antialias);
|
|
|
|
if(stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->smoothing = mode;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %d)\n", graphics, contrast);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
graphics->textcontrast = contrast;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
|
|
|
|
TextRenderingHint hint)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %d)\n", graphics, hint);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if(!graphics || hint > TextRenderingHintClearTypeGridFit)
|
2008-03-25 17:34:57 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if(graphics->texthint == hint)
|
|
|
|
return Ok;
|
|
|
|
|
|
|
|
if(graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
GpStatus stat;
|
|
|
|
|
|
|
|
stat = METAFILE_AddSimpleProperty((GpMetafile*)graphics->image,
|
|
|
|
EmfPlusRecordTypeSetTextRenderingHint, hint);
|
|
|
|
if(stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
graphics->texthint = hint;
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
|
|
|
|
{
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, matrix);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics || !matrix)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
TRACE("%f,%f,%f,%f,%f,%f\n",
|
|
|
|
matrix->matrix[0], matrix->matrix[1], matrix->matrix[2],
|
|
|
|
matrix->matrix[3], matrix->matrix[4], matrix->matrix[5]);
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
stat = METAFILE_SetWorldTransform((GpMetafile*)graphics->image, matrix);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
graphics->worldtrans = *matrix;
|
|
|
|
|
|
|
|
return Ok;
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
|
|
|
|
REAL dy, GpMatrixOrder order)
|
|
|
|
{
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
|
|
|
|
|
2008-03-25 17:34:57 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
stat = METAFILE_TranslateWorldTransform((GpMetafile*)graphics->image, dx, dy, order);
|
|
|
|
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
return GdipTranslateMatrix(&graphics->worldtrans, dx, dy, order);
|
2008-03-25 17:34:57 +00:00
|
|
|
}
|
2008-04-14 12:53:30 +00:00
|
|
|
|
2009-02-14 08:14:34 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* GdipSetClipHrgn [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
|
|
|
|
{
|
|
|
|
GpRegion *region;
|
|
|
|
GpStatus status;
|
2018-01-18 23:17:41 +00:00
|
|
|
GpMatrix transform;
|
2009-02-14 08:14:34 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2013-09-19 15:10:19 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
/* hrgn is in gdi32 device units */
|
2009-02-14 08:14:34 +00:00
|
|
|
status = GdipCreateRegionHrgn(hrgn, ®ion);
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
if (status == Ok)
|
|
|
|
{
|
|
|
|
status = get_graphics_transform(graphics, CoordinateSpaceDevice, WineCoordinateSpaceGdiDevice, &transform);
|
|
|
|
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipTransformRegion(region, &transform);
|
|
|
|
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipCombineRegionRegion(graphics->clip, region, mode);
|
2009-02-14 08:14:34 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
GdipDeleteRegion(region);
|
|
|
|
}
|
2009-02-14 08:14:34 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
|
|
|
|
{
|
2013-09-19 15:10:19 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath *clip_path;
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
TRACE("(%p, %p, %d)\n", graphics, path, mode);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2013-09-19 15:10:19 +00:00
|
|
|
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;
|
2008-12-06 09:26:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
|
|
|
|
REAL width, REAL height,
|
|
|
|
CombineMode mode)
|
|
|
|
{
|
2013-09-19 15:10:19 +00:00
|
|
|
GpStatus status;
|
2008-12-06 09:26:01 +00:00
|
|
|
GpRectF rect;
|
2013-09-19 15:10:19 +00:00
|
|
|
GpRegion *region;
|
2008-12-06 09:26:01 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
status = METAFILE_SetClipRect((GpMetafile*)graphics->image, x, y, width, height, mode);
|
|
|
|
if (status != Ok)
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
rect.X = x;
|
|
|
|
rect.Y = y;
|
|
|
|
rect.Width = width;
|
|
|
|
rect.Height = height;
|
2013-09-19 15:10:19 +00:00
|
|
|
status = GdipCreateRegionRect(&rect, ®ion);
|
|
|
|
if (status == Ok)
|
|
|
|
{
|
|
|
|
GpMatrix world_to_device;
|
2018-05-27 03:10:39 +00:00
|
|
|
BOOL identity;
|
2013-09-19 15:10:19 +00:00
|
|
|
|
|
|
|
get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
|
2018-05-27 03:10:39 +00:00
|
|
|
status = GdipIsMatrixIdentity(&world_to_device, &identity);
|
|
|
|
if (status == Ok && !identity)
|
|
|
|
status = GdipTransformRegion(region, &world_to_device);
|
2013-09-19 15:10:19 +00:00
|
|
|
if (status == Ok)
|
|
|
|
status = GdipCombineRegionRegion(graphics->clip, region, mode);
|
2008-12-06 09:26:01 +00:00
|
|
|
|
2013-09-19 15:10:19 +00:00
|
|
|
GdipDeleteRegion(region);
|
|
|
|
}
|
|
|
|
return status;
|
2008-12-06 09:26:01 +00:00
|
|
|
}
|
|
|
|
|
2008-04-14 12:53:30 +00:00
|
|
|
GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
|
|
|
|
INT width, INT height,
|
2008-12-06 09:26:01 +00:00
|
|
|
CombineMode mode)
|
2008-04-14 12:53:30 +00:00
|
|
|
{
|
2008-12-06 09:26:01 +00:00
|
|
|
TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
|
2008-04-14 12:53:30 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2008-12-06 09:26:01 +00:00
|
|
|
return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
|
2008-04-14 12:53:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
|
2008-09-07 10:32:49 +00:00
|
|
|
CombineMode mode)
|
2008-04-14 12:53:30 +00:00
|
|
|
{
|
2013-09-19 15:10:19 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpRegion *clip;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d)\n", graphics, region, mode);
|
2008-04-14 12:53:30 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(!graphics || !region)
|
|
|
|
return InvalidParameter;
|
2008-04-14 12:53:30 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
status = METAFILE_SetClipRegion((GpMetafile*)graphics->image, region, mode);
|
|
|
|
if (status != Ok)
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2013-09-19 15:10:19 +00:00
|
|
|
status = GdipCloneRegion(region, &clip);
|
|
|
|
if (status == Ok)
|
|
|
|
{
|
|
|
|
GpMatrix world_to_device;
|
2018-05-27 03:10:39 +00:00
|
|
|
BOOL identity;
|
2013-09-19 15:10:19 +00:00
|
|
|
|
|
|
|
get_graphics_transform(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
|
2018-05-27 03:10:39 +00:00
|
|
|
status = GdipIsMatrixIdentity(&world_to_device, &identity);
|
|
|
|
if (status == Ok && !identity)
|
|
|
|
status = GdipTransformRegion(clip, &world_to_device);
|
2013-09-19 15:10:19 +00:00
|
|
|
if (status == Ok)
|
|
|
|
status = GdipCombineRegionRegion(graphics->clip, clip, mode);
|
|
|
|
|
|
|
|
GdipDeleteRegion(clip);
|
|
|
|
}
|
|
|
|
return status;
|
2008-04-14 12:53:30 +00:00
|
|
|
}
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
|
|
|
|
INT count)
|
|
|
|
{
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus status;
|
|
|
|
GpPath* path;
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d)\n", graphics, points, count);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!graphics || !pen || count<=0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
status = GdipCreatePath(FillModeAlternate, &path);
|
|
|
|
if (status != Ok) return status;
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
status = GdipAddPathPolygon(path, points, count);
|
|
|
|
if (status == Ok)
|
|
|
|
status = GdipDrawPath(graphics, pen, path);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
GdipDeletePath(path);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
return status;
|
2008-05-04 19:41:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
|
|
|
|
INT count)
|
|
|
|
{
|
|
|
|
GpStatus ret;
|
|
|
|
GpPointF *ptf;
|
|
|
|
INT i;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(count<=0) return InvalidParameter;
|
2015-11-22 10:10:02 +00:00
|
|
|
ptf = heap_alloc_zero(sizeof(GpPointF) * count);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
for(i = 0;i < count; i++){
|
|
|
|
ptf[i].X = (REAL)points[i].X;
|
|
|
|
ptf[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipDrawPolygon(graphics,pen,ptf,count);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(ptf);
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, dpi);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!graphics || !dpi)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
*dpi = graphics->xres;
|
2008-05-04 19:41:25 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, dpi);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!graphics || !dpi)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
*dpi = graphics->yres;
|
2008-05-04 19:41:25 +00:00
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
|
|
|
|
GpMatrixOrder order)
|
|
|
|
{
|
|
|
|
GpMatrix m;
|
|
|
|
GpStatus ret;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p, %d)\n", graphics, matrix, order);
|
|
|
|
|
2008-05-04 19:41:25 +00:00
|
|
|
if(!graphics || !matrix)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile) {
|
|
|
|
ret = METAFILE_MultiplyWorldTransform((GpMetafile*)graphics->image, matrix, order);
|
|
|
|
|
|
|
|
if (ret != Ok)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
m = graphics->worldtrans;
|
2008-05-04 19:41:25 +00:00
|
|
|
|
2009-01-27 18:03:33 +00:00
|
|
|
ret = GdipMultiplyMatrix(&m, matrix, order);
|
2008-05-04 19:41:25 +00:00
|
|
|
if(ret == Ok)
|
2012-12-11 21:40:15 +00:00
|
|
|
graphics->worldtrans = m;
|
2008-05-04 19:41:25 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2008-06-01 13:14:02 +00:00
|
|
|
|
2010-11-20 11:24:17 +00:00
|
|
|
/* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
|
|
|
|
static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
|
|
|
|
|
2008-06-01 13:14:02 +00:00
|
|
|
GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat=Ok;
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, hdc);
|
2008-06-01 13:14:02 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
if(!graphics || !hdc)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
|
|
|
|
}
|
2015-07-19 13:31:15 +00:00
|
|
|
else if (!graphics->hdc ||
|
2010-11-20 11:24:17 +00:00
|
|
|
(graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
|
|
|
|
{
|
|
|
|
/* Create a fake HDC and fill it with a constant color. */
|
|
|
|
HDC temp_hdc;
|
|
|
|
HBITMAP hbitmap;
|
|
|
|
GpRectF bounds;
|
|
|
|
BITMAPINFOHEADER bmih;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
stat = get_graphics_bounds(graphics, &bounds);
|
|
|
|
if (stat != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
graphics->temp_hbitmap_width = bounds.Width;
|
|
|
|
graphics->temp_hbitmap_height = bounds.Height;
|
|
|
|
|
|
|
|
bmih.biSize = sizeof(bmih);
|
|
|
|
bmih.biWidth = graphics->temp_hbitmap_width;
|
|
|
|
bmih.biHeight = -graphics->temp_hbitmap_height;
|
|
|
|
bmih.biPlanes = 1;
|
|
|
|
bmih.biBitCount = 32;
|
|
|
|
bmih.biCompression = BI_RGB;
|
|
|
|
bmih.biSizeImage = 0;
|
|
|
|
bmih.biXPelsPerMeter = 0;
|
|
|
|
bmih.biYPelsPerMeter = 0;
|
|
|
|
bmih.biClrUsed = 0;
|
|
|
|
bmih.biClrImportant = 0;
|
|
|
|
|
|
|
|
hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
|
|
|
|
(void**)&graphics->temp_bits, NULL, 0);
|
|
|
|
if (!hbitmap)
|
|
|
|
return GenericError;
|
|
|
|
|
|
|
|
temp_hdc = CreateCompatibleDC(0);
|
|
|
|
if (!temp_hdc)
|
|
|
|
{
|
|
|
|
DeleteObject(hbitmap);
|
|
|
|
return GenericError;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
|
|
|
|
((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
|
|
|
|
|
|
|
|
SelectObject(temp_hdc, hbitmap);
|
|
|
|
|
|
|
|
graphics->temp_hbitmap = hbitmap;
|
|
|
|
*hdc = graphics->temp_hdc = temp_hdc;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*hdc = graphics->hdc;
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
graphics->busy = TRUE;
|
2008-09-07 10:32:49 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
2008-06-01 13:14:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat=Ok;
|
2008-06-01 13:14:02 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
TRACE("(%p, %p)\n", graphics, hdc);
|
2008-09-07 10:32:49 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if(!graphics || !hdc || !graphics->busy)
|
2008-09-07 10:32:49 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (graphics->image && graphics->image->type == ImageTypeMetafile)
|
|
|
|
{
|
|
|
|
stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
|
|
|
|
}
|
|
|
|
else if (graphics->temp_hdc == hdc)
|
2010-11-20 11:24:17 +00:00
|
|
|
{
|
|
|
|
DWORD* pos;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Find the pixels that have changed, and mark them as opaque. */
|
|
|
|
pos = (DWORD*)graphics->temp_bits;
|
|
|
|
for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
|
|
|
|
{
|
|
|
|
if (*pos != DC_BACKGROUND_KEY)
|
|
|
|
{
|
|
|
|
*pos |= 0xff000000;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the changed pixels to the real target. */
|
|
|
|
alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
|
|
|
|
graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
|
2015-07-19 13:31:15 +00:00
|
|
|
graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB);
|
2010-11-20 11:24:17 +00:00
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
DeleteDC(graphics->temp_hdc);
|
|
|
|
DeleteObject(graphics->temp_hbitmap);
|
|
|
|
graphics->temp_hdc = NULL;
|
|
|
|
graphics->temp_hbitmap = NULL;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
else if (hdc != graphics->hdc)
|
|
|
|
{
|
|
|
|
stat = InvalidParameter;
|
|
|
|
}
|
2010-11-20 11:24:17 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (stat == Ok)
|
|
|
|
graphics->busy = FALSE;
|
2008-09-07 10:32:49 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
2008-06-01 13:14:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
|
|
|
|
{
|
2008-09-07 10:32:49 +00:00
|
|
|
GpRegion *clip;
|
|
|
|
GpStatus status;
|
2013-09-19 15:10:19 +00:00
|
|
|
GpMatrix device_to_world;
|
2008-09-07 10:32:49 +00:00
|
|
|
|
|
|
|
TRACE("(%p, %p)\n", graphics, region);
|
|
|
|
|
|
|
|
if(!graphics || !region)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
|
|
|
|
return status;
|
|
|
|
|
2013-09-19 15:10:19 +00:00
|
|
|
get_graphics_transform(graphics, CoordinateSpaceWorld, CoordinateSpaceDevice, &device_to_world);
|
|
|
|
status = GdipTransformRegion(clip, &device_to_world);
|
|
|
|
if (status != Ok)
|
|
|
|
{
|
|
|
|
GdipDeleteRegion(clip);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
/* free everything except root node and header */
|
|
|
|
delete_element(®ion->node);
|
|
|
|
memcpy(region, clip, sizeof(GpRegion));
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(clip);
|
2008-06-01 13:14:02 +00:00
|
|
|
|
2008-09-07 10:32:49 +00:00
|
|
|
return Ok;
|
2008-06-01 13:14:02 +00:00
|
|
|
}
|
2008-08-05 12:23:58 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
GpStatus gdi_transform_acquire(GpGraphics *graphics)
|
|
|
|
{
|
|
|
|
if (graphics->gdi_transform_acquire_count == 0 && graphics->hdc)
|
|
|
|
{
|
|
|
|
graphics->gdi_transform_save = SaveDC(graphics->hdc);
|
|
|
|
SetGraphicsMode(graphics->hdc, GM_COMPATIBLE);
|
|
|
|
SetMapMode(graphics->hdc, MM_TEXT);
|
|
|
|
SetWindowOrgEx(graphics->hdc, 0, 0, NULL);
|
|
|
|
SetViewportOrgEx(graphics->hdc, 0, 0, NULL);
|
|
|
|
}
|
|
|
|
graphics->gdi_transform_acquire_count++;
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus gdi_transform_release(GpGraphics *graphics)
|
|
|
|
{
|
|
|
|
if (graphics->gdi_transform_acquire_count <= 0)
|
|
|
|
{
|
2018-01-18 23:17:41 +00:00
|
|
|
ERR("called without matching gdi_transform_acquire\n");
|
2017-09-17 12:34:15 +00:00
|
|
|
return GenericError;
|
|
|
|
}
|
|
|
|
if (graphics->gdi_transform_acquire_count == 1 && graphics->hdc)
|
|
|
|
{
|
|
|
|
RestoreDC(graphics->hdc, graphics->gdi_transform_save);
|
|
|
|
}
|
|
|
|
graphics->gdi_transform_acquire_count--;
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
2012-12-11 21:40:15 +00:00
|
|
|
GpCoordinateSpace src_space, GpMatrix *matrix)
|
2008-08-05 12:23:58 +00:00
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
GpStatus stat = Ok;
|
|
|
|
REAL scale_x, scale_y;
|
2009-05-23 10:32:19 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipSetMatrixElements(matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
|
|
|
|
|
|
|
|
if (dst_space != src_space)
|
2009-05-23 10:32:19 +00:00
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
scale_x = units_to_pixels(1.0, graphics->unit, graphics->xres);
|
|
|
|
scale_y = units_to_pixels(1.0, graphics->unit, graphics->yres);
|
2009-05-23 10:32:19 +00:00
|
|
|
|
|
|
|
if(graphics->unit != UnitDisplay)
|
2012-12-11 21:40:15 +00:00
|
|
|
{
|
|
|
|
scale_x *= graphics->scale;
|
|
|
|
scale_y *= graphics->scale;
|
|
|
|
}
|
2009-05-23 10:32:19 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
if (dst_space < src_space)
|
2009-05-23 10:32:19 +00:00
|
|
|
{
|
2017-09-17 12:34:15 +00:00
|
|
|
/* transform towards world space */
|
|
|
|
switch ((int)src_space)
|
|
|
|
{
|
|
|
|
case WineCoordinateSpaceGdiDevice:
|
|
|
|
{
|
|
|
|
GpMatrix gdixform;
|
2018-01-18 23:17:41 +00:00
|
|
|
gdixform = graphics->gdi_transform;
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = GdipInvertMatrix(&gdixform);
|
|
|
|
if (stat != Ok)
|
|
|
|
break;
|
|
|
|
GdipMultiplyMatrix(matrix, &gdixform, MatrixOrderAppend);
|
|
|
|
if (dst_space == CoordinateSpaceDevice)
|
|
|
|
break;
|
|
|
|
/* else fall-through */
|
|
|
|
}
|
|
|
|
case CoordinateSpaceDevice:
|
|
|
|
GdipScaleMatrix(matrix, 1.0/scale_x, 1.0/scale_y, MatrixOrderAppend);
|
|
|
|
if (dst_space == CoordinateSpacePage)
|
|
|
|
break;
|
|
|
|
/* else fall-through */
|
|
|
|
case CoordinateSpacePage:
|
2009-05-23 10:32:19 +00:00
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix inverted_transform = graphics->worldtrans;
|
|
|
|
stat = GdipInvertMatrix(&inverted_transform);
|
2009-05-23 10:32:19 +00:00
|
|
|
if (stat == Ok)
|
2012-12-11 21:40:15 +00:00
|
|
|
GdipMultiplyMatrix(matrix, &inverted_transform, MatrixOrderAppend);
|
2009-05-23 10:32:19 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-09-17 12:34:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* transform towards device space */
|
|
|
|
switch ((int)src_space)
|
|
|
|
{
|
|
|
|
case CoordinateSpaceWorld:
|
|
|
|
GdipMultiplyMatrix(matrix, &graphics->worldtrans, MatrixOrderAppend);
|
|
|
|
if (dst_space == CoordinateSpacePage)
|
|
|
|
break;
|
|
|
|
/* else fall-through */
|
|
|
|
case CoordinateSpacePage:
|
|
|
|
GdipScaleMatrix(matrix, scale_x, scale_y, MatrixOrderAppend);
|
|
|
|
if (dst_space == CoordinateSpaceDevice)
|
|
|
|
break;
|
|
|
|
/* else fall-through */
|
|
|
|
case CoordinateSpaceDevice:
|
|
|
|
{
|
2018-01-18 23:17:41 +00:00
|
|
|
GdipMultiplyMatrix(matrix, &graphics->gdi_transform, MatrixOrderAppend);
|
2017-09-17 12:34:15 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-05-23 10:32:19 +00:00
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
return stat;
|
|
|
|
}
|
2009-05-23 10:32:19 +00:00
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
GpStatus gdip_transform_points(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
|
|
|
GpCoordinateSpace src_space, GpPointF *points, INT count)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
2012-12-11 21:40:15 +00:00
|
|
|
GpMatrix matrix;
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus stat;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
|
|
|
|
if (stat != Ok) return stat;
|
|
|
|
|
|
|
|
return GdipTransformMatrixPoints(&matrix, points, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
|
|
|
GpCoordinateSpace src_space, GpPointF *points, INT count)
|
|
|
|
{
|
|
|
|
if(!graphics || !points || count <= 0 ||
|
|
|
|
dst_space < 0 || dst_space > CoordinateSpaceDevice ||
|
|
|
|
src_space < 0 || src_space > CoordinateSpaceDevice)
|
2012-03-05 20:17:42 +00:00
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
|
|
|
|
|
|
|
|
if (src_space == dst_space) return Ok;
|
|
|
|
|
2017-09-17 12:34:15 +00:00
|
|
|
return gdip_transform_points(graphics, dst_space, src_space, points, count);
|
2008-08-05 12:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
|
|
|
|
GpCoordinateSpace src_space, GpPoint *points, INT count)
|
|
|
|
{
|
2009-05-23 10:32:19 +00:00
|
|
|
GpPointF *pointsF;
|
|
|
|
GpStatus ret;
|
|
|
|
INT i;
|
2008-08-05 12:23:58 +00:00
|
|
|
|
2009-05-23 10:32:19 +00:00
|
|
|
TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
|
|
|
|
|
|
|
|
if(count <= 0)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
pointsF = heap_alloc_zero(sizeof(GpPointF) * count);
|
2009-05-23 10:32:19 +00:00
|
|
|
if(!pointsF)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
for(i = 0; i < count; i++){
|
|
|
|
pointsF[i].X = (REAL)points[i].X;
|
|
|
|
pointsF[i].Y = (REAL)points[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
|
|
|
|
|
|
|
|
if(ret == Ok)
|
|
|
|
for(i = 0; i < count; i++){
|
2012-12-11 21:40:15 +00:00
|
|
|
points[i].X = gdip_round(pointsF[i].X);
|
|
|
|
points[i].Y = gdip_round(pointsF[i].Y);
|
2009-05-23 10:32:19 +00:00
|
|
|
}
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pointsF);
|
2009-05-23 10:32:19 +00:00
|
|
|
|
|
|
|
return ret;
|
2008-08-05 12:23:58 +00:00
|
|
|
}
|
2008-12-06 09:26:01 +00:00
|
|
|
|
|
|
|
HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
|
|
|
|
{
|
2010-04-20 08:30:10 +00:00
|
|
|
static int calls;
|
|
|
|
|
|
|
|
TRACE("\n");
|
|
|
|
|
|
|
|
if (!calls++)
|
|
|
|
FIXME("stub\n");
|
2008-12-06 09:26:01 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-02-14 08:14:34 +00:00
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* GdipTranslateClip [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
return GdipTranslateRegion(graphics->clip, dx, dy);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* GdipTranslateClipI [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %d, %d)\n", graphics, dx, dy);
|
|
|
|
|
|
|
|
if(!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if(graphics->busy)
|
|
|
|
return ObjectBusy;
|
|
|
|
|
|
|
|
return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
|
|
|
|
}
|
2009-09-03 15:07:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* GdipMeasureDriverString [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
|
|
|
|
GDIPCONST GpFont *font, GDIPCONST PointF *positions,
|
|
|
|
INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
|
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
|
|
|
|
HFONT hfont;
|
|
|
|
HDC hdc;
|
|
|
|
REAL min_x, min_y, max_x, max_y, x, y;
|
|
|
|
int i;
|
|
|
|
TEXTMETRICW textmetric;
|
|
|
|
const WORD *glyph_indices;
|
|
|
|
WORD *dynamic_glyph_indices=NULL;
|
|
|
|
REAL rel_width, rel_height, ascent, descent;
|
|
|
|
GpPointF pt[3];
|
|
|
|
|
|
|
|
TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
|
|
|
|
|
|
|
|
if (!graphics || !text || !font || !positions || !boundingBox)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if (length == -1)
|
|
|
|
length = strlenW(text);
|
|
|
|
|
|
|
|
if (length == 0)
|
|
|
|
{
|
|
|
|
boundingBox->X = 0.0;
|
|
|
|
boundingBox->Y = 0.0;
|
|
|
|
boundingBox->Width = 0.0;
|
|
|
|
boundingBox->Height = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & unsupported_flags)
|
|
|
|
FIXME("Ignoring flags %x\n", flags & unsupported_flags);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
get_font_hfont(graphics, font, NULL, &hfont, matrix);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
hdc = CreateCompatibleDC(0);
|
|
|
|
SelectObject(hdc, hfont);
|
|
|
|
|
|
|
|
GetTextMetricsW(hdc, &textmetric);
|
|
|
|
|
|
|
|
pt[0].X = 0.0;
|
|
|
|
pt[0].Y = 0.0;
|
|
|
|
pt[1].X = 1.0;
|
|
|
|
pt[1].Y = 0.0;
|
|
|
|
pt[2].X = 0.0;
|
|
|
|
pt[2].Y = 1.0;
|
2012-12-11 21:40:15 +00:00
|
|
|
if (matrix)
|
|
|
|
{
|
|
|
|
GpMatrix xform = *matrix;
|
|
|
|
GdipTransformMatrixPoints(&xform, pt, 3);
|
|
|
|
}
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, pt, 3);
|
2012-03-05 20:17:42 +00:00
|
|
|
rel_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));
|
|
|
|
rel_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));
|
|
|
|
|
|
|
|
if (flags & DriverStringOptionsCmapLookup)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
glyph_indices = dynamic_glyph_indices = heap_alloc_zero(sizeof(WORD) * length);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!glyph_indices)
|
|
|
|
{
|
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(hfont);
|
|
|
|
return OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
glyph_indices = text;
|
|
|
|
|
|
|
|
min_x = max_x = x = positions[0].X;
|
|
|
|
min_y = max_y = y = positions[0].Y;
|
|
|
|
|
|
|
|
ascent = textmetric.tmAscent / rel_height;
|
|
|
|
descent = textmetric.tmDescent / rel_height;
|
|
|
|
|
|
|
|
for (i=0; i<length; i++)
|
|
|
|
{
|
|
|
|
int char_width;
|
|
|
|
ABC abc;
|
|
|
|
|
|
|
|
if (!(flags & DriverStringOptionsRealizedAdvance))
|
|
|
|
{
|
|
|
|
x = positions[i].X;
|
|
|
|
y = positions[i].Y;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
|
2012-12-11 21:40:15 +00:00
|
|
|
char_width = abc.abcA + abc.abcB + abc.abcC;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (min_y > y - ascent) min_y = y - ascent;
|
|
|
|
if (max_y < y + descent) max_y = y + descent;
|
|
|
|
if (min_x > x) min_x = x;
|
|
|
|
|
|
|
|
x += char_width / rel_width;
|
|
|
|
|
|
|
|
if (max_x < x) max_x = x;
|
|
|
|
}
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(dynamic_glyph_indices);
|
2012-03-05 20:17:42 +00:00
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(hfont);
|
|
|
|
|
|
|
|
boundingBox->X = min_x;
|
|
|
|
boundingBox->Y = min_y;
|
|
|
|
boundingBox->Width = max_x - min_x;
|
|
|
|
boundingBox->Height = max_y - min_y;
|
|
|
|
|
|
|
|
return Ok;
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
|
2012-12-11 21:40:15 +00:00
|
|
|
GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
|
|
|
|
GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
|
|
|
|
INT flags, GDIPCONST GpMatrix *matrix)
|
2012-03-05 20:17:42 +00:00
|
|
|
{
|
|
|
|
static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
|
|
|
|
INT save_state;
|
|
|
|
GpPointF pt;
|
|
|
|
HFONT hfont;
|
|
|
|
UINT eto_flags=0;
|
2016-11-17 22:35:44 +00:00
|
|
|
GpStatus status;
|
|
|
|
HRGN hrgn;
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (flags & unsupported_flags)
|
|
|
|
FIXME("Ignoring flags %x\n", flags & unsupported_flags);
|
|
|
|
|
|
|
|
if (!(flags & DriverStringOptionsCmapLookup))
|
|
|
|
eto_flags |= ETO_GLYPH_INDEX;
|
|
|
|
|
|
|
|
save_state = SaveDC(graphics->hdc);
|
|
|
|
SetBkMode(graphics->hdc, TRANSPARENT);
|
2012-05-19 10:03:40 +00:00
|
|
|
SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2016-11-17 22:35:44 +00:00
|
|
|
status = get_clip_hrgn(graphics, &hrgn);
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
if (status == Ok)
|
2016-11-17 22:35:44 +00:00
|
|
|
{
|
2018-01-18 23:17:41 +00:00
|
|
|
ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY);
|
2016-11-17 22:35:44 +00:00
|
|
|
DeleteObject(hrgn);
|
|
|
|
}
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
pt = positions[0];
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &pt, 1);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
get_font_hfont(graphics, font, format, &hfont, matrix);
|
2012-03-05 20:17:42 +00:00
|
|
|
SelectObject(graphics->hdc, hfont);
|
|
|
|
|
|
|
|
SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
ExtTextOutW(graphics->hdc, gdip_round(pt.X), gdip_round(pt.Y), eto_flags, NULL, text, length, NULL);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
RestoreDC(graphics->hdc, save_state);
|
|
|
|
|
|
|
|
DeleteObject(hfont);
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
|
2012-12-11 21:40:15 +00:00
|
|
|
GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
|
|
|
|
GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
|
|
|
|
INT flags, GDIPCONST GpMatrix *matrix)
|
2009-09-03 15:07:25 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
|
|
|
|
GpStatus stat;
|
|
|
|
PointF *real_positions, real_position;
|
|
|
|
POINT *pti;
|
|
|
|
HFONT hfont;
|
|
|
|
HDC hdc;
|
|
|
|
int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
|
|
|
|
DWORD max_glyphsize=0;
|
|
|
|
GLYPHMETRICS glyphmetrics;
|
|
|
|
static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
|
|
|
|
BYTE *glyph_mask;
|
|
|
|
BYTE *text_mask;
|
|
|
|
int text_mask_stride;
|
|
|
|
BYTE *pixel_data;
|
|
|
|
int pixel_data_stride;
|
|
|
|
GpRect pixel_area;
|
|
|
|
UINT ggo_flags = GGO_GRAY8_BITMAP;
|
|
|
|
|
|
|
|
if (length <= 0)
|
|
|
|
return Ok;
|
2009-09-03 15:07:25 +00:00
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!(flags & DriverStringOptionsCmapLookup))
|
|
|
|
ggo_flags |= GGO_GLYPH_INDEX;
|
|
|
|
|
|
|
|
if (flags & unsupported_flags)
|
|
|
|
FIXME("Ignoring flags %x\n", flags & unsupported_flags);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
pti = heap_alloc_zero(sizeof(POINT) * length);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!pti)
|
|
|
|
return OutOfMemory;
|
|
|
|
|
|
|
|
if (flags & DriverStringOptionsRealizedAdvance)
|
|
|
|
{
|
|
|
|
real_position = positions[0];
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, &real_position, 1);
|
|
|
|
round_points(pti, &real_position, 1);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
real_positions = heap_alloc_zero(sizeof(PointF) * length);
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!real_positions)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pti);
|
2012-03-05 20:17:42 +00:00
|
|
|
return OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(real_positions, positions, sizeof(PointF) * length);
|
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdip_transform_points(graphics, WineCoordinateSpaceGdiDevice, CoordinateSpaceWorld, real_positions, length);
|
|
|
|
round_points(pti, real_positions, length);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(real_positions);
|
2012-03-05 20:17:42 +00:00
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
get_font_hfont(graphics, font, format, &hfont, matrix);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
hdc = CreateCompatibleDC(0);
|
|
|
|
SelectObject(hdc, hfont);
|
|
|
|
|
|
|
|
/* Get the boundaries of the text to be drawn */
|
|
|
|
for (i=0; i<length; i++)
|
|
|
|
{
|
|
|
|
DWORD glyphsize;
|
|
|
|
int left, top, right, bottom;
|
|
|
|
|
|
|
|
glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
|
|
|
|
&glyphmetrics, 0, NULL, &identity);
|
|
|
|
|
|
|
|
if (glyphsize == GDI_ERROR)
|
|
|
|
{
|
|
|
|
ERR("GetGlyphOutlineW failed\n");
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pti);
|
2012-03-05 20:17:42 +00:00
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(hfont);
|
|
|
|
return GenericError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (glyphsize > max_glyphsize)
|
|
|
|
max_glyphsize = glyphsize;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (glyphsize != 0)
|
|
|
|
{
|
|
|
|
left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
|
|
|
|
top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
|
|
|
|
right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
|
|
|
|
bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
|
|
|
|
|
|
|
|
if (left < min_x) min_x = left;
|
|
|
|
if (top < min_y) min_y = top;
|
|
|
|
if (right > max_x) max_x = right;
|
|
|
|
if (bottom > max_y) max_y = bottom;
|
|
|
|
}
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
|
|
|
|
{
|
|
|
|
pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
|
|
|
|
pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (max_glyphsize == 0)
|
|
|
|
/* Nothing to draw. */
|
|
|
|
return Ok;
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
glyph_mask = heap_alloc_zero(max_glyphsize);
|
|
|
|
text_mask = heap_alloc_zero((max_x - min_x) * (max_y - min_y));
|
2012-03-05 20:17:42 +00:00
|
|
|
text_mask_stride = max_x - min_x;
|
|
|
|
|
|
|
|
if (!(glyph_mask && text_mask))
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(glyph_mask);
|
|
|
|
heap_free(text_mask);
|
|
|
|
heap_free(pti);
|
2012-03-05 20:17:42 +00:00
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(hfont);
|
|
|
|
return OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate a mask for the text */
|
|
|
|
for (i=0; i<length; i++)
|
|
|
|
{
|
2014-04-22 15:42:03 +00:00
|
|
|
DWORD ret;
|
2012-03-05 20:17:42 +00:00
|
|
|
int left, top, stride;
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
ret = GetGlyphOutlineW(hdc, text[i], ggo_flags,
|
2012-03-05 20:17:42 +00:00
|
|
|
&glyphmetrics, max_glyphsize, glyph_mask, &identity);
|
|
|
|
|
2014-04-22 15:42:03 +00:00
|
|
|
if (ret == GDI_ERROR || ret == 0)
|
|
|
|
continue; /* empty glyph */
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
|
|
|
|
top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
|
|
|
|
stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
|
|
|
|
|
|
|
|
for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
|
|
|
|
{
|
|
|
|
BYTE *glyph_val = glyph_mask + y * stride;
|
|
|
|
BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
|
|
|
|
for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
|
|
|
|
{
|
|
|
|
*text_val = min(64, *text_val + *glyph_val);
|
|
|
|
glyph_val++;
|
|
|
|
text_val++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pti);
|
2012-03-05 20:17:42 +00:00
|
|
|
DeleteDC(hdc);
|
|
|
|
DeleteObject(hfont);
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(glyph_mask);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
/* get the brush data */
|
2015-11-22 10:10:02 +00:00
|
|
|
pixel_data = heap_alloc_zero(4 * (max_x - min_x) * (max_y - min_y));
|
2012-03-05 20:17:42 +00:00
|
|
|
if (!pixel_data)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(text_mask);
|
2012-03-05 20:17:42 +00:00
|
|
|
return OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
pixel_area.X = min_x;
|
|
|
|
pixel_area.Y = min_y;
|
|
|
|
pixel_area.Width = max_x - min_x;
|
|
|
|
pixel_area.Height = max_y - min_y;
|
|
|
|
pixel_data_stride = pixel_area.Width * 4;
|
|
|
|
|
|
|
|
stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
|
|
|
|
if (stat != Ok)
|
|
|
|
{
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(text_mask);
|
|
|
|
heap_free(pixel_data);
|
2012-03-05 20:17:42 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* multiply the brush data by the mask */
|
|
|
|
for (y=0; y<pixel_area.Height; y++)
|
|
|
|
{
|
|
|
|
BYTE *text_val = text_mask + text_mask_stride * y;
|
|
|
|
BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
|
|
|
|
for (x=0; x<pixel_area.Width; x++)
|
|
|
|
{
|
|
|
|
*pixel_val = (*pixel_val) * (*text_val) / 64;
|
|
|
|
text_val++;
|
|
|
|
pixel_val+=4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(text_mask);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_acquire(graphics);
|
|
|
|
|
2012-03-05 20:17:42 +00:00
|
|
|
/* draw the result */
|
|
|
|
stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
|
2015-07-19 13:31:15 +00:00
|
|
|
pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
2018-01-18 23:17:41 +00:00
|
|
|
gdi_transform_release(graphics);
|
|
|
|
|
2015-11-22 10:10:02 +00:00
|
|
|
heap_free(pixel_data);
|
2012-03-05 20:17:42 +00:00
|
|
|
|
|
|
|
return stat;
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
}
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
|
|
|
|
GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format,
|
|
|
|
GDIPCONST GpBrush *brush, GDIPCONST PointF *positions,
|
|
|
|
INT flags, GDIPCONST GpMatrix *matrix)
|
|
|
|
{
|
|
|
|
GpStatus stat = NotImplemented;
|
|
|
|
|
|
|
|
if (length == -1)
|
|
|
|
length = strlenW(text);
|
|
|
|
|
|
|
|
if (graphics->hdc && !graphics->alpha_hdc &&
|
|
|
|
((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
|
|
|
|
brush->bt == BrushTypeSolidColor &&
|
|
|
|
(((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
|
|
|
|
stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format,
|
|
|
|
brush, positions, flags, matrix);
|
|
|
|
if (stat == NotImplemented)
|
|
|
|
stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, format,
|
|
|
|
brush, positions, flags, matrix);
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
|
2009-09-03 15:07:25 +00:00
|
|
|
/*****************************************************************************
|
2012-03-05 20:17:42 +00:00
|
|
|
* GdipDrawDriverString [GDIPLUS.@]
|
2009-09-03 15:07:25 +00:00
|
|
|
*/
|
2012-03-05 20:17:42 +00:00
|
|
|
GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
|
|
|
|
GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
|
|
|
|
GDIPCONST PointF *positions, INT flags,
|
|
|
|
GDIPCONST GpMatrix *matrix )
|
2009-09-03 15:07:25 +00:00
|
|
|
{
|
2012-03-05 20:17:42 +00:00
|
|
|
TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
|
|
|
|
|
|
|
|
if (!graphics || !text || !font || !brush || !positions)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
2012-12-11 21:40:15 +00:00
|
|
|
return draw_driver_string(graphics, text, length, font, NULL,
|
|
|
|
brush, positions, flags, matrix);
|
2009-09-03 15:07:25 +00:00
|
|
|
}
|
2010-03-04 13:34:05 +00:00
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* GdipIsVisibleClipEmpty [GDIPLUS.@]
|
|
|
|
*/
|
|
|
|
GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
|
|
|
|
{
|
|
|
|
GpStatus stat;
|
|
|
|
GpRegion* rgn;
|
|
|
|
|
|
|
|
TRACE("(%p, %p)\n", graphics, res);
|
|
|
|
|
|
|
|
if((stat = GdipCreateRegion(&rgn)) != Ok)
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
stat = GdipIsEmptyRegion(rgn, graphics, res);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
GdipDeleteRegion(rgn);
|
|
|
|
return stat;
|
|
|
|
}
|
2012-05-19 10:03:40 +00:00
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
|
|
|
|
{
|
|
|
|
static int calls;
|
|
|
|
|
|
|
|
TRACE("(%p) stub\n", graphics);
|
|
|
|
|
|
|
|
if(!(calls++))
|
|
|
|
FIXME("not implemented\n");
|
|
|
|
|
|
|
|
return NotImplemented;
|
|
|
|
}
|
2017-09-17 12:34:15 +00:00
|
|
|
|
|
|
|
GpStatus WINGDIPAPI GdipGraphicsSetAbort(GpGraphics *graphics, GdiplusAbort *pabort)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", graphics, pabort);
|
|
|
|
|
|
|
|
if (!graphics)
|
|
|
|
return InvalidParameter;
|
|
|
|
|
|
|
|
if (pabort)
|
|
|
|
FIXME("Abort callback is not supported.\n");
|
|
|
|
|
|
|
|
return Ok;
|
|
|
|
}
|