public void RecreateQuadBatch() { if (font == null) { return; } // // first, cut into lines // // the line list is a list of pair <first char index, line text> lineList.Clear(); { int lineStartIndex = 0; int lineBreakIndex = 0; int length = text.Length; while (lineBreakIndex < length) { char c = text[lineBreakIndex]; bool antiSlashN = (c == '\n'); bool antiSlashR = (c == '\r'); if (antiSlashN || antiSlashR) { lineList.Add(new LineDescriptor(lineStartIndex, (lineBreakIndex - lineStartIndex), false)); if (antiSlashR && // if \r is found (lineBreakIndex < length - 1) && (text[lineBreakIndex + 1] == '\n')) // skip possible \n following it { lineBreakIndex++; } lineStartIndex = lineBreakIndex + 1; // start index of next line } lineBreakIndex++; } lineList.Add(new LineDescriptor(lineStartIndex, (text.Length - lineStartIndex), false)); // int startIndex = 0; // int lineBreakIndex = -1; // while ( ( lineBreakIndex = text.IndexOf ( '\n', startIndex ) ) != -1 ) // { // lineList.Add ( new LineDescriptor ( startIndex, lineBreakIndex - startIndex, false ) ); // startIndex = lineBreakIndex + 1; // start index of next line // } // lineList.Add ( new LineDescriptor ( startIndex, text.Length - startIndex, false ) ); } #if UNITY_EDITOR errorMessage = null; #endif // // new create lines geometry // quadBatch.setSize(text.Length); int lineIndex = 0; Vector3 linePosition = new Vector3(position.x, position.y, position.z); int quadStartIndex = 0; while (lineIndex < lineList.Count) { // TODO : optim : "lineList.get( lineIndex )" as local ? int startIndex = lineList[lineIndex].charStartIndexInText; int length = lineList[lineIndex].charCount; bool issuedFromLineCut = lineList[lineIndex].issuedFromLineCut; CreateTextGeometryResult result = createTextGeometry(linePosition, text, startIndex, length, quadStartIndex, issuedFromLineCut, scale, lineMaxWidth, preserveWords); if ((result.processedCharCount > 0) && (result.processedCharCount != length)) // line not fully processed ? (see line break near end of // createTextGeometry method) { // insert remaining of the line lineList.Insert(lineIndex + 1, new LineDescriptor(startIndex + result.processedCharCount, length - result.processedCharCount, true)); } // updates line description lineList[lineIndex] = new LineDescriptor(startIndex + result.spaceRemovedAtBeginning, result.processedCharCount - result.spaceRemovedAtBeginning, quadStartIndex, result.renderedCharCount, issuedFromLineCut); quadStartIndex += result.renderedCharCount; // moves into the quad buffer if (fontData != null) { if (letterSpacingMode == LetterSpacingMode.Add) { linePosition.y -= ((fontData.LineHeight + letterSpacing.y) * scale); } else { linePosition.y -= (letterSpacing.y * scale); } } // TODO : might be an option instead of lineHeight : result.boundingRectMax.Y // result.boundingRectMin.Y; lineIndex++; } #if UNITY_EDITOR if (errorMessage != null) { Debug.LogError("Invalid char found in text to render :\n" + errorMessage); } #endif quadBatch.setSize(quadStartIndex); // // align each line's geometry // bounds = new Rect(0, 0, -1, -1); if (lineList.Count > 1) { float lineWidth = lineMaxWidth; if ( (lineMaxWidth == float.MaxValue) || (alignment == TextAlignment.Center) // we also want real width when centering ) { bounds = quadBatch.getBounds(); lineWidth = bounds.width; } switch (alignment) { case TextAlignment.Left: // default alignment break; case TextAlignment.Center: for (int i = 0; i < lineList.Count; i++) { LineDescriptor lineDesc = lineList[i]; Rect boundingRect = quadBatch.getBounds(lineDesc.quadStartIndexInBatch, lineDesc.quadCount); quadBatch.translate(lineDesc.quadStartIndexInBatch, lineDesc.quadCount, new Vector3((lineWidth - boundingRect.width) / 2, 0, 0)); } break; case TextAlignment.Right: for (int i = 0; i < lineList.Count; i++) { LineDescriptor lineDesc = lineList[i]; Rect boundingRect = quadBatch.getBounds(lineDesc.quadStartIndexInBatch, lineDesc.quadCount); quadBatch.translate(lineDesc.quadStartIndexInBatch, lineDesc.quadCount, new Vector3(lineWidth - boundingRect.width, 0, 0)); } break; case TextAlignment.Justify: for (int i = 0; i < lineList.Count; i++) { LineDescriptor lineDesc = lineList[i]; // line is cut if next line is issued from the cut bool lineWasCut = (i < lineList.Count - 1) && (lineList[i + 1].issuedFromLineCut); // TODO : add right/centered aligned justify as Photoshop do ? if (lineWasCut) // only align line that are cut, others are already left aligned { // get space count in that line List <int> charCountsToProcess = new List <int> (); int noSpaceCharCount = 0; for (int j = 0; j < lineDesc.charCount; j++) { char charToRender = text[lineDesc.charStartIndexInText + j]; if (charToRender == 0xA0) { charToRender = (char)0x20; // convert unbreakable space into space } if (charToRender == 0x20) { charCountsToProcess.Add(noSpaceCharCount); noSpaceCharCount = 0; } else { noSpaceCharCount++; } } charCountsToProcess.Add(noSpaceCharCount); if (charCountsToProcess.Count > 1) { Rect boundingRect = quadBatch.getBounds(lineDesc.quadStartIndexInBatch, lineDesc.quadCount); float spaceToAdd = (lineWidth - boundingRect.width) / (charCountsToProcess.Count - 1); if (spaceToAdd > 0) { // distribute translation to each spacing int quadIndex = lineDesc.quadStartIndexInBatch; int quadCount = 0; for (int j = 0; j < charCountsToProcess.Count; j++) { quadCount = charCountsToProcess[j]; if (quadCount > 0) { // don't process first word, that stay left aligned if (j > 0) { // quadBatch.colorize ( quadIndex, 1, Color.RED ); // DEBUG : colorize first letter in red quadBatch.translate(quadIndex, quadCount, new Vector3(spaceToAdd * j, 0, 0)); } quadIndex += charCountsToProcess[j]; } quadIndex++; // skip space char, that have not to be processed } } } } } break; } } if (bounds.width == -1 || bounds.height == -1) // might have been already computed above, don't do it twice { bounds = quadBatch.getBounds(); } // if ( trimTopLeftSpace ) // remove empty top left space created by glyph xoffset/yoffset // { // position.x = quadBounds.x; // position.y = quadBounds.y; // bounds.x = quadBounds.x; // bounds.y = quadBounds.y; // } float dx = 0; switch (anchor) { case TextAnchor.UpperLeft: case TextAnchor.MiddleLeft: case TextAnchor.LowerLeft: // dx = - bounds.width / 2; dx = -bounds.xMin; break; case TextAnchor.UpperCenter: case TextAnchor.MiddleCenter: case TextAnchor.LowerCenter: // dx = - bounds.width / 2; dx = -bounds.center.x; break; case TextAnchor.UpperRight: case TextAnchor.MiddleRight: case TextAnchor.LowerRight: // dx = - bounds.width; dx = -bounds.xMax; break; } // float dy = -( bounds.y + bounds.height ); float dy = 0; switch (anchor) { case TextAnchor.UpperCenter: case TextAnchor.UpperLeft: case TextAnchor.UpperRight: // dy += bounds.height / 2; dy = -bounds.yMax; break; case TextAnchor.MiddleCenter: case TextAnchor.MiddleLeft: case TextAnchor.MiddleRight: // dy += bounds.height / 2; dy = -bounds.center.y; break; case TextAnchor.LowerCenter: case TextAnchor.LowerLeft: case TextAnchor.LowerRight: // dy += bounds.height; dy = -bounds.yMin; break; } quadBatch.translate(new Vector3(dx, dy, 0)); }