protected void readColors(String flameXML, Layer layer) {
   // Colors
   {
     int p = 0;
     while (true) {
       int ps = flameXML.indexOf("<color ", p + 1);
       if (ps < 0) break;
       int pe = flameXML.indexOf("/>", ps + 1);
       String hs = flameXML.substring(ps + 7, pe);
       {
         int index = 0;
         int r = 0, g = 0, b = 0;
         XMLAttributes atts = Tools.parseAttributes(hs);
         String attr;
         if ((attr = atts.get(ATTR_INDEX)) != null) {
           index = Integer.parseInt(attr);
         }
         if ((attr = atts.get(ATTR_RGB)) != null) {
           String s[] = attr.split(" ");
           r = Tools.FTOI(Double.parseDouble(s[0]));
           g = Tools.FTOI(Double.parseDouble(s[1]));
           b = Tools.FTOI(Double.parseDouble(s[2]));
         }
         layer.getPalette().setColor(index, r, g, b);
       }
       p = pe + 2;
     }
   }
   // Palette
   {
     int ps = flameXML.indexOf("<palette ");
     if (ps >= 0) {
       ps = flameXML.indexOf(">", ps + 1);
       int pe = flameXML.indexOf("</palette>", ps + 1);
       String hs = flameXML.substring(ps + 1, pe);
       StringBuilder sb = new StringBuilder();
       for (int i = 0; i < hs.length(); i++) {
         char c = hs.charAt(i);
         if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
           sb.append(c);
         }
       }
       hs = sb.toString();
       if ((hs.length() % 6) != 0) throw new RuntimeException("Invalid/unknown palette");
       int index = 0;
       for (int i = 0; i < hs.length(); i += 6) {
         int r = Integer.parseInt(hs.substring(i, i + 2), 16);
         int g = Integer.parseInt(hs.substring(i + 2, i + 4), 16);
         int b = Integer.parseInt(hs.substring(i + 4, i + 6), 16);
         // System.out.println(hs.substring(i, i + 2) + "#" + hs.substring(i + 2, i + 4) + "#" +
         // hs.substring(i + 4, i + 6));
         // System.out.println("  flame->palette->setColor(" + index + "," + r + "," + g + "," + b
         // + ");");
         layer.getPalette().setColor(index++, r, g, b);
       }
     }
   }
 }
 private Flame validateDancingFlame(Flame pFlame) {
   for (Layer layer : pFlame.getLayers()) {
     if (layer.getFinalXForms().size() == 0) {
       XForm xForm = new XForm();
       xForm.addVariation(1.0, new Linear3DFunc());
       layer.getFinalXForms().add(xForm);
     }
   }
   return pFlame;
 }
 @Override
 public void init(
     FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
   colorMap = null;
   //    renderColors = pContext.getFlameRenderer().getColorMap();
   // TODO optimize
   renderColors =
       pLayer
           .getPalette()
           .createRenderPalette(pContext.getFlameRenderer().getFlame().getWhiteLevel());
   if (inlinedImage != null) {
     try {
       colorMap = RessourceManager.getImage(inlinedImageHash, inlinedImage);
     } catch (Exception e) {
       e.printStackTrace();
     }
   } else if (imageFilename != null && imageFilename.length() > 0) {
     try {
       colorMap = RessourceManager.getImage(imageFilename);
     } catch (Exception e) {
       e.printStackTrace();
     }
   }
   if (colorMap == null) {
     colorMap = getDfltImage();
   }
   imgWidth = colorMap.getImageWidth();
   imgHeight = colorMap.getImageHeight();
 }
  private void setRandomFlameProperty(Layer pLayer, double pAmount) {
    List<VariationFunc> variations = new ArrayList<VariationFunc>();

    for (XForm xForm : pLayer.getXForms()) {
      addVariations(variations, xForm);
    }
    for (XForm xForm : pLayer.getFinalXForms()) {
      addVariations(variations, xForm);
    }
    filterVariations(variations);
    if (variations.size() > 0) {
      int idx = (int) (Math.random() * variations.size());
      VariationFunc var = variations.get(idx);
      int pIdx = (int) (Math.random() * var.getParameterNames().length);
      Object oldVal = var.getParameterValues()[pIdx];
      if (oldVal instanceof Integer) {
        int o = (Integer) oldVal;
        int da = Tools.FTOI(pAmount);
        if (da < 1) {
          da = 1;
        }
        if (o >= 0) {
          o += da;
        } else {
          o -= da;
        }
        var.setParameter(var.getParameterNames()[pIdx], o);
      } else if (oldVal instanceof Double) {
        double o = (Double) oldVal;
        if (o < EPSILON || Math.random() < 0.3) {
          if (o >= 0) {
            o += 0.1 * pAmount;
          } else {
            o -= 0.1 * pAmount;
          }
        } else {
          if (o >= 0) {
            o += o / 100.0 * pAmount;
          } else {
            o -= o / 100.0 * pAmount;
          }
        }
        var.setParameter(var.getParameterNames()[pIdx], o);
      }
    }
  }
 protected void readXForms(String flameXML, Flame flame, Layer layer) {
   // XForms
   {
     int p = 0;
     while (true) {
       int ps = flameXML.indexOf("<xform ", p + 1);
       if (ps < 0) break;
       int pe = flameXML.indexOf("</xform>", ps + 1);
       if (pe < 0) {
         pe = flameXML.indexOf("/>", ps + 1);
       }
       String hs = flameXML.substring(ps + 7, pe);
       XForm xForm = new XForm();
       parseXFormAttributes(flame, xForm, hs);
       layer.getXForms().add(xForm);
       p = pe + 2;
     }
   }
 }
 private static Flame morphFlames_fade(
     Prefs pPrefs, Flame pFlame1, Flame pFlame2, int pFrame, int pFrames) {
   if (pFrame < 1 || pFrames < 2) return pFlame1;
   double fScl = (double) (pFrame - 1) / (pFrames - 1);
   if (fScl <= MathLib.EPSILON) {
     return pFlame1;
   } else if (fScl >= 1.0 - MathLib.EPSILON) {
     return pFlame2;
   }
   // fade out layerz of the source flame
   Flame res = pFlame1.makeCopy();
   morphFlameValues(pFlame1, pFlame2, fScl, res);
   for (Layer layer : res.getLayers()) {
     layer.setWeight(layer.getWeight() * (1.0 - fScl));
   }
   // add and fade in layerz of the dest flame
   for (Layer layer : pFlame2.getLayers()) {
     Layer copy = layer.makeCopy();
     copy.setWeight(copy.getWeight() * fScl);
     res.getLayers().add(copy);
   }
   return res;
 }
  private static Flame morphFlames_morph(
      Prefs pPrefs, Flame pFlame1, Flame pFlame2, int pFrame, int pFrames) {
    if (pFrame < 1 || pFrames < 2) return pFlame1;
    double fScl = (double) (pFrame - 1) / (pFrames - 1);
    if (fScl <= MathLib.EPSILON) {
      return pFlame1;
    } else if (fScl >= 1.0 - MathLib.EPSILON) {
      return pFlame2;
    }
    Flame res = pFlame1.makeCopy();
    res.getLayers().clear();
    int layerSize1 = pFlame1.getLayers().size();
    int layerSize2 = pFlame2.getLayers().size();
    int maxLayerSize = layerSize1 > layerSize2 ? layerSize1 : layerSize2;
    for (int lIdx = 0; lIdx < maxLayerSize; lIdx++) {
      Layer layer = new Layer();
      res.getLayers().add(layer);
      // Morph layers
      if (lIdx < layerSize1 && lIdx < layerSize2) {
        Layer layer1 = pFlame1.getLayers().get(lIdx);
        Layer layer2 = pFlame2.getLayers().get(lIdx);
        layer.assign(layer1);
        layer.getXForms().clear();
        layer.getFinalXForms().clear();
        layer.setWeight(morphValue(layer1.getWeight(), layer2.getWeight(), fScl));
        // morph XForms
        {
          int size1 = layer1.getXForms().size();
          int size2 = layer2.getXForms().size();
          int maxSize = size1 > size2 ? size1 : size2;
          for (int i = 0; i < maxSize; i++) {
            XForm xForm1 = i < size1 ? layer1.getXForms().get(i) : null;
            if (xForm1 == null) {
              xForm1 = new XForm();
              xForm1.addVariation(
                  0.0, VariationFuncList.getVariationFuncInstance("linear3D", true));
              xForm1.setWeight(0.0);
            }

            XForm xForm2 = i < size2 ? layer2.getXForms().get(i) : null;
            if (xForm2 == null) {
              xForm2 = new XForm();
              xForm2.addVariation(
                  0.0, VariationFuncList.getVariationFuncInstance("linear3D", true));
              xForm2.setWeight(0.0);
            }

            XForm morphedXForm = morphXForms(pPrefs, xForm1, xForm2, fScl, pFrame, pFrames);
            layer.getXForms().add(morphedXForm);
          }
        }
        // morph final XForms
        {
          int size1 = layer1.getFinalXForms().size();
          int size2 = layer2.getFinalXForms().size();
          int maxSize = size1 > size2 ? size1 : size2;
          for (int i = 0; i < maxSize; i++) {
            XForm xForm1 = i < size1 ? layer1.getFinalXForms().get(i) : null;
            if (xForm1 == null) {
              xForm1 = new XForm();
              xForm1.addVariation(
                  0.0, VariationFuncList.getVariationFuncInstance("linear3D", true));
              xForm1.setWeight(0.0);
            }

            XForm xForm2 = i < size2 ? layer2.getFinalXForms().get(i) : null;
            if (xForm2 == null) {
              xForm2 = new XForm();
              xForm2.addVariation(
                  0.0, VariationFuncList.getVariationFuncInstance("linear3D", true));
              xForm2.setWeight(0.0);
            }

            XForm morphedXForm = morphXForms(pPrefs, xForm1, xForm2, fScl, pFrame, pFrames);
            layer.getFinalXForms().add(morphedXForm);
          }
        }
        // morph colors
        RGBPalette palette1 = layer1.getPalette();
        RGBPalette palette2 = layer2.getPalette();
        for (int i = 0; i < RGBPalette.PALETTE_SIZE; i++) {
          RGBColor color1 = palette1.getColor(i);
          RGBColor color2 = palette2.getColor(i);
          int red = Tools.roundColor(color1.getRed() + (color2.getRed() - color1.getRed()) * fScl);
          int green =
              Tools.roundColor(color1.getGreen() + (color2.getGreen() - color1.getGreen()) * fScl);
          int blue =
              Tools.roundColor(color1.getBlue() + (color2.getBlue() - color1.getBlue()) * fScl);
          layer.getPalette().setColor(i, red, green, blue);
        }
      }
      // fade out layer1 to black
      else if (lIdx < layerSize1) {
        Layer layer1 = pFlame1.getLayers().get(lIdx);
        layer.assign(layer1);
        layer.setWeight(morphValue(layer1.getWeight(), 0.0, fScl));
      }
      // fade in layer2 from black
      else if (lIdx < layerSize2) {
        Layer layer2 = pFlame2.getLayers().get(lIdx);
        layer.assign(layer2);
        layer.setWeight(morphValue(0.0, layer2.getWeight(), fScl));
      }
    }
    // morph camera settings etc.
    morphFlameValues(pFlame1, pFlame2, fScl, res);
    return res;
  }
  @Override
  public Flame prepareFlame(RandomFlameGeneratorState pState) {
    Flame flame = new Flame();
    Layer layer = flame.getFirstLayer();
    flame.setCamRoll(0);
    flame.setCamPitch(90.0 - Math.random() * 180.0);
    flame.setCamYaw(30.0 - Math.random() * 60.0);
    flame.setCamPerspective(Math.random() * 0.2);
    flame.setWidth(601);
    flame.setHeight(338);
    flame.setPixelsPerUnit(92.48366013);
    flame.setCamZoom(0.3 + Math.random() * 0.5);

    randomizeSolidRenderingSettings(flame);

    layer.getFinalXForms().clear();
    layer.getXForms().clear();

    // create transform 1
    {
      XForm xForm = new XForm();
      layer.getXForms().add(xForm);
      xForm.setWeight(0.5);
      xForm.setColor(Math.random());
      xForm.setColorSymmetry(1.0 - 2 * Math.random());
      xForm.setMaterial(0);
      xForm.setMaterialSpeed(0);

      xForm.setCoeff00(1); // a
      xForm.setCoeff10(0); // b
      xForm.setCoeff20(0); // e
      xForm.setCoeff01(0); // c
      xForm.setCoeff11(1); // d
      xForm.setCoeff21(0); // f

      xForm.setPostCoeff00(1);
      xForm.setPostCoeff10(0);
      xForm.setPostCoeff01(0);
      xForm.setPostCoeff11(1);
      xForm.setPostCoeff20(0);
      xForm.setPostCoeff21(0);

      if (Math.random() > 0.125) {
        xForm.setDrawMode(DrawMode.HIDDEN);
      }

      // variation 1
      xForm.addVariation(0.2 + Math.random(), getRandom3DShape());
      // set default edit plane
      flame.setEditPlane(
          Math.random() > 0.666 ? EditPlane.XY : Math.random() < 0.5 ? EditPlane.YZ : EditPlane.ZX);
      XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, false);
      XFormTransformService.rotate(xForm, 360.0 * Math.random(), false);
      XFormTransformService.localTranslate(
          xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), false);

      if (Math.random() > 0.5) {
        XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, true);
        XFormTransformService.rotate(xForm, 360.0 * Math.random(), true);
        XFormTransformService.localTranslate(
            xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), true);
      }
    }

    // create transform 2
    {
      XForm xForm = new XForm();
      layer.getXForms().add(xForm);
      xForm.setWeight(0.5);
      xForm.setColor(0);
      xForm.setColorSymmetry(0);
      xForm.setMaterial(0);
      xForm.setMaterialSpeed(0);

      xForm.setXYCoeff00(0.54625622); // a
      xForm.setXYCoeff10(0.26758811); // b
      xForm.setXYCoeff20(0); // e
      xForm.setXYCoeff01(-0.26758811); // c
      xForm.setXYCoeff11(0.54625622); // d
      xForm.setXYCoeff21(0); // f

      xForm.setXYPostCoeff00(1);
      xForm.setXYPostCoeff10(0);
      xForm.setXYPostCoeff01(0);
      xForm.setXYPostCoeff11(1);
      xForm.setXYPostCoeff20(0);
      xForm.setXYPostCoeff21(0);

      xForm.setYZCoeff00(0.46864442);
      xForm.setYZCoeff10(-0.17017929);
      xForm.setYZCoeff20(1.21536218);
      xForm.setYZCoeff01(0.17017929);
      xForm.setYZCoeff11(0.46864442);
      xForm.setYZCoeff21(-0.02558657);

      xForm.setYZPostCoeff00(1);
      xForm.setYZPostCoeff10(0);
      xForm.setYZPostCoeff01(0);
      xForm.setYZPostCoeff11(1);
      xForm.setYZPostCoeff20(0);
      xForm.setYZPostCoeff21(0);

      xForm.setZXCoeff00(0.81078767);
      xForm.setZXCoeff10(0.12035676);
      xForm.setZXCoeff20(0.21748586);
      xForm.setZXCoeff01(-0.12035676);
      xForm.setZXCoeff11(0.81078767);
      xForm.setZXCoeff21(-0.05117314);

      xForm.setZXPostCoeff00(1);
      xForm.setZXPostCoeff10(0);
      xForm.setZXPostCoeff01(0);
      xForm.setZXPostCoeff11(1);
      xForm.setZXPostCoeff20(0);
      xForm.setZXPostCoeff21(0);

      // variation 1
      xForm.addVariation(1, VariationFuncList.getVariationFuncInstance("linear3D", true));
      // set default edit plane
      flame.setEditPlane(EditPlane.XY);
      XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, false);
      XFormTransformService.rotate(xForm, 360.0 * Math.random(), false);
      XFormTransformService.localTranslate(
          xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), false);
      if (Math.random() > 0.666) {
        XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, true);
        XFormTransformService.rotate(xForm, 360.0 * Math.random(), true);
        XFormTransformService.localTranslate(
            xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), true);
      }
    }
    // create transform 3
    if (Math.random() > 0.666) {
      XForm xForm = new XForm();
      layer.getXForms().add(xForm);
      xForm.setWeight(0.5);
      xForm.setColor(0);
      xForm.setColorSymmetry(0);
      xForm.setMaterial(0);
      xForm.setMaterialSpeed(0);

      xForm.setXYCoeff00(0.54625622); // a
      xForm.setXYCoeff10(0.26758811); // b
      xForm.setXYCoeff20(0); // e
      xForm.setXYCoeff01(-0.26758811); // c
      xForm.setXYCoeff11(0.54625622); // d
      xForm.setXYCoeff21(0); // f

      xForm.setXYPostCoeff00(1);
      xForm.setXYPostCoeff10(0);
      xForm.setXYPostCoeff01(0);
      xForm.setXYPostCoeff11(1);
      xForm.setXYPostCoeff20(0);
      xForm.setXYPostCoeff21(0);

      xForm.setYZCoeff00(0.46864442);
      xForm.setYZCoeff10(-0.17017929);
      xForm.setYZCoeff20(1.21536218);
      xForm.setYZCoeff01(0.17017929);
      xForm.setYZCoeff11(0.46864442);
      xForm.setYZCoeff21(-0.02558657);

      xForm.setYZPostCoeff00(1);
      xForm.setYZPostCoeff10(0);
      xForm.setYZPostCoeff01(0);
      xForm.setYZPostCoeff11(1);
      xForm.setYZPostCoeff20(0);
      xForm.setYZPostCoeff21(0);

      xForm.setZXCoeff00(0.59207155);
      xForm.setZXCoeff10(-0.56684538);
      xForm.setZXCoeff20(1.31770847);
      xForm.setZXCoeff01(0.56684538);
      xForm.setZXCoeff11(0.59207155);
      xForm.setZXCoeff21(-0.84435688);

      xForm.setZXPostCoeff00(1);
      xForm.setZXPostCoeff10(0);
      xForm.setZXPostCoeff01(0);
      xForm.setZXPostCoeff11(1);
      xForm.setZXPostCoeff20(0);
      xForm.setZXPostCoeff21(0);

      // variation 1
      xForm.addVariation(1, VariationFuncList.getVariationFuncInstance("linear3D", true));
      // set default edit plane
      flame.setEditPlane(EditPlane.XY);
      XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, false);
      XFormTransformService.rotate(xForm, 360.0 * Math.random(), false);
      XFormTransformService.localTranslate(
          xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), false);
      if (Math.random() > 0.5) {
        XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, true);
        XFormTransformService.rotate(xForm, 360.0 * Math.random(), true);
        XFormTransformService.localTranslate(
            xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), true);
      }
    }

    // create final transform 1
    if (Math.random() > 0.42) {
      XForm xForm = new XForm();
      layer.getFinalXForms().add(xForm);
      xForm.setWeight(0);
      xForm.setColor(0);
      xForm.setColorSymmetry(0);
      xForm.setMaterial(0);
      xForm.setMaterialSpeed(0);

      xForm.setCoeff00(1); // a
      xForm.setCoeff10(0); // b
      xForm.setCoeff20(0); // e
      xForm.setCoeff01(0); // c
      xForm.setCoeff11(1); // d
      xForm.setCoeff21(0); // f

      xForm.setPostCoeff00(1);
      xForm.setPostCoeff10(0);
      xForm.setPostCoeff01(0);
      xForm.setPostCoeff11(1);
      xForm.setPostCoeff20(0);
      xForm.setPostCoeff21(0);

      // variation 1
      xForm.addVariation(0.75 + Math.random() * 0.25, getRandomVariation());
      // variation 2
      if (Math.random() > 0.5) {
        xForm.addVariation(
            0.25 + Math.random() * 0.25,
            VariationFuncList.getVariationFuncInstance("linear3D", true));
      }
      // set default edit plane
      flame.setEditPlane(EditPlane.XY);
      if (Math.random() > 0.5) {
        XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, false);
        XFormTransformService.rotate(xForm, 360.0 * Math.random(), false);
        XFormTransformService.localTranslate(
            xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), false);
      }
      if (Math.random() > 0.5) {
        XFormTransformService.scale(xForm, 1.25 - Math.random() * 0.5, true, true, true);
        XFormTransformService.rotate(xForm, 360.0 * Math.random(), true);
        XFormTransformService.localTranslate(
            xForm, 1.0 - 2.0 * Math.random(), 1.0 - 2.0 * Math.random(), true);
      }
    }

    return flame;
  }