From 788db64b8642bf31de6930d18a62177c64163ee0 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 13 Mar 2015 12:24:02 +0200 Subject: Add grlib, fixes #5 --- grlib/string.c | 2853 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2853 insertions(+) create mode 100644 grlib/string.c (limited to 'grlib/string.c') 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 +#include +#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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1