diff --git a/data/widgets/font_popup.xml b/data/widgets/font_popup.xml
new file mode 100644
index 000000000..b890db815
--- /dev/null
+++ b/data/widgets/font_popup.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/data/widgets/paste_text.xml b/data/widgets/paste_text.xml
index f76bdb6c8..bb0eab010 100644
--- a/data/widgets/paste_text.xml
+++ b/data/widgets/paste_text.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 4319e57ac..30e1b09de 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -353,6 +353,7 @@ add_library(app-lib
ui/editor/zooming_state.cpp
ui/file_list.cpp
ui/file_selector.cpp
+ ui/font_popup.cpp
ui/frame_tag_window.cpp
ui/hex_color_entry.cpp
ui/home_view.cpp
@@ -393,6 +394,7 @@ add_library(app-lib
util/create_cel_copy.cpp
util/expand_cel_canvas.cpp
util/filetoks.cpp
+ util/freetype_utils.cpp
util/msk_file.cpp
util/new_image_from_mask.cpp
util/pic_file.cpp
diff --git a/src/app/commands/cmd_paste_text.cpp b/src/app/commands/cmd_paste_text.cpp
index 29fcbb383..2f515691b 100644
--- a/src/app/commands/cmd_paste_text.cpp
+++ b/src/app/commands/cmd_paste_text.cpp
@@ -11,22 +11,19 @@
#include "app/app.h"
#include "app/commands/command.h"
+#include "app/console.h"
#include "app/context.h"
#include "app/file_selector.h"
#include "app/pref/preferences.h"
+#include "app/ui/drop_down_button.h"
+#include "app/ui/font_popup.h"
#include "app/util/clipboard.h"
+#include "app/util/freetype_utils.h"
#include "base/bind.h"
#include "base/path.h"
#include "base/string.h"
-#include "doc/blend_funcs.h"
-#include "doc/blend_internals.h"
-#include "doc/color.h"
+#include "base/unique_ptr.h"
#include "doc/image.h"
-#include "doc/primitives.h"
-
-#include "freetype/ftglyph.h"
-#include "ft2build.h"
-#include FT_FREETYPE_H
#include "paste_text.xml.h"
@@ -34,37 +31,6 @@ namespace app {
static std::string last_text_used;
-template
-static void for_each_glyph(FT_Face face, Iterator first, Iterator end, Func callback)
-{
- bool use_kerning = (FT_HAS_KERNING(face) ? true: false);
-
- // Calculate size
- FT_UInt prev_glyph = 0;
- int x = 0;
- for (; first != end; ++first) {
- FT_UInt glyph_index = FT_Get_Char_Index(face, *first);
-
- if (use_kerning && prev_glyph && glyph_index) {
- FT_Vector kerning;
- FT_Get_Kerning(face, prev_glyph, glyph_index,
- FT_KERNING_DEFAULT, &kerning);
- x += kerning.x >> 6;
- }
-
- FT_Error err = FT_Load_Glyph(
- face, glyph_index,
- FT_LOAD_RENDER | FT_LOAD_NO_BITMAP);
-
- if (!err) {
- callback(x, face->glyph);
- x += face->glyph->advance.x >> 6;
- }
-
- prev_glyph = glyph_index;
- }
-}
-
class PasteTextCommand : public Command {
public:
PasteTextCommand();
@@ -97,7 +63,8 @@ public:
updateFontFaceButton();
fontSize()->setTextf("%d", size);
- fontFace()->Click.connect(Bind(&PasteTextWindow::onSelectFont, this));
+ fontFace()->Click.connect(Bind(&PasteTextWindow::onSelectFontFile, this));
+ fontFace()->DropDownClick.connect(Bind(&PasteTextWindow::onSelectSystemFont, this));
fontColor()->setColor(color);
}
@@ -113,11 +80,12 @@ public:
private:
void updateFontFaceButton() {
- fontFace()->setTextf("Select Font: %s",
- base::get_file_name(m_face).c_str());
+ fontFace()->mainButton()
+ ->setTextf("Select Font: %s",
+ base::get_file_title(m_face).c_str());
}
- void onSelectFont() {
+ void onSelectFontFile() {
std::string face = show_file_selector(
"Select a TrueType Font",
m_face,
@@ -126,13 +94,41 @@ private:
nullptr);
if (!face.empty()) {
- m_face = face;
- ok()->setEnabled(true);
- updateFontFaceButton();
+ setFontFace(face);
+ }
+ }
+
+ void setFontFace(const std::string& face) {
+ m_face = face;
+ ok()->setEnabled(true);
+ updateFontFaceButton();
+ }
+
+ void onSelectSystemFont() {
+ if (!m_fontPopup) {
+ try {
+ m_fontPopup.reset(new FontPopup());
+ m_fontPopup->Load.connect(&PasteTextWindow::setFontFace, this);
+ }
+ catch (const std::exception& ex) {
+ Console::showException(ex);
+ return;
+ }
+ }
+
+ if (!m_fontPopup->isVisible()) {
+ gfx::Rect bounds = fontFace()->getBounds();
+ m_fontPopup->showPopup(
+ gfx::Rect(bounds.x, bounds.y+bounds.h,
+ ui::display_w()/2, ui::display_h()/2));
+ }
+ else {
+ m_fontPopup->closeWindow(NULL);
}
}
std::string m_face;
+ base::UniquePtr m_fontPopup;
};
void PasteTextCommand::onExecute(Context* ctx)
@@ -155,17 +151,7 @@ void PasteTextCommand::onExecute(Context* ctx)
pref.textTool.fontFace(faceName);
pref.textTool.fontSize(size);
- FT_Library ft;
- FT_Init_FreeType(&ft);
-
- FT_Open_Args args;
- memset(&args, 0, sizeof(args));
- args.flags = FT_OPEN_PATHNAME;
- args.pathname = (FT_String*)faceName.c_str();
-
- FT_Face face;
- FT_Error err = FT_Open_Face(ft, &args, 0, &face);
- if (!err) {
+ try {
std::string text = window.userText()->getText();
app::Color appColor = window.fontColor()->getColor();
doc::color_t color = doc::rgba(appColor.getRed(),
@@ -173,65 +159,17 @@ void PasteTextCommand::onExecute(Context* ctx)
appColor.getBlue(),
appColor.getAlpha());
- // Set font size
- FT_Set_Pixel_Sizes(face, size, size);
-
- // Calculate text size
- base::utf8_iterator begin(text.begin()), end(text.end());
- gfx::Rect bounds(0, 0, 0, 0);
- for_each_glyph(
- face, begin, end,
- [&bounds](int x, FT_GlyphSlot glyph) {
- bounds |= gfx::Rect(x + glyph->bitmap_left,
- -glyph->bitmap_top,
- (int)glyph->bitmap.width,
- (int)glyph->bitmap.rows);
- });
-
- // Render the image and copy it to the clipboard
- if (!bounds.isEmpty()) {
- doc::Image* image = doc::Image::create(IMAGE_RGB, bounds.w, bounds.h);
- doc::clear_image(image, 0);
-
- for_each_glyph(
- face, begin, end,
- [&bounds, &image, color](int x, FT_GlyphSlot glyph) {
- int t, yimg = - bounds.y - glyph->bitmap_top;
- for (int v=0; v<(int)glyph->bitmap.rows; ++v, ++yimg) {
- const uint8_t* p = glyph->bitmap.buffer + v*glyph->bitmap.pitch;
- int ximg = x - bounds.x + glyph->bitmap_left;
- for (int u=0; u<(int)glyph->bitmap.width; ++u, ++p, ++ximg) {
- int alpha = *p;
- doc::put_pixel(
- image, ximg, yimg,
- doc::rgba_blender_normal(
- doc::get_pixel(image, ximg, yimg),
- doc::rgba(doc::rgba_getr(color),
- doc::rgba_getg(color),
- doc::rgba_getb(color),
- MUL_UN8(doc::rgba_geta(color), alpha, t)), 255));
- }
- }
- });
-
+ doc::Image* image = render_text(faceName, size, text, color);
+ if (image) {
clipboard::copy_image(image, nullptr, nullptr);
clipboard::paste();
}
- else {
- ui::Alert::show(PACKAGE
- "<
+#include
+#endif
+
+namespace app {
+
+using namespace ui;
+
+class FontItem : public ListItem {
+public:
+ FontItem(std::string& fontsDir, const std::string& fn)
+ : ListItem(fn)
+ , m_fontsDir(fontsDir) {
+ }
+
+private:
+ void onPaint(PaintEvent& ev) override {
+ if (!m_image) {
+ ListItem::onPaint(ev);
+ }
+ else {
+ app::skin::SkinTheme* theme = app::skin::SkinTheme::instance();
+ Graphics* g = ev.getGraphics();
+ she::Surface* sur = she::instance()->createRgbaSurface(m_image->width(),
+ m_image->height());
+
+ convert_image_to_surface(
+ m_image.get(), nullptr, sur,
+ 0, 0, 0, 0, m_image->width(), m_image->height());
+
+ gfx::Color bg;
+ if (isSelected())
+ bg = theme->colors.listitemSelectedFace();
+ else
+ bg = theme->colors.listitemNormalFace();
+
+ g->fillRect(bg, getClientBounds());
+ g->drawRgbaSurface(sur, 0, 0);
+ }
+ }
+
+ void onPreferredSize(PreferredSizeEvent& ev) override {
+ if (m_image)
+ ev.setPreferredSize(m_image->width(),
+ m_image->height());
+ else
+ ListItem::onPreferredSize(ev);
+ }
+
+ void onSelect() override {
+ if (!getParent())
+ return;
+
+ ASSERT(getParent());
+ ASSERT(getParent()->type() == kListBoxWidget);
+
+ std::string filename = base::join_path(m_fontsDir, getText());
+ app::skin::SkinTheme* theme = app::skin::SkinTheme::instance();
+ gfx::Color color = theme->colors.text();
+
+ try {
+ m_image.reset(
+ render_text(
+ filename, 16,
+ getText(),
+ doc::rgba(gfx::getr(color),
+ gfx::getg(color),
+ gfx::getb(color),
+ gfx::geta(color))));
+
+ getParent()->layout();
+ invalidate();
+ }
+ catch (const std::exception& ex) {
+ Console::showException(ex);
+ }
+ }
+
+private:
+ std::string& m_fontsDir;
+ base::UniquePtr m_image;
+};
+
+FontPopup::FontPopup()
+ : PopupWindow("Fonts", kCloseOnClickInOtherWindow)
+ , m_popup(new gen::FontPopup())
+{
+ setAutoRemap(false);
+ setBorder(gfx::Border(4*guiscale()));
+
+ addChild(m_popup);
+
+ m_popup->loadFont()->Click.connect(Bind(&FontPopup::onLoadFont, this));
+ m_listBox.Change.connect(Bind(&FontPopup::onChangeFont, this));
+ m_listBox.DoubleClickItem.connect(Bind(&FontPopup::onLoadFont, this));
+
+ m_popup->view()->attachToView(&m_listBox);
+
+#if _WIN32
+ {
+ std::vector buf(MAX_PATH);
+ HRESULT hr = SHGetFolderPath(NULL, CSIDL_FONTS, NULL,
+ SHGFP_TYPE_DEFAULT, &buf[0]);
+ if (hr == S_OK) {
+ m_fontsDir = base::to_utf8(&buf[0]);
+ }
+ }
+#endif
+
+ if (!m_fontsDir.empty()) {
+ auto& files = base::list_files(m_fontsDir);
+ std::sort(files.begin(), files.end());
+ for (auto& file : files) {
+ if (base::string_to_lower(base::get_file_extension(file)) == "ttf")
+ m_listBox.addChild(new FontItem(m_fontsDir, file));
+ }
+ }
+
+ if (m_listBox.getChildren().empty())
+ m_listBox.addChild(new ListItem("No system fonts were found"));
+}
+
+void FontPopup::showPopup(const gfx::Rect& bounds)
+{
+ m_popup->loadFont()->setEnabled(false);
+ m_listBox.selectChild(NULL);
+
+ moveWindow(bounds);
+
+ // Setup the hot-region
+ setHotRegion(gfx::Region(gfx::Rect(bounds).enlarge(32 * guiscale())));
+
+ openWindow();
+}
+
+void FontPopup::onChangeFont()
+{
+ m_popup->loadFont()->setEnabled(true);
+}
+
+void FontPopup::onLoadFont()
+{
+ Widget* child = m_listBox.getSelectedChild();
+ if (!child)
+ return;
+
+ std::string filename = base::join_path(m_fontsDir,
+ child->getText());
+ if (base::is_file(filename))
+ Load(filename); // Fire Load signal
+
+ closeWindow(nullptr);
+}
+
+} // namespace app
diff --git a/src/app/ui/font_popup.h b/src/app/ui/font_popup.h
new file mode 100644
index 000000000..3b710e554
--- /dev/null
+++ b/src/app/ui/font_popup.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef APP_UI_FONT_POPUP_H_INCLUDED
+#define APP_UI_FONT_POPUP_H_INCLUDED
+#pragma once
+
+#include "ui/listbox.h"
+#include "ui/popup_window.h"
+
+namespace ui {
+ class Button;
+ class View;
+}
+
+namespace app {
+
+ namespace gen {
+ class FontPopup;
+ }
+
+ class FontPopup : public ui::PopupWindow {
+ public:
+ FontPopup();
+
+ void showPopup(const gfx::Rect& bounds);
+
+ const std::string& fontsDir() const {
+ return m_fontsDir;
+ }
+
+ Signal1 Load;
+
+ protected:
+ void onChangeFont();
+ void onLoadFont();
+
+ private:
+ gen::FontPopup* m_popup;
+ ui::ListBox m_listBox;
+ std::string m_fontsDir;
+ };
+
+} // namespace app
+
+#endif
diff --git a/src/app/util/freetype_utils.cpp b/src/app/util/freetype_utils.cpp
new file mode 100644
index 000000000..dc4b67884
--- /dev/null
+++ b/src/app/util/freetype_utils.cpp
@@ -0,0 +1,152 @@
+// 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/util/freetype_utils.h"
+
+#include "base/string.h"
+#include "base/unique_ptr.h"
+#include "doc/blend_funcs.h"
+#include "doc/blend_internals.h"
+#include "doc/color.h"
+#include "doc/image.h"
+#include "doc/primitives.h"
+
+#include "freetype/ftglyph.h"
+#include "ft2build.h"
+#include FT_FREETYPE_H
+
+namespace app {
+
+template
+static void for_each_glyph(FT_Face face, Iterator first, Iterator end, Func callback)
+{
+ bool use_kerning = (FT_HAS_KERNING(face) ? true: false);
+
+ // Calculate size
+ FT_UInt prev_glyph = 0;
+ int x = 0;
+ for (; first != end; ++first) {
+ FT_UInt glyph_index = FT_Get_Char_Index(face, *first);
+
+ if (use_kerning && prev_glyph && glyph_index) {
+ FT_Vector kerning;
+ FT_Get_Kerning(face, prev_glyph, glyph_index,
+ FT_KERNING_DEFAULT, &kerning);
+ x += kerning.x >> 6;
+ }
+
+ FT_Error err = FT_Load_Glyph(
+ face, glyph_index,
+ FT_LOAD_RENDER | FT_LOAD_NO_BITMAP);
+
+ if (!err) {
+ callback(x, face->glyph);
+ x += face->glyph->advance.x >> 6;
+ }
+
+ prev_glyph = glyph_index;
+ }
+}
+
+class ScopedFTLib {
+public:
+ ScopedFTLib(FT_Library& ft) : m_ft(ft) {
+ FT_Init_FreeType(&m_ft);
+ }
+ ~ScopedFTLib() {
+ FT_Done_FreeType(m_ft);
+ }
+private:
+ FT_Library& m_ft;
+};
+
+class ScopedFTFace {
+public:
+ ScopedFTFace(FT_Face& face) : m_face(face) {
+ }
+ ~ScopedFTFace() {
+ FT_Done_Face(m_face);
+ }
+private:
+ FT_Face& m_face;
+};
+
+doc::Image* render_text(const std::string& fontfile, int fontsize,
+ const std::string& text,
+ doc::color_t color)
+{
+ base::UniquePtr image(nullptr);
+ FT_Library ft;
+ ScopedFTLib init_and_done_ft(ft);
+
+ FT_Open_Args args;
+ memset(&args, 0, sizeof(args));
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (FT_String*)fontfile.c_str();
+
+ FT_Face face;
+ FT_Error err = FT_Open_Face(ft, &args, 0, &face);
+ if (!err) {
+ ScopedFTFace done_face(face);
+
+ // Set font size
+ FT_Set_Pixel_Sizes(face, fontsize, fontsize);
+
+ // Calculate text size
+ base::utf8_const_iterator begin(text.begin()), end(text.end());
+ gfx::Rect bounds(0, 0, 0, 0);
+ for_each_glyph(
+ face, begin, end,
+ [&bounds](int x, FT_GlyphSlot glyph) {
+ bounds |= gfx::Rect(x + glyph->bitmap_left,
+ -glyph->bitmap_top,
+ (int)glyph->bitmap.width,
+ (int)glyph->bitmap.rows);
+ });
+
+ // Render the image and copy it to the clipboard
+ if (!bounds.isEmpty()) {
+ image.reset(doc::Image::create(doc::IMAGE_RGB, bounds.w, bounds.h));
+ doc::clear_image(image, 0);
+
+ for_each_glyph(
+ face, begin, end,
+ [&bounds, &image, color](int x, FT_GlyphSlot glyph) {
+ int t, yimg = - bounds.y - glyph->bitmap_top;
+ for (int v=0; v<(int)glyph->bitmap.rows; ++v, ++yimg) {
+ const uint8_t* p = glyph->bitmap.buffer + v*glyph->bitmap.pitch;
+ int ximg = x - bounds.x + glyph->bitmap_left;
+ for (int u=0; u<(int)glyph->bitmap.width; ++u, ++p, ++ximg) {
+ int alpha = *p;
+ doc::put_pixel(
+ image, ximg, yimg,
+ doc::rgba_blender_normal(
+ doc::get_pixel(image, ximg, yimg),
+ doc::rgba(doc::rgba_getr(color),
+ doc::rgba_getg(color),
+ doc::rgba_getb(color),
+ MUL_UN8(doc::rgba_geta(color), alpha, t)), 255));
+ }
+ }
+ });
+ }
+ else {
+ throw std::runtime_error("There is no text");
+ }
+ }
+ else {
+ throw std::runtime_error("Error loading font face");
+ }
+
+ return (image ? image.release(): nullptr);
+}
+
+} // namespace app
diff --git a/src/app/util/freetype_utils.h b/src/app/util/freetype_utils.h
new file mode 100644
index 000000000..d2589ae83
--- /dev/null
+++ b/src/app/util/freetype_utils.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef APP_UTIL_FREETYPE_UTILS_H_INCLUDED
+#define APP_UTIL_FREETYPE_UTILS_H_INCLUDED
+#pragma once
+
+#include "doc/color.h"
+
+#include
+
+namespace doc {
+ class Image;
+}
+
+namespace app {
+
+ doc::Image* render_text(const std::string& fontfile, int fontsize,
+ const std::string& text,
+ doc::color_t color);
+
+} // namespace app
+
+#endif