diff --git a/app/src/main/scala/app/logorrr/model/LogEntryFileReader.scala b/app/src/main/scala/app/logorrr/model/LogEntryFileReader.scala index c05fdd92..52fd273f 100644 --- a/app/src/main/scala/app/logorrr/model/LogEntryFileReader.scala +++ b/app/src/main/scala/app/logorrr/model/LogEntryFileReader.scala @@ -12,7 +12,7 @@ import java.util object LogEntryFileReader extends CanLog { private def mkLogEntryList(parseEntryForTimeInstant: String => Option[Instant]) - (logFilePath: Path): ObservableList[LogEntry] = timeR({ + (logFilePath: Path): ObservableList[LogEntry] = { var lineNumber: Int = 0 val arraylist = new util.ArrayList[LogEntry]() LogFileReader.readFromFile(logFilePath).map(l => { @@ -20,13 +20,13 @@ object LogEntryFileReader extends CanLog { arraylist.add(LogEntry(lineNumber, l, parseEntryForTimeInstant(l))) }) FXCollections.observableList(arraylist) - }, s"Imported ${logFilePath.toAbsolutePath.toString} ... ") + } - def from(logFilePath: Path, filters: Seq[Fltr], logEntryTimeFormat: LogEntryInstantFormat): ObservableList[LogEntry] = { + def from(logFilePath: Path, logEntryTimeFormat: LogEntryInstantFormat): ObservableList[LogEntry] = { mkLogEntryList(l => LogEntryInstantFormat.parseInstant(l, logEntryTimeFormat))(logFilePath) } - def from(logFile: Path, filters: Seq[Fltr]): ObservableList[LogEntry] = mkLogEntryList(_ => None)(logFile) + def from(logFile: Path): ObservableList[LogEntry] = mkLogEntryList(_ => None)(logFile) } diff --git a/app/src/main/scala/app/logorrr/model/LogFileReader.scala b/app/src/main/scala/app/logorrr/model/LogFileReader.scala index 45658df8..76b7c14e 100644 --- a/app/src/main/scala/app/logorrr/model/LogFileReader.scala +++ b/app/src/main/scala/app/logorrr/model/LogFileReader.scala @@ -14,15 +14,10 @@ object LogFileReader extends CanLog { OsxBridge.registerPath(logFile.toAbsolutePath.toString) } val lines = FileManager.fromPath(logFile) - logEmptyLogFile(logFile, lines) - lines - } - - private def logEmptyLogFile(logFile: Path, lines: Seq[String]): Unit = { if (lines.isEmpty) { logWarn(s"${logFile.toAbsolutePath.toString} was empty.") - } else { - // logTrace(s"${logFile.toAbsolutePath.toString} has ${lines.size} lines.") } + lines } + } diff --git a/app/src/main/scala/app/logorrr/model/LogFileSettings.scala b/app/src/main/scala/app/logorrr/model/LogFileSettings.scala index 00be75dd..3850786c 100644 --- a/app/src/main/scala/app/logorrr/model/LogFileSettings.scala +++ b/app/src/main/scala/app/logorrr/model/LogFileSettings.scala @@ -77,14 +77,13 @@ case class LogFileSettings(pathAsString: String def readEntries(): ObservableList[LogEntry] = { if (isPathValid) { Try(someLogEntryInstantFormat match { - case Some(instantFormat) => LogEntryFileReader.from(path, filters, instantFormat) - case None => LogEntryFileReader.from(path, filters) + case Some(instantFormat) => LogEntryFileReader.from(path, instantFormat) + case None => LogEntryFileReader.from(path) }) match { case Success(logEntries) => - // logTrace(s"Opened $pathAsString ... ") logEntries case Failure(ex) => - val msg = s"Could not import file $pathAsString" + val msg = s"Could not load file $pathAsString" logException(msg, ex) FXCollections.observableArrayList() } diff --git a/app/src/main/scala/app/logorrr/views/block/BlockImage.scala b/app/src/main/scala/app/logorrr/views/block/BlockImage.scala index 7744afb5..1b2b60eb 100644 --- a/app/src/main/scala/app/logorrr/views/block/BlockImage.scala +++ b/app/src/main/scala/app/logorrr/views/block/BlockImage.scala @@ -4,14 +4,12 @@ import app.logorrr.model.LogEntry import app.logorrr.util.{CanLog, ColorUtil, JfxUtils} import app.logorrr.views.search.Filter import javafx.beans.property.{SimpleIntegerProperty, SimpleListProperty, SimpleObjectProperty} +import javafx.beans.value.ChangeListener import javafx.beans.{InvalidationListener, Observable} -import javafx.collections.FXCollections -import javafx.geometry.Rectangle2D -import javafx.scene.image.{PixelBuffer, PixelFormat, WritableImage} +import javafx.scene.image.WritableImage import javafx.scene.paint.Color -import java.nio.IntBuffer -import scala.jdk.CollectionConverters.ListHasAsScala +import scala.jdk.CollectionConverters.CollectionHasAsScala object BlockImage { @@ -23,189 +21,81 @@ object BlockImage { } -class BlockImage extends CanLog { - val filtersProperty = new SimpleListProperty[Filter]() +class BlockImage(name: String + , widthProperty: SimpleIntegerProperty + , blockSizeProperty: SimpleIntegerProperty + , entriesProperty: SimpleListProperty[LogEntry] + , filtersProperty: SimpleListProperty[Filter] + , selectedElemProperty: SimpleObjectProperty[LogEntry]) extends CanLog { - var pixelBuffer: PixelBuffer[IntBuffer] = _ - var intBuffer: IntBuffer = _ - var rawInts: Array[Int] = _ - var background: Array[Int] = _ - var roi: Rectangle2D = _ + var lpb: LPixelBuffer = _ - private val redrawListener: InvalidationListener = (_: Observable) => repaint() + /** + * height property is calculated on the fly depending on the blockwidth/blockheight, + * width of BlockImage, number of elements and max size of possible of texture (4096). + * */ + val heightProperty: SimpleIntegerProperty = new SimpleIntegerProperty() - val entries = new SimpleListProperty[LogEntry](FXCollections.observableArrayList()) + val imageProperty = new SimpleObjectProperty[WritableImage]() - /* if blockwidth is changed redraw */ - val blockWidthProperty: SimpleIntegerProperty = { - val p = new SimpleIntegerProperty() - p.addListener(redrawListener) - p - } + private val heightListener: ChangeListener[Number] = JfxUtils.onNew[Number](height => { + // logTrace("heightListener " + height) + resetBackingImage(getWidth, height.intValue) + }) - /* if blockheight is changed redraw */ - val blockHeightProperty: SimpleIntegerProperty = { - val p = new SimpleIntegerProperty() - p.addListener(redrawListener) - p + private val blockSizeListener: InvalidationListener = (_: Observable) => { + repaint("blockSizeListener") } - private def getBlockWidth(): Int = blockWidthProperty.get() + val widthListener: ChangeListener[Number] = JfxUtils.onNew[Number](_ => { + repaint("widthListener") + }) - def shutdown() : Unit = { - clearBackingPixelBuffer() + init() + def init(): Unit = { + addListener() + } + + + def shutdown(): Unit = { + lpb = null // just wipe out everything (?!) - Option(pixelBuffer).foreach(_.getBuffer.clear()) - Option(intBuffer).foreach(_.clear()) - this.rawInts = null - this.background = null - this.intBuffer = null - this.pixelBuffer = null - this.roi = null imageProperty.set(null) removeListener() } - def removeListener(): Unit = { - blockWidthProperty.removeListener(redrawListener) - blockHeightProperty.removeListener(redrawListener) - } - - private def getBlockHeight(): Int = blockHeightProperty.get() - - val imageProperty = new SimpleObjectProperty[WritableImage]() - /** - * height property is calculated on the fly depending on the blockwidth/blockheight, - * width of SQImage, number of elements and max size of possible of texture (4096). - * */ - val heightProperty: SimpleIntegerProperty = { - val p = new SimpleIntegerProperty() - p.addListener(JfxUtils.onNew[Number](height => resetBackingImage(getWidth(), height.intValue))) - p - } - val widthProperty: SimpleIntegerProperty = { - val p = new SimpleIntegerProperty() - p.addListener(JfxUtils.onNew[Number](_ => repaint())) - p + def addListener(): Unit = { + heightProperty.addListener(heightListener) + widthProperty.addListener(widthListener) + blockSizeProperty.addListener(blockSizeListener) } - def setHeight(height: Int): Unit = heightProperty.set(height) - - def getHeight(): Int = heightProperty.get() - - def getWidth(): Int = widthProperty.get() - - private def resetBackingImage(width: Int, height: Int): Unit = { - clearBackingPixelBuffer() - - assert(width != 0, s"width was $width.") - assert(height != 0, s"height was $height.") - assert(width <= BlockImage.MaxWidth, s"width was $width which exceeds ${BlockImage.MaxWidth}.") - assert(height <= BlockImage.MaxHeight, s"height was $height which exceeds ${BlockImage.MaxHeight}.") - assert(height * width > 0) - - val bgColor = Color.WHITE - val rawInts = Array.fill(width * height)(ColorUtil.toARGB(Color.WHITE)) - val buffer: IntBuffer = IntBuffer.wrap(rawInts) - val pixelBuffer = new PixelBuffer[IntBuffer](width, height, buffer, PixelFormat.getIntArgbPreInstance) - val backingImage = new WritableImage(pixelBuffer) - this.intBuffer = buffer - this.rawInts = buffer.array() - this.background = Array.fill(width * height)(ColorUtil.toARGB(bgColor)) - this.pixelBuffer = pixelBuffer - this.roi = new Rectangle2D(0, 0, width, height) - this.imageProperty.set(backingImage) + def removeListener(): Unit = { + heightProperty.removeListener(heightListener) + widthProperty.removeListener(widthListener) + blockSizeProperty.removeListener(blockSizeListener) } - def clearBackingPixelBuffer(): Unit = { - Option(this.intBuffer) match { - case Some(value) => - value.clear() - this.rawInts = null - case None => - } + private def resetBackingImage(width: Int, height: Int): Unit = { + lpb = LPixelBuffer(width + , height + , blockSizeProperty + , entriesProperty + , filtersProperty + , selectedElemProperty + , Array.fill(width * height)(ColorUtil.toARGB(Color.WHITE))) + this.imageProperty.set(new WritableImage(lpb)) } - def cleanBackground(): Unit = System.arraycopy(background, 0, rawInts, 0, background.length) - // todo check visibility - def repaint(): Unit = { - Option(pixelBuffer) match { - case Some(pb) => - if (getBlockWidth() != 0) { - pb.updateBuffer((_: PixelBuffer[IntBuffer]) => { - cleanBackground() - var i = 0 - entries.forEach(e => { - drawRect(i, Filter.calcColor(e.value, filtersProperty.asScala.toSeq)) - i = i + 1 - }) - roi - }) - } else { - logWarn(s"getBlockWidth() = ${getBlockWidth()}") - } - case None => // logTrace("pixelBuffer was null") - } - } + def repaint(ctx: String): Unit = lpb.repaint(ctx, filtersProperty.asScala.toSeq, selectedElemProperty.get()) - /** - * draws a filled rectangle on the given index - * - * @param e - */ - def draw(index: Int, color: Color): Unit = { - Option(pixelBuffer) foreach { - pb => - if (getBlockWidth() != 0) { - repaint() - pb.updateBuffer((_: PixelBuffer[IntBuffer]) => { - // drawRect(e.lineNumber - 1, Filter.calcColor(e.value, filtersProperty.asScala.toSeq).darker().darker()) - drawRect(index, color) - roi - }) - } - } - } - - def drawRect(i: Int, color: Color): Unit = { - val width = getWidth() - val nrOfBlocksInX = width / getBlockWidth() - val xPos = (i % nrOfBlocksInX) * getBlockWidth() - val yPos = (i / nrOfBlocksInX) * getBlockHeight() - drawRect( - color - , xPos - , yPos - , getBlockWidth() - , getBlockHeight() - , getWidth() - ) - } - - def drawRect(color: Color - , x: Int - , y: Int - , width: Int - , height: Int - , canvasWidth: Int): Unit = { - - val col = ColorUtil.toARGB(color) - val maxHeight = y + height - val lineArray = Array.fill(width - 1)(col) - for (ly <- y until maxHeight - 1) { - setPixelsAt(ly * canvasWidth + x, lineArray) - } - } + def setHeight(height: Int): Unit = heightProperty.set(height) - def setPixelsAt(destPos: Int, newPixels: Array[Int]): Unit = { - if (destPos + newPixels.length < rawInts.length) { // TODO check should not be necessary?? - System.arraycopy(newPixels, 0, rawInts, destPos, newPixels.length) - } - } + def getWidth: Int = widthProperty.get() } diff --git a/app/src/main/scala/app/logorrr/views/block/BlockView.scala b/app/src/main/scala/app/logorrr/views/block/BlockView.scala index e99b081d..758e9e79 100644 --- a/app/src/main/scala/app/logorrr/views/block/BlockView.scala +++ b/app/src/main/scala/app/logorrr/views/block/BlockView.scala @@ -55,7 +55,8 @@ object BlockView { /** * Displays a region with max 4096 x 4096 pixels and as many entries as can fit in this region. */ -class BlockView(selectedLineNumberProperty: SimpleIntegerProperty +class BlockView(name: String + , selectedLineNumberProperty: SimpleIntegerProperty , filtersProperty: SimpleListProperty[Filter] , blockSizeProperty: SimpleIntegerProperty , outerWidthProperty: ReadOnlyDoubleProperty @@ -66,29 +67,32 @@ class BlockView(selectedLineNumberProperty: SimpleIntegerProperty val entriesProperty = new SimpleListProperty[LogEntry](FXCollections.observableArrayList()) private val onClickListener: ChangeListener[LogEntry] = JfxUtils.onNew { - logEntry => selectedElemProperty.set(logEntry) - blockImage.draw(entriesProperty.indexOf(logEntry), Color.YELLOW) + logEntry => + selectedElemProperty.set(logEntry) + val i = entriesProperty.indexOf(logEntry) + if (i >= 0) { + blockImage.lpb.draw(i, Color.YELLOW) + } else { + logWarn("onClickListener " + i) + } } - val selectedLineNumberListener = new ChangeListener[Number] { + private val selectedLineNumberListener = new ChangeListener[Number] { override def changed(observableValue: ObservableValue[_ <: Number], t: Number, t1: Number): Unit = { if (t.intValue() != t1.intValue()) { - blockImage.draw(t1.intValue(), Color.YELLOW) + blockImage.lpb.draw(t1.intValue(), Color.YELLOW) } } } val selectedEntryProperty: SimpleObjectProperty[LogEntry] = new SimpleObjectProperty[LogEntry]() - private val blockImage = { - val bi = new BlockImage - bi.widthProperty.bind(widthProperty) - bi.blockWidthProperty.bind(blockSizeProperty) - bi.blockHeightProperty.bind(blockSizeProperty) - bi.entries.bind(entriesProperty) - bi.filtersProperty.bind(filtersProperty) - bi - } + private val blockImage = new BlockImage(name + , widthProperty + , blockSizeProperty + , entriesProperty + , filtersProperty + , selectedElemProperty) private val widthListener = JfxUtils.onNew[Number](n => { val scrollPaneWidth = n.intValue() @@ -97,15 +101,14 @@ class BlockView(selectedLineNumberProperty: SimpleIntegerProperty if (proposedWidth > BlockView.MinWidth) { setWidth(proposedWidth) } else { - // logTrace(s"Proposed width ($proposedWidth) < SQView.MinWidth (${BlockView.MinWidth}), not adjusting width of canvas ...") + // logTrace(s"Proposed width ($proposedWidth) < SQView.MinWidth (${BlockView.MinWidth}), not adjusting width of canvas ...") } } else { - // logTrace(s"ScrollPaneWidth ($scrollPaneWidth) >= SQImage.MaxWidth (${BlockImage.MaxWidth}), not adjusting width of canvas ...") + // logTrace(s"ScrollPaneWidth ($scrollPaneWidth) >= SQImage.MaxWidth (${BlockImage.MaxWidth}), not adjusting width of canvas ...") } }) - init() def init(): Unit = { @@ -150,6 +153,7 @@ class BlockView(selectedLineNumberProperty: SimpleIntegerProperty removeListener() removeBindings() } + def setHeight(height: Int): Unit = blockImage.setHeight(height) def setWidth(width: Int): Unit = widthProperty.set(width) @@ -158,6 +162,6 @@ class BlockView(selectedLineNumberProperty: SimpleIntegerProperty def getEntryAt(index: Int): Option[LogEntry] = Try(entriesProperty.get(index)).toOption - def repaint(): Unit = blockImage.repaint() + def repaint(ctx: String): Unit = blockImage.repaint(ctx) } \ No newline at end of file diff --git a/app/src/main/scala/app/logorrr/views/block/BlockViewPane.scala b/app/src/main/scala/app/logorrr/views/block/BlockViewPane.scala index 91d04e18..d1d9507b 100644 --- a/app/src/main/scala/app/logorrr/views/block/BlockViewPane.scala +++ b/app/src/main/scala/app/logorrr/views/block/BlockViewPane.scala @@ -78,62 +78,74 @@ class BlockViewPane(selectedLineNumberProperty: SimpleIntegerProperty) def setEntries(es: ObservableList[LogEntry]): Unit = entriesProperty.setValue(es) - def repaint(): Unit = { - if (isVisible) { + private def doRepaint(): Boolean = { + val visible = isVisible + val blockSizeGreaterZero = getBlockSize() > 0 + val witdhGreaterZero = getWidth.toInt > 0 + val entriesNotEmpty = !entriesProperty.isEmpty + val r = visible && blockSizeGreaterZero && witdhGreaterZero && entriesNotEmpty + if (!r) { + // logTrace(s"Visible: $visible, entriesNotEmpty: $entriesNotEmpty, blockSizeGreaterZero: $blockSizeGreaterZero, widthGreaterZero: $witdhGreaterZero, ") + } + r + } + def repaint(): Unit = { + if (doRepaint()) { val blockSize = getBlockSize() + // free old memory and listeners otherwise we get a memory leak + vbox.getChildren.forEach { + case c: BlockView => c.shutdown() + case _ => + } - if (blockSize > 0 && getWidth.toInt > 0) { - // free old memory and listeners otherwise we get a memory leak - vbox.getChildren.forEach { - case c: BlockView => c.shutdown() - case _ => - } - - val blockHeight: Int = - BlockView.calcVirtualHeight(blockSize, blockSize, getWidth.toInt, getEntriesSize()) - - val blockViews: Seq[BlockView] = { - // if virtual canvas height is lower than maxheight, just create one sqView and be done with it - if (blockHeight <= BlockImage.MaxHeight) { - val blockView = new BlockView(selectedLineNumberProperty, filtersProperty, blockSizeProperty, widthProperty, selectedElemProperty) - blockView.setWidth(getWidth.toInt) - blockView.setHeight(blockHeight) - blockView.setEntries(entriesProperty) - Seq(blockView) - } else { - // if the virtual canvas height exceeds SQImage.MaxHeight, iterate and create new SQViews - val nrOfElemsInRow = (getWidth.toInt / blockSizeProperty.get()) - val nrOfRowsPerSquareView = (BlockImage.MaxHeight / blockSizeProperty.get()) - val nrElemsInSqView = nrOfRowsPerSquareView * nrOfElemsInRow - var curIndex = 0 - val lb = new ListBuffer[BlockView] - - while (curIndex < getEntriesSize()) { - val v = new BlockView(selectedLineNumberProperty, filtersProperty, blockSizeProperty, widthProperty, selectedElemProperty) - v.setWidth(getWidth.toInt) - val end = if (curIndex + nrElemsInSqView < getEntriesSize()) { - curIndex + nrElemsInSqView - } else { - getEntriesSize() - } - val blockViewEntries = entriesProperty.subList(curIndex, end) - v.setEntries(blockViewEntries) - v.setHeight(BlockView.calcVirtualHeight(blockSize, blockSize, getWidth.toInt, blockViewEntries.size)) - lb.addOne(v) - curIndex = curIndex + nrElemsInSqView + val blockHeight: Int = + BlockView.calcVirtualHeight(blockSize, blockSize, getWidth.toInt, getEntriesSize) + + val blockViews: Seq[BlockView] = { + // if virtual canvas height is lower than maxheight, just create one sqView and be done with it + if (blockHeight <= BlockImage.MaxHeight) { + val name = s"0_${getEntriesSize}" + val blockView = new BlockView(name + , selectedLineNumberProperty + , filtersProperty + , blockSizeProperty + , widthProperty + , selectedElemProperty) + blockView.setWidth(getWidth.toInt) + blockView.setHeight(blockHeight) + blockView.setEntries(entriesProperty) + Seq(blockView) + } else { + // if the virtual canvas height exceeds SQImage.MaxHeight, iterate and create new SQViews + val nrOfElemsInRow = getWidth.toInt / blockSizeProperty.get() + val nrOfRowsPerSquareView = BlockImage.MaxHeight / blockSizeProperty.get() + val nrElemsInSqView = nrOfRowsPerSquareView * nrOfElemsInRow + var curIndex = 0 + val lb = new ListBuffer[BlockView] + + while (curIndex < getEntriesSize) { + val end = if (curIndex + nrElemsInSqView < getEntriesSize) { + curIndex + nrElemsInSqView + } else { + getEntriesSize } - lb.toSeq + val blockViewEntries = entriesProperty.subList(curIndex, end) + val name = s"${curIndex}_$end" + val blockView = + new BlockView(name, selectedLineNumberProperty, filtersProperty, blockSizeProperty, widthProperty, selectedElemProperty) + blockView.setWidth(getWidth.toInt) + blockView.setEntries(blockViewEntries) + blockView.setHeight(BlockView.calcVirtualHeight(blockSize, blockSize, getWidth.toInt, blockViewEntries.size)) + lb.addOne(blockView) + curIndex = curIndex + nrElemsInSqView } + lb.toSeq } - - vbox.getChildren.setAll(blockViews: _*) - blockViews.foreach(_.repaint()) - } else { - logWarn(s"Blocksize: $blockSize, getWidth: $getWidth ") } - } else { - // logTrace("invisible ...") + + vbox.getChildren.setAll(blockViews: _*) + blockViews.foreach(_.repaint("blockview")) } () } diff --git a/app/src/main/scala/app/logorrr/views/block/LPixelBuffer.scala b/app/src/main/scala/app/logorrr/views/block/LPixelBuffer.scala new file mode 100644 index 00000000..334cb26d --- /dev/null +++ b/app/src/main/scala/app/logorrr/views/block/LPixelBuffer.scala @@ -0,0 +1,113 @@ +package app.logorrr.views.block + +import app.logorrr.model.LogEntry +import app.logorrr.util.{CanLog, ColorUtil} +import app.logorrr.views.search.Filter +import javafx.beans.property.{SimpleIntegerProperty, SimpleListProperty, SimpleObjectProperty} +import javafx.geometry.Rectangle2D +import javafx.scene.image.{PixelBuffer, PixelFormat} +import javafx.scene.paint.Color + +import java.nio.IntBuffer +import scala.jdk.CollectionConverters.CollectionHasAsScala + +case class LPixelBuffer(width: Int + , height: Int + , blockSizeProperty: SimpleIntegerProperty + , entriesProperty: SimpleListProperty[LogEntry] + , filtersProperty: SimpleListProperty[Filter] + , selectedEntryProperty: SimpleObjectProperty[LogEntry] + , rawInts: Array[Int]) extends { + private val buffer: IntBuffer = IntBuffer.wrap(rawInts) +} with PixelBuffer[IntBuffer](width, height, buffer, PixelFormat.getIntArgbPreInstance) with CanLog { + + assert(width != 0, s"width was $width.") + assert(height != 0, s"height was $height.") + assert(width <= BlockImage.MaxWidth, s"width was $width which exceeds ${BlockImage.MaxWidth}.") + assert(height <= BlockImage.MaxHeight, s"height was $height which exceeds ${BlockImage.MaxHeight}.") + assert(height * width > 0) + + lazy val background: Array[Int] = Array.fill(width * height)(ColorUtil.toARGB(Color.AZURE)) + + private val roi = new Rectangle2D(0, 0, width, height) + + private def cleanBackground(): Unit = System.arraycopy(background, 0, rawInts, 0, background.length) + + def blockSize: Int = blockSizeProperty.get() + + // todo check visibility + def repaint(ctx: String + , filters: Seq[Filter] + , selectedEntry: LogEntry): Unit = timeR({ + if (blockSize != 0) { + if (Option(filters).isEmpty) { + logWarn("filters is null") + } else + updateBuffer((_: PixelBuffer[IntBuffer]) => { + cleanBackground() + var i = 0 + entriesProperty.forEach(e => { + if (e.equals(selectedEntry)) { + drawRect(i, Color.YELLOW) + } else { + drawRect(i, Filter.calcColor(e.value, filters)) + } + i = i + 1 + }) + roi + }) + } else { + logWarn(s"getBlockWidth() = $blockSize") + } + }, s"$ctx repaint") + + + /** + * draws a filled rectangle on the given index + */ + def draw(index: Int, color: Color): Unit = { + if (blockSize != 0) { + repaint(s"draw[$index]", filtersProperty.get().asScala.toSeq, selectedEntryProperty.get()) + updateBuffer((_: PixelBuffer[IntBuffer]) => { + // drawRect(e.lineNumber - 1, Filter.calcColor(e.value, filtersProperty.asScala.toSeq).darker().darker()) + drawRect(index, color) + roi + }) + } + } + + private def drawRect(i: Int, color: Color): Unit = { + val nrOfBlocksInX = width / blockSize + val xPos = (i % nrOfBlocksInX) * blockSize + val yPos = (i / nrOfBlocksInX) * blockSize + drawRect( + color + , xPos + , yPos + , blockSize + , blockSize + , width + ) + } + + private def drawRect(color: Color + , x: Int + , y: Int + , width: Int + , height: Int + , canvasWidth: Int): Unit = { + + val col = ColorUtil.toARGB(color) + val maxHeight = y + height + val lineArray = Array.fill(width - 1)(col) + for (ly <- y until maxHeight - 1) { + val pos = ly * canvasWidth + x + lineArray.length + if (pos >= 0 && pos < rawInts.length) { + System.arraycopy(lineArray, 0, rawInts, ly * canvasWidth + x, lineArray.length) + } else { + // logWarn("out of bounds:" + (ly * canvasWidth + x) + " ly " + ly + ", rawints.length " + rawInts.length + "!") + } + } + } + +} diff --git a/app/src/test/scala/app/logorrr/LogEntryFileReaderSpec.scala b/app/src/test/scala/app/logorrr/LogEntryFileReaderSpec.scala index 24e72070..1ffadeb3 100644 --- a/app/src/test/scala/app/logorrr/LogEntryFileReaderSpec.scala +++ b/app/src/test/scala/app/logorrr/LogEntryFileReaderSpec.scala @@ -16,7 +16,7 @@ class LogEntryFileReaderSpec extends AnyWordSpec { "exist" in assert(Files.exists(p)) "be readable" in { if (!OsUtil.isMac) { // fixme: currently this guard exists since otherwise we would have to fiddle around loading native libs on mac - val r = LogEntryFileReader.from(p, Seq()) + val r = LogEntryFileReader.from(p) assert(!r.isEmpty) } }