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
Text.cpp
- Committer:
- IanBenzMaxim
- Date:
- 2017-04-10
- Revision:
- 10:71359af61af8
- Parent:
- 8:a0d75dff3c9b
- Child:
- 11:989eabe2a376
File content as of revision 10:71359af61af8:
/*******************************************************************************
* Copyright (C) 2017 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[][characterWidth] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00 }, // ' '
{ 0x00, 0x00, 0xF2, 0x00, 0x00 }, // '!'
{ 0x00, 0xE0, 0x00, 0xE0, 0x00 }, // '"'
{ 0x28, 0xFE, 0x28, 0xFE, 0x28 }, // '#'
{ 0x24, 0x54, 0xFE, 0x54, 0x48 }, // '$'
{ 0xC4, 0xC8, 0x10, 0x26, 0x46 }, // '%'
{ 0x6C, 0x92, 0xAA, 0x44, 0x0A }, // '&'
{ 0x00, 0xA0, 0xC0, 0x00, 0x00 }, // '''
{ 0x00, 0x28, 0x44, 0x82, 0x00 }, // '('
{ 0x00, 0x82, 0x44, 0x38, 0x00 }, // ')'
{ 0x28, 0x10, 0x7C, 0x10, 0x28 }, // '*'
{ 0x10, 0x10, 0x7C, 0x10, 0x10 }, // '+'
{ 0x00, 0x0A, 0x0C, 0x00, 0x00 }, // ','
{ 0x10, 0x10, 0x10, 0x10, 0x10 }, // '-'
{ 0x00, 0x06, 0x06, 0x00, 0x00 }, // '.'
{ 0x04, 0x08, 0x10, 0x20, 0x40 }, // '/'
{ 0x7C, 0x8A, 0x92, 0xA2, 0x7C }, // '0'
{ 0x00, 0x42, 0xFE, 0x02, 0x00 }, // '1'
{ 0x42, 0x86, 0x8A, 0x92, 0x62 }, // '2'
{ 0x84, 0x82, 0xA2, 0xD2, 0x8C }, // '3'
{ 0x18, 0x28, 0x48, 0xFE, 0x08 }, // '4'
{ 0xE4, 0xA2, 0xA2, 0xA2, 0x9C }, // '5'
{ 0x3C, 0x52, 0x92, 0x92, 0x0C }, // '6'
{ 0x80, 0x8E, 0x90, 0xA0, 0xC0 }, // '7'
{ 0x6C, 0x92, 0x92, 0x92, 0x6C }, // '8'
{ 0x60, 0x92, 0x92, 0x94, 0x78 }, // '9'
{ 0x00, 0x6C, 0x6C, 0x00, 0x00 }, // ':'
{ 0x00, 0x6A, 0x6C, 0x00, 0x00 }, // ';'
{ 0x10, 0x28, 0x44, 0x82, 0x00 }, // '<'
{ 0x28, 0x28, 0x28, 0x28, 0x28 }, // '='
{ 0x00, 0x82, 0x44, 0x28, 0x10 }, // '>'
{ 0x40, 0x80, 0x8A, 0x90, 0x60 }, // '?'
{ 0x4C, 0x92, 0x9E, 0x82, 0x7C }, // '@'
{ 0x7E, 0x88, 0x88, 0x88, 0x7E }, // 'A'
{ 0xFE, 0x92, 0x92, 0x92, 0x6C }, // 'B'
{ 0x7C, 0x82, 0x82, 0x82, 0x44 }, // 'C'
{ 0xFE, 0x82, 0x82, 0x44, 0x38 }, // 'D'
{ 0xFE, 0x92, 0x92, 0x92, 0x82 }, // 'E'
{ 0xFE, 0x90, 0x90, 0x90, 0x80 }, // 'F'
{ 0x7C, 0x82, 0x92, 0x92, 0x5E }, // 'G'
{ 0xFE, 0x10, 0x10, 0x10, 0xFE }, // 'H'
{ 0x00, 0x82, 0xFE, 0x82, 0x00 }, // 'I'
{ 0x04, 0x02, 0x82, 0xFC, 0x80 }, // 'J'
{ 0xFE, 0x10, 0x28, 0x44, 0x82 }, // 'K'
{ 0xFE, 0x02, 0x02, 0x02, 0x02 }, // 'L'
{ 0xFE, 0x40, 0x21, 0x40, 0xFE }, // 'M'
{ 0xFE, 0x20, 0x10, 0x08, 0xFE }, // 'N'
{ 0x7C, 0x82, 0x82, 0x82, 0x7C }, // 'O'
{ 0xFE, 0x90, 0x90, 0x90, 0x60 }, // 'P'
{ 0x7C, 0x82, 0x8A, 0x82, 0x7A }, // 'Q'
{ 0xFE, 0x90, 0x98, 0x94, 0x62 }, // 'R'
{ 0x62, 0x92, 0x92, 0x92, 0x8C }, // 'S'
{ 0x80, 0x80, 0xFE, 0x80, 0x80 }, // 'T'
{ 0xFC, 0x02, 0x02, 0x02, 0xFC }, // 'U'
{ 0xF8, 0x04, 0x02, 0x04, 0xF8 }, // 'V'
{ 0xFC, 0x02, 0x1C, 0x02, 0xFC }, // 'W'
{ 0xC6, 0x28, 0x10, 0x28, 0xC6 }, // 'X'
{ 0xF0, 0x10, 0x0F, 0x10, 0xF0 }, // 'Y'
{ 0x86, 0x8A, 0x92, 0xA2, 0xC2 }, // 'Z'
{ 0x00, 0xFE, 0x82, 0x82, 0x00 }, // '['
{ 0x40, 0x20, 0x10, 0x08, 0x04 }, // '\'
{ 0x00, 0x82, 0x82, 0xFE, 0x00 }, // ']'
{ 0x20, 0x40, 0x80, 0x40, 0x20 }, // '^'
{ 0x02, 0x02, 0x02, 0x02, 0x02 }, // '_'
{ 0x00, 0x80, 0x40, 0x20, 0x00 }, // '`'
{ 0x04, 0x2A, 0x2A, 0x2A, 0x1E }, // 'a'
{ 0xFE, 0x12, 0x22, 0x22, 0x1C }, // 'b'
{ 0x1C, 0x22, 0x22, 0x22, 0x04 }, // 'c'
{ 0x1C, 0x22, 0x22, 0x12, 0xFE }, // 'd'
{ 0x1C, 0x2A, 0x2A, 0x2A, 0x18 }, // 'e'
{ 0x10, 0x7E, 0x90, 0x80, 0x40 }, // 'f'
{ 0x30, 0x4A, 0x4A, 0x4A, 0x7C }, // 'g'
{ 0xFE, 0x10, 0x20, 0x20, 0x1E }, // 'h'
{ 0x00, 0x22, 0xBE, 0x02, 0x00 }, // 'i'
{ 0x04, 0x02, 0x22, 0xBC, 0x00 }, // 'j'
{ 0xFE, 0x08, 0x14, 0x22, 0x00 }, // 'k'
{ 0x00, 0x82, 0xFE, 0x02, 0x00 }, // 'l'
{ 0x3E, 0x20, 0x18, 0x20, 0x1E }, // 'm'
{ 0x3E, 0x10, 0x20, 0x20, 0x1E }, // 'n'
{ 0x1C, 0x22, 0x22, 0x22, 0x1C }, // 'o'
{ 0x3E, 0x28, 0x28, 0x28, 0x10 }, // 'p'
{ 0x10, 0x28, 0x28, 0x18, 0x3E }, // 'q'
{ 0x3E, 0x10, 0x20, 0x20, 0x10 }, // 'r'
{ 0x12, 0x2A, 0x2A, 0x2A, 0x04 }, // 's'
{ 0x20, 0xFC, 0x22, 0x02, 0x04 }, // 't'
{ 0x3C, 0x02, 0x02, 0x04, 0x3E }, // 'u'
{ 0x38, 0x04, 0x02, 0x04, 0x38 }, // 'v'
{ 0x3C, 0x02, 0x0C, 0x02, 0x3C }, // 'w'
{ 0x22, 0x14, 0x08, 0x14, 0x22 }, // 'x'
{ 0x30, 0x0A, 0x0A, 0x0A, 0x3C }, // 'y'
{ 0x22, 0x26, 0x2A, 0x32, 0x22 }, // 'z'
{ 0x00, 0x10, 0x6C, 0x82, 0x00 }, // '{'
{ 0x00, 0x00, 0xFE, 0x00, 0x00 }, // '|'
{ 0x00, 0x82, 0x6C, 0x10, 0x00 }, // '}'
{ 0x10, 0x20, 0x10, 0x08, 0x10 } // '~'
};
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();
}
// 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;
};
// 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() : m_text(), m_wordWrap(false), m_charSpacing(1), m_lineSpacing(1),
m_textLines(), m_preferredWidth(invalidWidthHeight), m_preferredHeight(invalidWidthHeight) { }
void Text::setText(const string & text)
{
if (m_text != text)
{
m_text = text;
invalidate();
m_preferredWidth = m_preferredHeight = invalidWidthHeight;
}
}
void Text::setWordWrap(bool wordWrap)
{
if (m_wordWrap != wordWrap)
{
m_wordWrap = wordWrap;
invalidate();
m_preferredWidth = m_preferredHeight = invalidWidthHeight;
}
}
void Text::setLineSpacing(int lineSpacing)
{
if (lineSpacing > 0 && m_lineSpacing != lineSpacing)
{
m_lineSpacing = lineSpacing;
invalidate();
m_preferredHeight = invalidWidthHeight;
}
}
void Text::setCharSpacing(int charSpacing)
{
if (charSpacing > 0 && m_charSpacing != charSpacing)
{
m_charSpacing = charSpacing;
invalidate();
m_preferredWidth = invalidWidthHeight;
if (wordWrap())
{
m_preferredHeight = invalidWidthHeight;
}
}
}
int Text::preferredWidth() const
{
if (m_preferredWidth == invalidWidthHeight)
{
calculateLayout();
}
return m_preferredWidth;
}
int Text::preferredHeight() const
{
if (m_preferredHeight == invalidWidthHeight)
{
calculateLayout();
}
return m_preferredHeight;
}
void Text::resized()
{
m_preferredWidth = invalidWidthHeight;
if (wordWrap())
{
m_preferredHeight = invalidWidthHeight;
}
}
void Text::doRender(Bitmap & bitmap, int xOffset, int yOffset) const
{
using std::list;
// Ensure layout is up to date.
if (m_preferredWidth == invalidWidthHeight || m_preferredHeight == invalidWidthHeight)
{
calculateLayout();
}
// Render each line.
int lineNum = 0;
for (list<string>::const_iterator lineIt = m_textLines.begin(); lineIt != m_textLines.end(); lineIt++)
{
// Render each character.
for (string::size_type charNum = 0; charNum < lineIt->length(); charNum++)
{
Bitmap character(characterWidth, characterHeight,
&characterMap[(*lineIt)[charNum] - printableCharBegin][0]);
bitmap.overlay(
character,
xOffset + x() + charNum * (characterWidth + m_charSpacing),
yOffset + y() + lineNum * (characterHeight + m_lineSpacing)
);
}
lineNum++;
}
}
void Text::calculateLayout() const
{
using std::list;
// Split string into lines.
m_textLines = tokenizeString(m_text, '\n');
// Remove non-printable characters.
std::for_each(m_textLines.begin(), m_textLines.end(), removeNonprintableChars);
const int lineLen =
(width() / (characterWidth + m_charSpacing)) +
(((width() % (characterWidth + m_charSpacing)) >= characterWidth) ? 1 : 0);
const int numLines =
(height() / (characterHeight + m_lineSpacing)) +
(((height() % (characterHeight + m_lineSpacing)) >= characterHeight) ? 1 : 0);
// Word wrap lines if enabled.
if (m_wordWrap)
{
list<string>::iterator lineIt = m_textLines.begin();
while (lineIt != m_textLines.end())
{
list<string>::iterator nextLineIt = lineIt;
nextLineIt++;
// Wrap current line.
list<string> wrappedLines = wrapLines(*lineIt, lineLen);
m_textLines.splice(lineIt, wrappedLines);
// Remove old line.
m_textLines.erase(lineIt);
lineIt = nextLineIt;
}
}
// Calculate preferred size.
string::size_type maxLineLength =
std::max_element(m_textLines.begin(), m_textLines.end(), compareStringLength)->length();
m_preferredWidth = (maxLineLength > 0) ?
(maxLineLength * characterWidth) + ((maxLineLength - 1) * m_charSpacing) : 1;
m_preferredHeight =
(m_textLines.size() * characterHeight) + ((m_textLines.size() - 1) * m_lineSpacing);
// Remove clipped text.
if (m_textLines.size() > static_cast<list<string>::size_type>(numLines))
m_textLines.resize(numLines);
std::for_each(m_textLines.begin(), m_textLines.end(), TrimString(lineLen));
}