xref: /reactos/win32ss/gdi/ntgdi/brush.cpp (revision 84ccccab)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS win32 subsystem
4  * PURPOSE:           BRUSH class implementation
5  * PROGRAMER:         Timo Kreuzer (timo.kreuzer@reactos.org)
6  *
7  * REFERENCES:        http://support.microsoft.com/kb/kbview/108497
8  */
9 
10 #include "brush.hpp"
11 
12 DBG_DEFAULT_CHANNEL(GdiBrush);
13 
14 BRUSH::BRUSH(
15     _In_ FLONG flAttrs,
16     _In_ COLORREF crColor,
17     _In_ ULONG iHatch,
18     _In_opt_ HBITMAP hbmPattern,
19     _In_opt_ PVOID pvClient,
20     _In_ GDILOOBJTYPE loobjtype = GDILoObjType_LO_BRUSH_TYPE)
21     : BASEOBJECT(loobjtype)
22 {
23     static ULONG ulGlobalBrushUnique = 0;
24 
25     /* Get a unique value */
26     this->ulBrushUnique = InterlockedIncrementUL(&ulGlobalBrushUnique);
27 
28     /* Start with kmode brush attribute */
29     this->pBrushAttr = &this->BrushAttr;
30 
31     /* Set parameters */
32     this->flAttrs = flAttrs;
33     this->iHatch = iHatch;
34     this->hbmPattern = hbmPattern;
35     this->hbmClient = (HBITMAP)pvClient;
36     this->pBrushAttr->lbColor = crColor;
37 
38     /* Initialize the other fields */
39     this->ptOrigin.x = 0;
40     this->ptOrigin.y = 0;
41     this->bCacheGrabbed = FALSE;
42     this->crBack = 0;
43     this->crFore = 0;
44     this->ulPalTime = 0;
45     this->ulSurfTime = 0;
46     this->pvRBrush = NULL;
47     this->hdev = NULL;
48 
49     /* FIXME: should be done only in PEN constructor,
50        but our destructor needs it! */
51     this->dwStyleCount = 0;
52     this->pStyle = NULL;
53 }
54 
55 BRUSH::~BRUSH(
56     VOID)
57 {
58     /* Check if we have a user mode brush attribute */
59     if (this->pBrushAttr != &this->BrushAttr)
60     {
61         /* Free memory to the process GDI pool */
62         GdiPoolFree(GetBrushAttrPool(), this->pBrushAttr);
63     }
64 
65     /* Delete the pattern bitmap (may have already been deleted during gdi cleanup) */
66     if (this->hbmPattern != NULL && GreIsHandleValid(this->hbmPattern))
67     {
68         GreSetBitmapOwner(this->hbmPattern, BASEOBJECT::OWNER::POWNED);
69         GreDeleteObject(this->hbmPattern);
70     }
71 
72     /* Delete styles */
73     if ((this->pStyle != NULL) && !(this->flAttrs & BR_IS_DEFAULTSTYLE))
74     {
75         ExFreePoolWithTag(this->pStyle, GDITAG_PENSTYLE);
76     }
77 }
78 
79 VOID
80 BRUSH::vDeleteObject(
81     _In_ PVOID pvObject)
82 {
83     PBRUSH pbr = static_cast<PBRUSH>(pvObject);
84     NT_ASSERT((GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_BRUSH_TYPE) ||
85               (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_PEN_TYPE) ||
86               (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_EXTPEN_TYPE));
87     delete pbr;
88 }
89 
90 BOOL
91 BRUSH::bAllocateBrushAttr(
92     VOID)
93 {
94     PBRUSH_ATTR pBrushAttr;
95     NT_ASSERT(this->pBrushAttr == &this->BrushAttr);
96 
97     /* Allocate a brush attribute from the pool */
98     pBrushAttr = static_cast<PBRUSH_ATTR>(GdiPoolAllocate(GetBrushAttrPool()));
99     if (pBrushAttr == NULL)
100     {
101         ERR("Could not allocate brush attr\n");
102         return FALSE;
103     }
104 
105     /* Copy the content from the kernel mode brush attribute */
106     this->pBrushAttr = pBrushAttr;
107     *this->pBrushAttr = this->BrushAttr;
108 
109     /* Set the object attribute in the handle table */
110     vSetObjectAttr(pBrushAttr);
111 
112     return TRUE;
113 }
114 
115 VOID
116 BRUSH::vSetSolidColor(
117     _In_ COLORREF crColor)
118 {
119     NT_ASSERT(this->flAttrs & BR_IS_SOLID);
120 
121     /* Set new color and reset the pal times */
122     this->pBrushAttr->lbColor = crColor & 0xFFFFFF;
123     this->ulPalTime = -1;
124     this->ulSurfTime = -1;
125 }
126 
127 HBITMAP
128 BRUSH::hbmGetBitmapHandle(
129     _Out_ PUINT puUsage) const
130 {
131     /* Return the color usage based on flags */
132     *puUsage = (this->flAttrs & BR_IS_DIBPALCOLORS) ? DIB_PAL_COLORS :
133                (this->flAttrs & BR_IS_DIBPALINDICES) ? DIB_PAL_INDICES :
134                DIB_RGB_COLORS;
135 
136     return this->hbmPattern;
137 }
138 
139 UINT
140 BRUSH::cjGetObject(
141     _In_ UINT cjSize,
142     _Out_bytecap_(cjSize) PLOGBRUSH plb) const
143 {
144     /* Check if only size is requested */
145     if (plb == NULL)
146         return sizeof(LOGBRUSH);
147 
148     /* Check if size is ok */
149     if (cjSize == 0)
150         return 0;
151 
152     /* Set color */
153     plb->lbColor = this->BrushAttr.lbColor;
154 
155     /* Set style and hatch based on the attribute flags */
156     if (this->flAttrs & BR_IS_SOLID)
157     {
158         plb->lbStyle = BS_SOLID;
159         plb->lbHatch = 0;
160     }
161     else if (this->flAttrs & BR_IS_HATCH)
162     {
163         plb->lbStyle = BS_HATCHED;
164         plb->lbHatch = this->iHatch;
165     }
166     else if (this->flAttrs & BR_IS_DIB)
167     {
168         plb->lbStyle = BS_DIBPATTERN;
169         plb->lbHatch = (ULONG_PTR)this->hbmClient;
170     }
171     else if (this->flAttrs & BR_IS_BITMAP)
172     {
173         plb->lbStyle = BS_PATTERN;
174         plb->lbHatch = (ULONG_PTR)this->hbmClient;
175     }
176     else if (this->flAttrs & BR_IS_NULL)
177     {
178         plb->lbStyle = BS_NULL;
179         plb->lbHatch = 0;
180     }
181     else
182     {
183         NT_ASSERT(FALSE);
184     }
185 
186     return sizeof(LOGBRUSH);
187 }
188 
189 static
190 HBRUSH
191 CreateBrushInternal(
192     _In_ ULONG flAttrs,
193     _In_ COLORREF crColor,
194     _In_ ULONG iHatch,
195     _In_opt_ HBITMAP hbmPattern,
196     _In_opt_ PVOID pvClient)
197 {
198     BASEOBJECT::OWNER owner;
199     PBRUSH pbr;
200     HBRUSH hbr;
201 
202     NT_ASSERT(((flAttrs & BR_IS_BITMAP) == 0) || (hbmPattern != NULL));
203 
204     /* Create the brush (brush takes ownership of the bitmap) */
205     pbr = new BRUSH(flAttrs, crColor, iHatch, hbmPattern, pvClient);
206     if (pbr == NULL)
207     {
208         ERR("Failed to allocate a brush\n");
209         GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::POWNED);
210         GreDeleteObject(hbmPattern);
211         return NULL;
212     }
213 
214     /* Check if this is a global brush */
215     if (!(flAttrs & BR_IS_GLOBAL))
216     {
217         /* Not a global brush, so allocate a user mode brush attribute */
218         if (!pbr->bAllocateBrushAttr())
219         {
220             ERR("Failed to allocate brush attribute\n");
221             delete pbr;
222             return NULL;
223         }
224     }
225 
226     /* Set the owner, either public or process owned */
227     owner = (flAttrs & BR_IS_GLOBAL) ? BASEOBJECT::OWNER::PUBLIC :
228                                        BASEOBJECT::OWNER::POWNED;
229 
230     /* Insert the object into the GDI handle table */
231     hbr =  static_cast<HBRUSH>(pbr->hInsertObject(owner));
232     if (hbr == NULL)
233     {
234         ERR("Failed to insert brush\n");
235         delete pbr;
236         return NULL;
237     }
238 
239     /* Unlock the brush */
240     pbr->vUnlock();
241 
242     return hbr;
243 }
244 
245 
246 /* C interface ***************************************************************/
247 
248 extern "C" {
249 
250 VOID
251 NTAPI
252 BRUSH_vDeleteObject(
253     PVOID pvObject)
254 {
255     BRUSH::vDeleteObject(pvObject);
256 }
257 
258 INT
259 FASTCALL
260 BRUSH_GetObject(
261     PBRUSH pbr,
262     INT cjBuffer,
263     LPLOGBRUSH plbBuffer)
264 {
265     return pbr->cjGetObject(cjBuffer, plbBuffer);
266 }
267 
268 HBRUSH
269 NTAPI
270 IntGdiCreateNullBrush(
271     VOID)
272 {
273     /* Call the internal function */
274     return CreateBrushInternal(BR_IS_NULL | BR_IS_GLOBAL, 0, 0, NULL, NULL);
275 }
276 
277 HBRUSH
278 APIENTRY
279 IntGdiCreateSolidBrush(
280     COLORREF crColor)
281 {
282     /* Call the internal function */
283     return CreateBrushInternal(BR_IS_SOLID | BR_IS_GLOBAL,
284                                crColor,
285                                0,
286                                NULL,
287                                NULL);
288 }
289 
290 HBRUSH
291 NTAPI
292 IntGdiCreatePatternBrush(
293     HBITMAP hbmPattern)
294 {
295     NT_ASSERT(hbmPattern != NULL);
296     GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::PUBLIC);
297     return CreateBrushInternal(BR_IS_BITMAP | BR_IS_GLOBAL,
298                                0,
299                                0,
300                                hbmPattern,
301                                NULL);
302 }
303 
304 VOID
305 NTAPI
306 IntGdiSetSolidBrushColor(
307     _In_ HBRUSH hbr,
308     _In_ COLORREF crColor)
309 {
310     PBRUSH pbr;
311 
312     /* Lock the brush */
313     pbr = BRUSH::LockAny(hbr);
314     if (pbr == NULL)
315     {
316         ERR("Failed to lock brush %p\n", hbr);
317         return;
318     }
319 
320     /* Call the member function */
321     pbr->vSetSolidColor(crColor);
322 
323     /* Unlock the brush */
324     pbr->vUnlock();
325 }
326 
327 __kernel_entry
328 HBRUSH
329 APIENTRY
330 NtGdiCreateSolidBrush(
331     _In_ COLORREF crColor,
332     _In_opt_ HBRUSH hbr)
333 {
334     if (hbr != NULL)
335     {
336         WARN("hbr is not supported, ignoring\n");
337     }
338 
339     /* Call the internal function */
340     return CreateBrushInternal(BR_IS_SOLID, crColor, 0, NULL, NULL);
341 }
342 
343 __kernel_entry
344 HBRUSH
345 APIENTRY
346 NtGdiCreateHatchBrushInternal(
347     _In_ ULONG iHatch,
348     _In_ COLORREF crColor,
349     _In_ BOOL bPen)
350 {
351     FLONG flAttr;
352 
353     if (bPen)
354     {
355         WARN("bPen is not supported, ignoring\n");
356     }
357 
358     /* Check what kind if hatch style this is */
359     if (iHatch < HS_DDI_MAX)
360     {
361         flAttr = BR_IS_HATCH;
362     }
363     else if (iHatch < HS_API_MAX)
364     {
365         flAttr = BR_IS_SOLID;
366     }
367     else
368     {
369         ERR("Invalid iHatch: %lu\n", iHatch);
370         return NULL;
371     }
372 
373     /* Call the internal function */
374     return CreateBrushInternal(flAttr, crColor, iHatch, NULL, NULL);
375 }
376 
377 __kernel_entry
378 HBRUSH
379 APIENTRY
380 NtGdiCreatePatternBrushInternal(
381     _In_ HBITMAP hbmClient,
382     _In_ BOOL bPen,
383     _In_ BOOL b8X8)
384 {
385     HBITMAP hbmPattern;
386 
387     if (b8X8)
388     {
389         WARN("b8X8 is not supported, ignoring\n");
390     }
391 
392     if (bPen)
393     {
394         WARN("bPen is not supported, ignoring\n");
395     }
396 
397     /* Copy the bitmap */
398     hbmPattern = BITMAP_CopyBitmap(hbmClient);
399     if (hbmPattern == NULL)
400     {
401         ERR("Failed to copy the bitmap %p\n", hbmPattern);
402         return NULL;
403     }
404 
405     /* Call the internal function (will delete hbmPattern on failure) */
406     return CreateBrushInternal(BR_IS_BITMAP, 0, 0, hbmPattern, hbmClient);
407 }
408 
409 __kernel_entry
410 HBRUSH
411 APIENTRY
412 NtGdiCreateDIBBrush(
413     _In_reads_bytes_(cj) PVOID pv,
414     _In_ FLONG uUsage,
415     _In_ UINT cj,
416     _In_ BOOL b8X8,
417     _In_ BOOL bPen,
418     _In_ PVOID pvClient)
419 {
420     PVOID pvPackedDIB;
421     FLONG flAttrs;
422     HBITMAP hbm;
423     HBRUSH hbr = NULL;
424 
425     if (b8X8)
426     {
427         WARN("b8X8 is not supported, ignoring\n");
428     }
429 
430     if (bPen)
431     {
432         WARN("bPen is not supported, ignoring\n");
433     }
434 
435     if (uUsage > DIB_PAL_INDICES)
436     {
437         ERR("Invalid uUsage value: %lu\n", uUsage);
438         EngSetLastError(ERROR_INVALID_PARAMETER);
439         return NULL;
440     }
441 
442     /* Allocate a buffer for the packed DIB */
443     pvPackedDIB = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_TEMP);
444     if (pvPackedDIB == NULL)
445     {
446         ERR("Failed to allocate temp buffer of %u bytes\n", cj);
447         return NULL;
448     }
449 
450     /* Probe and copy the packed DIB */
451     _SEH2_TRY
452     {
453         ProbeForRead(pv, cj, 1);
454         RtlCopyMemory(pvPackedDIB, pv, cj);
455     }
456     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
457     {
458         ERR("Got exception, pv = %p, cj = %lu\n", pv, cj);
459         goto cleanup;
460     }
461     _SEH2_END;
462 
463     flAttrs = BR_IS_BITMAP | BR_IS_DIB;
464 
465     /* Check what kind of color table we have */
466     if (uUsage == DIB_PAL_COLORS)
467     {
468         /* Remember it and use DIB_PAL_BRUSHHACK to create a "special" palette */
469         flAttrs |= BR_IS_DIBPALCOLORS;
470         uUsage = DIB_PAL_BRUSHHACK;
471     }
472     else if (uUsage == DIB_PAL_INDICES)
473     {
474         /* No color table, bitmap contains device palette indices */
475         flAttrs |= BR_IS_DIBPALINDICES;
476 
477         /* FIXME: This makes tests pass, but needs investigation. */
478         flAttrs |= BR_IS_NULL;
479     }
480 
481     /* Create a bitmap from the DIB */
482     hbm = GreCreateDIBitmapFromPackedDIB(pvPackedDIB, cj, uUsage);
483     if (hbm == NULL)
484     {
485         ERR("Failed to create bitmap from DIB\n");
486         goto cleanup;
487     }
488 
489     /* Call the internal function (will delete hbm on failure) */
490     hbr = CreateBrushInternal(flAttrs, 0, 0, hbm, pvClient);
491 
492 cleanup:
493 
494     ExFreePoolWithTag(pvPackedDIB, GDITAG_TEMP);
495 
496     return hbr;
497 }
498 
499 __kernel_entry
500 HBITMAP
501 APIENTRY
502 NtGdiGetObjectBitmapHandle(
503     _In_ HBRUSH hbr,
504     _Out_ UINT *piUsage)
505 {
506     PBRUSH pbr;
507     HBITMAP hbm;
508     UINT uUsage;
509 
510     /* Lock the brush */
511     pbr = BRUSH::LockForRead(hbr);
512     if (pbr == NULL)
513     {
514         ERR("Failed to lock brush %p\n", hbr);
515         return NULL;
516     }
517 
518     /* Call the member function */
519     hbm = pbr->hbmGetBitmapHandle(&uUsage);
520 
521     /* Unlock the brush */
522     pbr->vUnlock();
523 
524     _SEH2_TRY
525     {
526         ProbeForWrite(piUsage, sizeof(*piUsage), 1);
527         *piUsage = uUsage;
528     }
529     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
530     {
531         ERR("Got exception! piUsage = %p\n", piUsage);
532         hbm = NULL;
533     }
534     _SEH2_END;
535 
536     return hbm;
537 }
538 
539 __kernel_entry
540 HBRUSH
541 APIENTRY
542 NtGdiSetBrushAttributes(
543     _In_ HBRUSH hbr,
544     _In_ DWORD dwFlags)
545 {
546     __debugbreak();
547     return NULL;
548 }
549 
550 __kernel_entry
551 HBRUSH
552 APIENTRY
553 NtGdiClearBrushAttributes(
554     _In_ HBRUSH hbr,
555     _In_ DWORD dwFlags)
556 {
557     __debugbreak();
558     return NULL;
559 }
560 
561 } /* extern "C" */
562