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

eu.joaocosta.minart.graphics.image.pdi.PdiImageReader.scala Maven / Gradle / Ivy

The newest version!
package eu.joaocosta.minart.graphics.image.pdi

import java.io.InputStream

import scala.annotation.tailrec

import eu.joaocosta.minart.graphics.*
import eu.joaocosta.minart.graphics.image.*
import eu.joaocosta.minart.internal.*

/** Image reader for PDI files.
  *
  * Supports uncompressed Playdate PDIs.
  */
trait PdiImageReader extends ImageReader {
  import ByteReader.*

  @tailrec
  private def loadBits(
      data: CustomInputStream,
      remainingLines: Int,
      width: Int,
      lineBytes: Int,
      acc: Vector[Array[Boolean]] = Vector()
  ): ParseResult[Vector[Array[Boolean]]] = {
    if (isEmpty(data) || remainingLines == 0) Right(data -> acc)
    else {
      readPaddedBits(width, lineBytes).run(data) match {
        case Left(error) => Left(error)
        case Right((remaining, line)) =>
          loadBits(remaining, remainingLines - 1, width, lineBytes, acc :+ line)
      }
    }
  }

  private def loadHeader(bytes: CustomInputStream): ParseResult[Header] = {
    (for {
      magic <- readString(12).validate(
        PdiImageFormat.supportedFormats,
        m => s"Unsupported format: $m."
      )
      flags       <- readLENumber(4)
      compression <- State.cond((flags & 0x80000000) == 0, false, "Unsuported compression")
      header = Header(magic, compression)
    } yield header).run(bytes)
  }

  private def loadCellHeader(bytes: CustomInputStream): ParseResult[CellHeader] = {
    (for {
      clipWidth  <- readLENumber(2)
      clipHeight <- readLENumber(2)
      cellStride <- readLENumber(2)
      clipLeft   <- readLENumber(2)
      clipRight  <- readLENumber(2)
      clipTop    <- readLENumber(2)
      clipBottom <- readLENumber(2)
      flags      <- readLENumber(2)
      transparency = (flags & 0x0003) != 0
      header = CellHeader(clipWidth, clipHeight, cellStride, clipLeft, clipRight, clipTop, clipBottom, transparency)
    } yield header).run(bytes)
  }

  final def loadImage(is: InputStream): Either[String, RamSurface] = {
    val bytes = fromInputStream(is)
    loadHeader(bytes).flatMap(_ => loadCellHeader(bytes)).flatMap { case (data, cellHeader) =>
      val emptyColor = Color(0, 0, 0, 0)
      val width      = cellHeader.clipLeft + cellHeader.clipWidth + cellHeader.clipRight
      val leftPad    = Array.fill(cellHeader.clipLeft)(emptyColor)
      val rightPad   = Array.fill(cellHeader.clipRight)(emptyColor)
      for {
        centerColors <- loadBits(data, cellHeader.clipHeight, cellHeader.clipWidth, cellHeader.stride)
        centerMask   <- loadBits(data, cellHeader.clipHeight, cellHeader.clipWidth, cellHeader.stride)
        centerPixels = centerColors._2.zip(centerMask._2).map { (colorLine, maskLine) =>
          colorLine.zip(maskLine).map {
            case (_, false)    => Color(0, 0, 0, 0)
            case (false, true) => Color(0, 0, 0)
            case (true, true)  => Color(255, 255, 255)
          }
        }
        pixels =
          Vector.fill(cellHeader.clipTop)(Array.fill(width)(emptyColor)) ++
            centerPixels.map(center => leftPad ++ center ++ rightPad) ++
            Vector.fill(cellHeader.clipBottom)(Array.fill(width)(emptyColor))
      } yield (new RamSurface(pixels))
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy