<commit_before>/*
 * This file is part of the Electron Orbital Explorer. The Electron
 * Orbital Explorer is distributed under the Simplified BSD License
 * (also called the "BSD 2-Clause License"), in hopes that these
 * rendering techniques might be used by other programmers in
 * applications such as scientific visualization, video gaming, and so
 * on. If you find value in this software and use its technologies for
 * another purpose, I would love to hear back from you at bjthinks (at)
 * gmail (dot) com. If you improve this software and agree to release
 * your modifications under the below license, I encourage you to fork
 * the development tree on github and push your modifications. The
 * Electron Orbital Explorer's development URL is:
 * https://github.com/bjthinks/orbital-explorer
 * (This paragraph is not part of the software license and may be
 * removed.)
 *
 * Copyright (c) 2013, Brian W. Johnson
 * 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.
 *
 * 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 HOLDER 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.
 */

#ifndef WIDGET_HH
#define WIDGET_HH

#include <list>
#include <limits>

#include "util.hh"
#include "oopengl.hh"
#include "vector.hh"
#include "color.hh"
#include "font.hh"
#include "shaders.hh"
#include "event.hh"

// Small aggregate to encapsulate rectangular regions.

struct Region
{
  Region()
    : left(0), bottom(0),
      width(std::numeric_limits<int>::max()),
      height(std::numeric_limits<int>::max())
  {}
  Region(int new_left, int new_bottom, int new_width, int new_height)
    : left(new_left), bottom(new_bottom), width(new_width), height(new_height)
  {}
  // Compose two Regions: inner = outer * inner_relative_to_outer
  Region operator*(const Region &rhs)
  {
    return Region(left + rhs.left, bottom + rhs.bottom,
                  std::max(0, std::min(rhs.width, width - rhs.left)),
                  std::max(0, std::min(rhs.height, height - rhs.bottom)));
  }
  int left, bottom, width, height;
};

inline bool isEventInsideRegion(const PositionedEvent &e, const Region &r)
{
  if (e.x() >= r.left   && e.x() < r.left   + r.width &&
      e.y() >= r.bottom && e.y() < r.bottom + r.height)
    return true;
  else
    return false;
}

// Time to make a fun class hierarchy!
// The base class is Widget, which can be drawn, can handle events,
// and has a position and size.

class Widget : public Uncopyable, public Handler
{
public:
  virtual ~Widget() {}
  virtual void draw(Region r) = 0;
  virtual bool handle(Region r, const PositionedEvent &e) = 0;
  void move(int x, int y)
  {
    geometry.left = x;
    geometry.bottom = y;
  }
  void resize(int width, int height)
  {
    geometry.width = width;
    geometry.height = height;
  }

protected:
  Region geometry;
};

// A Container is a Widget that can contain other Widgets.
// An Element is a Widget that goes in a Container.

class Contained;

class Container : virtual public Widget
{
public:
  ~Container();
  void draw(Region r);
  bool handle(Region r, const PositionedEvent &e);

  // Called only by constructors and destructors of Contained widgets
  void addContents(Contained *c);
  void deleteContents(Contained *c);

private:
  std::list<Contained *> contents;
};

// All but top-level Widgets will go in a Container. They are Contained.

class Contained : virtual public Widget
{
public:
  Contained(Container &e);
  ~Contained();

private:
  Container &enclosure;
};

// A Widget that's both a Container and an Contained is a Composite.

class Composite : public Container, public Contained
{
public:
  Composite(Container &e);
};

// Might also define a top-level Container here...

// A Widget that's only a Contained is an Element.

class Element : public Contained
{
public:
  Element(Container &e);
  bool handle(Region r, const PositionedEvent &e);
};

// Triangles are a basic drawing primitive.

class Triangle : public Element
{
public:
  Triangle(Container &e);
  Triangle &x(Vector<2> x_);
  Triangle &y(Vector<2> y_);
  Triangle &z(Vector<2> z_);
  Triangle &color(Color c);
  void draw(Region r);

private:
  static Program *triangleProg;
  static VertexArrayObject *triangleVAO;
  Vector<2> xx, yy, zz;
  Color cc;
};

// Quads are implemented as a pair of triangles.

class Quad : public Composite
{
public:
  Quad(Container &e);
  Quad &x(Vector<2> x_);
  Quad &y(Vector<2> z_);
  Quad &z(Vector<2> y_);
  Quad &w(Vector<2> w_);
  Quad &color(Color c);

private:
  Triangle s, t;
};

// A Rectangle is a convenience wrapper around a Quad.

class Rectangle : public Composite
{
public:
  Rectangle(Container &e);
  Rectangle &ll(Vector<2> llcorner);
  Rectangle &ur(Vector<2> urcorner);
  Rectangle &color(Color c);

private:
  Vector<2> llc, urc;
  Quad q;
};

// A Box is a hollow rectangle

class Box : public Composite
{
public:
  Box(Container &e);
  Box &ll(Vector<2> llcorner);
  Box &ur(Vector<2> urcorner);
  Box &width(double width_);
  Box &color(Color c);

private:
  Vector<2> llc, urc;
  double wid;
  Rectangle left, right, bottom, top;
  void setCoords();
};

// An antialiased 7-bit ASCII character

class Character : public Element
{
public:
  Character(Container &e, const Font &f);
  Character &set(char c);
  Character &color(Color c);
  void draw(Region r);
  int bottom()  const { return font.bottom(ch); }
  int left()    const { return font.left(ch); }
  int width()   const { return font.width(ch); }
  int advance() const { return font.advance(ch); }

private:
  static Program *characterProg;
  static VertexArrayObject *characterVAO;
  const Font &font;
  Color cc;
  char ch;
};

// A character string - note absence of line wrapping

enum Justification { LeftJustified, CenterJustified, RightJustified };

class String : public Composite
{
public:
  String(Container &e, const Font &f);
  ~String();
  void set(const std::string &s);
  void color(Color c);
  void justify(Justification j);

private:
  const Font &font;
  std::vector<Character *> str;
  Color cc;
  Justification jj;

  void moveCharacters();
  void setCharacterColors();
};

//
// Implementations of "simple" functions follow.
//

inline Container::~Container()
{
  if (contents.size() != 0)
    throw std::logic_error("Container destructed but has contents");
}

inline void Container::draw(Region r)
{
  for (std::list<Contained *>::iterator i = contents.begin();
       i != contents.end(); ++i)
    (*i)->draw(r * geometry);
}

inline bool Container::handle(Region r, const PositionedEvent &e)
{
  r = r * geometry;

  if (!isEventInsideRegion(e, r))
    return false;

  if (e.dispatchTo(*this))
    return true;

  for (std::list<Contained *>::iterator i = contents.begin();
       i != contents.end(); ++i) {
    if ((*i)->handle(r, e))
      return true;
  }

  return false;
}

inline void Container::addContents(Contained *c)
{
  contents.push_front(c);
}

inline void Container::deleteContents(Contained *c)
{
  contents.remove(c);
}

inline Contained::Contained(Container &e)
  : enclosure(e)
{
  enclosure.addContents(this);
}

inline Contained::~Contained()
{
  enclosure.deleteContents(this);
}

inline Composite::Composite(Container &e)
  : Contained(e)
{}

inline Element::Element(Container &e)
  : Contained(e)
{}

inline bool Element::handle(Region r, const PositionedEvent &e)
{
  r = r * geometry;

  if (!isEventInsideRegion(e, r))
    return false;

  return e.dispatchTo(*this);
}

inline Triangle &Triangle::x(Vector<2> x_)
{
  xx = x_;
  return *this;
}

inline Triangle &Triangle::y(Vector<2> y_)
{
  yy = y_;
  return *this;
}

inline Triangle &Triangle::z(Vector<2> z_)
{
  zz = z_;
  return *this;
}

inline Triangle &Triangle::color(Color c)
{
  cc = c;
  return *this;
}

inline Quad::Quad(Container &e)
  : Composite(e),
    s(*this),
    t(*this)
{}

inline Quad &Quad::x(Vector<2> x_)
{
  s.x(x_);
  t.x(x_);
  return *this;
}

inline Quad &Quad::y(Vector<2> y_)
{
  s.y(y_);
  return *this;
}

inline Quad &Quad::z(Vector<2> z_)
{
  s.z(z_);
  t.y(z_);
  return *this;
}

inline Quad &Quad::w(Vector<2> w_)
{
  t.z(w_);
  return *this;
}

inline Quad &Quad::color(Color c)
{
  s.color(c);
  t.color(c);
  return *this;
}

inline Rectangle::Rectangle(Container &e)
  : Composite(e),
    llc(Vector2(0, 0)),
    urc(Vector2(0, 0)),
    q(*this)
{}

inline Rectangle &Rectangle::ll(Vector<2> llcorner)
{
  llc = llcorner;
  q .x(Vector2(llc[0], llc[1]))
    .y(Vector2(urc[0], llc[1]))
    .w(Vector2(llc[0], urc[1]));
  return *this;
}

inline Rectangle &Rectangle::ur(Vector<2> urcorner)
{
  urc = urcorner;
  q .y(Vector2(urc[0], llc[1]))
    .z(Vector2(urc[0], urc[1]))
    .w(Vector2(llc[0], urc[1]));
  return *this;
}

inline Rectangle &Rectangle::color(Color c)
{
  q.color(c);
  return *this;
}

inline Box::Box(Container &e)
  : Composite(e),
    llc(Vector2(0, 0)),
    urc(Vector2(0, 0)),
    wid(0),
    left(*this),
    right(*this),
    bottom(*this),
    top(*this)
{}

inline void Box::setCoords()
{
  left  .ll(llc).ur(Vector2(llc[0]+wid, urc[1]));
  bottom.ll(llc).ur(Vector2(urc[0], llc[1]+wid));
  right .ll(Vector2(urc[0]-wid, llc[1])).ur(urc);
  top   .ll(Vector2(llc[0], urc[1]-wid)).ur(urc);
}

inline Box &Box::ll(Vector<2> llcorner)
{
  llc = llcorner;
  setCoords();
  return *this;
}

inline Box &Box::ur(Vector<2> urcorner)
{
  urc = urcorner;
  setCoords();
  return *this;
}

inline Box &Box::width(double width_)
{
  wid = width_;
  setCoords();
  return *this;
}

inline Box &Box::color(Color c)
{
  left.color(c);
  right.color(c);
  bottom.color(c);
  top.color(c);
  return *this;
}

inline Character &Character::set(char c)
{
  ch = c;
  resize(font.width(ch), font.height(ch));
  return *this;
}

inline Character &Character::color(Color c)
{
  cc = c;
  return *this;
}

inline String::String(Container &e, const Font &f)
  : Composite(e),
    font(f),
    jj(LeftJustified)
{}

inline String::~String()
{
  for (int i = 0; i < int(str.size()); ++i)
    delete str[i];
}

inline void String::set(const std::string &s)
{
  for (int i = 0; i < int(str.size()); ++i)
    delete str[i];
  str.resize(0);
  for (int i = 0; i < int(s.size()); ++i) {
    Character *c = new Character(*this, font);
    c->set(s[i]);
    str.push_back(c);
  }
  moveCharacters();
  setCharacterColors();
}

inline void String::color(Color c)
{
  cc = c;
  setCharacterColors();
}

inline void String::justify(Justification j)
{
  jj = j;
  moveCharacters();
}

inline void String::moveCharacters()
{
  if (str.size() == 0)
    return;

  int x = 0;
  int y = font.descender();

  switch (jj) {
  case LeftJustified:
    {
      if (str[0]->left() < 0)
        x = -str[0]->left();
    }
    break;
  case RightJustified:
    {
      x = geometry.width;

      int n = str.size() - 1;
      int right_dangle = str[n]->left() + str[n]->width() - str[n]->advance();
      if (right_dangle > 0)
        x -= right_dangle;

      for (int i = 0; i < int(str.size()); ++i)
        x -= str[i]->advance();
    }
    break;
  case CenterJustified:
    {
      int twice_x = geometry.width;

      for (int i = 0; i < int(str.size()); ++i)
        twice_x -= str[i]->advance();

      x = twice_x / 2;
    }
    break;
  }

  for (int i = 0; i < int(str.size()); ++i) {
    int cx = x + str[i]->left();
    int cy = y + str[i]->bottom();
    str[i]->move(cx, cy);
    x += str[i]->advance();
  }

  return;
}

inline void String::setCharacterColors()
{
  for (int i = 0; i < int(str.size()); ++i)
    str[i]->color(cc);
}

#endif
<commit_msg>String has method chaining for property setting<commit_after>/*
 * This file is part of the Electron Orbital Explorer. The Electron
 * Orbital Explorer is distributed under the Simplified BSD License
 * (also called the "BSD 2-Clause License"), in hopes that these
 * rendering techniques might be used by other programmers in
 * applications such as scientific visualization, video gaming, and so
 * on. If you find value in this software and use its technologies for
 * another purpose, I would love to hear back from you at bjthinks (at)
 * gmail (dot) com. If you improve this software and agree to release
 * your modifications under the below license, I encourage you to fork
 * the development tree on github and push your modifications. The
 * Electron Orbital Explorer's development URL is:
 * https://github.com/bjthinks/orbital-explorer
 * (This paragraph is not part of the software license and may be
 * removed.)
 *
 * Copyright (c) 2013, Brian W. Johnson
 * 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.
 *
 * 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 HOLDER 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.
 */

#ifndef WIDGET_HH
#define WIDGET_HH

#include <list>
#include <limits>

#include "util.hh"
#include "oopengl.hh"
#include "vector.hh"
#include "color.hh"
#include "font.hh"
#include "shaders.hh"
#include "event.hh"

// Small aggregate to encapsulate rectangular regions.

struct Region
{
  Region()
    : left(0), bottom(0),
      width(std::numeric_limits<int>::max()),
      height(std::numeric_limits<int>::max())
  {}
  Region(int new_left, int new_bottom, int new_width, int new_height)
    : left(new_left), bottom(new_bottom), width(new_width), height(new_height)
  {}
  // Compose two Regions: inner = outer * inner_relative_to_outer
  Region operator*(const Region &rhs)
  {
    return Region(left + rhs.left, bottom + rhs.bottom,
                  std::max(0, std::min(rhs.width, width - rhs.left)),
                  std::max(0, std::min(rhs.height, height - rhs.bottom)));
  }
  int left, bottom, width, height;
};

inline bool isEventInsideRegion(const PositionedEvent &e, const Region &r)
{
  if (e.x() >= r.left   && e.x() < r.left   + r.width &&
      e.y() >= r.bottom && e.y() < r.bottom + r.height)
    return true;
  else
    return false;
}

// Time to make a fun class hierarchy!
// The base class is Widget, which can be drawn, can handle events,
// and has a position and size.

class Widget : public Uncopyable, public Handler
{
public:
  virtual ~Widget() {}
  virtual void draw(Region r) = 0;
  virtual bool handle(Region r, const PositionedEvent &e) = 0;
  void move(int x, int y)
  {
    geometry.left = x;
    geometry.bottom = y;
  }
  void resize(int width, int height)
  {
    geometry.width = width;
    geometry.height = height;
  }

protected:
  Region geometry;
};

// A Container is a Widget that can contain other Widgets.
// An Element is a Widget that goes in a Container.

class Contained;

class Container : virtual public Widget
{
public:
  ~Container();
  void draw(Region r);
  bool handle(Region r, const PositionedEvent &e);

  // Called only by constructors and destructors of Contained widgets
  void addContents(Contained *c);
  void deleteContents(Contained *c);

private:
  std::list<Contained *> contents;
};

// All but top-level Widgets will go in a Container. They are Contained.

class Contained : virtual public Widget
{
public:
  Contained(Container &e);
  ~Contained();

private:
  Container &enclosure;
};

// A Widget that's both a Container and an Contained is a Composite.

class Composite : public Container, public Contained
{
public:
  Composite(Container &e);
};

// Might also define a top-level Container here...

// A Widget that's only a Contained is an Element.

class Element : public Contained
{
public:
  Element(Container &e);
  bool handle(Region r, const PositionedEvent &e);
};

// Triangles are a basic drawing primitive.

class Triangle : public Element
{
public:
  Triangle(Container &e);
  Triangle &x(Vector<2> x_);
  Triangle &y(Vector<2> y_);
  Triangle &z(Vector<2> z_);
  Triangle &color(Color c);
  void draw(Region r);

private:
  static Program *triangleProg;
  static VertexArrayObject *triangleVAO;
  Vector<2> xx, yy, zz;
  Color cc;
};

// Quads are implemented as a pair of triangles.

class Quad : public Composite
{
public:
  Quad(Container &e);
  Quad &x(Vector<2> x_);
  Quad &y(Vector<2> z_);
  Quad &z(Vector<2> y_);
  Quad &w(Vector<2> w_);
  Quad &color(Color c);

private:
  Triangle s, t;
};

// A Rectangle is a convenience wrapper around a Quad.

class Rectangle : public Composite
{
public:
  Rectangle(Container &e);
  Rectangle &ll(Vector<2> llcorner);
  Rectangle &ur(Vector<2> urcorner);
  Rectangle &color(Color c);

private:
  Vector<2> llc, urc;
  Quad q;
};

// A Box is a hollow rectangle

class Box : public Composite
{
public:
  Box(Container &e);
  Box &ll(Vector<2> llcorner);
  Box &ur(Vector<2> urcorner);
  Box &width(double width_);
  Box &color(Color c);

private:
  Vector<2> llc, urc;
  double wid;
  Rectangle left, right, bottom, top;
  void setCoords();
};

// An antialiased 7-bit ASCII character

class Character : public Element
{
public:
  Character(Container &e, const Font &f);
  Character &set(char c);
  Character &color(Color c);
  void draw(Region r);
  int bottom()  const { return font.bottom(ch); }
  int left()    const { return font.left(ch); }
  int width()   const { return font.width(ch); }
  int advance() const { return font.advance(ch); }

private:
  static Program *characterProg;
  static VertexArrayObject *characterVAO;
  const Font &font;
  Color cc;
  char ch;
};

// A character string - note absence of line wrapping

enum Justification { LeftJustified, CenterJustified, RightJustified };

class String : public Composite
{
public:
  String(Container &e, const Font &f);
  ~String();
  String &set(const std::string &s);
  String &color(Color c);
  String &justify(Justification j);

private:
  const Font &font;
  std::vector<Character *> str;
  Color cc;
  Justification jj;

  void moveCharacters();
  void setCharacterColors();
};

//
// Implementations of "simple" functions follow.
//

inline Container::~Container()
{
  if (contents.size() != 0)
    throw std::logic_error("Container destructed but has contents");
}

inline void Container::draw(Region r)
{
  for (std::list<Contained *>::iterator i = contents.begin();
       i != contents.end(); ++i)
    (*i)->draw(r * geometry);
}

inline bool Container::handle(Region r, const PositionedEvent &e)
{
  r = r * geometry;

  if (!isEventInsideRegion(e, r))
    return false;

  if (e.dispatchTo(*this))
    return true;

  for (std::list<Contained *>::iterator i = contents.begin();
       i != contents.end(); ++i) {
    if ((*i)->handle(r, e))
      return true;
  }

  return false;
}

inline void Container::addContents(Contained *c)
{
  contents.push_front(c);
}

inline void Container::deleteContents(Contained *c)
{
  contents.remove(c);
}

inline Contained::Contained(Container &e)
  : enclosure(e)
{
  enclosure.addContents(this);
}

inline Contained::~Contained()
{
  enclosure.deleteContents(this);
}

inline Composite::Composite(Container &e)
  : Contained(e)
{}

inline Element::Element(Container &e)
  : Contained(e)
{}

inline bool Element::handle(Region r, const PositionedEvent &e)
{
  r = r * geometry;

  if (!isEventInsideRegion(e, r))
    return false;

  return e.dispatchTo(*this);
}

inline Triangle &Triangle::x(Vector<2> x_)
{
  xx = x_;
  return *this;
}

inline Triangle &Triangle::y(Vector<2> y_)
{
  yy = y_;
  return *this;
}

inline Triangle &Triangle::z(Vector<2> z_)
{
  zz = z_;
  return *this;
}

inline Triangle &Triangle::color(Color c)
{
  cc = c;
  return *this;
}

inline Quad::Quad(Container &e)
  : Composite(e),
    s(*this),
    t(*this)
{}

inline Quad &Quad::x(Vector<2> x_)
{
  s.x(x_);
  t.x(x_);
  return *this;
}

inline Quad &Quad::y(Vector<2> y_)
{
  s.y(y_);
  return *this;
}

inline Quad &Quad::z(Vector<2> z_)
{
  s.z(z_);
  t.y(z_);
  return *this;
}

inline Quad &Quad::w(Vector<2> w_)
{
  t.z(w_);
  return *this;
}

inline Quad &Quad::color(Color c)
{
  s.color(c);
  t.color(c);
  return *this;
}

inline Rectangle::Rectangle(Container &e)
  : Composite(e),
    llc(Vector2(0, 0)),
    urc(Vector2(0, 0)),
    q(*this)
{}

inline Rectangle &Rectangle::ll(Vector<2> llcorner)
{
  llc = llcorner;
  q .x(Vector2(llc[0], llc[1]))
    .y(Vector2(urc[0], llc[1]))
    .w(Vector2(llc[0], urc[1]));
  return *this;
}

inline Rectangle &Rectangle::ur(Vector<2> urcorner)
{
  urc = urcorner;
  q .y(Vector2(urc[0], llc[1]))
    .z(Vector2(urc[0], urc[1]))
    .w(Vector2(llc[0], urc[1]));
  return *this;
}

inline Rectangle &Rectangle::color(Color c)
{
  q.color(c);
  return *this;
}

inline Box::Box(Container &e)
  : Composite(e),
    llc(Vector2(0, 0)),
    urc(Vector2(0, 0)),
    wid(0),
    left(*this),
    right(*this),
    bottom(*this),
    top(*this)
{}

inline void Box::setCoords()
{
  left  .ll(llc).ur(Vector2(llc[0]+wid, urc[1]));
  bottom.ll(llc).ur(Vector2(urc[0], llc[1]+wid));
  right .ll(Vector2(urc[0]-wid, llc[1])).ur(urc);
  top   .ll(Vector2(llc[0], urc[1]-wid)).ur(urc);
}

inline Box &Box::ll(Vector<2> llcorner)
{
  llc = llcorner;
  setCoords();
  return *this;
}

inline Box &Box::ur(Vector<2> urcorner)
{
  urc = urcorner;
  setCoords();
  return *this;
}

inline Box &Box::width(double width_)
{
  wid = width_;
  setCoords();
  return *this;
}

inline Box &Box::color(Color c)
{
  left.color(c);
  right.color(c);
  bottom.color(c);
  top.color(c);
  return *this;
}

inline Character &Character::set(char c)
{
  ch = c;
  resize(font.width(ch), font.height(ch));
  return *this;
}

inline Character &Character::color(Color c)
{
  cc = c;
  return *this;
}

inline String::String(Container &e, const Font &f)
  : Composite(e),
    font(f),
    jj(LeftJustified)
{}

inline String::~String()
{
  for (int i = 0; i < int(str.size()); ++i)
    delete str[i];
}

inline String &String::set(const std::string &s)
{
  for (int i = 0; i < int(str.size()); ++i)
    delete str[i];
  str.resize(0);
  for (int i = 0; i < int(s.size()); ++i) {
    Character *c = new Character(*this, font);
    c->set(s[i]);
    str.push_back(c);
  }
  moveCharacters();
  setCharacterColors();

  return *this;
}

inline String &String::color(Color c)
{
  cc = c;
  setCharacterColors();

  return *this;
}

inline String &String::justify(Justification j)
{
  jj = j;
  moveCharacters();

  return *this;
}

inline void String::moveCharacters()
{
  if (str.size() == 0)
    return;

  int x = 0;
  int y = font.descender();

  switch (jj) {
  case LeftJustified:
    {
      if (str[0]->left() < 0)
        x = -str[0]->left();
    }
    break;
  case RightJustified:
    {
      x = geometry.width;

      int n = str.size() - 1;
      int right_dangle = str[n]->left() + str[n]->width() - str[n]->advance();
      if (right_dangle > 0)
        x -= right_dangle;

      for (int i = 0; i < int(str.size()); ++i)
        x -= str[i]->advance();
    }
    break;
  case CenterJustified:
    {
      int twice_x = geometry.width;

      for (int i = 0; i < int(str.size()); ++i)
        twice_x -= str[i]->advance();

      x = twice_x / 2;
    }
    break;
  }

  for (int i = 0; i < int(str.size()); ++i) {
    int cx = x + str[i]->left();
    int cy = y + str[i]->bottom();
    str[i]->move(cx, cy);
    x += str[i]->advance();
  }

  return;
}

inline void String::setCharacterColors()
{
  for (int i = 0; i < int(str.size()); ++i)
    str[i]->color(cc);
}

#endif
<|endoftext|>


<commit_before>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageEncoderPriv.h"

#ifdef SK_HAS_PNG_LIBRARY

#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkDither.h"
#include "SkICC.h"
#include "SkMath.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkUnPreMultiply.h"
#include "SkUtils.h"
#include "transform_scanline.h"

#include "png.h"

// Suppress most PNG warnings when calling image decode functions.
static const bool c_suppressPNGImageDecoderWarnings = true;

static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
    if (!c_suppressPNGImageDecoderWarnings) {
        SkDEBUGF(("------ png error %s\n", msg));
    }
    longjmp(png_jmpbuf(png_ptr), 1);
}

static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
    SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
    if (!sk_stream->write(data, len)) {
        png_error(png_ptr, "sk_write_fn Error!");
    }
}

static transform_scanline_proc choose_proc(const SkImageInfo& info) {
    const bool isGammaEncoded = info.gammaCloseToSRGB();
    switch (info.colorType()) {
        case kRGBA_8888_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                    return transform_scanline_RGBX;
                case kUnpremul_SkAlphaType:
                    return transform_scanline_memcpy;
                case kPremul_SkAlphaType:
                    return isGammaEncoded ? transform_scanline_srgbA :
                                            transform_scanline_rgbA;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        case kBGRA_8888_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                    return transform_scanline_BGRX;
                case kUnpremul_SkAlphaType:
                    return transform_scanline_BGRA;
                case kPremul_SkAlphaType:
                    return isGammaEncoded ? transform_scanline_sbgrA :
                                            transform_scanline_bgrA;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        case kRGB_565_SkColorType:
            return transform_scanline_565;
        case kARGB_4444_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                    return transform_scanline_444;
                case kPremul_SkAlphaType:
                    // 4444 is assumed to be legacy premul.
                    return transform_scanline_4444;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        case kIndex_8_SkColorType:
        case kGray_8_SkColorType:
            return transform_scanline_memcpy;
        case kRGBA_F16_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                case kUnpremul_SkAlphaType:
                    return transform_scanline_F16;
                case kPremul_SkAlphaType:
                    return transform_scanline_F16_premul;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        default:
            SkASSERT(false);
            return nullptr;
    }
}

/*  Pack palette[] with the corresponding colors, and if the image has alpha, also
    pack trans[] and return the number of alphas[] entries written. If the image is
    opaque, the return value will always be 0.
*/
static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
                               png_byte* SK_RESTRICT alphas, const SkImageInfo& info) {
    const SkPMColor* colors = ctable->readColors();
    const int count = ctable->count();
    SkPMColor storage[256];
    if (kPremul_SkAlphaType == info.alphaType()) {
        // Unpremultiply the colors.
        const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType);
        transform_scanline_proc proc = choose_proc(rgbaInfo);
        proc((char*) storage, (const char*) colors, ctable->count(), 4, nullptr);
        colors = storage;
    }

    int numWithAlpha = 0;
    if (kOpaque_SkAlphaType != info.alphaType()) {
        // PNG requires that all non-opaque colors come first in the palette.  Write these first.
        for (int i = 0; i < count; i++) {
            uint8_t alpha = SkGetPackedA32(colors[i]);
            if (0xFF != alpha) {
                alphas[numWithAlpha] = alpha;
                palette[numWithAlpha].red   = SkGetPackedR32(colors[i]);
                palette[numWithAlpha].green = SkGetPackedG32(colors[i]);
                palette[numWithAlpha].blue  = SkGetPackedB32(colors[i]);
                numWithAlpha++;
            }
        }

    }

    if (0 == numWithAlpha) {
        // All of the entries are opaque.
        for (int i = 0; i < count; i++) {
            SkPMColor c = *colors++;
            palette[i].red   = SkGetPackedR32(c);
            palette[i].green = SkGetPackedG32(c);
            palette[i].blue  = SkGetPackedB32(c);
        }
    } else {
        // We have already written the non-opaque colors.  Now just write the opaque colors.
        int currIndex = numWithAlpha;
        int i = 0;
        while (currIndex != count) {
            uint8_t alpha = SkGetPackedA32(colors[i]);
            if (0xFF == alpha) {
                palette[currIndex].red   = SkGetPackedR32(colors[i]);
                palette[currIndex].green = SkGetPackedG32(colors[i]);
                palette[currIndex].blue  = SkGetPackedB32(colors[i]);
                currIndex++;
            }

            i++;
        }
    }

    return numWithAlpha;
}

static bool do_encode(SkWStream*, const SkPixmap&, int, int, png_color_8&);

bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& src, const SkEncodeOptions& opts) {
    SkASSERT(!src.colorSpace() || src.colorSpace()->gammaCloseToSRGB() ||
             src.colorSpace()->gammaIsLinear());

    SkPixmap pixmap = src;
    if (SkEncodeOptions::ColorBehavior::kLegacy == opts.fColorBehavior) {
        pixmap.setColorSpace(nullptr);
    } else {
        if (!pixmap.colorSpace()) {
            return false;
        }
    }

    if (!pixmap.addr() || pixmap.info().isEmpty()) {
        return false;
    }

    const SkColorType colorType = pixmap.colorType();
    const SkAlphaType alphaType = pixmap.alphaType();
    switch (alphaType) {
        case kUnpremul_SkAlphaType:
            if (kARGB_4444_SkColorType == colorType) {
                return false;
            }

            break;
        case kOpaque_SkAlphaType:
        case kPremul_SkAlphaType:
            break;
        default:
            return false;
    }

    const bool isOpaque = (kOpaque_SkAlphaType == alphaType);
    int bitDepth = 8;
    png_color_8 sig_bit;
    sk_bzero(&sig_bit, sizeof(png_color_8));
    int pngColorType;
    switch (colorType) {
        case kRGBA_F16_SkColorType:
            if (!pixmap.colorSpace() || !pixmap.colorSpace()->gammaIsLinear()) {
                return false;
            }

            sig_bit.red = 16;
            sig_bit.green = 16;
            sig_bit.blue = 16;
            sig_bit.alpha = 16;
            bitDepth = 16;
            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
            break;
        case kIndex_8_SkColorType:
            sig_bit.red = 8;
            sig_bit.green = 8;
            sig_bit.blue = 8;
            sig_bit.alpha = 8;
            pngColorType = PNG_COLOR_TYPE_PALETTE;
            break;
        case kGray_8_SkColorType:
            sig_bit.gray = 8;
            pngColorType = PNG_COLOR_TYPE_GRAY;
            SkASSERT(isOpaque);
            break;
        case kRGBA_8888_SkColorType:
        case kBGRA_8888_SkColorType:
            sig_bit.red = 8;
            sig_bit.green = 8;
            sig_bit.blue = 8;
            sig_bit.alpha = 8;
            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
            break;
        case kARGB_4444_SkColorType:
            sig_bit.red = 4;
            sig_bit.green = 4;
            sig_bit.blue = 4;
            sig_bit.alpha = 4;
            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
            break;
        case kRGB_565_SkColorType:
            sig_bit.red = 5;
            sig_bit.green = 6;
            sig_bit.blue = 5;
            pngColorType = PNG_COLOR_TYPE_RGB;
            SkASSERT(isOpaque);
            break;
        default:
            return false;
    }

    if (kIndex_8_SkColorType == colorType) {
        SkColorTable* ctable = pixmap.ctable();
        if (!ctable || ctable->count() == 0) {
            return false;
        }

        // Currently, we always use 8-bit indices for paletted pngs.
        // When ctable->count() <= 16, we could potentially use 1, 2,
        // or 4 bit indices.
    }

    return do_encode(stream, pixmap, pngColorType, bitDepth, sig_bit);
}

static int num_components(int pngColorType) {
    switch (pngColorType) {
        case PNG_COLOR_TYPE_PALETTE:
        case PNG_COLOR_TYPE_GRAY:
            return 1;
        case PNG_COLOR_TYPE_RGB:
            return 3;
        case PNG_COLOR_TYPE_RGBA:
            return 4;
        default:
            SkASSERT(false);
            return 0;
    }
}

static bool do_encode(SkWStream* stream, const SkPixmap& pixmap,
                      int pngColorType, int bitDepth, png_color_8& sig_bit) {
    png_structp png_ptr;
    png_infop info_ptr;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
    if (nullptr == png_ptr) {
        return false;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (nullptr == info_ptr) {
        png_destroy_write_struct(&png_ptr,  nullptr);
        return false;
    }

    /* Set error handling.  REQUIRED if you aren't supplying your own
    * error handling functions in the png_create_write_struct() call.
    */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return false;
    }

    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, nullptr);

    /* Set the image information here.  Width and height are up to 2^31,
    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
    */

    png_set_IHDR(png_ptr, info_ptr, pixmap.width(), pixmap.height(),
                 bitDepth, pngColorType,
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                 PNG_FILTER_TYPE_BASE);

    // set our colortable/trans arrays if needed
    png_color paletteColors[256];
    png_byte trans[256];
    if (kIndex_8_SkColorType == pixmap.colorType()) {
        SkColorTable* colorTable = pixmap.ctable();
        SkASSERT(colorTable);
        int numTrans = pack_palette(colorTable, paletteColors, trans, pixmap.info());
        png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count());
        if (numTrans > 0) {
            png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr);
        }
    }

    if (pixmap.colorSpace()) {
        SkColorSpaceTransferFn fn;
        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
        if (pixmap.colorSpace()->isSRGB()) {
            png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
        } else if (pixmap.colorSpace()->isNumericalTransferFn(&fn) &&
                   pixmap.colorSpace()->toXYZD50(&toXYZD50))
        {
            sk_sp<SkData> icc = SkICC::WriteToICC(fn, toXYZD50);
            png_set_iCCP(png_ptr, info_ptr, "Skia", 0, icc->bytes(), icc->size());
        }

        // TODO: Should we support writing ICC profiles for additional color spaces?
    }

    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
    png_write_info(png_ptr, info_ptr);
    int pngBytesPerPixel = num_components(pngColorType) * (bitDepth / 8);
    if (kRGBA_F16_SkColorType == pixmap.colorType() && kOpaque_SkAlphaType == pixmap.alphaType()) {
        // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
        // to skip the alpha channel.
        png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
        pngBytesPerPixel = 8;
    }

    SkAutoSTMalloc<1024, char> rowStorage(pixmap.width() * pngBytesPerPixel);
    char* storage = rowStorage.get();
    const char* srcImage = (const char*)pixmap.addr();
    transform_scanline_proc proc = choose_proc(pixmap.info());
    for (int y = 0; y < pixmap.height(); y++) {
        png_bytep row_ptr = (png_bytep)storage;
        proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(pixmap.colorType()),
             nullptr);
        png_write_rows(png_ptr, &row_ptr, 1);
        srcImage += pixmap.rowBytes();
    }

    png_write_end(png_ptr, info_ptr);

    /* clean up after the write, and free any memory allocated */
    png_destroy_write_struct(&png_ptr, &info_ptr);
    return true;
}

#endif
<commit_msg>Compatible use of png_set_iCCP<commit_after>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageEncoderPriv.h"

#ifdef SK_HAS_PNG_LIBRARY

#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkDither.h"
#include "SkICC.h"
#include "SkMath.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkUnPreMultiply.h"
#include "SkUtils.h"
#include "transform_scanline.h"

#include "png.h"

// Suppress most PNG warnings when calling image decode functions.
static const bool c_suppressPNGImageDecoderWarnings = true;

static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
    if (!c_suppressPNGImageDecoderWarnings) {
        SkDEBUGF(("------ png error %s\n", msg));
    }
    longjmp(png_jmpbuf(png_ptr), 1);
}

static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
    SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
    if (!sk_stream->write(data, len)) {
        png_error(png_ptr, "sk_write_fn Error!");
    }
}

static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkColorSpaceTransferFn& fn,
                    const SkMatrix44& toXYZD50) {
    sk_sp<SkData> icc = SkICC::WriteToICC(fn, toXYZD50);
#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
    png_const_bytep iccPtr = icc->bytes();
#else
    png_charp iccPtr = (png_charp) icc->writable_data();
#endif
    png_set_iCCP(png_ptr, info_ptr, "Skia", 0, iccPtr, icc->size());
}

static transform_scanline_proc choose_proc(const SkImageInfo& info) {
    const bool isGammaEncoded = info.gammaCloseToSRGB();
    switch (info.colorType()) {
        case kRGBA_8888_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                    return transform_scanline_RGBX;
                case kUnpremul_SkAlphaType:
                    return transform_scanline_memcpy;
                case kPremul_SkAlphaType:
                    return isGammaEncoded ? transform_scanline_srgbA :
                                            transform_scanline_rgbA;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        case kBGRA_8888_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                    return transform_scanline_BGRX;
                case kUnpremul_SkAlphaType:
                    return transform_scanline_BGRA;
                case kPremul_SkAlphaType:
                    return isGammaEncoded ? transform_scanline_sbgrA :
                                            transform_scanline_bgrA;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        case kRGB_565_SkColorType:
            return transform_scanline_565;
        case kARGB_4444_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                    return transform_scanline_444;
                case kPremul_SkAlphaType:
                    // 4444 is assumed to be legacy premul.
                    return transform_scanline_4444;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        case kIndex_8_SkColorType:
        case kGray_8_SkColorType:
            return transform_scanline_memcpy;
        case kRGBA_F16_SkColorType:
            switch (info.alphaType()) {
                case kOpaque_SkAlphaType:
                case kUnpremul_SkAlphaType:
                    return transform_scanline_F16;
                case kPremul_SkAlphaType:
                    return transform_scanline_F16_premul;
                default:
                    SkASSERT(false);
                    return nullptr;
            }
        default:
            SkASSERT(false);
            return nullptr;
    }
}

/*  Pack palette[] with the corresponding colors, and if the image has alpha, also
    pack trans[] and return the number of alphas[] entries written. If the image is
    opaque, the return value will always be 0.
*/
static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
                               png_byte* SK_RESTRICT alphas, const SkImageInfo& info) {
    const SkPMColor* colors = ctable->readColors();
    const int count = ctable->count();
    SkPMColor storage[256];
    if (kPremul_SkAlphaType == info.alphaType()) {
        // Unpremultiply the colors.
        const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType);
        transform_scanline_proc proc = choose_proc(rgbaInfo);
        proc((char*) storage, (const char*) colors, ctable->count(), 4, nullptr);
        colors = storage;
    }

    int numWithAlpha = 0;
    if (kOpaque_SkAlphaType != info.alphaType()) {
        // PNG requires that all non-opaque colors come first in the palette.  Write these first.
        for (int i = 0; i < count; i++) {
            uint8_t alpha = SkGetPackedA32(colors[i]);
            if (0xFF != alpha) {
                alphas[numWithAlpha] = alpha;
                palette[numWithAlpha].red   = SkGetPackedR32(colors[i]);
                palette[numWithAlpha].green = SkGetPackedG32(colors[i]);
                palette[numWithAlpha].blue  = SkGetPackedB32(colors[i]);
                numWithAlpha++;
            }
        }

    }

    if (0 == numWithAlpha) {
        // All of the entries are opaque.
        for (int i = 0; i < count; i++) {
            SkPMColor c = *colors++;
            palette[i].red   = SkGetPackedR32(c);
            palette[i].green = SkGetPackedG32(c);
            palette[i].blue  = SkGetPackedB32(c);
        }
    } else {
        // We have already written the non-opaque colors.  Now just write the opaque colors.
        int currIndex = numWithAlpha;
        int i = 0;
        while (currIndex != count) {
            uint8_t alpha = SkGetPackedA32(colors[i]);
            if (0xFF == alpha) {
                palette[currIndex].red   = SkGetPackedR32(colors[i]);
                palette[currIndex].green = SkGetPackedG32(colors[i]);
                palette[currIndex].blue  = SkGetPackedB32(colors[i]);
                currIndex++;
            }

            i++;
        }
    }

    return numWithAlpha;
}

static bool do_encode(SkWStream*, const SkPixmap&, int, int, png_color_8&);

bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& src, const SkEncodeOptions& opts) {
    SkASSERT(!src.colorSpace() || src.colorSpace()->gammaCloseToSRGB() ||
             src.colorSpace()->gammaIsLinear());

    SkPixmap pixmap = src;
    if (SkEncodeOptions::ColorBehavior::kLegacy == opts.fColorBehavior) {
        pixmap.setColorSpace(nullptr);
    } else {
        if (!pixmap.colorSpace()) {
            return false;
        }
    }

    if (!pixmap.addr() || pixmap.info().isEmpty()) {
        return false;
    }

    const SkColorType colorType = pixmap.colorType();
    const SkAlphaType alphaType = pixmap.alphaType();
    switch (alphaType) {
        case kUnpremul_SkAlphaType:
            if (kARGB_4444_SkColorType == colorType) {
                return false;
            }

            break;
        case kOpaque_SkAlphaType:
        case kPremul_SkAlphaType:
            break;
        default:
            return false;
    }

    const bool isOpaque = (kOpaque_SkAlphaType == alphaType);
    int bitDepth = 8;
    png_color_8 sig_bit;
    sk_bzero(&sig_bit, sizeof(png_color_8));
    int pngColorType;
    switch (colorType) {
        case kRGBA_F16_SkColorType:
            if (!pixmap.colorSpace() || !pixmap.colorSpace()->gammaIsLinear()) {
                return false;
            }

            sig_bit.red = 16;
            sig_bit.green = 16;
            sig_bit.blue = 16;
            sig_bit.alpha = 16;
            bitDepth = 16;
            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
            break;
        case kIndex_8_SkColorType:
            sig_bit.red = 8;
            sig_bit.green = 8;
            sig_bit.blue = 8;
            sig_bit.alpha = 8;
            pngColorType = PNG_COLOR_TYPE_PALETTE;
            break;
        case kGray_8_SkColorType:
            sig_bit.gray = 8;
            pngColorType = PNG_COLOR_TYPE_GRAY;
            SkASSERT(isOpaque);
            break;
        case kRGBA_8888_SkColorType:
        case kBGRA_8888_SkColorType:
            sig_bit.red = 8;
            sig_bit.green = 8;
            sig_bit.blue = 8;
            sig_bit.alpha = 8;
            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
            break;
        case kARGB_4444_SkColorType:
            sig_bit.red = 4;
            sig_bit.green = 4;
            sig_bit.blue = 4;
            sig_bit.alpha = 4;
            pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
            break;
        case kRGB_565_SkColorType:
            sig_bit.red = 5;
            sig_bit.green = 6;
            sig_bit.blue = 5;
            pngColorType = PNG_COLOR_TYPE_RGB;
            SkASSERT(isOpaque);
            break;
        default:
            return false;
    }

    if (kIndex_8_SkColorType == colorType) {
        SkColorTable* ctable = pixmap.ctable();
        if (!ctable || ctable->count() == 0) {
            return false;
        }

        // Currently, we always use 8-bit indices for paletted pngs.
        // When ctable->count() <= 16, we could potentially use 1, 2,
        // or 4 bit indices.
    }

    return do_encode(stream, pixmap, pngColorType, bitDepth, sig_bit);
}

static int num_components(int pngColorType) {
    switch (pngColorType) {
        case PNG_COLOR_TYPE_PALETTE:
        case PNG_COLOR_TYPE_GRAY:
            return 1;
        case PNG_COLOR_TYPE_RGB:
            return 3;
        case PNG_COLOR_TYPE_RGBA:
            return 4;
        default:
            SkASSERT(false);
            return 0;
    }
}

static bool do_encode(SkWStream* stream, const SkPixmap& pixmap,
                      int pngColorType, int bitDepth, png_color_8& sig_bit) {
    png_structp png_ptr;
    png_infop info_ptr;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
    if (nullptr == png_ptr) {
        return false;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (nullptr == info_ptr) {
        png_destroy_write_struct(&png_ptr,  nullptr);
        return false;
    }

    /* Set error handling.  REQUIRED if you aren't supplying your own
    * error handling functions in the png_create_write_struct() call.
    */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return false;
    }

    png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, nullptr);

    /* Set the image information here.  Width and height are up to 2^31,
    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
    */

    png_set_IHDR(png_ptr, info_ptr, pixmap.width(), pixmap.height(),
                 bitDepth, pngColorType,
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                 PNG_FILTER_TYPE_BASE);

    // set our colortable/trans arrays if needed
    png_color paletteColors[256];
    png_byte trans[256];
    if (kIndex_8_SkColorType == pixmap.colorType()) {
        SkColorTable* colorTable = pixmap.ctable();
        SkASSERT(colorTable);
        int numTrans = pack_palette(colorTable, paletteColors, trans, pixmap.info());
        png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count());
        if (numTrans > 0) {
            png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr);
        }
    }

    if (pixmap.colorSpace()) {
        SkColorSpaceTransferFn fn;
        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
        if (pixmap.colorSpace()->isSRGB()) {
            png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
        } else if (pixmap.colorSpace()->isNumericalTransferFn(&fn) &&
                   pixmap.colorSpace()->toXYZD50(&toXYZD50))
        {
            set_icc(png_ptr, info_ptr, fn, toXYZD50);
        }

        // TODO: Should we support writing ICC profiles for additional color spaces?
    }

    png_set_sBIT(png_ptr, info_ptr, &sig_bit);
    png_write_info(png_ptr, info_ptr);
    int pngBytesPerPixel = num_components(pngColorType) * (bitDepth / 8);
    if (kRGBA_F16_SkColorType == pixmap.colorType() && kOpaque_SkAlphaType == pixmap.alphaType()) {
        // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
        // to skip the alpha channel.
        png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
        pngBytesPerPixel = 8;
    }

    SkAutoSTMalloc<1024, char> rowStorage(pixmap.width() * pngBytesPerPixel);
    char* storage = rowStorage.get();
    const char* srcImage = (const char*)pixmap.addr();
    transform_scanline_proc proc = choose_proc(pixmap.info());
    for (int y = 0; y < pixmap.height(); y++) {
        png_bytep row_ptr = (png_bytep)storage;
        proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(pixmap.colorType()),
             nullptr);
        png_write_rows(png_ptr, &row_ptr, 1);
        srcImage += pixmap.rowBytes();
    }

    png_write_end(png_ptr, info_ptr);

    /* clean up after the write, and free any memory allocated */
    png_destroy_write_struct(&png_ptr, &info_ptr);
    return true;
}

#endif
<|endoftext|>


<commit_before>#include <vector>
#include <cmath>

#include "ingameelements/character.h"
#include "gamestate.h"
#include "ingameelements/movingentity.h"
#include "ingameelements/player.h"
#include "global_constants.h"
#include "engine.h"
#include "ingameelements/weapon.h"

Character::Character(uint64_t id_, Gamestate *state, EntityPtr owner_, CharacterChildParameters parameters) : MovingEntity(id_, state),
            owner(owner_), weapon(parameters.weapon), hp(parameters.maxhp), hpdir(-1), isflipped(false), runanim(parameters.characterfolder+"run/"),
            crouchanim(parameters.characterfolder+"crouchwalk/"), pressed_keys(), held_keys(), lastdirectionpressed(0)
{
    acceleration = 300;
    runpower = parameters.runpower;
    // friction factor per second of null movement; calculated directly from Gang Garrison 2
    // from pygg2
    friction = 0.01510305449388463132584804061124;
}

void Character::setinput(Gamestate *state, INPUT_CONTAINER pressed_keys_, INPUT_CONTAINER held_keys_, double mouse_x_, double mouse_y_)
{
    pressed_keys = pressed_keys_;
    held_keys = held_keys_;
    mouse_x = mouse_x_;
    mouse_y = mouse_y_;
    Weapon *w = state->get<Weapon>(weapon);
    w->setaim(mouse_x, mouse_y);
}

void Character::beginstep(Gamestate *state, double frametime)
{
    state->get<Weapon>(weapon)->beginstep(state, frametime);
}

void Character::midstep(Gamestate *state, double frametime)
{
    if (cangetinput(state))
    {
        double maxhspeed;
        if (crouchanim.active())
        {
            maxhspeed = 60.0;
        }
        else
        {
            maxhspeed = 153.0;
        }

        if (pressed_keys.LEFT)
        {
            lastdirectionpressed = LEFT;
        }
        else if (lastdirectionpressed == LEFT and not held_keys.LEFT)
        {
            lastdirectionpressed = RIGHT*held_keys.RIGHT;
        }
        if (pressed_keys.RIGHT)
        {
            lastdirectionpressed = RIGHT;
        }
        else if (lastdirectionpressed == RIGHT and not held_keys.RIGHT)
        {
            lastdirectionpressed = LEFT*held_keys.LEFT;
        }

        if (lastdirectionpressed == LEFT)
        {
            hspeed = std::max(hspeed - acceleration * runpower * frametime, -maxhspeed);
        }
        else if (lastdirectionpressed == RIGHT)
        {
            hspeed = std::min(hspeed + acceleration * runpower * frametime, maxhspeed);
        }

        if (pressed_keys.JUMP)
        {
            if (onground(state))
            {
                vspeed = -240.0;
            }
        }
        if (held_keys.CROUCH)
        {
            if (not crouchanim.active())
            {
                crouchanim.active(true);
                crouchanim.reset();
            }
        }
        else if (crouchanim.active())
        {
            // We're crouched and we'd like to uncrouch
            // Do so only if we have the room
            if (not state->currentmap->collides(getstandingcollisionrect(state)))
            {
                crouchanim.active(false);
            }
        }

        if (held_keys.RELOAD)
        {
            Weapon *w = state->get<Weapon>(weapon);
            w->reload(state);
        }
        // Shooting
        if (held_keys.PRIMARY_FIRE and state->engine->isserver)
        {
            Weapon *w = state->get<Weapon>(weapon);
            w->fireprimary(state);
        }
        if (held_keys.SECONDARY_FIRE and state->engine->isserver)
        {
            Weapon *w = state->get<Weapon>(weapon);
            w->firesecondary(state);
        }

        if (isflipped != (mouse_x < 0))
        {
            // Spinjumping (compensate for later gravity)
            vspeed -= 540.0*frametime*3.0/4.0;
            isflipped = (mouse_x < 0);
        }
    }

    damage(20*frametime);
    if (hp.total() <= 0)
    {
        hp = getmaxhp();
    }

    // Gravity
    vspeed += 540.0*frametime;

    // apply friction
    hspeed *= std::pow(friction, frametime);

    state->get<Weapon>(weapon)->midstep(state, frametime);
}

void Character::endstep(Gamestate *state, double frametime)
{
    MovingEntity::endstep(state, frametime);

    // Collision with wallmask
    if (state->currentmap->collides(getcollisionrect(state)))
    {
        // We collide, do collision handling
        double hs = hspeed*frametime, vs = vspeed*frametime;
        // Buffers for "undone" horizontal/vertical movement
        double xbuffer = 0.0, ybuffer = 0.0;
        int steps = std::ceil(std::fmax(std::abs(hs), std::abs(vs)));
        double xstep = hs/steps, ystep = vs/steps;
        // Pull us out of the wall
        for (int i=0; i<steps; ++i)
        {
            x -= xstep; y -= ystep;
            xbuffer += xstep; ybuffer += ystep;
            if (not state->currentmap->collides(getcollisionrect(state)))
            {
                break;
            }
        }
        // We're at the point where the character touched the wallmask for the first time
        // Now keep moving one unit in either direction until all possible movement is exhausted
        bool xblocked = false, yblocked = false;
        bool xfinished = false, yfinished = false;
        double oldxbuffer = xbuffer, oldybuffer = ybuffer;
        while (not xfinished or not yfinished)
        {
            oldxbuffer = xbuffer; oldybuffer = ybuffer;
            // Try first moving horizontally
            if (not xfinished)
            {
                if (not state->currentmap->collides(getcollisionrect(state).offset(xstep, 0)))
                {
                    x += xstep;
                    xbuffer -= xstep;
                    if (xbuffer == 0.0 or std::signbit(xbuffer) != std::signbit(oldxbuffer))
                    {
                        // We're done for this coordinate
                        xfinished = true;
                    }
                }
                else
                {
                    // There's the possibility we might have walked into a staircase
                    if (not state->currentmap->collides(getcollisionrect(state).offset(xstep, STAIRCASE_STEPSIZE)))
                    {
                        // Indeed we did. Move up
                        x += xstep;
                        y += STAIRCASE_STEPSIZE;
                        xbuffer -= xstep;
                        if (xbuffer == 0.0 or std::signbit(xbuffer) != std::signbit(oldxbuffer))
                        {
                            // We're done for this coordinate
                            xfinished = true;
                        }
                    }
                    else
                    {
                        // No more moving, we're blocked
                        xblocked = true;
                        xfinished = true;
                    }
                }
            }
            // Do the same vertically, but without stair code
            if (not yfinished)
            {
                if (not state->currentmap->collides(getcollisionrect(state).offset(0, ystep)))
                {
                    // We cam move through
                    y += ystep;
                    ybuffer -= ystep;
                    if (ybuffer == 0.0 or std::signbit(ybuffer) != std::signbit(oldybuffer))
                    {
                        // We're done for this coordinate
                        yfinished = true;
                    }
                }
                else
                {
                    // No more moving, we're blocked
                    yblocked = true;
                    yfinished = true;
                }
            }
        }
        // Set hspeed and vspeed to what they should be
        if (xblocked)
        {
            hspeed = 0;
        }
        if (yblocked)
        {
            vspeed = 0;
        }
        if (state->currentmap->collides(getcollisionrect(state)))
        {
            printf("\nError: Still in wallmask after collision handling.");
        }
    } // end collision with wallmask

    // Stick to stairs when going down
    if (std::fabs(vspeed*frametime) <= 3.0 and not std::signbit(vspeed*frametime) and not onground(state))
    {
        Rect r = getcollisionrect(state);
        int stepsize = (-STAIRCASE_STEPSIZE)*std::ceil(std::fabs(hspeed)*frametime/(-STAIRCASE_STEPSIZE));
        if (state->currentmap->collides(Rect(r.x, r.y+r.h+stepsize, r.w, 1)))
        {
            for (int i=0; i<stepsize; ++i)
            {
                ++y;
                if (onground(state))
                {
                    break;
                }
            }
        }
    }

    // Running animation
    if (onground(state))
    {
        if (isflipped)
        {
            runanim.update(state, -hspeed*frametime);
            crouchanim.update(state, -hspeed*frametime);
        }
        else
        {
            runanim.update(state, hspeed*frametime);
            crouchanim.update(state, hspeed*frametime);
        }
    }
    if (hspeed == 0.0)
    {
        bool run=runanim.active(), crouch=crouchanim.active();
        runanim.reset();
        runanim.active(run);
        crouchanim.reset();
        crouchanim.active(crouch);
    }

    state->get<Weapon>(weapon)->endstep(state, frametime);
}

bool Character::onground(Gamestate *state)
{
    Rect r = getcollisionrect(state);
    return state->currentmap->collides(Rect(r.x, r.y+r.h, r.w, 1));
}

void Character::interpolate(Entity *prev_entity, Entity *next_entity, double alpha)
{
    MovingEntity::interpolate(prev_entity, next_entity, alpha);

    Character *prev_e = static_cast<Character*>(prev_entity);
    Character *next_e = static_cast<Character*>(next_entity);

    if (alpha < 0.5)
    {
        held_keys = prev_e->held_keys;
        pressed_keys = prev_e->pressed_keys;
        crouchanim.active(prev_e->crouchanim.active());
    }
    else
    {
        held_keys = next_e->held_keys;
        pressed_keys = next_e->pressed_keys;
        crouchanim.active(prev_e->crouchanim.active());
    }
    mouse_x = prev_e->mouse_x + alpha*(next_e->mouse_x - prev_e->mouse_x);
    mouse_y = prev_e->mouse_y + alpha*(next_e->mouse_y - prev_e->mouse_y);
    runanim.interpolate(&(prev_e->runanim), &(next_e->runanim), alpha);
    hp.normal = prev_e->hp.normal + alpha*(next_e->hp.normal - prev_e->hp.normal);
    hp.armor = prev_e->hp.armor + alpha*(next_e->hp.armor - prev_e->hp.armor);
    hp.shields = prev_e->hp.shields + alpha*(next_e->hp.shields - prev_e->hp.shields);
}

void Character::serialize(Gamestate *state, WriteBuffer *buffer, bool fullupdate)
{
    MovingEntity::serialize(state, buffer, fullupdate);

    buffer->write<float>(hp.normal);
    buffer->write<float>(hp.armor);
    buffer->write<float>(hp.shields);

    pressed_keys.serialize(buffer);
    held_keys.serialize(buffer);
    buffer->write<float>(mouse_x);
    buffer->write<float>(mouse_y);

    Weapon *w = state->get<Weapon>(weapon);
    w->serialize(state, buffer, fullupdate);
}

void Character::deserialize(Gamestate *state, ReadBuffer *buffer, bool fullupdate)
{
    MovingEntity::deserialize(state, buffer, fullupdate);

    hp.normal = buffer->read<float>();
    hp.armor = buffer->read<float>();
    hp.shields = buffer->read<float>();

    pressed_keys.deserialize(buffer);
    held_keys.deserialize(buffer);
    mouse_x = buffer->read<float>();
    mouse_y = buffer->read<float>();

    Weapon *w = state->get<Weapon>(weapon);
    w->deserialize(state, buffer, fullupdate);
}

void Character::damage(double amount)
{
    if (hp.shields < amount)
    {
        amount -= hp.shields;
        hp.shields = 0;
        if (hp.armor > 0)
        {
            amount -= std::min(amount/2.0, 5.0);
        }
        if (hp.armor < amount)
        {
            amount -= hp.armor;
            hp.armor = 0;
            hp.normal = std::max(0.0, hp.normal-amount);
        }
        else
        {
            hp.armor -= amount;
        }
    }
    else
    {
        hp.shields -= amount;
    }
}

void Character::destroy(Gamestate *state)
{
    state->get<Weapon>(weapon)->destroy(state);
}
<commit_msg>Health doesn't need sending that much data.<commit_after>#include <vector>
#include <cmath>

#include "ingameelements/character.h"
#include "gamestate.h"
#include "ingameelements/movingentity.h"
#include "ingameelements/player.h"
#include "global_constants.h"
#include "engine.h"
#include "ingameelements/weapon.h"

Character::Character(uint64_t id_, Gamestate *state, EntityPtr owner_, CharacterChildParameters parameters) : MovingEntity(id_, state),
            owner(owner_), weapon(parameters.weapon), hp(parameters.maxhp), hpdir(-1), isflipped(false), runanim(parameters.characterfolder+"run/"),
            crouchanim(parameters.characterfolder+"crouchwalk/"), pressed_keys(), held_keys(), lastdirectionpressed(0)
{
    acceleration = 300;
    runpower = parameters.runpower;
    // friction factor per second of null movement; calculated directly from Gang Garrison 2
    // from pygg2
    friction = 0.01510305449388463132584804061124;
}

void Character::setinput(Gamestate *state, INPUT_CONTAINER pressed_keys_, INPUT_CONTAINER held_keys_, double mouse_x_, double mouse_y_)
{
    pressed_keys = pressed_keys_;
    held_keys = held_keys_;
    mouse_x = mouse_x_;
    mouse_y = mouse_y_;
    Weapon *w = state->get<Weapon>(weapon);
    w->setaim(mouse_x, mouse_y);
}

void Character::beginstep(Gamestate *state, double frametime)
{
    state->get<Weapon>(weapon)->beginstep(state, frametime);
}

void Character::midstep(Gamestate *state, double frametime)
{
    if (cangetinput(state))
    {
        double maxhspeed;
        if (crouchanim.active())
        {
            maxhspeed = 60.0;
        }
        else
        {
            maxhspeed = 153.0;
        }

        if (pressed_keys.LEFT)
        {
            lastdirectionpressed = LEFT;
        }
        else if (lastdirectionpressed == LEFT and not held_keys.LEFT)
        {
            lastdirectionpressed = RIGHT*held_keys.RIGHT;
        }
        if (pressed_keys.RIGHT)
        {
            lastdirectionpressed = RIGHT;
        }
        else if (lastdirectionpressed == RIGHT and not held_keys.RIGHT)
        {
            lastdirectionpressed = LEFT*held_keys.LEFT;
        }

        if (lastdirectionpressed == LEFT)
        {
            hspeed = std::max(hspeed - acceleration * runpower * frametime, -maxhspeed);
        }
        else if (lastdirectionpressed == RIGHT)
        {
            hspeed = std::min(hspeed + acceleration * runpower * frametime, maxhspeed);
        }

        if (pressed_keys.JUMP)
        {
            if (onground(state))
            {
                vspeed = -240.0;
            }
        }
        if (held_keys.CROUCH)
        {
            if (not crouchanim.active())
            {
                crouchanim.active(true);
                crouchanim.reset();
            }
        }
        else if (crouchanim.active())
        {
            // We're crouched and we'd like to uncrouch
            // Do so only if we have the room
            if (not state->currentmap->collides(getstandingcollisionrect(state)))
            {
                crouchanim.active(false);
            }
        }

        if (held_keys.RELOAD)
        {
            Weapon *w = state->get<Weapon>(weapon);
            w->reload(state);
        }
        // Shooting
        if (held_keys.PRIMARY_FIRE and state->engine->isserver)
        {
            Weapon *w = state->get<Weapon>(weapon);
            w->fireprimary(state);
        }
        if (held_keys.SECONDARY_FIRE and state->engine->isserver)
        {
            Weapon *w = state->get<Weapon>(weapon);
            w->firesecondary(state);
        }

        if (isflipped != (mouse_x < 0))
        {
            // Spinjumping (compensate for later gravity)
            vspeed -= 540.0*frametime*3.0/4.0;
            isflipped = (mouse_x < 0);
        }
    }

    damage(20*frametime);
    if (hp.total() <= 0)
    {
        hp = getmaxhp();
    }

    // Gravity
    vspeed += 540.0*frametime;

    // apply friction
    hspeed *= std::pow(friction, frametime);

    state->get<Weapon>(weapon)->midstep(state, frametime);
}

void Character::endstep(Gamestate *state, double frametime)
{
    MovingEntity::endstep(state, frametime);

    // Collision with wallmask
    if (state->currentmap->collides(getcollisionrect(state)))
    {
        // We collide, do collision handling
        double hs = hspeed*frametime, vs = vspeed*frametime;
        // Buffers for "undone" horizontal/vertical movement
        double xbuffer = 0.0, ybuffer = 0.0;
        int steps = std::ceil(std::fmax(std::abs(hs), std::abs(vs)));
        double xstep = hs/steps, ystep = vs/steps;
        // Pull us out of the wall
        for (int i=0; i<steps; ++i)
        {
            x -= xstep; y -= ystep;
            xbuffer += xstep; ybuffer += ystep;
            if (not state->currentmap->collides(getcollisionrect(state)))
            {
                break;
            }
        }
        // We're at the point where the character touched the wallmask for the first time
        // Now keep moving one unit in either direction until all possible movement is exhausted
        bool xblocked = false, yblocked = false;
        bool xfinished = false, yfinished = false;
        double oldxbuffer = xbuffer, oldybuffer = ybuffer;
        while (not xfinished or not yfinished)
        {
            oldxbuffer = xbuffer; oldybuffer = ybuffer;
            // Try first moving horizontally
            if (not xfinished)
            {
                if (not state->currentmap->collides(getcollisionrect(state).offset(xstep, 0)))
                {
                    x += xstep;
                    xbuffer -= xstep;
                    if (xbuffer == 0.0 or std::signbit(xbuffer) != std::signbit(oldxbuffer))
                    {
                        // We're done for this coordinate
                        xfinished = true;
                    }
                }
                else
                {
                    // There's the possibility we might have walked into a staircase
                    if (not state->currentmap->collides(getcollisionrect(state).offset(xstep, STAIRCASE_STEPSIZE)))
                    {
                        // Indeed we did. Move up
                        x += xstep;
                        y += STAIRCASE_STEPSIZE;
                        xbuffer -= xstep;
                        if (xbuffer == 0.0 or std::signbit(xbuffer) != std::signbit(oldxbuffer))
                        {
                            // We're done for this coordinate
                            xfinished = true;
                        }
                    }
                    else
                    {
                        // No more moving, we're blocked
                        xblocked = true;
                        xfinished = true;
                    }
                }
            }
            // Do the same vertically, but without stair code
            if (not yfinished)
            {
                if (not state->currentmap->collides(getcollisionrect(state).offset(0, ystep)))
                {
                    // We cam move through
                    y += ystep;
                    ybuffer -= ystep;
                    if (ybuffer == 0.0 or std::signbit(ybuffer) != std::signbit(oldybuffer))
                    {
                        // We're done for this coordinate
                        yfinished = true;
                    }
                }
                else
                {
                    // No more moving, we're blocked
                    yblocked = true;
                    yfinished = true;
                }
            }
        }
        // Set hspeed and vspeed to what they should be
        if (xblocked)
        {
            hspeed = 0;
        }
        if (yblocked)
        {
            vspeed = 0;
        }
        if (state->currentmap->collides(getcollisionrect(state)))
        {
            printf("\nError: Still in wallmask after collision handling.");
        }
    } // end collision with wallmask

    // Stick to stairs when going down
    if (std::fabs(vspeed*frametime) <= 3.0 and not std::signbit(vspeed*frametime) and not onground(state))
    {
        Rect r = getcollisionrect(state);
        int stepsize = (-STAIRCASE_STEPSIZE)*std::ceil(std::fabs(hspeed)*frametime/(-STAIRCASE_STEPSIZE));
        if (state->currentmap->collides(Rect(r.x, r.y+r.h+stepsize, r.w, 1)))
        {
            for (int i=0; i<stepsize; ++i)
            {
                ++y;
                if (onground(state))
                {
                    break;
                }
            }
        }
    }

    // Running animation
    if (onground(state))
    {
        if (isflipped)
        {
            runanim.update(state, -hspeed*frametime);
            crouchanim.update(state, -hspeed*frametime);
        }
        else
        {
            runanim.update(state, hspeed*frametime);
            crouchanim.update(state, hspeed*frametime);
        }
    }
    if (hspeed == 0.0)
    {
        bool run=runanim.active(), crouch=crouchanim.active();
        runanim.reset();
        runanim.active(run);
        crouchanim.reset();
        crouchanim.active(crouch);
    }

    state->get<Weapon>(weapon)->endstep(state, frametime);
}

bool Character::onground(Gamestate *state)
{
    Rect r = getcollisionrect(state);
    return state->currentmap->collides(Rect(r.x, r.y+r.h, r.w, 1));
}

void Character::interpolate(Entity *prev_entity, Entity *next_entity, double alpha)
{
    MovingEntity::interpolate(prev_entity, next_entity, alpha);

    Character *prev_e = static_cast<Character*>(prev_entity);
    Character *next_e = static_cast<Character*>(next_entity);

    if (alpha < 0.5)
    {
        held_keys = prev_e->held_keys;
        pressed_keys = prev_e->pressed_keys;
        crouchanim.active(prev_e->crouchanim.active());
    }
    else
    {
        held_keys = next_e->held_keys;
        pressed_keys = next_e->pressed_keys;
        crouchanim.active(prev_e->crouchanim.active());
    }
    mouse_x = prev_e->mouse_x + alpha*(next_e->mouse_x - prev_e->mouse_x);
    mouse_y = prev_e->mouse_y + alpha*(next_e->mouse_y - prev_e->mouse_y);
    runanim.interpolate(&(prev_e->runanim), &(next_e->runanim), alpha);
    hp.normal = prev_e->hp.normal + alpha*(next_e->hp.normal - prev_e->hp.normal);
    hp.armor = prev_e->hp.armor + alpha*(next_e->hp.armor - prev_e->hp.armor);
    hp.shields = prev_e->hp.shields + alpha*(next_e->hp.shields - prev_e->hp.shields);
}

void Character::serialize(Gamestate *state, WriteBuffer *buffer, bool fullupdate)
{
    MovingEntity::serialize(state, buffer, fullupdate);

    buffer->write<uint16_t>(hp.normal);
    buffer->write<uint16_t>(hp.armor);
    buffer->write<uint16_t>(hp.shields);

    pressed_keys.serialize(buffer);
    held_keys.serialize(buffer);
    buffer->write<float>(mouse_x);
    buffer->write<float>(mouse_y);

    Weapon *w = state->get<Weapon>(weapon);
    w->serialize(state, buffer, fullupdate);
}

void Character::deserialize(Gamestate *state, ReadBuffer *buffer, bool fullupdate)
{
    MovingEntity::deserialize(state, buffer, fullupdate);

    hp.normal = buffer->read<uint16_t>();
    hp.armor = buffer->read<uint16_t>();
    hp.shields = buffer->read<uint16_t>();

    pressed_keys.deserialize(buffer);
    held_keys.deserialize(buffer);
    mouse_x = buffer->read<float>();
    mouse_y = buffer->read<float>();

    Weapon *w = state->get<Weapon>(weapon);
    w->deserialize(state, buffer, fullupdate);
}

void Character::damage(double amount)
{
    if (hp.shields < amount)
    {
        amount -= hp.shields;
        hp.shields = 0;
        if (hp.armor > 0)
        {
            amount -= std::min(amount/2.0, 5.0);
        }
        if (hp.armor < amount)
        {
            amount -= hp.armor;
            hp.armor = 0;
            hp.normal = std::max(0.0, hp.normal-amount);
        }
        else
        {
            hp.armor -= amount;
        }
    }
    else
    {
        hp.shields -= amount;
    }
}

void Character::destroy(Gamestate *state)
{
    state->get<Weapon>(weapon)->destroy(state);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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.
 */

#ifndef BENG_PROXY_UNUSED_ISTREAM_PTR_HXX
#define BENG_PROXY_UNUSED_ISTREAM_PTR_HXX

#include <utility>

class Istream;

/**
 * This class holds a pointer to an unused #Istream and auto-closes
 * it.  It can be moved to other instances, until it is finally
 * "stolen" using Steal() to actually use it.
 */
class UnusedIstreamPtr {
    Istream *stream = nullptr;

public:
    UnusedIstreamPtr() = default;

    explicit UnusedIstreamPtr(Istream *_stream)
        :stream(_stream) {}

    UnusedIstreamPtr(UnusedIstreamPtr &&src)
        :stream(std::exchange(src.stream, nullptr)) {}

    ~UnusedIstreamPtr() {
        if (stream != nullptr)
            Close(*stream);
    }

    UnusedIstreamPtr &operator=(UnusedIstreamPtr &&src) {
        using std::swap;
        swap(stream, src.stream);
        return *this;
    }

    Istream *Steal() {
        return std::exchange(stream, nullptr);
    }

    void Clear() {
        auto *s = Steal();
        if (s != nullptr)
            Close(*s);
    }

private:
    static void Close(Istream &i);
};

#endif
<commit_msg>istream/UnusedPtr: add operator bool<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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.
 */

#ifndef BENG_PROXY_UNUSED_ISTREAM_PTR_HXX
#define BENG_PROXY_UNUSED_ISTREAM_PTR_HXX

#include <utility>

class Istream;

/**
 * This class holds a pointer to an unused #Istream and auto-closes
 * it.  It can be moved to other instances, until it is finally
 * "stolen" using Steal() to actually use it.
 */
class UnusedIstreamPtr {
    Istream *stream = nullptr;

public:
    UnusedIstreamPtr() = default;

    explicit UnusedIstreamPtr(Istream *_stream)
        :stream(_stream) {}

    UnusedIstreamPtr(UnusedIstreamPtr &&src)
        :stream(std::exchange(src.stream, nullptr)) {}

    ~UnusedIstreamPtr() {
        if (stream != nullptr)
            Close(*stream);
    }

    UnusedIstreamPtr &operator=(UnusedIstreamPtr &&src) {
        using std::swap;
        swap(stream, src.stream);
        return *this;
    }

    operator bool() const {
        return stream != nullptr;
    }

    Istream *Steal() {
        return std::exchange(stream, nullptr);
    }

    void Clear() {
        auto *s = Steal();
        if (s != nullptr)
            Close(*s);
    }

private:
    static void Close(Istream &i);
};

#endif
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2015 Marvin Sinister <marvin@sinister.io>
**
** This file is part of small-scope-qt.
**
** small-scope-qt 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 3 of the License, or
** (at your option) any later version.
**
** small-scope-qt 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 small-scope-qt.  If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

/****************************************************************************
icons:
start/stop: http://www.virtuallnk.com/
connect/disconnect: http://p.yusukekamiyamane.com/
****************************************************************************/

#include <QTimer>
#include <QTime>

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "settingsdialog.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        qDebug() << "Name        : " << info.portName();
    }

    settings = new SettingsDialog;
    aboutDialog = new About;
    serial = new QSerialPort(this);
    buffer = new unsigned char[Settings::bufferSize];
    memset(buffer, 0, Settings::bufferSize);
    bufferFilled = 0;

    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());

    //QTimer *timer = new QTimer(this);
    //connect(timer, SIGNAL(timeout()), this, SLOT(immaginaryData()));
    //timer->start(10000);

    cursorPosition = 0;
    measure1Position = 0;
    measure2Position = 0;

    connect(ui->renderArea, SIGNAL(mouseMoved(int)), this, SLOT(setValuesCursor(int)));
    connect(ui->renderArea, SIGNAL(lPositionChanged(int)), this, SLOT(setValues1(int)));
    connect(ui->renderArea, SIGNAL(rPositionChanged(int)), this, SLOT(setValues2(int)));
    connect(ui->renderArea, SIGNAL(fpsChanged(float)), this, SLOT(setTitle(float)));

    connect(this, SIGNAL(valuesChanged()), this, SLOT(recalculateValues()));

    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError)));

    connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));

    ui->renderArea->setMinimumSize(Settings::bufferSize,256*Settings::heightConstant);
    ui->renderArea->setMaximumSize(Settings::bufferSize,256*Settings::heightConstant);
    ui->renderArea->resize(Settings::bufferSize,256*Settings::heightConstant);

    resize(minimumWidth(), minimumHeight());
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::setPrescalerValue(int value) {
    value = (int)pow(2, value);
    Settings::prescaler = value;
    ui->sbPrescaler->setValue(value);
    QByteArray d;
    d = QByteArray("p",1);
    d.append(QByteArray::number(value, 10 ));
    writeData(d);
}

void MainWindow::setTriggerLevel(int value) {
    QByteArray d;
    d = QByteArray("t",1);
    d.append(QByteArray::number(value, 10 ));
    writeData(d);
    Settings::triggerLevel = value;
    ui->dsbThreshold->setValue(value*Settings::VPerUnit);
}

void MainWindow::setHoldoffValue (int value) {
    Settings::holdoff = value;
    QByteArray d;
    d = QByteArray("w",1);
    d.append(QByteArray::number(value, 10 ));
    writeData(d);
}

void MainWindow::recalculateValues()
{
    float voltageC = buffer[cursorPosition] * 0.02;
    ui->leVoltage->setText(QString::number(voltageC,'f',2));
    float voltage1 = buffer[measure1Position] * 0.02;
    ui->leVoltage1->setText(QString::number(voltage1,'f',2));
    float voltage2 = buffer[measure2Position] * 0.02;
    ui->leVoltage2->setText(QString::number(voltage2,'f',2));
    float timeC = ((cursorPosition- Settings::bufferSize/2)/Settings::prescalerConstant)*Settings::prescaler;
    ui->leTime->setText(QString::number(timeC,'f',2));
    float time1 = ((measure1Position - Settings::bufferSize/2)/Settings::prescalerConstant)*Settings::prescaler;
    ui->leTime1->setText(QString::number(time1,'f',2));
    float time2 = ((measure2Position - Settings::bufferSize/2)/Settings::prescalerConstant)*Settings::prescaler;
    ui->leTime2->setText(QString::number(time2,'f',2));

    ui->leDiffV21->setText(QString::number(fabs(voltage2 - voltage1),'f',2));
    ui->leDiffVc1->setText(QString::number(fabs(voltageC - voltage1),'f',2));
    ui->leDiffVc2->setText(QString::number(fabs(voltageC - voltage2),'f',2));
    ui->leDifft21->setText(QString::number(fabs(time2 - time1),'f',2));
    ui->leDifftc1->setText(QString::number(fabs(timeC - time1),'f',2));
    ui->leDifftc2->setText(QString::number(fabs(timeC - time2),'f',2));

    ui->dsbHoldoff->setValue(Settings::holdoff/Settings::prescalerConstant*Settings::prescaler);
    ui->dsbThreshold->setValue(Settings::triggerLevel*Settings::VPerUnit);

}

void MainWindow::setTriggerEventToggle() {
    writeData(QByteArray("e0",2));
}

void MainWindow::setTriggerEventFalling() {
    writeData(QByteArray("e2",2));
}

void MainWindow::setTriggerEventRising() {
    writeData(QByteArray("e3",2));
}

void MainWindow::setVRefAREF() {
    writeData(QByteArray("r0",2));
}

void MainWindow::setVRefAVCC() {
    writeData(QByteArray("r1",2));
}

void MainWindow::setVRefInternal() {
    writeData(QByteArray("r3",2));
}

void MainWindow::setValuesCursor(int value) {
    cursorPosition = value;
    emit recalculateValues();
}

void MainWindow::setValues1 (int value) {
    measure1Position = value;
    emit recalculateValues();
}

void MainWindow::setValues2 (int value) {
    measure2Position = value;
    emit recalculateValues();
}

void MainWindow::setTitle(float fps) {
    this->setWindowTitle (QString::number(fps));
}

void MainWindow::setupCOM() {
    settings->show();
}

void MainWindow::startSampling() {
    QByteArray d;
    d = QByteArray("s", 1);
    writeData(d);
}

void MainWindow::stopSampling(){
    QByteArray d;
    d = QByteArray("S", 1);
    writeData(d);
    bufferFilled = 0;
}

void MainWindow::connectCOM() {
    qDebug() << "Connecting to serial.";
    openSerialPort();
    if (serial->isOpen ()) {
        qDebug() << "Connected";
    }
    else {
        qDebug() << "Failed";
    }
}

void MainWindow::disconnectCOM() {
    closeSerialPort();
}
void MainWindow::status() {
    QByteArray d;
    d = QByteArray("d", 1);
    writeData(d);
}

void MainWindow::openSerialPort() {
    SettingsDialog::Settings p = settings->settings();
    serial->setPortName(p.name);
    if (serial->open(QIODevice::ReadWrite)) {
        if (serial->setBaudRate(p.baudRate)
                && serial->setDataBits(p.dataBits)
                && serial->setParity(p.parity)
                && serial->setStopBits(p.stopBits)
                && serial->setFlowControl(p.flowControl)) {        }
        else {
            serial->close();
            qDebug() << "Error" << serial->errorString();
        }
    }
    else {
        qDebug() << "Error" << serial->errorString();
    }
}

void MainWindow::closeSerialPort() {
    serial->close();
}

void MainWindow::about() {

}

void MainWindow::writeData(const QByteArray &data) {
    if (serial->isOpen ()) {
        serial->write(data);
    }
    qDebug() << "Writing to serial: " << data;
}

void MainWindow::readData() {
    QByteArray data = serial->readAll();
    if ((bufferFilled + data.size()) > Settings::bufferSize) {
        bufferFilled = 0;
    }
    for (int i = 0; i < data.size(); ++i) {
        buffer[bufferFilled + i] = data[i];
    }
    bufferFilled+=data.size();
    ui->renderArea->drawData(buffer);
}

/*void MainWindow::immaginaryData() {
    for (int i = 0; i < BUFFER_SIZE; ++i) {
        buffer[i] = (unsigned char)((qrand() % 256));
    }
    ui->renderArea->drawData(buffer);
}*/

void MainWindow::handleError(QSerialPort::SerialPortError error) {
    if (error == QSerialPort::ResourceError) {
        qDebug() << "Critical Error" << serial->errorString();
        closeSerialPort();
    }
}

void MainWindow::on_actionExit_triggered() {
    close();
}

void MainWindow::on_actionAbout_triggered() {
    aboutDialog->show();
}

void MainWindow::on_actionCOM_Port_Settings_triggered() {
    setupCOM();
}
<commit_msg>changed window title<commit_after>/****************************************************************************
**
** Copyright (C) 2015 Marvin Sinister <marvin@sinister.io>
**
** This file is part of small-scope-qt.
**
** small-scope-qt 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 3 of the License, or
** (at your option) any later version.
**
** small-scope-qt 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 small-scope-qt.  If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/

/****************************************************************************
icons:
start/stop: http://www.virtuallnk.com/
connect/disconnect: http://p.yusukekamiyamane.com/
****************************************************************************/

#include <QTimer>
#include <QTime>

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "settingsdialog.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        qDebug() << "Name        : " << info.portName();
    }

    settings = new SettingsDialog;
    aboutDialog = new About;
    serial = new QSerialPort(this);
    buffer = new unsigned char[Settings::bufferSize];
    memset(buffer, 0, Settings::bufferSize);
    bufferFilled = 0;

    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());

    //QTimer *timer = new QTimer(this);
    //connect(timer, SIGNAL(timeout()), this, SLOT(immaginaryData()));
    //timer->start(10000);

    cursorPosition = 0;
    measure1Position = 0;
    measure2Position = 0;

    connect(ui->renderArea, SIGNAL(mouseMoved(int)), this, SLOT(setValuesCursor(int)));
    connect(ui->renderArea, SIGNAL(lPositionChanged(int)), this, SLOT(setValues1(int)));
    connect(ui->renderArea, SIGNAL(rPositionChanged(int)), this, SLOT(setValues2(int)));
    connect(ui->renderArea, SIGNAL(fpsChanged(float)), this, SLOT(setTitle(float)));

    connect(this, SIGNAL(valuesChanged()), this, SLOT(recalculateValues()));

    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError)));

    connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));

    ui->renderArea->setMinimumSize(Settings::bufferSize,256*Settings::heightConstant);
    ui->renderArea->setMaximumSize(Settings::bufferSize,256*Settings::heightConstant);
    ui->renderArea->resize(Settings::bufferSize,256*Settings::heightConstant);

    resize(minimumWidth(), minimumHeight());
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::setPrescalerValue(int value) {
    value = (int)pow(2, value);
    Settings::prescaler = value;
    ui->sbPrescaler->setValue(value);
    QByteArray d;
    d = QByteArray("p",1);
    d.append(QByteArray::number(value, 10 ));
    writeData(d);
}

void MainWindow::setTriggerLevel(int value) {
    QByteArray d;
    d = QByteArray("t",1);
    d.append(QByteArray::number(value, 10 ));
    writeData(d);
    Settings::triggerLevel = value;
    ui->dsbThreshold->setValue(value*Settings::VPerUnit);
}

void MainWindow::setHoldoffValue (int value) {
    Settings::holdoff = value;
    QByteArray d;
    d = QByteArray("w",1);
    d.append(QByteArray::number(value, 10 ));
    writeData(d);
}

void MainWindow::recalculateValues()
{
    float voltageC = buffer[cursorPosition] * 0.02;
    ui->leVoltage->setText(QString::number(voltageC,'f',2));
    float voltage1 = buffer[measure1Position] * 0.02;
    ui->leVoltage1->setText(QString::number(voltage1,'f',2));
    float voltage2 = buffer[measure2Position] * 0.02;
    ui->leVoltage2->setText(QString::number(voltage2,'f',2));
    float timeC = ((cursorPosition- Settings::bufferSize/2)/Settings::prescalerConstant)*Settings::prescaler;
    ui->leTime->setText(QString::number(timeC,'f',2));
    float time1 = ((measure1Position - Settings::bufferSize/2)/Settings::prescalerConstant)*Settings::prescaler;
    ui->leTime1->setText(QString::number(time1,'f',2));
    float time2 = ((measure2Position - Settings::bufferSize/2)/Settings::prescalerConstant)*Settings::prescaler;
    ui->leTime2->setText(QString::number(time2,'f',2));

    ui->leDiffV21->setText(QString::number(fabs(voltage2 - voltage1),'f',2));
    ui->leDiffVc1->setText(QString::number(fabs(voltageC - voltage1),'f',2));
    ui->leDiffVc2->setText(QString::number(fabs(voltageC - voltage2),'f',2));
    ui->leDifft21->setText(QString::number(fabs(time2 - time1),'f',2));
    ui->leDifftc1->setText(QString::number(fabs(timeC - time1),'f',2));
    ui->leDifftc2->setText(QString::number(fabs(timeC - time2),'f',2));

    ui->dsbHoldoff->setValue(Settings::holdoff/Settings::prescalerConstant*Settings::prescaler);
    ui->dsbThreshold->setValue(Settings::triggerLevel*Settings::VPerUnit);

}

void MainWindow::setTriggerEventToggle() {
    writeData(QByteArray("e0",2));
}

void MainWindow::setTriggerEventFalling() {
    writeData(QByteArray("e2",2));
}

void MainWindow::setTriggerEventRising() {
    writeData(QByteArray("e3",2));
}

void MainWindow::setVRefAREF() {
    writeData(QByteArray("r0",2));
}

void MainWindow::setVRefAVCC() {
    writeData(QByteArray("r1",2));
}

void MainWindow::setVRefInternal() {
    writeData(QByteArray("r3",2));
}

void MainWindow::setValuesCursor(int value) {
    cursorPosition = value;
    emit recalculateValues();
}

void MainWindow::setValues1 (int value) {
    measure1Position = value;
    emit recalculateValues();
}

void MainWindow::setValues2 (int value) {
    measure2Position = value;
    emit recalculateValues();
}

void MainWindow::setTitle(float fps) {
    this->setWindowTitle ("small-scope-qt - " + QString::number(fps) + "fps");
}

void MainWindow::setupCOM() {
    settings->show();
}

void MainWindow::startSampling() {
    QByteArray d;
    d = QByteArray("s", 1);
    writeData(d);
}

void MainWindow::stopSampling(){
    QByteArray d;
    d = QByteArray("S", 1);
    writeData(d);
    bufferFilled = 0;
}

void MainWindow::connectCOM() {
    qDebug() << "Connecting to serial.";
    openSerialPort();
    if (serial->isOpen ()) {
        qDebug() << "Connected";
    }
    else {
        qDebug() << "Failed";
    }
}

void MainWindow::disconnectCOM() {
    closeSerialPort();
}
void MainWindow::status() {
    QByteArray d;
    d = QByteArray("d", 1);
    writeData(d);
}

void MainWindow::openSerialPort() {
    SettingsDialog::Settings p = settings->settings();
    serial->setPortName(p.name);
    if (serial->open(QIODevice::ReadWrite)) {
        if (serial->setBaudRate(p.baudRate)
                && serial->setDataBits(p.dataBits)
                && serial->setParity(p.parity)
                && serial->setStopBits(p.stopBits)
                && serial->setFlowControl(p.flowControl)) {        }
        else {
            serial->close();
            qDebug() << "Error" << serial->errorString();
        }
    }
    else {
        qDebug() << "Error" << serial->errorString();
    }
}

void MainWindow::closeSerialPort() {
    serial->close();
}

void MainWindow::about() {

}

void MainWindow::writeData(const QByteArray &data) {
    if (serial->isOpen ()) {
        serial->write(data);
    }
    qDebug() << "Writing to serial: " << data;
}

void MainWindow::readData() {
    QByteArray data = serial->readAll();
    if ((bufferFilled + data.size()) > Settings::bufferSize) {
        bufferFilled = 0;
    }
    for (int i = 0; i < data.size(); ++i) {
        buffer[bufferFilled + i] = data[i];
    }
    bufferFilled+=data.size();
    ui->renderArea->drawData(buffer);
}

/*void MainWindow::immaginaryData() {
    for (int i = 0; i < BUFFER_SIZE; ++i) {
        buffer[i] = (unsigned char)((qrand() % 256));
    }
    ui->renderArea->drawData(buffer);
}*/

void MainWindow::handleError(QSerialPort::SerialPortError error) {
    if (error == QSerialPort::ResourceError) {
        qDebug() << "Critical Error" << serial->errorString();
        closeSerialPort();
    }
}

void MainWindow::on_actionExit_triggered() {
    close();
}

void MainWindow::on_actionAbout_triggered() {
    aboutDialog->show();
}

void MainWindow::on_actionCOM_Port_Settings_triggered() {
    setupCOM();
}
<|endoftext|>