/* --COPYRIGHT--,BSD
 * Copyright (c) 2013, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/

#include "grlib.h"

//*****************************************************************************
//
//! \addtogroup image_api
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
// The buffer that holds the converted palette of the image to be drawn. This
// buffer contains the actual data to be written to the LCD after translation.
//
//*****************************************************************************
static unsigned int g_pulConvertedPalette[256];


//*****************************************************************************
//
//! Converts the bitmap image palette.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pBitmap is a pointer to the image.
//!
//! This function converts the palette of a bitmap image. The image palette is in
//! 24 bit RGB form, and this function converts that to a format to be sent to the
//! LCD using DpyColorTranslate function. The converted palette is contained in a
//! global buffer while the original image palette remains unchanged.
//!
//! \return is the address of the global buffer containing the converted palette.
//
//*****************************************************************************
unsigned int * GrPaletteConversion(const tContext *pContext, const tImage *  pBitmap)
{  
  if (pBitmap->NumColors > 256) 
  {
    return 0;
  }
  
  int i;
  int NumColors = pBitmap->NumColors;
  const unsigned long * pulPalette = &pBitmap->pPalette[0];
  for (i = 0; i < NumColors; i++) 
  {
    g_pulConvertedPalette[i] = DpyColorTranslate(pContext->pDisplay, *(pulPalette + i));
  }
  return &g_pulConvertedPalette[0];
}

//*****************************************************************************
//
//! Draws a bitmap image.
//!
//! \param pContext is a pointer to the drawing context to use.
//! \param pucImage is a pointer to the image to draw.
//! \param lX is the X coordinate of the upper left corner of the image.
//! \param lY 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, 4
//! bits per pixel or 8 bits per pixel (using a palette supplied in the image 
//! data).  It can be uncompressed data, or it can be compressed using 
//! several different compression types. Compression options are 4-bit run
//! length encoding, 8-bit run length encoding, and a custom run length encoding
//! variation written for complex 8-bit per pixel images.
//!
//! \return None.
//
//*****************************************************************************
void
GrImageDraw(const tContext *pContext, const tImage *pBitmap, int lX,
            int lY)
{
    int lBPP, lWidth, lHeight, lX0, lX1, lX2;
    const unsigned int *pucPalette;
    const unsigned char * pucImage;

    //
    // Check the arguments.
    //
    assert(pContext);
    assert(pBitmap);

    //
    // Get the image format from the image data.
    //
    lBPP = pBitmap->BPP;

    //
    // Get the image width from the image data.
    //
    lWidth = pBitmap->XSize;


    //
    // Get the image height from the image data.
    //
    lHeight = pBitmap->YSize;

    //
    // Return without doing anything if the entire image lies outside the
    // current clipping region.
    //
    if((lX > pContext->sClipRegion.sXMax) ||
       ((lX + lWidth - 1) < pContext->sClipRegion.sXMin) ||
       (lY > pContext->sClipRegion.sYMax) ||
       ((lY + lHeight - 1) < pContext->sClipRegion.sYMin))
    {
        return;
    }

    //
    // Get the starting X offset within the image based on the current clipping
    // region.
    //
    if(lX < pContext->sClipRegion.sXMin)
    {
        lX0 = pContext->sClipRegion.sXMin - lX;
    }
    else
    {
        lX0 = 0;
    }

    //
    // Get the ending X offset within the image based on the current clipping
    // region.
    //
    if((lX + lWidth - 1) > pContext->sClipRegion.sXMax)
    {
        lX2 = pContext->sClipRegion.sXMax - lX;
    }
    else
    {
        lX2 = lWidth - 1;
    }

    //
    // Reduce the height of the image, if required, based on the current
    // clipping region.
    //
    if((lY + lHeight - 1) > pContext->sClipRegion.sYMax)
    {
        lHeight = pContext->sClipRegion.sYMax - lY + 1;
    }

    //
    // The image palette is in 24 bit R-G-B format. The palette needs
    // to be translated into the color format accepted by the LCD 
    // and stored in g_pulConvertedPalette[] buffer
    //
    pucPalette = GrPaletteConversion(pContext, pBitmap); 
    pucImage = pBitmap->pPixel;
    
    //
    // Check if the image is not compressed.
    //
    if(!(lBPP & 0xF0))
    {
        //
        // The image is not compressed.  See if the top portion of the image
        // lies above the clipping region.
        //
        if(lY < pContext->sClipRegion.sYMin)
        {
            //
            // Determine the number of rows that lie above the clipping region.
            //
            lX1 = pContext->sClipRegion.sYMin - lY;

            //
            // Skip past the data for the rows that lie above the clipping
            // region.
            //
            pucImage += (((lWidth * lBPP) + 7) / 8) * lX1;

            //
            // Decrement the image height by the number of skipped rows.
            //
            lHeight -= lX1;

            //
            // Increment the starting Y coordinate by the number of skipped
            // rows.
            //
            lY += lX1;
        }
        
        while(lHeight--)
        {
          //
          // Draw this row of image pixels.
          //
          DpyPixelDrawMultiple(pContext->pDisplay, lX + lX0, lY, lX0 & 7,
                               lX2 - lX0 + 1, lBPP,
                               pucImage + ((lX0 * lBPP) / 8), pucPalette);
          
          //
          // Skip past the data for this row.
          //
          pucImage += ((lWidth * lBPP) + 7) / 8;
          
          //
          // Increment the Y coordinate.
          //
          lY++;
        }
    }
    else
        {
           //
           // The image is compressed with RLE4 or RLE8 Algorithm
           //

          const unsigned char *pucData = pucImage;
          unsigned char ucRunLength, ucTempRun;
          unsigned char ucNoOverrun = 1;
          unsigned int uiColor;
          unsigned int uiLineCnt = 0;
          unsigned int lXS = lX;

           lBPP &= 0x0F;

           while(lHeight--)
           {
             //
             // Draw this row of image pixels.
             //
             while(uiLineCnt < lWidth)
             {
               if(ucNoOverrun)      // Read next byte
               {
                 if(lBPP == 8)      // RLE 8 bit encoding
                 {
                   // Read Run Length
                   ucRunLength = *pucData++;
                   // Read Color Pointer
                   uiColor = *pucData++;
                 }
                 else     // lBPP = 4; RLE 4 bit encoding
                 {

                   // Read Run Length
                   ucRunLength = (*pucData) >> 4;
                   // Read Color Pointer
                   uiColor = (*pucData++) & 0x0F;
                 }
                 uiColor = (*(unsigned int *)(pucPalette + uiColor));

               }
               else
               {
                 //Last line run overran the edge
                 //Skip color and run length update and clear overrun flag
                 ucNoOverrun = 1;
                 ucRunLength = ucTempRun;
               }

               //After original value, but it still needs to be written once
               if((ucRunLength + uiLineCnt) >= lWidth)
               {
                 //Run length runs past current line

                 //Set left over run length for next line to ucTempRun
                 ucTempRun = ucRunLength - (lWidth - uiLineCnt);
                 //Set ucRunLength to finish this line
                 ucRunLength = (lWidth - uiLineCnt);
                 //Set line overrun flag
                 ucNoOverrun = 0;
               }

               //Draw single pixel
               DpyPixelDraw(pContext->pDisplay, lX, lY, uiColor);
			   lX++;

               uiLineCnt++;

               while(ucRunLength--)
               {
                 //Draw run of pixels
            	DpyPixelDraw(pContext->pDisplay, lX, lY, uiColor);

				lX++;

				uiLineCnt++;
               }
             }
             //End of line reached
             uiLineCnt = 0;   //Reset Pixel Count
             lY++;            //Increment Y value
             lX = lXS;
           }
       }
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
