저자 | 록타르 오가르
가이드
이 기사는 Duka Editing APP 텍스트 템플릿의 개발 관행을 기반으로 비디오 편집 시나리오에서 정적 텍스트 템플릿 렌더링 기능의 기술 솔루션을 공유합니다. 서식 있는 텍스트 렌더링 솔루션의 상위 집합인 이 기술 솔루션은 복잡한 서식 있는 텍스트 렌더링이 필요한 다른 시나리오로 확장될 수 있습니다.
전체 텍스트는 6745단어이며, 예상 읽기 시간은 17분입니다.
살짝 엿보기
텍스트 템플릿 효과 표시: △Duka 편집에서 텍스트 템플릿 적용
01 배경
동영상 제작 도구의 핵심 경쟁력 중 하나는 다양한 동영상 자료, 오디오 자료, 스티커 자료 등 풍부한 자료 라이브러리입니다. 텍스트 템플릿도 필수적인 부분입니다. 텍스트 템플릿은 풍부한 텍스트 편집 기능을 제공하여 사용자가 비디오에 더 아름다운 스타일의 텍스트 정보를 추가할 수 있도록 하여 비디오 자료의 다양성을 높입니다. 또한 미리 설정된 스타일을 통해 사용자는 자신에게 맞는 텍스트 템플릿을 보다 편리하게 선택할 수 있으므로 재료 선택 시간을 절약하고 사용자 경험을 향상시킬 수 있습니다. Duka 초기 버전에서는 텍스트 템플릿의 재질 유형을 제공하지 않았습니다. 제품의 경쟁력을 높이고 재료의 보급률을 높이기 위해 특정 연구 개발 작업을 수행하고 마침내 텍스트 템플릿 재료를 출시했습니다. 이러한 텍스트 템플릿 자료는 사용자의 요구를 충족시킬 수 있을 뿐만 아니라 사용자에게 더 창의적인 영감과 아이디어를 제공할 수 있습니다. 동시에 사용자가 최신 최고 품질의 재료 리소스를 얻을 수 있도록 재료 라이브러리를 지속적으로 업데이트하고 최적화하고 있습니다. 텍스트 템플릿이 표시해야 하는 그래픽 및 텍스트 스타일은 비교적 복잡합니다. Duka 텍스트 템플릿에서 지원하는 기능은 다음과 같습니다.
02 전반적인 디자인
우리가 구축한 머티리얼 플랫폼을 기반으로 새로운 형태의 텍스트 템플릿을 추가하고 머티리얼 플랫폼에서 머티리얼 편집, 미리보기, 설정, 온라인 기능을 제공했습니다. 재료 제작과 미리보기를 결합하여 동일한 인터페이스에서 방금 조정한 효과를 미리보고 Duka의 글꼴 라이브러리와 직접 일치시키고 이미지 리소스를 직접 수정할 수 있습니다. 텍스트 템플릿을 사용하여 배경 이미지를 변경하고 획을 추가하면 다른 텍스트 템플릿을 직접 제작할 수 있어 재사용성이 높은 소재 제작 방식입니다. 이 템플릿을 게시하고 효과 다이어그램을 내보낸 후 검토할 대기열에 들어가고 검토 후 구성하고 실행할 수 있습니다. 지금까지 361개의 텍스트 템플릿 세트를 시작했으며 [Material Production] - [Material Platform Preview] - [Material Delivery and Client Loading] - [Client Rendering]의 전체 링크를 완료했습니다.
03 기능 구현
3.1 재료 생산
현재 비디오 편집 업계의 주류 자료 형식은 일반적으로 리소스 파일과 구성 파일(설명 파일)을 채택합니다. 이 중 리소스 파일에는 이미지 리소스와 글꼴 파일이 포함되며 구성 파일은 주로 텍스트 템플릿의 조판 속성 및 렌더링 매개 변수를 설명하는 데 사용됩니다. 이 방식의 장점은 프로덕션 측에서는 특정 필드를 통해 해당 특성을 설명하기만 하면 되고, 렌더링 측에서는 제시할 수 있다는 것입니다. 이 방법은 매우 유연하며 특정 시나리오의 요구 사항에 따라 간단한 것부터 복잡한 것까지 관련 기능을 반복할 수 있으며 구현 비용은 상대적으로 낮습니다. 다만 재료 제작의 형태가 커스터마이즈되어 있어 일정량의 디자인과 학습비가 필요하다는 단점이 있다. 또한 Photoshop (PS)을 예로 들어 전문 디자인 소프트웨어를 제작하는 또 다른 방법이 있습니다. PS는 다양한 데이터 구조를 포함하여 상대적으로 성숙한 파일 형식 문서를 보유하고 있으며 PSD 파일을 직접 사용하여 렌더링을 위한 그래픽 및 텍스트 속성을 분석할 수 있습니다. 이 방법의 장점은 재료 생산 방법이 보다 일반적이고 디자인에 학습 비용이 거의 없다는 것입니다. 하지만 단점은 여러 개의 텍스트 상자를 겹쳐서 얻는 효과인 다중 레이어 그림자 효과와 같이 PSD만으로는 우리가 필요로 하는 일부 기능을 충족할 수 없다는 것입니다. 텍스트 내용을 수정할 때 이러한 텍스트 상자를 동시에 수정해야 하므로 그룹으로 처리해야 하며 논리가 더 복잡해집니다. 설정 파일로 기술하면 복잡한 로직 처리 없이 바로 다층 드로잉이 가능하다. 비즈니스 ROI의 타당성과 단기 온라인 기능을 고려하여 첫 번째 방법을 채택하였고, Butter Camera 팀의 재료 제작 표준을 참고하여 조판 속성과 렌더링 매개변수를 설명하기 위한 JSON 구조를 설계했습니다.
3.2 사이드 렌더링
비디오 편집 시나리오에서 텍스트 처리에는 텍스트 레이아웃과 텍스트 그리기의 두 부분이 필요합니다. 텍스트 조판의 경우 iOS 플랫폼은 조판 처리를 위해 CoreText의 기본 프레임워크를 사용하는 반면 Android는 FontMetrics 등을 통해 기본 FreeType의 글꼴 처리 결과를 얻을 수 있습니다. 텍스트 조각이 전체적으로 서식을 지정하든 각 텍스트의 위치를 개별적으로 계산하든 전체 처리의 성능 소비는 동일합니다. 텍스트 그리기 측면에서 성능 오버헤드와 개발 비용 사이의 균형을 맞출 필요가 있습니다. 결국 iOS는 QuartzCore 프레임워크를 채택했고 Android는 텍스트 그리기에 Canvas를 사용했습니다. 이러한 방식으로 미리보기 시 텍스트가 뷰에 직접 표시되어 실시간 편집 및 미리보기를 지원합니다. 동영상을 내보내야 하는 경우 스티커로 처리하여 동영상에 추가합니다. iOS를 예로 들면 Huazi 구성 요소 아키텍처는 다음과 같습니다.
3.3 설명 파일 디자인
위에서 언급한 바와 같이 우리는 json 파일을 사용하여 텍스트 템플릿의 조판 속성과 렌더링 매개변수를 설명하고 리소스가 클라이언트에 전달된 후 클라이언트는 해당 매개변수를 구문 분석하여 텍스트 템플릿의 조판 및 최종 효과 프레젠테이션을 수행합니다. . 설명 파일에는 다음 내용이 포함됩니다.
(1) 텍스트 레이아웃 속성
- 베이스라인: 캐릭터 베이스라인, 베이스라인은 가상선
- ascent: 글리프의 가장 높은 지점에서 기준선까지의 권장 거리
- 하강: 글리프의 가장 낮은 지점에서 기준선까지의 권장 거리
- 선행: 줄 간격, 즉 이전 줄의 하강과 다음 줄의 상승 사이의 거리
- 사전 너비: 원점에서 다음 글리프 원점까지의 거리
- 왼쪽 베어링: 원점에서 글리프의 가장 왼쪽까지의 거리
- 오른쪽 베어링: 글리프의 가장 오른쪽에서 다음 글리프의 원점까지의 거리
- 테두리 상자: 글리프를 포함하는 가장 작은 사각형
- x 높이: 일반적으로 소문자 x의 가장 높은 지점에서 기준선까지의 권장 거리를 나타냅니다.
- 캡 높이: 일반적으로 H 또는 I의 가장 높은 지점에서 기준선까지의 권장 거리를 나타냅니다.
(2) 텍스트 개체의 조합 아래 그림은 두 개의 텍스트 그리기 영역을 조합한 예입니다.
3.4 조판 및 드로잉 프로세스
텍스트 템플릿에서 조판과 그리기는 분리할 수 없으며 처리를 위해 코드 논리에 분산되어야 합니다. 우리의 그리기 단계는 맨 아래 레이어에서 맨 위 레이어로 레이어를 그리는 것이지만 일부 그리기 프로세스는 많은 시간을 소비하기 때문에 메인 스레드 차단을 피하기 위해 비동기 그리기 기술을 사용합니다. 비동기 그리기 프로세스에서 우리는 사용자의 정상적인 사용에 영향을 미치지 않도록 처리를 위해 백그라운드 스레드에 일부 시간 소모적 그리기 프로세스를 넣습니다. 동시에 비동기 그리기 과정에서 텍스트 레이아웃도 계산하여 후속 그리기 프로세스에서 텍스트 관련 정보를 빠르게 얻을 수 있으므로 그리기 효율성이 향상됩니다. 일반적으로 비동기 그리기 방식을 채택함으로써 사용자에게 과도한 간섭을 주지 않고 텍스트 템플릿의 조판 및 그리기 프로세스가 원활하게 진행되도록 할 수 있습니다.
04 어려움과 도전
1. 다중 단자 효과 정렬
우리 프로젝트는 웹, iOS, 안드로이드 렌더링을 지원하지만 일반적인 크로스엔드 솔루션은 하단 레이어에서 OpenGL 렌더링을 사용해야 하고 당시 인적 자원 제약으로 인해 단기간에 달성하기 어려웠습니다. 따라서 다중 단말 독립 렌더링 방식을 채택했으며 각 플랫폼마다 독립적인 렌더링 솔루션이 있습니다. 이 방법은 또한 문제를 가져옵니다. 다른 플랫폼의 렌더링 효과는 다를 것입니다. 이 문제를 해결하려면 다중 터미널 효과의 일관성을 보장해야 합니다. 기술적인 차원에서 차이를 좁히기 어렵기 때문에 규칙과 기준을 통일해 일관성을 유지하기로 했다. json 파일의 형식을 설계할 때 텍스트를 기준으로 한 텍스트 장식의 초기 위치를 왼쪽 상단에 맞출지 중앙에 맞출 것인지, 좌표의 원점을 통일. 동시에 해당 매개변수가 사용하는 단위를 통일하여 최종 렌더링 효과의 일관성을 최대한 보장합니다. 이러한 방식으로 렌더링되는 플랫폼에 관계없이 일관된 결과를 얻을 수 있으므로 사용자 경험이 보다 균일하고 우수해집니다.
2. 텍스트 사전 조판
텍스트 템플릿에서 글꼴 크기를 고정 글꼴 크기와 비고정 글꼴 크기의 두 가지 유형으로 나눕니다. 고정된 글꼴 크기의 경우 텍스트 레이아웃 계산 및 그리기를 직접 수행할 수 있습니다. 그러나 글꼴 크기가 고정되지 않은 글꼴의 경우 현재 텍스트 콘텐츠에서 해당 글꼴 크기를 계산하기 위해 사전 조판 계산을 수행해야 합니다. 여기서 일부 솔루션은 이분법을 사용하여 먼저 큰 글꼴 크기 값을 설정하고 0에서 글꼴 크기 값의 범위 내에서 점차 올바른 값에 접근하지만 실제로는 텍스트의 기본 논리 및 제한 사항과 결합하여 불필요한 시간 손실이 발생합니다. 레이아웃 조건, 우리는 O(1)에 가까운 시간 복잡도를 가진 알고리즘을 만들 수 있습니다: 최대 문자 높이 계산 -> 최소 문자 높이 계산 -> 가장 긴 줄의 문자 높이 계산 -> 그에 따른 문자 높이 계산 줄 수 -> 최종 문자 높이 계산 -> 문자 높이에 따라 글꼴 크기를 계산합니다. iOS에서 CoreText 조판 기술을 사용할 때, 소수의 경우에 채울 수 없는 텍스트가 자동으로 잘려지는 경우가 있는데 계산된 글꼴 크기를 직접 사용하면 일부 텍스트가 잘릴 수 있으므로 위의 결과는 다음과 같아야 합니다. 추정 결과로 사용되며 경로를 채울 수 있을 때까지 크기가 단계적으로 1씩 축소됩니다.
CGFloat ascent, descent;
UIFont *font = [self.calFont fontWithSize:size];
CTFontRef fontRefMeasure = (__bridge CTFontRef)font;
[attrString addAttribute:(id)kCTFontAttributeName value:(__bridge id)fontRefMeasure range:NSMakeRange(0, attrString.length)];
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)attrString);
CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
//calculate max font size
CGFloat calFontHeight = MIN(height, width);
self.maxFontHeight = calFontHeight;
//calculate min font size
CGFloat maxLine = self.document.maxLine * BDTZBigFontDataOriginScale;
if (maxLine <= 0) {
maxLine = 1;
}
calFontHeight = [self itemWidth] / (maxLine + (maxLine - 1) * (self.leadingRatio * BDTZBigFontDataOriginScale - 1));
self.minFontHeight = MIN(self.maxFontHeight, calFontHeight);
// longest column
int64_t n = 0;
NSArray *strArray = [self.document.content componentsSeparatedByString:@"\n"];
NSString *measureStr = self.document.content;
// 这里是针对多行文本的处理,循环次数为行数,量级较小(一般为1-10行)
for (NSString *str in strArray) {
if (str.length > n) {
n = str.length;
measureStr = str;
}
}
CGFloat fontWidthRatioOrigin = (self.document.fontWidthRatio * BDTZBigFontDataOriginScale);
CGFloat trackingRatio = (self.document.trackingRatio * BDTZBigFontDataOriginScale) * (ascent + descent) / ascent;
CGRect rect = [@"我" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.calFont} context:nil];
CGFloat fontWidthRatio = fontWidthRatioOrigin > 0 ? fontWidthRatioOrigin * (ascent + descent) / ascent : rect.size.width / rect.size.height;
CGFloat fontHeight = width / (n * fontWidthRatio + n * trackingRatio);
if (strArray.count > 1) {
//calculate font size accoring column count
calFontHeight = [self itemWidth] / (strArray.count + (strArray.count - 1) * (self.leadingRatio * BDTZBigFontDataOriginScale - 1));
//take the min value of the above two font sizes
fontHeight = MIN(fontHeight, calFontHeight);
}
if (fontHeight > self.maxFontHeight) {
fontHeight = self.maxFontHeight;
} else if (fontHeight < self.minFontHeight) {
fontHeight = self.minFontHeight;
}
CGFloat calSize = fontHeight;
calFontHeight = [self calculateFontHeightSize:calSize];
calSize = floorf(calSize / (calFontHeight * (ascent + descent) / ascent) * calSize);
//exact value, calculate repeatedly with frame until the path can be filled
//根据估算结果,将size逐次减1,直至能填入path,此处代码省略
if (calSize <= 0) {
return calSize;
}
calFontHeight = [self calculateFontHeightSize:calSize];
self.fontHeight = calFontHeight * (ascent + descent) / ascent;
self.font = [self.calFont fontWithSize:calSize];
3. 드로잉 성능
텍스트 템플릿의 실시간 미리보기는 자주 그려야 하므로 CPU에 큰 부담을 주어 지연이 발생합니다. 이 문제를 해결하기 위해서는 비동기 드로잉을 사용해야 합니다. 특히 각 사용자 작업의 텍스트 콘텐츠 상태를 저장하기 위해 비동기 직렬 대기열을 만들 수 있습니다. 사용자가 수정할 때마다 우리는 현재 상태를 대기열에 넣고 백그라운드 스레드가 비동기적으로 그릴 때까지 기다립니다. 이전 상태가 그려진 후 모든 상태가 그려질 때까지 대기열에서 그릴 다음 상태를 꺼냅니다. 이와 같이 메인 쓰레드가 멈추는 것을 방지하기 위해 비동기 드로잉을 구현하고, 사용자가 각각 수정한 결과를 완벽하게 제시합니다. 텍스트 템플릿의 사용자 경험을 더욱 최적화하기 위해 비동기 그리기 외에도 캐싱 메커니즘을 사용하여 렌더링 성능을 향상시킬 수도 있습니다. 사용자가 텍스트 템플릿에서 작업할 때 텍스트 보기는 다시 레이아웃되고 그려집니다.전체 템플릿을 매번 다시 그리면 많은 CPU 리소스를 차지할 뿐만 아니라 사용자 경험도 감소합니다. 따라서 캐시를 사용하여 그려진 템플릿 보기를 저장할 수 있으며 사용자가 텍스트 내용을 수정하면 전체 보기가 아닌 수정된 부분만 다시 그려야 합니다. 이러한 방식으로 리소스 소비를 줄이고 시스템 응답성을 향상시키면서 렌더링 성능을 향상시킬 수 있습니다.
4. 메모리 최적화
우리의 텍스트 템플릿은 주로 비디오 편집 시나리오에서 사용되며 사용자는 특정 상황에 따라 텍스트 템플릿을 확대하거나 축소해야 합니다. 순수한 벡터 드로잉 새로 고침 방법을 사용하는 경우 사용자가 텍스트 템플릿을 어느 정도 확대하면 메모리 사용량이 매우 높아집니다. 또한 저희 사용자들은 보통 에디터에서 스티커, 특수효과, 자막 등 많은 자료를 추가하는데, 이러한 자료들은 각각 많은 메모리를 차지하며 일정 기간 사용 후 메모리가 증가할 가능성이 높습니다. OOM 임계값에 도달하여 응용 프로그램이 충돌합니다. 따라서 현재 단일 텍스트 템플릿의 메모리를 20M 미만으로 제어하고 텍스트 템플릿에 필요한 너비 및 높이 임계값을 계산하여 다양한 비디오 너비 및 높이에 따라 예상 선명도를 달성하여 선명도 간의 균형을 달성합니다. 및 메모리 사용 각 텍스트 템플릿에는 서로 다른 균형 매개 변수가 있습니다. 이는 메모리 최적화 세부 사항일 뿐이지만 자료의 메모리 사용량과 온라인 OOM 속도를 제어하는 데 큰 역할을 했습니다.
05 에필로그
비디오 편집 세계에서 서식 있는 텍스트 렌더링은 다소 복잡한 프로세스입니다. 최종 렌더링에 관한 한 모든 것에 맞는 단일 솔루션은 없으며 특정 장면에 가장 적합한 솔루션만 있을 뿐입니다. 텍스트 템플릿 렌더링 솔루션을 설계하고 구현하는 과정에서 고려해야 할 많은 세부 사항이 있습니다. 동시에 PS 및 Figma와 같은 주류 디자인 소프트웨어의 파일 형식에 대한 심층적인 이해도 필요합니다. 우리 팀은 보다 일반적인 서식 있는 텍스트 렌더링 시나리오를 충족할 수 있는 정적 텍스트 템플릿과 관련된 기술 솔루션을 제공합니다. 전반적인 아이디어는 텍스트 레이아웃과 그리기에 대해 거의 비슷합니다. 이 기사에서는 독자가 기술 솔루션을 더 잘 이해할 수 있도록 기본 개념과 서식 있는 텍스트 기능을 소개합니다. 그러나 우리가 제공하는 솔루션을 사용하더라도 구현 시 많은 세부 사항을 고려해야 합니다. 글꼴 크기, 색상, 정렬, 문자 간격, 줄 간격 및 기타 요소를 고려하여 렌더링된 서식 있는 텍스트가 예상한 효과를 얻을 수 있는지 확인해야 합니다. 따라서 리치 텍스트 렌더링의 최상의 효과를 얻기 위해서는 설계 및 구현에 많은 시간과 노력을 투자해야 합니다. 리치 텍스트의 특성과 디자인 원칙을 깊이 이해해야만 사용자에게 고품질 비디오 편집 경험을 제공할 수 있습니다.
--끝--
추천 읽기: 이벤트 시나리오에서 그래프 알고리즘의 부정 행위 방지 적용에 대해 이야기하기
서버리스: 개인화된 서비스 초상화를 기반으로 한 유연한 확장 사례