[WIN32K]
[reactos.git] / reactos / win32ss / gdi / ntgdi / brush.cpp
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" */