xref: /reactos/win32ss/gdi/eng/surface.c (revision 9d3c3a75)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS kernel
4  * PURPOSE:           GDI Driver Surace Functions
5  * FILE:              win32ss/gdi/eng/surface.c
6  * PROGRAMERS:        Jason Filby
7  *                    Timo Kreuzer
8  * TESTING TO BE DONE:
9  * - Create a GDI bitmap with all formats, perform all drawing operations on them, render to VGA surface
10  *   refer to \test\microwin\src\engine\devdraw.c for info on correct pixel plotting for various formats
11  */
12 
13 #include <win32k.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 LONG giUniqueSurface = 0;
19 
20 UCHAR
21 gajBitsPerFormat[11] =
22 {
23     0, /*  0: unused */
24     1, /*  1: BMF_1BPP */
25     4, /*  2: BMF_4BPP */
26     8, /*  3: BMF_8BPP */
27    16, /*  4: BMF_16BPP */
28    24, /*  5: BMF_24BPP */
29    32, /*  6: BMF_32BPP */
30     4, /*  7: BMF_4RLE */
31     8, /*  8: BMF_8RLE */
32     0, /*  9: BMF_JPEG */
33     0, /* 10: BMF_PNG */
34 };
35 
36 
37 ULONG
38 FASTCALL
39 BitmapFormat(ULONG cBits, ULONG iCompression)
40 {
41     switch (iCompression)
42     {
43         case BI_RGB:
44             /* Fall through */
45         case BI_BITFIELDS:
46             if (cBits <= 1) return BMF_1BPP;
47             if (cBits <= 4) return BMF_4BPP;
48             if (cBits <= 8) return BMF_8BPP;
49             if (cBits <= 16) return BMF_16BPP;
50             if (cBits <= 24) return BMF_24BPP;
51             if (cBits <= 32) return BMF_32BPP;
52             return 0;
53 
54         case BI_RLE4:
55             return BMF_4RLE;
56 
57         case BI_RLE8:
58             return BMF_8RLE;
59 
60         default:
61             return 0;
62     }
63 }
64 
65 VOID
66 NTAPI
67 SURFACE_vCleanup(PVOID ObjectBody)
68 {
69     PSURFACE psurf = (PSURFACE)ObjectBody;
70     PVOID pvBits = psurf->SurfObj.pvBits;
71 
72     /* Check if the surface has bits */
73     if (pvBits)
74     {
75         /* Only bitmaps can have bits */
76         ASSERT(psurf->SurfObj.iType == STYPE_BITMAP);
77 
78         /* Check if it is a DIB section */
79         if (psurf->hDIBSection)
80         {
81             /* Unmap the section view */
82             EngUnmapSectionView(pvBits, psurf->dwOffset, psurf->hSecure);
83         }
84         else if (psurf->SurfObj.fjBitmap & BMF_USERMEM)
85         {
86             /* Bitmap was allocated from usermode memory */
87             EngFreeUserMem(pvBits);
88         }
89         else if (psurf->SurfObj.fjBitmap & BMF_KMSECTION)
90         {
91             /* Bitmap was allocated from a kernel section */
92             if (!EngFreeSectionMem(NULL, pvBits))
93             {
94                 DPRINT1("EngFreeSectionMem failed for %p!\n", pvBits);
95                 // Should we BugCheck here?
96                 ASSERT(FALSE);
97             }
98         }
99         else if (psurf->SurfObj.fjBitmap & BMF_POOLALLOC)
100         {
101             /* Free a pool allocation */
102             EngFreeMem(pvBits);
103         }
104     }
105 
106     /* Free palette */
107     if(psurf->ppal)
108     {
109         PALETTE_ShareUnlockPalette(psurf->ppal);
110     }
111 }
112 
113 
114 PSURFACE
115 NTAPI
116 SURFACE_AllocSurface(
117     _In_ USHORT iType,
118     _In_ ULONG cx,
119     _In_ ULONG cy,
120     _In_ ULONG iFormat,
121     _In_ ULONG fjBitmap,
122     _In_opt_ ULONG cjWidth,
123     _In_opt_ ULONG cjBufSize,
124     _In_opt_ PVOID pvBits)
125 {
126     ULONG cBitsPixel, cjBits, cjObject;
127     PSURFACE psurf;
128     SURFOBJ *pso;
129     PVOID pvSection;
130 
131     NT_ASSERT(!pvBits || (iType == STYPE_BITMAP));
132     NT_ASSERT((iFormat <= BMF_32BPP) || (cjBufSize != 0));
133     NT_ASSERT((LONG)cy > 0);
134 
135     /* Verify format */
136     if ((iFormat < BMF_1BPP) || (iFormat > BMF_PNG))
137     {
138         DPRINT1("Invalid bitmap format: %lu\n", iFormat);
139         return NULL;
140     }
141 
142     /* Get bits per pixel from the format */
143     cBitsPixel = gajBitsPerFormat[iFormat];
144 
145     /* Are bits and a width in bytes given? */
146     if (pvBits && cjWidth)
147     {
148         /* Align the width (Windows compatibility, drivers expect that) */
149         cjWidth = WIDTH_BYTES_ALIGN32((cjWidth << 3) / cBitsPixel, cBitsPixel);
150     }
151     else
152     {
153         /* Calculate width from the bitmap width in pixels */
154         cjWidth = WIDTH_BYTES_ALIGN32(cx, cBitsPixel);
155     }
156 
157     /* Is this an uncompressed format? */
158     if (iFormat <= BMF_32BPP)
159     {
160         /* Calculate the correct bitmap size in bytes */
161         if (!NT_SUCCESS(RtlULongMult(cjWidth, cy, &cjBits)))
162         {
163             DPRINT1("Overflow calculating size: cjWidth %lu, cy %lu\n",
164                     cjWidth, cy);
165             return NULL;
166         }
167 
168         /* Did we get a buffer and size? */
169         if ((pvBits != NULL) && (cjBufSize != 0))
170         {
171             /* Make sure the buffer is large enough */
172             if (cjBufSize < cjBits)
173             {
174                 DPRINT1("Buffer is too small, required: %lu, got %lu\n",
175                         cjBits, cjBufSize);
176                 return NULL;
177             }
178         }
179     }
180     else
181     {
182         /* Compressed format, use the provided size */
183         NT_ASSERT(cjBufSize != 0);
184         cjBits = cjBufSize;
185     }
186 
187     /* Check if we need an extra large object */
188     if ((iType == STYPE_BITMAP) && (pvBits == NULL) &&
189         !(fjBitmap & BMF_USERMEM) && !(fjBitmap & BMF_KMSECTION))
190     {
191         /* Allocate an object large enough to hold the bits */
192         cjObject = sizeof(SURFACE) + cjBits;
193     }
194     else
195     {
196         /* Otherwise just allocate the SURFACE structure */
197         cjObject = sizeof(SURFACE);
198     }
199 
200     /* Check for arithmetic overflow */
201     if (cjObject < sizeof(SURFACE))
202     {
203         /* Fail! */
204         DPRINT1("Overflow calculating cjObject: cjBits %lu\n", cjBits);
205         return NULL;
206     }
207 
208     /* Allocate a SURFACE object */
209     psurf = (PSURFACE)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_BITMAP, cjObject);
210     if (!psurf)
211     {
212         return NULL;
213     }
214 
215     /* Initialize the basic fields */
216     pso = &psurf->SurfObj;
217     pso->hsurf = psurf->BaseObject.hHmgr;
218     pso->sizlBitmap.cx = cx;
219     pso->sizlBitmap.cy = cy;
220     pso->iBitmapFormat = iFormat;
221     pso->iType = iType;
222     pso->fjBitmap = (USHORT)fjBitmap;
223     pso->iUniq = InterlockedIncrement(&giUniqueSurface);
224     pso->cjBits = cjBits;
225 
226     /* Check if we need a bitmap buffer */
227     if (iType == STYPE_BITMAP)
228     {
229         /* Check if we got one or if we need to allocate one */
230         if (pvBits != NULL)
231         {
232             /* Use the caller provided buffer */
233             pso->pvBits = pvBits;
234         }
235         else if (fjBitmap & BMF_USERMEM)
236         {
237             /* User mode memory was requested */
238             pso->pvBits = EngAllocUserMem(cjBits, 0);
239 
240             /* Check for failure */
241             if (!pso->pvBits)
242             {
243                 GDIOBJ_vDeleteObject(&psurf->BaseObject);
244                 return NULL;
245             }
246         }
247         else if (fjBitmap & BMF_KMSECTION)
248         {
249             /* Use a kernel mode section */
250             pso->pvBits = EngAllocSectionMem(&pvSection,
251                                              (fjBitmap & BMF_NOZEROINIT) ?
252                                                  0 : FL_ZERO_MEMORY,
253                                              cjBits, TAG_DIB);
254 
255             /* Check for failure */
256             if (!pso->pvBits)
257             {
258                 GDIOBJ_vDeleteObject(&psurf->BaseObject);
259                 return NULL;
260             }
261 
262             /* Free the section already, but keep the mapping */
263             EngFreeSectionMem(pvSection, NULL);
264         }
265         else
266         {
267             /* Buffer is after the object */
268             pso->pvBits = psurf + 1;
269 
270             /* Zero the buffer, except requested otherwise */
271             if (!(fjBitmap & BMF_NOZEROINIT))
272             {
273                 RtlZeroMemory(pso->pvBits, cjBits);
274             }
275         }
276 
277         /* Set pvScan0 and lDelta */
278         if (fjBitmap & BMF_TOPDOWN)
279         {
280             /* Topdown is the normal way */
281             pso->pvScan0 = pso->pvBits;
282             pso->lDelta = cjWidth;
283         }
284         else
285         {
286             /* Inversed bitmap (bottom up) */
287             pso->pvScan0 = ((PCHAR)pso->pvBits + pso->cjBits - cjWidth);
288             pso->lDelta = -(LONG)cjWidth;
289         }
290     }
291     else
292     {
293         /* There are no bitmap bits */
294         pso->pvScan0 = pso->pvBits = NULL;
295         pso->lDelta = 0;
296     }
297 
298     /* Assign a default palette and increment its reference count */
299     SURFACE_vSetPalette(psurf, appalSurfaceDefault[iFormat]);
300 
301     return psurf;
302 }
303 
304 HBITMAP
305 APIENTRY
306 EngCreateBitmap(
307     _In_ SIZEL sizl,
308     _In_ LONG lWidth,
309     _In_ ULONG iFormat,
310     _In_ ULONG fl,
311     _In_opt_ PVOID pvBits)
312 {
313     PSURFACE psurf;
314     HBITMAP hbmp;
315 
316     /* Allocate a surface */
317     psurf = SURFACE_AllocSurface(STYPE_BITMAP,
318                                  sizl.cx,
319                                  sizl.cy,
320                                  iFormat,
321                                  fl,
322                                  lWidth,
323                                  0,
324                                  pvBits);
325     if (!psurf)
326     {
327         DPRINT1("SURFACE_AllocSurface failed. (STYPE_BITMAP, sizl.cx=%ld, sizl.cy=%ld, iFormat=%lu, fl=%lu, lWidth=%ld, pvBits=0x%p)\n",
328                 sizl.cx, sizl.cy, iFormat, fl, lWidth, pvBits);
329         return NULL;
330     }
331 
332     /* Get the handle for the bitmap */
333     hbmp = (HBITMAP)psurf->SurfObj.hsurf;
334 
335     /* Mark as API bitmap */
336     psurf->flags = API_BITMAP;
337 
338     /* Set public ownership */
339     GDIOBJ_vSetObjectOwner(&psurf->BaseObject, GDI_OBJ_HMGR_PUBLIC);
340 
341     /* Unlock the surface and return */
342     SURFACE_UnlockSurface(psurf);
343     return hbmp;
344 }
345 
346 /*
347  * @implemented
348  */
349 HBITMAP
350 APIENTRY
351 EngCreateDeviceBitmap(
352     _In_ DHSURF dhsurf,
353     _In_ SIZEL sizl,
354     _In_ ULONG iFormat)
355 {
356     PSURFACE psurf;
357     HBITMAP hbmp;
358 
359     /* Allocate a surface */
360     psurf = SURFACE_AllocSurface(STYPE_DEVBITMAP,
361                                  sizl.cx,
362                                  sizl.cy,
363                                  iFormat,
364                                  0,
365                                  0,
366                                  0,
367                                  NULL);
368     if (!psurf)
369     {
370         DPRINT1("SURFACE_AllocSurface failed. (STYPE_DEVBITMAP, sizl.cx=%ld, sizl.cy=%ld, iFormat=%lu)\n",
371                 sizl.cx, sizl.cy, iFormat);
372         return NULL;
373     }
374 
375     /* Set the device handle */
376     psurf->SurfObj.dhsurf = dhsurf;
377 
378     /* Set public ownership */
379     GDIOBJ_vSetObjectOwner(&psurf->BaseObject, GDI_OBJ_HMGR_PUBLIC);
380 
381     /* Get the handle for the bitmap */
382     hbmp = (HBITMAP)psurf->SurfObj.hsurf;
383 
384     /* Unlock the surface and return */
385     SURFACE_UnlockSurface(psurf);
386     return hbmp;
387 }
388 
389 HSURF
390 APIENTRY
391 EngCreateDeviceSurface(
392     _In_ DHSURF dhsurf,
393     _In_ SIZEL sizl,
394     _In_ ULONG iFormat)
395 {
396     PSURFACE psurf;
397     HSURF hsurf;
398 
399     /* Allocate a surface */
400     psurf = SURFACE_AllocSurface(STYPE_DEVICE,
401                                  sizl.cx,
402                                  sizl.cy,
403                                  iFormat,
404                                  0,
405                                  0,
406                                  0,
407                                  NULL);
408     if (!psurf)
409     {
410         DPRINT1("SURFACE_AllocSurface failed. (STYPE_DEVICE, sizl.cx=%ld, sizl.cy=%ld, iFormat=%lu)\n",
411                 sizl.cx, sizl.cy, iFormat);
412         return NULL;
413     }
414 
415     /* Set the device handle */
416     psurf->SurfObj.dhsurf = dhsurf;
417 
418     /* Set public ownership */
419     GDIOBJ_vSetObjectOwner(&psurf->BaseObject, GDI_OBJ_HMGR_PUBLIC);
420 
421     /* Get the handle for the surface */
422     hsurf = psurf->SurfObj.hsurf;
423 
424     /* Unlock the surface and return */
425     SURFACE_UnlockSurface(psurf);
426     return hsurf;
427 }
428 
429 BOOL
430 APIENTRY
431 EngAssociateSurface(
432     _In_ HSURF hsurf,
433     _In_ HDEV hdev,
434     _In_ FLONG flHooks)
435 {
436     SURFOBJ *pso;
437     PSURFACE psurf;
438     PDEVOBJ* ppdev;
439     PPALETTE ppal;
440 
441     ppdev = (PDEVOBJ*)hdev;
442 
443     /* Lock the surface */
444     psurf = SURFACE_ShareLockSurface(hsurf);
445     if (!psurf)
446     {
447         return FALSE;
448     }
449     pso = &psurf->SurfObj;
450 
451     /* Associate the hdev */
452     pso->hdev = hdev;
453     pso->dhpdev = ppdev->dhpdev;
454 
455     /* Hook up specified functions */
456     psurf->flags &= ~HOOK_FLAGS;
457     psurf->flags |= (flHooks & HOOK_FLAGS);
458 
459     /* Assign the PDEV's palette */
460     ppal = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
461     SURFACE_vSetPalette(psurf, ppal);
462     PALETTE_ShareUnlockPalette(ppal);
463 
464     SURFACE_ShareUnlockSurface(psurf);
465 
466     return TRUE;
467 }
468 
469 BOOL
470 APIENTRY
471 EngModifySurface(
472     _In_ HSURF hsurf,
473     _In_ HDEV hdev,
474     _In_ FLONG flHooks,
475     _In_ FLONG flSurface,
476     _In_ DHSURF dhsurf,
477     _In_ PVOID pvScan0,
478     _In_ LONG lDelta,
479     _Reserved_ PVOID pvReserved)
480 {
481     SURFOBJ *pso;
482     PSURFACE psurf;
483     PDEVOBJ* ppdev;
484     PPALETTE ppal;
485 
486     /* Lock the surface */
487     psurf = SURFACE_ShareLockSurface(hsurf);
488     if (psurf == NULL)
489     {
490         DPRINT1("Failed to reference surface %p\n", hsurf);
491         return FALSE;
492     }
493 
494     ppdev = (PDEVOBJ*)hdev;
495     pso = &psurf->SurfObj;
496     pso->dhsurf = dhsurf;
497 
498     /* Associate the hdev */
499     pso->hdev = hdev;
500     pso->dhpdev = ppdev->dhpdev;
501 
502     /* Hook up specified functions */
503     psurf->flags &= ~HOOK_FLAGS;
504     psurf->flags |= (flHooks & HOOK_FLAGS);
505 
506     /* Assign the PDEV's palette */
507     ppal = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
508     SURFACE_vSetPalette(psurf, ppal);
509     PALETTE_ShareUnlockPalette(ppal);
510 
511     /* Update surface flags */
512     if (flSurface & MS_NOTSYSTEMMEMORY)
513          pso->fjBitmap |= BMF_NOTSYSMEM;
514     else
515         pso->fjBitmap &= ~BMF_NOTSYSMEM;
516     if (flSurface & MS_SHAREDACCESS)
517          psurf->flags |= SHAREACCESS_SURFACE;
518     else
519         psurf->flags &= ~SHAREACCESS_SURFACE;
520 
521     /* Check if the caller passed bitmap bits */
522     if ((pvScan0 != NULL) && (lDelta != 0))
523     {
524         /* Update the fields */
525         pso->pvScan0 = pvScan0;
526         pso->lDelta = lDelta;
527 
528         /* This is a bitmap now! */
529         pso->iType = STYPE_BITMAP;
530 
531         /* Check memory layout */
532         if (lDelta > 0)
533         {
534             /* Topdown is the normal way */
535             pso->cjBits = lDelta * pso->sizlBitmap.cy;
536             pso->pvBits = pso->pvScan0;
537             pso->fjBitmap |= BMF_TOPDOWN;
538         }
539         else
540         {
541             /* Inversed bitmap (bottom up) */
542             pso->cjBits = (-lDelta) * pso->sizlBitmap.cy;
543             pso->pvBits = (PCHAR)pso->pvScan0 - pso->cjBits - lDelta;
544             pso->fjBitmap &= ~BMF_TOPDOWN;
545         }
546     }
547     else
548     {
549         /* Set bits to NULL */
550         pso->pvBits = NULL;
551         pso->pvScan0 = NULL;
552         pso->lDelta = 0;
553 
554         /* Set appropriate surface type */
555         if (pso->iType != STYPE_DEVICE)
556             pso->iType = STYPE_DEVBITMAP;
557     }
558 
559     SURFACE_ShareUnlockSurface(psurf);
560 
561     return TRUE;
562 }
563 
564 
565 BOOL
566 APIENTRY
567 EngDeleteSurface(
568     _In_ _Post_ptr_invalid_ HSURF hsurf)
569 {
570     PSURFACE psurf;
571 
572     psurf = SURFACE_ShareLockSurface(hsurf);
573     if (!psurf)
574     {
575         DPRINT1("Could not reference surface %p to delete\n", hsurf);
576         return FALSE;
577     }
578 
579     GDIOBJ_vDeleteObject(&psurf->BaseObject);
580     return TRUE;
581 }
582 
583 BOOL
584 APIENTRY
585 EngEraseSurface(
586     _In_ SURFOBJ *pso,
587     _In_ RECTL *prcl,
588     _In_ ULONG iColor)
589 {
590     ASSERT(pso);
591     ASSERT(prcl);
592     return FillSolid(pso, prcl, iColor);
593 }
594 
595 /*
596  * @implemented
597  */
598 SURFOBJ * APIENTRY
599 NtGdiEngLockSurface(IN HSURF hsurf)
600 {
601     return EngLockSurface(hsurf);
602 }
603 
604 
605 SURFOBJ *
606 APIENTRY
607 EngLockSurface(
608     _In_ HSURF hsurf)
609 {
610     SURFACE *psurf = SURFACE_ShareLockSurface(hsurf);
611 
612     return psurf ? &psurf->SurfObj : NULL;
613 }
614 
615 __kernel_entry
616 NTSTATUS
617 APIENTRY
618 NtGdiEngUnlockSurface(
619     _In_ SURFOBJ *pso)
620 {
621     UNIMPLEMENTED;
622     ASSERT(FALSE);
623     return STATUS_NOT_IMPLEMENTED;
624 }
625 
626 VOID
627 APIENTRY
628 EngUnlockSurface(
629     _In_ _Post_ptr_invalid_ SURFOBJ *pso)
630 {
631     if (pso != NULL)
632     {
633         SURFACE *psurf = CONTAINING_RECORD(pso, SURFACE, SurfObj);
634         SURFACE_ShareUnlockSurface(psurf);
635     }
636 }
637 
638 /* EOF */
639