diff --git a/effects/scaler/Makefile b/effects/scaler/Makefile new file mode 100644 index 00000000..86a04c5a --- /dev/null +++ b/effects/scaler/Makefile @@ -0,0 +1,8 @@ +TOPDIR := $(realpath ../..) + +CLEAN-FILES := data/stars-die.c +SUBDIRS := scalertab + +PNG2C.stars-die := --bitmap image,320x256x5,+interleaved --palette image_pal,32 + +include $(TOPDIR)/build/effect.mk diff --git a/effects/scaler/data/stars-die.png b/effects/scaler/data/stars-die.png new file mode 100644 index 00000000..0faf9936 --- /dev/null +++ b/effects/scaler/data/stars-die.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7aecfc44106b2d8168984b948dc78f6be1509d3c6f1db368fac17b2686fb0863 +size 24313 diff --git a/effects/scaler/scaler.c b/effects/scaler/scaler.c new file mode 100644 index 00000000..aee139f6 --- /dev/null +++ b/effects/scaler/scaler.c @@ -0,0 +1,148 @@ +#include "effect.h" +#include "custom.h" +#include "copper.h" +#include "bitmap.h" +#include "palette.h" +#include "profiler.h" + +#include "data/stars-die.c" + +#define LINES 256 +#define DEPTH 5 + +static CopListT *cp[2]; +static short active = 0; + +static void VerticalScalerForward(CopListT *cp, short ys, short height) { + short rowmod = image.bytesPerRow * image.depth; + int dy = (LINES << 16) / height; + short n = (short)(dy >> 16) * (short)image.depth; + short mod = (short)image.bytesPerRow * (short)(n - 1); + int y = 0; + short i; + + for (i = 1; i < height; i++) { + short _mod = mod; + int ny = y + dy; + if ((u_short)ny < (u_short)y) + _mod += rowmod; + CopWaitSafe(cp, Y(ys + i), X(0)); + CopMove16(cp, bpl1mod, _mod); + CopMove16(cp, bpl2mod, _mod); + y = ny; + } +} + +static void CopSetupBitplanesReverse(CopListT *list, CopInsT **bplptr, + const BitmapT *bitmap, u_short depth) +{ + short bytesPerLine = bitmap->bytesPerRow; + int start; + + if (bitmap->flags & BM_INTERLEAVED) + bytesPerLine *= (short)bitmap->depth; + + start = bytesPerLine * (short)(bitmap->height - 1); + + { + void **planes = bitmap->planes; + short n = depth - 1; + short reg = CSREG(bplpt); + + do { + CopInsT *ins = CopMoveLong(list, reg, (int)(*planes++) + start); + + if (bplptr) + *bplptr++ = ins; + + reg += 4; + } while (--n != -1); + } + + { + short modulo; + + if (bitmap->flags & BM_INTERLEAVED) + modulo = (short)bitmap->bytesPerRow * (short)(depth + 1); + else + modulo = 2 * bitmap->bytesPerRow; + + CopMove16(list, bpl1mod, -modulo); + CopMove16(list, bpl2mod, -modulo); + } +} + +static void VerticalScalerReverse(CopListT *cp, short ys, short height) { + short rowmod = image.bytesPerRow * image.depth; + int dy = (LINES << 16) / height; + short n = (short)(dy >> 16) * (short)image.depth; + short mod = (short)image.bytesPerRow * (short)(n + 1); + int y = 0; + short i; + + for (i = 1; i < height; i++) { + short _mod = mod; + int ny = y + dy; + if ((u_short)ny < (u_short)y) + _mod += rowmod; + CopWaitSafe(cp, Y(ys + i), X(0)); + CopMove16(cp, bpl1mod, -_mod); + CopMove16(cp, bpl2mod, -_mod); + y = ny; + } +} + +static void MakeCopperList(CopListT *cp, short height) { + short ys = (LINES - abs(height)) / 2; + + CopInit(cp); + CopSetupDisplayWindow(cp, MODE_LORES, X(0), Y(ys), image.width, abs(height)); + if (height > 0) { + CopSetupBitplanes(cp, NULL, &image, image.depth); + VerticalScalerForward(cp, ys, height); + } else if (height < 0) { + CopSetupBitplanesReverse(cp, NULL, &image, image.depth); + VerticalScalerReverse(cp, ys, -height); + } + CopEnd(cp); +} + +static void Init(void) { + LoadPalette(&image_pal, 0); + SetupPlayfield(MODE_LORES, image.depth, + X(0), Y(0), image.width, image.height); + + cp[0] = NewCopList(40 + LINES * 3); + cp[1] = NewCopList(40 + LINES * 3); + MakeCopperList(cp[active], LINES); + CopListActivate(cp[active]); + + EnableDMA(DMAF_RASTER); +} + +static void Kill(void) { + DisableDMA(DMAF_COPPER | DMAF_RASTER); + DeleteCopList(cp[0]); + DeleteCopList(cp[1]); +} + +PROFILE(Scaler); + +static void Render(void) { + static short val = LINES, dir = -1; + + ProfilerStart(Scaler); + MakeCopperList(cp[active], val); + ProfilerStop(Scaler); + + CopListRun(cp[active]); + TaskWaitVBlank(); + + val += dir; + if (abs(val) == LINES) + dir = -dir; + + active ^= 1; +} + +EFFECT(scaler, NULL, NULL, Init, Kill, Render); diff --git a/effects/scaler/scalertab/.gitignore b/effects/scaler/scalertab/.gitignore new file mode 100644 index 00000000..d675eb8e --- /dev/null +++ b/effects/scaler/scalertab/.gitignore @@ -0,0 +1 @@ +scalertab diff --git a/effects/scaler/scalertab/Makefile b/effects/scaler/scalertab/Makefile new file mode 100644 index 00000000..339e6e1c --- /dev/null +++ b/effects/scaler/scalertab/Makefile @@ -0,0 +1,3 @@ +TOPDIR := $(realpath ../../..) + +include $(TOPDIR)/build/go.mk diff --git a/effects/scaler/scalertab/main.go b/effects/scaler/scalertab/main.go new file mode 100644 index 00000000..969ef43a --- /dev/null +++ b/effects/scaler/scalertab/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "fmt" + "math" + "sort" +) + +type Row struct { + err float64 // average error + columns []int // current set of columns + removed int // which column was removed from working image + original int // the column from original picture that was removed +} + +func estimate(S []int, n int, N int) []Row { + if n == 1 { + R := make([]Row, 1) + R[0] = Row{0.0, []int{S[0]}, 1, S[1]} + return R + } + + di := float64(N-1) / float64(n-1) + I := make([]float64, n) + for i := 0; i < n; i++ { + I[i] = float64(i) * di + } + + rows := make([]Row, n+1) + + for i := 0; i <= n; i++ { + C := make([]int, n) + sum := 0.0 + for j := 0; j < n; j++ { + if j >= i { + C[j] = S[j+1] + } else { + C[j] = S[j] + } + delta := I[j] - float64(C[j]) + sum += delta * delta + } + rows[i] = Row{math.Sqrt(sum) / float64(n), C, i, S[i]} + } + + sort.Slice(rows, func(i, j int) bool { + return rows[i].err < rows[j].err + }) + + R := estimate(rows[0].columns, n-1, N) + for _, row := range rows[1:1] { + C := estimate(row.columns, n-1, N) + if R[len(R)-1].err > C[len(C)-1].err { + R = C + } + } + + return append(R, rows[0]) +} + +func main() { + N := 40 + S := make([]int, N) + for i := 0; i < N; i++ { + S[i] = i + } + R := estimate(S, N-1, N) + R = append(R, Row{0.0, S, -1, -1}) + err := 0.0 + for i, row := range R { + fmt.Printf("%3d: [%d] = %d\n", i+1, row.removed, row.original) + err += row.err + } + fmt.Printf("\naverage error: %.3f\n", err/float64(N-1)) +} diff --git a/lib/libgfx/CopSetupBitplanes.c b/lib/libgfx/CopSetupBitplanes.c index eafdcb6c..b6b67d60 100644 --- a/lib/libgfx/CopSetupBitplanes.c +++ b/lib/libgfx/CopSetupBitplanes.c @@ -6,13 +6,15 @@ void CopSetupBitplanes(CopListT *list, CopInsT **bplptr, { void **planes = bitmap->planes; short n = depth - 1; - short i = 0; + short reg = CSREG(bplpt); do { - CopInsT *ins = CopMove32(list, bplpt[i++], *planes++); + CopInsT *ins = CopMoveLong(list, reg, (int)(*planes++)); if (bplptr) *bplptr++ = ins; + + reg += 4; } while (--n != -1); }