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

com.github.pawelkrol.Afterimage.Mode.MultiColour.scala Maven / Gradle / Ivy

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

import Mode.Data.{Bitmap,Screen}
import Mode.Data.Row.{MultiColourRow,Row}

/** MultiColour image data abstraction providing convenient access to the bitmap and screen colours.
  *
  * @constructor create a new `MultiColour` image data
  * @param bitmap plain hi-resolution bitmap data of an image
  * @param screen screen portion of colours data of an image
  * @param colors colors portion of colours data of an image
  * @param border optional single byte of image border colour
  * @param bckgrd single byte of image background colour
  */
case class MultiColour(
  val bitmap: Bitmap,
  val screen: Screen,
  val colors: Screen,
  val border: Option[Byte],
  val bckgrd: Byte
) extends CBM {

  /** An actual pixel width of this MultiColour image. */
  val width = MultiColour.maxWidth

  /** An actual pixel height of this MultiColour image. */
  val height = MultiColour.maxHeight

  /** Image pixel width rounded up to the right margin of an 8x8 character. */
  val widthRounded = {
    val addition = if ((width & 0x03) != 0) 0x04 else 0x00
    (width & 0xfc) + addition
  }

  /** Image pixel height rounded up to the bottom margin of an 8x8 character. */
  val heightRounded = {
    val addition = if ((height & 0x07) != 0) 0x08 else 0x00
    (height & 0xf8) + addition
  }

  /** An actual char width of this MultiColour image. */
  val numCharCols = widthRounded / 4

  /** An actual char height of this MultiColour image. */
  val numCharRows = heightRounded / 8

  validate()

  /** Validates consistency of an object instance data. */
  def validate(): Unit = {
    require(bitmap.get().length == MultiColour.size("bitmap"), "Invalid %s image data".format(imageMode))
    require(screen.get().length == MultiColour.size("screen"), "Invalid %s image data".format(imageMode))
    require(colors.get().length == MultiColour.size("colors"), "Invalid %s image data".format(imageMode))
  }

  private def pixelBits(x: Int, y: Int) = bitmap.getPixels(2 * x, y, 2)

  private def colour(x: Int, y: Int, bits: Byte): Byte = {
    val colour: Int = bits match {
      case 0x00 => bckgrd
      case 0x01 => (screen(x, y) & 0xf0) >> 0x04
      case 0x02 => screen(x, y) & 0x0f
      case 0x03 => colors(x, y) & 0x0f
      case _ => throw new RuntimeException("Something went wrong...")
    }
    colour.toByte
  }

  /** Returns the C64 colour of the pixel at [x,y].
    *
    * @param x X coordinate of a requested pixel
    * @param y Y coordinate of a requested pixel
    */
  def pixel(x: Int, y: Int) = {
    validatePixelCoordinates(x, y)

    val bits = pixelBits(x, y)

    val (screenX, screenY) = pixelCoordinatesToScreen(x * 0x02, y)
    colour(screenX, screenY, bits)
  }

  /** Returns a new MultiColourSlice instance with truncated contents of the image.
   *
   * @param fromY first screen row of a rectangular selection area considered as a number of 8x8 character blocks
   * @param toY last screen row a rectangular selection area (inclusive) considered as a number of 8x8 character blocks
   */
  def slice(fromY: Int, toY: Int) = {

    val minY = 0
    val maxY = numCharRows - 1

    require(
      fromY >= minY && toY <= maxY,
      "Invalid slice selection area in MultiColour mode requested: got %s, but expected rows between 0 and %s".format(
        "[%d,%d]".format(fromY, toY),
        maxY
      )
    )

    val screenHeight = toY - fromY + 1

    val newWidth = widthRounded * 0x02
    val newHeight = screenHeight * 0x08

    val y = fromY * 0x08

    val newBitmap = bitmap.slice(0, y, newWidth, newHeight)
    val newScreen = screen.slice(0, fromY, numCharCols, screenHeight)
    val newColors = colors.slice(0, fromY, numCharCols, screenHeight)

    MultiColourSlice(newBitmap, newScreen, newColors, border, bckgrd, widthRounded, newHeight)
  }

  /** Returns image data as an array of multicolour rows ([[com.github.pawelkrol.Afterimage.Mode.Data.Row.MultiColourRow]] objects).
    *
    * Note that fetching rows data from an image slice will also always return an array of full rows with 40 columns length each!
    */
  def rows = (0 to numCharRows - 1).map(row =>
    MultiColourRow(
      Row.getBitmapRow(row, bitmap.get(), widthRounded * 0x02),
      Row.getScreenRow(row, screen.get(), numCharCols),
      Row.getColorsRow(row, colors.get(), numCharCols),
      bckgrd
    )
  ).toArray

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

  override def equals(other: Any) = other match {
    case that: MultiColour =>
      (that canEqual this) && (this.bitmap == that.bitmap) && (this.screen == that.screen) && (this.colors == that.colors) && (this.border == that.border) && (this.bckgrd == that.bckgrd)
    case _ =>
      false
  }
}

/** Factory for [[com.github.pawelkrol.Afterimage.Mode.MultiColour]] instances. */
object MultiColour {

  /** Maximum possible pixel width of a MultiColour image. */
  val maxWidth = 160

  /** Maximum possible pixel height of a MultiColour image. */
  val maxHeight = 200

  /** Default size of raw byte arrays comprising a MultiColour image. */
  val size = Map[String,Int](
    "bitmap" -> 0x1f40,
    "screen" -> 0x03e8,
    "colors" -> 0x03e8
  )

  /** Creates an empty MultiColour image. */
  def apply(): MultiColour =
    MultiColour(
      bitmap = Array.fill(MultiColour.size("bitmap")){0x00},
      screen = Array.fill(MultiColour.size("screen")){0x00},
      colors = Array.fill(MultiColour.size("colors")){0x00},
      bckgrd = 0x00
    )

  /** Creates a new MultiColour image with a given bitmap data and screen, background and border colours.
    *
    * @param bitmap array of 8000 raw bytes with image bitmap data
    * @param screen array of 1000 raw bytes with image screen data
    * @param colors array of 1000 raw bytes with image colors data
    * @param bckgrd single byte of image background colour
    * @param border single byte of image border colour
    */
  def apply(bitmap: Array[Byte], screen: Array[Byte], colors: Array[Byte], bckgrd: Byte, border: Byte): MultiColour =
    MultiColour(
      Bitmap(bitmap, Bitmap.maxCols, Bitmap.maxRows),
      Screen(screen, Screen.maxCols, Screen.maxRows),
      Screen(colors, Screen.maxCols, Screen.maxRows),
      Some(border),
      bckgrd
    )

  /** Creates a new MultiColour image with a given bitmap data and screen and background colours.
    *
    * @param bitmap array of 8000 raw bytes with image bitmap data
    * @param screen array of 1000 raw bytes with image screen data
    * @param colors array of 1000 raw bytes with image colors data
    * @param bckgrd single byte of image background colour
    */
  def apply(bitmap: Array[Byte], screen: Array[Byte], colors: Array[Byte], bckgrd: Byte): MultiColour =
    MultiColour(
      Bitmap(bitmap, Bitmap.maxCols, Bitmap.maxRows),
      Screen(screen, Screen.maxCols, Screen.maxRows),
      Screen(colors, Screen.maxCols, Screen.maxRows),
      None,
      bckgrd
    )
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy