123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- /* GCSx
- ** IMPORTIMG.CPP
- **
- ** Import image (creating tileset/fontset) dialog
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** 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 2 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, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- // Import image dialog
- ImportImgDialog* ImportImgDialog::dialogFontSet = NULL;
- ImportImgDialog* ImportImgDialog::dialogTileSet = NULL;
- ImportImgDialog* ImportImgDialog::createFontSet() { start_func
- if (!dialogFontSet) {
- dialogFontSet = new ImportImgDialog(1);
- }
- return dialogFontSet;
- }
- ImportImgDialog* ImportImgDialog::createTileSet() { start_func
- if (!dialogTileSet) {
- dialogTileSet = new ImportImgDialog(0);
- }
- return dialogTileSet;
- }
- void ImportImgDialog::destroy() { start_func
- if (dialogFontSet) {
- delete dialogFontSet;
- dialogFontSet = NULL;
- }
- if (dialogTileSet) {
- delete dialogTileSet;
- dialogTileSet = NULL;
- }
- }
- ImportImgDialog::ImportImgDialog(int font) : Dialog(font ? "Import Image as Font" : "Import Image as Image/Tile Set") { start_func
- initialized = 0;
- isFont = font;
- image = NULL;
- height = width = TileSet::TILE_DEFAULT_SIZE;
- left = top = horiz = vert = 0;
- skipBlank = skipDupe = importScene = 0;
- }
- ImportImgDialog::~ImportImgDialog() { start_func
- if (image) SDL_FreeSurface(image);
- }
- void ImportImgDialog::run(WorldEdit* world, Window* srcWin) { start_func
- if (!initialized) {
- Widget* w = NULL;
- Widget* tr = new WStatic(ID_LABEL, "Transparency:");
- w = new WStatic(ID_LABEL, "\tImage File:");
- w->addTo(this);
- w = new WTextBox(ID_FILE, &filename, 0);
- w->disable();
- w->addTo(this);
-
- w = new WButton(ID_FILECHOOSE, "\tChoose...", BUTTON_NOTHING);
- w->addTo(this);
- arrangeRow(1, tr->getWidth(), 1);
- w = new WStatic(ID_LABEL, "Preview:");
- w->addTo(this);
-
- w = new WSplitPreview(ID_PREVIEW, &bkColor, &image, 200, 100);
- w->addTo(this);
- arrangeRow(1, tr->getWidth(), 1);
-
- tr->addTo(this);
-
- w = new WShowColor(ID_TRANSPARENCY, 30, 30, &bkColor);
- w->setToolTip("Click on preview to select transparency color");
- w->addTo(this);
- w = new WCheckBox(ID_SKIPBLANK, "Skip \tblank tiles", &skipBlank, 1);
- w->setToolTip("Don't import tiles that are entirely transparent or black");
- w->addTo(this);
- arrangeRow(1, tr->getWidth(), 1);
- w = new WStatic(ID_LABEL, "\tTile size:");
- w->addTo(this);
- w = new WNumberBox(ID_WIDTH, &width, MIN_TILESIZE, MAX_TILESIZE);
- w->addTo(this);
- w = new WStatic(ID_LABEL, "wide;");
- w->addTo(this);
- w = new WNumberBox(ID_HEIGHT, &height, MIN_TILESIZE, MAX_TILESIZE);
- w->addTo(this);
- w = new WStatic(ID_LABEL, "high");
- w->addTo(this);
- arrangeRow(1, tr->getWidth(), 1);
- w = new WStatic(ID_LABEL, "\tPadding:");
- w->addTo(this);
- w = new WNumberBox(ID_LEFT, &left, 0, 999);
- w->setToolTip("Skip this many pixels on the left of the image");
- w->addTo(this);
- w = new WStatic(ID_LABEL, "left;");
- w->addTo(this);
- w = new WNumberBox(ID_TOP, &top, 0, 999);
- w->setToolTip("Skip this many pixels on the top of the image");
- w->addTo(this);
- w = new WStatic(ID_LABEL, "top");
- w->addTo(this);
- arrangeRow(1, tr->getWidth(), 1);
- w = new WStatic(ID_LABEL, "\tSpacing:");
- w->addTo(this);
- w = new WNumberBox(ID_HORIZ, &horiz, 0, 99);
- w->setToolTip("Skip this many pixels between tiles");
- w->addTo(this);
- w = new WStatic(ID_LABEL, "horizontal;");
- w->addTo(this);
- w = new WNumberBox(ID_VERT, &vert, 0, 99);
- w->setToolTip("Skip this many pixels between tiles");
- w->addTo(this);
- w = new WStatic(ID_LABEL, "vertical");
- w->addTo(this);
- arrangeRow(1, tr->getWidth(), 1);
- w = new WStatic(ID_LABEL, blankString);
- w->addTo(this);
- w = new WCheckBox(ID_SKIPDUPE, "Skip \tduplicate tiles", &skipDupe, 1);
- w->setToolTip("Don't import tiles that are exact duplicates");
- w->addTo(this);
- arrangeRow(1, tr->getWidth());
- if (!isFont) {
- w = new WStatic(ID_LABEL, blankString);
- w->addTo(this);
- w = new WCheckBox(ID_IMPORTSCENE, "C\treate tiled scene from image", &importScene, 1);
- w->setToolTip("Creates a new one-layer scene using the new tileset, based on the image");
- w->addTo(this);
- arrangeRow(1, tr->getWidth());
- }
- w = new WButton(ID_OK, messageBoxOK, BUTTON_OK);
- w->addTo(this);
- w = new WButton(ID_CANCEL, messageBoxCancel, BUTTON_CANCEL);
- w->addTo(this);
- makePretty();
- initialized = 1;
- }
-
- // Only reset filename, image, and bk color from last import
- bkColor.max = bkColor.maxAlpha = (1 << 8) - 1;
- bkColor.R = bkColor.G = bkColor.B = bkColor.A = 0;
- bkColor.RGBtoHSL();
-
- filename = blankString;
- if (image) SDL_FreeSurface(image);
- image = NULL;
- // Hit OK?
- if (runModal() == ID_OK) {
- if (image) {
- // Import now!
- TileSetEdit* myTileSet = new TileSetEdit(NULL, isFont, 0);
- SceneEdit* myScene = NULL;
- LayerEdit* myLayer = NULL;
- if (importScene) {
- myScene = new SceneEdit(NULL, 0);
- myLayer = new LayerEdit(NULL, myScene, 0);
- // Note that layer isn't actually added to scene yet
- }
- if (doImport(myTileSet, myScene, myLayer, world)) {
- myTileSet->setInfo(world, world->unusedTileSetId());
- world->importTileSet(myTileSet, 1, srcWin);
- if (importScene) {
- myScene->setInfo(world, world->unusedSceneId());
- myLayer->setInfo(world, world->unusedLayerId());
- // @TODO: can't be before setinfo, as needs world to work
- // but here, it triggers undo which is wrong
- myLayer->setTileset(myTileSet->getId());
- world->importScene(myScene, 1, srcWin);
- }
- }
- else {
- delete myLayer;
- delete myScene;
- delete myTileSet;
- }
- }
- else {
- guiErrorBox("No file was selected to import", errorTitleImport);
- }
- }
- // Free up some memory
- if (image) SDL_FreeSurface(image);
- image = NULL;
- }
- int ImportImgDialog::doImport(TileSetEdit* ts, SceneEdit* sc, LayerEdit* sl, WorldEdit* world) { start_func
- assert(image);
-
- // Work areas
- SDL_Surface* workArea = createSurface32(width, height);
- int pitch = workArea->pitch / 4;
- pitch -= width;
- Uint32* layerWork = NULL;
-
- // Size of source
- int sW = image->w;
- int sH = image->h;
-
- // Determine number of images we'd like (don't import partial tiles)
- int countW = (sW - left + horiz) / (width + horiz);
- int countH = (sH - top + vert) / (height + vert);
- if (countW < 1) {
- guiErrorBox("Image is not wide enough to import requested tile width", errorTitleImport);
- return 0;
- }
- if (countH < 1) {
- guiErrorBox("Image is not tall enough to import requested tile height", errorTitleImport);
- return 0;
- }
-
- int surfaceW, surfaceH, perLine; // We don't need these, but the next function needs references
- int numTiles = countW * countH;
- if (numTiles > MAX_TILES) numTiles = MAX_TILES;
- numTiles = TileSetEdit::calculateTileSurfaceSizing(width, height, numTiles, surfaceW, surfaceH, perLine);
-
- // If we're doing dupe checking, count total number of dupe checks
- long long dupeChecks = 0;
- long long totalProgress = 0;
- if (skipDupe) dupeChecks = ((long long)numTiles - 1) * (long long)numTiles / 2;
-
- // Initialize progress meter
- ProgressMeter* progress = new ProgressMeter(numTiles + dupeChecks);
-
- // Prep tileset/layer
- ts->markLock(); // @TODO: throw_File
- ts->setSize(width, height, numTiles, 0);
- if (sl) {
- assert(sc);
- sl->setType(Layer::LAYER_TILE);
- sl->setSize(countW, countH);
- sl->setExtended(0);
- sl->setEffects(0);
- layerWork = new Uint32[countW * countH];
- sc->markLock(); // @TODO: throw_File
- }
-
- // Start at...
- int sX = left;
- int sY = top;
-
- // Target tile
- int tTile = 1;
-
- // What tile we should be on if no dupes; also used for layer
- long ghostTile = 1;
-
- // Transparency
- Uint32 trans = mapColor32(bkColor.R, bkColor.G, bkColor.B, bkColor.A);
- Uint32 blank = mapColor32(0, 0, 0, 0);
- int anyBlank = 0; // Any transparent areas?
- // For dupe checking
- int cPitch = ts->viewTilePitch() / 4;
- cPitch -= width;
-
- while (tTile <= numTiles) {
- // Pull one tile
- blit(sX, sY, image, 0, 0, workArea, width, height);
- // Update transparency and check for all black/blank
- Uint32* point = (Uint32*)workArea->pixels;
- int allBlack = skipBlank ? 1 : 0;
- int allBlank = skipBlank ? 1 : 0;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- // Transparency
- if (*point == trans) *point = blank;
-
- // All alpha 0 or black?
- int R, G, B, A;
- splitColor32(*point, R, G, B, A);
- if ((R != 0) || (G != 0) || (B != 0)) allBlack = 0;
- if (A != 0) allBlank = 0;
- if (A == 0) anyBlank = 1;
-
- ++point;
- }
- point += pitch;
- }
-
- // Save tile?
- if ((!allBlank) && (!allBlack)) {
- int duplicated = 0;
-
- // Compare to all previously imported tiles to find duplicates?
- if ((skipDupe) && (tTile > 1)) {
- // Scan each existing tile
- int dupe;
- for (dupe = 1; dupe < tTile; ++dupe) {
- // Two points to compare
- Uint32* point = (Uint32*)workArea->pixels;
- const Uint32* check = (Uint32*)ts->viewTileData(dupe);
- int same = 1;
-
- // Scan entire tile
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- // Stop as soon as something is different
- if (*point != *check) {
- same = 0;
- break;
- }
- ++point;
- ++check;
- }
- if (!same) break;
- point += pitch;
- check += cPitch;
- }
-
- progress->updateProgress(++totalProgress);
- if (same) {
- duplicated = dupe;
- // (so value of dupe is consistently the same
- // whether we exit normally or shortcut here)
- ++dupe;
- break;
- }
- }
-
- // Update meter for skipped checks
- if (dupe < ghostTile)
- totalProgress += ghostTile - dupe;
- }
- else if (skipDupe) {
- // Update meter for skipped dupe checks
- totalProgress += ghostTile - 1;
- }
-
- if (!duplicated) {
- if (sl) layerWork[ghostTile - 1] = tTile | Layer::LAYER_TILE_DEFAULT;
- ts->saveTile(tTile++, workArea, NULL);
- }
- else if (sl) layerWork[ghostTile - 1] = duplicated | Layer::LAYER_TILE_DEFAULT;
- }
- else {
- if (sl) layerWork[ghostTile - 1] = Layer::LAYER_TILE_DEFAULT;
- // Update meter for skipped dupe checks
- if (skipDupe) totalProgress += ghostTile - 1;
- }
-
- progress->updateProgress(++totalProgress);
- ++ghostTile;
- // Next tile
- sX += width + horiz;
- if (sX + width > sW) {
- sX = left;
- sY += height + vert;
- if (sY + height > sH) break;
- }
- }
-
- // Adjust size if less tiles (error if 0)
- --tTile;
- --ghostTile;
- // (don't waste time resizing if we're just gonna delete it)
- if ((tTile < numTiles) && (tTile >= 1)) ts->setSize(width, height, tTile, 0);
- progress->doneProgress();
-
- if (tTile < 1) {
- guiErrorBox("No non-blank tiles were found to import", errorTitleImport);
- ts->markUnlock();
- if (sl) sc->markUnlock();
- delete[] layerWork;
- return 0;
- }
-
- if (ghostTile < countW * countH) {
- guiErrorBox("Too many tiles- not all possible tiles were imported", errorTitleImport);
- if (sl)
- memSet32(layerWork + ghostTile, Layer::LAYER_TILE_DEFAULT, countW * countH - ghostTile);
- }
-
- // Name
- vector<string> splitName;
- splitFilename(filename.c_str(), splitName);
- string findName(splitName[splitName.size() - 1]);
- string lFindName = findName;
- toLower(lFindName);
- int pos = 1;
- while (world->findTileSet(lFindName)) {
- findName = splitName[splitName.size() - 1];
- findName += " ";
- findName += intToStr(++pos);
- lFindName = findName;
- toLower(lFindName);
- }
- ts->setName(findName);
-
- // Other settings
- ts->setTilesPerLine(tTile == ghostTile ? countW : 0);
- ts->setDefaultTransparent(anyBlank);
- ts->markUnlock();
- // Layer/scene
- if (sl) {
- // Layer load, name
- Rect rect;
- rect.x = rect.y = 0;
- rect.w = countW;
- rect.h = countH;
- sl->saveLayer(layerWork, NULL, NULL, countW, rect, NULL);
- sl->setName(splitName[splitName.size() - 1]);
-
- // Scene name
- findName = splitName[splitName.size() - 1];
- lFindName = findName;
- toLower(lFindName);
- pos = 1;
- while (world->findScene(lFindName)) {
- findName = splitName[splitName.size() - 1];
- findName += " ";
- findName += intToStr(++pos);
- lFindName = findName;
- toLower(lFindName);
- }
- sc->setName(findName);
- sc->markUnlock();
-
- sc->insertLayer(sl, 0);
- }
- delete[] layerWork;
- return 1;
- }
- void ImportImgDialog::childModified(Window* modified) { start_func
- Dialog::childModified(modified);
-
- WShowColor* wColor = NULL;
- WSplitPreview* wImg = NULL;
- Widget* widget = dynamic_cast<Widget*>(modified);
- if (widget) {
- switch (widget->getId()) {
- case ID_WIDTH:
- case ID_HEIGHT:
- case ID_LEFT:
- case ID_TOP:
- case ID_HORIZ:
- case ID_VERT:
- widget->apply();
- wImg = dynamic_cast<WSplitPreview*>(findWidget(ID_PREVIEW));
- wImg->changeStats(width, height, top, left, horiz, vert);
- break;
- case ID_PREVIEW:
- widget->apply();
- wColor = dynamic_cast<WShowColor*>(findWidget(ID_TRANSPARENCY));
- wColor->load();
- break;
- case ID_TRANSPARENCY:
- widget->apply();
- wImg = dynamic_cast<WSplitPreview*>(findWidget(ID_PREVIEW));
- wImg->load();
- break;
- }
- }
- }
- Dialog::ButtonAction ImportImgDialog::verifyEntry(int buttonId, Dialog::ButtonAction buttonType) { start_func
- if (buttonId == ID_FILECHOOSE) {
- try {
- if (fileOpen(FILETYPE_IMAGE, 0, filename)) {
- WTextBox* fn = dynamic_cast<WTextBox*>(findWidget(ID_FILE));
- fn->load();
- if (image) SDL_FreeSurface(image);
- image = NULL;
- image = IMG_Load(filename.c_str());
- if (image == NULL) {
- throw VideoException("Error loading image: %s", SDL_GetError());
- }
- else {
- SDL_SetAlpha(image, 0, 255);
- WSplitPreview* wImg = dynamic_cast<WSplitPreview*>(findWidget(ID_PREVIEW));
- wImg->load();
- }
- }
- }
- catch (FileException& e) {
- guiErrorBox(string(e.details), errorTitleFile);
- }
- }
-
- return Dialog::verifyEntry(buttonId, buttonType);
- }
- WSplitPreview::WSplitPreview(int pId, color* pSetting, SDL_Surface* const* src, int myWidth, int myHeight) : Widget(pId, blankString, pSetting) { start_func
- assert(src);
- source = src;
- split = NULL;
- dragMode = WSPLITPREVIEW_DRAG_NONE;
- topPad = leftPad = 0;
- horizSplit = vertSplit = 0;
- tileWidth = tileHeight = TileSet::TILE_DEFAULT_SIZE;
- changeSize(myWidth, myHeight);
- }
- WSplitPreview::~WSplitPreview() { start_func
- if (split) SDL_FreeSurface(split);
- }
- void WSplitPreview::changeSize(int newWidth, int newHeight) { start_func
- displayW = newWidth;
- displayH = newHeight;
- if ((newWidth <= 0) || (newHeight <= 0)) {
- resize(0, 0);
- }
- else {
- resize(newWidth + PREVIEW_BORDER_SIZE * 2, newHeight + PREVIEW_BORDER_SIZE * 2);
- }
- }
- void WSplitPreview::changeStats(int width, int height, int top, int left, int horiz, int vert) { start_func
- topPad = top;
- leftPad = left;
- horizSplit = horiz;
- vertSplit = vert;
- tileWidth = width;
- tileHeight = height;
- load();
- }
- void WSplitPreview::setBk(int x, int y) { start_func
- if (split) {
- splitColor32(getPixel(x, y, split), bkColor.R, bkColor.G, bkColor.B, bkColor.A);
- bkColor.RGBtoHSL();
- parent->childModified(this);
- }
- }
- int WSplitPreview::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
- switch (event->type) {
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONDBL:
- if (event->button.button == SDL_BUTTON_LEFT) {
- dragMode = WSPLITPREVIEW_DRAG_NONE;
-
- if ((event->button.y >= PREVIEW_BORDER_SIZE) && (event->button.y < height - PREVIEW_BORDER_SIZE)) {
- setBk(event->button.x - PREVIEW_BORDER_SIZE, event->button.y - PREVIEW_BORDER_SIZE);
- dragMode = WSPLITPREVIEW_DRAG_BK;
- }
- return 1;
- }
- break;
- case SDL_MOUSEBUTTONUP:
- if (event->button.button == SDL_BUTTON_LEFT) {
- dragMode = WSPLITPREVIEW_DRAG_NONE;
- return 1;
- }
- break;
- case SDL_MOUSEMOTION:
- if (event->motion.state & SDL_BUTTON_LMASK) {
- if (dragMode == WSPLITPREVIEW_DRAG_BK) {
- setBk((Sint16)event->motion.x - PREVIEW_BORDER_SIZE, (Sint16)event->motion.y - PREVIEW_BORDER_SIZE);
- }
- return 1;
- }
- else if ((event->motion.y >= PREVIEW_BORDER_SIZE) &&
- (event->motion.y < height - PREVIEW_BORDER_SIZE) &&
- (event->motion.x >= PREVIEW_BORDER_SIZE) &&
- (event->motion.x < width - PREVIEW_BORDER_SIZE)) {
- selectMouse(MOUSE_PIXEL);
- }
- else {
- selectMouse(MOUSE_NORMAL);
- }
- break;
- }
- return 0;
- }
- void WSplitPreview::load() { start_func
- bkColor = *((color*)setting);
- if ((*source == NULL) || (displayW <= 0) || (displayH <= 0)) {
- if (split) SDL_FreeSurface(split);
- split = NULL;
- }
- else {
- SDL_Surface* src = *source;
- if (split) SDL_FreeSurface(split);
- split = NULL;
- split = createSurface32(displayW, displayH);
- // Start at...
- int sX = leftPad;
- int sY = topPad;
-
- // Target placement
- int tX = 0;
- int tY = 0;
-
- do {
- // Pull one tile
- blit(sX, sY, src, tX, tY, split, tileWidth, tileHeight);
-
- // Next tile
- tX += tileWidth + TILE_SPACING;
- if (tX >= displayW) {
- tX = 0;
- tY += tileHeight + TILE_SPACING;
- if (tY >= displayH) break;
- sX = leftPad;
- sY += tileHeight + vertSplit;
- }
- else {
- sX += tileWidth + horizSplit;
- }
- } while (1);
- }
- setDirty();
- }
- void WSplitPreview::apply() { start_func
- *((color*)setting) = bkColor;
- }
- void WSplitPreview::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // If dirty, redraw all
- if (dirty) {
- getRect(toDisplay);
- toDisplay.x += xOffset;
- toDisplay.y += yOffset;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
- xOffset += x;
- yOffset += y;
- // We specifically use black, except for "white" copy
- SDL_FillRect(destSurface, &toDisplay, SDL_MapRGB(destSurface->format, 0, 0, 0));
- // @TODO: Corners of inverted box aren't grey
- if (PREVIEW_BORDER_SIZE) drawGuiBoxInvert(xOffset, yOffset, width, height, PREVIEW_BORDER_SIZE, destSurface);
- if (split) blit(0, 0, split, xOffset + PREVIEW_BORDER_SIZE, yOffset + PREVIEW_BORDER_SIZE, destSurface);
- }
- }
- }
|