gps/GPSResources/tcpmp/common/overlay.c

649 lines
17 KiB
C
Executable File

/*****************************************************************************
*
* This program is free software ; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: overlay.c 551 2006-01-09 11:55:09Z picard $
*
* The Core Pocket Media Player
* Copyright (c) 2004-2005 Gabor Kovacs
*
****************************************************************************/
#include "common.h"
int OverlayDefaultBlit(overlay* p, const constplanes Data, const constplanes DataLast)
{
planes Planes;
int Result = p->Lock(p,Planes,1);
if (Result==ERR_NONE)
{
BlitImage(p->Soft,Planes,Data,DataLast,p->Output.Format.Video.Pitch,-1);
p->Unlock(p);
}
return Result;
}
int OverlayEnum(overlay* p, int* No, datadef* Param)
{
return VOutEnum(p,No,Param);
}
static int Create(overlay* p)
{
p->Node.Enum = (nodeenum)OverlayEnum;
p->Node.Get = (nodeget)OverlayGet;
p->Node.Set = (nodeset)OverlaySet;
p->Blit = (ovlblit)OverlayDefaultBlit;
p->Update = (ovlfunc)OverlayUpdateAlign;
p->Caps = -1;
p->Soft = NULL;
p->Primary = 1;
p->Overlay = 0;
p->DoPowerOff = 0;
memset(&p->Backup,0,sizeof(idctbackup));
memset(&p->OrigFX,0,sizeof(blitfx));
p->OrigFX.ScaleX = SCALE_ONE;
p->OrigFX.ScaleY = SCALE_ONE;
p->AutoPrerotate = 0;
p->ColorKey = RGB_NULL;
p->Aspect.Num = 0;
p->Aspect.Den = 1;
p->Output.Type = PACKET_VIDEO;
return ERR_NONE;
}
static void Delete(overlay* p)
{
BlitRelease(p->Soft);
ReleaseModule(&p->Module);
}
void OverlayClearBorder(overlay* p)
{
planes Planes;
p->Dirty = 1;
if (p->Lock && p->Lock(p,Planes,0) == ERR_NONE)
{
uint32_t c = RGBToFormat(CRGB(0,0,0),&p->Output.Format.Video.Pixel);
rect Viewport;
VirtToPhy(&p->Viewport,&Viewport,&p->Output.Format.Video);
FillColor(Planes[0],p->Output.Format.Video.Pitch,Viewport.x,Viewport.y,
Viewport.Width,p->DstAlignedRect.y-Viewport.y,
p->Output.Format.Video.Pixel.BitCount,c);
FillColor(Planes[0],p->Output.Format.Video.Pitch,Viewport.x,p->DstAlignedRect.y,
p->DstAlignedRect.x-Viewport.x,p->DstAlignedRect.Height,
p->Output.Format.Video.Pixel.BitCount,c);
FillColor(Planes[0],p->Output.Format.Video.Pitch,p->DstAlignedRect.x+p->DstAlignedRect.Width,p->DstAlignedRect.y,
(Viewport.x+Viewport.Width)-(p->DstAlignedRect.x+p->DstAlignedRect.Width),p->DstAlignedRect.Height,
p->Output.Format.Video.Pixel.BitCount,c);
FillColor(Planes[0],p->Output.Format.Video.Pitch,Viewport.x,p->DstAlignedRect.y+p->DstAlignedRect.Height,
Viewport.Width,(Viewport.y+Viewport.Height)-(p->DstAlignedRect.y+p->DstAlignedRect.Height),
p->Output.Format.Video.Pixel.BitCount,c);
p->Unlock(p);
}
}
int OverlayUpdateAlign(overlay* p)
{
rect OldGUI = p->GUIAlignedRect;
rect Old = p->DstAlignedRect;
DEBUG_MSG4(DEBUG_TEST,T("BLIT Viewport:%d %d %d %d"),p->Viewport.x,p->Viewport.y,p->Viewport.Width,p->Viewport.Height);
VirtToPhy(&p->Viewport,&p->DstAlignedRect,&p->Output.Format.Video);
VirtToPhy(NULL,&p->SrcAlignedRect,&p->Input.Format.Video);
BlitRelease(p->Soft);
p->Soft = BlitCreate(&p->Output.Format.Video,&p->Input.Format.Video,&p->FX,&p->Caps);
BlitAlign(p->Soft,&p->DstAlignedRect, &p->SrcAlignedRect);
PhyToVirt(&p->DstAlignedRect,&p->GUIAlignedRect,&p->Output.Format.Video);
//DEBUG_MSG4(DEBUG_TEST,T("BLIT DstRect:%d %d %d %d"),p->DstAlignedRect.x,p->DstAlignedRect.y,p->DstAlignedRect.Width,p->DstAlignedRect.Height);
//DEBUG_MSG4(DEBUG_TEST,T("BLIT SrcRect:%d %d %d %d"),p->SrcAlignedRect.x,p->SrcAlignedRect.y,p->SrcAlignedRect.Width,p->SrcAlignedRect.Height);
if (!EqRect(&Old,&p->DstAlignedRect) && p->Show && p->Primary)
{
WinInvalidate(&OldGUI,0);
WinInvalidate(&p->Viewport,1);
WinValidate(&p->GUIAlignedRect);
}
if (p->Show && (p->FullScreenViewport || !p->Primary))
OverlayClearBorder(p);
return ERR_NONE;
}
static int Blit(overlay* p,const packet* Packet,const flowstate* State)
{
int Result = ERR_NONE;
if (Packet)
{
if (State->DropLevel)
{
++p->Dropped;
return ERR_NONE;
}
if (State->CurrTime >= 0)
{
if (!p->Play && State->CurrTime == p->LastTime)
return ERR_BUFFER_FULL;
if (Packet->RefTime >= (State->CurrTime + SHOWAHEAD))
return ERR_BUFFER_FULL;
p->LastTime = State->CurrTime;
}
else
p->LastTime = Packet->RefTime;
p->CurrTime = State->CurrTime;
if (State->CurrTime != TIME_RESEND)
++p->Total;
if (p->Soft && p->Inited && p->Show && Packet->Data[0])
{
const constplanes* LastData = &Packet->LastData;
if (p->Dirty)
LastData = NULL;
/* { static tick_t Last = 0;
DebugMessage("%d %d",Packet->RefTime,Packet->RefTime-Last);
Last = Packet->RefTime; } */
Result = p->Blit(p,Packet->Data,*LastData);
if (Result == ERR_NONE)
p->Dirty = 0;
}
}
else
if (State->DropLevel)
++p->Dropped;
return Result;
}
int OverlayGet(overlay* p,int No,void* Data,int Size)
{
int Result = ERR_INVALID_PARAM;
switch (No)
{
case OUT_INPUT: GETVALUE(p->Pin,pin); break;
case OUT_OUTPUT|PIN_FORMAT: GETVALUE(p->Output,packetformat); break;
case OUT_INPUT|PIN_FORMAT: GETVALUE(p->Input,packetformat); break;
case OUT_INPUT|PIN_PROCESS: GETVALUE((packetprocess)Blit,packetprocess); break;
case OUT_TOTAL: GETVALUE(p->Total,int); break;
case OUT_DROPPED: GETVALUE(p->Dropped,int); break;
case VOUT_PRIMARY: GETVALUE(p->Primary,bool_t); break;
case VOUT_OVERLAY: GETVALUE(p->Overlay,bool_t); break;
case VOUT_IDCT: GETVALUE(p->AccelIDCT,idct*); break;
case VOUT_VISIBLE: GETVALUE(p->Visible,bool_t); break;
case VOUT_CLIPPING: GETVALUE(p->Clipping,bool_t); break;
case VOUT_FX: GETVALUE(p->OrigFX,blitfx); break;
case VOUT_VIEWPORT: GETVALUE(p->Viewport,rect); break;
case VOUT_FULLSCREEN: GETVALUE(p->FullScreenViewport,bool_t); break;
case VOUT_OUTPUTRECT: GETVALUECOND(p->GUIAlignedRect,rect,!p->Disabled); break;
case VOUT_AUTOPREROTATE: GETVALUE(p->AutoPrerotate,bool_t); break;
case VOUT_UPDATING: GETVALUE(p->Updating,bool_t); break;
case VOUT_PLAY: GETVALUE(p->Play,bool_t); break;
case VOUT_CAPS: GETVALUE(p->Caps,int); break;
case VOUT_COLORKEY: GETVALUECOND(p->ColorKey,rgbval_t,!p->Disabled && p->ColorKey!=RGB_NULL); break;
case VOUT_ASPECT: GETVALUE(p->Aspect,fraction); break;
case FLOW_BACKGROUND: GETVALUECOND(p->Background,bool_t,p->DoPowerOff); break;
}
return Result;
}
static NOINLINE void UpdateInputFormat(overlay* p)
{
if (p->Inited && !p->UpdateInputFormat && p->PrefDirection != p->Input.Format.Video.Direction && p->Pin.Node)
{
packetformat Format = p->Input;
if ((p->PrefDirection ^ Format.Format.Video.Direction) & DIR_SWAPXY)
SwapInt(&Format.Format.Video.Width,&Format.Format.Video.Height);
Format.Format.Video.Direction = p->PrefDirection;
p->UpdateInputFormat = 1;
p->Pin.Node->Set(p->Pin.Node,p->Pin.No|PIN_FORMAT,&Format,sizeof(Format));
p->UpdateInputFormat = 0;
}
}
int OverlayUpdateFX(overlay* p, bool_t ForceUpdate)
{
fraction Aspect = p->Aspect;
blitfx LastFX = p->FX;
int Width,Height,ScaleX,ScaleY;
if (!p->Inited || p->Updating) return ERR_NONE;
if (Aspect.Num==0) // source
{
Aspect.Num = p->Input.Format.Video.Aspect;
Aspect.Den = ASPECT_ONE;
if (Aspect.Num == ASPECT_ONE || !Aspect.Num)
Aspect.Num = DefaultAspect(p->Input.Format.Video.Width,p->Input.Format.Video.Height);
}
else
if (Aspect.Num<0)
{
// screen aspect ratio -> pixel aspect ratio
if (p->InputDirection & DIR_SWAPXY)
{
Aspect.Num *= -p->Input.Format.Video.Width;
Aspect.Den *= p->Input.Format.Video.Height;
}
else
{
Aspect.Num *= -p->Input.Format.Video.Height;
Aspect.Den *= p->Input.Format.Video.Width;
}
}
p->FX = p->OrigFX;
p->FX.Flags |= p->SetFX;
p->FX.Flags &= ~p->ClearFX;
p->FX.Direction = CombineDir(p->InputDirection,p->OrigFX.Direction,p->Output.Format.Video.Direction);
p->PrefDirection = CombineDir(p->FX.Direction,0,p->Input.Format.Video.Direction);
if (p->OrigFX.ScaleX<=0)
{
int v = p->OrigFX.ScaleX;
Width = p->Viewport.Width;
Height = p->Viewport.Height;
if ((p->InputDirection ^ p->OrigFX.Direction) & DIR_SWAPXY)
SwapInt(&Width,&Height);
if (p->InputDirection & DIR_SWAPXY)
{
ScaleX = Scale(Width,SCALE_ONE,p->Input.Format.Video.Width);
ScaleY = Scale(Height,SCALE_ONE,Scale(p->Input.Format.Video.Height,Aspect.Num,Aspect.Den));
}
else
{
ScaleX = Scale(Width,SCALE_ONE,Scale(p->Input.Format.Video.Width,Aspect.Num,Aspect.Den));
ScaleY = Scale(Height,SCALE_ONE,p->Input.Format.Video.Height);
}
if ((p->InputDirection & DIR_SWAPXY) && (v == -1 || v == -2))
v = -3-v; // swap 'fit width' and 'fit height'
if (v==-3)
{
//todo: fill screen, but always using the fullscreen aspect ratio!
p->FX.ScaleY = ScaleY;
p->FX.ScaleX = ScaleX;
}
else
{
if ((v == -2) || (v!=-1 && v!=-4 && ScaleX>ScaleY) || (v==-4 && ScaleX<ScaleY))
ScaleX = ScaleY;
if (v<-4)
ScaleX = Scale(ScaleX,-v,SCALE_ONE);
p->FX.ScaleY = ScaleX;
p->FX.ScaleX = Scale(ScaleX,Aspect.Num,Aspect.Den);
if (p->InputDirection & DIR_SWAPXY)
SwapInt(&p->FX.ScaleX,&p->FX.ScaleY);
}
}
else
{
// prefer source horizontal scaling (because of smooth scale option)
if (p->InputDirection & DIR_SWAPXY)
p->FX.ScaleY = Scale(p->FX.ScaleY,Aspect.Num,Aspect.Den);
else
p->FX.ScaleX = Scale(p->FX.ScaleX,Aspect.Num,Aspect.Den);
}
if (p->Output.Format.Video.Pixel.Flags & PF_PIXELDOUBLE)
{
p->FX.ScaleX >>= 1;
p->FX.ScaleY >>= 1;
}
if ((p->FX.Flags & BLITFX_ONLYDIFF) && p->FX.ScaleX < (SCALE_ONE*2)/3)
p->FX.Flags &= ~BLITFX_ONLYDIFF;
if (p->ForceUpdate)
{
p->ForceUpdate = 0;
ForceUpdate = 1;
}
if (ForceUpdate || !EqBlitFX(&p->FX,&LastFX))
{
p->Update(p);
p->Dirty = 1;
p->LastTime = -1;
UpdateInputFormat(p);
}
return ERR_NONE;
}
static int UpdateInputDirection(overlay* p,bool_t Update)
{
if (p->Inited && !p->Updating)
{
p->InputDirection = p->Input.Format.Video.Direction;
p->PreRotate = 0;
if (p->AutoPrerotate && !(p->Input.Format.Video.Pixel.Flags & PF_NOPREROTATE))
{
rect r;
PhyToVirt(NULL,&r,&p->Input.Format.Video);
// portrait?
if (r.Width < r.Height)
{
p->PreRotate = 1;
if (p->InputDirection & DIR_SWAPXY)
p->InputDirection ^= DIR_SWAPXY | DIR_MIRRORLEFTRIGHT;
else
p->InputDirection ^= DIR_SWAPXY | DIR_MIRRORUPDOWN;
}
}
if (Update)
OverlayUpdateFX(p,0);
}
return ERR_NONE;
}
int OverlayUpdateShow(overlay* p,bool_t Temp)
{
bool_t Show = !p->Disabled && p->Visible && (p->ColorKey != RGB_NULL || !p->Clipping || !p->Primary);
if (!p->Updating && p->Show != Show)
{
p->Show = Show;
p->Dirty = 1;
p->LastTime = -1;
if (p->Inited && !Temp)
{
if (p->Primary && p->Overlay && p->ColorKey != RGB_NULL && !p->Show)
{
// clear colorkey before turning off overlay
WinInvalidate(&p->Viewport,1);
WinUpdate();
}
if (p->UpdateShow)
p->UpdateShow(p);
if (p->Primary)
{
if (p->Overlay)
{
if (p->ColorKey != RGB_NULL && p->Show)
WinInvalidate(&p->Viewport,1);
}
else
if (p->Show)
{
WinInvalidate(&p->Viewport,1); // redraw border (zoom may have changed)
WinValidate(&p->GUIAlignedRect);
}
else
{
WinInvalidate(&p->Viewport,0); // redraw other windows
WinValidate(&p->Viewport); // own window is fine
}
}
}
}
return ERR_NONE;
}
static int Updating(overlay* p)
{
if (!p->Updating)
{
UpdateInputDirection(p,0);
OverlayUpdateFX(p,0);
OverlayUpdateShow(p,0);
}
return ERR_NONE;
}
static int UpdateInput(overlay* p)
{
if (p->Inited)
{
if (p->Show)
{
bool_t Old = p->Visible;
p->Visible = 0;
p->Show = 0;
if (p->UpdateShow)
p->UpdateShow(p); // maybe calls OverlayUpdateShow
p->Visible = Old;
}
p->Done(p);
BlitRelease(p->Soft);
p->Soft = NULL;
p->Inited = 0;
memset(&p->GUIAlignedRect,0,sizeof(rect));
if (p->Primary)
WinInvalidate(&p->Viewport,1);
}
if (!p->TurningOff)
IDCTRestore(NULL,&p->Backup);
memset(&p->FX,0,sizeof(blitfx));
memset(&p->DstAlignedRect,0,sizeof(rect));
memset(&p->SrcAlignedRect,0,sizeof(rect));
memset(&p->GUIAlignedRect,0,sizeof(rect));
p->Total = 0;
p->Dropped = 0;
p->Disabled = 0;
p->Updating = 0;
p->Show = 0;
p->Dirty = 1;
p->LastTime = -1;
p->ColorKey = RGB_NULL;
if (p->Input.Type == PACKET_VIDEO)
{
if (Compressed(&p->Input.Format.Video.Pixel))
return ERR_INVALID_DATA;
if (p->Input.Format.Video.Width<=0)
{
p->Disabled = 1;
p->Input.Format.Video.Width = 2;
}
if (p->Input.Format.Video.Height<=0)
{
p->Disabled = 1;
p->Input.Format.Video.Height = 2;
}
if (!p->Background)
{
int Result = p->Init(p);
if (Result != ERR_NONE)
{
if (Result == ERR_DEVICE_ERROR)
ShowError(p->Node.Class,ERR_ID,ERR_DEVICE_ERROR);
return Result;
}
p->Inited = 1;
p->ForceUpdate = 1;
UpdateInputDirection(p,0);
OverlayUpdateFX(p,1);
OverlayUpdateShow(p,0);
if (p->Primary && !p->TurningOff)
WinInvalidate(&p->Viewport,1);
}
}
else
if (p->Input.Type != PACKET_NONE)
return ERR_INVALID_DATA;
return ERR_NONE;
}
static int UpdateBackground(overlay* p)
{
if (p->DoPowerOff)
{
p->TurningOff = 1;
if (p->Background)
{
IDCTRestore(NULL,&p->Backup); // free old backup
if (!p->AccelIDCT || p->AccelIDCT->Get(p->AccelIDCT,IDCT_BACKUP,&p->Backup,sizeof(idctbackup))!=ERR_NONE)
UpdateInput(p);
}
else
{
if (p->AccelIDCT && p->Backup.Format.Pixel.Flags)
{
p->AccelIDCT->Set(p->AccelIDCT,IDCT_BACKUP,&p->Backup,sizeof(idctbackup));
p->AccelIDCT->Set(p->AccelIDCT,FLOW_RESEND,NULL,0);
}
else
{
node* Player = Context()->Player;
UpdateInput(p);
if (p->Inited && Player)
Player->Set(Player,PLAYER_UPDATEVIDEO,NULL,0);
}
IDCTRestore(NULL,&p->Backup); // free backup in any case
}
p->TurningOff = 0;
}
return ERR_NONE;
}
static bool_t PitchChanged(const packetformat* Current, const packetformat* New)
{
if (Current && Current->Type == PACKET_VIDEO && New && New->Type == PACKET_VIDEO)
{
video Tmp = New->Format.Video;
Tmp.Pitch = Current->Format.Video.Pitch;
return EqVideo(&Current->Format.Video,&Tmp);
}
return 0;
}
int OverlaySet(overlay* p,int No,const void* Data,int Size)
{
int Result = ERR_INVALID_PARAM;
switch (No)
{
case NODE_SETTINGSCHANGED:
p->ForceUpdate = 1;
break;
case OUT_INPUT: SETVALUE(p->Pin,pin,ERR_NONE); break;
case OUT_TOTAL: SETVALUE(p->Total,int,ERR_NONE); break;
case OUT_DROPPED: SETVALUE(p->Dropped,int,ERR_NONE); break;
case OUT_INPUT|PIN_FORMAT:
if (p->Inited && (
PitchChanged(&p->Input,(const packetformat*)Data) ||
PacketFormatRotatedVideo(&p->Input,(const packetformat*)Data,DIR_SWAPXY|DIR_MIRRORLEFTRIGHT|DIR_MIRRORUPDOWN)))
{
PacketFormatCopy(&p->Input,(const packetformat*)Data);
UpdateInputDirection(p,0);
Result = OverlayUpdateFX(p,1);
}
else
SETPACKETFORMAT(p->Input,packetformat,UpdateInput(p));
break;
case FLOW_BACKGROUND: SETVALUECMP(p->Background,bool_t,UpdateBackground(p),EqBool); break;
case VOUT_PLAY: SETVALUE(p->Play,bool_t,ERR_NONE); break;
case VOUT_UPDATING: SETVALUECMP(p->Updating,bool_t,Updating(p),EqBool); break;
case VOUT_CLIPPING: SETVALUE(p->Clipping,bool_t,OverlayUpdateShow(p,0)); break;
case VOUT_VISIBLE: SETVALUE(p->Visible,bool_t,OverlayUpdateShow(p,0)); break;
case VOUT_AUTOPREROTATE: SETVALUE(p->AutoPrerotate,bool_t,UpdateInputDirection(p,1)); break;
case VOUT_ASPECT: SETVALUECMP(p->Aspect,fraction,OverlayUpdateFX(p,0),EqFrac); break;
case VOUT_FX: SETVALUE(p->OrigFX,blitfx,OverlayUpdateFX(p,0)); break;
case VOUT_FULLSCREEN: SETVALUECMP(p->FullScreenViewport,bool_t,OverlayUpdateFX(p,0),EqBool); break;
case VOUT_VIEWPORT:
if (Size == sizeof(rect))
{
Result = ERR_NONE;
if (!EqRect(&p->Viewport,(const rect*)Data))
{
p->Viewport = *(const rect*)Data;
p->ForceUpdate = 1; // when in updating
OverlayUpdateFX(p,1);
}
}
break;
case FLOW_FLUSH:
p->Dirty = 1;
break;
case VOUT_RESET:
if (p->Inited && p->Reset)
{
p->Reset(p);
OverlayUpdateFX(p,1);
}
Result = ERR_NONE;
break;
}
return Result;
}
static const nodedef Overlay =
{
sizeof(overlay)|CF_ABSTRACT,
OVERLAY_CLASS,
VOUT_CLASS,
PRI_DEFAULT,
(nodecreate)Create,
(nodedelete)Delete,
};
void Overlay_Init()
{
NodeRegisterClass(&Overlay);
}
void Overlay_Done()
{
NodeUnRegisterClass(OVERLAY_CLASS);
}