579 lines
18 KiB
C
579 lines
18 KiB
C
/** @file
|
|
Setup Mouse Protocol implementation
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2019, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include "SetupMouse.h"
|
|
|
|
extern PRIVATE_MOUSE_DATA *mPrivate;
|
|
|
|
/**
|
|
SetupMouseScreenBltWorker Blt function to process keyboard display
|
|
|
|
@param [in] Private Setup mouse private data
|
|
@param [in] GopEntry Gop Entry
|
|
@param [in] BltOperation The operation to perform
|
|
@param [in] SourceX The X coordinate of the source for BltOperation
|
|
@param [in] SourceY The Y coordinate of the source for BltOperation
|
|
@param [in] DestinationX The X coordinate of the destination for BltOperation
|
|
@param [in] DestinationY The Y coordinate of the destination for BltOperation
|
|
@param [in] Width The width of a rectangle in the blt rectangle in pixels
|
|
@param [in] Height The height of a rectangle in the blt rectangle in pixels
|
|
@param [in] Delta Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
|
|
If a of 0 is used, the entire BltBuffer will be operated on.
|
|
If a subrectangle of the BltBuffer is used, then represents
|
|
the number of bytes in a row of the BltBuffer.
|
|
|
|
@return SetupMouseCursorBlt function status
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SetupMouseScreenBltWorker (
|
|
IN PRIVATE_MOUSE_DATA *Private,
|
|
IN GOP_ENTRY *GopEntry,
|
|
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
|
|
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
|
|
IN UINTN SourceX,
|
|
IN UINTN SourceY,
|
|
IN UINTN DestinationX,
|
|
IN UINTN DestinationY,
|
|
IN UINTN Width,
|
|
IN UINTN Height,
|
|
IN UINTN Delta
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ScreenFrameBuffer;
|
|
EFI_TPL OriginalTPL;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *VScreen;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *VScreenSrc;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
|
|
INTN Y;
|
|
UINT32 FillPixel;
|
|
UINT32 VerticalResolution;
|
|
UINT32 HorizontalResolution;
|
|
RECT InvalidateRc;
|
|
UINTN CopyBytes;
|
|
|
|
if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Width == 0 || Height == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// If Delta is zero, then the entire BltBuffer is being used, so Delta
|
|
// is the number of bytes in each row of BltBuffer. Since BltBuffer is Width pixels size,
|
|
// the number of bytes in each row can be computed.
|
|
//
|
|
if (Delta == 0) {
|
|
Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
}
|
|
|
|
//
|
|
// We need to fill the Virtual Screen buffer with the blt data.
|
|
// The virtual screen is upside down, as the first row is the bootom row of
|
|
// the image.
|
|
//
|
|
Gop = GopEntry->GraphicsOutput;
|
|
VerticalResolution = Gop->Mode->Info->VerticalResolution;
|
|
HorizontalResolution = Gop->Mode->Info->HorizontalResolution;
|
|
ScreenFrameBuffer = GopEntry->Screen.Image;
|
|
|
|
if (BltOperation == EfiBltVideoToBltBuffer) {
|
|
|
|
//
|
|
// Video to BltBuffer: Source is Video, destination is BltBuffer
|
|
//
|
|
if (SourceY + Height > VerticalResolution) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (SourceX + Width > HorizontalResolution) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We have to raise to TPL Notify, so we make an atomic write the frame buffer.
|
|
// We would not want a timer based event (Cursor, ...) to come in while we are
|
|
// doing this operation.
|
|
//
|
|
CopyBytes = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
|
|
OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
VScreenSrc = ScreenFrameBuffer + SourceY * HorizontalResolution + SourceX;
|
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)((UINT8 *)BltBuffer + DestinationY * Delta + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
CopyMem (Blt, VScreenSrc, CopyBytes);
|
|
VScreenSrc += HorizontalResolution;
|
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)((UINT8 *)Blt + Delta);
|
|
}
|
|
|
|
gBS->RestoreTPL (OriginalTPL);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// BltBuffer to Video: Source is BltBuffer, destination is Video
|
|
//
|
|
if (DestinationY + Height > VerticalResolution) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (DestinationX + Width > HorizontalResolution) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We have to raise to TPL Notify, so we make an atomic write the frame buffer.
|
|
// We would not want a timer based event (Cursor, ...) to come in while we are
|
|
// doing this operation.
|
|
//
|
|
CopyBytes = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
|
|
OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
|
|
|
|
switch ((UINTN) BltOperation) {
|
|
|
|
case EfiBltBufferToVideo:
|
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)((UINT8 *) BltBuffer + SourceY * Delta + SourceX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
|
VScreen = ScreenFrameBuffer + DestinationY * HorizontalResolution + DestinationX;
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
CopyMem (VScreen, Blt, CopyBytes);
|
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)((UINT8 *)Blt + Delta);
|
|
VScreen += HorizontalResolution;
|
|
}
|
|
break;
|
|
|
|
case EfiBltVideoToVideo:
|
|
if (DestinationY <= SourceY) {
|
|
|
|
VScreenSrc = ScreenFrameBuffer + SourceY * HorizontalResolution + SourceX;
|
|
VScreen = ScreenFrameBuffer + DestinationY * HorizontalResolution + DestinationX;
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
CopyMem (VScreen, VScreenSrc, CopyBytes);
|
|
VScreenSrc += HorizontalResolution;
|
|
VScreen += HorizontalResolution;
|
|
}
|
|
} else {
|
|
VScreenSrc = ScreenFrameBuffer + (SourceY + Height - 1) * HorizontalResolution + SourceX;
|
|
VScreen = ScreenFrameBuffer + (DestinationY + Height - 1) * HorizontalResolution + DestinationX;
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
CopyMem (VScreen, VScreenSrc, CopyBytes);
|
|
VScreenSrc -= HorizontalResolution;
|
|
VScreen -= HorizontalResolution;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EfiBltVideoFill:
|
|
FillPixel = *(UINT32 *)BltBuffer;
|
|
Blt = GopEntry->FillLine;
|
|
|
|
Y = Width;
|
|
while ((--Y) >= 0) {
|
|
*(UINT32 *)Blt = FillPixel;
|
|
Blt++;
|
|
}
|
|
|
|
Blt = GopEntry->FillLine;
|
|
VScreen = ScreenFrameBuffer + DestinationY * HorizontalResolution + DestinationX;
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
CopyMem (VScreen, Blt, CopyBytes);
|
|
VScreen += HorizontalResolution;
|
|
}
|
|
break;
|
|
}
|
|
|
|
SetRect (
|
|
&InvalidateRc,
|
|
(INT32)DestinationX,
|
|
(INT32)DestinationY,
|
|
(INT32)(DestinationX + Width),
|
|
(INT32)(DestinationY + Height)
|
|
);
|
|
UnionRect (&GopEntry->InvalidateRc, &GopEntry->InvalidateRc, &InvalidateRc);
|
|
Status = RenderImage (Private, GopEntry, FALSE);
|
|
|
|
gBS->RestoreTPL (OriginalTPL);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
SetupMouseCursorBlt Blt function to process keyboard display
|
|
|
|
@param [in] This Pointer to Graphics Output protocol instance
|
|
@param [in] BltBuffer The data to transfer to screen
|
|
@param [in] BltOperation The operation to perform
|
|
@param [in] SourceX The X coordinate of the source for BltOperation
|
|
@param [in] SourceY The Y coordinate of the source for BltOperation
|
|
@param [in] DestinationX The X coordinate of the destination for BltOperation
|
|
@param [in] DestinationY The Y coordinate of the destination for BltOperation
|
|
@param [in] Width The width of a rectangle in the blt rectangle in pixels
|
|
@param [in] Height The height of a rectangle in the blt rectangle in pixels
|
|
@param [in] Delta Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
|
|
If a of 0 is used, the entire BltBuffer will be operated on.
|
|
If a subrectangle of the BltBuffer is used, then represents
|
|
the number of bytes in a row of the BltBuffer.
|
|
|
|
@return SetupMouseCursorBlt function status
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetupMouseScreenBlt (
|
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
|
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
|
|
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
|
|
IN UINTN SourceX,
|
|
IN UINTN SourceY,
|
|
IN UINTN DestinationX,
|
|
IN UINTN DestinationY,
|
|
IN UINTN Width,
|
|
IN UINTN Height,
|
|
IN UINTN Delta OPTIONAL
|
|
)
|
|
{
|
|
PRIVATE_MOUSE_DATA *Private;
|
|
LIST_ENTRY *Node;
|
|
GOP_ENTRY *GopEntry;
|
|
|
|
Private = mPrivate;
|
|
GopEntry = NULL;
|
|
|
|
Node = GetFirstNode (&Private->GopList);
|
|
while (!IsNull (&Private->GopList, Node)) {
|
|
GopEntry = GOP_ENTRY_FROM_THIS (Node);
|
|
if (GopEntry->GraphicsOutput == This) {
|
|
break;
|
|
}
|
|
Node = GetNextNode (&Private->GopList, Node);
|
|
}
|
|
|
|
ASSERT (!IsNull (&Private->GopList, Node));
|
|
if (IsNull (&Private->GopList, Node) || GopEntry == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (!Private->IsStart) {
|
|
return GopEntry->OriginalBlt (
|
|
GopEntry->GraphicsOutput,
|
|
BltBuffer,
|
|
BltOperation,
|
|
SourceX,
|
|
SourceY,
|
|
DestinationX,
|
|
DestinationY,
|
|
Width,
|
|
Height,
|
|
Delta
|
|
);
|
|
} else {
|
|
return SetupMouseScreenBltWorker (
|
|
Private,
|
|
GopEntry,
|
|
BltBuffer,
|
|
BltOperation,
|
|
SourceX,
|
|
SourceY,
|
|
DestinationX,
|
|
DestinationY,
|
|
Width,
|
|
Height,
|
|
Delta
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Copy image to blend image
|
|
|
|
@param [in] Private Setup mouse private data
|
|
@param [in] GopEntry Gop Entry
|
|
@param [in] Rc Sync rectangle
|
|
@param [out] ImageIsSame Screen Image is same as GOP
|
|
|
|
@retval N/A
|
|
|
|
**/
|
|
VOID
|
|
SyncScreenImage (
|
|
IN PRIVATE_MOUSE_DATA *Private,
|
|
IN GOP_ENTRY *GopEntry,
|
|
IN RECT *Rc,
|
|
OUT BOOLEAN *ImageIsSame
|
|
)
|
|
{
|
|
INTN X;
|
|
INTN Y;
|
|
INT32 Width;
|
|
INT32 Height;
|
|
UINT32 *BlendBlt;
|
|
UINT32 *CheckBlt;
|
|
UINT32 *ScreenBlt;
|
|
UINTN HorizontalResolution;
|
|
BOOLEAN IsSame;
|
|
UINT32 *BlendBuffer;
|
|
UINT32 *CheckBuffer;
|
|
UINT32 *ScreenBuffer;
|
|
UINTN BufferOffset;
|
|
|
|
|
|
ASSERT (ImageIsSame != NULL);
|
|
|
|
IsSame = TRUE;
|
|
HorizontalResolution = GopEntry->GraphicsOutput->Mode->Info->HorizontalResolution;
|
|
Width = Rc->right - Rc->left;
|
|
Height = Rc->bottom - Rc->top;
|
|
|
|
//
|
|
// read image from gop
|
|
//
|
|
BufferOffset = Rc->left * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + Rc->top * GopEntry->BytesPerScanLine;
|
|
GopEntry->OriginalBlt (
|
|
GopEntry->GraphicsOutput,
|
|
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)((UINT8 *)GopEntry->CheckBuffer + BufferOffset),
|
|
EfiBltVideoToBltBuffer,
|
|
Rc->left,
|
|
Rc->top,
|
|
0,
|
|
0,
|
|
Width,
|
|
Height,
|
|
GopEntry->BytesPerScanLine
|
|
);
|
|
|
|
*ImageIsSame = TRUE;
|
|
BlendBuffer = (UINT32 *) GopEntry->BlendBuffer + Rc->top * HorizontalResolution + Rc->left;
|
|
CheckBuffer = (UINT32 *) GopEntry->CheckBuffer + Rc->top * HorizontalResolution + Rc->left;
|
|
ScreenBuffer = (UINT32 *) GopEntry->Screen.Image + Rc->top * HorizontalResolution + Rc->left;
|
|
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
BlendBlt = BlendBuffer;
|
|
CheckBlt = CheckBuffer;
|
|
ScreenBlt = ScreenBuffer;
|
|
|
|
X = Width;
|
|
while ((--X) >= 0) {
|
|
if (*BlendBlt != *CheckBlt) {
|
|
*ScreenBlt = *CheckBlt;
|
|
IsSame = FALSE;
|
|
}
|
|
BlendBlt++;
|
|
CheckBlt++;
|
|
ScreenBlt++;
|
|
}
|
|
BlendBuffer += HorizontalResolution;
|
|
CheckBuffer += HorizontalResolution;
|
|
ScreenBuffer += HorizontalResolution;
|
|
}
|
|
|
|
*ImageIsSame = IsSame;
|
|
}
|
|
|
|
|
|
/**
|
|
Copy image to blend image
|
|
|
|
@param [in] Private Setup mouse private data
|
|
@param [in] GopEntry Gop Entry
|
|
@param [in] ImageInfo Source image information
|
|
@param [in] UpdateRc Need update region
|
|
@param [in] Transparent Whether have transparent color
|
|
|
|
@retval N/A
|
|
|
|
**/
|
|
VOID
|
|
UpdateImage (
|
|
IN PRIVATE_MOUSE_DATA *Private,
|
|
IN GOP_ENTRY *GopEntry,
|
|
IN IMAGE_INFO *ImageInfo,
|
|
IN RECT *UpdateRc,
|
|
IN BOOLEAN Transparent
|
|
)
|
|
{
|
|
INTN X;
|
|
INTN Y;
|
|
INT32 Width;
|
|
INT32 Height;
|
|
INT32 ImageWidth;
|
|
INT32 ImageHeight;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ImageBlt;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BlendBlt;
|
|
RECT *ImageRc;
|
|
UINTN HorizontalResolution;
|
|
UINTN CopyBytes;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ImageBuffer;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BlendBuffer;
|
|
|
|
|
|
|
|
Width = UpdateRc->right - UpdateRc->left;
|
|
Height = UpdateRc->bottom - UpdateRc->top;
|
|
ImageWidth = ImageInfo->ImageRc.right - ImageInfo->ImageRc.left;
|
|
ImageHeight = ImageInfo->ImageRc.bottom - ImageInfo->ImageRc.top;
|
|
|
|
ImageRc = &ImageInfo->ImageRc;
|
|
HorizontalResolution = GopEntry->GraphicsOutput->Mode->Info->HorizontalResolution;
|
|
|
|
ASSERT (Width <= ImageWidth);
|
|
ASSERT (Height <= ImageHeight);
|
|
|
|
ImageBuffer = ImageInfo->Image + (UpdateRc->top - ImageRc->top) * ImageWidth + (UpdateRc->left - ImageRc->left);
|
|
BlendBuffer = GopEntry->BlendBuffer + UpdateRc->top * HorizontalResolution + UpdateRc->left;
|
|
if (!Transparent) {
|
|
|
|
CopyBytes = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
CopyMem (BlendBuffer, ImageBuffer, CopyBytes);
|
|
ImageBuffer += ImageWidth;
|
|
BlendBuffer += HorizontalResolution;
|
|
}
|
|
} else {
|
|
|
|
ImageBuffer = ImageInfo->Image + (UpdateRc->top - ImageRc->top) * ImageWidth + (UpdateRc->left - ImageRc->left);
|
|
BlendBuffer = GopEntry->BlendBuffer + UpdateRc->top * HorizontalResolution + UpdateRc->left;
|
|
|
|
Y = Height;
|
|
while ((--Y) >= 0) {
|
|
ImageBlt = ImageBuffer;
|
|
BlendBlt = BlendBuffer;
|
|
|
|
X = Width;
|
|
while ((--X) >= 0) {
|
|
BlendPixel (BlendBlt, ImageBlt);
|
|
*(UINT32 *)BlendBlt = (0x80000000 | (*(UINT32 *)BlendBlt & 0x00FFFFFF));
|
|
ImageBlt++;
|
|
BlendBlt++;
|
|
|
|
}
|
|
ImageBuffer += ImageWidth;
|
|
BlendBuffer += HorizontalResolution;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Render Image to GOP
|
|
|
|
@param [in] Private Setup mouse private data
|
|
@param [in] GopEntry Gop Entry
|
|
@param [in] NeedSyncScreen Whether need sync screen image
|
|
|
|
@return EFI_STATUS return original BLT function status
|
|
|
|
**/
|
|
EFI_STATUS
|
|
RenderImage (
|
|
IN PRIVATE_MOUSE_DATA *Private,
|
|
IN GOP_ENTRY *GopEntry,
|
|
IN BOOLEAN NeedSyncScreen
|
|
)
|
|
{
|
|
|
|
RECT UpdateRc;
|
|
RECT InvalidateRc;
|
|
BOOLEAN ImageIsSame;
|
|
EFI_STATUS Status;
|
|
UINTN BufferOffset;
|
|
|
|
|
|
IntersectRect (&InvalidateRc, &GopEntry->InvalidateRc, &GopEntry->Screen.ImageRc);
|
|
if (IsRectEmpty (&InvalidateRc)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (NeedSyncScreen && Private->NeedSyncFrameBuffer) {
|
|
SyncScreenImage (Private, GopEntry, &InvalidateRc, &ImageIsSame);
|
|
}
|
|
|
|
if (IntersectRect (&UpdateRc, &GopEntry->Screen.ImageRc, &InvalidateRc)) {
|
|
UpdateImage (Private, GopEntry, &GopEntry->Screen, &UpdateRc, FALSE);
|
|
}
|
|
|
|
if (Private->Keyboard.Visible && IntersectRect (&UpdateRc, &Private->Keyboard.ImageRc, &InvalidateRc)) {
|
|
UpdateImage (Private, GopEntry, &Private->Keyboard, &UpdateRc, TRUE);
|
|
}
|
|
|
|
if (Private->Cursor.Visible && IntersectRect (&UpdateRc, &Private->Cursor.ImageRc, &InvalidateRc)) {
|
|
UpdateImage (Private, GopEntry, &Private->Cursor, &UpdateRc, TRUE);
|
|
}
|
|
|
|
BufferOffset = InvalidateRc.left + InvalidateRc.top * GopEntry->GraphicsOutput->Mode->Info->HorizontalResolution;
|
|
Status = GopEntry->OriginalBlt (
|
|
GopEntry->GraphicsOutput,
|
|
GopEntry->BlendBuffer + BufferOffset,
|
|
EfiBltBufferToVideo,
|
|
0,
|
|
0,
|
|
InvalidateRc.left,
|
|
InvalidateRc.top,
|
|
InvalidateRc.right - InvalidateRc.left,
|
|
InvalidateRc.bottom - InvalidateRc.top,
|
|
sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * GopEntry->GraphicsOutput->Mode->Info->HorizontalResolution
|
|
);
|
|
|
|
SetRectEmpty (&GopEntry->InvalidateRc);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Render Image for All GOPs
|
|
|
|
@param [in] Private Setup mouse private data
|
|
|
|
@retval N/A
|
|
|
|
**/
|
|
VOID
|
|
RenderImageForAllGop (
|
|
IN PRIVATE_MOUSE_DATA *Private
|
|
)
|
|
|
|
{
|
|
GOP_ENTRY *GopEntry;
|
|
LIST_ENTRY *Node;
|
|
|
|
ASSERT_LOCKED (&Private->SetupMouseLock);
|
|
|
|
Node = GetFirstNode (&Private->GopList);
|
|
while (!IsNull (&Private->GopList, Node)) {
|
|
GopEntry = GOP_ENTRY_FROM_THIS (Node);
|
|
RenderImage (Private, GopEntry, TRUE);
|
|
Node = GetNextNode (&Private->GopList, Node);
|
|
}
|
|
}
|
|
|