xref: /reactos/win32ss/gdi/ntgdi/brush.cpp (revision b5fae844)
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 
BRUSH(_In_ FLONG flAttrs,_In_ COLORREF crColor,_In_ ULONG iHatch,_In_opt_ HBITMAP hbmPattern,_In_opt_ PVOID pvClient,_In_ GDILOOBJTYPE loobjtype=GDILoObjType_LO_BRUSH_TYPE)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 
~BRUSH(VOID)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
vReleaseAttribute(VOID)80 BRUSH::vReleaseAttribute(VOID)
81 {
82     if (this->pBrushAttr != &this->BrushAttr)
83     {
84         this->BrushAttr = *this->pBrushAttr;
85         GdiPoolFree(GetBrushAttrPool(), this->pBrushAttr);
86         this->pBrushAttr = &this->BrushAttr;
87     }
88 }
89 
90 VOID
vDeleteObject(_In_ PVOID pvObject)91 BRUSH::vDeleteObject(
92     _In_ PVOID pvObject)
93 {
94     PBRUSH pbr = static_cast<PBRUSH>(pvObject);
95     NT_ASSERT((GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_BRUSH_TYPE) ||
96               (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_PEN_TYPE) ||
97               (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_EXTPEN_TYPE));
98     delete pbr;
99 }
100 
101 BOOL
bAllocateBrushAttr(VOID)102 BRUSH::bAllocateBrushAttr(
103     VOID)
104 {
105     PBRUSH_ATTR pBrushAttr;
106     NT_ASSERT(this->pBrushAttr == &this->BrushAttr);
107 
108     /* Allocate a brush attribute from the pool */
109     pBrushAttr = static_cast<PBRUSH_ATTR>(GdiPoolAllocate(GetBrushAttrPool()));
110     if (pBrushAttr == NULL)
111     {
112         ERR("Could not allocate brush attr\n");
113         return FALSE;
114     }
115 
116     /* Copy the content from the kernel mode brush attribute */
117     this->pBrushAttr = pBrushAttr;
118     *this->pBrushAttr = this->BrushAttr;
119 
120     /* Set the object attribute in the handle table */
121     vSetObjectAttr(pBrushAttr);
122 
123     return TRUE;
124 }
125 
126 VOID
vSetSolidColor(_In_ COLORREF crColor)127 BRUSH::vSetSolidColor(
128     _In_ COLORREF crColor)
129 {
130     NT_ASSERT(this->flAttrs & BR_IS_SOLID);
131 
132     /* Set new color and reset the pal times */
133     this->pBrushAttr->lbColor = crColor & 0xFFFFFF;
134     this->ulPalTime = -1;
135     this->ulSurfTime = -1;
136 }
137 
138 HBITMAP
hbmGetBitmapHandle(_Out_ PUINT puUsage) const139 BRUSH::hbmGetBitmapHandle(
140     _Out_ PUINT puUsage) const
141 {
142     /* Return the color usage based on flags */
143     *puUsage = (this->flAttrs & BR_IS_DIBPALCOLORS) ? DIB_PAL_COLORS :
144                (this->flAttrs & BR_IS_DIBPALINDICES) ? DIB_PAL_INDICES :
145                DIB_RGB_COLORS;
146 
147     return this->hbmPattern;
148 }
149 
150 UINT
cjGetObject(_In_ UINT cjSize,_Out_bytecap_ (cjSize)PLOGBRUSH plb) const151 BRUSH::cjGetObject(
152     _In_ UINT cjSize,
153     _Out_bytecap_(cjSize) PLOGBRUSH plb) const
154 {
155     /* Check if only size is requested */
156     if (plb == NULL)
157         return sizeof(LOGBRUSH);
158 
159     /* Check if size is ok */
160     if (cjSize == 0)
161         return 0;
162 
163     /* Set color */
164     plb->lbColor = this->BrushAttr.lbColor;
165 
166     /* Set style and hatch based on the attribute flags */
167     if (this->flAttrs & BR_IS_SOLID)
168     {
169         plb->lbStyle = BS_SOLID;
170         plb->lbHatch = 0;
171     }
172     else if (this->flAttrs & BR_IS_HATCH)
173     {
174         plb->lbStyle = BS_HATCHED;
175         plb->lbHatch = this->iHatch;
176     }
177     else if (this->flAttrs & BR_IS_DIB)
178     {
179         plb->lbStyle = BS_DIBPATTERN;
180         plb->lbHatch = (ULONG_PTR)this->hbmClient;
181     }
182     else if (this->flAttrs & BR_IS_BITMAP)
183     {
184         plb->lbStyle = BS_PATTERN;
185         plb->lbHatch = (ULONG_PTR)this->hbmClient;
186     }
187     else if (this->flAttrs & BR_IS_NULL)
188     {
189         plb->lbStyle = BS_NULL;
190         plb->lbHatch = 0;
191     }
192     else
193     {
194         NT_ASSERT(FALSE);
195     }
196 
197     return sizeof(LOGBRUSH);
198 }
199 
200 static
201 HBRUSH
CreateBrushInternal(_In_ ULONG flAttrs,_In_ COLORREF crColor,_In_ ULONG iHatch,_In_opt_ HBITMAP hbmPattern,_In_opt_ PVOID pvClient)202 CreateBrushInternal(
203     _In_ ULONG flAttrs,
204     _In_ COLORREF crColor,
205     _In_ ULONG iHatch,
206     _In_opt_ HBITMAP hbmPattern,
207     _In_opt_ PVOID pvClient)
208 {
209     BASEOBJECT::OWNER owner;
210     PBRUSH pbr;
211     HBRUSH hbr;
212 
213     NT_ASSERT(((flAttrs & BR_IS_BITMAP) == 0) || (hbmPattern != NULL));
214 
215     /* Create the brush (brush takes ownership of the bitmap) */
216     pbr = new BRUSH(flAttrs, crColor, iHatch, hbmPattern, pvClient);
217     if (pbr == NULL)
218     {
219         ERR("Failed to allocate a brush\n");
220         GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::POWNED);
221         GreDeleteObject(hbmPattern);
222         return NULL;
223     }
224 
225     /* Check if this is a global brush */
226     if (!(flAttrs & BR_IS_GLOBAL))
227     {
228         /* Not a global brush, so allocate a user mode brush attribute */
229         if (!pbr->bAllocateBrushAttr())
230         {
231             ERR("Failed to allocate brush attribute\n");
232             delete pbr;
233             return NULL;
234         }
235     }
236 
237     /* Set the owner, either public or process owned */
238     owner = (flAttrs & BR_IS_GLOBAL) ? BASEOBJECT::OWNER::PUBLIC :
239                                        BASEOBJECT::OWNER::POWNED;
240 
241     /* Insert the object into the GDI handle table */
242     hbr =  static_cast<HBRUSH>(pbr->hInsertObject(owner));
243     if (hbr == NULL)
244     {
245         ERR("Failed to insert brush\n");
246         delete pbr;
247         return NULL;
248     }
249 
250     /* Unlock the brush */
251     pbr->vUnlock();
252 
253     return hbr;
254 }
255 
256 
257 /* C interface ***************************************************************/
258 
259 extern "C" {
260 
261 VOID
262 NTAPI
BRUSH_vDeleteObject(PVOID pvObject)263 BRUSH_vDeleteObject(
264     PVOID pvObject)
265 {
266     BRUSH::vDeleteObject(pvObject);
267 }
268 
269 INT
270 FASTCALL
BRUSH_GetObject(PBRUSH pbr,INT cjBuffer,LPLOGBRUSH plbBuffer)271 BRUSH_GetObject(
272     PBRUSH pbr,
273     INT cjBuffer,
274     LPLOGBRUSH plbBuffer)
275 {
276     return pbr->cjGetObject(cjBuffer, plbBuffer);
277 }
278 
279 HBRUSH
280 NTAPI
IntGdiCreateNullBrush(VOID)281 IntGdiCreateNullBrush(
282     VOID)
283 {
284     /* Call the internal function */
285     return CreateBrushInternal(BR_IS_NULL | BR_IS_GLOBAL, 0, 0, NULL, NULL);
286 }
287 
288 HBRUSH
289 APIENTRY
IntGdiCreateSolidBrush(COLORREF crColor)290 IntGdiCreateSolidBrush(
291     COLORREF crColor)
292 {
293     /* Call the internal function */
294     return CreateBrushInternal(BR_IS_SOLID | BR_IS_GLOBAL,
295                                crColor,
296                                0,
297                                NULL,
298                                NULL);
299 }
300 
301 HBRUSH
302 NTAPI
IntGdiCreatePatternBrush(HBITMAP hbmPattern)303 IntGdiCreatePatternBrush(
304     HBITMAP hbmPattern)
305 {
306     NT_ASSERT(hbmPattern != NULL);
307     GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::PUBLIC);
308     return CreateBrushInternal(BR_IS_BITMAP | BR_IS_GLOBAL,
309                                0,
310                                0,
311                                hbmPattern,
312                                NULL);
313 }
314 
315 VOID
316 NTAPI
IntGdiSetSolidBrushColor(_In_ HBRUSH hbr,_In_ COLORREF crColor)317 IntGdiSetSolidBrushColor(
318     _In_ HBRUSH hbr,
319     _In_ COLORREF crColor)
320 {
321     PBRUSH pbr;
322 
323     /* Lock the brush */
324     pbr = BRUSH::LockAny(hbr);
325     if (pbr == NULL)
326     {
327         ERR("Failed to lock brush %p\n", hbr);
328         return;
329     }
330 
331     /* Call the member function */
332     pbr->vSetSolidColor(crColor);
333 
334     /* Unlock the brush */
335     pbr->vUnlock();
336 }
337 
338 __kernel_entry
339 HBRUSH
340 APIENTRY
NtGdiCreateSolidBrush(_In_ COLORREF crColor,_In_opt_ HBRUSH hbr)341 NtGdiCreateSolidBrush(
342     _In_ COLORREF crColor,
343     _In_opt_ HBRUSH hbr)
344 {
345     if (hbr != NULL)
346     {
347         WARN("hbr is not supported, ignoring\n");
348     }
349 
350     /* Call the internal function */
351     return CreateBrushInternal(BR_IS_SOLID, crColor, 0, NULL, NULL);
352 }
353 
354 __kernel_entry
355 HBRUSH
356 APIENTRY
NtGdiCreateHatchBrushInternal(_In_ ULONG iHatch,_In_ COLORREF crColor,_In_ BOOL bPen)357 NtGdiCreateHatchBrushInternal(
358     _In_ ULONG iHatch,
359     _In_ COLORREF crColor,
360     _In_ BOOL bPen)
361 {
362     FLONG flAttr;
363 
364     if (bPen)
365     {
366         WARN("bPen is not supported, ignoring\n");
367     }
368 
369     /* Check what kind if hatch style this is */
370     if (iHatch < HS_DDI_MAX)
371     {
372         flAttr = BR_IS_HATCH;
373     }
374     else if (iHatch < HS_API_MAX)
375     {
376         flAttr = BR_IS_SOLID;
377     }
378     else
379     {
380         ERR("Invalid iHatch: %lu\n", iHatch);
381         return NULL;
382     }
383 
384     /* Call the internal function */
385     return CreateBrushInternal(flAttr, crColor, iHatch, NULL, NULL);
386 }
387 
388 __kernel_entry
389 HBRUSH
390 APIENTRY
NtGdiCreatePatternBrushInternal(_In_ HBITMAP hbmClient,_In_ BOOL bPen,_In_ BOOL b8X8)391 NtGdiCreatePatternBrushInternal(
392     _In_ HBITMAP hbmClient,
393     _In_ BOOL bPen,
394     _In_ BOOL b8X8)
395 {
396     HBITMAP hbmPattern;
397 
398     if (b8X8)
399     {
400         WARN("b8X8 is not supported, ignoring\n");
401     }
402 
403     if (bPen)
404     {
405         WARN("bPen is not supported, ignoring\n");
406     }
407 
408     /* Copy the bitmap */
409     hbmPattern = BITMAP_CopyBitmap(hbmClient);
410     if (hbmPattern == NULL)
411     {
412         ERR("Failed to copy the bitmap %p\n", hbmPattern);
413         return NULL;
414     }
415 
416     /* Call the internal function (will delete hbmPattern on failure) */
417     return CreateBrushInternal(BR_IS_BITMAP, 0, 0, hbmPattern, hbmClient);
418 }
419 
420 __kernel_entry
421 HBRUSH
422 APIENTRY
NtGdiCreateDIBBrush(_In_reads_bytes_ (cj)PVOID pv,_In_ FLONG uUsage,_In_ UINT cj,_In_ BOOL b8X8,_In_ BOOL bPen,_In_ PVOID pvClient)423 NtGdiCreateDIBBrush(
424     _In_reads_bytes_(cj) PVOID pv,
425     _In_ FLONG uUsage,
426     _In_ UINT cj,
427     _In_ BOOL b8X8,
428     _In_ BOOL bPen,
429     _In_ PVOID pvClient)
430 {
431     PVOID pvPackedDIB;
432     FLONG flAttrs;
433     HBITMAP hbm;
434     HBRUSH hbr = NULL;
435 
436     if (b8X8)
437     {
438         WARN("b8X8 is not supported, ignoring\n");
439     }
440 
441     if (bPen)
442     {
443         WARN("bPen is not supported, ignoring\n");
444     }
445 
446     if (uUsage > DIB_PAL_INDICES)
447     {
448         ERR("Invalid uUsage value: %lu\n", uUsage);
449         EngSetLastError(ERROR_INVALID_PARAMETER);
450         return NULL;
451     }
452 
453     /* Allocate a buffer for the packed DIB */
454     pvPackedDIB = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_TEMP);
455     if (pvPackedDIB == NULL)
456     {
457         ERR("Failed to allocate temp buffer of %u bytes\n", cj);
458         return NULL;
459     }
460 
461     /* Probe and copy the packed DIB */
462     _SEH2_TRY
463     {
464         ProbeForRead(pv, cj, 1);
465         RtlCopyMemory(pvPackedDIB, pv, cj);
466     }
467     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
468     {
469         ERR("Got exception, pv = %p, cj = %lu\n", pv, cj);
470         goto cleanup;
471     }
472     _SEH2_END;
473 
474     flAttrs = BR_IS_BITMAP | BR_IS_DIB;
475 
476     /* Check what kind of color table we have */
477     if (uUsage == DIB_PAL_COLORS)
478     {
479         /* Remember it and use DIB_PAL_BRUSHHACK to create a "special" palette */
480         flAttrs |= BR_IS_DIBPALCOLORS;
481         uUsage = DIB_PAL_BRUSHHACK;
482     }
483     else if (uUsage == DIB_PAL_INDICES)
484     {
485         /* No color table, bitmap contains device palette indices */
486         flAttrs |= BR_IS_DIBPALINDICES;
487 
488         /* FIXME: This makes tests pass, but needs investigation. */
489         flAttrs |= BR_IS_NULL;
490     }
491 
492     /* Create a bitmap from the DIB */
493     hbm = GreCreateDIBitmapFromPackedDIB(pvPackedDIB, cj, uUsage);
494     if (hbm == NULL)
495     {
496         ERR("Failed to create bitmap from DIB\n");
497         goto cleanup;
498     }
499 
500     /* Call the internal function (will delete hbm on failure) */
501     hbr = CreateBrushInternal(flAttrs, 0, 0, hbm, pvClient);
502 
503 cleanup:
504 
505     ExFreePoolWithTag(pvPackedDIB, GDITAG_TEMP);
506 
507     return hbr;
508 }
509 
510 __kernel_entry
511 HBITMAP
512 APIENTRY
NtGdiGetObjectBitmapHandle(_In_ HBRUSH hbr,_Out_ UINT * piUsage)513 NtGdiGetObjectBitmapHandle(
514     _In_ HBRUSH hbr,
515     _Out_ UINT *piUsage)
516 {
517     PBRUSH pbr;
518     HBITMAP hbm;
519     UINT uUsage;
520 
521     /* Lock the brush */
522     pbr = BRUSH::LockForRead(hbr);
523     if (pbr == NULL)
524     {
525         ERR("Failed to lock brush %p\n", hbr);
526         return NULL;
527     }
528 
529     /* Call the member function */
530     hbm = pbr->hbmGetBitmapHandle(&uUsage);
531 
532     /* Unlock the brush */
533     pbr->vUnlock();
534 
535     _SEH2_TRY
536     {
537         ProbeForWrite(piUsage, sizeof(*piUsage), 1);
538         *piUsage = uUsage;
539     }
540     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
541     {
542         ERR("Got exception! piUsage = %p\n", piUsage);
543         hbm = NULL;
544     }
545     _SEH2_END;
546 
547     return hbm;
548 }
549 
550 __kernel_entry
551 HBRUSH
552 APIENTRY
NtGdiSetBrushAttributes(_In_ HBRUSH hbr,_In_ DWORD dwFlags)553 NtGdiSetBrushAttributes(
554     _In_ HBRUSH hbr,
555     _In_ DWORD dwFlags)
556 {
557     PBRUSH pbr;
558     if ( dwFlags & SC_BB_STOCKOBJ )
559     {
560         if (GDIOBJ_ConvertToStockObj((HGDIOBJ*)&hbr))
561         {
562             pbr = BRUSH::LockAny(hbr);
563             if (pbr == NULL)
564             {
565                 ERR("Failed to lock brush %p\n", hbr);
566                 return NULL;
567             }
568             pbr->vReleaseAttribute();
569             pbr->vUnlock();
570             return hbr;
571         }
572     }
573     return NULL;
574 }
575 
576 __kernel_entry
577 HBRUSH
578 APIENTRY
NtGdiClearBrushAttributes(_In_ HBRUSH hbr,_In_ DWORD dwFlags)579 NtGdiClearBrushAttributes(
580     _In_ HBRUSH hbr,
581     _In_ DWORD dwFlags)
582 {
583     PBRUSH pbr;
584     if ( dwFlags & SC_BB_STOCKOBJ )
585     {
586         if (GDIOBJ_ConvertFromStockObj((HGDIOBJ*)&hbr))
587         {
588             pbr = BRUSH::LockAny(hbr);
589             if (pbr == NULL)
590             {
591                 ERR("Failed to lock brush %p\n", hbr);
592                 return NULL;
593             }
594             if (!pbr->bAllocateBrushAttr())
595             {
596                 ERR("Failed to allocate brush attribute\n");
597             }
598             pbr->vUnlock();
599             return hbr;
600         }
601     }
602     return NULL;
603 }
604 
605 } /* extern "C" */
606