Return a stock brush when asking for a brush with the same
[wine] / dlls / gdi / brush.c
1 /*
2  * GDI brush objects
3  *
4  * Copyright 1993, 1994  Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <stdarg.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "wine/wingdi16.h"
30 #include "gdi.h"
31 #include "wownt32.h"
32 #include "gdi_private.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(gdi);
36
37 /* GDI logical brush object */
38 typedef struct
39 {
40     GDIOBJHDR header;
41     LOGBRUSH  logbrush;
42 } BRUSHOBJ;
43
44 #define NB_HATCH_STYLES  6
45
46 static HGDIOBJ BRUSH_SelectObject( HGDIOBJ handle, void *obj, HDC hdc );
47 static INT BRUSH_GetObject16( HGDIOBJ handle, void *obj, INT count, LPVOID buffer );
48 static INT BRUSH_GetObject( HGDIOBJ handle, void *obj, INT count, LPVOID buffer );
49 static BOOL BRUSH_DeleteObject( HGDIOBJ handle, void *obj );
50
51 static const struct gdi_obj_funcs brush_funcs =
52 {
53     BRUSH_SelectObject,  /* pSelectObject */
54     BRUSH_GetObject16,   /* pGetObject16 */
55     BRUSH_GetObject,     /* pGetObjectA */
56     BRUSH_GetObject,     /* pGetObjectW */
57     NULL,                /* pUnrealizeObject */
58     BRUSH_DeleteObject   /* pDeleteObject */
59 };
60
61 static HGLOBAL16 dib_copy(BITMAPINFO *info, UINT coloruse)
62 {
63     BITMAPINFO  *newInfo;
64     HGLOBAL16   hmem;
65     INT         size;
66
67     if (info->bmiHeader.biCompression)
68         size = info->bmiHeader.biSizeImage;
69     else
70         size = DIB_GetDIBImageBytes(info->bmiHeader.biWidth,
71                                     info->bmiHeader.biHeight,
72                                     info->bmiHeader.biBitCount);
73     size += DIB_BitmapInfoSize( info, coloruse );
74
75     if (!(hmem = GlobalAlloc16( GMEM_MOVEABLE, size )))
76     {
77         return 0;
78     }
79     newInfo = (BITMAPINFO *) GlobalLock16( hmem );
80     memcpy( newInfo, info, size );
81     GlobalUnlock16( hmem );
82     return hmem;
83 }
84
85
86 /***********************************************************************
87  *           CreateBrushIndirect    (GDI32.@)
88  *
89  * Create a logical brush with a given style, color or pattern.
90  *
91  * PARAMS
92  *  brush [I] Pointer to a LOGBRUSH structure describing the desired brush.
93  *
94  * RETURNS
95  *  A handle to the created brush, or a NULL handle if the brush cannot be 
96  *  created.
97  *
98  * NOTES
99  * - The brush returned should be freed by the caller using DeleteObject()
100  *   when it is no longer required.
101  * - Windows 95 and earlier cannot create brushes from bitmaps or DIBs larger
102  *   than 8x8 pixels. If a larger bitmap is given, only a portion of the bitmap
103  *   is used.
104  * - If the brush to be created matches a stock brush, a stock brush will be
105  *   returned. This behaviour is undocumented.
106  */
107 HBRUSH WINAPI CreateBrushIndirect( const LOGBRUSH * brush )
108 {
109     static const DWORD stockMap[] = { /* Map of RGB colors of stock brushes */
110         RGB(255,255,255), WHITE_BRUSH,
111         RGB(192,192,192), LTGRAY_BRUSH,
112         RGB(128,128,128), GRAY_BRUSH,
113         RGB(0,0,0),       BLACK_BRUSH
114     };
115     BRUSHOBJ * ptr;
116     HBRUSH hbrush;
117
118     if (brush->lbStyle == BS_SOLID)
119     {
120         size_t i;
121
122         /* If a solid brush is created in a color matching one of the 
123          * stock brushes, native returns the stock object (GDI heap 
124          * optimisation). Some apps rely on this as they otherwise
125          * would leak their brushes.
126          */
127         for (i = 0; i < (sizeof(stockMap)/sizeof(stockMap[0])) / 2; i += 2)
128         {
129             if (brush->lbColor == stockMap[i])
130             {
131                 HBRUSH hBr = GetStockObject(stockMap[i + 1]);
132                 if (hBr)
133                     return hBr; /* Return stock brush */
134                 break; /* Being called to create a stock brush, fall through */
135             }
136         }
137     }
138     
139     if (!(ptr = GDI_AllocObject( sizeof(BRUSHOBJ), BRUSH_MAGIC,
140                                 (HGDIOBJ *)&hbrush, &brush_funcs ))) return 0;
141     ptr->logbrush.lbStyle = brush->lbStyle;
142     ptr->logbrush.lbColor = brush->lbColor;
143     ptr->logbrush.lbHatch = brush->lbHatch;
144
145     switch (ptr->logbrush.lbStyle)
146     {
147     case BS_PATTERN8X8:
148         ptr->logbrush.lbStyle = BS_PATTERN;
149         /* fall through */
150     case BS_PATTERN:
151         ptr->logbrush.lbHatch = (LONG)BITMAP_CopyBitmap( (HBITMAP) ptr->logbrush.lbHatch );
152         if (!ptr->logbrush.lbHatch) goto error;
153         break;
154
155     case BS_DIBPATTERNPT:
156         ptr->logbrush.lbStyle = BS_DIBPATTERN;
157         ptr->logbrush.lbHatch = (LONG)dib_copy( (BITMAPINFO *) ptr->logbrush.lbHatch,
158                                                 ptr->logbrush.lbColor);
159         if (!ptr->logbrush.lbHatch) goto error;
160         break;
161
162     case BS_DIBPATTERN8X8:
163     case BS_DIBPATTERN:
164        {
165             BITMAPINFO* bmi;
166             HGLOBAL h = (HGLOBAL)ptr->logbrush.lbHatch;
167
168             ptr->logbrush.lbStyle = BS_DIBPATTERN;
169             if (!(bmi = (BITMAPINFO *)GlobalLock( h ))) goto error;
170             ptr->logbrush.lbHatch = dib_copy( bmi, ptr->logbrush.lbColor);
171             GlobalUnlock( h );
172             if (!ptr->logbrush.lbHatch) goto error;
173             break;
174        }
175
176     default:
177         if(ptr->logbrush.lbStyle > BS_MONOPATTERN) goto error;
178         break;
179     }
180
181     GDI_ReleaseObj( hbrush );
182     TRACE("%p\n", hbrush);
183     return hbrush;
184
185  error:
186     GDI_FreeObject( hbrush, ptr );
187     return 0;
188 }
189
190
191 /***********************************************************************
192  *           CreateHatchBrush    (GDI32.@)
193  *
194  * Create a logical brush with a hatched pattern.
195  *
196  * PARAMS
197  *  style [I] Direction of lines for the hatch pattern (HS_* values from "wingdi.h")
198  *  color [I] Colour of the hatched pattern
199  *
200  * RETURNS
201  *  A handle to the created brush, or a NULL handle if the brush cannot
202  *  be created.
203  *
204  * NOTES
205  * - This function uses CreateBrushIndirect() to create the brush.
206  * - The brush returned should be freed by the caller using DeleteObject()
207  *   when it is no longer required.
208  */
209 HBRUSH WINAPI CreateHatchBrush( INT style, COLORREF color )
210 {
211     LOGBRUSH logbrush;
212
213     TRACE("%d %06lx\n", style, color );
214
215     logbrush.lbStyle = BS_HATCHED;
216     logbrush.lbColor = color;
217     logbrush.lbHatch = style;
218
219     return CreateBrushIndirect( &logbrush );
220 }
221
222
223 /***********************************************************************
224  *           CreatePatternBrush    (GDI32.@)
225  *
226  * Create a logical brush with a pattern from a bitmap.
227  *
228  * PARAMS
229  *  hbitmap  [I] Bitmap containing pattern for the brush
230  *
231  * RETURNS
232  *  A handle to the created brush, or a NULL handle if the brush cannot 
233  *  be created.
234  *
235  * NOTES
236  * - This function uses CreateBrushIndirect() to create the brush.
237  * - The brush returned should be freed by the caller using DeleteObject()
238  *   when it is no longer required.
239  */
240 HBRUSH WINAPI CreatePatternBrush( HBITMAP hbitmap )
241 {
242     LOGBRUSH logbrush = { BS_PATTERN, 0, 0 };
243     TRACE("%p\n", hbitmap );
244
245     logbrush.lbHatch = (ULONG_PTR)hbitmap;
246     return CreateBrushIndirect( &logbrush );
247 }
248
249
250 /***********************************************************************
251  *           CreateDIBPatternBrush    (GDI32.@)
252  *
253  * Create a logical brush with a pattern from a DIB.
254  *
255  * PARAMS
256  *  hbitmap  [I] Global object containing BITMAPINFO structure for the pattern
257  *  coloruse [I] Specifies color format, if provided
258  *
259  * RETURNS
260  *  A handle to the created brush, or a NULL handle if the brush cannot 
261  *  be created.
262  *
263  * NOTES
264  * - This function uses CreateBrushIndirect() to create the brush.
265  * - The brush returned should be freed by the caller using DeleteObject()
266  *   when it is no longer required.
267  * - This function is for compatibility only. CreateDIBPatternBrushPt() should 
268  *   be used instead.
269  */
270 HBRUSH WINAPI CreateDIBPatternBrush( HGLOBAL hbitmap, UINT coloruse )
271 {
272     LOGBRUSH logbrush;
273
274     TRACE("%p\n", hbitmap );
275
276     logbrush.lbStyle = BS_DIBPATTERN;
277     logbrush.lbColor = coloruse;
278
279     logbrush.lbHatch = (LONG)hbitmap;
280
281     return CreateBrushIndirect( &logbrush );
282 }
283
284
285 /***********************************************************************
286  *           CreateDIBPatternBrushPt    (GDI32.@)
287  *
288  * Create a logical brush with a pattern from a DIB.
289  *
290  * PARAMS
291  *  data     [I] Pointer to a BITMAPINFO structure and image data  for the pattern
292  *  coloruse [I] Specifies color format, if provided
293  *
294  * RETURNS
295  *  A handle to the created brush, or a NULL handle if the brush cannot
296  *  be created.
297  *
298  * NOTES
299  * - This function uses CreateBrushIndirect() to create the brush.
300  * - The brush returned should be freed by the caller using DeleteObject()
301  *   when it is no longer required.
302  */
303 HBRUSH WINAPI CreateDIBPatternBrushPt( const void* data, UINT coloruse )
304 {
305     BITMAPINFO *info=(BITMAPINFO*)data;
306     LOGBRUSH logbrush;
307
308     if (!data)
309         return NULL;
310
311     TRACE("%p %ldx%ld %dbpp\n", info, info->bmiHeader.biWidth,
312           info->bmiHeader.biHeight,  info->bmiHeader.biBitCount);
313
314     logbrush.lbStyle = BS_DIBPATTERNPT;
315     logbrush.lbColor = coloruse;
316     logbrush.lbHatch = (LONG) data;
317
318     return CreateBrushIndirect( &logbrush );
319 }
320
321
322 /***********************************************************************
323  *           CreateSolidBrush    (GDI32.@)
324  *
325  * Create a logical brush consisting of a single colour.
326  *
327  * PARAMS
328  *  color [I] Colour to make the solid brush
329  *
330  * RETURNS
331  *  A handle to the newly created brush, or a NULL handle if the brush cannot
332  *  be created.
333  *
334  * NOTES
335  * - This function uses CreateBrushIndirect() to create the brush.
336  * - The brush returned should be freed by the caller using DeleteObject()
337  *   when it is no longer required.
338  */
339 HBRUSH WINAPI CreateSolidBrush( COLORREF color )
340 {
341     LOGBRUSH logbrush;
342
343     TRACE("%06lx\n", color );
344
345     logbrush.lbStyle = BS_SOLID;
346     logbrush.lbColor = color;
347     logbrush.lbHatch = 0;
348
349     return CreateBrushIndirect( &logbrush );
350 }
351
352
353 /***********************************************************************
354  *           SetBrushOrgEx    (GDI32.@)
355  *
356  * Set the brush origin for a device context.
357  *
358  * PARAMS
359  *  hdc    [I] Device context to set the brush origin for 
360  *  x      [I] New x origin
361  *  y      [I] Ney y origin
362  *  oldorg [O] If non NULL, destination for previously set brush origin.
363  *
364  * RETURNS
365  *  Success: TRUE. The origin is set to (x,y), and oldorg is updated if given.
366  */
367 BOOL WINAPI SetBrushOrgEx( HDC hdc, INT x, INT y, LPPOINT oldorg )
368 {
369     DC *dc = DC_GetDCPtr( hdc );
370
371     if (!dc) return FALSE;
372     if (oldorg)
373     {
374         oldorg->x = dc->brushOrgX;
375         oldorg->y = dc->brushOrgY;
376     }
377     dc->brushOrgX = x;
378     dc->brushOrgY = y;
379     GDI_ReleaseObj( hdc );
380     return TRUE;
381 }
382
383 /***********************************************************************
384  *           FixBrushOrgEx    (GDI32.@)
385  *
386  * See SetBrushOrgEx.
387  *
388  * NOTES
389  *  This function is no longer documented by MSDN, but in Win95 GDI32 it
390  *  is the same as SetBrushOrgEx().
391  */
392 BOOL WINAPI FixBrushOrgEx( HDC hdc, INT x, INT y, LPPOINT oldorg )
393 {
394     return SetBrushOrgEx(hdc,x,y,oldorg);
395 }
396
397
398 /***********************************************************************
399  *           BRUSH_SelectObject
400  */
401 static HGDIOBJ BRUSH_SelectObject( HGDIOBJ handle, void *obj, HDC hdc )
402 {
403     BRUSHOBJ *brush = obj;
404     HGDIOBJ ret;
405     DC *dc = DC_GetDCPtr( hdc );
406
407     if (!dc) return 0;
408
409     if (brush->logbrush.lbStyle == BS_PATTERN)
410         BITMAP_SetOwnerDC( (HBITMAP)brush->logbrush.lbHatch, dc );
411
412     ret = dc->hBrush;
413     if (dc->funcs->pSelectBrush) handle = dc->funcs->pSelectBrush( dc->physDev, handle );
414     if (handle) dc->hBrush = handle;
415     else ret = 0;
416     GDI_ReleaseObj( hdc );
417     return ret;
418 }
419
420
421 /***********************************************************************
422  *           BRUSH_DeleteObject
423  */
424 static BOOL BRUSH_DeleteObject( HGDIOBJ handle, void *obj )
425 {
426     BRUSHOBJ *brush = obj;
427
428     switch(brush->logbrush.lbStyle)
429     {
430       case BS_PATTERN:
431           DeleteObject( (HGDIOBJ)brush->logbrush.lbHatch );
432           break;
433       case BS_DIBPATTERN:
434           GlobalFree16( (HGLOBAL16)brush->logbrush.lbHatch );
435           break;
436     }
437     return GDI_FreeObject( handle, obj );
438 }
439
440
441 /***********************************************************************
442  *           BRUSH_GetObject16
443  */
444 static INT BRUSH_GetObject16( HGDIOBJ handle, void *obj, INT count, LPVOID buffer )
445 {
446     BRUSHOBJ *brush = obj;
447     LOGBRUSH16 logbrush;
448
449     logbrush.lbStyle = brush->logbrush.lbStyle;
450     logbrush.lbColor = brush->logbrush.lbColor;
451     logbrush.lbHatch = brush->logbrush.lbHatch;
452     if (count > sizeof(logbrush)) count = sizeof(logbrush);
453     memcpy( buffer, &logbrush, count );
454     return count;
455 }
456
457
458 /***********************************************************************
459  *           BRUSH_GetObject
460  */
461 static INT BRUSH_GetObject( HGDIOBJ handle, void *obj, INT count, LPVOID buffer )
462 {
463     BRUSHOBJ *brush = obj;
464
465     if( !buffer )
466         return sizeof(brush->logbrush);
467
468     if (count > sizeof(brush->logbrush)) count = sizeof(brush->logbrush);
469     memcpy( buffer, &brush->logbrush, count );
470     return count;
471 }
472
473
474 /***********************************************************************
475  *           SetSolidBrush   (GDI.604)
476  *
477  * Change the color of a solid brush.
478  *
479  * PARAMS
480  *  hBrush   [I] Brush to change the color of
481  *  newColor [I] New color for hBrush
482  *
483  * RETURNS
484  *  Success: TRUE. The color of hBrush is set to newColor.
485  *  Failure: FALSE.
486  *
487  * FIXME
488  *  This function is undocumented and untested. The implementation may
489  *  not be correct.
490  */
491 BOOL16 WINAPI SetSolidBrush16(HBRUSH16 hBrush, COLORREF newColor )
492 {
493     BRUSHOBJ * brushPtr;
494     BOOL16 res = FALSE;
495
496     TRACE("(hBrush %04x, newColor %08lx)\n", hBrush, (DWORD)newColor);
497     if (!(brushPtr = (BRUSHOBJ *) GDI_GetObjPtr( HBRUSH_32(hBrush), BRUSH_MAGIC )))
498         return FALSE;
499
500     if (brushPtr->logbrush.lbStyle == BS_SOLID)
501     {
502         brushPtr->logbrush.lbColor = newColor;
503         res = TRUE;
504     }
505
506      GDI_ReleaseObj( HBRUSH_32(hBrush) );
507      return res;
508 }