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