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); }
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); }
// 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); }
/// <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 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); }
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 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; } } } }
/// <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 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); } } }
/// <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; } }