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);
       }
     }
   }
 }
 @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 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;
  }