pxt-common-packages
Version:
Microsoft MakeCode (PXT) common packages
147 lines (130 loc) • 4.61 kB
text/typescript
enum BackgroundAlignment {
//% block="left"
Left = 1,
//% block="right"
Right,
//% block="top"
Top,
//% block="bottom"
Bottom,
//% block="center"
Center
}
namespace scene {
export class Background {
color: number;
_image: Image;
camera: Camera;
private _layers: BackgroundLayer[];
constructor(camera: Camera) {
this.color = 0;
this.camera = camera;
this._layers = [];
}
public addLayer(pic: Image, distance: number, alignment: BackgroundAlignment) {
const layer = new BackgroundLayer(distance, alignment, pic);
this._layers.push(layer);
this._layers.sort((a, b) => b.distance - a.distance);
return layer;
}
get image() {
if (!this._image) {
this._image = image.create(screen.width, screen.height);
}
return this._image;
}
set image(image: Image) {
this._image = image;
}
hasBackgroundImage(): boolean {
return !!this._image;
}
draw() {
screen.fill(this.color);
if (this._image)
screen.drawTransparentImage(this._image, 0, 0)
if (this._layers) {
this._layers.forEach(layer => {
// compute displacement based on distance
const ox = Math.round(this.camera.drawOffsetX / (1 + layer.distance));
const oy = Math.round(this.camera.drawOffsetY / (1 + layer.distance));
layer.draw(ox, oy);
});
}
}
}
export class BackgroundLayer {
distance: number;
img: Image;
repeatX: boolean;
repeatY: boolean;
alignX: BackgroundAlignment;
alignY: BackgroundAlignment;
constructor(distance: number, alignment: BackgroundAlignment, img: Image) {
this.distance = Math.max(1, distance);
this.img = img;
switch (alignment) {
case BackgroundAlignment.Center:
this.repeatX = true;
this.repeatY = true;
this.alignX = BackgroundAlignment.Center;
this.alignY = BackgroundAlignment.Center;
break;
case BackgroundAlignment.Left:
case BackgroundAlignment.Right:
this.repeatX = false;
this.repeatY = true;
this.alignX = alignment;
this.alignY = BackgroundAlignment.Center;
break;
case BackgroundAlignment.Top:
case BackgroundAlignment.Bottom:
this.repeatX = true;
this.repeatY = false;
this.alignX = BackgroundAlignment.Center;
this.alignY = alignment;
break;
}
}
draw(offsetX: number, offsetY: number) {
const w = screen.width;
const h = screen.height;
const pw = this.img.width;
const ph = this.img.height;
if (!pw || !ph) return; // empty image.
// left, top aligned
let rx = -offsetX;
let ry = -offsetY;
switch (this.alignX) {
case BackgroundAlignment.Right: rx -= (w + pw); break;
case BackgroundAlignment.Center: rx -= (w + pw) >> 1; break;
}
switch (this.alignY) {
case BackgroundAlignment.Bottom: ry -= (h + ph); break;
case BackgroundAlignment.Center: ry -= (h + ph) >> 1; break;
}
rx %= w; if (rx < 0) rx += w;
ry %= h; if (ry < 0) ry += h;
// avoid subpixel aliasing
rx = Math.floor(rx);
ry = Math.floor(ry);
let y = 0;
let py = 0;
while (y < h) {
py = y % ph;
let dh = Math.min(ph - py, h - ry);
let x = 0;
let rxl = rx;
while (x < w) {
let px = x % pw;
let dw = Math.min(pw - px, w - rxl);
screen.drawImage(this.img, rxl, ry);
rxl = (rxl + dw) % w;
x += this.repeatX ? dw : w;
}
ry = (ry + dh) % h;
y += this.repeatY ? dh : h;
}
}
}
}