
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2018 Matthias Koefferlein

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#include "layRedrawThreadCanvas.h"
#include "layCanvasPlane.h"
#include "layBitmapsToImage.h"
#include "layDrawing.h"
#include "layBitmap.h"

#include <QImage>

namespace lay
{

// ------------------------------------------------------------------------

BitmapCanvasData::BitmapCanvasData ()
  : m_width (0), m_height (0)
{
  //  .. nothing yet ..
}

BitmapCanvasData::~BitmapCanvasData ()
{
  clear_planes ();
}

BitmapCanvasData::BitmapCanvasData (const std::vector <lay::Bitmap *> &plane_buffers, const std::vector <std::vector <lay::Bitmap *> > &drawing_plane_buffers, unsigned int width, unsigned int height)
  : m_width (0), m_height (0)
{
  assign (mp_plane_buffers, plane_buffers);
  assign (mp_drawing_plane_buffers, drawing_plane_buffers);
  m_width = width;
  m_height = height;
}

BitmapCanvasData &BitmapCanvasData::operator= (const BitmapCanvasData &data)
{
  if (this != &data) {
    assign (mp_plane_buffers, data.mp_plane_buffers);
    assign (mp_drawing_plane_buffers, data.mp_drawing_plane_buffers);
    m_width = data.m_width;
    m_height = data.m_height;
  }
  return *this;
}

void BitmapCanvasData::fetch (std::vector <lay::Bitmap *> &plane_buffers, std::vector <std::vector <lay::Bitmap *> > &drawing_plane_buffers, unsigned int &width, unsigned int &height) const
{
  assign (plane_buffers, mp_plane_buffers);
  assign (drawing_plane_buffers, mp_drawing_plane_buffers);
  width = m_width;
  height = m_height;
}

bool BitmapCanvasData::can_fetch (const std::vector <lay::Bitmap *> &plane_buffers, const std::vector <std::vector <lay::Bitmap *> > &drawing_plane_buffers, unsigned int width, unsigned int height) const
{
  if (m_width != width || m_height != height) {
    return false;
  }
  if (plane_buffers.size () != mp_plane_buffers.size ()) {
    return false;
  }
  if (drawing_plane_buffers.size () != mp_drawing_plane_buffers.size ()) {
    return false;
  }
  for (size_t i = 0; i < drawing_plane_buffers.size (); ++i) {
    if (drawing_plane_buffers[i].size () != mp_drawing_plane_buffers[i].size ()) {
      return false;
    }
  }
  return true;
}

void BitmapCanvasData::assign (std::vector <lay::Bitmap *> &to, const std::vector <lay::Bitmap *> &from)
{
  while (! to.empty ()) {
    delete to.back ();
    to.pop_back ();
  }

  for (std::vector <lay::Bitmap *>::const_iterator b = from.begin (); b != from.end (); ++b) {
    to.push_back (new lay::Bitmap (**b));
  }
}

void BitmapCanvasData::assign (std::vector <std::vector <lay::Bitmap *> > &to, const std::vector <std::vector <lay::Bitmap *> > &from)
{
  while (! to.empty ()) {
    while (! to.back ().empty ()) {
      delete to.back ().back ();
      to.back ().pop_back ();
    }
    to.pop_back ();
  }

  for (std::vector <std::vector <lay::Bitmap *> >::const_iterator f = from.begin (); f != from.end (); ++f) {
    to.push_back (std::vector <lay::Bitmap *> ());
    for (std::vector <lay::Bitmap *>::const_iterator ff = f->begin (); ff != f->end (); ++ff) {
      to.back ().push_back (new lay::Bitmap (**ff));
    }
  }
}

void BitmapCanvasData::clear_planes ()
{
  assign (mp_plane_buffers, std::vector <lay::Bitmap *> ());
  assign (mp_drawing_plane_buffers, std::vector <std::vector <lay::Bitmap *> > ());
}

void BitmapCanvasData::swap (BitmapCanvasData &other)
{
  mp_plane_buffers.swap (other.mp_plane_buffers);
  mp_drawing_plane_buffers.swap (other.mp_drawing_plane_buffers);
  std::swap (m_width, other.m_width);
  std::swap (m_height, other.m_height);
}

// ------------------------------------------------------------------------

BitmapRedrawThreadCanvas::BitmapRedrawThreadCanvas ()
  : m_width (1), m_height (1)
{
  // .. nothing yet ..
}

BitmapRedrawThreadCanvas::~BitmapRedrawThreadCanvas ()
{
  lock ();
  clear_planes ();
  unlock ();
}

bool
BitmapRedrawThreadCanvas::shift_supported () const
{
  return true;
}

bool 
BitmapRedrawThreadCanvas::is_plane_empty (unsigned int n) 
{
  bool ret = true;

  lock ();
  if (n < mp_plane_buffers.size () && mp_plane_buffers [n] != 0) {
    ret = mp_plane_buffers [n]->empty ();
  }
  unlock ();

  return ret;
}

static void 
shift_bitmap (const lay::Bitmap *from, lay::Bitmap *to, int dx, int dy)
{
  tl_assert (from->width () == to->width ());
  tl_assert (from->height () == to->height ());

  to->clear ();

  if (dy <= -int (from->height()) || dy >= int (from->height ()) || 
      dx <= -int (from->width()) || dx >= int (from->width ())) {
    return;
  }

  int nn = int (to->height ()) - std::max (0, dy);
  for (int n = std::max (-dy, 0); n < nn; ++n) {

    if (from->is_scanline_empty (n)) {
      continue;
    }

    const uint32_t *sl_from = from->scanline (n);
    uint32_t *sl_to = to->scanline (n + dy);

    if (dx < 0) {

      unsigned int mo = ((unsigned int) -dx) / 32;
      unsigned int m = (to->width () + 31) / 32 - mo;
      sl_from += mo;

      unsigned int s1 = ((unsigned int) -dx) % 32;
      if (! s1) {
        for (unsigned int i = 0; i < m; ++i) {
          *sl_to++ = *sl_from++;
        }
      } else {
        unsigned int s2 = 32 - s1;
        for (unsigned int i = 1; i < m; ++i) {
          *sl_to++ = (sl_from[0] >> s1) | (sl_from[1] << s2);
          ++sl_from;
        }
        if (m) {
          *sl_to++ = (sl_from[0] >> s1);
        }
      }

    } else {

      unsigned int mo = ((unsigned int) dx) / 32;
      unsigned int m = (to->width () + 31) / 32 - mo;
      sl_to += mo;

      unsigned int s1 = ((unsigned int) dx) % 32;
      if (! s1) {
        for (unsigned int i = 0; i < m; ++i) {
          *sl_to++ = *sl_from++;
        }
      } else {
        unsigned int s2 = 32 - s1;
        if (m) {
          *sl_to++ = (sl_from[0] << s1);
        }
        for (unsigned int i = 1; i < m; ++i) {
          *sl_to++ = (sl_from[0] >> s2) | (sl_from[1] << s1);
          ++sl_from;
        }
      }

    }

  }
}

void 
BitmapRedrawThreadCanvas::prepare (unsigned int nlayers, unsigned int width, unsigned int height, double resolution, const db::Vector *shift_vector, const std::vector<int> *planes, const lay::Drawings *drawings)
{
  RedrawThreadCanvas::prepare (nlayers, width, height, resolution, shift_vector, planes, drawings);

  lock ();

  if (shift_vector) {

    tl_assert (width == m_width);
    tl_assert (height == m_height);
    tl_assert (nlayers == mp_plane_buffers.size ());

    for (size_t i = 0; i < mp_plane_buffers.size (); ++i) {
      lay::Bitmap *from = mp_plane_buffers [i];
      lay::Bitmap *to   = mp_plane_buffers [i] = new lay::Bitmap (width, height, resolution);
      shift_bitmap (from, to, shift_vector->x (), shift_vector->y ());
      delete from;
    }

    size_t di = 0;
    for (lay::Drawings::const_iterator d = drawings->begin (); d != drawings->end (); ++d, ++di) {
      for (unsigned int i = 0; i < d->num_planes (); ++i) {
        lay::Bitmap *from = mp_drawing_plane_buffers[di][i];
        lay::Bitmap *to   = mp_drawing_plane_buffers[di][i] = new lay::Bitmap (width, height, resolution);
        shift_bitmap (from, to, shift_vector->x (), shift_vector->y ());
        delete from;
      }
    }

  } else if (planes) {

    tl_assert (width == m_width);
    tl_assert (height == m_height);

    for (std::vector<int>::const_iterator l = planes->begin (); l != planes->end (); ++l) {

      if (*l < 0) {

        //  clear the custom drawing planes
        unsigned int di = 0;
        for (lay::Drawings::const_iterator d = drawings->begin (); d != drawings->end (); ++d, ++di) {
          for (unsigned int i = 0; i < d->num_planes (); ++i) {
            if (mp_drawing_plane_buffers.size () > di && mp_drawing_plane_buffers[di].size () > i) {
              mp_drawing_plane_buffers[di][i]->clear ();
            }
          }
        }

      } else if (size_t (*l) < mp_plane_buffers.size ()) {
        mp_plane_buffers[*l]->clear ();
      }

    }

  } else {

    m_width = width;
    m_height = height;

    //  delete the buffers and create new ones 
    clear_planes ();

    for (unsigned int i = 0; i < nlayers; ++i) {
      mp_plane_buffers.push_back (new lay::Bitmap (width, height, resolution));
    }

    for (lay::Drawings::const_iterator d = drawings->begin (); d != drawings->end (); ++d) {
      mp_drawing_plane_buffers.push_back (std::vector <lay::Bitmap *> ());
      for (unsigned int i = 0; i < d->num_planes (); ++i) {
        mp_drawing_plane_buffers.back ().push_back (new lay::Bitmap (width, height, resolution));
      }
    }

  }

  unlock ();
}

void 
BitmapRedrawThreadCanvas::set_plane (unsigned int n, const lay::CanvasPlane *plane)
{ 
  lock ();
  if (n < mp_plane_buffers.size ()) {
    const lay::Bitmap *bitmap = dynamic_cast<const lay::Bitmap *> (plane);
    tl_assert (bitmap != 0);
    *(mp_plane_buffers [n]) = *bitmap; 
  }
  unlock ();
}

void 
BitmapRedrawThreadCanvas::set_drawing_plane (unsigned int d, unsigned int n, const lay::CanvasPlane *plane)
{ 
  lock ();
  if (d < mp_drawing_plane_buffers.size () && n < mp_drawing_plane_buffers [d].size ()) {
    const lay::Bitmap *bitmap = dynamic_cast<const lay::Bitmap *> (plane);
    tl_assert (bitmap != 0);
    *(mp_drawing_plane_buffers [d][n]) = *bitmap; 
  }
  unlock ();
}

void 
BitmapRedrawThreadCanvas::clear_planes ()
{
  while (! mp_plane_buffers.empty ()) {
    delete mp_plane_buffers.back ();
    mp_plane_buffers.pop_back ();
  }

  while (! mp_drawing_plane_buffers.empty ()) {
    while (! mp_drawing_plane_buffers.back ().empty ()) {
      delete mp_drawing_plane_buffers.back ().back ();
      mp_drawing_plane_buffers.back ().pop_back ();
    }
    mp_drawing_plane_buffers.pop_back ();
  }
}

lay::CanvasPlane *
BitmapRedrawThreadCanvas::create_drawing_plane ()
{
  return new lay::Bitmap(m_width, m_height, resolution ());
}

void 
BitmapRedrawThreadCanvas::initialize_plane (lay::CanvasPlane *plane, unsigned int n)
{
  lock ();
  if (n < mp_plane_buffers.size ()) {
    lay::Bitmap *bitmap = dynamic_cast<lay::Bitmap *> (plane);
    tl_assert (bitmap != 0);
    *bitmap = *(mp_plane_buffers [n]);
  }
  unlock ();
}

void 
BitmapRedrawThreadCanvas::initialize_plane (lay::CanvasPlane *plane, unsigned int d, unsigned int n)
{
  lock ();
  if (d < mp_drawing_plane_buffers.size () && n < mp_drawing_plane_buffers [d].size ()) {
    lay::Bitmap *bitmap = dynamic_cast<lay::Bitmap *> (plane);
    tl_assert (bitmap != 0);
    *bitmap = *(mp_drawing_plane_buffers [d][n]);
  }
  unlock ();
}

void 
BitmapRedrawThreadCanvas::to_image (const std::vector <lay::ViewOp> &view_ops, const lay::DitherPattern &dp, const lay::LineStyles &ls, QColor background, QColor foreground, QColor active, const lay::Drawings *drawings, QImage &img, unsigned int width, unsigned int height)
{
  //  convert the plane data to image data
  bitmaps_to_image (view_ops, mp_plane_buffers, dp, ls, &img, width, height, true, &mutex ());

  //  convert the planes of the "drawing" objects too:
  std::vector <std::vector <lay::Bitmap *> >::const_iterator bt = mp_drawing_plane_buffers.begin ();
  for (lay::Drawings::const_iterator d = drawings->begin (); d != drawings->end () && bt != mp_drawing_plane_buffers.end (); ++d, ++bt) {
    bitmaps_to_image (d->get_view_ops (*this, background, foreground, active), *bt, dp, ls, &img, width, height, true, &mutex ());
  }
}

}

