Skip to content

Commit

Permalink
added subpixel filtering for marquee
Browse files Browse the repository at this point in the history
  • Loading branch information
psy0rz committed Dec 16, 2024
1 parent 9491af0 commit a98cf58
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 11 deletions.
2 changes: 1 addition & 1 deletion ledder/PixelList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class PixelList extends Set<Pixel | PixelList> {


/* Creates a traditional x/y raster, with seperate pixels that each get a copy of the color object.
* (Dont use this if you can do it on the more modern ledder-way where each pixel is an object in a container-list)
* Note that this can be more efficient compared to creating/destroying lost of pixel objects for every frame.
* raster[x][y] corresponds to Pixel(x,y)
* @param YX swap array layout so raster[y][x] corresponds to Pixel(x,y)
* @param xFlip Flip x axis: 0,0 corresponds to Pixel(xMax,0)
Expand Down
23 changes: 16 additions & 7 deletions ledder/animations/Text/Marquee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Animator from "../../Animator.js"
import FxTwinkle from "../../fx/FxTwinkle.js"
import FxColorPattern from "../../fx/FxColorPattern.js"
import TheMatrix from "../MovieFx/TheMatrix.js"

import FxSubpixels from "../../fx/FxSubpixels.js"


export default class Marquee extends Animator {
Expand All @@ -33,9 +33,6 @@ export default class Marquee extends Animator {

textPixels.centerV(box)

//target container for scroll-view
// const textBox = new PixelBox(box)

let starsGroup = control.group("Stars", false, false)
if (starsGroup.switch('Enabled', false).enabled) {
new MovingStars().run(box, scheduler, starsGroup)
Expand All @@ -51,10 +48,9 @@ export default class Marquee extends Animator {
new TheMatrix().run(box, scheduler, theMatrixGroup)
}

//add text on top of background stuff
box.add(textPixels)

let scrollGroup = control.group("Scrolling")

let rotatorPromise
if (scrollGroup.switch('Enabled', true).enabled) {

Expand All @@ -64,7 +60,17 @@ export default class Marquee extends Animator {
scheduler.setFps(fpsControl.value)
})

const rotator = new FxRotate(scheduler, scrollGroup)
if (scrollGroup.switch("Subpixel filtering", false).enabled) {
const subpixelFilter = new FxSubpixels(scheduler, control)
const filteredTextPixels = new PixelBox(box)
box.add(filteredTextPixels)
subpixelFilter.run(textPixels, filteredTextPixels)
} else {
box.add(textPixels)
}


const rotator = new FxRotate(scheduler, scrollGroup, -1, 0, 1, 0, 0.01)

//show one time or loop?
let waitX = 0
Expand Down Expand Up @@ -100,8 +106,11 @@ export default class Marquee extends Animator {
textBoundBox.xMin = 0

rotatorPromise = rotator.run(textPixels, textBoundBox, waitX, null)


} else {
//no scrolling
box.add(textPixels)
textPixels.centerH(box)
}

Expand Down
6 changes: 3 additions & 3 deletions ledder/fx/FxRotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export default class FxRotate extends Fx {
intervalRandomizerControl: ControlValue


constructor(scheduler: Scheduler, controlGroup: ControlGroup, xStep = -1, yStep = 0, interval = 2, intervalRandomizer = 0) {
constructor(scheduler: Scheduler, controlGroup: ControlGroup, xStep = -1, yStep = 0, interval = 2, intervalRandomizer = 0, minStep=1) {
super(scheduler, controlGroup)

this.intervalControl = controlGroup.value('Rotate interval', interval, 1, 60, 1)
this.intervalRandomizerControl = controlGroup.value('Rotate interval randomizer', intervalRandomizer, 0, 60, 1, true)
this.xStepControl = controlGroup.value('Rotate X step', xStep, -5, 5, 1)
this.yStepControl = controlGroup.value('Rotate Y step', yStep, -5, 5, 1)
this.xStepControl = controlGroup.value('Rotate X step', xStep, -5, 5, minStep)
this.yStepControl = controlGroup.value('Rotate Y step', yStep, -5, 5, minStep)

}

Expand Down
101 changes: 101 additions & 0 deletions ledder/fx/FxSubpixels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Fx from "../Fx.js"
import ControlGroup from "../ControlGroup.js"
import PixelList from "../PixelList.js"
import Scheduler from "../Scheduler.js"
import PixelBox from "../PixelBox.js"
import {colorBlack} from "../Colors.js"

/**
* This is an additive filter that combines floating point x,y coordinates to exact coordinates.
* Neighbouring pixels are combined to one by adding the colors together by ratio.
* This is used for Marquees, to blur the steps by slow scrolling, making it smoother.
*/
export default class FxSubpixels extends Fx {


constructor(scheduler: Scheduler, controls: ControlGroup) {
super(scheduler, controls)


}

run(sourceList: PixelList, targetBox: PixelBox) {
this.running = true

const targetXY = targetBox.raster(targetBox, colorBlack)


this.scheduler.interval(1, () => {

//reset all colors to 0,0,0,0
targetBox.forEachPixel((p) => {
p.color.r = 0
p.color.g = 0
p.color.b = 0

})


//traverse all the source pixels and update target
sourceList.forEachPixel((p) => {
let x = ~~p.x
let y = ~~p.y

const factorX=p.x-x
const factorY=p.y-y

const sourceColor = p.color


//left top:
if (x<=targetBox.xMax && y<=targetBox.yMax) {
const targetColor = targetXY[x][y].color
const factor = (1 - factorX) * (1 - factorY)

targetColor.r = targetColor.r + (sourceColor.r * factor)
targetColor.g = targetColor.g + (sourceColor.g * factor)
targetColor.b = targetColor.b + (sourceColor.b * factor)
}

//right top:
x=x+1
if (x<=targetBox.xMax && y<=targetBox.yMax) {
const targetColor = targetXY[x][y].color
const factor = (factorX) * (1 - factorY)

targetColor.r = targetColor.r + (sourceColor.r * factor)
targetColor.g = targetColor.g + (sourceColor.g * factor)
targetColor.b = targetColor.b + (sourceColor.b * factor)
}

//right bottom:
y=y+1
if (x<=targetBox.xMax && y<=targetBox.yMax) {
const targetColor = targetXY[x][y].color
const factor = (factorX) * (factorY)

targetColor.r = targetColor.r + (sourceColor.r * factor)
targetColor.g = targetColor.g + (sourceColor.g * factor)
targetColor.b = targetColor.b + (sourceColor.b * factor)
}

//left bottom:
x=x-1
if (x<=targetBox.xMax && y<=targetBox.yMax) {
const targetColor = targetXY[x][y].color
const factor = (1-factorX) * (factorY)

targetColor.r = targetColor.r + (sourceColor.r * factor)
targetColor.g = targetColor.g + (sourceColor.g * factor)
targetColor.b = targetColor.b + (sourceColor.b * factor)
}

})


})

return (this.promise)
}
}

0 comments on commit a98cf58

Please sign in to comment.