/*********************************************************************** 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 . ***********************************************************************/ #include "DVDSubber-compile.h" #include "DVDSubber-format.h" #include "DVDSubber-render.h" #include #include template static inline T Min(T a, T b) { return a static inline T Max(T a, T b) { return aGetTextWidth(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); }