/* Format.c -- handles JACOsub style formatting for a line */ #include "Format.h" #include "Directives.h" #include "RunScript.h" #include #include #include struct FontStuff { SInt16 id; SInt16 size; SInt16 ascent; SInt16 descent; SInt16 leading; SInt16 widMax; SInt16 lineHeight; SInt16 italWidth; SInt16 method; }; typedef struct FontStuff FontStuff; FontStuff fonts[10]; // info on the fonts (0..9) RGBColor palet[10][16]; Rect lastBox; // bounding box of last subtitle for VA/VU Buffer buffer[numBuffers+1]; SInt16 jacoWidth; // width of JACOsub coordinate space SInt16 jacoHeight; // height of JACOsub coordinate space SInt16 jacoTop; // top of JACOsub coordinate space void DoBuffer( UInt16 buf ) { Directive dir; // working directive for current line SubLinePtr line; SInt16 x,y; SInt32 i,j,n,w; char* lastWordEnd; SInt16 lastWordX; char c,*p,*p0,*e; Rect r; Boolean inWord; SInt16 textHeight,lineHeight,lineAscent,widest; SInt16 margin,pass,passStartLine; char *passStart; Str255 s; // output buffer SInt16 wordX; UInt16 txFace; SInt16 state; UInt16 lineNum; UInt16 nLines; SInt16 nextState; state = buffer[buf].state; if (state<0) nextState = state; else nextState = state + 1; line = buffer[buf].line; dir = line -> dir; r = line -> box; SetPort(buffer[buf].port); if (state==0) { r.top = dir.vTop + jacoTop; r.left = dir.hlMargin; r.right = dir.hrMargin; r.bottom = dir.vBottom + jacoTop; buffer[buf].breaks[0] = 0; nLines = 1; x = dir.hlMargin; } else { if (state<0) { SetPort(scrnWindow); ForeColor(blackColor); if ( buffer[buf].bits ) CopyBits( &buffer[buf].port->portBits, &scrnWindow->portBits, &r, &r, srcOr, nil); } nLines = buffer[buf].nLines; s[0] = 0; y = r.top + 2; lineNum = 1; y = y + buffer[buf].ascent[lineNum]; switch(dir.justify) // this is the place to handle justification { case justFull: // full justify not supported case justLeft: x = r.left + 2; break; case justRight: x = r.right - buffer[buf].widths[lineNum] - 2; break; case justCenter: x = (r.left + r.right - buffer[buf].widths[lineNum]) / 2; break; } wordX = x; } TextFont(fonts[dir.fontNum].id); TextSize(fonts[dir.fontNum].size); txFace = dir.txFace; TextFace(txFace); p = line -> text; p0 = p; e = p + GetPtrSize( line->text ); textHeight = 0; lineHeight = fonts[dir.fontNum].lineHeight; lineAscent = fonts[dir.fontNum].ascent; lastWordX = x; lastWordEnd = nil; widest = 0; inWord = false; pass = 1; passStart = p; passStartLine = nLines; margin = dir.hrMargin; while ((c=*p++) != 0 && p>=p0 && pwidest) widest = buffer[buf].widths[nLines]; textHeight = textHeight + lineHeight + 1; } nLines++; x = dir.hlMargin; lastWordX = x; lastWordEnd = nil; inWord = false; if (pass==1 && dir.wrapType==wrapSmart) { margin = 0; for (i=passStartLine; i=buffer[buf].breaks[lineNum]) && s[0] != 0) { i=0; j=0; if (state==-1) { RGBForeColor( &palet[dir.paletNum][dir.fgColor] ); } else { // RGBForeColor( &palet[dir.paletNum][2] ); // should be black ForeColor( blackColor ); // switch (fonts[dir.fontNum].method) // { // case 0: // 8-point 2 pixels thick (for bigger thicker fonts) // n = state; // x . x . x // if (n==8) nextState=-1; // . . . . . // if (n<5) n--; // x . . . x // i = n % 3 * 2 - 2; // . . . . . // j = n / 3 * 2 - 2; // x . x . x // break; // // case 1: // 24-point 2 pixels thick (for smaller pointier fonts) n = state; // x x x x x if (n==24) nextState=-1; // x x x x x if (n<13) n--; // x x . x x i = n % 5 - 2; // x x x x x j = n / 5 - 2; // x x x x x // break; // // case 2: // 12-point 2 pixels thick (for smaller thicker fonts) // n = state; // . x . x . // if (n==12) nextState=-1; // x . x . x // n = n * 2 - 1; // . x . x . // i = n % 5 - 2; // x . x . x // j = n / 5 - 2; // . x . x . // break; // } } MoveTo(wordX+i,y+j); DrawString(s); s[0] = 0; TextFace( txFace ); } x = x + w; if (p-p0>=buffer[buf].breaks[lineNum]) { s[0] = 0; y = y + buffer[buf].height[lineNum] - buffer[buf].ascent[lineNum]; lineNum++; y = y + buffer[buf].ascent[lineNum]; switch(dir.justify) // this is the place to handle justification { case justFull: // full justify not supported case justLeft: x = r.left + 2; break; case justRight: x = r.right - buffer[buf].widths[lineNum] - 2; break; case justCenter: x = (r.left + r.right - buffer[buf].widths[lineNum]) / 2; break; } wordX = x; } } else { if (inWord & c==' ') // test for end of word { inWord = false; lastWordX = x; lastWordEnd = p; } // test for right margin if ((x+w >= dir.hrMargin && dir.wrapType!=wrapNone) || (x-dir.hlMargin+w > margin && dir.wrapType==wrapSmart && !inWord )) { if (lastWordEnd) p = lastWordEnd; // back up to last end of word else while (*p!=' ' && *p!=0) p++; // skip to end of only word while (*p==' ') p++; // skip white space if (*p) // ignore last white-only line when auto-wrapping { if (dir.txFace & italic) // trailing italics adjust { lastWordX = lastWordX + fonts[dir.fontNum].italWidth; } buffer[buf].breaks[nLines] = p-p0; buffer[buf].ascent[nLines] = lineAscent; buffer[buf].height[nLines] = lineHeight; buffer[buf].widths[nLines] = lastWordX - dir.hlMargin; if (pass==2 || dir.wrapType!=wrapSmart) { if (buffer[buf].widths[nLines]>widest) widest = buffer[buf].widths[nLines]; textHeight = textHeight + lineHeight + 1; } nLines++; x = dir.hlMargin; lastWordX = x; lastWordEnd = nil; inWord = false; } else x = x + w; } else x = x + w; if (*p==0) { if (inWord) // test for end of word { lastWordX = x; lastWordEnd = p; } if (dir.txFace & italic) // trailing italics adjust { lastWordX = lastWordX + fonts[dir.fontNum].italWidth; } buffer[buf].breaks[nLines] = p-p0; buffer[buf].ascent[nLines] = lineAscent; buffer[buf].height[nLines] = lineHeight; buffer[buf].widths[nLines] = lastWordX - dir.hlMargin; if (pass==2 || dir.wrapType!=wrapSmart) { if (buffer[buf].widths[nLines]>widest) widest = buffer[buf].widths[nLines]; textHeight = textHeight + lineHeight + 1; } if (pass==1 && dir.wrapType==wrapSmart) { nLines++; x = dir.hlMargin; lastWordX = x; lastWordEnd = nil; inWord = false; margin = 0; for (i=passStartLine; i0) { // should be handled similar to VM but from top of screen } r.bottom = r.top + textHeight; break; case vpVPosition: // VP r.top = dir.vpPosition - buffer[buf].ascent[1] + jacoTop; r.bottom = r.top + textHeight; break; case vpVAbove: // VA r.bottom = lastBox.top; r.top = r.bottom - textHeight; break; case vpVUnder: // VU r.top = lastBox.bottom; r.bottom = r.top + textHeight; break; } switch (dir.justifyBlock) { case justLeft: r.right = r.left + widest; case justRight: r.left = r.right - widest; case justCenter: n = (r.right - r.left - widest) / 2; r.left = r.left + n; r.right = r.right - n; // case justFull: // use normal margins } switch (dir.justify) // shrink left & right edges of box { case justFull: // full justify not yet supported break; // note: full justify is not affected by justifyBlock case justLeft: r.right = r.left + widest; break; case justRight: r.left = r.right - widest; break; case justCenter: r.left = (r.left + r.right - widest) / 2; r.right = r.left + widest; break; } // special case for text ending in \n while (nLines>=1 && buffer[buf].widths[nLines]==0) { nLines--; r.bottom = r.bottom - lineHeight - 1; } InsetRect(& r, -2, -2); // add in space for the shadows r.right = r.right + 3; line -> box = r; buffer[buf].nLines = nLines; lastBox = r; } buffer[buf].state = nextState; //#define showrect 1 #ifdef showrect if (state==-1) { SetPort(scrnWindow); ForeColor(yellowColor); FrameRect( & line -> box ); } #endif } void InitBuffers( void ); void InitBuffers( void ) { UInt16 i; BitMap bm; Rect r; r = scrnWindow->portRect; for (i=0; i<=numBuffers; i++) { buffer[i].line = nil; if (i==0) { buffer[i].bits = nil; buffer[i].port = scrnWindow; buffer[i].inUse = true; } else { bm.bounds = r; bm.rowBytes = (r.right - r.left + 15) / 16 * 2; bm.baseAddr = NewPtrClear( (bm.rowBytes * (SInt32) (r.bottom - r.top) + 3) & 0xFFFFFFFC ); if (bm.baseAddr==nil) DebugStr("\pOut of memory in InitBuffers!"); buffer[i].inUse = false; buffer[i].port = &buffer[i].bufPort; buffer[i].bits = bm.baseAddr; OpenPort( buffer[i].port ); SetPort( buffer[i].port ); SetPortBits( &bm ); RectRgn( qd.thePort->visRgn, & qd.thePort->portBits.bounds ); } } SetPort( scrnWindow ); } void FreeBuffers( void ); void FreeBuffers( void ) { UInt16 i; for (i=0; i<=numBuffers; i++) { if (buffer[i].port == &buffer[i].bufPort) ClosePort( buffer[i].port ); if (buffer[i].bits) DisposePtr( buffer[i].bits ); } } void StartBuffer( UInt16 buf, SubLinePtr line ) { buffer[buf].inUse = true; buffer[buf].state = 0; buffer[buf].line = line; line -> buf = buf; DoBuffer( buf ); // do the metrics first so that lastBox will always be correct if ( buffer[buf].bits ) { SetPort( buffer[buf].port ); EraseRect( &qd.thePort->portRect); SetPort( scrnWindow ); } } void DrawBufferLine( SubLinePtr line ) { UInt16 i; i = 1; while ( i<=numBuffers && buffer[i].line!=line ) i++; if (i>numBuffers) { // DrawSubLine(line,true); } else { while (buffer[i].state>0) DoBuffer( i ); DoBuffer( i ); } } void DoneBuffer( UInt16 buf ) { buffer[buf].inUse = false; } UInt16 GetBuffer( void ) { UInt16 i; i = 1; // a LRU algorithm might be more useful in the future while ( i<=numBuffers && buffer[i].inUse ) i++; if (i>numBuffers) i=0; return i; } void DefineFont( UInt16 i, Str255 s, UInt16 size) { SInt16 fontID; FontInfo fntInfo; GetFNum(s, & fontID); SetPort(scrnWindow); TextFont(fontID); TextSize(size); GetFontInfo(&fntInfo); fonts[i].id = scrnWindow->txFont; fonts[i].size = scrnWindow->txSize; fonts[i].ascent = fntInfo.ascent; fonts[i].descent = fntInfo.descent; fonts[i].leading = /*fntInfo.leading +*/ 0; fonts[i].widMax = fntInfo.widMax; fonts[i].lineHeight = fonts[i].ascent + fonts[i].descent + fonts[i].leading; fonts[i].method = 0; // if (fonts[i].size<24) fonts[i].method = 1; if (fonts[i].size<24) fonts[i].method = 2; // I have no formula for italic overhang width, but these will work for now. fonts[i].italWidth = fntInfo.ascent / 6; switch (fonts[i].size) { case 9: case 10: case 14: case 18: case 20: case 24: fonts[i].italWidth = fntInfo.ascent / 2; break; case 36: fonts[i].italWidth = fntInfo.ascent / 4; } } void DefaultFonts( void ); void DefaultFonts( void ) { UInt16 i; Str255 s; SInt32 size; for (i=0; i<=9; i++) { GetIndString(s,256,i*2+2); StringToNum(s,&size); GetIndString(s,256,i*2+1); DefineFont(i,s,size); } } void ResetFormatCompile( void ) { UInt16 i; RGBColor defpalet[16] = { // 0x9999, 0xAAAA, 0xBBBB, // 0 background (transparent when genlocked) 0xFFFF, 0xFFFF, 0xFFFF, // 0 background (transparent when genlocked) 0x0000, 0xCCCC, 0x6666, // 1 alternate font color (usually green) 0x0000, 0x0000, 0x0000, // 2 font outline/shadow color (should be dark) 0xFFFF, 0xFFFF, 0x0000, // 3 primary font face color (usually yellow) 0xDDDD, 0x0000, 0x0000, // 4 red - remaining settings are arbitrary 0xFFFF, 0x8888, 0x0000, // 5 orange 0xDDDD, 0x8888, 0xDDDD, // 6 light magenta / hot pink 0x0000, 0xDDDD, 0xEEEE, // 7 cyan 0xBBBB, 0x7777, 0x3333, // 8 brown 0xDDDD, 0xBBBB, 0x9999, // 9 light beige 0x5555, 0xAAAA, 0xEEEE, // 10 deep sky blue 0x3333, 0x3333, 0x3333, // 11 gray shades... 0x6666, 0x6666, 0x6666, // 12 0x8888, 0x8888, 0x8888, // 13 0xBBBB, 0xBBBB, 0xBBBB, // 14 0xDDDD, 0xDDDD, 0xDDDD // 15 }; for (i=0; i<=9; i++) memcpy(&palet[i],&defpalet,sizeof(defpalet)); DefaultFonts( ); } void ResetFormatRun( void ) { UInt16 i; for (i=0; i<=numBuffers; i++) { buffer[i].inUse = false; buffer[i].state = 0; buffer[i].line = nil; } } void FormatInit( void ) { jacoWidth = 640; jacoHeight = 400; jacoTop = 50; InitBuffers( ); ResetFormatCompile( ); } void FormatFinal( void ) { FreeBuffers( ); }