我的应用程序显示了一些我使用Image.createImage()创建的图像。在某些情况下,图像完全空白,但仅限于iOS。这些图片在Android上运行良好。另外,我使用Image.createImage()创建了几个图像,并且大多数都可以正常工作。我不明白这些与这些之间有什么区别。
要重现,请在Android和iOS上运行随附的应用。该应用程序显示两个图像。第二个是从第一个的下半部分。在Android上,图像显示正常。在iOS上,图像显示几秒钟,然后消失。事实证明,它们只在iOS显示启动屏幕时出现。一旦切换到实际的应用程序,图像是空白的,虽然它们占用相同的空间。进一步的测试表明,图像尺寸正确,但是用透明像素填充。
我应该说,在我的实际应用中,图像会按照屏幕大小进行缩放,并根据用户偏好进行着色,因此我无法从资源中加载它们。
(顺便说一下注意我对停止方法所做的改变。这是无关的,但值得一提。)
以下是测试用例:
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Dialog;
import com.codename1.ui.Graphics;
import com.codename1.ui.Image;
import com.codename1.ui.Label;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.io.Log;
import com.codename1.ui.Toolbar;
import java.util.Arrays;
/**
* This file was generated by <a href="https://www.codenameone.com/">Codename One</a> for the purpose
* of building native mobile applications using Java.
*/
@SuppressWarnings("unused")
public class HalfImageBug {
private Form current;
private Resources theme;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
}
public void start() {
if (current != null) {
current.show();
return;
}
Form hi = new Form("Hi World", new BorderLayout());
hi.addComponent(BorderLayout.CENTER, makeComponent());
hi.show();
}
public void stop() {
current = Display.getInstance().getCurrent();
// This was originally if, but it should be while, in case there are multiple layers of dialogs.
while (current instanceof Dialog) {
((Dialog) current).dispose();
current = Display.getInstance().getCurrent();
}
}
public void destroy() {
}
private Component makeComponent() {
final Container container = new Container(new BoxLayout(BoxLayout.Y_AXIS));
container.setScrollableY(true);
container.add(new Label("Full Image:"));
Image fullIcon = createFullImage(0x44ff00, 40, 30);
Label fullImage = new Label(fullIcon);
container.add(fullImage);
container.add(new Label("---"));
container.add(new Label("Half Image:"));
Image halfIcon = createHalfSizeImage(fullIcon);
Label halfImage = new Label(halfIcon);
container.add(halfImage);
return container;
}
private Image createFullImage(int color, int verticalDiameter, int horizontalRadius) {
// Make sure it's an even number. Otherwise the half image will have its right and left halves reversed!
int diameter = (verticalDiameter / 2) * 2;
final int iconWidth = 2 * horizontalRadius;
int imageWidth = iconWidth + 2;
int imageHt = diameter + 2;
Image fullImage = Image.createImage(imageWidth, imageHt);
Graphics g = fullImage.getGraphics();
g.setAntiAliased(true);
g.setColor(color);
g.fillRect(0, 0, imageWidth, imageHt);
g.setColor(darken(color, 25));
g.fillArc(1, 1, iconWidth, diameter, 180, 360);
g.setColor(0xbfbfbf);
final int smallerHt = (9 * diameter) / 10;
g.fillArc(0, 0, iconWidth, smallerHt, 180, 360);
Image maskImage = Image.createImage(imageWidth, imageHt);
g = maskImage.getGraphics();
g.setAntiAliased(true);
g.setColor(0);
g.fillRect(0, 0, imageWidth, imageHt);
g.setColor(0xFF);
g.fillArc(1, 1, iconWidth, diameter, 180, 360);
fullImage = fullImage.applyMask(maskImage.createMask());
return fullImage;
}
private Image createHalfSizeImage(Image fullImage) {
int imageWidth = fullImage.getWidth();
int imageHt = fullImage.getHeight();
int[] rgbValues = fullImage.getRGB();
// yeah, I've since discovered a much more sensible way to do this, but it doesn't fix the bug.
int[] bottomHalf = Arrays.copyOfRange(rgbValues, rgbValues.length / 2, rgbValues.length);
//noinspection StringConcatenation
Log.p("Cutting side image from " + imageWidth + " x " + imageHt + " to " + imageWidth + " x " + (imageHt / 2));
return Image.createImage(bottomHalf, imageWidth, imageHt / 2);
}
private static int darken(int color, int percent) {
if ((percent > 100) || (percent < 0)) {
throw new IllegalArgumentException("Percent out of range: " + percent);
}
int percentRemaining = 100 - percent;
return (darkenPrimary((color & 0xFF0000) >> 16, percentRemaining) << 16)
| (darkenPrimary((color & 0xFF00) >> 8, percentRemaining) << 8)
| (darkenPrimary(color & 0xFF, percentRemaining));
}
private static int darkenPrimary(int primaryValue, int percentRemaining) {
if ((primaryValue < 0) || (primaryValue > 255)) {
throw new IllegalArgumentException("Primary value out of range (0-255): " + primaryValue);
}
return (primaryValue * percentRemaining) / 100;
}
}
答案 0 :(得分:0)
这在this issue中进行了讨论。
通常情况下,图片最初会显示,因为屏幕截图显示了这些图片,因此它们从未真正显示在iOS本机上。
这些问题的一个常见原因是从EDT创建图像,这似乎不是此特定代码中的问题。
很难看到发生了什么,所以我想我们需要评估这个问题。
答案 1 :(得分:0)
这是一种解决方法。这有效,但不能很好地消除别名。它会一直运行,直到iOS代码得到修复。
问题如其他地方所述。 Graphics.fillArc()和drawArc()方法在Android上运行良好,但在iOS上经常失败。这是行为:
width == height
,他们会正确画一个圆圈。 width < height
,它们应绘制一个椭圆,但它们绘制一个圆,以预期的椭圆为中心,直径等于width
。 width > height
,他们什么都没画。变通方法在透明背景上绘制一个圆圈,然后将该圆圈沿一个方向挤压成椭圆,进入适当的位置。它在抗锯齿方面做得不是很好,所以这不是工作代码的良好替代品,但它会一直存在,直到修复bug。 (这种解决方法处理fillArc,但是对于drawArc()
来修改它应该不难/**
* Workaround for fillArc bug. Graphics.fillArc() works fine on android, but usually fails on iOS. There are three
* cases for its behavior.
*
* If width < height, it draws a circle with a diameter equal to width, and concentric with the intended ellipse.<br>
* If width > height, it draws nothing.<br>
* If width == height, it works correctly.
*
* To work around this we create a separate image, draw a circle, re-proportion it to the proper ellipse, then draw
* it to the Graphics object. It doesn't anti-alias very well.
*/
public static void fillArcWorkaround(Graphics masterG, int x, int y, int width, int height, int startAngle, int arcAngle) {
if (width == height) {
masterG.fillArc(x, y, width, height, startAngle, arcAngle);
} else {
int max = Math.max(width, height);
Image tempCircle = Image.createImage(max, max);
Graphics tempG = tempCircle.getGraphics();
tempG.setColor(masterG.getColor());
tempG.fillRect(0, 0, max, max);
// At this point tempCircle is just a colored rectangle. It becomes a circle when we apply the circle mask. The
// region outside the circle becomes transparent that way.
Image mask = Image.createImage(max, max);
tempG = mask.getGraphics();
tempG.setAntiAliased(masterG.isAntiAliased());
tempG.setColor(0);
tempG.fillRect(0, 0, max, max);
tempG.setColor(0xFF); // blue
tempG.fillArc(0, 0, max, max, startAngle, arcAngle);
tempCircle = tempCircle.applyMask(mask.createMask());
// Now tempCircle is a filled circle of the correct color. We now draw it in its intended proportions.
masterG.setAntiAliased(true);
masterG.drawImage(tempCircle, x, y, width, height);
}
}