/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "m4/gui/gui_vmng_rectangles.h" #include "m4/gui/gui_vmng.h" #include "m4/core/errors.h" #include "m4/core/imath.h" #include "m4/mem/mem.h" #include "m4/vars.h" namespace M4 { #define LEFT_EDGE 1 #define RIGHT_EDGE 2 RectList *vmng_CreateNewRect(int32 x1, int32 y1, int32 x2, int32 y2) { RectList *newRect; if ((newRect = (RectList *)mem_get_from_stash(_G(memtypeRECT), "+guiRectList")) == nullptr) { error_show(FL, 'OOS!', "vmng_CreateNewRect"); } newRect->x1 = x1; newRect->y1 = y1; newRect->x2 = x2; newRect->y2 = y2; newRect->next = nullptr; newRect->prev = nullptr; return newRect; } void vmng_AddRectToRectList(RectList **theRectList, int32 rectX1, int32 rectY1, int32 rectX2, int32 rectY2) { // First make sure we have a valid rectangle if ((rectX1 > rectX2) || (rectY1 > rectY2)) { return; } // Initialize the dirty rect list RectList *dirtyRectList = vmng_CreateNewRect(rectX1, rectY1, rectX2, rectY2); // Initialize the clean rectList RectList *cleanRectList = nullptr; RectList *endCleanRectList = nullptr; // Use a local var for theRectlist RectList *myRectList = *theRectList; // Loop through all the dirtyRects RectList *dirtyRect = dirtyRectList; while (dirtyRect) { // Remove dirtyRect from the head of the dirtyRectList dirtyRectList = dirtyRectList->next; // Set the intersected flag bool intersected = false; // Loop on through RectList *myRect = myRectList; while (myRect) { // If the two rectangles intersect if ((dirtyRect->x1 <= myRect->x2) && (dirtyRect->x2 >= myRect->x1) && (dirtyRect->y1 <= myRect->y2) && (dirtyRect->y2 >= myRect->y1)) { // Set the intersected flag intersected = true; // If dirtyRect is not completely contained within myRect if ((dirtyRect->x1 < myRect->x1) || (dirtyRect->y1 < myRect->y1) || (dirtyRect->x2 > myRect->x2) || (dirtyRect->y2 > myRect->y2)) { // First remove it from the list if (myRect->prev) { myRect->prev->next = myRect->next; } else { myRectList = myRect->next; } if (myRect->next) { myRect->next->prev = myRect->prev; } RectList *newRect; // So now there is an intersection. // If myRect sticks out above dirtyRect, chop it off and put it in the main rect list, to be recheck by other dirty rects if (myRect->y1 < dirtyRect->y1) { newRect = vmng_CreateNewRect(myRect->x1, myRect->y1, myRect->x2, dirtyRect->y1 - 1); newRect->prev = nullptr; newRect->next = myRectList; if (myRectList) { myRectList->prev = newRect; } myRectList = newRect; // And set the top of myRect to be the same as dirtyRect myRect->y1 = dirtyRect->y1; } else if (dirtyRect->y1 < myRect->y1) { // else if dirtyRect sticks out above chop it off and put it on the dirty list newRect = vmng_CreateNewRect(dirtyRect->x1, dirtyRect->y1, dirtyRect->x2, myRect->y1 - 1); newRect->next = dirtyRectList; dirtyRectList = newRect; // and set the top of dirtyRect to be the same as myRect dirtyRect->y1 = myRect->y1; } // If myRect sticks out below dirtyRect, chop it off and put it in the main rect list, to be recheck by other dirty rects if (myRect->y2 > dirtyRect->y2) { newRect = vmng_CreateNewRect(myRect->x1, dirtyRect->y2 + 1, myRect->x2, myRect->y2); newRect->prev = nullptr; newRect->next = myRectList; if (myRectList) { myRectList->prev = newRect; } myRectList = newRect; // and set the bottom of myRect to be the same as dirtyRect myRect->y2 = dirtyRect->y2; } else if (dirtyRect->y2 > myRect->y2) { //else if dirtyRect sticks out below myRect... newRect = vmng_CreateNewRect(dirtyRect->x1, myRect->y2 + 1, dirtyRect->x2, dirtyRect->y2); newRect->next = dirtyRectList; dirtyRectList = newRect; // and set the bottom of dirtyRect to be the same as myRect dirtyRect->y2 = myRect->y2; } // Now we've got overlapping rectangles which are the same height. create one max width one // If the dirtyRect sticks out on either side, the resulting rect is still dirty, otherwise clean if ((dirtyRect->x1 < myRect->x1) || (dirtyRect->x2 > myRect->x2)) { // Use dirtyRect to become the max width rect dirtyRect->x1 = imath_min(dirtyRect->x1, myRect->x1); dirtyRect->x2 = imath_max(dirtyRect->x2, myRect->x2); dirtyRect->next = dirtyRectList; dirtyRectList = dirtyRect; // And turf myRect mem_free_to_stash(myRect, _G(memtypeRECT)); } else { // Else we can put what's left of myRect onto the clean list and turf dirtyRect // Note: it is impossible to split the dirtyRect list vertically, they always stretch horizontally, // therefore if this dirty rect does not stick out the sides, what's left of myRect is clean myRect->prev = nullptr; myRect->next = cleanRectList; if (cleanRectList) { cleanRectList->prev = myRect; } else { endCleanRectList = myRect; } cleanRectList = myRect; mem_free_to_stash((void *)dirtyRect, _G(memtypeRECT)); } // Exit the loop myRect = nullptr; } else { // else through away dirtyRect, and get the next dirtyRect mem_free_to_stash(dirtyRect, _G(memtypeRECT)); myRect = nullptr; } } else { // else get the next rect myRect = myRect->next; } } //if we didn't intersect, put the dirtyRect on the clean list if (!intersected) { dirtyRect->prev = nullptr; dirtyRect->next = cleanRectList; if (cleanRectList) { cleanRectList->prev = dirtyRect; } else { endCleanRectList = dirtyRect; } cleanRectList = dirtyRect; } // get the next dirty rect dirtyRect = dirtyRectList; } // now, just add the clean list onto the dirty list if (cleanRectList) { //now add the entire cleanRectList to the front of myRectList endCleanRectList->next = myRectList; if (myRectList) { myRectList->prev = endCleanRectList; } myRectList = cleanRectList; } // Return the rect list *theRectList = myRectList; } RectList *vmng_DuplicateRectList(RectList *myRectList) { RectList *newRectList = nullptr; RectList *prevRect = nullptr; RectList *myRect = myRectList; while (myRect) { // Duplicate myRect and stick it on the newRectList RectList *tempRect = (RectList *)mem_get_from_stash(_G(memtypeRECT), "+guiRectList"); if (tempRect == nullptr) { error_show(FL, 'OOS!', "vmng_DuplicateRectList()"); } tempRect->x1 = myRect->x1; tempRect->y1 = myRect->y1; tempRect->x2 = myRect->x2; tempRect->y2 = myRect->y2; tempRect->prev = prevRect; tempRect->next = nullptr; if (prevRect) { prevRect->next = tempRect; } else { newRectList = tempRect; } prevRect = tempRect; //get the next rectangle myRect = myRect->next; } return newRectList; } bool vmng_RectIntersectsRectList(RectList *myRectList, int32 x1, int32 y1, int32 x2, int32 y2) { // Parameter verification if ((!myRectList) || (x1 > x2) || (y1 > y2)) { return false; } // Loop through the list, and break as soon as there is an intersection RectList *myRect = myRectList; while (myRect) { // Calculate the intersection const int32 intrX1 = imath_max(myRect->x1, x1); const int32 intrY1 = imath_max(myRect->y1, y1); const int32 intrX2 = imath_min(myRect->x2, x2); const int32 intrY2 = imath_min(myRect->y2, y2); // If we intersected, return true if ((intrX1 <= intrX2) && (intrY1 <= intrY2)) { return true; } // else get the next rect in the list myRect = myRect->next; } // We made it through the entire list with no intersections - return false return false; } bool vmng_ClipRectList(RectList **myRectList, int32 clipX1, int32 clipY1, int32 clipX2, int32 clipY2) { // Loop through myRect list RectList *myRect = *myRectList; while (myRect) { // Set the next rect RectList *nextRect = myRect->next; // Clip myRect const int32 x1 = imath_max(myRect->x1, clipX1); const int32 y1 = imath_max(myRect->y1, clipY1); const int32 x2 = imath_min(myRect->x2, clipX2); const int32 y2 = imath_min(myRect->y2, clipY2); // If we have a valid rectangle if ((x1 <= x2) && (y1 <= y2)) { // Clip the rectangle myRect->x1 = x1; myRect->y1 = y1; myRect->x2 = x2; myRect->y2 = y2; } else { // Else remove it from the rectList and turf it if (myRect->prev) { myRect->prev->next = myRect->next; } else { *myRectList = myRect->next; } if (myRect->next) { myRect->next->prev = myRect->prev; } mem_free_to_stash((void *)myRect, _G(memtypeRECT)); } // Check the next rect myRect = nextRect; } return true; } bool vmng_RectListValid(RectList *myRectList) { RectList *myRect = myRectList; while (myRect) { RectList *tempRectList = myRect->next; if (vmng_RectIntersectsRectList(tempRectList, myRect->x1, myRect->y1, myRect->x2, myRect->y2)) { return false; } myRect = myRect->next; } return true; } void vmng_DisposeRectList(RectList **rectList) { // Loop through the rect list RectList *myRect = *rectList; while (myRect) { // Remove myRect from the head of the list *rectList = myRect->next; // Dispose of myRect; mem_free_to_stash((void *)myRect, _G(memtypeRECT)); // Get the next rectangle myRect = *rectList; } } void vmng_RemoveRectFromRectList(RectList **scrnRectList, int32 x1, int32 y1, int32 x2, int32 y2) { RectList *tempRect; RectList *rectList = *scrnRectList; // Go through the rectList list breaking down any rects which intersect the given coords RectList *unsortedRectList = nullptr; RectList *myRect = rectList; while (myRect) { // Set the nextRect pointer RectList *nextRect = myRect->next; // Check for an intersection const int32 tempX1 = imath_max(x1, myRect->x1); const int32 tempY1 = imath_max(y1, myRect->y1); const int32 tempX2 = imath_min(x2, myRect->x2); const int32 tempY2 = imath_min(y2, myRect->y2); // If we have an intersection if ((tempX1 <= tempX2) && (tempY1 <= tempY2)) { // Break myRect apart into any pieces not covered by x1, y1, x2, y2 // Top edge if (myRect->y1 < y1) { // Create a new rect of just the part that extends beyond the top of myRect tempRect = (RectList *)mem_get_from_stash(_G(memtypeRECT), "+guiRectangle"); if (tempRect == nullptr) { error_show(FL, 'OOS!', "vmng_AddRectToRectList"); } tempRect->x1 = myRect->x1; tempRect->y1 = myRect->y1; tempRect->x2 = myRect->x2; tempRect->y2 = y1 - 1; // Add tempRect to the unsortedRectList tempRect->next = unsortedRectList; unsortedRectList = tempRect; // Update myRect myRect->y1 = y1; } // Bottom edge if (myRect->y2 > y2) { // Create a new rect of just the part that extends beyond the top of myRect tempRect = (RectList *)mem_get_from_stash(_G(memtypeRECT), "+guiRectangle"); if (tempRect == nullptr) { error_show(FL, 'OOS!', "vmng_AddRectToRectList"); } tempRect->x1 = myRect->x1; tempRect->y1 = y2 + 1; tempRect->x2 = myRect->x2; tempRect->y2 = myRect->y2; // Add tempRect to the unsortedRectList tempRect->next = unsortedRectList; unsortedRectList = tempRect; // Update myRect myRect->y2 = y2; } // Left edge if (myRect->x1 < x1) { // Create a new rect of just the part that extends beyond the top of myRect tempRect = (RectList *)mem_get_from_stash(_G(memtypeRECT), "+guiRectangle"); if (tempRect == nullptr) { error_show(FL, 'OOS!', "vmng_AddRectToRectList"); } tempRect->x1 = myRect->x1; tempRect->y1 = myRect->y1; tempRect->x2 = x1 - 1; tempRect->y2 = myRect->y2; // Add tempRect to the unsortedRectList tempRect->next = unsortedRectList; unsortedRectList = tempRect; } // Right edge if (myRect->x2 > x2) { // Create a new rect of just the part that extends beyond the top of myRect tempRect = (RectList *)mem_get_from_stash(_G(memtypeRECT), "+guiRectangle"); if (tempRect == nullptr) { error_show(FL, 'OOS!', "vmng_AddRectToRectList"); } tempRect->x1 = x2 + 1; tempRect->y1 = myRect->y1; tempRect->x2 = myRect->x2; tempRect->y2 = myRect->y2; // Add tempRect to the unsortedRectList tempRect->next = unsortedRectList; unsortedRectList = tempRect; } // Remove myRect from the list and turf it if (myRect->next) { myRect->next->prev = myRect->prev; } if (myRect->prev) { myRect->prev->next = myRect->next; } else { rectList = myRect->next; } mem_free_to_stash((void *)myRect, _G(memtypeRECT)); } // Get the next rect myRect = nextRect; } // Now go through the unsorted list and insert them into the main list tempRect = unsortedRectList; while (tempRect) { unsortedRectList = unsortedRectList->next; // For each unsorted rect, loop through the rect list until its place is found bool finished = false; RectList *prevRect = nullptr; myRect = rectList; while (myRect && (!finished)) { // If it goes before myRect if (tempRect->y2 <= myRect->y2) { finished = true; } else { prevRect = myRect; myRect = myRect->next; } } // tempRect belongs after prevRect if (prevRect) { tempRect->prev = prevRect; tempRect->next = prevRect->next; if (prevRect->next) { prevRect->next->prev = tempRect; } prevRect->next = tempRect; } else { // else it belongs at the front of rectList tempRect->prev = nullptr; tempRect->next = rectList; if (rectList) { rectList->prev = tempRect; } rectList = tempRect; } // Get the next unsorted rect tempRect = unsortedRectList; } // Set the screen rect list to the resulting rect list *scrnRectList = rectList; } } // End of namespace M4