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