avifil32: Ukranian translation.
[wine] / dlls / gdiplus / brush.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "wingdi.h"
25
26 #define COBJMACROS
27 #include "objbase.h"
28 #include "olectl.h"
29 #include "ole2.h"
30
31 #include "gdiplus.h"
32 #include "gdiplus_private.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
36
37 /******************************************************************************
38  * GdipCloneBrush [GDIPLUS.@]
39  */
40 GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone)
41 {
42     TRACE("(%p, %p)\n", brush, clone);
43
44     if(!brush || !clone)
45         return InvalidParameter;
46
47     switch(brush->bt){
48         case BrushTypeSolidColor:
49             *clone = GdipAlloc(sizeof(GpSolidFill));
50             if (!*clone) return OutOfMemory;
51
52             memcpy(*clone, brush, sizeof(GpSolidFill));
53
54             (*clone)->gdibrush = CreateBrushIndirect(&(*clone)->lb);
55             break;
56         case BrushTypeHatchFill:
57             *clone = GdipAlloc(sizeof(GpHatch));
58             if (!*clone) return OutOfMemory;
59
60             memcpy(*clone, brush, sizeof(GpHatch));
61
62             (*clone)->gdibrush = CreateBrushIndirect(&(*clone)->lb);
63             break;
64         case BrushTypePathGradient:{
65             GpPathGradient *src, *dest;
66             INT count;
67
68             *clone = GdipAlloc(sizeof(GpPathGradient));
69             if (!*clone) return OutOfMemory;
70
71             src = (GpPathGradient*) brush,
72             dest = (GpPathGradient*) *clone;
73             count = src->pathdata.Count;
74
75             memcpy(dest, src, sizeof(GpPathGradient));
76
77             dest->pathdata.Count = count;
78             dest->pathdata.Points = GdipAlloc(count * sizeof(PointF));
79             dest->pathdata.Types = GdipAlloc(count);
80
81             if(!dest->pathdata.Points || !dest->pathdata.Types){
82                 GdipFree(dest->pathdata.Points);
83                 GdipFree(dest->pathdata.Types);
84                 GdipFree(dest);
85                 return OutOfMemory;
86             }
87
88             memcpy(dest->pathdata.Points, src->pathdata.Points, count * sizeof(PointF));
89             memcpy(dest->pathdata.Types, src->pathdata.Types, count);
90
91             /* blending */
92             count = src->blendcount;
93             dest->blendcount = count;
94             dest->blendfac = GdipAlloc(count * sizeof(REAL));
95             dest->blendpos = GdipAlloc(count * sizeof(REAL));
96
97             if(!dest->blendfac || !dest->blendpos){
98                 GdipFree(dest->pathdata.Points);
99                 GdipFree(dest->pathdata.Types);
100                 GdipFree(dest->blendfac);
101                 GdipFree(dest->blendpos);
102                 GdipFree(dest);
103                 return OutOfMemory;
104             }
105
106             memcpy(dest->blendfac, src->blendfac, count * sizeof(REAL));
107             memcpy(dest->blendpos, src->blendpos, count * sizeof(REAL));
108
109             break;
110         }
111         case BrushTypeLinearGradient:
112             *clone = GdipAlloc(sizeof(GpLineGradient));
113             if(!*clone)    return OutOfMemory;
114
115             memcpy(*clone, brush, sizeof(GpLineGradient));
116
117             (*clone)->gdibrush = CreateSolidBrush((*clone)->lb.lbColor);
118             break;
119         case BrushTypeTextureFill:
120             *clone = GdipAlloc(sizeof(GpTexture));
121             if(!*clone)    return OutOfMemory;
122
123             memcpy(*clone, brush, sizeof(GpTexture));
124
125             (*clone)->gdibrush = CreateBrushIndirect(&(*clone)->lb);
126             break;
127         default:
128             ERR("not implemented for brush type %d\n", brush->bt);
129             return NotImplemented;
130     }
131
132     return Ok;
133 }
134
135 static LONG HatchStyleToHatch(HatchStyle hatchstyle)
136 {
137     switch (hatchstyle)
138     {
139         case HatchStyleHorizontal:        return HS_HORIZONTAL;
140         case HatchStyleVertical:          return HS_VERTICAL;
141         case HatchStyleForwardDiagonal:   return HS_FDIAGONAL;
142         case HatchStyleBackwardDiagonal:  return HS_BDIAGONAL;
143         case HatchStyleCross:             return HS_CROSS;
144         case HatchStyleDiagonalCross:     return HS_DIAGCROSS;
145         default:                          return 0;
146     }
147 }
148
149 /******************************************************************************
150  * GdipCreateHatchBrush [GDIPLUS.@]
151  */
152 GpStatus WINGDIPAPI GdipCreateHatchBrush(HatchStyle hatchstyle, ARGB forecol, ARGB backcol, GpHatch **brush)
153 {
154     COLORREF fgcol = ARGB2COLORREF(forecol);
155
156     TRACE("(%d, %d, %d, %p)\n", hatchstyle, forecol, backcol, brush);
157
158     if(!brush)  return InvalidParameter;
159
160     *brush = GdipAlloc(sizeof(GpHatch));
161     if (!*brush) return OutOfMemory;
162
163     switch (hatchstyle)
164     {
165         case HatchStyleHorizontal:
166         case HatchStyleVertical:
167         case HatchStyleForwardDiagonal:
168         case HatchStyleBackwardDiagonal:
169         case HatchStyleCross:
170         case HatchStyleDiagonalCross:
171             /* Brushes that map to BS_HATCHED */
172             (*brush)->brush.lb.lbStyle = BS_HATCHED;
173             (*brush)->brush.lb.lbColor = fgcol;
174             (*brush)->brush.lb.lbHatch = HatchStyleToHatch(hatchstyle);
175             break;
176
177         default:
178             FIXME("Unimplemented hatch style %d\n", hatchstyle);
179
180             (*brush)->brush.lb.lbStyle = BS_SOLID;
181             (*brush)->brush.lb.lbColor = fgcol;
182             (*brush)->brush.lb.lbHatch = 0;
183             break;
184     }
185
186
187     (*brush)->brush.gdibrush = CreateBrushIndirect(&(*brush)->brush.lb);
188     (*brush)->brush.bt = BrushTypeHatchFill;
189     (*brush)->forecol = forecol;
190     (*brush)->backcol = backcol;
191     (*brush)->hatchstyle = hatchstyle;
192
193     return Ok;
194 }
195
196 /******************************************************************************
197  * GdipCreateLineBrush [GDIPLUS.@]
198  */
199 GpStatus WINGDIPAPI GdipCreateLineBrush(GDIPCONST GpPointF* startpoint,
200     GDIPCONST GpPointF* endpoint, ARGB startcolor, ARGB endcolor,
201     GpWrapMode wrap, GpLineGradient **line)
202 {
203     COLORREF col = ARGB2COLORREF(startcolor);
204
205     TRACE("(%p, %p, %x, %x, %d, %p)\n", startpoint, endpoint,
206           startcolor, endcolor, wrap, line);
207
208     if(!line || !startpoint || !endpoint || wrap == WrapModeClamp)
209         return InvalidParameter;
210
211     *line = GdipAlloc(sizeof(GpLineGradient));
212     if(!*line)  return OutOfMemory;
213
214     (*line)->brush.lb.lbStyle = BS_SOLID;
215     (*line)->brush.lb.lbColor = col;
216     (*line)->brush.lb.lbHatch = 0;
217     (*line)->brush.gdibrush = CreateSolidBrush(col);
218     (*line)->brush.bt = BrushTypeLinearGradient;
219
220     (*line)->startpoint.X = startpoint->X;
221     (*line)->startpoint.Y = startpoint->Y;
222     (*line)->endpoint.X = endpoint->X;
223     (*line)->endpoint.Y = endpoint->Y;
224     (*line)->startcolor = startcolor;
225     (*line)->endcolor = endcolor;
226     (*line)->wrap = wrap;
227     (*line)->gamma = FALSE;
228
229     return Ok;
230 }
231
232 GpStatus WINGDIPAPI GdipCreateLineBrushI(GDIPCONST GpPoint* startpoint,
233     GDIPCONST GpPoint* endpoint, ARGB startcolor, ARGB endcolor,
234     GpWrapMode wrap, GpLineGradient **line)
235 {
236     GpPointF stF;
237     GpPointF endF;
238
239     TRACE("(%p, %p, %x, %x, %d, %p)\n", startpoint, endpoint,
240           startcolor, endcolor, wrap, line);
241
242     if(!startpoint || !endpoint)
243         return InvalidParameter;
244
245     stF.X  = (REAL)startpoint->X;
246     stF.Y  = (REAL)startpoint->Y;
247     endF.X = (REAL)endpoint->X;
248     endF.X = (REAL)endpoint->Y;
249
250     return GdipCreateLineBrush(&stF, &endF, startcolor, endcolor, wrap, line);
251 }
252
253 GpStatus WINGDIPAPI GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect,
254     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
255     GpLineGradient **line)
256 {
257     GpPointF start, end;
258
259     TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
260           wrap, line);
261
262     if(!line || !rect)
263         return InvalidParameter;
264
265     start.X = rect->X;
266     start.Y = rect->Y;
267     end.X = rect->X + rect->Width;
268     end.Y = rect->Y + rect->Height;
269
270     return GdipCreateLineBrush(&start, &end, startcolor, endcolor, wrap, line);
271 }
272
273 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectI(GDIPCONST GpRect* rect,
274     ARGB startcolor, ARGB endcolor, LinearGradientMode mode, GpWrapMode wrap,
275     GpLineGradient **line)
276 {
277     GpRectF rectF;
278
279     TRACE("(%p, %x, %x, %d, %d, %p)\n", rect, startcolor, endcolor, mode,
280           wrap, line);
281
282     rectF.X      = (REAL) rect->X;
283     rectF.Y      = (REAL) rect->Y;
284     rectF.Width  = (REAL) rect->Width;
285     rectF.Height = (REAL) rect->Height;
286
287     return GdipCreateLineBrushFromRect(&rectF, startcolor, endcolor, mode, wrap, line);
288 }
289
290 /******************************************************************************
291  * GdipCreateLineBrushFromRectWithAngle [GDIPLUS.@]
292  *
293  * FIXME: angle value completely ignored. Don't know how to use it since native
294  *        always set Brush rectangle to rect (independetly of this angle).
295  *        Maybe it's used only on drawing.
296  */
297 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngle(GDIPCONST GpRectF* rect,
298     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
299     GpLineGradient **line)
300 {
301     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
302           wrap, line);
303
304     return GdipCreateLineBrushFromRect(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
305                                        wrap, line);
306 }
307
308 GpStatus WINGDIPAPI GdipCreateLineBrushFromRectWithAngleI(GDIPCONST GpRect* rect,
309     ARGB startcolor, ARGB endcolor, REAL angle, BOOL isAngleScalable, GpWrapMode wrap,
310     GpLineGradient **line)
311 {
312     TRACE("(%p, %x, %x, %.2f, %d, %d, %p)\n", rect, startcolor, endcolor, angle, isAngleScalable,
313           wrap, line);
314
315     return GdipCreateLineBrushFromRectI(rect, startcolor, endcolor, LinearGradientModeForwardDiagonal,
316                                         wrap, line);
317 }
318
319 GpStatus WINGDIPAPI GdipCreatePathGradient(GDIPCONST GpPointF* points,
320     INT count, GpWrapMode wrap, GpPathGradient **grad)
321 {
322     COLORREF col = ARGB2COLORREF(0xffffffff);
323
324     TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
325
326     if(!points || !grad)
327         return InvalidParameter;
328
329     if(count <= 0)
330         return OutOfMemory;
331
332     *grad = GdipAlloc(sizeof(GpPathGradient));
333     if (!*grad) return OutOfMemory;
334
335     (*grad)->blendfac = GdipAlloc(sizeof(REAL));
336     if(!(*grad)->blendfac){
337         GdipFree(*grad);
338         return OutOfMemory;
339     }
340     (*grad)->blendfac[0] = 1.0;
341     (*grad)->blendpos    = NULL;
342     (*grad)->blendcount  = 1;
343
344     (*grad)->pathdata.Count = count;
345     (*grad)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
346     (*grad)->pathdata.Types = GdipAlloc(count);
347
348     if(!(*grad)->pathdata.Points || !(*grad)->pathdata.Types){
349         GdipFree((*grad)->pathdata.Points);
350         GdipFree((*grad)->pathdata.Types);
351         GdipFree(*grad);
352         return OutOfMemory;
353     }
354
355     memcpy((*grad)->pathdata.Points, points, count * sizeof(PointF));
356     memset((*grad)->pathdata.Types, PathPointTypeLine, count);
357
358     (*grad)->brush.lb.lbStyle = BS_SOLID;
359     (*grad)->brush.lb.lbColor = col;
360     (*grad)->brush.lb.lbHatch = 0;
361
362     (*grad)->brush.gdibrush = CreateSolidBrush(col);
363     (*grad)->brush.bt = BrushTypePathGradient;
364     (*grad)->centercolor = 0xffffffff;
365     (*grad)->wrap = wrap;
366     (*grad)->gamma = FALSE;
367     (*grad)->center.X = 0.0;
368     (*grad)->center.Y = 0.0;
369     (*grad)->focus.X = 0.0;
370     (*grad)->focus.Y = 0.0;
371
372     return Ok;
373 }
374
375 GpStatus WINGDIPAPI GdipCreatePathGradientI(GDIPCONST GpPoint* points,
376     INT count, GpWrapMode wrap, GpPathGradient **grad)
377 {
378     GpPointF *pointsF;
379     GpStatus ret;
380     INT i;
381
382     TRACE("(%p, %d, %d, %p)\n", points, count, wrap, grad);
383
384     if(!points || !grad)
385         return InvalidParameter;
386
387     if(count <= 0)
388         return OutOfMemory;
389
390     pointsF = GdipAlloc(sizeof(GpPointF) * count);
391     if(!pointsF)
392         return OutOfMemory;
393
394     for(i = 0; i < count; i++){
395         pointsF[i].X = (REAL)points[i].X;
396         pointsF[i].Y = (REAL)points[i].Y;
397     }
398
399     ret = GdipCreatePathGradient(pointsF, count, wrap, grad);
400     GdipFree(pointsF);
401
402     return ret;
403 }
404
405 /******************************************************************************
406  * GdipCreatePathGradientFromPath [GDIPLUS.@]
407  *
408  * FIXME: path gradient brushes not truly supported (drawn as solid brushes)
409  */
410 GpStatus WINGDIPAPI GdipCreatePathGradientFromPath(GDIPCONST GpPath* path,
411     GpPathGradient **grad)
412 {
413     COLORREF col = ARGB2COLORREF(0xffffffff);
414
415     TRACE("(%p, %p)\n", path, grad);
416
417     if(!path || !grad)
418         return InvalidParameter;
419
420     *grad = GdipAlloc(sizeof(GpPathGradient));
421     if (!*grad) return OutOfMemory;
422
423     (*grad)->blendfac = GdipAlloc(sizeof(REAL));
424     if(!(*grad)->blendfac){
425         GdipFree(*grad);
426         return OutOfMemory;
427     }
428     (*grad)->blendfac[0] = 1.0;
429     (*grad)->blendpos    = NULL;
430     (*grad)->blendcount  = 1;
431
432     (*grad)->pathdata.Count = path->pathdata.Count;
433     (*grad)->pathdata.Points = GdipAlloc(path->pathdata.Count * sizeof(PointF));
434     (*grad)->pathdata.Types = GdipAlloc(path->pathdata.Count);
435
436     if(!(*grad)->pathdata.Points || !(*grad)->pathdata.Types){
437         GdipFree((*grad)->pathdata.Points);
438         GdipFree((*grad)->pathdata.Types);
439         GdipFree(*grad);
440         return OutOfMemory;
441     }
442
443     memcpy((*grad)->pathdata.Points, path->pathdata.Points,
444            path->pathdata.Count * sizeof(PointF));
445     memcpy((*grad)->pathdata.Types, path->pathdata.Types, path->pathdata.Count);
446
447     (*grad)->brush.lb.lbStyle = BS_SOLID;
448     (*grad)->brush.lb.lbColor = col;
449     (*grad)->brush.lb.lbHatch = 0;
450
451     (*grad)->brush.gdibrush = CreateSolidBrush(col);
452     (*grad)->brush.bt = BrushTypePathGradient;
453     (*grad)->centercolor = 0xffffffff;
454     (*grad)->wrap = WrapModeClamp;
455     (*grad)->gamma = FALSE;
456     /* FIXME: this should be set to the "centroid" of the path by default */
457     (*grad)->center.X = 0.0;
458     (*grad)->center.Y = 0.0;
459     (*grad)->focus.X = 0.0;
460     (*grad)->focus.Y = 0.0;
461
462     return Ok;
463 }
464
465 /******************************************************************************
466  * GdipCreateSolidFill [GDIPLUS.@]
467  */
468 GpStatus WINGDIPAPI GdipCreateSolidFill(ARGB color, GpSolidFill **sf)
469 {
470     COLORREF col = ARGB2COLORREF(color);
471
472     TRACE("(%x, %p)\n", color, sf);
473
474     if(!sf)  return InvalidParameter;
475
476     *sf = GdipAlloc(sizeof(GpSolidFill));
477     if (!*sf) return OutOfMemory;
478
479     (*sf)->brush.lb.lbStyle = BS_SOLID;
480     (*sf)->brush.lb.lbColor = col;
481     (*sf)->brush.lb.lbHatch = 0;
482
483     (*sf)->brush.gdibrush = CreateSolidBrush(col);
484     (*sf)->brush.bt = BrushTypeSolidColor;
485     (*sf)->color = color;
486
487     return Ok;
488 }
489
490 /******************************************************************************
491  * GdipCreateTexture [GDIPLUS.@]
492  *
493  * PARAMS
494  *  image       [I] image to use
495  *  wrapmode    [I] optional
496  *  texture     [O] pointer to the resulting texturebrush
497  *
498  * RETURNS
499  *  SUCCESS: Ok
500  *  FAILURE: element of GpStatus
501  */
502 GpStatus WINGDIPAPI GdipCreateTexture(GpImage *image, GpWrapMode wrapmode,
503         GpTexture **texture)
504 {
505     UINT width, height;
506     GpImageAttributes attributes;
507     GpStatus stat;
508
509     TRACE("%p, %d %p\n", image, wrapmode, texture);
510
511     if (!(image && texture))
512         return InvalidParameter;
513
514     stat = GdipGetImageWidth(image, &width);
515     if (stat != Ok) return stat;
516     stat = GdipGetImageHeight(image, &height);
517     if (stat != Ok) return stat;
518     attributes.wrap = wrapmode;
519
520     return GdipCreateTextureIA(image, &attributes, 0, 0, width, height,
521             texture);
522 }
523
524 /******************************************************************************
525  * GdipCreateTexture2 [GDIPLUS.@]
526  */
527 GpStatus WINGDIPAPI GdipCreateTexture2(GpImage *image, GpWrapMode wrapmode,
528         REAL x, REAL y, REAL width, REAL height, GpTexture **texture)
529 {
530     GpImageAttributes attributes;
531
532     TRACE("%p %d %f %f %f %f %p\n", image, wrapmode,
533             x, y, width, height, texture);
534
535     attributes.wrap = wrapmode;
536     return GdipCreateTextureIA(image, &attributes, x, y, width, height,
537             texture);
538 }
539
540 /******************************************************************************
541  * GdipCreateTextureIA [GDIPLUS.@]
542  *
543  * FIXME: imageattr ignored
544  */
545 GpStatus WINGDIPAPI GdipCreateTextureIA(GpImage *image,
546     GDIPCONST GpImageAttributes *imageattr, REAL x, REAL y, REAL width,
547     REAL height, GpTexture **texture)
548 {
549     HDC hdc;
550     HBITMAP hbm, old = NULL;
551     BITMAPINFO *pbmi;
552     BITMAPINFOHEADER *bmih;
553     INT n_x, n_y, n_width, n_height, abs_height, stride, image_stride, i, bytespp;
554     BOOL bm_is_selected;
555     BYTE *dibits, *buff, *textbits;
556     GpStatus status;
557
558     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %p)\n", image, imageattr, x, y, width, height,
559            texture);
560
561     if(!image || !texture || x < 0.0 || y < 0.0 || width < 0.0 || height < 0.0)
562         return InvalidParameter;
563
564     if(image->type != ImageTypeBitmap){
565         FIXME("not implemented for image type %d\n", image->type);
566         return NotImplemented;
567     }
568
569     n_x = roundr(x);
570     n_y = roundr(y);
571     n_width = roundr(width);
572     n_height = roundr(height);
573
574     if(n_x + n_width > ((GpBitmap*)image)->width ||
575        n_y + n_height > ((GpBitmap*)image)->height)
576         return InvalidParameter;
577
578     IPicture_get_Handle(image->picture, (OLE_HANDLE*)&hbm);
579     if(!hbm)   return GenericError;
580     IPicture_get_CurDC(image->picture, &hdc);
581     bm_is_selected = (hdc != 0);
582
583     pbmi = GdipAlloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
584     if (!pbmi)
585         return OutOfMemory;
586     pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
587     pbmi->bmiHeader.biBitCount = 0;
588
589     if(!bm_is_selected){
590         hdc = CreateCompatibleDC(0);
591         old = SelectObject(hdc, hbm);
592     }
593
594     /* fill out bmi */
595     GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
596
597     bytespp = pbmi->bmiHeader.biBitCount / 8;
598     abs_height = abs(pbmi->bmiHeader.biHeight);
599
600     if(n_x > pbmi->bmiHeader.biWidth || n_x + n_width > pbmi->bmiHeader.biWidth ||
601        n_y > abs_height || n_y + n_height > abs_height){
602         GdipFree(pbmi);
603         return InvalidParameter;
604     }
605
606     dibits = GdipAlloc(pbmi->bmiHeader.biSizeImage);
607
608     if(dibits)  /* this is not a good place to error out */
609         GetDIBits(hdc, hbm, 0, abs_height, dibits, pbmi, DIB_RGB_COLORS);
610
611     if(!bm_is_selected){
612         SelectObject(hdc, old);
613         DeleteDC(hdc);
614     }
615
616     if(!dibits){
617         GdipFree(pbmi);
618         return OutOfMemory;
619     }
620
621     image_stride = (pbmi->bmiHeader.biWidth * bytespp + 3) & ~3;
622     stride = (n_width * bytespp + 3) & ~3;
623     buff = GdipAlloc(sizeof(BITMAPINFOHEADER) + stride * n_height);
624     if(!buff){
625         GdipFree(pbmi);
626         GdipFree(dibits);
627         return OutOfMemory;
628     }
629
630     bmih = (BITMAPINFOHEADER*)buff;
631     textbits = (BYTE*) (bmih + 1);
632     bmih->biSize = sizeof(BITMAPINFOHEADER);
633     bmih->biWidth = n_width;
634     bmih->biHeight = n_height;
635     bmih->biCompression = BI_RGB;
636     bmih->biSizeImage = stride * n_height;
637     bmih->biBitCount = pbmi->bmiHeader.biBitCount;
638     bmih->biClrUsed = 0;
639     bmih->biPlanes = 1;
640
641     /* image is flipped */
642     if(pbmi->bmiHeader.biHeight > 0){
643         dibits += pbmi->bmiHeader.biSizeImage;
644         image_stride *= -1;
645         textbits += stride * (n_height - 1);
646         stride *= -1;
647     }
648
649     GdipFree(pbmi);
650
651     for(i = 0; i < n_height; i++)
652         memcpy(&textbits[i * stride],
653                &dibits[n_x * bytespp + (n_y + i) * image_stride],
654                abs(stride));
655
656     *texture = GdipAlloc(sizeof(GpTexture));
657     if (!*texture){
658         GdipFree(dibits);
659         GdipFree(buff);
660         return OutOfMemory;
661     }
662
663     if((status = GdipCreateMatrix(&(*texture)->transform)) != Ok){
664         GdipFree(*texture);
665         GdipFree(dibits);
666         GdipFree(buff);
667         return status;
668     }
669
670     (*texture)->brush.lb.lbStyle = BS_DIBPATTERNPT;
671     (*texture)->brush.lb.lbColor = DIB_RGB_COLORS;
672     (*texture)->brush.lb.lbHatch = (ULONG_PTR)buff;
673
674     (*texture)->brush.gdibrush = CreateBrushIndirect(&(*texture)->brush.lb);
675     (*texture)->brush.bt = BrushTypeTextureFill;
676     (*texture)->wrap = imageattr->wrap;
677
678     GdipFree(dibits);
679     GdipFree(buff);
680
681     return Ok;
682 }
683
684 /******************************************************************************
685  * GdipCreateTextureIAI [GDIPLUS.@]
686  */
687 GpStatus WINGDIPAPI GdipCreateTextureIAI(GpImage *image, GDIPCONST GpImageAttributes *imageattr,
688     INT x, INT y, INT width, INT height, GpTexture **texture)
689 {
690     TRACE("(%p, %p, %d, %d, %d, %d, %p)\n", image, imageattr, x, y, width, height,
691            texture);
692
693     return GdipCreateTextureIA(image,imageattr,(REAL)x,(REAL)y,(REAL)width,(REAL)height,texture);
694 }
695
696 GpStatus WINGDIPAPI GdipCreateTexture2I(GpImage *image, GpWrapMode wrapmode,
697         INT x, INT y, INT width, INT height, GpTexture **texture)
698 {
699     GpImageAttributes imageattr;
700
701     TRACE("%p %d %d %d %d %d %p\n", image, wrapmode, x, y, width, height,
702             texture);
703
704     imageattr.wrap = wrapmode;
705
706     return GdipCreateTextureIA(image, &imageattr, x, y, width, height, texture);
707 }
708
709 GpStatus WINGDIPAPI GdipGetBrushType(GpBrush *brush, GpBrushType *type)
710 {
711     TRACE("(%p, %p)\n", brush, type);
712
713     if(!brush || !type)  return InvalidParameter;
714
715     *type = brush->bt;
716
717     return Ok;
718 }
719
720 GpStatus WINGDIPAPI GdipGetHatchBackgroundColor(GpHatch *brush, ARGB *backcol)
721 {
722     TRACE("(%p, %p)\n", brush, backcol);
723
724     if(!brush || !backcol)  return InvalidParameter;
725
726     *backcol = brush->backcol;
727
728     return Ok;
729 }
730
731 GpStatus WINGDIPAPI GdipGetHatchForegroundColor(GpHatch *brush, ARGB *forecol)
732 {
733     TRACE("(%p, %p)\n", brush, forecol);
734
735     if(!brush || !forecol)  return InvalidParameter;
736
737     *forecol = brush->forecol;
738
739     return Ok;
740 }
741
742 GpStatus WINGDIPAPI GdipGetHatchStyle(GpHatch *brush, HatchStyle *hatchstyle)
743 {
744     TRACE("(%p, %p)\n", brush, hatchstyle);
745
746     if(!brush || !hatchstyle)  return InvalidParameter;
747
748     *hatchstyle = brush->hatchstyle;
749
750     return Ok;
751 }
752
753 GpStatus WINGDIPAPI GdipDeleteBrush(GpBrush *brush)
754 {
755     TRACE("(%p)\n", brush);
756
757     if(!brush)  return InvalidParameter;
758
759     switch(brush->bt)
760     {
761         case BrushTypePathGradient:
762             GdipFree(((GpPathGradient*) brush)->pathdata.Points);
763             GdipFree(((GpPathGradient*) brush)->pathdata.Types);
764             GdipFree(((GpPathGradient*) brush)->blendfac);
765             GdipFree(((GpPathGradient*) brush)->blendpos);
766             break;
767         case BrushTypeSolidColor:
768         case BrushTypeLinearGradient:
769             break;
770         case BrushTypeTextureFill:
771             GdipDeleteMatrix(((GpTexture*)brush)->transform);
772             break;
773         default:
774             break;
775     }
776
777     DeleteObject(brush->gdibrush);
778     GdipFree(brush);
779
780     return Ok;
781 }
782
783 GpStatus WINGDIPAPI GdipGetLineGammaCorrection(GpLineGradient *line,
784     BOOL *usinggamma)
785 {
786     TRACE("(%p, %p)\n", line, usinggamma);
787
788     if(!line || !usinggamma)
789         return InvalidParameter;
790
791     *usinggamma = line->gamma;
792
793     return Ok;
794 }
795
796 GpStatus WINGDIPAPI GdipGetLineWrapMode(GpLineGradient *brush, GpWrapMode *wrapmode)
797 {
798     TRACE("(%p, %p)\n", brush, wrapmode);
799
800     if(!brush || !wrapmode)
801         return InvalidParameter;
802
803     *wrapmode = brush->wrap;
804
805     return Ok;
806 }
807
808 GpStatus WINGDIPAPI GdipGetPathGradientBlend(GpPathGradient *brush, REAL *blend,
809     REAL *positions, INT count)
810 {
811     TRACE("(%p, %p, %p, %d)\n", brush, blend, positions, count);
812
813     if(!brush || !blend || !positions || count <= 0)
814         return InvalidParameter;
815
816     if(count < brush->blendcount)
817         return InsufficientBuffer;
818
819     memcpy(blend, brush->blendfac, count*sizeof(REAL));
820     if(brush->blendcount > 1){
821         memcpy(positions, brush->blendpos, count*sizeof(REAL));
822     }
823
824     return Ok;
825 }
826
827 GpStatus WINGDIPAPI GdipGetPathGradientBlendCount(GpPathGradient *brush, INT *count)
828 {
829     TRACE("(%p, %p)\n", brush, count);
830
831     if(!brush || !count)
832         return InvalidParameter;
833
834     *count = brush->blendcount;
835
836     return Ok;
837 }
838
839 GpStatus WINGDIPAPI GdipGetPathGradientCenterPoint(GpPathGradient *grad,
840     GpPointF *point)
841 {
842     TRACE("(%p, %p)\n", grad, point);
843
844     if(!grad || !point)
845         return InvalidParameter;
846
847     point->X = grad->center.X;
848     point->Y = grad->center.Y;
849
850     return Ok;
851 }
852
853 GpStatus WINGDIPAPI GdipGetPathGradientCenterPointI(GpPathGradient *grad,
854     GpPoint *point)
855 {
856     GpStatus ret;
857     GpPointF ptf;
858
859     TRACE("(%p, %p)\n", grad, point);
860
861     if(!point)
862         return InvalidParameter;
863
864     ret = GdipGetPathGradientCenterPoint(grad,&ptf);
865
866     if(ret == Ok){
867         point->X = roundr(ptf.X);
868         point->Y = roundr(ptf.Y);
869     }
870
871     return ret;
872 }
873
874 GpStatus WINGDIPAPI GdipGetPathGradientFocusScales(GpPathGradient *grad,
875     REAL *x, REAL *y)
876 {
877     TRACE("(%p, %p, %p)\n", grad, x, y);
878
879     if(!grad || !x || !y)
880         return InvalidParameter;
881
882     *x = grad->focus.X;
883     *y = grad->focus.Y;
884
885     return Ok;
886 }
887
888 GpStatus WINGDIPAPI GdipGetPathGradientGammaCorrection(GpPathGradient *grad,
889     BOOL *gamma)
890 {
891     TRACE("(%p, %p)\n", grad, gamma);
892
893     if(!grad || !gamma)
894         return InvalidParameter;
895
896     *gamma = grad->gamma;
897
898     return Ok;
899 }
900
901 GpStatus WINGDIPAPI GdipGetPathGradientPointCount(GpPathGradient *grad,
902     INT *count)
903 {
904     TRACE("(%p, %p)\n", grad, count);
905
906     if(!grad || !count)
907         return InvalidParameter;
908
909     *count = grad->pathdata.Count;
910
911     return Ok;
912 }
913
914 GpStatus WINGDIPAPI GdipGetPathGradientRect(GpPathGradient *brush, GpRectF *rect)
915 {
916     GpRectF r;
917     GpPath* path;
918     GpStatus stat;
919
920     TRACE("(%p, %p)\n", brush, rect);
921
922     if(!brush || !rect)
923         return InvalidParameter;
924
925     stat = GdipCreatePath2(brush->pathdata.Points, brush->pathdata.Types,
926                            brush->pathdata.Count, FillModeAlternate, &path);
927     if(stat != Ok)  return stat;
928
929     stat = GdipGetPathWorldBounds(path, &r, NULL, NULL);
930     if(stat != Ok){
931         GdipDeletePath(path);
932         return stat;
933     }
934
935     memcpy(rect, &r, sizeof(GpRectF));
936
937     GdipDeletePath(path);
938
939     return Ok;
940 }
941
942 GpStatus WINGDIPAPI GdipGetPathGradientRectI(GpPathGradient *brush, GpRect *rect)
943 {
944     GpRectF rectf;
945     GpStatus stat;
946
947     TRACE("(%p, %p)\n", brush, rect);
948
949     if(!brush || !rect)
950         return InvalidParameter;
951
952     stat = GdipGetPathGradientRect(brush, &rectf);
953     if(stat != Ok)  return stat;
954
955     rect->X = roundr(rectf.X);
956     rect->Y = roundr(rectf.Y);
957     rect->Width  = roundr(rectf.Width);
958     rect->Height = roundr(rectf.Height);
959
960     return Ok;
961 }
962
963 GpStatus WINGDIPAPI GdipGetPathGradientSurroundColorsWithCount(GpPathGradient
964     *grad, ARGB *argb, INT *count)
965 {
966     static int calls;
967
968     if(!grad || !argb || !count || (*count < grad->pathdata.Count))
969         return InvalidParameter;
970
971     if(!(calls++))
972         FIXME("not implemented\n");
973
974     return NotImplemented;
975 }
976
977 GpStatus WINGDIPAPI GdipGetPathGradientWrapMode(GpPathGradient *brush,
978     GpWrapMode *wrapmode)
979 {
980     TRACE("(%p, %p)\n", brush, wrapmode);
981
982     if(!brush || !wrapmode)
983         return InvalidParameter;
984
985     *wrapmode = brush->wrap;
986
987     return Ok;
988 }
989
990 GpStatus WINGDIPAPI GdipGetSolidFillColor(GpSolidFill *sf, ARGB *argb)
991 {
992     TRACE("(%p, %p)\n", sf, argb);
993
994     if(!sf || !argb)
995         return InvalidParameter;
996
997     *argb = sf->color;
998
999     return Ok;
1000 }
1001
1002 /******************************************************************************
1003  * GdipGetTextureTransform [GDIPLUS.@]
1004  */
1005 GpStatus WINGDIPAPI GdipGetTextureTransform(GpTexture *brush, GpMatrix *matrix)
1006 {
1007     TRACE("(%p, %p)\n", brush, matrix);
1008
1009     if(!brush || !matrix)
1010         return InvalidParameter;
1011
1012     memcpy(matrix, brush->transform, sizeof(GpMatrix));
1013
1014     return Ok;
1015 }
1016
1017 /******************************************************************************
1018  * GdipGetTextureWrapMode [GDIPLUS.@]
1019  */
1020 GpStatus WINGDIPAPI GdipGetTextureWrapMode(GpTexture *brush, GpWrapMode *wrapmode)
1021 {
1022     TRACE("(%p, %p)\n", brush, wrapmode);
1023
1024     if(!brush || !wrapmode)
1025         return InvalidParameter;
1026
1027     *wrapmode = brush->wrap;
1028
1029     return Ok;
1030 }
1031
1032 /******************************************************************************
1033  * GdipMultiplyTextureTransform [GDIPLUS.@]
1034  */
1035 GpStatus WINGDIPAPI GdipMultiplyTextureTransform(GpTexture* brush,
1036     GDIPCONST GpMatrix *matrix, GpMatrixOrder order)
1037 {
1038     TRACE("(%p, %p, %d)\n", brush, matrix, order);
1039
1040     if(!brush || !matrix)
1041         return InvalidParameter;
1042
1043     return GdipMultiplyMatrix(brush->transform, matrix, order);
1044 }
1045
1046 /******************************************************************************
1047  * GdipResetTextureTransform [GDIPLUS.@]
1048  */
1049 GpStatus WINGDIPAPI GdipResetTextureTransform(GpTexture* brush)
1050 {
1051     TRACE("(%p)\n", brush);
1052
1053     if(!brush)
1054         return InvalidParameter;
1055
1056     return GdipSetMatrixElements(brush->transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1057 }
1058
1059 /******************************************************************************
1060  * GdipScaleTextureTransform [GDIPLUS.@]
1061  */
1062 GpStatus WINGDIPAPI GdipScaleTextureTransform(GpTexture* brush,
1063     REAL sx, REAL sy, GpMatrixOrder order)
1064 {
1065     TRACE("(%p, %.2f, %.2f, %d)\n", brush, sx, sy, order);
1066
1067     if(!brush)
1068         return InvalidParameter;
1069
1070     return GdipScaleMatrix(brush->transform, sx, sy, order);
1071 }
1072
1073 GpStatus WINGDIPAPI GdipSetLineBlend(GpLineGradient *brush,
1074     GDIPCONST REAL *blend, GDIPCONST REAL* positions, INT count)
1075 {
1076     static int calls;
1077
1078     if(!brush || !blend || !positions || count <= 0)
1079         return InvalidParameter;
1080
1081     if(!(calls++))
1082         FIXME("not implemented\n");
1083
1084     return Ok;
1085 }
1086
1087 GpStatus WINGDIPAPI GdipSetLineGammaCorrection(GpLineGradient *line,
1088     BOOL usegamma)
1089 {
1090     TRACE("(%p, %d)\n", line, usegamma);
1091
1092     if(!line)
1093         return InvalidParameter;
1094
1095     line->gamma = usegamma;
1096
1097     return Ok;
1098 }
1099
1100 GpStatus WINGDIPAPI GdipSetLineSigmaBlend(GpLineGradient *line, REAL focus,
1101     REAL scale)
1102 {
1103     static int calls;
1104
1105     if(!line || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
1106         return InvalidParameter;
1107
1108     if(!(calls++))
1109         FIXME("not implemented\n");
1110
1111     return NotImplemented;
1112 }
1113
1114 GpStatus WINGDIPAPI GdipSetLineWrapMode(GpLineGradient *line,
1115     GpWrapMode wrap)
1116 {
1117     TRACE("(%p, %d)\n", line, wrap);
1118
1119     if(!line || wrap == WrapModeClamp)
1120         return InvalidParameter;
1121
1122     line->wrap = wrap;
1123
1124     return Ok;
1125 }
1126
1127 GpStatus WINGDIPAPI GdipSetPathGradientBlend(GpPathGradient *brush, GDIPCONST REAL *blend,
1128     GDIPCONST REAL *pos, INT count)
1129 {
1130     static int calls;
1131
1132     if(!(calls++))
1133         FIXME("not implemented\n");
1134
1135     return NotImplemented;
1136 }
1137
1138 GpStatus WINGDIPAPI GdipSetPathGradientCenterColor(GpPathGradient *grad,
1139     ARGB argb)
1140 {
1141     TRACE("(%p, %x)\n", grad, argb);
1142
1143     if(!grad)
1144         return InvalidParameter;
1145
1146     grad->centercolor = argb;
1147     grad->brush.lb.lbColor = ARGB2COLORREF(argb);
1148
1149     DeleteObject(grad->brush.gdibrush);
1150     grad->brush.gdibrush = CreateSolidBrush(grad->brush.lb.lbColor);
1151
1152     return Ok;
1153 }
1154
1155 GpStatus WINGDIPAPI GdipSetPathGradientCenterPoint(GpPathGradient *grad,
1156     GpPointF *point)
1157 {
1158     TRACE("(%p, %p)\n", grad, point);
1159
1160     if(!grad || !point)
1161         return InvalidParameter;
1162
1163     grad->center.X = point->X;
1164     grad->center.Y = point->Y;
1165
1166     return Ok;
1167 }
1168
1169 GpStatus WINGDIPAPI GdipSetPathGradientCenterPointI(GpPathGradient *grad,
1170     GpPoint *point)
1171 {
1172     GpPointF ptf;
1173
1174     TRACE("(%p, %p)\n", grad, point);
1175
1176     if(!point)
1177         return InvalidParameter;
1178
1179     ptf.X = (REAL)point->X;
1180     ptf.Y = (REAL)point->Y;
1181
1182     return GdipSetPathGradientCenterPoint(grad,&ptf);
1183 }
1184
1185 GpStatus WINGDIPAPI GdipSetPathGradientFocusScales(GpPathGradient *grad,
1186     REAL x, REAL y)
1187 {
1188     TRACE("(%p, %.2f, %.2f)\n", grad, x, y);
1189
1190     if(!grad)
1191         return InvalidParameter;
1192
1193     grad->focus.X = x;
1194     grad->focus.Y = y;
1195
1196     return Ok;
1197 }
1198
1199 GpStatus WINGDIPAPI GdipSetPathGradientGammaCorrection(GpPathGradient *grad,
1200     BOOL gamma)
1201 {
1202     TRACE("(%p, %d)\n", grad, gamma);
1203
1204     if(!grad)
1205         return InvalidParameter;
1206
1207     grad->gamma = gamma;
1208
1209     return Ok;
1210 }
1211
1212 GpStatus WINGDIPAPI GdipSetPathGradientSigmaBlend(GpPathGradient *grad,
1213     REAL focus, REAL scale)
1214 {
1215     static int calls;
1216
1217     if(!grad || focus < 0.0 || focus > 1.0 || scale < 0.0 || scale > 1.0)
1218         return InvalidParameter;
1219
1220     if(!(calls++))
1221         FIXME("not implemented\n");
1222
1223     return NotImplemented;
1224 }
1225
1226 GpStatus WINGDIPAPI GdipSetPathGradientSurroundColorsWithCount(GpPathGradient
1227     *grad, ARGB *argb, INT *count)
1228 {
1229     static int calls;
1230
1231     if(!grad || !argb || !count || (*count <= 0) ||
1232         (*count > grad->pathdata.Count))
1233         return InvalidParameter;
1234
1235     if(!(calls++))
1236         FIXME("not implemented\n");
1237
1238     return NotImplemented;
1239 }
1240
1241 GpStatus WINGDIPAPI GdipSetPathGradientWrapMode(GpPathGradient *grad,
1242     GpWrapMode wrap)
1243 {
1244     TRACE("(%p, %d)\n", grad, wrap);
1245
1246     if(!grad)
1247         return InvalidParameter;
1248
1249     grad->wrap = wrap;
1250
1251     return Ok;
1252 }
1253
1254 GpStatus WINGDIPAPI GdipSetSolidFillColor(GpSolidFill *sf, ARGB argb)
1255 {
1256     TRACE("(%p, %x)\n", sf, argb);
1257
1258     if(!sf)
1259         return InvalidParameter;
1260
1261     sf->color = argb;
1262     sf->brush.lb.lbColor = ARGB2COLORREF(argb);
1263
1264     DeleteObject(sf->brush.gdibrush);
1265     sf->brush.gdibrush = CreateSolidBrush(sf->brush.lb.lbColor);
1266
1267     return Ok;
1268 }
1269
1270 /******************************************************************************
1271  * GdipSetTextureTransform [GDIPLUS.@]
1272  */
1273 GpStatus WINGDIPAPI GdipSetTextureTransform(GpTexture *texture,
1274     GDIPCONST GpMatrix *matrix)
1275 {
1276     TRACE("(%p, %p)\n", texture, matrix);
1277
1278     if(!texture || !matrix)
1279         return InvalidParameter;
1280
1281     memcpy(texture->transform, matrix, sizeof(GpMatrix));
1282
1283     return Ok;
1284 }
1285
1286 /******************************************************************************
1287  * GdipSetTextureWrapMode [GDIPLUS.@]
1288  *
1289  * WrapMode not used, only stored
1290  */
1291 GpStatus WINGDIPAPI GdipSetTextureWrapMode(GpTexture *brush, GpWrapMode wrapmode)
1292 {
1293     TRACE("(%p, %d)\n", brush, wrapmode);
1294
1295     if(!brush)
1296         return InvalidParameter;
1297
1298     brush->wrap = wrapmode;
1299
1300     return Ok;
1301 }
1302
1303 GpStatus WINGDIPAPI GdipSetLineColors(GpLineGradient *brush, ARGB color1,
1304     ARGB color2)
1305 {
1306     TRACE("(%p, %x, %x)\n", brush, color1, color2);
1307
1308     if(!brush)
1309         return InvalidParameter;
1310
1311     brush->startcolor = color1;
1312     brush->endcolor   = color2;
1313
1314     return Ok;
1315 }
1316
1317 GpStatus WINGDIPAPI GdipGetLineColors(GpLineGradient *brush, ARGB *colors)
1318 {
1319     TRACE("(%p, %p)\n", brush, colors);
1320
1321     if(!brush || !colors)
1322         return InvalidParameter;
1323
1324     colors[0] = brush->startcolor;
1325     colors[1] = brush->endcolor;
1326
1327     return Ok;
1328 }
1329
1330 /******************************************************************************
1331  * GdipRotateTextureTransform [GDIPLUS.@]
1332  */
1333 GpStatus WINGDIPAPI GdipRotateTextureTransform(GpTexture* brush, REAL angle,
1334     GpMatrixOrder order)
1335 {
1336     TRACE("(%p, %.2f, %d)\n", brush, angle, order);
1337
1338     if(!brush)
1339         return InvalidParameter;
1340
1341     return GdipRotateMatrix(brush->transform, angle, order);
1342 }
1343
1344 GpStatus WINGDIPAPI GdipSetLineLinearBlend(GpLineGradient *brush, REAL focus,
1345     REAL scale)
1346 {
1347     static int calls;
1348
1349     if(!(calls++))
1350         FIXME("not implemented\n");
1351
1352     return NotImplemented;
1353 }
1354
1355 GpStatus WINGDIPAPI GdipSetLinePresetBlend(GpLineGradient *brush,
1356     GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
1357 {
1358     static int calls;
1359
1360     if(!(calls++))
1361         FIXME("not implemented\n");
1362
1363     return NotImplemented;
1364 }
1365
1366 GpStatus WINGDIPAPI GdipSetLineTransform(GpLineGradient *brush,
1367     GDIPCONST GpMatrix *matrix)
1368 {
1369     static int calls;
1370
1371     if(!(calls++))
1372         FIXME("not implemented\n");
1373
1374     return NotImplemented;
1375 }
1376
1377 GpStatus WINGDIPAPI GdipTranslateLineTransform(GpLineGradient* brush,
1378         REAL dx, REAL dy, GpMatrixOrder order)
1379 {
1380     FIXME("stub: %p %f %f %d\n", brush, dx, dy, order);
1381
1382     return NotImplemented;
1383 }
1384
1385 /******************************************************************************
1386  * GdipTranslateTextureTransform [GDIPLUS.@]
1387  */
1388 GpStatus WINGDIPAPI GdipTranslateTextureTransform(GpTexture* brush, REAL dx, REAL dy,
1389     GpMatrixOrder order)
1390 {
1391     TRACE("(%p, %.2f, %.2f, %d)\n", brush, dx, dy, order);
1392
1393     if(!brush)
1394         return InvalidParameter;
1395
1396     return GdipTranslateMatrix(brush->transform, dx, dy, order);
1397 }
1398
1399 GpStatus WINGDIPAPI GdipGetLineRect(GpLineGradient *brush, GpRectF *rect)
1400 {
1401     TRACE("(%p, %p)\n", brush, rect);
1402
1403     if(!brush || !rect)
1404         return InvalidParameter;
1405
1406     rect->X = (brush->startpoint.X < brush->endpoint.X ? brush->startpoint.X: brush->endpoint.X);
1407     rect->Y = (brush->startpoint.Y < brush->endpoint.Y ? brush->startpoint.Y: brush->endpoint.Y);
1408
1409     rect->Width  = fabs(brush->startpoint.X - brush->endpoint.X);
1410     rect->Height = fabs(brush->startpoint.Y - brush->endpoint.Y);
1411
1412     return Ok;
1413 }
1414
1415 GpStatus WINGDIPAPI GdipGetLineRectI(GpLineGradient *brush, GpRect *rect)
1416 {
1417     GpRectF  rectF;
1418     GpStatus ret;
1419
1420     TRACE("(%p, %p)\n", brush, rect);
1421
1422     if(!rect)
1423         return InvalidParameter;
1424
1425     ret = GdipGetLineRect(brush, &rectF);
1426
1427     if(ret == Ok){
1428         rect->X      = roundr(rectF.X);
1429         rect->Y      = roundr(rectF.Y);
1430         rect->Width  = roundr(rectF.Width);
1431         rect->Height = roundr(rectF.Height);
1432     }
1433
1434     return ret;
1435 }
1436
1437 GpStatus WINGDIPAPI GdipRotateLineTransform(GpLineGradient* brush,
1438     REAL angle, GpMatrixOrder order)
1439 {
1440     static int calls;
1441
1442     if(!brush)
1443         return InvalidParameter;
1444
1445     if(!(calls++))
1446         FIXME("(%p, %.2f, %d) stub\n", brush, angle, order);
1447
1448     return NotImplemented;
1449 }