From fcb804a94f94ee93dc193f67d690bee363fdd926 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Mon, 16 May 2016 03:05:51 +0300 Subject: [PATCH 1/7] Code cleanup --- src/commandlinehandler.cpp | 557 +++++++++++++++++++++++++++++++++++++ src/commandlinehandler.h | 8 + src/imagepacker.cpp | 8 +- src/imagepacker.h | 12 +- src/imagesort.cpp | 8 +- src/main.cpp | 538 +---------------------------------- src/mainwindow.cpp | 39 +-- src/mainwindow.h | 82 +++--- src/maxrects.cpp | 2 +- src/maxrects.h | 4 +- src/tile.pro | 8 +- 11 files changed, 652 insertions(+), 614 deletions(-) create mode 100644 src/commandlinehandler.cpp create mode 100644 src/commandlinehandler.h diff --git a/src/commandlinehandler.cpp b/src/commandlinehandler.cpp new file mode 100644 index 0000000..533469a --- /dev/null +++ b/src/commandlinehandler.cpp @@ -0,0 +1,557 @@ +#include "commandlinehandler.h" +#include "imagepacker.h" +#include +#include +#include +#include + +namespace +{ + +const char C_USAGE_TEXT[] = +R"***(Usage: cheetah-texture-packer [-s size] [-o OUTFILE] [options] [file|dir ...] +Avaiable options: + -s, --size W[xH] atlas maximum size (if it is not enough - create + more than 1 atlas) + -o, --out-file OUTFILE output atlas name + --disable-merge do not merge similar images + --disable-crop do not crop images + --crop-threshold N crop threshold (0-255) + --disable-border do not make 1px border + --border-size set border size in pixels + --extrude-size set extrude size in pixels + --enable-rotate enable sprites rotation + --disable-recursion disable recursive scan (pack only given directory) + --square force to make square textures + --autosize-threshold N auto-optimize atlas size (0-100, 0 - disabled) + --min-texture-size WxH auto-optimize minimum size + --sort-order N select sorting order algorithm (0-4) + -h, -?, --help show this help and exit)***"; + +struct packerData +{ + QString path, file; +}; + +class CommandLineHandler +{ +public: + CommandLineHandler() + : m_imageExtensions({"bmp", "png", "jpg", "jpeg"}) + { + } + + int run(const QStringList &argv) + { + int textureWidth = 512; + int textureHeight = 512; + bool merge = true; + bool crop = true; + int border = 1; + int extrude = 0; + bool rotate = false; + bool recursion = true; + bool square = false; + bool autosize = false; + int cropThreshold = 1; + int autosizeThreshold = 80; + int minTextureSizeX = 32; + int minTextureSizeY = 32; + int sortorder = 4; + QString outDir = QDir::currentPath(); + QString outFile = "atlas"; + + int argc = argv.count(); + for(int i = 1; i < argc; ++i) + { + auto isOption = [&](const char *opt) -> bool { + return argv[i] == QLatin1String(opt); + }; + + if(isOption("--help") || isOption("-h") || isOption("-?")) + { + return printHelp(); + } + else if(isOption("-s") || isOption("--size")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option -s"); + } + if (!parseSize(argv[i], textureWidth, textureHeight)) + { + if (!parseInt(argv[i], textureWidth)) + { + return printHelp("Wrong texture size format"); + } + else + { + textureHeight = textureWidth; + } + } + printf("Setting texture size: %dx%d\n", textureWidth, textureHeight); + } + else if(isOption("-o") || isOption("--out-file")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option -o"); + } + QFileInfo info(argv[i]); + outFile = info.baseName(); + outDir = info.absolutePath(); + } + else if(isOption("--disable-merge")) + { + merge = false; + } + else if(isOption("--disable-crop")) + { + crop = false; + } + else if(isOption("--disable-recursion")) + { + recursion = false; + } + else if(isOption("--square")) + { + square = true; + } + else if(isOption("--autosize-threshold")) + { + autosize = true; + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option --autosize-threshold"); + } + if(!parseInt(argv[i], autosizeThreshold) || + (autosizeThreshold < 0) || + (autosizeThreshold > 100)) + { + return printHelp("Wrong autosize threshold"); + } + } + else if(isOption("--extrude-size")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option --extrude-size"); + } + if(!parseInt(argv[i], extrude) || (extrude < 0)) + { + return printHelp("Wrong extrude size"); + } + } + else if(isOption("--border-size")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option --border-size"); + } + if(!parseInt(argv[i], border) || (border < 0)) + { + return printHelp("Wrong border size"); + } + } + else if(isOption("--min-texture-size")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option -min-texture-size"); + } + if(!parseSize(argv[i], minTextureSizeX, minTextureSizeY)) + { + if(!parseInt(argv[i], minTextureSizeX)) + { + return printHelp("Wrong texture size format"); + } + else + { + minTextureSizeY = minTextureSizeX; + } + } + } + else if(isOption("--crop-threshold")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option --crop-threshold"); + } + if(!parseInt(argv[i], cropThreshold) || + (cropThreshold < 0) || + (cropThreshold > 255)) + { + return printHelp("Wrong crop threshold"); + } + } + else if(isOption("--sort-order")) + { + ++i; + if(i >= argc) + { + return printHelp("Argument needed for option --sort-order"); + } + if(!parseInt(argv[i], sortorder) || + (sortorder < 0) || + (sortorder > 4)) + { + return printHelp("Wrong sortorder must be from 0 to 4"); + } + } + else if(isOption("--disable-border")) + { + border = 0; + } + else if(isOption("--enable-rotate")) + { + rotate = true; + } + //dir or file + else + { + QFileInfo file(argv[i]); + if(file.isFile()) + { + packerData *data = new packerData; + data->path = file.absoluteFilePath(); + data->file = file.fileName(); + m_packer.addItem(data->path, data); + } + else if(file.isDir()) + { + m_topImageDir = file.absoluteFilePath(); + recurseDirectory(file.absoluteFilePath(), recursion); + } + } + } + + qDebug() << "Saving to dir" << outDir << "and file" << outFile; + m_packer.sortOrder = sortorder; + m_packer.border.t = 0; + m_packer.border.l = 0; + m_packer.border.r = border; + m_packer.border.b = border; + m_packer.extrude = extrude; + m_packer.cropThreshold = crop ? cropThreshold : 0; + m_packer.minFillRate = autosize ? autosizeThreshold : 0; + m_packer.minTextureSizeX = minTextureSizeX; + m_packer.minTextureSizeY = minTextureSizeY; + m_packer.merge = merge; + m_packer.mergeBF = false; + m_packer.rotate = rotate; + m_packer.square = square; + m_packer.autosize = autosize; + int heuristic = 1; + + QString outFormat("PNG"); + + if(m_packer.images.size() == 0) + { + fprintf(stderr, "No images found, exitting\n"); + return 1; + } + + m_packer.pack(heuristic, textureWidth, textureHeight); + + QList textures; + for(int i = 0; i < m_packer.bins.size(); i++) + { + QImage texture(m_packer.bins.at(i).width(), m_packer.bins.at(i).height(), + QImage::Format_ARGB32); + texture.fill(Qt::transparent); + textures << texture; + } + for(int j = 0; j < textures.count(); j++) + { + QString outputFile = outDir; + outputFile += QDir::separator(); + outputFile += outFile; + if(textures.count() > 1) + { + outputFile += QString("_") + QString::number(j + 1); + } + outputFile += ".atlas"; + QString imgFile = outFile; + if(textures.count() > 1) + { + imgFile += QString("_") + QString::number(j + 1); + } + imgFile += "."; + imgFile += outFormat.toLower(); + + QFile positionsFile(outputFile); + if(!positionsFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + fprintf(stderr, "Cannot create file %s", qPrintable(outputFile)); + } + else + { + QTextStream out(&positionsFile); + out << "textures: " << imgFile << "\n"; + for(int i = 0; i < m_packer.images.size(); i++) + { + if(m_packer.images.at(i).textureId != j) + { + continue; + } + QPoint pos(m_packer.images.at(i).pos.x() + m_packer.border.l + m_packer.extrude, + m_packer.images.at(i).pos.y() + m_packer.border.t + m_packer.extrude); + QSize size, sizeOrig; + QRect crop; + sizeOrig = m_packer.images.at(i).size; + if(!m_packer.cropThreshold) + { + size = m_packer.images.at(i).size; + crop = QRect(0, 0, size.width(), size.height()); + } + else + { + size = m_packer.images.at(i).crop.size(); + crop = m_packer.images.at(i).crop; + } + if(m_packer.images.at(i).rotated) + { + size.transpose(); + crop = QRect(crop.y(), crop.x(), crop.height(), crop.width()); + } + out << (static_cast(m_packer.images.at(i).id))->file << + "\t" << + pos.x() << "\t" << + pos.y() << "\t" << + crop.width() << "\t" << + crop.height() << "\t" << + crop.x() << "\t" << + crop.y() << "\t" << + sizeOrig.width() << "\t" << + sizeOrig.height() << "\t" << + (m_packer.images.at(i).rotated ? "r" : "") << "\n"; + } + } + } + + for(int i = 0; i < m_packer.images.size(); i++) + { + qDebug() << "Processing" << (static_cast(m_packer.images.at( + i).id))->file; + if(m_packer.images.at(i).duplicateId != NULL && m_packer.merge) + { + continue; + } + QPoint pos(m_packer.images.at(i).pos.x() + m_packer.border.l, + m_packer.images.at(i).pos.y() + m_packer.border.t); + QSize size; + QRect crop; + if(!m_packer.cropThreshold) + { + size = m_packer.images.at(i).size; + crop = QRect(0, 0, size.width(), size.height()); + } + else + { + size = m_packer.images.at(i).crop.size(); + crop = m_packer.images.at(i).crop; + } + QImage img; + img = QImage((static_cast(m_packer.images.at(i).id))->path); + if(m_packer.images.at(i).rotated) + { + QTransform myTransform; + myTransform.rotate(90); + img = img.transformed(myTransform); + size.transpose(); + crop = QRect(m_packer.images.at(i).size.height() - crop.y() - crop.height(), + crop.x(), crop.height(), crop.width()); + } + if(m_packer.images.at(i).textureId < m_packer.bins.size()) + { + QPainter p(&textures.operator [](m_packer.images.at(i).textureId)); + + if(m_packer.extrude) + { + QColor color1 = QColor::fromRgba(img.pixel(crop.x(), crop.y())); + p.setPen(color1); + p.setBrush(color1); + if(m_packer.extrude == 1) + { + p.drawPoint(QPoint(pos.x(), pos.y())); + } + else + { + p.drawRect(QRect(pos.x(), pos.y(), m_packer.extrude - 1, m_packer.extrude - 1)); + } + + QColor color2 = QColor::fromRgba(img.pixel(crop.x(), + crop.y() + crop.height() - 1)); + p.setPen(color2); + p.setBrush(color2); + if(m_packer.extrude == 1) + { + p.drawPoint(QPoint(pos.x(), pos.y() + crop.height() + m_packer.extrude)); + } + else + { + p.drawRect(QRect(pos.x(), pos.y() + crop.height() + m_packer.extrude, + m_packer.extrude - 1, m_packer.extrude - 1)); + } + + QColor color3 = QColor::fromRgba(img.pixel(crop.x() + crop.width() - 1, + crop.y())); + p.setPen(color3); + p.setBrush(color3); + if(m_packer.extrude == 1) + { + p.drawPoint(QPoint(pos.x() + crop.width() + m_packer.extrude, pos.y())); + } + else + { + p.drawRect(QRect(pos.x() + crop.width() + m_packer.extrude, pos.y(), + m_packer.extrude - 1, m_packer.extrude - 1)); + } + + QColor color4 = QColor::fromRgba(img.pixel(crop.x() + crop.width() - 1, + crop.y() + crop.height() - 1)); + p.setPen(color4); + p.setBrush(color4); + if(m_packer.extrude == 1) + { + p.drawPoint(QPoint(pos.x() + crop.width() + m_packer.extrude, + pos.y() + crop.height() + m_packer.extrude)); + } + else + { + p.drawRect(QRect(pos.x() + crop.width() + m_packer.extrude, + pos.y() + crop.height() + m_packer.extrude, m_packer.extrude - 1, + m_packer.extrude - 1)); + } + + p.drawImage(QRect(pos.x(), pos.y() + m_packer.extrude, m_packer.extrude, + crop.height()), img, QRect(crop.x(), crop.y(), 1, crop.height())); + p.drawImage(QRect(pos.x() + crop.width() + m_packer.extrude, + pos.y() + m_packer.extrude, m_packer.extrude, crop.height()), img, + QRect(crop.x() + crop.width() - 1, crop.y(), 1, crop.height())); + + p.drawImage(QRect(pos.x() + m_packer.extrude, pos.y(), crop.width(), + m_packer.extrude), img, QRect(crop.x(), crop.y(), crop.width(), 1)); + p.drawImage(QRect(pos.x() + m_packer.extrude, + pos.y() + crop.height() + m_packer.extrude, crop.width(), m_packer.extrude), img, + QRect(crop.x(), crop.y() + crop.height() - 1, crop.width(), 1)); + + p.drawImage(pos.x() + m_packer.extrude, pos.y() + m_packer.extrude, img, crop.x(), + crop.y(), crop.width(), crop.height()); + } + else + { + p.drawImage(pos.x(), pos.y(), img, crop.x(), crop.y(), crop.width(), + crop.height()); + } + } + } + qint64 area = 0; + for(int i = 0; i < textures.count(); i++) + { + area += textures.at(i).width() * textures.at(i).height(); + } + float percent = (((float)m_packer.area / (float)area) * 100.0f); + // float percent2 = (float)(((float)packer.neededArea / (float)area) * 100.0f ); + printf("Atlas generated. %f%% filled, %d images missed, %d merged, %d KB\n", + percent, m_packer.missingImages, m_packer.mergedImages, (int)((area * 4) / 1024)); + + // const char * format = qPrintable(outFormat); + for(int i = 0; i < textures.count(); i++) + { + QString imgdirFile; + imgdirFile = outDir; + imgdirFile += QDir::separator(); + imgdirFile += outFile; + if(textures.count() > 1) + { + imgdirFile += QString("_") + QString::number(i + 1); + } + imgdirFile += "."; + imgdirFile += outFormat.toLower(); + textures.at(i).save(imgdirFile); + } + + return 0; + } + +private: + bool parseInt(const QString &str, int &value) + { + bool ok = false; + const int readValue = str.toInt(&ok); + if (ok) + { + value = readValue; + } + return ok; + } + + bool parseSize(const QString &str, int sizeX, int sizeY) + { + int readCount = sscanf(str.toLatin1().data(), "%10dx%10d", &sizeX, &sizeY); + return (readCount == 2); + } + + int printHelp(const char *error = NULL) + { + if(error) + { + fputs(error, stderr); + } + printf(C_USAGE_TEXT); + + return (error) ? 1 : 0; + } + + void recurseDirectory(const QString &dir, bool recursion) + { + QDir dirEnt(dir); + QFileInfoList list = dirEnt.entryInfoList(); + for(int i = 0; i < list.count(); i++) + { + QFileInfo info = list[i]; + + QString filePath = info.filePath(); + QString fileExt = info.suffix().toLower(); + QString name = dir + QDir::separator(); + if(recursion && info.isDir()) + { + // recursive + if(info.fileName() != ".." && info.fileName() != ".") + { + recurseDirectory(filePath, recursion); + } + } + else if(m_imageExtensions.contains(fileExt)) + { + if(!QFile::exists(name + info.completeBaseName() + QString(".atlas"))) + { + packerData *data = new packerData; + data->path = info.absoluteFilePath(); + data->file = filePath.replace(m_topImageDir, ""); +// qDebug() << "Packing " << data->path << "..."; + m_packer.addItem(data->path, data); + } + } + } + } + + QStringList m_imageExtensions; + ImagePacker m_packer; + QString m_topImageDir; +}; +} + +int doCommandLineJobs(const QStringList &args) +{ + CommandLineHandler handler; + return handler.run(args); +} diff --git a/src/commandlinehandler.h b/src/commandlinehandler.h new file mode 100644 index 0000000..e0f78f3 --- /dev/null +++ b/src/commandlinehandler.h @@ -0,0 +1,8 @@ +#ifndef COMMANDLINEHANDLER_H +#define COMMANDLINEHANDLER_H + +#include + +int doCommandLineJobs(const QStringList &args); + +#endif // COMMANDLINEHANDLER_H diff --git a/src/imagepacker.cpp b/src/imagepacker.cpp index 5d5ba1d..cbd0fdc 100644 --- a/src/imagepacker.cpp +++ b/src/imagepacker.cpp @@ -238,7 +238,7 @@ void ImagePacker::UpdateCrop() void ImagePacker::addItem(const QImage &img, void *data, QString path) { - inputImage i; + InputImage i; if(img.width() == 0 || img.height() == 0) { return; @@ -293,7 +293,7 @@ void ImagePacker::removeId(void *data) } } } -const inputImage *ImagePacker::find(void *data) +const InputImage *ImagePacker::find(void *data) { for(int i = 0; i < images.count(); i++) { @@ -426,7 +426,7 @@ unsigned ImagePacker::AddImgesToBins(int heur, int w, int h) void ImagePacker::CropLastImage(int heur, int w, int h, bool wh) { missingImages = 0; - QList last_images = images; + QList last_images = images; QList last_bins = bins; quint64 last_area = area; @@ -503,7 +503,7 @@ void ImagePacker::CropLastImage(int heur, int w, int h, bool wh) void ImagePacker::DivideLastImage(int heur, int w, int h, bool wh) { missingImages = 0; - QList last_images = images; + QList last_images = images; QList last_bins = bins; quint64 last_area = area; diff --git a/src/imagepacker.h b/src/imagepacker.h index a3d2767..6f715c4 100644 --- a/src/imagepacker.h +++ b/src/imagepacker.h @@ -6,7 +6,7 @@ class MaxRects; -struct packedImage +struct PackedImage { QImage img; QRect rc; @@ -16,7 +16,7 @@ struct packedImage int id; }; -struct inputImage +struct InputImage { quint32 hash; int textureId; @@ -30,7 +30,7 @@ struct inputImage bool cropped, rotated; }; -struct border_t +struct Border { int t, b, l, r; }; @@ -44,7 +44,7 @@ class ImagePacker : public QObject void SortImages(int w, int h); public: - QList images; + QList images; QList bins; ImagePacker(); bool compareImages(QImage *img1, QImage *img2, int *i, int *j); @@ -67,7 +67,7 @@ class ImagePacker : public QObject void sort(); void addItem(const QImage &img, void *data, QString path); void addItem(QString path, void *data); - const inputImage *find(void *data); + const InputImage *find(void *data); void removeId(void *); void realculateDuplicates(); void clear(); @@ -77,7 +77,7 @@ class ImagePacker : public QObject int mergedImages; bool ltr, merge, square, autosize, mergeBF; int cropThreshold; - border_t border; + Border border; int extrude; int rotate; int sortOrder; diff --git a/src/imagesort.cpp b/src/imagesort.cpp index 048e7e8..37e8695 100644 --- a/src/imagesort.cpp +++ b/src/imagesort.cpp @@ -1,22 +1,22 @@ #include "imagepacker.h" -bool ImageCompareByHeight(const inputImage &i1, const inputImage &i2) +bool ImageCompareByHeight(const InputImage &i1, const InputImage &i2) { return (i1.sizeCurrent.height() << 10) + i1.sizeCurrent.width() > (i2.sizeCurrent.height() << 10) + i2.sizeCurrent.width(); } -bool ImageCompareByWidth(const inputImage &i1, const inputImage &i2) +bool ImageCompareByWidth(const InputImage &i1, const InputImage &i2) { return (i1.sizeCurrent.width() << 10) + i1.sizeCurrent.height() > (i2.sizeCurrent.width() << 10) + i2.sizeCurrent.height(); } -bool ImageCompareByArea(const inputImage &i1, const inputImage &i2) +bool ImageCompareByArea(const InputImage &i1, const InputImage &i2) { return i1.sizeCurrent.height() * i1.sizeCurrent.width() > i2.sizeCurrent.height() * i2.sizeCurrent.width(); } -bool ImageCompareByMax(const inputImage &i1, const inputImage &i2) +bool ImageCompareByMax(const InputImage &i1, const InputImage &i2) { int first = i1.sizeCurrent.height() > i1.sizeCurrent.width() ? i1.sizeCurrent.height() : i1.sizeCurrent.width(); diff --git a/src/main.cpp b/src/main.cpp index 17df51b..b84b7d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,547 +9,15 @@ #include "mainwindow.h" #include #include -#include -#include -#include -#include "stdio.h" -#include "stdlib.h" - -QStringList imageExtensions; - -void printHelp(const char *error = NULL) -{ - if(error) - { - fputs(error, stderr); - } - printf( - "Usage: cheetah-texture-packer [-s size] [-o OUTFILE] [options] [file|dir ...]\n" - "Avaiable options:\n" - " -s, --size W[xH] atlas maximum size (if it is not enough - create\n" - " more than 1 atlas)\n" - " -o, --out-file OUTFILE output atlas name\n" - " --disable-merge do not merge similar images\n" - " --disable-crop do not crop images\n" - " --crop-threshold N crop threshold (0-255)\n" - " --disable-border do not make 1px border\n" - " --border-size set border size in pixels\n" - " --extrude-size set extrude size in pixels\n" - " --enable-rotate enable sprites rotation\n" - " --disable-recursion disable recursive scan (pack only given directory)\n" - " --square force to make square textures\n" - " --autosize-threshold N auto-optimize atlas size (0-100, 0 - disabled)\n" - " --min-texture-size WxH auto-optimize minimum size\n" - " --sort-order N select sorting order algorithm (0-4)\n" - " -h, -?, --help show this help and exit\n"); - if(error) - { - exit(1); - } - else - { - exit(0); - } -} -struct packerData -{ - QString path, file; -}; - -ImagePacker *mainPacker; -QString topImageDir; - -void RecurseDirectory(const QString dir, bool recursion) -{ - QDir dirEnt(dir); - QFileInfoList list = dirEnt.entryInfoList(); - for(int i = 0; i < list.count(); i++) - { - QFileInfo info = list[i]; - - QString filePath = info.filePath(); - QString fileExt = info.suffix().toLower(); - QString name = dir + QDir::separator(); - if(recursion && info.isDir()) - { - // recursive - if(info.fileName() != ".." && info.fileName() != ".") - { - RecurseDirectory(filePath, recursion); - } - } - else - if(imageExtensions.contains(fileExt)) - { - if(!QFile::exists(name + info.completeBaseName() + QString(".atlas"))) - { - packerData *data = new packerData; - data->path = info.absoluteFilePath(); - data->file = filePath.replace(topImageDir, ""); - // qDebug() << "Packing " << data->path << "..."; - mainPacker->addItem(data->path, data); - } - } - } -} - -#define check_opt(opt) (strncmp(argv[i], opt, sizeof(opt) - 1) == 0) +#include "commandlinehandler.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); - imageExtensions << "bmp" << "png" << "jpg" << "jpeg"; - //command-line version if(argc > 1) { - int textureWidth = 512; - int textureHeight = 512; - bool merge = true; - bool crop = true; - int border = 1; - int extrude = 0; - bool rotate = false; - bool recursion = true; - bool square = false; - bool autosize = false; - int cropThreshold = 1; - int autosizeThreshold = 80; - int minTextureSizeX = 32; - int minTextureSizeY = 32; - int sortorder = 4; - ImagePacker packer; - mainPacker = &packer; - QString outDir = QDir::currentPath(); - QString outFile = "atlas"; - for(int i = 1; i < argc; ++i) - { - if(check_opt("--help") || check_opt("-h") || check_opt("-?")) - { - printHelp(); - } - else - if(check_opt("-s") || check_opt("--size")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option -s"); - } - if(sscanf(argv[i], "%10dx%10d", &textureWidth, &textureHeight) != 2) - { - if(sscanf(argv[i], "%10d", &textureWidth) != 1) - { - printHelp("Wrong texture size format"); - } - else - { - textureHeight = textureWidth; - } - } - printf("Setting texture size: %dx%d\n", textureWidth, textureHeight); - } - else - if(check_opt("-o") || check_opt("--out-file")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option -o"); - } - QFileInfo info(argv[i]); - outFile = info.baseName(); - outDir = info.absolutePath(); - } - else - if(check_opt("--disable-merge")) - { - merge = false; - } - else - if(check_opt("--disable-crop")) - { - crop = false; - } - else - if(check_opt("--disable-recursion")) - { - recursion = false; - } - else - if(check_opt("--square")) - { - square = true; - } - else - if(check_opt("--autosize-threshold")) - { - autosize = true; - ++i; - if(i >= argc) - { - printHelp("Argument needed for option --autosize-threshold"); - } - if((sscanf(argv[i], "%10d", &autosizeThreshold) != 1) || - (autosizeThreshold < 0) || - (autosizeThreshold > 100)) - { - printHelp("Wrong autosize threshold"); - } - } - else - if(check_opt("--extrude-size")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option --extrude-size"); - } - if((sscanf(argv[i], "%10d", &extrude) != 1) || (extrude < 0)) - { - printHelp("Wrong extrude size"); - } - } - else - if(check_opt("--border-size")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option --border-size"); - } - if((sscanf(argv[i], "%10d", &border) != 1) || (border < 0)) - { - printHelp("Wrong border size"); - } - } - else - if(check_opt("--min-texture-size")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option -min-texture-size"); - } - if(sscanf(argv[i], "%10dx%10d", &minTextureSizeX, &minTextureSizeY) != 2) - { - if(sscanf(argv[i], "%10d", &minTextureSizeX) != 1) - { - printHelp("Wrong texture size format"); - } - else - { - minTextureSizeY = minTextureSizeX; - } - } - } - else - if(check_opt("--crop-threshold")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option --crop-threshold"); - } - if((sscanf(argv[i], "%10d", &cropThreshold) != 1) || - (cropThreshold < 0) || - (cropThreshold > 255)) - { - printHelp("Wrong crop threshold"); - } - } - else - if(check_opt("--sort-order")) - { - ++i; - if(i >= argc) - { - printHelp("Argument needed for option --sort-order"); - } - if((sscanf(argv[i], "%10d", &sortorder) != 1) || - (sortorder < 0) || - (sortorder > 4)) - { - printHelp("Wrong sortorder must be from 0 to 4"); - } - } - else - if(check_opt("--disable-border")) - { - border = 0; - } - else - if(check_opt("--enable-rotate")) - { - rotate = true; - } - //dir or file - else - { - QFileInfo file(argv[i]); - if(file.isFile()) - { - packerData *data = new packerData; - data->path = file.absoluteFilePath(); - data->file = file.fileName(); - packer.addItem(data->path, data); - } - else - if(file.isDir()) - { - topImageDir = file.absoluteFilePath(); - RecurseDirectory(file.absoluteFilePath(), recursion); - } - } - } - - qDebug() << "Saving to dir" << outDir << "and file" << outFile; - packer.sortOrder = sortorder; - packer.border.t = 0; - packer.border.l = 0; - packer.border.r = border; - packer.border.b = border; - packer.extrude = extrude; - packer.cropThreshold = crop ? cropThreshold : 0; - packer.minFillRate = autosize ? autosizeThreshold : 0; - packer.minTextureSizeX = minTextureSizeX; - packer.minTextureSizeY = minTextureSizeY; - packer.merge = merge; - packer.mergeBF = false; - packer.rotate = rotate; - packer.square = square; - packer.autosize = autosize; - int heuristic = 1; - - QString outFormat("PNG"); - - if(packer.images.size() == 0) - { - fprintf(stderr, "No images found, exitting\n"); - exit(1); - } - - packer.pack(heuristic, textureWidth, textureHeight); - - QList textures; - for(int i = 0; i < packer.bins.size(); i++) - { - QImage texture(packer.bins.at(i).width(), packer.bins.at(i).height(), - QImage::Format_ARGB32); - texture.fill(Qt::transparent); - textures << texture; - } - for(int j = 0; j < textures.count(); j++) - { - QString outputFile = outDir; - outputFile += QDir::separator(); - outputFile += outFile; - if(textures.count() > 1) - { - outputFile += QString("_") + QString::number(j + 1); - } - outputFile += ".atlas"; - QString imgFile = outFile; - if(textures.count() > 1) - { - imgFile += QString("_") + QString::number(j + 1); - } - imgFile += "."; - imgFile += outFormat.toLower(); - - QFile positionsFile(outputFile); - if(!positionsFile.open(QIODevice::WriteOnly | QIODevice::Text)) - { - fprintf(stderr, "Cannot create file %s", qPrintable(outputFile)); - } - else - { - QTextStream out(&positionsFile); - out << "textures: " << imgFile << "\n"; - for(int i = 0; i < packer.images.size(); i++) - { - if(packer.images.at(i).textureId != j) - { - continue; - } - QPoint pos(packer.images.at(i).pos.x() + packer.border.l + packer.extrude, - packer.images.at(i).pos.y() + packer.border.t + packer.extrude); - QSize size, sizeOrig; - QRect crop; - sizeOrig = packer.images.at(i).size; - if(!packer.cropThreshold) - { - size = packer.images.at(i).size; - crop = QRect(0, 0, size.width(), size.height()); - } - else - { - size = packer.images.at(i).crop.size(); - crop = packer.images.at(i).crop; - } - if(packer.images.at(i).rotated) - { - size.transpose(); - crop = QRect(crop.y(), crop.x(), crop.height(), crop.width()); - } - out << (static_cast(packer.images.at(i).id))->file << - "\t" << - pos.x() << "\t" << - pos.y() << "\t" << - crop.width() << "\t" << - crop.height() << "\t" << - crop.x() << "\t" << - crop.y() << "\t" << - sizeOrig.width() << "\t" << - sizeOrig.height() << "\t" << - (packer.images.at(i).rotated ? "r" : "") << "\n"; - } - } - } - - for(int i = 0; i < packer.images.size(); i++) - { - qDebug() << "Processing" << (static_cast(packer.images.at( - i).id))->file; - if(packer.images.at(i).duplicateId != NULL && packer.merge) - { - continue; - } - QPoint pos(packer.images.at(i).pos.x() + packer.border.l, - packer.images.at(i).pos.y() + packer.border.t); - QSize size; - QRect crop; - if(!packer.cropThreshold) - { - size = packer.images.at(i).size; - crop = QRect(0, 0, size.width(), size.height()); - } - else - { - size = packer.images.at(i).crop.size(); - crop = packer.images.at(i).crop; - } - QImage img; - img = QImage((static_cast(packer.images.at(i).id))->path); - if(packer.images.at(i).rotated) - { - QTransform myTransform; - myTransform.rotate(90); - img = img.transformed(myTransform); - size.transpose(); - crop = QRect(packer.images.at(i).size.height() - crop.y() - crop.height(), - crop.x(), crop.height(), crop.width()); - } - if(packer.images.at(i).textureId < packer.bins.size()) - { - QPainter p(&textures.operator [](packer.images.at(i).textureId)); - - if(packer.extrude) - { - QColor color1 = QColor::fromRgba(img.pixel(crop.x(), crop.y())); - p.setPen(color1); - p.setBrush(color1); - if(packer.extrude == 1) - { - p.drawPoint(QPoint(pos.x(), pos.y())); - } - else - { - p.drawRect(QRect(pos.x(), pos.y(), packer.extrude - 1, packer.extrude - 1)); - } - - QColor color2 = QColor::fromRgba(img.pixel(crop.x(), - crop.y() + crop.height() - 1)); - p.setPen(color2); - p.setBrush(color2); - if(packer.extrude == 1) - { - p.drawPoint(QPoint(pos.x(), pos.y() + crop.height() + packer.extrude)); - } - else - { - p.drawRect(QRect(pos.x(), pos.y() + crop.height() + packer.extrude, - packer.extrude - 1, packer.extrude - 1)); - } - - QColor color3 = QColor::fromRgba(img.pixel(crop.x() + crop.width() - 1, - crop.y())); - p.setPen(color3); - p.setBrush(color3); - if(packer.extrude == 1) - { - p.drawPoint(QPoint(pos.x() + crop.width() + packer.extrude, pos.y())); - } - else - { - p.drawRect(QRect(pos.x() + crop.width() + packer.extrude, pos.y(), - packer.extrude - 1, packer.extrude - 1)); - } - - QColor color4 = QColor::fromRgba(img.pixel(crop.x() + crop.width() - 1, - crop.y() + crop.height() - 1)); - p.setPen(color4); - p.setBrush(color4); - if(packer.extrude == 1) - { - p.drawPoint(QPoint(pos.x() + crop.width() + packer.extrude, - pos.y() + crop.height() + packer.extrude)); - } - else - { - p.drawRect(QRect(pos.x() + crop.width() + packer.extrude, - pos.y() + crop.height() + packer.extrude, packer.extrude - 1, - packer.extrude - 1)); - } - - p.drawImage(QRect(pos.x(), pos.y() + packer.extrude, packer.extrude, - crop.height()), img, QRect(crop.x(), crop.y(), 1, crop.height())); - p.drawImage(QRect(pos.x() + crop.width() + packer.extrude, - pos.y() + packer.extrude, packer.extrude, crop.height()), img, - QRect(crop.x() + crop.width() - 1, crop.y(), 1, crop.height())); - - p.drawImage(QRect(pos.x() + packer.extrude, pos.y(), crop.width(), - packer.extrude), img, QRect(crop.x(), crop.y(), crop.width(), 1)); - p.drawImage(QRect(pos.x() + packer.extrude, - pos.y() + crop.height() + packer.extrude, crop.width(), packer.extrude), img, - QRect(crop.x(), crop.y() + crop.height() - 1, crop.width(), 1)); - - p.drawImage(pos.x() + packer.extrude, pos.y() + packer.extrude, img, crop.x(), - crop.y(), crop.width(), crop.height()); - } - else - { - p.drawImage(pos.x(), pos.y(), img, crop.x(), crop.y(), crop.width(), - crop.height()); - } - } - } - qint64 area = 0; - for(int i = 0; i < textures.count(); i++) - { - area += textures.at(i).width() * textures.at(i).height(); - } - float percent = (((float)packer.area / (float)area) * 100.0f); - // float percent2 = (float)(((float)packer.neededArea / (float)area) * 100.0f ); - printf("Atlas generated. %f%% filled, %d images missed, %d merged, %d KB\n", - percent, packer.missingImages, packer.mergedImages, (int)((area * 4) / 1024)); - - // const char * format = qPrintable(outFormat); - for(int i = 0; i < textures.count(); i++) - { - QString imgdirFile; - imgdirFile = outDir; - imgdirFile += QDir::separator(); - imgdirFile += outFile; - if(textures.count() > 1) - { - imgdirFile += QString("_") + QString::number(i + 1); - } - imgdirFile += "."; - imgdirFile += outFormat.toLower(); - textures.at(i).save(imgdirFile); - } - - return 0; + // run command-line version + return doCommandLineJobs(a.arguments()); } QTranslator myTranslator; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9fe069b..6d1494a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5,9 +5,11 @@ #include #include -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , imageExtensions({"bmp", "png", "jpg", "jpeg"}) { exporting = false; ui->setupUi(this); @@ -21,8 +23,8 @@ MainWindow::MainWindow(QWidget *parent) : pattern = QPixmap(20, 20); QPainter painter(&pattern); -#define BRIGHT 190 -#define SHADOW 150 + const int BRIGHT = 190; + const int SHADOW = 150; painter.fillRect(0, 0, 10, 10, QColor(SHADOW, SHADOW, SHADOW)); painter.fillRect(10, 0, 10, 10, QColor(BRIGHT, BRIGHT, BRIGHT)); painter.fillRect(10, 10, 10, 10, QColor(SHADOW, SHADOW, SHADOW)); @@ -35,11 +37,11 @@ MainWindow::~MainWindow() delete ui; } -void MainWindow::RecurseDirectory(const QString &dir) +void MainWindow::recurseDirectory(const QString &dir) { QDir dirEnt(dir); QFileInfoList list = dirEnt.entryInfoList(); - for(int i = 0; i < list.count() && !recursiveLoaderDone; i++) + for (int i = 0; i < list.count() && !recursiveLoaderDone; i++) { recursiveLoaderCounter++; QFileInfo info = list[i]; @@ -52,21 +54,20 @@ void MainWindow::RecurseDirectory(const QString &dir) // recursive if(info.fileName() != ".." && info.fileName() != ".") { - RecurseDirectory(filePath); + recurseDirectory(filePath); } } - else - if(imageExtensions.contains(fileExt)) + else if(imageExtensions.contains(fileExt)) + { + if(!QFile::exists(name + info.completeBaseName() + QString(".atlas"))) { - if(!QFile::exists(name + info.completeBaseName() + QString(".atlas"))) - { - ui->tilesList->addItem(filePath.replace(topImageDir, "")); - packerData *data = new packerData; - data->listItem = ui->tilesList->item(ui->tilesList->count() - 1); - data->path = info.absoluteFilePath(); - packer.addItem(data->path, data); - } + ui->tilesList->addItem(filePath.replace(topImageDir, "")); + packerData *data = new packerData; + data->listItem = ui->tilesList->item(ui->tilesList->count() - 1); + data->path = info.absoluteFilePath(); + packer.addItem(data->path, data); } + } if(recursiveLoaderCounter == 500) { if(QMessageBox::No == @@ -106,7 +107,7 @@ void MainWindow::addDir(QString dir) recursiveLoaderCounter = 0; recursiveLoaderDone = false; //packer.clear(); - RecurseDirectory(dir); + recurseDirectory(dir); QFileInfo info(dir); ui->outFile->setText(info.baseName()); diff --git a/src/mainwindow.h b/src/mainwindow.h index 8b9f795..5725def 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -16,8 +16,6 @@ #include #include "imagepacker.h" -extern QStringList imageExtensions; - namespace Ui { class MainWindow; @@ -25,48 +23,50 @@ namespace Ui class MainWindow : public QMainWindow { - Q_OBJECT +Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); - public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); +private: + void recurseDirectory(const QString &dir); - private: - Ui::MainWindow *ui; - void RecurseDirectory(const QString &dir); - QString topImageDir; - ImagePacker packer; - QList packedImageList; - bool exporting; - int recursiveLoaderCounter; - bool recursiveLoaderDone; - QPixmap pattern; - void addDir(QString dir); - struct packerData - { - QListWidgetItem *listItem; - QString path; - }; + Ui::MainWindow *ui; + QStringList imageExtensions; + QString topImageDir; + ImagePacker packer; + QList packedImageList; + bool exporting; + int recursiveLoaderCounter; + bool recursiveLoaderDone; + QPixmap pattern; + void addDir(QString dir); + struct packerData + { + QListWidgetItem *listItem; + QString path; + }; - protected: - void dropEvent(QDropEvent *event); - void dragEnterEvent(QDragEnterEvent *event); - signals: - void renderedImage(const QList &image); - public slots: - void addTiles(); - void deleteSelectedTiles(); - void packerUpdate(); - void updateAuto(); - void setTextureSize2048(); - void setTextureSize256(); - void setTextureSize512(); - void setTextureSize1024(); - void updateAplhaThreshold(); - void getFolder(); - void exportImage(); - void swapSizes(); - void clearTiles(); +protected: + void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event); +signals: + void renderedImage(const QList &image); +public slots: + void addTiles(); + void deleteSelectedTiles(); + void packerUpdate(); + void updateAuto(); + void setTextureSize2048(); + void setTextureSize256(); + void setTextureSize512(); + void setTextureSize1024(); + void updateAplhaThreshold(); + void getFolder(); + void exportImage(); + void swapSizes(); + void clearTiles(); }; #endif // MAINWINDOW_H diff --git a/src/maxrects.cpp b/src/maxrects.cpp index c697b50..ad07217 100644 --- a/src/maxrects.cpp +++ b/src/maxrects.cpp @@ -4,7 +4,7 @@ MaxRects::MaxRects() { } -QPoint MaxRects::insertNode(inputImage *input) +QPoint MaxRects::insertNode(InputImage *input) { int i; int min = 999999999, mini = -1, m; diff --git a/src/maxrects.h b/src/maxrects.h index 72286af..230734e 100644 --- a/src/maxrects.h +++ b/src/maxrects.h @@ -21,10 +21,10 @@ class MaxRects QList F; QList R; QList FR; - QPoint insertNode(inputImage *); + QPoint insertNode(InputImage *); int heuristic, w, h, rotation; bool leftToRight; - border_t *border; + Border *border; }; #endif // MAXRECTS_H diff --git a/src/tile.pro b/src/tile.pro index 841b731..43ad93b 100644 --- a/src/tile.pro +++ b/src/tile.pro @@ -8,6 +8,8 @@ QT += core gui TARGET = cheetah-texture-packer +CONFIG += c++11 + QT_VERSION=$$[QT_VERSION] contains(QT_VERSION, "^5.*") { @@ -23,12 +25,14 @@ SOURCES += main.cpp\ imagepacker.cpp \ imagecrop.cpp \ imagesort.cpp \ - maxrects.cpp + maxrects.cpp \ + commandlinehandler.cpp HEADERS += mainwindow.h \ view.h \ imagepacker.h \ - maxrects.h + maxrects.h \ + commandlinehandler.h FORMS += mainwindow.ui QMAKE_CXXFLAGS += -Wextra -Werror From d33ccaf16a99482f9c10f4e0d6786aa9e7a3ca9c Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Mon, 16 May 2016 10:04:08 +0300 Subject: [PATCH 2/7] Code cleanup for exporting images --- src/atlasmetadatawriter.cpp | 33 +++++++++ src/atlasmetadatawriter.h | 21 ++++++ src/commandlinehandler.cpp | 53 +++----------- src/imetadatawriter.h | 17 +++++ src/main.cpp | 3 + src/mainwindow.cpp | 106 ++++++++++------------------ src/mainwindow.h | 1 + src/mainwindow.ui | 137 ++++++++++++++++++++++++------------ src/tile.pro | 9 ++- src/utils.cpp | 51 ++++++++++++++ src/utils.h | 19 +++++ 11 files changed, 292 insertions(+), 158 deletions(-) create mode 100644 src/atlasmetadatawriter.cpp create mode 100644 src/atlasmetadatawriter.h create mode 100644 src/imetadatawriter.h create mode 100644 src/utils.cpp create mode 100644 src/utils.h diff --git a/src/atlasmetadatawriter.cpp b/src/atlasmetadatawriter.cpp new file mode 100644 index 0000000..37957c1 --- /dev/null +++ b/src/atlasmetadatawriter.cpp @@ -0,0 +1,33 @@ +#include "atlasmetadatawriter.h" + +AtlasMetadataWriter::AtlasMetadataWriter() + : m_stream(&m_bytes) +{ +} + +void AtlasMetadataWriter::WriteTexture(const QString &path, const QSize &size) +{ + (void)size; + m_stream << "textures: " << path << "\n"; +} + +void AtlasMetadataWriter::WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, + const QSize &origSize, bool rotated) +{ + m_stream << name << "\t" << + pos.x() << "\t" << + pos.y() << "\t" << + crop.width() << "\t" << + crop.height() << "\t" << + crop.x() << "\t" << + crop.y() << "\t" << + origSize.width() << "\t" << + origSize.height() << "\t" << + (rotated ? "r" : "") << "\n"; +} + +QByteArray AtlasMetadataWriter::ToBytes() const +{ + m_stream.flush(); + return m_bytes; +} diff --git a/src/atlasmetadatawriter.h b/src/atlasmetadatawriter.h new file mode 100644 index 0000000..1a4559f --- /dev/null +++ b/src/atlasmetadatawriter.h @@ -0,0 +1,21 @@ +#ifndef ATLASMETADATAWRITER_H +#define ATLASMETADATAWRITER_H + +#include "imetadatawriter.h" +#include + +class AtlasMetadataWriter : public IMetadataWriter +{ +public: + AtlasMetadataWriter(); + + void WriteTexture(const QString &path, const QSize &size) override; + void WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, const QSize &origSize, bool rotated) override; + QByteArray ToBytes()const override; + +private: + QByteArray m_bytes; + mutable QTextStream m_stream; +}; + +#endif // ATLASMETADATAWRITER_H diff --git a/src/commandlinehandler.cpp b/src/commandlinehandler.cpp index 533469a..4c0421d 100644 --- a/src/commandlinehandler.cpp +++ b/src/commandlinehandler.cpp @@ -1,5 +1,7 @@ +#include "atlasmetadatawriter.h" #include "commandlinehandler.h" #include "imagepacker.h" +#include "utils.h" #include #include #include @@ -286,53 +288,16 @@ class CommandLineHandler imgFile += "."; imgFile += outFormat.toLower(); - QFile positionsFile(outputFile); - if(!positionsFile.open(QIODevice::WriteOnly | QIODevice::Text)) + QStringList frameNames; + for(int i = 0; i < m_packer.images.size(); i++) { - fprintf(stderr, "Cannot create file %s", qPrintable(outputFile)); + frameNames << (static_cast(m_packer.images.at(i).id))->file; } - else + + AtlasMetadataWriter writer; + if (!Utils::exportMetadata(outputFile, imgFile, textures[j].size(), j, frameNames, m_packer, writer)) { - QTextStream out(&positionsFile); - out << "textures: " << imgFile << "\n"; - for(int i = 0; i < m_packer.images.size(); i++) - { - if(m_packer.images.at(i).textureId != j) - { - continue; - } - QPoint pos(m_packer.images.at(i).pos.x() + m_packer.border.l + m_packer.extrude, - m_packer.images.at(i).pos.y() + m_packer.border.t + m_packer.extrude); - QSize size, sizeOrig; - QRect crop; - sizeOrig = m_packer.images.at(i).size; - if(!m_packer.cropThreshold) - { - size = m_packer.images.at(i).size; - crop = QRect(0, 0, size.width(), size.height()); - } - else - { - size = m_packer.images.at(i).crop.size(); - crop = m_packer.images.at(i).crop; - } - if(m_packer.images.at(i).rotated) - { - size.transpose(); - crop = QRect(crop.y(), crop.x(), crop.height(), crop.width()); - } - out << (static_cast(m_packer.images.at(i).id))->file << - "\t" << - pos.x() << "\t" << - pos.y() << "\t" << - crop.width() << "\t" << - crop.height() << "\t" << - crop.x() << "\t" << - crop.y() << "\t" << - sizeOrig.width() << "\t" << - sizeOrig.height() << "\t" << - (m_packer.images.at(i).rotated ? "r" : "") << "\n"; - } + fprintf(stderr, "Cannot create file %s", qPrintable(outputFile)); } } diff --git a/src/imetadatawriter.h b/src/imetadatawriter.h new file mode 100644 index 0000000..a59a9a6 --- /dev/null +++ b/src/imetadatawriter.h @@ -0,0 +1,17 @@ +#ifndef IMETADATAWRITER_H +#define IMETADATAWRITER_H + +#include +#include + +class IMetadataWriter +{ +public: + virtual ~IMetadataWriter() = default; + virtual void WriteTexture(const QString &path, const QSize &size) = 0; + virtual void WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, + const QSize &origSize, bool rotated) = 0; + virtual QByteArray ToBytes()const = 0; +}; + +#endif // IMETADATAWRITER_H diff --git a/src/main.cpp b/src/main.cpp index b84b7d4..17d28fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,7 @@ #include "mainwindow.h" #include #include +#include #include "commandlinehandler.h" int main(int argc, char *argv[]) @@ -20,6 +21,8 @@ int main(int argc, char *argv[]) return doCommandLineJobs(a.arguments()); } + QApplication::setStyle(QStyleFactory::create("Fusion")); + QTranslator myTranslator; myTranslator.load("tile_" + QLocale::system().name(), "qm"); a.installTranslator(&myTranslator); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6d1494a..7fecfbc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,8 +1,9 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "atlasmetadatawriter.h" +#include "utils.h" #include #include -#include #include @@ -91,6 +92,41 @@ void MainWindow::recurseDirectory(const QString &dir) } } +void MainWindow::writeMetadataFile(const QList &images, int imageIndex) +{ + QStringList frameNames; + for (int i = 0; i < packer.images.size(); i++) + { + frameNames << ((static_cast(packer.images.at(i).id))->listItem)->text(); + } + + QString outDir = ui->outDir->text(); + QString outFile = ui->outFile->text(); + QString outFormat = ui->outFormat->currentText(); + + QString outputFile = outDir; + outputFile += QDir::separator(); + outputFile += outFile; + if(images.count() > 1) + { + outputFile += QString("_") + QString::number(imageIndex + 1); + } + outputFile += ".atlas"; + QString imgFile = outFile; + if(images.count() > 1) + { + imgFile += QString("_") + QString::number(imageIndex + 1); + } + imgFile += "."; + imgFile += outFormat.toLower(); + + AtlasMetadataWriter writer; + if(!Utils::exportMetadata(outputFile, imgFile, images[imageIndex].size(), imageIndex, frameNames, packer, writer)) + { + QMessageBox::critical(0, tr("Error"), tr("Cannot create file ") + outputFile); + } +} + void MainWindow::addDir(QString dir) { //FIXME @@ -179,71 +215,7 @@ void MainWindow::packerUpdate() { for(int j = 0; j < textures.count(); j++) { - QString outputFile = outDir; - outputFile += QDir::separator(); - outputFile += outFile; - if(textures.count() > 1) - { - outputFile += QString("_") + QString::number(j + 1); - } - outputFile += ".atlas"; - QString imgFile = outFile; - if(textures.count() > 1) - { - imgFile += QString("_") + QString::number(j + 1); - } - imgFile += "."; - imgFile += outFormat.toLower(); - - QFile positionsFile(outputFile); - if(!positionsFile.open(QIODevice::WriteOnly | QIODevice::Text)) - { - QMessageBox::critical(0, tr("Error"), tr("Cannot create file ") + outputFile); - } - else - { - QTextStream out(&positionsFile); - out << "textures: " << imgFile << "\n"; - for(i = 0; i < packer.images.size(); i++) - { - if(packer.images.at(i).textureId != j) - { - continue; - } - QPoint pos(packer.images.at(i).pos.x() + packer.border.l + packer.extrude, - packer.images.at(i).pos.y() + packer.border.t + packer.extrude); - QSize size, sizeOrig; - QRect crop; - sizeOrig = packer.images.at(i).size; - if(!packer.cropThreshold) - { - size = packer.images.at(i).size; - crop = QRect(0, 0, size.width(), size.height()); - } - else - { - size = packer.images.at(i).crop.size(); - crop = packer.images.at(i).crop; - } - if(packer.images.at(i).rotated) - { - size.transpose(); - crop = QRect(crop.y(), crop.x(), crop.height(), crop.width()); - } - out << ((static_cast(packer.images.at(i).id))->listItem)->text() - << - "\t" << - pos.x() << "\t" << - pos.y() << "\t" << - crop.width() << "\t" << - crop.height() << "\t" << - crop.x() << "\t" << - crop.y() << "\t" << - sizeOrig.width() << "\t" << - sizeOrig.height() << "\t" << - (packer.images.at(i).rotated ? "r" : "") << "\n"; - } - } + writeMetadataFile(textures, j); } } for(i = 0; i < packer.images.size(); i++) @@ -415,7 +387,7 @@ void MainWindow::packerUpdate() } imgdirFile += "."; imgdirFile += outFormat.toLower(); - if(outFormat == "JPG") + if (outFormat == "JPG") { textures.at(i).save(imgdirFile, format, 100); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 5725def..aaa1e90 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -31,6 +31,7 @@ Q_OBJECT private: void recurseDirectory(const QString &dir); + void writeMetadataFile(const QList &images, int imageIndex); Ui::MainWindow *ui; QStringList imageExtensions; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 714d6dc..a9ab8f0 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 821 - 698 + 960 + 780 @@ -21,7 +21,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -111,8 +120,8 @@ 0 0 - 325 - 666 + 380 + 737 @@ -158,15 +167,15 @@ - + 0 0 - 170 - 270 + 278 + 309 @@ -182,15 +191,33 @@ 1 + + + 0 + 0 + + + + 2 + + + 2 + - + 0 0 + + + 0 + 100 + + QAbstractItemView::ExtendedSelection @@ -329,16 +356,22 @@ - 242 - 380 + 298 + 400 9999 - 380 + 400 + + + 75 + true + + false @@ -361,7 +394,19 @@ 0 + + + 50 + false + + + + 2 + + + 2 + @@ -688,14 +733,8 @@ - - - 0 - - - 0 - - + + true @@ -714,20 +753,11 @@ - - - - - 60 - 16777215 - - - - 1 - - - - + + + + + @@ -746,7 +776,20 @@ - + + + + Border around each image. If atlas will be saled (OpenGL), keep at least 1 px border. + + + Border + + + Qt::AlignCenter + + + + @@ -762,16 +805,20 @@ - - - - Border around each image. If atlas will be saled (OpenGL), keep at least 1 px border. - - - Border + + + + + + + + + 60 + 16777215 + - - Qt::AlignCenter + + 1 @@ -1010,7 +1057,7 @@ - 176 + 221 321 diff --git a/src/tile.pro b/src/tile.pro index 43ad93b..f379b62 100644 --- a/src/tile.pro +++ b/src/tile.pro @@ -26,13 +26,18 @@ SOURCES += main.cpp\ imagecrop.cpp \ imagesort.cpp \ maxrects.cpp \ - commandlinehandler.cpp + commandlinehandler.cpp \ + atlasmetadatawriter.cpp \ + utils.cpp HEADERS += mainwindow.h \ view.h \ imagepacker.h \ maxrects.h \ - commandlinehandler.h + commandlinehandler.h \ + imetadatawriter.h \ + atlasmetadatawriter.h \ + utils.h FORMS += mainwindow.ui QMAKE_CXXFLAGS += -Wextra -Werror diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..f6670a1 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.h" +#include "imagepacker.h" +#include "imetadatawriter.h" +#include +#include +#include + +bool Utils::exportMetadata(const QString &outputFile, const QString &imgFile, const QSize &imgSize, int textureId, + const QStringList &frameNames, const ImagePacker &packer, IMetadataWriter &writer) +{ + writer.WriteTexture(imgFile, imgSize); + for(int i = 0; i < packer.images.size(); i++) + { + if(packer.images.at(i).textureId != textureId) + { + continue; + } + QPoint pos(packer.images.at(i).pos.x() + packer.border.l + packer.extrude, + packer.images.at(i).pos.y() + packer.border.t + packer.extrude); + QSize size, origSize; + QRect crop; + origSize = packer.images.at(i).size; + if(!packer.cropThreshold) + { + size = packer.images.at(i).size; + crop = QRect(0, 0, size.width(), size.height()); + } + else + { + size = packer.images.at(i).crop.size(); + crop = packer.images.at(i).crop; + } + if(packer.images.at(i).rotated) + { + size.transpose(); + crop = QRect(crop.y(), crop.x(), crop.height(), crop.width()); + } + QString frameName = frameNames[i]; + writer.WriteFrame(frameName, pos, crop, origSize, packer.images.at(i).rotated); + } + + QFile positionsFile(outputFile); + if(!positionsFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + return false; + } + QTextStream out(&positionsFile); + out << writer.ToBytes(); + + return true; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..592d6b6 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,19 @@ +#ifndef METADATAEXPORTER_H +#define METADATAEXPORTER_H + +#include +#include + +class ImagePacker; +class IMetadataWriter; + +class Utils +{ +public: + Utils() = delete; + + static bool exportMetadata(const QString &outputFile, const QString &imgFile, const QSize &imgSize, int textureId, + const QStringList &frameNames, const ImagePacker &packer, IMetadataWriter &writer); +}; + +#endif // METADATAEXPORTER_H From 2fbaf749dba7f0d7eeb7b6e622ef5749e94a73ed Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Sun, 22 May 2016 10:58:44 +0300 Subject: [PATCH 3/7] Added cocos2d-x plist (XML) export format. --- src/cocosmetadatawriter.cpp | 132 ++++++++++++++++++++++++++++++++++++ src/cocosmetadatawriter.h | 43 ++++++++++++ src/mainwindow.cpp | 37 +++++----- src/mainwindow.ui | 14 ++-- src/tile.pro | 8 ++- src/utils.cpp | 36 ++++++++++ src/utils.h | 12 +++- 7 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 src/cocosmetadatawriter.cpp create mode 100644 src/cocosmetadatawriter.h diff --git a/src/cocosmetadatawriter.cpp b/src/cocosmetadatawriter.cpp new file mode 100644 index 0000000..a6aaada --- /dev/null +++ b/src/cocosmetadatawriter.cpp @@ -0,0 +1,132 @@ +#include "cocosmetadatawriter.h" +#include + +namespace +{ +const char PLIST_DTD[] = R"***()***"; + +void writeKey(QXmlStreamWriter &xml, const QString &key) +{ + xml.writeTextElement("key", key); +} + +void writeInteger(QXmlStreamWriter &xml, int value) +{ + xml.writeTextElement("integer", QString::number(value)); +} + +void writeReal(QXmlStreamWriter &xml, double value) +{ + xml.writeTextElement("real", QString::number(value)); +} + +void writeString(QXmlStreamWriter &xml, const QString &value) +{ + xml.writeTextElement("string", value); +} + +void writeSize(QXmlStreamWriter &xml, const QSize &value) +{ + char buffer[80]; + sprintf(buffer, "{%d,%d}", value.width(), value.height()); + writeString(xml, buffer); +} +} + +CocosMetadataWriter::CocosMetadataWriter() +{ +} + +void CocosMetadataWriter::WriteTexture(const QString &path, const QSize &size) +{ + m_meta.texturePath = path; + m_meta.textureSize = size; +} + +void CocosMetadataWriter::WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, const QSize &origSize, bool rotated) +{ + FrameData data; + data.name = name; + data.pos = pos; + data.crop = crop; + data.origSize = origSize; + data.rotated = rotated; + m_frames << data; +} + +QByteArray CocosMetadataWriter::ToBytes() const +{ + QByteArray content; + QXmlStreamWriter xml(&content); + xml.setAutoFormatting(true); + xml.setAutoFormattingIndent(-1); + + xml.writeStartDocument("1.0"); + xml.writeDTD(PLIST_DTD); + xml.writeStartElement("plist"); + xml.writeAttribute("version", "1.0"); + xml.writeStartElement("dict"); + writeFrames(xml); + writeMetadata(xml); + writeTexture(xml); + xml.writeEndElement(); // dict + xml.writeEndElement(); // plist + xml.writeEndDocument(); + + return content; +} + +void CocosMetadataWriter::writeFrames(QXmlStreamWriter &xml) const +{ + writeKey(xml, "frames"); + xml.writeStartElement("dict"); + for (const FrameData &frame : m_frames) + { + writeKey(xml, frame.name); + xml.writeStartElement("dict"); + writeKey(xml, "width"); + writeInteger(xml, frame.crop.width()); + writeKey(xml, "height"); + writeInteger(xml, frame.crop.height()); + writeKey(xml, "originalWidth"); + writeInteger(xml, frame.origSize.width()); + writeKey(xml, "originalHeight"); + writeInteger(xml, frame.origSize.height()); + writeKey(xml, "x"); + writeInteger(xml, frame.pos.x()); + writeKey(xml, "y"); + writeInteger(xml, frame.pos.y()); + writeKey(xml, "offsetX"); + writeReal(xml, frame.crop.x()); + writeKey(xml, "offsetY"); + writeReal(xml, frame.crop.y()); + xml.writeEndElement(); // dict + } + xml.writeEndElement(); // dict +} + +void CocosMetadataWriter::writeMetadata(QXmlStreamWriter &xml) const +{ + writeKey(xml, "metadata"); + xml.writeStartElement("dict"); + writeKey(xml, "format"); + writeInteger(xml, 0); + writeKey(xml, "textureFileName"); + writeString(xml, m_meta.texturePath); + writeKey(xml, "realTextureFileName"); + writeString(xml, m_meta.texturePath); + writeKey(xml, "size"); + writeSize(xml, m_meta.textureSize); + xml.writeEndElement(); // dict +} + +void CocosMetadataWriter::writeTexture(QXmlStreamWriter &xml) const +{ + writeKey(xml, "texture"); + xml.writeStartElement("dict"); + writeKey(xml, "width"); + writeInteger(xml, m_meta.textureSize.width()); + writeKey(xml, "height"); + writeInteger(xml, m_meta.textureSize.height()); + xml.writeEndElement(); // dict +} diff --git a/src/cocosmetadatawriter.h b/src/cocosmetadatawriter.h new file mode 100644 index 0000000..1b7e07e --- /dev/null +++ b/src/cocosmetadatawriter.h @@ -0,0 +1,43 @@ +#ifndef COCOSMETADATAWRITER_H +#define COCOSMETADATAWRITER_H + +#include "imetadatawriter.h" +#include +QT_BEGIN_NAMESPACE +class QXmlStreamWriter; +QT_END_NAMESPACE + +class CocosMetadataWriter : public IMetadataWriter +{ +public: + CocosMetadataWriter(); + + void WriteTexture(const QString &path, const QSize &size) override; + void WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, const QSize &origSize, bool rotated) override; + QByteArray ToBytes() const override; + +private: + struct FrameData + { + QString name; + QPoint pos; + QRect crop; + QSize origSize; + bool rotated = false; + }; + + struct Metadata + { + QString texturePath; + QSize textureSize; + }; + + void writeFrames(QXmlStreamWriter &xml)const; + void writeMetadata(QXmlStreamWriter &xml)const; + void writeTexture(QXmlStreamWriter &xml)const; + + QList m_frames; + Metadata m_meta; +}; + +#endif // COCOSMETADATAWRITER_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7fecfbc..4c9c1ac 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,7 +1,7 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "atlasmetadatawriter.h" #include "utils.h" +#include #include #include #include @@ -100,9 +100,10 @@ void MainWindow::writeMetadataFile(const QList &images, int imageIndex) frameNames << ((static_cast(packer.images.at(i).id))->listItem)->text(); } - QString outDir = ui->outDir->text(); - QString outFile = ui->outFile->text(); - QString outFormat = ui->outFormat->currentText(); + const QString outDir = ui->outDir->text(); + const QString outFile = ui->outFile->text(); + const QString imageFormat = ui->imageFormat->currentText(); + const OutFormat outFormat = static_cast(ui->outFormat->currentIndex()); QString outputFile = outDir; outputFile += QDir::separator(); @@ -111,17 +112,17 @@ void MainWindow::writeMetadataFile(const QList &images, int imageIndex) { outputFile += QString("_") + QString::number(imageIndex + 1); } - outputFile += ".atlas"; + outputFile += Utils::getFormatExtension(outFormat); QString imgFile = outFile; if(images.count() > 1) { imgFile += QString("_") + QString::number(imageIndex + 1); } imgFile += "."; - imgFile += outFormat.toLower(); + imgFile += imageFormat.toLower(); - AtlasMetadataWriter writer; - if(!Utils::exportMetadata(outputFile, imgFile, images[imageIndex].size(), imageIndex, frameNames, packer, writer)) + auto pWriter = Utils::makeMetadataWriter(outFormat); + if(!Utils::exportMetadata(outputFile, imgFile, images[imageIndex].size(), imageIndex, frameNames, packer, *pWriter)) { QMessageBox::critical(0, tr("Error"), tr("Cannot create file ") + outputFile); } @@ -179,7 +180,6 @@ void MainWindow::deleteSelectedTiles() void MainWindow::packerUpdate() { - int i; quint64 area = 0; packer.sortOrder = ui->sortOrder->currentIndex(); packer.border.t = ui->borderTop->value(); @@ -197,14 +197,13 @@ void MainWindow::packerUpdate() int heuristic = ui->comboHeuristic->currentIndex(); QString outDir = ui->outDir->text(); QString outFile = ui->outFile->text(); - QString outFormat = ui->outFormat->currentText(); + QString imageFormat = ui->imageFormat->currentText(); bool previewWithImages = ui->previewWithImages->isChecked(); - packer.pack(heuristic, textureWidth, textureHeight); QList textures; - for(i = 0; i < packer.bins.size(); i++) + for(int i = 0; i < packer.bins.size(); i++) { QImage texture(packer.bins.at(i).width(), packer.bins.at(i).height(), QImage::Format_ARGB32); @@ -218,7 +217,7 @@ void MainWindow::packerUpdate() writeMetadataFile(textures, j); } } - for(i = 0; i < packer.images.size(); i++) + for(int i = 0; i < packer.images.size(); i++) { if(packer.images.at(i).pos == QPoint(999999, 999999)) { @@ -358,10 +357,10 @@ void MainWindow::packerUpdate() } for(int i = 0; i < textures.count(); i++) { - area += textures.at(i).width() * textures.at(i).height(); + area += quint64(textures.at(i).width()) * quint64(textures.at(i).height()); } - float percent = (((float)packer.area / (float)area) * 100.0f); - float percent2 = (float)(((float)packer.neededArea / (float)area) * 100.0f); + float percent = 100.0f * float(packer.area) / float(area); + float percent2 = 100.0f * float(packer.neededArea) / float(area) * 100.0f; ui->preview->setText(tr("Preview: ") + QString::number(percent) + QString("% filled, ") + (packer.missingImages == 0 ? QString::number(packer.missingImages) + @@ -374,7 +373,7 @@ void MainWindow::packerUpdate() area * 4 / 1024)); if(exporting) { - const char *format = qPrintable(outFormat); + const char *format = qPrintable(imageFormat); for(int i = 0; i < textures.count(); i++) { QString imgdirFile; @@ -386,8 +385,8 @@ void MainWindow::packerUpdate() imgdirFile += QString("_") + QString::number(i + 1); } imgdirFile += "."; - imgdirFile += outFormat.toLower(); - if (outFormat == "JPG") + imgdirFile += imageFormat.toLower(); + if (imageFormat == "JPG") { textures.at(i).save(imgdirFile, format, 100); } diff --git a/src/mainwindow.ui b/src/mainwindow.ui index a9ab8f0..d665e19 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1111,7 +1111,7 @@ - + PNG @@ -1141,13 +1141,15 @@ - - - false - + + + + Cheetah (text) + + - Cheetah + Cocos2d-x (XML) diff --git a/src/tile.pro b/src/tile.pro index f379b62..7693900 100644 --- a/src/tile.pro +++ b/src/tile.pro @@ -8,7 +8,7 @@ QT += core gui TARGET = cheetah-texture-packer -CONFIG += c++11 +CONFIG += c++14 QT_VERSION=$$[QT_VERSION] @@ -28,7 +28,8 @@ SOURCES += main.cpp\ maxrects.cpp \ commandlinehandler.cpp \ atlasmetadatawriter.cpp \ - utils.cpp + utils.cpp \ + cocosmetadatawriter.cpp HEADERS += mainwindow.h \ view.h \ @@ -37,7 +38,8 @@ HEADERS += mainwindow.h \ commandlinehandler.h \ imetadatawriter.h \ atlasmetadatawriter.h \ - utils.h + utils.h \ + cocosmetadatawriter.h FORMS += mainwindow.ui QMAKE_CXXFLAGS += -Wextra -Werror diff --git a/src/utils.cpp b/src/utils.cpp index f6670a1..f2f8516 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,9 +1,45 @@ #include "utils.h" #include "imagepacker.h" #include "imetadatawriter.h" +#include "atlasmetadatawriter.h" +#include "cocosmetadatawriter.h" #include #include #include +#include + +std::unique_ptr Utils::makeMetadataWriter(OutFormat outFormat) +{ + switch (outFormat) + { + case OutFormat::CHEETAH: + return std::make_unique(); + case OutFormat::COCOS2DX: + return std::make_unique(); + case OutFormat::CSS_SPRITE: + // FIXME: not implemented + break; + } + Q_ASSERT(false); // unhandled enum option. + QMessageBox::critical(0, QObject::tr("Internal Error"), + QObject::tr("Exporting to selected format not implemented. Falling back to Cheetah format")); + return std::make_unique(); +} + +QString Utils::getFormatExtension(OutFormat outFormat) +{ + switch (outFormat) + { + case OutFormat::CHEETAH: + return ".atlas"; + case OutFormat::COCOS2DX: + return ".plist"; + case OutFormat::CSS_SPRITE: + return ".css"; + } + Q_ASSERT(false); // unhandled enum option. + return ".txt"; +} bool Utils::exportMetadata(const QString &outputFile, const QString &imgFile, const QSize &imgSize, int textureId, const QStringList &frameNames, const ImagePacker &packer, IMetadataWriter &writer) diff --git a/src/utils.h b/src/utils.h index 592d6b6..fddadb1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -3,15 +3,25 @@ #include #include +#include +#include "imetadatawriter.h" class ImagePacker; -class IMetadataWriter; + +enum class OutFormat +{ + CHEETAH = 0, + COCOS2DX = 1, + CSS_SPRITE = 2 +}; class Utils { public: Utils() = delete; + static std::unique_ptr makeMetadataWriter(OutFormat outFormat); + static QString getFormatExtension(OutFormat outFormat); static bool exportMetadata(const QString &outputFile, const QString &imgFile, const QSize &imgSize, int textureId, const QStringList &frameNames, const ImagePacker &packer, IMetadataWriter &writer); }; From ddad0dbcf62f6758ee4f640072a4a8aed7e36f18 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Sun, 22 May 2016 12:52:39 +0300 Subject: [PATCH 4/7] Implemented CSS Sprite export (no options provided for CSS format) --- src/cssmetadatawriter.cpp | 56 +++++++++++++++++++++++++++++++++++++++ src/cssmetadatawriter.h | 22 +++++++++++++++ src/tile.pro | 6 +++-- src/utils.cpp | 4 +-- 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/cssmetadatawriter.cpp create mode 100644 src/cssmetadatawriter.h diff --git a/src/cssmetadatawriter.cpp b/src/cssmetadatawriter.cpp new file mode 100644 index 0000000..da0183b --- /dev/null +++ b/src/cssmetadatawriter.cpp @@ -0,0 +1,56 @@ +#include "cssmetadatawriter.h" +#include + +CSSMetadataWriter::CSSMetadataWriter() + : m_stream(&m_bytes) +{ +} + + +void CSSMetadataWriter::WriteTexture(const QString &path, const QSize &size) +{ + m_textureFilename = QFileInfo(path).fileName(); + (void)size; +} + +void CSSMetadataWriter::WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, const QSize &origSize, bool rotated) +{ + (void)crop; + (void)origSize; + (void)rotated; + QString className = QFileInfo(name).baseName(); + const QLatin1Char underscore('_'); + for (QChar ch : className) + { + if (!ch.isLetterOrNumber()) + { + ch = underscore; + } + } + + m_stream << "." << className << "\n{\n" + << " width: " << coordinateToString(crop.width()) << ";\n" + << " height: " << coordinateToString(crop.height()) << ";\n" + << " background: url('" << m_textureFilename << "') " << positionToString(pos) << ";\n" + << "}\n"; +} + +QByteArray CSSMetadataWriter::ToBytes() const +{ + m_stream.flush(); + return m_bytes; +} + +QString CSSMetadataWriter::positionToString(const QPoint &pos) +{ + return coordinateToString(pos.x()) + " " + coordinateToString(pos.y()); +} + +QString CSSMetadataWriter::coordinateToString(const int value) +{ + if (value == 0) + { + return "0"; + } + return QString::number(value) + "px"; +} diff --git a/src/cssmetadatawriter.h b/src/cssmetadatawriter.h new file mode 100644 index 0000000..a5a3d5f --- /dev/null +++ b/src/cssmetadatawriter.h @@ -0,0 +1,22 @@ +#pragma once + +#include "imetadatawriter.h" +#include + +class CSSMetadataWriter : public IMetadataWriter +{ +public: + CSSMetadataWriter(); + + void WriteTexture(const QString &path, const QSize &size) override; + void WriteFrame(const QString &name, const QPoint &pos, const QRect &crop, const QSize &origSize, bool rotated) override; + QByteArray ToBytes() const override; + +private: + static QString positionToString(const QPoint &pos); + static QString coordinateToString(const int value); + + QString m_textureFilename; + QByteArray m_bytes; + mutable QTextStream m_stream; +}; diff --git a/src/tile.pro b/src/tile.pro index 7693900..724c7b6 100644 --- a/src/tile.pro +++ b/src/tile.pro @@ -29,7 +29,8 @@ SOURCES += main.cpp\ commandlinehandler.cpp \ atlasmetadatawriter.cpp \ utils.cpp \ - cocosmetadatawriter.cpp + cocosmetadatawriter.cpp \ + cssmetadatawriter.cpp HEADERS += mainwindow.h \ view.h \ @@ -39,7 +40,8 @@ HEADERS += mainwindow.h \ imetadatawriter.h \ atlasmetadatawriter.h \ utils.h \ - cocosmetadatawriter.h + cocosmetadatawriter.h \ + cssmetadatawriter.h FORMS += mainwindow.ui QMAKE_CXXFLAGS += -Wextra -Werror diff --git a/src/utils.cpp b/src/utils.cpp index f2f8516..30bd228 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -3,6 +3,7 @@ #include "imetadatawriter.h" #include "atlasmetadatawriter.h" #include "cocosmetadatawriter.h" +#include "cssmetadatawriter.h" #include #include #include @@ -17,8 +18,7 @@ std::unique_ptr Utils::makeMetadataWriter(OutFormat outFormat) case OutFormat::COCOS2DX: return std::make_unique(); case OutFormat::CSS_SPRITE: - // FIXME: not implemented - break; + return std::make_unique(); } Q_ASSERT(false); // unhandled enum option. QMessageBox::critical(0, QObject::tr("Internal Error"), From 221ec86325b454ab3e3ab4912cfccecc089ba82e Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Sun, 22 May 2016 13:48:31 +0300 Subject: [PATCH 5/7] Implemented platform-dependent exporting to webp, tiff, dds formats. --- src/mainwindow.cpp | 40 +++++++++++++++++++++++----------------- src/mainwindow.h | 43 +++++++++++++++++++++++-------------------- src/mainwindow.ui | 17 +---------------- src/utils.cpp | 32 ++++++++++++++++++++++++++++++-- src/utils.h | 2 ++ 5 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4c9c1ac..f11dbf6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -14,23 +14,7 @@ MainWindow::MainWindow(QWidget *parent) { exporting = false; ui->setupUi(this); - connect(this, SIGNAL(renderedImage(QList)), ui->widget, - SLOT(updatePixmap(QList))); - ui->outDir->setText(QDir::homePath()); - exporting = false; - ui->widget->scaleBox = ui->scale; - tabifyDockWidget(ui->dockPreferences, ui->dockExport); - ui->dockPreferences->raise(); - - pattern = QPixmap(20, 20); - QPainter painter(&pattern); - const int BRIGHT = 190; - const int SHADOW = 150; - painter.fillRect(0, 0, 10, 10, QColor(SHADOW, SHADOW, SHADOW)); - painter.fillRect(10, 0, 10, 10, QColor(BRIGHT, BRIGHT, BRIGHT)); - painter.fillRect(10, 10, 10, 10, QColor(SHADOW, SHADOW, SHADOW)); - painter.fillRect(0, 10, 10, 10, QColor(BRIGHT, BRIGHT, BRIGHT)); - setAcceptDrops(true); + customizeUI(); } MainWindow::~MainWindow() @@ -92,6 +76,28 @@ void MainWindow::recurseDirectory(const QString &dir) } } +void MainWindow::customizeUI() +{ + connect(this, SIGNAL(renderedImage(QList)), ui->widget, + SLOT(updatePixmap(QList))); + ui->outDir->setText(QDir::homePath()); + exporting = false; + ui->widget->scaleBox = ui->scale; + tabifyDockWidget(ui->dockPreferences, ui->dockExport); + ui->dockPreferences->raise(); + ui->imageFormat->addItems(Utils::GetWritableImageFormats()); + setAcceptDrops(true); + + pattern = QPixmap(20, 20); + QPainter painter(&pattern); + const int BRIGHT = 190; + const int SHADOW = 150; + painter.fillRect(0, 0, 10, 10, QColor(SHADOW, SHADOW, SHADOW)); + painter.fillRect(10, 0, 10, 10, QColor(BRIGHT, BRIGHT, BRIGHT)); + painter.fillRect(10, 10, 10, 10, QColor(SHADOW, SHADOW, SHADOW)); + painter.fillRect(0, 10, 10, 10, QColor(BRIGHT, BRIGHT, BRIGHT)); +} + void MainWindow::writeMetadataFile(const QList &images, int imageIndex) { QStringList frameNames; diff --git a/src/mainwindow.h b/src/mainwindow.h index aaa1e90..d4b106b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -29,8 +29,31 @@ Q_OBJECT explicit MainWindow(QWidget *parent = 0); ~MainWindow(); +public slots: + void addTiles(); + void deleteSelectedTiles(); + void packerUpdate(); + void updateAuto(); + void setTextureSize2048(); + void setTextureSize256(); + void setTextureSize512(); + void setTextureSize1024(); + void updateAplhaThreshold(); + void getFolder(); + void exportImage(); + void swapSizes(); + void clearTiles(); + +signals: + void renderedImage(const QList &image); + +protected: + void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + private: void recurseDirectory(const QString &dir); + void customizeUI(); void writeMetadataFile(const QList &images, int imageIndex); Ui::MainWindow *ui; @@ -48,26 +71,6 @@ Q_OBJECT QListWidgetItem *listItem; QString path; }; - -protected: - void dropEvent(QDropEvent *event); - void dragEnterEvent(QDragEnterEvent *event); -signals: - void renderedImage(const QList &image); -public slots: - void addTiles(); - void deleteSelectedTiles(); - void packerUpdate(); - void updateAuto(); - void setTextureSize2048(); - void setTextureSize256(); - void setTextureSize512(); - void setTextureSize1024(); - void updateAplhaThreshold(); - void getFolder(); - void exportImage(); - void swapSizes(); - void clearTiles(); }; #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d665e19..fd38791 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1112,21 +1112,6 @@ - - - PNG - - - - - JPG - - - - - BMP - - @@ -1154,7 +1139,7 @@ - CSS + CSS Sprite diff --git a/src/utils.cpp b/src/utils.cpp index 30bd228..ec1d3b9 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -8,6 +8,36 @@ #include #include #include +#include +#include +#include + + +namespace +{ +QStringList FilterAcceptableFormats(const QList &supportedFormats) +{ + QStringList formats = { "png", "jpg", "bmp", "dds", "tiff", "webp" }; + + auto newEnd = std::remove_if(formats.begin(), formats.end(), [&](const QString &format) { + return !supportedFormats.contains(format.toLatin1()); + }); + formats.erase(newEnd, formats.end()); + + return formats; +} +} + + +QStringList Utils::GetReadableImageFormats() +{ + return FilterAcceptableFormats(QImageReader::supportedImageFormats()); +} + +QStringList Utils::GetWritableImageFormats() +{ + return FilterAcceptableFormats(QImageWriter::supportedImageFormats()); +} std::unique_ptr Utils::makeMetadataWriter(OutFormat outFormat) { @@ -21,8 +51,6 @@ std::unique_ptr Utils::makeMetadataWriter(OutFormat outFormat) return std::make_unique(); } Q_ASSERT(false); // unhandled enum option. - QMessageBox::critical(0, QObject::tr("Internal Error"), - QObject::tr("Exporting to selected format not implemented. Falling back to Cheetah format")); return std::make_unique(); } diff --git a/src/utils.h b/src/utils.h index fddadb1..178f203 100644 --- a/src/utils.h +++ b/src/utils.h @@ -20,6 +20,8 @@ class Utils public: Utils() = delete; + static QStringList GetReadableImageFormats(); + static QStringList GetWritableImageFormats(); static std::unique_ptr makeMetadataWriter(OutFormat outFormat); static QString getFormatExtension(OutFormat outFormat); static bool exportMetadata(const QString &outputFile, const QString &imgFile, const QSize &imgSize, int textureId, From d3fc07009095c3782ec2bec505382633d8ef79e9 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Sun, 22 May 2016 14:19:45 +0300 Subject: [PATCH 6/7] Removed Fusion style, added TGA (platform-optional) image format. --- src/main.cpp | 3 --- src/utils.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 17d28fe..b84b7d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,6 @@ #include "mainwindow.h" #include #include -#include #include "commandlinehandler.h" int main(int argc, char *argv[]) @@ -21,8 +20,6 @@ int main(int argc, char *argv[]) return doCommandLineJobs(a.arguments()); } - QApplication::setStyle(QStyleFactory::create("Fusion")); - QTranslator myTranslator; myTranslator.load("tile_" + QLocale::system().name(), "qm"); a.installTranslator(&myTranslator); diff --git a/src/utils.cpp b/src/utils.cpp index ec1d3b9..9dee53c 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -17,7 +17,7 @@ namespace { QStringList FilterAcceptableFormats(const QList &supportedFormats) { - QStringList formats = { "png", "jpg", "bmp", "dds", "tiff", "webp" }; + QStringList formats = { "png", "jpg", "bmp", "dds", "webp", "tiff", "tga" }; auto newEnd = std::remove_if(formats.begin(), formats.end(), [&](const QString &format) { return !supportedFormats.contains(format.toLatin1()); From 7873b9725fbfaa52732a23df59e31be205755cd6 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Sat, 5 Nov 2016 14:57:38 +0300 Subject: [PATCH 7/7] =?UTF-8?q?=D0=AD=D0=BA=D1=81=D0=BF=D0=BE=D1=80=D1=82?= =?UTF-8?q?=20=D0=B2=20cocos2d-x:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D1=8C=20=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B0=20rotated=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B2=D1=91=D1=80=D0=BD=D1=83=D1=82=D1=8B=D1=85=20=D1=84?= =?UTF-8?q?=D1=80=D0=B5=D0=B9=D0=BC=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cocosmetadatawriter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cocosmetadatawriter.cpp b/src/cocosmetadatawriter.cpp index a6aaada..ccd9ac7 100644 --- a/src/cocosmetadatawriter.cpp +++ b/src/cocosmetadatawriter.cpp @@ -25,6 +25,11 @@ void writeString(QXmlStreamWriter &xml, const QString &value) xml.writeTextElement("string", value); } +void writeBool(QXmlStreamWriter &xml, bool value) +{ + xml.writeEmptyElement(value ? "true" : "false"); +} + void writeSize(QXmlStreamWriter &xml, const QSize &value) { char buffer[80]; @@ -100,6 +105,8 @@ void CocosMetadataWriter::writeFrames(QXmlStreamWriter &xml) const writeReal(xml, frame.crop.x()); writeKey(xml, "offsetY"); writeReal(xml, frame.crop.y()); + writeKey(xml, "rotated"); + writeBool(xml, frame.rotated); xml.writeEndElement(); // dict } xml.writeEndElement(); // dict @@ -117,6 +124,7 @@ void CocosMetadataWriter::writeMetadata(QXmlStreamWriter &xml) const writeString(xml, m_meta.texturePath); writeKey(xml, "size"); writeSize(xml, m_meta.textureSize); + xml.writeEndElement(); // dict }