📄 dvdsubber-format.cpp
字号:
/***********************************************************************
Copyright 2002 Ben Rudiak-Gould.
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,
or visit <http://www.gnu.org/copyleft/gpl.html>.
***********************************************************************/
#include "DVDSubber-compile.h"
#include "DVDSubber-format.h"
#include "DVDSubber-render.h"
#include <string.h>
#include <malloc.h>
template<class T> static inline T Min(T a, T b) { return a<b?a:b; }
template<class T> static inline T Max(T a, T b) { return a<b?b:a; }
extern "C"
__declspec(dllimport)
int __stdcall WideCharToMultiByte(
unsigned int codepage,
unsigned long flags,
const wchar_t* src,
int cch,
char* dst,
int cb,
const char* default_char,
int* used_default_char);
class NullDrawable : public Drawable {
public:
int GetAscent() const { return 0; }
int GetHeight() const { return 0; }
int GetWidth() const { return 0; }
int GetOnTime() const { return 0; }
int GetOffTime() const { return 0; }
void DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const {}
unsigned* GetEventTimes(unsigned* buf, unsigned* bufend) const { return buf; }
bool NothingToDraw(int time) const { return true; }
char* GetPrintableText(char* buf, char* bufend) const { return buf; }
int* GetSpaces(int, int* buf, int*) const { return buf; }
Drawable* BreakAt(int) { return 0; }
};
class Text : public Drawable {
wchar_t* text;
FontInfo* font;
unsigned char textcolor, halocolor;
int spacing;
int width;
int time0, time1, time2, time3;
unsigned GetColorAtTime(unsigned full_color, unsigned time) const;
public:
Text(wchar_t* _text, FontInfo* _font, unsigned char _textcolor, unsigned char _halocolor, int _spacing, int _time0, int _time1, int _time2, int _time3) {
text = _text;
font = _font;
textcolor = _textcolor;
halocolor = _halocolor;
spacing = _spacing;
width = font->GetTextWidth(text, wcslen(text));
time0 = _time0;
time1 = _time1;
time2 = _time2;
time3 = _time3;
}
int GetAscent() const {
return font->ascent + (spacing + font->external_leading);
}
int GetHeight() const {
return font->height + (spacing + font->external_leading);
}
int GetWidth() const { return width; }
int GetOnTime() const { return time0; }
int GetOffTime() const { return time3; }
void DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const;
unsigned* GetEventTimes(unsigned* buf, unsigned* bufend) const;
bool NothingToDraw(int time) const { return time < time0 || time >= time3; }
char* GetPrintableText(char* buf, char* bufend) const {
return buf + WideCharToMultiByte(0, 0, text, -1, buf, bufend-buf, 0, 0) - 1;
}
int* GetSpaces(int left, int* buf, int* bufend) const;
Drawable* BreakAt(int x);
~Text() { free(text); }
};
// IntermediateColor(0x3E, 0, 4) -> 0x1E
// IntermediateColor(0x3E, 1, 4) -> 0x1E
// IntermediateColor(0x3E, 2, 4) -> 0x2E
// IntermediateColor(0x3E, 3, 4) -> 0x2E
static unsigned IntermediateColor(unsigned full_color, unsigned fade_pos, unsigned fade_len) {
if (full_color == 0) return 0;
// The rounding down is deliberate!
return (full_color & 15) + 16 * (1 + ((full_color >> 4) - 1) * fade_pos / fade_len);
}
unsigned Text::GetColorAtTime(unsigned full_color, unsigned time) const {
if (time < time0) {
return 0;
} else if (time < time1) {
return IntermediateColor(full_color, time - time0, time1 - time0);
} else if (time < time2) {
return full_color;
} else if (time < time3) {
return IntermediateColor(full_color, time3 - time, time3 - time2);
} else {
return 0;
}
}
void Text::DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const {
unsigned tc = GetColorAtTime(textcolor, time);
unsigned hc = GetColorAtTime(halocolor, time);
if (tc || hc) {
int left = left_times_2 / 2;
top += (spacing + font->external_leading);
RenderText(text, font, tc, hc, buf->ptr, buf->width, buf->height, buf->xstride, buf->ystride, left, top);
int height = font->height;
buf->left = Max(0, Min(buf->left, left-2));
buf->right = Min(buf->width, Max(buf->right, left+width+2));
buf->top = Max(0, Min(buf->top, top-2));
buf->bottom = Min(buf->height, Max(buf->bottom, top+height+2));
}
}
unsigned* Text::GetEventTimes(unsigned* buf, unsigned* bufend) const {
unsigned oldtc = 0, oldhc = 0;
if (buf < bufend) {
*buf++ = time0 * 4;
}
int t = time0 + 1;
while (t < time3) {
if (buf >= bufend) {
break;
}
unsigned newtc = GetColorAtTime(textcolor, t);
unsigned newhc = GetColorAtTime(halocolor, t);
if (newtc != oldtc || newhc != oldhc) {
oldtc = newtc;
oldhc = newhc;
*buf++ = t * 4 + 1;
}
if (t == time1 && time1 < time2) {
t = time2;
} else {
++t;
}
}
if (buf < bufend) {
*buf++ = time3 * 4 + 2;
}
return buf;
}
int* Text::GetSpaces(int left, int* buf, int* bufend) const {
left += font->width_of_space / 2;
for (wchar_t* p = text; *p; ++p) {
if (*p == 32) {
if (buf < bufend) {
*buf++ = left + font->GetTextWidth(text, p - text);
}
}
}
return buf;
}
Drawable* Text::BreakAt(int x) {
for (wchar_t* p = text; *p; ++p) {
if (*p == 32) {
int left_width = font->GetTextWidth(text, p - text);
if (left_width <= x && x < left_width + font->width_of_space) {
*p = 0;
this->width = left_width;
return new Text(p+1, font, textcolor, halocolor, spacing, time0, time1, time2, time3);
}
}
}
return 0;
}
Drawable* new_Text(wchar_t* text, FontInfo* font, unsigned char textcolor, unsigned char halocolor, int spacing, int time0, int time1, int time2, int time3) {
return new Text(text, font, textcolor, halocolor, spacing, time0, time1, time2, time3);
}
/********************************************************************
********************************************************************/
class TextLine : public Drawable {
Drawable *l, *r;
int max_ascent, max_descent;
public:
TextLine(Drawable* _l, Drawable* _r) {
l = _l; r = _r;
int l_ascent = l->GetAscent();
int r_ascent = r->GetAscent();
max_ascent = Max(l_ascent, r_ascent);
max_descent = Max(l->GetHeight() - l_ascent, r->GetHeight() - r_ascent);
}
int GetAscent() const {
return max_ascent;
}
int GetHeight() const {
return max_ascent + max_descent;
}
int GetWidth() const {
return l->GetWidth() + r->GetWidth();
}
int GetOnTime() const {
return Min(l->GetOnTime(), r->GetOnTime());
}
int GetOffTime() const {
return Max(l->GetOffTime(), r->GetOffTime());
}
void DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const {
l->DrawSelf(time, buf, left_times_2, top + max_ascent - l->GetAscent());
left_times_2 += l->GetWidth() * 2;
r->DrawSelf(time, buf, left_times_2, top + max_ascent - r->GetAscent());
}
unsigned* GetEventTimes(unsigned* buf, unsigned* bufend) const {
buf = l->GetEventTimes(buf, bufend);
buf = r->GetEventTimes(buf, bufend);
return buf;
}
bool NothingToDraw(int time) const {
return l->NothingToDraw(time) && r->NothingToDraw(time);
}
char* GetPrintableText(char* buf, char* bufend) const {
buf = l->GetPrintableText(buf, bufend);
buf = r->GetPrintableText(buf, bufend);
return buf;
}
int* GetSpaces(int left, int* buf, int* bufend) const {
buf = l->GetSpaces(left, buf, bufend);
buf = r->GetSpaces(left + l->GetWidth(), buf, bufend);
return buf;
}
Drawable* BreakAt(int x) {
Drawable* mid = l->BreakAt(x);
if (mid) {
TextLine* result = new TextLine(mid, r);
r = new NullDrawable;
return result;
} else {
Drawable* rr = r->BreakAt(x - l->GetWidth());
if (rr) {
return rr;
}
}
return 0;
}
};
Drawable* new_TextLine(Drawable* l, Drawable* r) {
return new TextLine(l,r);
}
/********************************************************************
********************************************************************/
class TextLines : public Drawable {
Drawable *t, *b;
int max_width;
int align;
public:
TextLines(Drawable* _t, Drawable* _b, int _align) {
t = _t;
b = _b;
max_width = Max(t->GetWidth(), b->GetWidth());
align = _align;
}
int GetAscent() const {
return 0;
}
int GetHeight() const {
return t->GetHeight() + b->GetHeight();
}
int GetWidth() const {
return max_width;
}
int GetOnTime() const {
return Min(t->GetOnTime(), b->GetOnTime());
}
int GetOffTime() const {
return Max(t->GetOffTime(), b->GetOffTime());
}
void DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const {
t->DrawSelf(time, buf, left_times_2 + (max_width - t->GetWidth()) * (align+1), top);
top += t->GetHeight();
b->DrawSelf(time, buf, left_times_2 + (max_width - b->GetWidth()) * (align+1), top);
}
unsigned* GetEventTimes(unsigned* buf, unsigned* bufend) const {
buf = t->GetEventTimes(buf, bufend);
buf = b->GetEventTimes(buf, bufend);
return buf;
}
bool NothingToDraw(int time) const {
return t->NothingToDraw(time) && b->NothingToDraw(time);
}
char* GetPrintableText(char* buf, char* bufend) const {
buf = t->GetPrintableText(buf, bufend);
if (buf < bufend) *buf++ = '|';
buf = b->GetPrintableText(buf, bufend);
return buf;
}
int* GetSpaces(int left, int* buf, int* bufend) const { return buf; }
Drawable* BreakAt(int x) { return 0; }
};
Drawable* new_TextLines(Drawable* t, Drawable* b, int align) {
return new TextLines(t,b,align);
}
/********************************************************************
********************************************************************/
class TextBox : public Drawable {
Drawable* lines;
bool vertical;
int xoffs_times_2, yoffs;
int BestBreakPoint(int* spaces, int num_spaces, int width) {
int best_n = -1, best_dist = width;
for (int n=0; n < num_spaces; ++n) {
int dist = spaces[n]*2 - width;
if (dist < 0) dist = -dist;
if (dist < best_dist) {
best_dist = dist;
best_n = n;
}
}
return spaces[best_n];
}
public:
TextBox(Drawable* _lines, int left, int top, int right, int bottom, int linealign, int boxalign, bool _vertical) {
lines = _lines;
vertical = _vertical;
int width = lines->GetWidth();
if (width > right-left) {
int spaces[500];
int num_spaces = lines->GetSpaces(0, spaces, spaces + 500) - spaces;
int bp = BestBreakPoint(spaces, num_spaces, width);
if (bp >= 0) {
Drawable* newline = lines->BreakAt(bp);
if (newline) {
lines = new TextLines(lines, newline, linealign);
width = lines->GetWidth();
}
}
}
if (width > right-left) {
char buf[2048];
strcpy(buf, "box overflow: ");
char* p = buf + strlen(buf);
p = lines->GetPrintableText(p, buf + sizeof(buf) - 1);
*p = 0;
extern ICompilerCallbacks* g_compiler_callbacks;
g_compiler_callbacks->Warning(buf);
}
int height = lines->GetHeight();
xoffs_times_2 = left*2 + (right-left-width) * (boxalign%3);
yoffs = top + (bottom-top-height) * (2 - boxalign/3) / 2;
}
int GetAscent() const { return 0; }
int GetHeight() const { return 0; }
int GetWidth() const { return 0; }
int GetOnTime() const { return lines->GetOnTime(); }
int GetOffTime() const { return lines->GetOffTime(); }
void DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const {
// always called with left = top = 0
if (!vertical) {
lines->DrawSelf(time, buf, xoffs_times_2, yoffs);
} else {
DrawingBuf rotated_buf;
rotated_buf.ptr = buf->ptr + (buf->width - 1) * buf->xstride;
rotated_buf.width = buf->height;
rotated_buf.height = buf->width;
rotated_buf.xstride = buf->ystride;
rotated_buf.ystride = -buf->xstride;
rotated_buf.left = buf->top;
rotated_buf.right = buf->bottom;
rotated_buf.top = buf->width - buf->right;
rotated_buf.bottom = buf->width - buf->left;
lines->DrawSelf(time, &rotated_buf, xoffs_times_2, yoffs);
buf->left = buf->width - rotated_buf.bottom;
buf->right = buf->width - rotated_buf.top;
buf->top = rotated_buf.left;
buf->bottom = rotated_buf.right;
}
}
unsigned* GetEventTimes(unsigned* buf, unsigned* bufend) const {
return lines->GetEventTimes(buf, bufend);
}
bool NothingToDraw(int time) const {
return lines->NothingToDraw(time);
}
char* GetPrintableText(char* buf, char* bufend) const { return buf; }
int* GetSpaces(int left, int* buf, int* bufend) const { return buf; }
Drawable* BreakAt(int x) { return 0; }
};
Drawable* new_TextBox(Drawable* lines, int left, int top, int right, int bottom, int linealign, int boxalign, bool vertical) {
return new TextBox(lines, left, top, right, bottom, linealign, boxalign, vertical);
}
/********************************************************************
********************************************************************/
class TextBoxen : public Drawable {
Drawable *a, *b;
int min_on_time, max_off_time;
public:
TextBoxen(Drawable* _a, Drawable* _b) {
a = _a; b = _b;
min_on_time = Min(a->GetOnTime(), b->GetOnTime());
max_off_time = Max(a->GetOffTime(), b->GetOffTime());
}
int GetAscent() const { return 0; }
int GetHeight() const { return 0; }
int GetWidth() const { return 0; }
int GetOnTime() const { return min_on_time; }
int GetOffTime() const { return max_off_time; }
void DrawSelf(int time, DrawingBuf* buf, int left_times_2, int top) const {
if (time < min_on_time || time >= max_off_time)
return;
a->DrawSelf(time, buf, left_times_2, top);
b->DrawSelf(time, buf, left_times_2, top);
}
unsigned* GetEventTimes(unsigned* buf, unsigned* bufend) const {
buf = a->GetEventTimes(buf, bufend);
buf = b->GetEventTimes(buf, bufend);
return buf;
}
bool NothingToDraw(int time) const {
return time < min_on_time || time >= max_off_time || (a->NothingToDraw(time) && b->NothingToDraw(time));
}
char* GetPrintableText(char* buf, char* bufend) const { return buf; }
int* GetSpaces(int left, int* buf, int* bufend) const { return buf; }
Drawable* BreakAt(int x) { return 0; }
};
Drawable* new_TextBoxen(Drawable* a, Drawable* b) {
return new TextBoxen(a,b);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -