/** * Copyright 2008 - 2015 The Loon Game Engine Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loon * @author cping * @email:javachenpeng@yahoo.com * @version 0.5 */ package loon.robovm; import loon.font.TextFormat; import loon.font.TextLayout; import loon.font.TextWrap; import loon.geom.RectBox; import loon.jni.CTFrame; import loon.jni.CTFramesetter; import org.robovm.apple.corefoundation.CFArray; import org.robovm.apple.corefoundation.CFRange; import org.robovm.apple.coregraphics.CGAffineTransform; import org.robovm.apple.coregraphics.CGBitmapContext; import org.robovm.apple.coregraphics.CGPath; import org.robovm.apple.coregraphics.CGRect; import org.robovm.apple.coretext.CTFont; import org.robovm.apple.coretext.CTLine; import org.robovm.apple.foundation.NSAttributedString; import org.robovm.apple.uikit.NSAttributedStringAttributes; import org.robovm.apple.uikit.UIColor; import org.robovm.apple.uikit.UIFont; class RoboVMTextLayout extends TextLayout { public static RoboVMTextLayout layoutText(RoboVMGraphics gfx, final String text, TextFormat format) { final CTFont font = RoboVMFont.resolveFont(format.font); NSAttributedStringAttributes attribs = createAttribs(font); CTLine line = CTLine.create(new NSAttributedString(text, attribs)); return new RoboVMTextLayout(gfx, text, format, font, line); } public static RoboVMTextLayout[] layoutText(RoboVMGraphics gfx, String text, TextFormat format, TextWrap wrap) { text = normalizeEOL(text); final CTFont font = RoboVMFont.resolveFont(format.font); NSAttributedStringAttributes attribs = createAttribs(font); CFArray lines = wrapLines(new NSAttributedString(text, attribs), wrap.width); RoboVMTextLayout[] layouts = new RoboVMTextLayout[(int) lines.size()]; for (int ii = 0; ii < layouts.length; ii++) { CTLine line = lines.get(ii, CTLine.class); CFRange range = line.getStringRange(); String ltext = text.substring((int) range.getLocation(), (int) (range.getLocation() + range.getLength())); layouts[ii] = new RoboVMTextLayout(gfx, ltext, format, font, line); } return layouts; } private static NSAttributedStringAttributes createAttribs(CTFont font) { NSAttributedStringAttributes attribs = new NSAttributedStringAttributes(); attribs.setFont(font.as(UIFont.class)); return attribs; } private static void addStroke(NSAttributedStringAttributes attribs, CTFont font, float strokeWidth, int strokeColor) { double strokePct = 100 * strokeWidth / font.getSize(); attribs.setStrokeWidth(strokePct); attribs.setStrokeColor(toUIColor(strokeColor)); } private static CFArray wrapLines(NSAttributedString astring, float wrapWidth) { CTFramesetter fs = CTFramesetter.create(astring); try { CGPath path = CGPath.createWithRect(new CGRect(0, 0, wrapWidth, Float.MAX_VALUE / 2), CGAffineTransform.Identity()); CTFrame frame = fs.createFrame(new CFRange(0, 0), path, null); return frame.getLines(); } finally { fs.dispose(); } } private final CTFont font; private CTLine fillLine; private int fillColor; private CTLine strokeLine; private float strokeWidth; private int strokeColor; private RoboVMTextLayout(RoboVMGraphics gfx, String text, TextFormat format, CTFont font, CTLine fillLine) { super(text, format, computeBounds(font, fillLine.getImageBounds(gfx.scratchCtx)), (float) (font .getAscent() + font.getDescent())); this.font = font; this.fillLine = fillLine; } @Override public float ascent() { return (float) font.getAscent(); } @Override public float descent() { return (float) font.getDescent(); } @Override public float leading() { return (float) font.getLeading(); } void stroke(CGBitmapContext bctx, float x, float y, float strokeWidth, int strokeColor) { if (strokeLine == null || strokeWidth != this.strokeWidth || strokeColor != this.strokeColor) { this.strokeWidth = strokeWidth; this.strokeColor = strokeColor; NSAttributedStringAttributes attribs = createAttribs(font); addStroke(attribs, font, strokeWidth, strokeColor); strokeLine = CTLine.create(new NSAttributedString(text, attribs)); } paint(bctx, strokeLine, x, y); } void fill(CGBitmapContext bctx, float x, float y, int fillColor) { if (this.fillColor != fillColor) { this.fillColor = fillColor; NSAttributedStringAttributes attribs = createAttribs(font); attribs.setForegroundColor(toUIColor(fillColor)); fillLine = CTLine.create(new NSAttributedString(text, attribs)); } paint(bctx, fillLine, x, y); } private void paint(CGBitmapContext bctx, CTLine line, float x, float y) { bctx.saveGState(); bctx.translateCTM(x, y + ascent()); bctx.scaleCTM(1, -1); bctx.setShouldAntialias(format.antialias); bctx.setTextPosition(0, 0); line.draw(bctx); bctx.restoreGState(); } private static UIColor toUIColor(int color) { float blue = (color & 0xFF) / 255f; color >>= 8; float green = (color & 0xFF) / 255f; color >>= 8; float red = (color & 0xFF) / 255f; color >>= 8; float alpha = (color & 0xFF) / 255f; return new UIColor(red, green, blue, alpha); } private static RectBox computeBounds(CTFont font, CGRect bounds) { float ascent = (float) font.getAscent(); return new RectBox((float) bounds.getMinX(), ascent - (float) (bounds.getHeight() + bounds.getMinY()), (float) bounds.getWidth(), (float) bounds.getHeight()); } @Override public int stringWidth(String message) { NSAttributedStringAttributes attribs = createAttribs(font); CTLine line = CTLine.create(new NSAttributedString(message, attribs)); return (int) line.getWidth(); } @Override public int getHeight() { return (int) font.getSize(); } @Override public int charWidth(char ch) { NSAttributedStringAttributes attribs = createAttribs(font); CTLine line = CTLine.create(new NSAttributedString(String.valueOf(ch), attribs)); return (int) line.getWidth(); } }