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

eu.joaocosta.minart.backend.SdlSurface.scala Maven / Gradle / Ivy

The newest version!
package eu.joaocosta.minart.backend

import scala.scalanative.unsafe.*
import scala.scalanative.unsigned.*

import sdl2.all.*
import sdl2.enumerations.SDL_BlendMode.*

import eu.joaocosta.minart.graphics.{BlendMode, Color, MutableSurface, Surface}

/** Mutable surface backed by an SDL surface.
  *
  * This class assumes that the surface is in `SDL_PIXELFORMAT_ARGB8888`.
  * It also does not free the surface, that's expected to be handled manually.
  *
  * However, when not in use anymore, one should call `cleanup()` to cleanup
  * some temporary resources.
  */
final class SdlSurface(val data: Ptr[SDL_Surface]) extends MutableSurface {

  val width: Int       = (!data).w
  val height: Int      = (!data).h
  private val renderer = SDL_CreateSoftwareRenderer(data)
  SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE)
  private val dataBuffer = (!data).pixels.asInstanceOf[Ptr[Int]]

  def unsafeGetPixel(x: Int, y: Int): Color = {
    Color.fromARGB(dataBuffer(y * width + x))
  }

  def unsafePutPixel(x: Int, y: Int, color: Color): Unit = dataBuffer(y * width + x) = color.argb

  override def fill(color: Color): Unit = {
    SDL_SetRenderDrawColor(renderer, color.r.toUByte, color.g.toUByte, color.b.toUByte, color.a.toUByte)
    SDL_RenderClear(renderer)
  }

  def fillRegion(x: Int, y: Int, w: Int, h: Int, color: Color): Unit = {
    val x1 = Math.max(x, 0)
    val y1 = Math.max(y, 0)
    val x2 = Math.min(x + w, width)
    val y2 = Math.min(y + h, height)
    if (x1 != x2 && y1 != y2) {
      val _w = x2 - x1
      val _h = y2 - y1
      SDL_SetRenderDrawColor(renderer, color.r.toUByte, color.g.toUByte, color.b.toUByte, color.a.toUByte)
      val rect = stackalloc[SDL_Rect]()
      (!rect).x = x1
      (!rect).y = y1
      (!rect).w = _w
      (!rect).h = _h
      SDL_RenderFillRect(renderer, rect)
    }
  }

  override def blit(
      that: Surface,
      blendMode: BlendMode = BlendMode.Copy
  )(x: Int, y: Int, cx: Int = 0, cy: Int = 0, cw: Int = that.width, ch: Int = that.height): Unit =
    (that, blendMode) match {
      case (img: SdlSurface, BlendMode.Copy) =>
        val srcRect = stackalloc[SDL_Rect]()
        (!srcRect).x = cx
        (!srcRect).y = cy
        (!srcRect).w = cw
        (!srcRect).h = ch
        val dstRect = stackalloc[SDL_Rect]()
        (!dstRect).x = x
        (!dstRect).y = y
        (!dstRect).w = cw
        (!dstRect).h = ch
        SDL_UpperBlit(img.data, srcRect, this.data, dstRect)
      case _ =>
        super.blit(that, blendMode)(x, y, cx, cy, cw, ch)
    }

  /** Cleans up the internal datastructures used by this surface.
    *
    * Note that the underlying data is not freed by this method.
    */
  def cleanup(): Unit = {
    SDL_DestroyRenderer(renderer)
  }
}

object SdlSurface {
  /* Converts a raw SDL surface to ARGB 8888, so that it can be used by SdlSurface.
   *
   * This method will only copy the surface if necessary.
   * When that happens, the original pointer will be invalidated.
   *
   * As such, the pointer used to call this method should never be reused, only the
   * pointer returned.
   *
   * @param original original data to convert
   */
  def ensureARGBFormat(original: Ptr[SDL_Surface]): Ptr[SDL_Surface] = {
    val originalFormat = (!(!original).format).format
    if (originalFormat == SDL_PixelFormatEnum.SDL_PIXELFORMAT_ARGB8888.uint) {
      original
    } else {
      val formattedSurface =
        SDL_ConvertSurfaceFormat(original, SDL_PixelFormatEnum.SDL_PIXELFORMAT_RGBA32.uint, 0.toUInt)
      SDL_FreeSurface(original)
      formattedSurface
    }
  }

  /** Processes a raw SDL surface as if it was a Minart SdlSurface.
    *
    * The temporary surface is released after the code block is executed, so it should
    * be returned.
    *
    * This method assumes that the data is in RGBA32.
    *
    * @param data raw SDL data
    * @param f operation to apply
    * @return result of the operation
    */
  inline def withRawData[T](data: Ptr[SDL_Surface])(inline f: SdlSurface => T): T = {
    val tempSurface = new SdlSurface(data)
    val ret         = f(tempSurface)
    tempSurface.cleanup()
    ret
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy