summaryrefslogtreecommitdiff
path: root/grlib/string.c
diff options
context:
space:
mode:
authorYuval Adam <yuval@y3xz.com>2015-03-13 12:24:02 +0200
committerYuval Adam <yuval@y3xz.com>2015-03-13 12:24:02 +0200
commit788db64b8642bf31de6930d18a62177c64163ee0 (patch)
tree38e8291d1fcfceac7ad2a393ad74396ed6453d24 /grlib/string.c
parentc686407d271989d5c4bcc1b3c4f5bb6fcd1846a8 (diff)
Add grlib, fixes #5
Diffstat (limited to 'grlib/string.c')
-rw-r--r--grlib/string.c2853
1 files changed, 2853 insertions, 0 deletions
diff --git a/grlib/string.c b/grlib/string.c
new file mode 100644
index 0000000..53e3004
--- /dev/null
+++ b/grlib/string.c
@@ -0,0 +1,2853 @@
+//*****************************************************************************
+//
+// string.c - Routines for drawing text.
+//
+// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the Tiva Graphics Library.
+//
+//*****************************************************************************
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "inc/hw_types.h"
+#include "driverlib/debug.h"
+#include "grlib/grlib.h"
+
+//*****************************************************************************
+//
+//! \addtogroup primitives_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 '.'
+
+//*****************************************************************************
+//
+//! 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 i32Length 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 i32Length 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.
+//
+//*****************************************************************************
+#ifdef GRLIB_REMOVE_WIDE_FONT_SUPPORT
+int32_t
+GrStringWidthGet(const tContext *pContext, const char *pcString,
+ int32_t i32Length)
+{
+ const uint16_t *pui16Offset;
+ const uint8_t *pui8Data;
+ uint8_t ui8First, ui8Last, ui8Absent;
+ int32_t i32Width;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pContext);
+ ASSERT(pcString);
+
+ //
+ // This function doesn't support wide character fonts or wrapped fonts.
+ //
+ ASSERT(!(pContext->psFont->ui8Format &&
+ (FONT_FMT_WRAPPED | FONT_WIDE_MARKER)));
+
+ //
+ // 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->psFont->ui8Format & FONT_EX_MARKER)
+ {
+ tFontEx *psFont;
+
+ psFont = (tFontEx *)(pContext->psFont);
+
+ pui8Data = psFont->pui8Data;
+ pui16Offset = psFont->pui16Offset;
+ ui8First = psFont->ui8First;
+ ui8Last = psFont->ui8Last;
+
+ //
+ // Does the default absent character replacement exist in the font?
+ //
+ if((ABSENT_CHAR_REPLACEMENT >= ui8First) &&
+ (ABSENT_CHAR_REPLACEMENT <= ui8Last))
+ {
+ //
+ // Yes - use the standard character when an absent character is
+ // found.
+ //
+ ui8Absent = 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.
+ //
+ ui8Absent = psFont->ui8First;
+ }
+ }
+ else
+ {
+ pui8Data = pContext->psFont->pui8Data;
+ pui16Offset = pContext->psFont->pui16Offset;
+ ui8First = 32;
+ ui8Last = 126;
+ ui8Absent = ABSENT_CHAR_REPLACEMENT;
+ }
+
+ //
+ // Loop through the characters in the string.
+ //
+ for(i32Width = 0; *pcString && i32Length; pcString++, i32Length--)
+ {
+ //
+ // 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 >= ui8First) && (*pcString <= ui8Last))
+ {
+ //
+ // Add the width of this character as drawn with the given font.
+ //
+ i32Width += pui8Data[pui16Offset[*pcString - ui8First] + 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.
+ //
+ i32Width += pui8Data[pui16Offset[ui8Absent - ui8First] + 1];
+ }
+ }
+
+ //
+ // Return the width of the string.
+ //
+ return(i32Width);
+}
+#else
+int32_t
+GrStringWidthGet(const tContext *pContext, const char *pcString,
+ int32_t i32Length)
+{
+ const uint8_t *pui8Data;
+ uint8_t ui8Width, ui8Height, ui8Baseline, ui8Format;
+ uint32_t ui32Count, ui32Char, ui32Skip;
+ int32_t i32Width;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pContext);
+ ASSERT(pcString);
+
+ //
+ // Initialize our string length.
+ //
+ i32Width = 0;
+
+ //
+ // Set the maximum number of characters we should render. Note that the
+ // value -1 is used to indicate that the function should render until it
+ // hits the end of the string so casting it to a uint32_t here is
+ // fine since this says keep rendering for 2^32 characters. We are very
+ // unlikely to ever be passed a string this long.
+ //
+ ui32Count = (uint32_t)i32Length;
+
+ //
+ // Loop through each character in the string.
+ //
+ while(ui32Count)
+ {
+ //
+ // Get the next character to render.
+ //
+ ui32Char = GrStringNextCharGet(pContext, pcString, ui32Count,
+ &ui32Skip);
+
+ //
+ // If we ran out of characters to render, drop out of the loop.
+ //
+ if(!ui32Char)
+ {
+ break;
+ }
+
+ //
+ // Get information on this glyph.
+ //
+ pui8Data = GrFontGlyphDataGet(pContext->psFont, ui32Char, &ui8Width);
+
+ //
+ // Does the glyph exist?
+ //
+ if(!pui8Data)
+ {
+ //
+ // No - get the absent character replacement information.
+ //
+ pui8Data = GrFontGlyphDataGet(pContext->psFont,
+ ABSENT_CHAR_REPLACEMENT, &ui8Width);
+
+ //
+ // Does this character exist in the font?
+ //
+ if(!pui8Data)
+ {
+ //
+ // No - look for the ASCII/Unicode space character.
+ //
+ pui8Data = GrFontGlyphDataGet(pContext->psFont, 0x20,
+ &ui8Width);
+
+ //
+ // Does this exist?
+ //
+ if(!pui8Data)
+ {
+ //
+ // No - give up and just pad with a character cell of space.
+ //
+ GrFontInfoGet(pContext->psFont, &ui8Format, &ui8Width,
+ &ui8Height, &ui8Baseline);
+ }
+ }
+ }
+
+ //
+ // Increment our string length.
+ //
+ i32Width += (int32_t)ui8Width;
+
+ //
+ // Move on to the next character.
+ //
+ pcString += ui32Skip;
+ ui32Count -= ui32Skip;
+ }
+
+ //
+ // Return the width of the string.
+ //
+ return(i32Width);
+}
+#endif
+
+//*****************************************************************************
+//
+//! 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 i32Length is the number of characters from the string that should be
+//! drawn on the screen.
+//! \param i32X is the X coordinate of the upper left corner of the string
+//! position on the screen.
+//! \param i32Y 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 text on the screen. The \e i32Length
+//! 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.
+//
+//*****************************************************************************
+#ifdef GRLIB_REMOVE_WIDE_FONT_SUPPORT
+//
+// This version of GrStringDraw supports the original tFont and tFontEx ASCII
+// and ISO8859 (8 bit) fonts only.
+//
+void
+GrStringDraw(const tContext *pContext, const char *pcString, int32_t i32Length,
+ int32_t i32X, int32_t i32Y, uint32_t bOpaque)
+{
+ int32_t i32Idx, i32X0, i32Y0, i32Count, i32Off, i32On, i32Bit;
+ const uint8_t *pui8Data;
+ const uint8_t *pui8Glyphs;
+ const uint16_t *pui16Offset;
+ uint8_t ui8First, ui8Last, ui8Absent;
+ tContext i16Con;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pContext);
+ ASSERT(pcString);
+
+ //
+ // This function doesn't support wide character fonts or wrapped fonts.
+ //
+ ASSERT(!(pContext->psFont->ui8Format &&
+ (FONT_FMT_WRAPPED | FONT_WIDE_MARKER)));
+
+ //
+ // Copy the drawing context into a local structure that can be modified.
+ //
+ i16Con = *pContext;
+
+ //
+ // Extract various parameters from the font depending upon whether it's
+ // in the tFont or tFontEx format.
+ //
+ if(pContext->psFont->ui8Format & FONT_EX_MARKER)
+ {
+ tFontEx *psFont;
+
+ psFont = (tFontEx *)(pContext->psFont);
+
+ pui8Glyphs = psFont->pui8Data;
+ pui16Offset = psFont->pui16Offset;
+ ui8First = psFont->ui8First;
+ ui8Last = psFont->ui8Last;
+
+ //
+ // Does the default absent character replacement exist in the font?
+ //
+ if((ABSENT_CHAR_REPLACEMENT >= ui8First) &&
+ (ABSENT_CHAR_REPLACEMENT <= ui8Last))
+ {
+ //
+ // Yes - use the standard character when an absent character is
+ // found.
+ //
+ ui8Absent = ABSENT_CHAR_REPLACEMENT;
+ }
+ else
+ {
+ //
+ // The default absent character is not present in the font so use
+ // the first character instead.
+ //
+ ui8Absent = psFont->ui8First;
+ }
+ }
+ else
+ {
+ pui8Glyphs = pContext->psFont->pui8Data;
+ pui16Offset = pContext->psFont->pui16Offset;
+ ui8First = 32;
+ ui8Last = 126;
+ ui8Absent = ABSENT_CHAR_REPLACEMENT;
+ }
+
+ //
+ // Loop through the characters in the string.
+ //
+ while(*pcString && i32Length--)
+ {
+ //
+ // Stop drawing the string if the right edge of the clipping region has
+ // been exceeded.
+ //
+ if(i32X > i16Con.sClipRegion.i16XMax)
+ {
+ 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 >= ui8First) && (*pcString <= ui8Last))
+ {
+ pui8Data = (pui8Glyphs + pui16Offset[*pcString - ui8First]);
+ }
+ else
+ {
+ pui8Data = (pui8Glyphs + pui16Offset[ui8Absent - ui8First]);
+ }
+ pcString++;
+
+ //
+ // See if the entire character is to the left of the clipping region.
+ //
+ if((i32X + pui8Data[1]) < i16Con.sClipRegion.i16XMin)
+ {
+ //
+ // Increment the X coordinate by the width of the character.
+ //
+ i32X += pui8Data[1];
+
+ //
+ // Go to the next character in the string.
+ //
+ continue;
+ }
+
+ //
+ // Loop through the bytes in the encoded data for this glyph.
+ //
+ for(i32Idx = 2, i32X0 = 0, i32Bit = 0, i32Y0 = 0;
+ i32Idx < pui8Data[0]; )
+ {
+ //
+ // See if the bottom of the clipping region has been exceeded.
+ //
+ if((i32Y + i32Y0) > i16Con.sClipRegion.i16YMax)
+ {
+ //
+ // Stop drawing this character.
+ //
+ break;
+ }
+
+ //
+ // See if the font is uncompressed.
+ //
+ if((i16Con.psFont->ui8Format & ~FONT_EX_MARKER) ==
+ FONT_FMT_UNCOMPRESSED)
+ {
+ //
+ // Count the number of off pixels from this position in the
+ // glyph image.
+ //
+ for(i32Off = 0; i32Idx < pui8Data[0]; )
+ {
+ //
+ // Get the number of zero pixels at this position.
+ //
+ i32Count = NumLeadingZeros(pui8Data[i32Idx] <<
+ (24 + i32Bit));
+
+ //
+ // 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(i32Count > 8)
+ {
+ i32Count = 8 - i32Bit;
+ }
+
+ //
+ // Increment the number of off pixels.
+ //
+ i32Off += i32Count;
+
+ //
+ // Increment the bit position within the byte.
+ //
+ i32Bit += i32Count;
+
+ //
+ // See if the end of the byte has been reached.
+ //
+ if(i32Bit == 8)
+ {
+ //
+ // Advance to the next byte and continue counting off
+ // pixels.
+ //
+ i32Bit = 0;
+ i32Idx++;
+ }
+ 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(i32On = 0; i32Idx < pui8Data[0]; )
+ {
+ //
+ // Get the number of one pixels at this location (by
+ // inverting the data and counting the number of zeros).
+ //
+ i32Count = NumLeadingZeros(~(pui8Data[i32Idx] <<
+ (24 + i32Bit)));
+
+ //
+ // 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(i32Count > 8)
+ {
+ i32Count = 8 - i32Bit;
+ }
+
+ //
+ // Increment the number of on pixels.
+ //
+ i32On += i32Count;
+
+ //
+ // Increment the bit position within the byte.
+ //
+ i32Bit += i32Count;
+
+ //
+ // See if the end of the byte has been reached.
+ //
+ if(i32Bit == 8)
+ {
+ //
+ // Advance to the next byte and continue counting on
+ // pixels.
+ //
+ i32Bit = 0;
+ i32Idx++;
+ }
+ 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(pui8Data[i32Idx])
+ {
+ //
+ // Extract the number of off pixels.
+ //
+ i32Off = (pui8Data[i32Idx] >> 4) & 15;
+
+ //
+ // Extract the number of on pixels.
+ //
+ i32On = pui8Data[i32Idx] & 15;
+
+ //
+ // Skip past this encoded byte.
+ //
+ i32Idx++;
+ }
+
+ //
+ // Otherwise, see if this is a repeated on pixel byte.
+ //
+ else if(pui8Data[i32Idx + 1] & 0x80)
+ {
+ //
+ // There are no off pixels in this encoding.
+ //
+ i32Off = 0;
+
+ //
+ // Extract the number of on pixels.
+ //
+ i32On = (pui8Data[i32Idx + 1] & 0x7f) * 8;
+
+ //
+ // Skip past these two encoded bytes.
+ //
+ i32Idx += 2;
+ }
+
+ //
+ // Otherwise, this is a repeated off pixel byte.
+ //
+ else
+ {
+ //
+ // Extract the number of off pixels.
+ //
+ i32Off = pui8Data[i32Idx + 1] * 8;
+
+ //
+ // There are no on pixels in this encoding.
+ //
+ i32On = 0;
+
+ //
+ // Skip past these two encoded bytes.
+ //
+ i32Idx += 2;
+ }
+ }
+
+ //
+ // Loop while there are any off pixels.
+ //
+ while(i32Off)
+ {
+ //
+ // See if the bottom of the clipping region has been exceeded.
+ //
+ if((i32Y + i32Y0) > i16Con.sClipRegion.i16YMax)
+ {
+ //
+ // 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((i32Off > 1) && ((i32X0 + 1) < pui8Data[1]))
+ {
+ //
+ // Determine the number of on pixels that will fit on this
+ // row.
+ //
+ i32Count = (((i32X0 + i32Off) > pui8Data[1]) ?
+ pui8Data[1] - i32X0 : i32Off);
+
+ //
+ // If this row is within the clipping region, draw a
+ // horizontal line that corresponds to the sequence of on
+ // pixels.
+ //
+ if(((i32Y + i32Y0) >= i16Con.sClipRegion.i16YMin) &&
+ bOpaque)
+ {
+ i16Con.ui32Foreground = pContext->ui32Background;
+ GrLineDrawH(&i16Con, i32X + i32X0, i32X + i32X0 +
+ i32Count - 1, i32Y + i32Y0);
+ }
+
+ //
+ // Decrement the count of on pixels by the number on this
+ // row.
+ //
+ i32Off -= i32Count;
+
+ //
+ // Increment the X offset by the number of on pixels.
+ //
+ i32X0 += i32Count;
+ }
+
+ //
+ // 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(((i32X + i32X0) >= i16Con.sClipRegion.i16XMin) &&
+ ((i32X + i32X0) <= i16Con.sClipRegion.i16XMax) &&
+ ((i32Y + i32Y0) >= i16Con.sClipRegion.i16YMin) &&
+ bOpaque)
+ {
+ DpyPixelDraw(pContext->psDisplay, i32X + i32X0,
+ i32Y + i32Y0, pContext->ui32Background);
+ }
+
+ //
+ // Decrement the count of on pixels.
+ //
+ i32Off--;
+
+ //
+ // Increment the X offset.
+ //
+ i32X0++;
+ }
+
+ //
+ // See if the X offset has reached the right side of the
+ // character glyph.
+ //
+ if(i32X0 == pui8Data[1])
+ {
+ //
+ // Increment the Y offset.
+ //
+ i32Y0++;
+
+ //
+ // Reset the X offset to the left side of the character
+ // glyph.
+ //
+ i32X0 = 0;
+ }
+ }
+
+ //
+ // Loop while there are any on pixels.
+ //
+ while(i32On)
+ {
+ //
+ // See if the bottom of the clipping region has been exceeded.
+ //
+ if((i32Y + i32Y0) > i16Con.sClipRegion.i16YMax)
+ {
+ //
+ // 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((i32On > 1) && ((i32X0 + 1) < pui8Data[1]))
+ {
+ //
+ // Determine the number of on pixels that will fit on this
+ // row.
+ //
+ i32Count = (((i32X0 + i32On) > pui8Data[1]) ?
+ pui8Data[1] - i32X0 : i32On);
+
+ //
+ // If this row is within the clipping region, draw a
+ // horizontal line that corresponds to the sequence of on
+ // pixels.
+ //
+ if((i32Y + i32Y0) >= i16Con.sClipRegion.i16YMin)
+ {
+ i16Con.ui32Foreground = pContext->ui32Foreground;
+ GrLineDrawH(&i16Con, i32X + i32X0, i32X + i32X0 +
+ i32Count - 1, i32Y + i32Y0);
+ }
+
+ //
+ // Decrement the count of on pixels by the number on this
+ // row.
+ //
+ i32On -= i32Count;
+
+ //
+ // Increment the X offset by the number of on pixels.
+ //
+ i32X0 += i32Count;
+ }
+
+ //
+ // 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(((i32X + i32X0) >= i16Con.sClipRegion.i16XMin) &&
+ ((i32X + i32X0) <= i16Con.sClipRegion.i16XMax) &&
+ ((i32Y + i32Y0) >= i16Con.sClipRegion.i16YMin))
+ {
+ DpyPixelDraw(pContext->psDisplay, i32X + i32X0,
+ i32Y + i32Y0, pContext->ui32Foreground);
+ }
+
+ //
+ // Decrement the count of on pixels.
+ //
+ i32On--;
+
+ //
+ // Increment the X offset.
+ //
+ i32X0++;
+ }
+
+ //
+ // See if the X offset has reached the right side of the
+ // character glyph.
+ //
+ if(i32X0 == pui8Data[1])
+ {
+ //
+ // Increment the Y offset.
+ //
+ i32Y0++;
+
+ //
+ // Reset the X offset to the left side of the character
+ // glyph.
+ //
+ i32X0 = 0;
+ }
+ }
+ }
+
+ //
+ // Increment the X coordinate by the width of the character.
+ //
+ i32X += pui8Data[1];
+ }
+}
+#else
+//
+// This version of GrStringDraw supports the original tFont and tFontEx ASCII
+// and ISO8859 (8 bit) fonts along with new wide character set, relocatable
+// fonts. Support for source and font codepages is also included.
+//
+void
+GrStringDraw(const tContext *pContext, const char *pcString, int32_t i32Length,
+ int32_t i32X, int32_t i32Y, uint32_t bOpaque)
+{
+ ASSERT(pContext);
+ ASSERT(pContext->pfnStringRenderer);
+
+ //
+ // Call the currently registered string rendering function.
+ //
+ pContext->pfnStringRenderer(pContext, pcString, i32Length, i32X, i32Y,
+ bOpaque);
+}
+
+//*****************************************************************************
+//
+//! The default text string rendering function.
+//!
+//! \param pContext is a pointer to the drawing context to use.
+//! \param pcString is a pointer to the string to be drawn.
+//! \param i32Length is the number of characters from the string that should be
+//! drawn on the screen.
+//! \param i32X is the X coordinate of the upper left corner of the string
+//! position on the screen.
+//! \param i32Y 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 acts as the default string rendering function called by
+//! GrStringDraw() if no language-specific renderer is registered. It draws a
+//! string of text on the screen using the text orientation currently set in
+//! the graphics context. The \e i32Length 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).
+//!
+//! Applications are not expected to call this function directly but should
+//! call GrStringDraw() instead. This function is provided as an aid to
+//! language-specific renders which may call it to render parts of a string
+//! at particular positions after dealing with any language-specific layout
+//! issues such as, for example, inserting left-to-right numbers within a
+//! right-to-left Arabic string.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+GrDefaultStringRenderer(const tContext *pContext, const char *pcString,
+ int32_t i32Length, int32_t i32X, int32_t i32Y,
+ bool bOpaque)
+{
+ uint8_t ui8Format, ui8Width, ui8MaxWidth, ui8Height, ui8Baseline;
+ uint32_t ui32Char, ui32Count, ui32Skip;
+ const uint8_t *pui8Data;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pContext);
+ ASSERT(pcString);
+
+ //
+ // Get information on the font we are rendering the text in.
+ //
+ GrFontInfoGet(pContext->psFont, &ui8Format, &ui8MaxWidth, &ui8Height,
+ &ui8Baseline);
+
+ //
+ // If the string is completely outside the clipping region, don't even
+ // start rendering it.
+ //
+ if((i32Y > pContext->sClipRegion.i16YMax) ||
+ ((i32Y + ui8Height) < pContext->sClipRegion.i16YMin))
+ {
+ return;
+ }
+
+ //
+ // Set the maximum number of characters we should render. Note that the
+ // value -1 is used to indicate that the function should render until it
+ // hits the end of the string so casting it to an uint32_t here is
+ // fine since this says keep rendering for 2^32 characters. We are very
+ // unlikely to ever be passed a string this long.
+ //
+ ui32Count = (uint32_t)i32Length;
+
+ //
+ // Loop through each character in the string.
+ //
+ while(ui32Count)
+ {
+ //
+ // Get the next character to render.
+ //
+ ui32Char = GrStringNextCharGet(pContext, pcString, ui32Count,
+ &ui32Skip);
+
+ //
+ // If we ran out of characters to render, return immediately.
+ //
+ if(!ui32Char)
+ {
+ return;
+ }
+
+ //
+ // If we are already outside the clipping region, exit early.
+ //
+ if(i32X >= pContext->sClipRegion.i16XMax)
+ {
+ return;
+ }
+
+ //
+ // Get the glyph data pointer for this character.
+ //
+ pui8Data = GrFontGlyphDataGet(pContext->psFont, ui32Char, &ui8Width);
+
+ //
+ // Does this glyph exist in the font?
+ //
+ if(!pui8Data)
+ {
+ //
+ // Look for the character we are supposed to use in place of absent
+ // glyphs.
+ //
+ pui8Data = GrFontGlyphDataGet(pContext->psFont,
+ ABSENT_CHAR_REPLACEMENT, &ui8Width);
+
+ //
+ // Does this glyph exist in the font?
+ //
+ if(!pui8Data)
+ {
+ //
+ // Last chance - look for the space character.
+ //
+ pui8Data = GrFontGlyphDataGet(pContext->psFont, ' ',
+ &ui8Width);
+ }
+ }
+
+ //
+ // Did we find something to render?
+ //
+ if(pui8Data)
+ {
+ GrFontGlyphRender(pContext, pui8Data, i32X, i32Y,
+ (ui8Format & FONT_FMT_PIXEL_RLE) ? true : false,
+ bOpaque);
+ i32X += ui8Width;
+ }
+ else
+ {
+ //
+ // Leave a space in place of the undefined glyph.
+ //
+ i32X += ui8MaxWidth;
+ }
+
+ //
+ // Move on to the next character.
+ //
+ pcString += ui32Skip;
+ ui32Count -= ui32Skip;
+ }
+}
+
+//*****************************************************************************
+//
+//! Returns the codepoint of the first character in a string.
+//!
+//! \param pContext points to the graphics context in use.
+//! \param pcString points to the first byte of the string from which the
+//! next character is to be parsed.
+//! \param ui32Count provides the number of bytes in the pcString buffer.
+//! \param pui32Skip points to storage which will be written with the number of
+//! bytes that must be skipped in the string buffer to move past the
+//! current character.
+//!
+//! This function is used to walk through a string extracting one character at
+//! a time. The input string is assumed to be encoded using the currently-
+//! selected string codepage (as set via a call to the GrStringCodepageSet()
+//! function). The value returned is the codepoint of the first character in
+//! the string as mapped into the current font's codepage. This may be passed
+//! to the GrFontGlyphDataGet() function to retrieve the glyph data for the
+//! character.
+//!
+//! Since variable length encoding schemes such as UTF-8 are supported, this
+//! function also returns information on the size of the character that has
+//! been parsed, allowing the caller to increment the string pointer by the
+//! relevant amount before querying the next character in the string.
+//!
+//! \return Returns the font codepoint representing the first character in the
+//! string or 0 if no valid character was found.
+//!
+//*****************************************************************************
+uint32_t
+GrStringNextCharGet(const tContext *pContext, const char *pcString,
+ uint32_t ui32Count, uint32_t *pui32Skip)
+{
+ ASSERT(pContext);
+ ASSERT(pcString);
+ ASSERT(pui32Skip);
+
+ //
+ // If the string is empty, return immediately.
+ //
+ if(!ui32Count)
+ {
+ return(0);
+ }
+
+ //
+ // Has a codepage mapping table been registered for this context?
+ //
+ if(pContext->pCodePointMapTable)
+ {
+ //
+ // Yes - use the relevant mapping function
+ //
+ return(pContext->pCodePointMapTable[pContext->ui8CodePointMap].
+ pfnMapChar(pcString, ui32Count, pui32Skip));
+ }
+ else
+ {
+ //
+ // No codepage mapping table has been registered so fall back on the
+ // assumption that we are using ASCII or ISO8859-1 for both the
+ // string and font codepages (i.e. the legacy case).
+ //
+ *pui32Skip = 1;
+ return((uint32_t)*pcString);
+ }
+}
+
+//*****************************************************************************
+//
+//! Renders a single character glyph on the display at a given position.
+//!
+//! \param pContext points to the graphics context in use.
+//! \param pui8Data points to the first byte of data for the glyph to be
+//! rendered.
+//! \param i32X is the X coordinate of the top left pixel of the glyph.
+//! \param i32Y is the Y coordinate of the top left pixel of the glyph.
+//! \param bCompressed is \b true if the data pointed to by \b pui8Data is in
+//! compressed format or \b false if uncompressed.
+//! \param bOpaque is \b true of background pixels are to be written or \b
+//! false if only foreground pixels are drawn.
+//!
+//! This function is included as an aid to language-specific string rendering
+//! functions. Applications are expected to render strings and characters
+//! using calls to GrStringDraw or GrStringDrawCentered and should not call
+//! this function directly.
+//!
+//! A string rendering function may call this low level API to place a single
+//! character glyph on the display at a particular position. The rendered
+//! glyph is subject to the clipping rectangle currently set in the passed
+//! graphics context. Rendering colors are also taken from the context
+//! structure. Glyph data pointed to by \b pui8Data should be retrieved using
+//! a call to GrFontGlyphDataGet().
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+GrFontGlyphRender(const tContext *pContext, const uint8_t *pui8Data,
+ int32_t i32X, int32_t i32Y, bool bCompressed,
+ bool bOpaque)
+{
+ int32_t i32Idx, i32X0, i32Y0, i32Count, i32Off, i32On, i32Bit;
+ int32_t i32ClipX1, i32ClipX2;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pContext);
+ ASSERT(pui8Data);
+
+ //
+ // Stop drawing the string if the right edge of the clipping region has
+ // been exceeded.
+ //
+ if(i32X > pContext->sClipRegion.i16XMax)
+ {
+ return;
+ }
+
+ //
+ // See if the entire character is to the left of the clipping region.
+ //
+ if((i32X + pui8Data[1]) < pContext->sClipRegion.i16XMin)
+ {
+ return;
+ }
+
+ //
+ // Loop through the bytes in the encoded data for this glyph.
+ //
+ for(i32Idx = 2, i32X0 = 0, i32Bit = 0, i32Y0 = 0; i32Idx < pui8Data[0]; )
+ {
+ //
+ // See if the bottom of the clipping region has been exceeded.
+ //
+ if((i32Y + i32Y0) > pContext->sClipRegion.i16YMax)
+ {
+ //
+ // Stop drawing this character.
+ //
+ break;
+ }
+
+ //
+ // See if the font is uncompressed.
+ //
+ if(!bCompressed)
+ {
+ //
+ // Count the number of off pixels from this position in the
+ // glyph image.
+ //
+ for(i32Off = 0; i32Idx < pui8Data[0]; )
+ {
+ //
+ // Get the number of zero pixels at this position.
+ //
+ i32Count = NumLeadingZeros(pui8Data[i32Idx] << (24 + i32Bit));
+
+ //
+ // 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(i32Count > 8)
+ {
+ i32Count = 8 - i32Bit;
+ }
+
+ //
+ // Increment the number of off pixels.
+ //
+ i32Off += i32Count;
+
+ //
+ // Increment the bit position within the byte.
+ //
+ i32Bit += i32Count;
+
+ //
+ // See if the end of the byte has been reached.
+ //
+ if(i32Bit == 8)
+ {
+ //
+ // Advance to the next byte and continue counting off
+ // pixels.
+ //
+ i32Bit = 0;
+ i32Idx++;
+ }
+ 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(i32On = 0; i32Idx < pui8Data[0]; )
+ {
+ //
+ // Get the number of one pixels at this location (by
+ // inverting the data and counting the number of zeros).
+ //
+ i32Count = NumLeadingZeros(~(pui8Data[i32Idx] <<
+ (24 + i32Bit)));
+
+ //
+ // 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(i32Count > 8)
+ {
+ i32Count = 8 - i32Bit;
+ }
+
+ //
+ // Increment the number of on pixels.
+ //
+ i32On += i32Count;
+
+ //
+ // Increment the bit position within the byte.
+ //
+ i32Bit += i32Count;
+
+ //
+ // See if the end of the byte has been reached.
+ //
+ if(i32Bit == 8)
+ {
+ //
+ // Advance to the next byte and continue counting on
+ // pixels.
+ //
+ i32Bit = 0;
+ i32Idx++;
+ }
+ 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(pui8Data[i32Idx])
+ {
+ //
+ // Extract the number of off pixels.
+ //
+ i32Off = (pui8Data[i32Idx] >> 4) & 15;
+
+ //
+ // Extract the number of on pixels.
+ //
+ i32On = pui8Data[i32Idx] & 15;
+
+ //
+ // Skip past this encoded byte.
+ //
+ i32Idx++;
+ }
+
+ //
+ // Otherwise, see if this is a repeated on pixel byte.
+ //
+ else if(pui8Data[i32Idx + 1] & 0x80)
+ {
+ //
+ // There are no off pixels in this encoding.
+ //
+ i32Off = 0;
+
+ //
+ // Extract the number of on pixels.
+ //
+ i32On = (pui8Data[i32Idx + 1] & 0x7f) * 8;
+
+ //
+ // Skip past these two encoded bytes.
+ //
+ i32Idx += 2;
+ }
+
+ //
+ // Otherwise, this is a repeated off pixel byte.
+ //
+ else
+ {
+ //
+ // Extract the number of off pixels.
+ //
+ i32Off = pui8Data[i32Idx + 1] * 8;
+
+ //
+ // There are no on pixels in this encoding.
+ //
+ i32On = 0;
+
+ //
+ // Skip past these two encoded bytes.
+ //
+ i32Idx += 2;
+ }
+ }
+
+ //
+ // Loop while there are any off pixels.
+ //
+ while(i32Off)
+ {
+ //
+ // See if the bottom of the clipping region has been exceeded.
+ //
+ if((i32Y + i32Y0) > pContext->sClipRegion.i16YMax)
+ {
+ //
+ // 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((i32Off > 1) && ((i32X0 + 1) < pui8Data[1]))
+ {
+ //
+ // Determine the number of on pixels that will fit on this
+ // row.
+ //
+ i32Count = (((i32X0 + i32Off) > pui8Data[1]) ?
+ pui8Data[1] - i32X0 : i32Off);
+
+ //
+ // If this row is within the clipping region, draw a
+ // horizontal line that corresponds to the sequence of on
+ // pixels.
+ //
+ if(((i32Y + i32Y0) >= pContext->sClipRegion.i16YMin) &&
+ bOpaque)
+ {
+ if((i32X + i32X0) < pContext->sClipRegion.i16XMin)
+ {
+ i32ClipX1 = pContext->sClipRegion.i16XMin;
+ }
+ else
+ {
+ i32ClipX1 = i32X + i32X0;
+ }
+
+ if((i32X + i32X0 + i32Count - 1) >
+ pContext->sClipRegion.i16XMax)
+ {
+ i32ClipX2 = pContext->sClipRegion.i16XMax;
+ }
+ else
+ {
+ i32ClipX2 = i32X + i32X0 + i32Count - 1;
+ }
+
+ DpyLineDrawH(pContext->psDisplay, i32ClipX1, i32ClipX2,
+ i32Y + i32Y0, pContext->ui32Background);
+ }
+
+ //
+ // Decrement the count of on pixels by the number on this
+ // row.
+ //
+ i32Off -= i32Count;
+
+ //
+ // Increment the X offset by the number of on pixels.
+ //
+ i32X0 += i32Count;
+ }
+
+ //
+ // 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(((i32X + i32X0) >= pContext->sClipRegion.i16XMin) &&
+ ((i32X + i32X0) <= pContext->sClipRegion.i16XMax) &&
+ ((i32Y + i32Y0) >= pContext->sClipRegion.i16YMin) &&
+ bOpaque)
+ {
+ DpyPixelDraw(pContext->psDisplay, i32X + i32X0,
+ i32Y + i32Y0, pContext->ui32Background);
+ }
+
+ //
+ // Decrement the count of on pixels.
+ //
+ i32Off--;
+
+ //
+ // Increment the X offset.
+ //
+ i32X0++;
+ }
+
+ //
+ // See if the X offset has reached the right side of the
+ // character glyph.
+ //
+ if(i32X0 == pui8Data[1])
+ {
+ //
+ // Increment the Y offset.
+ //
+ i32Y0++;
+
+ //
+ // Reset the X offset to the left side of the character
+ // glyph.
+ //
+ i32X0 = 0;
+ }
+ }
+
+ //
+ // Loop while there are any on pixels.
+ //
+ while(i32On)
+ {
+ //
+ // See if the bottom of the clipping region has been exceeded.
+ //
+ if((i32Y + i32Y0) > pContext->sClipRegion.i16YMax)
+ {
+ //
+ // 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((i32On > 1) && ((i32X0 + 1) < pui8Data[1]))
+ {
+ //
+ // Determine the number of on pixels that will fit on this
+ // row.
+ //
+ i32Count = (((i32X0 + i32On) > pui8Data[1]) ?
+ pui8Data[1] - i32X0 : i32On);
+
+ //
+ // If this row is within the clipping region, draw a
+ // horizontal line that corresponds to the sequence of on
+ // pixels.
+ //
+ if((i32Y + i32Y0) >= pContext->sClipRegion.i16YMin)
+ {
+ if((i32X + i32X0) >= pContext->sClipRegion.i16XMin)
+ {
+ i32ClipX1 = i32X + i32X0;
+ }
+ else
+ {
+ i32ClipX1 = pContext->sClipRegion.i16XMin;
+ }
+
+ if((i32X + i32X0 + i32Count - 1) >
+ pContext->sClipRegion.i16XMax)
+ {
+ i32ClipX2 = pContext->sClipRegion.i16XMax;
+ }
+ else
+ {
+ i32ClipX2 = i32X + i32X0 + i32Count - 1;
+ }
+
+ DpyLineDrawH(pContext->psDisplay, i32ClipX1, i32ClipX2,
+ i32Y + i32Y0, pContext->ui32Foreground);
+ }
+
+ //
+ // Decrement the count of on pixels by the number on this
+ // row.
+ //
+ i32On -= i32Count;
+
+ //
+ // Increment the X offset by the number of on pixels.
+ //
+ i32X0 += i32Count;
+ }
+
+ //
+ // 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(((i32X + i32X0) >= pContext->sClipRegion.i16XMin) &&
+ ((i32X + i32X0) <= pContext->sClipRegion.i16XMax) &&
+ ((i32Y + i32Y0) >= pContext->sClipRegion.i16YMin))
+ {
+ DpyPixelDraw(pContext->psDisplay, i32X + i32X0,
+ i32Y + i32Y0, pContext->ui32Foreground);
+ }
+
+ //
+ // Decrement the count of on pixels.
+ //
+ i32On--;
+
+ //
+ // Increment the X offset.
+ //
+ i32X0++;
+ }
+
+ //
+ // See if the X offset has reached the right side of the
+ // character glyph.
+ //
+ if(i32X0 == pui8Data[1])
+ {
+ //
+ // Increment the Y offset.
+ //
+ i32Y0++;
+
+ //
+ // Reset the X offset to the left side of the character
+ // glyph.
+ //
+ i32X0 = 0;
+ }
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! Retrieves header information from a font.
+//!
+//! \param psFont points to the font whose information is to be queried.
+//! \param pui8Format points to storage which will be written with the font
+//! format.
+//! \param pui8MaxWidth points to storage which will be written with the
+//! maximum character width for the font in pixels.
+//! \param pui8Height points to storage which will be written with the height
+//! of the font character cell in pixels.
+//! \param pui8Baseline points to storage which will be written with the font
+//! baseline offset in pixels.
+//!
+//! This function may be used to retrieve information about a given font. The
+//! \e psFont parameter may point to any supported font format including wrapped
+//! fonts described using a \e tFontWrapper structure (with the pointer cast
+//! to a tFont pointer).
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+GrFontInfoGet(const tFont *psFont, uint8_t *pui8Format, uint8_t *pui8MaxWidth,
+ uint8_t *pui8Height, uint8_t *pui8Baseline)
+{
+ //
+ // Parameter sanity checks.
+ //
+ ASSERT(psFont);
+ ASSERT(pui8Format);
+ ASSERT(pui8MaxWidth);
+ ASSERT(pui8Height);
+ ASSERT(pui8Baseline);
+
+ //
+ // Is this a wrapped font?
+ //
+ if(psFont->ui8Format & FONT_FMT_WRAPPED)
+ {
+ tFontWrapper *psFontWrapper;
+
+ //
+ // Yes - get a pointer to the relevant header type and call the
+ // access function to retrieve the font information.
+ //
+ psFontWrapper = (tFontWrapper *)psFont;
+ psFontWrapper->pFuncs->pfnFontInfoGet(psFontWrapper->pui8FontId,
+ pui8Format, pui8MaxWidth,
+ pui8Height, pui8Baseline);
+ }
+ else
+ {
+ //
+ // This is not a wrapped font so we can read the information directly
+ // from the font structure passed.
+ //
+ *pui8Format = psFont->ui8Format;
+ *pui8MaxWidth = psFont->ui8MaxWidth;
+ *pui8Height = psFont->ui8Height;
+ *pui8Baseline = psFont->ui8Baseline;
+ }
+}
+
+//*****************************************************************************
+//
+//! Gets the baseline of a font.
+//!
+//! \param psFont is a pointer to the font to query.
+//!
+//! This function determines the baseline position of a font. The baseline is
+//! the offset between the top of the font and the bottom of the capital
+//! letters. The only font data that exists below the baseline are the
+//! descenders on some lower-case letters (such as ``y'').
+//!
+//! \return Returns the baseline of the font, in pixels.
+//
+//*****************************************************************************
+uint32_t
+GrFontBaselineGet(const tFont *psFont)
+{
+ uint8_t ui8Format, ui8Width, ui8Height, ui8Baseline;
+
+ ASSERT(psFont);
+
+ if(psFont->ui8Format != FONT_FMT_WRAPPED)
+ {
+ return((uint32_t)(psFont->ui8Baseline));
+ }
+ else
+ {
+ tFontWrapper *pWrap;
+
+ pWrap = (tFontWrapper *)psFont;
+ pWrap->pFuncs->pfnFontInfoGet(pWrap->pui8FontId, &ui8Format, &ui8Width,
+ &ui8Height, &ui8Baseline);
+ return((uint32_t)ui8Baseline);
+ }
+}
+
+//*****************************************************************************
+//
+//! Gets the height of a font.
+//!
+//! \param psFont is a pointer to the font to query.
+//!
+//! This function determines the height of a font. The height is the offset
+//! between the top of the font and the bottom of the font, including any
+//! ascenders and descenders.
+//!
+//! \return Returns the height of the font, in pixels.
+//
+//*****************************************************************************
+uint32_t
+GrFontHeightGet(const tFont *psFont)
+{
+ uint8_t ui8Format, ui8Width, ui8Height, ui8Baseline;
+
+ ASSERT(psFont);
+
+ if(psFont->ui8Format != FONT_FMT_WRAPPED)
+ {
+ return(psFont->ui8Height);
+ }
+ else
+ {
+ tFontWrapper *pWrap;
+
+ pWrap = (tFontWrapper *)psFont;
+ pWrap->pFuncs->pfnFontInfoGet(pWrap->pui8FontId, &ui8Format, &ui8Width,
+ &ui8Height, &ui8Baseline);
+ return((uint32_t)ui8Height);
+ }
+}
+
+//*****************************************************************************
+//
+//! Gets the maximum width of a font.
+//!
+//! \param psFont is a pointer to the font to query.
+//!
+//! This function determines the maximum width of a font. The maximum width is
+//! the width of the widest individual character in the font.
+//!
+//! \return Returns the maximum width of the font, in pixels.
+//
+//*****************************************************************************
+uint32_t
+GrFontMaxWidthGet(const tFont *psFont)
+{
+ uint8_t ui8Format, ui8Width, ui8Height, ui8Baseline;
+
+ ASSERT(psFont);
+
+ if(psFont->ui8Format != FONT_FMT_WRAPPED)
+ {
+ return(psFont->ui8MaxWidth);
+ }
+ else
+ {
+ tFontWrapper *pWrap;
+
+ pWrap = (tFontWrapper *)psFont;
+ pWrap->pFuncs->pfnFontInfoGet(pWrap->pui8FontId, &ui8Format, &ui8Width,
+ &ui8Height, &ui8Baseline);
+ return((uint32_t)ui8Width);
+ }
+}
+
+//*****************************************************************************
+//
+// Retrieves a pointer to the data for a specific glyph in a tFont or tFontEx
+// font.
+//
+// \param psFont points to the font whose glyph is to be queried.
+// \param ui32CodePoint idenfities the specific glyph whose data is being
+// queried.
+// \param pui8Width points to storage which will be written with the
+// width of the requested glyph in pixels.
+//
+// This function may be used to retrieve the pixel data for a particular glyph
+// in a font described using a tFont or tFontEx type.
+//
+// \return Returns a pointer to the data for the requested glyph or NULL if
+// the glyph does not exist in the font.
+//
+//*****************************************************************************
+static const uint8_t *
+FontGlyphDataGet(const tFont *psFont, uint32_t ui32CodePoint,
+ uint8_t *pui8Width)
+{
+ const uint8_t *pui8Glyphs;
+ const uint16_t *pui16Offset;
+ uint8_t ui8First, ui8Last;
+ const tFontEx *psFontEx;
+ const uint8_t *pui8Data;
+
+ //
+ // Extract various parameters from the font depending upon whether it's
+ // in the tFont or tFontEx format.
+ //
+ if(psFont->ui8Format & FONT_EX_MARKER)
+ {
+ psFontEx = (const tFontEx *)psFont;
+
+ pui8Glyphs = psFontEx->pui8Data;
+ pui16Offset = psFontEx->pui16Offset;
+ ui8First = psFontEx->ui8First;
+ ui8Last = psFontEx->ui8Last;
+ }
+ else
+ {
+ pui8Glyphs = psFont->pui8Data;
+ pui16Offset = psFont->pui16Offset;
+ ui8First = 32;
+ ui8Last = 126;
+ }
+
+ //
+ // Does the codepoint passed exist in the font?
+ //
+ if((ui32CodePoint >= ui8First) && (ui32CodePoint <= ui8Last))
+ {
+ //
+ // Yes - return a pointer to the glyph data for the character.
+ //
+ pui8Data = pui8Glyphs + pui16Offset[ui32CodePoint - ui8First];
+ *pui8Width = pui8Data[1];
+ return(pui8Data);
+ }
+ else
+ {
+ //
+ // No - the glyph doesn't exist so return NULL to indicate this.
+ //
+ return(0);
+ }
+}
+
+//*****************************************************************************
+//
+// Retrieves a pointer to the data for a specific glyph in a tFontWide font.
+//
+// \param psFont points to the font whose glyph is to be queried.
+// \param ui32CodePoint idenfities the specific glyph whose data is being
+// queried.
+// \param pui8Width points to storage which will be written with the
+// width of the requested glyph in pixels.
+//
+// This function may be used to retrieve the pixel data for a particular glyph
+// in a font described using a tFontWide type.
+//
+// \return Returns a pointer to the data for the requested glyph or NULL if
+// the glyph does not exist in the font.
+//
+//*****************************************************************************
+static const uint8_t *
+FontWideGlyphDataGet(const tFontWide *psFont, uint32_t ui32CodePoint,
+ uint8_t *pui8Width)
+{
+ const uint8_t *pui8Data;
+ tFontBlock *pBlock;
+ uint32_t *pui32OffsetTable;
+ uint32_t ui32Loop;
+ uint32_t ui32Offset;
+
+ //
+ // Get a pointer to the first block description in the font.
+ //
+ pBlock = (tFontBlock *)(psFont + 1);
+
+ //
+ // Run through the blocks of the font looking for the one that contains
+ // our codepoint.
+ //
+ for(ui32Loop = 0; ui32Loop < psFont->ui16NumBlocks; ui32Loop++)
+ {
+ //
+ // Does the codepoint lie within this block?
+ //
+ if((ui32CodePoint >= pBlock[ui32Loop].ui32StartCodepoint) &&
+ (ui32CodePoint < (pBlock[ui32Loop].ui32StartCodepoint +
+ pBlock[ui32Loop].ui32NumCodepoints)))
+ {
+ //
+ // Yes - drop out of the loop early.
+ //
+ break;
+ }
+ }
+
+ //
+ // Did we find the block?
+ //
+ if(ui32Loop == psFont->ui16NumBlocks)
+ {
+ //
+ // No - return NULL to indicate that the character wasn't found.
+ //
+ return(0);
+ }
+
+ //
+ // Get the offset to the glyph data via the block's offset table.
+ //
+ pui32OffsetTable = (uint32_t *)((uint8_t *)psFont +
+ pBlock[ui32Loop].ui32GlyphTableOffset);
+ ui32Offset = pui32OffsetTable[ui32CodePoint -
+ pBlock[ui32Loop].ui32StartCodepoint];
+
+ //
+ // Is the offset non-zero? Zero offset indicates that the glyph is not
+ // encoded in the font.
+ //
+ if(ui32Offset)
+ {
+ //
+ // The offset is not 0 so this glyph does exist. Return a pointer to
+ // its data.
+ //
+ pui8Data = (const uint8_t *)pui32OffsetTable + ui32Offset;
+ *pui8Width = pui8Data[1];
+ return(pui8Data);
+ }
+ else
+ {
+ //
+ // The glyph offset was 0 so this implies that the glyph does not
+ // exist. Return NULL to indicate this.
+ //
+ return(0);
+ }
+}
+
+//*****************************************************************************
+//
+//! Retrieves a pointer to the data for a specific font glyph.
+//!
+//! \param psFont points to the font whose glyph is to be queried.
+//! \param ui32CodePoint idenfities the specific glyph whose data is being
+//! queried.
+//! \param pui8Width points to storage which will be written with the
+//! width of the requested glyph in pixels.
+//!
+//! This function may be used to retrieve the pixel data for a particular glyph
+//! in a font. The pointer returned may be passed to GrFontGlyphRender to
+//! draw the glyph on the display. The format of the data may be determined
+//! from the font format returned via a call to GrFontInfoGet().
+//!
+//! \return Returns a pointer to the data for the requested glyph or NULL if
+//! the glyph does not exist in the font.
+//
+//*****************************************************************************
+const uint8_t *
+GrFontGlyphDataGet(const tFont *psFont, uint32_t ui32CodePoint,
+ uint8_t *pui8Width)
+{
+ ASSERT(psFont);
+ ASSERT(pui8Width);
+
+ //
+ // What type of font are we dealing with here?
+ //
+ if(psFont->ui8Format == FONT_FMT_WRAPPED)
+ {
+ tFontWrapper *psFontWrapper;
+
+ //
+ // This is a wrapped font so call the access function required to get
+ // the required information.
+ //
+ psFontWrapper = (tFontWrapper *)psFont;
+ return(psFontWrapper->pFuncs->
+ pfnFontGlyphDataGet(psFontWrapper->pui8FontId, ui32CodePoint,
+ pui8Width));
+ }
+ else if (psFont->ui8Format & FONT_WIDE_MARKER)
+ {
+ //
+ // This is a wide character set font so call the relevant function to
+ // retrieve the glyph data pointer.
+ //
+ return(FontWideGlyphDataGet((const tFontWide *)psFont, ui32CodePoint,
+ pui8Width));
+ }
+ else
+ {
+ //
+ // This is an 8 bit font so call the relevant function to retrieve the
+ // glyph data pointer.
+ //
+ return(FontGlyphDataGet(psFont, ui32CodePoint, pui8Width));
+ }
+}
+
+//*****************************************************************************
+//
+//! Returns the codepage supported by the given font.
+//!
+//! \param psFont points to the font whose codepage is to be returned.
+//!
+//! This function returns the codepage supported by the font whose pointer is
+//! passed. The codepage defines the mapping between a given character code
+//! and the glyph that represents it. Standard codepages are identified by
+//! labels of the form \b CODEPAGE_xxxx. Fonts may also be encoded using
+//! application specific codepages with values of 0x8000 or higher.
+//!
+//! \return Returns the font codepage identifier.
+//!
+//*****************************************************************************
+uint16_t
+GrFontCodepageGet(const tFont *psFont)
+{
+ ASSERT(psFont);
+
+ //
+ // Is this a wide character set font?
+ //
+ if(psFont->ui8Format & FONT_WIDE_MARKER)
+ {
+ //
+ // Yes - read the font codepage from the header.
+ //
+ return(((tFontWide *)psFont)->ui16Codepage);
+ }
+ else if(psFont->ui8Format & FONT_FMT_WRAPPED)
+ {
+ //
+ // This is a wrapper-based font so call the access function to get
+ // its codepage.
+ //
+ ASSERT(((tFontWrapper *)psFont)->pFuncs->pfnFontCodepageGet);
+
+ return(((tFontWrapper *)psFont)->pFuncs->
+ pfnFontCodepageGet(((tFontWrapper *)psFont)->pui8FontId));
+ }
+ else
+ {
+ //
+ // No - this is an old format font so just return ISO8859-1. This is
+ // compatible with ASCII so should be benign.
+ //
+ return(CODEPAGE_ISO8859_1);
+ }
+}
+
+//*****************************************************************************
+//
+// Determines which codepoint mapping function to use based on the current
+// source codepage and font selection in the context.
+//
+//*****************************************************************************
+static int32_t
+UpdateContextCharMapping(tContext *pContext)
+{
+ uint32_t ui32Loop;
+ uint16_t ui16FontCodepage;
+
+ //
+ // Make sure we have a font selected.
+ //
+ if(!pContext->psFont)
+ {
+ //
+ // No font is yet selected so we can't determine the codepage map to
+ // use.
+ //
+ return(-1);
+ }
+
+ //
+ // Get the current font's codepage.
+ //
+ ui16FontCodepage = GrFontCodepageGet(pContext->psFont);
+
+ //
+ // Look through the codepage mapping functions we have been given and
+ // find an appropriate one.
+ //
+ for(ui32Loop = 0; ui32Loop < pContext->ui8NumCodePointMaps; ui32Loop++)
+ {
+ if((pContext->pCodePointMapTable[ui32Loop].ui16SrcCodepage ==
+ pContext->ui16Codepage) &&
+ (pContext->pCodePointMapTable[ui32Loop].ui16FontCodepage ==
+ ui16FontCodepage))
+ {
+ //
+ // We found a suitable mapping function so remember it.
+ //
+ pContext->ui8CodePointMap = (uint8_t)ui32Loop;
+ return((int32_t)ui32Loop);
+ }
+ }
+
+ //
+ // If we get here, no suitable mapping function could be found. Set things
+ // up to use the first mapping function (even though it's not right).
+ //
+ pContext->ui8CodePointMap = 0;
+ return(-1);
+}
+//*****************************************************************************
+//
+//! Returns the number of blocks of character encoded by a font.
+//!
+//! \param psFont is a pointer to the font which is to be queried.
+//!
+//! This function may be used to query the number of contiguous blocks of
+//! codepoints (characters) encoded by a given font. This is primarily of use
+//! to applications which wish to parse fonts directly to, for example, display
+//! all glyphs in the font. It is unlikely that applications which wish to
+//! display text strings would need to call this function.
+//!
+//! The \e psFont parameter may point to any supported font format including
+//! wrapped fonts described using the \e tFontWrapper structure (assuming, of
+//! course, that the structure pointer is cast to a \e tFont pointer).
+//!
+//! \return Returns the number of blocks of codepoints within the font.
+//
+//*****************************************************************************
+uint16_t
+GrFontNumBlocksGet(const tFont *psFont)
+{
+ ASSERT(psFont);
+
+ //
+ // Is this a wide character set font?
+ //
+ if(psFont->ui8Format & FONT_WIDE_MARKER)
+ {
+ //
+ // Yes - read the number of blocks from the header.
+ //
+ return(((tFontWide *)psFont)->ui16NumBlocks);
+ }
+ else if(psFont->ui8Format & FONT_FMT_WRAPPED)
+ {
+ //
+ // This is a wrapper-based font so call the access function to get
+ // the information.
+ //
+ ASSERT(((tFontWrapper *)psFont)->pFuncs->pfnFontNumBlocksGet);
+
+ return(((tFontWrapper *)psFont)->pFuncs->
+ pfnFontNumBlocksGet(((tFontWrapper *)psFont)->pui8FontId));
+ }
+ else
+ {
+ //
+ // No - this is an old format font so it only supports a single block
+ // of characters.
+ //
+ return(1);
+ }
+}
+
+//*****************************************************************************
+//
+//! Returns the number of blocks of character encoded by a font.
+//!
+//! \param psFont is a pointer to the font which is to be queried.
+//! \param ui16BlockIndex is the index of the codepoint block to be queried.
+//! \param pui32Start points to storage which is written with the codepoint
+//! number of the first glyph in the block.
+//!
+//! This function may be used to query the contents of a particular block of
+//! codepoints (characters) encoded by a given font. This is primarily of use
+//! to applications which wish to parse fonts directly to, for example, display
+//! all glyphs in the font. It is unlikely that applications which wish to
+//! display text strings would need to call this function.
+//!
+//! The number of blocks in the font may be queried by calling
+//! GrFontNumBlocksGet(). The \e ui16BlockIndex selects a block and valid
+//! values are from, 0 to the number of blocks in the font - 1.
+//!
+//! The \e pui32Start pointer is written with the codepoint number of the
+//! first glyph in the given block. It is assumed that each block contains
+//! a contiguous block of glyphs so the actual codepoints represented in the
+//! block will be from \e *pui32Start to (\e *pui32Start + return value - 1).
+//!
+//! The \e psFont parameter may point to any supported font format including
+//! wrapped fonts described using the \e tFontWrapper structure (assuming, of
+//! course, that the structure pointer is cast to a \e tFont pointer).
+//!
+//! \return Returns the number of blocks of codepoints within the block.
+//
+//*****************************************************************************
+uint32_t
+GrFontBlockCodepointsGet(const tFont *psFont, uint16_t ui16BlockIndex,
+ uint32_t *pui32Start)
+{
+ ASSERT(psFont);
+ ASSERT(pui32Start);
+
+ //
+ // Is this a wide character set font?
+ //
+ if(psFont->ui8Format & FONT_WIDE_MARKER)
+ {
+ tFontWide *psFontWide;
+ tFontBlock *pBlock;
+
+ //
+ // This is a wide character set font. Is the block index valid?
+ //
+ psFontWide = (tFontWide *)psFont;
+ if(ui16BlockIndex >= psFontWide->ui16NumBlocks)
+ {
+ //
+ // The block number is invalid so return 0 to indicate the error.
+ //
+ return(0);
+ }
+
+ //
+ // Yes - find the relevant block table and extract the required
+ // information from it.
+ //
+ pBlock = (tFontBlock *)(psFontWide + 1);
+ *pui32Start = pBlock[ui16BlockIndex].ui32StartCodepoint;
+ return(pBlock[ui16BlockIndex].ui32NumCodepoints);
+ }
+ else if(psFont->ui8Format & FONT_FMT_WRAPPED)
+ {
+ //
+ // This is a wrapper-based font so call the access function to get
+ // its codepage.
+ //
+ ASSERT(((tFontWrapper *)psFont)->pFuncs->pfnFontBlockCodepointsGet);
+
+ return(((tFontWrapper *)psFont)->pFuncs->
+ pfnFontBlockCodepointsGet(((tFontWrapper *)psFont)->pui8FontId,
+ ui16BlockIndex, pui32Start));
+ }
+ else
+ {
+ //
+ // No - this is an old format font so it only supports a single block
+ // of
+ //
+ if(ui16BlockIndex != 0)
+ {
+ //
+ // An invalid block number was passed so return 0 to indicate an
+ // error.
+ //
+ return(0);
+ }
+
+ //
+ // We were passed a valid block number (0) so return the start
+ // codepoint and number of characters. Is this an extended font or
+ // the original ASCII-only flavor?
+ //
+ if(psFont->ui8Format & FONT_EX_MARKER)
+ {
+ tFontEx *psFontEx;
+
+ //
+ // It's an extended font so read the character range from the
+ // header.
+ //
+ psFontEx = (tFontEx *)psFont;
+
+ *pui32Start = (uint32_t)psFontEx->ui8First;
+ return((uint32_t)(psFontEx->ui8Last - psFontEx->ui8First + 1));
+ }
+ else
+ {
+ //
+ // This is an ASCII font so it supports a fixed set of characters.
+ //
+ *pui32Start = 0x20;
+ return(96);
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! Provides GrLib with a table of source/font codepage mapping functions.
+//!
+//! \param pContext is a pointer to the context to modify.
+//! \param pCodePointMapTable points to an array of structures each defining
+//! the mapping from a source text codepage to a destination font codepage.
+//! \param ui8NumMaps provides the number of entries in the \e
+//! pCodePointMapTable array.
+//!
+//! This function provides GrLib with a set of functions that can be used to
+//! map text encoded in a particular codepage to one other codepage. These
+//! functions are used to allow GrLib to parse text strings and display the
+//! correct glyphs from the font. The mapping function used by the library
+//! will depend upon the source text codepage set using a call to
+//! GrStringCodepageSet() and the context's font, set using GrContextFontSet().
+//!
+//! If no conversion function is available to map from the selected source
+//! codepage to the font's codepage, GrLib use the first conversion function
+//! provided in the codepoint map table and the displayed text will likely be
+//! incorrect.
+//!
+//! If this call is not made, GrLib assumes ISO8859-1 encoding for both the
+//! source text and font to maintain backwards compatibility for applications
+//! which were developed prior to the introduction of international character
+//! set support.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+GrCodepageMapTableSet(tContext *pContext, tCodePointMap *pCodePointMapTable,
+ uint8_t ui8NumMaps)
+{
+ ASSERT(pContext);
+ ASSERT(pCodePointMapTable);
+ ASSERT(ui8NumMaps);
+
+ //
+ // Remember the table details.
+ //
+ pContext->pCodePointMapTable = pCodePointMapTable;
+ pContext->ui8NumCodePointMaps = ui8NumMaps;
+
+ //
+ // Update the character mapping to ensure that we show the right glyphs.
+ //
+ UpdateContextCharMapping(pContext);
+}
+
+//*****************************************************************************
+//
+//! Sets the source text codepage to be used.
+//!
+//! \param pContext is a pointer to the context to modify.
+//! \param ui16Codepage is the identifier of the codepage for the text that the
+//! application will pass on future calls to the GrStringDraw() and
+//! GrStringDrawCentered() functions.
+//!
+//! This function sets the codepage that will be used when rendering text
+//! via future calls to GrStringDraw() or GrStringDrawCentered(). The codepage
+//! defines the mapping between specific numbers used to define characters and
+//! the actual character glyphs displayed. By default, GrLib assumes text
+//! passed is encoded using the ISO8859-1 which supports ASCII and western
+//! European character sets. Applications wishing to use multi-byte character
+//! sets or alphabets other than those supported by ISO8859-1 should set an
+//! appropriate codepage such as UTF-8.
+//!
+//! It is important to ensure that your application makes use of fonts which
+//! support the required codepage or that you have supplied GrLib with a
+//! codepage mapping function that allows translation of your chosen text
+//! codepage into the codepage supported by the fonts in use. Several
+//! mapping functions for commonly-used codepages are provided and others can
+//! be written easily to support different text and font codepage combinations.
+//! Codepage mapping functions are provided to GrLib in a table passed as a
+//! parameter to the function GrCodepageMapTableSet().
+//!
+//! \return None.
+//
+//*****************************************************************************
+int32_t
+GrStringCodepageSet(tContext *pContext, uint16_t ui16Codepage)
+{
+ ASSERT(pContext);
+
+ //
+ // Remember the codepage to be used.
+ //
+ pContext->ui16Codepage = ui16Codepage;
+
+ //
+ // Update the character mapping to ensure that we show the right glyphs.
+ //
+ return(UpdateContextCharMapping(pContext));
+}
+
+//*****************************************************************************
+//
+//! Sets the font to be used.
+//!
+//! \param pContext is a pointer to the drawing context to modify.
+//! \param psFont is a pointer to the font to be used.
+//!
+//! This function sets the font to be used for string drawing operations in the
+//! specified drawing context.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+GrContextFontSet(tContext *pContext, const tFont *psFont)
+{
+ ASSERT(pContext);
+ ASSERT(psFont);
+
+ //
+ // Remember the font to be used.
+ //
+ pContext->psFont = psFont;
+
+ //
+ // Update the character mapping to ensure that we show the right glyphs.
+ //
+ UpdateContextCharMapping(pContext);
+}
+#endif
+
+//*****************************************************************************
+//
+// Definitions and variables used by the decompression routine for the string
+// table.
+//
+//*****************************************************************************
+#define SC_MAX_INDEX 2047
+#define SC_IS_NULL 0x0000ffff
+#define SC_GET_LEN(v) ((v) >> (32 - 5))
+#define SC_GET_INDEX(v) (((v) >> 16) & SC_MAX_INDEX)
+#define SC_GET_OFF(v) ((v) & SC_IS_NULL)
+
+#define SC_FLAG_COMPRESSED 0x00008000
+#define SC_OFFSET_M 0x00007fff
+
+//*****************************************************************************
+//
+// The globals that hold the shortcuts to various locations and values in the
+// table.
+//
+//*****************************************************************************
+static const uint32_t *g_pui32StringTable;
+static const uint16_t *g_pui16LanguageTable;
+static const uint8_t *g_pui8StringData;
+
+static uint16_t g_ui16Language;
+static uint16_t g_ui16NumLanguages;
+static uint16_t g_ui16NumStrings;
+
+//*****************************************************************************
+//
+//! This function sets the location of the current string table.
+//!
+//! \param pvTable is a pointer to a string table that was generated by the
+//! string compression utility.
+//!
+//! This function is used to set the string table to use for strings in an
+//! application. This string table is created by the string compression
+//! utility. This function is used to swap out multiple string tables if the
+//! application requires more than one table. It does not allow using more
+//! than one string table at a time.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+GrStringTableSet(const void *pvTable)
+{
+ //
+ // Save the number of languages and number of strings.
+ //
+ g_ui16NumStrings = ((uint16_t *)pvTable)[0];
+ g_ui16NumLanguages = ((uint16_t *)pvTable)[1];
+
+ //
+ // Save a pointer to the Language Identifier table.
+ //
+ g_pui16LanguageTable = (uint16_t *)pvTable + 2;
+
+ //
+ // Save a pointer to the String Index table.
+ //
+ g_pui32StringTable = (uint32_t *)(g_pui16LanguageTable +
+ g_ui16NumLanguages);
+
+ //
+ // Save a pointer to the String Data.
+ //
+ g_pui8StringData = (uint8_t *)(g_pui32StringTable +
+ (g_ui16NumStrings * g_ui16NumLanguages));
+}
+
+//*****************************************************************************
+//
+//! This function sets the current language for strings returned by the
+//! GrStringGet() function.
+//!
+//! \param ui16LangID is one of the language identifiers provided in the string
+//! table.
+//!
+//! This function is used to set the language identifier for the strings
+//! returned by the GrStringGet() function. The \e ui16LangID parameter should
+//! match one of the identifiers that was included in the string table. These
+//! are provided in a header file in the graphics library and must match the
+//! values that were passed through the sting compression utility.
+//!
+//! \return This function returns 0 if the language was not found and a
+//! non-zero value if the laguage was found.
+//
+//*****************************************************************************
+uint32_t
+GrStringLanguageSet(uint16_t ui16LangID)
+{
+ int32_t i32Lang;
+
+ //
+ // Search for the requested language.
+ //
+ for(i32Lang = 0; i32Lang < g_ui16NumLanguages; i32Lang++)
+ {
+ //
+ // Once found, break out and save the new language.
+ //
+ if(g_pui16LanguageTable[i32Lang] == ui16LangID)
+ {
+ break;
+ }
+ }
+
+ //
+ // Only accept the language if it was found, otherwise continue using
+ // previous language.
+ //
+ if(i32Lang != g_ui16NumLanguages)
+ {
+ g_ui16Language = i32Lang;
+ return(1);
+ }
+
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! This function returns a string from the current string table.
+//!
+//! \param i32Index is the index of the string to retrieve.
+//! \param pcData is the pointer to the buffer to store the string into.
+//! \param ui32Size is the size of the buffer provided by pcData.
+//!
+//! This function will return a string from the string table in the language
+//! set by the GrStringLanguageSet() function. The value passed in \e iIndex
+//! parameter is the string that is being requested and will be returned in
+//! the buffer provided in the \e pcData parameter. The amount of data
+//! returned will be limited by the ui32Size parameter.
+//!
+//! \return Returns the number of valid bytes returned in the \e pcData buffer.
+//
+//*****************************************************************************
+uint32_t
+GrStringGet(int32_t i32Index, char *pcData, uint32_t ui32Size)
+{
+ uint32_t ui32Len, ui32Offset, ui32SubCode[16];
+ int32_t i32Pos, i32Idx, i32Bit, i32Skip, i32Buf;
+ uint8_t *pui8BufferOut;
+ const uint8_t *pui8String;
+
+ ASSERT(i32Index < g_ui16NumStrings);
+ ASSERT(pcData != 0);
+
+ //
+ // Initialize the output buffer state.
+ //
+ i32Pos = 0;
+ pui8BufferOut = 0;
+
+ //
+ // if built up from another string, we need to process that
+ // this could nest multiple layers, so we follow in
+ //
+ ui32SubCode[i32Pos] = g_pui32StringTable[(g_ui16Language *
+ g_ui16NumStrings) + i32Index];
+
+ if(SC_GET_LEN(ui32SubCode[i32Pos]))
+ {
+ //
+ // recurse down
+ //
+ while(i32Pos < 16)
+ {
+ //
+ // Copy over the partial (if any) from a previous string.
+ //
+ i32Idx = SC_GET_INDEX(ui32SubCode[i32Pos++]);
+
+ ui32SubCode[i32Pos] = g_pui32StringTable[(g_ui16Language *
+ g_ui16NumStrings) +
+ i32Idx];
+
+ if(!SC_GET_LEN(ui32SubCode[i32Pos]))
+ {
+ //
+ // not linked, just string
+ //
+ break;
+ }
+ }
+ }
+
+ //
+ // Now work backwards out.
+ //
+ i32Idx = 0;
+
+ //
+ // Build up the string in pieces.
+ //
+ while(i32Pos >= 0)
+ {
+ //
+ // Get the offset in string table.
+ //
+ ui32Offset = SC_GET_OFF(ui32SubCode[i32Pos]);
+
+ if(ui32Offset == SC_IS_NULL)
+ {
+ //
+ // An empty string.
+ //
+ pcData[i32Idx] = 0;
+ }
+ else if(ui32Offset & SC_FLAG_COMPRESSED)
+ {
+ //
+ // This is a compressed string so initialize the pointer to the
+ // compressed data.
+ //
+ pui8String = g_pui8StringData + (ui32Offset & SC_OFFSET_M);
+
+ //
+ // Initialize the bit variables.
+ //
+ i32Bit = 0;
+ i32Skip = 0;
+
+ //
+ // Make a pointer to the current buffer out location.
+ //
+ pui8BufferOut = (uint8_t *)pcData + i32Idx;
+
+ //
+ // If the out buffer is beyond the maximum size then just break
+ // out and return what we have so far.
+ //
+ if((char *)pui8BufferOut > (pcData + ui32Size))
+ {
+ break;
+ }
+
+ //
+ // Now build up real string by decompressing bits.
+ //
+ if(!SC_GET_LEN(ui32SubCode[i32Pos]) &&
+ SC_GET_INDEX(ui32SubCode[i32Pos]))
+ {
+ i32Skip = SC_GET_INDEX(ui32SubCode[i32Pos]);
+
+ if(i32Pos)
+ {
+ ui32Len = SC_GET_LEN(ui32SubCode[i32Pos - 1]);
+ }
+ else
+ {
+ ui32Len = (i32Skip & 0x3f);
+ }
+
+ i32Skip >>= 6;
+ i32Idx += ui32Len;
+ ui32Len += i32Skip;
+ }
+ else if(i32Pos)
+ {
+ //
+ // Get the length of the partial string.
+ //
+ ui32Len = SC_GET_LEN(ui32SubCode[i32Pos - 1]) - i32Idx;
+ i32Idx += ui32Len;
+ }
+ else if(!SC_GET_LEN(ui32SubCode[0]) &&
+ SC_GET_INDEX(ui32SubCode[0]))
+ {
+ ui32Len = SC_GET_INDEX(ui32SubCode[0]);
+ i32Skip = ui32Len >> 6;
+ ui32Len = (ui32Len & 0x3f) + i32Skip;
+ }
+ else
+ {
+ //
+ // Arbitrary as null character ends the string.
+ //
+ ui32Len = 1024;
+ }
+
+ for(; ui32Len; ui32Len--)
+ {
+ //
+ // Packed 6 bits for each char
+ //
+ *pui8BufferOut = (*pui8String >> i32Bit) & 0x3f;
+
+ if(i32Bit >= 2)
+ {
+ *pui8BufferOut |= (*++pui8String << (8 - i32Bit)) & 0x3f;
+ }
+
+ i32Bit = (i32Bit + 6) & 0x7;
+
+ if(!*pui8BufferOut)
+ {
+ //
+ // end of string
+ //
+ break;
+ }
+
+ if(i32Skip)
+ {
+ i32Skip--;
+ continue;
+ }
+
+ //
+ // Put back removed bit
+ //
+ *pui8BufferOut |= 0x40;
+
+ //
+ // Now look for a few special chars we mapped up into other
+ // characters.
+ //
+ if(*pui8BufferOut == '`')
+ {
+ *pui8BufferOut = ' ';
+ }
+ else if(*pui8BufferOut == '~')
+ {
+ *pui8BufferOut = '-';
+ }
+ else if(*pui8BufferOut == 0x7f)
+ {
+ *pui8BufferOut = '.';
+ }
+ else if(*pui8BufferOut == '\\')
+ {
+ *pui8BufferOut = ':';
+ }
+
+ //
+ // Increment the pointer and break out if the pointer is now
+ // beyond the end of the buffer provided.
+ //
+ pui8BufferOut++;
+
+ if((char *)pui8BufferOut >= (pcData + ui32Size))
+ {
+ break;
+ }
+ }
+ }
+ else if(i32Pos)
+ {
+ //
+ // Part of another string
+ //
+ ui32Len = SC_GET_LEN(ui32SubCode[i32Pos - 1]) - i32Idx;
+
+ //
+ // Prevent this copy from going beyond the end of the buffer
+ // provided.
+ //
+ if((i32Idx + ui32Len) > ui32Size)
+ {
+ ui32Len = ui32Size - i32Idx;
+ }
+
+ //
+ // Copy this portion of the string to the output buffer.
+ //
+ for(i32Buf = 0; i32Buf < ui32Len; i32Buf++)
+ {
+ pcData[i32Idx + i32Buf] = g_pui8StringData[ui32Offset +
+ i32Buf];
+ }
+
+ i32Idx += ui32Len;
+ }
+ else if(SC_GET_INDEX(ui32SubCode[0]) && !SC_GET_LEN(ui32SubCode[0]))
+ {
+ //
+ // Copy this portion of the string to the output buffer.
+ //
+ for(i32Buf = 0; i32Buf < SC_GET_INDEX(ui32SubCode[0]); i32Buf++)
+ {
+ if((i32Idx + i32Buf) < ui32Size)
+ {
+ pcData[i32Idx + i32Buf] = g_pui8StringData[ui32Offset +
+ i32Buf];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ //
+ // Now copy the last piece of the string.
+ //
+ for(i32Buf = 0; i32Buf < (ui32Size - i32Idx); i32Buf++)
+ {
+ //
+ // Copy the string to the output buffer.
+ //
+ pcData[i32Idx + i32Buf] = g_pui8StringData[ui32Offset +
+ i32Buf];
+
+ //
+ // If a null is hit then terminate the copy.
+ //
+ if(pcData[i32Idx + i32Buf] == 0)
+ {
+ break;
+ }
+ }
+
+ //
+ // If we had not copied any characters before hitting this case,
+ // initialize the output pointer (this keeps the code at the end of
+ // the function that returns the length happy). This will be the
+ // case if we are using an uncompressed string table.
+ //
+ if(!pui8BufferOut)
+ {
+ pui8BufferOut = (uint8_t *)pcData + (i32Idx + i32Buf);
+ }
+ }
+ i32Pos--;
+ }
+
+ //
+ // Return the number of bytes copied into the output buffer.
+ //
+ if(pui8BufferOut)
+ {
+ ui32Len = ((uint32_t)pui8BufferOut - (uint32_t)pcData);
+
+ //
+ // Null terminate the string if there is room.
+ //
+ if(ui32Len < ui32Size)
+ {
+ pcData[ui32Len] = 0;
+ }
+ }
+ else
+ {
+ ui32Len = 0;
+ }
+
+ return(ui32Len);
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************