Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: MaximInterface
src/Text.cpp
- Committer:
- IanBenzMaxim
- Date:
- 2019-10-03
- Revision:
- 16:a004191a79ab
- Parent:
- Text.cpp@ 13:6a6225690c2e
File content as of revision 16:a004191a79ab:
/*******************************************************************************
* Copyright (C) Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************/
#include <algorithm>
#include <functional>
#include <list>
#include "Bitmap.hpp"
#include "Display.hpp"
#include "Text.hpp"
using std::string;
static const int invalidWidthHeight = -1;
static const int characterWidth = 5;
static const int characterHeight = 7;
static const unsigned char printableCharBegin = 0x20;
static const unsigned char printableCharEnd = 0x7E;
static const uint8_t characterMap[][characterHeight] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ' '
{0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x20}, // '!'
{0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00}, // '"'
{0x50, 0x50, 0xF8, 0x50, 0xF8, 0x50, 0x50}, // '#'
{0x20, 0x78, 0xA0, 0x70, 0x28, 0xF0, 0x20}, // '$'
{0xC0, 0xC8, 0x10, 0x20, 0x40, 0x98, 0x18}, // '%'
{0x60, 0x90, 0xA0, 0x40, 0xA8, 0x90, 0x68}, // '&'
{0x60, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00}, // '''
{0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10}, // '('
{0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40}, // ')'
{0x00, 0x20, 0xA8, 0x70, 0xA8, 0x20, 0x00}, // '*'
{0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00}, // '+'
{0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x40}, // ','
{0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00}, // '-'
{0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60}, // '.'
{0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00}, // '/'
{0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70}, // '0'
{0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70}, // '1'
{0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xF8}, // '2'
{0xF8, 0x10, 0x20, 0x10, 0x08, 0x88, 0x70}, // '3'
{0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10}, // '4'
{0xF8, 0x80, 0xF0, 0x08, 0x08, 0x88, 0x70}, // '5'
{0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70}, // '6'
{0xF8, 0x08, 0x10, 0x20, 0x40, 0x40, 0x40}, // '7'
{0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70}, // '8'
{0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60}, // '9'
{0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00}, // ':'
{0x00, 0x60, 0x60, 0x00, 0x60, 0x20, 0x40}, // ';'
{0x10, 0x20, 0x40, 0x80, 0x40, 0x20, 0x10}, // '<'
{0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00}, // '='
{0x40, 0x20, 0x10, 0x08, 0x10, 0x20, 0x40}, // '>'
{0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x20}, // '?'
{0x70, 0x88, 0x08, 0x68, 0xA8, 0xA8, 0x70}, // '@'
{0x70, 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88}, // 'A'
{0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0}, // 'B'
{0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70}, // 'C'
{0xE0, 0x90, 0x88, 0x88, 0x88, 0x90, 0xE0}, // 'D'
{0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8}, // 'E'
{0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80}, // 'F'
{0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x78}, // 'G'
{0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88}, // 'H'
{0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70}, // 'I'
{0x38, 0x10, 0x10, 0x10, 0x10, 0x90, 0x60}, // 'J'
{0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88}, // 'K'
{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8}, // 'L'
{0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88}, // 'M'
{0x88, 0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88}, // 'N'
{0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70}, // 'O'
{0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80}, // 'P'
{0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68}, // 'Q'
{0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88}, // 'R'
{0x78, 0x80, 0x80, 0x70, 0x08, 0x08, 0xF0}, // 'S'
{0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, // 'T'
{0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70}, // 'U'
{0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20}, // 'V'
{0x88, 0x88, 0x88, 0xA8, 0xA8, 0xA8, 0x50}, // 'W'
{0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88}, // 'X'
{0x88, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20}, // 'Y'
{0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8}, // 'Z'
{0x70, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70}, // '['
{0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00}, // '\'
{0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70}, // ']'
{0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00}, // '^'
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8}, // '_'
{0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00}, // '`'
{0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78}, // 'a'
{0x80, 0x80, 0xB0, 0xC8, 0x88, 0x88, 0xF0}, // 'b'
{0x00, 0x00, 0x70, 0x80, 0x80, 0x88, 0x70}, // 'c'
{0x08, 0x08, 0x68, 0x98, 0x88, 0x88, 0x78}, // 'd'
{0x00, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70}, // 'e'
{0x30, 0x48, 0x40, 0xE0, 0x40, 0x40, 0x40}, // 'f'
{0x00, 0x78, 0x88, 0x88, 0x78, 0x08, 0x70}, // 'g'
{0x80, 0x80, 0xB0, 0xC8, 0x88, 0x88, 0x88}, // 'h'
{0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70}, // 'i'
{0x10, 0x00, 0x30, 0x10, 0x10, 0x90, 0x60}, // 'j'
{0x80, 0x80, 0x90, 0xA0, 0xC0, 0xA0, 0x90}, // 'k'
{0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70}, // 'l'
{0x00, 0x00, 0xD0, 0xA8, 0xA8, 0x88, 0x88}, // 'm'
{0x00, 0x00, 0xB0, 0xC8, 0x88, 0x88, 0x88}, // 'n'
{0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70}, // 'o'
{0x00, 0x00, 0xF0, 0x88, 0xF0, 0x80, 0x80}, // 'p'
{0x00, 0x00, 0x68, 0x98, 0x78, 0x08, 0x08}, // 'q'
{0x00, 0x00, 0xB0, 0xC8, 0x80, 0x80, 0x80}, // 'r'
{0x00, 0x00, 0x70, 0x80, 0x70, 0x08, 0xF0}, // 's'
{0x40, 0x40, 0xE0, 0x40, 0x40, 0x48, 0x30}, // 't'
{0x00, 0x00, 0x88, 0x88, 0x88, 0x98, 0x68}, // 'u'
{0x00, 0x00, 0x88, 0x88, 0x88, 0x41, 0x20}, // 'v'
{0x00, 0x00, 0x88, 0x88, 0xA8, 0xA8, 0x50}, // 'w'
{0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88}, // 'x'
{0x00, 0x00, 0x88, 0x88, 0x78, 0x08, 0x70}, // 'y'
{0x00, 0x00, 0xF8, 0x10, 0x20, 0x40, 0xF8}, // 'z'
{0x10, 0x20, 0x20, 0x40, 0x20, 0x20, 0x10}, // '{'
{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, // '|'
{0x40, 0x20, 0x20, 0x10, 0x20, 0x20, 0x40}, // '}'
{0x00, 0x00, 0x40, 0xA8, 0x10, 0x00, 0x00} // '~'
};
static bool charPrintable(unsigned char c) {
return (c >= printableCharBegin) && (c <= printableCharEnd);
}
static void removeNonprintableChars(string & s) {
s.erase(std::remove_if(s.begin(), s.end(),
std::not1(std::ptr_fun(charPrintable))),
s.end());
}
static bool compareStringLength(const string & a, const string & b) {
return a.length() < b.length();
}
namespace {
// Functor trims string if it exceeds a certain length.
class TrimString {
public:
TrimString(string::size_type maxSize) : maxSize(maxSize) {}
void operator()(string & s) {
if (s.size() > maxSize) {
s.resize(maxSize);
}
}
private:
string::size_type maxSize;
};
} // namespace
// Split a string based on a character token. Token will be removed.
static std::list<string> tokenizeString(const string & toSplit, char token) {
std::list<string> toSplitLines;
string::size_type beginIdx = 0, endIdx;
do {
endIdx = toSplit.find(token, beginIdx);
toSplitLines.push_back(toSplit.substr(
beginIdx, endIdx == string::npos ? string::npos : endIdx - beginIdx));
beginIdx = endIdx + 1;
} while (endIdx != string::npos);
return toSplitLines;
}
// Word wrap string into lines based on a specified line length.
static std::list<string> wrapLines(const string & toSplit,
const string::size_type lineLen) {
std::list<string> toSplitLines;
string::size_type beginIdx = 0;
if (lineLen > 0) {
// Split lines as much as necessary.
string::size_type endIdx = lineLen;
while (((toSplit.length() - beginIdx) > lineLen) &&
(endIdx != string::npos)) {
endIdx = toSplit.rfind(' ', endIdx);
if ((endIdx == string::npos) || (endIdx <= beginIdx)) {
// Current word is too long to split. Find end of current word.
endIdx = toSplit.find(' ', beginIdx);
}
if (endIdx != string::npos) {
toSplitLines.push_back(toSplit.substr(beginIdx, endIdx - beginIdx));
beginIdx = endIdx + 1;
endIdx = beginIdx + lineLen;
}
}
}
// Last line is any remaining characters.
toSplitLines.push_back(toSplit.substr(beginIdx, toSplit.length() - beginIdx));
return toSplitLines;
}
Text::Text()
: text_(), wordWrap_(false), charSpacing_(1), lineSpacing_(1), textLines_(),
preferredWidth_(invalidWidthHeight),
preferredHeight_(invalidWidthHeight) {}
void Text::setText(const string & text) {
if (text_ != text) {
text_ = text;
invalidate();
preferredWidth_ = preferredHeight_ = invalidWidthHeight;
}
}
void Text::setWordWrap(bool wordWrap) {
if (wordWrap_ != wordWrap) {
wordWrap_ = wordWrap;
invalidate();
preferredWidth_ = preferredHeight_ = invalidWidthHeight;
}
}
void Text::setLineSpacing(int lineSpacing) {
if (lineSpacing > 0 && lineSpacing_ != lineSpacing) {
lineSpacing_ = lineSpacing;
invalidate();
preferredHeight_ = invalidWidthHeight;
}
}
void Text::setCharSpacing(int charSpacing) {
if (charSpacing > 0 && charSpacing_ != charSpacing) {
charSpacing_ = charSpacing;
invalidate();
preferredWidth_ = invalidWidthHeight;
if (wordWrap()) {
preferredHeight_ = invalidWidthHeight;
}
}
}
int Text::preferredWidth() const {
if (preferredWidth_ == invalidWidthHeight) {
calculateLayout();
}
return preferredWidth_;
}
int Text::preferredHeight() const {
if (preferredHeight_ == invalidWidthHeight) {
calculateLayout();
}
return preferredHeight_;
}
void Text::resized() {
preferredWidth_ = invalidWidthHeight;
if (wordWrap()) {
preferredHeight_ = invalidWidthHeight;
}
}
void Text::doRender(Bitmap & bitmap, int xOffset, int yOffset) const {
using std::list;
// Ensure layout is up to date.
if (preferredWidth_ == invalidWidthHeight ||
preferredHeight_ == invalidWidthHeight) {
calculateLayout();
}
// Render each line.
int lineNum = 0;
for (list<string>::const_iterator lineIt = textLines_.begin();
lineIt != textLines_.end(); ++lineIt) {
// Render each character.
for (string::size_type charNum = 0; charNum < lineIt->length(); ++charNum) {
bitmap.overlay(xOffset + x() + charNum * (characterWidth + charSpacing_),
yOffset + y() + lineNum * (characterHeight + lineSpacing_),
&characterMap[(*lineIt)[charNum] - printableCharBegin][0],
characterHeight, characterWidth);
}
++lineNum;
}
}
void Text::calculateLayout() const {
using std::list;
// Split string into lines.
textLines_ = tokenizeString(text_, '\n');
// Remove non-printable characters.
std::for_each(textLines_.begin(), textLines_.end(), removeNonprintableChars);
const int lineLen =
(width() / (characterWidth + charSpacing_)) +
(((width() % (characterWidth + charSpacing_)) >= characterWidth) ? 1 : 0);
const int numLines =
(height() / (characterHeight + lineSpacing_)) +
(((height() % (characterHeight + lineSpacing_)) >= characterHeight) ? 1
: 0);
// Word wrap lines if enabled.
if (wordWrap_) {
list<string>::iterator lineIt = textLines_.begin();
while (lineIt != textLines_.end()) {
list<string>::iterator nextLineIt = lineIt;
++nextLineIt;
// Wrap current line.
list<string> wrappedLines = wrapLines(*lineIt, lineLen);
textLines_.splice(lineIt, wrappedLines);
// Remove old line.
textLines_.erase(lineIt);
lineIt = nextLineIt;
}
}
// Calculate preferred size.
string::size_type maxLineLength =
std::max_element(textLines_.begin(), textLines_.end(),
compareStringLength)
->length();
preferredWidth_ = (maxLineLength > 0)
? (maxLineLength * characterWidth) +
((maxLineLength - 1) * charSpacing_)
: 1;
preferredHeight_ = (textLines_.size() * characterHeight) +
((textLines_.size() - 1) * lineSpacing_);
// Remove clipped text.
if (textLines_.size() > static_cast<list<string>::size_type>(numLines)) {
textLines_.resize(numLines);
}
std::for_each(textLines_.begin(), textLines_.end(), TrimString(lineLen));
}