Merge branch 'css-export'

This commit is contained in:
David Capello 2021-01-05 15:03:09 -03:00
commit 090743155b
11 changed files with 353 additions and 1 deletions

View File

@ -344,6 +344,12 @@
<option id="bits_per_pixel" type="int" default="0" />
<option id="compress" type="bool" default="true" />
</section>
<section id="css">
<option id="show_alert" type="bool" default="true" />
<option id="pixel_scale" type="int" default="1" />
<option id="with_vars" type="bool" default="false" />
<option id="generate_html" type="bool" default="false" />
</section>
<section id="webp">
<option id="show_alert" type="bool" default="true" />
<option id="loop" type="bool" default="true" />

View File

@ -1474,6 +1474,12 @@ title = TGA Options
bits_per_pixel = Bits Per Pixel
compress = Compress
[css_options]
title = CSS Options
pixel_scale = Pixel Scale
with_vars = Use CSS3 Variables
generate_html = Generate Sample HTML File
[timeline_conf]
position = Position:
left = &Left

View File

@ -0,0 +1,25 @@
<!-- Aseprite -->
<!-- Copyright (C) 2018 by Igara Studio S.A. -->
<gui>
<window id="css_options" text="@.title">
<grid columns="2">
<label text="@.pixel_scale" />
<expr id="pixel_scale" magnet="true" cell_align="horizontal"/>
<check text="@.with_vars" id="with_vars" cell_hspan="2" />
<check text="@.generate_html" id="generate_html" cell_hspan="2" />
<separator horizontal="true" cell_hspan="2" />
<hbox cell_hspan="2">
<check text="@general.dont_show" id="dont_show" tooltip="@general.dont_show_tooltip" />
<boxfiller />
</hbox>
<box horizontal="true" homogeneous="true" cell_hspan="2" cell_align="right">
<button text="@general.ok" closewindow="true" id="ok" magnet="true" minwidth="60" />
<button text="@general.cancel" closewindow="true" />
</box>
</grid>
</window>
</gui>

View File

@ -440,6 +440,7 @@
pref="scripts.show_run_script_alert" />
<hbox>
<label text="@.image_format_alerts" />
<check id="css_options_alert" text="!css" pref="css.show_alert" />
<check id="gif_options_alert" text="!gif" pref="gif.show_alert" />
<check id="jpeg_options_alert" text="!jpeg" pref="jpeg.show_alert" />
<check id="svg_options_alert" text="!svg" pref="svg.show_alert" />

View File

@ -125,6 +125,7 @@ endif()
set(file_formats
file/ase_format.cpp
file/bmp_format.cpp
file/css_format.cpp
file/fli_format.cpp
file/gif_format.cpp
file/ico_format.cpp

View File

@ -987,6 +987,7 @@ private:
advancedModeAlert()->resetWithDefaultValue();
invalidFgBgColorAlert()->resetWithDefaultValue();
runScriptAlert()->resetWithDefaultValue();
cssOptionsAlert()->resetWithDefaultValue();
gifOptionsAlert()->resetWithDefaultValue();
jpegOptionsAlert()->resetWithDefaultValue();
svgOptionsAlert()->resetWithDefaultValue();

303
src/app/file/css_format.cpp Normal file
View File

@ -0,0 +1,303 @@
// Aseprite
// Copyright (c) 2018-2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/console.h"
#include "app/context.h"
#include "app/doc.h"
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/format_options.h"
#include "app/pref/preferences.h"
#include "base/convert_to.h"
#include "base/cfile.h"
#include "base/file_handle.h"
#include "base/string.h"
#include "doc/doc.h"
#include "ui/window.h"
#include "css_options.xml.h"
namespace app {
using namespace base;
class CssFormat : public FileFormat {
class CssOptions : public FormatOptions {
public:
CssOptions() : pixelScale(1), gutterSize(0),
generateHtml(false),
withVars(false) { }
int pixelScale;
int gutterSize;
bool generateHtml;
bool withVars;
};
const char* onGetName() const override {
return "css";
}
void onGetExtensions(base::paths& exts) const override {
exts.push_back("css");
}
dio::FileFormat onGetDioFormat() const override {
return dio::FileFormat::CSS_STYLE;
}
int onGetFlags() const override {
return
FILE_SUPPORT_SAVE |
FILE_SUPPORT_RGB |
FILE_SUPPORT_RGBA |
FILE_SUPPORT_GRAY |
FILE_SUPPORT_GRAYA |
FILE_SUPPORT_INDEXED |
FILE_SUPPORT_SEQUENCES |
FILE_SUPPORT_GET_FORMAT_OPTIONS |
FILE_SUPPORT_PALETTE_WITH_ALPHA;
}
bool onLoad(FileOp* fop) override;
#ifdef ENABLE_SAVE
bool onSave(FileOp* fop) override;
#endif
FormatOptionsPtr onAskUserForFormatOptions(FileOp* fop) override;
};
FileFormat *CreateCssFormat()
{
return new CssFormat;
}
bool CssFormat::onLoad(FileOp* fop)
{
return false;
}
#ifdef ENABLE_SAVE
bool CssFormat::onSave(FileOp* fop)
{
const Image* image = fop->sequenceImage();
int x, y, c, r, g, b, a, alpha;
const auto css_options = std::static_pointer_cast<CssOptions>(fop->formatOptions());
FileHandle handle(open_file_with_exception_sync_on_close(fop->filename(), "wb"));
FILE* f = handle.get();
auto print_color = [f](int r, int g, int b, int a) {
if (a == 255) {
fprintf(f, "#%02X%02X%02X", r, g, b);
}
else {
fprintf(f, "rgba(%d, %d, %d, %d)", r, g, b, a);
}
};
auto print_shadow_color = [f, css_options, print_color](int x, int y, int r,
int g, int b, int a,
bool comma = true) {
fprintf(f, comma?",\n":"\n");
if (css_options->withVars) {
fprintf(f, "\tcalc(%d*var(--shadow-mult)) calc(%d*var(--shadow-mult)) var(--blur) var(--spread) ",
x, y);
}
else {
int x_loc = x * (css_options->pixelScale + css_options->gutterSize);
int y_loc = y * (css_options->pixelScale + css_options->gutterSize);
fprintf(f, "%dpx %dpx ", x_loc, y_loc);
}
print_color(r, g, b, a);
};
auto print_shadow_index = [f, css_options](int x, int y, int i, bool comma=true) {
fprintf(f, comma?",\n":"\n");
fprintf(f, "\tcalc(%d*var(--shadow-mult)) calc(%d*var(--shadow-mult)) var(--blur) var(--spread) var(--color-%d)",
x, y, i);
};
if (css_options->withVars) {
fprintf(f, ":root {\n"
"\t--blur: 0px;\n"
"\t--spread: 0px;\n"
"\t--pixel-size: %dpx;\n"
"\t--gutter-size: %dpx;\n",
css_options->pixelScale,
css_options->gutterSize);
fprintf(f, "\t--shadow-mult: calc(var(--gutter-size) + var(--pixel-size));\n");
if (image->pixelFormat() == IMAGE_INDEXED) {
for (y = 0; y < 256; y++) {
fop->sequenceGetColor(y, &r, &g, &b);
fop->sequenceGetAlpha(y, &a);
fprintf(f, "\t--color-%d: ", y);
print_color(r, g, b, a);
fprintf(f, ";\n");
}
}
fprintf(f, "}\n\n");
}
fprintf(f, ".pixel-art {\n");
fprintf(f, "\tposition: relative;\n");
fprintf(f, "\ttop: 0;\n");
fprintf(f, "\tleft: 0;\n");
if (css_options->withVars) {
fprintf(f, "\theight: var(--pixel-size);\n");
fprintf(f, "\twidth: var(--pixel-size);\n");
}
else {
fprintf(f, "\theight: %dpx;\n", css_options->pixelScale);
fprintf(f, "\twidth: %dpx;\n", css_options->pixelScale);
}
fprintf(f, "\tbox-shadow:\n");
int num_printed_pixels = 0;
switch (image->pixelFormat()) {
case IMAGE_RGB: {
for (y=0; y<image->height(); y++) {
for (x=0; x<image->width(); x++) {
c = get_pixel_fast<RgbTraits>(image, x, y);
alpha = rgba_geta(c);
if (alpha != 0x00) {
print_shadow_color(x, y, rgba_getr(c), rgba_getg(c), rgba_getb(c),
alpha, num_printed_pixels>0);
num_printed_pixels ++;
}
}
fop->setProgress((float)y / (float)(image->height()));
}
break;
}
case IMAGE_GRAYSCALE: {
for (y=0; y<image->height(); y++) {
for (x=0; x<image->width(); x++) {
c = get_pixel_fast<GrayscaleTraits>(image, x, y);
auto v = graya_getv(c);
alpha = graya_geta(c);
if (alpha != 0x00) {
print_shadow_color(x, y, v, v, v, alpha, num_printed_pixels>0);
num_printed_pixels ++;
}
}
fop->setProgress((float)y / (float)(image->height()));
}
break;
}
case IMAGE_INDEXED: {
unsigned char image_palette[256][4];
for (y=0; !css_options->withVars && y<256; y++) {
fop->sequenceGetColor(y, &r, &g, &b);
image_palette[y][0] = r;
image_palette[y][1] = g;
image_palette[y][2] = b;
fop->sequenceGetAlpha(y, &a);
image_palette[y][3] = a;
}
color_t mask_color = -1;
if (fop->document()->sprite()->backgroundLayer() == NULL ||
!fop->document()->sprite()->backgroundLayer()->isVisible()) {
mask_color = fop->document()->sprite()->transparentColor();
}
for (y=0; y<image->height(); y++) {
for (x=0; x<image->width(); x++) {
c = get_pixel_fast<IndexedTraits>(image, x, y);
if (c != mask_color) {
if (css_options->withVars) {
print_shadow_index(x, y, c, num_printed_pixels>0);
}
else {
print_shadow_color(x, y,
image_palette[c][0] & 0xff,
image_palette[c][1] & 0xff,
image_palette[c][2] & 0xff,
image_palette[c][3] & 0xff,
num_printed_pixels>0);
}
num_printed_pixels ++;
}
}
fop->setProgress((float)y / (float)(image->height()));
}
break;
}
}
fprintf(f, ";\n}\n");
if (ferror(f)) {
fop->setError("Error writing file.\n");
return false;
}
if (css_options->generateHtml) {
std::string html_filepath = fop->filename() + ".html";
FileHandle handle(open_file_with_exception_sync_on_close(html_filepath, "wb"));
FILE* h = handle.get();
fprintf(h,
"<html><head><link rel=\"stylesheet\" media=\"all\" "
"href=\"%s\"></head><body><div "
"class=\"pixel-art\"></div></body></html>",
fop->filename().c_str());
if (ferror(h)) {
fop->setError("Error writing html file.\n");
return false;
}
}
return true;
}
#endif
// Shows the CSS configuration dialog.
FormatOptionsPtr CssFormat::onAskUserForFormatOptions(FileOp* fop)
{
auto opts = fop->formatOptionsOfDocument<CssOptions>();
#ifdef ENABLE_UI
if (fop->context() && fop->context()->isUIAvailable()) {
try {
auto &pref = Preferences::instance();
if (pref.isSet(pref.css.pixelScale))
opts->pixelScale = pref.css.pixelScale();
if (pref.isSet(pref.css.withVars))
opts->withVars = pref.css.withVars();
if (pref.isSet(pref.css.generateHtml))
opts->generateHtml = pref.css.generateHtml();
if (pref.css.showAlert()) {
app::gen::CssOptions win;
win.pixelScale()->setTextf("%d", opts->pixelScale);
win.withVars()->setSelected(opts->withVars);
win.generateHtml()->setSelected(opts->generateHtml);
win.openWindowInForeground();
if (win.closer() == win.ok()) {
pref.css.showAlert(!win.dontShow()->isSelected());
pref.css.pixelScale((int)win.pixelScale()->textInt());
pref.css.withVars(win.withVars()->isSelected());
pref.css.generateHtml(win.generateHtml()->isSelected());
opts->generateHtml = pref.css.generateHtml();
opts->withVars = pref.css.withVars();
opts->pixelScale = pref.css.pixelScale();
}
else {
opts.reset();
}
}
}
catch (std::exception &e) {
Console::showException(e);
return std::shared_ptr<CssOptions>(nullptr);
}
}
#endif
return opts;
}
} // namespace app

View File

@ -22,6 +22,7 @@ namespace app {
extern FileFormat* CreateAseFormat();
extern FileFormat* CreateBmpFormat();
extern FileFormat* CreateCssFormat();
extern FileFormat* CreateFliFormat();
extern FileFormat* CreateGifFormat();
extern FileFormat* CreateIcoFormat();
@ -57,6 +58,7 @@ FileFormatsManager::FileFormatsManager()
// The first format is the default image format in FileSelector
registerFormat(CreateAseFormat());
registerFormat(CreateBmpFormat());
registerFormat(CreateCssFormat());
registerFormat(CreateFliFormat());
registerFormat(CreateGifFormat());
registerFormat(CreateIcoFormat());

View File

@ -73,7 +73,10 @@ FileFormat* CreateSvgFormat()
return new SvgFormat;
}
bool SvgFormat::onLoad(FileOp* fop) { return false;}
bool SvgFormat::onLoad(FileOp* fop)
{
return false;
}
#ifdef ENABLE_SAVE

View File

@ -141,6 +141,9 @@ FileFormat detect_format_by_file_extension(const std::string& filename)
if (ext == "tga")
return FileFormat::TARGA_IMAGE;
if (ext == "css")
return FileFormat::CSS_STYLE;
if (ext == "webp")
return FileFormat::WEBP_ANIMATION;

View File

@ -31,6 +31,7 @@ enum class FileFormat {
SVG_IMAGE,
TARGA_IMAGE,
WEBP_ANIMATION,
CSS_STYLE,
};
} // namespace dio