Skip to content

Instantly share code, notes, and snippets.

@brikp
Created December 6, 2021 12:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brikp/e181c17577a922340442ebe0419e2c3a to your computer and use it in GitHub Desktop.
Save brikp/e181c17577a922340442ebe0419e2c3a to your computer and use it in GitHub Desktop.
using System;
using Godot;
using Jigsaw.Entities;
using System.Collections.Generic;
using System.Linq;
public enum PuzzlePosition { CENTER, TOP, RIGHT, BOTTOM, LEFT, TOP_LEFT, TOP_RIGHT, BOT_LEFT, BOT_RIGHT };
public class PuzzleGen
{
private const int MASK_REFERENCE_SIZE = 100;
private const int PUZZLE_REFERENCE_SIZE = 100;
public int PieceSize { get; set; }
public bool HasFinishedGenerating = false;
private Image fullPuzzleImage;
private Texture fullPuzzleTexture;
private Image adjustedPuzzleImage;
private ImageTexture adjustedPuzzleTexture;
private ShaderMaterial puzzleShader;
//private Dictionary<PuzzleMasks, (Texture top, Texture bottom, Texture right, Texture left)> maskTextures =
// new Dictionary<PuzzleMasks, (Texture top, Texture bottom, Texture right, Texture left)>();
private Dictionary<PuzzleMasks, (Image top, Image bottom, Image right, Image left)> maskImages =
new Dictionary<PuzzleMasks, (Image top, Image bottom, Image right, Image left)>();
private Dictionary<Vector2, PuzzlePosition> puzzlePositionLookup;
private Dictionary<Vector2, PuzzleSideData> tabsAndBlanks;
private int maskAdjustedSize;
private PackedScene puzzlePieceScene;
private int adjustedTextureWidth;
private int adjustedTextureHeight;
private int generatorX;
private int generatorY;
public int PuzzleCount;
private string saveFilePath;
File saveFile = new File();
private Queue<string> sideDataSaveQueue = new Queue<string>();
private bool isLoadingSaveData = false;
public PuzzleGen(string puzzleTexturePath, int pieceSize = 100)
{
saveFilePath = GameState.currentSaveDirPath + "/side_data.txt";
if (saveFile.FileExists(saveFilePath))
isLoadingSaveData = true;
GD.Print("Side data filepath: ", saveFilePath);
PieceSize = pieceSize;
puzzlePieceScene = GD.Load("res://Entities/PuzzlePiece.tscn") as PackedScene;
fullPuzzleTexture = GD.Load(puzzleTexturePath) as Texture;
fullPuzzleImage = fullPuzzleTexture.GetData();
fullPuzzleImage.Convert(Image.Format.Rgba8);
puzzleShader = GD.Load<ShaderMaterial>("res://Scenes/test.material");
// puzzleShader = GD.Load<ShaderMaterial>("res://Scenes/puzzle_bevel_shader.material");
// TODO: This should be moved to the main menu and passed to puzzle manager. Also image size should be saved in puzzle manager when saving data
// Adjust size to the puzzle size
int puzzleWidth = fullPuzzleTexture.GetWidth();
int puzzleHeight = fullPuzzleTexture.GetHeight();
// 1. Check how many pieces fit and get adjusted width and height
int pieceXCount = puzzleWidth / PieceSize;
int pieceYCount = puzzleHeight / PieceSize;
int adjustedWidth = pieceXCount * pieceSize;
int adjustedHeight = pieceYCount * pieceSize;
// 2. Cut off new size from the full image from middle out
int startingX = (puzzleWidth - adjustedWidth) / 2;
int startingY = (puzzleHeight - adjustedHeight) / 2;
Rect2 usedImagePartRect = new Rect2(startingX, startingY, adjustedWidth, adjustedHeight);
adjustedPuzzleImage = new Image();
adjustedPuzzleTexture = new ImageTexture();
adjustedPuzzleImage = fullPuzzleImage.GetRect(usedImagePartRect);
adjustedPuzzleTexture.CreateFromImage(adjustedPuzzleImage);
float puzzleSizeAdjustment = (float)PieceSize / PUZZLE_REFERENCE_SIZE;
maskAdjustedSize = (int)(MASK_REFERENCE_SIZE * puzzleSizeAdjustment);
foreach (PuzzleMasks key in PuzzleMask.PickedMasks)
{
string maskPath = PuzzleMask.MaskInfo[key].maskPath;
Texture maskTexture = GD.Load(maskPath) as Texture;
(Image top, Image bottom, Image right, Image left) imageTuple = (
getRotatedImage(maskTexture.GetData(), 270),
getRotatedImage(maskTexture.GetData(), 90),
maskTexture.GetData(),
getRotatedImage(maskTexture.GetData(), 180)
);
imageTuple.top.Resize(maskAdjustedSize, maskAdjustedSize);
imageTuple.right.Resize(maskAdjustedSize, maskAdjustedSize);
imageTuple.bottom.Resize(maskAdjustedSize, maskAdjustedSize);
imageTuple.left.Resize(maskAdjustedSize, maskAdjustedSize);
maskImages.Add(key, imageTuple);
}
}
public void StartPuzzleGeneration(Node2D puzzleBox)
{
GD.Print("Starting puzzle generation, ", OS.GetTicksMsec());
adjustedTextureWidth = adjustedPuzzleTexture.GetWidth();
adjustedTextureHeight = adjustedPuzzleTexture.GetHeight();
if (isLoadingSaveData)
LoadPuzzleLookupsFromSaveFile();
else
BuildPuzzleLookups(adjustedTextureWidth, adjustedTextureHeight);
generatorX = 0;
generatorY = 0;
}
private void LoadPuzzleLookupsFromSaveFile()
{
GD.Print("Loading puzzle side data from file: ", saveFilePath, ", ", OS.GetTicksMsec());
tabsAndBlanks = new Dictionary<Vector2, PuzzleSideData>();
saveFile.Open(saveFilePath, File.ModeFlags.Read);
int pieceCount = Int32.Parse(saveFile.GetLine());
GD.Print("Loading ", pieceCount, " pieces ", OS.GetTicksMsec());
for (int i = 0; i < pieceCount; i++)
{
Vector2 index = Vector2.Zero;
PuzzleSideData sideData = new PuzzleSideData();
string[] data = saveFile.GetLine().Split(",");
index.x = float.Parse(data[0]);
index.y = float.Parse(data[1]);
sideData.Top.mask = (PuzzleMasks)Int32.Parse(data[2]);
sideData.Top.isTab = data[3] == "1";
sideData.Top.isEdge = data[4] == "1";
sideData.Right.mask = (PuzzleMasks)Int32.Parse(data[5]);
sideData.Right.isTab = data[6] == "1";
sideData.Right.isEdge = data[7] == "1";
sideData.Bottom.mask = (PuzzleMasks)Int32.Parse(data[8]);
sideData.Bottom.isTab = data[9] == "1";
sideData.Bottom.isEdge = data[10] == "1";
sideData.Left.mask = (PuzzleMasks)Int32.Parse(data[11]);
sideData.Left.isTab = data[12] == "1";
sideData.Left.isEdge = data[13] == "1";
tabsAndBlanks.Add(index, sideData);
}
saveFile.Close();
}
public PuzzlePiece GetNextPuzzlePiece()
{
if (generatorY >= adjustedTextureHeight)
{
generatorY = 0;
generatorX += PieceSize;
}
if (generatorX >= adjustedTextureWidth)
{
return null;
}
if (PuzzleCount % 50 == 0)
{
GD.Print("Generating... puzzle ", PuzzleCount);
}
//GD.Print("Generating puzzle (", generatorX, ",", generatorY, "), ", OS.GetTicksMsec());
Vector2 puzzlePieceIndex = new Vector2(generatorX / PieceSize, generatorY / PieceSize);
PuzzlePiece puzzlePiece = GetPuzzlePieceWithNew(puzzlePieceIndex, generatorX, generatorY, PieceSize);
puzzlePiece.Index = puzzlePieceIndex;
puzzlePiece.BoundingBoxTopLeftIndex = puzzlePieceIndex;
puzzlePiece.BoundingBoxBotRightIndex = puzzlePieceIndex;
puzzlePiece.state = PuzzlePiece.PuzzleStates.IN_BOX;
puzzlePiece.PuzzleSize = PieceSize;
puzzlePiece.IndexSet.Add(puzzlePieceIndex);
Label puzzleLabel = new Label();
puzzleLabel.Name = "Label";
puzzleLabel.Visible = GameState.debugModeOn;
puzzleLabel.Text = "(" + generatorX / PieceSize + "," + generatorY / PieceSize + ")";
puzzlePiece.AddChild(puzzleLabel);
generatorY += PieceSize;
PuzzleCount += 1;
return puzzlePiece;
}
private PuzzlePiece GetPuzzlePieceWithNew(Vector2 puzzleIndex, int x, int y, int step)
{
PuzzlePiece node = puzzlePieceScene.Instance() as PuzzlePiece;
Rect2 imageRect = new Rect2(x, y, step, step);
Sprite sprite = new Sprite();
sprite.Name = "Sprite";
sprite.Texture = GetPuzzleTextureFromRect(puzzleIndex, imageRect);
float shaderOutline = Mathf.Max((float)PieceSize / 100, 0.5f);
puzzleShader.SetShaderParam("line_thickness", shaderOutline);
sprite.Material = puzzleShader;
CollisionShape2D shape = new CollisionShape2D();
RectangleShape2D rect = new RectangleShape2D();
rect.Extents = new Vector2(sprite.GetRect().Size.x / 2.0f, sprite.GetRect().Size.y / 2.0f);
shape.Shape = rect;
shape.Name = "CollisionShape";
Area2D area2d = new Area2D();
area2d.Name = "Area2D";
area2d.CollisionLayer = 2;
area2d.CollisionMask = 2;
area2d.Monitorable = false;
area2d.Monitoring = false;
area2d.AddChild(shape);
node.AddChild(sprite);
node.AddChild(area2d);
return node;
}
private ImageTexture GetPuzzleTextureFromRect(Vector2 puzzleIndex, Rect2 rect)
{
Image image = adjustedPuzzleImage.GetRect(rect);
PuzzleSideData puzzleSideData = tabsAndBlanks[puzzleIndex];
//ColorImageBorder(image, new Color(0, 0, 0, 0.35f));
AddBlanksToImage(image, puzzleSideData);
image = AddTabsToImage(image, rect, puzzleSideData);
ImageTexture texture = new ImageTexture();
texture.CreateFromImage(image);
return texture;
}
private void AddBlanksToImage(Image image, PuzzleSideData puzzleSideData)
{
if (puzzleSideData.Top.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Top.isTab == false)
{
ApplyBlankMask(puzzleSideData, image, maskImages[puzzleSideData.Top.mask].bottom, PuzzlePosition.TOP);
}
if (puzzleSideData.Right.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Right.isTab == false)
{
ApplyBlankMask(puzzleSideData, image, maskImages[puzzleSideData.Right.mask].left, PuzzlePosition.RIGHT); ;
}
if (puzzleSideData.Bottom.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Bottom.isTab == false)
{
ApplyBlankMask(puzzleSideData, image, maskImages[puzzleSideData.Bottom.mask].top, PuzzlePosition.BOTTOM);
}
if (puzzleSideData.Left.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Left.isTab == false)
{
ApplyBlankMask(puzzleSideData, image, maskImages[puzzleSideData.Left.mask].right, PuzzlePosition.LEFT);
}
}
private Image AddTabsToImage(Image image, Rect2 rect, PuzzleSideData puzzleSideData)
{
Image mask, sideImage;
Vector2 topLeft = new Vector2(maskAdjustedSize / 2, maskAdjustedSize / 2);
Rect2 maskRect;
// 1. Create new image with target size
Vector2 targetSize = image.GetSize() + topLeft * 2;
Image targetImage = new Image();
targetImage.Create((int)targetSize.x, (int)targetSize.y, false, image.GetFormat());
// 2. Set pixels for base image (starting from top+left resize vector position)
targetImage.BlitRect(image, image.GetUsedRect(), topLeft);
// 3. Get pixels for the tabs from the full puzzle
// 4. Blit mask with image on each side if needed
if (puzzleSideData.Top.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Top.isTab)
{
mask = maskImages[puzzleSideData.Top.mask].top;
maskRect = mask.GetUsedRect();
maskRect.Position = rect.Position;
maskRect.Position -= new Vector2(0, maskRect.Size.y);
sideImage = adjustedPuzzleImage.GetRect(maskRect);
ApplyImageMask(sideImage, mask.GetRect(mask.GetUsedRect()));
targetImage.BlitRect(
sideImage,
new Rect2(0, 0, maskRect.Size.x, maskRect.Size.y),
new Vector2(topLeft.x, topLeft.y - maskRect.Size.y));
}
if (puzzleSideData.Right.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Right.isTab == true)
{
mask = maskImages[puzzleSideData.Right.mask].right;
maskRect = mask.GetUsedRect();
maskRect.Position = rect.Position + new Vector2(image.GetWidth(), 0);
sideImage = adjustedPuzzleImage.GetRect(maskRect);
ApplyImageMask(sideImage, mask.GetRect(mask.GetUsedRect()));
targetImage.BlitRect(
sideImage,
new Rect2(0, 0, maskRect.Size.x, maskRect.Size.y),
new Vector2(targetSize.y - topLeft.x, topLeft.y));
}
if (puzzleSideData.Bottom.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Bottom.isTab == true)
{
mask = maskImages[puzzleSideData.Bottom.mask].bottom;
maskRect = mask.GetUsedRect();
maskRect.Position = rect.Position + new Vector2(0, image.GetHeight());
sideImage = adjustedPuzzleImage.GetRect(maskRect);
ApplyImageMask(sideImage, mask.GetRect(mask.GetUsedRect()));
targetImage.BlitRect(
sideImage,
new Rect2(0, 0, maskRect.Size.x, maskRect.Size.y),
new Vector2(topLeft.x, targetSize.y - topLeft.y));
}
if (puzzleSideData.Left.mask != PuzzleMasks.NOT_SET &&
puzzleSideData.Left.isTab == true)
{
mask = maskImages[puzzleSideData.Left.mask].left;
maskRect = mask.GetUsedRect();
maskRect.Position = rect.Position;
maskRect.Position -= new Vector2(maskRect.Size.x, 0);
sideImage = adjustedPuzzleImage.GetRect(maskRect);
ApplyImageMask(sideImage, mask.GetRect(mask.GetUsedRect()));
targetImage.BlitRect(
sideImage,
new Rect2(0, 0, maskRect.Size.x, maskRect.Size.y),
new Vector2(topLeft.x - maskRect.Size.x, topLeft.y));
}
return targetImage;
}
private void ApplyImageMask(Image image, Image mask)
{
image.Lock();
mask.Lock();
for (int x = 0; x < image.GetWidth(); x += 1)
{
for (int y = 0; y < image.GetHeight(); y += 1)
{
Color maskColor = mask.GetPixel(x, y);
if (maskColor.a == 0)
{
image.SetPixel(x, y, new Color(0, 0, 0, 0));
}
if (maskColor.a > 0 && maskColor.a < 1)
{
Color imageColor = image.GetPixel(x, y);
imageColor.a = maskColor.a;
image.SetPixel(x, y, imageColor);
}
}
}
image.Unlock();
mask.Unlock();
}
private void ApplyBlankMask(PuzzleSideData puzzleSideData, Image image, Image blankMask, PuzzlePosition side)
{
Rect2 maskRect = blankMask.GetUsedRect();
Vector2 point = Vector2.Zero;
int maskX = (int)maskRect.Position.x;
int maskY;
image.Lock();
blankMask.Lock();
// 1. Get starting point - middle of side being masked
// 2. Move starting point to align with top left corner of the maskRect
switch (side)
{
case PuzzlePosition.RIGHT:
point.x = image.GetWidth();
point.y = 0;
point.x -= maskRect.Size.x;
//point.y -= maskRect.Size.y / 2;
break;
case PuzzlePosition.BOTTOM:
point.x = 0;
point.y = image.GetHeight();
//point.x -= maskRect.Size.x / 2;
point.y -= maskRect.Size.y;
break;
case PuzzlePosition.LEFT:
point.x = 0;
point.y = 0;
//point.y -= maskRect.Size.y / 2;
break;
case PuzzlePosition.TOP:
point.x = 0;
point.y = 0;
//point.x -= maskRect.Size.x / 2;
break;
}
// 3. Apply the mask by iterating over the pixels
for (int x = (int)point.x; x < point.x + maskRect.Size.x; x += 1)
{
maskY = (int)maskRect.Position.y;
for (int y = (int)point.y; y < point.y + maskRect.Size.y; y += 1)
{
Color color = blankMask.GetPixel(maskX, maskY);
Color imageColor = image.GetPixel(x, y);
if (color.a > 0.1f)
{
imageColor.a = 1 - color.a;
image.SetPixel(x, y, imageColor);
}
maskY += 1;
}
maskX += 1;
}
image.Unlock();
blankMask.Unlock();
}
private void BuildPuzzleLookups(int fullPuzzleWidth, int fullPuzzleHeight)
{
GD.Print("No sidedata saved - Starting BuildPuzzleLookups, ", OS.GetTicksMsec());
puzzlePositionLookup = new Dictionary<Vector2, PuzzlePosition>();
tabsAndBlanks = new Dictionary<Vector2, PuzzleSideData>();
int xIndexMax = fullPuzzleWidth / PieceSize - 1;
int yIndexMax = fullPuzzleHeight / PieceSize - 1;
for (int x = 0; x <= xIndexMax; x += 1)
{
for (int y = 0; y <= yIndexMax; y += 1)
{
var puzzleIndex = new Vector2(x, y);
if (y == 0)
{
if (x == 0)
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.TOP_LEFT);
}
else if (x == xIndexMax)
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.TOP_RIGHT);
}
else
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.TOP);
}
}
else if (y == yIndexMax)
{
if (x == 0)
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.BOT_LEFT);
}
else if (x == xIndexMax)
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.BOT_RIGHT);
}
else puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.BOTTOM);
}
else if (x == 0)
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.LEFT);
}
else if (x == xIndexMax)
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.RIGHT);
}
else
{
puzzlePositionLookup.Add(puzzleIndex, PuzzlePosition.CENTER);
}
var sideData = GeneratePuzzleSideData(puzzleIndex, puzzlePositionLookup[puzzleIndex]);
tabsAndBlanks.Add(puzzleIndex, sideData);
var serializedSideData = "" +
puzzleIndex.x + "," +
puzzleIndex.y + "," +
(int)sideData.Top.mask + "," +
Convert.ToInt32(sideData.Top.isTab) + "," +
Convert.ToInt32(sideData.Top.isEdge) + "," +
(int)sideData.Right.mask + "," +
Convert.ToInt32(sideData.Right.isTab) + "," +
Convert.ToInt32(sideData.Right.isEdge) + "," +
(int)sideData.Bottom.mask + "," +
Convert.ToInt32(sideData.Bottom.isTab) + "," +
Convert.ToInt32(sideData.Bottom.isEdge) + "," +
(int)sideData.Left.mask + "," +
Convert.ToInt32(sideData.Left.isTab) + "," +
Convert.ToInt32(sideData.Left.isEdge);
sideDataSaveQueue.Enqueue(serializedSideData);
}
}
GD.Print("Finishing BuildPuzzleLookups, ", OS.GetTicksMsec());
GD.Print("Saving tabsAndBlanks, ", OS.GetTicksMsec());
var fullSideData = "" + sideDataSaveQueue.Count + '\n';
while (sideDataSaveQueue.Count > 0)
{
fullSideData += sideDataSaveQueue.Dequeue() + '\n';
}
Error error = saveFile.Open(saveFilePath, File.ModeFlags.WriteRead);
GD.Print(error);
saveFile.StoreString(fullSideData);
saveFile.Close();
GD.Print("Finished saving tabsAndBlanks, ", OS.GetTicksMsec());
}
private PuzzleSideData GeneratePuzzleSideData(Vector2 puzzleIndex, PuzzlePosition puzzlePosition)
{
// We start from top to bottom, column by column, left to right, so (0,0), (0,1), (0,2), (1,0), (1,1) etc.
// This is why we can always assume right and bottom sides need to be generated. Left and top will be an edge (so skipped) or matched with neighbour.
GD.Randomize();
var result = new PuzzleSideData();
PuzzleMasks[] availableMasksKeys = maskImages.Keys.ToArray();
PuzzleMasks maskKey;
bool isTab;
// Top side of the puzzle
if (puzzlePosition != PuzzlePosition.TOP_LEFT &&
puzzlePosition != PuzzlePosition.TOP &&
puzzlePosition != PuzzlePosition.TOP_RIGHT)
{
Vector2 neighbourPuzzleIndex = puzzleIndex;
neighbourPuzzleIndex.y -= 1;
maskKey = tabsAndBlanks[neighbourPuzzleIndex].Bottom.mask;
isTab = !tabsAndBlanks[neighbourPuzzleIndex].Bottom.isTab;
result.Top = (false, maskKey, isTab);
}
// Left side of the puzzle
if (puzzlePosition != PuzzlePosition.TOP_LEFT &&
puzzlePosition != PuzzlePosition.LEFT &&
puzzlePosition != PuzzlePosition.BOT_LEFT)
{
Vector2 neighbourPuzzleIndex = puzzleIndex;
neighbourPuzzleIndex.x -= 1;
maskKey = tabsAndBlanks[neighbourPuzzleIndex].Right.mask;
isTab = !tabsAndBlanks[neighbourPuzzleIndex].Right.isTab;
result.Left = (false, maskKey, isTab);
}
// Right side of the puzzle
if (puzzlePosition != PuzzlePosition.TOP_RIGHT &&
puzzlePosition != PuzzlePosition.RIGHT &&
puzzlePosition != PuzzlePosition.BOT_RIGHT)
{
int randomKey = (int)GD.RandRange(0, availableMasksKeys.Length - 0.001);
maskKey = availableMasksKeys[randomKey];
isTab = GD.Randf() <= 0.5f;
result.Right = (false, maskKey, isTab);
}
// Bottom side of the puzzle
if (puzzlePosition != PuzzlePosition.BOT_LEFT &&
puzzlePosition != PuzzlePosition.BOTTOM &&
puzzlePosition != PuzzlePosition.BOT_RIGHT)
{
int randomKey = (int)GD.RandRange(0, availableMasksKeys.Length - 0.001);
maskKey = availableMasksKeys[randomKey];
isTab = GD.Randf() <= 0.5f;
result.Bottom = (false, maskKey, isTab);
}
// Skew random generation of tabs/blanks to greatly favor 2/2 puzzles (2 tabs and 2 blanks)
// Only 4 possible top/left combinations: true/true, true/false, false/true, false/false
// Check each possible combination and set other sides accordingly with a X% chance
float twoTabFactor = 0.8f;
if (result.Left.isTab && result.Top.isTab)
{
if (GD.Randf() < twoTabFactor)
{
result.Bottom.isTab = false;
result.Right.isTab = false;
}
}
else if (result.Left.isTab == true && result.Top.isTab == false)
{
if (GD.Randf() < twoTabFactor)
{
result.Bottom.isTab = false;
result.Right.isTab = true;
}
}
else if (result.Left.isTab == false && result.Top.isTab == true)
{
if (GD.Randf() < twoTabFactor)
{
result.Bottom.isTab = true;
result.Right.isTab = false;
}
}
else if (result.Left.isTab == false && result.Top.isTab == false)
{
if (GD.Randf() < twoTabFactor)
{
result.Bottom.isTab = true;
result.Right.isTab = true;
}
}
return result;
}
private Image getRotatedImage(Image image, int degree)
{
Image result = new Image();
result.Create(image.GetWidth(), image.GetHeight(), false, image.GetFormat());
image.Lock();
result.Lock();
if (degree == 90)
{
for (int x = 0; x < image.GetWidth(); x += 1)
{
for (int y = 0; y < image.GetHeight(); y += 1)
{
Color color = image.GetPixel(x, y);
result.SetPixel(image.GetWidth() - y - 1, x, color);
}
}
}
else if (degree == 180)
{
for (int x = 0; x < image.GetWidth(); x += 1)
{
for (int y = 0; y < image.GetHeight(); y += 1)
{
Color color = image.GetPixel(x, y);
result.SetPixel(image.GetWidth() - x - 1, y, color);
}
}
}
else if (degree == 270)
{
for (int x = 0; x < image.GetWidth(); x += 1)
{
for (int y = 0; y < image.GetHeight(); y += 1)
{
Color color = image.GetPixel(x, y);
result.SetPixel(image.GetWidth() - y - 1, image.GetHeight() - x - 1, color);
}
}
}
image.Unlock();
result.Unlock();
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment