ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

graphics.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Google (Evan Stade)
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00017  */
00018 
00019 #include <stdarg.h>
00020 #include <math.h>
00021 #include <limits.h>
00022 
00023 #include "windef.h"
00024 #include "winbase.h"
00025 #include "winuser.h"
00026 #include "wingdi.h"
00027 #include "wine/unicode.h"
00028 
00029 #define COBJMACROS
00030 #include "objbase.h"
00031 #include "ocidl.h"
00032 #include "olectl.h"
00033 #include "ole2.h"
00034 
00035 #include "winreg.h"
00036 #include "shlwapi.h"
00037 
00038 #include "gdiplus.h"
00039 #include "gdiplus_private.h"
00040 #include "wine/debug.h"
00041 #include "wine/list.h"
00042 
00043 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
00044 
00045 /* looks-right constants */
00046 #define ANCHOR_WIDTH (2.0)
00047 #define MAX_ITERS (50)
00048 
00049 /* Converts angle (in degrees) to x/y coordinates */
00050 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
00051 {
00052     REAL radAngle, hypotenuse;
00053 
00054     radAngle = deg2rad(angle);
00055     hypotenuse = 50.0; /* arbitrary */
00056 
00057     *x = x_0 + cos(radAngle) * hypotenuse;
00058     *y = y_0 + sin(radAngle) * hypotenuse;
00059 }
00060 
00061 /* Converts from gdiplus path point type to gdi path point type. */
00062 static BYTE convert_path_point_type(BYTE type)
00063 {
00064     BYTE ret;
00065 
00066     switch(type & PathPointTypePathTypeMask){
00067         case PathPointTypeBezier:
00068             ret = PT_BEZIERTO;
00069             break;
00070         case PathPointTypeLine:
00071             ret = PT_LINETO;
00072             break;
00073         case PathPointTypeStart:
00074             ret = PT_MOVETO;
00075             break;
00076         default:
00077             ERR("Bad point type\n");
00078             return 0;
00079     }
00080 
00081     if(type & PathPointTypeCloseSubpath)
00082         ret |= PT_CLOSEFIGURE;
00083 
00084     return ret;
00085 }
00086 
00087 static REAL graphics_res(GpGraphics *graphics)
00088 {
00089     if (graphics->image) return graphics->image->xres;
00090     else return (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
00091 }
00092 
00093 static COLORREF get_gdi_brush_color(const GpBrush *brush)
00094 {
00095     ARGB argb;
00096 
00097     switch (brush->bt)
00098     {
00099         case BrushTypeSolidColor:
00100         {
00101             const GpSolidFill *sf = (const GpSolidFill *)brush;
00102             argb = sf->color;
00103             break;
00104         }
00105         case BrushTypeHatchFill:
00106         {
00107             const GpHatch *hatch = (const GpHatch *)brush;
00108             argb = hatch->forecol;
00109             break;
00110         }
00111         case BrushTypeLinearGradient:
00112         {
00113             const GpLineGradient *line = (const GpLineGradient *)brush;
00114             argb = line->startcolor;
00115             break;
00116         }
00117         case BrushTypePathGradient:
00118         {
00119             const GpPathGradient *grad = (const GpPathGradient *)brush;
00120             argb = grad->centercolor;
00121             break;
00122         }
00123         default:
00124             FIXME("unhandled brush type %d\n", brush->bt);
00125             argb = 0;
00126             break;
00127     }
00128     return ARGB2COLORREF(argb);
00129 }
00130 
00131 static HBITMAP create_hatch_bitmap(const GpHatch *hatch)
00132 {
00133     HBITMAP hbmp;
00134     BITMAPINFOHEADER bmih;
00135     DWORD *bits;
00136     int x, y;
00137 
00138     bmih.biSize = sizeof(bmih);
00139     bmih.biWidth = 8;
00140     bmih.biHeight = 8;
00141     bmih.biPlanes = 1;
00142     bmih.biBitCount = 32;
00143     bmih.biCompression = BI_RGB;
00144     bmih.biSizeImage = 0;
00145 
00146     hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
00147     if (hbmp)
00148     {
00149         const char *hatch_data;
00150 
00151         if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok)
00152         {
00153             for (y = 0; y < 8; y++)
00154             {
00155                 for (x = 0; x < 8; x++)
00156                 {
00157                     if (hatch_data[y] & (0x80 >> x))
00158                         bits[y * 8 + x] = hatch->forecol;
00159                     else
00160                         bits[y * 8 + x] = hatch->backcol;
00161                 }
00162             }
00163         }
00164         else
00165         {
00166             FIXME("Unimplemented hatch style %d\n", hatch->hatchstyle);
00167 
00168             for (y = 0; y < 64; y++)
00169                 bits[y] = hatch->forecol;
00170         }
00171     }
00172 
00173     return hbmp;
00174 }
00175 
00176 static GpStatus create_gdi_logbrush(const GpBrush *brush, LOGBRUSH *lb)
00177 {
00178     switch (brush->bt)
00179     {
00180         case BrushTypeSolidColor:
00181         {
00182             const GpSolidFill *sf = (const GpSolidFill *)brush;
00183             lb->lbStyle = BS_SOLID;
00184             lb->lbColor = ARGB2COLORREF(sf->color);
00185             lb->lbHatch = 0;
00186             return Ok;
00187         }
00188 
00189         case BrushTypeHatchFill:
00190         {
00191             const GpHatch *hatch = (const GpHatch *)brush;
00192             HBITMAP hbmp;
00193 
00194             hbmp = create_hatch_bitmap(hatch);
00195             if (!hbmp) return OutOfMemory;
00196 
00197             lb->lbStyle = BS_PATTERN;
00198             lb->lbColor = 0;
00199             lb->lbHatch = (ULONG_PTR)hbmp;
00200             return Ok;
00201         }
00202 
00203         default:
00204             FIXME("unhandled brush type %d\n", brush->bt);
00205             lb->lbStyle = BS_SOLID;
00206             lb->lbColor = get_gdi_brush_color(brush);
00207             lb->lbHatch = 0;
00208             return Ok;
00209     }
00210 }
00211 
00212 static GpStatus free_gdi_logbrush(LOGBRUSH *lb)
00213 {
00214     switch (lb->lbStyle)
00215     {
00216         case BS_PATTERN:
00217             DeleteObject((HGDIOBJ)(ULONG_PTR)lb->lbHatch);
00218             break;
00219     }
00220     return Ok;
00221 }
00222 
00223 static HBRUSH create_gdi_brush(const GpBrush *brush)
00224 {
00225     LOGBRUSH lb;
00226     HBRUSH gdibrush;
00227 
00228     if (create_gdi_logbrush(brush, &lb) != Ok) return 0;
00229 
00230     gdibrush = CreateBrushIndirect(&lb);
00231     free_gdi_logbrush(&lb);
00232 
00233     return gdibrush;
00234 }
00235 
00236 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
00237 {
00238     LOGBRUSH lb;
00239     HPEN gdipen;
00240     REAL width;
00241     INT save_state, i, numdashes;
00242     GpPointF pt[2];
00243     DWORD dash_array[MAX_DASHLEN];
00244 
00245     save_state = SaveDC(graphics->hdc);
00246 
00247     EndPath(graphics->hdc);
00248 
00249     if(pen->unit == UnitPixel){
00250         width = pen->width;
00251     }
00252     else{
00253         /* Get an estimate for the amount the pen width is affected by the world
00254          * transform. (This is similar to what some of the wine drivers do.) */
00255         pt[0].X = 0.0;
00256         pt[0].Y = 0.0;
00257         pt[1].X = 1.0;
00258         pt[1].Y = 1.0;
00259         GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
00260         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
00261                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
00262 
00263         width *= pen->width * convert_unit(graphics_res(graphics),
00264                               pen->unit == UnitWorld ? graphics->unit : pen->unit);
00265     }
00266 
00267     if(pen->dash == DashStyleCustom){
00268         numdashes = min(pen->numdashes, MAX_DASHLEN);
00269 
00270         TRACE("dashes are: ");
00271         for(i = 0; i < numdashes; i++){
00272             dash_array[i] = roundr(width * pen->dashes[i]);
00273             TRACE("%d, ", dash_array[i]);
00274         }
00275         TRACE("\n and the pen style is %x\n", pen->style);
00276 
00277         create_gdi_logbrush(pen->brush, &lb);
00278         gdipen = ExtCreatePen(pen->style, roundr(width), &lb,
00279                               numdashes, dash_array);
00280         free_gdi_logbrush(&lb);
00281     }
00282     else
00283     {
00284         create_gdi_logbrush(pen->brush, &lb);
00285         gdipen = ExtCreatePen(pen->style, roundr(width), &lb, 0, NULL);
00286         free_gdi_logbrush(&lb);
00287     }
00288 
00289     SelectObject(graphics->hdc, gdipen);
00290 
00291     return save_state;
00292 }
00293 
00294 static void restore_dc(GpGraphics *graphics, INT state)
00295 {
00296     DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
00297     RestoreDC(graphics->hdc, state);
00298 }
00299 
00300 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
00301         GpCoordinateSpace src_space, GpMatrix **matrix);
00302 
00303 /* This helper applies all the changes that the points listed in ptf need in
00304  * order to be drawn on the device context.  In the end, this should include at
00305  * least:
00306  *  -scaling by page unit
00307  *  -applying world transformation
00308  *  -converting from float to int
00309  * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
00310  * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
00311  * gdi to draw, and these functions would irreparably mess with line widths.
00312  */
00313 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
00314     GpPointF *ptf, INT count)
00315 {
00316     REAL unitscale;
00317     GpMatrix *matrix;
00318     int i;
00319 
00320     unitscale = convert_unit(graphics_res(graphics), graphics->unit);
00321 
00322     /* apply page scale */
00323     if(graphics->unit != UnitDisplay)
00324         unitscale *= graphics->scale;
00325 
00326     GdipCloneMatrix(graphics->worldtrans, &matrix);
00327     GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
00328     GdipTransformMatrixPoints(matrix, ptf, count);
00329     GdipDeleteMatrix(matrix);
00330 
00331     for(i = 0; i < count; i++){
00332         pti[i].x = roundr(ptf[i].X);
00333         pti[i].y = roundr(ptf[i].Y);
00334     }
00335 }
00336 
00337 static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height,
00338                             HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height)
00339 {
00340     if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
00341     {
00342         TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
00343 
00344         StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
00345                    hdc, src_x, src_y, src_width, src_height, SRCCOPY);
00346     }
00347     else
00348     {
00349         BLENDFUNCTION bf;
00350 
00351         bf.BlendOp = AC_SRC_OVER;
00352         bf.BlendFlags = 0;
00353         bf.SourceConstantAlpha = 255;
00354         bf.AlphaFormat = AC_SRC_ALPHA;
00355 
00356         GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height,
00357                       hdc, src_x, src_y, src_width, src_height, bf);
00358     }
00359 }
00360 
00361 /* Draw non-premultiplied ARGB data to the given graphics object */
00362 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
00363     const BYTE *src, INT src_width, INT src_height, INT src_stride)
00364 {
00365     if (graphics->image && graphics->image->type == ImageTypeBitmap)
00366     {
00367         GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
00368         INT x, y;
00369 
00370         for (x=0; x<src_width; x++)
00371         {
00372             for (y=0; y<src_height; y++)
00373             {
00374                 ARGB dst_color, src_color;
00375                 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
00376                 src_color = ((ARGB*)(src + src_stride * y))[x];
00377                 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
00378             }
00379         }
00380 
00381         return Ok;
00382     }
00383     else if (graphics->image && graphics->image->type == ImageTypeMetafile)
00384     {
00385         ERR("This should not be used for metafiles; fix caller\n");
00386         return NotImplemented;
00387     }
00388     else
00389     {
00390         HDC hdc;
00391         HBITMAP hbitmap;
00392         BITMAPINFOHEADER bih;
00393         BYTE *temp_bits;
00394 
00395         hdc = CreateCompatibleDC(0);
00396 
00397         bih.biSize = sizeof(BITMAPINFOHEADER);
00398         bih.biWidth = src_width;
00399         bih.biHeight = -src_height;
00400         bih.biPlanes = 1;
00401         bih.biBitCount = 32;
00402         bih.biCompression = BI_RGB;
00403         bih.biSizeImage = 0;
00404         bih.biXPelsPerMeter = 0;
00405         bih.biYPelsPerMeter = 0;
00406         bih.biClrUsed = 0;
00407         bih.biClrImportant = 0;
00408 
00409         hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
00410             (void**)&temp_bits, NULL, 0);
00411 
00412         convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
00413             4 * src_width, src, src_stride);
00414 
00415         SelectObject(hdc, hbitmap);
00416         gdi_alpha_blend(graphics, dst_x, dst_y, src_width, src_height,
00417                         hdc, 0, 0, src_width, src_height);
00418         DeleteDC(hdc);
00419         DeleteObject(hbitmap);
00420 
00421         return Ok;
00422     }
00423 }
00424 
00425 static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
00426     const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion)
00427 {
00428     GpStatus stat=Ok;
00429 
00430     if (graphics->image && graphics->image->type == ImageTypeBitmap)
00431     {
00432         int i, size;
00433         RGNDATA *rgndata;
00434         RECT *rects;
00435 
00436         size = GetRegionData(hregion, 0, NULL);
00437 
00438         rgndata = GdipAlloc(size);
00439         if (!rgndata)
00440             return OutOfMemory;
00441 
00442         GetRegionData(hregion, size, rgndata);
00443 
00444         rects = (RECT*)&rgndata->Buffer;
00445 
00446         for (i=0; stat == Ok && i<rgndata->rdh.nCount; i++)
00447         {
00448             stat = alpha_blend_pixels(graphics, rects[i].left, rects[i].top,
00449                 &src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
00450                 rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
00451                 src_stride);
00452         }
00453 
00454         GdipFree(rgndata);
00455 
00456         return stat;
00457     }
00458     else if (graphics->image && graphics->image->type == ImageTypeMetafile)
00459     {
00460         ERR("This should not be used for metafiles; fix caller\n");
00461         return NotImplemented;
00462     }
00463     else
00464     {
00465         int save;
00466 
00467         save = SaveDC(graphics->hdc);
00468 
00469         ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
00470 
00471         stat = alpha_blend_pixels(graphics, dst_x, dst_y, src, src_width,
00472             src_height, src_stride);
00473 
00474         RestoreDC(graphics->hdc, save);
00475 
00476         return stat;
00477     }
00478 }
00479 
00480 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
00481 {
00482     ARGB result=0;
00483     ARGB i;
00484     INT a1, a2, a3;
00485 
00486     a1 = (start >> 24) & 0xff;
00487     a2 = (end >> 24) & 0xff;
00488 
00489     a3 = (int)(a1*(1.0f - position)+a2*(position));
00490 
00491     result |= a3 << 24;
00492 
00493     for (i=0xff; i<=0xff0000; i = i << 8)
00494         result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i;
00495     return result;
00496 }
00497 
00498 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
00499 {
00500     REAL blendfac;
00501 
00502     /* clamp to between 0.0 and 1.0, using the wrap mode */
00503     if (brush->wrap == WrapModeTile)
00504     {
00505         position = fmodf(position, 1.0f);
00506         if (position < 0.0f) position += 1.0f;
00507     }
00508     else /* WrapModeFlip* */
00509     {
00510         position = fmodf(position, 2.0f);
00511         if (position < 0.0f) position += 2.0f;
00512         if (position > 1.0f) position = 2.0f - position;
00513     }
00514 
00515     if (brush->blendcount == 1)
00516         blendfac = position;
00517     else
00518     {
00519         int i=1;
00520         REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
00521         REAL range;
00522 
00523         /* locate the blend positions surrounding this position */
00524         while (position > brush->blendpos[i])
00525             i++;
00526 
00527         /* interpolate between the blend positions */
00528         left_blendpos = brush->blendpos[i-1];
00529         left_blendfac = brush->blendfac[i-1];
00530         right_blendpos = brush->blendpos[i];
00531         right_blendfac = brush->blendfac[i];
00532         range = right_blendpos - left_blendpos;
00533         blendfac = (left_blendfac * (right_blendpos - position) +
00534                     right_blendfac * (position - left_blendpos)) / range;
00535     }
00536 
00537     if (brush->pblendcount == 0)
00538         return blend_colors(brush->startcolor, brush->endcolor, blendfac);
00539     else
00540     {
00541         int i=1;
00542         ARGB left_blendcolor, right_blendcolor;
00543         REAL left_blendpos, right_blendpos;
00544 
00545         /* locate the blend colors surrounding this position */
00546         while (blendfac > brush->pblendpos[i])
00547             i++;
00548 
00549         /* interpolate between the blend colors */
00550         left_blendpos = brush->pblendpos[i-1];
00551         left_blendcolor = brush->pblendcolor[i-1];
00552         right_blendpos = brush->pblendpos[i];
00553         right_blendcolor = brush->pblendcolor[i];
00554         blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
00555         return blend_colors(left_blendcolor, right_blendcolor, blendfac);
00556     }
00557 }
00558 
00559 static ARGB transform_color(ARGB color, const ColorMatrix *matrix)
00560 {
00561     REAL val[5], res[4];
00562     int i, j;
00563     unsigned char a, r, g, b;
00564 
00565     val[0] = ((color >> 16) & 0xff) / 255.0; /* red */
00566     val[1] = ((color >> 8) & 0xff) / 255.0; /* green */
00567     val[2] = (color & 0xff) / 255.0; /* blue */
00568     val[3] = ((color >> 24) & 0xff) / 255.0; /* alpha */
00569     val[4] = 1.0; /* translation */
00570 
00571     for (i=0; i<4; i++)
00572     {
00573         res[i] = 0.0;
00574 
00575         for (j=0; j<5; j++)
00576             res[i] += matrix->m[j][i] * val[j];
00577     }
00578 
00579     a = min(max(floorf(res[3]*255.0), 0.0), 255.0);
00580     r = min(max(floorf(res[0]*255.0), 0.0), 255.0);
00581     g = min(max(floorf(res[1]*255.0), 0.0), 255.0);
00582     b = min(max(floorf(res[2]*255.0), 0.0), 255.0);
00583 
00584     return (a << 24) | (r << 16) | (g << 8) | b;
00585 }
00586 
00587 static int color_is_gray(ARGB color)
00588 {
00589     unsigned char r, g, b;
00590 
00591     r = (color >> 16) & 0xff;
00592     g = (color >> 8) & 0xff;
00593     b = color & 0xff;
00594 
00595     return (r == g) && (g == b);
00596 }
00597 
00598 static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
00599     UINT width, UINT height, INT stride, ColorAdjustType type)
00600 {
00601     UINT x, y, i;
00602 
00603     if (attributes->colorkeys[type].enabled ||
00604         attributes->colorkeys[ColorAdjustTypeDefault].enabled)
00605     {
00606         const struct color_key *key;
00607         BYTE min_blue, min_green, min_red;
00608         BYTE max_blue, max_green, max_red;
00609 
00610         if (attributes->colorkeys[type].enabled)
00611             key = &attributes->colorkeys[type];
00612         else
00613             key = &attributes->colorkeys[ColorAdjustTypeDefault];
00614 
00615         min_blue = key->low&0xff;
00616         min_green = (key->low>>8)&0xff;
00617         min_red = (key->low>>16)&0xff;
00618 
00619         max_blue = key->high&0xff;
00620         max_green = (key->high>>8)&0xff;
00621         max_red = (key->high>>16)&0xff;
00622 
00623         for (x=0; x<width; x++)
00624             for (y=0; y<height; y++)
00625             {
00626                 ARGB *src_color;
00627                 BYTE blue, green, red;
00628                 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
00629                 blue = *src_color&0xff;
00630                 green = (*src_color>>8)&0xff;
00631                 red = (*src_color>>16)&0xff;
00632                 if (blue >= min_blue && green >= min_green && red >= min_red &&
00633                     blue <= max_blue && green <= max_green && red <= max_red)
00634                     *src_color = 0x00000000;
00635             }
00636     }
00637 
00638     if (attributes->colorremaptables[type].enabled ||
00639         attributes->colorremaptables[ColorAdjustTypeDefault].enabled)
00640     {
00641         const struct color_remap_table *table;
00642 
00643         if (attributes->colorremaptables[type].enabled)
00644             table = &attributes->colorremaptables[type];
00645         else
00646             table = &attributes->colorremaptables[ColorAdjustTypeDefault];
00647 
00648         for (x=0; x<width; x++)
00649             for (y=0; y<height; y++)
00650             {
00651                 ARGB *src_color;
00652                 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
00653                 for (i=0; i<table->mapsize; i++)
00654                 {
00655                     if (*src_color == table->colormap[i].oldColor.Argb)
00656                     {
00657                         *src_color = table->colormap[i].newColor.Argb;
00658                         break;
00659                     }
00660                 }
00661             }
00662     }
00663 
00664     if (attributes->colormatrices[type].enabled ||
00665         attributes->colormatrices[ColorAdjustTypeDefault].enabled)
00666     {
00667         const struct color_matrix *colormatrices;
00668 
00669         if (attributes->colormatrices[type].enabled)
00670             colormatrices = &attributes->colormatrices[type];
00671         else
00672             colormatrices = &attributes->colormatrices[ColorAdjustTypeDefault];
00673 
00674         for (x=0; x<width; x++)
00675             for (y=0; y<height; y++)
00676             {
00677                 ARGB *src_color;
00678                 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
00679 
00680                 if (colormatrices->flags == ColorMatrixFlagsDefault ||
00681                     !color_is_gray(*src_color))
00682                 {
00683                     *src_color = transform_color(*src_color, &colormatrices->colormatrix);
00684                 }
00685                 else if (colormatrices->flags == ColorMatrixFlagsAltGray)
00686                 {
00687                     *src_color = transform_color(*src_color, &colormatrices->graymatrix);
00688                 }
00689             }
00690     }
00691 
00692     if (attributes->gamma_enabled[type] ||
00693         attributes->gamma_enabled[ColorAdjustTypeDefault])
00694     {
00695         REAL gamma;
00696 
00697         if (attributes->gamma_enabled[type])
00698             gamma = attributes->gamma[type];
00699         else
00700             gamma = attributes->gamma[ColorAdjustTypeDefault];
00701 
00702         for (x=0; x<width; x++)
00703             for (y=0; y<height; y++)
00704             {
00705                 ARGB *src_color;
00706                 BYTE blue, green, red;
00707                 src_color = (ARGB*)(data + stride * y + sizeof(ARGB) * x);
00708 
00709                 blue = *src_color&0xff;
00710                 green = (*src_color>>8)&0xff;
00711                 red = (*src_color>>16)&0xff;
00712 
00713                 /* FIXME: We should probably use a table for this. */
00714                 blue = floorf(powf(blue / 255.0, gamma) * 255.0);
00715                 green = floorf(powf(green / 255.0, gamma) * 255.0);
00716                 red = floorf(powf(red / 255.0, gamma) * 255.0);
00717 
00718                 *src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
00719             }
00720     }
00721 }
00722 
00723 /* Given a bitmap and its source rectangle, find the smallest rectangle in the
00724  * bitmap that contains all the pixels we may need to draw it. */
00725 static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
00726     GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
00727     GpRect *rect)
00728 {
00729     INT left, top, right, bottom;
00730 
00731     switch (interpolation)
00732     {
00733     case InterpolationModeHighQualityBilinear:
00734     case InterpolationModeHighQualityBicubic:
00735     /* FIXME: Include a greater range for the prefilter? */
00736     case InterpolationModeBicubic:
00737     case InterpolationModeBilinear:
00738         left = (INT)(floorf(srcx));
00739         top = (INT)(floorf(srcy));
00740         right = (INT)(ceilf(srcx+srcwidth));
00741         bottom = (INT)(ceilf(srcy+srcheight));
00742         break;
00743     case InterpolationModeNearestNeighbor:
00744     default:
00745         left = roundr(srcx);
00746         top = roundr(srcy);
00747         right = roundr(srcx+srcwidth);
00748         bottom = roundr(srcy+srcheight);
00749         break;
00750     }
00751 
00752     if (wrap == WrapModeClamp)
00753     {
00754         if (left < 0)
00755             left = 0;
00756         if (top < 0)
00757             top = 0;
00758         if (right >= bitmap->width)
00759             right = bitmap->width-1;
00760         if (bottom >= bitmap->height)
00761             bottom = bitmap->height-1;
00762     }
00763     else
00764     {
00765         /* In some cases we can make the rectangle smaller here, but the logic
00766          * is hard to get right, and tiling suggests we're likely to use the
00767          * entire source image. */
00768         if (left < 0 || right >= bitmap->width)
00769         {
00770             left = 0;
00771             right = bitmap->width-1;
00772         }
00773 
00774         if (top < 0 || bottom >= bitmap->height)
00775         {
00776             top = 0;
00777             bottom = bitmap->height-1;
00778         }
00779     }
00780 
00781     rect->X = left;
00782     rect->Y = top;
00783     rect->Width = right - left + 1;
00784     rect->Height = bottom - top + 1;
00785 }
00786 
00787 static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
00788     UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
00789 {
00790     if (attributes->wrap == WrapModeClamp)
00791     {
00792         if (x < 0 || y < 0 || x >= width || y >= height)
00793             return attributes->outside_color;
00794     }
00795     else
00796     {
00797         /* Tiling. Make sure co-ordinates are positive as it simplifies the math. */
00798         if (x < 0)
00799             x = width*2 + x % (width * 2);
00800         if (y < 0)
00801             y = height*2 + y % (height * 2);
00802 
00803         if ((attributes->wrap & 1) == 1)
00804         {
00805             /* Flip X */
00806             if ((x / width) % 2 == 0)
00807                 x = x % width;
00808             else
00809                 x = width - 1 - x % width;
00810         }
00811         else
00812             x = x % width;
00813 
00814         if ((attributes->wrap & 2) == 2)
00815         {
00816             /* Flip Y */
00817             if ((y / height) % 2 == 0)
00818                 y = y % height;
00819             else
00820                 y = height - 1 - y % height;
00821         }
00822         else
00823             y = y % height;
00824     }
00825 
00826     if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
00827     {
00828         ERR("out of range pixel requested\n");
00829         return 0xffcd0084;
00830     }
00831 
00832     return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
00833 }
00834 
00835 static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
00836     UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes,
00837     InterpolationMode interpolation)
00838 {
00839     static int fixme;
00840 
00841     switch (interpolation)
00842     {
00843     default:
00844         if (!fixme++)
00845             FIXME("Unimplemented interpolation %i\n", interpolation);
00846         /* fall-through */
00847     case InterpolationModeBilinear:
00848     {
00849         REAL leftxf, topyf;
00850         INT leftx, rightx, topy, bottomy;
00851         ARGB topleft, topright, bottomleft, bottomright;
00852         ARGB top, bottom;
00853         float x_offset;
00854 
00855         leftxf = floorf(point->X);
00856         leftx = (INT)leftxf;
00857         rightx = (INT)ceilf(point->X);
00858         topyf = floorf(point->Y);
00859         topy = (INT)topyf;
00860         bottomy = (INT)ceilf(point->Y);
00861 
00862         if (leftx == rightx && topy == bottomy)
00863             return sample_bitmap_pixel(src_rect, bits, width, height,
00864                 leftx, topy, attributes);
00865 
00866         topleft = sample_bitmap_pixel(src_rect, bits, width, height,
00867             leftx, topy, attributes);
00868         topright = sample_bitmap_pixel(src_rect, bits, width, height,
00869             rightx, topy, attributes);
00870         bottomleft = sample_bitmap_pixel(src_rect, bits, width, height,
00871             leftx, bottomy, attributes);
00872         bottomright = sample_bitmap_pixel(src_rect, bits, width, height,
00873             rightx, bottomy, attributes);
00874 
00875         x_offset = point->X - leftxf;
00876         top = blend_colors(topleft, topright, x_offset);
00877         bottom = blend_colors(bottomleft, bottomright, x_offset);
00878 
00879         return blend_colors(top, bottom, point->Y - topyf);
00880     }
00881     case InterpolationModeNearestNeighbor:
00882         return sample_bitmap_pixel(src_rect, bits, width, height,
00883             roundr(point->X), roundr(point->Y), attributes);
00884     }
00885 }
00886 
00887 static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y)
00888 {
00889     return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X;
00890 }
00891 
00892 static INT brush_can_fill_path(GpBrush *brush)
00893 {
00894     switch (brush->bt)
00895     {
00896     case BrushTypeSolidColor:
00897         return 1;
00898     case BrushTypeHatchFill:
00899     {
00900         GpHatch *hatch = (GpHatch*)brush;
00901         return ((hatch->forecol & 0xff000000) == 0xff000000) &&
00902                ((hatch->backcol & 0xff000000) == 0xff000000);
00903     }
00904     case BrushTypeLinearGradient:
00905     case BrushTypeTextureFill:
00906     /* Gdi32 isn't much help with these, so we should use brush_fill_pixels instead. */
00907     default:
00908         return 0;
00909     }
00910 }
00911 
00912 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
00913 {
00914     switch (brush->bt)
00915     {
00916     case BrushTypeSolidColor:
00917     {
00918         GpSolidFill *fill = (GpSolidFill*)brush;
00919         HBITMAP bmp = ARGB2BMP(fill->color);
00920 
00921         if (bmp)
00922         {
00923             RECT rc;
00924             /* partially transparent fill */
00925 
00926             SelectClipPath(graphics->hdc, RGN_AND);
00927             if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
00928             {
00929                 HDC hdc = CreateCompatibleDC(NULL);
00930 
00931                 if (!hdc) break;
00932 
00933                 SelectObject(hdc, bmp);
00934                 gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
00935                                 hdc, 0, 0, 1, 1);
00936                 DeleteDC(hdc);
00937             }
00938 
00939             DeleteObject(bmp);
00940             break;
00941         }
00942         /* else fall through */
00943     }
00944     default:
00945     {
00946         HBRUSH gdibrush, old_brush;
00947 
00948         gdibrush = create_gdi_brush(brush);
00949         if (!gdibrush) return;
00950 
00951         old_brush = SelectObject(graphics->hdc, gdibrush);
00952         FillPath(graphics->hdc);
00953         SelectObject(graphics->hdc, old_brush);
00954         DeleteObject(gdibrush);
00955         break;
00956     }
00957     }
00958 }
00959 
00960 static INT brush_can_fill_pixels(GpBrush *brush)
00961 {
00962     switch (brush->bt)
00963     {
00964     case BrushTypeSolidColor:
00965     case BrushTypeHatchFill:
00966     case BrushTypeLinearGradient:
00967     case BrushTypeTextureFill:
00968     case BrushTypePathGradient:
00969         return 1;
00970     default:
00971         return 0;
00972     }
00973 }
00974 
00975 static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
00976     DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride)
00977 {
00978     switch (brush->bt)
00979     {
00980     case BrushTypeSolidColor:
00981     {
00982         int x, y;
00983         GpSolidFill *fill = (GpSolidFill*)brush;
00984         for (x=0; x<fill_area->Width; x++)
00985             for (y=0; y<fill_area->Height; y++)
00986                 argb_pixels[x + y*cdwStride] = fill->color;
00987         return Ok;
00988     }
00989     case BrushTypeHatchFill:
00990     {
00991         int x, y;
00992         GpHatch *fill = (GpHatch*)brush;
00993         const char *hatch_data;
00994 
00995         if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok)
00996             return NotImplemented;
00997 
00998         for (x=0; x<fill_area->Width; x++)
00999             for (y=0; y<fill_area->Height; y++)
01000             {
01001                 int hx, hy;
01002 
01003                 /* FIXME: Account for the rendering origin */
01004                 hx = (x + fill_area->X) % 8;
01005                 hy = (y + fill_area->Y) % 8;
01006 
01007                 if ((hatch_data[7-hy] & (0x80 >> hx)) != 0)
01008                     argb_pixels[x + y*cdwStride] = fill->forecol;
01009                 else
01010                     argb_pixels[x + y*cdwStride] = fill->backcol;
01011             }
01012 
01013         return Ok;
01014     }
01015     case BrushTypeLinearGradient:
01016     {
01017         GpLineGradient *fill = (GpLineGradient*)brush;
01018         GpPointF draw_points[3], line_points[3];
01019         GpStatus stat;
01020         static const GpRectF box_1 = { 0.0, 0.0, 1.0, 1.0 };
01021         GpMatrix *world_to_gradient; /* FIXME: Store this in the brush? */
01022         int x, y;
01023 
01024         draw_points[0].X = fill_area->X;
01025         draw_points[0].Y = fill_area->Y;
01026         draw_points[1].X = fill_area->X+1;
01027         draw_points[1].Y = fill_area->Y;
01028         draw_points[2].X = fill_area->X;
01029         draw_points[2].Y = fill_area->Y+1;
01030 
01031         /* Transform the points to a co-ordinate space where X is the point's
01032          * position in the gradient, 0.0 being the start point and 1.0 the
01033          * end point. */
01034         stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
01035             CoordinateSpaceDevice, draw_points, 3);
01036 
01037         if (stat == Ok)
01038         {
01039             line_points[0] = fill->startpoint;
01040             line_points[1] = fill->endpoint;
01041             line_points[2].X = fill->startpoint.X + (fill->startpoint.Y - fill->endpoint.Y);
01042             line_points[2].Y = fill->startpoint.Y + (fill->endpoint.X - fill->startpoint.X);
01043 
01044             stat = GdipCreateMatrix3(&box_1, line_points, &world_to_gradient);
01045         }
01046 
01047         if (stat == Ok)
01048         {
01049             stat = GdipInvertMatrix(world_to_gradient);
01050 
01051             if (stat == Ok)
01052                 stat = GdipTransformMatrixPoints(world_to_gradient, draw_points, 3);
01053 
01054             GdipDeleteMatrix(world_to_gradient);
01055         }
01056 
01057         if (stat == Ok)
01058         {
01059             REAL x_delta = draw_points[1].X - draw_points[0].X;
01060             REAL y_delta = draw_points[2].X - draw_points[0].X;
01061 
01062             for (y=0; y<fill_area->Height; y++)
01063             {
01064                 for (x=0; x<fill_area->Width; x++)
01065                 {
01066                     REAL pos = draw_points[0].X + x * x_delta + y * y_delta;
01067 
01068                     argb_pixels[x + y*cdwStride] = blend_line_gradient(fill, pos);
01069                 }
01070             }
01071         }
01072 
01073         return stat;
01074     }
01075     case BrushTypeTextureFill:
01076     {
01077         GpTexture *fill = (GpTexture*)brush;
01078         GpPointF draw_points[3];
01079         GpStatus stat;
01080         GpMatrix *world_to_texture;
01081         int x, y;
01082         GpBitmap *bitmap;
01083         int src_stride;
01084         GpRect src_area;
01085 
01086         if (fill->image->type != ImageTypeBitmap)
01087         {
01088             FIXME("metafile texture brushes not implemented\n");
01089             return NotImplemented;
01090         }
01091 
01092         bitmap = (GpBitmap*)fill->image;
01093         src_stride = sizeof(ARGB) * bitmap->width;
01094 
01095         src_area.X = src_area.Y = 0;
01096         src_area.Width = bitmap->width;
01097         src_area.Height = bitmap->height;
01098 
01099         draw_points[0].X = fill_area->X;
01100         draw_points[0].Y = fill_area->Y;
01101         draw_points[1].X = fill_area->X+1;
01102         draw_points[1].Y = fill_area->Y;
01103         draw_points[2].X = fill_area->X;
01104         draw_points[2].Y = fill_area->Y+1;
01105 
01106         /* Transform the points to the co-ordinate space of the bitmap. */
01107         stat = GdipTransformPoints(graphics, CoordinateSpaceWorld,
01108             CoordinateSpaceDevice, draw_points, 3);
01109 
01110         if (stat == Ok)
01111         {
01112             stat = GdipCloneMatrix(fill->transform, &world_to_texture);
01113         }
01114 
01115         if (stat == Ok)
01116         {
01117             stat = GdipInvertMatrix(world_to_texture);
01118 
01119             if (stat == Ok)
01120                 stat = GdipTransformMatrixPoints(world_to_texture, draw_points, 3);
01121 
01122             GdipDeleteMatrix(world_to_texture);
01123         }
01124 
01125         if (stat == Ok && !fill->bitmap_bits)
01126         {
01127             BitmapData lockeddata;
01128 
01129             fill->bitmap_bits = GdipAlloc(sizeof(ARGB) * bitmap->width * bitmap->height);
01130             if (!fill->bitmap_bits)
01131                 stat = OutOfMemory;
01132 
01133             if (stat == Ok)
01134             {
01135                 lockeddata.Width = bitmap->width;
01136                 lockeddata.Height = bitmap->height;
01137                 lockeddata.Stride = src_stride;
01138                 lockeddata.PixelFormat = PixelFormat32bppARGB;
01139                 lockeddata.Scan0 = fill->bitmap_bits;
01140 
01141                 stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
01142                     PixelFormat32bppARGB, &lockeddata);
01143             }
01144 
01145             if (stat == Ok)
01146                 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
01147 
01148             if (stat == Ok)
01149                 apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
01150                     bitmap->width, bitmap->height,
01151                     src_stride, ColorAdjustTypeBitmap);
01152 
01153             if (stat != Ok)
01154             {
01155                 GdipFree(fill->bitmap_bits);
01156                 fill->bitmap_bits = NULL;
01157             }
01158         }
01159 
01160         if (stat == Ok)
01161         {
01162             REAL x_dx = draw_points[1].X - draw_points[0].X;
01163             REAL x_dy = draw_points[1].Y - draw_points[0].Y;
01164             REAL y_dx = draw_points[2].X - draw_points[0].X;
01165             REAL y_dy = draw_points[2].Y - draw_points[0].Y;
01166 
01167             for (y=0; y<fill_area->Height; y++)
01168             {
01169                 for (x=0; x<fill_area->Width; x++)
01170                 {
01171                     GpPointF point;
01172                     point.X = draw_points[0].X + x * x_dx + y * y_dx;
01173                     point.Y = draw_points[0].Y + y * x_dy + y * y_dy;
01174 
01175                     argb_pixels[x + y*cdwStride] = resample_bitmap_pixel(
01176                         &src_area, fill->bitmap_bits, bitmap->width, bitmap->height,
01177                         &point, fill->imageattributes, graphics->interpolation);
01178                 }
01179             }
01180         }
01181 
01182         return stat;
01183     }
01184     case BrushTypePathGradient:
01185     {
01186         GpPathGradient *fill = (GpPathGradient*)brush;
01187         GpPath *flat_path;
01188         GpMatrix *world_to_device;
01189         GpStatus stat;
01190         int i, figure_start=0;
01191         GpPointF start_point, end_point, center_point;
01192         BYTE type;
01193         REAL min_yf, max_yf, line1_xf, line2_xf;
01194         INT min_y, max_y, min_x, max_x;
01195         INT x, y;
01196         ARGB outer_color;
01197         static int transform_fixme_once;
01198 
01199         if (fill->focus.X != 0.0 || fill->focus.Y != 0.0)
01200         {
01201             static int once;
01202             if (!once++)
01203                 FIXME("path gradient focus not implemented\n");
01204         }
01205 
01206         if (fill->gamma)
01207         {
01208             static int once;
01209             if (!once++)
01210                 FIXME("path gradient gamma correction not implemented\n");
01211         }
01212 
01213         if (fill->blendcount)
01214         {
01215             static int once;
01216             if (!once++)
01217                 FIXME("path gradient blend not implemented\n");
01218         }
01219 
01220         if (fill->pblendcount)
01221         {
01222             static int once;
01223             if (!once++)
01224                 FIXME("path gradient preset blend not implemented\n");
01225         }
01226 
01227         if (!transform_fixme_once)
01228         {
01229             BOOL is_identity=TRUE;
01230             GdipIsMatrixIdentity(fill->transform, &is_identity);
01231             if (!is_identity)
01232             {
01233                 FIXME("path gradient transform not implemented\n");
01234                 transform_fixme_once = 1;
01235             }
01236         }
01237 
01238         stat = GdipClonePath(fill->path, &flat_path);
01239 
01240         if (stat != Ok)
01241             return stat;
01242 
01243         stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
01244             CoordinateSpaceWorld, &world_to_device);
01245         if (stat == Ok)
01246         {
01247             stat = GdipTransformPath(flat_path, world_to_device);
01248 
01249             if (stat == Ok)
01250             {
01251                 center_point = fill->center;
01252                 stat = GdipTransformMatrixPoints(world_to_device, &center_point, 1);
01253             }
01254 
01255             if (stat == Ok)
01256                 stat = GdipFlattenPath(flat_path, NULL, 0.5);
01257 
01258             GdipDeleteMatrix(world_to_device);
01259         }
01260 
01261         if (stat != Ok)
01262         {
01263             GdipDeletePath(flat_path);
01264             return stat;
01265         }
01266 
01267         for (i=0; i<flat_path->pathdata.Count; i++)
01268         {
01269             int start_center_line=0, end_center_line=0;
01270             int seen_start=0, seen_end=0, seen_center=0;
01271             REAL center_distance;
01272             ARGB start_color, end_color;
01273             REAL dy, dx;
01274 
01275             type = flat_path->pathdata.Types[i];
01276 
01277             if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
01278                 figure_start = i;
01279 
01280             start_point = flat_path->pathdata.Points[i];
01281 
01282             start_color = fill->surroundcolors[min(i, fill->surroundcolorcount-1)];
01283 
01284             if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath || i+1 >= flat_path->pathdata.Count)
01285             {
01286                 end_point = flat_path->pathdata.Points[figure_start];
01287                 end_color = fill->surroundcolors[min(figure_start, fill->surroundcolorcount-1)];
01288             }
01289             else if ((flat_path->pathdata.Types[i+1] & PathPointTypePathTypeMask) == PathPointTypeLine)
01290             {
01291                 end_point = flat_path->pathdata.Points[i+1];
01292                 end_color = fill->surroundcolors[min(i+1, fill->surroundcolorcount-1)];
01293             }
01294             else
01295                 continue;
01296 
01297             outer_color = start_color;
01298 
01299             min_yf = center_point.Y;
01300             if (min_yf > start_point.Y) min_yf = start_point.Y;
01301             if (min_yf > end_point.Y) min_yf = end_point.Y;
01302 
01303             if (min_yf < fill_area->Y)
01304                 min_y = fill_area->Y;
01305             else
01306                 min_y = (INT)ceil(min_yf);
01307 
01308             max_yf = center_point.Y;
01309             if (max_yf < start_point.Y) max_yf = start_point.Y;
01310             if (max_yf < end_point.Y) max_yf = end_point.Y;
01311 
01312             if (max_yf > fill_area->Y + fill_area->Height)
01313                 max_y = fill_area->Y + fill_area->Height;
01314             else
01315                 max_y = (INT)ceil(max_yf);
01316 
01317             dy = end_point.Y - start_point.Y;
01318             dx = end_point.X - start_point.X;
01319 
01320             /* This is proportional to the distance from start-end line to center point. */
01321             center_distance = dy * (start_point.X - center_point.X) +
01322                 dx * (center_point.Y - start_point.Y);
01323 
01324             for (y=min_y; y<max_y; y++)
01325             {
01326                 REAL yf = (REAL)y;
01327 
01328                 if (!seen_start && yf >= start_point.Y)
01329                 {
01330                     seen_start = 1;
01331                     start_center_line ^= 1;
01332                 }
01333                 if (!seen_end && yf >= end_point.Y)
01334                 {
01335                     seen_end = 1;
01336                     end_center_line ^= 1;
01337                 }
01338                 if (!seen_center && yf >= center_point.Y)
01339                 {
01340                     seen_center = 1;
01341                     start_center_line ^= 1;
01342                     end_center_line ^= 1;
01343                 }
01344 
01345                 if (start_center_line)
01346                     line1_xf = intersect_line_scanline(&start_point, &center_point, yf);
01347                 else
01348                     line1_xf = intersect_line_scanline(&start_point, &end_point, yf);
01349 
01350                 if (end_center_line)
01351                     line2_xf = intersect_line_scanline(&end_point, &center_point, yf);
01352                 else
01353                     line2_xf = intersect_line_scanline(&start_point, &end_point, yf);
01354 
01355                 if (line1_xf < line2_xf)
01356                 {
01357                     min_x = (INT)ceil(line1_xf);
01358                     max_x = (INT)ceil(line2_xf);
01359                 }
01360                 else
01361                 {
01362                     min_x = (INT)ceil(line2_xf);
01363                     max_x = (INT)ceil(line1_xf);
01364                 }
01365 
01366                 if (min_x < fill_area->X)
01367                     min_x = fill_area->X;
01368                 if (max_x > fill_area->X + fill_area->Width)
01369                     max_x = fill_area->X + fill_area->Width;
01370 
01371                 for (x=min_x; x<max_x; x++)
01372                 {
01373                     REAL xf = (REAL)x;
01374                     REAL distance;
01375 
01376                     if (start_color != end_color)
01377                     {
01378                         REAL blend_amount, pdy, pdx;
01379                         pdy = yf - center_point.Y;
01380                         pdx = xf - center_point.X;
01381                         blend_amount = ( (center_point.Y - start_point.Y) * pdx + (start_point.X - center_point.X) * pdy ) / ( dy * pdx - dx * pdy );
01382                         outer_color = blend_colors(start_color, end_color, blend_amount);
01383                     }
01384 
01385                     distance = (end_point.Y - start_point.Y) * (start_point.X - xf) +
01386                         (end_point.X - start_point.X) * (yf - start_point.Y);
01387 
01388                     distance = distance / center_distance;
01389 
01390                     argb_pixels[(x-fill_area->X) + (y-fill_area->Y)*cdwStride] =
01391                         blend_colors(outer_color, fill->centercolor, distance);
01392                 }
01393             }
01394         }
01395 
01396         GdipDeletePath(flat_path);
01397         return stat;
01398     }
01399     default:
01400         return NotImplemented;
01401     }
01402 }
01403 
01404 /* GdipDrawPie/GdipFillPie helper function */
01405 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
01406     REAL height, REAL startAngle, REAL sweepAngle)
01407 {
01408     GpPointF ptf[4];
01409     POINT pti[4];
01410 
01411     ptf[0].X = x;
01412     ptf[0].Y = y;
01413     ptf[1].X = x + width;
01414     ptf[1].Y = y + height;
01415 
01416     deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
01417     deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
01418 
01419     transform_and_round_points(graphics, pti, ptf, 4);
01420 
01421     Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
01422         pti[2].y, pti[3].x, pti[3].y);
01423 }
01424 
01425 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
01426  * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
01427  * should not be called on an hdc that has a path you care about. */
01428 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
01429     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
01430 {
01431     HGDIOBJ oldbrush = NULL, oldpen = NULL;
01432     GpMatrix *matrix = NULL;
01433     HBRUSH brush = NULL;
01434     HPEN pen = NULL;
01435     PointF ptf[4], *custptf = NULL;
01436     POINT pt[4], *custpt = NULL;
01437     BYTE *tp = NULL;
01438     REAL theta, dsmall, dbig, dx, dy = 0.0;
01439     INT i, count;
01440     LOGBRUSH lb;
01441     BOOL customstroke;
01442 
01443     if((x1 == x2) && (y1 == y2))
01444         return;
01445 
01446     theta = gdiplus_atan2(y2 - y1, x2 - x1);
01447 
01448     customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
01449     if(!customstroke){
01450         brush = CreateSolidBrush(color);
01451         lb.lbStyle = BS_SOLID;
01452         lb.lbColor = color;
01453         lb.lbHatch = 0;
01454         pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
01455                            PS_JOIN_MITER, 1, &lb, 0,
01456                            NULL);
01457         oldbrush = SelectObject(graphics->hdc, brush);
01458         oldpen = SelectObject(graphics->hdc, pen);
01459     }
01460 
01461     switch(cap){
01462         case LineCapFlat:
01463             break;
01464         case LineCapSquare:
01465         case LineCapSquareAnchor:
01466         case LineCapDiamondAnchor:
01467             size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
01468             if(cap == LineCapDiamondAnchor){
01469                 dsmall = cos(theta + M_PI_2) * size;
01470                 dbig = sin(theta + M_PI_2) * size;
01471             }
01472             else{
01473                 dsmall = cos(theta + M_PI_4) * size;
01474                 dbig = sin(theta + M_PI_4) * size;
01475             }
01476 
01477             ptf[0].X = x2 - dsmall;
01478             ptf[1].X = x2 + dbig;
01479 
01480             ptf[0].Y = y2 - dbig;
01481             ptf[3].Y = y2 + dsmall;
01482 
01483             ptf[1].Y = y2 - dsmall;
01484             ptf[2].Y = y2 + dbig;
01485 
01486             ptf[3].X = x2 - dbig;
01487             ptf[2].X = x2 + dsmall;
01488 
01489             transform_and_round_points(graphics, pt, ptf, 4);
01490             Polygon(graphics->hdc, pt, 4);
01491 
01492             break;
01493         case LineCapArrowAnchor:
01494             size = size * 4.0 / sqrt(3.0);
01495 
01496             dx = cos(M_PI / 6.0 + theta) * size;
01497             dy = sin(M_PI / 6.0 + theta) * size;
01498 
01499             ptf[0].X = x2 - dx;
01500             ptf[0].Y = y2 - dy;
01501 
01502             dx = cos(- M_PI / 6.0 + theta) * size;
01503             dy = sin(- M_PI / 6.0 + theta) * size;
01504 
01505             ptf[1].X = x2 - dx;
01506             ptf[1].Y = y2 - dy;
01507 
01508             ptf[2].X = x2;
01509             ptf[2].Y = y2;
01510 
01511             transform_and_round_points(graphics, pt, ptf, 3);
01512             Polygon(graphics->hdc, pt, 3);
01513 
01514             break;
01515         case LineCapRoundAnchor:
01516             dx = dy = ANCHOR_WIDTH * size / 2.0;
01517 
01518             ptf[0].X = x2 - dx;
01519             ptf[0].Y = y2 - dy;
01520             ptf[1].X = x2 + dx;
01521             ptf[1].Y = y2 + dy;
01522 
01523             transform_and_round_points(graphics, pt, ptf, 2);
01524             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
01525 
01526             break;
01527         case LineCapTriangle:
01528             size = size / 2.0;
01529             dx = cos(M_PI_2 + theta) * size;
01530             dy = sin(M_PI_2 + theta) * size;
01531 
01532             ptf[0].X = x2 - dx;
01533             ptf[0].Y = y2 - dy;
01534             ptf[1].X = x2 + dx;
01535             ptf[1].Y = y2 + dy;
01536 
01537             dx = cos(theta) * size;
01538             dy = sin(theta) * size;
01539 
01540             ptf[2].X = x2 + dx;
01541             ptf[2].Y = y2 + dy;
01542 
01543             transform_and_round_points(graphics, pt, ptf, 3);
01544             Polygon(graphics->hdc, pt, 3);
01545 
01546             break;
01547         case LineCapRound:
01548             dx = dy = size / 2.0;
01549 
01550             ptf[0].X = x2 - dx;
01551             ptf[0].Y = y2 - dy;
01552             ptf[1].X = x2 + dx;
01553             ptf[1].Y = y2 + dy;
01554 
01555             dx = -cos(M_PI_2 + theta) * size;
01556             dy = -sin(M_PI_2 + theta) * size;
01557 
01558             ptf[2].X = x2 - dx;
01559             ptf[2].Y = y2 - dy;
01560             ptf[3].X = x2 + dx;
01561             ptf[3].Y = y2 + dy;
01562 
01563             transform_and_round_points(graphics, pt, ptf, 4);
01564             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
01565                 pt[2].y, pt[3].x, pt[3].y);
01566 
01567             break;
01568         case LineCapCustom:
01569             if(!custom)
01570                 break;
01571 
01572             count = custom->pathdata.Count;
01573             custptf = GdipAlloc(count * sizeof(PointF));
01574             custpt = GdipAlloc(count * sizeof(POINT));
01575             tp = GdipAlloc(count);
01576 
01577             if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
01578                 goto custend;
01579 
01580             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
01581 
01582             GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
01583             GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
01584                              MatrixOrderAppend);
01585             GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
01586             GdipTransformMatrixPoints(matrix, custptf, count);
01587 
01588             transform_and_round_points(graphics, custpt, custptf, count);
01589 
01590             for(i = 0; i < count; i++)
01591                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
01592 
01593             if(custom->fill){
01594                 BeginPath(graphics->hdc);
01595                 PolyDraw(graphics->hdc, custpt, tp, count);
01596                 EndPath(graphics->hdc);
01597                 StrokeAndFillPath(graphics->hdc);
01598             }
01599             else
01600                 PolyDraw(graphics->hdc, custpt, tp, count);
01601 
01602 custend:
01603             GdipFree(custptf);
01604             GdipFree(custpt);
01605             GdipFree(tp);
01606             GdipDeleteMatrix(matrix);
01607             break;
01608         default:
01609             break;
01610     }
01611 
01612     if(!customstroke){
01613         SelectObject(graphics->hdc, oldbrush);
01614         SelectObject(graphics->hdc, oldpen);
01615         DeleteObject(brush);
01616         DeleteObject(pen);
01617     }
01618 }
01619 
01620 /* Shortens the line by the given percent by changing x2, y2.
01621  * If percent is > 1.0 then the line will change direction.
01622  * If percent is negative it can lengthen the line. */
01623 static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
01624 {
01625     REAL dist, theta, dx, dy;
01626 
01627     if((y1 == *y2) && (x1 == *x2))
01628         return;
01629 
01630     dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
01631     theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
01632     dx = cos(theta) * dist;
01633     dy = sin(theta) * dist;
01634 
01635     *x2 = *x2 + dx;
01636     *y2 = *y2 + dy;
01637 }
01638 
01639 /* Shortens the line by the given amount by changing x2, y2.
01640  * If the amount is greater than the distance, the line will become length 0.
01641  * If the amount is negative, it can lengthen the line. */
01642 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
01643 {
01644     REAL dx, dy, percent;
01645 
01646     dx = *x2 - x1;
01647     dy = *y2 - y1;
01648     if(dx == 0 && dy == 0)
01649         return;
01650 
01651     percent = amt / sqrt(dx * dx + dy * dy);
01652     if(percent >= 1.0){
01653         *x2 = x1;
01654         *y2 = y1;
01655         return;
01656     }
01657 
01658     shorten_line_percent(x1, y1, x2, y2, percent);
01659 }
01660 
01661 /* Draws lines between the given points, and if caps is true then draws an endcap
01662  * at the end of the last line. */
01663 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
01664     GDIPCONST GpPointF * pt, INT count, BOOL caps)
01665 {
01666     POINT *pti = NULL;
01667     GpPointF *ptcopy = NULL;
01668     GpStatus status = GenericError;
01669 
01670     if(!count)
01671         return Ok;
01672 
01673     pti = GdipAlloc(count * sizeof(POINT));
01674     ptcopy = GdipAlloc(count * sizeof(GpPointF));
01675 
01676     if(!pti || !ptcopy){
01677         status = OutOfMemory;
01678         goto end;
01679     }
01680 
01681     memcpy(ptcopy, pt, count * sizeof(GpPointF));
01682 
01683     if(caps){
01684         if(pen->endcap == LineCapArrowAnchor)
01685             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
01686                              &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
01687         else if((pen->endcap == LineCapCustom) && pen->customend)
01688             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
01689                              &ptcopy[count-1].X, &ptcopy[count-1].Y,
01690                              pen->customend->inset * pen->width);
01691 
01692         if(pen->startcap == LineCapArrowAnchor)
01693             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
01694                              &ptcopy[0].X, &ptcopy[0].Y, pen->width);
01695         else if((pen->startcap == LineCapCustom) && pen->customstart)
01696             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
01697                              &ptcopy[0].X, &ptcopy[0].Y,
01698                              pen->customstart->inset * pen->width);
01699 
01700         draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
01701                  pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
01702         draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
01703                          pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
01704     }
01705 
01706     transform_and_round_points(graphics, pti, ptcopy, count);
01707 
01708     if(Polyline(graphics->hdc, pti, count))
01709         status = Ok;
01710 
01711 end:
01712     GdipFree(pti);
01713     GdipFree(ptcopy);
01714 
01715     return status;
01716 }
01717 
01718 /* Conducts a linear search to find the bezier points that will back off
01719  * the endpoint of the curve by a distance of amt. Linear search works
01720  * better than binary in this case because there are multiple solutions,
01721  * and binary searches often find a bad one. I don't think this is what
01722  * Windows does but short of rendering the bezier without GDI's help it's
01723  * the best we can do. If rev then work from the start of the passed points
01724  * instead of the end. */
01725 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
01726 {
01727     GpPointF origpt[4];
01728     REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
01729     INT i, first = 0, second = 1, third = 2, fourth = 3;
01730 
01731     if(rev){
01732         first = 3;
01733         second = 2;
01734         third = 1;
01735         fourth = 0;
01736     }
01737 
01738     origx = pt[fourth].X;
01739     origy = pt[fourth].Y;
01740     memcpy(origpt, pt, sizeof(GpPointF) * 4);
01741 
01742     for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
01743         /* reset bezier points to original values */
01744         memcpy(pt, origpt, sizeof(GpPointF) * 4);
01745         /* Perform magic on bezier points. Order is important here.*/
01746         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
01747         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
01748         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
01749         shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
01750         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
01751         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
01752 
01753         dx = pt[fourth].X - origx;
01754         dy = pt[fourth].Y - origy;
01755 
01756         diff = sqrt(dx * dx + dy * dy);
01757         percent += 0.0005 * amt;
01758     }
01759 }
01760 
01761 /* Draws bezier curves between given points, and if caps is true then draws an
01762  * endcap at the end of the last line. */
01763 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
01764     GDIPCONST GpPointF * pt, INT count, BOOL caps)
01765 {
01766     POINT *pti;
01767     GpPointF *ptcopy;
01768     GpStatus status = GenericError;
01769 
01770     if(!count)
01771         return Ok;
01772 
01773     pti = GdipAlloc(count * sizeof(POINT));
01774     ptcopy = GdipAlloc(count * sizeof(GpPointF));
01775 
01776     if(!pti || !ptcopy){
01777         status = OutOfMemory;
01778         goto end;
01779     }
01780 
01781     memcpy(ptcopy, pt, count * sizeof(GpPointF));
01782 
01783     if(caps){
01784         if(pen->endcap == LineCapArrowAnchor)
01785             shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
01786         else if((pen->endcap == LineCapCustom) && pen->customend)
01787             shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
01788                                FALSE);
01789 
01790         if(pen->startcap == LineCapArrowAnchor)
01791             shorten_bezier_amt(ptcopy, pen->width, TRUE);
01792         else if((pen->startcap == LineCapCustom) && pen->customstart)
01793             shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
01794 
01795         /* the direction of the line cap is parallel to the direction at the
01796          * end of the bezier (which, if it has been shortened, is not the same
01797          * as the direction from pt[count-2] to pt[count-1]) */
01798         draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
01799             pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
01800             pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
01801             pt[count - 1].X, pt[count - 1].Y);
01802 
01803         draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
01804             pt[0].X - (ptcopy[0].X - ptcopy[1].X),
01805             pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
01806     }
01807 
01808     transform_and_round_points(graphics, pti, ptcopy, count);
01809 
01810     PolyBezier(graphics->hdc, pti, count);
01811 
01812     status = Ok;
01813 
01814 end:
01815     GdipFree(pti);
01816     GdipFree(ptcopy);
01817 
01818     return status;
01819 }
01820 
01821 /* Draws a combination of bezier curves and lines between points. */
01822 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
01823     GDIPCONST BYTE * types, INT count, BOOL caps)
01824 {
01825     POINT *pti = GdipAlloc(count * sizeof(POINT));
01826     BYTE *tp = GdipAlloc(count);
01827     GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
01828     INT i, j;
01829     GpStatus status = GenericError;
01830 
01831     if(!count){
01832         status = Ok;
01833         goto end;
01834     }
01835     if(!pti || !tp || !ptcopy){
01836         status = OutOfMemory;
01837         goto end;
01838     }
01839 
01840     for(i = 1; i < count; i++){
01841         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
01842             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
01843                 || !(types[i + 1] & PathPointTypeBezier)){
01844                 ERR("Bad bezier points\n");
01845                 goto end;
01846             }
01847             i += 2;
01848         }
01849     }
01850 
01851     memcpy(ptcopy, pt, count * sizeof(GpPointF));
01852 
01853     /* If we are drawing caps, go through the points and adjust them accordingly,
01854      * and draw the caps. */
01855     if(caps){
01856         switch(types[count - 1] & PathPointTypePathTypeMask){
01857             case PathPointTypeBezier:
01858                 if(pen->endcap == LineCapArrowAnchor)
01859                     shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
01860                 else if((pen->endcap == LineCapCustom) && pen->customend)
01861                     shorten_bezier_amt(&ptcopy[count - 4],
01862                                        pen->width * pen->customend->inset, FALSE);
01863 
01864                 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
01865                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
01866                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
01867                     pt[count - 1].X, pt[count - 1].Y);
01868 
01869                 break;
01870             case PathPointTypeLine:
01871                 if(pen->endcap == LineCapArrowAnchor)
01872                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
01873                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
01874                                      pen->width);
01875                 else if((pen->endcap == LineCapCustom) && pen->customend)
01876                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
01877                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
01878                                      pen->customend->inset * pen->width);
01879 
01880                 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->endcap, pen->width, pen->customend,
01881                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
01882                          pt[count - 1].Y);
01883 
01884                 break;
01885             default:
01886                 ERR("Bad path last point\n");
01887                 goto end;
01888         }
01889 
01890         /* Find start of points */
01891         for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
01892             == PathPointTypeStart); j++);
01893 
01894         switch(types[j] & PathPointTypePathTypeMask){
01895             case PathPointTypeBezier:
01896                 if(pen->startcap == LineCapArrowAnchor)
01897                     shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
01898                 else if((pen->startcap == LineCapCustom) && pen->customstart)
01899                     shorten_bezier_amt(&ptcopy[j - 1],
01900                                        pen->width * pen->customstart->inset, TRUE);
01901 
01902                 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
01903                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
01904                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
01905                     pt[j - 1].X, pt[j - 1].Y);
01906 
01907                 break;
01908             case PathPointTypeLine:
01909                 if(pen->startcap == LineCapArrowAnchor)
01910                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
01911                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
01912                                      pen->width);
01913                 else if((pen->startcap == LineCapCustom) && pen->customstart)
01914                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
01915                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
01916                                      pen->customstart->inset * pen->width);
01917 
01918                 draw_cap(graphics, get_gdi_brush_color(pen->brush), pen->startcap, pen->width, pen->customstart,
01919                          pt[j].X, pt[j].Y, pt[j - 1].X,
01920                          pt[j - 1].Y);
01921 
01922                 break;
01923             default:
01924                 ERR("Bad path points\n");
01925                 goto end;
01926         }
01927     }
01928 
01929     transform_and_round_points(graphics, pti, ptcopy, count);
01930 
01931     for(i = 0; i < count; i++){
01932         tp[i] = convert_path_point_type(types[i]);
01933     }
01934 
01935     PolyDraw(graphics->hdc, pti, tp, count);
01936 
01937     status = Ok;
01938 
01939 end:
01940     GdipFree(pti);
01941     GdipFree(ptcopy);
01942     GdipFree(tp);
01943 
01944     return status;
01945 }
01946 
01947 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
01948 {
01949     GpStatus result;
01950 
01951     BeginPath(graphics->hdc);
01952     result = draw_poly(graphics, NULL, path->pathdata.Points,
01953                        path->pathdata.Types, path->pathdata.Count, FALSE);
01954     EndPath(graphics->hdc);
01955     return result;
01956 }
01957 
01958 typedef struct _GraphicsContainerItem {
01959     struct list entry;
01960     GraphicsContainer contid;
01961 
01962     SmoothingMode smoothing;
01963     CompositingQuality compqual;
01964     InterpolationMode interpolation;
01965     CompositingMode compmode;
01966     TextRenderingHint texthint;
01967     REAL scale;
01968     GpUnit unit;
01969     PixelOffsetMode pixeloffset;
01970     UINT textcontrast;
01971     GpMatrix* worldtrans;
01972     GpRegion* clip;
01973     INT origin_x, origin_y;
01974 } GraphicsContainerItem;
01975 
01976 static GpStatus init_container(GraphicsContainerItem** container,
01977         GDIPCONST GpGraphics* graphics){
01978     GpStatus sts;
01979 
01980     *container = GdipAlloc(sizeof(GraphicsContainerItem));
01981     if(!(*container))
01982         return OutOfMemory;
01983 
01984     (*container)->contid = graphics->contid + 1;
01985 
01986     (*container)->smoothing = graphics->smoothing;
01987     (*container)->compqual = graphics->compqual;
01988     (*container)->interpolation = graphics->interpolation;
01989     (*container)->compmode = graphics->compmode;
01990     (*container)->texthint = graphics->texthint;
01991     (*container)->scale = graphics->scale;
01992     (*container)->unit = graphics->unit;
01993     (*container)->textcontrast = graphics->textcontrast;
01994     (*container)->pixeloffset = graphics->pixeloffset;
01995     (*container)->origin_x = graphics->origin_x;
01996     (*container)->origin_y = graphics->origin_y;
01997 
01998     sts = GdipCloneMatrix(graphics->worldtrans, &(*container)->worldtrans);
01999     if(sts != Ok){
02000         GdipFree(*container);
02001         *container = NULL;
02002         return sts;
02003     }
02004 
02005     sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
02006     if(sts != Ok){
02007         GdipDeleteMatrix((*container)->worldtrans);
02008         GdipFree(*container);
02009         *container = NULL;
02010         return sts;
02011     }
02012 
02013     return Ok;
02014 }
02015 
02016 static void delete_container(GraphicsContainerItem* container){
02017     GdipDeleteMatrix(container->worldtrans);
02018     GdipDeleteRegion(container->clip);
02019     GdipFree(container);
02020 }
02021 
02022 static GpStatus restore_container(GpGraphics* graphics,
02023         GDIPCONST GraphicsContainerItem* container){
02024     GpStatus sts;
02025     GpMatrix *newTrans;
02026     GpRegion *newClip;
02027 
02028     sts = GdipCloneMatrix(container->worldtrans, &newTrans);
02029     if(sts != Ok)
02030         return sts;
02031 
02032     sts = GdipCloneRegion(container->clip, &newClip);
02033     if(sts != Ok){
02034         GdipDeleteMatrix(newTrans);
02035         return sts;
02036     }
02037 
02038     GdipDeleteMatrix(graphics->worldtrans);
02039     graphics->worldtrans = newTrans;
02040 
02041     GdipDeleteRegion(graphics->clip);
02042     graphics->clip = newClip;
02043 
02044     graphics->contid = container->contid - 1;
02045 
02046     graphics->smoothing = container->smoothing;
02047     graphics->compqual = container->compqual;
02048     graphics->interpolation = container->interpolation;
02049     graphics->compmode = container->compmode;
02050     graphics->texthint = container->texthint;
02051     graphics->scale = container->scale;
02052     graphics->unit = container->unit;
02053     graphics->textcontrast = container->textcontrast;
02054     graphics->pixeloffset = container->pixeloffset;
02055     graphics->origin_x = container->origin_x;
02056     graphics->origin_y = container->origin_y;
02057 
02058     return Ok;
02059 }
02060 
02061 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
02062 {
02063     RECT wnd_rect;
02064     GpStatus stat=Ok;
02065     GpUnit unit;
02066 
02067     if(graphics->hwnd) {
02068         if(!GetClientRect(graphics->hwnd, &wnd_rect))
02069             return GenericError;
02070 
02071         rect->X = wnd_rect.left;
02072         rect->Y = wnd_rect.top;
02073         rect->Width = wnd_rect.right - wnd_rect.left;
02074         rect->Height = wnd_rect.bottom - wnd_rect.top;
02075     }else if (graphics->image){
02076         stat = GdipGetImageBounds(graphics->image, rect, &unit);
02077         if (stat == Ok && unit != UnitPixel)
02078             FIXME("need to convert from unit %i\n", unit);
02079     }else{
02080         rect->X = 0;
02081         rect->Y = 0;
02082         rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
02083         rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
02084     }
02085 
02086     return stat;
02087 }
02088 
02089 /* on success, rgn will contain the region of the graphics object which
02090  * is visible after clipping has been applied */
02091 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
02092 {
02093     GpStatus stat;
02094     GpRectF rectf;
02095     GpRegion* tmp;
02096 
02097     if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
02098         return stat;
02099 
02100     if((stat = GdipCreateRegion(&tmp)) != Ok)
02101         return stat;
02102 
02103     if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
02104         goto end;
02105 
02106     if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
02107         goto end;
02108 
02109     stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
02110 
02111 end:
02112     GdipDeleteRegion(tmp);
02113     return stat;
02114 }
02115 
02116 void get_font_hfont(GpGraphics *graphics, GDIPCONST GpFont *font, HFONT *hfont)
02117 {
02118     HDC hdc = CreateCompatibleDC(0);
02119     GpPointF pt[3];
02120     REAL angle, rel_width, rel_height;
02121     LOGFONTW lfw;
02122     HFONT unscaled_font;
02123     TEXTMETRICW textmet;
02124 
02125     pt[0].X = 0.0;
02126     pt[0].Y = 0.0;
02127     pt[1].X = 1.0;
02128     pt[1].Y = 0.0;
02129     pt[2].X = 0.0;
02130     pt[2].Y = 1.0;
02131     if (graphics)
02132         GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
02133     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
02134     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
02135                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
02136     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
02137                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
02138 
02139     GdipGetLogFontW((GpFont *)font, graphics, &lfw);
02140     lfw.lfHeight = roundr(lfw.lfHeight * rel_height);
02141     unscaled_font = CreateFontIndirectW(&lfw);
02142 
02143     SelectObject(hdc, unscaled_font);
02144     GetTextMetricsW(hdc, &textmet);
02145 
02146     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width / rel_height);
02147     lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
02148 
02149     *hfont = CreateFontIndirectW(&lfw);
02150 
02151     DeleteDC(hdc);
02152     DeleteObject(unscaled_font);
02153 }
02154 
02155 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
02156 {
02157     TRACE("(%p, %p)\n", hdc, graphics);
02158 
02159     return GdipCreateFromHDC2(hdc, NULL, graphics);
02160 }
02161 
02162 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
02163 {
02164     GpStatus retval;
02165 
02166     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
02167 
02168     if(hDevice != NULL) {
02169         FIXME("Don't know how to handle parameter hDevice\n");
02170         return NotImplemented;
02171     }
02172 
02173     if(hdc == NULL)
02174         return OutOfMemory;
02175 
02176     if(graphics == NULL)
02177         return InvalidParameter;
02178 
02179     *graphics = GdipAlloc(sizeof(GpGraphics));
02180     if(!*graphics)  return OutOfMemory;
02181 
02182     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
02183         GdipFree(*graphics);
02184         return retval;
02185     }
02186 
02187     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
02188         GdipFree((*graphics)->worldtrans);
02189         GdipFree(*graphics);
02190         return retval;
02191     }
02192 
02193     (*graphics)->hdc = hdc;
02194     (*graphics)->hwnd = WindowFromDC(hdc);
02195     (*graphics)->owndc = FALSE;
02196     (*graphics)->smoothing = SmoothingModeDefault;
02197     (*graphics)->compqual = CompositingQualityDefault;
02198     (*graphics)->interpolation = InterpolationModeBilinear;
02199     (*graphics)->pixeloffset = PixelOffsetModeDefault;
02200     (*graphics)->compmode = CompositingModeSourceOver;
02201     (*graphics)->unit = UnitDisplay;
02202     (*graphics)->scale = 1.0;
02203     (*graphics)->busy = FALSE;
02204     (*graphics)->textcontrast = 4;
02205     list_init(&(*graphics)->containers);
02206     (*graphics)->contid = 0;
02207 
02208     TRACE("<-- %p\n", *graphics);
02209 
02210     return Ok;
02211 }
02212 
02213 GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics)
02214 {
02215     GpStatus retval;
02216 
02217     *graphics = GdipAlloc(sizeof(GpGraphics));
02218     if(!*graphics)  return OutOfMemory;
02219 
02220     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
02221         GdipFree(*graphics);
02222         return retval;
02223     }
02224 
02225     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
02226         GdipFree((*graphics)->worldtrans);
02227         GdipFree(*graphics);
02228         return retval;
02229     }
02230 
02231     (*graphics)->hdc = NULL;
02232     (*graphics)->hwnd = NULL;
02233     (*graphics)->owndc = FALSE;
02234     (*graphics)->image = image;
02235     (*graphics)->smoothing = SmoothingModeDefault;
02236     (*graphics)->compqual = CompositingQualityDefault;
02237     (*graphics)->interpolation = InterpolationModeBilinear;
02238     (*graphics)->pixeloffset = PixelOffsetModeDefault;
02239     (*graphics)->compmode = CompositingModeSourceOver;
02240     (*graphics)->unit = UnitDisplay;
02241     (*graphics)->scale = 1.0;
02242     (*graphics)->busy = FALSE;
02243     (*graphics)->textcontrast = 4;
02244     list_init(&(*graphics)->containers);
02245     (*graphics)->contid = 0;
02246 
02247     TRACE("<-- %p\n", *graphics);
02248 
02249     return Ok;
02250 }
02251 
02252 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
02253 {
02254     GpStatus ret;
02255     HDC hdc;
02256 
02257     TRACE("(%p, %p)\n", hwnd, graphics);
02258 
02259     hdc = GetDC(hwnd);
02260 
02261     if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
02262     {
02263         ReleaseDC(hwnd, hdc);
02264         return ret;
02265     }
02266 
02267     (*graphics)->hwnd = hwnd;
02268     (*graphics)->owndc = TRUE;
02269 
02270     return Ok;
02271 }
02272 
02273 /* FIXME: no icm handling */
02274 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
02275 {
02276     TRACE("(%p, %p)\n", hwnd, graphics);
02277 
02278     return GdipCreateFromHWND(hwnd, graphics);
02279 }
02280 
02281 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
02282     GpMetafile **metafile)
02283 {
02284     IStream *stream = NULL;
02285     UINT read;
02286     ENHMETAHEADER *copy;
02287     GpStatus retval = Ok;
02288 
02289     TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
02290 
02291     if(!hemf || !metafile)
02292         return InvalidParameter;
02293 
02294     read = GetEnhMetaFileBits(hemf, 0, NULL);
02295     copy = GdipAlloc(read);
02296     GetEnhMetaFileBits(hemf, read, (BYTE *)copy);
02297 
02298     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
02299         ERR("could not make stream\n");
02300         GdipFree(copy);
02301         retval = GenericError;
02302         goto err;
02303     }
02304 
02305     *metafile = GdipAlloc(sizeof(GpMetafile));
02306     if(!*metafile){
02307         retval = OutOfMemory;
02308         goto err;
02309     }
02310 
02311     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
02312         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
02313     {
02314         retval = GenericError;
02315         goto err;
02316     }
02317 
02318 
02319     (*metafile)->image.type = ImageTypeMetafile;
02320     memcpy(&(*metafile)->image.format, &ImageFormatWMF, sizeof(GUID));
02321     (*metafile)->image.palette_flags = 0;
02322     (*metafile)->image.palette_count = 0;
02323     (*metafile)->image.palette_size = 0;
02324     (*metafile)->image.palette_entries = NULL;
02325     (*metafile)->image.xres = (REAL)copy->szlDevice.cx;
02326     (*metafile)->image.yres = (REAL)copy->szlDevice.cy;
02327     (*metafile)->bounds.X = (REAL)copy->rclBounds.left;
02328     (*metafile)->bounds.Y = (REAL)copy->rclBounds.top;
02329     (*metafile)->bounds.Width = (REAL)(copy->rclBounds.right - copy->rclBounds.left);
02330     (*metafile)->bounds.Height = (REAL)(copy->rclBounds.bottom - copy->rclBounds.top);
02331     (*metafile)->unit = UnitPixel;
02332 
02333     if(delete)
02334         DeleteEnhMetaFile(hemf);
02335 
02336     TRACE("<-- %p\n", *metafile);
02337 
02338 err:
02339     if (retval != Ok)
02340         GdipFree(*metafile);
02341     IStream_Release(stream);
02342     return retval;
02343 }
02344 
02345 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
02346     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
02347 {
02348     UINT read;
02349     BYTE *copy;
02350     HENHMETAFILE hemf;
02351     GpStatus retval = Ok;
02352 
02353     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
02354 
02355     if(!hwmf || !metafile || !placeable)
02356         return InvalidParameter;
02357 
02358     *metafile = NULL;
02359     read = GetMetaFileBitsEx(hwmf, 0, NULL);
02360     if(!read)
02361         return GenericError;
02362     copy = GdipAlloc(read);
02363     GetMetaFileBitsEx(hwmf, read, copy);
02364 
02365     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
02366     GdipFree(copy);
02367 
02368     retval = GdipCreateMetafileFromEmf(hemf, FALSE, metafile);
02369 
02370     if (retval == Ok)
02371     {
02372         (*metafile)->image.xres = (REAL)placeable->Inch;
02373         (*metafile)->image.yres = (REAL)placeable->Inch;
02374         (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
02375         (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
02376         (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
02377                                            placeable->BoundingBox.Left);
02378         (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
02379                                             placeable->BoundingBox.Top);
02380 
02381         if (delete) DeleteMetaFile(hwmf);
02382     }
02383     return retval;
02384 }
02385 
02386 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
02387     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
02388 {
02389     HMETAFILE hmf = GetMetaFileW(file);
02390 
02391     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
02392 
02393     if(!hmf) return InvalidParameter;
02394 
02395     return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
02396 }
02397 
02398 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
02399     GpMetafile **metafile)
02400 {
02401     FIXME("(%p, %p): stub\n", file, metafile);
02402     return NotImplemented;
02403 }
02404 
02405 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
02406     GpMetafile **metafile)
02407 {
02408     FIXME("(%p, %p): stub\n", stream, metafile);
02409     return NotImplemented;
02410 }
02411 
02412 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
02413     UINT access, IStream **stream)
02414 {
02415     DWORD dwMode;
02416     HRESULT ret;
02417 
02418     TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
02419 
02420     if(!stream || !filename)
02421         return InvalidParameter;
02422 
02423     if(access & GENERIC_WRITE)
02424         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
02425     else if(access & GENERIC_READ)
02426         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
02427     else
02428         return InvalidParameter;
02429 
02430     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
02431 
02432     return hresult_to_status(ret);
02433 }
02434 
02435 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
02436 {
02437     GraphicsContainerItem *cont, *next;
02438     GpStatus stat;
02439     TRACE("(%p)\n", graphics);
02440 
02441     if(!graphics) return InvalidParameter;
02442     if(graphics->busy) return ObjectBusy;
02443 
02444     if (graphics->image && graphics->image->type == ImageTypeMetafile)
02445     {
02446         stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
02447         if (stat != Ok)
02448             return stat;
02449     }
02450 
02451     if(graphics->owndc)
02452         ReleaseDC(graphics->hwnd, graphics->hdc);
02453 
02454     LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
02455         list_remove(&cont->entry);
02456         delete_container(cont);
02457     }
02458 
02459     GdipDeleteRegion(graphics->clip);
02460     GdipDeleteMatrix(graphics->worldtrans);
02461     GdipFree(graphics);
02462 
02463     return Ok;
02464 }
02465 
02466 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
02467     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
02468 {
02469     INT save_state, num_pts;
02470     GpPointF points[MAX_ARC_PTS];
02471     GpStatus retval;
02472 
02473     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
02474           width, height, startAngle, sweepAngle);
02475 
02476     if(!graphics || !pen || width <= 0 || height <= 0)
02477         return InvalidParameter;
02478 
02479     if(graphics->busy)
02480         return ObjectBusy;
02481 
02482     if (!graphics->hdc)
02483     {
02484         FIXME("graphics object has no HDC\n");
02485         return Ok;
02486     }
02487 
02488     num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
02489 
02490     save_state = prepare_dc(graphics, pen);
02491 
02492     retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
02493 
02494     restore_dc(graphics, save_state);
02495 
02496     return retval;
02497 }
02498 
02499 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
02500     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
02501 {
02502     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
02503           width, height, startAngle, sweepAngle);
02504 
02505     return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
02506 }
02507 
02508 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
02509     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
02510 {
02511     INT save_state;
02512     GpPointF pt[4];
02513     GpStatus retval;
02514 
02515     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
02516           x2, y2, x3, y3, x4, y4);
02517 
02518     if(!graphics || !pen)
02519         return InvalidParameter;
02520 
02521     if(graphics->busy)
02522         return ObjectBusy;
02523 
02524     if (!graphics->hdc)
02525     {
02526         FIXME("graphics object has no HDC\n");
02527         return Ok;
02528     }
02529 
02530     pt[0].X = x1;
02531     pt[0].Y = y1;
02532     pt[1].X = x2;
02533     pt[1].Y = y2;
02534     pt[2].X = x3;
02535     pt[2].Y = y3;
02536     pt[3].X = x4;
02537     pt[3].Y = y4;
02538 
02539     save_state = prepare_dc(graphics, pen);
02540 
02541     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
02542 
02543     restore_dc(graphics, save_state);
02544 
02545     return retval;
02546 }
02547 
02548 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
02549     INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
02550 {
02551     INT save_state;
02552     GpPointF pt[4];
02553     GpStatus retval;
02554 
02555     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
02556           x2, y2, x3, y3, x4, y4);
02557 
02558     if(!graphics || !pen)
02559         return InvalidParameter;
02560 
02561     if(graphics->busy)
02562         return ObjectBusy;
02563 
02564     if (!graphics->hdc)
02565     {
02566         FIXME("graphics object has no HDC\n");
02567         return Ok;
02568     }
02569 
02570     pt[0].X = x1;
02571     pt[0].Y = y1;
02572     pt[1].X = x2;
02573     pt[1].Y = y2;
02574     pt[2].X = x3;
02575     pt[2].Y = y3;
02576     pt[3].X = x4;
02577     pt[3].Y = y4;
02578 
02579     save_state = prepare_dc(graphics, pen);
02580 
02581     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
02582 
02583     restore_dc(graphics, save_state);
02584 
02585     return retval;
02586 }
02587 
02588 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
02589     GDIPCONST GpPointF *points, INT count)
02590 {
02591     INT i;
02592     GpStatus ret;
02593 
02594     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
02595 
02596     if(!graphics || !pen || !points || (count <= 0))
02597         return InvalidParameter;
02598 
02599     if(graphics->busy)
02600         return ObjectBusy;
02601 
02602     for(i = 0; i < floor(count / 4); i++){
02603         ret = GdipDrawBezier(graphics, pen,
02604                              points[4*i].X, points[4*i].Y,
02605                              points[4*i + 1].X, points[4*i + 1].Y,
02606                              points[4*i + 2].X, points[4*i + 2].Y,
02607                              points[4*i + 3].X, points[4*i + 3].Y);
02608         if(ret != Ok)
02609             return ret;
02610     }
02611 
02612     return Ok;
02613 }
02614 
02615 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
02616     GDIPCONST GpPoint *points, INT count)
02617 {
02618     GpPointF *pts;
02619     GpStatus ret;
02620     INT i;
02621 
02622     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
02623 
02624     if(!graphics || !pen || !points || (count <= 0))
02625         return InvalidParameter;
02626 
02627     if(graphics->busy)
02628         return ObjectBusy;
02629 
02630     pts = GdipAlloc(sizeof(GpPointF) * count);
02631     if(!pts)
02632         return OutOfMemory;
02633 
02634     for(i = 0; i < count; i++){
02635         pts[i].X = (REAL)points[i].X;
02636         pts[i].Y = (REAL)points[i].Y;
02637     }
02638 
02639     ret = GdipDrawBeziers(graphics,pen,pts,count);
02640 
02641     GdipFree(pts);
02642 
02643     return ret;
02644 }
02645 
02646 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
02647     GDIPCONST GpPointF *points, INT count)
02648 {
02649     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
02650 
02651     return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
02652 }
02653 
02654 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
02655     GDIPCONST GpPoint *points, INT count)
02656 {
02657     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
02658 
02659     return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
02660 }
02661 
02662 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
02663     GDIPCONST GpPointF *points, INT count, REAL tension)
02664 {
02665     GpPath *path;
02666     GpStatus stat;
02667 
02668     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
02669 
02670     if(!graphics || !pen || !points || count <= 0)
02671         return InvalidParameter;
02672 
02673     if(graphics->busy)
02674         return ObjectBusy;
02675 
02676     if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
02677         return stat;
02678 
02679     stat = GdipAddPathClosedCurve2(path, points, count, tension);
02680     if(stat != Ok){
02681         GdipDeletePath(path);
02682         return stat;
02683     }
02684 
02685     stat = GdipDrawPath(graphics, pen, path);
02686 
02687     GdipDeletePath(path);
02688 
02689     return stat;
02690 }
02691 
02692 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
02693     GDIPCONST GpPoint *points, INT count, REAL tension)
02694 {
02695     GpPointF *ptf;
02696     GpStatus stat;
02697     INT i;
02698 
02699     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
02700 
02701     if(!points || count <= 0)
02702         return InvalidParameter;
02703 
02704     ptf = GdipAlloc(sizeof(GpPointF)*count);
02705     if(!ptf)
02706         return OutOfMemory;
02707 
02708     for(i = 0; i < count; i++){
02709         ptf[i].X = (REAL)points[i].X;
02710         ptf[i].Y = (REAL)points[i].Y;
02711     }
02712 
02713     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
02714 
02715     GdipFree(ptf);
02716 
02717     return stat;
02718 }
02719 
02720 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
02721     GDIPCONST GpPointF *points, INT count)
02722 {
02723     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
02724 
02725     return GdipDrawCurve2(graphics,pen,points,count,1.0);
02726 }
02727 
02728 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
02729     GDIPCONST GpPoint *points, INT count)
02730 {
02731     GpPointF *pointsF;
02732     GpStatus ret;
02733     INT i;
02734 
02735     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
02736 
02737     if(!points)
02738         return InvalidParameter;
02739 
02740     pointsF = GdipAlloc(sizeof(GpPointF)*count);
02741     if(!pointsF)
02742         return OutOfMemory;
02743 
02744     for(i = 0; i < count; i++){
02745         pointsF[i].X = (REAL)points[i].X;
02746         pointsF[i].Y = (REAL)points[i].Y;
02747     }
02748 
02749     ret = GdipDrawCurve(graphics,pen,pointsF,count);
02750     GdipFree(pointsF);
02751 
02752     return ret;
02753 }
02754 
02755 /* Approximates cardinal spline with Bezier curves. */
02756 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
02757     GDIPCONST GpPointF *points, INT count, REAL tension)
02758 {
02759     /* PolyBezier expects count*3-2 points. */
02760     INT i, len_pt = count*3-2, save_state;
02761     GpPointF *pt;
02762     REAL x1, x2, y1, y2;
02763     GpStatus retval;
02764 
02765     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
02766 
02767     if(!graphics || !pen)
02768         return InvalidParameter;
02769 
02770     if(graphics->busy)
02771         return ObjectBusy;
02772 
02773     if(count < 2)
02774         return InvalidParameter;
02775 
02776     if (!graphics->hdc)
02777     {
02778         FIXME("graphics object has no HDC\n");
02779         return Ok;
02780     }
02781 
02782     pt = GdipAlloc(len_pt * sizeof(GpPointF));
02783     if(!pt)
02784         return OutOfMemory;
02785 
02786     tension = tension * TENSION_CONST;
02787 
02788     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
02789         tension, &x1, &y1);
02790 
02791     pt[0].X = points[0].X;
02792     pt[0].Y = points[0].Y;
02793     pt[1].X = x1;
02794     pt[1].Y = y1;
02795 
02796     for(i = 0; i < count-2; i++){
02797         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
02798 
02799         pt[3*i+2].X = x1;
02800         pt[3*i+2].Y = y1;
02801         pt[3*i+3].X = points[i+1].X;
02802         pt[3*i+3].Y = points[i+1].Y;
02803         pt[3*i+4].X = x2;
02804         pt[3*i+4].Y = y2;
02805     }
02806 
02807     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
02808         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
02809 
02810     pt[len_pt-2].X = x1;
02811     pt[len_pt-2].Y = y1;
02812     pt[len_pt-1].X = points[count-1].X;
02813     pt[len_pt-1].Y = points[count-1].Y;
02814 
02815     save_state = prepare_dc(graphics, pen);
02816 
02817     retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
02818 
02819     GdipFree(pt);
02820     restore_dc(graphics, save_state);
02821 
02822     return retval;
02823 }
02824 
02825 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
02826     GDIPCONST GpPoint *points, INT count, REAL tension)
02827 {
02828     GpPointF *pointsF;
02829     GpStatus ret;
02830     INT i;
02831 
02832     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
02833 
02834     if(!points)
02835         return InvalidParameter;
02836 
02837     pointsF = GdipAlloc(sizeof(GpPointF)*count);
02838     if(!pointsF)
02839         return OutOfMemory;
02840 
02841     for(i = 0; i < count; i++){
02842         pointsF[i].X = (REAL)points[i].X;
02843         pointsF[i].Y = (REAL)points[i].Y;
02844     }
02845 
02846     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
02847     GdipFree(pointsF);
02848 
02849     return ret;
02850 }
02851 
02852 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
02853     GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
02854     REAL tension)
02855 {
02856     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
02857 
02858     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
02859         return InvalidParameter;
02860     }
02861 
02862     return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
02863 }
02864 
02865 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
02866     GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
02867     REAL tension)
02868 {
02869     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
02870 
02871     if(count < 0){
02872         return OutOfMemory;
02873     }
02874 
02875     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
02876         return InvalidParameter;
02877     }
02878 
02879     return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
02880 }
02881 
02882 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
02883     REAL y, REAL width, REAL height)
02884 {
02885     INT save_state;
02886     GpPointF ptf[2];
02887     POINT pti[2];
02888 
02889     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
02890 
02891     if(!graphics || !pen)
02892         return InvalidParameter;
02893 
02894     if(graphics->busy)
02895         return ObjectBusy;
02896 
02897     if (!graphics->hdc)
02898     {
02899         FIXME("graphics object has no HDC\n");
02900         return Ok;
02901     }
02902 
02903     ptf[0].X = x;
02904     ptf[0].Y = y;
02905     ptf[1].X = x + width;
02906     ptf[1].Y = y + height;
02907 
02908     save_state = prepare_dc(graphics, pen);
02909     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
02910 
02911     transform_and_round_points(graphics, pti, ptf, 2);
02912 
02913     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
02914 
02915     restore_dc(graphics, save_state);
02916 
02917     return Ok;
02918 }
02919 
02920 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
02921     INT y, INT width, INT height)
02922 {
02923     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
02924 
02925     return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
02926 }
02927 
02928 
02929 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
02930 {
02931     UINT width, height;
02932     GpPointF points[3];
02933 
02934     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
02935 
02936     if(!graphics || !image)
02937         return InvalidParameter;
02938 
02939     GdipGetImageWidth(image, &width);
02940     GdipGetImageHeight(image, &height);
02941 
02942     /* FIXME: we should use the graphics and image dpi, somehow */
02943 
02944     points[0].X = points[2].X = x;
02945     points[0].Y = points[1].Y = y;
02946     points[1].X = x + width;
02947     points[2].Y = y + height;
02948 
02949     return GdipDrawImagePointsRect(graphics, image, points, 3, 0, 0, width, height,
02950         UnitPixel, NULL, NULL, NULL);
02951 }
02952 
02953 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
02954     INT y)
02955 {
02956     TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
02957 
02958     return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
02959 }
02960 
02961 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
02962     REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
02963     GpUnit srcUnit)
02964 {
02965     GpPointF points[3];
02966     TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
02967 
02968     points[0].X = points[2].X = x;
02969     points[0].Y = points[1].Y = y;
02970 
02971     /* FIXME: convert image coordinates to Graphics coordinates? */
02972     points[1].X = x + srcwidth;
02973     points[2].Y = y + srcheight;
02974 
02975     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
02976         srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
02977 }
02978 
02979 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
02980     INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
02981     GpUnit srcUnit)
02982 {
02983     return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
02984 }
02985 
02986 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
02987     GDIPCONST GpPointF *dstpoints, INT count)
02988 {
02989     UINT width, height;
02990 
02991     TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
02992 
02993     if(!image)
02994         return InvalidParameter;
02995 
02996     GdipGetImageWidth(image, &width);
02997     GdipGetImageHeight(image, &height);
02998 
02999     return GdipDrawImagePointsRect(graphics, image, dstpoints, count, 0, 0,
03000         width, height, UnitPixel, NULL, NULL, NULL);
03001 }
03002 
03003 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
03004     GDIPCONST GpPoint *dstpoints, INT count)
03005 {
03006     GpPointF ptf[3];
03007 
03008     TRACE("(%p, %p, %p, %d)\n", graphics, image, dstpoints, count);
03009 
03010     if (count != 3 || !dstpoints)
03011         return InvalidParameter;
03012 
03013     ptf[0].X = (REAL)dstpoints[0].X;
03014     ptf[0].Y = (REAL)dstpoints[0].Y;
03015     ptf[1].X = (REAL)dstpoints[1].X;
03016     ptf[1].Y = (REAL)dstpoints[1].Y;
03017     ptf[2].X = (REAL)dstpoints[2].X;
03018     ptf[2].Y = (REAL)dstpoints[2].Y;
03019 
03020     return GdipDrawImagePoints(graphics, image, ptf, count);
03021 }
03022 
03023 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
03024      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
03025      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
03026      DrawImageAbort callback, VOID * callbackData)
03027 {
03028     GpPointF ptf[4];
03029     POINT pti[4];
03030     REAL dx, dy;
03031     GpStatus stat;
03032 
03033     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
03034           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
03035           callbackData);
03036 
03037     if (count > 3)
03038         return NotImplemented;
03039 
03040     if(!graphics || !image || !points || count != 3)
03041          return InvalidParameter;
03042 
03043     TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
03044         debugstr_pointf(&points[2]));
03045 
03046     memcpy(ptf, points, 3 * sizeof(GpPointF));
03047     ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
03048     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
03049     if (!srcwidth || !srcheight || ptf[3].X == ptf[0].X || ptf[3].Y == ptf[0].Y)
03050         return Ok;
03051     transform_and_round_points(graphics, pti, ptf, 4);
03052 
03053     if (image->picture)
03054     {
03055         if (!graphics->hdc)
03056         {
03057             FIXME("graphics object has no HDC\n");
03058         }
03059 
03060         /* FIXME: partially implemented (only works for rectangular parallelograms) */
03061         if(srcUnit == UnitInch)
03062             dx = dy = (REAL) INCH_HIMETRIC;
03063         else if(srcUnit == UnitPixel){
03064             dx = ((REAL) INCH_HIMETRIC) /
03065                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
03066             dy = ((REAL) INCH_HIMETRIC) /
03067                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
03068         }
03069         else
03070             return NotImplemented;
03071 
03072         if(IPicture_Render(image->picture, graphics->hdc,
03073             pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
03074             srcx * dx, srcy * dy,
03075             srcwidth * dx, srcheight * dy,
03076             NULL) != S_OK){
03077             if(callback)
03078                 callback(callbackData);
03079             return GenericError;
03080         }
03081     }
03082     else if (image->type == ImageTypeBitmap)
03083     {
03084         GpBitmap* bitmap = (GpBitmap*)image;
03085         int use_software=0;
03086 
03087         if (srcUnit == UnitInch)
03088             dx = dy = 96.0; /* FIXME: use the image resolution */
03089         else if (srcUnit == UnitPixel)
03090             dx = dy = 1.0;
03091         else
03092             return NotImplemented;
03093 
03094         srcx = srcx * dx;
03095         srcy = srcy * dy;
03096         srcwidth = srcwidth * dx;
03097         srcheight = srcheight * dy;
03098 
03099         if (imageAttributes ||
03100             (graphics->image && graphics->image->type == ImageTypeBitmap) ||
03101             !((GpBitmap*)image)->hbitmap ||
03102             ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
03103             ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
03104             srcx < 0 || srcy < 0 ||
03105             srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
03106             use_software = 1;
03107 
03108         if (use_software)
03109         {
03110             RECT dst_area;
03111             GpRect src_area;
03112             int i, x, y, src_stride, dst_stride;
03113             GpMatrix *dst_to_src;
03114             REAL m11, m12, m21, m22, mdx, mdy;
03115             LPBYTE src_data, dst_data;
03116             BitmapData lockeddata;
03117             InterpolationMode interpolation = graphics->interpolation;
03118             GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
03119             REAL x_dx, x_dy, y_dx, y_dy;
03120             static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
03121 
03122             if (!imageAttributes)
03123                 imageAttributes = &defaultImageAttributes;
03124 
03125             dst_area.left = dst_area.right = pti[0].x;
03126             dst_area.top = dst_area.bottom = pti[0].y;
03127             for (i=1; i<4; i++)
03128             {
03129                 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
03130                 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
03131                 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
03132                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
03133             }
03134 
03135             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
03136             m21 = (ptf[2].X - ptf[0].X) / srcheight;
03137             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
03138             m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
03139             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
03140             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
03141 
03142             stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
03143             if (stat != Ok) return stat;
03144 
03145             stat = GdipInvertMatrix(dst_to_src);
03146             if (stat != Ok)
03147             {
03148                 GdipDeleteMatrix(dst_to_src);
03149                 return stat;
03150             }
03151 
03152             dst_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
03153             if (!dst_data)
03154             {
03155                 GdipDeleteMatrix(dst_to_src);
03156                 return OutOfMemory;
03157             }
03158 
03159             dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
03160 
03161             get_bitmap_sample_size(interpolation, imageAttributes->wrap,
03162                 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
03163 
03164             src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
03165             if (!src_data)
03166             {
03167                 GdipFree(dst_data);
03168                 GdipDeleteMatrix(dst_to_src);
03169                 return OutOfMemory;
03170             }
03171             src_stride = sizeof(ARGB) * src_area.Width;
03172 
03173             /* Read the bits we need from the source bitmap into an ARGB buffer. */
03174             lockeddata.Width = src_area.Width;
03175             lockeddata.Height = src_area.Height;
03176             lockeddata.Stride = src_stride;
03177             lockeddata.PixelFormat = PixelFormat32bppARGB;
03178             lockeddata.Scan0 = src_data;
03179 
03180             stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
03181                 PixelFormat32bppARGB, &lockeddata);
03182 
03183             if (stat == Ok)
03184                 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
03185 
03186             if (stat != Ok)
03187             {
03188                 if (src_data != dst_data)
03189                     GdipFree(src_data);
03190                 GdipFree(dst_data);
03191                 GdipDeleteMatrix(dst_to_src);
03192                 return OutOfMemory;
03193             }
03194 
03195             apply_image_attributes(imageAttributes, src_data,
03196                 src_area.Width, src_area.Height,
03197                 src_stride, ColorAdjustTypeBitmap);
03198 
03199             /* Transform the bits as needed to the destination. */
03200             GdipTransformMatrixPoints(dst_to_src, dst_to_src_points, 3);
03201 
03202             x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
03203             x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
03204             y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
03205             y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
03206 
03207             for (x=dst_area.left; x<dst_area.right; x++)
03208             {
03209                 for (y=dst_area.top; y<dst_area.bottom; y++)
03210                 {
03211                     GpPointF src_pointf;
03212                     ARGB *dst_color;
03213 
03214                     src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
03215                     src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
03216 
03217                     dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
03218 
03219                     if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
03220                         *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, imageAttributes, interpolation);
03221                     else
03222                         *dst_color = 0;
03223                 }
03224             }
03225 
03226             GdipDeleteMatrix(dst_to_src);
03227 
03228             GdipFree(src_data);
03229 
03230             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
03231                 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
03232 
03233             GdipFree(dst_data);
03234 
03235             return stat;
03236         }
03237         else
03238         {
03239             HDC hdc;
03240             int temp_hdc=0, temp_bitmap=0;
03241             HBITMAP hbitmap, old_hbm=NULL;
03242 
03243             if (!(bitmap->format == PixelFormat16bppRGB555 ||
03244                   bitmap->format == PixelFormat24bppRGB ||
03245                   bitmap->format == PixelFormat32bppRGB ||
03246                   bitmap->format == PixelFormat32bppPARGB))
03247             {
03248                 BITMAPINFOHEADER bih;
03249                 BYTE *temp_bits;
03250                 PixelFormat dst_format;
03251 
03252                 /* we can't draw a bitmap of this format directly */
03253                 hdc = CreateCompatibleDC(0);
03254                 temp_hdc = 1;
03255                 temp_bitmap = 1;
03256 
03257                 bih.biSize = sizeof(BITMAPINFOHEADER);
03258                 bih.biWidth = bitmap->width;
03259                 bih.biHeight = -bitmap->height;
03260                 bih.biPlanes = 1;
03261                 bih.biBitCount = 32;
03262                 bih.biCompression = BI_RGB;
03263                 bih.biSizeImage = 0;
03264                 bih.biXPelsPerMeter = 0;
03265                 bih.biYPelsPerMeter = 0;
03266                 bih.biClrUsed = 0;
03267                 bih.biClrImportant = 0;
03268 
03269                 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
03270                     (void**)&temp_bits, NULL, 0);
03271 
03272                 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
03273                     dst_format = PixelFormat32bppPARGB;
03274                 else
03275                     dst_format = PixelFormat32bppRGB;
03276 
03277                 convert_pixels(bitmap->width, bitmap->height,
03278                     bitmap->width*4, temp_bits, dst_format,
03279                     bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
03280             }
03281             else
03282             {
03283                 hbitmap = bitmap->hbitmap;
03284                 hdc = bitmap->hdc;
03285                 temp_hdc = (hdc == 0);
03286             }
03287 
03288             if (temp_hdc)
03289             {
03290                 if (!hdc) hdc = CreateCompatibleDC(0);
03291                 old_hbm = SelectObject(hdc, hbitmap);
03292             }
03293 
03294             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
03295             {
03296                 gdi_alpha_blend(graphics, pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
03297                                 hdc, srcx, srcy, srcwidth, srcheight);
03298             }
03299             else
03300             {
03301                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
03302                     hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
03303             }
03304 
03305             if (temp_hdc)
03306             {
03307                 SelectObject(hdc, old_hbm);
03308                 DeleteDC(hdc);
03309             }
03310 
03311             if (temp_bitmap)
03312                 DeleteObject(hbitmap);
03313         }
03314     }
03315     else
03316     {
03317         ERR("GpImage with no IPicture or HBITMAP?!\n");
03318         return NotImplemented;
03319     }
03320 
03321     return Ok;
03322 }
03323 
03324 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
03325      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
03326      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
03327      DrawImageAbort callback, VOID * callbackData)
03328 {
03329     GpPointF pointsF[3];
03330     INT i;
03331 
03332     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
03333           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
03334           callbackData);
03335 
03336     if(!points || count!=3)
03337         return InvalidParameter;
03338 
03339     for(i = 0; i < count; i++){
03340         pointsF[i].X = (REAL)points[i].X;
03341         pointsF[i].Y = (REAL)points[i].Y;
03342     }
03343 
03344     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
03345                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
03346                                    callback, callbackData);
03347 }
03348 
03349 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
03350     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
03351     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
03352     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
03353     VOID * callbackData)
03354 {
03355     GpPointF points[3];
03356 
03357     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
03358           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
03359           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
03360 
03361     points[0].X = dstx;
03362     points[0].Y = dsty;
03363     points[1].X = dstx + dstwidth;
03364     points[1].Y = dsty;
03365     points[2].X = dstx;
03366     points[2].Y = dsty + dstheight;
03367 
03368     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
03369                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
03370 }
03371 
03372 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
03373     INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
03374     INT srcwidth, INT srcheight, GpUnit srcUnit,
03375     GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
03376     VOID * callbackData)
03377 {
03378     GpPointF points[3];
03379 
03380     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
03381           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
03382           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
03383 
03384     points[0].X = dstx;
03385     points[0].Y = dsty;
03386     points[1].X = dstx + dstwidth;
03387     points[1].Y = dsty;
03388     points[2].X = dstx;
03389     points[2].Y = dsty + dstheight;
03390 
03391     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
03392                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
03393 }
03394 
03395 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
03396     REAL x, REAL y, REAL width, REAL height)
03397 {
03398     RectF bounds;
03399     GpUnit unit;
03400     GpStatus ret;
03401 
03402     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
03403 
03404     if(!graphics || !image)
03405         return InvalidParameter;
03406 
03407     ret = GdipGetImageBounds(image, &bounds, &unit);
03408     if(ret != Ok)
03409         return ret;
03410 
03411     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
03412                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
03413                                  unit, NULL, NULL, NULL);
03414 }
03415 
03416 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
03417     INT x, INT y, INT width, INT height)
03418 {
03419     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
03420 
03421     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
03422 }
03423 
03424 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
03425     REAL y1, REAL x2, REAL y2)
03426 {
03427     INT save_state;
03428     GpPointF pt[2];
03429     GpStatus retval;
03430 
03431     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
03432 
03433     if(!pen || !graphics)
03434         return InvalidParameter;
03435 
03436     if(graphics->busy)
03437         return ObjectBusy;
03438 
03439     if (!graphics->hdc)
03440     {
03441         FIXME("graphics object has no HDC\n");
03442         return Ok;
03443     }
03444 
03445     pt[0].X = x1;
03446     pt[0].Y = y1;
03447     pt[1].X = x2;
03448     pt[1].Y = y2;
03449 
03450     save_state = prepare_dc(graphics, pen);
03451 
03452     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
03453 
03454     restore_dc(graphics, save_state);
03455 
03456     return retval;
03457 }
03458 
03459 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
03460     INT y1, INT x2, INT y2)
03461 {
03462     INT save_state;
03463     GpPointF pt[2];
03464     GpStatus retval;
03465 
03466     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
03467 
03468     if(!pen || !graphics)
03469         return InvalidParameter;
03470 
03471     if(graphics->busy)
03472         return ObjectBusy;
03473 
03474     if (!graphics->hdc)
03475     {
03476         FIXME("graphics object has no HDC\n");
03477         return Ok;
03478     }
03479 
03480     pt[0].X = (REAL)x1;
03481     pt[0].Y = (REAL)y1;
03482     pt[1].X = (REAL)x2;
03483     pt[1].Y = (REAL)y2;
03484 
03485     save_state = prepare_dc(graphics, pen);
03486 
03487     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
03488 
03489     restore_dc(graphics, save_state);
03490 
03491     return retval;
03492 }
03493 
03494 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
03495     GpPointF *points, INT count)
03496 {
03497     INT save_state;
03498     GpStatus retval;
03499 
03500     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
03501 
03502     if(!pen || !graphics || (count < 2))
03503         return InvalidParameter;
03504 
03505     if(graphics->busy)
03506         return ObjectBusy;
03507 
03508     if (!graphics->hdc)
03509     {
03510         FIXME("graphics object has no HDC\n");
03511         return Ok;
03512     }
03513 
03514     save_state = prepare_dc(graphics, pen);
03515 
03516     retval = draw_polyline(graphics, pen, points, count, TRUE);
03517 
03518     restore_dc(graphics, save_state);
03519 
03520     return retval;
03521 }
03522 
03523 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
03524     GpPoint *points, INT count)
03525 {
03526     INT save_state;
03527     GpStatus retval;
03528     GpPointF *ptf = NULL;
03529     int i;
03530 
03531     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
03532 
03533     if(!pen || !graphics || (count < 2))
03534         return InvalidParameter;
03535 
03536     if(graphics->busy)
03537         return ObjectBusy;
03538 
03539     if (!graphics->hdc)
03540     {
03541         FIXME("graphics object has no HDC\n");
03542         return Ok;
03543     }
03544 
03545     ptf = GdipAlloc(count * sizeof(GpPointF));
03546     if(!ptf) return OutOfMemory;
03547 
03548     for(i = 0; i < count; i ++){
03549         ptf[i].X = (REAL) points[i].X;
03550         ptf[i].Y = (REAL) points[i].Y;
03551     }
03552 
03553     save_state = prepare_dc(graphics, pen);
03554 
03555     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
03556 
03557     restore_dc(graphics, save_state);
03558 
03559     GdipFree(ptf);
03560     return retval;
03561 }
03562 
03563 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
03564 {
03565     INT save_state;
03566     GpStatus retval;
03567 
03568     TRACE("(%p, %p, %p)\n", graphics, pen, path);
03569 
03570     if(!pen || !graphics)
03571         return InvalidParameter;
03572 
03573     if(graphics->busy)
03574         return ObjectBusy;
03575 
03576     if (!graphics->hdc)
03577     {
03578         FIXME("graphics object has no HDC\n");
03579         return Ok;
03580     }
03581 
03582     save_state = prepare_dc(graphics, pen);
03583 
03584     retval = draw_poly(graphics, pen, path->pathdata.Points,
03585                        path->pathdata.Types, path->pathdata.Count, TRUE);
03586 
03587     restore_dc(graphics, save_state);
03588 
03589     return retval;
03590 }
03591 
03592 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
03593     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
03594 {
03595     INT save_state;
03596 
03597     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
03598             width, height, startAngle, sweepAngle);
03599 
03600     if(!graphics || !pen)
03601         return InvalidParameter;
03602 
03603     if(graphics->busy)
03604         return ObjectBusy;
03605 
03606     if (!graphics->hdc)
03607     {
03608         FIXME("graphics object has no HDC\n");
03609         return Ok;
03610     }
03611 
03612     save_state = prepare_dc(graphics, pen);
03613     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
03614 
03615     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
03616 
03617     restore_dc(graphics, save_state);
03618 
03619     return Ok;
03620 }
03621 
03622 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
03623     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
03624 {
03625     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
03626             width, height, startAngle, sweepAngle);
03627 
03628     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
03629 }
03630 
03631 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
03632     REAL y, REAL width, REAL height)
03633 {
03634     INT save_state;
03635     GpPointF ptf[4];
03636     POINT pti[4];
03637 
03638     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
03639 
03640     if(!pen || !graphics)
03641         return InvalidParameter;
03642 
03643     if(graphics->busy)
03644         return ObjectBusy;
03645 
03646     if (!graphics->hdc)
03647     {
03648         FIXME("graphics object has no HDC\n");
03649         return Ok;
03650     }
03651 
03652     ptf[0].X = x;
03653     ptf[0].Y = y;
03654     ptf[1].X = x + width;
03655     ptf[1].Y = y;
03656     ptf[2].X = x + width;
03657     ptf[2].Y = y + height;
03658     ptf[3].X = x;
03659     ptf[3].Y = y + height;
03660 
03661     save_state = prepare_dc(graphics, pen);
03662     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
03663 
03664     transform_and_round_points(graphics, pti, ptf, 4);
03665     Polygon(graphics->hdc, pti, 4);
03666 
03667     restore_dc(graphics, save_state);
03668 
03669     return Ok;
03670 }
03671 
03672 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
03673     INT y, INT width, INT height)
03674 {
03675     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
03676 
03677     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
03678 }
03679 
03680 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
03681     GDIPCONST GpRectF* rects, INT count)
03682 {
03683     GpPointF *ptf;
03684     POINT *pti;
03685     INT save_state, i;
03686 
03687     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
03688 
03689     if(!graphics || !pen || !rects || count < 1)
03690         return InvalidParameter;
03691 
03692     if(graphics->busy)
03693         return ObjectBusy;
03694 
03695     if (!graphics->hdc)
03696     {
03697         FIXME("graphics object has no HDC\n");
03698         return Ok;
03699     }
03700 
03701     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
03702     pti = GdipAlloc(4 * count * sizeof(POINT));
03703 
03704     if(!ptf || !pti){
03705         GdipFree(ptf);
03706         GdipFree(pti);
03707         return OutOfMemory;
03708     }
03709 
03710     for(i = 0; i < count; i++){
03711         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
03712         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
03713         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
03714         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
03715     }
03716 
03717     save_state = prepare_dc(graphics, pen);
03718     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
03719 
03720     transform_and_round_points(graphics, pti, ptf, 4 * count);
03721 
03722     for(i = 0; i < count; i++)
03723         Polygon(graphics->hdc, &pti[4 * i], 4);
03724 
03725     restore_dc(graphics, save_state);
03726 
03727     GdipFree(ptf);
03728     GdipFree(pti);
03729 
03730     return Ok;
03731 }
03732 
03733 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
03734     GDIPCONST GpRect* rects, INT count)
03735 {
03736     GpRectF *rectsF;
03737     GpStatus ret;
03738     INT i;
03739 
03740     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
03741 
03742     if(!rects || count<=0)
03743         return InvalidParameter;
03744 
03745     rectsF = GdipAlloc(sizeof(GpRectF) * count);
03746     if(!rectsF)
03747         return OutOfMemory;
03748 
03749     for(i = 0;i < count;i++){
03750         rectsF[i].X      = (REAL)rects[i].X;
03751         rectsF[i].Y      = (REAL)rects[i].Y;
03752         rectsF[i].Width  = (REAL)rects[i].Width;
03753         rectsF[i].Height = (REAL)rects[i].Height;
03754     }
03755 
03756     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
03757     GdipFree(rectsF);
03758 
03759     return ret;
03760 }
03761 
03762 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
03763     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
03764 {
03765     GpPath *path;
03766     GpStatus stat;
03767 
03768     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
03769             count, tension, fill);
03770 
03771     if(!graphics || !brush || !points)
03772         return InvalidParameter;
03773 
03774     if(graphics->busy)
03775         return ObjectBusy;
03776 
03777     if(count == 1)    /* Do nothing */
03778         return Ok;
03779 
03780     stat = GdipCreatePath(fill, &path);
03781     if(stat != Ok)
03782         return stat;
03783 
03784     stat = GdipAddPathClosedCurve2(path, points, count, tension);
03785     if(stat != Ok){
03786         GdipDeletePath(path);
03787         return stat;
03788     }
03789 
03790     stat = GdipFillPath(graphics, brush, path);
03791     if(stat != Ok){
03792         GdipDeletePath(path);
03793         return stat;
03794     }
03795 
03796     GdipDeletePath(path);
03797 
03798     return Ok;
03799 }
03800 
03801 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
03802     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
03803 {
03804     GpPointF *ptf;
03805     GpStatus stat;
03806     INT i;
03807 
03808     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
03809             count, tension, fill);
03810 
03811     if(!points || count == 0)
03812         return InvalidParameter;
03813 
03814     if(count == 1)    /* Do nothing */
03815         return Ok;
03816 
03817     ptf = GdipAlloc(sizeof(GpPointF)*count);
03818     if(!ptf)
03819         return OutOfMemory;
03820 
03821     for(i = 0;i < count;i++){
03822         ptf[i].X = (REAL)points[i].X;
03823         ptf[i].Y = (REAL)points[i].Y;
03824     }
03825 
03826     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
03827 
03828     GdipFree(ptf);
03829 
03830     return stat;
03831 }
03832 
03833 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
03834     GDIPCONST GpPointF *points, INT count)
03835 {
03836     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
03837     return GdipFillClosedCurve2(graphics, brush, points, count,
03838                0.5f, FillModeAlternate);
03839 }
03840 
03841 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
03842     GDIPCONST GpPoint *points, INT count)
03843 {
03844     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
03845     return GdipFillClosedCurve2I(graphics, brush, points, count,
03846                0.5f, FillModeAlternate);
03847 }
03848 
03849 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
03850     REAL y, REAL width, REAL height)
03851 {
03852     GpStatus stat;
03853     GpPath *path;
03854 
03855     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
03856 
03857     if(!graphics || !brush)
03858         return InvalidParameter;
03859 
03860     if(graphics->busy)
03861         return ObjectBusy;
03862 
03863     stat = GdipCreatePath(FillModeAlternate, &path);
03864 
03865     if (stat == Ok)
03866     {
03867         stat = GdipAddPathEllipse(path, x, y, width, height);
03868 
03869         if (stat == Ok)
03870             stat = GdipFillPath(graphics, brush, path);
03871 
03872         GdipDeletePath(path);
03873     }
03874 
03875     return stat;
03876 }
03877 
03878 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
03879     INT y, INT width, INT height)
03880 {
03881     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
03882 
03883     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
03884 }
03885 
03886 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
03887 {
03888     INT save_state;
03889     GpStatus retval;
03890 
03891     if(!graphics->hdc || !brush_can_fill_path(brush))
03892         return NotImplemented;
03893 
03894     save_state = SaveDC(graphics->hdc);
03895     EndPath(graphics->hdc);
03896     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
03897                                                                     : WINDING));
03898 
03899     BeginPath(graphics->hdc);
03900     retval = draw_poly(graphics, NULL, path->pathdata.Points,
03901                        path->pathdata.Types, path->pathdata.Count, FALSE);
03902 
03903     if(retval != Ok)
03904         goto end;
03905 
03906     EndPath(graphics->hdc);
03907     brush_fill_path(graphics, brush);
03908 
03909     retval = Ok;
03910 
03911 end:
03912     RestoreDC(graphics->hdc, save_state);
03913 
03914     return retval;
03915 }
03916 
03917 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
03918 {
03919     GpStatus stat;
03920     GpRegion *rgn;
03921 
03922     if (!brush_can_fill_pixels(brush))
03923         return NotImplemented;
03924 
03925     /* FIXME: This could probably be done more efficiently without regions. */
03926 
03927     stat = GdipCreateRegionPath(path, &rgn);
03928 
03929     if (stat == Ok)
03930     {
03931         stat = GdipFillRegion(graphics, brush, rgn);
03932 
03933         GdipDeleteRegion(rgn);
03934     }
03935 
03936     return stat;
03937 }
03938 
03939 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
03940 {
03941     GpStatus stat = NotImplemented;
03942 
03943     TRACE("(%p, %p, %p)\n", graphics, brush, path);
03944 
03945     if(!brush || !graphics || !path)
03946         return InvalidParameter;
03947 
03948     if(graphics->busy)
03949         return ObjectBusy;
03950 
03951     if (!graphics->image)
03952         stat = GDI32_GdipFillPath(graphics, brush, path);
03953 
03954     if (stat == NotImplemented)
03955         stat = SOFTWARE_GdipFillPath(graphics, brush, path);
03956 
03957     if (stat == NotImplemented)
03958     {
03959         FIXME("Not implemented for brushtype %i\n", brush->bt);
03960         stat = Ok;
03961     }
03962 
03963     return stat;
03964 }
03965 
03966 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
03967     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
03968 {
03969     GpStatus stat;
03970     GpPath *path;
03971 
03972     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
03973             graphics, brush, x, y, width, height, startAngle, sweepAngle);
03974 
03975     if(!graphics || !brush)
03976         return InvalidParameter;
03977 
03978     if(graphics->busy)
03979         return ObjectBusy;
03980 
03981     stat = GdipCreatePath(FillModeAlternate, &path);
03982 
03983     if (stat == Ok)
03984     {
03985         stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
03986 
03987         if (stat == Ok)
03988             stat = GdipFillPath(graphics, brush, path);
03989 
03990         GdipDeletePath(path);
03991     }
03992 
03993     return stat;
03994 }
03995 
03996 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
03997     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
03998 {
03999     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
04000             graphics, brush, x, y, width, height, startAngle, sweepAngle);
04001 
04002     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
04003 }
04004 
04005 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
04006     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
04007 {
04008     GpStatus stat;
04009     GpPath *path;
04010 
04011     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
04012 
04013     if(!graphics || !brush || !points || !count)
04014         return InvalidParameter;
04015 
04016     if(graphics->busy)
04017         return ObjectBusy;
04018 
04019     stat = GdipCreatePath(fillMode, &path);
04020 
04021     if (stat == Ok)
04022     {
04023         stat = GdipAddPathPolygon(path, points, count);
04024 
04025         if (stat == Ok)
04026             stat = GdipFillPath(graphics, brush, path);
04027 
04028         GdipDeletePath(path);
04029     }
04030 
04031     return stat;
04032 }
04033 
04034 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
04035     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
04036 {
04037     GpStatus stat;
04038     GpPath *path;
04039 
04040     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
04041 
04042     if(!graphics || !brush || !points || !count)
04043         return InvalidParameter;
04044 
04045     if(graphics->busy)
04046         return ObjectBusy;
04047 
04048     stat = GdipCreatePath(fillMode, &path);
04049 
04050     if (stat == Ok)
04051     {
04052         stat = GdipAddPathPolygonI(path, points, count);
04053 
04054         if (stat == Ok)
04055             stat = GdipFillPath(graphics, brush, path);
04056 
04057         GdipDeletePath(path);
04058     }
04059 
04060     return stat;
04061 }
04062 
04063 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
04064     GDIPCONST GpPointF *points, INT count)
04065 {
04066     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
04067 
04068     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
04069 }
04070 
04071 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
04072     GDIPCONST GpPoint *points, INT count)
04073 {
04074     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
04075 
04076     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
04077 }
04078 
04079 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
04080     REAL x, REAL y, REAL width, REAL height)
04081 {
04082     GpStatus stat;
04083     GpPath *path;
04084 
04085     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
04086 
04087     if(!graphics || !brush)
04088         return InvalidParameter;
04089 
04090     if(graphics->busy)
04091         return ObjectBusy;
04092 
04093     stat = GdipCreatePath(FillModeAlternate, &path);
04094 
04095     if (stat == Ok)
04096     {
04097         stat = GdipAddPathRectangle(path, x, y, width, height);
04098 
04099         if (stat == Ok)
04100             stat = GdipFillPath(graphics, brush, path);
04101 
04102         GdipDeletePath(path);
04103     }
04104 
04105     return stat;
04106 }
04107 
04108 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
04109     INT x, INT y, INT width, INT height)
04110 {
04111     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
04112 
04113     return GdipFillRectangle(graphics, brush, x, y, width, height);
04114 }
04115 
04116 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
04117     INT count)
04118 {
04119     GpStatus ret;
04120     INT i;
04121 
04122     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
04123 
04124     if(!rects)
04125         return InvalidParameter;
04126 
04127     for(i = 0; i < count; i++){
04128         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
04129         if(ret != Ok)   return ret;
04130     }
04131 
04132     return Ok;
04133 }
04134 
04135 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
04136     INT count)
04137 {
04138     GpRectF *rectsF;
04139     GpStatus ret;
04140     INT i;
04141 
04142     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
04143 
04144     if(!rects || count <= 0)
04145         return InvalidParameter;
04146 
04147     rectsF = GdipAlloc(sizeof(GpRectF)*count);
04148     if(!rectsF)
04149         return OutOfMemory;
04150 
04151     for(i = 0; i < count; i++){
04152         rectsF[i].X      = (REAL)rects[i].X;
04153         rectsF[i].Y      = (REAL)rects[i].Y;
04154         rectsF[i].X      = (REAL)rects[i].Width;
04155         rectsF[i].Height = (REAL)rects[i].Height;
04156     }
04157 
04158     ret = GdipFillRectangles(graphics,brush,rectsF,count);
04159     GdipFree(rectsF);
04160 
04161     return ret;
04162 }
04163 
04164 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
04165     GpRegion* region)
04166 {
04167     INT save_state;
04168     GpStatus status;
04169     HRGN hrgn;
04170     RECT rc;
04171 
04172     if(!graphics->hdc || !brush_can_fill_path(brush))
04173         return NotImplemented;
04174 
04175     status = GdipGetRegionHRgn(region, graphics, &hrgn);
04176     if(status != Ok)
04177         return status;
04178 
04179     save_state = SaveDC(graphics->hdc);
04180     EndPath(graphics->hdc);
04181 
04182     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
04183 
04184     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
04185     {
04186         BeginPath(graphics->hdc);
04187         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
04188         EndPath(graphics->hdc);
04189 
04190         brush_fill_path(graphics, brush);
04191     }
04192 
04193     RestoreDC(graphics->hdc, save_state);
04194 
04195     DeleteObject(hrgn);
04196 
04197     return Ok;
04198 }
04199 
04200 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
04201     GpRegion* region)
04202 {
04203     GpStatus stat;
04204     GpRegion *temp_region;
04205     GpMatrix *world_to_device;
04206     GpRectF graphics_bounds;
04207     DWORD *pixel_data;
04208     HRGN hregion;
04209     RECT bound_rect;
04210     GpRect gp_bound_rect;
04211 
04212     if (!brush_can_fill_pixels(brush))
04213         return NotImplemented;
04214 
04215     stat = get_graphics_bounds(graphics, &graphics_bounds);
04216 
04217     if (stat == Ok)
04218         stat = GdipCloneRegion(region, &temp_region);
04219 
04220     if (stat == Ok)
04221     {
04222         stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
04223             CoordinateSpaceWorld, &world_to_device);
04224 
04225         if (stat == Ok)
04226         {
04227             stat = GdipTransformRegion(temp_region, world_to_device);
04228 
04229             GdipDeleteMatrix(world_to_device);
04230         }
04231 
04232         if (stat == Ok)
04233             stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
04234 
04235         if (stat == Ok)
04236             stat = GdipGetRegionHRgn(temp_region, NULL, &hregion);
04237 
04238         GdipDeleteRegion(temp_region);
04239     }
04240 
04241     if (stat == Ok && GetRgnBox(hregion, &bound_rect) == NULLREGION)
04242     {
04243         DeleteObject(hregion);
04244         return Ok;
04245     }
04246 
04247     if (stat == Ok)
04248     {
04249         gp_bound_rect.X = bound_rect.left;
04250         gp_bound_rect.Y = bound_rect.top;
04251         gp_bound_rect.Width = bound_rect.right - bound_rect.left;
04252         gp_bound_rect.Height = bound_rect.bottom - bound_rect.top;
04253 
04254         pixel_data = GdipAlloc(sizeof(*pixel_data) * gp_bound_rect.Width * gp_bound_rect.Height);
04255         if (!pixel_data)
04256             stat = OutOfMemory;
04257 
04258         if (stat == Ok)
04259         {
04260             stat = brush_fill_pixels(graphics, brush, pixel_data,
04261                 &gp_bound_rect, gp_bound_rect.Width);
04262 
04263             if (stat == Ok)
04264                 stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
04265                     gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
04266                     gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion);
04267 
04268             GdipFree(pixel_data);
04269         }
04270 
04271         DeleteObject(hregion);
04272     }
04273 
04274     return stat;
04275 }
04276 
04277 /*****************************************************************************
04278  * GdipFillRegion [GDIPLUS.@]
04279  */
04280 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
04281         GpRegion* region)
04282 {
04283     GpStatus stat = NotImplemented;
04284 
04285     TRACE("(%p, %p, %p)\n", graphics, brush, region);
04286 
04287     if (!(graphics && brush && region))
04288         return InvalidParameter;
04289 
04290     if(graphics->busy)
04291         return ObjectBusy;
04292 
04293     if (!graphics->image)
04294         stat = GDI32_GdipFillRegion(graphics, brush, region);
04295 
04296     if (stat == NotImplemented)
04297         stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
04298 
04299     if (stat == NotImplemented)
04300     {
04301         FIXME("not implemented for brushtype %i\n", brush->bt);
04302         stat = Ok;
04303     }
04304 
04305     return stat;
04306 }
04307 
04308 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
04309 {
04310     TRACE("(%p,%u)\n", graphics, intention);
04311 
04312     if(!graphics)
04313         return InvalidParameter;
04314 
04315     if(graphics->busy)
04316         return ObjectBusy;
04317 
04318     /* We have no internal operation queue, so there's no need to clear it. */
04319 
04320     if (graphics->hdc)
04321         GdiFlush();
04322 
04323     return Ok;
04324 }
04325 
04326 /*****************************************************************************
04327  * GdipGetClipBounds [GDIPLUS.@]
04328  */
04329 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
04330 {
04331     TRACE("(%p, %p)\n", graphics, rect);
04332 
04333     if(!graphics)
04334         return InvalidParameter;
04335 
04336     if(graphics->busy)
04337         return ObjectBusy;
04338 
04339     return GdipGetRegionBounds(graphics->clip, graphics, rect);
04340 }
04341 
04342 /*****************************************************************************
04343  * GdipGetClipBoundsI [GDIPLUS.@]
04344  */
04345 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
04346 {
04347     TRACE("(%p, %p)\n", graphics, rect);
04348 
04349     if(!graphics)
04350         return InvalidParameter;
04351 
04352     if(graphics->busy)
04353         return ObjectBusy;
04354 
04355     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
04356 }
04357 
04358 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
04359 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
04360     CompositingMode *mode)
04361 {
04362     TRACE("(%p, %p)\n", graphics, mode);
04363 
04364     if(!graphics || !mode)
04365         return InvalidParameter;
04366 
04367     if(graphics->busy)
04368         return ObjectBusy;
04369 
04370     *mode = graphics->compmode;
04371 
04372     return Ok;
04373 }
04374 
04375 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
04376 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
04377     CompositingQuality *quality)
04378 {
04379     TRACE("(%p, %p)\n", graphics, quality);
04380 
04381     if(!graphics || !quality)
04382         return InvalidParameter;
04383 
04384     if(graphics->busy)
04385         return ObjectBusy;
04386 
04387     *quality = graphics->compqual;
04388 
04389     return Ok;
04390 }
04391 
04392 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
04393 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
04394     InterpolationMode *mode)
04395 {
04396     TRACE("(%p, %p)\n", graphics, mode);
04397 
04398     if(!graphics || !mode)
04399         return InvalidParameter;
04400 
04401     if(graphics->busy)
04402         return ObjectBusy;
04403 
04404     *mode = graphics->interpolation;
04405 
04406     return Ok;
04407 }
04408 
04409 /* FIXME: Need to handle color depths less than 24bpp */
04410 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
04411 {
04412     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
04413 
04414     if(!graphics || !argb)
04415         return InvalidParameter;
04416 
04417     if(graphics->busy)
04418         return ObjectBusy;
04419 
04420     return Ok;
04421 }
04422 
04423 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
04424 {
04425     TRACE("(%p, %p)\n", graphics, scale);
04426 
04427     if(!graphics || !scale)
04428         return InvalidParameter;
04429 
04430     if(graphics->busy)
04431         return ObjectBusy;
04432 
04433     *scale = graphics->scale;
04434 
04435     return Ok;
04436 }
04437 
04438 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
04439 {
04440     TRACE("(%p, %p)\n", graphics, unit);
04441 
04442     if(!graphics || !unit)
04443         return InvalidParameter;
04444 
04445     if(graphics->busy)
04446         return ObjectBusy;
04447 
04448     *unit = graphics->unit;
04449 
04450     return Ok;
04451 }
04452 
04453 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
04454 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
04455     *mode)
04456 {
04457     TRACE("(%p, %p)\n", graphics, mode);
04458 
04459     if(!graphics || !mode)
04460         return InvalidParameter;
04461 
04462     if(graphics->busy)
04463         return ObjectBusy;
04464 
04465     *mode = graphics->pixeloffset;
04466 
04467     return Ok;
04468 }
04469 
04470 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
04471 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
04472 {
04473     TRACE("(%p, %p)\n", graphics, mode);
04474 
04475     if(!graphics || !mode)
04476         return InvalidParameter;
04477 
04478     if(graphics->busy)
04479         return ObjectBusy;
04480 
04481     *mode = graphics->smoothing;
04482 
04483     return Ok;
04484 }
04485 
04486 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
04487 {
04488     TRACE("(%p, %p)\n", graphics, contrast);
04489 
04490     if(!graphics || !contrast)
04491         return InvalidParameter;
04492 
04493     *contrast = graphics->textcontrast;
04494 
04495     return Ok;
04496 }
04497 
04498 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
04499 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
04500     TextRenderingHint *hint)
04501 {
04502     TRACE("(%p, %p)\n", graphics, hint);
04503 
04504     if(!graphics || !hint)
04505         return InvalidParameter;
04506 
04507     if(graphics->busy)
04508         return ObjectBusy;
04509 
04510     *hint = graphics->texthint;
04511 
04512     return Ok;
04513 }
04514 
04515 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
04516 {
04517     GpRegion *clip_rgn;
04518     GpStatus stat;
04519 
04520     TRACE("(%p, %p)\n", graphics, rect);
04521 
04522     if(!graphics || !rect)
04523         return InvalidParameter;
04524 
04525     if(graphics->busy)
04526         return ObjectBusy;
04527 
04528     /* intersect window and graphics clipping regions */
04529     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
04530         return stat;
04531 
04532     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
04533         goto cleanup;
04534 
04535     /* get bounds of the region */
04536     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
04537 
04538 cleanup:
04539     GdipDeleteRegion(clip_rgn);
04540 
04541     return stat;
04542 }
04543 
04544 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
04545 {
04546     GpRectF rectf;
04547     GpStatus stat;
04548 
04549     TRACE("(%p, %p)\n", graphics, rect);
04550 
04551     if(!graphics || !rect)
04552         return InvalidParameter;
04553 
04554     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
04555     {
04556         rect->X = roundr(rectf.X);
04557         rect->Y = roundr(rectf.Y);
04558         rect->Width  = roundr(rectf.Width);
04559         rect->Height = roundr(rectf.Height);
04560     }
04561 
04562     return stat;
04563 }
04564 
04565 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
04566 {
04567     TRACE("(%p, %p)\n", graphics, matrix);
04568 
04569     if(!graphics || !matrix)
04570         return InvalidParameter;
04571 
04572     if(graphics->busy)
04573         return ObjectBusy;
04574 
04575     *matrix = *graphics->worldtrans;
04576     return Ok;
04577 }
04578 
04579 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
04580 {
04581     GpSolidFill *brush;
04582     GpStatus stat;
04583     GpRectF wnd_rect;
04584 
04585     TRACE("(%p, %x)\n", graphics, color);
04586 
04587     if(!graphics)
04588         return InvalidParameter;
04589 
04590     if(graphics->busy)
04591         return ObjectBusy;
04592 
04593     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
04594         return stat;
04595 
04596     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
04597         GdipDeleteBrush((GpBrush*)brush);
04598         return stat;
04599     }
04600 
04601     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
04602                                                  wnd_rect.Width, wnd_rect.Height);
04603 
04604     GdipDeleteBrush((GpBrush*)brush);
04605 
04606     return Ok;
04607 }
04608 
04609 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
04610 {
04611     TRACE("(%p, %p)\n", graphics, res);
04612 
04613     if(!graphics || !res)
04614         return InvalidParameter;
04615 
04616     return GdipIsEmptyRegion(graphics->clip, graphics, res);
04617 }
04618 
04619 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
04620 {
04621     GpStatus stat;
04622     GpRegion* rgn;
04623     GpPointF pt;
04624 
04625     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
04626 
04627     if(!graphics || !result)
04628         return InvalidParameter;
04629 
04630     if(graphics->busy)
04631         return ObjectBusy;
04632 
04633     pt.X = x;
04634     pt.Y = y;
04635     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
04636                    CoordinateSpaceWorld, &pt, 1)) != Ok)
04637         return stat;
04638 
04639     if((stat = GdipCreateRegion(&rgn)) != Ok)
04640         return stat;
04641 
04642     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
04643         goto cleanup;
04644 
04645     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
04646 
04647 cleanup:
04648     GdipDeleteRegion(rgn);
04649     return stat;
04650 }
04651 
04652 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
04653 {
04654     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
04655 }
04656 
04657 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
04658 {
04659     GpStatus stat;
04660     GpRegion* rgn;
04661     GpPointF pts[2];
04662 
04663     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
04664 
04665     if(!graphics || !result)
04666         return InvalidParameter;
04667 
04668     if(graphics->busy)
04669         return ObjectBusy;
04670 
04671     pts[0].X = x;
04672     pts[0].Y = y;
04673     pts[1].X = x + width;
04674     pts[1].Y = y + height;
04675 
04676     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
04677                     CoordinateSpaceWorld, pts, 2)) != Ok)
04678         return stat;
04679 
04680     pts[1].X -= pts[0].X;
04681     pts[1].Y -= pts[0].Y;
04682 
04683     if((stat = GdipCreateRegion(&rgn)) != Ok)
04684         return stat;
04685 
04686     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
04687         goto cleanup;
04688 
04689     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
04690 
04691 cleanup:
04692     GdipDeleteRegion(rgn);
04693     return stat;
04694 }
04695 
04696 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
04697 {
04698     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
04699 }
04700 
04701 GpStatus gdip_format_string(HDC hdc,
04702     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
04703     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
04704     gdip_format_string_callback callback, void *user_data)
04705 {
04706     WCHAR* stringdup;
04707     int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
04708         nheight, lineend, lineno = 0;
04709     RectF bounds;
04710     StringAlignment halign;
04711     GpStatus stat = Ok;
04712     SIZE size;
04713     HotkeyPrefix hkprefix;
04714     INT *hotkeyprefix_offsets=NULL;
04715     INT hotkeyprefix_count=0;
04716     INT hotkeyprefix_pos=0, hotkeyprefix_end_pos=0;
04717     int seen_prefix=0;
04718 
04719     if(length == -1) length = lstrlenW(string);
04720 
04721     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
04722     if(!stringdup) return OutOfMemory;
04723 
04724     nwidth = roundr(rect->Width);
04725     nheight = roundr(rect->Height);
04726 
04727     if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
04728     if (rect->Height >= INT_MAX || rect->Height < 0.5) nheight = INT_MAX;
04729 
04730     if (format)
04731         hkprefix = format->hkprefix;
04732     else
04733         hkprefix = HotkeyPrefixNone;
04734 
04735     if (hkprefix == HotkeyPrefixShow)
04736     {
04737         for (i=0; i<length; i++)
04738         {
04739             if (string[i] == '&')
04740                 hotkeyprefix_count++;
04741         }
04742     }
04743 
04744     if (hotkeyprefix_count)
04745         hotkeyprefix_offsets = GdipAlloc(sizeof(INT) * hotkeyprefix_count);
04746 
04747     hotkeyprefix_count = 0;
04748 
04749     for(i = 0, j = 0; i < length; i++){
04750         /* FIXME: This makes the indexes passed to callback inaccurate. */
04751         if(!isprintW(string[i]) && (string[i] != '\n'))
04752             continue;
04753 
04754         if (seen_prefix && hkprefix == HotkeyPrefixShow && string[i] != '&')
04755             hotkeyprefix_offsets[hotkeyprefix_count++] = j;
04756         else if (!seen_prefix && hkprefix != HotkeyPrefixNone && string[i] == '&')
04757         {
04758             seen_prefix = 1;
04759             continue;
04760         }
04761 
04762         seen_prefix = 0;
04763 
04764         stringdup[j] = string[i];
04765         j++;
04766     }
04767 
04768     length = j;
04769 
04770     if (format) halign = format->align;
04771     else halign = StringAlignmentNear;
04772 
04773     while(sum < length){
04774         GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
04775                               nwidth, &fit, NULL, &size);
04776         fitcpy = fit;
04777 
04778         if(fit == 0)
04779             break;
04780 
04781         for(lret = 0; lret < fit; lret++)
04782             if(*(stringdup + sum + lret) == '\n')
04783                 break;
04784 
04785         /* Line break code (may look strange, but it imitates windows). */
04786         if(lret < fit)
04787             lineend = fit = lret;    /* this is not an off-by-one error */
04788         else if(fit < (length - sum)){
04789             if(*(stringdup + sum + fit) == ' ')
04790                 while(*(stringdup + sum + fit) == ' ')
04791                     fit++;
04792             else
04793                 while(*(stringdup + sum + fit - 1) != ' '){
04794                     fit--;
04795 
04796                     if(*(stringdup + sum + fit) == '\t')
04797                         break;
04798 
04799                     if(fit == 0){
04800                         fit = fitcpy;
04801                         break;
04802                     }
04803                 }
04804             lineend = fit;
04805             while(*(stringdup + sum + lineend - 1) == ' ' ||
04806                   *(stringdup + sum + lineend - 1) == '\t')
04807                 lineend--;
04808         }
04809         else
04810             lineend = fit;
04811 
04812         GetTextExtentExPointW(hdc, stringdup + sum, lineend,
04813                               nwidth, &j, NULL, &size);
04814 
04815         bounds.Width = size.cx;
04816 
04817         if(height + size.cy > nheight)
04818             bounds.Height = nheight - (height + size.cy);
04819         else
04820             bounds.Height = size.cy;
04821 
04822         bounds.Y = rect->Y + height;
04823 
04824         switch (halign)
04825         {
04826         case StringAlignmentNear:
04827         default:
04828             bounds.X = rect->X;
04829             break;
04830         case StringAlignmentCenter:
04831             bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
04832             break;
04833         case StringAlignmentFar:
04834             bounds.X = rect->X + rect->Width - bounds.Width;
04835             break;
04836         }
04837 
04838         for (hotkeyprefix_end_pos=hotkeyprefix_pos; hotkeyprefix_end_pos<hotkeyprefix_count; hotkeyprefix_end_pos++)
04839             if (hotkeyprefix_offsets[hotkeyprefix_end_pos] >= sum + lineend)
04840                 break;
04841 
04842         stat = callback(hdc, stringdup, sum, lineend,
04843             font, rect, format, lineno, &bounds,
04844             &hotkeyprefix_offsets[hotkeyprefix_pos],
04845             hotkeyprefix_end_pos-hotkeyprefix_pos, user_data);
04846 
04847         if (stat != Ok)
04848             break;
04849 
04850         sum += fit + (lret < fitcpy ? 1 : 0);
04851         height += size.cy;
04852         lineno++;
04853 
04854         hotkeyprefix_pos = hotkeyprefix_end_pos;
04855 
04856         if(height > nheight)
04857             break;
04858 
04859         /* Stop if this was a linewrap (but not if it was a linebreak). */
04860         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
04861             break;
04862     }
04863 
04864     GdipFree(stringdup);
04865     GdipFree(hotkeyprefix_offsets);
04866 
04867     return stat;
04868 }
04869 
04870 struct measure_ranges_args {
04871     GpRegion **regions;
04872 };
04873 
04874 static GpStatus measure_ranges_callback(HDC hdc,
04875     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
04876     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
04877     INT lineno, const RectF *bounds, INT *underlined_indexes,
04878     INT underlined_index_count, void *user_data)
04879 {
04880     int i;
04881     GpStatus stat = Ok;
04882     struct measure_ranges_args *args = user_data;
04883 
04884     for (i=0; i<format->range_count; i++)
04885     {
04886         INT range_start = max(index, format->character_ranges[i].First);
04887         INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
04888         if (range_start < range_end)
04889         {
04890             GpRectF range_rect;
04891             SIZE range_size;
04892 
04893             range_rect.Y = bounds->Y;
04894             range_rect.Height = bounds->Height;
04895 
04896             GetTextExtentExPointW(hdc, string + index, range_start - index,
04897                                   INT_MAX, NULL, NULL, &range_size);
04898             range_rect.X = bounds->X + range_size.cx;
04899 
04900             GetTextExtentExPointW(hdc, string + index, range_end - index,
04901                                   INT_MAX, NULL, NULL, &range_size);
04902             range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
04903 
04904             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
04905             if (stat != Ok)
04906                 break;
04907         }
04908     }
04909 
04910     return stat;
04911 }
04912 
04913 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
04914         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
04915         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
04916         INT regionCount, GpRegion** regions)
04917 {
04918     GpStatus stat;
04919     int i;
04920     LOGFONTW lfw;
04921     HFONT oldfont;
04922     struct measure_ranges_args args;
04923     HDC hdc, temp_hdc=NULL;
04924 
04925     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
04926             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
04927 
04928     if (!(graphics && string && font && layoutRect && stringFormat && regions))
04929         return InvalidParameter;
04930 
04931     if (regionCount < stringFormat->range_count)
04932         return InvalidParameter;
04933 
04934     stat = GdipGetLogFontW((GpFont *)font, graphics, &lfw);
04935     if (stat != Ok) return stat;
04936 
04937     if(!graphics->hdc)
04938     {
04939         hdc = temp_hdc = CreateCompatibleDC(0);
04940         if (!temp_hdc) return OutOfMemory;
04941     }
04942     else
04943         hdc = graphics->hdc;
04944 
04945     if (stringFormat->attr)
04946         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
04947 
04948     oldfont = SelectObject(hdc, CreateFontIndirectW(&lfw));
04949 
04950     for (i=0; i<stringFormat->range_count; i++)
04951     {
04952         stat = GdipSetEmpty(regions[i]);
04953         if (stat != Ok)
04954             return stat;
04955     }
04956 
04957     args.regions = regions;
04958 
04959     stat = gdip_format_string(hdc, string, length, font, layoutRect, stringFormat,
04960         measure_ranges_callback, &args);
04961 
04962     DeleteObject(SelectObject(hdc, oldfont));
04963 
04964     if (temp_hdc)
04965         DeleteDC(temp_hdc);
04966 
04967     return stat;
04968 }
04969 
04970 struct measure_string_args {
04971     RectF *bounds;
04972     INT *codepointsfitted;
04973     INT *linesfilled;
04974     REAL rel_width, rel_height;
04975 };
04976 
04977 static GpStatus measure_string_callback(HDC hdc,
04978     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
04979     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
04980     INT lineno, const RectF *bounds, INT *underlined_indexes,
04981     INT underlined_index_count, void *user_data)
04982 {
04983     struct measure_string_args *args = user_data;
04984     REAL new_width, new_height;
04985 
04986     new_width = bounds->Width / args->rel_width;
04987     new_height = (bounds->Height + bounds->Y - args->bounds->Y) / args->rel_height;
04988 
04989     if (new_width > args->bounds->Width)
04990         args->bounds->Width = new_width;
04991 
04992     if (new_height > args->bounds->Height)
04993         args->bounds->Height = new_height;
04994 
04995     if (args->codepointsfitted)
04996         *args->codepointsfitted = index + length;
04997 
04998     if (args->linesfilled)
04999         (*args->linesfilled)++;
05000 
05001     return Ok;
05002 }
05003 
05004 /* Find the smallest rectangle that bounds the text when it is printed in rect
05005  * according to the format options listed in format. If rect has 0 width and
05006  * height, then just find the smallest rectangle that bounds the text when it's
05007  * printed at location (rect->X, rect-Y). */
05008 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
05009     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
05010     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
05011     INT *codepointsfitted, INT *linesfilled)
05012 {
05013     HFONT oldfont, gdifont;
05014     struct measure_string_args args;
05015     HDC temp_hdc=NULL, hdc;
05016     GpPointF pt[3];
05017 
05018     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
05019         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
05020         bounds, codepointsfitted, linesfilled);
05021 
05022     if(!graphics || !string || !font || !rect || !bounds)
05023         return InvalidParameter;
05024 
05025     if(!graphics->hdc)
05026     {
05027         hdc = temp_hdc = CreateCompatibleDC(0);
05028         if (!temp_hdc) return OutOfMemory;
05029     }
05030     else
05031         hdc = graphics->hdc;
05032 
05033     if(linesfilled) *linesfilled = 0;
05034     if(codepointsfitted) *codepointsfitted = 0;
05035 
05036     if(format)
05037         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
05038 
05039     pt[0].X = 0.0;
05040     pt[0].Y = 0.0;
05041     pt[1].X = 1.0;
05042     pt[1].Y = 0.0;
05043     pt[2].X = 0.0;
05044     pt[2].Y = 1.0;
05045     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
05046     args.rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
05047                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
05048     args.rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
05049                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
05050 
05051     get_font_hfont(graphics, font, &gdifont);
05052     oldfont = SelectObject(hdc, gdifont);
05053 
05054     bounds->X = rect->X;
05055     bounds->Y = rect->Y;
05056     bounds->Width = 0.0;
05057     bounds->Height = 0.0;
05058 
05059     args.bounds = bounds;
05060     args.codepointsfitted = codepointsfitted;
05061     args.linesfilled = linesfilled;
05062 
05063     gdip_format_string(hdc, string, length, font, rect, format,
05064         measure_string_callback, &args);
05065 
05066     SelectObject(hdc, oldfont);
05067     DeleteObject(gdifont);
05068 
05069     if (temp_hdc)
05070         DeleteDC(temp_hdc);
05071 
05072     return Ok;
05073 }
05074 
05075 struct draw_string_args {
05076     GpGraphics *graphics;
05077     GDIPCONST GpBrush *brush;
05078     REAL x, y, rel_width, rel_height, ascent;
05079 };
05080 
05081 static GpStatus draw_string_callback(HDC hdc,
05082     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
05083     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
05084     INT lineno, const RectF *bounds, INT *underlined_indexes,
05085     INT underlined_index_count, void *user_data)
05086 {
05087     struct draw_string_args *args = user_data;
05088     PointF position;
05089     GpStatus stat;
05090 
05091     position.X = args->x + bounds->X / args->rel_width;
05092     position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
05093 
05094     stat = GdipDrawDriverString(args->graphics, &string[index], length, font,
05095         args->brush, &position,
05096         DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
05097 
05098     if (stat == Ok && underlined_index_count)
05099     {
05100         OUTLINETEXTMETRICW otm;
05101         REAL underline_y, underline_height;
05102         int i;
05103 
05104         GetOutlineTextMetricsW(hdc, sizeof(otm), &otm);
05105 
05106         underline_height = otm.otmsUnderscoreSize / args->rel_height;
05107         underline_y = position.Y - otm.otmsUnderscorePosition / args->rel_height - underline_height / 2;
05108 
05109         for (i=0; i<underlined_index_count; i++)
05110         {
05111             REAL start_x, end_x;
05112             SIZE text_size;
05113             INT ofs = underlined_indexes[i] - index;
05114 
05115             GetTextExtentExPointW(hdc, string + index, ofs, INT_MAX, NULL, NULL, &text_size);
05116             start_x = text_size.cx / args->rel_width;
05117 
05118             GetTextExtentExPointW(hdc, string + index, ofs+1, INT_MAX, NULL, NULL, &text_size);
05119             end_x = text_size.cx / args->rel_width;
05120 
05121             GdipFillRectangle(args->graphics, (GpBrush*)args->brush, position.X+start_x, underline_y, end_x-start_x, underline_height);
05122         }
05123     }
05124 
05125     return stat;
05126 }
05127 
05128 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
05129     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
05130     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
05131 {
05132     HRGN rgn = NULL;
05133     HFONT gdifont;
05134     GpPointF pt[3], rectcpy[4];
05135     POINT corners[4];
05136     REAL rel_width, rel_height;
05137     INT save_state;
05138     REAL offsety = 0.0;
05139     struct draw_string_args args;
05140     RectF scaled_rect;
05141     HDC hdc, temp_hdc=NULL;
05142     TEXTMETRICW textmetric;
05143 
05144     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
05145         length, font, debugstr_rectf(rect), format, brush);
05146 
05147     if(!graphics || !string || !font || !brush || !rect)
05148         return InvalidParameter;
05149 
05150     if(graphics->hdc)
05151     {
05152         hdc = graphics->hdc;
05153     }
05154     else
05155     {
05156         hdc = temp_hdc = CreateCompatibleDC(0);
05157     }
05158 
05159     if(format){
05160         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
05161 
05162         /* Should be no need to explicitly test for StringAlignmentNear as
05163          * that is default behavior if no alignment is passed. */
05164         if(format->vertalign != StringAlignmentNear){
05165             RectF bounds;
05166             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
05167 
05168             if(format->vertalign == StringAlignmentCenter)
05169                 offsety = (rect->Height - bounds.Height) / 2;
05170             else if(format->vertalign == StringAlignmentFar)
05171                 offsety = (rect->Height - bounds.Height);
05172         }
05173     }
05174 
05175     save_state = SaveDC(hdc);
05176 
05177     pt[0].X = 0.0;
05178     pt[0].Y = 0.0;
05179     pt[1].X = 1.0;
05180     pt[1].Y = 0.0;
05181     pt[2].X = 0.0;
05182     pt[2].Y = 1.0;
05183     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
05184     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
05185                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
05186     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
05187                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
05188 
05189     rectcpy[3].X = rectcpy[0].X = rect->X;
05190     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
05191     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
05192     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
05193     transform_and_round_points(graphics, corners, rectcpy, 4);
05194 
05195     scaled_rect.X = 0.0;
05196     scaled_rect.Y = 0.0;
05197     scaled_rect.Width = rel_width * rect->Width;
05198     scaled_rect.Height = rel_height * rect->Height;
05199 
05200     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
05201     {
05202         /* FIXME: If only the width or only the height is 0, we should probably still clip */
05203         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
05204         SelectClipRgn(hdc, rgn);
05205     }
05206 
05207     get_font_hfont(graphics, font, &gdifont);
05208     SelectObject(hdc, gdifont);
05209 
05210     args.graphics = graphics;
05211     args.brush = brush;
05212 
05213     args.x = rect->X;
05214     args.y = rect->Y + offsety;
05215 
05216     args.rel_width = rel_width;
05217     args.rel_height = rel_height;
05218 
05219     GetTextMetricsW(hdc, &textmetric);
05220     args.ascent = textmetric.tmAscent / rel_height;
05221 
05222     gdip_format_string(hdc, string, length, font, &scaled_rect, format,
05223         draw_string_callback, &args);
05224 
05225     DeleteObject(rgn);
05226     DeleteObject(gdifont);
05227 
05228     RestoreDC(hdc, save_state);
05229 
05230     DeleteDC(temp_hdc);
05231 
05232     return Ok;
05233 }
05234 
05235 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
05236 {
05237     TRACE("(%p)\n", graphics);
05238 
05239     if(!graphics)
05240         return InvalidParameter;
05241 
05242     if(graphics->busy)
05243         return ObjectBusy;
05244 
05245     return GdipSetInfinite(graphics->clip);
05246 }
05247 
05248 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
05249 {
05250     TRACE("(%p)\n", graphics);
05251 
05252     if(!graphics)
05253         return InvalidParameter;
05254 
05255     if(graphics->busy)
05256         return ObjectBusy;
05257 
05258     graphics->worldtrans->matrix[0] = 1.0;
05259     graphics->worldtrans->matrix[1] = 0.0;
05260     graphics->worldtrans->matrix[2] = 0.0;
05261     graphics->worldtrans->matrix[3] = 1.0;
05262     graphics->worldtrans->matrix[4] = 0.0;
05263     graphics->worldtrans->matrix[5] = 0.0;
05264 
05265     return Ok;
05266 }
05267 
05268 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
05269 {
05270     return GdipEndContainer(graphics, state);
05271 }
05272 
05273 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
05274     GpMatrixOrder order)
05275 {
05276     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
05277 
05278     if(!graphics)
05279         return InvalidParameter;
05280 
05281     if(graphics->busy)
05282         return ObjectBusy;
05283 
05284     return GdipRotateMatrix(graphics->worldtrans, angle, order);
05285 }
05286 
05287 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
05288 {
05289     return GdipBeginContainer2(graphics, state);
05290 }
05291 
05292 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
05293         GraphicsContainer *state)
05294 {
05295     GraphicsContainerItem *container;
05296     GpStatus sts;
05297 
05298     TRACE("(%p, %p)\n", graphics, state);
05299 
05300     if(!graphics || !state)
05301         return InvalidParameter;
05302 
05303     sts = init_container(&container, graphics);
05304     if(sts != Ok)
05305         return sts;
05306 
05307     list_add_head(&graphics->containers, &container->entry);
05308     *state = graphics->contid = container->contid;
05309 
05310     return Ok;
05311 }
05312 
05313 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
05314 {
05315     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
05316     return NotImplemented;
05317 }
05318 
05319 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
05320 {
05321     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
05322     return NotImplemented;
05323 }
05324 
05325 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
05326 {
05327     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
05328     return NotImplemented;
05329 }
05330 
05331 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
05332 {
05333     GpStatus sts;
05334     GraphicsContainerItem *container, *container2;
05335 
05336     TRACE("(%p, %x)\n", graphics, state);
05337 
05338     if(!graphics)
05339         return InvalidParameter;
05340 
05341     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
05342         if(container->contid == state)
05343             break;
05344     }
05345 
05346     /* did not find a matching container */
05347     if(&container->entry == &graphics->containers)
05348         return Ok;
05349 
05350     sts = restore_container(graphics, container);
05351     if(sts != Ok)
05352         return sts;
05353 
05354     /* remove all of the containers on top of the found container */
05355     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
05356         if(container->contid == state)
05357             break;
05358         list_remove(&container->entry);
05359         delete_container(container);
05360     }
05361 
05362     list_remove(&container->entry);
05363     delete_container(container);
05364 
05365     return Ok;
05366 }
05367 
05368 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
05369     REAL sy, GpMatrixOrder order)
05370 {
05371     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
05372 
05373     if(!graphics)
05374         return InvalidParameter;
05375 
05376     if(graphics->busy)
05377         return ObjectBusy;
05378 
05379     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
05380 }
05381 
05382 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
05383     CombineMode mode)
05384 {
05385     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
05386 
05387     if(!graphics || !srcgraphics)
05388         return InvalidParameter;
05389 
05390     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
05391 }
05392 
05393 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
05394     CompositingMode mode)
05395 {
05396     TRACE("(%p, %d)\n", graphics, mode);
05397 
05398     if(!graphics)
05399         return InvalidParameter;
05400 
05401     if(graphics->busy)
05402         return ObjectBusy;
05403 
05404     graphics->compmode = mode;
05405 
05406     return Ok;
05407 }
05408 
05409 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
05410     CompositingQuality quality)
05411 {
05412     TRACE("(%p, %d)\n", graphics, quality);
05413 
05414     if(!graphics)
05415         return InvalidParameter;
05416 
05417     if(graphics->busy)
05418         return ObjectBusy;
05419 
05420     graphics->compqual = quality;
05421 
05422     return Ok;
05423 }
05424 
05425 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
05426     InterpolationMode mode)
05427 {
05428     TRACE("(%p, %d)\n", graphics, mode);
05429 
05430     if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
05431         return InvalidParameter;
05432 
05433     if(graphics->busy)
05434         return ObjectBusy;
05435 
05436     if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
05437         mode = InterpolationModeBilinear;
05438 
05439     if (mode == InterpolationModeHighQuality)
05440         mode = InterpolationModeHighQualityBicubic;
05441 
05442     graphics->interpolation = mode;
05443 
05444     return Ok;
05445 }
05446 
05447 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
05448 {
05449     TRACE("(%p, %.2f)\n", graphics, scale);
05450 
05451     if(!graphics || (scale <= 0.0))
05452         return InvalidParameter;
05453 
05454     if(graphics->busy)
05455         return ObjectBusy;
05456 
05457     graphics->scale = scale;
05458 
05459     return Ok;
05460 }
05461 
05462 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
05463 {
05464     TRACE("(%p, %d)\n", graphics, unit);
05465 
05466     if(!graphics)
05467         return InvalidParameter;
05468 
05469     if(graphics->busy)
05470         return ObjectBusy;
05471 
05472     if(unit == UnitWorld)
05473         return InvalidParameter;
05474 
05475     graphics->unit = unit;
05476 
05477     return Ok;
05478 }
05479 
05480 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
05481     mode)
05482 {
05483     TRACE("(%p, %d)\n", graphics, mode);
05484 
05485     if(!graphics)
05486         return InvalidParameter;
05487 
05488     if(graphics->busy)
05489         return ObjectBusy;
05490 
05491     graphics->pixeloffset = mode;
05492 
05493     return Ok;
05494 }
05495 
05496 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
05497 {
05498     static int calls;
05499 
05500     TRACE("(%p,%i,%i)\n", graphics, x, y);
05501 
05502     if (!(calls++))
05503         FIXME("value is unused in rendering\n");
05504 
05505     if (!graphics)
05506         return InvalidParameter;
05507 
05508     graphics->origin_x = x;
05509     graphics->origin_y = y;
05510 
05511     return Ok;
05512 }
05513 
05514 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
05515 {
05516     TRACE("(%p,%p,%p)\n", graphics, x, y);
05517 
05518     if (!graphics || !x || !y)
05519         return InvalidParameter;
05520 
05521     *x = graphics->origin_x;
05522     *y = graphics->origin_y;
05523 
05524     return Ok;
05525 }
05526 
05527 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
05528 {
05529     TRACE("(%p, %d)\n", graphics, mode);
05530 
05531     if(!graphics)
05532         return InvalidParameter;
05533 
05534     if(graphics->busy)
05535         return ObjectBusy;
05536 
05537     graphics->smoothing = mode;
05538 
05539     return Ok;
05540 }
05541 
05542 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
05543 {
05544     TRACE("(%p, %d)\n", graphics, contrast);
05545 
05546     if(!graphics)
05547         return InvalidParameter;
05548 
05549     graphics->textcontrast = contrast;
05550 
05551     return Ok;
05552 }
05553 
05554 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
05555     TextRenderingHint hint)
05556 {
05557     TRACE("(%p, %d)\n", graphics, hint);
05558 
05559     if(!graphics || hint > TextRenderingHintClearTypeGridFit)
05560         return InvalidParameter;
05561 
05562     if(graphics->busy)
05563         return ObjectBusy;
05564 
05565     graphics->texthint = hint;
05566 
05567     return Ok;
05568 }
05569 
05570 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
05571 {
05572     TRACE("(%p, %p)\n", graphics, matrix);
05573 
05574     if(!graphics || !matrix)
05575         return InvalidParameter;
05576 
05577     if(graphics->busy)
05578         return ObjectBusy;
05579 
05580     GdipDeleteMatrix(graphics->worldtrans);
05581     return GdipCloneMatrix(matrix, &graphics->worldtrans);
05582 }
05583 
05584 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
05585     REAL dy, GpMatrixOrder order)
05586 {
05587     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
05588 
05589     if(!graphics)
05590         return InvalidParameter;
05591 
05592     if(graphics->busy)
05593         return ObjectBusy;
05594 
05595     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
05596 }
05597 
05598 /*****************************************************************************
05599  * GdipSetClipHrgn [GDIPLUS.@]
05600  */
05601 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
05602 {
05603     GpRegion *region;
05604     GpStatus status;
05605 
05606     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
05607 
05608     if(!graphics)
05609         return InvalidParameter;
05610 
05611     status = GdipCreateRegionHrgn(hrgn, &region);
05612     if(status != Ok)
05613         return status;
05614 
05615     status = GdipSetClipRegion(graphics, region, mode);
05616 
05617     GdipDeleteRegion(region);
05618     return status;
05619 }
05620 
05621 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
05622 {
05623     TRACE("(%p, %p, %d)\n", graphics, path, mode);
05624 
05625     if(!graphics)
05626         return InvalidParameter;
05627 
05628     if(graphics->busy)
05629         return ObjectBusy;
05630 
05631     return GdipCombineRegionPath(graphics->clip, path, mode);
05632 }
05633 
05634 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
05635                                     REAL width, REAL height,
05636                                     CombineMode mode)
05637 {
05638     GpRectF rect;
05639 
05640     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
05641 
05642     if(!graphics)
05643         return InvalidParameter;
05644 
05645     if(graphics->busy)
05646         return ObjectBusy;
05647 
05648     rect.X = x;
05649     rect.Y = y;
05650     rect.Width  = width;
05651     rect.Height = height;
05652 
05653     return GdipCombineRegionRect(graphics->clip, &rect, mode);
05654 }
05655 
05656 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
05657                                      INT width, INT height,
05658                                      CombineMode mode)
05659 {
05660     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
05661 
05662     if(!graphics)
05663         return InvalidParameter;
05664 
05665     if(graphics->busy)
05666         return ObjectBusy;
05667 
05668     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
05669 }
05670 
05671 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
05672                                       CombineMode mode)
05673 {
05674     TRACE("(%p, %p, %d)\n", graphics, region, mode);
05675 
05676     if(!graphics || !region)
05677         return InvalidParameter;
05678 
05679     if(graphics->busy)
05680         return ObjectBusy;
05681 
05682     return GdipCombineRegionRegion(graphics->clip, region, mode);
05683 }
05684 
05685 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
05686     UINT limitDpi)
05687 {
05688     static int calls;
05689 
05690     TRACE("(%p,%u)\n", metafile, limitDpi);
05691 
05692     if(!(calls++))
05693         FIXME("not implemented\n");
05694 
05695     return NotImplemented;
05696 }
05697 
05698 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
05699     INT count)
05700 {
05701     INT save_state;
05702     POINT *pti;
05703 
05704     TRACE("(%p, %p, %d)\n", graphics, points, count);
05705 
05706     if(!graphics || !pen || count<=0)
05707         return InvalidParameter;
05708 
05709     if(graphics->busy)
05710         return ObjectBusy;
05711 
05712     if (!graphics->hdc)
05713     {
05714         FIXME("graphics object has no HDC\n");
05715         return Ok;
05716     }
05717 
05718     pti = GdipAlloc(sizeof(POINT) * count);
05719 
05720     save_state = prepare_dc(graphics, pen);
05721     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
05722 
05723     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
05724     Polygon(graphics->hdc, pti, count);
05725 
05726     restore_dc(graphics, save_state);
05727     GdipFree(pti);
05728 
05729     return Ok;
05730 }
05731 
05732 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
05733     INT count)
05734 {
05735     GpStatus ret;
05736     GpPointF *ptf;
05737     INT i;
05738 
05739     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
05740 
05741     if(count<=0)    return InvalidParameter;
05742     ptf = GdipAlloc(sizeof(GpPointF) * count);
05743 
05744     for(i = 0;i < count; i++){
05745         ptf[i].X = (REAL)points[i].X;
05746         ptf[i].Y = (REAL)points[i].Y;
05747     }
05748 
05749     ret = GdipDrawPolygon(graphics,pen,ptf,count);
05750     GdipFree(ptf);
05751 
05752     return ret;
05753 }
05754 
05755 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
05756 {
05757     TRACE("(%p, %p)\n", graphics, dpi);
05758 
05759     if(!graphics || !dpi)
05760         return InvalidParameter;
05761 
05762     if(graphics->busy)
05763         return ObjectBusy;
05764 
05765     if (graphics->image)
05766         *dpi = graphics->image->xres;
05767     else
05768         *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
05769 
05770     return Ok;
05771 }
05772 
05773 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
05774 {
05775     TRACE("(%p, %p)\n", graphics, dpi);
05776 
05777     if(!graphics || !dpi)
05778         return InvalidParameter;
05779 
05780     if(graphics->busy)
05781         return ObjectBusy;
05782 
05783     if (graphics->image)
05784         *dpi = graphics->image->yres;
05785     else
05786         *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
05787 
05788     return Ok;
05789 }
05790 
05791 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
05792     GpMatrixOrder order)
05793 {
05794     GpMatrix m;
05795     GpStatus ret;
05796 
05797     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
05798 
05799     if(!graphics || !matrix)
05800         return InvalidParameter;
05801 
05802     if(graphics->busy)
05803         return ObjectBusy;
05804 
05805     m = *(graphics->worldtrans);
05806 
05807     ret = GdipMultiplyMatrix(&m, matrix, order);
05808     if(ret == Ok)
05809         *(graphics->worldtrans) = m;
05810 
05811     return ret;
05812 }
05813 
05814 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
05815 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
05816 
05817 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
05818 {
05819     GpStatus stat=Ok;
05820 
05821     TRACE("(%p, %p)\n", graphics, hdc);
05822 
05823     if(!graphics || !hdc)
05824         return InvalidParameter;
05825 
05826     if(graphics->busy)
05827         return ObjectBusy;
05828 
05829     if (graphics->image && graphics->image->type == ImageTypeMetafile)
05830     {
05831         stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
05832     }
05833     else if (!graphics->hdc ||
05834         (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
05835     {
05836         /* Create a fake HDC and fill it with a constant color. */
05837         HDC temp_hdc;
05838         HBITMAP hbitmap;
05839         GpRectF bounds;
05840         BITMAPINFOHEADER bmih;
05841         int i;
05842 
05843         stat = get_graphics_bounds(graphics, &bounds);
05844         if (stat != Ok)
05845             return stat;
05846 
05847         graphics->temp_hbitmap_width = bounds.Width;
05848         graphics->temp_hbitmap_height = bounds.Height;
05849 
05850         bmih.biSize = sizeof(bmih);
05851         bmih.biWidth = graphics->temp_hbitmap_width;
05852         bmih.biHeight = -graphics->temp_hbitmap_height;
05853         bmih.biPlanes = 1;
05854         bmih.biBitCount = 32;
05855         bmih.biCompression = BI_RGB;
05856         bmih.biSizeImage = 0;
05857         bmih.biXPelsPerMeter = 0;
05858         bmih.biYPelsPerMeter = 0;
05859         bmih.biClrUsed = 0;
05860         bmih.biClrImportant = 0;
05861 
05862         hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
05863             (void**)&graphics->temp_bits, NULL, 0);
05864         if (!hbitmap)
05865             return GenericError;
05866 
05867         temp_hdc = CreateCompatibleDC(0);
05868         if (!temp_hdc)
05869         {
05870             DeleteObject(hbitmap);
05871             return GenericError;
05872         }
05873 
05874         for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
05875             ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
05876 
05877         SelectObject(temp_hdc, hbitmap);
05878 
05879         graphics->temp_hbitmap = hbitmap;
05880         *hdc = graphics->temp_hdc = temp_hdc;
05881     }
05882     else
05883     {
05884         *hdc = graphics->hdc;
05885     }
05886 
05887     if (stat == Ok)
05888         graphics->busy = TRUE;
05889 
05890     return stat;
05891 }
05892 
05893 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
05894 {
05895     GpStatus stat=Ok;
05896 
05897     TRACE("(%p, %p)\n", graphics, hdc);
05898 
05899     if(!graphics || !hdc || !graphics->busy)
05900         return InvalidParameter;
05901 
05902     if (graphics->image && graphics->image->type == ImageTypeMetafile)
05903     {
05904         stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
05905     }
05906     else if (graphics->temp_hdc == hdc)
05907     {
05908         DWORD* pos;
05909         int i;
05910 
05911         /* Find the pixels that have changed, and mark them as opaque. */
05912         pos = (DWORD*)graphics->temp_bits;
05913         for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
05914         {
05915             if (*pos != DC_BACKGROUND_KEY)
05916             {
05917                 *pos |= 0xff000000;
05918             }
05919             pos++;
05920         }
05921 
05922         /* Write the changed pixels to the real target. */
05923         alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
05924             graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
05925             graphics->temp_hbitmap_width * 4);
05926 
05927         /* Clean up. */
05928         DeleteDC(graphics->temp_hdc);
05929         DeleteObject(graphics->temp_hbitmap);
05930         graphics->temp_hdc = NULL;
05931         graphics->temp_hbitmap = NULL;
05932     }
05933     else if (hdc != graphics->hdc)
05934     {
05935         stat = InvalidParameter;
05936     }
05937 
05938     if (stat == Ok)
05939         graphics->busy = FALSE;
05940 
05941     return stat;
05942 }
05943 
05944 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
05945 {
05946     GpRegion *clip;
05947     GpStatus status;
05948 
05949     TRACE("(%p, %p)\n", graphics, region);
05950 
05951     if(!graphics || !region)
05952         return InvalidParameter;
05953 
05954     if(graphics->busy)
05955         return ObjectBusy;
05956 
05957     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
05958         return status;
05959 
05960     /* free everything except root node and header */
05961     delete_element(&region->node);
05962     memcpy(region, clip, sizeof(GpRegion));
05963     GdipFree(clip);
05964 
05965     return Ok;
05966 }
05967 
05968 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
05969         GpCoordinateSpace src_space, GpMatrix **matrix)
05970 {
05971     GpStatus stat = GdipCreateMatrix(matrix);
05972     REAL unitscale;
05973 
05974     if (dst_space != src_space && stat == Ok)
05975     {
05976         unitscale = convert_unit(graphics_res(graphics), graphics->unit);
05977 
05978         if(graphics->unit != UnitDisplay)
05979             unitscale *= graphics->scale;
05980 
05981         /* transform from src_space to CoordinateSpacePage */
05982         switch (src_space)
05983         {
05984         case CoordinateSpaceWorld:
05985             GdipMultiplyMatrix(*matrix, graphics->worldtrans, MatrixOrderAppend);
05986             break;
05987         case CoordinateSpacePage:
05988             break;
05989         case CoordinateSpaceDevice:
05990             GdipScaleMatrix(*matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
05991             break;
05992         }
05993 
05994         /* transform from CoordinateSpacePage to dst_space */
05995         switch (dst_space)
05996         {
05997         case CoordinateSpaceWorld:
05998             {
05999                 GpMatrix *inverted_transform;
06000                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
06001                 if (stat == Ok)
06002                 {
06003                     stat = GdipInvertMatrix(inverted_transform);
06004                     if (stat == Ok)
06005                         GdipMultiplyMatrix(*matrix, inverted_transform, MatrixOrderAppend);
06006                     GdipDeleteMatrix(inverted_transform);
06007                 }
06008                 break;
06009             }
06010         case CoordinateSpacePage:
06011             break;
06012         case CoordinateSpaceDevice:
06013             GdipScaleMatrix(*matrix, unitscale, unitscale, MatrixOrderAppend);
06014             break;
06015         }
06016     }
06017     return stat;
06018 }
06019 
06020 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
06021                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
06022 {
06023     GpMatrix *matrix;
06024     GpStatus stat;
06025 
06026     if(!graphics || !points || count <= 0)
06027         return InvalidParameter;
06028 
06029     if(graphics->busy)
06030         return ObjectBusy;
06031 
06032     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
06033 
06034     if (src_space == dst_space) return Ok;
06035 
06036     stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
06037 
06038     if (stat == Ok)
06039     {
06040         stat = GdipTransformMatrixPoints(matrix, points, count);
06041 
06042         GdipDeleteMatrix(matrix);
06043     }
06044 
06045     return stat;
06046 }
06047 
06048 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
06049                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
06050 {
06051     GpPointF *pointsF;
06052     GpStatus ret;
06053     INT i;
06054 
06055     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
06056 
06057     if(count <= 0)
06058         return InvalidParameter;
06059 
06060     pointsF = GdipAlloc(sizeof(GpPointF) * count);
06061     if(!pointsF)
06062         return OutOfMemory;
06063 
06064     for(i = 0; i < count; i++){
06065         pointsF[i].X = (REAL)points[i].X;
06066         pointsF[i].Y = (REAL)points[i].Y;
06067     }
06068 
06069     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
06070 
06071     if(ret == Ok)
06072         for(i = 0; i < count; i++){
06073             points[i].X = roundr(pointsF[i].X);
06074             points[i].Y = roundr(pointsF[i].Y);
06075         }
06076     GdipFree(pointsF);
06077 
06078     return ret;
06079 }
06080 
06081 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
06082 {
06083     static int calls;
06084 
06085     TRACE("\n");
06086 
06087     if (!calls++)
06088       FIXME("stub\n");
06089 
06090     return NULL;
06091 }
06092 
06093 /*****************************************************************************
06094  * GdipTranslateClip [GDIPLUS.@]
06095  */
06096 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
06097 {
06098     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
06099 
06100     if(!graphics)
06101         return InvalidParameter;
06102 
06103     if(graphics->busy)
06104         return ObjectBusy;
06105 
06106     return GdipTranslateRegion(graphics->clip, dx, dy);
06107 }
06108 
06109 /*****************************************************************************
06110  * GdipTranslateClipI [GDIPLUS.@]
06111  */
06112 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
06113 {
06114     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
06115 
06116     if(!graphics)
06117         return InvalidParameter;
06118 
06119     if(graphics->busy)
06120         return ObjectBusy;
06121 
06122     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
06123 }
06124 
06125 
06126 /*****************************************************************************
06127  * GdipMeasureDriverString [GDIPLUS.@]
06128  */
06129 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
06130                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
06131                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
06132 {
06133     static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
06134     HFONT hfont;
06135     HDC hdc;
06136     REAL min_x, min_y, max_x, max_y, x, y;
06137     int i;
06138     TEXTMETRICW textmetric;
06139     const WORD *glyph_indices;
06140     WORD *dynamic_glyph_indices=NULL;
06141     REAL rel_width, rel_height, ascent, descent;
06142     GpPointF pt[3];
06143 
06144     TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
06145 
06146     if (!graphics || !text || !font || !positions || !boundingBox)
06147         return InvalidParameter;
06148 
06149     if (length == -1)
06150         length = strlenW(text);
06151 
06152     if (length == 0)
06153     {
06154         boundingBox->X = 0.0;
06155         boundingBox->Y = 0.0;
06156         boundingBox->Width = 0.0;
06157         boundingBox->Height = 0.0;
06158     }
06159 
06160     if (flags & unsupported_flags)
06161         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
06162 
06163     if (matrix)
06164         FIXME("Ignoring matrix\n");
06165 
06166     get_font_hfont(graphics, font, &hfont);
06167 
06168     hdc = CreateCompatibleDC(0);
06169     SelectObject(hdc, hfont);
06170 
06171     GetTextMetricsW(hdc, &textmetric);
06172 
06173     pt[0].X = 0.0;
06174     pt[0].Y = 0.0;
06175     pt[1].X = 1.0;
06176     pt[1].Y = 0.0;
06177     pt[2].X = 0.0;
06178     pt[2].Y = 1.0;
06179     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
06180     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
06181                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
06182     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
06183                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
06184 
06185     if (flags & DriverStringOptionsCmapLookup)
06186     {
06187         glyph_indices = dynamic_glyph_indices = GdipAlloc(sizeof(WORD) * length);
06188         if (!glyph_indices)
06189         {
06190             DeleteDC(hdc);
06191             DeleteObject(hfont);
06192             return OutOfMemory;
06193         }
06194 
06195         GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
06196     }
06197     else
06198         glyph_indices = text;
06199 
06200     min_x = max_x = x = positions[0].X;
06201     min_y = max_y = y = positions[0].Y;
06202 
06203     ascent = textmetric.tmAscent / rel_height;
06204     descent = textmetric.tmDescent / rel_height;
06205 
06206     for (i=0; i<length; i++)
06207     {
06208         int char_width;
06209         ABC abc;
06210 
06211         if (!(flags & DriverStringOptionsRealizedAdvance))
06212         {
06213             x = positions[i].X;
06214             y = positions[i].Y;
06215         }
06216 
06217         GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
06218         char_width = abc.abcA + abc.abcB + abc.abcB;
06219 
06220         if (min_y > y - ascent) min_y = y - ascent;
06221         if (max_y < y + descent) max_y = y + descent;
06222         if (min_x > x) min_x = x;
06223 
06224         x += char_width / rel_width;
06225 
06226         if (max_x < x) max_x = x;
06227     }
06228 
06229     GdipFree(dynamic_glyph_indices);
06230     DeleteDC(hdc);
06231     DeleteObject(hfont);
06232 
06233     boundingBox->X = min_x;
06234     boundingBox->Y = min_y;
06235     boundingBox->Width = max_x - min_x;
06236     boundingBox->Height = max_y - min_y;
06237 
06238     return Ok;
06239 }
06240 
06241 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
06242                                      GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
06243                                      GDIPCONST PointF *positions, INT flags,
06244                                      GDIPCONST GpMatrix *matrix )
06245 {
06246     static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
06247     INT save_state;
06248     GpPointF pt;
06249     HFONT hfont;
06250     UINT eto_flags=0;
06251 
06252     if (flags & unsupported_flags)
06253         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
06254 
06255     if (matrix)
06256         FIXME("Ignoring matrix\n");
06257 
06258     if (!(flags & DriverStringOptionsCmapLookup))
06259         eto_flags |= ETO_GLYPH_INDEX;
06260 
06261     save_state = SaveDC(graphics->hdc);
06262     SetBkMode(graphics->hdc, TRANSPARENT);
06263     SetTextColor(graphics->hdc, get_gdi_brush_color(brush));
06264 
06265     pt = positions[0];
06266     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
06267 
06268     get_font_hfont(graphics, font, &hfont);
06269     SelectObject(graphics->hdc, hfont);
06270 
06271     SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
06272 
06273     ExtTextOutW(graphics->hdc, roundr(pt.X), roundr(pt.Y), eto_flags, NULL, text, length, NULL);
06274 
06275     RestoreDC(graphics->hdc, save_state);
06276 
06277     DeleteObject(hfont);
06278 
06279     return Ok;
06280 }
06281 
06282 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
06283                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
06284                                          GDIPCONST PointF *positions, INT flags,
06285                                          GDIPCONST GpMatrix *matrix )
06286 {
06287     static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
06288     GpStatus stat;
06289     PointF *real_positions, real_position;
06290     POINT *pti;
06291     HFONT hfont;
06292     HDC hdc;
06293     int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
06294     DWORD max_glyphsize=0;
06295     GLYPHMETRICS glyphmetrics;
06296     static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
06297     BYTE *glyph_mask;
06298     BYTE *text_mask;
06299     int text_mask_stride;
06300     BYTE *pixel_data;
06301     int pixel_data_stride;
06302     GpRect pixel_area;
06303     UINT ggo_flags = GGO_GRAY8_BITMAP;
06304 
06305     if (length <= 0)
06306         return Ok;
06307 
06308     if (!(flags & DriverStringOptionsCmapLookup))
06309         ggo_flags |= GGO_GLYPH_INDEX;
06310 
06311     if (flags & unsupported_flags)
06312         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
06313 
06314     if (matrix)
06315         FIXME("Ignoring matrix\n");
06316 
06317     pti = GdipAlloc(sizeof(POINT) * length);
06318     if (!pti)
06319         return OutOfMemory;
06320 
06321     if (flags & DriverStringOptionsRealizedAdvance)
06322     {
06323         real_position = positions[0];
06324 
06325         transform_and_round_points(graphics, pti, &real_position, 1);
06326     }
06327     else
06328     {
06329         real_positions = GdipAlloc(sizeof(PointF) * length);
06330         if (!real_positions)
06331         {
06332             GdipFree(pti);
06333             return OutOfMemory;
06334         }
06335 
06336         memcpy(real_positions, positions, sizeof(PointF) * length);
06337 
06338         transform_and_round_points(graphics, pti, real_positions, length);
06339 
06340         GdipFree(real_positions);
06341     }
06342 
06343     get_font_hfont(graphics, font, &hfont);
06344 
06345     hdc = CreateCompatibleDC(0);
06346     SelectObject(hdc, hfont);
06347 
06348     /* Get the boundaries of the text to be drawn */
06349     for (i=0; i<length; i++)
06350     {
06351         DWORD glyphsize;
06352         int left, top, right, bottom;
06353 
06354         glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
06355             &glyphmetrics, 0, NULL, &identity);
06356 
06357         if (glyphsize == GDI_ERROR)
06358         {
06359             ERR("GetGlyphOutlineW failed\n");
06360             GdipFree(pti);
06361             DeleteDC(hdc);
06362             DeleteObject(hfont);
06363             return GenericError;
06364         }
06365 
06366         if (glyphsize > max_glyphsize)
06367             max_glyphsize = glyphsize;
06368 
06369         left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
06370         top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
06371         right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
06372         bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
06373 
06374         if (left < min_x) min_x = left;
06375         if (top < min_y) min_y = top;
06376         if (right > max_x) max_x = right;
06377         if (bottom > max_y) max_y = bottom;
06378 
06379         if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
06380         {
06381             pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
06382             pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
06383         }
06384     }
06385 
06386     glyph_mask = GdipAlloc(max_glyphsize);
06387     text_mask = GdipAlloc((max_x - min_x) * (max_y - min_y));
06388     text_mask_stride = max_x - min_x;
06389 
06390     if (!(glyph_mask && text_mask))
06391     {
06392         GdipFree(glyph_mask);
06393         GdipFree(text_mask);
06394         GdipFree(pti);
06395         DeleteDC(hdc);
06396         DeleteObject(hfont);
06397         return OutOfMemory;
06398     }
06399 
06400     /* Generate a mask for the text */
06401     for (i=0; i<length; i++)
06402     {
06403         int left, top, stride;
06404 
06405         GetGlyphOutlineW(hdc, text[i], ggo_flags,
06406             &glyphmetrics, max_glyphsize, glyph_mask, &identity);
06407 
06408         left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
06409         top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
06410         stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
06411 
06412         for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
06413         {
06414             BYTE *glyph_val = glyph_mask + y * stride;
06415             BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
06416             for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
06417             {
06418                 *text_val = min(64, *text_val + *glyph_val);
06419                 glyph_val++;
06420                 text_val++;
06421             }
06422         }
06423     }
06424 
06425     GdipFree(pti);
06426     DeleteDC(hdc);
06427     DeleteObject(hfont);
06428     GdipFree(glyph_mask);
06429 
06430     /* get the brush data */
06431     pixel_data = GdipAlloc(4 * (max_x - min_x) * (max_y - min_y));
06432     if (!pixel_data)
06433     {
06434         GdipFree(text_mask);
06435         return OutOfMemory;
06436     }
06437 
06438     pixel_area.X = min_x;
06439     pixel_area.Y = min_y;
06440     pixel_area.Width = max_x - min_x;
06441     pixel_area.Height = max_y - min_y;
06442     pixel_data_stride = pixel_area.Width * 4;
06443 
06444     stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
06445     if (stat != Ok)
06446     {
06447         GdipFree(text_mask);
06448         GdipFree(pixel_data);
06449         return stat;
06450     }
06451 
06452     /* multiply the brush data by the mask */
06453     for (y=0; y<pixel_area.Height; y++)
06454     {
06455         BYTE *text_val = text_mask + text_mask_stride * y;
06456         BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
06457         for (x=0; x<pixel_area.Width; x++)
06458         {
06459             *pixel_val = (*pixel_val) * (*text_val) / 64;
06460             text_val++;
06461             pixel_val+=4;
06462         }
06463     }
06464 
06465     GdipFree(text_mask);
06466 
06467     /* draw the result */
06468     stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
06469         pixel_area.Height, pixel_data_stride);
06470 
06471     GdipFree(pixel_data);
06472 
06473     return stat;
06474 }
06475 
06476 /*****************************************************************************
06477  * GdipDrawDriverString [GDIPLUS.@]
06478  */
06479 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
06480                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
06481                                          GDIPCONST PointF *positions, INT flags,
06482                                          GDIPCONST GpMatrix *matrix )
06483 {
06484     GpStatus stat=NotImplemented;
06485 
06486     TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
06487 
06488     if (!graphics || !text || !font || !brush || !positions)
06489         return InvalidParameter;
06490 
06491     if (length == -1)
06492         length = strlenW(text);
06493 
06494     if (graphics->hdc &&
06495         ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
06496         brush->bt == BrushTypeSolidColor &&
06497         (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
06498         stat = GDI32_GdipDrawDriverString(graphics, text, length, font, brush,
06499             positions, flags, matrix);
06500 
06501     if (stat == NotImplemented)
06502         stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, brush,
06503             positions, flags, matrix);
06504 
06505     return stat;
06506 }
06507 
06508 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
06509                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
06510 {
06511     FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
06512     return NotImplemented;
06513 }
06514 
06515 /*****************************************************************************
06516  * GdipIsVisibleClipEmpty [GDIPLUS.@]
06517  */
06518 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
06519 {
06520     GpStatus stat;
06521     GpRegion* rgn;
06522 
06523     TRACE("(%p, %p)\n", graphics, res);
06524 
06525     if((stat = GdipCreateRegion(&rgn)) != Ok)
06526         return stat;
06527 
06528     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
06529         goto cleanup;
06530 
06531     stat = GdipIsEmptyRegion(rgn, graphics, res);
06532 
06533 cleanup:
06534     GdipDeleteRegion(rgn);
06535     return stat;
06536 }
06537 
06538 GpStatus WINGDIPAPI GdipResetPageTransform(GpGraphics *graphics)
06539 {
06540     static int calls;
06541 
06542     TRACE("(%p) stub\n", graphics);
06543 
06544     if(!(calls++))
06545         FIXME("not implemented\n");
06546 
06547     return NotImplemented;
06548 }

Generated on Sat May 26 2012 04:22:10 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.