private void SetPalette(VisBitmap8 vb, Palette pal) { vb.AddColor(0, 0, 0, 0); // 0=transparent vb.AddColor(0xff, 0x01, 0x01, 0x01); // 1=near black (so VB doesn't uniquify) vb.AddColor(0xff, 0xfe, 0xfe, 0xfe); // 2=near white switch (pal) { case Palette.Greyscale: default: vb.AddColor(0xff, 0x00, 0x00, 0x00); // black vb.AddColor(0xff, 0x80, 0x80, 0x80); // dark grey vb.AddColor(0xff, 0xb0, 0xb0, 0xb0); // medium grey vb.AddColor(0xff, 0xe0, 0xe0, 0xe0); // light grey break; case Palette.Pinkish: vb.AddColor(0xff, 0x49, 0x99, 0xfe); // sky blue vb.AddColor(0xff, 0xff, 0xbd, 0xaf); // pinkish vb.AddColor(0xff, 0xcd, 0x50, 0x00); // dark orange vb.AddColor(0xff, 0x00, 0x00, 0x00); // black break; case Palette.Greenish: vb.AddColor(0xff, 0x49, 0x99, 0xfe); // sky blue vb.AddColor(0xff, 0x00, 0xa4, 0x00); // medium green vb.AddColor(0xff, 0xfc, 0xfc, 0xfc); // near white vb.AddColor(0xff, 0xff, 0x99, 0x2b); // orange break; } }
private IVisualization2d GenerateHiResSprite(ReadOnlyDictionary <string, object> parms) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); byte color = (byte)Util.GetFromObjDict(parms, P_COLOR, 0); bool isDoubleWide = Util.GetFromObjDict(parms, P_DOUBLE_WIDE, false); bool isDoubleHigh = Util.GetFromObjDict(parms, P_DOUBLE_HIGH, false); if (offset < 0 || offset >= mFileData.Length || color < 0 || color > MAX_COLOR) { // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + SPRITE_SIZE - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Sprite runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } int xwide = isDoubleWide ? 2 : 1; int xhigh = isDoubleHigh ? 2 : 1; VisBitmap8 vb = new VisBitmap8(BYTE_WIDTH * 8 * xwide, HEIGHT * xhigh); SetPalette(vb); // Clear all pixels to transparent, then just draw the non-transparent ones. vb.SetAllPixelIndices(TRANSPARENT); for (int row = 0; row < HEIGHT; row++) { for (int col = 0; col < BYTE_WIDTH; col++) { byte val = mFileData[offset + row * BYTE_WIDTH + col]; for (int bit = 0; bit < 8; bit++) { if ((val & 0x80) != 0) { int xc = (col * 8 + bit) * xwide; int yc = row * xhigh; vb.SetPixelIndex(xc, yc, color); if (isDoubleWide || isDoubleHigh) { // Draw doubled pixels. If we're only doubled in one dimension // this will draw pixels twice. vb.SetPixelIndex(xc + xwide - 1, yc, color); vb.SetPixelIndex(xc, yc + xhigh - 1, color); vb.SetPixelIndex(xc + xwide - 1, yc + xhigh - 1, color); } } val <<= 1; } } } return(vb); }
// C64 colors, from http://unusedino.de/ec64/technical/misc/vic656x/colors/ // (the ones on https://www.c64-wiki.com/wiki/Color looked wrong) private void SetPalette(VisBitmap8 vb) { vb.SetColor(0, 0xff, 0x00, 0x00, 0x00); // 0=black vb.SetColor(1, 0xff, 0xff, 0xff, 0xff); // 1=white vb.SetColor(2, 0xff, 0x68, 0x37, 0x2b); // 2=red vb.SetColor(3, 0xff, 0x70, 0xa4, 0xb2); // 3=cyan vb.SetColor(4, 0xff, 0x6f, 0x3d, 0x86); // 4=purple vb.SetColor(5, 0xff, 0x58, 0x8d, 0x43); // 5=green vb.SetColor(6, 0xff, 0x35, 0x28, 0x79); // 6=blue vb.SetColor(7, 0xff, 0xb8, 0xc7, 0x6f); // 7=yellow vb.SetColor(8, 0xff, 0x6f, 0x4f, 0x25); // 8=orange vb.SetColor(9, 0xff, 0x43, 0x39, 0x00); // 9-brown vb.SetColor(10, 0xff, 0x9a, 0x67, 0x59); // 10=light red vb.SetColor(11, 0xff, 0x44, 0x44, 0x44); // 11=dark grey vb.SetColor(12, 0xff, 0x6c, 0x6c, 0x6c); // 12=grey vb.SetColor(13, 0xff, 0x9a, 0xd2, 0x84); // 13=light green vb.SetColor(14, 0xff, 0x6c, 0x5e, 0xb5); // 14=light blue vb.SetColor(15, 0xff, 0x95, 0x95, 0x95); // 15=light grey vb.SetColor(16, 0, 0, 0, 0); // 16=transparent #if SHOW_BORDER vb.SetColor(17, 0xff, 0x00, 0xd6, 0xff); // 17=grid border #else vb.SetColor(17, 0, 0, 0, 0); // 17=grid border #endif }
private void SetPalette(VisBitmap8 vb, Palette pal) { vb.SetColor((byte)Color.Transparent, 0x00, 0x00, 0x00, 0x00); vb.SetColor((byte)Color.Black, 0xff, 0x00, 0x00, 0x00); vb.SetColor((byte)Color.White, 0xff, 0xff, 0xff, 0xff); switch (pal) { case Palette.Greyscale: default: vb.SetColor((byte)Color.Color0, 0xff, 0x00, 0x00, 0x00); // black vb.SetColor((byte)Color.Color1, 0xff, 0x80, 0x80, 0x80); // dark grey vb.SetColor((byte)Color.Color2, 0xff, 0xb0, 0xb0, 0xb0); // medium grey vb.SetColor((byte)Color.Color3, 0xff, 0xe0, 0xe0, 0xe0); // light grey break; case Palette.Pinkish: vb.SetColor((byte)Color.Color0, 0xff, 0x49, 0x99, 0xfe); // sky blue vb.SetColor((byte)Color.Color1, 0xff, 0xff, 0xbd, 0xaf); // pinkish vb.SetColor((byte)Color.Color2, 0xff, 0xcd, 0x50, 0x00); // dark orange vb.SetColor((byte)Color.Color3, 0xff, 0x00, 0x00, 0x00); // black break; case Palette.Greenish: vb.SetColor((byte)Color.Color0, 0xff, 0x49, 0x99, 0xfe); // sky blue vb.SetColor((byte)Color.Color1, 0xff, 0x00, 0xa4, 0x00); // medium green vb.SetColor((byte)Color.Color2, 0xff, 0xfc, 0xfc, 0xfc); // near white vb.SetColor((byte)Color.Color3, 0xff, 0xff, 0x99, 0x2b); // orange break; } }
private IVisualization2d GenerateBitmap(ReadOnlyDictionary <string, object> parms) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 1); // width ignoring colStride int height = Util.GetFromObjDict(parms, P_HEIGHT, 1); int colStride = Util.GetFromObjDict(parms, P_COL_STRIDE, 0); int rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0); bool isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true); bool isFirstOdd = Util.GetFromObjDict(parms, P_IS_FIRST_ODD, false); bool isHighBitFlipped = Util.GetFromObjDict(parms, P_IS_HIGH_BIT_FLIPPED, false); int colorConvMode = !isColor ? (int)ColorMode.Mono : Util.GetFromObjDict(parms, P_COLOR_CONV_MODE, (int)ColorMode.SimpleColor); // We allow the stride entries to be zero to indicate a "dense" bitmap. if (colStride == 0) { colStride = 1; } if (rowStride == 0) { rowStride = byteWidth * colStride; } if (offset < 0 || offset >= mFileData.Length || byteWidth <= 0 || byteWidth > MAX_DIM || height <= 0 || height > MAX_DIM) { // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.ReportError("Invalid parameter"); return(null); } if (colStride <= 0 || colStride > MAX_DIM) { mAppRef.ReportError("Invalid column stride"); return(null); } if (rowStride < byteWidth * colStride - (colStride - 1) || rowStride > MAX_DIM) { mAppRef.ReportError("Invalid row stride"); return(null); } int lastOffset = offset + rowStride * height - (colStride - 1) - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Bitmap runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } VisBitmap8 vb = new VisBitmap8(byteWidth * 7, height); SetHiResPalette(vb); RenderBitmap(mFileData, offset, byteWidth, height, colStride, rowStride, (ColorMode)colorConvMode, isFirstOdd, isHighBitFlipped, vb, 0, 0); return(vb); }
private IVisualization2d GenerateBitmap(ReadOnlyDictionary <string, object> parms) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 1); int height = Util.GetFromObjDict(parms, P_HEIGHT, 1); int rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0); if (rowStride == 0) { rowStride = byteWidth; // provide nice default when stride==0 } if (offset < 0 || offset >= mFileData.Length || byteWidth <= 0 || byteWidth > MAX_DIM || height <= 0 || height > MAX_DIM) { mAppRef.ReportError("Invalid parameter"); return(null); } if (rowStride < byteWidth || rowStride > MAX_DIM) { mAppRef.ReportError("Invalid row stride"); return(null); } int lastOffset = offset + rowStride * height - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Bitmap runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } VisBitmap8 vb = new VisBitmap8(byteWidth * 8, height); SetPalette(vb); // Convert bits to pixels. for (int row = 0; row < height; row++) { for (int byteCol = 0; byteCol < byteWidth; byteCol++) { byte val = mFileData[offset + row * rowStride + byteCol]; for (int bit = 0; bit < 8; bit++) { if ((val & 0x80) != 0) { vb.SetPixelIndex(byteCol * 8 + bit, row, (byte)Color.Solid); } else { vb.SetPixelIndex(byteCol * 8 + bit, row, (byte)Color.Transparent); } val <<= 1; } } } return(vb); }
private void SetPalette(VisBitmap8 vb) { vb.AddColor(0, 0, 0, 0); // 0=transparent vb.AddColor(0xff, 0x20, 0x20, 0xe0); // 1=solid (mostly blue) vb.AddColor(0xff, 0x00, 0x00, 0x00); // 2=black vb.AddColor(0xff, 0xff, 0xff, 0xff); // 3=white vb.AddColor(0xff, 0xd0, 0xd0, 0xd0); // 4=grey }
// IPlugin_Visualizer public IVisualization2d Generate2d(VisDescr descr, ReadOnlyDictionary <string, object> parms) { VisBitmap8 vb = new VisBitmap8(1, 1); vb.AddColor(0, 0, 0, 0); vb.SetPixelIndex(0, 0, 0); return(vb); }
private IVisualization2d GenerateSprite(ReadOnlyDictionary <string, object> parms, bool isMultiColor) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); byte color = (byte)Util.GetFromObjDict(parms, P_COLOR, 0); bool isDoubleWide = Util.GetFromObjDict(parms, P_DOUBLE_WIDE, false); bool isDoubleHigh = Util.GetFromObjDict(parms, P_DOUBLE_HIGH, false); byte color01 = 0; byte color11 = 0; if (isMultiColor) { color01 = (byte)Util.GetFromObjDict(parms, P_COLOR_01, 0); color11 = (byte)Util.GetFromObjDict(parms, P_COLOR_11, 0); } if (offset < 0 || offset >= mFileData.Length || color < 0 || color > MAX_COLOR || color01 < 0 || color01 > MAX_COLOR || color11 < 0 || color11 > MAX_COLOR) { // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + SPRITE_SIZE - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Sprite runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } int xwide = isDoubleWide ? 2 : 1; int xhigh = isDoubleHigh ? 2 : 1; VisBitmap8 vb = new VisBitmap8(SPRITE_BYTE_WIDTH * 8 * xwide, SPRITE_HEIGHT * xhigh); SetPalette(vb); vb.SetAllPixelIndices(TRANSPARENT); if (isMultiColor) { RenderMultiColorBitmap(offset, SPRITE_BYTE_WIDTH, SPRITE_HEIGHT, isDoubleWide, isDoubleHigh, color, color01, color11, vb, 0, 0); } else { RenderHiResBitmap(offset, SPRITE_BYTE_WIDTH, SPRITE_HEIGHT, isDoubleWide, isDoubleHigh, color, vb, 0, 0); } return(vb); }
private IVisualization2d GenerateRomChart(ReadOnlyDictionary <string, object> parms) { int paletteNum = Util.GetFromObjDict(parms, P_COLOR_PALETTE, 0); bool showLabels = Util.GetFromObjDict(parms, P_SHOW_LABELS, true); if (mChrRomOffset < 0) { mAppRef.ReportError("CHR_ROM symbol not found"); return(null); } if (mChrRomOffset + 8192 > mFileData.Length) { mAppRef.ReportError("8KB CHR ROM runs off end of file"); return(null); } const int spacing = 1; const int tWidth = TileWidth + spacing; const int tHeight = TileHeight + spacing; const int gap = 4 + spacing * 2; int labelSpacing = showLabels ? 9 : 0; VisBitmap8 vb = new VisBitmap8(tWidth * 16 * 2 + gap + labelSpacing * 2 + 1, tHeight * 16 + labelSpacing + 1); SetPalette(vb, (Palette)paletteNum); if (showLabels) { for (int i = 0; i < 16; i++) { char ch = (i < 10) ? (char)('0' + i) : (char)('A' + i - 10); VisBitmap8.DrawChar(vb, ch, (i + 1) * tWidth + 1, 1, (byte)Color.Black, (byte)Color.White); VisBitmap8.DrawChar(vb, ch, (i + 16 + 1) * tWidth + gap + 1, 1, (byte)Color.Black, (byte)Color.White); VisBitmap8.DrawChar(vb, ch, 1, (i + 1) * tHeight + 1, (byte)Color.Black, (byte)Color.White); VisBitmap8.DrawChar(vb, ch, (1 + 16 + 16) * tWidth + gap + 1, (i + 1) * tHeight + 1, (byte)Color.Black, (byte)Color.White); } } for (int idx = 0; idx < 512; idx++) { int xshift = idx < 256 ? 0 : tWidth * 16 + gap; int xc = (idx & 0x0f) * tWidth + xshift + labelSpacing + 1; int yc = ((idx & 0xff) >> 4) * tHeight + labelSpacing + 1; RenderTile(idx, vb, xc, yc, false); } return(vb); }
private IVisualization2d GeneratePlayfield(ReadOnlyDictionary <string, object> parms) { const int BYTE_WIDTH = 3; int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int height = Util.GetFromObjDict(parms, P_HEIGHT, 1); int rowThick = Util.GetFromObjDict(parms, P_ROW_THICKNESS, 4); bool isReflected = Util.GetFromObjDict(parms, P_REFLECTED, false); if (offset < 0 || offset >= mFileData.Length || height <= 0 || height > MAX_HEIGHT || rowThick <= 0 || rowThick > MAX_HEIGHT) { // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + BYTE_WIDTH * height - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Playfield runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } // Each half of the playfield is 20 bits wide. VisBitmap8 vb = new VisBitmap8(40, height * rowThick); SetPalette(vb); for (int row = 0; row < height; row++) { // Assume data is stored as PF0,PF1,PF2. PF0/PF2 are in reverse order, so // start by assembling them as a reversed 20-bit word. int srcOff = offset + row * BYTE_WIDTH; int rev = (mFileData[srcOff] >> 4) | (RevBits(mFileData[srcOff + 1], 8) << 4) | (mFileData[srcOff + 2] << 12); // Now generate the forward order. int fwd = RevBits(rev, 20); // Render the first part of the line forward. RenderHalfField(vb, row * rowThick, rowThick, 0, fwd, Color.White); // Render the second half forward or reversed, in grey. RenderHalfField(vb, row * rowThick, rowThick, HALF_WIDTH, isReflected ? rev : fwd, Color.Grey); } return(vb); }
private void SetHiResPalette(VisBitmap8 vb) { // These don't match directly to hi-res color numbers because we want to // avoid adding black/white twice. The colors correspond to Apple IIgs RGB // monitor output. vb.AddColor(0, 0, 0, 0); // 0=transparent vb.AddColor(0xff, 0x00, 0x00, 0x00); // 1=black0/black1 vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1 vb.AddColor(0xff, 0x11, 0xdd, 0x00); // 3=green vb.AddColor(0xff, 0xdd, 0x22, 0xdd); // 4=purple vb.AddColor(0xff, 0xff, 0x66, 0x00); // 5=orange vb.AddColor(0xff, 0x22, 0x22, 0xff); // 6=blue }
private void RenderMultiColorBitmap(int offset, int byteWidth, int height, bool isDoubleWide, bool isDoubleHigh, byte color, byte color01, byte color11, VisBitmap8 vb, int startx, int starty) { int xwide = isDoubleWide ? 2 : 1; int xhigh = isDoubleHigh ? 2 : 1; for (int row = 0; row < height; row++) { for (int col = 0; col < byteWidth; col++) { byte val = mFileData[offset + row * byteWidth + col]; for (int bit = 0; bit < 8; bit += 2) { byte pixColor = 0; switch (val & 0xc0) { case 0x00: pixColor = TRANSPARENT; break; case 0x80: pixColor = color; break; case 0x40: pixColor = color01; break; case 0xc0: pixColor = color11; break; } int xc = startx + (col * 8 + bit) * xwide; int yc = starty + row * xhigh; // Set two adjacent pixels. vb.SetPixelIndex(xc, yc, pixColor); vb.SetPixelIndex(xc + 1, yc, pixColor); if (isDoubleWide || isDoubleHigh) { // Draw doubled pixels. If we're only doubled in one dimension // this will draw some pixels twice. vb.SetPixelIndex(xc + xwide * 2 - 2, yc, pixColor); vb.SetPixelIndex(xc + xwide * 2 - 1, yc, pixColor); vb.SetPixelIndex(xc, yc + xhigh - 1, pixColor); vb.SetPixelIndex(xc + 1, yc + xhigh - 1, pixColor); vb.SetPixelIndex(xc + xwide * 2 - 2, yc + xhigh - 1, pixColor); vb.SetPixelIndex(xc + xwide * 2 - 1, yc + xhigh - 1, pixColor); } val <<= 2; } } } }
private IVisualization2d GenerateSprite(ReadOnlyDictionary <string, object> parms) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int height = Util.GetFromObjDict(parms, P_HEIGHT, 1); if (offset < 0 || offset >= mFileData.Length || height <= 0 || height > MAX_HEIGHT) { // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + height - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Sprite runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } VisBitmap8 vb = new VisBitmap8(8, height); SetPalette(vb); for (int row = 0; row < height; row++) { byte val = mFileData[offset + row]; for (int col = 0; col < 8; col++) { if ((val & 0x80) != 0) { vb.SetPixelIndex(col, row, (byte)Color.Solid); } else { vb.SetPixelIndex(col, row, (byte)Color.Transparent); } val <<= 1; } } return(vb); }
/// <summary> /// Renders a tile from the PPU pattern table. /// </summary> /// <param name="tileNum">Tile number (0-511).</param> /// <param name="vb">Bitmap to render to.</param> /// <param name="xc">X coordinate for upper-left coordinate.</param> /// <param name="yc">Y coordinate for upper-left coordinate.</param> /// <param name="flipHoriz">Flip pixels horizontally</param> private void RenderTile(int tileNum, VisBitmap8 vb, int xc, int yc, bool flipHoriz) { int tileOff = mChrRomOffset + tileNum * BytesPerTile; for (int row = 0; row < 8; row++) { byte part0 = mFileData[tileOff]; byte part1 = mFileData[tileOff + 8]; for (int bit = 7; bit >= 0; bit--) { int val = ((part0 >> bit) & 0x01) | (((part1 >> bit) & 0x01) << 1); vb.SetPixelIndex(xc + (flipHoriz ? bit : 7 - bit), yc, (byte)((byte)Color.Color0 + val)); } tileOff++; yc++; } }
private IVisualization2d GenerateTileGrid(ReadOnlyDictionary <string, object> parms) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int paletteNum = Util.GetFromObjDict(parms, P_COLOR_PALETTE, 0); int width = Util.GetFromObjDict(parms, P_WIDTH, 1); int height = Util.GetFromObjDict(parms, P_HEIGHT, 1); bool flipRight = Util.GetFromObjDict(parms, P_FLIP_RIGHT, false); bool useRightTable = Util.GetFromObjDict(parms, P_RIGHT_TABLE, false); if (mChrRomOffset < 0) { mAppRef.ReportError("CHR_ROM symbol not found"); return(null); } if (offset < 0 || offset >= mFileData.Length) { mAppRef.ReportError("Invalid parameter"); return(null); } if (offset + width * height > mFileData.Length) { mAppRef.ReportError("Data runs off end of file"); return(null); } VisBitmap8 vb = new VisBitmap8(TileWidth * width, TileHeight * height); SetPalette(vb, (Palette)paletteNum); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { int tileNum = mFileData[offset + row * width + col] + (useRightTable ? 256 : 0); RenderTile(tileNum, vb, TileWidth * col, TileHeight * row, flipRight && col >= (width + 1) / 2); } } return(vb); }
// C64 colors, from http://unusedino.de/ec64/technical/misc/vic656x/colors/ // (the ones on https://www.c64-wiki.com/wiki/Color looked wrong) private void SetPalette(VisBitmap8 vb) { vb.AddColor(0xff, 0x00, 0x00, 0x00); // 0=black vb.AddColor(0xff, 0xff, 0xff, 0xff); // 1=white vb.AddColor(0xff, 0x68, 0x37, 0x2b); // 2=red vb.AddColor(0xff, 0x70, 0xa4, 0xb2); // 3=cyan vb.AddColor(0xff, 0x6f, 0x3d, 0x86); // 4=purple vb.AddColor(0xff, 0x58, 0x8d, 0x43); // 5=green vb.AddColor(0xff, 0x35, 0x28, 0x79); // 6=blue vb.AddColor(0xff, 0xb8, 0xc7, 0x6f); // 7=yellow vb.AddColor(0xff, 0x6f, 0x4f, 0x25); // 8=orange vb.AddColor(0xff, 0x43, 0x39, 0x00); // 9-brown vb.AddColor(0xff, 0x9a, 0x67, 0x59); // 10=light red vb.AddColor(0xff, 0x44, 0x44, 0x44); // 11=dark grey vb.AddColor(0xff, 0x6c, 0x6c, 0x6c); // 12=grey vb.AddColor(0xff, 0x9a, 0xd2, 0x84); // 13=light green vb.AddColor(0xff, 0x6c, 0x5e, 0xb5); // 14=light blue vb.AddColor(0xff, 0x95, 0x95, 0x95); // 15=light grey vb.AddColor(0, 0, 0, 0); // 16=transparent }
private bool PlotVectors(int shapeOffset, ref int xmin, ref int xmax, ref int ymin, ref int ymax, bool doPlot, out VisBitmap8 vb) { if (doPlot) { vb = new VisBitmap8(xmax - xmin + 1, ymax - ymin + 1); SetHiResPalette(vb); } else { vb = null; } int xc = 0; int yc = 0; while (true) { if (shapeOffset >= mFileData.Length) { mAppRef.ReportError("Shape definition ran off end of file"); return(false); } byte val = mFileData[shapeOffset++]; if (val == 0) { // done break; } int bits = val & 0x07; DrawVector(bits, false, ref xc, ref yc, ref xmin, ref xmax, ref ymin, ref ymax, vb); bits = (val >> 3) & 0x07; DrawVector(bits, false, ref xc, ref yc, ref xmin, ref xmax, ref ymin, ref ymax, vb); bits = (val >> 6) & 0x03; DrawVector(bits, true, ref xc, ref yc, ref xmin, ref xmax, ref ymin, ref ymax, vb); } return(true); }
private void RenderHalfField(VisBitmap8 vb, int row, int rowDup, int startCol, int val, Color setColor) { for (int col = startCol; col < startCol + HALF_WIDTH; col++) { val <<= 1; byte colorIdx; if ((val & (1 << HALF_WIDTH)) != 0) { colorIdx = (byte)setColor; } else { colorIdx = (byte)Color.Black; } for (int r = row; r < row + rowDup; r++) { vb.SetPixelIndex(col, r, colorIdx); } } }
private void RenderHiResBitmap(int offset, int byteWidth, int height, bool isDoubleWide, bool isDoubleHigh, byte color, VisBitmap8 vb, int startx, int starty) { int xwide = isDoubleWide ? 2 : 1; int xhigh = isDoubleHigh ? 2 : 1; for (int row = 0; row < height; row++) { for (int col = 0; col < byteWidth; col++) { byte val = mFileData[offset + row * byteWidth + col]; for (int bit = 0; bit < 8; bit++) { byte pixColor; if ((val & 0x80) == 0) { pixColor = TRANSPARENT; } else { pixColor = color; } int xc = startx + (col * 8 + bit) * xwide; int yc = starty + row * xhigh; vb.SetPixelIndex(xc, yc, pixColor); if (isDoubleWide || isDoubleHigh) { // Draw doubled pixels. If we're only doubled in one dimension // this will draw some pixels twice. vb.SetPixelIndex(xc + xwide - 1, yc, pixColor); vb.SetPixelIndex(xc, yc + xhigh - 1, pixColor); vb.SetPixelIndex(xc + xwide - 1, yc + xhigh - 1, pixColor); } val <<= 1; } } } }
private IVisualization2d GenerateSpriteGrid(ReadOnlyDictionary <string, object> parms, bool isMultiColor) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int count = Util.GetFromObjDict(parms, P_COUNT, 16); byte color = (byte)Util.GetFromObjDict(parms, P_COLOR, 0); bool isDoubleWide = Util.GetFromObjDict(parms, P_DOUBLE_WIDE, false); bool isDoubleHigh = Util.GetFromObjDict(parms, P_DOUBLE_HIGH, false); byte color01 = 0; byte color11 = 0; if (isMultiColor) { color01 = (byte)Util.GetFromObjDict(parms, P_COLOR_01, 0); color11 = (byte)Util.GetFromObjDict(parms, P_COLOR_11, 0); } if (offset < 0 || offset >= mFileData.Length || color < 0 || color > MAX_COLOR || color01 < 0 || color01 > MAX_COLOR || color11 < 0 || color11 > MAX_COLOR) { // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + SPRITE_STRIDE * count - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Sprite set runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } int xwide = isDoubleWide ? 2 : 1; int xhigh = isDoubleHigh ? 2 : 1; // Try to make it square, unless there's a large number of them. Limit the width // to 16 sprites (384 pixels + padding). int hcells; if (count * xwide > 64) { hcells = 16 / xwide; } else if (count * xwide >= 32) { hcells = 8 / xwide; } else { hcells = (int)Math.Sqrt(count * xwide + 1); } int vcells = (count + hcells - 1) / hcells; VisBitmap8 vb = new VisBitmap8(1 + hcells * SPRITE_BYTE_WIDTH * 8 * xwide + hcells, 1 + vcells * SPRITE_HEIGHT * xhigh + vcells); SetPalette(vb); vb.SetAllPixelIndices(BORDER_COLOR); int cellX = 1; int cellY = 1; for (int idx = 0; idx < count; idx++) { if (isMultiColor) { RenderMultiColorBitmap(offset + idx * SPRITE_STRIDE, SPRITE_BYTE_WIDTH, SPRITE_HEIGHT, isDoubleWide, isDoubleHigh, color, color01, color11, vb, cellX, cellY); } else { RenderHiResBitmap(offset + idx * SPRITE_STRIDE, SPRITE_BYTE_WIDTH, SPRITE_HEIGHT, isDoubleWide, isDoubleHigh, color, vb, cellX, cellY); } cellX += SPRITE_BYTE_WIDTH * 8 * xwide + 1; if (cellX == vb.Width) { cellX = 1; cellY += SPRITE_HEIGHT * xhigh + 1; } } return(vb); }
private void SetPalette(VisBitmap8 vb) { vb.AddColor(0, 0, 0, 0); // 0=transparent vb.AddColor(0xff, 0x20, 0x20, 0xff); // 1=solid }
/// <summary> /// Renders bitmap data. /// </summary> /// <param name="data">Data source, typically the file data.</param> /// <param name="offset">Offset into data of the first byte.</param> /// <param name="byteWidth">Width, in bytes, of the data to render. Each byte /// represents 7 pixels in the output (more or less).</param> /// <param name="height">Height, in lines, of the data to render.</param> /// <param name="colStride">Column stride. The number of bytes used to hold each /// byte of data. Must be >= 1.</param> /// <param name="rowStride">Row stride. The number of bytes used to hold each row /// of data. Must be >= (colStride * byteWidth - (colStride - 1)).</param> /// <param name="colorMode">Color conversion mode.</param> /// <param name="isFirstOdd">If true, render as if we're starting on an odd column. /// This affects the colors.</param> /// <param name="isHighBitFlipped">If true, render as if the high bit has the /// opposite value. This affects the colors.</param> /// <param name="vb">Output bitmap object.</param> /// <param name="xstart">Initial X position in the output.</param> /// <param name="ystart">Initial Y position in the output.</param> private void RenderBitmap(byte[] data, int offset, int byteWidth, int height, int colStride, int rowStride, ColorMode colorMode, bool isFirstOdd, bool isHighBitFlipped, VisBitmap8 vb, int xstart, int ystart) { int bx = xstart; int by = ystart; switch (colorMode) { case ColorMode.Mono: { // Since we're not displaying this we don't need to worry about // half-pixel shifts, and can just convert 7 bits to pixels. for (int row = 0; row < height; row++) { int colIdx = 0; for (int col = 0; col < byteWidth; col++) { byte val = data[offset + colIdx]; for (int bit = 0; bit < 7; bit++) { if ((val & 0x01) == 0) { vb.SetPixelIndex(bx, by, (int)HiResColors.Black0); } else { vb.SetPixelIndex(bx, by, (int)HiResColors.White0); } val >>= 1; bx++; } colIdx += colStride; } bx = xstart; by++; offset += rowStride; } } break; case ColorMode.SimpleColor: { // Straightforward conversion, with no funky border effects. This // represents an idealized version of the hardware. // Bits for every byte, plus a couple of "fake" bits on the ends so // we don't have to throw range-checks everywhere. const int OVER = 2; bool[] lineBits = new bool[OVER + byteWidth * 7 + OVER]; bool[] hiFlags = new bool[OVER + byteWidth * 7 + OVER]; for (int row = 0; row < height; row++) { // Unravel the bits. Note we do each byte "backwards", i.e. the // low bit (which is generally considered to be on the right) is // the leftmost pixel. int idx = OVER; // start past "fake" bits int colIdx = 0; for (int col = 0; col < byteWidth; col++) { byte val = data[offset + colIdx]; bool hiBitSet = (val & 0x80) != 0; for (int bit = 0; bit < 7; bit++) { hiFlags[idx] = hiBitSet ^ isHighBitFlipped; lineBits[idx] = (val & 0x01) != 0; idx++; val >>= 1; } colIdx += colStride; } // Convert to color. int lastBit = byteWidth * 7; for (idx = OVER; idx < lastBit + OVER; idx++) { int colorShift = hiFlags[idx] ? 2 : 0; if (lineBits[idx] && (lineBits[idx - 1] || lineBits[idx + 1])) { // [X]11 or [1]1X; two 1s in a row is always white vb.SetPixelIndex(bx++, by, (byte)HiResColors.White0); } else if (lineBits[idx]) { // [0]10, color pixel bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd; if (isOdd) { vb.SetPixelIndex(bx++, by, (byte)((int)HiResColors.Green + colorShift)); } else { vb.SetPixelIndex(bx++, by, (byte)((int)HiResColors.Purple + colorShift)); } } else if (lineBits[idx - 1] && lineBits[idx + 1]) { // [1]01, keep color going bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd; if (isOdd) { vb.SetPixelIndex(bx++, by, (byte)((int)HiResColors.Purple + colorShift)); } else { vb.SetPixelIndex(bx++, by, (byte)((int)HiResColors.Green + colorShift)); } } else { // [0]0X or [X]01 vb.SetPixelIndex(bx++, by, (byte)HiResColors.Black0); } } // move to next row bx = xstart; by++; offset += rowStride; } } break; case ColorMode.IIgsRGB: { // Color conversion similar to what CiderPress does, but without the // half-pixel shift (we're trying to create a 1:1 bitmap, not 1:2). // // This replicates some of the oddness in Apple IIgs RGB monitor output, // but it's not quite right though. For example: // // observed generated // d5 2a: blue [dk blue] purple ... black ... // aa 55: orange [yellow] green ... white ... // 55 aa: purple [lt blue] blue ... black ... // 2a d5: green [brown] orange ... black ... // // KEGS doesn't seem to try to model this; it shows solid colors with no // wackiness. AppleWin in "Color TV" mode shows similar effects, but is // much blurrier (by design). bool[] lineBits = new bool[byteWidth * 7]; bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simpler int[] colorBuf = new int[byteWidth * 7]; for (int row = 0; row < height; row++) { // Unravel the bits. int idx = 0; int colIdx = 0; for (int col = 0; col < byteWidth; col++) { byte val = data[offset + colIdx]; bool hiBitSet = (val & 0x80) != 0; for (int bit = 0; bit < 7; bit++) { hiFlags[idx] = hiBitSet ^ isHighBitFlipped; lineBits[idx] = (val & 0x01) != 0; idx++; val >>= 1; } colIdx += colStride; } // Convert to color. int lastBit = byteWidth * 7; for (idx = 0; idx < lastBit; idx++) { int colorShift = hiFlags[idx] ? 2 : 0; if (!lineBits[idx]) { // Bit not set, set pixel to black. colorBuf[idx] = (int)HiResColors.Black0; } else { // Bit set, set pixel to white or color. if (idx > 0 && colorBuf[idx - 1] != (int)HiResColors.Black0) { // previous bit was also set, this is white colorBuf[idx] = (int)HiResColors.White0; // the previous pixel is part of a run of white colorBuf[idx - 1] = (int)HiResColors.White0; } else { // previous bit not set *or* was first pixel in line; // set color based on whether this is even or odd pixel col bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd; if (isOdd) { colorBuf[idx] = (int)HiResColors.Green + colorShift; } else { colorBuf[idx] = (int)HiResColors.Purple + colorShift; } } // Do we have a run of the same color? If so, smooth the // color out. Note that white blends smoothly with everything. if (idx > 1 && (colorBuf[idx - 2] == colorBuf[idx] || colorBuf[idx - 2] == (int)HiResColors.White0)) { colorBuf[idx - 1] = colorBuf[idx]; } } } // Write to bitmap. for (idx = 0; idx < lastBit; idx++) { vb.SetPixelIndex(bx++, by, (byte)colorBuf[idx]); } // move to next row bx = xstart; by++; offset += rowStride; } } break; default: // just leave the bitmap empty mAppRef.ReportError("Unknown ColorMode " + colorMode); break; } }
private IVisualization2d GenerateScreen(ReadOnlyDictionary <string, object> parms) { //const int RAW_IMAGE_SIZE = 0x1ff8; const int HR_WIDTH = 280; const int HR_BYTE_WIDTH = HR_WIDTH / 7; const int HR_HEIGHT = 192; int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); bool isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true); if (offset < 0 || offset >= mFileData.Length) { // should be caught by editor mAppRef.ReportError("Invalid parameter"); return(null); } //int lastOffset = offset + RAW_IMAGE_SIZE - 1; //if (lastOffset >= mFileData.Length) { // mAppRef.ReportError("Bitmap runs off end of file (last offset +" + // lastOffset.ToString("x6") + ")"); // return null; //} // Linearize the data. To handle programs that move themselves around before // executing we use the address translator (e.g. the title screen in Space Eggs // is contiguous in memory but split in half in the file). This is slower, but // mAddrTrans is a local (not proxy) object, so it's not too bad. byte[] buf = new byte[HR_BYTE_WIDTH * HR_HEIGHT]; int outIdx = 0; int baseAddr = mAddrTrans.OffsetToAddress(offset); for (int row = 0; row < HR_HEIGHT; row++) { // If row is ABCDEFGH, we want pppFGHCD EABAB000 (where p would be $20/$40). int low = ((row & 0xc0) >> 1) | ((row & 0xc0) >> 3) | ((row & 0x08) << 4); int high = ((row & 0x07) << 2) | ((row & 0x30) >> 4); int rowAddr = baseAddr + ((high << 8) | low); // Not expecting the data to wrap around, but it's possible. rowAddr = (baseAddr & 0xff0000) | (rowAddr & 0xffff); for (int col = 0; col < HR_BYTE_WIDTH; col++) { int srcOffset = mAddrTrans.AddressToOffset(offset, rowAddr + col); if (srcOffset < 0) { mAppRef.ReportError("Address $" + (rowAddr + col).ToString("x4") + " is outside of file"); return(null); } buf[outIdx++] = mFileData[srcOffset]; } } VisBitmap8 vb = new VisBitmap8(HR_WIDTH, HR_HEIGHT); SetHiResPalette(vb); RenderBitmap(buf, 0, HR_BYTE_WIDTH, HR_HEIGHT, 1, HR_BYTE_WIDTH, isColor ? ColorMode.SimpleColor : ColorMode.Mono, false, false, vb, 0, 0); return(vb); }
private IVisualization2d GenerateGrid(int offset, int itemByteWidth, int itemHeight, int colStride, int rowStride, int cellStride, int count, ColorMode colorConvMode, bool isFirstOdd, bool isHighBitFlipped) { // We allow the stride entries to be zero to indicate a "dense" bitmap. if (colStride == 0) { colStride = 1; } if (rowStride == 0) { rowStride = itemByteWidth * colStride; } if (cellStride == 0) { cellStride = rowStride * itemHeight; } if (offset < 0 || offset >= mFileData.Length || itemByteWidth <= 0 || itemByteWidth > MAX_DIM || itemHeight <= 0 || itemHeight > MAX_DIM || count <= 0 || count > MAX_DIM) { // should be caught by editor mAppRef.ReportError("Invalid parameter"); return(null); } if (colStride <= 0 || colStride > MAX_DIM) { mAppRef.ReportError("Invalid column stride"); return(null); } if (rowStride < itemByteWidth * colStride - (colStride - 1) || rowStride > MAX_DIM) { mAppRef.ReportError("Invalid row stride"); return(null); } int lastOffset = offset + (cellStride * (count - 1)) + rowStride * (itemHeight - 1) + colStride * (itemByteWidth - 1); if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Bitmap runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); //} else { // mAppRef.DebugLog("lastOffset=+" + lastOffset.ToString("x6") + // ", len=" + mFileData.Length.ToString("x6")); } // Set the number of horizontal cells. For small counts we try to make it square, // for larger counts we use a reasonable power of 2. int hcells; if (count > 128) { hcells = 32; } else if (count > 64) { hcells = 16; } else if (count >= 32) { hcells = 8; } else { hcells = (int)Math.Sqrt(count + 1); } int vcells = (count + hcells - 1) / hcells; // Create a bitmap with room for each cell, plus a 1-pixel transparent boundary // between them and around the edges. VisBitmap8 vb = new VisBitmap8(1 + hcells * itemByteWidth * 7 + hcells, 1 + vcells * itemHeight + vcells); SetHiResPalette(vb); int cellx = 1; int celly = 1; for (int idx = 0; idx < count; idx++) { RenderBitmap(mFileData, offset + idx * cellStride, itemByteWidth, itemHeight, colStride, rowStride, colorConvMode, isFirstOdd, isHighBitFlipped, vb, cellx, celly); cellx += itemByteWidth * 7 + 1; if (cellx == vb.Width) { cellx = 1; celly += itemHeight + 1; } } return(vb); }
private IVisualization2d GenerateBitmapFont(ReadOnlyDictionary <string, object> parms) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int itemByteWidth = Util.GetFromObjDict(parms, P_ITEM_BYTE_WIDTH, 1); int itemHeight = Util.GetFromObjDict(parms, P_ITEM_HEIGHT, 8); int count = Util.GetFromObjDict(parms, P_COUNT, 96); if (offset < 0 || offset >= mFileData.Length || itemByteWidth <= 0 || itemByteWidth > MAX_DIM || itemHeight <= 0 || itemHeight > MAX_DIM) { // should be caught by editor mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + itemByteWidth * itemHeight * count - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Bitmap runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } // Set the number of horizontal cells to 16 or 32 based on the number of elements. int hcells; if (count > 128) { hcells = 32; } else { hcells = 16; } int vcells = (count + hcells - 1) / hcells; // Create a bitmap with room for each cell, plus a 1-pixel transparent boundary // between them and around the edges. VisBitmap8 vb = new VisBitmap8(1 + hcells * itemByteWidth * 7 + hcells, 1 + vcells * itemHeight + vcells); SetHiResPalette(vb); int cellx = 1; int celly = 1; for (int idx = 0; idx < count; idx++) { //byte color = (byte)(1 + idx % 6); //for (int y = 0; y < itemHeight; y++) { // for (int x = 0; x < itemByteWidth * 7; x++) { // vb.SetPixelIndex(cellx + x, celly + y, color); // } //} RenderBitmap(mFileData, offset + idx * itemByteWidth * itemHeight, itemByteWidth, itemHeight, 1, itemByteWidth, ColorMode.Mono, false, vb, cellx, celly); cellx += itemByteWidth * 7 + 1; if (cellx == vb.Width) { cellx = 1; celly += itemHeight + 1; } } return(vb); }
private void SetHiResPalette(VisBitmap8 vb) { vb.AddColor(0x00, 0x00, 0x00, 0x00); // 0=transparent vb.AddColor(0xff, 0x20, 0x20, 0xe0); // 1=solid (mostly blue) }
/// <summary> /// Plots a point if appropriate, and updates xc/yc according to the vector. The /// min/max values are updated if a point is plotted -- no need to expand the bitmap /// outside the range of actual plotted points. /// </summary> private void DrawVector(int bits, bool isC, ref int xc, ref int yc, ref int xmin, ref int xmax, ref int ymin, ref int ymax, VisBitmap8 vb) { if ((bits & 0x04) != 0) { if (vb != null) { // plot a point vb.SetPixelIndex(xc - xmin, yc - ymin, 1); } if (xmin > xc) { xmin = xc; } if (xmax < xc) { xmax = xc; } if (ymin > yc) { ymin = yc; } if (ymax < yc) { ymax = yc; } } switch (bits & 0x03) { case 0x00: if (isC) { // do nothing } else { // move up yc--; } break; case 0x01: // move right xc++; break; case 0x02: // move down yc++; break; case 0x03: // move left xc--; break; } }
private IVisualization2d GenerateFont(ReadOnlyDictionary <string, object> parms, bool isMultiColor) { int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); int count = Util.GetFromObjDict(parms, P_COUNT, 96); if (offset < 0 || offset >= mFileData.Length) { mAppRef.ReportError("Invalid parameter"); return(null); } int lastOffset = offset + count - 1; if (lastOffset >= mFileData.Length) { mAppRef.ReportError("Font runs off end of file (last offset +" + lastOffset.ToString("x6") + ")"); return(null); } // Set the number of horizontal cells. For small counts we try to make it square, // for larger counts we use a reasonable power of 2. int hcells; if (count > 128) { hcells = 32; } else if (count > 64) { hcells = 16; } else if (count >= 32) { hcells = 8; } else { hcells = (int)Math.Sqrt(count + 1); } int vcells = (count + hcells - 1) / hcells; const int FONT_BYTE_WIDTH = 1; const int FONT_HEIGHT = 8; const int CELL_STRIDE = FONT_BYTE_WIDTH * FONT_HEIGHT; // Create a bitmap with room for each cell, plus a 1-pixel boundary // between them and around the edges. VisBitmap8 vb = new VisBitmap8(1 + hcells * FONT_BYTE_WIDTH * 8 + hcells, 1 + vcells * FONT_HEIGHT + vcells); SetPalette(vb); vb.SetAllPixelIndices(BORDER_COLOR); int cellX = 1; int cellY = 1; byte color = 0; /* black */ byte color01 = 11; /* dark grey */ byte color11 = 15; /* light grey */ for (int idx = 0; idx < count; idx++) { if (isMultiColor) { RenderMultiColorBitmap(offset + idx * CELL_STRIDE, FONT_BYTE_WIDTH, FONT_HEIGHT, false, false, color, color01, color11, vb, cellX, cellY); } else { RenderHiResBitmap(offset + idx * CELL_STRIDE, FONT_BYTE_WIDTH, FONT_HEIGHT, false, false, color, vb, cellX, cellY); } cellX += FONT_BYTE_WIDTH * 8 + 1; if (cellX == vb.Width) { cellX = 1; cellY += FONT_HEIGHT + 1; } } return(vb); }