/* --COPYRIGHT--,BSD
|
|
* Copyright (c) 2013, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* --/COPYRIGHT--*/
|
|
|
|
#include "grlib.h"
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! \addtogroup string_api
|
|
//! @{
|
|
//
|
|
//*****************************************************************************
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// The character printed by GrStringDraw in place of any character in the
|
|
// string which does not appear in the font. When using a font which does not
|
|
// include this character, a space is left instead.
|
|
//
|
|
//*****************************************************************************
|
|
#define ABSENT_CHAR_REPLACEMENT '.'
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Counts the number of zeros at the start of a word. This macro uses
|
|
// compiler-specific constructs to perform an inline insertion of the "clz"
|
|
// instruction, which counts the leading zeros directly.
|
|
//
|
|
//*****************************************************************************
|
|
int NumLeadingZeros(long x)
|
|
{
|
|
long y = 0x80, count =0;
|
|
long i;
|
|
for (i=0; i< 32; i++)
|
|
{
|
|
if (0x00 != (x & y ))
|
|
break;
|
|
count++;
|
|
y = y >> 1;
|
|
}
|
|
return count;
|
|
}
|
|
//*****************************************************************************
|
|
//
|
|
//! Determines the width of a string.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param pcString is the string in question.
|
|
//! \param lLength is the length of the string.
|
|
//!
|
|
//! This function determines the width of a string (or portion of the string)
|
|
//! when drawn with a particular font. The \e lLength parameter allows a
|
|
//! portion of the string to be examined without having to insert a NULL
|
|
//! character at the stopping point (would not be possible if the string was
|
|
//! located in flash); specifying a length of -1 will cause the width of the
|
|
//! entire string to be computed.
|
|
//!
|
|
//! \return Returns the width of the string in pixels.
|
|
//
|
|
//*****************************************************************************
|
|
long
|
|
GrStringWidthGet(const tContext *pContext, const char *pcString, long lLength)
|
|
{
|
|
const unsigned short *pusOffset;
|
|
const unsigned char *pucData;
|
|
unsigned char ucFirst, ucLast, ucAbsent;
|
|
long lWidth;
|
|
|
|
//
|
|
// Check the arguments.
|
|
//
|
|
assert(pContext);
|
|
assert(pcString);
|
|
|
|
//
|
|
// Get some pointers to relevant information in the font to make things
|
|
// easier, and give the compiler a hint about extraneous loads that it can
|
|
// avoid.
|
|
//
|
|
if(pContext->pFont->ucFormat & FONT_EX_MARKER)
|
|
{
|
|
tFontEx *pFont;
|
|
|
|
pFont = (tFontEx *)(pContext->pFont);
|
|
|
|
pucData = pFont->pucData;
|
|
pusOffset = pFont->pusOffset;
|
|
ucFirst = pFont->ucFirst;
|
|
ucLast = pFont->ucLast;
|
|
|
|
//
|
|
// Does the default absent character replacement exist in the font?
|
|
//
|
|
if((ABSENT_CHAR_REPLACEMENT >= ucFirst) &&
|
|
(ABSENT_CHAR_REPLACEMENT <= ucLast))
|
|
{
|
|
//
|
|
// Yes - use the standard character when an absent character is
|
|
// found.
|
|
//
|
|
ucAbsent = ABSENT_CHAR_REPLACEMENT;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The default absent character is not present in the font so use
|
|
// the first character (we only use its width here) instead.
|
|
//
|
|
ucAbsent = pFont->ucFirst;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pucData = pContext->pFont->pucData;
|
|
pusOffset = pContext->pFont->pusOffset;
|
|
ucFirst = 32;
|
|
ucLast = 126;
|
|
ucAbsent = ABSENT_CHAR_REPLACEMENT;
|
|
}
|
|
|
|
//
|
|
// Loop through the characters in the string.
|
|
//
|
|
for(lWidth = 0; *pcString && lLength; pcString++, lLength--)
|
|
{
|
|
//
|
|
// Get a pointer to the font data for the next character from the
|
|
// string. If there is not a glyph for the next character, replace it
|
|
// with a ".".
|
|
//
|
|
if((*pcString >= ucFirst) && (*pcString <= ucLast))
|
|
{
|
|
//
|
|
// Add the width of this character as drawn with the given font.
|
|
//
|
|
lWidth += pucData[pusOffset[*pcString - ucFirst] + 1];
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This character does not exist in the font so replace it with
|
|
// a '.' instead. This matches the approach taken in GrStringDraw
|
|
// and ensures that the width returned here represents the
|
|
// rendered dimension of the string.
|
|
//
|
|
lWidth += pucData[pusOffset[ucAbsent - ucFirst] + 1];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the width of the string.
|
|
//
|
|
return(lWidth);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! Draws a string.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param pcString is a pointer to the string to be drawn.
|
|
//! \param lLength is the number of characters from the string that should be
|
|
//! drawn on the screen.
|
|
//! \param lX is the X coordinate of the upper left corner of the string
|
|
//! position on the screen.
|
|
//! \param lY is the Y coordinate of the upper left corner of the string
|
|
//! position on the screen.
|
|
//! \param bOpaque is true of the background of each character should be drawn
|
|
//! and false if it should not (leaving the background as is).
|
|
//!
|
|
//! This function draws a string of test on the screen. The \e lLength
|
|
//! parameter allows a portion of the string to be examined without having to
|
|
//! insert a NULL character at the stopping point (which would not be possible
|
|
//! if the string was located in flash); specifying a length of -1 will cause
|
|
//! the entire string to be rendered (subject to clipping).
|
|
//!
|
|
//! \return None.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
GrStringDraw(const tContext *pContext, const char *pcString, long lLength,
|
|
long lX, long lY, unsigned long bOpaque)
|
|
{
|
|
long lIdx, lX0, lY0, lCount, lOff, lOn, lBit;
|
|
const unsigned char *pucData;
|
|
const unsigned char *pucGlyphs;
|
|
const unsigned short *pusOffset;
|
|
unsigned char ucFirst, ucLast, ucAbsent;
|
|
tContext sCon;
|
|
|
|
long lYSave = lY;
|
|
lY = 0;
|
|
|
|
|
|
//
|
|
// Check the arguments.
|
|
//
|
|
assert(pContext);
|
|
assert(pcString);
|
|
|
|
//
|
|
// Copy the drawing context into a local structure that can be modified.
|
|
//
|
|
sCon = *pContext;
|
|
|
|
//
|
|
// Extract various parameters from the font depending upon whether it's
|
|
// in the tFont or tFontEx format.
|
|
//
|
|
if(pContext->pFont->ucFormat & FONT_EX_MARKER)
|
|
{
|
|
tFontEx *pFont;
|
|
|
|
pFont = (tFontEx *)(pContext->pFont);
|
|
|
|
pucGlyphs = pFont->pucData;
|
|
pusOffset = pFont->pusOffset;
|
|
ucFirst = pFont->ucFirst;
|
|
ucLast = pFont->ucLast;
|
|
|
|
//
|
|
// Does the default absent character replacement exist in the font?
|
|
//
|
|
if((ABSENT_CHAR_REPLACEMENT >= ucFirst) &&
|
|
(ABSENT_CHAR_REPLACEMENT <= ucLast))
|
|
{
|
|
//
|
|
// Yes - use the standard character when an absent character is
|
|
// found.
|
|
//
|
|
ucAbsent = ABSENT_CHAR_REPLACEMENT;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The default absent character is not present in the font so use
|
|
// the first character instead.
|
|
//
|
|
ucAbsent = pFont->ucFirst;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pucGlyphs = pContext->pFont->pucData;
|
|
pusOffset = pContext->pFont->pusOffset;
|
|
ucFirst = 32;
|
|
ucLast = 126;
|
|
ucAbsent = ABSENT_CHAR_REPLACEMENT;
|
|
}
|
|
|
|
//
|
|
// Loop through the characters in the string.
|
|
//
|
|
while(*pcString && lLength--)
|
|
{
|
|
//
|
|
// Stop drawing the string if the right edge of the clipping region has
|
|
// been exceeded.
|
|
//
|
|
if(lX > sCon.sClipRegion.sXMax)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get a pointer to the font data for the next character from the
|
|
// string. If there is not a glyph for the next character, replace it
|
|
// with the "absent" character (usually '.').
|
|
//
|
|
if((*pcString >= ucFirst) && (*pcString <= ucLast))
|
|
{
|
|
pucData = (pucGlyphs + pusOffset[*pcString - ucFirst]);
|
|
}
|
|
else
|
|
{
|
|
pucData = (pucGlyphs + pusOffset[ucAbsent - ucFirst]);
|
|
}
|
|
pcString++;
|
|
|
|
//
|
|
// See if the entire character is to the left of the clipping region.
|
|
//
|
|
if((lX + pucData[1]) < sCon.sClipRegion.sXMin)
|
|
{
|
|
//
|
|
// Increment the X coordinate by the width of the character.
|
|
//
|
|
lX += pucData[1];
|
|
|
|
//
|
|
// Go to the next character in the string.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Loop through the bytes in the encoded data for this glyph.
|
|
//
|
|
for(lIdx = 2, lX0 = 0, lBit = 0, lY0 = 0; lIdx < pucData[0]; )
|
|
{
|
|
//
|
|
// See if the bottom of the clipping region has been exceeded.
|
|
//
|
|
if((lY + lY0) > sCon.sClipRegion.sYMax)
|
|
{
|
|
//
|
|
// Stop drawing this character.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if the font is uncompressed.
|
|
//
|
|
if((sCon.pFont->ucFormat & ~FONT_EX_MARKER) ==
|
|
FONT_FMT_UNCOMPRESSED)
|
|
{
|
|
//
|
|
// Count the number of off pixels from this position in the
|
|
// glyph image.
|
|
//
|
|
for(lOff = 0; lIdx < pucData[0]; )
|
|
{
|
|
//
|
|
// Get the number of zero pixels at this position.
|
|
//
|
|
lCount = NumLeadingZeros(pucData[lIdx] << lBit);
|
|
|
|
//
|
|
// If there were more than 8, then it is a "false" result
|
|
// since it counted beyond the end of the current byte.
|
|
// Therefore, simply limit it to the number of pixels
|
|
// remaining in this byte.
|
|
//
|
|
if(lCount > 8)
|
|
{
|
|
lCount = 8 - lBit;
|
|
}
|
|
|
|
//
|
|
// Increment the number of off pixels.
|
|
//
|
|
lOff += lCount;
|
|
|
|
//
|
|
// Increment the bit position within the byte.
|
|
//
|
|
lBit += lCount;
|
|
|
|
//
|
|
// See if the end of the byte has been reached.
|
|
//
|
|
if(lBit == 8)
|
|
{
|
|
//
|
|
// Advance to the next byte and continue counting off
|
|
// pixels.
|
|
//
|
|
lBit = 0;
|
|
lIdx++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Since the end of the byte was not reached, there
|
|
// must be an on pixel. Therefore, stop counting off
|
|
// pixels.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Count the number of on pixels from this position in the
|
|
// glyph image.
|
|
//
|
|
for(lOn = 0; lIdx < pucData[0]; )
|
|
{
|
|
//
|
|
// Get the number of one pixels at this location (by
|
|
// inverting the data and counting the number of zeros).
|
|
//
|
|
lCount = NumLeadingZeros(~(pucData[lIdx] << lBit));
|
|
|
|
|
|
//
|
|
// If there were more than 8, then it is a "false" result
|
|
// since it counted beyond the end of the current byte.
|
|
// Therefore, simply limit it to the number of pixels
|
|
// remaining in this byte.
|
|
//
|
|
if(lCount > 8)
|
|
{
|
|
lCount = 8 - lBit;
|
|
}
|
|
|
|
//
|
|
// Increment the number of on pixels.
|
|
//
|
|
lOn += lCount;
|
|
|
|
//
|
|
// Increment the bit position within the byte.
|
|
//
|
|
lBit += lCount;
|
|
|
|
//
|
|
// See if the end of the byte has been reached.
|
|
//
|
|
if(lBit == 8)
|
|
{
|
|
//
|
|
// Advance to the next byte and continue counting on
|
|
// pixels.
|
|
//
|
|
lBit = 0;
|
|
lIdx++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Since the end of the byte was not reached, there
|
|
// must be an off pixel. Therefore, stop counting on
|
|
// pixels.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Otherwise, the font is compressed with a pixel RLE scheme.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// See if this is a byte that encodes some on and off pixels.
|
|
//
|
|
if(pucData[lIdx])
|
|
{
|
|
//
|
|
// Extract the number of off pixels.
|
|
//
|
|
lOff = (pucData[lIdx] >> 4) & 15;
|
|
|
|
//
|
|
// Extract the number of on pixels.
|
|
//
|
|
lOn = pucData[lIdx] & 15;
|
|
|
|
//
|
|
// Skip past this encoded byte.
|
|
//
|
|
lIdx++;
|
|
}
|
|
|
|
//
|
|
// Otherwise, see if this is a repeated on pixel byte.
|
|
//
|
|
else if(pucData[lIdx + 1] & 0x80)
|
|
{
|
|
//
|
|
// There are no off pixels in this encoding.
|
|
//
|
|
lOff = 0;
|
|
|
|
//
|
|
// Extract the number of on pixels.
|
|
//
|
|
lOn = (pucData[lIdx + 1] & 0x7f) * 8;
|
|
|
|
//
|
|
// Skip past these two encoded bytes.
|
|
//
|
|
lIdx += 2;
|
|
}
|
|
|
|
//
|
|
// Otherwise, this is a repeated off pixel byte.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// Extract the number of off pixels.
|
|
//
|
|
lOff = pucData[lIdx + 1] * 8;
|
|
|
|
//
|
|
// There are no on pixels in this encoding.
|
|
//
|
|
lOn = 0;
|
|
|
|
//
|
|
// Skip past these two encoded bytes.
|
|
//
|
|
lIdx += 2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop while there are any off pixels.
|
|
//
|
|
while(lOff)
|
|
{
|
|
//
|
|
// See if the bottom of the clipping region has been exceeded.
|
|
//
|
|
if((lY + lY0) > sCon.sClipRegion.sYMax)
|
|
{
|
|
//
|
|
// Ignore the remainder of the on pixels.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if there is more than one on pixel that will fit onto
|
|
// the current row.
|
|
//
|
|
if((lOff > 1) && ((lX0 + 1) < pucData[1]))
|
|
{
|
|
//
|
|
// Determine the number of on pixels that will fit on this
|
|
// row.
|
|
//
|
|
lCount = (((lX0 + lOff) > pucData[1]) ? pucData[1] - lX0 :
|
|
lOff);
|
|
|
|
//
|
|
// If this row is within the clipping region, draw a
|
|
// horizontal line that corresponds to the sequence of on
|
|
// pixels.
|
|
//
|
|
if(((lY + lY0) >= sCon.sClipRegion.sYMin) && bOpaque)
|
|
{
|
|
sCon.ulForeground = pContext->ulBackground;
|
|
GrLineDrawH(&sCon, lX + lX0, lX + lX0 + lCount - 1,
|
|
lYSave + lY + lY0);
|
|
}
|
|
|
|
//
|
|
// Decrement the count of on pixels by the number on this
|
|
// row.
|
|
//
|
|
lOff -= lCount;
|
|
|
|
//
|
|
// Increment the X offset by the number of on pixels.
|
|
//
|
|
lX0 += lCount;
|
|
}
|
|
|
|
//
|
|
// Otherwise, there is only a single on pixel that can be
|
|
// drawn.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// If this pixel is within the clipping region, then draw
|
|
// it.
|
|
//
|
|
if(((lX + lX0) >= sCon.sClipRegion.sXMin) &&
|
|
((lX + lX0) <= sCon.sClipRegion.sXMax) &&
|
|
((lY + lY0) >= sCon.sClipRegion.sYMin) && bOpaque)
|
|
{
|
|
DpyPixelDraw(pContext->pDisplay, lX + lX0, lYSave + lY + lY0,
|
|
pContext->ulBackground);
|
|
}
|
|
|
|
//
|
|
// Decrement the count of on pixels.
|
|
//
|
|
lOff--;
|
|
|
|
//
|
|
// Increment the X offset.
|
|
//
|
|
lX0++;
|
|
}
|
|
|
|
//
|
|
// See if the X offset has reached the right side of the
|
|
// character glyph.
|
|
//
|
|
if(lX0 == pucData[1])
|
|
{
|
|
//
|
|
// Increment the Y offset.
|
|
//
|
|
lY0++;
|
|
|
|
//
|
|
// Reset the X offset to the left side of the character
|
|
// glyph.
|
|
//
|
|
lX0 = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop while there are any on pixels.
|
|
//
|
|
while(lOn)
|
|
{
|
|
//
|
|
// See if the bottom of the clipping region has been exceeded.
|
|
//
|
|
if((lY + lY0) > sCon.sClipRegion.sYMax)
|
|
{
|
|
//
|
|
// Ignore the remainder of the on pixels.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if there is more than one on pixel that will fit onto
|
|
// the current row.
|
|
//
|
|
if((lOn > 1) && ((lX0 + 1) < pucData[1]))
|
|
{
|
|
//
|
|
// Determine the number of on pixels that will fit on this
|
|
// row.
|
|
//
|
|
lCount = (((lX0 + lOn) > pucData[1]) ? pucData[1] - lX0 :
|
|
lOn);
|
|
|
|
//
|
|
// If this row is within the clipping region, draw a
|
|
// horizontal line that corresponds to the sequence of on
|
|
// pixels.
|
|
//
|
|
if((lY + lY0) >= sCon.sClipRegion.sYMin)
|
|
{
|
|
sCon.ulForeground = pContext->ulForeground;
|
|
GrLineDrawH(&sCon, lX + lX0, lX + lX0 + lCount - 1,
|
|
lYSave + lY + lY0);
|
|
|
|
}
|
|
|
|
//
|
|
// Decrement the count of on pixels by the number on this
|
|
// row.
|
|
//
|
|
lOn -= lCount;
|
|
|
|
//
|
|
// Increment the X offset by the number of on pixels.
|
|
//
|
|
lX0 += lCount;
|
|
}
|
|
|
|
//
|
|
// Otherwise, there is only a single on pixel that can be
|
|
// drawn.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// If this pixel is within the clipping region, then draw
|
|
// it.
|
|
//
|
|
if(((lX + lX0) >= sCon.sClipRegion.sXMin) &&
|
|
((lX + lX0) <= sCon.sClipRegion.sXMax) &&
|
|
((lY + lY0) >= sCon.sClipRegion.sYMin))
|
|
DpyPixelDraw(pContext->pDisplay, lX + lX0, lYSave + lY + lY0,
|
|
pContext->ulForeground);
|
|
|
|
//
|
|
// Decrement the count of on pixels.
|
|
//
|
|
lOn--;
|
|
|
|
//
|
|
// Increment the X offset.
|
|
//
|
|
lX0++;
|
|
}
|
|
|
|
//
|
|
// See if the X offset has reached the right side of the
|
|
// character glyph.
|
|
//
|
|
if(lX0 == pucData[1])
|
|
{
|
|
//
|
|
// Increment the Y offset.
|
|
//
|
|
lY0++;
|
|
|
|
//
|
|
// Reset the X offset to the left side of the character
|
|
// glyph.
|
|
//
|
|
lX0 = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Increment the X coordinate by the width of the character.
|
|
//
|
|
lX += pucData[1];
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Close the Doxygen group.
|
|
//! @}
|
|
//
|
|
//*****************************************************************************
|