GetScanLineOAMs(DisplayDefinition disDef, OAM[] spriteOAMs, int row, bool LCDCBit2) { int spriteSize = disDef.BytesPerTileShort / 2; if (LCDCBit2) { spriteSize = disDef.BytesPerTileLong / 2; } // Then we select the 10 that correspond int scanLineSize = 0; int maxScanLineSize = 10; foreach (OAM oam in spriteOAMs) { int y = oam.Y - 16; if ((y <= row) && (row < (y + spriteSize))) { scanLineOAMs[scanLineSize++] = oam; if (scanLineSize == maxScanLineSize) { break; } } } return(scanLineSize); }
/// <summary> /// Gets a row of pixels from the tilemap /// </summary> /// <param name="row">The row number to retreive [0, frameHeight)</param> /// <param name="LCDBit3"> /// Whether the LCDC Register (0xFF40) Bit 3 is enabled. /// Determines what tilemap (where the tile indexes are) is used: /// 0: 0x9800 - 0x9BFF /// 1: 0x9C00 - 0x9FFF /// </param> /// <param name="LCDBit4"> /// Whether the LCDC Register (0xFF40) Bit 3 is enabled. /// Determines the base address for the actual tiles and the /// accessing method (interpretation of the byte tile index retreived from the tilemap). /// 0: 0x8800 - 0x97FF | signed access /// 1: 0x8000 - 0x8FFF | unsigned access /// </param> /// <returns>An array with the pixels to show for that row (color already calculated)</returns> internal static void GetRowPixels(ref uint[] frameLineBuffer, short[] pixelLookupTable, DisplayDefinition disDef, Memory memory, uint[] pixelBuffer, int row, bool LCDBit3, bool LCDBit4) { // We determine the y tile int tileY = row / disDef.PixelPerTileY; int tileRemainder = row % disDef.PixelPerTileY; ushort tileMapBaseAddress = GetTileMapBaseAddress(LCDBit3); ushort tileBaseAddress = GetTileBaseAddress(LCDBit4); for (int tileX = 0; tileX < disDef.FrameTileCountX; tileX++) { // We obtain the correct tile index int tileOffset = GetTileOffset(disDef, memory, tileMapBaseAddress, LCDBit4, tileX, tileY); // We obtain both pixels int currentTileBaseAddress = tileBaseAddress + disDef.BytesPerTileShort * tileOffset; byte top = memory.LowLevelRead((ushort)(currentTileBaseAddress + 2 * tileRemainder)); byte bottom = memory.LowLevelRead((ushort)(currentTileBaseAddress + 2 * tileRemainder + 1)); GetPixelsFromTileBytes(pixelLookupTable, ref pixelBuffer, disDef.TilePallete, disDef.PixelPerTileX, top, bottom); int currentTileIndex = tileX * disDef.PixelPerTileX; for (int i = 0; i < disDef.PixelPerTileX; i++) { frameLineBuffer[currentTileIndex + i] = pixelBuffer[i]; } } }
GetRowPixels(ref uint[] frameLineBuffer, short[] pixelLookupTable, DisplayDefinition disDef, Memory memory, uint[] pixelBuffer, int row, bool LCDBit3, bool LCDBit4) { // We determine the y tile int tileY = row / disDef.PixelPerTileY; int tileRemainder = row % disDef.PixelPerTileY; ushort tileMapBaseAddress = GetTileMapBaseAddress(LCDBit3); ushort tileBaseAddress = GetTileBaseAddress(LCDBit4); for (int tileX = 0; tileX < disDef.FrameTileCountX; tileX++) { // We obtain the correct tile index int tileOffset = GetTileOffset(disDef, memory, tileMapBaseAddress, LCDBit4, tileX, tileY); // We obtain both pixels int currentTileBaseAddress = tileBaseAddress + disDef.BytesPerTileShort * tileOffset; byte top = memory.LowLevelRead((ushort)(currentTileBaseAddress + 2 * tileRemainder)); byte bottom = memory.LowLevelRead((ushort)(currentTileBaseAddress + 2 * tileRemainder + 1)); GetPixelsFromTileBytes(pixelLookupTable, ref pixelBuffer, disDef.TilePallete, disDef.PixelPerTileX, top, bottom); int currentTileIndex = tileX * disDef.PixelPerTileX; for (int i = 0; i < disDef.PixelPerTileX; i++) { frameLineBuffer[currentTileIndex + i] = pixelBuffer[i]; } } }
/// <summary> /// Draw a tile into a bitmap. /// IMPORTANT: presently it requires that the bitmap be the whole background (256x256 pixels) /// </summary> /// <param name="bmd">The bitmap data where to output the pixels</param> /// <param name="tileData">The 16 bytes that conform the 8x8 pixels</param> /// <param name="pX">x coord of the pixel where to start drawing the tile</param> /// <param name="pY">y coord of the pixel where to start drawing the tile</param> internal static void DrawTile(short[] pixelLookupTable, DisplayDefinition disDef, uint[] bitmapData, uint[] pixelBuffer, int stride, byte[] tileData, int pX, int pY, int maxPx, int maxPy) { // We iterate for the actual bytes for (int j = 0; j < tileData.Length; j += 2) { int pixelY = pY + (j / 2); if (pixelY < 0) { continue; } if (pixelY >= maxPy) { break; } // We can continue no further int index = pixelY * stride; // Only add every 2 bytes DisFuncs.GetPixelsFromTileBytes(pixelLookupTable, ref pixelBuffer, disDef.TilePallete, disDef.PixelPerTileX, tileData[j], tileData[j + 1]); for (int i = 0; i < 8; i++) { int pixelX = pX + i; if (pixelX < 0) { continue; } if (pixelX >= maxPx) { break; } int pIndex = index + pixelX; bitmapData[pIndex] = pixelBuffer[i]; } } }
SetupSpritePalletes(DisplayDefinition disDef, Memory memory, MMR pallete) { if (pallete == MMR.OBP0) { byte obp0 = memory.LowLevelRead((ushort)MMR.OBP0); disDef.SpritePallete0[0] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp0 >> (2 * color)) & 1; int up = (obp0 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete0[color] = disDef.SpriteColors[index]; } } else if (pallete == MMR.OBP1) { byte obp1 = memory.LowLevelRead((ushort)MMR.OBP1); disDef.SpritePallete1[1] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp1 >> (2 * color)) & 1; int up = (obp1 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete1[color] = disDef.SpriteColors[index]; } } else { throw new InvalidProgramException("Invalid pallete register given"); } }
GetTileOffset(DisplayDefinition disDef, Memory memory, ushort tileMapBaseAddress, bool LCDBit4, int tileX, int tileY) { int tileOffset; if (LCDBit4) { tileOffset = memory.LowLevelRead((ushort)(tileMapBaseAddress + (disDef.FrameTileCountX * tileY) + tileX)); } else { unchecked { byte t = memory.LowLevelRead((ushort)(tileMapBaseAddress + (disDef.FrameTileCountX * tileY) + tileX)); sbyte tR = (sbyte)t; tileOffset = tR; } } return(tileOffset); }
GetTileData(DisplayDefinition disDef, Memory memory, int tileX, int tileY, bool LCDCBit2, bool LCDBit3, bool LCDBit4, bool wrap) { if (wrap) { tileX %= disDef.FrameTileCountX; tileY %= disDef.ScreenTileCountY; } else { if (tileX >= disDef.FrameTileCountX) { tileX = disDef.FrameTileCountX - 1; } if (tileY >= disDef.ScreenTileCountY) { tileY = disDef.ScreenTileCountY - 1; } } ushort tileMapBaseAddress = GetTileMapBaseAddress(LCDBit3); ushort tileBaseAddress = GetTileBaseAddress(LCDBit4); int tileOffset = GetTileOffset(disDef, memory, tileMapBaseAddress, LCDBit4, tileX, tileY); // We obtain the correct tile index byte[] result = GetTileData(disDef, memory, tileBaseAddress, tileOffset, LCDCBit2); return(result); }
internal static void GetPixelRowFromBitmap(ref uint[] frameLineBuffer, DisplayDefinition disDef, uint[] bitmapData, int row, int stride) { int index = row * stride; for (int x = 0; x < stride; ++x) { frameLineBuffer[x] = bitmapData[index++]; } }
GetPixelRowFromBitmap(ref uint[] frameLineBuffer, DisplayDefinition disDef, uint[] bitmapData, int row, int stride) { int index = row * stride; for (int x = 0; x < stride; ++x) { frameLineBuffer[x] = bitmapData[index++]; } }
/// <summary> /// Display constructor. /// </summary> /// <param name="interruptController">A reference to the interrupt controller.</param> /// <param name="Memory">A reference to the memory.</param> public Display(InterruptController interruptController, Memory memory) { _interruptController = interruptController; _memory = memory; _disDef = new DisplayDefinition(); _state = new State(); GeneratePixelLookupTable(); Reset(); }
SetupTilePallete(DisplayDefinition disDef, Memory memory) { // We extract the BGP (BG Pallete Data) byte bgp = memory.LowLevelRead((ushort)MMR.BGP); for (int color = 0; color < 4; ++color) { int down = (bgp >> (2 * color)) & 1; int up = (bgp >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.TilePallete[color] = disDef.TileColors[index]; } }
GetTileData(DisplayDefinition disDef, Memory memory, int tileBaseAddress, int tileOffset, bool LCDCBit2, bool flipX = false, bool flipY = false) { int tileLength = 16; int spriteLength = disDef.BytesPerTileShort; if (LCDCBit2) { spriteLength = disDef.BytesPerTileLong; } // We obtain the tile memory byte[] data = memory.LowLevelArrayRead( (ushort)(tileBaseAddress + (tileLength * tileOffset)), spriteLength); if (flipX) { for (int i = 0; i < spriteLength; ++i) { byte d = data[i]; byte r = (byte)((flipLookup[d & 0x0F] << 4) | flipLookup[d >> 4]); data[i] = r; } } if (flipY) { // NOTE(Cristian): We have to flip them in pairs, because // otherwise the colors change! for (int i = 0; i < spriteLength / 4; ++i) { byte d0 = data[2 * i]; byte d1 = data[2 * i + 1]; int index = (spriteLength - 1) - 2 * i; data[2 * i] = data[index - 1]; data[2 * i + 1] = data[index]; data[index - 1] = d0; data[index] = d1; } } return(data); }
DrawTransparency(DisplayDefinition disDef, uint[] bitmapData, int stride, int minX, int minY, int maxX, int maxY) { uint[] colors = { 0xF0F0F0F0, 0xF0CDCDCD }; int squareSize = 5; for (int y = minY; y < maxY; y++) { int rowIndex = y * stride; for (int x = minX; x < maxX; x++) { int sX = x / squareSize; int sY = y / squareSize; int colorIndex = (sX + (sY % 2)) % 2; int pIndex = rowIndex + x; bitmapData[pIndex] = colors[colorIndex]; } } }
DrawRectangle(DisplayDefinition disDef, uint[] bitmapData, int stride, int rX, int rY, int rWidth, int rHeight, uint color, bool fill = false) { for (int y = 0; y < rHeight; y++) { for (int x = 0; x < rWidth; x++) { int pX = (rX + x) % disDef.FramePixelCountX; int pY = (rY + y) % disDef.FramePixelCountY; if (fill || x == 0 || x == (rWidth - 1) || y == 0 || y == (rHeight - 1)) { int index = pY * stride + pX; bitmapData[index] = color; } } } }
internal static void DrawRectangle(DisplayDefinition disDef, uint[] bitmapData, int stride, int rX, int rY, int rWidth, int rHeight, uint color, bool fill = false) { for (int y = 0; y < rHeight; y++) { for (int x = 0; x < rWidth; x++) { int pX = (rX + x) % disDef.FramePixelCountX; int pY = (rY + y) % disDef.FramePixelCountY; if (fill || x == 0 || x == (rWidth - 1) || y == 0 || y == (rHeight - 1)) { int index = pY * stride + pX; bitmapData[index] = color; } } } }
DrawLine(DisplayDefinition disDef, uint[] bitmapData, int stride, uint[] rowPixels, int targetX, int targetY, int rowStart, int rowSpan, bool CopyZeroPixels = false, bool wrap = false) { // We obtain the data int baseIndex = targetY * stride + targetX; // NOTE(Cristian): rowMax is included for (int i = 0; i < stride; i++) { // NOTE(Cristian): Sometimes the window is 7 pixels to the left (in the case of the windows) // We must draw on those cases if (targetX < 0) { targetX++; continue; } int rowIndex = rowStart + i; if (rowIndex >= rowSpan) { if (wrap) { rowIndex %= rowSpan; } else { break; } } uint pixel = rowPixels[rowIndex]; if (!CopyZeroPixels && pixel == 0) { continue; } bitmapData[baseIndex + i] = rowPixels[rowIndex]; } }
DrawTile(short[] pixelLookupTable, DisplayDefinition disDef, uint[] bitmapData, uint[] pixelBuffer, int stride, byte[] tileData, int pX, int pY, int maxPx, int maxPy) { // We iterate for the actual bytes for (int j = 0; j < tileData.Length; j += 2) { int pixelY = pY + (j / 2); if (pixelY < 0) { continue; } if (pixelY >= maxPy) { break; } // We can continue no further int index = pixelY * stride; // Only add every 2 bytes DisFuncs.GetPixelsFromTileBytes(pixelLookupTable, ref pixelBuffer, disDef.TilePallete, disDef.PixelPerTileX, tileData[j], tileData[j + 1]); for (int i = 0; i < 8; i++) { int pixelX = pX + i; if (pixelX < 0) { continue; } if (pixelX >= maxPx) { break; } int pIndex = index + pixelX; bitmapData[pIndex] = pixelBuffer[i]; } } }
internal static void DrawLine(DisplayDefinition disDef, uint[] bitmapData, int stride, uint[] rowPixels, int targetX, int targetY, int rowStart, int rowSpan, bool CopyZeroPixels = false, bool wrap = false) { // We obtain the data int baseIndex = targetY * stride + targetX; // NOTE(Cristian): rowMax is included for (int i = 0; i < stride; i++) { // NOTE(Cristian): Sometimes the window is 7 pixels to the left (in the case of the windows) // We must draw on those cases if (targetX < 0) { targetX++; continue; } int rowIndex = rowStart + i; if (rowIndex >= rowSpan) { if(wrap) { rowIndex %= rowSpan; } else { break; } } uint pixel = rowPixels[rowIndex]; if (!CopyZeroPixels && pixel == 0) { continue; } bitmapData[baseIndex + i] = rowPixels[rowIndex]; } }
GetSpriteRowPixels(short[] pixelLookupTable, DisplayDefinition disDef, Memory memory, OAM[] spriteOAMs, uint[] targetPixels, uint[] pixelBuffer, int row, bool LCDCBit2, bool ignoreBackgroundPriority = false) { int scanLineSize = GetScanLineOAMs(disDef, spriteOAMs, row, LCDCBit2); // We obtain the pixels we want from it for (int oamIndex = scanLineSize - 1; oamIndex >= 0; --oamIndex) { OAM oam = scanLineOAMs[oamIndex]; bool flipX = Utils.UtilFuncs.TestBit(oam.Flags, 5) != 0; bool flipY = Utils.UtilFuncs.TestBit(oam.Flags, 6) != 0; byte[] tilePixels = GetTileData(disDef, memory, 0x8000, oam.SpriteCode, LCDCBit2, flipX, flipY); int x = oam.X - 8; int y = row - (oam.Y - 16); uint[] spritePallete = (Utils.UtilFuncs.TestBit(oam.Flags, 4) == 0) ? disDef.SpritePallete0 : disDef.SpritePallete1; GetPixelsFromTileBytes(pixelLookupTable, ref pixelBuffer, spritePallete, disDef.PixelPerTileX, tilePixels[2 * y], tilePixels[2 * y + 1]); bool backgroundPriority = (Utils.UtilFuncs.TestBit(oam.Flags, 7) != 0); if (ignoreBackgroundPriority) { backgroundPriority = false; } for (int i = 0; i < 8; ++i) { int pX = x + i; if (pX < 0) { continue; } if (pX >= disDef.ScreenPixelCountX) { break; } uint color = pixelBuffer[i]; if (color == 0) { continue; } // transparent pixel // NOTE(Cristian): If the BG priority bit is set, the sprite is hidden // on every color except tile color 0 if (backgroundPriority) { if (targetPixels[pX] != disDef.TileColors[0]) { continue; } } targetPixels[pX] = color; } } }
internal static void SetupSpritePalletes(DisplayDefinition disDef, Memory memory, MMR pallete) { if (pallete == MMR.OBP0) { byte obp0 = memory.LowLevelRead((ushort)MMR.OBP0); disDef.SpritePallete0[0] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp0 >> (2 * color)) & 1; int up = (obp0 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete0[color] = disDef.SpriteColors[index]; } } else if (pallete == MMR.OBP1) { byte obp1 = memory.LowLevelRead((ushort)MMR.OBP1); disDef.SpritePallete1[1] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp1 >> (2 * color)) & 1; int up = (obp1 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete1[color] = disDef.SpriteColors[index]; } } else { throw new InvalidProgramException("Invalid pallete register given"); } }
internal static void SetupTilePallete(DisplayDefinition disDef, Memory memory) { // We extract the BGP (BG Pallete Data) byte bgp = memory.LowLevelRead((ushort)MMR.BGP); for (int color = 0; color < 4; ++color) { int down = (bgp >> (2 * color)) & 1; int up = (bgp >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.TilePallete[color] = disDef.TileColors[index]; } }
internal static byte[] GetTileData(DisplayDefinition disDef, Memory memory, int tileBaseAddress, int tileOffset, bool LCDCBit2, bool flipX = false, bool flipY = false) { int tileLength = 16; int spriteLength = disDef.BytesPerTileShort; if (LCDCBit2) { spriteLength = disDef.BytesPerTileLong; } // We obtain the tile memory byte[] data = memory.LowLevelArrayRead( (ushort)(tileBaseAddress + (tileLength * tileOffset)), spriteLength); if (flipX) { for (int i = 0; i < spriteLength; ++i) { byte d = data[i]; byte r = (byte)((flipLookup[d & 0x0F] << 4) | flipLookup[d >> 4]); data[i] = r; } } if (flipY) { // NOTE(Cristian): We have to flip them in pairs, because // otherwise the colors change! for (int i = 0; i < spriteLength / 4; ++i) { byte d0 = data[2 * i]; byte d1 = data[2 * i + 1]; int index = (spriteLength - 1) - 2 * i; data[2 * i] = data[index - 1]; data[2 * i + 1] = data[index]; data[index - 1] = d0; data[index] = d1; } } return data; }
internal static int GetTileOffset(DisplayDefinition disDef, Memory memory, ushort tileMapBaseAddress, bool LCDBit4, int tileX, int tileY) { int tileOffset; if (LCDBit4) { tileOffset = memory.LowLevelRead((ushort)(tileMapBaseAddress + (disDef.FrameTileCountX * tileY) + tileX)); } else { unchecked { byte t = memory.LowLevelRead((ushort)(tileMapBaseAddress + (disDef.FrameTileCountX * tileY) + tileX)); sbyte tR = (sbyte)t; tileOffset = tR; } } return tileOffset; }
/// <summary> /// Retreives a the contents on a tile depending on the coordinates and the accessing methods. /// </summary> /// <param name="tileX">The x coord for the tile</param> /// <param name="tileY">The y coord for the tile</param> /// <param name="LCDBit3"> /// Whether the LCDC Register (0xFF40) Bit 3 is enabled. /// Determines what tilemap (where the tile indexes are) is used: /// 0: 0x9800 - 0x9BFF /// 1: 0x9C00 - 0x9FFF /// </param> /// <param name="LCDBit4"> /// Whether the LCDC Register (0xFF40) Bit 3 is enabled. /// Determines the base address for the actual tiles and the /// accessing method (interpretation of the byte tile index retreived from the tilemap). /// 0: 0x8800 - 0x97FF | signed access /// 1: 0x8000 - 0x8FFF | unsigned access /// </param> /// <param name="wrap">Whether the x, y tile coordinates should wrap or be clipped</param> /// <returns>A byte[] with the 16 bytes that create a tile</returns> internal static byte[] GetTileData(DisplayDefinition disDef, Memory memory, int tileX, int tileY, bool LCDCBit2, bool LCDBit3, bool LCDBit4, bool wrap) { if (wrap) { tileX %= disDef.FrameTileCountX; tileY %= disDef.ScreenTileCountY; } else { if (tileX >= disDef.FrameTileCountX) { tileX = disDef.FrameTileCountX - 1; } if (tileY >= disDef.ScreenTileCountY) { tileY = disDef.ScreenTileCountY - 1; } } ushort tileMapBaseAddress = GetTileMapBaseAddress(LCDBit3); ushort tileBaseAddress = GetTileBaseAddress(LCDBit4); int tileOffset = GetTileOffset(disDef, memory, tileMapBaseAddress, LCDBit4, tileX, tileY); // We obtain the correct tile index byte[] result = GetTileData(disDef, memory, tileBaseAddress, tileOffset, LCDCBit2); return result; }
internal static void GetSpriteRowPixels(short[] pixelLookupTable, DisplayDefinition disDef, Memory memory, OAM[] spriteOAMs, uint[] targetPixels, uint[] pixelBuffer, int row, bool LCDCBit2, bool ignoreBackgroundPriority = false) { int scanLineSize = GetScanLineOAMs(disDef, spriteOAMs, row, LCDCBit2); // We obtain the pixels we want from it for (int oamIndex = scanLineSize - 1; oamIndex >= 0; --oamIndex) { OAM oam = scanLineOAMs[oamIndex]; bool flipX = Utils.UtilFuncs.TestBit(oam.Flags, 5) != 0; bool flipY = Utils.UtilFuncs.TestBit(oam.Flags, 6) != 0; byte[] tilePixels = GetTileData(disDef, memory, 0x8000, oam.SpriteCode, LCDCBit2, flipX, flipY); int x = oam.X - 8; int y = row - (oam.Y - 16); uint[] spritePallete = (Utils.UtilFuncs.TestBit(oam.Flags, 4) == 0) ? disDef.SpritePallete0 : disDef.SpritePallete1; GetPixelsFromTileBytes(pixelLookupTable, ref pixelBuffer, spritePallete, disDef.PixelPerTileX, tilePixels[2 * y], tilePixels[2 * y + 1]); bool backgroundPriority = (Utils.UtilFuncs.TestBit(oam.Flags, 7) != 0); if (ignoreBackgroundPriority) { backgroundPriority = false; } for (int i = 0; i < 8; ++i) { int pX = x + i; if (pX < 0) { continue; } if (pX >= disDef.ScreenPixelCountX) { break; } uint color = pixelBuffer[i]; if (color == 0) { continue; } // transparent pixel // NOTE(Cristian): If the BG priority bit is set, the sprite is hidden // on every color except tile color 0 if (backgroundPriority) { if (targetPixels[pX] != disDef.TileColors[0]) { continue; } } targetPixels[pX] = color; } } }
/// <summary> /// Gets the OAMs for the a certain row /// </summary> /// <param name="spriteOAMs"></param> /// <param name="row"></param> /// <param name="LCDCBit2"></param> /// <returns></returns> internal static int GetScanLineOAMs(DisplayDefinition disDef, OAM[] spriteOAMs, int row, bool LCDCBit2) { int spriteSize = disDef.BytesPerTileShort / 2; if (LCDCBit2) { spriteSize = disDef.BytesPerTileLong / 2; } // Then we select the 10 that correspond int scanLineSize = 0; int maxScanLineSize = 10; foreach (OAM oam in spriteOAMs) { int y = oam.Y - 16; if ((y <= row) && (row < (y + spriteSize))) { scanLineOAMs[scanLineSize++] = oam; if (scanLineSize == maxScanLineSize) { break; } } } return scanLineSize; }
internal static void DrawTransparency(DisplayDefinition disDef, uint[] bitmapData, int stride, int minX, int minY, int maxX, int maxY) { uint[] colors = { 0xF0F0F0F0, 0xF0CDCDCD }; int squareSize = 5; for (int y = minY; y < maxY; y++) { int rowIndex = y * stride; for (int x = minX; x < maxX; x++) { int sX = x / squareSize; int sY = y / squareSize; int colorIndex = (sX + (sY % 2)) % 2; int pIndex = rowIndex + x; bitmapData[pIndex] = colors[colorIndex]; } } }