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