mirror of
https://github.com/KDE/konsole.git
synced 2025-12-23 23:38:08 -05:00
Improve built-in line/block characters drawing
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -53,21 +53,6 @@ const RenditionFlags RE_STRIKEOUT = (1 << 8);
|
||||
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
|
||||
@@ -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) {
|
||||
|
||||
722
src/LineBlockCharacters.cpp
Normal file
722
src/LineBlockCharacters.cpp
Normal file
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
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");
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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);
|
||||
Q_FALLTHROUGH();
|
||||
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
|
||||
57
src/LineBlockCharacters.h
Normal file
57
src/LineBlockCharacters.h
Normal file
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
786
src/LineFont.src
786
src/LineFont.src
@@ -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
|
||||
|||
|
||||
|||
|
||||
|||
|
||||
|
|
||||
|
|
||||
@@ -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"
|
||||
@@ -73,6 +73,7 @@
|
||||
#include "IncrementalSearchBar.h"
|
||||
#include "Profile.h"
|
||||
#include "ViewManager.h" // for colorSchemeForProfile. // TODO: Rewrite this.
|
||||
#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,382 +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!
|
||||
Q_FALLTHROUGH();
|
||||
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!
|
||||
Q_FALLTHROUGH();
|
||||
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;
|
||||
|
||||
67
tests/line_block_characters_table.py
Executable file
67
tests/line_block_characters_table.py
Executable file
@@ -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)
|
||||
@@ -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 )
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user