Changeset 314
- Timestamp:
- 01/29/07 22:17:45 (2 years ago)
- Files:
-
- trunk/SSADocument.h (modified) (3 diffs)
- trunk/SSADocument.m (modified) (11 diffs)
- trunk/SSARenderCodec.h (modified) (1 diff)
- trunk/SSARenderCodec.m (modified) (7 diffs)
- trunk/SSATagParsing.h (modified) (1 diff)
- trunk/SSATagParsing.m.rl (modified) (4 diffs)
- trunk/TextSubCodec.c (modified) (10 diffs)
- trunk/TextSubCodec.r (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/SSADocument.h
r313 r314 47 47 int stylecount; 48 48 ssastyleline *styles; 49 ssastyleline *defaultstyle; 49 50 50 51 float resX, resY; … … 52 53 enum {Normal, Reverse} collisiontype; 53 54 enum {S_ASS, S_SSA} version; 55 Boolean disposedefaultstyle; 54 56 } 55 57 -(NSString *)header; … … 57 59 -(void) loadHeader:(NSString*)path; 58 60 -(unsigned)packetCount; 61 -(void)loadDefaultsWithWidth:(float)width height:(float)height; 59 62 @end 60 63 trunk/SSADocument.m
r313 r314 20 20 21 21 @implementation SSADocument 22 23 static ssastyleline SSA_DefaultStyle = (ssastyleline){ 24 @"Default",@"Helvetica", 25 32 * (96./72.), 26 {{1,1,1,1},{1,1,1,1},{0,0,0,1},{0,0,0,1}}, // white on black 27 1,0,0,0, 28 100,100,0,0, 29 0,1.5,1.5, 30 2,1,0, 31 10,10,10, 32 NULL, NULL 33 }; 34 35 -(void)dealloc 36 { 37 int i; 38 if (disposedefaultstyle) free(defaultstyle); 39 for (i=0; i < stylecount; i++) {ATSUDisposeStyle(styles[i].atsustyle); ATSUDisposeTextLayout(styles[i].layout);} 40 41 [_lines release]; 42 [header release]; 43 [super dealloc]; 44 } 22 45 23 46 static ATSURGBAlphaColor SSAParseColor(NSString *c) … … 147 170 -(void)setupStyles:(NSDictionary*)sDict 148 171 { 172 defaultstyle = NULL; 173 149 174 if ([sDict count] > 0) { 150 175 NSEnumerator *sEnum = [sDict objectEnumerator]; … … 188 213 189 214 styles[i] = s; 215 if (!defaultstyle && [styles[i].name isEqualToString:@"Default"]) {defaultstyle = &styles[i]; disposedefaultstyle = NO;} 190 216 i++; 191 217 } 218 } 219 220 if (!defaultstyle) { 221 disposedefaultstyle = YES; 222 defaultstyle = malloc(sizeof(ssastyleline)); 223 *defaultstyle = SSA_DefaultStyle; 224 [self makeATSUStylesForSSAStyle:defaultstyle]; 192 225 } 193 226 } … … 215 248 { 216 249 return [NSString stringWithFormat:@"%d,%d,%@,%@,%0.4d,%0.4d,%0.4d,%@,%@\n", 217 [ s objectForKey:@"ReadOrder"],250 [[s objectForKey:@"ReadOrder"] intValue], 218 251 [[s objectForKey:@"Layer"] intValue], 219 252 [s objectForKey:@"Style"], … … 255 288 line_range lines[num]; 256 289 unsigned i, j; 257 line_range li;258 290 NSMutableArray *outa = [[NSMutableArray alloc] init]; 259 291 … … 262 294 NSString *s,*e; 263 295 l = [linesa objectAtIndex:i]; 296 line_range li; 264 297 265 298 s = [l objectForKey:@"Start"]; … … 269 302 li.start = ParseSSATime(s); 270 303 li.end = ParseSSATime(e); 304 305 if (timescale != 1.) { 306 li.start *= timescale; 307 li.end *= timescale; 308 } 271 309 272 310 times[i*2] = li.start; … … 295 333 SSAEvent *event = [[[SSAEvent alloc] init] autorelease]; 296 334 297 if (timescale != 1.) {298 double ds = start, de = end;299 ds *= timescale;300 de *= timescale;301 start = ds;302 end = de;303 }304 335 event->begin_time = start; 305 336 event->end_time = end; … … 462 493 } 463 494 495 -(void)loadDefaultsWithWidth:(float)width height:(float)height 496 { 497 stylecount = 0; 498 styles = malloc(0); 499 defaultstyle = malloc(sizeof(ssastyleline)); 500 *defaultstyle = SSA_DefaultStyle; 501 disposedefaultstyle = YES; 502 resX = (width / height) * 480.; resY = 480; 503 timescale = 1; 504 collisiontype = Normal; 505 version = S_ASS; 506 507 [self makeATSUStylesForSSAStyle:defaultstyle]; 508 } 509 464 510 -(NSString*)header 465 511 { … … 486 532 ImageDescriptionHandle textDesc; 487 533 Rect movieBox; 488 static UInt8 path[PATH_MAX];489 534 UInt32 emptyDataRefExtension[2]; 535 TimeScale movieTimeScale = GetMovieTimeScale(theMovie); 536 UInt8 *path = malloc(PATH_MAX); 490 537 491 538 FSRefMakePath(theDirectory, path, PATH_MAX); 492 539 493 540 [ssa loadFile:[[NSString stringWithUTF8String:(char*)path] stringByAppendingPathComponent:(NSString*)filename]]; 541 542 free(path); 494 543 495 544 packetCount = [ssa packetCount]; … … 540 589 if (err != noErr) goto bail; 541 590 542 ConvertTimeScale(&movieStartTime, GetMovieTimeScale(theMovie));591 ConvertTimeScale(&movieStartTime, movieTimeScale); 543 592 544 593 err = InsertMediaIntoTrack(theTrack, movieStartTime.value.lo, sampleTime, p->end_time - p->begin_time, fixed1); trunk/SSARenderCodec.h
r313 r314 14 14 15 15 extern SSARenderGlobalsPtr SSA_Init(const char *header, size_t size); 16 extern void SSA_RenderLine(SSARenderGlobalsPtr glob, CGContextRef c, const char *data, size_t size, float width, float height); 16 extern SSARenderGlobalsPtr SSA_InitNonSSA(float width, float height); 17 extern void SSA_RenderLine(SSARenderGlobalsPtr glob, CGContextRef c, CFStringRef str, float width, float height); 17 18 extern void SSA_Dispose(SSARenderGlobalsPtr glob); trunk/SSARenderCodec.m
r313 r314 14 14 { 15 15 SSADocument *document; 16 Boolean plaintext; 16 17 } SSARenderGlobals; 17 18 … … 21 22 NSString *hdr = [[NSString alloc] initWithBytesNoCopy:(void*)header length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]; 22 23 g->document = [[SSADocument alloc] init]; 24 g->plaintext = false; 23 25 [g->document loadHeader:hdr]; 24 26 [hdr release]; 27 return g; 28 } 29 30 SSARenderGlobalsPtr SSA_InitNonSSA(float width, float height) 31 { 32 SSARenderGlobalsPtr g = (SSARenderGlobalsPtr)NewPtr(sizeof(SSARenderGlobals)); 33 g->document = [[SSADocument alloc] init]; 34 g->plaintext = true; 35 [g->document loadDefaultsWithWidth:width height:height]; 25 36 return g; 26 37 } … … 77 88 } 78 89 79 void SSA_RenderLine(SSARenderGlobalsPtr glob, CGContextRef c, const char *data, size_t size, float cWidth, float cHeight)90 void SSA_RenderLine(SSARenderGlobalsPtr glob, CGContextRef c, CFStringRef cfSub, float cWidth, float cHeight) 80 91 { 81 92 ItemCount breakCount; … … 86 97 SSADocument *ssa = glob->document; 87 98 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 88 NSString *curSub = [[NSString alloc] initWithBytesNoCopy:(void*)data length:size encoding:NSUTF8StringEncoding freeWhenDone:NO];89 NSArray *rentities = ParseSubPacket(curSub,ssa );99 NSString *curSub = (NSString*)cfSub; 100 NSArray *rentities = ParseSubPacket(curSub,ssa,glob->plaintext); 90 101 OSStatus err; 91 102 103 CGContextClearRect(c, CGRectMake(0,0,cWidth,cHeight)); 92 104 CGContextScaleCTM(c, cWidth / ssa->resX, cHeight / ssa->resY); 93 105 subcount = [rentities count]; … … 100 112 101 113 size_t sublen = [re->nstext length]; 102 114 103 115 err=ATSUSetTextPointerLocation(layout,re->text,kATSUFromTextBeginning,kATSUToTextEnd,sublen); 104 116 … … 132 144 case S_BottomAlign: default: //bottom 133 145 ATSUGetLineControl(layout, kATSUFromTextBeginning, kATSULineDescentTag, sizeof(ATSUTextMeasurement), &descent, NULL); 134 penY = (lastBottomPenY!=-1)?lastBottomPenY:(MAX(IntToFixed(re->marginv), descent) ); direction = 1;146 penY = (lastBottomPenY!=-1)?lastBottomPenY:(MAX(IntToFixed(re->marginv), descent) + FloatToFixed(shadow)); direction = 1; 135 147 lstart = breakCount; lend = -1; lstep = -1; 136 148 storePenY = &lastBottomPenY; … … 245 257 } 246 258 247 [curSub release];248 259 [pool release]; 249 CGContextSynchronize(c);250 CGContextRelease(c);251 260 } 252 261 trunk/SSATagParsing.h
r310 r314 37 37 @end 38 38 39 extern NSArray *ParseSubPacket(NSString *str, SSADocument *ssa );39 extern NSArray *ParseSubPacket(NSString *str, SSADocument *ssa, Boolean plaintext); trunk/SSATagParsing.m.rl
r313 r314 70 70 { 71 71 unsigned char r,g,b; 72 c = EndianU32_NtoB(c); 72 73 r = c & 0xff; 73 74 g = (c >> 8) & 0xff; … … 76 77 } 77 78 78 NSArray *ParseSubPacket(NSString *str, SSADocument *ssa )79 NSArray *ParseSubPacket(NSString *str, SSADocument *ssa, Boolean plaintext) 79 80 { 80 81 NSArray *linea = [str componentsSeparatedByString:@"\n"]; 81 int i, pcount = [linea count]; unsigned len;82 int i, j, pcount = [linea count]; unsigned len; 82 83 NSMutableArray *rentities = [NSMutableArray array]; 83 84 84 85 for (i = 0; i < pcount; i++) { 85 NSArray *ar = [[linea objectAtIndex:i] componentsSeparatedByString:@"," count:9]; 86 SSARenderEntity *re; 86 SSARenderEntity *re = [[[SSARenderEntity alloc] init] autorelease]; 87 87 BOOL ldirty = NO; 88 88 89 if ([ar count] < 9) continue; 90 91 re = [[[SSARenderEntity alloc] init] autorelease]; 92 re->layer = [[ar objectAtIndex:1] intValue]; 93 NSString *sn = [ar objectAtIndex:2]; 94 int j; 95 96 for (j=0; j < ssa->stylecount; j++) { 97 if ([sn isEqualToString:ssa->styles[j].name]) { 98 re->style = &ssa->styles[j]; 99 break; 89 if (plaintext) { 90 re->style = ssa->defaultstyle; 91 re->layout = re->style->layout; 92 re->layer = 0; 93 re->marginl = re->style->marginl; 94 re->marginr = re->style->marginl; 95 re->marginv = re->style->marginl; 96 re->usablewidth = re->style->usablewidth; 97 re->halign = 1; 98 re->valign = 0; 99 re->nstext = [linea objectAtIndex:i]; 100 re->styles = NULL; 101 re->disposelayout = NO; 102 } else { 103 NSArray *ar = [[linea objectAtIndex:i] componentsSeparatedByString:@"," count:9]; 104 105 if ([ar count] < 9) continue; 106 107 re->layer = [[ar objectAtIndex:1] intValue]; 108 NSString *sn = [ar objectAtIndex:2]; 109 110 re->style = NULL; 111 112 for (j=0; j < ssa->stylecount; j++) { 113 if ([sn isEqualToString:ssa->styles[j].name]) { 114 re->style = &ssa->styles[j]; 115 break; 116 } 100 117 } 118 119 if (!re->style) { 120 re->style = ssa->defaultstyle; 121 } 122 123 re->marginl = [[ar objectAtIndex:5] intValue]; 124 re->marginr = [[ar objectAtIndex:6] intValue]; 125 re->marginv = [[ar objectAtIndex:7] intValue]; 126 127 if (re->marginl == 0) re->marginl = re->style->marginl; else ldirty = TRUE; 128 if (re->marginr == 0) re->marginr = re->style->marginr; else ldirty = TRUE; 129 if (re->marginv == 0) re->marginv = re->style->marginv; else ldirty = TRUE; 130 131 if (ldirty) { 132 ATSUTextMeasurement width; 133 ATSUAttributeTag tag[] = {kATSULineWidthTag}; 134 ByteCount size[] = {sizeof(ATSUTextMeasurement)}; 135 ATSUAttributeValuePtr val[] = {&width}; 136 137 re->usablewidth = ssa->resX - re->marginl - re->marginr; 138 ATSUCreateAndCopyTextLayout(re->style->layout,&re->layout); 139 width = IntToFixed(re->usablewidth); 140 141 ATSUSetLayoutControls(re->layout,1,tag,size,val); 142 re->disposelayout = YES; 143 } else { 144 re->usablewidth = re->style->usablewidth; 145 re->layout = re->style->layout; 146 re->disposelayout = NO; 147 } 148 149 re->nstext = [ar objectAtIndex:8]; 101 150 } 102 103 re->marginl = [[ar objectAtIndex:5] intValue];104 re->marginr = [[ar objectAtIndex:6] intValue];105 re->marginv = [[ar objectAtIndex:7] intValue];106 107 if (re->marginl == 0) re->marginl = re->style->marginl; else ldirty = TRUE;108 if (re->marginr == 0) re->marginr = re->style->marginr; else ldirty = TRUE;109 if (re->marginv == 0) re->marginv = re->style->marginv; else ldirty = TRUE;110 111 if (ldirty) {112 ATSUTextMeasurement width;113 ATSUAttributeTag tag[] = {kATSULineWidthTag};114 ByteCount size[] = {sizeof(ATSUTextMeasurement)};115 ATSUAttributeValuePtr val[] = {&width};116 117 re->usablewidth = ssa->resX - re->marginl - re->marginr;118 ATSUCreateAndCopyTextLayout(re->style->layout,&re->layout);119 width = IntToFixed(re->usablewidth);120 121 ATSUSetLayoutControls(re->layout,1,tag,size,val);122 re->disposelayout = YES;123 } else {124 re->usablewidth = re->style->usablewidth;125 re->layout = re->style->layout;126 re->disposelayout = NO;127 }128 129 re->nstext = [ar objectAtIndex:8];130 151 len = [re->nstext length]; 131 152 re->text = malloc(sizeof(unichar[len])); … … 170 191 unsigned long inum; 171 192 unsigned outputoffset=0, lengthreduce=0; 172 BOOL flag, newLayout=FALSE, cur_be ;193 BOOL flag, newLayout=FALSE, cur_be = FALSE; 173 194 Fixed fixv; 174 195 … … 353 374 } 354 375 355 flag = [01] > {flag = *p- '0';};376 flag = [01]? > {flag = 1;} % {flag = *(p-1) - '0';}; 356 377 num_ = digit+ ('.' digit*)?; 357 378 num = num_ > {numbegin = p;} % {num = [[NSString stringWithCharacters:numbegin length:p-numbegin] doubleValue];}; trunk/TextSubCodec.c
r313 r314 30 30 31 31 CGColorSpaceRef colorSpace; 32 33 ATSUStyle textStyle, italicStyle, boldStyle, biStyle;34 ATSUTextLayout textLayout;35 32 36 33 SSARenderGlobalsPtr ssa; 37 Boolean useSSA;34 Boolean translateSRT; 38 35 } TextSubGlobalsRecord, *TextSubGlobals; 39 36 … … 49 46 } TextSubDecompressRecord; 50 47 51 static CFMutableStringRef CFStringCreateWithCStringMutable(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding) {52 CFStringRef s1 = CFStringCreateWithCString(alloc,cStr,encoding);53 if (!s1) return NULL;54 CFMutableStringRef s2 = CFStringCreateMutableCopy(alloc,0,s1);55 CFRelease(s1);56 return s2;57 }58 59 static size_t ParseSrtStyles(TextSubGlobals glob, CFMutableStringRef cfsub, UniChar *uc, size_t sublen);60 static inline int min(int a, int b) {return (a<=b)?a:b;}61 62 48 // Setup required for ComponentDispatchHelper.c 63 49 #define IMAGECODEC_BASENAME() TextSubCodec … … 84 70 #endif 85 71 72 static CFMutableStringRef CFStringCreateMutableWithBytes(CFAllocatorRef alloc, char *cStr, size_t size, CFStringEncoding encoding) { 73 CFStringRef s1 = CFStringCreateWithBytes(alloc,(UInt8*)cStr,size,encoding,false); 74 if (!s1) return NULL; 75 CFMutableStringRef s2 = CFStringCreateMutableCopy(alloc,0,s1); 76 CFRelease(s1); 77 return s2; 78 } 79 86 80 /* -- This Image Decompressor Uses the Base Image Decompressor Component -- 87 81 The base image decompressor is an Apple-supplied component … … 109 103 glob->drawBandUPP = NULL; 110 104 glob->ssa = NULL; 111 glob->useSSA = false;112 105 113 106 // Open and target an instance of the base decompressor as we delegate … … 139 132 CGColorSpaceRelease(glob->colorSpace); 140 133 } 141 if (glob->textStyle) { 142 ATSUDisposeStyle(glob->textStyle); 143 } 144 if (glob->italicStyle) { 145 ATSUDisposeStyle(glob->italicStyle); 146 } 147 if (glob->boldStyle) { 148 ATSUDisposeStyle(glob->boldStyle); 149 } 150 if (glob->boldStyle) { 151 ATSUDisposeStyle(glob->biStyle); 152 } 153 if (glob->textLayout) { 154 ATSUDisposeTextLayout(glob->textLayout); 155 } 156 if (glob->useSSA) SSA_Dispose(glob->ssa); 134 if (glob->ssa) SSA_Dispose(glob->ssa); 157 135 DisposePtr((Ptr)glob); 158 136 } … … 285 263 myDrp->dataSize = p->bufferSize; 286 264 287 glob->useSSA = (**p->imageDescription).cType == kSubFormatSSA; 288 if (glob->useSSA) { 265 glob->translateSRT = true; 266 267 if ((**p->imageDescription).cType == kSubFormatSSA) { 289 268 long count; 269 glob->translateSRT = false; 270 290 271 CountImageDescriptionExtensionType(p->imageDescription,kSubFormatSSA,&count); 291 272 if (count == 1) { … … 295 276 glob->ssa = SSA_Init(*ssaheader, GetHandleSize(ssaheader)); 296 277 } 297 } 278 } else glob->ssa = SSA_InitNonSSA(myDrp->width,myDrp->height); 298 279 299 280 return noErr; … … 313 294 pascal ComponentResult TextSubCodecDrawBand(TextSubGlobals glob, ImageSubCodecDecompressRecord *drp) 314 295 { 315 OSErr err = noErr;316 int i;317 296 TextSubDecompressRecord *myDrp = (TextSubDecompressRecord *)drp->userDecompressRecord; 318 float diagonalLength = sqrtf(myDrp->width*myDrp->width+myDrp->height*myDrp->height);319 ATSUTextMeasurement lineWidth = Long2Fix(myDrp->width), lineHeight = Long2Fix(25), ShadowDistFix = FloatToFixed(diagonalLength * ShadowDistance);320 // char *dataPtr = (char *)drp->codecData;321 // ICMDataProcRecordPtr dataProc = drp->dataProcRecord.dataProc ? &drp->dataProcRecord : NULL;322 297 323 298 CGContextRef c = CGBitmapContextCreate(drp->baseAddr, myDrp->width, myDrp->height, … … 325 300 kCGImageAlphaPremultipliedFirst); 326 301 327 if (glob->useSSA) { 328 SSA_RenderLine(glob->ssa,c,drp->codecData,myDrp->dataSize,myDrp->width,myDrp->height); 329 return noErr; 302 CFMutableStringRef buf; 303 304 if (glob->translateSRT) { 305 buf = CFStringCreateMutableWithBytes(NULL, drp->codecData, myDrp->dataSize, kCFStringEncodingUTF8); 306 if (!buf) return noErr; 307 CFStringFindAndReplace(buf, CFSTR("<i>"), CFSTR("{\\i}"), CFRangeMake(0,CFStringGetLength(buf)), 0); 308 CFStringFindAndReplace(buf, CFSTR("<b>"), CFSTR("{\\b}"), CFRangeMake(0,CFStringGetLength(buf)), 0); 309 CFStringFindAndReplace(buf, CFSTR("</i>"), CFSTR("{\\i0}"), CFRangeMake(0,CFStringGetLength(buf)), 0); 310 CFStringFindAndReplace(buf, CFSTR("</b>"), CFSTR("{\\b0}"), CFRangeMake(0,CFStringGetLength(buf)), 0); 311 } else { 312 buf = (CFMutableStringRef)CFStringCreateWithBytes(NULL, (UInt8*)drp->codecData, myDrp->dataSize, kCFStringEncodingUTF8, false); 313 if (!buf) return noErr; 330 314 } 331 315 332 if (!glob->textStyle) { //TODO: set language region based on track language... does that even matter? 333 ATSUAttributeTag tags[] = {kATSUSizeTag, kATSUStyleRenderingOptionsTag, kATSUFontTag}; 334 ByteCount sizes[] = {sizeof(Fixed), sizeof(ATSStyleRenderingOptions), sizeof(ATSUFontID)}; 335 Fixed size = X2Fix(diagonalLength * FontSizeRatio); 336 Boolean trueval = TRUE; ATSUFontID fid; ATSStyleRenderingOptions rend = kATSStyleApplyAntiAliasing; 337 ATSUAttributeValuePtr vals[] = {&size,&rend,&fid}; 338 ATSUCreateStyle(&glob->textStyle); 339 if (err = ATSUFindFontFromName(SubFontName,strlen(SubFontName),kFontFullName,kFontNoPlatform,kFontNoScript,kFontNoLanguage,&fid)) 340 fprintf(stderr,"Perian: ATSUFindFontFromName error %d for font \"%s\"\n", err, SubFontName); 341 ATSUSetAttributes(glob->textStyle,3, tags, sizes, vals); 342 343 ATSUAttributeTag ext[] = {kATSUQDItalicTag}; 344 ByteCount exs[] = {sizeof(Boolean)}; 345 ATSUAttributeValuePtr exv[] = {&trueval}; 346 ATSUCreateAndCopyStyle(glob->textStyle,&glob->italicStyle); 347 ATSUSetAttributes(glob->italicStyle,1,ext,exs,exv); 348 349 ext[0] = kATSUQDBoldfaceTag; 350 ATSUCreateAndCopyStyle(glob->textStyle,&glob->boldStyle); 351 ATSUSetAttributes(glob->boldStyle,1,ext,exs,exv); 352 353 ATSUCreateAndCopyStyle(glob->italicStyle,&glob->biStyle); 354 ATSUSetAttributes(glob->biStyle,1,ext,exs,exv); 355 } 356 357 if (!glob->textLayout) { 358 ATSUAttributeTag tags[] = {kATSULineFlushFactorTag, kATSULineWidthTag}; 359 ByteCount sizes[] = {sizeof(Fract),sizeof(ATSUTextMeasurement)}; 360 Fract lf = kATSUCenterAlignment; ATSUTextMeasurement w = Long2Fix(myDrp->width); 361 ATSUAttributeValuePtr vals[] = {&lf, &w}; 362 ATSUCreateTextLayout(&glob->textLayout); 363 ATSUSetLayoutControls(glob->textLayout, 2, tags, sizes, vals); 364 } 365 366 // QuickTime doesn't like it if we complain too much 367 if (!c || !glob->textStyle) 368 return noErr; 369 370 char textBuffer[myDrp->dataSize + 1]; 371 372 memcpy(textBuffer, drp->codecData, myDrp->dataSize); 373 textBuffer[myDrp->dataSize] = '\0'; 374 375 ATSUAttributeTag cgc[] = {kATSUCGContextTag}; 376 ByteCount cgc_s[] = {sizeof(CGContextRef)}; 377 ATSUAttributeValuePtr cgc_v[] = {&c}; 378 379 ATSUSetLayoutControls(glob->textLayout, 1, cgc, cgc_s, cgc_v); 380 381 CFMutableStringRef cfsub = CFStringCreateWithCStringMutable(NULL, textBuffer, kCFStringEncodingUTF8); 382 if (cfsub == NULL) 383 return noErr; 384 385 size_t sublen = CFStringGetLength(cfsub); 386 387 UniChar uc[sublen]; 388 389 sublen = ParseSrtStyles(glob, cfsub, uc, sublen); 390 391 ItemCount breakCount; 392 393 ATSUBatchBreakLines(glob->textLayout,kATSUFromTextBeginning,kATSUToTextEnd,lineWidth,&breakCount); // line wrapping 394 ATSUGetSoftLineBreaks(glob->textLayout,kATSUFromTextBeginning,kATSUToTextEnd,0,NULL,&breakCount); 395 UniCharArrayOffset breaks[breakCount+2]; // 0 = beginning, 1...n-1 automatic line breaks, n end of text 396 ATSUGetSoftLineBreaks(glob->textLayout,kATSUFromTextBeginning,kATSUToTextEnd,breakCount,&breaks[1],&breakCount); 397 398 breaks[0] = 0; 399 breaks[breakCount+1] = sublen; 400 401 CGContextSetLineJoin(c, kCGLineJoinRound); 402 CGContextSetLineCap(c, kCGLineCapRound); 403 404 CGContextClearRect(c, CGRectMake(0, 0, myDrp->width, myDrp->height)); 405 CGContextSetRGBFillColor(c, 0,0,0,1); 406 CGContextSetTextDrawingMode(c, kCGTextFill); 407 408 lineHeight -= ShadowDistFix; 409 410 /* Problem here: there can be "holes" between the shadow and the text from thin strokes, which is ugly. 411 I tried using CGContextSetShadow instead of this but it didn't seem to show up, 412 probably because I have to apply it to the stroke pass only... (so don't use Hoefler Text with a shadow) */ 413 414 for (i = breakCount; i >= 0; i--) { // render shadow by drawing black text at an offset 415 int end = breaks[i+1]; 416 417 ATSUDrawText(glob->textLayout, breaks[i], end-breaks[i], Long2Fix(0) + ShadowDistFix, lineHeight); 418 419 ATSUTextMeasurement ascent, descent; ByteCount unused; 420 ATSUGetLineControl(glob->textLayout, breaks[i], kATSULineAscentTag, sizeof(ATSUTextMeasurement), &ascent, &unused); 421 ATSUGetLineControl(glob->textLayout, breaks[i], kATSULineDescentTag, sizeof(ATSUTextMeasurement), &descent, &unused); 422 423 lineHeight += ascent + descent; 424 } 425 426 lineHeight = Long2Fix(25); // XXX constant 427 CGContextSetRGBFillColor(c, 1,1,1,1); // fill white 428 CGContextSetRGBStrokeColor(c, 0,0,0,1); // stroke black 429 CGContextSetLineWidth(c, diagonalLength * BorderSizeRatio); 430 431 for (i = breakCount; i >= 0; i--) { 432 int end = breaks[i+1]; 433 434 CGContextSetTextDrawingMode(c, kCGTextStroke); // render outline (ATSUI doesn't support outside stroke so we fake it) 435 436 ATSUDrawText(glob->textLayout, breaks[i], end-breaks[i], Long2Fix(0), lineHeight); 437 438 ATSUTextMeasurement ascent, descent; ByteCount unused; 439 ATSUGetLineControl(glob->textLayout, breaks[i], kATSULineAscentTag, sizeof(ATSUTextMeasurement), &ascent, &unused); 440 ATSUGetLineControl(glob->textLayout, breaks[i], kATSULineDescentTag, sizeof(ATSUTextMeasurement), &descent, &unused); 441 442 CGContextSetTextDrawingMode(c, kCGTextFill); // render filled text on top of stroke 443 444 ATSUDrawText(glob->textLayout, breaks[i], end-breaks[i], Long2Fix(0), lineHeight); 445 446 lineHeight += ascent + descent; 447 } 448 449 CFRelease(cfsub); 316 SSA_RenderLine(glob->ssa,c,buf,myDrp->width,myDrp->height); 317 318 CFRelease(buf); 450 319 CGContextSynchronize(c); 451 320 CGContextRelease(c); 452 321 453 return err;322 return noErr; 454 323 } 455 324 … … 534 403 return err; 535 404 } 536 537 // XXX the below code is really bad, though it works; it is impossible to scale it to any more tags. needs a real parser538 539 static CFRange *MakeStyleRanges(CFStringRef cfsub, CFStringRef open, CFStringRef close, size_t *rcount, size_t sublen) {540 CFArrayRef begins, ends; size_t tagcount, tagoffset = 0, bs = CFStringGetLength(open), cs = CFStringGetLength(close), i, rcount_ = 0;541 CFRange all = (CFRange){0,sublen};542 begins = CFStringCreateArrayWithFindResults(NULL,cfsub,open,all,0);543 CFRange *ranges = NULL;544 if (begins) {545 ends = CFStringCreateArrayWithFindResults(NULL,cfsub,close,all,0);546 if (ends && CFArrayGetCount(begins) == CFArrayGetCount(ends)) {547 tagcount = CFArrayGetCount(begins);548 ranges = (CFRange*)malloc(sizeof(CFRange[tagcount]));549 for (i = 0; i < tagcount; i++) {550 CFRange *btag, *etag;551 btag = (CFRange*)CFArrayGetValueAtIndex(begins,i);552 etag = (CFRange*)CFArrayGetValueAtIndex(ends,i);553 if (etag->location < btag->location) break; // something's messed up554 ranges[i] = (CFRange){btag->location, etag->location};555 ranges[i].length -= ranges[i].location;556 ranges[i].length -= bs;557 ranges[i].location -= tagoffset;558 rcount_++;559 tagoffset += bs + cs; // tracks sizes of tags we've encountered so far, so the ranges will be valid after we strip them560 }561 CFRelease(ends);562 }563 CFRelease(begins);564 }565 *rcount = rcount_;566 return ranges;567 }568 569 static size_t ParseSrtStyles(TextSubGlobals glob, CFMutableStringRef cfsub, UniChar *uc, size_t sublen)570 {571 int i;572 size_t italiccount=0, boldcount=0;573 CFRange *italics = NULL, *bolds = NULL; CFRange all = (CFRange){0,sublen};574 CFMutableStringRef cfsub_nob = CFStringCreateMutableCopy(NULL,0,cfsub);575 576 /* Because I try to predict what the ranges will be after stripping tags, makestyleranges won't work if other577 to-be-stripped tags are in its string, so we have to remove them... */578 CFStringFindAndReplace(cfsub_nob, CFSTR("<b>"),CFSTR(""),all,0);579 CFStringFindAndReplace(cfsub_nob, CFSTR("</b>"),CFSTR(""),CFRangeMake(0,CFStringGetLength(cfsub_nob)),0);580 italics = MakeStyleRanges(cfsub_nob, CFSTR("<i>"), CFSTR("</i>"), &italiccount, CFStringGetLength(cfsub_nob));581 582 if (italics) {583 CFStringFindAndReplace(cfsub, CFSTR("<i>"),CFSTR(""),all,0);584 all = (CFRange){0,CFStringGetLength(cfsub)};585 CFStringFindAndReplace(cfsub, CFSTR("</i>"),CFSTR(""),all,0);586 sublen = CFStringGetLength(cfsub);587 all = (CFRange){0,sublen};588 }589 bolds = MakeStyleRanges(cfsub, CFSTR("<b>"), CFSTR("</b>"), &boldcount, sublen);590 591 if (bolds) {592 CFStringFindAndReplace(cfsub, CFSTR("<b>"),CFSTR(""),all,0);593 all = (CFRange){0,CFStringGetLength(cfsub)};594 CFStringFindAndReplace(cfsub, CFSTR("</b>"),CFSTR(""),all,0);595 sublen = CFStringGetLength(cfsub);596 all = (CFRange){0,sublen};597 }598 599 CFStringGetCharacters(cfsub, all, uc);600 ATSUSetTextPointerLocation(glob->textLayout,uc,kATSUFromTextBeginning,kATSUToTextEnd,sublen);601 ATSUSetTransientFontMatching(glob->textLayout,TRUE);602 603 ATSUSetRunStyle(glob->textLayout,glob->textStyle,kATSUFromTextBeginning,kATSUToTextEnd);604 if (italics) {605 for (i = 0; i < italiccount; i++) ATSUSetRunStyle(glob->textLayout,glob->italicStyle,italics[i].location,italics[i].length);606 }607 if (bolds) {608 for (i = 0; i < boldcount; i++) ATSUSetRunStyle(glob->textLayout,glob->boldStyle,bolds[i].location,bolds[i].length);609 }610 if (italics && bolds) {611 // <i><b></b></i>612 for (i = 0; i < italiccount; i++) {613 CFRange ir = italics[i]; int j;614 for (j = 0; j < boldcount; j++) {615 CFRange br = bolds[j];616 if (br.location > ir.location && br.location < (ir.location + ir.length)) {617 ATSUSetRunStyle(glob->textLayout,glob->biStyle,br.location,min(br.length, ir.length - (br.location - ir.location)));618 }619 }620 }621 622 // <b><i></i></b>623 for (i = 0; i < boldcount; i++) {624 CFRange br = bolds[i]; int j;625 for (j = 0; j < italiccount; j++) {626 CFRange ir = italics[j];627 if (ir.location > br.location && ir.location < (br.location + br.length)) {628 ATSUSetRunStyle(glob->textLayout,glob->biStyle,ir.location,min(ir.length, br.length - (ir.location - br.location)));629 }630 }631 }632 }633 if (italics) free(italics);634 if (bolds) free(bolds);635 CFRelease(cfsub_nob);636 return sublen;637 }trunk/TextSubCodec.r
r313 r314 59 59 // These flags specify information about the capabilities of the component 60 60 // Works with 1-bit, 8-bit, 16-bit and 32-bit Pixel Maps 61 #define kTextSubDecoFlags ( codecInfoDoes32 | codecInfoDoes16 | codecInfoDoes8 | codecInfoDoes1 | codecInfoDoesSpool )61 #define kTextSubDecoFlags ( codecInfoDoes32 | codecInfoDoes16 | codecInfoDoes8 | codecInfoDoes1 | codecInfoDoesSpool | cmpThreadSafe) 62 62 63 63 // These flags specify the possible format of compressed data produced by the component 64 64 // and the format of compressed files that the component can handle during decompression 65 65 // The component can decompress from files at 1-bit, 8-bit, 16-bit, 24-bit and 32-bit depths 66 #define kTextSubFormatFlags ( codecInfoDepth32 | codecInfoDepth24 | codecInfoDepth16 | codecInfoDepth8 | codecInfoDepth1 )66 #define kTextSubFormatFlags ( codecInfoDepth32 | codecInfoDepth24 | codecInfoDepth16 | codecInfoDepth8 | codecInfoDepth1 | cmpThreadSafe) 67 67 68 68 // Component Description
