This is an automated email from the git hooks/post-receive script. thansen pushed a commit to branch master in repository aseprite.
commit 497fc3ed3ae60e39d3f1f85a448f38daf80549ad Author: Gabriel Rauter <[email protected]> Date: Tue Sep 1 13:05:24 2015 +0200 implement simple non animation webp for #273 This includes lossless and lossy webp file format. For this reason a save option dialog was added giving rudimentary options for saving to the user. --- data/widgets/webp_options.xml | 48 +++++ src/app/CMakeLists.txt | 2 + src/app/file/file_formats_manager.cpp | 2 + src/app/file/webp_format.cpp | 361 ++++++++++++++++++++++++++++++++++ 4 files changed, 413 insertions(+) diff --git a/data/widgets/webp_options.xml b/data/widgets/webp_options.xml new file mode 100644 index 0000000..2f9a9f6 --- /dev/null +++ b/data/widgets/webp_options.xml @@ -0,0 +1,48 @@ +<!-- ASEPRITE --> +<!-- Copyright (C) 2014, 2015 by David Capello --> +<gui> +<window text="WebP Options" id="webp_options"> + <vbox> + <label text="Save as:" /> + <radio group="1" text="Lossless WebP" id="lossless" tooltip="Save in simple WebP lossless format." /> + <hbox> + <label width="55" text="Compression:" /> + <slider min="0" max="9" id="compression" cell_align="horizontal" width="128" /> + </hbox> + <hbox> + <label width="55" text="Image Hint:" /> + <combobox width="128" id="image_hint"> + <listitem text="Default" value="0" /> + <listitem text="Picture" value="1" /> + <listitem text="Photo" value="2" /> + <listitem text="Graph" value="3" /> + <listitem text="Last" value="4" /> + </combobox> + </hbox> + <separator horizontal="true" /> + <radio group="1" text="Lossy WebP" id="lossy" tooltip="Save in simple WebP lossy format." /> + <hbox> + <label width="55" text="Quality:" /> + <slider min="0" max="100" id="quality" cell_align="horizontal" width="128" /> + </hbox> + <hbox> + <label width="55" text="Image Preset:" /> + <combobox width="128" id="image_preset"> + <listitem text="Default" value="0" /> + <listitem text="Picture" value="1" /> + <listitem text="Photo" value="2" /> + <listitem text="Drawing" value="3" /> + <listitem text="Icon" value="4" /> + <listitem text="Text" value="5" /> + </combobox> + </hbox> + <hbox> + <boxfiller /> + <hbox homogeneous="true"> + <button text="&OK" closewindow="true" id="ok" magnet="true" minwidth="60" /> + <button text="&Cancel" closewindow="true" /> + </hbox> + </hbox> + </vbox> +</window> +</gui> diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index e070386..0125cdd 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -274,6 +274,7 @@ add_library(app-lib file/png_format.cpp file/split_filename.cpp file/tga_format.cpp + file/webp_format.cpp file_selector.cpp file_system.cpp filename_formatter.cpp @@ -412,6 +413,7 @@ target_link_libraries(app-lib ${JPEG_LIBRARIES} ${GIF_LIBRARIES} ${PNG_LIBRARIES} + ${WEBP_LIBRARIES} ${ZLIB_LIBRARIES}) if(ENABLE_UPDATER) diff --git a/src/app/file/file_formats_manager.cpp b/src/app/file/file_formats_manager.cpp index 1a8f802..74b78e8 100644 --- a/src/app/file/file_formats_manager.cpp +++ b/src/app/file/file_formats_manager.cpp @@ -29,6 +29,7 @@ extern FileFormat* CreateJpegFormat(); extern FileFormat* CreatePcxFormat(); extern FileFormat* CreatePngFormat(); extern FileFormat* CreateTgaFormat(); +extern FileFormat* CreateWebPFormat(); static FileFormatsManager* singleton = NULL; @@ -67,6 +68,7 @@ void FileFormatsManager::registerAllFormats() registerFormat(CreatePcxFormat()); registerFormat(CreatePngFormat()); registerFormat(CreateTgaFormat()); + registerFormat(CreateWebPFormat()); } void FileFormatsManager::registerFormat(FileFormat* fileFormat) diff --git a/src/app/file/webp_format.cpp b/src/app/file/webp_format.cpp new file mode 100644 index 0000000..489a4ce --- /dev/null +++ b/src/app/file/webp_format.cpp @@ -0,0 +1,361 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/app.h" +#include "app/console.h" +#include "app/context.h" +#include "app/document.h" +#include "app/file/file.h" +#include "app/file/file_format.h" +#include "app/file/format_options.h" +#include "app/find_widget.h" +#include "app/ini_file.h" +#include "app/load_widget.h" +#include "base/file_handle.h" +#include "doc/doc.h" +#include "ui/ui.h" + +#include <cstdio> +#include <cstdlib> +#include <algorithm> +#include <map> + + +//include webp librarys +#include <webp/decode.h> +#include <webp/encode.h> + +namespace app { + +using namespace base; + +class WebPFormat : public FileFormat { + // Data for WEBP files + class WebPOptions : public FormatOptions { + public: + WebPOptions(): lossless(0), quality(75), method(6), image_hint(WEBP_HINT_DEFAULT), image_preset(WEBP_PRESET_DEFAULT) {}; + int lossless; // Lossless encoding (0=lossy(default), 1=lossless). + float quality; // between 0 (smallest file) and 100 (biggest) + int method; // quality/speed trade-off (0=fast, 9=slower-better) + WebPImageHint image_hint; // Hint for image type (lossless only for now). + WebPPreset image_preset; // Image Preset for lossy webp. + }; + + const char* onGetName() const { return "webp"; } + const char* onGetExtensions() const { return "webp"; } + int onGetFlags() const { + return + FILE_SUPPORT_LOAD | + FILE_SUPPORT_SAVE | + FILE_SUPPORT_RGB | + FILE_SUPPORT_RGBA | + FILE_SUPPORT_SEQUENCES | + FILE_SUPPORT_GET_FORMAT_OPTIONS; + } + + bool onLoad(FileOp* fop) override; +#ifdef ENABLE_SAVE + bool onSave(FileOp* fop) override; +#endif + +base::SharedPtr<FormatOptions> onGetFormatOptions(FileOp* fop) override; +}; + +FileFormat* CreateWebPFormat() +{ + return new WebPFormat; +} + +const std::pair<VP8StatusCode, std::string> dec_error_map_data[] = { + std::make_pair(VP8_STATUS_OK, ""), + std::make_pair(VP8_STATUS_OUT_OF_MEMORY, "out of memory"), + std::make_pair(VP8_STATUS_INVALID_PARAM, "invalid parameters"), + std::make_pair(VP8_STATUS_BITSTREAM_ERROR, "bitstream error"), + std::make_pair(VP8_STATUS_UNSUPPORTED_FEATURE, "unsupported feature"), + std::make_pair(VP8_STATUS_SUSPENDED, "suspended"), + std::make_pair(VP8_STATUS_USER_ABORT, "user aborted"), + std::make_pair(VP8_STATUS_NOT_ENOUGH_DATA, "not enough data") +}; + +const std::map<VP8StatusCode, std::string> WebPDecodingErrorMap(dec_error_map_data, dec_error_map_data + sizeof dec_error_map_data / sizeof dec_error_map_data[0]); + +bool WebPFormat::onLoad(FileOp* fop) +{ + FileHandle handle(open_file_with_exception(fop->filename, "rb")); + FILE* fp = handle.get(); + + long len; + uint8_t* buf = NULL; + + if (fseek(fp, 0, SEEK_END) != 0) { + fop_error(fop, "Error while getting WebP file size for %s\n", fop->filename.c_str()); + return false; + } + + len = ftell(fp); + rewind(fp); + + if (len < 4) { + fop_error(fop, "%s is corrupt or not a WebP file\n", fop->filename.c_str()); + return false; + } + + buf = (uint8_t*) malloc(len); + if (!buf) { + fop_error(fop, "Error while allocating memory for %s\n", fop->filename.c_str()); + return false; + } + + if (!fread(buf, len, 1, fp)) { + fop_error(fop, "Error while writing to %s to memory\n", fop->filename.c_str()); + free(buf); + return false; + } + + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) { + fop_error(fop, "LibWebP version mismatch %s\n", fop->filename.c_str()); + free(buf); + return false; + } + + if (WebPGetFeatures(buf, len, &config.input) != VP8_STATUS_OK) { + fop_error(fop, "Bad bitstream in %s\n", fop->filename.c_str()); + free(buf); + return false; + } + + fop->seq.has_alpha = config.input.has_alpha; + //TODO write imagefeatures + + auto image = fop_sequence_image(fop, IMAGE_RGB, config.input.width, config.input.height); + + config.output.colorspace = MODE_RGBA; + config.output.u.RGBA.rgba = (uint8_t*)image->getPixelAddress(0, 0); + config.output.u.RGBA.stride = config.input.width * sizeof(uint32_t); + config.output.u.RGBA.size = config.input.width * config.input.height * sizeof(uint32_t); + config.output.is_external_memory = 1; + + WebPIDecoder* idec = WebPIDecode(NULL, 0, &config); + if (idec == NULL) { + fop_error(fop, "Error creating WebP decoder for %s\n", fop->filename.c_str()); + free(buf); + return false; + } + + auto bytes_remaining = len; + auto bytes_read = std::max(4l, len/100l); + auto data = buf; + + while (bytes_remaining > 0) { + VP8StatusCode status = WebPIAppend(idec, data, bytes_read); + if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) { + bytes_remaining -= bytes_read; + data += bytes_read; + if (bytes_remaining < bytes_read) bytes_read = bytes_remaining; + fop_progress(fop, 1.0f - ((float)std::max(bytes_remaining, 0l)/(float)len)); + } else { + fop_error(fop, "Error during decoding %s : %s\n", fop->filename.c_str(), WebPDecodingErrorMap.find(status)->second.c_str()); + WebPIDelete(idec); + WebPFreeDecBuffer(&config.output); + free(buf); + return false; + } + if (fop_is_stop(fop)) + break; + } + WebPIDelete(idec); + WebPFreeDecBuffer(&config.output); + return true; +} + +#ifdef ENABLE_SAVE +struct writerData { + FILE* fp; + FileOp* fop; +}; + +const std::pair<WebPEncodingError, std::string> enc_error_map_data[] = { + std::make_pair(VP8_ENC_OK, ""), + std::make_pair(VP8_ENC_ERROR_OUT_OF_MEMORY, "memory error allocating objects"), + std::make_pair(VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, "memory error while flushing bits"), + std::make_pair(VP8_ENC_ERROR_NULL_PARAMETER, "a pointer parameter is NULL"), + std::make_pair(VP8_ENC_ERROR_INVALID_CONFIGURATION, "configuration is invalid"), + std::make_pair(VP8_ENC_ERROR_BAD_DIMENSION, "picture has invalid width/height"), + std::make_pair(VP8_ENC_ERROR_PARTITION0_OVERFLOW, "partition is bigger than 512k"), + std::make_pair(VP8_ENC_ERROR_PARTITION_OVERFLOW, "partition is bigger than 16M"), + std::make_pair(VP8_ENC_ERROR_BAD_WRITE, "error while flushing bytes"), + std::make_pair(VP8_ENC_ERROR_FILE_TOO_BIG, "file is bigger than 4G"), + std::make_pair(VP8_ENC_ERROR_OUT_OF_MEMORY, "memory error allocating objects"), + std::make_pair(VP8_ENC_ERROR_USER_ABORT, "abort request by user"), + std::make_pair(VP8_ENC_ERROR_LAST, "list terminator. always last.") +}; + +const std::map<WebPEncodingError, std::string> WebPEncodingErrorMap(enc_error_map_data, enc_error_map_data + sizeof enc_error_map_data / sizeof enc_error_map_data[0]); + +static int ProgressReport(int percent, const WebPPicture* const pic) +{ + fop_progress(((writerData*)pic->custom_ptr)->fop, (double)percent/(double)100); + if (fop_is_stop(((writerData*)pic->custom_ptr)->fop)) return false; + return true; +} + +static int FileWriter(const uint8_t* data, size_t data_size, const WebPPicture* const pic) +{ + return data_size ? (fwrite(data, data_size, 1, ((writerData*)pic->custom_ptr)->fp) == 1) : 1; +} + + +//Helper functions instead of std::stoi because of missing c++11 on mac os x +template<typename T> +static inline std::string ToString(const T& v) +{ + std::ostringstream ss; + ss << v; + return ss.str(); +} + +template<typename T> +static inline T FromString(const std::string& str) +{ + std::istringstream ss(str); + T ret; + ss >> ret; + return ret; +} + +bool WebPFormat::onSave(FileOp* fop) +{ + FileHandle handle(open_file_with_exception(fop->filename, "wb")); + FILE* fp = handle.get(); + + struct writerData wd = {fp, fop}; + + auto image = fop->seq.image.get(); + if (image->width() > WEBP_MAX_DIMENSION || image->height() > WEBP_MAX_DIMENSION) { + fop_error( + fop, "Error: WebP can only have a maximum width and height of %i but your %s has a size of %i x %i\n", + WEBP_MAX_DIMENSION, fop->filename.c_str(), image->width(), image->height() + ); + return false; + } + + base::SharedPtr<WebPOptions> webp_options = fop->seq.format_options; + + WebPConfig config; + + if (webp_options->lossless) { + if (!(WebPConfigInit(&config) && WebPConfigLosslessPreset(&config, webp_options->method))) { + fop_error(fop, "Error for WebP Config Version for file %s\n", fop->filename.c_str()); + return false; + } + config.image_hint = webp_options->image_hint; + } else { + if (!WebPConfigPreset(&config, webp_options->image_preset, webp_options->quality)) { + fop_error(fop, "Error for WebP Config Version for file %s\n", fop->filename.c_str()); + return false; + } + } + + if (!WebPValidateConfig(&config)) { + fop_error(fop, "Error in WebP Encoder Config for file %s\n", fop->filename.c_str()); + return false; + } + + WebPPicture pic; + if (!WebPPictureInit(&pic)) { + fop_error(fop, "Error for WebP Picture Version mismatch for file %s\n", fop->filename.c_str()); + return false; + } + + pic.width = image->width(); + pic.height = image->height(); + if (webp_options->lossless) { + pic.use_argb = true; + } + + if (!WebPPictureAlloc(&pic)) { + fop_error(fop, "Error for WebP Picture memory allocations for file %s\n", fop->filename.c_str()); + return false; + } + + if (!WebPPictureImportRGBA(&pic, (uint8_t*)image->getPixelAddress(0, 0), image->width() * sizeof(uint32_t))) { + fop_error(fop, "Error for LibWebP Import RGBA Buffer into Picture for %s\n", fop->filename.c_str()); + WebPPictureFree(&pic); + return false; + } + + pic.writer = FileWriter; + pic.custom_ptr = &wd; + pic.progress_hook = ProgressReport; + + if (!WebPEncode(&config, &pic)) { + fop_error(fop, "Error for LibWebP while Encoding %s: %s\n", fop->filename.c_str(), WebPEncodingErrorMap.find(pic.error_code)->second.c_str()); + WebPPictureFree(&pic); + return false; + } + + WebPPictureFree(&pic); + return true; +} +#endif + +// Shows the WebP configuration dialog. +base::SharedPtr<FormatOptions> WebPFormat::onGetFormatOptions(FileOp* fop) +{ + base::SharedPtr<WebPOptions> webp_options; + if (fop->document->getFormatOptions()) + webp_options = base::SharedPtr<WebPOptions>(fop->document->getFormatOptions()); + + if (!webp_options) + webp_options.reset(new WebPOptions); + + // Non-interactive mode + if (!fop->context || !fop->context->isUIAvailable()) + return webp_options; + + try { + + // Load the window to ask to the user the JPEG options he wants. + UniquePtr<ui::Window> window(app::load_widget<ui::Window>("webp_options.xml", "webp_options")); + ui::RadioButton* button_lossless = app::find_widget<ui::RadioButton>(window, "lossless"); + ui::Slider* slider_compression = app::find_widget<ui::Slider>(window, "compression"); + ui::Slider* slider_quality = app::find_widget<ui::Slider>(window, "quality"); + ui::Widget* ok = app::find_widget<ui::Widget>(window, "ok"); + ui::ComboBox* list_hint = app::find_widget<ui::ComboBox>(window, "image_hint"); + ui::ComboBox* list_preset = app::find_widget<ui::ComboBox>(window, "image_preset"); + + button_lossless->setSelected(true); + slider_compression->setValue(6); + slider_quality->setValue(75); + + window->openWindowInForeground(); + + if (window->getKiller() == ok) { + webp_options->quality = slider_quality->getValue(); + webp_options->method = slider_compression->getValue(); + webp_options->lossless = button_lossless->isSelected(); + webp_options->image_hint = static_cast<WebPImageHint>(FromString<int>(list_hint->getValue())); + webp_options->image_preset = static_cast<WebPPreset>(FromString<int>(list_preset->getValue())); + } + else { + webp_options.reset(NULL); + } + + return webp_options; + } + catch (std::exception& e) { + Console::showException(e); + return base::SharedPtr<WebPOptions>(0); + } +} + +} // namespace app -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git _______________________________________________ Pkg-games-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-games-commits

