From 24faae2ca5849d8385696b937136f7d1e8cae75b Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 20 Feb 2017 17:14:41 -0300 Subject: [PATCH] Use HarfBuzz to render combining characters correctly --- .gitmodules | 3 + CMakeLists.txt | 7 +- docs/LICENSES.md | 59 +++++++++ laf | 2 +- src/app/CMakeLists.txt | 3 +- src/app/ui/skin/skin_theme.cpp | 13 +- src/app/util/freetype_utils.cpp | 78 +++++------ src/ft/algorithm.h | 143 +++++++++++++++----- src/ft/hb_shaper.h | 90 +++++++++++++ src/she/draw_text.cpp | 225 +++++++++++++++----------------- src/she/draw_text.h | 14 +- src/ui/entry.cpp | 11 -- src/ui/entry.h | 3 - src/ui/graphics.cpp | 12 +- third_party/CMakeLists.txt | 3 +- third_party/harfbuzz | 1 + 16 files changed, 427 insertions(+), 240 deletions(-) create mode 100644 src/ft/hb_shaper.h create mode 160000 third_party/harfbuzz diff --git a/.gitmodules b/.gitmodules index d5ebf5d9d..07b5d8b55 100644 --- a/.gitmodules +++ b/.gitmodules @@ -42,3 +42,6 @@ [submodule "third_party/cmark"] path = third_party/cmark url = https://github.com/aseprite/cmark.git +[submodule "third_party/harfbuzz"] + path = third_party/harfbuzz + url = https://github.com/aseprite/harfbuzz.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 0abc55fef..3a50524f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # Aseprite -# Copyright (C) 2001-2016 David Capello +# Copyright (C) 2001-2017 David Capello # # Parts of this file come from the Allegro 4.4 CMakeLists.txt @@ -143,6 +143,7 @@ set(LOADPNG_DIR ${CMAKE_SOURCE_DIR}/third_party/loadpng) set(LIBWEBP_DIR ${CMAKE_SOURCE_DIR}/third_party/libwebp) set(PIXMAN_DIR ${CMAKE_SOURCE_DIR}/third_party/pixman) set(FREETYPE_DIR ${CMAKE_SOURCE_DIR}/third_party/freetype2) +set(HARFBUZZ_DIR ${CMAKE_SOURCE_DIR}/third_party/harfbuzz) set(SIMPLEINI_DIR ${CMAKE_SOURCE_DIR}/third_party/simpleini) set(TINYXML_DIR ${CMAKE_SOURCE_DIR}/third_party/tinyxml) set(ZLIB_DIR ${CMAKE_SOURCE_DIR}/third_party/zlib) @@ -263,6 +264,10 @@ else() endif() include_directories(${FREETYPE_INCLUDE_DIRS}) +# harfbuzz +set(HARFBUZZ_LIBRARIES harfbuzz) +set(HARFBUZZ_INCLUDE_DIRS ${HARFBUZZ_DIR}/src) + if(USE_SHARED_GIFLIB) find_package(GIF REQUIRED) else() diff --git a/docs/LICENSES.md b/docs/LICENSES.md index 222210e71..92cf1728c 100644 --- a/docs/LICENSES.md +++ b/docs/LICENSES.md @@ -486,6 +486,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ``` +# [harfbuzz](http://harfbuzz.org) + +``` +HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. +For parts of HarfBuzz that are licensed under different licenses see individual +files names COPYING in subdirectories where applicable. + +Copyright © 2010,2011,2012 Google, Inc. +Copyright © 2012 Mozilla Foundation +Copyright © 2011 Codethink Limited +Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) +Copyright © 2009 Keith Stribley +Copyright © 2009 Martin Hosken and SIL International +Copyright © 2007 Chris Wilson +Copyright © 2006 Behdad Esfahbod +Copyright © 2005 David Turner +Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. +Copyright © 1998-2004 David Turner and Werner Lemberg + +For full copyright notices consult the individual files in the package. + + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +``` + # [libjpeg](http://www.ijg.org/) ``` @@ -884,6 +925,24 @@ and releases new versions, with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community. ``` +# [ucdn](https://github.com/grigorig/ucdn) + +``` +Copyright (C) 2012 Grigori Goronzy + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + # [Wintab API](http://www.wacomeng.com/windows/docs/WintabBackground.htm) ``` diff --git a/laf b/laf index 2630b895f..f232eac37 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit 2630b895fa15b1ff863d47d4ca0ad12e93c22fa9 +Subproject commit f232eac37ef0c8a0f5324ed3154b314c64b9eb13 diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index ae7b408e7..7e83e6c09 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -517,7 +517,8 @@ target_link_libraries(app-lib ${PNG_LIBRARIES} ${WEBP_LIBRARIES} ${ZLIB_LIBRARIES} - ${FREETYPE_LIBRARIES}) + ${FREETYPE_LIBRARIES} + ${HARFBUZZ_LIBRARIES}) if(ENABLE_SCRIPTING) target_link_libraries(app-lib script-lib) diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index 0fe74c055..2434441cb 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -997,16 +997,9 @@ public: bool caretDrawn() const { return m_caretDrawn; } const gfx::Rect& textBounds() const { return m_textBounds; } - void preProcessChar(const base::utf8_const_iterator& it, - const base::utf8_const_iterator& end, - int& chr, + void preProcessChar(const int chr, gfx::Color& fg, - gfx::Color& bg, - bool& drawChar, - bool& moveCaret) override { - if (m_widget->isPassword()) - chr = '*'; - + gfx::Color& bg) override { // Normal text auto& colors = SkinTheme::instance()->colors; bg = ColorNone; @@ -1028,8 +1021,6 @@ public: fg = colors.disabled(); } - drawChar = true; - moveCaret = true; m_bg = bg; } diff --git a/src/app/util/freetype_utils.cpp b/src/app/util/freetype_utils.cpp index 791c13b9e..4e8f04f81 100644 --- a/src/app/util/freetype_utils.cpp +++ b/src/app/util/freetype_utils.cpp @@ -18,6 +18,7 @@ #include "doc/primitives.h" #include "ft/algorithm.h" #include "ft/face.h" +#include "ft/hb_shaper.h" #include "ft/lib.h" #include @@ -47,52 +48,51 @@ doc::Image* render_text(const std::string& fontfile, int fontsize, doc::clear_image(image, 0); ft::ForEachGlyph feg(face); - auto it = base::utf8_const_iterator(text.begin()); - auto end = base::utf8_const_iterator(text.end()); - while (it != end) { - feg.processChar( - *it, - [&bounds, &image, color, antialias](const ft::Glyph& glyph) { - int t, yimg = - bounds.y + int(glyph.y); + if (feg.initialize(base::utf8_const_iterator(text.begin()), + base::utf8_const_iterator(text.end()))) { + do { + auto glyph = feg.glyph(); + if (!glyph) + continue; - for (int v=0; vrows); ++v, ++yimg) { - const uint8_t* p = glyph.bitmap->buffer + v*glyph.bitmap->pitch; - int ximg = - bounds.x + int(glyph.x); - int bit = 0; + int t, yimg = - bounds.y + int(glyph->y); - for (int u=0; uwidth); ++u, ++ximg) { - int alpha; + for (int v=0; vbitmap->rows); ++v, ++yimg) { + const uint8_t* p = glyph->bitmap->buffer + v*glyph->bitmap->pitch; + int ximg = - bounds.x + int(glyph->x); + int bit = 0; - if (antialias) { - alpha = *(p++); - } - else { - alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); - if (bit == 8) { - bit = 0; - ++p; - } - } + for (int u=0; ubitmap->width); ++u, ++ximg) { + int alpha; - int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); - if (output_alpha) { - doc::color_t output_color = - doc::rgba(doc::rgba_getr(color), - doc::rgba_getg(color), - doc::rgba_getb(color), - output_alpha); - - doc::put_pixel( - image, ximg, yimg, - doc::rgba_blender_normal( - doc::get_pixel(image, ximg, yimg), - output_color)); + if (antialias) { + alpha = *(p++); + } + else { + alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); + if (bit == 8) { + bit = 0; + ++p; } } - } - }); - ++it; + int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); + if (output_alpha) { + doc::color_t output_color = + doc::rgba(doc::rgba_getr(color), + doc::rgba_getg(color), + doc::rgba_getb(color), + output_alpha); + + doc::put_pixel( + image, ximg, yimg, + doc::rgba_blender_normal( + doc::get_pixel(image, ximg, yimg), + output_color)); + } + } + } + } while (feg.nextChar()); } } else { diff --git a/src/ft/algorithm.h b/src/ft/algorithm.h index 46133eda5..b4072081f 100644 --- a/src/ft/algorithm.h +++ b/src/ft/algorithm.h @@ -10,24 +10,100 @@ #include "base/string.h" #include "ft/freetype_headers.h" +#include "ft/hb_shaper.h" #include "gfx/rect.h" namespace ft { template + class DefaultShaper { + public: + typedef typename FaceFT::Glyph Glyph; + + DefaultShaper(FaceFT& face) : m_face(face) { + } + + bool initialize(const base::utf8_const_iterator& it, + const base::utf8_const_iterator& end) { + m_it = it; + m_end = end; + return (m_it != end); + } + + bool nextChar() { + ++m_it; + return (m_it != m_end); + } + + int unicodeChar() const { + return *m_it; + } + + unsigned int glyphIndex() { + return m_face.cache().getGlyphIndex(m_face, unicodeChar()); + } + + void glyphOffsetXY(Glyph* glyph) { + // Do nothing + } + + void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) { + x += glyph->ft_glyph->advance.x / double(1 << 16); + y += glyph->ft_glyph->advance.y / double(1 << 16); + } + + private: + FaceFT& m_face; + base::utf8_const_iterator m_it; + base::utf8_const_iterator m_end; + }; + + template > class ForEachGlyph { public: + typedef typename FaceFT::Glyph Glyph; + ForEachGlyph(FaceFT& face) : m_face(face) + , m_shaper(face) + , m_glyph(nullptr) , m_useKerning(FT_HAS_KERNING((FT_Face)face) ? true: false) , m_prevGlyph(0) , m_x(0.0), m_y(0.0) - { + , m_index(0) { } - template - void processChar(const int chr, ProcessGlyph processGlyph) { - FT_UInt glyphIndex = m_face.cache().getGlyphIndex(m_face, chr); + ~ForEachGlyph() { + unloadGlyph(); + } + + int unicodeChar() { return m_shaper.unicodeChar(); } + + const Glyph* glyph() const { return m_glyph; } + + bool initialize(const base::utf8_const_iterator& it, + const base::utf8_const_iterator& end) { + bool res = m_shaper.initialize(it, end); + if (res) + prepareGlyph(); + return res; + } + + bool nextChar() { + m_prevGlyph = m_shaper.glyphIndex(); + + if (m_shaper.nextChar()) { + prepareGlyph(); + return true; + } + else + return false; + } + + private: + void prepareGlyph() { + FT_UInt glyphIndex = m_shaper.glyphIndex(); double initialX = m_x; if (m_useKerning && m_prevGlyph && glyphIndex) { @@ -37,52 +113,57 @@ namespace ft { m_x += kerning.x / 64.0; } - typename FaceFT::Glyph* glyph = m_face.cache().loadGlyph( - m_face, glyphIndex, m_face.antialias()); - if (glyph) { - glyph->bitmap = &FT_BitmapGlyph(glyph->ft_glyph)->bitmap; - glyph->x = m_x + glyph->bearingX; - glyph->y = m_y + unloadGlyph(); + + // Load new glyph + m_glyph = m_face.cache().loadGlyph(m_face, glyphIndex, m_face.antialias()); + if (m_glyph) { + m_glyph->bitmap = &FT_BitmapGlyph(m_glyph->ft_glyph)->bitmap; + m_glyph->x = m_x + + m_glyph->bearingX; + m_glyph->y = m_y + m_face.height() + m_face.descender() // descender is negative - - glyph->bearingY; + - m_glyph->bearingY; - m_x += glyph->ft_glyph->advance.x / double(1 << 16); - m_y += glyph->ft_glyph->advance.y / double(1 << 16); + m_shaper.glyphOffsetXY(m_glyph); + m_shaper.glyphAdvanceXY(m_glyph, m_x, m_y); - glyph->startX = initialX; - glyph->endX = m_x; - - processGlyph(*glyph); - - m_face.cache().doneGlyph(glyph); + m_glyph->startX = initialX; + m_glyph->endX = m_x; } + } - m_prevGlyph = glyphIndex; + void unloadGlyph() { + if (m_glyph) { + m_face.cache().doneGlyph(m_glyph); + m_glyph = nullptr; + } } private: FaceFT& m_face; + Shaper m_shaper; + Glyph* m_glyph; bool m_useKerning; FT_UInt m_prevGlyph; double m_x, m_y; + int m_index; }; template gfx::Rect calc_text_bounds(FaceFT& face, const std::string& str) { gfx::Rect bounds(0, 0, 0, 0); - auto it = base::utf8_const_iterator(str.begin()); - auto end = base::utf8_const_iterator(str.end()); ForEachGlyph feg(face); - for (; it != end; ++it) { - feg.processChar( - *it, - [&bounds](typename FaceFT::Glyph& glyph) { - bounds |= gfx::Rect(int(glyph.x), - int(glyph.y), - glyph.bitmap->width, - glyph.bitmap->rows); - }); + if (feg.initialize(base::utf8_const_iterator(str.begin()), + base::utf8_const_iterator(str.end()))) { + do { + if (auto glyph = feg.glyph()) + bounds |= gfx::Rect(int(glyph->x), + int(glyph->y), + glyph->bitmap->width, + glyph->bitmap->rows); + } while (feg.nextChar()); } return bounds; } diff --git a/src/ft/hb_shaper.h b/src/ft/hb_shaper.h new file mode 100644 index 000000000..90e0e4c25 --- /dev/null +++ b/src/ft/hb_shaper.h @@ -0,0 +1,90 @@ +// Aseprite FreeType Wrapper +// Copyright (c) 2017 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef FT_HB_SHAPER_H_INCLUDED +#define FT_HB_SHAPER_H_INCLUDED +#pragma once + +#include "ft/face.h" + +#include +#include + +#include + +namespace ft { + + template + class HBShaper { + public: + HBShaper(FaceFT& face) { + m_font = hb_ft_font_create((FT_Face)face, nullptr); + m_buffer = hb_buffer_create(); + } + + ~HBShaper() { + if (m_buffer) hb_buffer_destroy(m_buffer); + if (m_font) hb_font_destroy(m_font); + } + + bool initialize(const base::utf8_const_iterator& it, + const base::utf8_const_iterator& end) { + m_it = it; + m_end = end; + m_index = 0; + + hb_buffer_reset(m_buffer); + for (auto it=m_it; it!=end; ++it) + hb_buffer_add(m_buffer, *it, 0); + hb_buffer_set_content_type(m_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + hb_buffer_set_direction(m_buffer, HB_DIRECTION_LTR); + hb_buffer_guess_segment_properties(m_buffer); + + hb_shape(m_font, m_buffer, nullptr, 0); + + m_glyphInfo = hb_buffer_get_glyph_infos(m_buffer, &m_glyphCount); + m_glyphPos = hb_buffer_get_glyph_positions(m_buffer, &m_glyphCount); + return (m_glyphCount > 0); + } + + bool nextChar() { + ++m_it; + ++m_index; + return (m_index < m_glyphCount); + } + + int unicodeChar() const { + return *m_it; + } + + unsigned int glyphIndex() { + return m_glyphInfo[m_index].codepoint; + } + + void glyphOffsetXY(Glyph* glyph) { + glyph->x += m_glyphPos[m_index].x_offset / 64.0; + glyph->y += m_glyphPos[m_index].y_offset / 64.0; + } + + void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) { + x += m_glyphPos[m_index].x_advance / 64.0; + y += m_glyphPos[m_index].y_advance / 64.0; + } + + private: + hb_buffer_t* m_buffer; + hb_font_t* m_font; + hb_glyph_info_t* m_glyphInfo; + hb_glyph_position_t* m_glyphPos; + unsigned int m_glyphCount; + int m_index; + base::utf8_const_iterator m_it; + base::utf8_const_iterator m_end; + }; + +} // namespace ft + +#endif diff --git a/src/she/draw_text.cpp b/src/she/draw_text.cpp index 54b141e89..a9983e659 100644 --- a/src/she/draw_text.cpp +++ b/src/she/draw_text.cpp @@ -11,6 +11,7 @@ #include "she/draw_text.h" #include "ft/algorithm.h" +#include "ft/hb_shaper.h" #include "gfx/clip.h" #include "she/common/freetype_font.h" #include "she/common/generic_surface.h" @@ -26,8 +27,6 @@ gfx::Rect draw_text(Surface* surface, Font* font, DrawTextDelegate* delegate) { gfx::Rect textBounds; - bool drawChar = true; - bool moveCaret = true; switch (font->type()) { @@ -36,29 +35,26 @@ gfx::Rect draw_text(Surface* surface, Font* font, while (it != end) { int chr = *it; if (delegate) - delegate->preProcessChar(it, end, chr, fg, bg, drawChar, moveCaret); + delegate->preProcessChar(chr, fg, bg); - if (moveCaret) { - gfx::Rect charBounds = ssFont->getCharBounds(chr); - gfx::Rect outCharBounds(x, y, charBounds.w, charBounds.h); - if (delegate && !delegate->preDrawChar(outCharBounds)) - break; + gfx::Rect charBounds = ssFont->getCharBounds(chr); + gfx::Rect outCharBounds(x, y, charBounds.w, charBounds.h); + if (delegate && !delegate->preDrawChar(outCharBounds)) + break; - if (!charBounds.isEmpty()) { - if (surface && drawChar) { - Surface* sheet = ssFont->getSurfaceSheet(); - SurfaceLock lock(sheet); - surface->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds)); - } + if (!charBounds.isEmpty()) { + if (surface) { + Surface* sheet = ssFont->getSurfaceSheet(); + SurfaceLock lock(sheet); + surface->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds)); } - - textBounds |= outCharBounds; - if (delegate) - delegate->postDrawChar(outCharBounds); - - x += charBounds.w; } + textBounds |= outCharBounds; + if (delegate) + delegate->postDrawChar(outCharBounds); + + x += charBounds.w; ++it; } break; @@ -67,7 +63,6 @@ gfx::Rect draw_text(Surface* surface, Font* font, case FontType::kTrueType: { FreeTypeFont* ttFont = static_cast(font); bool antialias = ttFont->face().antialias(); - bool done = false; int fg_alpha = gfx::geta(fg); gfx::Rect clipBounds; @@ -78,118 +73,106 @@ gfx::Rect draw_text(Surface* surface, Font* font, } ft::ForEachGlyph feg(ttFont->face()); - while (it != end) { - int chr = *it; - if (delegate) - delegate->preProcessChar(it, end, chr, fg, bg, drawChar, moveCaret); + if (feg.initialize(it, end)) { + do { + if (delegate) + delegate->preProcessChar(feg.unicodeChar(), fg, bg); - if (!moveCaret) { - ++it; - continue; - } + auto glyph = feg.glyph(); + if (!glyph) + continue; - feg.processChar( - chr, - [x, y, fg, fg_alpha, bg, antialias, surface, - &clipBounds, &textBounds, &fd, &done, delegate, drawChar] - (const ft::Glyph& glyph) { - gfx::Rect origDstBounds( - x + int(glyph.startX), - y + int(glyph.y), - int(glyph.endX) - int(glyph.startX), - int(glyph.bitmap->rows) ? int(glyph.bitmap->rows): 1); + gfx::Rect origDstBounds( + x + int(glyph->startX), + y + int(glyph->y), + int(glyph->endX) - int(glyph->startX), + int(glyph->bitmap->rows) ? int(glyph->bitmap->rows): 1); - if (delegate && !delegate->preDrawChar(origDstBounds)) { - done = true; - return; - } - origDstBounds.x = x + int(glyph.x); - origDstBounds.w = int(glyph.bitmap->width); - origDstBounds.h = int(glyph.bitmap->rows); + if (delegate && !delegate->preDrawChar(origDstBounds)) + break; - gfx::Rect dstBounds = origDstBounds; - if (surface) - dstBounds &= clipBounds; + origDstBounds.x = x + int(glyph->x); + origDstBounds.w = int(glyph->bitmap->width); + origDstBounds.h = int(glyph->bitmap->rows); - if (surface && drawChar && !dstBounds.isEmpty()) { - int clippedRows = dstBounds.y - origDstBounds.y; - int dst_y = dstBounds.y; - int t; - for (int v=0; vbuffer - + (v+clippedRows)*glyph.bitmap->pitch; - int dst_x = dstBounds.x; - uint32_t* dst_address = - (uint32_t*)surface->getData(dst_x, dst_y); + gfx::Rect dstBounds = origDstBounds; + if (surface) + dstBounds &= clipBounds; - // Skip first clipped pixels - for (int u=0; ubitmap->buffer + + (v+clippedRows)*glyph->bitmap->pitch; + int dst_x = dstBounds.x; + uint32_t* dst_address = + (uint32_t*)surface->getData(dst_x, dst_y); + + // Skip first clipped pixels + for (int u=0; u> fd.redShift), - ((backdrop & fd.greenMask) >> fd.greenShift), - ((backdrop & fd.blueMask) >> fd.blueShift), - ((backdrop & fd.alphaMask) >> fd.alphaShift)); - - gfx::Color output = gfx::rgba(gfx::getr(fg), - gfx::getg(fg), - gfx::getb(fg), - MUL_UN8(fg_alpha, alpha, t)); - if (gfx::geta(bg) > 0) - output = blend(blend(backdropColor, bg), output); - else - output = blend(backdropColor, output); - - *dst_address = - ((gfx::getr(output) << fd.redShift ) & fd.redMask ) | - ((gfx::getg(output) << fd.greenShift) & fd.greenMask) | - ((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) | - ((gfx::geta(output) << fd.alphaShift) & fd.alphaMask); - - ++dst_address; } } + + for (int u=0; u> fd.redShift), + ((backdrop & fd.greenMask) >> fd.greenShift), + ((backdrop & fd.blueMask) >> fd.blueShift), + ((backdrop & fd.alphaMask) >> fd.alphaShift)); + + gfx::Color output = gfx::rgba(gfx::getr(fg), + gfx::getg(fg), + gfx::getb(fg), + MUL_UN8(fg_alpha, alpha, t)); + if (gfx::geta(bg) > 0) + output = blend(blend(backdropColor, bg), output); + else + output = blend(backdropColor, output); + + *dst_address = + ((gfx::getr(output) << fd.redShift ) & fd.redMask ) | + ((gfx::getg(output) << fd.greenShift) & fd.greenMask) | + ((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) | + ((gfx::geta(output) << fd.alphaShift) & fd.alphaMask); + + ++dst_address; + } } + } - if (!origDstBounds.w) origDstBounds.w = 1; - if (!origDstBounds.h) origDstBounds.h = 1; - textBounds |= origDstBounds; - if (delegate) - delegate->postDrawChar(origDstBounds); - }); - - if (done) - break; - - ++it; + if (!origDstBounds.w) origDstBounds.w = 1; + if (!origDstBounds.h) origDstBounds.h = 1; + textBounds |= origDstBounds; + if (delegate) + delegate->postDrawChar(origDstBounds); + } while (feg.nextChar()); } break; } diff --git a/src/she/draw_text.h b/src/she/draw_text.h index a8ef4cc5b..9caf1067c 100644 --- a/src/she/draw_text.h +++ b/src/she/draw_text.h @@ -22,18 +22,10 @@ namespace she { public: virtual ~DrawTextDelegate() { } - // This is called before drawing the character. Here you can - // modify the final painted character (e.g. for passwords you can - // modify chr='*') and change the specific fg/bg color for this - // char (e.g. to change the color depending if is a - // selected/highlighted portion of text). - virtual void preProcessChar(const base::utf8_const_iterator& it, - const base::utf8_const_iterator& end, - int& chr, + // This is called before drawing the character. + virtual void preProcessChar(const int chr, gfx::Color& fg, - gfx::Color& bg, - bool& drawChar, - bool& moveCaret) { + gfx::Color& bg) { // Do nothing } diff --git a/src/ui/entry.cpp b/src/ui/entry.cpp index 37e3fdaf7..34550eba5 100644 --- a/src/ui/entry.cpp +++ b/src/ui/entry.cpp @@ -59,7 +59,6 @@ Entry::Entry(const std::size_t maxsize, const char* format, ...) , m_hidden(false) , m_state(false) , m_readonly(false) - , m_password(false) , m_recent_focused(false) , m_lock_selection(false) , m_translate_dead_keys(true) @@ -101,21 +100,11 @@ bool Entry::isReadOnly() const return m_readonly; } -bool Entry::isPassword() const -{ - return m_password; -} - void Entry::setReadOnly(bool state) { m_readonly = state; } -void Entry::setPassword(bool state) -{ - m_password = state; -} - void Entry::showCaret() { m_hidden = false; diff --git a/src/ui/entry.h b/src/ui/entry.h index 268fecbde..7b451ac1f 100644 --- a/src/ui/entry.h +++ b/src/ui/entry.h @@ -23,10 +23,8 @@ namespace ui { void setMaxTextLength(const std::size_t maxsize); - bool isPassword() const; bool isReadOnly() const; void setReadOnly(bool state); - void setPassword(bool state); void showCaret(); void hideCaret(); @@ -95,7 +93,6 @@ namespace ui { bool m_hidden; bool m_state; // show or not the text caret bool m_readonly; - bool m_password; bool m_recent_focused; bool m_lock_selection; bool m_translate_dead_keys; diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index a0b4b7e15..c633c94d4 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -246,16 +246,10 @@ public: gfx::Rect bounds() const { return m_bounds; } - void preProcessChar(const base::utf8_const_iterator& it, - const base::utf8_const_iterator& end, - int& chr, + void preProcessChar(const int chr, gfx::Color& fg, - gfx::Color& bg, - bool& drawChar, - bool& moveCaret) override { - if (!m_surface) - drawChar = false; - else { + gfx::Color& bg) override { + if (m_surface) { if (m_mnemonic && std::tolower(chr) == m_mnemonic) { m_underscoreColor = fg; m_mnemonic = 0; // Just one time diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 37f9d3a8a..2d8d29a3f 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -1,5 +1,5 @@ # ASEPRITE -# Copyright (C) 2001-2016 David Capello +# Copyright (C) 2001-2017 David Capello include_directories(.) @@ -85,6 +85,7 @@ if(NOT USE_SHARED_FREETYPE) endif() endif() +add_subdirectory(harfbuzz-cmake) add_subdirectory(simpleini) # Add cmark without tests diff --git a/third_party/harfbuzz b/third_party/harfbuzz new file mode 160000 index 000000000..8a4c80563 --- /dev/null +++ b/third_party/harfbuzz @@ -0,0 +1 @@ +Subproject commit 8a4c80563dbc74608e5c65df395a13ee4840cbf3