2014년 11월 21일 금요일

Cocos2d-x Label에서 긴 문자열을 짧게 잘라 생략(Ellipse) 기호 붙이기

Cocos2d-x Label에서 긴 문자열을 제한된 폭으로 표시해야할 때가 종종있는데 생각보다 구현할 때 고려해야할 사항이 많다.


Cocos2d-x는 내부적으로 스트링 처리에 UTF8로 사용하기 때문에 유니코드에 대응해야하고 Label이 트루타입 폰트로 그려지는 폭을 Kerning을 고려하여 계산해야 하며, FreeType 폰트 캐쉬와 glyph 정보로 그리는 위치 또한 고려해야 한다.

아래의 코드는 Label의 폭을 limitWidth로 제한하고 생략 기호로 "..."를 붙이는 예제이다. 코드의 간략화를 위해서 함수 하나에 담았다.

void shortenLabelTTFString(Label *label, int limitWidth) {
  std::u16string utf16EllipseString;
  StringUtils::UTF8ToUTF16("...", utf16EllipseString);

  std::u16string utf16String;
  StringUtils::UTF8ToUTF16(label->getString(), utf16String);

  auto ttfConfig = label->getTTFConfig();
  auto ttfAtlas = FontAtlasCache::getFontAtlasTTF(ttfConfig);
  ttfAtlas->prepareLetterDefinitions(utf16EllipseString);

  int letterCount = 0;
  auto kerning = ttfAtlas->getFont()->getHorizontalKerningForTextUTF16(
    utf16EllipseString, letterCount);

  int ellipseWidth = 0;
  FontLetterDefinition letterDef = {0};

  for (int i = 0; i < utf16EllipseString.length(); i++) {
    ttfAtlas->getLetterDefinitionForChar(utf16EllipseString[i], letterDef);
    ellipseWidth += letterDef.xAdvance + kerning[i] + label->getAdditionalKerning();
  }

  if (kerning != nullptr)
    delete [] kerning;

  if (ellipseWidth > limitWidth) {
    label->setString("...");
    return;
  }

  letterCount = 0;
  kerning = ttfAtlas->getFont()->getHorizontalKerningForTextUTF16(
    utf16String, letterCount);

  std::string result;
  std::u16string utf16Result;
  int width = ellipseWidth;

  for (int i = 0; i < utf16String.length(); i++) {
    ttfAtlas->getLetterDefinitionForChar(utf16String[i], letterDef);

    if (width + letterDef.xAdvance > limitWidth) {
      StringUtils::UTF16ToUTF8(utf16Result, result);
      label->setString(result + "...");
      break;
    }

    width += letterDef.xAdvance + kerning[i] + label->getAdditionalKerning();

    utf16Result += utf16String[i];
  }

  if (kerning != nullptr)
    delete [] kerning;
}

댓글 없음:

댓글 쓰기