Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance renderer #24

Merged
merged 1 commit into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/pixel-profile/src/cards/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Resvg } from '@resvg/resvg-js'
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import satori from 'satori'
// import {glow} from "../shaders/glow";

export type Stats = {
name: string
Expand Down Expand Up @@ -149,6 +150,7 @@ export async function renderStats(stats: Stats, options: Options = {}): Promise<

if (screenEffect) {
pixels = scanline(pixels, width, height)
// pixels = glow(pixels, width, height)
pixels = curve(pixels, width, height)
}

Expand Down
14 changes: 11 additions & 3 deletions packages/pixel-profile/src/renderer/render.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { coordsToIndex, FragShader } from './common'
import { clamp } from '../utils'
import { Coordinates, coordsToIndex, FragShader, RGBA } from './common'
import { TEXTURE_FILTER, textureFilterGeneratorByName, TextureFilterName } from './texture-filter'

type Options = {
Expand All @@ -18,11 +19,18 @@ export function render(
const maxX = width - 1
const maxY = height - 1

const texture2D = textureFilterGeneratorByName[textureFilter](pixels, width, height)
const textureFilterFn = textureFilterGeneratorByName[textureFilter](pixels, width, height)

function texture2D(coords: Coordinates): RGBA {
coords[0] = clamp(coords[0], 0, maxX)
coords[1] = clamp(coords[1], 0, maxY)

return textureFilterFn(coords)
}

for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const rgba = fragShader([x / maxX, y / maxY], texture2D)
const rgba = fragShader([x, y], texture2D)
const index = coordsToIndex(x, y, width)
target[index] = rgba[0]
target[index + 1] = rgba[1]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ export function genBiLinearFilter(pixels: Buffer, width: number, height: number)
return tmp1 * (1 - sy) + tmp2 * sy
}
function biLinearFilter(coords: Coordinates): RGBA {
coords[0] = clamp(coords[0], 0, 1)
coords[1] = clamp(coords[1], 0, 1)

const x = coords[0] * maxX
const y = coords[1] * maxY
const x = coords[0]
const y = coords[1]
const x0 = clamp(Math.floor(x), 0, maxX)
const x1 = clamp(x0 + 1, 0, maxX)
const y0 = clamp(Math.floor(y), 0, maxY)
Expand Down
16 changes: 3 additions & 13 deletions packages/pixel-profile/src/renderer/texture-filter/nearest.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { clamp } from '../../utils'
import { Coordinates, coordsToPixel, RGBA, Texture2D } from '../common'

export function genNearestNeighborFilter(pixels: Buffer, width: number, height: number): Texture2D {
const maxX = width - 1
const maxY = height - 1

export function genNearestNeighborFilter(pixels: Buffer, width: number): Texture2D {
function nearestNeighborFilter(coords: Coordinates): RGBA {
coords[0] = clamp(coords[0], 0, 1)
coords[1] = clamp(coords[1], 0, 1)

const x = coords[0] * maxX
const y = coords[1] * maxY

const nearestX = Math.round(x)
const nearestY = Math.round(y)
const nearestX = Math.round(coords[0])
const nearestY = Math.round(coords[1])

return coordsToPixel(pixels, nearestX, nearestY, width)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/pixel-profile/src/shaders/border.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export function addBorder(
(uv, texture2D) => {
const maxX = width - 1
const maxY = height - 1
const x = uv[0] * maxX
const y = uv[1] * maxY
const x = uv[0]
const y = uv[1]

const frameWidth = frameWidthRatio * width

Expand Down
11 changes: 8 additions & 3 deletions packages/pixel-profile/src/shaders/curve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ const margin = [0, 0]
const screenCurvature = 0.1

export function curve(source: Buffer, width: number, height: number): Buffer {
return render(source, width, height, (uv, texture2D) => {
return render(source, width, height, (xy, texture2D) => {
const uv = [xy[0] / width, xy[1] / height]

const maxX = width - 1
const maxY = height - 1

function distortCoordinates(coords: Coordinates): Vec2 {
const cc = subtract2(coords, [0.5, 0.5])
const dist = dot2(cc, cc) * screenCurvature
Expand All @@ -17,15 +22,15 @@ export function curve(source: Buffer, width: number, height: number): Buffer {
return add2(coords, cc)
}

const coords = distortCoordinates(uv)
const coords = distortCoordinates([uv[0], uv[1]])

coords[0] = coords[0] * (margin[0] * 2 + 1) - margin[0]
coords[1] = coords[1] * (margin[1] * 2 + 1) - margin[1]

const vignetteCoords: Vec2 = [uv[0] * (1 - uv[1]), uv[1] * (1 - uv[0])]
const vignette = Math.pow(prod2(vignetteCoords) * 15, 0.25)

const samplerColor = texture2D(coords)
const samplerColor = texture2D([coords[0] * maxX, coords[1] * maxY])

return [samplerColor[0] * vignette, samplerColor[1] * vignette, samplerColor[2] * vignette, 255]
})
Expand Down
49 changes: 49 additions & 0 deletions packages/pixel-profile/src/shaders/glow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { render } from '../renderer'
import { add2, Vec2 } from '../utils'
import { add3, divide3, dot3, mix3, Vec3 } from '../utils/math'

const radius = 10
const intensity = 1
const threshold = 0.9
const _threshold = threshold * 255

export function glow(source: Buffer, width: number, height: number): Buffer {
return render(
source,
width,
height,
(uv, texture2D) => {
const originalColor = texture2D(uv)

let bloomColor: Vec3 = [0, 0, 0]
let n = 0

for (let i = -radius; i <= radius; i++) {
for (let j = -radius; j <= radius; j++) {
const offset: Vec2 = [i / width, j / height]
const sampledColor = texture2D(add2(uv, offset))
const luminance = dot3(sampledColor, [0.2126, 0.7152, 0.0722])
if (luminance > _threshold) {
bloomColor = add3(bloomColor, sampledColor)
n++
}
}
}

if (n === 0) {
return originalColor
}

bloomColor = divide3(bloomColor, n)

const _bloomIntensity = intensity * (n / Math.pow(radius * 2 + 1, 2))

const finalColor = mix3(originalColor, bloomColor, _bloomIntensity)

return [finalColor[0], finalColor[1], finalColor[2], 255]
},
{
textureFilter: 'NEAREST'
}
)
}
8 changes: 3 additions & 5 deletions packages/pixel-profile/src/shaders/pixelate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import { render } from '../renderer'

export function pixelate(source: Buffer, width: number, height: number, blockSize: number): Buffer {
return render(source, width, height, (uv, texture2D) => {
const blockW = blockSize / width
const blockH = blockSize / height
const x = Math.floor(uv[0] / blockW)
const y = Math.floor(uv[1] / blockH)
const x = Math.floor(uv[0] / blockSize)
const y = Math.floor(uv[1] / blockSize)

return texture2D([x * blockW + blockW / 2, y * blockH + blockH / 2])
return texture2D([x * blockSize + blockSize / 2, y * blockSize + blockSize / 2])
})
}
4 changes: 1 addition & 3 deletions packages/pixel-profile/src/shaders/scanline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ const scanlineThickness = 3

export function scanline(source: Buffer, width: number, height: number): Buffer {
return render(source, width, height, (uv, texture2D) => {
const scanlinePosition = Math.floor(uv[1] * height)

const onScanline = scanlinePosition % scanlineThickness === 0
const onScanline = uv[1] % scanlineThickness === 0

const samplerColor = texture2D(uv)

Expand Down
19 changes: 19 additions & 0 deletions packages/pixel-profile/src/utils/math.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { RGBA } from '../renderer'

export type Vec2 = [number, number]
export type Vec3 = [number, number, number]

export function clamp(x: number, min: number, max: number): number {
return Math.min(max, Math.max(min, x))
Expand All @@ -25,3 +28,19 @@ export function dot2(a: Vec2, b: Vec2): number {
export function prod2(v: Vec2): number {
return v[0] * v[1]
}

export function add3(a: Vec3 | RGBA, b: Vec3 | RGBA): Vec3 {
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
}

export function dot3(v1: Vec3 | RGBA, v2: Vec3 | RGBA): number {
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
}

export function divide3(v: Vec3 | RGBA, scalar: number): Vec3 {
return [v[0] / scalar, v[1] / scalar, v[2] / scalar]
}

export function mix3(v1: Vec3 | RGBA, v2: Vec3 | RGBA, t: number): Vec3 {
return [v1[0] * (1 - t) + v2[0] * t, v1[1] * (1 - t) + v2[1] * t, v1[2] * (1 - t) + v2[2] * t]
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.