fn save_png(path : String, png_data : Bytes) -> Bool raise @fs.IOError {
  match (try? @fs.write_bytes_to_file(path, png_data)) {
    Ok(_) => true
    Err(e) => {
      raise e
      false
    }
  }
}

// 渐变色映射工具
// 将 H, S, L 映射为 RGB(输入:h ∈ [0,360], s ∈ [0,1], l ∈ [0,1])
fn hsl_to_rgb(h : Float, s : Float, l : Float) -> (Byte, Byte, Byte) {
  let c = (1.0 - (2.0 * l.to_double() - 1.0).abs()) * s.to_double()
  let h_ = h / 60.0
  let x = c * (1.0 - (h_.to_double() % 2.0 - 1.0).abs())
  let (r1, g1, b1) = if h_ < 1.0 {
    (c, x, 0.0)
  } else if h_ < 2.0 {
    (x, c, 0.0)
  } else if h_ < 3.0 {
    (0.0, c, x)
  } else if h_ < 4.0 {
    (0.0, x, c)
  } else if h_ < 5.0 {
    (x, 0.0, c)
  } else {
    (c, 0.0, x)
  }
  let m = l - c.to_float() * 0.5
  fn to_byte_safe(v : Float) -> Byte {
    let p = 0
    let q = 255
    let i = (v * 255.0).to_int()
    if i < 0 {
      p.to_byte()
    } else if i > 255 {
      q.to_byte()
    } else {
      i.to_byte()
    }
  }

  let r = to_byte_safe(r1.to_float() + m)
  let g = to_byte_safe(g1.to_float() + m)
  let b = to_byte_safe(b1.to_float() + m)
  (r, g, b)
}


fn noise_to_color(val : Float) -> @color.RGBA {
  let hue = val * 360.0 % 360.0 // 色相映射(让噪声值对应颜色环)
  let sat : Float = 0.6
  let light : Float = 0.5
  let c = 255
  let (r, g, b) = hsl_to_rgb(hue, sat, light)
  @color.RGBA::new(r, g, b, c.to_byte())
}




// 生成指定尺寸的 2D Simplex Noise图像
fn generate_noise_png_buffer(
  width : Int,
  height : Int,
  scale : Float,
  seed : Int,
  grads : Array[Array[Float]],
  useWarp : Bool, // 添加 useWarp 参数来控制是否使用 warp2d
  isGrayScale : Bool, // 是否生成灰度图
  strength : Float // 扰动强度
) -> Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0.. Bytes raise @image.SizeError {
  let buf = @io.Buffer::new(size_hint=1024)
  let perm_table = init_noise(seed)
  let bounds = @image.rect(0, 0, width, height)
  if isGrayScale {
    let img = @image.Gray::new(bounds)
    for y in 0.. 255.0 {
          255
        } else {
          raw_val.to_int()
        }
        img.set_gray(x, y, @color.Gray::new(gray_val.to_byte()))
      }
    }
    ignore(@png.encode(buf, img))
  } else {
    let img = @image.RGBA::new(bounds)
    for y in 0..