8068cad5de1deb1788be3bb52c221185f0560213
[reactos.git] / subsystems / win32 / win32k / eng / surface.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: GDI Driver Surace Functions
5 * FILE: subsys/win32k/eng/surface.c
6 * PROGRAMER: Jason Filby
7 * REVISION HISTORY:
8 * 3/7/1999: Created
9 * 9/11/2000: Updated to handle real pixel packed bitmaps (UPDATE TO DATE COMPLETED)
10 * TESTING TO BE DONE:
11 * - Create a GDI bitmap with all formats, perform all drawing operations on them, render to VGA surface
12 * refer to \test\microwin\src\engine\devdraw.c for info on correct pixel plotting for various formats
13 */
14
15 #include <win32k.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 enum Rle_EscapeCodes
21 {
22 RLE_EOL = 0, /* End of line */
23 RLE_END = 1, /* End of bitmap */
24 RLE_DELTA = 2 /* Delta */
25 };
26
27 INT FASTCALL BitsPerFormat(ULONG Format)
28 {
29 switch (Format)
30 {
31 case BMF_1BPP:
32 return 1;
33
34 case BMF_4BPP:
35 /* Fall through */
36 case BMF_4RLE:
37 return 4;
38
39 case BMF_8BPP:
40 /* Fall through */
41 case BMF_8RLE:
42 return 8;
43
44 case BMF_16BPP:
45 return 16;
46
47 case BMF_24BPP:
48 return 24;
49
50 case BMF_32BPP:
51 return 32;
52
53 default:
54 return 0;
55 }
56 }
57
58 ULONG FASTCALL BitmapFormat(WORD Bits, DWORD Compression)
59 {
60 switch (Compression)
61 {
62 case BI_RGB:
63 /* Fall through */
64 case BI_BITFIELDS:
65 switch (Bits)
66 {
67 case 1:
68 return BMF_1BPP;
69 case 4:
70 return BMF_4BPP;
71 case 8:
72 return BMF_8BPP;
73 case 16:
74 return BMF_16BPP;
75 case 24:
76 return BMF_24BPP;
77 case 32:
78 return BMF_32BPP;
79 }
80 return 0;
81
82 case BI_RLE4:
83 return BMF_4RLE;
84
85 case BI_RLE8:
86 return BMF_8RLE;
87
88 default:
89 return 0;
90 }
91 }
92
93 BOOL INTERNAL_CALL
94 SURFACE_Cleanup(PVOID ObjectBody)
95 {
96 PSURFACE psurf = (PSURFACE)ObjectBody;
97 PVOID pvBits = psurf->SurfObj.pvBits;
98
99 /* If this is an API bitmap, free the bits */
100 if (pvBits != NULL &&
101 (psurf->flFlags & BITMAPOBJ_IS_APIBITMAP))
102 {
103 /* Check if we have a DIB section */
104 if (psurf->hSecure)
105 {
106 // FIXME: IMPLEMENT ME!
107 // MmUnsecureVirtualMemory(psurf->hSecure);
108 if (psurf->hDIBSection)
109 {
110 /* DIB was created from a section */
111 NTSTATUS Status;
112
113 pvBits = (PVOID)((ULONG_PTR)pvBits - psurf->dwOffset);
114 Status = ZwUnmapViewOfSection(NtCurrentProcess(), pvBits);
115 if (!NT_SUCCESS(Status))
116 {
117 DPRINT1("Could not unmap section view!\n");
118 // Should we BugCheck here?
119 }
120 }
121 else
122 {
123 /* DIB was allocated */
124 EngFreeUserMem(pvBits);
125 }
126 }
127 else
128 {
129 // FIXME: use TAG
130 ExFreePool(psurf->SurfObj.pvBits);
131 }
132
133 }
134
135 /* Free palette */
136 if(psurf->ppal)
137 {
138 PALETTE_ShareUnlockPalette(psurf->ppal);
139 }
140
141 return TRUE;
142 }
143
144 /*
145 * @implemented
146 */
147 HBITMAP APIENTRY
148 EngCreateDeviceBitmap(IN DHSURF dhsurf,
149 IN SIZEL Size,
150 IN ULONG Format)
151 {
152 HBITMAP NewBitmap;
153 SURFOBJ *pso;
154
155 NewBitmap = EngCreateBitmap(Size, DIB_GetDIBWidthBytes(Size.cx, BitsPerFormat(Format)), Format, 0, NULL);
156 if (!NewBitmap)
157 {
158 DPRINT1("EngCreateBitmap failed\n");
159 return 0;
160 }
161
162 pso = EngLockSurface((HSURF)NewBitmap);
163 if (!pso)
164 {
165 DPRINT1("EngLockSurface failed on newly created bitmap!\n");
166 GreDeleteObject(NewBitmap);
167 return NULL;
168 }
169
170 pso->dhsurf = dhsurf;
171 EngUnlockSurface(pso);
172
173 return NewBitmap;
174 }
175
176 VOID Decompress4bpp(SIZEL Size, BYTE *CompressedBits, BYTE *UncompressedBits, LONG Delta)
177 {
178 int x = 0;
179 int y = Size.cy - 1;
180 int c;
181 int length;
182 int width = ((Size.cx+1)/2);
183 int height = Size.cy - 1;
184 BYTE *begin = CompressedBits;
185 BYTE *bits = CompressedBits;
186 BYTE *temp;
187 while (y >= 0)
188 {
189 length = *bits++ / 2;
190 if (length)
191 {
192 c = *bits++;
193 while (length--)
194 {
195 if (x >= width) break;
196 temp = UncompressedBits + (((height - y) * Delta) + x);
197 x++;
198 *temp = c;
199 }
200 }
201 else
202 {
203 length = *bits++;
204 switch (length)
205 {
206 case RLE_EOL:
207 x = 0;
208 y--;
209 break;
210 case RLE_END:
211 return;
212 case RLE_DELTA:
213 x += (*bits++)/2;
214 y -= (*bits++)/2;
215 break;
216 default:
217 length /= 2;
218 while (length--)
219 {
220 c = *bits++;
221 if (x < width)
222 {
223 temp = UncompressedBits + (((height - y) * Delta) + x);
224 x++;
225 *temp = c;
226 }
227 }
228 if ((bits - begin) & 1)
229 {
230 bits++;
231 }
232 }
233 }
234 }
235 }
236
237 VOID Decompress8bpp(SIZEL Size, BYTE *CompressedBits, BYTE *UncompressedBits, LONG Delta)
238 {
239 int x = 0;
240 int y = Size.cy - 1;
241 int c;
242 int length;
243 int width = Size.cx;
244 int height = Size.cy - 1;
245 BYTE *begin = CompressedBits;
246 BYTE *bits = CompressedBits;
247 BYTE *temp;
248 while (y >= 0)
249 {
250 length = *bits++;
251 if (length)
252 {
253 c = *bits++;
254 while (length--)
255 {
256 if (x >= width) break;
257 temp = UncompressedBits + (((height - y) * Delta) + x);
258 x++;
259 *temp = c;
260 }
261 }
262 else
263 {
264 length = *bits++;
265 switch (length)
266 {
267 case RLE_EOL:
268 x = 0;
269 y--;
270 break;
271 case RLE_END:
272 return;
273 case RLE_DELTA:
274 x += *bits++;
275 y -= *bits++;
276 break;
277 default:
278 while (length--)
279 {
280 c = *bits++;
281 if (x < width)
282 {
283 temp = UncompressedBits + (((height - y) * Delta) + x);
284 x++;
285 *temp = c;
286 }
287 }
288 if ((bits - begin) & 1)
289 {
290 bits++;
291 }
292 }
293 }
294 }
295 }
296
297 HBITMAP FASTCALL
298 IntCreateBitmap(IN SIZEL Size,
299 IN LONG Width,
300 IN ULONG Format,
301 IN ULONG Flags,
302 IN PVOID Bits)
303 {
304 HBITMAP hbmp;
305 SURFOBJ *pso;
306 PSURFACE psurf;
307 PVOID UncompressedBits;
308 ULONG UncompressedFormat;
309
310 if (Format == 0)
311 return 0;
312
313 psurf = SURFACE_AllocSurfaceWithHandle();
314 if (psurf == NULL)
315 {
316 return 0;
317 }
318 hbmp = psurf->BaseObject.hHmgr;
319
320 pso = &psurf->SurfObj;
321
322 if (Format == BMF_4RLE)
323 {
324 pso->lDelta = DIB_GetDIBWidthBytes(Size.cx, BitsPerFormat(BMF_4BPP));
325 pso->cjBits = pso->lDelta * Size.cy;
326 UncompressedFormat = BMF_4BPP;
327 UncompressedBits = EngAllocMem(FL_ZERO_MEMORY, pso->cjBits, TAG_DIB);
328 Decompress4bpp(Size, (BYTE *)Bits, (BYTE *)UncompressedBits, pso->lDelta);
329 }
330 else if (Format == BMF_8RLE)
331 {
332 pso->lDelta = DIB_GetDIBWidthBytes(Size.cx, BitsPerFormat(BMF_8BPP));
333 pso->cjBits = pso->lDelta * Size.cy;
334 UncompressedFormat = BMF_8BPP;
335 UncompressedBits = EngAllocMem(FL_ZERO_MEMORY, pso->cjBits, TAG_DIB);
336 Decompress8bpp(Size, (BYTE *)Bits, (BYTE *)UncompressedBits, pso->lDelta);
337 }
338 else
339 {
340 pso->lDelta = abs(Width);
341 pso->cjBits = pso->lDelta * Size.cy;
342 UncompressedBits = Bits;
343 UncompressedFormat = Format;
344 }
345
346 if (UncompressedBits != NULL)
347 {
348 pso->pvBits = UncompressedBits;
349 }
350 else
351 {
352 if (pso->cjBits == 0)
353 {
354 pso->pvBits = NULL;
355 }
356 else
357 {
358 if (0 != (Flags & BMF_USERMEM))
359 {
360 pso->pvBits = EngAllocUserMem(pso->cjBits, 0);
361 }
362 else
363 {
364 pso->pvBits = EngAllocMem(0 != (Flags & BMF_NOZEROINIT) ?
365 0 : FL_ZERO_MEMORY,
366 pso->cjBits, TAG_DIB);
367 }
368 if (pso->pvBits == NULL)
369 {
370 SURFACE_UnlockSurface(psurf);
371 SURFACE_FreeSurfaceByHandle(hbmp);
372 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
373 return 0;
374 }
375 }
376 }
377
378 if (0 == (Flags & BMF_TOPDOWN))
379 {
380 pso->pvScan0 = (PVOID)((ULONG_PTR)pso->pvBits + pso->cjBits - pso->lDelta);
381 pso->lDelta = - pso->lDelta;
382 }
383 else
384 {
385 pso->pvScan0 = pso->pvBits;
386 }
387
388 pso->dhsurf = 0; /* device managed surface */
389 pso->hsurf = (HSURF)hbmp;
390 pso->dhpdev = NULL;
391 pso->hdev = NULL;
392 pso->sizlBitmap = Size;
393 pso->iBitmapFormat = UncompressedFormat;
394 pso->iType = STYPE_BITMAP;
395 pso->fjBitmap = Flags & (BMF_TOPDOWN | BMF_NOZEROINIT);
396 pso->iUniq = 0;
397
398 psurf->flHooks = 0;
399 psurf->flFlags = 0;
400 psurf->dimension.cx = 0;
401 psurf->dimension.cy = 0;
402
403 psurf->hSecure = NULL;
404 psurf->hDIBSection = NULL;
405
406 SURFACE_UnlockSurface(psurf);
407
408 return hbmp;
409 }
410
411 /* Name gleaned from C++ symbol information for SURFMEM::bInitDIB */
412 typedef struct _DEVBITMAPINFO
413 {
414 ULONG Format;
415 ULONG Width;
416 ULONG Height;
417 ULONG Flags;
418 ULONG Size;
419 } DEVBITMAPINFO, *PDEVBITMAPINFO;
420
421 SURFOBJ*
422 FASTCALL
423 SURFMEM_bCreateDib(IN PDEVBITMAPINFO BitmapInfo,
424 IN PVOID Bits)
425 {
426 BOOLEAN Compressed = FALSE;
427 ULONG ScanLine = 0; // Compiler is dumb
428 ULONG Size;
429 SURFOBJ *pso;
430 PSURFACE psurf;
431 SIZEL LocalSize;
432 BOOLEAN AllocatedLocally = FALSE;
433
434 /*
435 * First, check the format so we can get the aligned scanline width.
436 * RLE and the newer fancy-smanshy JPG/PNG support do NOT have scanlines
437 * since they are compressed surfaces!
438 */
439 switch (BitmapInfo->Format)
440 {
441 case BMF_1BPP:
442 ScanLine = ((BitmapInfo->Width + 31) & ~31) >> 3;
443 break;
444
445 case BMF_4BPP:
446 ScanLine = ((BitmapInfo->Width + 7) & ~7) >> 1;
447 break;
448
449 case BMF_8BPP:
450 ScanLine = (BitmapInfo->Width + 3) & ~3;
451 break;
452
453 case BMF_16BPP:
454 ScanLine = ((BitmapInfo->Width + 1) & ~1) << 1;
455 break;
456
457 case BMF_24BPP:
458 ScanLine = ((BitmapInfo->Width * 3) + 3) & ~3;
459 break;
460
461 case BMF_32BPP:
462 ScanLine = BitmapInfo->Width << 2;
463 break;
464
465 case BMF_8RLE:
466 case BMF_4RLE:
467 case BMF_JPEG:
468 case BMF_PNG:
469 Compressed = TRUE;
470 break;
471
472 default:
473 DPRINT1("Invalid bitmap format\n");
474 return NULL;
475 }
476
477 /* Does the device manage its own surface? */
478 if (!Bits)
479 {
480 /* We need to allocate bits for the caller, figure out the size */
481 if (Compressed)
482 {
483 /* Note: we should not be seeing this scenario from ENGDDI */
484 ASSERT(FALSE);
485 Size = BitmapInfo->Size;
486 }
487 else
488 {
489 /* The height times the bytes for each scanline */
490 Size = BitmapInfo->Height * ScanLine;
491 }
492
493 if (Size)
494 {
495 /* Check for allocation flag */
496 if (BitmapInfo->Flags & BMF_USERMEM)
497 {
498 /* Get the bits from user-mode memory */
499 Bits = EngAllocUserMem(Size, 'mbuG');
500 }
501 else
502 {
503 /* Get kernel bits (zeroed out if requested) */
504 Bits = EngAllocMem((BitmapInfo->Flags & BMF_NOZEROINIT) ? 0 : FL_ZERO_MEMORY,
505 Size,
506 TAG_DIB);
507 }
508 AllocatedLocally = TRUE;
509 /* Bail out if that failed */
510 if (!Bits) return NULL;
511 }
512 }
513 else
514 {
515 /* Should not have asked for user memory */
516 ASSERT((BitmapInfo->Flags & BMF_USERMEM) == 0);
517 }
518
519 /* Allocate the actual surface object structure */
520 psurf = SURFACE_AllocSurfaceWithHandle();
521 if (!psurf)
522 {
523 if(Bits && AllocatedLocally)
524 {
525 if(BitmapInfo->Flags & BMF_USERMEM)
526 EngFreeUserMem(Bits);
527 else
528 EngFreeMem(Bits);
529 }
530 return NULL;
531 }
532
533 /* We should now have our surface object */
534 pso = &psurf->SurfObj;
535
536 /* Save format and flags */
537 pso->iBitmapFormat = BitmapInfo->Format;
538 pso->fjBitmap = BitmapInfo->Flags & (BMF_TOPDOWN | BMF_UMPDMEM | BMF_USERMEM);
539
540 /* Save size and type */
541 LocalSize.cy = BitmapInfo->Height;
542 LocalSize.cx = BitmapInfo->Width;
543 pso->sizlBitmap = LocalSize;
544 pso->iType = STYPE_BITMAP;
545
546 /* Device-managed surface, no flags or dimension */
547 pso->dhsurf = 0;
548 pso->dhpdev = NULL;
549 pso->hdev = NULL;
550 psurf->flFlags = 0;
551 psurf->dimension.cx = 0;
552 psurf->dimension.cy = 0;
553 psurf->hSecure = NULL;
554 psurf->hDIBSection = NULL;
555 psurf->flHooks = 0;
556
557 /* Set bits */
558 pso->pvBits = Bits;
559
560 /* Check for bitmap type */
561 if (!Compressed)
562 {
563 /* Number of bits is based on the height times the scanline */
564 pso->cjBits = BitmapInfo->Height * ScanLine;
565 if (BitmapInfo->Flags & BMF_TOPDOWN)
566 {
567 /* For topdown, the base address starts with the bits */
568 pso->pvScan0 = pso->pvBits;
569 pso->lDelta = ScanLine;
570 }
571 else
572 {
573 /* Otherwise we start with the end and go up */
574 pso->pvScan0 = (PVOID)((ULONG_PTR)pso->pvBits + pso->cjBits - ScanLine);
575 pso->lDelta = -ScanLine;
576 }
577 }
578 else
579 {
580 /* Compressed surfaces don't have scanlines! */
581 pso->lDelta = 0;
582 pso->cjBits = BitmapInfo->Size;
583
584 /* Check for JPG or PNG */
585 if ((BitmapInfo->Format != BMF_JPEG) && (BitmapInfo->Format != BMF_PNG))
586 {
587 /* Wherever the bit data is */
588 pso->pvScan0 = pso->pvBits;
589 }
590 else
591 {
592 /* Fancy formats don't use a base address */
593 pso->pvScan0 = NULL;
594 ASSERT(FALSE); // ENGDDI shouldn't be creating PNGs for drivers ;-)
595 }
596 }
597
598 /* Finally set the handle and uniq */
599 pso->hsurf = (HSURF)psurf->BaseObject.hHmgr;
600 pso->iUniq = 0;
601
602 /* Unlock and return the surface */
603 SURFACE_UnlockSurface(psurf);
604 return pso;
605 }
606
607 /*
608 * @implemented
609 */
610 HBITMAP
611 APIENTRY
612 EngCreateBitmap(IN SIZEL Size,
613 IN LONG Width,
614 IN ULONG Format,
615 IN ULONG Flags,
616 IN PVOID Bits)
617 {
618 SURFOBJ* Surface;
619 DEVBITMAPINFO BitmapInfo;
620
621 /* Capture the parameters */
622 BitmapInfo.Format = Format;
623 BitmapInfo.Width = Size.cx;
624 BitmapInfo.Height = Size.cy;
625 BitmapInfo.Flags = Flags;
626
627 /*
628 * If the display driver supports framebuffer access, use the scanline width
629 * to determine the actual width of the bitmap, and convert it to pels instead
630 * of bytes.
631 */
632 if ((Bits) && (Width))
633 {
634 switch (BitmapInfo.Format)
635 {
636 /* Do the conversion for each bit depth we support */
637 case BMF_1BPP:
638 BitmapInfo.Width = Width * 8;
639 break;
640 case BMF_4BPP:
641 BitmapInfo.Width = Width * 2;
642 break;
643 case BMF_8BPP:
644 BitmapInfo.Width = Width;
645 break;
646 case BMF_16BPP:
647 BitmapInfo.Width = Width / 2;
648 break;
649 case BMF_24BPP:
650 BitmapInfo.Width = Width / 3;
651 break;
652 case BMF_32BPP:
653 BitmapInfo.Width = Width / 4;
654 break;
655 }
656 }
657
658 /* Now create the surface */
659 Surface = SURFMEM_bCreateDib(&BitmapInfo, Bits);
660 if (!Surface) return 0;
661
662 /* Set public ownership and reutrn the handle */
663 GDIOBJ_SetOwnership(Surface->hsurf, NULL);
664 return Surface->hsurf;
665 }
666
667 /*
668 * @unimplemented
669 */
670 HSURF APIENTRY
671 EngCreateDeviceSurface(IN DHSURF dhsurf,
672 IN SIZEL Size,
673 IN ULONG Format)
674 {
675 HSURF hsurf;
676 SURFOBJ *pso;
677 PSURFACE psurf;
678
679 psurf = SURFACE_AllocSurfaceWithHandle();
680 if (!psurf)
681 {
682 return 0;
683 }
684
685 hsurf = psurf->BaseObject.hHmgr;
686 GDIOBJ_SetOwnership(hsurf, NULL);
687
688 pso = &psurf->SurfObj;
689
690 pso->dhsurf = dhsurf;
691 pso->hsurf = hsurf;
692 pso->sizlBitmap = Size;
693 pso->iBitmapFormat = Format;
694 pso->lDelta = DIB_GetDIBWidthBytes(Size.cx, BitsPerFormat(Format));
695 pso->iType = STYPE_DEVICE;
696 pso->iUniq = 0;
697
698 psurf->flHooks = 0;
699
700 SURFACE_UnlockSurface(psurf);
701
702 return hsurf;
703 }
704
705 /*
706 * @implemented
707 */
708 BOOL
709 APIENTRY
710 EngAssociateSurface(
711 IN HSURF hsurf,
712 IN HDEV hdev,
713 IN FLONG flHooks)
714 {
715 SURFOBJ *pso;
716 PSURFACE psurf;
717 PDEVOBJ* ppdev;
718
719 ppdev = (PDEVOBJ*)hdev;
720
721 /* Lock the surface */
722 psurf = SURFACE_LockSurface(hsurf);
723 if (!psurf)
724 {
725 return FALSE;
726 }
727 pso = &psurf->SurfObj;
728
729 /* Associate the hdev */
730 pso->hdev = hdev;
731 pso->dhpdev = ppdev->dhpdev;
732
733 /* Hook up specified functions */
734 psurf->flHooks = flHooks;
735
736 /* Get palette */
737 psurf->ppal = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
738
739 SURFACE_UnlockSurface(psurf);
740
741 return TRUE;
742 }
743
744 /*
745 * @implemented
746 */
747 BOOL APIENTRY
748 EngModifySurface(
749 IN HSURF hsurf,
750 IN HDEV hdev,
751 IN FLONG flHooks,
752 IN FLONG flSurface,
753 IN DHSURF dhsurf,
754 OUT VOID *pvScan0,
755 IN LONG lDelta,
756 IN VOID *pvReserved)
757 {
758 SURFOBJ *pso;
759 PSURFACE psurf;
760 PDEVOBJ* ppdev;
761
762 psurf = SURFACE_LockSurface(hsurf);
763 if (psurf == NULL)
764 {
765 return FALSE;
766 }
767
768 ppdev = (PDEVOBJ*)hdev;
769 pso = &psurf->SurfObj;
770 pso->dhsurf = dhsurf;
771 pso->lDelta = lDelta;
772 pso->pvScan0 = pvScan0;
773
774 /* Associate the hdev */
775 pso->hdev = hdev;
776 pso->dhpdev = ppdev->dhpdev;
777
778 /* Hook up specified functions */
779 psurf->flHooks = flHooks;
780
781 /* Get palette */
782 psurf->ppal = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
783
784 SURFACE_UnlockSurface(psurf);
785
786 return TRUE;
787 }
788
789 /*
790 * @implemented
791 */
792 BOOL APIENTRY
793 EngDeleteSurface(IN HSURF hsurf)
794 {
795 GDIOBJ_SetOwnership(hsurf, PsGetCurrentProcess());
796 SURFACE_FreeSurfaceByHandle(hsurf);
797 return TRUE;
798 }
799
800 /*
801 * @implemented
802 */
803 BOOL APIENTRY
804 EngEraseSurface(SURFOBJ *pso,
805 RECTL *Rect,
806 ULONG iColor)
807 {
808 ASSERT(pso);
809 ASSERT(Rect);
810 return FillSolid(pso, Rect, iColor);
811 }
812
813 /*
814 * @implemented
815 */
816 SURFOBJ * APIENTRY
817 NtGdiEngLockSurface(IN HSURF hsurf)
818 {
819 return EngLockSurface(hsurf);
820 }
821
822
823 /*
824 * @implemented
825 */
826 SURFOBJ * APIENTRY
827 EngLockSurface(IN HSURF hsurf)
828 {
829 SURFACE *psurf = GDIOBJ_ShareLockObj(hsurf, GDI_OBJECT_TYPE_BITMAP);
830
831 if (psurf != NULL)
832 return &psurf->SurfObj;
833
834 return NULL;
835 }
836
837
838 /*
839 * @implemented
840 */
841 VOID APIENTRY
842 NtGdiEngUnlockSurface(IN SURFOBJ *pso)
843 {
844 EngUnlockSurface(pso);
845 }
846
847 /*
848 * @implemented
849 */
850 VOID APIENTRY
851 EngUnlockSurface(IN SURFOBJ *pso)
852 {
853 if (pso != NULL)
854 {
855 SURFACE *psurf = CONTAINING_RECORD(pso, SURFACE, SurfObj);
856 GDIOBJ_ShareUnlockObjByPtr((POBJ)psurf);
857 }
858 }
859
860
861 /* EOF */