All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.pawelkrol.Afterimage.Colour.Palette.scala Maven / Gradle / Ivy

The newest version!
package com.github.pawelkrol.Afterimage
package Colour

import org.json4s.{JArray, JObject}
import org.json4s.MonadicJValue.jvalueToMonadic
import org.json4s.native.JsonMethods.parse

import scala.math.Ordering.Double.TotalOrdering

import Util.Util.listResources

/** Colour palette which maps C64 colours from/to RGB colours.
  *
  * @constructor create a new colour palette with a set of 16 colours
  * @param colours definition of 16 colour mappings (C64 from/to RGB)
  */
case class Palette(colours: Array[Colour]) {

  require(
    colours.length == 16,
    "Invalid number of palette colours: got %d, but expected an array of 16 colours".format(colours.length)
  )

  /** Returns RGB colour for a given C64 colour.
    *
    * @param value C64 colour value (expected to be integer between 0 and 15)
    */
  def apply(value: Int) = {

    require(
      value >= 0 && value <= 15,
      "Invalid palette colour index: got %d, but expected an integer between 0 and 15".format(value)
    )

    colours(value)
  }

  /** Returns optional RGB colour for a given C64 colour name as defined in a colour palette data.
    *
    * @param name C64 colour name (expected to be a string matching colour name from a colour palette)
    */
  def apply(name: String) = {
    colours.find(_.name match {
      case Some(colourName) => name == colourName
      case None => false
    })
  }

  /** Returns ImageJ colour for a given C64 colour.
    *
    * @param value C64 colour value (expected to be integer between 0 and 15)
    */
  def pixel(value: Int) = this(value).pixel

  /** Returns C64 colour for a given RGB colour.
    *
    * @param colour RGB colour to be resolved into the closest matching C64 hue
    */
  def get(colour: Colour) = colours.zipWithIndex.minBy(_._1.delta_to(colour))(TotalOrdering)._2

  def canEqual(that: Any) = that.isInstanceOf[Palette]

  override def equals(other: Any) = other match {
    case that: Palette =>
      (that canEqual this) && (this.colours.toList == that.colours.toList)
    case _ =>
      false
  }

  /** Serialises colour palette to a JSON object.
    */
  def toJson = JObject(List(("palette", JArray(colours.map(_.toJson).toList))))
}

/** Factory for [[com.github.pawelkrol.Afterimage.Colour.Palette]] instances. */
object Palette {

  private def build(name: String) = {

    val filename = "/palettes/%s.json".format(name)
    val inputStream = getClass().getResourceAsStream(filename)

    val source = scala.io.Source.fromInputStream(inputStream)(scala.io.Codec.UTF8)

    parseJSON(source.mkString)
  }

  private def load(file: String) = {

    val source = scala.io.Source.fromFile(file)(scala.io.Codec.UTF8)

    parseJSON(source.mkString)
  }

  private def parseJSON(source: String) = {

    val palette = (parse(source) \ "palette").children

    val colours = palette.map(item => {
      item match {
        case colour: JObject => Colour(colour.values)
        case _ => throw new RuntimeException
      }
    }).toArray

    new Palette(colours)
  }

  import org.apache.commons.lang3.StringUtils

  private val availableColourPalettes =
    listResources("/palettes").filter(_.endsWith(".json")).map(fileName => "'%s'".format(StringUtils.removeEnd(fileName, ".json"))).mkString(", ")

  /** Creates a colour palette from a given JSON configuration file or as
    * a fallback from a pre-configured template name (if file does not exist
    * and template name does not match any predefined colour palette template
    * names, a runtime exception will be throw).
    *
    * @param name JSON configuration file or template name
    */
  def apply(name: String) = {
    try {
      fromFile(name)
    }
    catch {
      case _: Throwable =>
        try {
          fromTemplate(name)
        }
        catch {
          case _: IllegalArgumentException =>
            throw new IllegalArgumentException("Invalid colour palette: '%s' (no such file or template found, expected one of: %s)".format(name, availableColourPalettes))
          case e: Throwable =>
            throw new RuntimeException("Something went wrong: %s".format(e.getMessage()))
        }
    }
  }

  /** Creates a colour palette from a given template name.
    *
    * @param name template name, one of the currently available names: default
    */
  def fromTemplate(name: String) = {
    try {
      build(name)
    }
    catch {
      case e: NoClassDefFoundError =>
        throw new RuntimeException("Library missing from CLASSPATH: %s".format(e.getMessage()))
      case e: Throwable =>
        throw new IllegalArgumentException("Invalid colour palette template: check '%s' configuration".format(name))
    }
  }

  /** Creates a colour palette from a given JSON configuration file.
    *
    * @param name JSON configuration file with a customised colour palette
    */
  def fromFile(name: String) = {
    try {
      load(name)
    }
    catch {
      case _: Throwable =>
        throw new IllegalArgumentException("Invalid colour palette setup: malformed JSON data in '%s' file".format(name))
    }
  }

  /** Creates a colour palette from a given plain JSON string.
    *
    * @param json Plain JSON string with a customised colour palette
    */
  def fromJson(json: String) = {
    try {
      parseJSON(json)
    }
    catch {
      case _: Throwable =>
        throw new IllegalArgumentException("Invalid colour palette setup: malformed JSON data in input string")
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy