/*********************************************************************
*                SEGGER Microcontroller GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
**********************************************************************
*                                                                    *
*        (c) 1996 - 2016  SEGGER Microcontroller GmbH & Co. KG       *
*                                                                    *
*        Internet: www.segger.com    Support:  support@segger.com    *
*                                                                    *
**********************************************************************

** emWin V5.38 - Graphical user interface for embedded applications **
All  Intellectual Property rights  in the Software belongs to  SEGGER.
emWin is protected by  international copyright laws.  Knowledge of the
source code may not be used to write a similar product.  This file may
only be used in accordance with the following terms:

The software has been licensed to  NXP Semiconductors USA, Inc.  whose
registered  office  is  situated  at 411 E. Plumeria Drive, San  Jose,
CA 95134, USA  solely for  the  purposes  of  creating  libraries  for
NXPs M0, M3/M4 and  ARM7/9 processor-based  devices,  sublicensed  and
distributed under the terms and conditions of the NXP End User License
Agreement.
Full source code is available at: www.segger.com

We appreciate your understanding and fairness.
----------------------------------------------------------------------
Licensing information

Licensor:                 SEGGER Microcontroller Systems LLC
Licensed to:              NXP Semiconductors, 1109 McKay Dr, M/S 76, San Jose, CA 95131, USA
Licensed SEGGER software: emWin
License number:           GUI-00186
License model:            emWin License Agreement, dated August 20th 2011
Licensed product:         -
Licensed platform:        NXP's ARM 7/9, Cortex-M0,M3,M4
Licensed number of seats: -
----------------------------------------------------------------------
File        : GUI_SetOrientationC0.c
Purpose     : Runtime display orientation without cache
---------------------------END-OF-HEADER------------------------------
*/

#include "GUI_SetOrientation.h"

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
#define XY2PTR(x, y) (PIXEL *)(pContext->pData + y * pContext->BytesPerLine + x * pContext->pDrawingAPI->BytesPerPixel)

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/
/*********************************************************************
*
*       Static code
*
**********************************************************************
*/
/*********************************************************************
*
*       _Sort
*
* Purpose:
*   Sorts the values pointed by the given pointers. Please note that
*   the same static function is also in GUI_SetOrientationC0.h
*   to enable better compiler optimization.
*/
static void _Sort(int * p0, int * p1) {
  int temp;

  if (*p0 > *p1) {
    temp = *p0;
    *p0  = *p1;
    *p1  = temp;
  }
}

/*********************************************************************
*
*       Static code: Bitmap drawing routines
*
**********************************************************************
*/
/*********************************************************************
*
*       Draw Bitmap 1 BPP
*/
static void _DrawBitLine1BPP(GUI_DEVICE * pDevice, unsigned x, unsigned y, U8 const * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) {
  LCD_PIXELINDEX IndexMask, Index0, Index1, Pixel;
  LCD_PIXELINDEX (* pfGetPixelIndex)(GUI_DEVICE *, int, int);
  PIXEL * pData;
  int x_phys, y_phys;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  Index0 = *(pTrans + 0);
  Index1 = *(pTrans + 1);
  x += Diff;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  switch (GUI_pContext->DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
  case 0:
    do {
      *pData = (PIXEL)((*p & (0x80 >> Diff)) ? Index1 : Index0);
      pData += pContext->PixelOffset;
      if (++Diff == 8) {
        Diff = 0;
        p++;
      }
    } while (--xsize);
    break;
  case LCD_DRAWMODE_TRANS:
    do {
      if (*p & (0x80 >> Diff)) {
        *pData = (PIXEL)Index1;
      }
      pData += pContext->PixelOffset;
      if (++Diff == 8) {
        Diff = 0;
        p++;
      }
    } while (--xsize);
    break;
  case LCD_DRAWMODE_XOR | LCD_DRAWMODE_TRANS:
  case LCD_DRAWMODE_XOR:
    pfGetPixelIndex = pDevice->pDeviceAPI->pfGetPixelIndex;
    IndexMask = pDevice->pColorConvAPI->pfGetIndexMask();
    do {
      if (*p & (0x80 >> Diff)) {
        Pixel = pfGetPixelIndex(pDevice, x, y);
        Pixel ^= IndexMask;
        *pData = (PIXEL)Pixel;
      }
      pData += pContext->PixelOffset;
      x++;
      if (++Diff == 8) {
        Diff = 0;
        p++;
      }
    } while (--xsize);
    break;
  }
}

/*********************************************************************
*
*       Draw Bitmap 2 BPP
*/
static void _DrawBitLine2BPP(GUI_DEVICE * pDevice, int x, int y, U8 const * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) {
  LCD_PIXELINDEX Pixels, PixelIndex;
  int CurrentPixel, Shift, Index;
  PIXEL * pData;
  int x_phys, y_phys;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  Pixels = *p;
  CurrentPixel = Diff;
  x += Diff;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  switch (GUI_pContext->DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
  case 0:
    if (pTrans) {
      do {
        Shift = (3 - CurrentPixel) << 1;
        Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
        PixelIndex = *(pTrans + Index);
        *pData = (PIXEL)PixelIndex;
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 4) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    } else {
      do {
        Shift = (3 - CurrentPixel) << 1;
        Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
        *pData = (PIXEL)Index;
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 4) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    }
    break;
  case LCD_DRAWMODE_TRANS:
    if (pTrans) {
      do {
        Shift = (3 - CurrentPixel) << 1;
        Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
        if (Index) {
          PixelIndex = *(pTrans + Index);
          *pData = (PIXEL)PixelIndex;
        }
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 4) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    } else {
      do {
        Shift = (3 - CurrentPixel) << 1;
        Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
        if (Index) {
          *pData = (PIXEL)Index;
        }
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 4) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    }
    break;
  }
}

/*********************************************************************
*
*       Draw Bitmap 4 BPP
*/
static void _DrawBitLine4BPP(GUI_DEVICE * pDevice, int x, int y, U8 const * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) {
  LCD_PIXELINDEX Pixels, PixelIndex;
  int CurrentPixel, Shift, Index;
  PIXEL * pData;
  int x_phys, y_phys;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  Pixels = *p;
  CurrentPixel = Diff;
  x += Diff;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  switch (GUI_pContext->DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
  case 0:
    if (pTrans) {
      do {
        Shift = (1 - CurrentPixel) << 2;
        Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
        PixelIndex = *(pTrans + Index);
        *pData = (PIXEL)PixelIndex;
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 2) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    } else {
      do {
        Shift = (1 - CurrentPixel) << 2;
        Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
        *pData = (PIXEL)Index;
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 2) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    }
    break;
  case LCD_DRAWMODE_TRANS:
    if (pTrans) {
      do {
        Shift = (1 - CurrentPixel) << 2;
        Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
        if (Index) {
          PixelIndex = *(pTrans + Index);
          *pData = (PIXEL)PixelIndex;
        }
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 2) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    } else {
      do {
        Shift = (1 - CurrentPixel) << 2;
        Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
        if (Index) {
          *pData = (PIXEL)Index;
        }
        pData += pContext->PixelOffset;
        if (++CurrentPixel == 2) {
          CurrentPixel = 0;
          Pixels = *(++p);
        }
      } while (--xsize);
    }
    break;
  }
}

/*********************************************************************
*
*       Draw Bitmap 8 BPP
*/
static void _DrawBitLine8BPP(GUI_DEVICE * pDevice, int x, int y, U8 const * p, int xsize, const LCD_PIXELINDEX * pTrans) {
  LCD_PIXELINDEX Pixel;
  PIXEL * pData;
  int x_phys, y_phys;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  switch (GUI_pContext->DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
  case 0:
    if (pTrans) {
      do {
        Pixel = *p++;
        *pData = (PIXEL)*(pTrans + Pixel);
        pData += pContext->PixelOffset;
      } while (--xsize);
    } else {
      do {
        *pData = (PIXEL)*p++;
        pData += pContext->PixelOffset;
      } while (--xsize);
    }
    break;
  case LCD_DRAWMODE_TRANS:
    if (pTrans) {
      do {
        Pixel = *p++;
        if (Pixel) {
          *pData = (PIXEL)*(pTrans + Pixel);
        }
        pData += pContext->PixelOffset;
      } while (--xsize);
    } else {
      do {
        Pixel = *p++;
        if (Pixel) {
          *pData = (PIXEL)Pixel;
        }
        pData += pContext->PixelOffset;
      } while (--xsize);
    }
    break;
  }
}

/*********************************************************************
*
*       Draw Bitmap 16 BPP
*/
static void _DrawBitLine16BPP(GUI_DEVICE * pDevice, int x, int y, U16 const * p, int xsize) {
  PIXEL * pData;
  int x_phys, y_phys, PixelOffset;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  PixelOffset = pContext->PixelOffset;
  do {
    *pData = (PIXEL)*p++;
    pData += PixelOffset;
  } while (--xsize);
}

/*********************************************************************
*
*       Draw Bitmap 32 BPP
*/
static void _DrawBitLine32BPP(GUI_DEVICE * pDevice, int x, int y, U32 const * p, int xsize) {
  PIXEL * pData;
  int x_phys, y_phys, PixelOffset;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  PixelOffset = pContext->PixelOffset;
  do {
    *pData = (PIXEL)*p++;
    pData += PixelOffset;
  } while (--xsize);
}

/*********************************************************************
*
*       Static code: API functions for drawing operations, no cache
*
**********************************************************************
*/
/*********************************************************************
*
*       _DrawBitmap_CX
*/
static void _DrawBitmap_CX(GUI_DEVICE * pDevice, int x0, int y0,
                       int xSize, int ySize,
                       int BitsPerPixel,
                       int BytesPerLine,
                       const U8 * pData, int Diff,
                       const LCD_PIXELINDEX * pTrans) {
  int x0_phys, y0_phys, x1_phys, y1_phys;
  int i;
  PIXEL * pDataBM;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  switch (BitsPerPixel) {
  case 1:
    for (i = 0; i < ySize; i++) {
      _DrawBitLine1BPP(pDevice, x0, i + y0, pData, Diff, xSize, pTrans);
      pData += BytesPerLine;
    }
    break;
  case 2:
    for (i = 0; i < ySize; i++) {
      _DrawBitLine2BPP(pDevice, x0, i + y0, pData, Diff, xSize, pTrans);
      pData += BytesPerLine;
    }
    break;
  case 4:
    for (i = 0; i < ySize; i++) {
      _DrawBitLine4BPP(pDevice, x0, i + y0, pData, Diff, xSize, pTrans);
      pData += BytesPerLine;
    }
    break;
  case 8:
    for (i = 0; i < ySize; i++) {
      _DrawBitLine8BPP(pDevice, x0, i + y0, pData, xSize, pTrans);
      pData += BytesPerLine;
    }
    break;
  case 16:
    for (i = 0; i < ySize; i++) {
      _DrawBitLine16BPP(pDevice, x0, i + y0, (U16 *)pData, xSize);
      pData += BytesPerLine;
    }
    break;
  case 32:
    for (i = 0; i < ySize; i++) {
      _DrawBitLine32BPP(pDevice, x0, i + y0, (U32 *)pData, xSize);
      pData += BytesPerLine;
    }
    break;
  }

  pContext->pfLog2Phys(pContext, x0 + Diff,             y0,             &x0_phys, &y0_phys);
  pContext->pfLog2Phys(pContext, x0 + Diff + xSize - 1, y0 + ySize - 1, &x1_phys, &y1_phys);
  _Sort(&x0_phys, &x1_phys);
  _Sort(&y0_phys, &y1_phys);
  pDataBM = XY2PTR(x0_phys, y0_phys);
  pDevice = pDevice->pNext;
  pDevice->pDeviceAPI->pfDrawBitmap(pDevice,
                                    x0_phys, y0_phys,
                                    x1_phys - x0_phys + 1,
                                    y1_phys - y0_phys + 1,
                                    pContext->pDrawingAPI->BytesPerPixel << 3,
                                    pContext->pDrawingAPI->BytesPerPixel * pContext->vxSize,
                                    (U8 *)pDataBM, 0, NULL);
}

/*********************************************************************
*
*       _GetPixelIndex_CX
*/
static LCD_PIXELINDEX _GetPixelIndex_CX(GUI_DEVICE * pDevice, int x, int y) {
  PIXEL * pData;
  PIXEL Pixel;
  int x_phys, y_phys;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  Pixel = *pData;
  return Pixel;
}

/*********************************************************************
*
*       _SetPixelIndex_CX
*/
static void _SetPixelIndex_CX(GUI_DEVICE * pDevice, int x, int y, LCD_PIXELINDEX PixelIndex) {
  PIXEL * pData;
  int x_phys, y_phys;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  pContext->pfLog2Phys(pContext, x, y, &x_phys, &y_phys);
  pData = XY2PTR(x_phys, y_phys);
  *pData = (PIXEL)PixelIndex;
  pDevice = pDevice->pNext;
  pDevice->pDeviceAPI->pfSetPixelIndex(pDevice, x_phys, y_phys, PixelIndex);
}

/*********************************************************************
*
*       _XorPixel_CX
*/
static void _XorPixel_CX(GUI_DEVICE * pDevice, int x, int y) {
  PIXEL Pixel, IndexMask;

  IndexMask = (PIXEL)pDevice->pColorConvAPI->pfGetIndexMask();
  Pixel = (PIXEL)pDevice->pDeviceAPI->pfGetPixelIndex(pDevice, x, y);
  Pixel ^= IndexMask;
  pDevice->pDeviceAPI->pfSetPixelIndex(pDevice, x, y, Pixel);
}

/*********************************************************************
*
*       _DrawHLine_CX
*/
static void _DrawHLine_CX(GUI_DEVICE * pDevice, int x0, int y, int x1) {
  pDevice->pDeviceAPI->pfFillRect(pDevice, x0, y, x1, y);
}

/*********************************************************************
*
*       _DrawVLine_CX
*/
static void _DrawVLine_CX(GUI_DEVICE * pDevice, int x, int y0, int y1) {
  pDevice->pDeviceAPI->pfFillRect(pDevice, x, y0, x, y1);
}

/*********************************************************************
*
*       _FillRect_CX
*/
static void _FillRect_CX(GUI_DEVICE * pDevice, int x0, int y0, int x1, int y1) {
  PIXEL * pData;
  PIXEL * pLine;
  PIXEL * pPixel;
  PIXEL Pixel, IndexMask;
  int x0_phys, y0_phys, x1_phys, y1_phys;
  int NumPixels, NumLines;
  DRIVER_CONTEXT * pContext;

  pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
  pContext->pfLog2Phys(pContext, x0, y0, &x0_phys, &y0_phys);
  pContext->pfLog2Phys(pContext, x1, y1, &x1_phys, &y1_phys);
  _Sort(&x0_phys, &x1_phys);
  _Sort(&y0_phys, &y1_phys);
  pData = pLine = XY2PTR(x0_phys, y0_phys);
  NumLines = y1_phys - y0_phys + 1;
  if (GUI_pContext->DrawMode & LCD_DRAWMODE_XOR) {
    IndexMask = (PIXEL)pDevice->pColorConvAPI->pfGetIndexMask();
    do {
      pPixel    = pLine;
      NumPixels = x1_phys - x0_phys + 1;
      do {
        *pPixel++ ^= IndexMask;
      } while (--NumPixels);
      pLine += pContext->vxSize;
    } while (--NumLines);
  } else {
    Pixel = (PIXEL)LCD__GetColorIndex();
    if (sizeof(Pixel) == 1) {
      NumPixels = x1_phys - x0_phys + 1;
      do {
        GUI_MEMSET((U8 *)pLine, (U8)Pixel, NumPixels);
        pLine += pContext->vxSize;
      } while (--NumLines);
    } else {
      do {
        pPixel    = pLine;
        NumPixels = x1_phys - x0_phys + 1;
        do {
          *pPixel++ = Pixel;
        } while (--NumPixels);
        pLine += pContext->vxSize;
      } while (--NumLines);
    }
  }
  pDevice = pDevice->pNext;
  pDevice->pDeviceAPI->pfDrawBitmap(pDevice,
                                    x0_phys, y0_phys,
                                    x1_phys - x0_phys + 1,
                                    y1_phys - y0_phys + 1,
                                    pContext->pDrawingAPI->BytesPerPixel << 3,
                                    pContext->pDrawingAPI->BytesPerPixel * pContext->vxSize,
                                    (U8 *)pData, 0, NULL);
}

/*********************************************************************
*
*       Static data: Drawing API(s)
*
**********************************************************************
*/
/*********************************************************************
*
*       GUI_OrientationAPI_CX
*/
const GUI_ORIENTATION_API API_NAME = {
  _DrawBitmap_CX,
  _DrawHLine_CX,
  _DrawVLine_CX,
  _FillRect_CX,
  _GetPixelIndex_CX,
  _SetPixelIndex_CX,
  _XorPixel_CX,
  BYTES_PER_PIXEL
};

/*************************** End of file ****************************/
