mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-11 00:40:08 +00:00
Merge branch 'css-export'
This commit is contained in:
commit
090743155b
@ -344,6 +344,12 @@
|
|||||||
<option id="bits_per_pixel" type="int" default="0" />
|
<option id="bits_per_pixel" type="int" default="0" />
|
||||||
<option id="compress" type="bool" default="true" />
|
<option id="compress" type="bool" default="true" />
|
||||||
</section>
|
</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">
|
<section id="webp">
|
||||||
<option id="show_alert" type="bool" default="true" />
|
<option id="show_alert" type="bool" default="true" />
|
||||||
<option id="loop" type="bool" default="true" />
|
<option id="loop" type="bool" default="true" />
|
||||||
|
@ -1474,6 +1474,12 @@ title = TGA Options
|
|||||||
bits_per_pixel = Bits Per Pixel
|
bits_per_pixel = Bits Per Pixel
|
||||||
compress = Compress
|
compress = Compress
|
||||||
|
|
||||||
|
[css_options]
|
||||||
|
title = CSS Options
|
||||||
|
pixel_scale = Pixel Scale
|
||||||
|
with_vars = Use CSS3 Variables
|
||||||
|
generate_html = Generate Sample HTML File
|
||||||
|
|
||||||
[timeline_conf]
|
[timeline_conf]
|
||||||
position = Position:
|
position = Position:
|
||||||
left = &Left
|
left = &Left
|
||||||
|
25
data/widgets/css_options.xml
Normal file
25
data/widgets/css_options.xml
Normal 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>
|
@ -440,6 +440,7 @@
|
|||||||
pref="scripts.show_run_script_alert" />
|
pref="scripts.show_run_script_alert" />
|
||||||
<hbox>
|
<hbox>
|
||||||
<label text="@.image_format_alerts" />
|
<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="gif_options_alert" text="!gif" pref="gif.show_alert" />
|
||||||
<check id="jpeg_options_alert" text="!jpeg" pref="jpeg.show_alert" />
|
<check id="jpeg_options_alert" text="!jpeg" pref="jpeg.show_alert" />
|
||||||
<check id="svg_options_alert" text="!svg" pref="svg.show_alert" />
|
<check id="svg_options_alert" text="!svg" pref="svg.show_alert" />
|
||||||
|
@ -125,6 +125,7 @@ endif()
|
|||||||
set(file_formats
|
set(file_formats
|
||||||
file/ase_format.cpp
|
file/ase_format.cpp
|
||||||
file/bmp_format.cpp
|
file/bmp_format.cpp
|
||||||
|
file/css_format.cpp
|
||||||
file/fli_format.cpp
|
file/fli_format.cpp
|
||||||
file/gif_format.cpp
|
file/gif_format.cpp
|
||||||
file/ico_format.cpp
|
file/ico_format.cpp
|
||||||
|
@ -987,6 +987,7 @@ private:
|
|||||||
advancedModeAlert()->resetWithDefaultValue();
|
advancedModeAlert()->resetWithDefaultValue();
|
||||||
invalidFgBgColorAlert()->resetWithDefaultValue();
|
invalidFgBgColorAlert()->resetWithDefaultValue();
|
||||||
runScriptAlert()->resetWithDefaultValue();
|
runScriptAlert()->resetWithDefaultValue();
|
||||||
|
cssOptionsAlert()->resetWithDefaultValue();
|
||||||
gifOptionsAlert()->resetWithDefaultValue();
|
gifOptionsAlert()->resetWithDefaultValue();
|
||||||
jpegOptionsAlert()->resetWithDefaultValue();
|
jpegOptionsAlert()->resetWithDefaultValue();
|
||||||
svgOptionsAlert()->resetWithDefaultValue();
|
svgOptionsAlert()->resetWithDefaultValue();
|
||||||
|
303
src/app/file/css_format.cpp
Normal file
303
src/app/file/css_format.cpp
Normal 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
|
@ -22,6 +22,7 @@ namespace app {
|
|||||||
|
|
||||||
extern FileFormat* CreateAseFormat();
|
extern FileFormat* CreateAseFormat();
|
||||||
extern FileFormat* CreateBmpFormat();
|
extern FileFormat* CreateBmpFormat();
|
||||||
|
extern FileFormat* CreateCssFormat();
|
||||||
extern FileFormat* CreateFliFormat();
|
extern FileFormat* CreateFliFormat();
|
||||||
extern FileFormat* CreateGifFormat();
|
extern FileFormat* CreateGifFormat();
|
||||||
extern FileFormat* CreateIcoFormat();
|
extern FileFormat* CreateIcoFormat();
|
||||||
@ -57,6 +58,7 @@ FileFormatsManager::FileFormatsManager()
|
|||||||
// The first format is the default image format in FileSelector
|
// The first format is the default image format in FileSelector
|
||||||
registerFormat(CreateAseFormat());
|
registerFormat(CreateAseFormat());
|
||||||
registerFormat(CreateBmpFormat());
|
registerFormat(CreateBmpFormat());
|
||||||
|
registerFormat(CreateCssFormat());
|
||||||
registerFormat(CreateFliFormat());
|
registerFormat(CreateFliFormat());
|
||||||
registerFormat(CreateGifFormat());
|
registerFormat(CreateGifFormat());
|
||||||
registerFormat(CreateIcoFormat());
|
registerFormat(CreateIcoFormat());
|
||||||
|
@ -73,7 +73,10 @@ FileFormat* CreateSvgFormat()
|
|||||||
return new SvgFormat;
|
return new SvgFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SvgFormat::onLoad(FileOp* fop) { return false;}
|
bool SvgFormat::onLoad(FileOp* fop)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SAVE
|
#ifdef ENABLE_SAVE
|
||||||
|
|
||||||
|
@ -141,6 +141,9 @@ FileFormat detect_format_by_file_extension(const std::string& filename)
|
|||||||
if (ext == "tga")
|
if (ext == "tga")
|
||||||
return FileFormat::TARGA_IMAGE;
|
return FileFormat::TARGA_IMAGE;
|
||||||
|
|
||||||
|
if (ext == "css")
|
||||||
|
return FileFormat::CSS_STYLE;
|
||||||
|
|
||||||
if (ext == "webp")
|
if (ext == "webp")
|
||||||
return FileFormat::WEBP_ANIMATION;
|
return FileFormat::WEBP_ANIMATION;
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ enum class FileFormat {
|
|||||||
SVG_IMAGE,
|
SVG_IMAGE,
|
||||||
TARGA_IMAGE,
|
TARGA_IMAGE,
|
||||||
WEBP_ANIMATION,
|
WEBP_ANIMATION,
|
||||||
|
CSS_STYLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dio
|
} // namespace dio
|
||||||
|
Loading…
x
Reference in New Issue
Block a user