/***************************************************************************** * * 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 && ScaleXFX.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); }