//***************************************************************************** // // image.c - Routines for drawing bitmap images. // // Copyright (c) 2008-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 buffer that holds the dictionary used by the Lempel-Ziv-Storer-Szymanski // compression algorithm. This is simply the last 32 bytes decoded from the // stream, and is initially filled with zeros. // //***************************************************************************** static uint8_t g_pui8Dictionary[32]; //***************************************************************************** // // Draws a run of pixels, dropping out any in a given transparent color. // Returns true if any pixels were drawn or false if none were drawn. // //***************************************************************************** static bool PixelTransparentDraw(const tContext *pContext, int32_t i32X, int32_t i32Y, int32_t i32X0, int32_t i32Count, int32_t i32BPP, const uint8_t *pui8Data, const uint8_t *pui8Palette, uint32_t ui32Transparent) { int32_t i32Start, i32Len, i32Index, i32StartX0, i32On, i32Off; int32_t i32NumBytes, i32Bit, i32Draw; uint32_t ui32Mask; uint8_t ui8Pixel; bool bSkip, bRet; // // Assume we drew no pixels until we determine otherwise. // bRet = false; // // What format are we dealing with? // switch(i32BPP & 0xFF) { // // Two color bitmap. // case 1: { // // How many bytes do we need to read to cover the line of data // we've been passed. // i32NumBytes = (i32Count + i32X0 + 7) / 8; // // Where must we end the line of pixels? // i32Len = i32Count; // // Set our mask to allow us to make either foreground or background // pixels transparent. // ui32Mask = ui32Transparent ? 0xFF : 0; // // Loop through the bytes in the pixel data. // i32Bit = i32X0; i32X0 = 0; for(i32Index = 0; i32Index < i32NumBytes; ) { // // Count the number of off pixels from this position in the // glyph image. // for(i32Off = 0; i32Index < i32NumBytes; ) { // // Get the number of zero pixels at this position. // i32Count = NumLeadingZeros(((pui8Data[i32Index] ^ ui32Mask) << (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; i32Index++; } 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; i32Index < i32NumBytes; ) { // // Get the number of one pixels at this location (by // inverting the data and counting the number of zeros). // i32Count = NumLeadingZeros(~(((pui8Data[i32Index] ^ ui32Mask) << (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; i32Index++; } else { // // Since the end of the byte was not reached, there // must be an off pixel. Therefore, stop counting on // pixels. // break; } } // // At this point, we have the next off and on run lengths // determined so draw the on run if it is non-zero length // and falls within the range we need to draw. // if(i32On && (i32Off < i32Len)) { i32Draw = ((i32Off + i32On) > i32Len) ? (i32X + i32Len) : (i32X + i32Off + i32On); DpyLineDrawH(pContext->psDisplay, i32X + i32Off, i32Draw - 1, i32Y, *(uint32_t *)(pui8Palette + (ui32Transparent ? 0 : 4))); // // Remember that we actually drew something. // bRet = true; } // // Move right past these two runs. // i32X += (i32Off + i32On); i32Len -= (i32Off + i32On); } } break; // // 4 bits per pixel (16 color) bitmap. // case 4: { // // Are we starting by drawing or skipping pixels? // ui8Pixel = (pui8Data[0] >> (i32X0 ? 0 : 4)) & 0x0F; bSkip = (ui8Pixel == (uint8_t)ui32Transparent) ? true : false; i32Start = 0; i32StartX0 = i32X0; i32Bit = i32X0; i32Len = bSkip ? 0 : 1; // // Scan all pixels in the line of data provided. // for(i32Index = 1; i32Index < i32Count; i32Index++) { // // Toggle the sub-byte pixel indicator; // i32X0 = 1 - i32X0; // // Read the next pixel. // ui8Pixel = (pui8Data[(i32Index + i32Bit) / 2] >> (i32X0 ? 0 : 4)) & 0x0F; // // Is this pixel a transparent one? // if(ui8Pixel != (uint8_t)ui32Transparent) { // // It's not transparent. Have we just ended a run of // transparent pixels? // if(bSkip) { // // We are currently skipping pixels so this starts a // new run. // i32Start = i32Index; i32StartX0 = i32X0; i32Len = 1; bSkip = false; } else { // // We were already in the middle of a run of non- // transparent pixels so increment the run length. // i32Len++; } } else { // // Pixel is transparent. Do we have a run to draw? // if(!bSkip) { // // Yes - draw what we have. // DpyPixelDrawMultiple(pContext->psDisplay, i32X + i32Start, i32Y, i32StartX0, i32Len, i32BPP, &pui8Data[(i32Start + i32Bit) / 2], pui8Palette); // // Reset for the transparent run. // i32Len = 0; bSkip = true; // // Remember that we actually drew something. // bRet = true; } } } // // If we drop out of the pixel loop with a run not drawn, draw it // here. // if(!bSkip && i32Len) { DpyPixelDrawMultiple(pContext->psDisplay, i32X + i32Start, i32Y, i32StartX0, i32Len, i32BPP, &pui8Data[(i32Start + i32Bit) / 2], pui8Palette); // // Remember that we actually drew something. // bRet = true; } } break; // // 8 bit per pixel (256 color) bitmap. // case 8: { // // Are we starting by drawing or skipping pixels? // bSkip = (pui8Data[0] == (uint8_t)ui32Transparent) ? true : false; i32Start = 0; i32Len = bSkip ? 0 : 1; // // Scan all pixels in the line of data provided. // for(i32Index = 1; i32Index < i32Count; i32Index++) { // // Is this pixel a transparent one? // if(pui8Data[i32Index] != (uint8_t)ui32Transparent) { // // It's not transparent. Have we just ended a run of // transparent pixels? // if(bSkip) { // // We are currently skipping pixels so this starts a // new run. // i32Start = i32Index; i32Len = 1; bSkip = false; } else { // // We were already in the middle of a run of non- // transparent pixels so increment the run length. // i32Len++; } } else { // // Pixel is transparent. Do we have a run to draw? // if(!bSkip) { // // Yes - draw what we have. // DpyPixelDrawMultiple(pContext->psDisplay, i32X + i32Start, i32Y, 0, i32Len, i32BPP, &pui8Data[i32Start], pui8Palette); // // Reset for the transparent run. // i32Len = 0; bSkip = true; // // Remember that we actually drew something. // bRet = true; } } } // // If we drop out of the pixel loop with a run not drawn, draw it // here. // if(!bSkip && i32Len) { DpyPixelDrawMultiple(pContext->psDisplay, i32X + i32Start, i32Y, i32X0, i32Len, i32BPP, &pui8Data[i32Start], pui8Palette); // // Remember that we actually drew something. // bRet = true; } } break; } // // Tell the caller whether or not we actually drew something. // return(bRet); } //***************************************************************************** // // Internal function implementing both normal and transparent image drawing. // //***************************************************************************** static void InternalImageDraw(const tContext *pContext, const uint8_t *pui8Image, int32_t i32X, int32_t i32Y, uint32_t ui32Transparent, bool bTransparent) { uint32_t ui32Byte, ui32Bits, ui32Match, ui32Size, ui32Idx, ui32Count; uint32_t ui32Num; int32_t i32BPP, i32Width, i32Height, i32X0, i32X1, i32X2, i32XMask; const uint8_t *pui8Palette; uint32_t pui32BWPalette[2]; int32_t i32Flag; // // Check the arguments. // ASSERT(pContext); ASSERT(pui8Image); // // Get the image format from the image data. // i32BPP = *pui8Image++; // // Get the image width from the image data. // i32Width = *(uint16_t *)pui8Image; pui8Image += 2; // // Get the image height from the image data. // i32Height = *(uint16_t *)pui8Image; pui8Image += 2; // // Return without doing anything if the entire image lies outside the // current clipping region. // if((i32X > pContext->sClipRegion.i16XMax) || ((i32X + i32Width - 1) < pContext->sClipRegion.i16XMin) || (i32Y > pContext->sClipRegion.i16YMax) || ((i32Y + i32Height - 1) < pContext->sClipRegion.i16YMin)) { return; } // // Set the flag indicating that we are drawing a new image. This will // be cleared after the first pixel run is drawn. // i32Flag = GRLIB_DRIVER_FLAG_NEW_IMAGE; // // Get the starting X offset within the image based on the current clipping // region. // if(i32X < pContext->sClipRegion.i16XMin) { i32X0 = pContext->sClipRegion.i16XMin - i32X; } else { i32X0 = 0; } // // Get the ending X offset within the image based on the current clipping // region. // if((i32X + i32Width - 1) > pContext->sClipRegion.i16XMax) { i32X2 = pContext->sClipRegion.i16XMax - i32X; } else { i32X2 = i32Width - 1; } // // Reduce the height of the image, if required, based on the current // clipping region. // if((i32Y + i32Height - 1) > pContext->sClipRegion.i16YMax) { i32Height = pContext->sClipRegion.i16YMax - i32Y + 1; } // // Determine the color palette for the image based on the image format. // if((i32BPP & 0x7f) == IMAGE_FMT_1BPP_UNCOMP) { // // Construct a local "black & white" palette based on the foreground // and background colors of the drawing context. // pui32BWPalette[0] = pContext->ui32Background; pui32BWPalette[1] = pContext->ui32Foreground; // // Set the palette pointer to the local "black & white" palette. // pui8Palette = (uint8_t *)pui32BWPalette; } else { // // For 4 and 8 BPP images, the palette is contained at the start of the // image data. // pui8Palette = pui8Image + 1; pui8Image += (pui8Image[0] * 3) + 4; } // // See if the image is compressed. // if(!(i32BPP & 0x80)) { // // The image is not compressed. See if the top portion of the image // lies above the clipping region. // if(i32Y < pContext->sClipRegion.i16YMin) { // // Determine the number of rows that lie above the clipping region. // i32X1 = pContext->sClipRegion.i16YMin - i32Y; // // Skip past the data for the rows that lie above the clipping // region. // pui8Image += (((i32Width * i32BPP) + 7) / 8) * i32X1; // // Decrement the image height by the number of skipped rows. // i32Height -= i32X1; // // Increment the starting Y coordinate by the number of skipped // rows. // i32Y += i32X1; } // // Determine the starting offset for the first source pixel within // the byte. // switch(i32BPP) { case 1: { i32XMask = i32X0 & 7; break; } case 4: { i32XMask = i32X0 & 1; break; } default: { i32XMask = 0; break; } } // // Loop while there are more rows to draw. // while(i32Height--) { // // Draw this row of image pixels. // if(bTransparent) { bool bRet; // // Draw a run of pixels dropping out any which are // transparent. // bRet = PixelTransparentDraw(pContext, i32X + i32X0, i32Y, i32XMask, i32X2 - i32X0 + 1, i32BPP | i32Flag, pui8Image + ((i32X0 * i32BPP) / 8), pui8Palette, ui32Transparent); // // Did we actually draw anything in this run? // if(bRet) { // // Yes. Clear the flag that tells the driver that this is // the first run of a new image. If we clear this when // nothing was drawn, the driver will not see the flag // and may not correctly rebuild its color lookup table. // i32Flag = 0; } } else { DpyPixelDrawMultiple(pContext->psDisplay, i32X + i32X0, i32Y, i32XMask, i32X2 - i32X0 + 1, i32BPP | i32Flag, pui8Image + ((i32X0 * i32BPP) / 8), pui8Palette); // // Clear the flag since we've drawn the first line now. // i32Flag = 0; } // // Skip past the data for this row. // pui8Image += ((i32Width * i32BPP) + 7) / 8; // // Increment the Y coordinate. // i32Y++; } } else { // // The image is compressed. Clear the compressed flag in the format // specifier so that the bits per pixel remains. // i32BPP &= 0x7f; // // Reset the dictionary used to uncompress the image. // for(ui32Bits = 0; ui32Bits < sizeof(g_pui8Dictionary); ui32Bits += 4) { *(uint32_t *)(g_pui8Dictionary + ui32Bits) = 0; } // // Determine the number of bytes of data to decompress. // ui32Count = (((i32Width * i32BPP) + 7) / 8) * i32Height; // // Initialize the pointer into the dictionary. // ui32Idx = 0; // // Start off with no encoding byte. // ui32Bits = 0; ui32Byte = 0; // // Start from the upper left corner of the image. // i32X1 = 0; // // Loop while there are more rows or more data in the image. // while(i32Height && ui32Count) { // // See if an encoding byte needs to be read. // if(ui32Bits == 0) { // // Read the encoding byte, which indicates if each of the // following eight bytes are encoded or literal. // ui32Byte = *pui8Image++; ui32Bits = 8; } // // See if the next byte is encoded or literal. // if(ui32Byte & (1 << (ui32Bits - 1))) { // // This byte is encoded, so extract the location and size of // the encoded data within the dictionary. // ui32Match = *pui8Image >> 3; ui32Size = (*pui8Image++ & 7) + 2; // // Decrement the count of bytes to decode by the number of // copied bytes. // ui32Count -= ui32Size; } else { // // This byte is a literal, so copy it into the dictionary. // g_pui8Dictionary[ui32Idx++] = *pui8Image++; // // Decrement the count of bytes to decode. // ui32Count--; // // Clear any previous encoded data information. // ui32Match = 0; ui32Size = 0; } // // Loop while there are bytes to copy for the encoded data, or // once for literal data. // while(ui32Size || !(ui32Byte & (1 << (ui32Bits - 1)))) { // // Set the encoded data bit for this data so that this loop // will only be executed once for literal data. // ui32Byte |= 1 << (ui32Bits - 1); // // Loop while there is more encoded data to copy and there is // additional space in the dictionary (before the buffer // wraps). // while(ui32Size && (ui32Idx != sizeof(g_pui8Dictionary))) { // // Copy this byte. // g_pui8Dictionary[ui32Idx] = g_pui8Dictionary[(ui32Idx + ui32Match) % sizeof(g_pui8Dictionary)]; // // Increment the dictionary pointer. // ui32Idx++; // // Decrement the encoded data size. // ui32Size--; } // // See if the dictionary pointer is about to wrap, or if there // is no more data to decompress. // if((ui32Idx == sizeof(g_pui8Dictionary)) || !ui32Count) { // // Loop through the data in the dictionary buffer. // for(ui32Idx = 0; (ui32Idx < sizeof(g_pui8Dictionary)) && i32Height; ) { // // Compute the number of pixels that remain in the // dictionary buffer. // ui32Num = ((sizeof(g_pui8Dictionary) - ui32Idx) * 8) / i32BPP; // // See if any of the pixels in the dictionary buffer // are within the clipping region. // if((i32Y >= pContext->sClipRegion.i16YMin) && ((i32X1 + ui32Num) >= i32X0) && (i32X1 <= i32X2)) { // // Skip some pixels at the start of the scan line // if required to stay within the clipping region. // if(i32X1 < i32X0) { ui32Idx += ((i32X0 - i32X1) * i32BPP) / 8; i32X1 = i32X0; } // // Shorten the scan line if required to stay within // the clipping region. // if(ui32Num > (i32X2 - i32X1 + 1)) { ui32Num = i32X2 - i32X1 + 1; } // // Determine the starting offset for the first // source pixel within the byte. // switch(i32BPP) { case 1: { i32XMask = i32X1 & 7; break; } case 4: { i32XMask = i32X1 & 1; break; } default: { i32XMask = 0; break; } } // // Draw this row of image pixels. // if(bTransparent) { bool bRet; bRet = PixelTransparentDraw(pContext, i32X + i32X1, i32Y, i32XMask, ui32Num, i32BPP | i32Flag, g_pui8Dictionary + ui32Idx, pui8Palette, ui32Transparent); // // Clear the flag only if we actually drew // something. // if(bRet) { // // We drew something so that NEW_IMAGE // flag is no longer needed. // i32Flag = 0; } } else { DpyPixelDrawMultiple(pContext->psDisplay, i32X + i32X1, i32Y, i32XMask, ui32Num, i32BPP | i32Flag, g_pui8Dictionary + ui32Idx, pui8Palette); // // We've drawn the first line so clear the flag. // i32Flag = 0; } } // // Move the X coordinate back to the start of the first // data byte in this portion of the dictionary buffer. // i32X1 = ((i32X1 * i32BPP) & ~7) / i32BPP; // // See if the remainder of this scan line resides // within the dictionary buffer. // if(((((i32Width - i32X1) * i32BPP) + 7) / 8) > (sizeof(g_pui8Dictionary) - ui32Idx)) { // // There is more to this scan line than is in the // dictionary buffer at this point, so move the // X coordinate by by the number of pixels in the // dictionary buffer. // i32X1 += (((sizeof(g_pui8Dictionary) - ui32Idx) * 8) / i32BPP); // // The entire dictionary buffer has been scanned. // ui32Idx = sizeof(g_pui8Dictionary); } else { // // The remainder of this scan line resides in the // dictionary buffer, so skip past it. // ui32Idx += (((i32Width - i32X1) * i32BPP) + 7) / 8; // // Move to the start of the next scan line. // i32X1 = 0; i32Y++; // // There is one less scan line to process. // i32Height--; } } // // Start over from the beginning of the dictionary buffer. // ui32Idx = 0; } } // // Advance to the next bit in the encoding byte. // ui32Bits--; } } } //***************************************************************************** // //! Draws a bitmap image, dropping out a single transparent color. //! //! \param pContext is a pointer to the drawing context to use. //! \param pui8Image is a pointer to the image to draw. //! \param i32X is the X coordinate of the upper left corner of the image. //! \param i32Y is the Y coordinate of the upper left corner of the image. //! \param ui32Transparent is the image color which is to be considered //! transparent. //! //! This function draws a bitmap image but, unlike GrImageDraw, will drop out //! any pixel of a particular color allowing the previous background to ``shine //! through''. The image may be 1 bit per pixel (using the foreground and //! background color from the drawing context), 4 bits per pixel (using a //! palette supplied in the image data), or 8 bits per pixel (using a palette //! supplied in the image data). It can be uncompressed data, or it can be //! compressed using the Lempel-Ziv-Storer-Szymanski algorithm (as published in //! the Journal of the ACM, 29(4):928-951, October 1982). For 4bpp and 8bpp //! images, the \b ui32Transparent parameter contains the palette index of the //! colour which is to be considered transparent. For 1bpp images, the //! \b ui32Transparent parameter should be set to 0 to draw only foreground //! pixels or 1 to draw only background pixels. //! //! \return None. // //***************************************************************************** void GrTransparentImageDraw(const tContext *pContext, const uint8_t *pui8Image, int32_t i32X, int32_t i32Y, uint32_t ui32Transparent) { InternalImageDraw(pContext, pui8Image, i32X, i32Y, ui32Transparent, true); } //***************************************************************************** // //! Draws a bitmap image. //! //! \param pContext is a pointer to the drawing context to use. //! \param pui8Image is a pointer to the image to draw. //! \param i32X is the X coordinate of the upper left corner of the image. //! \param i32Y is the Y coordinate of the upper left corner of the image. //! //! This function draws a bitmap image. The image may be 1 bit per pixel //! (using the foreground and background color from the drawing context), 4 //! bits per pixel (using a palette supplied in the image data), or 8 bits per //! pixel (using a palette supplied in the image data). It can be uncompressed //! data, or it can be compressed using the Lempel-Ziv-Storer-Szymanski //! algorithm (as published in the Journal of the ACM, 29(4):928-951, October //! 1982). //! //! \return None. // //***************************************************************************** void GrImageDraw(const tContext *pContext, const uint8_t *pui8Image, int32_t i32X, int32_t i32Y) { InternalImageDraw(pContext, pui8Image, i32X, i32Y, 0, false); } //***************************************************************************** // // Close the Doxygen group. //! @} // //*****************************************************************************