iPhone 6s / RoboVM (?) GLTexImage2D 渲染问题
iPhone 6s / RoboVM (?) GLTexImage2D rendering issue
我们终于在 App Store 上发布了我们的独立游戏,只是发现 iPhone 6s 存在重大渲染问题。
该游戏是使用 LibGDX 和 RoboVM (v.1.12)
开发的
我们正在使用 iOS 本机图形库将 unicode 文本渲染为纹理,以将其传递给 LibGDX 以显示为图像。
为此,我们首先创建要打印的字符串,然后调用其 draw() 方法:
final String tmp = text.substring(0, text.length() >= MAX_NAME_LEN ? MAX_NAME_LEN : text.length());
final NSMutableString string = new NSMutableString(tmp.length());
//Allocate space for the bitmap to draw on
int w = (int) width;
int h = (int) height;
final int bufferLen = w * h;
final CGBitmapContext context = new CGBitmapContext(
w, h, 8, 0, CGColorSpace.deviceRGB(), CGImageAlphaInfo.PremultipliedLast
);
final CGColor bgColor = UIColor.clear().getCGColor();
final CGRect rect = new CGRect(0, 0, width, height);
string.setString(tmp);
final UIColor textColor = new UIColor(color.r, color.g, color.b, color.a);
final UIFont font = UIFont.getBoldSystemFont(size);
final NSString tester = new NSString("a");
final CGSize textSize = tester.getSize(font);
int strLen = string.toString().length();
/**
* Computes the optimal length of the string and removes characters beyound the
* screen bounduaries.
* If the string has been reduced, appends an ellipsis at the end
*/
final NSRange range = new NSRange();
boolean removed = false;
while (strLen > 0 && textSize.getWidth() * strLen > width - textSize.getWidth() * 2) {
range.setLocation(strLen - 1);
range.setLength(1);
string.deleteCharacters(range);
strLen = string.toString().length();
removed = true;
}
//append ellipsis at the end if we removed a char
if (removed) {
string.append("\u2026");
}
context.setFillColor(bgColor);
context.fillRect(rect);
//set up drawing attributes
final NSAttributedStringAttributes attributes = new NSAttributedStringAttributes();
attributes.set(NSAttributedStringAttribute.Font, font);
attributes.set(NSAttributedStringAttribute.ForegroundColor, textColor);
final NSMutableParagraphStyle paragraphStyle = new NSMutableParagraphStyle();
paragraphStyle.setParagraphStyle(NSParagraphStyle.getDefaultParagraphStyle());
paragraphStyle.setLineBreakMode(NSLineBreakMode.TruncatingTail);
attributes.set(NSAttributedStringAttribute.ParagraphStyle, paragraphStyle);
final NSAttributedString finalStr = new NSAttributedString(string.toString(), attributes);
UIGraphics.pushContext(context);
context.setTextMatrix(CGAffineTransform.Identity());
context.translateCTM(0f, h);
context.scaleCTM(1, -1);
finalStr.draw(new CGPoint(rect.getX(), (rect.getY() + textSize.getHeight()) / 2));
UIGraphics.popContext();
return new BufferTextureData((int)context.getWidth(), (int)context.getHeight(), 0, GL20.GL_RGBA, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, context.getData().asIntBuffer(bufferLen));
TextureBufferData 然后通过以下方式将 Buffer 绑定到纹理:
private Buffer data;
public BufferTextureData(int width, int height, int mipMapLevel, int internalFormat, int format, int type, Buffer data) {
this.width = width;
this.height = height;
this.mipLevel = mipMapLevel;
this.internalFormat = internalFormat;
this.format = format;
this.type = type;
this.data = data;
}
[...]
@Override
public void consumeCustomData(final int target) {
Gdx.app.debug("BufferTextureData", "consuming data for target: " + target);
Gdx.gl20.glTexImage2D(target, mipLevel, internalFormat, width, height, 0, format, type, data);
data = null;
}
我们在这些设备上测试了这段代码,没有任何问题:
- iPhone 6
- iPad 空气 2
- iPhone 5 秒
还有 iPhone 6s 模拟器。
问题是在 iPhone 6s 实际设备上我们得到这个:
如您所见,文字完全扭曲了。保存绘图数据的缓冲区似乎在绘图调用期间被写入,但我们确保在绘图线程上调用该方法以避免 GL 上下文切换。
我们真的不知道去哪里找。
代码在 iPhone6s 模拟器 (x64 CPU) 和所有其他设备上无缝运行的事实让我认为问题要么是硬件相关的,要么是由 RoboVM 处理内存的方式引起的,但它是只是猜测。
有没有人遇到过同样的问题或者知道是什么导致了这种行为?
提前致谢
这是对齐 and/or 行跨度问题。我建议您阅读有关 glPixelStore 及其 GL_UNPACK_… 参数的手册。在使用 glTexImage
加载图像数据之前,您必须设置像素存储参数以匹配图像数据
感谢@datenwolf 为我们指出了一个好的方向。
问题是由于 iphone 6s 的这个 "known bug":
http://ios9news.net/fix-ios-9-apps-zoomed-in-on-iphone-6s/
也就是在给定iphone6同样的分辨率和ppi的情况下,6s觉得他宽了一点。
因此,我们制作成 POT 倍数的游戏单元在 6s 设备中的长度为奇数(例如 iphone 6 上的 480px 宽度在 iphone 6s 上为 561px)。
当我们使用 CGContextCreate
创建图形上下文时,我们没有指定每行要考虑多少字节,而是让设备为我们决定。事实证明,OS 正在向每一行添加填充,弄乱了所有步幅和对齐方式。
解决方法是更改:
final CGBitmapContext context = new CGBitmapContext(
w, h, 8, 0, CGColorSpace.deviceRGB(), CGImageAlphaInfo.PremultipliedLast
);
至:
final CGBitmapContext context = new CGBitmapContext(
w, h, 8, 4 * w, CGColorSpace.deviceRGB(), CGImageAlphaInfo.PremultipliedLast
);
我们终于在 App Store 上发布了我们的独立游戏,只是发现 iPhone 6s 存在重大渲染问题。
该游戏是使用 LibGDX 和 RoboVM (v.1.12)
开发的我们正在使用 iOS 本机图形库将 unicode 文本渲染为纹理,以将其传递给 LibGDX 以显示为图像。 为此,我们首先创建要打印的字符串,然后调用其 draw() 方法:
final String tmp = text.substring(0, text.length() >= MAX_NAME_LEN ? MAX_NAME_LEN : text.length());
final NSMutableString string = new NSMutableString(tmp.length());
//Allocate space for the bitmap to draw on
int w = (int) width;
int h = (int) height;
final int bufferLen = w * h;
final CGBitmapContext context = new CGBitmapContext(
w, h, 8, 0, CGColorSpace.deviceRGB(), CGImageAlphaInfo.PremultipliedLast
);
final CGColor bgColor = UIColor.clear().getCGColor();
final CGRect rect = new CGRect(0, 0, width, height);
string.setString(tmp);
final UIColor textColor = new UIColor(color.r, color.g, color.b, color.a);
final UIFont font = UIFont.getBoldSystemFont(size);
final NSString tester = new NSString("a");
final CGSize textSize = tester.getSize(font);
int strLen = string.toString().length();
/**
* Computes the optimal length of the string and removes characters beyound the
* screen bounduaries.
* If the string has been reduced, appends an ellipsis at the end
*/
final NSRange range = new NSRange();
boolean removed = false;
while (strLen > 0 && textSize.getWidth() * strLen > width - textSize.getWidth() * 2) {
range.setLocation(strLen - 1);
range.setLength(1);
string.deleteCharacters(range);
strLen = string.toString().length();
removed = true;
}
//append ellipsis at the end if we removed a char
if (removed) {
string.append("\u2026");
}
context.setFillColor(bgColor);
context.fillRect(rect);
//set up drawing attributes
final NSAttributedStringAttributes attributes = new NSAttributedStringAttributes();
attributes.set(NSAttributedStringAttribute.Font, font);
attributes.set(NSAttributedStringAttribute.ForegroundColor, textColor);
final NSMutableParagraphStyle paragraphStyle = new NSMutableParagraphStyle();
paragraphStyle.setParagraphStyle(NSParagraphStyle.getDefaultParagraphStyle());
paragraphStyle.setLineBreakMode(NSLineBreakMode.TruncatingTail);
attributes.set(NSAttributedStringAttribute.ParagraphStyle, paragraphStyle);
final NSAttributedString finalStr = new NSAttributedString(string.toString(), attributes);
UIGraphics.pushContext(context);
context.setTextMatrix(CGAffineTransform.Identity());
context.translateCTM(0f, h);
context.scaleCTM(1, -1);
finalStr.draw(new CGPoint(rect.getX(), (rect.getY() + textSize.getHeight()) / 2));
UIGraphics.popContext();
return new BufferTextureData((int)context.getWidth(), (int)context.getHeight(), 0, GL20.GL_RGBA, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, context.getData().asIntBuffer(bufferLen));
TextureBufferData 然后通过以下方式将 Buffer 绑定到纹理:
private Buffer data;
public BufferTextureData(int width, int height, int mipMapLevel, int internalFormat, int format, int type, Buffer data) {
this.width = width;
this.height = height;
this.mipLevel = mipMapLevel;
this.internalFormat = internalFormat;
this.format = format;
this.type = type;
this.data = data;
}
[...]
@Override
public void consumeCustomData(final int target) {
Gdx.app.debug("BufferTextureData", "consuming data for target: " + target);
Gdx.gl20.glTexImage2D(target, mipLevel, internalFormat, width, height, 0, format, type, data);
data = null;
}
我们在这些设备上测试了这段代码,没有任何问题: - iPhone 6 - iPad 空气 2 - iPhone 5 秒 还有 iPhone 6s 模拟器。
问题是在 iPhone 6s 实际设备上我们得到这个:
如您所见,文字完全扭曲了。保存绘图数据的缓冲区似乎在绘图调用期间被写入,但我们确保在绘图线程上调用该方法以避免 GL 上下文切换。
我们真的不知道去哪里找。 代码在 iPhone6s 模拟器 (x64 CPU) 和所有其他设备上无缝运行的事实让我认为问题要么是硬件相关的,要么是由 RoboVM 处理内存的方式引起的,但它是只是猜测。
有没有人遇到过同样的问题或者知道是什么导致了这种行为?
提前致谢
这是对齐 and/or 行跨度问题。我建议您阅读有关 glPixelStore 及其 GL_UNPACK_… 参数的手册。在使用 glTexImage
加载图像数据之前,您必须设置像素存储参数以匹配图像数据感谢@datenwolf 为我们指出了一个好的方向。
问题是由于 iphone 6s 的这个 "known bug":
http://ios9news.net/fix-ios-9-apps-zoomed-in-on-iphone-6s/
也就是在给定iphone6同样的分辨率和ppi的情况下,6s觉得他宽了一点。 因此,我们制作成 POT 倍数的游戏单元在 6s 设备中的长度为奇数(例如 iphone 6 上的 480px 宽度在 iphone 6s 上为 561px)。
当我们使用 CGContextCreate
创建图形上下文时,我们没有指定每行要考虑多少字节,而是让设备为我们决定。事实证明,OS 正在向每一行添加填充,弄乱了所有步幅和对齐方式。
解决方法是更改:
final CGBitmapContext context = new CGBitmapContext(
w, h, 8, 0, CGColorSpace.deviceRGB(), CGImageAlphaInfo.PremultipliedLast
);
至:
final CGBitmapContext context = new CGBitmapContext(
w, h, 8, 4 * w, CGColorSpace.deviceRGB(), CGImageAlphaInfo.PremultipliedLast
);