/* ** tileatlas.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2013 Jonas Kulla ** ** mkxp 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 2 of the License, or ** (at your option) any later version. ** ** mkxp 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 mkxp. If not, see . */ #include "tileatlas.h" namespace TileAtlas { /* A Column represents a Rect * with undefined width */ struct Column { int x, y, h; Column(int x, int y, int h) : x(x), y(y), h(h) {} }; typedef QList ColumnList; /* Autotile area width */ static const int atAreaW = 96*4; /* Autotile area height */ static const int atAreaH = 128*7; /* Autotile area */ static const int atArea = atAreaW * atAreaH; static const int tilesetW = 256; static const int tsLaneW = tilesetW / 2; static int freeArea(int width, int height) { return width * height - atArea; } Vec2i minSize(int tilesetH, int maxAtlasSize) { int width = atAreaW; int height = atAreaH; const int tsArea = tilesetW * tilesetH; /* Expand vertically */ while (freeArea(width, height) < tsArea && height < maxAtlasSize) height += 32; if (freeArea(width, height) >= tsArea && height <= maxAtlasSize) return Vec2i(width, height); /* Expand horizontally */ while (freeArea(width, height) < tsArea && width < maxAtlasSize) width += tsLaneW; if (freeArea(width, height) >= tsArea && width <= maxAtlasSize) return Vec2i(width, height); return Vec2i(-1, -1); } static ColumnList calcSrcCols(int tilesetH) { ColumnList cols; cols << Column(0, 0, tilesetH); cols << Column(tsLaneW, 0, tilesetH); return cols; } static ColumnList calcDstCols(int atlasW, int atlasH) { ColumnList cols; /* Columns below the autotile area */ const int underAt = atlasH - atAreaH; for (int i = 0; i < 3; ++i) cols << Column(i*tsLaneW, atAreaH, underAt); if (atlasW <= atAreaW) return cols; const int remCols = (atlasW - atAreaW) / tsLaneW; for (int i = 0; i < remCols; ++i) cols << Column(i*tsLaneW + atAreaW, 0, atlasH); return cols; } static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) { BlitList blits; while (!srcCols.empty()) { Column srcCol = srcCols.takeFirst(); Q_ASSERT(srcCol.h > 0); while (!dstCols.empty() && srcCol.h > 0) { Column dstCol = dstCols.takeFirst(); if (srcCol.h > dstCol.h) { /* srcCol doesn't fully fit into dstCol */ blits << Blit(srcCol.x, srcCol.y, dstCol.x, dstCol.y, dstCol.h); srcCol.y += dstCol.h; srcCol.h -= dstCol.h; } else if (srcCol.h < dstCol.h) { /* srcCol fits into dstCol with space remaining */ blits << Blit(srcCol.x, srcCol.y, dstCol.x, dstCol.y, srcCol.h); dstCol.y += srcCol.h; dstCol.h -= srcCol.h; dstCols.prepend(dstCol); srcCol.h = 0; } else { /* srcCol fits perfectly into dstCol */ blits << Blit(srcCol.x, srcCol.y, dstCol.x, dstCol.y, dstCol.h); } } } return blits; } BlitList calcBlits(int tilesetH, const Vec2i &atlasSize) { ColumnList srcCols = calcSrcCols(tilesetH); ColumnList dstCols = calcDstCols(atlasSize.x, atlasSize.y); return calcBlitsInt(srcCols, dstCols); } Vec2i tileToAtlasCoor(int tileX, int tileY, int tilesetH, int atlasH) { int laneX = tileX*32; int laneY = tileY*32; int longlaneH = atlasH; int shortlaneH = longlaneH - atAreaH; int longlaneOffset = shortlaneH * 3; int laneIdx = 0; int atlasY = 0; /* Check if we're inside the 2nd lane */ if (laneX >= tsLaneW) { laneY += tilesetH; laneX -= tsLaneW; } if (laneY < longlaneOffset) { /* Below autotile area */ laneIdx = laneY / shortlaneH; atlasY = laneY % shortlaneH + atAreaH; } else { /* Right of autotile area */ int _y = laneY - longlaneOffset; laneIdx = 3 + _y / longlaneH; atlasY = _y % longlaneH; } int atlasX = laneIdx * tsLaneW + laneX; return Vec2i(atlasX, atlasY); } }