📄 tfont.cpp
字号:
/*
* Copyright (c) 2002-2006 Milan Cutka
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stdafx.h"
#include "Tsubreader.h"
#include "TsubtitleText.h"
#include "Tfont.h"
#include "TfontSettings.h"
#include "IffdshowBase.h"
#include "IffdshowDecVideo.h"
#include "TfontManager.h"
#include "simd.h"
#include "Tconfig.h"
#include "ffdebug.h"
#include "postproc/swscale.h"
#include "Tlibmplayer.h"
#include <mbstring.h>
#pragma warning(disable:4244)
//============================ TrenderedSubtitleWordBase =============================
TrenderedSubtitleWordBase::~TrenderedSubtitleWordBase()
{
if (own)
for (int i=0;i<3;i++)
{
aligned_free(bmp[i]);
aligned_free(msk[i]);
}
}
//============================== TrenderedSubtitleWord ===============================
// full rendering
template<class tchar> TrenderedSubtitleWord::TrenderedSubtitleWord(HDC hdc,const tchar *s0,size_t strlens,const short (*matrix)[5],const YUVcolor &yuv,const TrenderedSubtitleLines::TprintPrefs &prefs,int xscale,int alignment):TrenderedSubtitleWordBase(true),shiftChroma(true)
{
typedef typename tchar_traits<tchar>::ffstring ffstring;
typedef typename tchar_traits<tchar>::strings strings;
strings s1;
strtok(ffstring(s0,strlens).c_str(),_L("\t"),s1);
SIZE sz;sz.cx=sz.cy=0;ints cxs;
for (typename strings::iterator s=s1.begin();s!=s1.end();s++)
{
SIZE sz0;
prefs.config->getGDI<tchar>().getTextExtentPoint32(hdc,s->c_str(),(int)s->size(),&sz0);
sz.cx+=sz0.cx;
if (s+1!=s1.end())
{
int tabsize=prefs.tabsize*sz0.cy;
int newpos=(sz.cx/tabsize+1)*tabsize;
sz0.cx+=newpos-sz.cx;
sz.cx=newpos;
}
cxs.push_back(sz0.cx);
sz.cy=std::max(sz.cy,sz0.cy);
}
OUTLINETEXTMETRIC otm;
GetOutlineTextMetrics(hdc,sizeof(otm),&otm);
unsigned int shadowSize = getShadowSize(prefs,otm.otmTextMetrics.tmHeight);
if (otm.otmItalicAngle)
sz.cx-=LONG(sz.cy*sin(otm.otmItalicAngle*M_PI/1800));
else
if (otm.otmTextMetrics.tmItalic)
sz.cx+=sz.cy*0.35;
dx[0]=(sz.cx/4+2)*4;dy[0]=sz.cy+4;
unsigned char *bmp16=(unsigned char*)calloc(dx[0]*4,dy[0]);
HBITMAP hbmp=CreateCompatibleBitmap(hdc,dx[0],dy[0]);
HGDIOBJ old=SelectObject(hdc,hbmp);
RECT r={0,0,dx[0],dy[0]};
FillRect(hdc,&r,(HBRUSH)GetStockObject(BLACK_BRUSH));
SetTextColor(hdc,RGB(255,255,255));
SetBkColor(hdc,RGB(0,0,0));
int x=2;
ints::const_iterator cx=cxs.begin();
for (typename strings::const_iterator s=s1.begin();s!=s1.end();s++,cx++)
{
const char *t=(const char *)s->c_str();
int sz=s->size();
prefs.config->getGDI<tchar>().textOut(hdc,x,2,s->c_str(),sz/*(int)s->size()*/);
x+=*cx;
}
drawShadow(hdc,hbmp,bmp16,old,xscale,sz,prefs,matrix,yuv,shadowSize,alignment);
}
void TrenderedSubtitleWord::drawShadow(HDC hdc,HBITMAP hbmp,unsigned char *bmp16,HGDIOBJ old,int xscale,const SIZE &sz,const TrenderedSubtitleLines::TprintPrefs &prefs,const short (*matrix)[5],const YUVcolor &yuv,unsigned int shadowSize,int alignment)
{
BITMAPINFO bmi;
bmi.bmiHeader.biSize=sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth=dx[0];
bmi.bmiHeader.biHeight=-1*dy[0];
bmi.bmiHeader.biPlanes=1;
bmi.bmiHeader.biBitCount=32;
bmi.bmiHeader.biCompression=BI_RGB;
bmi.bmiHeader.biSizeImage=dx[0]*dy[0];
bmi.bmiHeader.biXPelsPerMeter=75;
bmi.bmiHeader.biYPelsPerMeter=75;
bmi.bmiHeader.biClrUsed=0;
bmi.bmiHeader.biClrImportant=0;
GetDIBits(hdc,hbmp,0,dy[0],bmp16,&bmi,DIB_RGB_COLORS); // copy bitmap, get it in bmp16
SelectObject(hdc,old);
DeleteObject(hbmp);
unsigned int _dx,_dy;
_dx=(xscale*dx[0]/100)/4+4+shadowSize;
_dy=dy[0]/4+4+shadowSize;
dxCharY=xscale*sz.cx/400;dyCharY=sz.cy/4;
_dx=(_dx/8+1)*8;
bmp[0]=(unsigned char*)aligned_calloc(_dx,_dy);
msk[0]=(unsigned char*)aligned_calloc(_dx,_dy);
int dxCharYstart;
switch (alignment)
{
case 1: // left(SSA)
case 5:
case 9:
dxCharYstart=0;
break;
case 2: // center(SSA)
case 6:
case 10:
dxCharYstart=(_dx-((sz.cx+3)/4+2+shadowSize)*xscale/100)/2;
break;
case 3: // right(SSA)
case 7:
case 11:
dxCharYstart=_dx-((sz.cx+3)/4+2+shadowSize)*xscale/100;
break;
case -1:
default:
dxCharYstart=((xscale==100 && prefs.align!=ALIGN_LEFT)?(_dx-dx[0]/4)/2:0);
break;
}
if (dxCharYstart<0) dxCharYstart=0;
for (unsigned int y=2;y<dy[0]-2;y+=4)
{
unsigned char *dstBmpY=bmp[0]+(y/4+2)*_dx+2+dxCharYstart;
for (unsigned int xstep=xscale==100?4*65536:400*65536/xscale,x=(2<<16)+xstep;x<((dx[0]-2)<<16);x+=xstep,dstBmpY++)
{
unsigned int sum=0;
for (const unsigned char *bmp16src=bmp16+((y-2)*dx[0]+((x>>16)-2))*4,*bmp16srcEnd=bmp16src+5*dx[0]*4;bmp16src!=bmp16srcEnd;bmp16src+=dx[0]*4)
{
for (int i=0;i<=12;i+=4)
sum+=bmp16src[i];
}
sum/=20; // average of 5x4=20pixels
*dstBmpY=(unsigned char)sum;
}
}
free(bmp16);
#if 0
// This code works. Blur characters. Edge gets more smooth, outline gets thicker.
// This code is better adapted to bigger characters.
if (prefs.deci)
{
unsigned char *blured_bmp=(unsigned char*)aligned_calloc(_dx,_dy);
Tlibmplayer *libmplayer;
SwsFilter filter;
SwsParams params;
prefs.deci->getPostproc(&libmplayer);
filter.lumH = filter.lumV = filter.chrH = filter.chrV = libmplayer->sws_getGaussianVec(0.7, 3.0);
libmplayer->sws_normalizeVec(filter.lumH, 1.0);
Tlibmplayer::swsInitParams(¶ms,SWS_GAUSS);
SwsContext *ctx=libmplayer->sws_getContext(_dx, _dy, IMGFMT_Y800, _dx, _dy, IMGFMT_Y800, ¶ms, &filter, NULL);
libmplayer->sws_scale_ordered(ctx,(const uint8_t**)&bmp[0],(const stride_t *)&_dx,0,_dy,(uint8_t**)&blured_bmp,(stride_t *)&_dx);
libmplayer->sws_freeContext(ctx);
libmplayer->sws_freeVec(filter.lumH);
libmplayer->Release();
aligned_free(bmp[0]);
bmp[0]=blured_bmp;
}
#endif
dx[0]=_dx;dy[0]=_dy;
if (!matrix)
memset(msk[0],255,dx[0]*dy[0]);
else
{
memcpy(msk[0],bmp[0],dx[0]*dy[0]);
#define MAKE_SHADOW(x,y) \
{ \
unsigned int s=0,cnt=0; \
for (int yy=-2;yy<=+2;yy++) \
{ \
if (y+yy<0 || (unsigned int)(y+yy)>=dy[0]) continue; \
for (int xx=-2;xx<=+2;xx++) \
{ \
if (x+xx<0 || (unsigned int)(x+xx)>=dx[0]) continue; \
s+=bmp[0][dx[0]*(y+yy)+(x+xx)]*matrix[yy+2][xx+2]; \
cnt++; \
} \
} \
s/=cnt*32; \
if (s>255) s=255; \
msk[0][dx[0]*y+x]=(unsigned char)s; \
}
int dyY1=dy[0]-1,dyY2=dy[0]-2;
for (unsigned int x=0;x<dx[0];x++)
{
MAKE_SHADOW(x,0);
MAKE_SHADOW(x,1);
MAKE_SHADOW(x,dyY2);
MAKE_SHADOW(x,dyY1);
}
int dxY1=dx[0]-1,dxY2=dx[0]-2;
for (unsigned int y0=2;y0<dy[0]-2;y0++)
{
MAKE_SHADOW(0,y0);
MAKE_SHADOW(1,y0);
MAKE_SHADOW(dxY2,y0);
MAKE_SHADOW(dxY1,y0);
}
__m64 matrix8_0=*(__m64*)matrix[0],matrix8_1=*(__m64*)matrix[1],matrix8_2=*(__m64*)matrix[2];
const int matrix4[3]={matrix[0][4],matrix[1][4],matrix[2][4]};
const unsigned char *bmpYsrc_2=bmp[0],*bmpYsrc_1=bmp[0]+1*dx[0],*bmpYsrc=bmp[0]+2*dx[0],*bmpYsrc1=bmp[0]+3*dx[0],*bmpYsrc2=bmp[0]+4*dx[0];
unsigned char *mskYdst=msk[0]+dx[0]*2;
__m64 m0=_mm_setzero_si64();
for (unsigned int y=2;y<dy[0]-2;y++,bmpYsrc_2+=dx[0],bmpYsrc_1+=dx[0],bmpYsrc+=dx[0],bmpYsrc1+=dx[0],bmpYsrc2+=dx[0],mskYdst+=dx[0])
for (unsigned int x=2;x<dx[0]-2;x++)
{
__m64 r_2=_mm_madd_pi16(_mm_unpacklo_pi8(*(__m64*)(bmpYsrc_2+x-2),m0),matrix8_0);
__m64 r_1=_mm_madd_pi16(_mm_unpacklo_pi8(*(__m64*)(bmpYsrc_1+x-2),m0),matrix8_1);
__m64 r =_mm_madd_pi16(_mm_unpacklo_pi8(*(__m64*)(bmpYsrc +x-2),m0),matrix8_2);
__m64 r1 =_mm_madd_pi16(_mm_unpacklo_pi8(*(__m64*)(bmpYsrc1 +x-2),m0),matrix8_1);
__m64 r2 =_mm_madd_pi16(_mm_unpacklo_pi8(*(__m64*)(bmpYsrc2 +x-2),m0),matrix8_0);
r=_mm_add_pi32(_mm_add_pi32(_mm_add_pi32(_mm_add_pi32(r_2,r_1),r),r1),r2);
r=_mm_add_pi32(_mm_srli_si64(r,32),r);
int s=bmpYsrc_2[x+2]*matrix4[0];
s+= bmpYsrc_1[x+2]*matrix4[1];
s+= bmpYsrc [x+2]*matrix4[2];
s+= bmpYsrc1 [x+2]*matrix4[1];
s+= bmpYsrc2 [x+2]*matrix4[0];
mskYdst[x]=limit_uint8((_mm_cvtsi64_si32(r)+s)/(25*32));
}
}
dx[1]=dx[0]>>prefs.shiftX[1];dx[2]=dx[0]>>prefs.shiftX[2];
dy[1]=dy[0]>>prefs.shiftY[1];dy[2]=dy[0]>>prefs.shiftY[2];
unsigned int _dxUV=dx[1];
dx[1]=(dx[1]/8+1)*8;
bmp[1]=(unsigned char*)aligned_calloc(dx[1],dy[1]);msk[1]=(unsigned char*)aligned_calloc(dx[1],dy[1]);
dx[2]=(dx[2]/8+1)*8;
bmp[2]=(unsigned char*)aligned_calloc(dx[2],dy[2]);msk[2]=(unsigned char*)aligned_calloc(dx[2],dy[2]);
unsigned char *bmpptr[3]={NULL,bmp[1],bmp[2]};
unsigned char *mskptr[3]={NULL,msk[1],msk[2]};
for (unsigned int y=0;y<dy[1];y++,bmpptr[1]+=dx[1],bmpptr[2]+=dx[2],mskptr[1]+=dx[1],mskptr[2]+=dx[2])
{
const unsigned char *bmpYptr=bmp[0]+dx[0]*(y*2),*mskYptr=msk[0]+dx[0]*(y*2);
for (unsigned int x=0;x<_dxUV;x++,bmpYptr+=2,mskYptr+=2)
{
unsigned int s;
s =bmpYptr[0];
s+=bmpYptr[1];
s+=bmpYptr[dx[0]];
s+=bmpYptr[dx[0]+1];
bmpptr[1][x]=(unsigned char)((yuv.U*s)>>9);
bmpptr[2][x]=(unsigned char)((yuv.V*s)>>9);
s =mskYptr[0];
s+=mskYptr[1];
s+=mskYptr[dx[0]];
s+=mskYptr[dx[0]+1];
mskptr[1][x]=
mskptr[2][x]=(unsigned char)(s/8);
}
}
unsigned int cnt=dx[0]*dy[0],i=0;
__m64 m0=_mm_setzero_si64(),yuvY=_mm_set1_pi16(yuv.Y);
for (;i<cnt-3;i+=4)
{
__m64 bmp8=_mm_unpacklo_pi8(_mm_cvtsi32_si64(*(int*)(bmp[0]+i)),m0);
bmp8=_mm_srli_pi16(_mm_mullo_pi16(bmp8,yuvY),8);
*(int*)(bmp[0]+i)=_mm_cvtsi64_si32(_mm_packs_pu16(bmp8,m0));
}
for (;i<cnt;i++)
{
bmp[0][i]=(unsigned char)((bmp[0][i]*yuv.Y)>>8);
}
bmpmskstride[0]=dx[0];bmpmskstride[1]=dx[1];bmpmskstride[2]=dx[2];
_mm_empty();
unsigned int shadowAlpha = prefs.shadowAlpha;
unsigned int shadowMode = prefs.shadowMode; // 0: glowing, 1:classic with gradient, 2: classic with no gradient, >=3: no shadow
if (shadowSize > 0)
if (shadowMode == 0) //Gradient glowing shadow (most complex)
{
if (dx[0]<shadowSize) shadowSize=dx[0];
if (dy[0]<shadowSize) shadowSize=dy[0];
unsigned int circle[1089]; // 1089=(16*2+1)^2
if (shadowSize>16) shadowSize=16;
int circleSize=shadowSize*2+1;
for (int y=0;y<circleSize;y++)
{
for (int x=0;x<circleSize;x++)
{
unsigned int rx=ff_abs(x-(int)shadowSize);
unsigned int ry=ff_abs(y-(int)shadowSize);
unsigned int r=(unsigned int)sqrt((double)(rx*rx+ry*ry));
if (r>shadowSize)
circle[circleSize*y+x] = 0;
else
circle[circleSize*y+x] = shadowAlpha*(shadowSize+1-r)/(shadowSize+1);
}
}
for (unsigned int y=0; y<_dy;y++)
{
int starty = y>=shadowSize ? 0 : shadowSize-y;
int endy = y+shadowSize<_dy ? circleSize : _dy-y+shadowSize;
for (unsigned int x=0; x<_dx;x++)
{
unsigned int pos = _dx*y+x;
int startx = x>=shadowSize ? 0 : shadowSize-x;
int endx = x+shadowSize<_dx ? circleSize : _dx-x+shadowSize;
if (bmp[0][pos] == 0) continue;
for (int ry=starty; ry<endy;ry++)
{
for (int rx=startx; rx<endx;rx++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -