📄 text-overlay.cpp
字号:
// Avisynth v0.3. Copyright 2000 Ben Rudiak-Gould. For distribution
// conditions, please see http://www.math.berkeley.edu/~benrg/avisynth.html .
// This file is adapted in part from VirtualDub source code. VirtualDub is
// Copyright 2000 Avery Lee.
#include "avisynth.h"
#include "internal-filters.h"
class Antialiaser {
const int w, h;
HDC hdcAntialias;
HBITMAP hbmAntialias;
void* lpAntialiasBits;
HFONT hfontDefault;
HBITMAP hbmDefault;
char* alpha_bits;
bool dirty;
void GetAlphaRect();
public:
Antialiaser(int width, int height, const char fontname[], int size);
HDC GetDC();
void Apply(unsigned char* pixel_buf, int textcolor, int halocolor, bool yuv);
~Antialiaser();
};
class ShowFrameNumber : public GenericVideoFilter {
Antialiaser antialiaser;
public:
ShowFrameNumber(PVideoFilter _child);
void GetFrame(int n, unsigned char* buf);
static PVideoFilter __cdecl Create(const FilterInfo*, const Arg* args, const char*) {
return new ShowFrameNumber(args[0].clip);
}
};
class Subtitle : public GenericVideoFilter {
const int x, y, firstframe, lastframe, size;
/*const*/ int textcolor, halocolor;
char* const fontname;
char* const text;
Antialiaser* antialiaser;
void InitAntialiaser();
public:
Subtitle(const char _text[], int _x, int _y, int _firstframe, int _lastframe, const char _fontname[], int _size,
int _textcolor, int _halocolor, PVideoFilter _child);
void GetFrame(int n, unsigned char* buf);
~Subtitle();
static PVideoFilter __cdecl Create(const FilterInfo*, const Arg* args, const char* arg_types) {
int num_args = strlen(arg_types);
if (num_args == 2) {
return new Subtitle(args[0].string, 4, 15, 0, 99999999, "Arial", 18, 0xFFFF00, 0, args[1].clip);
} else {
return new Subtitle(args[0].string, args[1].integer, args[2].integer,
args[3].integer, args[4].integer, args[5].string, args[6].integer,
(num_args >= 9) ? args[7].integer : 0xFFFF00,
(num_args >= 10) ? args[8].integer : 0, args[num_args-1].clip);
}
}
};
static HFONT LoadFont(int size, bool bold, bool italic, const char name[]) {
return CreateFont(size, 0, 0, 0, bold ? FW_BOLD : FW_NORMAL,
italic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE | DEFAULT_PITCH, name);
}
ShowFrameNumber::ShowFrameNumber(PVideoFilter _child)
: GenericVideoFilter(_child), antialiaser(vi.width, vi.height, "Arial", 192) {}
void ShowFrameNumber::GetFrame(int n, unsigned char* buf) {
child->GetFrame(n, buf);
HDC hdc = antialiaser.GetDC();
SetTextAlign(hdc, TA_BASELINE|TA_LEFT);
RECT r = { 0, 0, 32767, 32767 };
FillRect(hdc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
char text[40];
wsprintf(text, "%05d", n);
for (int y=192; y<vi.height*8; y += 192)
TextOut(hdc, child->GetParity(n) ? 32 : vi.width*8-512, y, text, strlen(text));
GdiFlush();
if (vi.IsYUY2())
antialiaser.Apply(buf, 0xD21092, 0x108080, true);
else
antialiaser.Apply(buf, 0xFFFF00, 0, false);
}
Antialiaser::Antialiaser(int width, int height, const char fontname[], int size)
: w(width), h(height)
{
struct {
BITMAPINFOHEADER bih;
RGBQUAD clr[2];
} b;
b.bih.biSize = sizeof(BITMAPINFOHEADER);
b.bih.biWidth = width * 8 + 32;
b.bih.biHeight = height * 8 + 32;
b.bih.biBitCount = 1;
b.bih.biPlanes = 1;
b.bih.biCompression = BI_RGB;
b.bih.biXPelsPerMeter = 0;
b.bih.biYPelsPerMeter = 0;
b.bih.biClrUsed = 2;
b.bih.biClrImportant = 2;
b.clr[0].rgbBlue = b.clr[0].rgbGreen = b.clr[0].rgbRed = 0;
b.clr[1].rgbBlue = b.clr[1].rgbGreen = b.clr[1].rgbRed = 255;
hdcAntialias = CreateCompatibleDC(NULL);
hbmAntialias = CreateDIBSection(
hdcAntialias,
(BITMAPINFO *)&b,
DIB_RGB_COLORS,
&lpAntialiasBits,
NULL,
0);
hbmDefault = (HBITMAP)SelectObject(hdcAntialias, hbmAntialias);
HFONT newfont = LoadFont(size, true, false, fontname);
hfontDefault = (HFONT)SelectObject(hdcAntialias, newfont);
SetMapMode(hdcAntialias, MM_TEXT);
SetTextColor(hdcAntialias, 0xffffff);
SetBkColor(hdcAntialias, 0);
alpha_bits = new char[width*height*2];
dirty = true;
}
Antialiaser::~Antialiaser() {
DeleteObject(SelectObject(hdcAntialias, hbmDefault));
DeleteObject(SelectObject(hdcAntialias, hfontDefault));
DeleteDC(hdcAntialias);
if (alpha_bits) delete[] alpha_bits;
}
HDC Antialiaser::GetDC() {
dirty = true;
return hdcAntialias;
}
char* MyStrdup(const char* s) {
return lstrcpy(new char[(lstrlen(s)+1)], s);
}
Subtitle::Subtitle(const char _text[], int _x, int _y, int _firstframe, int _lastframe, const char _fontname[], int _size, int _textcolor, int _halocolor, PVideoFilter _child)
: GenericVideoFilter(_child), antialiaser(0), text(MyStrdup(_text)), x(_x), y(_y), firstframe(_firstframe), lastframe(_lastframe), fontname(MyStrdup(_fontname)), size(_size*8)
{
if (vi.IsYUY2()) {
textcolor = RGB2YUV(_textcolor);
halocolor = RGB2YUV(_halocolor);
} else {
textcolor = _textcolor;
halocolor = _halocolor;
}
}
Subtitle::~Subtitle() {
if (antialiaser)
delete antialiaser;
if (text)
delete[] text;
if (fontname)
delete[] fontname;
}
void Subtitle::InitAntialiaser() {
antialiaser = new Antialiaser(vi.width, vi.height, fontname, size);
HDC hdcAntialias = antialiaser->GetDC();
int real_x;
if (x == -1) {
SetTextAlign(hdcAntialias, TA_BASELINE|TA_CENTER);
real_x = vi.width>>1;
} else {
SetTextAlign(hdcAntialias, TA_BASELINE|TA_LEFT);
real_x = x;
}
RECT r;
r.left = 0;
r.top = 0;
r.right = vi.width * 8 + 32;
r.bottom = vi.height * 8 + 32;
// FillRect(hdcAntialias, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
TextOut(hdcAntialias, real_x*8+16, y*8+16, text, strlen(text));
GdiFlush();
}
void Subtitle::GetFrame(int n, unsigned char* buf) {
child->GetFrame(n, buf);
if (n >= firstframe && n <= lastframe) {
if (!antialiaser)
InitAntialiaser();
antialiaser->Apply(buf, textcolor, halocolor, vi.IsYUY2());
} else {
// if we get far enough away from the frames we're supposed to
// subtitle, then junk the buffered drawing information
if (antialiaser && (n < firstframe-10 || n > lastframe+10)) {
delete antialiaser;
antialiaser = 0;
}
}
}
void Antialiaser::Apply(unsigned char* pixel_buf, int textcolor, int halocolor, bool yuv) {
if (dirty) GetAlphaRect();
if (yuv) {
int Ytext = ((textcolor>>16)&255), Utext = ((textcolor>>8)&255), Vtext = (textcolor&255);
int Yhalo = ((halocolor>>16)&255), Uhalo = ((halocolor>>8)&255), Vhalo = (halocolor&255);
for (int i=0; i<w*h*2; i+=4) {
if (*(long*)&alpha_bits[i]) {
pixel_buf[i] = (pixel_buf[i] * (64-alpha_bits[i]-alpha_bits[i+1]) + Ytext * alpha_bits[i] + Yhalo * alpha_bits[i+1]) >> 6;
pixel_buf[i+2] = (pixel_buf[i+2] * (64-alpha_bits[i+2]-alpha_bits[i+3]) + Ytext * alpha_bits[i+2] + Yhalo * alpha_bits[i+3]) >> 6;
int auv1 = alpha_bits[i]+alpha_bits[i+2];
int auv2 = alpha_bits[i+1]+alpha_bits[i+3];
pixel_buf[i+1] = (pixel_buf[i+1] * (128-auv1-auv2) + Utext * auv1 + Uhalo * auv2) >> 7;
pixel_buf[i+3] = (pixel_buf[i+3] * (128-auv1-auv2) + Vtext * auv1 + Vhalo * auv2) >> 7;
}
}
} else {
int Rtext = ((textcolor>>16)&255), Gtext = ((textcolor>>8)&255), Btext = (textcolor&255);
int Rhalo = ((halocolor>>16)&255), Ghalo = ((halocolor>>8)&255), Bhalo = (halocolor&255);
for (int y=0; y<h; ++y) {
unsigned char* pixel_buf_line = pixel_buf + y*w*3;
char* alpha_bits_line = alpha_bits + (h-y-1)*w*2;
for (int x=0; x<w; ++x) {
int textalpha = alpha_bits_line[0];
int haloalpha = alpha_bits_line[1];
if (textalpha | haloalpha) {
pixel_buf_line[0] = (pixel_buf_line[0] * (64-textalpha-haloalpha) + Btext * textalpha + Bhalo * haloalpha) >> 6;
pixel_buf_line[1] = (pixel_buf_line[1] * (64-textalpha-haloalpha) + Gtext * textalpha + Ghalo * haloalpha) >> 6;
pixel_buf_line[2] = (pixel_buf_line[2] * (64-textalpha-haloalpha) + Rtext * textalpha + Rhalo * haloalpha) >> 6;
}
pixel_buf_line += 3;
alpha_bits_line += 2;
}
}
}
}
void Antialiaser::GetAlphaRect() {
dirty = false;
static unsigned char bitcnt[256], // bit count
bitexl[256], // expand to left bit
bitexr[256]; // expand to right bit
static bool fInited = false;
if (!fInited) {
int i;
for(i=0; i<256; i++) {
unsigned char b=0, l=0, r=0;
if (i& 1) { b=1; l|=0x01; r|=0xFF; }
if (i& 2) { ++b; l|=0x03; r|=0xFE; }
if (i& 4) { ++b; l|=0x07; r|=0xFC; }
if (i& 8) { ++b; l|=0x0F; r|=0xF8; }
if (i& 16) { ++b; l|=0x1F; r|=0xF0; }
if (i& 32) { ++b; l|=0x3F; r|=0xE0; }
if (i& 64) { ++b; l|=0x7F; r|=0xC0; }
if (i&128) { ++b; l|=0xFF; r|=0x80; }
bitcnt[i] = b;
bitexl[i] = l;
bitexr[i] = r;
}
fInited = true;
}
int srcpitch = (w+4+3) & -4;
char* dst = alpha_bits;
for (int y=0; y<h; ++y) {
unsigned char* src = (unsigned char*)lpAntialiasBits + ((h-y-1)*8 + 20) * srcpitch + 2;
int wt = w;
do {
int alpha1, alpha2;
int i;
unsigned char bmasks[8], tmasks[8];
alpha1 = bitcnt[src[srcpitch*0]];
alpha1 += bitcnt[src[srcpitch*1]];
alpha1 += bitcnt[src[srcpitch*2]];
alpha1 += bitcnt[src[srcpitch*3]];
alpha1 += bitcnt[src[srcpitch*4]];
alpha1 += bitcnt[src[srcpitch*5]];
alpha1 += bitcnt[src[srcpitch*6]];
alpha1 += bitcnt[src[srcpitch*7]];
alpha2 = 0;
unsigned char cenmask = 0, mask1, mask2;
for(i=0; i<=8; i++) {
cenmask |= (unsigned char)(((long)-src[srcpitch*i ])>>31);
cenmask |= bitexl[src[srcpitch*i-1]];
cenmask |= bitexr[src[srcpitch*i+1]];
}
mask1 = mask2 = cenmask;
for(i=0; i<8; i++) {
mask1 |= (unsigned char)(((long)-src[srcpitch*(-i)])>>31);
mask1 |= bitexl[src[srcpitch*(-i)-1]];
mask1 |= bitexr[src[srcpitch*(-i)+1]];
mask2 |= (unsigned char)(((long)-src[srcpitch*(8+i)])>>31);
mask2 |= bitexl[src[srcpitch*(8+i)-1]];
mask2 |= bitexr[src[srcpitch*(8+i)+1]];
tmasks[i] = mask1;
bmasks[i] = mask2;
}
for(i=0; i<8; i++) {
alpha2 += bitcnt[cenmask | tmasks[7-i] | bmasks[i]];
}
dst[0] = alpha1;
dst[1] = alpha2-alpha1;
dst += 2;
++src;
} while(--wt);
}
}
FilterInfo text_filters[] = {
{ "ShowFrameNumber", "c", ShowFrameNumber::Create },
{ "Subtitle", "siiiisiiic", Subtitle::Create },
{ "Subtitle", "siiiisiic", Subtitle::Create },
{ "Subtitle", "siiiisic", Subtitle::Create },
{ "Subtitle", "sc", Subtitle::Create },
{ 0,0,0 }
};
PVideoFilter new_Subtitle(const char _text[], int _x, int _y, int _firstframe, int _lastframe, const char _fontname[], int _size, int _YUVtext, int _YUVhalo, PVideoFilter _child) {
return new Subtitle(_text, _x, _y, _firstframe, _lastframe, _fontname, _size, _YUVtext, _YUVhalo, _child);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -