I spent 2 hours last night trying to figure out why my rotation function isn't behaving properly with zoom values a multiple of 8. The generation of the image works properly, even with 64 used, but the rotation isn't. Can you find what's causing this? I've uploaded some screenshots to help explain.

ZoomFactor is 3, no rotation - displays correctly

ZoomFactor is 3, rotation used - displays correctly

ZoomFactor is 8, no rotation - displays correctly

ZoomFactor is 8, rotation used - very distorted when displayed

ZoomFactor is 60, no rotation - displays correctly

ZoomFactor is 60, rotation used - displays correctly

ZoomFactor is 48, no rotation - displays correctly

ZoomFactor is 48, rotation used - very distorted when displayed

This is the code that generates the image. The one with the rotation is the one in focus:

It's the rotation function in focus, not the bottom one that generates the terrain itself (the bottom one works just fine). Although integer division is involved, it's based on the width and height. In fact, the largest common multiple of the width and height is 1920* so anything in the ZoomFactor variable that is a multiple of 1920 would not cause rounding errors. 1920/8 is exactly 240. 1920/48 is exactly 40. I don't see why it's being shifted to the left 16 pixels when a multiple of 8 is used and otherwise random if a multiple of 16 and beyond are used.Code:// related globals unsigned char TerrainData[144000]; unsigned char TerrainPreviewData[15360000]; unsigned char TerrainRotatedImageData[15360000]; FILE *FileHandle; void RotateTerrainImage(unsigned int Width, unsigned int Height) // width and height are the number of blocks { unsigned char ImageRotateBlock[8]; // a block of 8x8 pixels unsigned char ImageRotateBits[64]; unsigned int ArrayIndex = 0; unsigned int SourceImageArrayIndex = 0; unsigned int TargetImageArrayIndex = 0; unsigned int Row = 0; // current row for the block unsigned int Column = 0; unsigned int DebugTest[16]; unsigned int LoopCount = 0; unsigned int StartLoopCount = 9; unsigned int StartArrayIndex = 0; Width /= 8; // for easier use of blocks - number of blocks wide printf("Width %d\nHeight %d\n", Width, Height); while (Row < Height) { ArrayIndex = 0; // reset everything as needed // first, read a block, going up - each row is an 8 wide by 1 high bunch of pixels while (ArrayIndex < 8) { ImageRotateBlock[ArrayIndex] = TerrainPreviewData[(Row+ArrayIndex)*Width+Column]; ArrayIndex++; } // next, copy the bits of the block ArrayIndex = 0; while (ArrayIndex < 8) { ImageRotateBits[ArrayIndex*8] = (ImageRotateBlock[ArrayIndex] & 128) / 128; // copies the bits of every block as a 0 or 1 ImageRotateBits[ArrayIndex*8+1] = (ImageRotateBlock[ArrayIndex] & 64) / 64; ImageRotateBits[ArrayIndex*8+2] = (ImageRotateBlock[ArrayIndex] & 32) / 32; ImageRotateBits[ArrayIndex*8+3] = (ImageRotateBlock[ArrayIndex] & 16) / 16; ImageRotateBits[ArrayIndex*8+4] = (ImageRotateBlock[ArrayIndex] & 8) / 8; ImageRotateBits[ArrayIndex*8+5] = (ImageRotateBlock[ArrayIndex] & 4) / 4; ImageRotateBits[ArrayIndex*8+6] = (ImageRotateBlock[ArrayIndex] & 2) / 2; ImageRotateBits[ArrayIndex*8+7] = (ImageRotateBlock[ArrayIndex] & 1) / 1; ArrayIndex++; } // after that, write the output as bottom up as left to right ArrayIndex = 0; while (ArrayIndex < 8) { // join the bits, reading what was left to right as bottom to top (which rotates it 90 degrees counterclockwise) TerrainRotatedImageData[(ArrayIndex*(Height/8))+(Column*Height)+Row/8] = ImageRotateBits[0+ArrayIndex]*128 + ImageRotateBits[8+ArrayIndex]*64 + ImageRotateBits[16+ArrayIndex]*32 + ImageRotateBits[24+ArrayIndex]*16 + ImageRotateBits[32+ArrayIndex]*8 + ImageRotateBits[40+ArrayIndex]*4 + ImageRotateBits[48+ArrayIndex]*2 + ImageRotateBits[56+ArrayIndex]*1; ArrayIndex++; } // lastly, the column (and row if at the end) should be incremented Column++; // go to the next block column if (Column >= Width) // if at the end { Column = 0; // return to the far left Row += 8; // but go up a row (8 because a block is 8x8, not 8x1) } LoopCount++; // this is for debugging } } void CheckTerrainHeights() { unsigned int ArrayIndex = 0; int CurrentHeight = 52; // you start here int MaxHeightPx = CurrentHeight; double MaxHeightFt = 0.0; unsigned int MaxHeightLocation = 0; int MinHeightPx = CurrentHeight; double MinHeightFt = 0.0; unsigned int MinHeightLocation = 0; int Errors = 0; double ImageTargetHeight = 0.0; int ImageBaseTargetHeight = 0; double ImageTerrainHeight = 0.0; unsigned int BitPos = 0; unsigned int ByteBlock = 0; unsigned int ImageBaseArrayIndex = 0; unsigned char Colors[8] = {48, 24, 16, 0, 0, 192, 96, 0}; // dark blue sky and green landscape long Temp; int ZoomFactor = 60; // sets the zoom factor value, or how much to shrink each side of the image by unsigned int DebugStart; BITMAPFILEHEADER TempFileHead; BITMAPINFOHEADER TempInfoHead; FileHandle = fopen("C:\\My Documents\\My programs\\Terrain height test.bmp", "rb"); // first, read the data fseek(FileHandle, 1078, SEEK_SET); // skip the header straight to the main data fread(&TerrainData, 1, 144000, FileHandle); fclose(FileHandle); /* slope Y change - slope angle 0 - 0.000 5 - 4.764 11 - 10.389 16 - 14.931 22 - 20.136 28 - 25.017 35 - 30.256 42 - 34.992 50 - 39.806 60 - 45.000 72 - 50.194 86 - 55.098 104 - 60.018 129 - 65.056 165 - 70.017 224 - 75.005 340 - 79.992 */ TempInfoHead.biSize = 40; TempInfoHead.biWidth = 7680/(long)ZoomFactor; // 7680 pixels wide (base image, high once rotated) at 1x zoom TempInfoHead.biHeight = 144000/(long)ZoomFactor; // 144000 pixels high (base image, wide once rotated) at 1x zoom TempInfoHead.biPlanes = 1; // must always be 1 TempInfoHead.biBitCount = 1; // monochrome bitmap TempInfoHead.biCompression = BI_RGB; // no compression TempInfoHead.biSizeImage = (DWORD)TempInfoHead.biWidth*(DWORD)TempInfoHead.biHeight/8; // the array size TempInfoHead.biXPelsPerMeter = 2835; // this doesn't really matter what it is TempInfoHead.biYPelsPerMeter = 2835; TempInfoHead.biClrUsed = 2; // black and white only TempInfoHead.biClrImportant = 2; // both possible are important TempFileHead.bfType = 19778; // fills out the structures // 19778 supposedly is "BM" TempFileHead.bfSize = TempInfoHead.biSizeImage+62; // array length plus off bits (or 62) TempFileHead.bfReserved1 = 0; TempFileHead.bfReserved2 = 0; TempFileHead.bfOffBits = 62; while ((ArrayIndex < 144000) && (TerrainData[ArrayIndex] != 0)) // loop until the end or unfinished areas are reached { if (TerrainData[ArrayIndex] == 203) { CurrentHeight -= 340; } // steepest downward slope else if (TerrainData[ArrayIndex] == 251) { CurrentHeight -= 224; } else if (TerrainData[ArrayIndex] == 48) { CurrentHeight -= 165; } else if (TerrainData[ArrayIndex] == 72) { CurrentHeight -= 129; } else if (TerrainData[ArrayIndex] == 96) { CurrentHeight -= 104; } else if (TerrainData[ArrayIndex] == 120) { CurrentHeight -= 86; } else if (TerrainData[ArrayIndex] == 143) { CurrentHeight -= 72; } else if (TerrainData[ArrayIndex] == 167) { CurrentHeight -= 60; } else if (TerrainData[ArrayIndex] == 191) { CurrentHeight -= 50; } else if (TerrainData[ArrayIndex] == 215) { CurrentHeight -= 42; } else if (TerrainData[ArrayIndex] == 239) { CurrentHeight -= 35; } else if (TerrainData[ArrayIndex] == 32) { CurrentHeight -= 28; } else if (TerrainData[ArrayIndex] == 56) { CurrentHeight -= 22; } else if (TerrainData[ArrayIndex] == 80) { CurrentHeight -= 16; } else if (TerrainData[ArrayIndex] == 104) { CurrentHeight -= 11; } else if (TerrainData[ArrayIndex] == 128) { CurrentHeight -= 5; } else if (TerrainData[ArrayIndex] == 151) { CurrentHeight += 0; } else if (TerrainData[ArrayIndex] == 175) { CurrentHeight += 5; } else if (TerrainData[ArrayIndex] == 199) { CurrentHeight += 11; } else if (TerrainData[ArrayIndex] == 223) { CurrentHeight += 16; } else if (TerrainData[ArrayIndex] == 247) { CurrentHeight += 22; } else if (TerrainData[ArrayIndex] == 40) { CurrentHeight += 28; } else if (TerrainData[ArrayIndex] == 64) { CurrentHeight += 35; } else if (TerrainData[ArrayIndex] == 88) { CurrentHeight += 42; } else if (TerrainData[ArrayIndex] == 112) { CurrentHeight += 50; } else if (TerrainData[ArrayIndex] == 135) { CurrentHeight += 60; } else if (TerrainData[ArrayIndex] == 159) { CurrentHeight += 72; } else if (TerrainData[ArrayIndex] == 183) { CurrentHeight += 86; } else if (TerrainData[ArrayIndex] == 207) { CurrentHeight += 104; } else if (TerrainData[ArrayIndex] == 231) { CurrentHeight += 129; } else if (TerrainData[ArrayIndex] == 255) { CurrentHeight += 165; } else if (TerrainData[ArrayIndex] == 36) { CurrentHeight += 224; } else if (TerrainData[ArrayIndex] == 84) { CurrentHeight += 340; } // steepest upward slope else { Errors++; printf("Invalid color at location %d\n", ArrayIndex); } // error occurred! if (CurrentHeight > MaxHeightPx) { MaxHeightPx = CurrentHeight; MaxHeightLocation = ArrayIndex;} // logs minimums and maximums if (CurrentHeight < MinHeightPx) { MinHeightPx = CurrentHeight; MinHeightLocation = ArrayIndex;} ImageTerrainHeight = CurrentHeight/(60.0*(double)ZoomFactor); // gives the height of the terrain in pixels ImageBaseTargetHeight = (int)ImageTerrainHeight; BitPos = (unsigned int)(ImageBaseTargetHeight) % 8; // gives offset for terrain data ByteBlock = 0; if (ArrayIndex % ZoomFactor == 0) // do only if the array index is a multiple of the zoom factor { while (ByteBlock < 960/ZoomFactor) // 256 bytes, 2048 pixels // each byte block is 26.4 feet { ImageTargetHeight = ((double)ByteBlock-1.0)*8.0; // /(double)ZoomFactor; ImageBaseArrayIndex = ByteBlock+((960/ZoomFactor)*(ArrayIndex/ZoomFactor)); if (ImageBaseTargetHeight < ImageTargetHeight) // if below, it should be all black { TerrainPreviewData[ImageBaseArrayIndex] = 0; // all 8 pixels are black } else if (ImageBaseTargetHeight >= ImageTargetHeight+8.0) // if above the next step, it should be all white { TerrainPreviewData[ImageBaseArrayIndex] = 255; // all 8 pixels are white } else { if (BitPos == 0) { TerrainPreviewData[ImageBaseArrayIndex] = 128; } // more white added for higher BitPos values else if (BitPos == 1) { TerrainPreviewData[ImageBaseArrayIndex] = 192; } else if (BitPos == 2) { TerrainPreviewData[ImageBaseArrayIndex] = 224; } else if (BitPos == 3) { TerrainPreviewData[ImageBaseArrayIndex] = 240; } else if (BitPos == 4) { TerrainPreviewData[ImageBaseArrayIndex] = 248; } else if (BitPos == 5) { TerrainPreviewData[ImageBaseArrayIndex] = 252; } else if (BitPos == 6) { TerrainPreviewData[ImageBaseArrayIndex] = 254; } else if (BitPos == 7) { TerrainPreviewData[ImageBaseArrayIndex] = 255; } } ByteBlock++; } } ArrayIndex++; } while (ImageBaseArrayIndex < 15360000) // to fill the rest of the image as something odd to indicate incomplete areas { TerrainPreviewData[ImageBaseArrayIndex] = 9; ImageBaseArrayIndex++; } MaxHeightFt = (double)MaxHeightPx*0.055; MinHeightFt = (double)MinHeightPx*0.055; RotateTerrainImage(TempInfoHead.biWidth, TempInfoHead.biHeight); // actual image dimensions passed to the function Temp = TempInfoHead.biWidth; // swap the width and height around TempInfoHead.biWidth = TempInfoHead.biHeight; TempInfoHead.biHeight = Temp; FileHandle = fopen("C:\\My Documents\\My programs\\Terrain height test heights.bmp", "wb"); // prepare to write a BMP file fwrite(&TempFileHead.bfType, 2, 1, FileHandle); // it's only intended to run on my computer fwrite(&TempFileHead.bfSize, 4, 1, FileHandle); fwrite(&TempFileHead.bfReserved1, 2, 1, FileHandle); fwrite(&TempFileHead.bfReserved2, 2, 1, FileHandle); fwrite(&TempFileHead.bfOffBits, 4, 1, FileHandle); fwrite(&TempInfoHead.biSize, 4, 1, FileHandle); fwrite(&TempInfoHead.biWidth, 4, 1, FileHandle); fwrite(&TempInfoHead.biHeight, 4, 1, FileHandle); fwrite(&TempInfoHead.biPlanes, 2, 1, FileHandle); fwrite(&TempInfoHead.biBitCount, 2, 1, FileHandle); fwrite(&TempInfoHead.biCompression, 4, 1, FileHandle); fwrite(&TempInfoHead.biSizeImage, 4, 1, FileHandle); fwrite(&TempInfoHead.biXPelsPerMeter, 4, 1, FileHandle); fwrite(&TempInfoHead.biYPelsPerMeter, 4, 1, FileHandle); fwrite(&TempInfoHead.biClrUsed, 4, 1, FileHandle); fwrite(&TempInfoHead.biClrImportant, 4, 1, FileHandle); fwrite(&Colors, 1, 8, FileHandle); // the colors black then white fwrite(&TerrainRotatedImageData, 1, TempInfoHead.biSizeImage, FileHandle); // writes the entire output image data (rotated) // fwrite(&TerrainPreviewData, 1, TempInfoHead.biSizeImage, FileHandle); // writes the entire output image data (nonrotated) fclose(FileHandle); printf("\n\nThe max height seen was %d (%6.3lf ft) at %d (%3.4lf mi).\nThe min height was %d (%6.3lf ft) at %d (%3.4lf mi).\nYou left off at %d (%3.4lf mi) and a height of %d (%6.3lf ft).\n\n", MaxHeightPx, MaxHeightFt, MaxHeightLocation, (double)MaxHeightLocation/1600.0, MinHeightPx, MinHeightFt, MinHeightLocation, (double)MinHeightLocation/1600.0, ArrayIndex, (double)ArrayIndex/1600.0, CurrentHeight, (double)CurrentHeight*0.055); }

* These are the prime factors, those in common with both are multiplied (7 2's, a 3, and a 5 are common in both):

7680

2 2 2 2 2 2 2 2 2 3 5

144000

2 2 2 2 2 2 2 3 3 5 5 5