图形绘制代码仅在iOS上生成空白图像

时间:2016-09-02 01:43:25

标签: ios codenameone

我的应用程序显示了一些我使用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;
  }
}

2 个答案:

答案 0 :(得分:0)

这在this issue中进行了讨论。

通常情况下,图片最初会显示,因为屏幕截图显示了这些图片,因此它们从未真正显示在iOS本机上。

这些问题的一个常见原因是从EDT创建图像,这似乎不是此特定代码中的问题。

很难看到发生了什么,所以我想我们需要评估这个问题。

答案 1 :(得分:0)

这是一种解决方法。这有效,但不能很好地消除别名。它会一直运行,直到iOS代码得到修复。

问题如其他地方所述。 Graphics.fillArc()和drawArc()方法在Android上运行良好,但在iOS上经常失败。这是行为:

  • 如果width == height,他们会正确画一个圆圈。
  • 如果width < height,它们应绘制一个椭圆,但它们绘制一个圆,以预期的椭圆为中心,直径等于width
  • if 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);
    }
}