File Improve-built-in-line_block-characters-drawing.patch of Package konsole
From 13132fc77bf90d0a39c62676f3ac75caa581962f Mon Sep 17 00:00:00 2001
From: Mariusz Glebocki <mglb@arccos-1.net>
Date: Thu, 7 Feb 2019 01:36:52 +0100
Subject: Improve built-in line/block characters drawing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Summary:
* Fix bold lines (BUG 402415).
* Make drawing pixel-perfect.
* Make line width proportional to font size.
* Move relevant code to separate file and namespace.
* Remove code for checking supported line characters from Character
class. Information about what is supported is now in one place
together width drawing code.
* Remove fontembedder/LineFont files (no longer used).
* Add test script for displaying supported characters table.
* Add triple and quadruple dashes (U+2504...U+250B).
* Change shade block characters (U+2591...U+2593) look. When
antialiasing is turned on, shades are drawn as transculent solid
rectangles with 25%, 50% and 75% alpha. This matches the characters
name/description and their usage. Without antialiasing, previous
method with patterns is used.
**Screenshots**
Font size: 10pt; character width: 8px
{F6602823}
Font size: 11pt; character width: 9px
{F6602824}
Font size: 12pt; character width: 10px
{F6602825}
Font size: 13-14pt; character width: 11px; w/o antialiasing
{F6602826}
Font size: 13-14pt; character width: 11px
{F6602827}
Font size: 15pt; character width: 12px
{F6602828}
Font size: 6-7pt; character width: 5px
{F6602829}
Font size: 8-9pt; character width: 7px; w/o antialiasing
{F6602830}
Font size: 8-9pt; character width: 7px
{F6602831}
Alignment test (8pt)
{F6602832}
Note: Copyrights in LineBlockCharactersDrawer.cpp are based on
`git blame -w src/TerminalDisplay.cpp` executed before moving the code
to a separate file. Years from first/last commit. Authors sorted by
year. Whitespace-only changes were ignored. Maksim's code was commited
by Waldo Bastian who mentioned him as the author in commit message
(see 5062b40dd).
BUG: 402415
Test Plan:
== Common steps for all tests ==
* Open //Edit Current Profile → Appearance//.
* Turn on //Draw intense colors in bold font//.
* Turn off //Use line characters contained in font//.
* (Optional) select a font which is able to display bold characters in
Konsole (e.g. DejaVu Sans Mono).
== Check characters validity ==
* Run `./tests/line_block_characters_table.py`.
* Open //Edit Current Profile → Appearance//.
* By switching //Use line characters contained in font// on and off,
compare built-in characters drawing with characters from a font.
General shape and line directions must be the same. Small offsets,
line width differences (as long as proportions between lines in
a character are kept), and quality differences are allowed.
== Review overall quality ==
* Run `./tests/line_block_characters_table.py`.
* Review glyphs quality in different font sizes.
* Open //Edit Current Profile → Appearance//.
* Toggle //Smooth fonts//, review quality again.
== Check alignment ==
* Display `tests/UTF-8-demo.txt`
* At the bottom of the file you can find a few alignment images. Check
if all lines align properly. If you're unsure how it should look,
compare it with font characters by turning on //Use line characters
contained in font// option.
Reviewers: #konsole, #vdg, fvogt, hindenburg
Reviewed By: #konsole, hindenburg
Subscribers: hindenburg, sandsmark, fvogt, konsole-devel
Tags: #konsole
Differential Revision: https://phabricator.kde.org/D18735
---
src/CMakeLists.txt | 3 +-
src/Character.h | 24 --
src/LineBlockCharacters.cpp | 722 ++++++++++++++++++++++++++++++++
src/LineBlockCharacters.h | 57 +++
src/LineFont.h | 21 -
src/LineFont.src | 786 -----------------------------------
src/TerminalDisplay.cpp | 392 +----------------
tests/line_block_characters_table.py | 67 +++
tools/CMakeLists.txt | 15 -
tools/fontembedder.cpp | 138 ------
10 files changed, 862 insertions(+), 1363 deletions(-)
create mode 100644 src/LineBlockCharacters.cpp
create mode 100644 src/LineBlockCharacters.h
delete mode 100644 src/LineFont.h
delete mode 100644 src/LineFont.src
create mode 100755 tests/line_block_characters_table.py
delete mode 100644 tools/fontembedder.cpp
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5b366c6..eae3344 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -34,8 +34,6 @@ endif()
option(REMOVE_SENDTEXT_RUNCOMMAND_DBUS_METHODS "Konsole: remove sendText and runCommand dbus methods" OFF)
### Development tools
-option(KONSOLE_BUILD_FONTEMBEDDER "Konsole: build fontembedder executable" OFF)
-option(KONSOLE_GENERATE_LINEFONT "Konsole: regenerate LineFont file" OFF)
option(KONSOLE_BUILD_UNI2CHARACTERWIDTH "Konsole: build uni2characterwidth executable" OFF)
### Konsole source files shared between embedded terminal and main application
@@ -99,6 +97,7 @@ set(konsoleprivate_SRCS ${sessionadaptors_SRCS}
ExtendedCharTable.cpp
TerminalDisplay.cpp
TerminalDisplayAccessible.cpp
+ LineBlockCharacters.cpp
ViewContainer.cpp
ViewManager.cpp
ViewProperties.cpp
diff --git a/src/Character.h b/src/Character.h
index ed54715..3e94c8f 100644
--- a/src/Character.h
+++ b/src/Character.h
@@ -54,21 +54,6 @@ const RenditionFlags RE_CONCEAL = (1 << 9);
const RenditionFlags RE_OVERLINE = (1 << 10);
/**
- * Unicode character in the range of U+2500 ~ U+257F are known as line
- * characters, or box-drawing characters. Currently, konsole draws those
- * characters itself, instead of using the glyph provided by the font.
- * Unfortunately, the triple and quadruple dash lines (┄┅┆┇┈┉┊┋) are too
- * detailed too be drawn cleanly at normal font scales without anti
- * -aliasing, so those are drawn as regular characters.
- */
-inline bool isSupportedLineChar(uint codePoint)
-{
- return ((codePoint & 0xFF80) == 0x2500 // Unicode block: Mathematical Symbols - Box Drawing
- && !(0x2504 <= codePoint && codePoint <= 0x250B)) || // Triple and quadruple dash range
- (codePoint >= 0x2580 && codePoint <= 0x259F); // Block characters
-}
-
-/**
* A single character in the terminal which consists of a unicode character
* value, foreground and background colors and a set of rendition attributes
* which specify how it should be drawn.
@@ -143,15 +128,6 @@ public:
*/
friend bool operator !=(const Character &a, const Character &b);
- inline bool isLineChar() const
- {
- if (rendition & RE_EXTENDED_CHAR) {
- return false;
- } else {
- return isSupportedLineChar(character);
- }
- }
-
inline bool isSpace() const
{
if (rendition & RE_EXTENDED_CHAR) {
diff --git a/src/LineBlockCharacters.cpp b/src/LineBlockCharacters.cpp
new file mode 100644
index 0000000..a562f45
--- /dev/null
+++ b/src/LineBlockCharacters.cpp
@@ -0,0 +1,736 @@
+/*
+ This file is part of Konsole, a terminal emulator for KDE.
+
+ Copyright 2018-2019 by Mariusz Glebocki <mglb@arccos-1.net>
+ Copyright 2018 by Martin T. H. Sandsmark <martin.sandsmark@kde.org>
+ Copyright 2015-2018 by Kurt Hindenburg <kurt.hindenburg@gmail.com>
+ Copyright 2005 by Maksim Orlovich
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+// Own
+#include "LineBlockCharacters.h"
+
+// Qt
+#include <QPainter>
+
+namespace Konsole {
+namespace LineBlockCharacters {
+
+enum LineType {
+ LtNone = 0,
+ LtDouble = 1,
+ LtLight = 2,
+ LtHeavy = 3,
+};
+
+// PackedLineTypes is an 8-bit number representing types of 4 line character's lines. Each line is
+// represented by 2 bits. Lines order, starting from MSB: top, right, bottom, left.
+static inline constexpr quint8 makePackedLineTypes(LineType top, LineType right, LineType bottom, LineType left)
+{
+ return (int(top) & 3) << 6 | (int(right) & 3) << 4 | (int(bottom) & 3) << 2 | (int(left) & 3);
+}
+
+static constexpr const quint8 PackedLineTypesLut[] = {
+ // top right bottom left
+ makePackedLineTypes(LtNone , LtLight , LtNone , LtLight ), /* U+2500 ─ */
+ makePackedLineTypes(LtNone , LtHeavy , LtNone , LtHeavy ), /* U+2501 ━ */
+ makePackedLineTypes(LtLight , LtNone , LtLight , LtNone ), /* U+2502 │ */
+ makePackedLineTypes(LtHeavy , LtNone , LtHeavy , LtNone ), /* U+2503 ┃ */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* U+2504-0x250b */
+ makePackedLineTypes(LtNone , LtLight , LtLight , LtNone ), /* U+250C ┌ */
+ makePackedLineTypes(LtNone , LtHeavy , LtLight , LtNone ), /* U+250D ┍ */
+ makePackedLineTypes(LtNone , LtLight , LtHeavy , LtNone ), /* U+250E ┎ */
+ makePackedLineTypes(LtNone , LtHeavy , LtHeavy , LtNone ), /* U+250F ┏ */
+ makePackedLineTypes(LtNone , LtNone , LtLight , LtLight ), /* U+2510 ┐ */
+ makePackedLineTypes(LtNone , LtNone , LtLight , LtHeavy ), /* U+2511 ┑ */
+ makePackedLineTypes(LtNone , LtNone , LtHeavy , LtLight ), /* U+2512 ┒ */
+ makePackedLineTypes(LtNone , LtNone , LtHeavy , LtHeavy ), /* U+2513 ┓ */
+ makePackedLineTypes(LtLight , LtLight , LtNone , LtNone ), /* U+2514 └ */
+ makePackedLineTypes(LtLight , LtHeavy , LtNone , LtNone ), /* U+2515 ┕ */
+ makePackedLineTypes(LtHeavy , LtLight , LtNone , LtNone ), /* U+2516 ┖ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtNone , LtNone ), /* U+2517 ┗ */
+ makePackedLineTypes(LtLight , LtNone , LtNone , LtLight ), /* U+2518 ┘ */
+ makePackedLineTypes(LtLight , LtNone , LtNone , LtHeavy ), /* U+2519 ┙ */
+ makePackedLineTypes(LtHeavy , LtNone , LtNone , LtLight ), /* U+251A ┚ */
+ makePackedLineTypes(LtHeavy , LtNone , LtNone , LtHeavy ), /* U+251B ┛ */
+ makePackedLineTypes(LtLight , LtLight , LtLight , LtNone ), /* U+251C ├ */
+ makePackedLineTypes(LtLight , LtHeavy , LtLight , LtNone ), /* U+251D ┝ */
+ makePackedLineTypes(LtHeavy , LtLight , LtLight , LtNone ), /* U+251E ┞ */
+ makePackedLineTypes(LtLight , LtLight , LtHeavy , LtNone ), /* U+251F ┟ */
+ makePackedLineTypes(LtHeavy , LtLight , LtHeavy , LtNone ), /* U+2520 ┠ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtNone ), /* U+2521 ┡ */
+ makePackedLineTypes(LtLight , LtHeavy , LtHeavy , LtNone ), /* U+2522 ┢ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtHeavy , LtNone ), /* U+2523 ┣ */
+ makePackedLineTypes(LtLight , LtNone , LtLight , LtLight ), /* U+2524 ┤ */
+ makePackedLineTypes(LtLight , LtNone , LtLight , LtHeavy ), /* U+2525 ┥ */
+ makePackedLineTypes(LtHeavy , LtNone , LtLight , LtLight ), /* U+2526 ┦ */
+ makePackedLineTypes(LtLight , LtNone , LtHeavy , LtLight ), /* U+2527 ┧ */
+ makePackedLineTypes(LtHeavy , LtNone , LtHeavy , LtLight ), /* U+2528 ┨ */
+ makePackedLineTypes(LtHeavy , LtNone , LtLight , LtHeavy ), /* U+2529 ┩ */
+ makePackedLineTypes(LtLight , LtNone , LtHeavy , LtHeavy ), /* U+252A ┪ */
+ makePackedLineTypes(LtHeavy , LtNone , LtHeavy , LtHeavy ), /* U+252B ┫ */
+ makePackedLineTypes(LtNone , LtLight , LtLight , LtLight ), /* U+252C ┬ */
+ makePackedLineTypes(LtNone , LtLight , LtLight , LtHeavy ), /* U+252D ┭ */
+ makePackedLineTypes(LtNone , LtHeavy , LtLight , LtLight ), /* U+252E ┮ */
+ makePackedLineTypes(LtNone , LtHeavy , LtLight , LtHeavy ), /* U+252F ┯ */
+ makePackedLineTypes(LtNone , LtLight , LtHeavy , LtLight ), /* U+2530 ┰ */
+ makePackedLineTypes(LtNone , LtLight , LtHeavy , LtHeavy ), /* U+2531 ┱ */
+ makePackedLineTypes(LtNone , LtHeavy , LtHeavy , LtLight ), /* U+2532 ┲ */
+ makePackedLineTypes(LtNone , LtHeavy , LtHeavy , LtHeavy ), /* U+2533 ┳ */
+ makePackedLineTypes(LtLight , LtLight , LtNone , LtLight ), /* U+2534 ┴ */
+ makePackedLineTypes(LtLight , LtLight , LtNone , LtHeavy ), /* U+2535 ┵ */
+ makePackedLineTypes(LtLight , LtHeavy , LtNone , LtLight ), /* U+2536 ┶ */
+ makePackedLineTypes(LtLight , LtHeavy , LtNone , LtHeavy ), /* U+2537 ┷ */
+ makePackedLineTypes(LtHeavy , LtLight , LtNone , LtLight ), /* U+2538 ┸ */
+ makePackedLineTypes(LtHeavy , LtLight , LtNone , LtHeavy ), /* U+2539 ┹ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtNone , LtLight ), /* U+253A ┺ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtNone , LtHeavy ), /* U+253B ┻ */
+ makePackedLineTypes(LtLight , LtLight , LtLight , LtLight ), /* U+253C ┼ */
+ makePackedLineTypes(LtLight , LtLight , LtLight , LtHeavy ), /* U+253D ┽ */
+ makePackedLineTypes(LtLight , LtHeavy , LtLight , LtLight ), /* U+253E ┾ */
+ makePackedLineTypes(LtLight , LtHeavy , LtLight , LtHeavy ), /* U+253F ┿ */
+ makePackedLineTypes(LtHeavy , LtLight , LtLight , LtLight ), /* U+2540 ╀ */
+ makePackedLineTypes(LtLight , LtLight , LtHeavy , LtLight ), /* U+2541 ╁ */
+ makePackedLineTypes(LtHeavy , LtLight , LtHeavy , LtLight ), /* U+2542 ╂ */
+ makePackedLineTypes(LtHeavy , LtLight , LtLight , LtHeavy ), /* U+2543 ╃ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtLight ), /* U+2544 ╄ */
+ makePackedLineTypes(LtLight , LtLight , LtHeavy , LtHeavy ), /* U+2545 ╅ */
+ makePackedLineTypes(LtLight , LtHeavy , LtHeavy , LtLight ), /* U+2546 ╆ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtHeavy ), /* U+2547 ╇ */
+ makePackedLineTypes(LtLight , LtHeavy , LtHeavy , LtHeavy ), /* U+2548 ╈ */
+ makePackedLineTypes(LtHeavy , LtLight , LtHeavy , LtHeavy ), /* U+2549 ╉ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtHeavy , LtLight ), /* U+254A ╊ */
+ makePackedLineTypes(LtHeavy , LtHeavy , LtHeavy , LtHeavy ), /* U+254B ╋ */
+ 0, 0, 0, 0, /* U+254C - U+254F */
+ makePackedLineTypes(LtNone , LtDouble, LtNone , LtDouble), /* U+2550 ═ */
+ makePackedLineTypes(LtDouble, LtNone , LtDouble, LtNone ), /* U+2551 ║ */
+ makePackedLineTypes(LtNone , LtDouble, LtLight , LtNone ), /* U+2552 ╒ */
+ makePackedLineTypes(LtNone , LtLight , LtDouble, LtNone ), /* U+2553 ╓ */
+ makePackedLineTypes(LtNone , LtDouble, LtDouble, LtNone ), /* U+2554 ╔ */
+ makePackedLineTypes(LtNone , LtNone , LtLight , LtDouble), /* U+2555 ╕ */
+ makePackedLineTypes(LtNone , LtNone , LtDouble, LtLight ), /* U+2556 ╖ */
+ makePackedLineTypes(LtNone , LtNone , LtDouble, LtDouble), /* U+2557 ╗ */
+ makePackedLineTypes(LtLight , LtDouble, LtNone , LtNone ), /* U+2558 ╘ */
+ makePackedLineTypes(LtDouble, LtLight , LtNone , LtNone ), /* U+2559 ╙ */
+ makePackedLineTypes(LtDouble, LtDouble, LtNone , LtNone ), /* U+255A ╚ */
+ makePackedLineTypes(LtLight , LtNone , LtNone , LtDouble), /* U+255B ╛ */
+ makePackedLineTypes(LtDouble, LtNone , LtNone , LtLight ), /* U+255C ╜ */
+ makePackedLineTypes(LtDouble, LtNone , LtNone , LtDouble), /* U+255D ╝ */
+ makePackedLineTypes(LtLight , LtDouble, LtLight , LtNone ), /* U+255E ╞ */
+ makePackedLineTypes(LtDouble, LtLight , LtDouble, LtNone ), /* U+255F ╟ */
+ makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtNone ), /* U+2560 ╠ */
+ makePackedLineTypes(LtLight , LtNone , LtLight , LtDouble), /* U+2561 ╡ */
+ makePackedLineTypes(LtDouble, LtNone , LtDouble, LtLight ), /* U+2562 ╢ */
+ makePackedLineTypes(LtDouble, LtNone , LtDouble, LtDouble), /* U+2563 ╣ */
+ makePackedLineTypes(LtNone , LtDouble, LtLight , LtDouble), /* U+2564 ╤ */
+ makePackedLineTypes(LtNone , LtLight , LtDouble, LtLight ), /* U+2565 ╥ */
+ makePackedLineTypes(LtNone , LtDouble, LtDouble, LtDouble), /* U+2566 ╦ */
+ makePackedLineTypes(LtLight , LtDouble, LtNone , LtDouble), /* U+2567 ╧ */
+ makePackedLineTypes(LtDouble, LtLight , LtNone , LtLight ), /* U+2568 ╨ */
+ makePackedLineTypes(LtDouble, LtDouble, LtNone , LtDouble), /* U+2569 ╩ */
+ makePackedLineTypes(LtLight , LtDouble, LtLight , LtDouble), /* U+256A ╪ */
+ makePackedLineTypes(LtDouble, LtLight , LtDouble, LtLight ), /* U+256B ╫ */
+ makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtDouble), /* U+256C ╬ */
+ 0, 0, 0, 0, 0, 0, 0, /* U+256D - U+2573 */
+ makePackedLineTypes(LtNone , LtNone , LtNone , LtLight ), /* U+2574 ╴ */
+ makePackedLineTypes(LtLight , LtNone , LtNone , LtNone ), /* U+2575 ╵ */
+ makePackedLineTypes(LtNone , LtLight , LtNone , LtNone ), /* U+2576 ╶ */
+ makePackedLineTypes(LtNone , LtNone , LtLight , LtNone ), /* U+2577 ╷ */
+ makePackedLineTypes(LtNone , LtNone , LtNone , LtHeavy ), /* U+2578 ╸ */
+ makePackedLineTypes(LtHeavy , LtNone , LtNone , LtNone ), /* U+2579 ╹ */
+ makePackedLineTypes(LtNone , LtHeavy , LtNone , LtNone ), /* U+257A ╺ */
+ makePackedLineTypes(LtNone , LtNone , LtHeavy , LtNone ), /* U+257B ╻ */
+ makePackedLineTypes(LtNone , LtHeavy , LtNone , LtLight ), /* U+257C ╼ */
+ makePackedLineTypes(LtLight , LtNone , LtHeavy , LtNone ), /* U+257D ╽ */
+ makePackedLineTypes(LtNone , LtLight , LtNone , LtHeavy ), /* U+257E ╾ */
+ makePackedLineTypes(LtHeavy , LtNone , LtLight , LtNone ), /* U+257F ╿ */
+};
+
+
+
+// Bitwise rotate left
+template <typename T>
+inline static T rotateBitsLeft(T value, quint8 amount)
+{
+ static_assert (std::is_unsigned<T>(), "T must be unsigned type");
+ Q_ASSERT(amount < sizeof(value) * 8);
+ return value << amount | value >> (sizeof(value) * 8 - amount);
+}
+
+inline static const QPen pen(const QPainter &paint, uint lineWidth)
+{
+ return QPen(paint.pen().brush(), lineWidth, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+}
+
+
+
+static inline uint lineWidth(uint fontWidth, bool heavy, bool bold)
+{
+ static const qreal LightWidthToFontWidthRatio = 1.0 / 6.5;
+ static const qreal HeavyHalfExtraToLightRatio = 1.0 / 3.0;
+ static const qreal BoldCoefficient = 1.5;
+
+ // ▄▄▄▄▄▄▄ } heavyHalfExtraWidth ⎫
+ // ██████████████ } lightWidth ⎬ heavyWidth
+ // ▀▀▀▀▀▀▀ ⎭
+ // light heavy
+
+ const qreal baseWidth = fontWidth * LightWidthToFontWidthRatio;
+ const qreal boldCoeff = bold ? BoldCoefficient : 1.0;
+ // Unless font size is too small, make bold lines at least 1px wider than regular lines
+ const qreal minWidth = bold && fontWidth >= 7 ? baseWidth + 1.0 : 1.0;
+ const uint lightWidth = qRound(qMax(baseWidth * boldCoeff, minWidth));
+ const uint heavyHalfExtraWidth = qRound(qMax(lightWidth * HeavyHalfExtraToLightRatio, 1.0));
+
+ return heavy ? lightWidth + 2 * heavyHalfExtraWidth : lightWidth;
+}
+
+// Draws characters composed of straight solid lines
+static bool drawBasicLineCharacter(QPainter& paint, int x, int y, int w, int h, uchar code,
+ bool bold)
+{
+ quint8 packedLineTypes = code >= sizeof(PackedLineTypesLut) ? 0 : PackedLineTypesLut[code];
+ if (packedLineTypes == 0) {
+ return false;
+ }
+
+ const uint lightLineWidth = lineWidth(w, false, bold);
+ const uint heavyLineWidth = lineWidth(w, true, bold);
+ // Distance from double line's parallel axis to each line's parallel axis
+ const uint doubleLinesDistance = lightLineWidth;
+
+ const QPen lightPen = pen(paint, lightLineWidth);
+ const QPen heavyPen = pen(paint, heavyLineWidth);
+
+ static constexpr const unsigned LinesNum = 4;
+
+ // Pixel aligned center point
+ const QPointF center = {
+ x + int(w/2) + 0.5 * (lightLineWidth % 2),
+ y + int(h/2) + 0.5 * (lightLineWidth % 2),
+ };
+
+ // Lines starting points, on the cell edges
+ const QPointF origin[] = {
+ QPointF(center.x(), y ),
+ QPointF(x+w , center.y() ),
+ QPointF(center.x(), y+h ),
+ QPointF(x , center.y() ),
+ };
+ // Unit vectors with directions from center to the line's origin point
+ static const QPointF dir[] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
+
+ const auto removeLineType = [&packedLineTypes](quint8 lineId)->void {
+ lineId = LinesNum - 1 - lineId % LinesNum;
+ packedLineTypes &= ~(3 << (2 * lineId));
+ };
+ const auto getLineType = [&packedLineTypes](quint8 lineId)->LineType {
+ lineId = LinesNum - 1 - lineId % LinesNum;
+ return LineType(packedLineTypes >> 2 * lineId & 3);
+ };
+
+ QPainterPath lightPath; // PainterPath for light lines (Painter Path Light)
+ QPainterPath heavyPath; // PainterPath for heavy lines (Painter Path Heavy)
+ // Returns ppl or pph depending on line type
+ const auto pathForLine = [&](quint8 lineId) -> QPainterPath& {
+ Q_ASSERT(getLineType(lineId) != LtNone);
+ return getLineType(lineId) == LtHeavy ? heavyPath : lightPath;
+ };
+
+ // Process all single up-down/left-right lines for every character that has them. Doing it here
+ // reduces amount of combinations below.
+ // Fully draws: ╋ ╂ ┃ ┿ ┼ │ ━ ─
+ for (unsigned topIndex = 0; topIndex < LinesNum/2; topIndex++) {
+ unsigned iB = (topIndex + 2) % LinesNum;
+ const bool isSingleLine = (getLineType(topIndex) == LtLight
+ || getLineType(topIndex) == LtHeavy);
+ if (isSingleLine && getLineType(topIndex) == getLineType(iB)) {
+ pathForLine(topIndex).moveTo(origin[topIndex]);
+ pathForLine(topIndex).lineTo(origin[iB]);
+ removeLineType(topIndex);
+ removeLineType(iB);
+ }
+ }
+
+ // Find base rotation of a character and map rotated line indices to the original rotation's
+ // indices. The base rotation is defined as the one with largest packedLineTypes value. This way
+ // we can use the same code for drawing 4 possible character rotations (see switch() below)
+
+ uint topIndex = 0; // index of an original top line in a base rotation
+ quint8 basePackedLineTypes = packedLineTypes;
+ for (uint i = 0; i < LinesNum; i++) {
+ const quint8 rotatedPackedLineTypes = rotateBitsLeft(packedLineTypes, i * 2);
+ if (rotatedPackedLineTypes > basePackedLineTypes) {
+ topIndex = i;
+ basePackedLineTypes = rotatedPackedLineTypes;
+ }
+ }
+ uint rightIndex = (topIndex + 1) % LinesNum;
+ uint bottomIndex = (topIndex + 2) % LinesNum;
+ uint leftIndex = (topIndex + 3) % LinesNum;
+
+ // Common paths
+ const auto drawDoubleUpRightShorterLine = [&](quint8 top, quint8 right) { // ╚
+ lightPath.moveTo(origin[top] + dir[right] * doubleLinesDistance);
+ lightPath.lineTo(center + (dir[right] + dir[top]) * doubleLinesDistance);
+ lightPath.lineTo(origin[right] + dir[top] * doubleLinesDistance);
+ };
+ const auto drawUpRight = [&](quint8 top, quint8 right) { // └┗
+ pathForLine(top).moveTo(origin[top]);
+ pathForLine(top).lineTo(center);
+ pathForLine(top).lineTo(origin[right]);
+ };
+
+ switch (basePackedLineTypes) {
+ case makePackedLineTypes(LtHeavy , LtNone , LtLight , LtNone ): // ╿ ; ╼ ╽ ╾ ╊ ╇ ╉ ╈ ╀ ┾ ╁ ┽
+ lightPath.moveTo(origin[bottomIndex]);
+ lightPath.lineTo(center + dir[topIndex] * lightLineWidth / 2.0);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtHeavy , LtNone , LtNone , LtNone ): // ╹ ; ╺ ╻ ╸ ┻ ┣ ┳ ┫ ┸ ┝ ┰ ┥
+ case makePackedLineTypes(LtLight , LtNone , LtNone , LtNone ): // ╵ ; ╶ ╷ ╴ ┷ ┠ ┯ ┨ ┴ ├ ┬ ┤
+ pathForLine(topIndex).moveTo(origin[topIndex]);
+ pathForLine(topIndex).lineTo(center);
+ break;
+
+ case makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtLight ): // ╄ ; ╃ ╆ ╅
+ drawUpRight(bottomIndex, leftIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtHeavy , LtHeavy , LtNone , LtNone ): // ┗ ; ┛ ┏ ┓
+ case makePackedLineTypes(LtLight , LtLight , LtNone , LtNone ): // └ ; ┘ ┌ ┐
+ drawUpRight(topIndex, rightIndex);
+ break;
+
+ case makePackedLineTypes(LtHeavy , LtLight , LtNone , LtNone ): // ┖ ; ┙ ┍ ┒
+ qSwap(leftIndex, rightIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtHeavy , LtNone , LtNone , LtLight ): // ┚ ; ┕ ┎ ┑
+ lightPath.moveTo(origin[leftIndex]);
+ lightPath.lineTo(center);
+ heavyPath.moveTo(origin[topIndex]);
+ heavyPath.lineTo(center + dir[bottomIndex] * lightLineWidth / 2.0);
+ break;
+
+ case makePackedLineTypes(LtLight , LtDouble, LtNone , LtNone ): // ╘ ; ╜ ╓ ╕
+ qSwap(leftIndex, rightIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtLight , LtNone , LtNone , LtDouble): // ╛ ; ╙ ╒ ╖
+ lightPath.moveTo(origin[topIndex]);
+ lightPath.lineTo(center + dir[bottomIndex] * doubleLinesDistance);
+ lightPath.lineTo(origin[leftIndex] + dir[bottomIndex] * doubleLinesDistance);
+ lightPath.moveTo(origin[leftIndex] - dir[bottomIndex] * doubleLinesDistance);
+ lightPath.lineTo(center - dir[bottomIndex] * doubleLinesDistance);
+ break;
+
+ case makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtNone ): // ┡ ; ┹ ┪ ┲
+ qSwap(leftIndex, bottomIndex);
+ qSwap(rightIndex, topIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtHeavy , LtHeavy , LtNone , LtLight ): // ┺ ; ┩ ┢ ┱
+ drawUpRight(topIndex, rightIndex);
+ lightPath.moveTo(origin[leftIndex]);
+ lightPath.lineTo(center);
+ break;
+
+ case makePackedLineTypes(LtHeavy , LtLight , LtLight , LtNone ): // ┞ ; ┵ ┧ ┮
+ qSwap(leftIndex, rightIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtHeavy , LtNone , LtLight , LtLight ): // ┦ ; ┶ ┟ ┭
+ heavyPath.moveTo(origin[topIndex]);
+ heavyPath.lineTo(center + dir[bottomIndex] * lightLineWidth / 2.0);
+ drawUpRight(bottomIndex, leftIndex);
+ break;
+
+ case makePackedLineTypes(LtLight , LtDouble, LtNone , LtDouble): // ╧ ; ╟ ╢ ╤
+ lightPath.moveTo(origin[topIndex]);
+ lightPath.lineTo(center - dir[bottomIndex] * doubleLinesDistance);
+ qSwap(leftIndex, bottomIndex);
+ qSwap(rightIndex, topIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ Q_FALLTHROUGH();
+#endif
+ case makePackedLineTypes(LtDouble, LtNone , LtDouble, LtNone ): // ║ ; ╫ ═ ╪
+ lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+ lightPath.lineTo(origin[bottomIndex] + dir[leftIndex] * doubleLinesDistance);
+ lightPath.moveTo(origin[topIndex] + dir[rightIndex] * doubleLinesDistance);
+ lightPath.lineTo(origin[bottomIndex] + dir[rightIndex] * doubleLinesDistance);
+ break;
+
+ case makePackedLineTypes(LtDouble, LtNone , LtNone , LtNone ): // ╨ ; ╞ ╥ ╡
+ lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+ lightPath.lineTo(center + dir[leftIndex] * doubleLinesDistance);
+ lightPath.moveTo(origin[topIndex] + dir[rightIndex] * doubleLinesDistance);
+ lightPath.lineTo(center + dir[rightIndex] * doubleLinesDistance);
+ break;
+
+ case makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtDouble): // ╬
+ drawDoubleUpRightShorterLine(topIndex, rightIndex);
+ drawDoubleUpRightShorterLine(bottomIndex, rightIndex);
+ drawDoubleUpRightShorterLine(topIndex, leftIndex);
+ drawDoubleUpRightShorterLine(bottomIndex, leftIndex);
+ break;
+
+ case makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtNone ): // ╠ ; ╩ ╣ ╦
+ lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+ lightPath.lineTo(origin[bottomIndex] + dir[leftIndex] * doubleLinesDistance);
+ drawDoubleUpRightShorterLine(topIndex, rightIndex);
+ drawDoubleUpRightShorterLine(bottomIndex, rightIndex);
+ break;
+
+ case makePackedLineTypes(LtDouble, LtDouble, LtNone , LtNone ): // ╚ ; ╝ ╔ ╗
+ lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+ lightPath.lineTo(center + (dir[leftIndex] + dir[bottomIndex]) * doubleLinesDistance);
+ lightPath.lineTo(origin[rightIndex] + dir[bottomIndex] * doubleLinesDistance);
+ drawDoubleUpRightShorterLine(topIndex, rightIndex);
+ break;
+ }
+
+ // Draw paths
+ if (!lightPath.isEmpty()) {
+ paint.strokePath(lightPath, lightPen);
+ }
+ if (!heavyPath.isEmpty()) {
+ paint.strokePath(heavyPath, heavyPen);
+ }
+
+ return true;
+}
+
+static inline bool drawDashedLineCharacter(QPainter &paint, int x, int y, int w, int h, uchar code,
+ bool bold)
+{
+ if (!((0x04 <= code && code <= 0x0B) || (0x4C <= code && code <= 0x4F))) {
+ return false;
+ }
+
+ const uint lightLineWidth = lineWidth(w, false, bold);
+ const uint heavyLineWidth = lineWidth(w, true, bold);
+
+ const auto lightPen = pen(paint, lightLineWidth);
+ const auto heavyPen = pen(paint, heavyLineWidth);
+
+ // Pixel aligned center point
+ const QPointF center = {
+ int(x + w/2.0) + 0.5 * (lightLineWidth%2),
+ int(y + h/2.0) + 0.5 * (lightLineWidth%2),
+ };
+
+ const qreal halfGapH = qMax(w / 20.0, 0.5);
+ const qreal halfGapV = qMax(h / 26.0, 0.5);
+ // For some reason vertical double dash has bigger gap
+ const qreal halfGapDDV = qMax(h / 14.0, 0.5);
+
+ static const int LinesNumMax = 4;
+
+ enum Orientation {Horizontal, Vertical};
+ struct {
+ int linesNum;
+ Orientation orientation;
+ QPen pen;
+ qreal halfGap;
+ } lineProps;
+
+ switch (code) {
+ case 0x4C: lineProps = {2, Horizontal, lightPen, halfGapH }; break; // ╌
+ case 0x4D: lineProps = {2, Horizontal, heavyPen, halfGapH }; break; // ╍
+ case 0x4E: lineProps = {2, Vertical , lightPen, halfGapDDV}; break; // ╎
+ case 0x4F: lineProps = {2, Vertical , heavyPen, halfGapDDV}; break; // ╏
+ case 0x04: lineProps = {3, Horizontal, lightPen, halfGapH }; break; // ┄
+ case 0x05: lineProps = {3, Horizontal, heavyPen, halfGapH }; break; // ┅
+ case 0x06: lineProps = {3, Vertical , lightPen, halfGapV }; break; // ┆
+ case 0x07: lineProps = {3, Vertical , heavyPen, halfGapV }; break; // ┇
+ case 0x08: lineProps = {4, Horizontal, lightPen, halfGapH }; break; // ┈
+ case 0x09: lineProps = {4, Horizontal, heavyPen, halfGapH }; break; // ┉
+ case 0x0A: lineProps = {4, Vertical , lightPen, halfGapV }; break; // ┊
+ case 0x0B: lineProps = {4, Vertical , heavyPen, halfGapV }; break; // ┋
+ }
+
+ Q_ASSERT(lineProps.linesNum <= LinesNumMax);
+ const int size = (lineProps.orientation == Horizontal ? w : h);
+ const int pos = (lineProps.orientation == Horizontal ? x : y);
+ QLineF lines[LinesNumMax];
+
+ for (int i = 0; i < lineProps.linesNum; i++) {
+ const qreal start = pos + qreal(size * (i )) / lineProps.linesNum;
+ const qreal end = pos + qreal(size * (i+1)) / lineProps.linesNum;
+ if (lineProps.orientation == Horizontal) {
+ lines[i] = QLineF{start + lineProps.halfGap, center.y(),
+ end - lineProps.halfGap, center.y()};
+ } else {
+ lines[i] = QLineF{center.x(), start + lineProps.halfGap,
+ center.x(), end - lineProps.halfGap};
+ }
+ }
+
+ const auto origPen = paint.pen();
+
+ paint.setPen(lineProps.pen);
+ paint.drawLines(lines, lineProps.linesNum);
+
+ paint.setPen(origPen);
+ return true;
+}
+
+static inline bool drawRoundedCornerLineCharacter(QPainter &paint, int x, int y, int w, int h,
+ uchar code, bool bold)
+{
+ if (!(0x6D <= code && code <= 0x70)) {
+ return false;
+ }
+
+ const uint lightLineWidth = lineWidth(w, false, bold);
+ const auto lightPen = pen(paint, lightLineWidth);
+
+ // Pixel aligned center point
+ const QPointF center = {
+ int(x + w/2.0) + 0.5 * (lightLineWidth%2),
+ int(y + h/2.0) + 0.5 * (lightLineWidth%2),
+ };
+
+ const int r = w * 3 / 8;
+ const int d = 2 * r;
+
+ QPainterPath path;
+
+ // lineTo() between moveTo and arcTo is redundant - arcTo() draws line
+ // to the arc's starting point
+ switch (code) {
+ case 0x6D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
+ path.moveTo(center.x(), y + h);
+ path.arcTo(center.x(), center.y(), d, d, 180, -90);
+ path.lineTo(x + w, center.y());
+ break;
+ case 0x6E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT
+ path.moveTo(center.x(), y + h);
+ path.arcTo(center.x() - d, center.y(), d, d, 0, 90);
+ path.lineTo(x, center.y());
+ break;
+ case 0x6F: // BOX DRAWINGS LIGHT ARC UP AND LEFT
+ path.moveTo(center.x(), y);
+ path.arcTo(center.x() - d, center.y() - d, d, d, 0, -90);
+ path.lineTo(x, center.y());
+ break;
+ case 0x70: // BOX DRAWINGS LIGHT ARC UP AND RIGHT
+ path.moveTo(center.x(), y);
+ path.arcTo(center.x(), center.y() - d, d, d, 180, 90);
+ path.lineTo(x + w, center.y());
+ break;
+ }
+ paint.strokePath(path, lightPen);
+
+ return true;
+}
+
+static inline bool drawDiagonalLineCharacter(QPainter &paint, int x, int y, int w, int h,
+ uchar code, bool bold)
+{
+ if (!(0x71 <= code && code <= 0x73)) {
+ return false;
+ }
+
+ const uint lightLineWidth = lineWidth(w, false, bold);
+ const auto lightPen = pen(paint, lightLineWidth);
+
+ const QLineF lines[] = {
+ QLineF(x+w, y, x , y+h), // '/'
+ QLineF(x , y, x+w, y+h), // '\'
+ };
+
+ const auto origPen = paint.pen();
+
+ paint.setPen(lightPen);
+ switch (code) {
+ case 0x71: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+ paint.drawLine(lines[0]);
+ break;
+ case 0x72: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+ paint.drawLine(lines[1]);
+ break;
+ case 0x73: // BOX DRAWINGS LIGHT DIAGONAL CROSS
+ paint.drawLines(lines, 2);
+ break;
+ }
+
+ paint.setPen(origPen);
+ return true;
+}
+
+static inline bool drawBlockCharacter(QPainter &paint, int x, int y, int w, int h, uchar code,
+ bool bold)
+{
+ Q_UNUSED(bold);
+
+ const QColor color = paint.pen().color();
+
+ // Center point
+ const QPointF center = {
+ x + w/2.0,
+ y + h/2.0,
+ };
+
+ // Default rect fills entire cell
+ QRectF rect(x, y, w, h);
+
+ // LOWER ONE EIGHTH BLOCK to LEFT ONE EIGHTH BLOCK
+ if (code >= 0x81 && code <= 0x8f) {
+ if (code < 0x88) { // Horizontal
+ const qreal height = h * (0x88 - code) / 8.0;
+ rect.setY(y + height);
+ rect.setHeight(h - height);
+ } else if (code > 0x88) { // Vertical
+ const qreal width = w * (0x90 - code) / 8.0;
+ rect.setWidth(width);
+ }
+ paint.fillRect(rect, color);
+
+ return true;
+ }
+
+ // Combinations of quarter squares
+ // LEFT ONE EIGHTH BLOCK to QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
+ if (code >= 0x96 && code <= 0x9F) {
+ const QRectF upperLeft (x , y , w/2.0, h/2.0);
+ const QRectF upperRight(center.x(), y , w/2.0, h/2.0);
+ const QRectF lowerLeft (x , center.y(), w/2.0, h/2.0);
+ const QRectF lowerRight(center.x(), center.y(), w/2.0, h/2.0);
+
+ QPainterPath path;
+
+ switch (code) {
+ case 0x96: // ▖
+ path.addRect(lowerLeft);
+ break;
+ case 0x97: // ▗
+ path.addRect(lowerRight);
+ break;
+ case 0x98: // ▘
+ path.addRect(upperLeft);
+ break;
+ case 0x99: // ▙
+ path.addRect(upperLeft);
+ path.addRect(lowerLeft);
+ path.addRect(lowerRight);
+ break;
+ case 0x9a: // ▚
+ path.addRect(upperLeft);
+ path.addRect(lowerRight);
+ break;
+ case 0x9b: // ▛
+ path.addRect(upperLeft);
+ path.addRect(upperRight);
+ path.addRect(lowerLeft);
+ break;
+ case 0x9c: // ▜
+ path.addRect(upperLeft);
+ path.addRect(upperRight);
+ path.addRect(lowerRight);
+ break;
+ case 0x9d: // ▝
+ path.addRect(upperRight);
+ break;
+ case 0x9e: // ▞
+ path.addRect(upperRight);
+ path.addRect(lowerLeft);
+ break;
+ case 0x9f: // ▟
+ path.addRect(upperRight);
+ path.addRect(lowerLeft);
+ path.addRect(lowerRight);
+ break;
+ }
+ paint.fillPath(path, color);
+
+ return true;
+ }
+
+ QBrush lightShade, mediumShade, darkShade;
+ if (paint.testRenderHint(QPainter::Antialiasing)) {
+ lightShade = QColor(color.red(), color.green(), color.blue(), 64);
+ mediumShade = QColor(color.red(), color.green(), color.blue(), 128);
+ darkShade = QColor(color.red(), color.green(), color.blue(), 192);
+ } else {
+ lightShade = QBrush(color, Qt::Dense6Pattern);
+ mediumShade = QBrush(color, Qt::Dense4Pattern);
+ darkShade = QBrush(color, Qt::Dense2Pattern);
+ }
+ // And the random stuff
+ switch (code) {
+ case 0x80: // Top half block
+ rect.setHeight(h/2.0);
+ paint.fillRect(rect, color);
+ return true;
+ case 0x90: // Right half block
+ rect.moveLeft(center.x());
+ paint.fillRect(rect, color);
+ return true;
+ case 0x94: // Top one eighth block
+ rect.setHeight(h/8.0);
+ paint.fillRect(rect, color);
+ return true;
+ case 0x95: { // Right one eighth block
+ const qreal width = 7 * w / 8.0;
+ rect.moveLeft(x + width);
+ paint.fillRect(rect, color);
+ return true;
+ }
+ case 0x91: // Light shade
+ paint.fillRect(rect, lightShade);
+ return true;
+ case 0x92: // Medium shade
+ paint.fillRect(rect, mediumShade);
+ return true;
+ case 0x93: // Dark shade
+ paint.fillRect(rect, darkShade);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void draw(QPainter &paint, const QRect &cellRect, const QChar &chr, bool bold)
+{
+ static const ushort FirstBoxDrawingCharacterCodePoint = 0x2500;
+ const uchar code = chr.unicode() - FirstBoxDrawingCharacterCodePoint;
+
+ int x = cellRect.x();
+ int y = cellRect.y();
+ int w = cellRect.width();
+ int h = cellRect.height();
+
+ // Each function below returns true when it has drawn the character, false otherwise.
+ drawBasicLineCharacter(paint, x, y, w, h, code, bold)
+ || drawDashedLineCharacter(paint, x, y, w, h, code, bold)
+ || drawRoundedCornerLineCharacter(paint, x, y, w, h, code, bold)
+ || drawDiagonalLineCharacter(paint, x, y, w, h, code, bold)
+ || drawBlockCharacter(paint, x, y, w, h, code, bold);
+}
+
+} // namespace LineBlockCharacters
+} // namespace Konsole
diff --git a/src/LineBlockCharacters.h b/src/LineBlockCharacters.h
new file mode 100644
index 0000000..a29f057
--- /dev/null
+++ b/src/LineBlockCharacters.h
@@ -0,0 +1,57 @@
+/*
+ This file is part of Konsole, a terminal emulator for KDE.
+
+ Copyright 2019 by Mariusz Glebocki <mglb@arccos-1.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef LINEBLOCKCHARACTERS_H
+#define LINEBLOCKCHARACTERS_H
+
+// Qt
+#include <QPainter>
+#include <QtGlobal>
+
+namespace Konsole {
+
+/**
+ * Helper functions for drawing characters from "Box Drawing" and "Block Elements" Unicode blocks.
+ */
+namespace LineBlockCharacters {
+
+ /**
+ * Returns true if the character can be drawn by draw() function.
+ *
+ * @param ucs4cp Character to test's UCS4 code point
+ */
+ inline static bool canDraw(uint ucs4cp) {
+ return (0x2500 <= ucs4cp && ucs4cp <= 0x259F);
+ }
+
+ /**
+ * Draws character.
+ *
+ * @param paint QPainter to draw on
+ * @param cellRect Rectangle to draw in
+ * @param chr Character to be drawn
+ */
+ void draw(QPainter &paint, const QRect &cellRect, const QChar &chr, bool bold);
+
+} // namespace LineBlockCharacters
+} // namespace Konsole
+
+#endif // LINEBLOCKCHARACTERS_H
diff --git a/src/LineFont.h b/src/LineFont.h
deleted file mode 100644
index 5f81754..0000000
--- a/src/LineFont.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// WARNING: Autogenerated by "fontembedder /Volumes/Projects/KDE/src/kde/applications/konsole/src/LineFont.src".
-// You probably do not want to hand-edit this!
-
-static const quint32 LineChars[] = {
- 0x00007c00, 0x000fffe0, 0x00421084, 0x00e739ce, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00427000, 0x004e7380, 0x00e77800, 0x00ef7bc0,
- 0x00421c00, 0x00439ce0, 0x00e73c00, 0x00e7bde0, 0x00007084, 0x000e7384, 0x000079ce, 0x000f7bce,
- 0x00001c84, 0x00039ce4, 0x00003dce, 0x0007bdee, 0x00427084, 0x004e7384, 0x004279ce, 0x00e77884,
- 0x00e779ce, 0x004f7bce, 0x00ef7bc4, 0x00ef7bce, 0x00421c84, 0x00439ce4, 0x00423dce, 0x00e73c84,
- 0x00e73dce, 0x0047bdee, 0x00e7bde4, 0x00e7bdee, 0x00427c00, 0x0043fce0, 0x004e7f80, 0x004fffe0,
- 0x00e77c00, 0x00e7fde0, 0x00ef7fc0, 0x00efffe0, 0x00007c84, 0x0003fce4, 0x000e7f84, 0x000fffe4,
- 0x00007dce, 0x0007fdee, 0x000f7fce, 0x000fffee, 0x00427c84, 0x0043fce4, 0x004e7f84, 0x004fffe4,
- 0x00427dce, 0x00e77c84, 0x00e77dce, 0x0047fdee, 0x004f7fce, 0x00e7fde4, 0x00ef7fc4, 0x004fffee,
- 0x00efffe4, 0x00e7fdee, 0x00ef7fce, 0x00efffee, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x000f83e0, 0x00a5294a, 0x004e1380, 0x00a57800, 0x00ad0bc0, 0x004390e0, 0x00a53c00, 0x00a5a1e0,
- 0x000e1384, 0x0000794a, 0x000f0b4a, 0x000390e4, 0x00003d4a, 0x0007a16a, 0x004e1384, 0x00a5694a,
- 0x00ad0b4a, 0x004390e4, 0x00a52d4a, 0x00a5a16a, 0x004f83e0, 0x00a57c00, 0x00ad83e0, 0x000f83e4,
- 0x00007d4a, 0x000f836a, 0x004f93e4, 0x00a57d4a, 0x00ad836a, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00001c00, 0x00001084, 0x00007000, 0x00421000,
- 0x00039ce0, 0x000039ce, 0x000e7380, 0x00e73800, 0x000e7f80, 0x00e73884, 0x0003fce0, 0x004239ce
-};
diff --git a/src/LineFont.src b/src/LineFont.src
deleted file mode 100644
index 1b5967d..0000000
--- a/src/LineFont.src
+++ /dev/null
@@ -1,786 +0,0 @@
-#2500: single horizontal line
-2500
-
-
------
-
-
-
-#2501: triple horizontal line
-2501
-
------
------
------
-
-
-#2502: single vertical line
-2502
- |
- |
- |
- |
- |
-
-#2503: triple vertical line
-2503
- |||
- |||
- |||
- |||
- |||
-
-#2504-250B are dashed - not handled
-
-#250C: top-left corner (lines on bottom + right)
-250C
-
-
- .--
- |
- |
-
-#250D: as above, but top line triple-width
-250D
-
- .--
- .--
- |--
- |
-
-#250E: now the vert line triple-width
-250E
-
-
- ..--
- |||
- |||
-
-#250F: and now both lines triple-width
-250F
-
- .___
- |.--
- ||._
- |||
-
-#2510: top-right corner
-2510
-
-
---.
- |
- |
-
-2511
-
-==.
-==.
-==|
- |
-
-2512
-
-
-==..
- |||
- |||
-
-2513
-
-===.
-==.|
-=.||
- |||
-
-#2514: bottom-left corner
-2514
- |
- |
- .==
-
-
-
-2515
- |
- |==
- |==
- ===
-
-
-
-2516
- |||
- |||
- |.==
-
-
-
-2517
- |||
- ||.=
- |.==
- .===
-
-
-#2518: bottm-right corner
-2518
- |
- |
-==.
-
-
-
-2519
- |
-==|
-==|
-===
-
-
-
-251A
- |||
- |||
-====
-
-
-
-251B
- |||
-=.||
-==.|
-===.
-
-
-#251C: Join of vertical line and one from the right
-251C
- |
- |
- |==
- |
- |
-
-251D
- |
- |==
- |==
- |==
- |
-
-251E
- |||
- |||
- ||==
- |
- |
-
-251F
- |
- |
- ||==
- |||
- |||
-
-
-2520
- |||
- |||
- ||==
- |||
- |||
-
-2521
- |||
- |||=
- ||==
- .|==
- |
-
-2522
- |
- .|==
- ||==
- |||=
- |||
-
-2523
- |||
- ||.=
- ||==
- ||.=
- |||
-
-#2524: Join of vertical line and one from the left
-2524
- |
- |
-==|
- |
- |
-
-2525
- |
-==|
-==|
-==|
- |
-
-2526
- |||
- |||
-==+|
- |
- |
-
-2527
- |
- |
-==+|
- |||
- |||
-
-2528
- |||
- |||
-==+|
- |||
- |||
-
-2529
- |||
-=+||
-==+|
-===+
- |
-
-252A
- |
-=+||
-==+|
-===+
- |||
-
-252B
- |||
-=+||
-==+|
-=+||
- |||
-
-#252C: horizontal line joined to from below
-252C
-
-
-=====
- |
- |
-
-252D
-
-===
-==|==
-==|
- |
-
-252E
-
- ===
-==|==
- |==
- |
-
-252F
-
-==+==
-==|==
-==|==
- |
-
-2530
-
-
-=====
- |||
- |||
-
-2531
-
-===|
-==||=
-=|||
- |||
-
-2532
-
- |===
-=||==
- ||==
- |||
-
-2533
-
-=====
-==|==
-=+|+=
- |||
-
-#2534: bottom line, connected to from top
-2534
- |
- |
-=====
-
-
-
-2535
- |
-==|
-=====
-===
-
-
-2536
- |
- |==
-=====
- ===
-
-
-2537
- |
-==|==
-=====
-=====
-
-
-2538
- |||
- |||
-=====
-
-
-
-2539
- |||
-==||
-=====
-===|
-
-
-
-253A
- |||
- ||==
-=|===
- |===
-
-
-253B
- |||
-==|==
-=====
-=====
-
-
-#253C: vertical + horizontal lines intersecting
-253C
- |
- |
-=====
- |
- |
-
-253D
- |
-==|
-=====
-==|
- |
-
-253E
- |
- |==
-=====
- |==
- |
-
-253F
- |
-==|==
-=====
-==|==
- |
-
-2540
- |||
- |||
-=====
- |
- |
-
-2541
- |
- |
-=====
- |||
- |||
-
-2542
- |||
- |||
-=====
- |||
- |||
-
-2543
- |||
-=|||
-=====
-==|+
- |
-
-2544
- |||
- ||==
-=====
- ||==
- |
-
-2545
- |
-==|+
-=====
-=|||
- |||
-
-2546
- |
- ||==
-=====
- ||==
- |||
-
-2547
- |||
-=|||=
-=====
-=|||=
- |
-
-2548
- |
-=|||=
-=====
-=|||=
- |||
-
-2549
- |||
-=|||
-=====
-=|||
- |||
-
-254A
- |||
- |||=
-=====
- |||=
- |||
-
-254B
- |||
-=|||=
-=====
-=|||=
- |||
-
-#254C-254F are dashed
-2550
-
-_____
-
-_____
-
-
-2551
- | |
- | |
- | |
- | |
- | |
-
-2552
-
- |--
- |
- |--
- |
-
-2553
-
-
- ----
- | |
- | |
-
-2554
-
- +---
- |
- + +-
- | |
-
-2555
-
---+
- |
---+
- |
-
-2556
-
-
--+-+
- | |
- | |
-
-2557
-
----+
- |
--+ |
- | |
-
-2558
- |
- +--
- |
- +--
-
-2559
- | |
- | |
- +-+-
-
-
-
-255A
- | |
- | +-
- |
- +---
-
-
-255B
- |
---+
- |
---+
-
-
-255C
- | |
- | |
--+-+
-
-
-255D
- | |
--+ |
- |
----+
-
-
-255E
- |
- +--
- |
- +--
- |
-
-255F
- | |
- | |
- | +-
- | |
- | |
-
-2560
- | |
- | +-
- |
- | +-
- | |
-
-2561
- |
---+
- |
---+
- |
-
-2562
- | |
- | |
--+ +
- | |
- | |
-
-2563
- | |
--+ |
- |
--+ |
- | |
-
-2564
-
------
-
---+--
- |
-
-2565
-
-
--+-+-
- | |
- | |
-
-2566
-
------
-
--+ +-
- | |
-
-2567
- |
---+--
-
------
-
-
-2568
- | |
- | |
--+-+-
-
-
-
-2569
- | |
--+ +-
-
------
-
-
-256A
- |
---+--
- |
---+--
- |
-
-256B
- | |
- | |
--+-+-
- | |
- | |
-
-256C
- | |
--+ +-
-
--+ +-
- | |
-
-#256F-2570 are curly,
-#2571-2573 are slashes and X
-
-2574
-
-
-___
-
-
-
-2575
- |
- |
- |
-
-
-
-2576
-
-
- ___
-
-
-
-2577
-
-
- |
- |
- |
-
-2578
-
-___
-___
-___
-
-
-2579
- |||
- |||
- |||
-
-
-
-257A
-
- ___
- ___
- ___
-
-
-257B
-
-
- |||
- |||
- |||
-
-257C
-
- ___
-_____
- ___
-
-
-257D
- |
- |
- |||
- |||
- |||
-
-257E
-
-___
-_____
-___
-
-
-257F
- |||
- |||
- |||
- |
- |
diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp
index 3169a50..140ecfb 100644
--- a/src/TerminalDisplay.cpp
+++ b/src/TerminalDisplay.cpp
@@ -45,6 +45,7 @@
#include <QDrag>
#include <QDesktopServices>
#include <QAccessible>
+#include <QtMath>
// KDE
#include <KShell>
@@ -63,7 +64,6 @@
#include "konsoledebug.h"
#include "TerminalCharacterDecoder.h"
#include "Screen.h"
-#include "LineFont.h"
#include "SessionController.h"
#include "ExtendedCharTable.h"
#include "TerminalDisplayAccessible.h"
@@ -71,6 +71,7 @@
#include "Session.h"
#include "WindowSystemInfo.h"
#include "IncrementalSearchBar.h"
+#include "LineBlockCharacters.h"
using namespace Konsole;
@@ -211,7 +212,7 @@ static inline bool isLineCharString(const QString& string)
return false;
}
- return isSupportedLineChar(string.at(0).unicode());
+ return LineBlockCharacters::canDraw(string.at(0).unicode());
}
void TerminalDisplay::fontChange(const QFont&)
@@ -566,386 +567,16 @@ TerminalDisplay::~TerminalDisplay()
/* */
/* ------------------------------------------------------------------------- */
-/**
- A table for emulating the simple (single width) unicode drawing chars.
- It represents the 250x - 257x glyphs. If it's zero, we can't use it.
- if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
- 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
-
- Then, the pixels basically have the following interpretation:
- _|||_
- -...-
- -...-
- -...-
- _|||_
-
-where _ = none
- | = vertical line.
- - = horizontal line.
- */
-
-enum LineEncode {
- TopL = (1 << 1),
- TopC = (1 << 2),
- TopR = (1 << 3),
-
- LeftT = (1 << 5),
- Int11 = (1 << 6),
- Int12 = (1 << 7),
- Int13 = (1 << 8),
- RightT = (1 << 9),
-
- LeftC = (1 << 10),
- Int21 = (1 << 11),
- Int22 = (1 << 12),
- Int23 = (1 << 13),
- RightC = (1 << 14),
-
- LeftB = (1 << 15),
- Int31 = (1 << 16),
- Int32 = (1 << 17),
- Int33 = (1 << 18),
- RightB = (1 << 19),
-
- BotL = (1 << 21),
- BotC = (1 << 22),
- BotR = (1 << 23)
-};
-
-static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
-{
- //Calculate cell midpoints, end points.
- const int cx = x + w / 2;
- const int cy = y + h / 2. - 0.5;
- const int ex = x + w - 1;
- const int ey = y + h - 1;
-
- const quint32 toDraw = LineChars[code];
-
- //Top _lines:
- if ((toDraw & TopL) != 0u) {
- paint.drawLine(cx - 1, y, cx - 1, cy - 2);
- }
- if ((toDraw & TopC) != 0u) {
- paint.drawLine(cx, y, cx, cy - 2);
- }
- if ((toDraw & TopR) != 0u) {
- paint.drawLine(cx + 1, y, cx + 1, cy - 2);
- }
-
- //Bot _lines:
- if ((toDraw & BotL) != 0u) {
- paint.drawLine(cx - 1, cy + 2, cx - 1, ey);
- }
- if ((toDraw & BotC) != 0u) {
- paint.drawLine(cx, cy + 2, cx, ey);
- }
- if ((toDraw & BotR) != 0u) {
- paint.drawLine(cx + 1, cy + 2, cx + 1, ey);
- }
-
- //Left _lines:
- if ((toDraw & LeftT) != 0u) {
- paint.drawLine(x, cy - 1, cx - 2, cy - 1);
- }
- if ((toDraw & LeftC) != 0u) {
- paint.drawLine(x, cy, cx - 2, cy);
- }
- if ((toDraw & LeftB) != 0u) {
- paint.drawLine(x, cy + 1, cx - 2, cy + 1);
- }
-
- //Right _lines:
- if ((toDraw & RightT) != 0u) {
- paint.drawLine(cx + 2, cy - 1, ex, cy - 1);
- }
- if ((toDraw & RightC) != 0u) {
- paint.drawLine(cx + 2, cy, ex, cy);
- }
- if ((toDraw & RightB) != 0u) {
- paint.drawLine(cx + 2, cy + 1, ex, cy + 1);
- }
-
- //Intersection points.
- if ((toDraw & Int11) != 0u) {
- paint.drawPoint(cx - 2, cy - 2);
- }
- if ((toDraw & Int12) != 0u) {
- paint.drawPoint(cx - 1, cy - 2);
- }
- if ((toDraw & Int13) != 0u) {
- paint.drawPoint(cx - 0, cy - 2);
- }
-
- if ((toDraw & Int21) != 0u) {
- paint.drawPoint(cx - 2, cy - 1);
- }
- if ((toDraw & Int22) != 0u) {
- paint.drawPoint(cx - 1, cy - 1);
- }
- if ((toDraw & Int23) != 0u) {
- paint.drawPoint(cx - 0, cy - 1);
- }
-
- if ((toDraw & Int31) != 0u) {
- paint.drawPoint(cx - 2, cy);
- }
- if ((toDraw & Int32) != 0u) {
- paint.drawPoint(cx - 1, cy);
- }
- if ((toDraw & Int33) != 0u) {
- paint.drawPoint(cx - 0, cy);
- }
-}
-
-static void drawOtherChar(QPainter& paint, int x, int y, int w, int h, uchar code)
-{
- //Calculate cell midpoints, end points.
- const int cx = x + w / 2;
- const int cy = y + h / 2. - 0.5; // Compensate for the translation, to match fonts
- const int ex = x + w - 1;
- const int ey = y + h - 1;
-
- // Double dashes
- if (0x4C <= code && code <= 0x4F) {
- const int xHalfGap = qMax(w / 15, 1);
- const int yHalfGap = qMax(h / 15, 1);
- switch (code) {
- case 0x4D: // BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL
- paint.drawLine(x, cy - 1, cx - xHalfGap - 1, cy - 1);
- paint.drawLine(x, cy + 1, cx - xHalfGap - 1, cy + 1);
- paint.drawLine(cx + xHalfGap, cy - 1, ex, cy - 1);
- paint.drawLine(cx + xHalfGap, cy + 1, ex, cy + 1);
- // No break!
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
- Q_FALLTHROUGH();
-#endif
- case 0x4C: // BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL
- paint.drawLine(x, cy, cx - xHalfGap - 1, cy);
- paint.drawLine(cx + xHalfGap, cy, ex, cy);
- break;
- case 0x4F: // BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
- paint.drawLine(cx - 1, y, cx - 1, cy - yHalfGap - 1);
- paint.drawLine(cx + 1, y, cx + 1, cy - yHalfGap - 1);
- paint.drawLine(cx - 1, cy + yHalfGap, cx - 1, ey);
- paint.drawLine(cx + 1, cy + yHalfGap, cx + 1, ey);
- // No break!
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
- Q_FALLTHROUGH();
-#endif
- case 0x4E: // BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL
- paint.drawLine(cx, y, cx, cy - yHalfGap - 1);
- paint.drawLine(cx, cy + yHalfGap, cx, ey);
- break;
- }
- }
-
- // Rounded corner characters
- else if (0x6D <= code && code <= 0x70) {
- const int r = w * 3 / 8;
- const int d = 2 * r;
- switch (code) {
- case 0x6D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
- paint.drawLine(cx, cy + r, cx, ey);
- paint.drawLine(cx + r, cy, ex, cy);
- paint.drawArc(cx, cy, d, d, 90 * 16, 90 * 16);
- break;
- case 0x6E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT
- paint.drawLine(cx, cy + r, cx, ey);
- paint.drawLine(x, cy, cx - r, cy);
- paint.drawArc(cx - d, cy, d, d, 0 * 16, 90 * 16);
- break;
- case 0x6F: // BOX DRAWINGS LIGHT ARC UP AND LEFT
- paint.drawLine(cx, y, cx, cy - r);
- paint.drawLine(x, cy, cx - r, cy);
- paint.drawArc(cx - d, cy - d, d, d, 270 * 16, 90 * 16);
- break;
- case 0x70: // BOX DRAWINGS LIGHT ARC UP AND RIGHT
- paint.drawLine(cx, y, cx, cy - r);
- paint.drawLine(cx + r, cy, ex, cy);
- paint.drawArc(cx, cy - d, d, d, 180 * 16, 90 * 16);
- break;
- }
- }
-
- // Diagonals
- else if (0x71 <= code && code <= 0x73) {
- switch (code) {
- case 0x71: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
- paint.drawLine(ex, y, x, ey);
- break;
- case 0x72: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
- paint.drawLine(x, y, ex, ey);
- break;
- case 0x73: // BOX DRAWINGS LIGHT DIAGONAL CROSS
- paint.drawLine(ex, y, x, ey);
- paint.drawLine(x, y, ex, ey);
- break;
- }
- }
-}
-
-static void drawBlockChar(QPainter& paint, int x, int y, int w, int h, uchar code)
-{
- const QColor color = paint.pen().color();
-
- const float left = x - 0.5;
- const float top = y - 0.5;
-
- const float cx = left + w / 2;
- const float cy = top + h / 2;
- const float right = x + w - 0.5;
- const float bottom = y + h - 0.5;
-
- // Default rect fills entire cell
- QRectF rect(left, top, w, h);
-
- // LOWER ONE EIGHTH BLOCK to LEFT ONE EIGHTH BLOCK
- if (code >= 0x81 && code <= 0x8f) {
- if (code < 0x88) { // Horizontal
- const int height = h * (0x88 - code) / 8;
- rect.setY(top + height);
- rect.setHeight(h - height);
- } else if (code > 0x88) { // Vertical
- const int width = w * (0x90 - code) / 8;
- rect.setWidth(width);
- }
-
- paint.fillRect(rect, color);
-
- return;
- }
-
- // Combinations of quarter squares
- // LEFT ONE EIGHTH BLOCK to QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
- if (code >= 0x96 && code <= 0x9F) {
- bool upperLeft = false, upperRight = false,
- lowerLeft = false, lowerRight = false;
-
- switch(code) {
- case 0x96:
- lowerLeft = true;
- break;
- case 0x97:
- lowerRight = true;
- break;
- case 0x98:
- upperLeft = true;
- break;
- case 0x99:
- upperLeft = true;
- lowerLeft = true;
- lowerRight = true;
- break;
- case 0x9a:
- upperLeft = true;
- lowerRight = true;
- break;
- case 0x9b:
- upperLeft = true;
- upperRight = true;
- lowerLeft = true;
- break;
- case 0x9c:
- upperLeft = true;
- upperRight = true;
- lowerRight = true;
- break;
- case 0x9d:
- upperRight = true;
- break;
- case 0x9e:
- upperRight = true;
- lowerLeft = true;
- break;
- case 0x9f:
- upperRight = true;
- lowerLeft = true;
- lowerRight = true;
- break;
- default:
- break;
- }
-
- if (upperLeft) {
- paint.fillRect(QRectF(QPointF(left, top), QPointF(cx, cy)), color);
- }
- if (upperRight) {
- paint.fillRect(QRectF(QPointF(cx, top), QPointF(right, cy)), color);
- }
- if (lowerLeft) {
- paint.fillRect(QRectF(QPointF(left, cy), QPointF(cx, bottom)), color);
- }
- if (lowerRight) {
- paint.fillRect(QRectF(QPointF(cx, cy), QPointF(right, bottom)), color);
- }
-
- return;
- }
-
- // And the random stuff
- switch(code) {
- case 0x80: // Top half block
- rect.setHeight(h / 2);
- paint.fillRect(rect, color);
- return;
- case 0x90: // Right half block
- paint.fillRect(QRectF(QPointF(cx, top), QPointF(right, bottom)), color);
- return;
- case 0x94: // Top one eighth block
- rect.setHeight(h / 8);
- paint.fillRect(rect, color);
- return;
- case 0x95: { // Right one eighth block
- const float width = 7 * w / 8;
- rect.setX(left + width);
- rect.setWidth(w - width);
- paint.fillRect(rect, color);
- return;
- }
- case 0x91: // Light shade
- paint.fillRect(rect, QBrush(color, Qt::Dense6Pattern));
- return;
- case 0x92: // Medium shade
- paint.fillRect(rect, QBrush(color, Qt::Dense4Pattern));
- return;
- case 0x93: // Dark shade
- paint.fillRect(rect, QBrush(color, Qt::Dense2Pattern));
- return;
-
- default:
- break;
- }
-}
-
void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str,
const Character* attributes)
{
- painter.save();
-
- // For antialiasing, we need to shift it so the single pixel width is in the middle
- painter.translate(0.5, 0.5);
-
- if (((attributes->rendition & RE_BOLD) != 0) && _boldIntense) {
- QPen boldPen(painter.pen());
- boldPen.setWidth(4);
- painter.setPen(boldPen);
- }
-
+ const bool useBoldPen = (attributes->rendition & RE_BOLD) != 0 && _boldIntense;
+ QRect cellRect = {x, y, _fontWidth, _fontHeight};
for (int i = 0 ; i < str.length(); i++) {
- const uchar code = str[i].cell();
-
- if (code >= 0x80 && code <= 0x9F) { // UPPER HALF BLOCK to QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
- drawBlockChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
- } else if (LineChars[code] != 0u) {
- drawLineChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
- } else {
- drawOtherChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
- }
+ LineBlockCharacters::draw(painter, cellRect.translated(i * _fontWidth, 0), str[i],
+ useBoldPen);
}
-
- painter.restore();
}
void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape)
@@ -1135,6 +770,9 @@ void TerminalDisplay::drawCharacters(QPainter& painter,
painter.setPen(color);
}
+ const bool origClipping = painter.hasClipping();
+ const auto origClipRegion = painter.clipRegion();
+ painter.setClipRect(rect);
// draw text
if (isLineCharString(text) && !_useFontLineCharacters) {
drawLineCharString(painter, rect.x(), rect.y(), text, style);
@@ -1145,14 +783,14 @@ void TerminalDisplay::drawCharacters(QPainter& painter,
// This still allows RTL characters to be rendered in the RTL way.
painter.setLayoutDirection(Qt::LeftToRight);
- painter.setClipRect(rect);
if (_bidiEnabled) {
painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, text);
} else {
painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, LTR_OVERRIDE_CHAR + text);
}
- painter.setClipping(false);
}
+ painter.setClipRegion(origClipRegion);
+ painter.setClipping(origClipping);
}
void TerminalDisplay::drawTextFragment(QPainter& painter ,
@@ -1450,7 +1088,7 @@ void TerminalDisplay::updateImage()
if (newLine[x + 0].character == 0u) {
continue;
}
- const bool lineDraw = newLine[x + 0].isLineChar();
+ const bool lineDraw = LineBlockCharacters::canDraw(newLine[x + 0].character);
const bool doubleWidth = (x + 1 == columnsToUpdate) ? false : (newLine[x + 1].character == 0);
const RenditionFlags cr = newLine[x].rendition;
const CharacterColor clipboard = newLine[x].backgroundColor;
@@ -1471,7 +1109,7 @@ void TerminalDisplay::updateImage()
ch.backgroundColor != clipboard ||
(ch.rendition & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR) ||
(dirtyMask[x + len] == 0) ||
- ch.isLineChar() != lineDraw ||
+ LineBlockCharacters::canDraw(ch.character) != lineDraw ||
nextIsDoubleWidth != doubleWidth) {
break;
}
@@ -1874,7 +1512,7 @@ void TerminalDisplay::drawContents(QPainter& paint, const QRect& rect)
}
}
- const bool lineDraw = _image[loc(x, y)].isLineChar();
+ const bool lineDraw = LineBlockCharacters::canDraw(_image[loc(x, y)].character);
const bool doubleWidth = (_image[qMin(loc(x, y) + 1, _imageSize - 1)].character == 0);
const CharacterColor currentForeground = _image[loc(x, y)].foregroundColor;
const CharacterColor currentBackground = _image[loc(x, y)].backgroundColor;
diff --git a/tests/line_block_characters_table.py b/tests/line_block_characters_table.py
new file mode 100755
index 0000000..39422b4
--- /dev/null
+++ b/tests/line_block_characters_table.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python3
+
+# Prints tables with all characters supported by LineBlockCharactersDrawer,
+# one for normal weight and one for bold.
+
+first = 0x2500
+last = 0x259F
+
+cpPerLine = 32
+
+lineFmt = "\033[48;5;243;38;5;231m"
+
+def fmtLine(text):
+ return "{}\033[{}X {}\033[49;39m".format(lineFmt, cpPerLine*2+1, text)
+def fmtCh(text):
+ return "\033[48;5;231;38;5;16m{}{}".format(text, lineFmt)
+def fmtRefCh(text):
+ return "\033[48;5;252;38;5;16m{}{}".format(text, lineFmt)
+def setNoWrap(enable):
+ print("\033[?7l" if enable else "\033[?7h", end="")
+def setBold(enable):
+ print("\033[1m" if enable else "\033[21m", end="")
+def fmtBold(text):
+ return "\033[1m{}\033[21m".format(text)
+
+refChars = [["|", "│┃"], ["_-", "─━"], ["L", "└┗"], ["+", "┼╋"], ["=F", "╒╬"],
+ ["/", "╱"], ["\\", "╲"], ["X", "╳"]]
+boxes = \
+ " +-----------+ ************* ,============, ╲\\ ╱/\n" \
+ " | ┌───────┐ | @ ┏━━━━━━━┓ @ # ╔════════╗ # ╲\\╱/ \n" \
+ " | │ Light │ | @ ┃ Heavy ┃ @ # ║ Double ║ # ╳X \n" \
+ " | └───────┘ | @ ┗━━━━━━━┛ @ # ╚════════╝ # ╱/╲\\ \n" \
+ " +-----------+ ************* \"============\" ╱/ ╲\\\n" \
+
+lines = []
+for cp in range(first, last+1):
+ columnId = int((cp - first) % cpPerLine)
+ lineId = int((cp - first) / cpPerLine)
+ if columnId == 0:
+ lines.append([])
+ lines[lineId].append(chr(cp))
+
+setNoWrap(True)
+
+refCharsLine = " ".join(fmtRefCh(rc[0]) + fmtCh(rc[1]) for rc in refChars)
+print(fmtLine("{:8s} line width reference: {}".format("Normal", refCharsLine)))
+
+print(fmtLine(""))
+for line in lines:
+ print(fmtLine(" ".join(fmtCh(ch) for ch in line)))
+ print(fmtLine(""))
+
+print("\n" + boxes)
+
+setBold(True)
+refCharsLine = " ".join(fmtRefCh(rc[0]) + fmtCh(rc[1]) for rc in refChars)
+print(fmtLine("{:8s} line width reference: {}".format("Bold", refCharsLine)))
+
+print(fmtLine(""))
+for line in lines:
+ print(fmtLine(" ".join(fmtCh(ch) for ch in line)))
+ print(fmtLine(""))
+
+print("\n" + boxes)
+
+setBold(False)
+setNoWrap(False)
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 284160a..6b32433 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -2,19 +2,4 @@
### konsoleprofile command-line tool
install(PROGRAMS konsoleprofile DESTINATION ${KDE_INSTALL_BINDIR})
-### Line graphics font
-### Attempting to auto-create LineFont.h for multiple systems is a headache.
-### If LineFont.h is needed to be recreated use:
-### fontembedder LineFont.src > LineFont.h
-### Then commit the new LineFont.h
-if(KONSOLE_BUILD_FONTEMBEDDER OR KONSOLE_GENERATE_LINEFONT)
-
- find_package(Qt5Core ${QT_MIN_VERSION} CONFIG REQUIRED)
-
- ### Font Embedder
- set(fontembedder_SRCS fontembedder.cpp)
- add_executable(fontembedder ${fontembedder_SRCS})
- target_link_libraries(fontembedder Qt5::Core)
-endif()
-
add_subdirectory( uni2characterwidth )
diff --git a/tools/fontembedder.cpp b/tools/fontembedder.cpp
deleted file mode 100644
index 7c6f2fb..0000000
--- a/tools/fontembedder.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- This file is part of Konsole, an X terminal.
- Copyright 2005 by Maksim Orlovich <maksim@kde.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301 USA.
-*/
-
-// Standard
-#include <cstdlib>
-#include <iostream>
-#include <iomanip>
-
-// Qt
-#include <QFile>
-#include <QTextStream>
-#include <QDebug>
-
-using namespace std;
-
-static quint32 charVal(QChar val)
-{
- if (val == QLatin1Char(' ')) {
- return 0;
- } else {
- return 1;
- }
-}
-
-static quint32 readGlyphLine(QTextStream& input)
-{
- QString line = input.readLine();
- while (line.length() < 5) {
- line += QLatin1Char(' ');
- }
-
- quint32 val = charVal(line[0]) |
- (charVal(line[1]) << 1) |
- (charVal(line[2]) << 2) |
- (charVal(line[3]) << 3) |
- (charVal(line[4]) << 4);
- return val;
-}
-
-static quint32 readGlyph(QTextStream& input)
-{
- return readGlyphLine(input) |
- (readGlyphLine(input) << 5) |
- (readGlyphLine(input) << 10) |
- (readGlyphLine(input) << 15) |
- (readGlyphLine(input) << 20);
-}
-
-int main(int argc, char **argv)
-{
- if (argc != 2) {
- qWarning("usage: fontembedder LineFont.src > LineFont.h");
- exit(1);
- }
- QFile inFile(QString::fromLocal8Bit(argv[1]));
- if (!inFile.open(QIODevice::ReadOnly)) {
- qWarning("Can not open %s", argv[1]);
- exit(1);
- }
-
- QTextStream input(&inFile);
-
- // Currently, for Konsole, the input glyphs file ranges from 0x2500
- // (9472) to x257f (9599) so this 128 will handle it. However, if
- // more glyphs are added to the input file, this will be an issue.
- quint32 glyphStates[128];
- QMap<quint32, int> glyphMap;
-
- for (unsigned int & glyphState : glyphStates) {
- glyphState = 0; //nothing..
- }
-
- while (!input.atEnd()) {
- QString line = input.readLine();
- line = line.trimmed();
- if (line.isEmpty()) {
- continue; //Skip empty lines
- }
- if (line[0] == QLatin1Char('#')) {
- continue; //Skip comments
- }
-
- //Must be a glyph ID.
- int glyph = line.toInt(nullptr, 16);
- if ((glyph < 0x2500) || (glyph > 0x257f)) {
- qWarning("Invalid glyph number: %d aborting...", glyph);
- exit(1);
- } else {
- glyph = glyph - 0x2500;
-
- glyphStates[glyph] = readGlyph(input);
- // qWarning()<<glyph<<";"<<glyphStates[glyph];
-
- if (glyphMap.contains(glyphStates[glyph])) {
- // FIXME: get this qWarning working again
- //qWarning()<<"Code "<<glyph<<" and "<<glyphMap.value(glyphStates[glyph])<<"have the same glyph state"<<glyphStates[glyph];
- }
- glyphMap[glyphStates[glyph]] = glyph;
- }
- }
-
- //Output.
- cout << "// WARNING: Autogenerated by \"fontembedder " << argv[1] << "\".\n";
- cout << "// You probably do not want to hand-edit this!\n\n";
- cout << "static const quint32 LineChars[] = {\n";
-
- //Nicely formatted: 8 per line, 16 lines
- for (int line = 0; line < 128; line += 8) {
- cout << "\t";
- for (int col = line; col < line + 8; ++col) {
- cout << "0x" << hex << setw(8) << setfill('0') << glyphStates[col];
- if (col != 127) {
- cout << ", ";
- }
- }
- cout << "\n";
- }
- cout << "};\n";
- return 0;
-}
-
--
cgit v1.1