UNPKG

pxt-common-packages

Version:
1,142 lines (1,001 loc) 33.7 kB
namespace pxsim { export class RefImage extends RefObject { _width: number; _height: number; _bpp: number; data: Uint8Array; isStatic = true; revision: number; constructor(w: number, h: number, bpp: number) { super(); this.revision = 0; this.data = new Uint8Array(w * h) this._width = w this._height = h this._bpp = bpp } scan(mark: (path: string, v: any) => void) { } gcKey() { return "Image" } gcSize() { return 4 + (this.data.length + 3 >> 3) } gcIsStatic() { return this.isStatic } pix(x: number, y: number) { return (x | 0) + (y | 0) * this._width } inRange(x: number, y: number) { return 0 <= (x | 0) && (x | 0) < this._width && 0 <= (y | 0) && (y | 0) < this._height; } color(c: number): number { return c & 0xff } clamp(x: number, y: number) { x |= 0 y |= 0 if (x < 0) x = 0 else if (x >= this._width) x = this._width - 1 if (y < 0) y = 0 else if (y >= this._height) y = this._height - 1 return [x, y] } makeWritable() { this.revision++; this.isStatic = false } toDebugString() { return this._width + "x" + this._height } } } namespace pxsim.ImageMethods { export function XX(x: number) { return (x << 16) >> 16 } export function YY(x: number) { return x >> 16 } export function width(img: RefImage) { return img._width } export function height(img: RefImage) { return img._height } export function isMono(img: RefImage) { return img._bpp == 1 } export function isStatic(img: RefImage) { return img.gcIsStatic() } export function revision(img: RefImage) { return img.revision } export function setPixel(img: RefImage, x: number, y: number, c: number) { img.makeWritable() if (img.inRange(x, y)) img.data[img.pix(x, y)] = img.color(c) } export function getPixel(img: RefImage, x: number, y: number) { if (img.inRange(x, y)) return img.data[img.pix(x, y)] return 0 } export function fill(img: RefImage, c: number) { img.makeWritable() img.data.fill(img.color(c)) } export function fillRect(img: RefImage, x: number, y: number, w: number, h: number, c: number) { if (w == 0 || h == 0 || x >= img._width || y >= img._height || x + w - 1 < 0 || y + h - 1 < 0) return; img.makeWritable() let [x2, y2] = img.clamp(x + w - 1, y + h - 1); [x, y] = img.clamp(x, y) let p = img.pix(x, y) w = x2 - x + 1 h = y2 - y + 1 let d = img._width - w c = img.color(c) while (h-- > 0) { for (let i = 0; i < w; ++i) img.data[p++] = c p += d } } export function _fillRect(img: RefImage, xy: number, wh: number, c: number) { fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c) } export function mapRect(img: RefImage, x: number, y: number, w: number, h: number, c: RefBuffer) { if (c.data.length < 16) return img.makeWritable() let [x2, y2] = img.clamp(x + w - 1, y + h - 1); [x, y] = img.clamp(x, y) let p = img.pix(x, y) w = x2 - x + 1 h = y2 - y + 1 let d = img._width - w while (h-- > 0) { for (let i = 0; i < w; ++i) { img.data[p] = c.data[img.data[p]] p++ } p += d } } export function _mapRect(img: RefImage, xy: number, wh: number, c: RefBuffer) { mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c) } export function equals(img: RefImage, other: RefImage) { if (!other || img._bpp != other._bpp || img._width != other._width || img._height != other._height) { return false; } let imgData = img.data; let otherData = other.data; let len = imgData.length; for (let i = 0; i < len; i++) { if (imgData[i] != otherData[i]) { return false; } } return true; } export function getRows(img: RefImage, x: number, dst: RefBuffer) { x |= 0 if (!img.inRange(x, 0)) return let dp = 0 let len = Math.min(dst.data.length, (img._width - x) * img._height) let sp = x let hh = 0 while (len--) { if (hh++ >= img._height) { hh = 1 sp = ++x } dst.data[dp++] = img.data[sp] sp += img._width } } export function setRows(img: RefImage, x: number, src: RefBuffer) { x |= 0 if (!img.inRange(x, 0)) return let sp = 0 let len = Math.min(src.data.length, (img._width - x) * img._height) let dp = x let hh = 0 while (len--) { if (hh++ >= img._height) { hh = 1 dp = ++x } img.data[dp] = src.data[sp++] dp += img._width } } export function clone(img: RefImage) { let r = new RefImage(img._width, img._height, img._bpp) r.data.set(img.data) return r } export function flipX(img: RefImage) { img.makeWritable() const w = img._width const h = img._height for (let i = 0; i < h; ++i) { img.data.subarray(i * w, (i + 1) * w).reverse() } } export function flipY(img: RefImage) { img.makeWritable() const w = img._width const h = img._height const d = img.data for (let i = 0; i < w; ++i) { let top = i let bot = i + (h - 1) * w while (top < bot) { let c = d[top] d[top] = d[bot] d[bot] = c top += w bot -= w } } } export function transposed(img: RefImage) { const w = img._width const h = img._height const d = img.data const r = new RefImage(h, w, img._bpp) const n = r.data let src = 0 for (let i = 0; i < h; ++i) { let dst = i for (let j = 0; j < w; ++j) { n[dst] = d[src++] dst += w } } return r } export function copyFrom(img: RefImage, from: RefImage) { if (img._width != from._width || img._height != from._height || img._bpp != from._bpp) return; img.data.set(from.data) } export function scroll(img: RefImage, dx: number, dy: number) { img.makeWritable() dx |= 0 dy |= 0 if (dx != 0) { const img2 = clone(img) img.data.fill(0) drawTransparentImage(img, img2, dx, dy) } else if (dy < 0) { dy = -dy if (dy < img._height) img.data.copyWithin(0, dy * img._width) else dy = img._height img.data.fill(0, (img._height - dy) * img._width) } else if (dy > 0) { if (dy < img._height) img.data.copyWithin(dy * img._width, 0) else dy = img._height img.data.fill(0, 0, dy * img._width) } // TODO implement dx } export function replace(img: RefImage, from: number, to: number) { to &= 0xf; const d = img.data for (let i = 0; i < d.length; ++i) if (d[i] == from) d[i] = to } export function doubledX(img: RefImage) { const w = img._width const h = img._height const d = img.data const r = new RefImage(w * 2, h, img._bpp) const n = r.data let dst = 0 for (let src = 0; src < d.length; ++src) { let c = d[src] n[dst++] = c n[dst++] = c } return r } export function doubledY(img: RefImage) { const w = img._width const h = img._height const d = img.data const r = new RefImage(w, h * 2, img._bpp) const n = r.data let src = 0 let dst0 = 0 let dst1 = w for (let i = 0; i < h; ++i) { for (let j = 0; j < w; ++j) { let c = d[src++] n[dst0++] = c n[dst1++] = c } dst0 += w dst1 += w } return r } export function doubled(img: RefImage) { return doubledX(doubledY(img)) } function drawImageCore(img: RefImage, from: RefImage, x: number, y: number, clear: boolean, check: boolean) { x |= 0 y |= 0 const w = from._width let h = from._height const sh = img._height const sw = img._width if (x + w <= 0) return false if (x >= sw) return false if (y + h <= 0) return false if (y >= sh) return false if (clear) fillRect(img, x, y, from._width, from._height, 0) else if (!check) img.makeWritable() const len = x < 0 ? Math.min(sw, w + x) : Math.min(sw - x, w) const fdata = from.data const tdata = img.data for (let p = 0; h--; y++ , p += w) { if (0 <= y && y < sh) { let dst = y * sw let src = p if (x < 0) src += -x else dst += x for (let i = 0; i < len; ++i) { const v = fdata[src++] if (v) { if (check) { if (tdata[dst]) return true } else { tdata[dst] = v } } dst++ } } } return false } export function drawImage(img: RefImage, from: RefImage, x: number, y: number) { drawImageCore(img, from, x, y, true, false) } export function drawTransparentImage(img: RefImage, from: RefImage, x: number, y: number) { drawImageCore(img, from, x, y, false, false) } export function overlapsWith(img: RefImage, other: RefImage, x: number, y: number) { return drawImageCore(img, other, x, y, false, true) } function drawLineLow(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { let dx = x1 - x0; let dy = y1 - y0; let yi = img._width; if (dy < 0) { yi = -yi; dy = -dy; } let D = 2 * dy - dx; dx <<= 1; dy <<= 1; c = img.color(c); let ptr = img.pix(x0, y0) for (let x = x0; x <= x1; ++x) { img.data[ptr] = c if (D > 0) { ptr += yi; D -= dx; } D += dy; ptr++; } } function drawLineHigh(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { let dx = x1 - x0; let dy = y1 - y0; let xi = 1; if (dx < 0) { xi = -1; dx = -dx; } let D = 2 * dx - dy; dx <<= 1; dy <<= 1; c = img.color(c); let ptr = img.pix(x0, y0); for (let y = y0; y <= y1; ++y) { img.data[ptr] = c; if (D > 0) { ptr += xi; D -= dy; } D += dx; ptr += img._width; } } export function _drawLine(img: RefImage, xy: number, wh: number, c: number) { drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c) } export function drawLine(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { x0 |= 0 y0 |= 0 x1 |= 0 y1 |= 0 if (x1 < x0) { drawLine(img, x1, y1, x0, y0, c); return; } let w = x1 - x0; let h = y1 - y0; if (h == 0) { if (w == 0) setPixel(img, x0, y0, c); else fillRect(img, x0, y0, w + 1, 1, c); return; } if (w == 0) { if (h > 0) fillRect(img, x0, y0, 1, h + 1, c); else fillRect(img, x0, y1, 1, -h + 1, c); return; } if (x1 < 0 || x0 >= img._width) return; if (x0 < 0) { y0 -= (h * x0 / w) | 0; x0 = 0; } if (x1 >= img._width) { let d = (img._width - 1) - x1; y1 += (h * d / w) | 0; x1 = img._width - 1 } if (y0 < y1) { if (y0 >= img._height || y1 < 0) return; if (y0 < 0) { x0 -= (w * y0 / h) | 0; y0 = 0; } if (y1 >= img._height) { let d = (img._height - 1) - y1; x1 += (w * d / h) | 0; y1 = img._height } } else { if (y1 >= img._height || y0 < 0) return; if (y1 < 0) { x1 -= (w * y1 / h) | 0; y1 = 0; } if (y0 >= img._height) { let d = (img._height - 1) - y0; x0 += (w * d / h) | 0; y0 = img._height } } img.makeWritable() if (h < 0) { h = -h; if (h < w) drawLineLow(img, x0, y0, x1, y1, c); else drawLineHigh(img, x1, y1, x0, y0, c); } else { if (h < w) drawLineLow(img, x0, y0, x1, y1, c); else drawLineHigh(img, x0, y0, x1, y1, c); } } export function drawIcon(img: RefImage, icon: RefBuffer, x: number, y: number, color: number) { const src: Uint8Array = icon.data if (!image.isValidImage(icon)) return if (src[1] != 1) return // only mono let width = image.bufW(src) let height = image.bufH(src) let byteH = image.byteHeight(height, 1) x |= 0 y |= 0 const destHeight = img._height const destWidth = img._width if (x + width <= 0) return if (x >= destWidth) return if (y + height <= 0) return if (y >= destHeight) return img.makeWritable() let srcPointer = 8 color = img.color(color) const screen = img.data for (let i = 0; i < width; ++i) { let destX = x + i if (0 <= destX && destX < destWidth) { let destIndex = destX + y * destWidth let srcIndex = srcPointer let destY = y let destEnd = Math.min(destHeight, height + y) if (y < 0) { srcIndex += ((-y) >> 3) destY += ((-y) >> 3) * 8 destIndex += (destY - y) * destWidth } let mask = 0x01 let srcByte = src[srcIndex++] while (destY < destEnd) { if (destY >= 0 && (srcByte & mask)) { screen[destIndex] = color } mask <<= 1 if (mask == 0x100) { mask = 0x01 srcByte = src[srcIndex++] } destIndex += destWidth destY++ } } srcPointer += byteH } } export function _drawIcon(img: RefImage, icon: RefBuffer, xy: number, color: number) { drawIcon(img, icon, XX(xy), YY(xy), color) } export function fillCircle(img: RefImage, cx: number, cy: number, r: number, c: number) { let x = r - 1; let y = 0; let dx = 1; let dy = 1; let err = dx - (r << 1); while (x >= y) { fillRect(img, cx + x, cy - y, 1, 1 + (y << 1), c); fillRect(img, cx + y, cy - x, 1, 1 + (x << 1), c); fillRect(img, cx - x, cy - y, 1, 1 + (y << 1), c); fillRect(img, cx - y, cy - x, 1, 1 + (x << 1), c); if (err <= 0) { y++; err += dy; dy += 2; } if (err > 0) { x--; dx += 2; err += dx - (r << 1); } } } export function _fillCircle(img: RefImage, cxy: number, r: number, c: number) { fillCircle(img, XX(cxy), YY(cxy), r, c); } interface LineGenState { x: number; y: number; x0: number; y0: number; x1: number; y1: number; W: number; H: number; dx: number; dy: number; yi: number; xi: number; D: number; nextFuncIndex: number; } interface ValueRange { min: number; max: number; } function nextYRange_Low(x: number, line: LineGenState, yRange: ValueRange) { while (line.x === x && line.x <= line.x1 && line.x < line.W) { if (0 <= line.x) { if (line.y < yRange.min) yRange.min = line.y; if (line.y > yRange.max) yRange.max = line.y } if (line.D > 0) { line.y += line.yi; line.D -= line.dx; } line.D += line.dy; ++line.x; } } function nextYRange_HighUp(x: number, line: LineGenState, yRange: ValueRange) { while (line.x == x && line.y >= line.y1 && line.x < line.W) { if (0 <= line.x) { if (line.y < yRange.min) yRange.min = line.y; if (line.y > yRange.max) yRange.max = line.y; } if (line.D > 0) { line.x += line.xi; line.D += line.dy; } line.D += line.dx; --line.y; } } function nextYRange_HighDown(x: number, line: LineGenState, yRange: ValueRange) { while (line.x == x && line.y <= line.y1 && line.x < line.W) { if (0 <= line.x) { if (line.y < yRange.min) yRange.min = line.y; if (line.y > yRange.max) yRange.max = line.y; } if (line.D > 0) { line.x += line.xi; line.D -= line.dy; } line.D += line.dx; ++line.y; } } function initYRangeGenerator(X0: number, Y0: number, X1: number, Y1: number): LineGenState { const line: LineGenState = { x: X0, y: Y0, x0: X0, y0: Y0, x1: X1, y1: Y1, W: 0, H: 0, dx: X1 - X0, dy: Y1 - Y0, yi: 0, xi: 0, D: 0, nextFuncIndex: 0, }; if ((line.dy < 0 ? -line.dy : line.dy) < line.dx) { line.yi = 1; if (line.dy < 0) { line.yi = -1; line.dy = -line.dy; } line.D = 2 * line.dy - line.dx; line.dx = line.dx << 1; line.dy = line.dy << 1; line.nextFuncIndex = 0; return line; } else { line.xi = 1; if (line.dy < 0) { line.D = 2 * line.dx + line.dy; line.dx = line.dx << 1; line.dy = line.dy << 1; line.nextFuncIndex = 1; return line; } else { line.D = 2 * line.dx - line.dy; line.dx = line.dx << 1; line.dy = line.dy << 1; line.nextFuncIndex = 2; return line; } } } export function fillTriangle(img: RefImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, c: number) { if (x1 < x0) { [x1, x0] = [x0, x1]; [y1, y0] = [y0, y1]; } if (x2 < x1) { [x2, x1] = [x1, x2]; [y2, y1] = [y1, y2]; } if (x1 < x0) { [x1, x0] = [x0, x1]; [y1, y0] = [y0, y1]; } const lines: LineGenState[] = [ initYRangeGenerator(x0, y0, x2, y2), initYRangeGenerator(x0, y0, x1, y1), initYRangeGenerator(x1, y1, x2, y2) ]; lines[0].W = lines[1].W = lines[2].W = width(img); lines[0].H = lines[1].H = lines[2].H = height(img); type FP_NEXT = (x: number, line: LineGenState, yRange: ValueRange) => void; const nextFuncList: FP_NEXT[] = [ nextYRange_Low, nextYRange_HighUp, nextYRange_HighDown ]; const fpNext0 = nextFuncList[lines[0].nextFuncIndex]; const fpNext1 = nextFuncList[lines[1].nextFuncIndex]; const fpNext2 = nextFuncList[lines[2].nextFuncIndex]; const yRange= { min: lines[0].H, max: -1 }; for (let x = lines[1].x0; x <= lines[1].x1; x++) { yRange.min = lines[0].H; yRange.max = -1; fpNext0(x, lines[0], yRange); fpNext1(x, lines[1], yRange); fillRect(img, x, yRange.min, 1, yRange.max - yRange.min + 1, c); } fpNext2(lines[2].x0, lines[2], yRange); for (let x = lines[2].x0 + 1; x <= lines[2].x1; x++) { yRange.min = lines[0].H; yRange.max = -1; fpNext0(x, lines[0], yRange); fpNext2(x, lines[2], yRange); fillRect(img, x, yRange.min, 1, yRange.max - yRange.min + 1, c); } } export function _fillTriangle(img: RefImage, args: RefCollection) { fillTriangle( img, args.getAt(0) | 0, args.getAt(1) | 0, args.getAt(2) | 0, args.getAt(3) | 0, args.getAt(4) | 0, args.getAt(5) | 0, args.getAt(6) | 0, ); } export function fillPolygon4(img: RefImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, c: number) { const lines: LineGenState[]= [ (x0 < x1) ? initYRangeGenerator(x0, y0, x1, y1) : initYRangeGenerator(x1, y1, x0, y0), (x1 < x2) ? initYRangeGenerator(x1, y1, x2, y2) : initYRangeGenerator(x2, y2, x1, y1), (x2 < x3) ? initYRangeGenerator(x2, y2, x3, y3) : initYRangeGenerator(x3, y3, x2, y2), (x0 < x3) ? initYRangeGenerator(x0, y0, x3, y3) : initYRangeGenerator(x3, y3, x0, y0) ]; lines[0].W = lines[1].W = lines[2].W = lines[3].W = width(img); lines[0].H = lines[1].H = lines[2].H = lines[3].H = height(img); let minX = Math.min(Math.min(x0, x1), Math.min(x2, x3)); let maxX = Math.min(Math.max(Math.max(x0, x1), Math.max(x2, x3)), lines[0].W - 1); type FP_NEXT = (x: number, line: LineGenState, yRange: ValueRange) => void; const nextFuncList: FP_NEXT[] = [ nextYRange_Low, nextYRange_HighUp, nextYRange_HighDown ]; const fpNext0 = nextFuncList[lines[0].nextFuncIndex]; const fpNext1 = nextFuncList[lines[1].nextFuncIndex]; const fpNext2 = nextFuncList[lines[2].nextFuncIndex]; const fpNext3 = nextFuncList[lines[3].nextFuncIndex]; const yRange: ValueRange = { min: lines[0].H, max: -1 }; for (let x = minX; x <= maxX; x++) { yRange.min = lines[0].H; yRange.max = -1; fpNext0(x, lines[0], yRange); fpNext1(x, lines[1], yRange); fpNext2(x, lines[2], yRange); fpNext3(x, lines[3], yRange); fillRect(img, x,yRange.min, 1, yRange.max - yRange.min + 1, c); } } export function _fillPolygon4(img: RefImage, args: RefCollection) { fillPolygon4( img, args.getAt(0) | 0, args.getAt(1) | 0, args.getAt(2) | 0, args.getAt(3) | 0, args.getAt(4) | 0, args.getAt(5) | 0, args.getAt(6) | 0, args.getAt(7) | 0, args.getAt(8) | 0, ); } export function _blitRow(img: RefImage, xy: number, from: RefImage, xh: number) { blitRow(img, XX(xy), YY(xy), from, XX(xh), YY(xh)) } export function blitRow(img: RefImage, x: number, y: number, from: RefImage, fromX: number, fromH: number) { x |= 0 y |= 0 fromX |= 0 fromH |= 0 if (!img.inRange(x, 0) || !img.inRange(fromX, 0) || fromH <= 0) return let fy = 0 let stepFY = ((from._width << 16) / fromH) | 0 let endY = y + fromH if (endY > img._height) endY = img._height if (y < 0) { fy += -y * stepFY y = 0 } while (y < endY) { img.data[img.pix(x, y)] = from.data[from.pix(fromX, fy >> 16)] y++ fy += stepFY } } export function _blit(img: RefImage, src: RefImage, args: RefCollection): boolean { return blit(img, src, args); } export function blit(dst: RefImage, src: RefImage, args: RefCollection): boolean { const xDst = args.getAt(0) as number; const yDst = args.getAt(1) as number; const wDst = args.getAt(2) as number; const hDst = args.getAt(3) as number; const xSrc = args.getAt(4) as number; const ySrc = args.getAt(5) as number; const wSrc = args.getAt(6) as number; const hSrc = args.getAt(7) as number; const transparent = args.getAt(8) as number; const check = args.getAt(9) as number; const xSrcStep = ((wSrc << 16) / wDst) | 0; const ySrcStep = ((hSrc << 16) / hDst) | 0; const xDstClip = Math.abs(Math.min(0, xDst)); const yDstClip = Math.abs(Math.min(0, yDst)); const xDstStart = xDst + xDstClip; const yDstStart = yDst + yDstClip; const xDstEnd = Math.min(dst._width, xDst + wDst); const yDstEnd = Math.min(dst._height, yDst + hDst); const xSrcStart = Math.max(0, (xSrc << 16) + xDstClip * xSrcStep); const ySrcStart = Math.max(0, (ySrc << 16) + yDstClip * ySrcStep); const xSrcEnd = Math.min(src._width, xSrc + wSrc) << 16; const ySrcEnd = Math.min(src._height, ySrc + hSrc) << 16; if (!check) dst.makeWritable(); for (let yDstCur = yDstStart, ySrcCur = ySrcStart; yDstCur < yDstEnd && ySrcCur < ySrcEnd; ++yDstCur, ySrcCur += ySrcStep) { const ySrcCurI = ySrcCur >> 16; for (let xDstCur = xDstStart, xSrcCur = xSrcStart; xDstCur < xDstEnd && xSrcCur < xSrcEnd; ++xDstCur, xSrcCur += xSrcStep) { const xSrcCurI = xSrcCur >> 16; const cSrc = getPixel(src, xSrcCurI, ySrcCurI); if (check && cSrc) { const cDst = getPixel(dst, xDstCur, yDstCur); if (cDst) { return true; } continue; } if (!transparent || cSrc) { setPixel(dst, xDstCur, yDstCur, cSrc); } } } return false; } } namespace pxsim.image { export function byteHeight(h: number, bpp: number) { if (bpp == 1) return h * bpp + 7 >> 3 else return ((h * bpp + 31) >> 5) << 2 } function isLegacyImage(buf: RefBuffer) { if (!buf || buf.data.length < 5) return false; if (buf.data[0] != 0xe1 && buf.data[0] != 0xe4) return false; const bpp = buf.data[0] & 0xf; const sz = buf.data[1] * byteHeight(buf.data[2], bpp) if (4 + sz != buf.data.length) return false; return true; } export function bufW(data: Uint8Array) { return data[2] | (data[3] << 8) } export function bufH(data: Uint8Array) { return data[4] | (data[5] << 8) } export function isValidImage(buf: RefBuffer) { if (!buf || buf.data.length < 5) return false; if (buf.data[0] != 0x87) return false if (buf.data[1] != 1 && buf.data[1] != 4) return false; const bpp = buf.data[1]; const sz = bufW(buf.data) * byteHeight(bufH(buf.data), bpp) if (8 + sz != buf.data.length) return false; return true; } export function create(w: number, h: number) { // truncate decimal sizes w |= 0 h |= 0 return new RefImage(w, h, getScreenState().bpp()) } export function ofBuffer(buf: RefBuffer): RefImage { const src: Uint8Array = buf.data let srcP = 4 let w = 0, h = 0, bpp = 0 if (isLegacyImage(buf)) { w = src[1] h = src[2] bpp = src[0] & 0xf; // console.log("using legacy image") } else if (isValidImage(buf)) { srcP = 8 w = bufW(src) h = bufH(src) bpp = src[1] } if (w == 0 || h == 0) return null const r = new RefImage(w, h, bpp) const dst = r.data r.isStatic = buf.isStatic if (bpp == 1) { for (let i = 0; i < w; ++i) { let dstP = i let mask = 0x01 let v = src[srcP++] for (let j = 0; j < h; ++j) { if (mask == 0x100) { mask = 0x01 v = src[srcP++] } if (v & mask) dst[dstP] = 1 dstP += w mask <<= 1 } } } else if (bpp == 4) { for (let i = 0; i < w; ++i) { let dstP = i for (let j = 0; j < h >> 1; ++j) { const v = src[srcP++] dst[dstP] = v & 0xf dstP += w dst[dstP] = v >> 4 dstP += w } if (h & 1) dst[dstP] = src[srcP++] & 0xf srcP = (srcP + 3) & ~3 } } return r } export function toBuffer(img: RefImage): RefBuffer { let col = byteHeight(img._height, img._bpp) let sz = 8 + img._width * col let r = new Uint8Array(sz) r[0] = 0x87 r[1] = img._bpp r[2] = img._width & 0xff r[3] = img._width >> 8 r[4] = img._height & 0xff r[5] = img._height >> 8 let dstP = 8 const w = img._width const h = img._height const data = img.data for (let i = 0; i < w; ++i) { if (img._bpp == 4) { let p = i for (let j = 0; j < h; j += 2) { r[dstP++] = ((data[p + w] & 0xf) << 4) | ((data[p] || 0) & 0xf) p += 2 * w } dstP = (dstP + 3) & ~3 } else if (img._bpp == 1) { let mask = 0x01 let p = i for (let j = 0; j < h; j++) { if (data[p]) r[dstP] |= mask mask <<= 1 p += w if (mask == 0x100) { mask = 0x01 dstP++ } } if (mask != 0x01) dstP++ } } return new RefBuffer(r) } export function doubledIcon(buf: RefBuffer): RefBuffer { let img = ofBuffer(buf) if (!img) return null img = ImageMethods.doubled(img) return toBuffer(img) } } namespace pxsim.pxtcore { export function updateScreen(img: RefImage) { const state = getScreenState(); if (state) state.showImage(img) } export function updateStats(s: string) { const state = getScreenState(); if (state) state.updateStats(s); } export function setPalette(b: RefBuffer) { const state = getScreenState(); if (state) state.setPalette(b) } export function setupScreenStatusBar(barHeight: number) { const state = getScreenState(); if (state) state.setupScreenStatusBar(barHeight); } export function updateScreenStatusBar(img: RefImage) { const state = getScreenState(); if (state) state.updateScreenStatusBar(img); } export function setScreenBrightness(b: number) { // I guess we could at least turn the screen off, when b==0, // otherwise, it probably doesn't make much sense to do anything. const state = getScreenState(); if (state) state.setScreenBrightness(b); } }