- Sync comdlg32, crypt32, gdiplus, hhctrl.ocx to Wine-1.1.43.
authorAleksey Bragin <aleksey@reactos.org>
Tue, 20 Apr 2010 08:30:10 +0000 (08:30 +0000)
committerAleksey Bragin <aleksey@reactos.org>
Tue, 20 Apr 2010 08:30:10 +0000 (08:30 +0000)
svn path=/trunk/; revision=46955

12 files changed:
reactos/dll/win32/comdlg32/filedlg.c
reactos/dll/win32/comdlg32/fontdlg.c
reactos/dll/win32/comdlg32/printdlg.c
reactos/dll/win32/crypt32/decode.c
reactos/dll/win32/gdiplus/brush.c
reactos/dll/win32/gdiplus/font.c
reactos/dll/win32/gdiplus/gdiplus.spec
reactos/dll/win32/gdiplus/gdiplus_private.h
reactos/dll/win32/gdiplus/graphics.c
reactos/dll/win32/gdiplus/image.c
reactos/dll/win32/hhctrl.ocx/hhctrl.c
reactos/include/psdk/gdiplusflat.h

index 58b57cc..64cb393 100644 (file)
@@ -176,7 +176,7 @@ static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
 /* Internal functions used by the dialog */
 static LRESULT FILEDLG95_ResizeControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
 static LRESULT FILEDLG95_FillControls(HWND hwnd, WPARAM wParam, LPARAM lParam);
-static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam, LPARAM lParam);
+static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam);
 static LRESULT FILEDLG95_OnWMGetIShellBrowser(HWND hwnd);
 static BOOL    FILEDLG95_OnOpen(HWND hwnd);
 static LRESULT FILEDLG95_InitControls(HWND hwnd);
@@ -240,7 +240,7 @@ static BOOL GetFileName95(FileOpenDlgInfos *fodInfos)
 {
 
     LRESULT lRes;
-    LPCVOID template;
+    LPVOID template;
     HRSRC hRes;
     HANDLE hDlgTmpl = 0;
     HRESULT hr;
@@ -991,7 +991,7 @@ static LRESULT FILEDLG95_OnWMGetMMI( HWND hwnd, LPMINMAXINFO mmiptr)
  * vertically or horizontally to get out of the way. Only the "grip"
  * is moved in both directions to stay in the corner.
  */
-static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
+static LRESULT FILEDLG95_OnWMSize(HWND hwnd, WPARAM wParam)
 {
     RECT rc, rcview;
     int chgx, chgy;
@@ -1223,11 +1223,11 @@ INT_PTR CALLBACK FileOpenDlgProc95(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
          return 0;
        }
     case WM_SIZE:
-      return FILEDLG95_OnWMSize(hwnd, wParam, lParam);
+      return FILEDLG95_OnWMSize(hwnd, wParam);
     case WM_GETMINMAXINFO:
       return FILEDLG95_OnWMGetMMI( hwnd, (LPMINMAXINFO)lParam);
     case WM_COMMAND:
-      return FILEDLG95_OnWMCommand(hwnd, wParam, lParam);
+      return FILEDLG95_OnWMCommand(hwnd, wParam);
     case WM_DRAWITEM:
       {
         switch(((LPDRAWITEMSTRUCT)lParam)->CtlID)
@@ -1733,7 +1733,7 @@ void FILEDLG95_Clean(HWND hwnd)
  *
  * WM_COMMAND message handler
  */
-static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam, LPARAM lParam)
+static LRESULT FILEDLG95_OnWMCommand(HWND hwnd, WPARAM wParam)
 {
   WORD wNotifyCode = HIWORD(wParam); /* notification code */
   WORD wID = LOWORD(wParam);         /* item, control, or accelerator identifier */
@@ -3799,7 +3799,7 @@ BOOL FD32_GetTemplate(PFD31_DATA lfs)
 /***********************************************************************
  *                              FD32_WMMeasureItem           [internal]
  */
-static LONG FD32_WMMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
+static LONG FD32_WMMeasureItem(LPARAM lParam)
 {
     LPMEASUREITEMSTRUCT lpmeasure;
 
@@ -3832,7 +3832,7 @@ static INT_PTR CALLBACK FD32_FileOpenDlgProc(HWND hWnd, UINT wMsg,
         return FD31_WMInitDialog(hWnd, wParam, lParam);
 
     case WM_MEASUREITEM:
-        return FD32_WMMeasureItem(hWnd, wParam, lParam);
+        return FD32_WMMeasureItem(lParam);
 
     case WM_DRAWITEM:
         return FD31_WMDrawItem(hWnd, wParam, lParam, !lfs->open, (DRAWITEMSTRUCT *)lParam);
index 1ad881c..80f1ad9 100644 (file)
@@ -766,7 +766,7 @@ static LRESULT CFn_WMInitDialog(HWND hDlg, LPARAM lParam, LPCHOOSEFONTW lpcf)
 /***********************************************************************
  *           CFn_WMMeasureItem                           [internal]
  */
-static LRESULT CFn_WMMeasureItem(HWND hDlg, WPARAM wParam, LPARAM lParam)
+static LRESULT CFn_WMMeasureItem(HWND hDlg, LPARAM lParam)
 {
     HDC hdc;
     HFONT hfontprev;
@@ -794,7 +794,7 @@ static LRESULT CFn_WMMeasureItem(HWND hDlg, WPARAM wParam, LPARAM lParam)
 /***********************************************************************
  *           CFn_WMDrawItem                              [internal]
  */
-static LRESULT CFn_WMDrawItem(HWND hDlg, WPARAM wParam, LPARAM lParam)
+static LRESULT CFn_WMDrawItem(LPARAM lParam)
 {
     HBRUSH hBrush;
     WCHAR buffer[40];
@@ -1190,9 +1190,9 @@ static INT_PTR CALLBACK FormatCharDlgProcA(HWND hDlg, UINT uMsg, WPARAM wParam,
     switch (uMsg)
     {
     case WM_MEASUREITEM:
-        return CFn_WMMeasureItem(hDlg, wParam, lParam);
+        return CFn_WMMeasureItem(hDlg,lParam);
     case WM_DRAWITEM:
-        return CFn_WMDrawItem(hDlg, wParam, lParam);
+        return CFn_WMDrawItem(lParam);
     case WM_COMMAND:
         return CFn_WMCommand(hDlg, wParam, lParam, lpcfw);
     case WM_DESTROY:
@@ -1239,9 +1239,9 @@ static INT_PTR CALLBACK FormatCharDlgProcW(HWND hDlg, UINT uMsg, WPARAM wParam,
     switch (uMsg)
     {
     case WM_MEASUREITEM:
-        return CFn_WMMeasureItem(hDlg, wParam, lParam);
+        return CFn_WMMeasureItem(hDlg, lParam);
     case WM_DRAWITEM:
-        return CFn_WMDrawItem(hDlg, wParam, lParam);
+        return CFn_WMDrawItem(lParam);
     case WM_COMMAND:
         return CFn_WMCommand(hDlg, wParam, lParam, lpcf);
     case WM_DESTROY:
index f653251..74a06ff 100644 (file)
@@ -1504,7 +1504,7 @@ static LRESULT PRINTDLG_WMInitDialogW(HWND hDlg,
  *                              PRINTDLG_WMCommand               [internal]
  */
 static LRESULT PRINTDLG_WMCommandA(HWND hDlg, WPARAM wParam,
-                                   LPARAM lParam, PRINT_PTRA* PrintStructures)
+                                   PRINT_PTRA* PrintStructures)
 {
     LPPRINTDLGA lppd = PrintStructures->lpPrintDlg;
     UINT PrinterComboID = (lppd->Flags & PD_PRINTSETUP) ? cmb1 : cmb4;
@@ -1658,7 +1658,7 @@ static LRESULT PRINTDLG_WMCommandA(HWND hDlg, WPARAM wParam,
 }
 
 static LRESULT PRINTDLG_WMCommandW(HWND hDlg, WPARAM wParam,
-                       LPARAM lParam, PRINT_PTRW* PrintStructures)
+                                  PRINT_PTRW* PrintStructures)
 {
     LPPRINTDLGW lppd = PrintStructures->lpPrintDlg;
     UINT PrinterComboID = (lppd->Flags & PD_PRINTSETUP) ? cmb1 : cmb4;
@@ -1846,7 +1846,7 @@ static INT_PTR CALLBACK PrintDlgProcA(HWND hDlg, UINT uMsg, WPARAM wParam,
 
     switch (uMsg) {
     case WM_COMMAND:
-        return PRINTDLG_WMCommandA(hDlg, wParam, lParam, PrintStructures);
+        return PRINTDLG_WMCommandA(hDlg, wParam, PrintStructures);
 
     case WM_DESTROY:
        DestroyIcon(PrintStructures->hCollateIcon);
@@ -1892,7 +1892,7 @@ static INT_PTR CALLBACK PrintDlgProcW(HWND hDlg, UINT uMsg, WPARAM wParam,
 
     switch (uMsg) {
     case WM_COMMAND:
-        return PRINTDLG_WMCommandW(hDlg, wParam, lParam, PrintStructures);
+        return PRINTDLG_WMCommandW(hDlg, wParam, PrintStructures);
 
     case WM_DESTROY:
        DestroyIcon(PrintStructures->hCollateIcon);
index fb3d36e..655a5b2 100644 (file)
@@ -523,9 +523,11 @@ static BOOL CRYPT_AsnDecodeSequence(struct AsnDecodeSequenceItem items[],
 
                 for (i = 0; i < cItem; i++)
                 {
-                    bytesNeeded += items[i].size;
+                    if (items[i].size > items[i].minSize)
+                        bytesNeeded += items[i].size - items[i].minSize;
                     structSize = max( structSize, items[i].offset + items[i].minSize );
                 }
+                bytesNeeded += structSize;
                 if (pcbDecoded)
                     *pcbDecoded = 1 + lenBytes + cbDecoded;
                 if (!pvStructInfo)
index 2d28004..67be6b4 100644 (file)
@@ -1194,6 +1194,16 @@ GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
     return NotImplemented;
 }
 
+GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient *brush, INT *count)
+{
+    TRACE("(%p, %p)\n", brush, count);
+
+    if (!brush || !count)
+       return InvalidParameter;
+
+    return NotImplemented;
+}
+
 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
     GpWrapMode *wrapmode)
 {
@@ -1559,7 +1569,7 @@ GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
 }
 
 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
-    *grad, ARGB *argb, INT *count)
+    *grad, GDIPCONST ARGB *argb, INT *count)
 {
     static int calls;
 
index fda5428..3a29771 100644 (file)
@@ -489,6 +489,7 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi,
     switch (font->unit)
     {
         case UnitPixel:
+        case UnitWorld:
             *height = font_height;
             break;
         case UnitPoint:
@@ -520,7 +521,7 @@ GpStatus WINGDIPAPI GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi,
 static INT CALLBACK is_font_installed_proc(const LOGFONTW *elf,
                             const TEXTMETRICW *ntm, DWORD type, LPARAM lParam)
 {
-    if (!ntm)
+    if (!ntm || type == RASTER_FONTTYPE)
     {
         return 1;
     }
@@ -642,12 +643,14 @@ GpStatus WINGDIPAPI GdipCloneFontFamily(GpFontFamily* FontFamily, GpFontFamily**
 GpStatus WINGDIPAPI GdipGetFamilyName (GDIPCONST GpFontFamily *family,
                                        WCHAR *name, LANGID language)
 {
+    static int lang_fixme;
+
     if (family == NULL)
          return InvalidParameter;
 
     TRACE("%p, %p, %d\n", family, name, language);
 
-    if (language != LANG_NEUTRAL)
+    if (language != LANG_NEUTRAL && !lang_fixme++)
         FIXME("No support for handling of multiple languages!\n");
 
     lstrcpynW (name, family->FamilyName, LF_FACESIZE);
@@ -826,15 +829,21 @@ GpStatus WINGDIPAPI GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
  */
 GpStatus WINGDIPAPI GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
 {
-    /* FIXME: On Windows this is called Microsoft Sans Serif, this shouldn't
-     * affect anything */
-    static const WCHAR MSSansSerif[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
+    GpStatus stat;
+    static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
+    static const WCHAR Tahoma[] = {'T','a','h','o','m','a','\0'};
 
     TRACE("(%p)\n", nativeFamily);
 
     if (nativeFamily == NULL) return InvalidParameter;
 
-    return GdipCreateFontFamilyFromName(MSSansSerif, NULL, nativeFamily);
+    stat = GdipCreateFontFamilyFromName(MicrosoftSansSerif, NULL, nativeFamily);
+
+    if (stat == FontFamilyNotFound)
+        /* FIXME: Microsoft Sans Serif is not installed on Wine. */
+        stat = GdipCreateFontFamilyFromName(Tahoma, NULL, nativeFamily);
+
+    return stat;
 }
 
 /*****************************************************************************
@@ -957,6 +966,9 @@ static INT CALLBACK add_font_proc(const LOGFONTW *lfw, const TEXTMETRICW *ntm,
     GpFontCollection* fonts = (GpFontCollection*)lParam;
     int i;
 
+    if (type == RASTER_FONTTYPE)
+        return 1;
+
     /* skip duplicates */
     for (i=0; i<fonts->count; i++)
         if (strcmpiW(lfw->lfFaceName, fonts->FontFamilies[i]->FamilyName) == 0)
index e6cea5a..14e7fbf 100644 (file)
 @ stub GdipGetPathGradientPresetBlendCount
 @ stdcall GdipGetPathGradientRect(ptr ptr)
 @ stdcall GdipGetPathGradientRectI(ptr ptr)
-@ stub GdipGetPathGradientSurroundColorCount
+@ stdcall GdipGetPathGradientSurroundColorCount(ptr ptr)
 @ stdcall GdipGetPathGradientSurroundColorsWithCount(ptr ptr ptr)
 @ stub GdipGetPathGradientTransform
 @ stdcall GdipGetPathGradientWrapMode(ptr ptr)
index ca1cba6..5ff7125 100644 (file)
@@ -78,6 +78,29 @@ static inline REAL deg2rad(REAL degrees)
     return M_PI * degrees / 180.0;
 }
 
+static inline ARGB color_over(ARGB bg, ARGB fg)
+{
+    BYTE b, g, r, a;
+    BYTE bg_alpha, fg_alpha;
+
+    fg_alpha = (fg>>24)&0xff;
+
+    if (fg_alpha == 0xff) return fg;
+
+    if (fg_alpha == 0) return bg;
+
+    bg_alpha = (((bg>>24)&0xff) * (0xff-fg_alpha)) / 0xff;
+
+    if (bg_alpha == 0) return fg;
+
+    a = bg_alpha + fg_alpha;
+    b = ((bg&0xff)*bg_alpha + (fg&0xff)*fg_alpha)*0xff/a;
+    g = (((bg>>8)&0xff)*bg_alpha + ((fg>>8)&0xff)*fg_alpha)*0xff/a;
+    r = (((bg>>16)&0xff)*bg_alpha + ((fg>>16)&0xff)*fg_alpha)*0xff/a;
+
+    return (a<<24)|(r<<16)|(g<<8)|b;
+}
+
 extern const char *debugstr_rectf(CONST RectF* rc);
 
 extern const char *debugstr_pointf(CONST PointF* pt);
@@ -112,6 +135,7 @@ struct GpGraphics{
     HDC hdc;
     HWND hwnd;
     BOOL owndc;
+    GpImage *image;
     SmoothingMode smoothing;
     CompositingQuality compqual;
     InterpolationMode interpolation;
index 1fd870e..7a6fad3 100644 (file)
@@ -173,6 +173,74 @@ static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
     }
 }
 
+/* Draw non-premultiplied ARGB data to the given graphics object */
+static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
+    const BYTE *src, INT src_width, INT src_height, INT src_stride)
+{
+    if (graphics->image && graphics->image->type == ImageTypeBitmap)
+    {
+        GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
+        INT x, y;
+
+        for (x=0; x<src_width; x++)
+        {
+            for (y=0; y<src_height; y++)
+            {
+                ARGB dst_color, src_color;
+                GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
+                src_color = ((ARGB*)(src + src_stride * y))[x];
+                GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
+            }
+        }
+
+        return Ok;
+    }
+    else
+    {
+        HDC hdc;
+        HBITMAP hbitmap, old_hbm=NULL;
+        BITMAPINFOHEADER bih;
+        BYTE *temp_bits;
+        BLENDFUNCTION bf;
+
+        hdc = CreateCompatibleDC(0);
+
+        bih.biSize = sizeof(BITMAPINFOHEADER);
+        bih.biWidth = src_width;
+        bih.biHeight = -src_height;
+        bih.biPlanes = 1;
+        bih.biBitCount = 32;
+        bih.biCompression = BI_RGB;
+        bih.biSizeImage = 0;
+        bih.biXPelsPerMeter = 0;
+        bih.biYPelsPerMeter = 0;
+        bih.biClrUsed = 0;
+        bih.biClrImportant = 0;
+
+        hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
+            (void**)&temp_bits, NULL, 0);
+
+        convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
+            4 * src_width, src, src_stride);
+
+        old_hbm = SelectObject(hdc, hbitmap);
+
+        bf.BlendOp = AC_SRC_OVER;
+        bf.BlendFlags = 0;
+        bf.SourceConstantAlpha = 255;
+        bf.AlphaFormat = AC_SRC_ALPHA;
+
+        GdiAlphaBlend(graphics->hdc, dst_x, dst_y, src_width, src_height,
+            hdc, 0, 0, src_width, src_height, bf);
+
+        SelectObject(hdc, old_hbm);
+        DeleteDC(hdc);
+        DeleteObject(hbitmap);
+
+        return Ok;
+    }
+}
+
 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
 {
     ARGB result=0;
@@ -1273,10 +1341,10 @@ GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Top) / ((REAL) placeable->Inch);
     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
-                    - placeable->BoundingBox.Left)) / ((REAL) placeable->Inch);
+                    - placeable->BoundingBox.Left));
     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
-                   - placeable->BoundingBox.Top)) / ((REAL) placeable->Inch);
-    (*metafile)->unit = UnitInch;
+                   - placeable->BoundingBox.Top));
+    (*metafile)->unit = UnitPixel;
 
     if(delete)
         DeleteMetaFile(hwmf);
@@ -1866,15 +1934,15 @@ GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
     return NotImplemented;
 }
 
-/* FIXME: partially implemented (only works for rectangular parallelograms) */
 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
      DrawImageAbort callback, VOID * callbackData)
 {
-    GpPointF ptf[3];
-    POINT pti[3];
+    GpPointF ptf[4];
+    POINT pti[4];
     REAL dx, dy;
+    GpStatus stat;
 
     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
@@ -1887,10 +1955,13 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
         debugstr_pointf(&points[2]));
 
     memcpy(ptf, points, 3 * sizeof(GpPointF));
-    transform_and_round_points(graphics, pti, ptf, 3);
+    ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
+    ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
+    transform_and_round_points(graphics, pti, ptf, 4);
 
     if (image->picture)
     {
+        /* FIXME: partially implemented (only works for rectangular parallelograms) */
         if(srcUnit == UnitInch)
             dx = dy = (REAL) INCH_HIMETRIC;
         else if(srcUnit == UnitPixel){
@@ -1914,10 +1985,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
     }
     else if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
     {
-        HDC hdc;
         GpBitmap* bitmap = (GpBitmap*)image;
-        int temp_hdc=0, temp_bitmap=0;
-        HBITMAP hbitmap, old_hbm=NULL;
+        int use_software=0;
 
         if (srcUnit == UnitInch)
             dx = dy = 96.0; /* FIXME: use the image resolution */
@@ -1926,83 +1995,233 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
         else
             return NotImplemented;
 
-        if (!(bitmap->format == PixelFormat16bppRGB555 ||
-              bitmap->format == PixelFormat24bppRGB ||
-              bitmap->format == PixelFormat32bppRGB ||
-              bitmap->format == PixelFormat32bppPARGB))
+        if (imageAttributes ||
+            (graphics->image && graphics->image->type == ImageTypeBitmap) ||
+            ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X)
+            use_software = 1;
+
+        if (use_software)
         {
-            BITMAPINFOHEADER bih;
-            BYTE *temp_bits;
-            PixelFormat dst_format;
-
-            /* we can't draw a bitmap of this format directly */
-            hdc = CreateCompatibleDC(0);
-            temp_hdc = 1;
-            temp_bitmap = 1;
-
-            bih.biSize = sizeof(BITMAPINFOHEADER);
-            bih.biWidth = bitmap->width;
-            bih.biHeight = -bitmap->height;
-            bih.biPlanes = 1;
-            bih.biBitCount = 32;
-            bih.biCompression = BI_RGB;
-            bih.biSizeImage = 0;
-            bih.biXPelsPerMeter = 0;
-            bih.biYPelsPerMeter = 0;
-            bih.biClrUsed = 0;
-            bih.biClrImportant = 0;
-
-            hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
-                (void**)&temp_bits, NULL, 0);
+            RECT src_area, dst_area;
+            int i, x, y, stride;
+            GpMatrix *dst_to_src;
+            REAL m11, m12, m21, m22, mdx, mdy;
+            LPBYTE data;
+
+            src_area.left = srcx*dx;
+            src_area.top = srcy*dy;
+            src_area.right = (srcx+srcwidth)*dx;
+            src_area.bottom = (srcy+srcheight)*dy;
+
+            dst_area.left = dst_area.right = pti[0].x;
+            dst_area.top = dst_area.bottom = pti[0].y;
+            for (i=1; i<4; i++)
+            {
+                if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
+                if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
+                if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
+                if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
+            }
 
-            if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
-                dst_format = PixelFormat32bppPARGB;
-            else
-                dst_format = PixelFormat32bppRGB;
+            m11 = (ptf[1].X - ptf[0].X) / srcwidth;
+            m21 = (ptf[2].X - ptf[0].X) / srcheight;
+            mdx = ptf[0].X - m11 * srcx - m21 * srcy;
+            m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
+            m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
+            mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
 
-            convert_pixels(bitmap->width, bitmap->height,
-                bitmap->width*4, temp_bits, dst_format,
-                bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
-        }
-        else
-        {
-            hbitmap = bitmap->hbitmap;
-            hdc = bitmap->hdc;
-            temp_hdc = (hdc == 0);
-        }
+            stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
+            if (stat != Ok) return stat;
 
-        if (temp_hdc)
-        {
-            if (!hdc) hdc = CreateCompatibleDC(0);
-            old_hbm = SelectObject(hdc, hbitmap);
-        }
+            stat = GdipInvertMatrix(dst_to_src);
+            if (stat != Ok)
+            {
+                GdipDeleteMatrix(dst_to_src);
+                return stat;
+            }
 
-        if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
-        {
-            BLENDFUNCTION bf;
+            data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
+            if (!data)
+            {
+                GdipDeleteMatrix(dst_to_src);
+                return OutOfMemory;
+            }
+
+            stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
+
+            for (x=dst_area.left; x<dst_area.right; x++)
+            {
+                for (y=dst_area.top; y<dst_area.bottom; y++)
+                {
+                    GpPointF src_pointf;
+                    int src_x, src_y;
+                    ARGB *src_color;
+
+                    src_pointf.X = x;
+                    src_pointf.Y = y;
+
+                    GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
+
+                    src_x = roundr(src_pointf.X);
+                    src_y = roundr(src_pointf.Y);
+
+                    src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
+
+                    if (src_x < src_area.left || src_x >= src_area.right ||
+                        src_y < src_area.top || src_y >= src_area.bottom)
+                        /* FIXME: Use wrapmode */
+                        *src_color = 0;
+                    else
+                        GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
+                }
+            }
+
+            GdipDeleteMatrix(dst_to_src);
+
+            if (imageAttributes)
+            {
+                if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled ||
+                    imageAttributes->colorkeys[ColorAdjustTypeDefault].enabled)
+                {
+                    static int fixme;
+                    if (!fixme++)
+                        FIXME("Color keying not implemented\n");
+                }
+
+                if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled ||
+                    imageAttributes->colorremaptables[ColorAdjustTypeDefault].enabled)
+                {
+                    const struct color_remap_table *table;
+
+                    if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled)
+                        table = &imageAttributes->colorremaptables[ColorAdjustTypeBitmap];
+                    else
+                        table = &imageAttributes->colorremaptables[ColorAdjustTypeDefault];
+
+                    for (x=dst_area.left; x<dst_area.right; x++)
+                        for (y=dst_area.top; y<dst_area.bottom; y++)
+                        {
+                            ARGB *src_color;
+                            src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
+                            for (i=0; i<table->mapsize; i++)
+                            {
+                                if (*src_color == table->colormap[i].oldColor.Argb)
+                                {
+                                    *src_color = table->colormap[i].newColor.Argb;
+                                    break;
+                                }
+                            }
+                        }
+                }
+
+                if (imageAttributes->colormatrices[ColorAdjustTypeBitmap].enabled ||
+                    imageAttributes->colormatrices[ColorAdjustTypeDefault].enabled)
+                {
+                    static int fixme;
+                    if (!fixme++)
+                        FIXME("Color transforms not implemented\n");
+                }
+
+                if (imageAttributes->gamma_enabled[ColorAdjustTypeBitmap] ||
+                    imageAttributes->gamma_enabled[ColorAdjustTypeDefault])
+                {
+                    static int fixme;
+                    if (!fixme++)
+                        FIXME("Gamma adjustment not implemented\n");
+                }
+            }
+
+            stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
+                data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
 
-            bf.BlendOp = AC_SRC_OVER;
-            bf.BlendFlags = 0;
-            bf.SourceConstantAlpha = 255;
-            bf.AlphaFormat = AC_SRC_ALPHA;
+            GdipFree(data);
 
-            GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
-                hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, bf);
+            return stat;
         }
         else
         {
-            StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
-                hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
-        }
+            HDC hdc;
+            int temp_hdc=0, temp_bitmap=0;
+            HBITMAP hbitmap, old_hbm=NULL;
+
+            if (!(bitmap->format == PixelFormat16bppRGB555 ||
+                  bitmap->format == PixelFormat24bppRGB ||
+                  bitmap->format == PixelFormat32bppRGB ||
+                  bitmap->format == PixelFormat32bppPARGB))
+            {
+                BITMAPINFOHEADER bih;
+                BYTE *temp_bits;
+                PixelFormat dst_format;
+
+                /* we can't draw a bitmap of this format directly */
+                hdc = CreateCompatibleDC(0);
+                temp_hdc = 1;
+                temp_bitmap = 1;
+
+                bih.biSize = sizeof(BITMAPINFOHEADER);
+                bih.biWidth = bitmap->width;
+                bih.biHeight = -bitmap->height;
+                bih.biPlanes = 1;
+                bih.biBitCount = 32;
+                bih.biCompression = BI_RGB;
+                bih.biSizeImage = 0;
+                bih.biXPelsPerMeter = 0;
+                bih.biYPelsPerMeter = 0;
+                bih.biClrUsed = 0;
+                bih.biClrImportant = 0;
+
+                hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
+                    (void**)&temp_bits, NULL, 0);
+
+                if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
+                    dst_format = PixelFormat32bppPARGB;
+                else
+                    dst_format = PixelFormat32bppRGB;
 
-        if (temp_hdc)
-        {
-            SelectObject(hdc, old_hbm);
-            DeleteDC(hdc);
-        }
+                convert_pixels(bitmap->width, bitmap->height,
+                    bitmap->width*4, temp_bits, dst_format,
+                    bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
+            }
+            else
+            {
+                hbitmap = bitmap->hbitmap;
+                hdc = bitmap->hdc;
+                temp_hdc = (hdc == 0);
+            }
+
+            if (temp_hdc)
+            {
+                if (!hdc) hdc = CreateCompatibleDC(0);
+                old_hbm = SelectObject(hdc, hbitmap);
+            }
+
+            if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
+            {
+                BLENDFUNCTION bf;
+
+                bf.BlendOp = AC_SRC_OVER;
+                bf.BlendFlags = 0;
+                bf.SourceConstantAlpha = 255;
+                bf.AlphaFormat = AC_SRC_ALPHA;
+
+                GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
+                    hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, bf);
+            }
+            else
+            {
+                StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
+                    hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
+            }
+
+            if (temp_hdc)
+            {
+                SelectObject(hdc, old_hbm);
+                DeleteDC(hdc);
+            }
 
-        if (temp_bitmap)
-            DeleteObject(hbitmap);
+            if (temp_bitmap)
+                DeleteObject(hbitmap);
+        }
     }
     else
     {
@@ -2403,292 +2622,90 @@ GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
     return ret;
 }
 
-GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
-    INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
-    GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
+GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
+    GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
 {
-    HRGN rgn = NULL;
-    HFONT gdifont;
-    LOGFONTW lfw;
-    TEXTMETRICW textmet;
-    GpPointF pt[3], rectcpy[4];
-    POINT corners[4];
-    WCHAR* stringdup;
-    REAL angle, ang_cos, ang_sin, rel_width, rel_height;
-    INT sum = 0, height = 0, offsety = 0, fit, fitcpy, save_state, i, j, lret, nwidth,
-        nheight, lineend;
-    SIZE size;
-    POINT drawbase;
-    UINT drawflags;
-    RECT drawcoord;
+    GpPath *path;
+    GpStatus stat;
 
-    TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
-        length, font, debugstr_rectf(rect), format, brush);
+    TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
+            count, tension, fill);
 
-    if(!graphics || !string || !font || !brush || !rect)
+    if(!graphics || !brush || !points)
         return InvalidParameter;
 
-    if((brush->bt != BrushTypeSolidColor)){
-        FIXME("not implemented for given parameters\n");
-        return NotImplemented;
-    }
-
-    if(format){
-        TRACE("may be ignoring some format flags: attr %x\n", format->attr);
+    if(graphics->busy)
+        return ObjectBusy;
 
-        /* Should be no need to explicitly test for StringAlignmentNear as
-         * that is default behavior if no alignment is passed. */
-        if(format->vertalign != StringAlignmentNear){
-            RectF bounds;
-            GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
+    stat = GdipCreatePath(fill, &path);
+    if(stat != Ok)
+        return stat;
 
-            if(format->vertalign == StringAlignmentCenter)
-                offsety = (rect->Height - bounds.Height) / 2;
-            else if(format->vertalign == StringAlignmentFar)
-                offsety = (rect->Height - bounds.Height);
-        }
+    stat = GdipAddPathClosedCurve2(path, points, count, tension);
+    if(stat != Ok){
+        GdipDeletePath(path);
+        return stat;
     }
 
-    if(length == -1) length = lstrlenW(string);
+    stat = GdipFillPath(graphics, brush, path);
+    if(stat != Ok){
+        GdipDeletePath(path);
+        return stat;
+    }
 
-    stringdup = GdipAlloc(length * sizeof(WCHAR));
-    if(!stringdup) return OutOfMemory;
+    GdipDeletePath(path);
 
-    save_state = SaveDC(graphics->hdc);
-    SetBkMode(graphics->hdc, TRANSPARENT);
-    SetTextColor(graphics->hdc, brush->lb.lbColor);
+    return Ok;
+}
 
-    pt[0].X = 0.0;
-    pt[0].Y = 0.0;
-    pt[1].X = 1.0;
-    pt[1].Y = 0.0;
-    pt[2].X = 0.0;
-    pt[2].Y = 1.0;
-    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
-    angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
-    ang_cos = cos(angle);
-    ang_sin = sin(angle);
-    rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
-                     (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
-    rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
-                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
+    GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
+{
+    GpPointF *ptf;
+    GpStatus stat;
+    INT i;
 
-    rectcpy[3].X = rectcpy[0].X = rect->X;
-    rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
-    rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
-    rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
-    transform_and_round_points(graphics, corners, rectcpy, 4);
+    TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
+            count, tension, fill);
 
-    if (roundr(rect->Width) == 0)
-        nwidth = INT_MAX;
-    else
-        nwidth = roundr(rel_width * rect->Width);
+    if(!points || count <= 0)
+        return InvalidParameter;
 
-    if (roundr(rect->Height) == 0)
-        nheight = INT_MAX;
-    else
-        nheight = roundr(rel_height * rect->Height);
+    ptf = GdipAlloc(sizeof(GpPointF)*count);
+    if(!ptf)
+        return OutOfMemory;
 
-    if (roundr(rect->Width) != 0 && roundr(rect->Height) != 0)
-    {
-        /* FIXME: If only the width or only the height is 0, we should probably still clip */
-        rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
-        SelectClipRgn(graphics->hdc, rgn);
+    for(i = 0;i < count;i++){
+        ptf[i].X = (REAL)points[i].X;
+        ptf[i].Y = (REAL)points[i].Y;
     }
 
-    /* Use gdi to find the font, then perform transformations on it (height,
-     * width, angle). */
-    SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
-    GetTextMetricsW(graphics->hdc, &textmet);
-    lfw = font->lfw;
-
-    lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
-    lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
+    stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
 
-    lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
+    GdipFree(ptf);
 
-    gdifont = CreateFontIndirectW(&lfw);
-    DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
+    return stat;
+}
 
-    for(i = 0, j = 0; i < length; i++){
-        if(!isprintW(string[i]) && (string[i] != '\n'))
-            continue;
+GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
+    REAL y, REAL width, REAL height)
+{
+    INT save_state;
+    GpPointF ptf[2];
+    POINT pti[2];
 
-        stringdup[j] = string[i];
-        j++;
-    }
+    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
 
-    length = j;
+    if(!graphics || !brush)
+        return InvalidParameter;
 
-    if (!format || format->align == StringAlignmentNear)
-    {
-        drawbase.x = corners[0].x;
-        drawbase.y = corners[0].y;
-        drawflags = DT_NOCLIP | DT_EXPANDTABS;
-    }
-    else if (format->align == StringAlignmentCenter)
-    {
-        drawbase.x = (corners[0].x + corners[1].x)/2;
-        drawbase.y = (corners[0].y + corners[1].y)/2;
-        drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
-    }
-    else /* (format->align == StringAlignmentFar) */
-    {
-        drawbase.x = corners[1].x;
-        drawbase.y = corners[1].y;
-        drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
-    }
+    if(graphics->busy)
+        return ObjectBusy;
 
-    while(sum < length){
-        drawcoord.left = drawcoord.right = drawbase.x + roundr(ang_sin * (REAL) height);
-        drawcoord.top = drawcoord.bottom = drawbase.y + roundr(ang_cos * (REAL) height);
-
-        GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
-                              nwidth, &fit, NULL, &size);
-        fitcpy = fit;
-
-        if(fit == 0){
-            DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, drawflags);
-            break;
-        }
-
-        for(lret = 0; lret < fit; lret++)
-            if(*(stringdup + sum + lret) == '\n')
-                break;
-
-        /* Line break code (may look strange, but it imitates windows). */
-        if(lret < fit)
-            lineend = fit = lret;    /* this is not an off-by-one error */
-        else if(fit < (length - sum)){
-            if(*(stringdup + sum + fit) == ' ')
-                while(*(stringdup + sum + fit) == ' ')
-                    fit++;
-            else
-                while(*(stringdup + sum + fit - 1) != ' '){
-                    fit--;
-
-                    if(*(stringdup + sum + fit) == '\t')
-                        break;
-
-                    if(fit == 0){
-                        fit = fitcpy;
-                        break;
-                    }
-                }
-            lineend = fit;
-            while(*(stringdup + sum + lineend - 1) == ' ' ||
-                  *(stringdup + sum + lineend - 1) == '\t')
-                lineend--;
-        }
-        else
-            lineend = fit;
-        DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, lineend),
-                  &drawcoord, drawflags);
-
-        sum += fit + (lret < fitcpy ? 1 : 0);
-        height += size.cy;
-
-        if(height > nheight)
-            break;
-
-        /* Stop if this was a linewrap (but not if it was a linebreak). */
-        if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
-            break;
-    }
-
-    GdipFree(stringdup);
-    DeleteObject(rgn);
-    DeleteObject(gdifont);
-
-    RestoreDC(graphics->hdc, save_state);
-
-    return Ok;
-}
-
-GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
-    GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
-{
-    GpPath *path;
-    GpStatus stat;
-
-    TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
-            count, tension, fill);
-
-    if(!graphics || !brush || !points)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    stat = GdipCreatePath(fill, &path);
-    if(stat != Ok)
-        return stat;
-
-    stat = GdipAddPathClosedCurve2(path, points, count, tension);
-    if(stat != Ok){
-        GdipDeletePath(path);
-        return stat;
-    }
-
-    stat = GdipFillPath(graphics, brush, path);
-    if(stat != Ok){
-        GdipDeletePath(path);
-        return stat;
-    }
-
-    GdipDeletePath(path);
-
-    return Ok;
-}
-
-GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
-    GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
-{
-    GpPointF *ptf;
-    GpStatus stat;
-    INT i;
-
-    TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
-            count, tension, fill);
-
-    if(!points || count <= 0)
-        return InvalidParameter;
-
-    ptf = GdipAlloc(sizeof(GpPointF)*count);
-    if(!ptf)
-        return OutOfMemory;
-
-    for(i = 0;i < count;i++){
-        ptf[i].X = (REAL)points[i].X;
-        ptf[i].Y = (REAL)points[i].Y;
-    }
-
-    stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
-
-    GdipFree(ptf);
-
-    return stat;
-}
-
-GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
-    REAL y, REAL width, REAL height)
-{
-    INT save_state;
-    GpPointF ptf[2];
-    POINT pti[2];
-
-    TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
-
-    if(!graphics || !brush)
-        return InvalidParameter;
-
-    if(graphics->busy)
-        return ObjectBusy;
-
-    ptf[0].X = x;
-    ptf[0].Y = y;
-    ptf[1].X = x + width;
-    ptf[1].Y = y + height;
+    ptf[0].X = x;
+    ptf[0].Y = y;
+    ptf[1].X = x + width;
+    ptf[1].Y = y + height;
 
     save_state = SaveDC(graphics->hdc);
     EndPath(graphics->hdc);
@@ -3074,8 +3091,6 @@ GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
 
 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
 {
-    static int calls;
-
     TRACE("(%p,%u)\n", graphics, intention);
 
     if(!graphics)
@@ -3084,10 +3099,12 @@ GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
     if(graphics->busy)
         return ObjectBusy;
 
-    if(!(calls++))
-        FIXME("not implemented\n");
+    /* We have no internal operation queue, so there's no need to clear it. */
 
-    return NotImplemented;
+    if (graphics->hdc)
+        GdiFlush();
+
+    return Ok;
 }
 
 /*****************************************************************************
@@ -3465,61 +3482,37 @@ GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT w
     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
 }
 
-GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
-        GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
-        GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
-        INT regionCount, GpRegion** regions)
-{
-    FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
-            length, font, layoutRect, stringFormat, regionCount, regions);
-
-    if (!(graphics && string && font && layoutRect && stringFormat && regions))
-        return InvalidParameter;
+typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics,
+    GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+    INT lineno, const RectF *bounds, void *user_data);
 
-    return NotImplemented;
-}
-
-/* Find the smallest rectangle that bounds the text when it is printed in rect
- * according to the format options listed in format. If rect has 0 width and
- * height, then just find the smallest rectangle that bounds the text when it's
- * printed at location (rect->X, rect-Y). */
-GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
+static GpStatus gdip_format_string(GpGraphics *graphics,
     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
-    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
-    INT *codepointsfitted, INT *linesfilled)
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+    gdip_format_string_callback callback, void *user_data)
 {
-    HFONT oldfont;
     WCHAR* stringdup;
-    INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
-        nheight, lineend;
+    INT sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
+        nheight, lineend, lineno = 0;
+    RectF bounds;
+    StringAlignment halign;
+    GpStatus stat = Ok;
     SIZE size;
 
-    TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
-        debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
-        bounds, codepointsfitted, linesfilled);
-
-    if(!graphics || !string || !font || !rect)
-        return InvalidParameter;
-
-    if(linesfilled) *linesfilled = 0;
-    if(codepointsfitted) *codepointsfitted = 0;
-
-    if(format)
-        TRACE("may be ignoring some format flags: attr %x\n", format->attr);
-
     if(length == -1) length = lstrlenW(string);
 
     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
     if(!stringdup) return OutOfMemory;
 
-    oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
     nwidth = roundr(rect->Width);
     nheight = roundr(rect->Height);
 
-    if((nwidth == 0) && (nheight == 0))
-        nwidth = nheight = INT_MAX;
+    if (nwidth == 0) nwidth = INT_MAX;
+    if (nheight == 0) nheight = INT_MAX;
 
     for(i = 0, j = 0; i < length; i++){
+        /* FIXME: This makes the indexes passed to callback inaccurate. */
         if(!isprintW(string[i]) && (string[i] != '\n'))
             continue;
 
@@ -3527,9 +3520,11 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
         j++;
     }
 
-    stringdup[j] = 0;
     length = j;
 
+    if (format) halign = format->align;
+    else halign = StringAlignmentNear;
+
     while(sum < length){
         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
                               nwidth, &fit, NULL, &size);
@@ -3572,12 +3567,38 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
         GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend,
                               nwidth, &j, NULL, &size);
 
-        sum += fit + (lret < fitcpy ? 1 : 0);
-        if(codepointsfitted) *codepointsfitted = sum;
+        bounds.Width = size.cx;
+
+        if(height + size.cy > nheight)
+            bounds.Height = nheight - (height + size.cy);
+        else
+            bounds.Height = size.cy;
+
+        bounds.Y = rect->Y + height;
 
+        switch (halign)
+        {
+        case StringAlignmentNear:
+        default:
+            bounds.X = rect->X;
+            break;
+        case StringAlignmentCenter:
+            bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
+            break;
+        case StringAlignmentFar:
+            bounds.X = rect->X + rect->Width - bounds.Width;
+            break;
+        }
+
+        stat = callback(graphics, stringdup, sum, lineend,
+            font, rect, format, lineno, &bounds, user_data);
+
+        if (stat != Ok)
+            break;
+
+        sum += fit + (lret < fitcpy ? 1 : 0);
         height += size.cy;
-        if(linesfilled) *linesfilled += size.cy;
-        max_width = max(max_width, size.cx);
+        lineno++;
 
         if(height > nheight)
             break;
@@ -3587,17 +3608,311 @@ GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
             break;
     }
 
+    GdipFree(stringdup);
+
+    return stat;
+}
+
+struct measure_ranges_args {
+    GpRegion **regions;
+};
+
+GpStatus measure_ranges_callback(GpGraphics *graphics,
+    GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+    INT lineno, const RectF *bounds, void *user_data)
+{
+    int i;
+    GpStatus stat = Ok;
+    struct measure_ranges_args *args = user_data;
+
+    for (i=0; i<format->range_count; i++)
+    {
+        INT range_start = max(index, format->character_ranges[i].First);
+        INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
+        if (range_start < range_end)
+        {
+            GpRectF range_rect;
+            SIZE range_size;
+
+            range_rect.Y = bounds->Y;
+            range_rect.Height = bounds->Height;
+
+            GetTextExtentExPointW(graphics->hdc, string + index, range_start - index,
+                                  INT_MAX, NULL, NULL, &range_size);
+            range_rect.X = bounds->X + range_size.cx;
+
+            GetTextExtentExPointW(graphics->hdc, string + index, range_end - index,
+                                  INT_MAX, NULL, NULL, &range_size);
+            range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
+
+            stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
+            if (stat != Ok)
+                break;
+        }
+    }
+
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
+        GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
+        GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
+        INT regionCount, GpRegion** regions)
+{
+    GpStatus stat;
+    int i;
+    HFONT oldfont;
+    struct measure_ranges_args args;
+
+    TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
+            length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
+
+    if (!(graphics && string && font && layoutRect && stringFormat && regions))
+        return InvalidParameter;
+
+    if (regionCount < stringFormat->range_count)
+        return InvalidParameter;
+
+    if (stringFormat->attr)
+        TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
+
+    oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
+
+    for (i=0; i<stringFormat->range_count; i++)
+    {
+        stat = GdipSetEmpty(regions[i]);
+        if (stat != Ok)
+            return stat;
+    }
+
+    args.regions = regions;
+
+    stat = gdip_format_string(graphics, string, length, font, layoutRect, stringFormat,
+        measure_ranges_callback, &args);
+
+    DeleteObject(SelectObject(graphics->hdc, oldfont));
+
+    return stat;
+}
+
+struct measure_string_args {
+    RectF *bounds;
+    INT *codepointsfitted;
+    INT *linesfilled;
+};
+
+static GpStatus measure_string_callback(GpGraphics *graphics,
+    GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+    INT lineno, const RectF *bounds, void *user_data)
+{
+    struct measure_string_args *args = user_data;
+
+    if (bounds->Width > args->bounds->Width)
+        args->bounds->Width = bounds->Width;
+
+    if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
+        args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
+
+    if (args->codepointsfitted)
+        *args->codepointsfitted = index + length;
+
+    if (args->linesfilled)
+        (*args->linesfilled)++;
+
+    return Ok;
+}
+
+/* Find the smallest rectangle that bounds the text when it is printed in rect
+ * according to the format options listed in format. If rect has 0 width and
+ * height, then just find the smallest rectangle that bounds the text when it's
+ * printed at location (rect->X, rect-Y). */
+GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
+    GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
+    INT *codepointsfitted, INT *linesfilled)
+{
+    HFONT oldfont;
+    struct measure_string_args args;
+
+    TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
+        debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
+        bounds, codepointsfitted, linesfilled);
+
+    if(!graphics || !string || !font || !rect || !bounds)
+        return InvalidParameter;
+
+    if(linesfilled) *linesfilled = 0;
+    if(codepointsfitted) *codepointsfitted = 0;
+
+    if(format)
+        TRACE("may be ignoring some format flags: attr %x\n", format->attr);
+
+    oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
+
     bounds->X = rect->X;
     bounds->Y = rect->Y;
-    bounds->Width = (REAL)max_width;
-    bounds->Height = (REAL) min(height, nheight);
+    bounds->Width = 0.0;
+    bounds->Height = 0.0;
+
+    args.bounds = bounds;
+    args.codepointsfitted = codepointsfitted;
+    args.linesfilled = linesfilled;
+
+    gdip_format_string(graphics, string, length, font, rect, format,
+        measure_string_callback, &args);
 
-    GdipFree(stringdup);
     DeleteObject(SelectObject(graphics->hdc, oldfont));
 
     return Ok;
 }
 
+struct draw_string_args {
+    POINT drawbase;
+    UINT drawflags;
+    REAL ang_cos, ang_sin;
+};
+
+static GpStatus draw_string_callback(GpGraphics *graphics,
+    GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
+    GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
+    INT lineno, const RectF *bounds, void *user_data)
+{
+    struct draw_string_args *args = user_data;
+    RECT drawcoord;
+
+    drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
+    drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
+
+    DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
+
+    return Ok;
+}
+
+GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
+    INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
+    GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
+{
+    HRGN rgn = NULL;
+    HFONT gdifont;
+    LOGFONTW lfw;
+    TEXTMETRICW textmet;
+    GpPointF pt[3], rectcpy[4];
+    POINT corners[4];
+    REAL angle, rel_width, rel_height;
+    INT offsety = 0, save_state;
+    struct draw_string_args args;
+    RectF scaled_rect;
+
+    TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
+        length, font, debugstr_rectf(rect), format, brush);
+
+    if(!graphics || !string || !font || !brush || !rect)
+        return InvalidParameter;
+
+    if((brush->bt != BrushTypeSolidColor)){
+        FIXME("not implemented for given parameters\n");
+        return NotImplemented;
+    }
+
+    if(format){
+        TRACE("may be ignoring some format flags: attr %x\n", format->attr);
+
+        /* Should be no need to explicitly test for StringAlignmentNear as
+         * that is default behavior if no alignment is passed. */
+        if(format->vertalign != StringAlignmentNear){
+            RectF bounds;
+            GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
+
+            if(format->vertalign == StringAlignmentCenter)
+                offsety = (rect->Height - bounds.Height) / 2;
+            else if(format->vertalign == StringAlignmentFar)
+                offsety = (rect->Height - bounds.Height);
+        }
+    }
+
+    save_state = SaveDC(graphics->hdc);
+    SetBkMode(graphics->hdc, TRANSPARENT);
+    SetTextColor(graphics->hdc, brush->lb.lbColor);
+
+    pt[0].X = 0.0;
+    pt[0].Y = 0.0;
+    pt[1].X = 1.0;
+    pt[1].Y = 0.0;
+    pt[2].X = 0.0;
+    pt[2].Y = 1.0;
+    GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
+    angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
+    args.ang_cos = cos(angle);
+    args.ang_sin = sin(angle);
+    rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
+                     (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
+    rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
+                      (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
+
+    rectcpy[3].X = rectcpy[0].X = rect->X;
+    rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
+    rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
+    rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
+    transform_and_round_points(graphics, corners, rectcpy, 4);
+
+    scaled_rect.X = 0.0;
+    scaled_rect.Y = 0.0;
+    scaled_rect.Width = rel_width * rect->Width;
+    scaled_rect.Height = rel_height * rect->Height;
+
+    if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
+    {
+        /* FIXME: If only the width or only the height is 0, we should probably still clip */
+        rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
+        SelectClipRgn(graphics->hdc, rgn);
+    }
+
+    /* Use gdi to find the font, then perform transformations on it (height,
+     * width, angle). */
+    SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
+    GetTextMetricsW(graphics->hdc, &textmet);
+    lfw = font->lfw;
+
+    lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
+    lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
+
+    lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
+
+    gdifont = CreateFontIndirectW(&lfw);
+    DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
+
+    if (!format || format->align == StringAlignmentNear)
+    {
+        args.drawbase.x = corners[0].x;
+        args.drawbase.y = corners[0].y;
+        args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
+    }
+    else if (format->align == StringAlignmentCenter)
+    {
+        args.drawbase.x = (corners[0].x + corners[1].x)/2;
+        args.drawbase.y = (corners[0].y + corners[1].y)/2;
+        args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
+    }
+    else /* (format->align == StringAlignmentFar) */
+    {
+        args.drawbase.x = corners[1].x;
+        args.drawbase.y = corners[1].y;
+        args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
+    }
+
+    gdip_format_string(graphics, string, length, font, &scaled_rect, format,
+        draw_string_callback, &args);
+
+    DeleteObject(rgn);
+    DeleteObject(gdifont);
+
+    RestoreDC(graphics->hdc, save_state);
+
+    return Ok;
+}
+
 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
 {
     TRACE("(%p)\n", graphics);
@@ -4300,7 +4615,12 @@ GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace
 
 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
 {
-    FIXME("\n");
+    static int calls;
+
+    TRACE("\n");
+
+    if (!calls++)
+      FIXME("stub\n");
 
     return NULL;
 }
index 58f3c65..0a73dcf 100644 (file)
@@ -122,7 +122,7 @@ static inline void getpixel_16bppGrayScale(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
 static inline void getpixel_16bppRGB555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
     const BYTE *row, UINT x)
 {
-    WORD pixel = *((WORD*)(row)+x);
+    WORD pixel = *((const WORD*)(row)+x);
     *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
     *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
     *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
@@ -132,7 +132,7 @@ static inline void getpixel_16bppRGB555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
 static inline void getpixel_16bppRGB565(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
     const BYTE *row, UINT x)
 {
-    WORD pixel = *((WORD*)(row)+x);
+    WORD pixel = *((const WORD*)(row)+x);
     *r = (pixel>>8&0xf8)|(pixel>>13&0x7);
     *g = (pixel>>3&0xfc)|(pixel>>9&0x3);
     *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
@@ -142,7 +142,7 @@ static inline void getpixel_16bppRGB565(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
 static inline void getpixel_16bppARGB1555(BYTE *r, BYTE *g, BYTE *b, BYTE *a,
     const BYTE *row, UINT x)
 {
-    WORD pixel = *((WORD*)(row)+x);
+    WORD pixel = *((const WORD*)(row)+x);
     *r = (pixel>>7&0xf8)|(pixel>>12&0x7);
     *g = (pixel>>2&0xf8)|(pixel>>6&0x7);
     *b = (pixel<<3&0xf8)|(pixel>>2&0x7);
@@ -1831,6 +1831,37 @@ GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
     return NotImplemented;
 }
 
+/* Internal utility function: Replace the image data of dst with that of src,
+ * and free src. */
+static void move_bitmap(GpBitmap *dst, GpBitmap *src, BOOL clobber_palette)
+{
+    GdipFree(dst->bitmapbits);
+    DeleteDC(dst->hdc);
+    DeleteObject(dst->hbitmap);
+
+    if (clobber_palette)
+    {
+        GdipFree(dst->image.palette_entries);
+        dst->image.palette_flags = src->image.palette_flags;
+        dst->image.palette_count = src->image.palette_count;
+        dst->image.palette_entries = src->image.palette_entries;
+    }
+    else
+        GdipFree(src->image.palette_entries);
+
+    dst->image.xres = src->image.xres;
+    dst->image.yres = src->image.yres;
+    dst->width = src->width;
+    dst->height = src->height;
+    dst->format = src->format;
+    dst->hbitmap = src->hbitmap;
+    dst->hdc = src->hdc;
+    dst->bits = src->bits;
+    dst->stride = src->stride;
+
+    GdipFree(src);
+}
+
 GpStatus WINGDIPAPI GdipDisposeImage(GpImage *image)
 {
     TRACE("%p\n", image);
@@ -1946,6 +1977,7 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
     GpGraphics **graphics)
 {
     HDC hdc;
+    GpStatus stat;
 
     TRACE("%p %p\n", image, graphics);
 
@@ -1965,7 +1997,12 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
         ((GpBitmap*)image)->hdc = hdc;
     }
 
-    return GdipCreateFromHDC(hdc, graphics);
+    stat = GdipCreateFromHDC(hdc, graphics);
+
+    if (stat == Ok)
+        (*graphics)->image = image;
+
+    return stat;
 }
 
 GpStatus WINGDIPAPI GdipGetImageHeight(GpImage *image, UINT *height)
@@ -2459,6 +2496,11 @@ static GpStatus decode_image_gif(IStream* stream, REFCLSID clsid, GpImage **imag
     return decode_image_wic(stream, &CLSID_WICGifDecoder, image);
 }
 
+static GpStatus decode_image_tiff(IStream* stream, REFCLSID clsid, GpImage **image)
+{
+    return decode_image_wic(stream, &CLSID_WICTiffDecoder, image);
+}
+
 static GpStatus decode_image_olepicture_metafile(IStream* stream, REFCLSID clsid, GpImage **image)
 {
     IPicture *pic;
@@ -2505,6 +2547,7 @@ typedef enum {
     BMP,
     JPEG,
     GIF,
+    TIFF,
     EMF,
     WMF,
     PNG,
@@ -2517,10 +2560,11 @@ static const struct image_codec codecs[NUM_CODECS];
 static GpStatus get_decoder_info(IStream* stream, const struct image_codec **result)
 {
     BYTE signature[8];
+    const BYTE *pattern, *mask;
     LARGE_INTEGER seek;
     HRESULT hr;
     UINT bytesread;
-    int i, j;
+    int i, j, sig;
 
     /* seek to the start of the stream */
     seek.QuadPart = 0;
@@ -2528,7 +2572,7 @@ static GpStatus get_decoder_info(IStream* stream, const struct image_codec **res
     if (FAILED(hr)) return hresult_to_status(hr);
 
     /* read the first 8 bytes */
-    /* FIXME: This assumes all codecs have one signature <= 8 bytes in length */
+    /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */
     hr = IStream_Read(stream, signature, 8, &bytesread);
     if (FAILED(hr)) return hresult_to_status(hr);
     if (hr == S_FALSE || bytesread == 0) return GenericError;
@@ -2537,13 +2581,18 @@ static GpStatus get_decoder_info(IStream* stream, const struct image_codec **res
         if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
             bytesread >= codecs[i].info.SigSize)
         {
-            for (j=0; j<codecs[i].info.SigSize; j++)
-                if ((signature[j] & codecs[i].info.SigMask[j]) != codecs[i].info.SigPattern[j])
-                    break;
-            if (j == codecs[i].info.SigSize)
+            for (sig=0; sig<codecs[i].info.SigCount; sig++)
             {
-                *result = &codecs[i];
-                return Ok;
+                pattern = &codecs[i].info.SigPattern[codecs[i].info.SigSize*sig];
+                mask = &codecs[i].info.SigMask[codecs[i].info.SigSize*sig];
+                for (j=0; j<codecs[i].info.SigSize; j++)
+                    if ((signature[j] & mask[j]) != pattern[j])
+                        break;
+                if (j == codecs[i].info.SigSize)
+                {
+                    *result = &codecs[i];
+                    return Ok;
+                }
             }
         }
     }
@@ -2900,6 +2949,13 @@ static const WCHAR gif_format[] = {'G','I','F',0};
 static const BYTE gif_sig_pattern[4] = "GIF8";
 static const BYTE gif_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF };
 
+static const WCHAR tiff_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'T','I','F','F', 0};
+static const WCHAR tiff_extension[] = {'*','.','T','I','F','F',';','*','.','T','I','F',0};
+static const WCHAR tiff_mimetype[] = {'i','m','a','g','e','/','t','i','f','f', 0};
+static const WCHAR tiff_format[] = {'T','I','F','F',0};
+static const BYTE tiff_sig_pattern[] = {0x49,0x49,42,0,0x4d,0x4d,0,42};
+static const BYTE tiff_sig_mask[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
 static const WCHAR emf_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'E','M','F', 0};
 static const WCHAR emf_extension[] = {'*','.','E','M','F',0};
 static const WCHAR emf_mimetype[] = {'i','m','a','g','e','/','x','-','e','m','f', 0};
@@ -2986,6 +3042,25 @@ static const struct image_codec codecs[NUM_CODECS] = {
         NULL,
         decode_image_gif
     },
+    {
+        { /* TIFF */
+            /* Clsid */              { 0x557cf405, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
+            /* FormatID */           { 0xb96b3cb1U, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} },
+            /* CodecName */          tiff_codecname,
+            /* DllName */            NULL,
+            /* FormatDescription */  tiff_format,
+            /* FilenameExtension */  tiff_extension,
+            /* MimeType */           tiff_mimetype,
+            /* Flags */              ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin,
+            /* Version */            1,
+            /* SigCount */           2,
+            /* SigSize */            4,
+            /* SigPattern */         tiff_sig_pattern,
+            /* SigMask */            tiff_sig_mask,
+        },
+        NULL,
+        decode_image_tiff
+    },
     {
         { /* EMF */
             /* Clsid */              { 0x557cf403, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } },
@@ -3394,6 +3469,112 @@ GpStatus WINGDIPAPI GdipGetImageThumbnail(GpImage *image, UINT width, UINT heigh
  */
 GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
 {
-    FIXME("(%p %u) stub\n", image, type);
-    return NotImplemented;
+    GpBitmap *new_bitmap;
+    GpBitmap *bitmap;
+    int bpp, bytesperpixel;
+    int rotate_90, flip_x, flip_y;
+    int src_x_offset, src_y_offset;
+    LPBYTE src_origin;
+    UINT x, y, width, height;
+    BitmapData src_lock, dst_lock;
+    GpStatus stat;
+
+    TRACE("(%p, %u)\n", image, type);
+
+    rotate_90 = type&1;
+    flip_x = (type&6) == 2 || (type&6) == 4;
+    flip_y = (type&3) == 1 || (type&3) == 2;
+
+    if (image->type != ImageTypeBitmap)
+    {
+        FIXME("Not implemented for type %i\n", image->type);
+        return NotImplemented;
+    }
+
+    bitmap = (GpBitmap*)image;
+    bpp = PIXELFORMATBPP(bitmap->format);
+
+    if (bpp < 8)
+    {
+        FIXME("Not implemented for %i bit images\n", bpp);
+        return NotImplemented;
+    }
+
+    if (rotate_90)
+    {
+        width = bitmap->height;
+        height = bitmap->width;
+    }
+    else
+    {
+        width = bitmap->width;
+        height = bitmap->height;
+    }
+
+    bytesperpixel = bpp/8;
+
+    stat = GdipCreateBitmapFromScan0(width, height, 0, bitmap->format, NULL, &new_bitmap);
+
+    if (stat != Ok)
+        return stat;
+
+    stat = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, bitmap->format, &src_lock);
+
+    if (stat == Ok)
+    {
+        stat = GdipBitmapLockBits(new_bitmap, NULL, ImageLockModeWrite, bitmap->format, &dst_lock);
+
+        if (stat == Ok)
+        {
+            LPBYTE src_row, src_pixel;
+            LPBYTE dst_row, dst_pixel;
+
+            src_origin = src_lock.Scan0;
+            if (flip_x) src_origin += bytesperpixel * (bitmap->width - 1);
+            if (flip_y) src_origin += src_lock.Stride * (bitmap->height - 1);
+
+            if (rotate_90)
+            {
+                if (flip_y) src_x_offset = -src_lock.Stride;
+                else src_x_offset = src_lock.Stride;
+                if (flip_x) src_y_offset = -bytesperpixel;
+                else src_y_offset = bytesperpixel;
+            }
+            else
+            {
+                if (flip_x) src_x_offset = -bytesperpixel;
+                else src_x_offset = bytesperpixel;
+                if (flip_y) src_y_offset = -src_lock.Stride;
+                else src_y_offset = src_lock.Stride;
+            }
+
+            src_row = src_origin;
+            dst_row = dst_lock.Scan0;
+            for (y=0; y<height; y++)
+            {
+                src_pixel = src_row;
+                dst_pixel = dst_row;
+                for (x=0; x<width; x++)
+                {
+                    /* FIXME: This could probably be faster without memcpy. */
+                    memcpy(dst_pixel, src_pixel, bytesperpixel);
+                    dst_pixel += bytesperpixel;
+                    src_pixel += src_x_offset;
+                }
+                src_row += src_y_offset;
+                dst_row += dst_lock.Stride;
+            }
+
+            GdipBitmapUnlockBits(new_bitmap, &dst_lock);
+        }
+
+        GdipBitmapUnlockBits(bitmap, &src_lock);
+    }
+
+    if (stat == Ok)
+        move_bitmap(bitmap, new_bitmap, FALSE);
+    else
+        GdipDisposeImage((GpImage*)new_bitmap);
+
+    return stat;
 }
index 99f7130..9ab66de 100644 (file)
@@ -302,7 +302,7 @@ int WINAPI doWinMain(HINSTANCE hInstance, LPSTR szCmdLine)
         }
         else
         {
-            FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", space-szCmdLine, szCmdLine);
+            FIXME("Unhandled HTML Help command line parameter! (%.*s)\n", (int)(space-szCmdLine), szCmdLine);
             return 0;
         }
     }
index cd97a70..8312e16 100644 (file)
@@ -561,8 +561,9 @@ GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient*,REAL,REAL);
 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient*,BOOL);
 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient*,REAL,REAL);
 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient*,
-    ARGB*,INT*);
+    GDIPCONST ARGB*,INT*);
 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient*,GpWrapMode);
+GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorCount(GpPathGradient*,INT*);
 
 /* PathIterator */
 GpStatus WINGDIPAPI GdipCreatePathIter(GpPathIterator**,GpPath*);