<commit_before>#include <ContextGDIPlus.h>

#include "utf8.h"

#include <cassert>

bool canvas::ContextGDIPlus::is_initialized = false;
ULONG_PTR canvas::ContextGDIPlus::m_gdiplusToken;

using namespace std;
using namespace canvas;

static std::wstring convert_to_wstring(const std::string & input) {
  const char * str = input.c_str();
  const char * str_i = str;
  const char * end = str + input.size();
  std::wstring output;
  while (str_i < end) {
    output += (wchar_t)utf8::next(str_i, end);
  }
  return output;
}

static void toGDIPath(const Path2D & path, Gdiplus::GraphicsPath & output, float display_scale) {
  output.StartFigure();
  Gdiplus::PointF current_pos;

  for (auto pc : path.getData()) {
    switch (pc.type) {
    case PathComponent::MOVE_TO:
      current_pos = Gdiplus::PointF(Gdiplus::REAL(pc.x0 * display_scale), Gdiplus::REAL(pc.y0 * display_scale));
      break;
    case PathComponent::LINE_TO:
      {
	Gdiplus::PointF point(Gdiplus::REAL(pc.x0 * display_scale), Gdiplus::REAL(pc.y0 * display_scale));
	output.AddLine(current_pos, point);
	current_pos = point;
      }
      break;
    case PathComponent::CLOSE:
      output.CloseFigure();
      break;
    case PathComponent::ARC:
      {
	double span = 0;
	if (0 && ((!pc.anticlockwise && (pc.ea - pc.sa >= 2 * M_PI)) || (pc.anticlockwise && (pc.sa - pc.ea >= 2 * M_PI)))) {
	  // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
	  // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
	  // circumference of this circle.
	  span = 2 * M_PI;
	} else {
	  if (!pc.anticlockwise && (pc.ea < pc.sa)) {
	    span += 2 * M_PI;
	  } else if (pc.anticlockwise && (pc.sa < pc.ea)) {
	    span -= 2 * M_PI;
	  }

#if 0
    // this is also due to switched coordinate system
    // we would end up with a 0 span instead of 360
    if (!(qFuzzyCompare(span + (ea - sa) + 1, 1.0) && qFuzzyCompare(qAbs(span), 360.0))) {
      // mod 360
      span += (ea - sa) - (static_cast<int>((ea - sa) / 360)) * 360;
    }
#else
	  span += pc.ea - pc.sa;
#endif
	}

#if 0
  // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
  // NOTE: QPainterPath::isEmpty() won't work here since it ignores a lone MoveToElement
  if (!m_path.elementCount())
    m_path.arcMoveTo(xs, ys, width, height, sa);
  else if (!radius) {
    m_path.lineTo(xc, yc);
    return;
  }
#endif

#if 0
  if (anticlockwise) {
    span = -M_PI / 2.0;
  } else {
    span = M_PI / 2.0;
  }
#endif
  Gdiplus::RectF rect(Gdiplus::REAL(pc.x0 * display_scale - pc.radius * display_scale), Gdiplus::REAL(pc.y0 * display_scale - pc.radius * display_scale), Gdiplus::REAL(2 * pc.radius * display_scale), Gdiplus::REAL(2 * pc.radius * display_scale));

	output.AddArc(rect, Gdiplus::REAL(pc.sa * 180.0f / M_PI), Gdiplus::REAL(span * 180.0f / M_PI));
	output.GetLastPoint(&current_pos);
      }
      break;
    }
  }
}

static Gdiplus::Color toGDIColor(const Color & input, float globalAlpha = 1.0f) {
  int red = int(input.red * 255), green = int(input.green * 255), blue = int(input.blue * 255), alpha = int(input.alpha * globalAlpha * 255);
  if (red < 0) red = 0;
  else if (red > 255) red = 255;
  if (green < 0) green = 0;
  else if (green > 255) green = 255;
  if (blue < 0) blue = 0;
  else if (blue > 255) blue = 255;
  if (alpha < 0) alpha = 0;
  else if (alpha > 255) alpha = 255;
#if 0
  return Gdiplus::Color::FromArgb(alpha, red, green, blue);
#else
  return Gdiplus::Color(alpha, red, green, blue);
#endif
}

GDIPlusSurface::GDIPlusSurface(const std::string & filename) : Surface(0, 0, 0, 0, false) {
  std::wstring tmp = convert_to_wstring(filename);
  bitmap = std::shared_ptr<Gdiplus::Bitmap>(Gdiplus::Bitmap::FromFile(tmp.data()));
  Surface::resize(bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetWidth(), bitmap->GetHeight(), true);
}

GDIPlusSurface::GDIPlusSurface(const unsigned char * buffer, size_t size) : Surface(0, 0, 0, 0, false) {
	assert(0);
}

void
GDIPlusSurface::renderPath(RenderMode mode, const Path2D & input_path, const Style & style, float lineWidth, Operator op, float display_scale, float globalAlpha) {
  initializeContext();

  switch (op) {
  case SOURCE_OVER:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);
    break;
  case COPY:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
    break;
  }

  Gdiplus::GraphicsPath path;
  toGDIPath(input_path, path, display_scale);

  switch (mode) {
  case STROKE:
    {
      Gdiplus::Pen pen(toGDIColor(style.color, globalAlpha), lineWidth);
      g->DrawPath(&pen, &path);
    }
    break;
  case FILL:
    if (style.getType() == Style::LINEAR_GRADIENT) {
      const std::map<float, Color> & colors = style.getColors();
      if (!colors.empty()) {
	std::map<float, Color>::const_iterator it0 = colors.begin(), it1 = colors.end();
	it1--;
	const Color & c0 = it0->second, c1 = it1->second;
	Gdiplus::LinearGradientBrush brush(Gdiplus::PointF(Gdiplus::REAL(style.x0), Gdiplus::REAL(style.y0)),
					   Gdiplus::PointF(Gdiplus::REAL(style.x1), Gdiplus::REAL(style.y1)),
					   toGDIColor(c0, globalAlpha),
					   toGDIColor(c1, globalAlpha));
	g->FillPath(&brush, &path);
      }
    } else {
      Gdiplus::SolidBrush brush(toGDIColor(style.color, globalAlpha));
      g->FillPath(&brush, &path);
    }
  }
}

void
GDIPlusSurface::clip(const Path2D & input_path, float display_scale) {
  initializeContext();

  Gdiplus::GraphicsPath path;
  toGDIPath(input_path, path, display_scale);

  Gdiplus::Region region(&path);
  g->SetClip(&region);
}

void
GDIPlusSurface::drawNativeSurface(GDIPlusSurface & img, const Point & p, double w, double h, float displayScale, float globalAlpha, bool imageSmoothingEnabled) {
  initializeContext();

  g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);

  if (imageSmoothingEnabled) {
    // g->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
    g->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear );
  } else {
    g->SetInterpolationMode( Gdiplus::InterpolationModeNearestNeighbor );
  }
  if (globalAlpha < 1.0f && 0) {
#if 0
    ImageAttributes  imageAttributes;
    ColorMatrix colorMatrix = {
      1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
      0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
      0.0f, 0.0f, 0.0f, alpha, 0.0f,
      0.0f, 0.0f, 0.0f, 0.0f, 1.0f};

    imageAttributes.SetColorMatrix( &colorMatrix,
				    ColorMatrixFlagsDefault,
				    ColorAdjustTypeBitmap);
    graphics.DrawImage( &(*(img.bitmap)),
			Gdiplus::Rect(p.x, p.y, w, h), // destination rectangle
			0, 0,        // upper-left corner of source rectangle
			getWidth(),       // width of source rectangle
			getHeight(),      // height of source rectangle
			Gdiplus::UnitPixel,
			&imageAttributes);
#endif
  } else if (img.getActualWidth() == (unsigned int)w && img.getActualHeight() == (unsigned int)h && 0) { // this scales image weirdly
    g->DrawImage(&(*(img.bitmap)), Gdiplus::REAL(p.x), Gdiplus::REAL(p.y));
  } else {
    g->DrawImage(&(*(img.bitmap)), Gdiplus::REAL(p.x), Gdiplus::REAL(p.y), Gdiplus::REAL(w), Gdiplus::REAL(h));
  }
}

void
GDIPlusSurface::drawImage(Surface & _img, const Point & p, double w, double h, gfloat displayScale, float globalAlpha, bool imageSmoothingEnabled) {
  GDIPlusSurface * img = dynamic_cast<GDIPlusSurface*>(&_img);
  if (img) {
    drawNativeSurface(*img, p, w, h, displayScale, globalAlpha, imageSmoothingEnabled);
  } else {
    auto img = _img.createImage();
    GDIPlusSurface cs(*img);
    drawNativeSurface(cs, p, w, h, displayScale, globalAlpha, imageSmoothingEnabled);
  }
}

void
GDIPlusSurface::renderText(RenderMode mode, const Font & font, const Style & style, TextBaseline textBaseline, TextAlign textAlign, const std::string & text, const Point & p, float lineWidth, Operator op, float display_scale, float globalAlpha) {
  initializeContext();

  double x = round(p.x);
  double y = round(p.y);

  switch (op) {
  case SOURCE_OVER:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);
    break;
  case COPY:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
    break;
  }

  if (font.cleartype) {
    g->SetTextRenderingHint(Gdiplus::TextRenderingHintClearTypeGridFit);
  } else if (font.antialiasing && font.hinting && 0) {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintAntiAliasGridFit );
  } else if (font.antialiasing) {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintAntiAlias );
  } else if (font.hinting) {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintSingleBitPerPixelGridFit );
  } else {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintSingleBitPerPixel );
  }

  std::wstring text2 = convert_to_wstring(text);
  int style_bits = 0;
  if (font.weight == Font::BOLD || font.weight == Font::BOLDER) {
    style_bits |= Gdiplus::FontStyleBold;
  }
  if (font.slant == Font::ITALIC) {
    style_bits |= Gdiplus::FontStyleItalic;
  }
  Gdiplus::Font gdifont(&Gdiplus::FontFamily(L"Arial"), font.size * display_scale, style_bits, Gdiplus::UnitPixel);

  Gdiplus::RectF rect(Gdiplus::REAL(x * display_scale), Gdiplus::REAL(y * display_scale), 0.0f, 0.0f);
  Gdiplus::StringFormat f;

  switch (textBaseline.getType()) {
  case TextBaseline::TOP: break;
  case TextBaseline::HANGING: break;
  case TextBaseline::MIDDLE: f.SetLineAlignment(Gdiplus::StringAlignmentCenter); break;
  case TextBaseline::BOTTOM: f.SetLineAlignment(Gdiplus::StringAlignmentFar);
  }

  switch (textAlign.getType()) {
  case TextAlign::CENTER: f.SetAlignment(Gdiplus::StringAlignmentCenter); break;
  case TextAlign::END: case TextAlign::RIGHT: f.SetAlignment(Gdiplus::StringAlignmentFar); break;
  case TextAlign::START: case TextAlign::LEFT: f.SetAlignment(Gdiplus::StringAlignmentNear); break;
  }

  f.SetFormatFlags(Gdiplus::StringFormatFlagsBypassGDI);

  switch (mode) {
  case STROKE:
    // implement
    break;
  case FILL:
    {
      Gdiplus::SolidBrush brush(toGDIColor(style.color, globalAlpha));
      g->DrawString(text2.data(), text2.size(), &gdifont, rect, &f, &brush);
    }
    break;
  }
}

TextMetrics
GDIPlusSurface::measureText(const Font & font, const std::string & text, float display_scale) {
  initializeContext();
  std::wstring text2 = convert_to_wstring(text);
  int style = 0;
  if (font.weight == Font::BOLD || font.weight == Font::BOLDER) {
    style |= Gdiplus::FontStyleBold;
  }
  if (font.slant == Font::ITALIC) {
    style |= Gdiplus::FontStyleItalic;
  }
  Gdiplus::Font gdi_font(&Gdiplus::FontFamily(L"Arial"), font.size * display_scale, style, Gdiplus::UnitPixel);
  Gdiplus::RectF layoutRect(0, 0, 512, 512), boundingBox;
  g->MeasureString(text2.data(), text2.size(), &gdi_font, layoutRect, &boundingBox);
  Gdiplus::SizeF size;
  boundingBox.GetSize(&size);

  float ascent = &Gdiplus::FontFamily(L"Arial")::GetCellAscent(style);
  float descent = &Gdiplus::FontFamily(L"Arial")::GetCellDescent(style);
  float baseline = 0;
  if (textBaseline == TextBaseline::MIDDLE) {
    baseline = (ascent + descent) / 2;
  } else if (textBaseline == TextBaseline::TOP) {
    baseline = (ascent + descent);
  }

  return TextMetrics(size.Width / display_scale, (descent - baseline) / dispaly_scale, (ascent - baseline) / display_scale);
}
<commit_msg>Fix typo<commit_after>#include <ContextGDIPlus.h>

#include "utf8.h"

#include <cassert>

bool canvas::ContextGDIPlus::is_initialized = false;
ULONG_PTR canvas::ContextGDIPlus::m_gdiplusToken;

using namespace std;
using namespace canvas;

static std::wstring convert_to_wstring(const std::string & input) {
  const char * str = input.c_str();
  const char * str_i = str;
  const char * end = str + input.size();
  std::wstring output;
  while (str_i < end) {
    output += (wchar_t)utf8::next(str_i, end);
  }
  return output;
}

static void toGDIPath(const Path2D & path, Gdiplus::GraphicsPath & output, float display_scale) {
  output.StartFigure();
  Gdiplus::PointF current_pos;

  for (auto pc : path.getData()) {
    switch (pc.type) {
    case PathComponent::MOVE_TO:
      current_pos = Gdiplus::PointF(Gdiplus::REAL(pc.x0 * display_scale), Gdiplus::REAL(pc.y0 * display_scale));
      break;
    case PathComponent::LINE_TO:
      {
	Gdiplus::PointF point(Gdiplus::REAL(pc.x0 * display_scale), Gdiplus::REAL(pc.y0 * display_scale));
	output.AddLine(current_pos, point);
	current_pos = point;
      }
      break;
    case PathComponent::CLOSE:
      output.CloseFigure();
      break;
    case PathComponent::ARC:
      {
	double span = 0;
	if (0 && ((!pc.anticlockwise && (pc.ea - pc.sa >= 2 * M_PI)) || (pc.anticlockwise && (pc.sa - pc.ea >= 2 * M_PI)))) {
	  // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
	  // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
	  // circumference of this circle.
	  span = 2 * M_PI;
	} else {
	  if (!pc.anticlockwise && (pc.ea < pc.sa)) {
	    span += 2 * M_PI;
	  } else if (pc.anticlockwise && (pc.sa < pc.ea)) {
	    span -= 2 * M_PI;
	  }

#if 0
    // this is also due to switched coordinate system
    // we would end up with a 0 span instead of 360
    if (!(qFuzzyCompare(span + (ea - sa) + 1, 1.0) && qFuzzyCompare(qAbs(span), 360.0))) {
      // mod 360
      span += (ea - sa) - (static_cast<int>((ea - sa) / 360)) * 360;
    }
#else
	  span += pc.ea - pc.sa;
#endif
	}

#if 0
  // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
  // NOTE: QPainterPath::isEmpty() won't work here since it ignores a lone MoveToElement
  if (!m_path.elementCount())
    m_path.arcMoveTo(xs, ys, width, height, sa);
  else if (!radius) {
    m_path.lineTo(xc, yc);
    return;
  }
#endif

#if 0
  if (anticlockwise) {
    span = -M_PI / 2.0;
  } else {
    span = M_PI / 2.0;
  }
#endif
  Gdiplus::RectF rect(Gdiplus::REAL(pc.x0 * display_scale - pc.radius * display_scale), Gdiplus::REAL(pc.y0 * display_scale - pc.radius * display_scale), Gdiplus::REAL(2 * pc.radius * display_scale), Gdiplus::REAL(2 * pc.radius * display_scale));

	output.AddArc(rect, Gdiplus::REAL(pc.sa * 180.0f / M_PI), Gdiplus::REAL(span * 180.0f / M_PI));
	output.GetLastPoint(&current_pos);
      }
      break;
    }
  }
}

static Gdiplus::Color toGDIColor(const Color & input, float globalAlpha = 1.0f) {
  int red = int(input.red * 255), green = int(input.green * 255), blue = int(input.blue * 255), alpha = int(input.alpha * globalAlpha * 255);
  if (red < 0) red = 0;
  else if (red > 255) red = 255;
  if (green < 0) green = 0;
  else if (green > 255) green = 255;
  if (blue < 0) blue = 0;
  else if (blue > 255) blue = 255;
  if (alpha < 0) alpha = 0;
  else if (alpha > 255) alpha = 255;
#if 0
  return Gdiplus::Color::FromArgb(alpha, red, green, blue);
#else
  return Gdiplus::Color(alpha, red, green, blue);
#endif
}

GDIPlusSurface::GDIPlusSurface(const std::string & filename) : Surface(0, 0, 0, 0, false) {
  std::wstring tmp = convert_to_wstring(filename);
  bitmap = std::shared_ptr<Gdiplus::Bitmap>(Gdiplus::Bitmap::FromFile(tmp.data()));
  Surface::resize(bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetWidth(), bitmap->GetHeight(), true);
}

GDIPlusSurface::GDIPlusSurface(const unsigned char * buffer, size_t size) : Surface(0, 0, 0, 0, false) {
	assert(0);
}

void
GDIPlusSurface::renderPath(RenderMode mode, const Path2D & input_path, const Style & style, float lineWidth, Operator op, float display_scale, float globalAlpha) {
  initializeContext();

  switch (op) {
  case SOURCE_OVER:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);
    break;
  case COPY:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
    break;
  }

  Gdiplus::GraphicsPath path;
  toGDIPath(input_path, path, display_scale);

  switch (mode) {
  case STROKE:
    {
      Gdiplus::Pen pen(toGDIColor(style.color, globalAlpha), lineWidth);
      g->DrawPath(&pen, &path);
    }
    break;
  case FILL:
    if (style.getType() == Style::LINEAR_GRADIENT) {
      const std::map<float, Color> & colors = style.getColors();
      if (!colors.empty()) {
	std::map<float, Color>::const_iterator it0 = colors.begin(), it1 = colors.end();
	it1--;
	const Color & c0 = it0->second, c1 = it1->second;
	Gdiplus::LinearGradientBrush brush(Gdiplus::PointF(Gdiplus::REAL(style.x0), Gdiplus::REAL(style.y0)),
					   Gdiplus::PointF(Gdiplus::REAL(style.x1), Gdiplus::REAL(style.y1)),
					   toGDIColor(c0, globalAlpha),
					   toGDIColor(c1, globalAlpha));
	g->FillPath(&brush, &path);
      }
    } else {
      Gdiplus::SolidBrush brush(toGDIColor(style.color, globalAlpha));
      g->FillPath(&brush, &path);
    }
  }
}

void
GDIPlusSurface::clip(const Path2D & input_path, float display_scale) {
  initializeContext();

  Gdiplus::GraphicsPath path;
  toGDIPath(input_path, path, display_scale);

  Gdiplus::Region region(&path);
  g->SetClip(&region);
}

void
GDIPlusSurface::drawNativeSurface(GDIPlusSurface & img, const Point & p, double w, double h, float displayScale, float globalAlpha, bool imageSmoothingEnabled) {
  initializeContext();

  g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);

  if (imageSmoothingEnabled) {
    // g->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
    g->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear );
  } else {
    g->SetInterpolationMode( Gdiplus::InterpolationModeNearestNeighbor );
  }
  if (globalAlpha < 1.0f && 0) {
#if 0
    ImageAttributes  imageAttributes;
    ColorMatrix colorMatrix = {
      1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
      0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
      0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
      0.0f, 0.0f, 0.0f, alpha, 0.0f,
      0.0f, 0.0f, 0.0f, 0.0f, 1.0f};

    imageAttributes.SetColorMatrix( &colorMatrix,
				    ColorMatrixFlagsDefault,
				    ColorAdjustTypeBitmap);
    graphics.DrawImage( &(*(img.bitmap)),
			Gdiplus::Rect(p.x, p.y, w, h), // destination rectangle
			0, 0,        // upper-left corner of source rectangle
			getWidth(),       // width of source rectangle
			getHeight(),      // height of source rectangle
			Gdiplus::UnitPixel,
			&imageAttributes);
#endif
  } else if (img.getActualWidth() == (unsigned int)w && img.getActualHeight() == (unsigned int)h && 0) { // this scales image weirdly
    g->DrawImage(&(*(img.bitmap)), Gdiplus::REAL(p.x), Gdiplus::REAL(p.y));
  } else {
    g->DrawImage(&(*(img.bitmap)), Gdiplus::REAL(p.x), Gdiplus::REAL(p.y), Gdiplus::REAL(w), Gdiplus::REAL(h));
  }
}

void
GDIPlusSurface::drawImage(Surface & _img, const Point & p, double w, double h, gfloat displayScale, float globalAlpha, bool imageSmoothingEnabled) {
  GDIPlusSurface * img = dynamic_cast<GDIPlusSurface*>(&_img);
  if (img) {
    drawNativeSurface(*img, p, w, h, displayScale, globalAlpha, imageSmoothingEnabled);
  } else {
    auto img = _img.createImage();
    GDIPlusSurface cs(*img);
    drawNativeSurface(cs, p, w, h, displayScale, globalAlpha, imageSmoothingEnabled);
  }
}

void
GDIPlusSurface::renderText(RenderMode mode, const Font & font, const Style & style, TextBaseline textBaseline, TextAlign textAlign, const std::string & text, const Point & p, float lineWidth, Operator op, float display_scale, float globalAlpha) {
  initializeContext();

  double x = round(p.x);
  double y = round(p.y);

  switch (op) {
  case SOURCE_OVER:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);
    break;
  case COPY:
    g->SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
    break;
  }

  if (font.cleartype) {
    g->SetTextRenderingHint(Gdiplus::TextRenderingHintClearTypeGridFit);
  } else if (font.antialiasing && font.hinting && 0) {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintAntiAliasGridFit );
  } else if (font.antialiasing) {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintAntiAlias );
  } else if (font.hinting) {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintSingleBitPerPixelGridFit );
  } else {
    g->SetTextRenderingHint( Gdiplus::TextRenderingHintSingleBitPerPixel );
  }

  std::wstring text2 = convert_to_wstring(text);
  int style_bits = 0;
  if (font.weight == Font::BOLD || font.weight == Font::BOLDER) {
    style_bits |= Gdiplus::FontStyleBold;
  }
  if (font.slant == Font::ITALIC) {
    style_bits |= Gdiplus::FontStyleItalic;
  }
  Gdiplus::Font gdifont(&Gdiplus::FontFamily(L"Arial"), font.size * display_scale, style_bits, Gdiplus::UnitPixel);

  Gdiplus::RectF rect(Gdiplus::REAL(x * display_scale), Gdiplus::REAL(y * display_scale), 0.0f, 0.0f);
  Gdiplus::StringFormat f;

  switch (textBaseline.getType()) {
  case TextBaseline::TOP: break;
  case TextBaseline::HANGING: break;
  case TextBaseline::MIDDLE: f.SetLineAlignment(Gdiplus::StringAlignmentCenter); break;
  case TextBaseline::BOTTOM: f.SetLineAlignment(Gdiplus::StringAlignmentFar);
  }

  switch (textAlign.getType()) {
  case TextAlign::CENTER: f.SetAlignment(Gdiplus::StringAlignmentCenter); break;
  case TextAlign::END: case TextAlign::RIGHT: f.SetAlignment(Gdiplus::StringAlignmentFar); break;
  case TextAlign::START: case TextAlign::LEFT: f.SetAlignment(Gdiplus::StringAlignmentNear); break;
  }

  f.SetFormatFlags(Gdiplus::StringFormatFlagsBypassGDI);

  switch (mode) {
  case STROKE:
    // implement
    break;
  case FILL:
    {
      Gdiplus::SolidBrush brush(toGDIColor(style.color, globalAlpha));
      g->DrawString(text2.data(), text2.size(), &gdifont, rect, &f, &brush);
    }
    break;
  }
}

TextMetrics
GDIPlusSurface::measureText(const Font & font, const std::string & text, float display_scale) {
  initializeContext();
  std::wstring text2 = convert_to_wstring(text);
  int style = 0;
  if (font.weight == Font::BOLD || font.weight == Font::BOLDER) {
    style |= Gdiplus::FontStyleBold;
  }
  if (font.slant == Font::ITALIC) {
    style |= Gdiplus::FontStyleItalic;
  }
  Gdiplus::Font gdi_font(&Gdiplus::FontFamily(L"Arial"), font.size * display_scale, style, Gdiplus::UnitPixel);
  Gdiplus::RectF layoutRect(0, 0, 512, 512), boundingBox;
  g->MeasureString(text2.data(), text2.size(), &gdi_font, layoutRect, &boundingBox);
  Gdiplus::SizeF size;
  boundingBox.GetSize(&size);

  float ascent = &Gdiplus::FontFamily(L"Arial")::GetCellAscent(style);
  float descent = &Gdiplus::FontFamily(L"Arial")::GetCellDescent(style);
  float baseline = 0;
  if (textBaseline == TextBaseline::MIDDLE) {
    baseline = (ascent + descent) / 2;
  } else if (textBaseline == TextBaseline::TOP) {
    baseline = (ascent + descent);
  }

  return TextMetrics(size.Width / display_scale, (descent - baseline) / display_scale, (ascent - baseline) / display_scale);
}
<|endoftext|>


<commit_before>#include	<cstdlib>
#include	<cmath>
#include	<fstream>
#include	<sstream>
#include	<iostream>
#include	<iomanip>
#include	<string>
#include	<vector>
#include	<algorithm>
#include	<exception>
#include	<sys/time.h>
#include	"modules/htmTree.h"
#include	"modules/kdTree.h"
#include        "misc.h"
#include        "feat.h"
#include        "structs.h"
#include        "collision.h"
#include        "global.h"
//reduce redistributes, updates  07/02/15 rnc
int main(int argc, char **argv) {
	//// Initializations ---------------------------------------------
	srand48(1234); // Make sure we have reproducability
	check_args(argc);
	Time t, time; // t for global, time for local
	init_time(t);
	Feat F;
    MTL M;

	// Read parameters file //
	F.readInputFile(argv[1]);
	printFile(argv[1]);
	// Read Secretfile
    // Secret contains the identity of each target: QSO-Ly-a, QSO-tracers, LRG, ELG, fake QSO, fake LRG, SS, SF
    Gals Secret;
    printf("before read secretfile \n");
    init_time_at(time,"# read Secret file",t);

    Secret=read_Secretfile(F.Secretfile,F);
    printf("# Read %d galaxies from %s \n",Secret.size(),F.Secretfile.c_str());
	print_time(time,"# ... took :");
    std::vector<int> count;
    count=count_galaxies(Secret);
    printf(" Number of galaxies by type, QSO-Ly-a, QSO-tracers, LRG, ELG, fake QSO, fake LRG, SS, SF\n");
    for(int i=0;i<8;i++){printf (" type %d number  %d  \n",i, count[i]);}
    //read the three input files
    init_time_at(time,"# read target, SS, SF files",t);
    MTL Targ=read_MTLfile(F.Targfile,F,0,0);
    MTL SStars=read_MTLfile(F.SStarsfile,F,1,0);
    MTL SkyF=read_MTLfile(F.SkyFfile,F,0,1);
    print_time(time,"# ... took :");
    //combine the three input files

    M=Targ;

    printf(" M size %d \n",M.size());
    M.insert(M.end(),SStars.begin(),SStars.end());
    printf(" M size %d \n",M.size());
    M.insert(M.end(),SkyF.begin(),SkyF.end());
    printf(" M size %d \n",M.size());
    F.Ngal=M.size();
    assign_priority_class(M);

    //establish priority classes
    init_time_at(time,"# establish priority clasess",t);
    std::vector <int> count_class(M.priority_list.size(),0);
    for(int i;i<M.size();++i){
        if(!M[i].SS&&!M[i].SF){
        count_class[M[i].priority_class]+=1;
        }
    }
	print_time(time,"# ... took :");

    for(int i;i<M.priority_list.size();++i){
        printf("  class  %d  number  %d\n",i,count_class[i]);
    }

    PP pp;
	pp.read_fiber_positions(F);
	F.Nfiber = pp.fp.size()/2;
	F.Npetal = max(pp.spectrom)+1;
    F.Nfbp = (int) (F.Nfiber/F.Npetal);// fibers per petal = 500
	pp.get_neighbors(F); pp.compute_fibsofsp(F);

    //P is original list of plates
	Plates P = read_plate_centers(F);

    printf(" future plates %d\n",P.size());
    F.Nplate=P.size();
	printf("# Read %s plate centers from %s and %d fibers from %s\n",f(F.Nplate).c_str(),F.tileFile.c_str(),F.Nfiber,F.fibFile.c_str());

	// Computes geometries of cb and fh: pieces of positioner - used to determine possible collisions
	F.cb = create_cb(); // cb=central body
	F.fh = create_fh(); // fh=fiber holder

	//// Collect available galaxies <-> tilefibers --------------------
	// HTM Tree of galaxies
	const double MinTreeSize = 0.01;
	init_time_at(time,"# Start building HTM tree",t);
	htmTree<struct target> T(M,MinTreeSize);
	print_time(time,"# ... took :");//T.stats();
    init_time_at(time,"# collect galaxies at ",t);

	// For plates/fibers, collect available galaxies; done in parallel
    collect_galaxies_for_all(M,T,P,pp,F);
    print_time(time,"# ... took :");//T.stats();
    init_time_at(time,"# collect available tile-fibers at",t);
	// For each galaxy, computes available tilefibers  G[i].av_tfs = [(j1,k1),(j2,k2),..]
	collect_available_tilefibers(M,P,F);

	//results_on_inputs("doc/figs/",G,P,F,true);

	//// Assignment |||||||||||||||||||||||||||||||||||||||||||||||||||
    printf(" Nplate %d  Ngal %d   Nfiber %d \n", F.Nplate, F.Ngal, F.Nfiber);
    Assignment A(M,F);

	print_time(t,"# Start assignment at : ");

	// Make a plan ----------------------------------------------------
    // Plans whole survey without sky fibers, standard stars
    // assumes maximum number of observations needed for QSOs, LRGs

    simple_assign(M,P,pp,F,A);

    //check to see if there are tiles with no galaxies
    //need to keep mapping of old tile list to new tile list
    //and inverse map
    A.inv_order=initList(F.Nplate,0);
    for (int j=0;j<F.Nplate ;++j){
        bool not_done=true;
        for(int k=0;k<F.Nfiber && not_done;++k){
            if(A.TF[j][k]!=-1){
                A.suborder.push_back(j);
                not_done=false;

            }
        }
    }
    F.NUsedplate=A.suborder.size();
    printf(" Plates after screening %d \n",F.NUsedplate);

    //if(F.diagnose)diagnostic(M,G,F,A);

    print_hist("Unused fibers",5,histogram(A.unused_fbp(pp,F),5),false); // Hist of unused fibs

	// Smooth out distribution of free fibers, and increase the number of assignments

	for (int i=0; i<1; i++) redistribute_tf(M,P,pp,F,A,0);// more iterations will improve performance slightly
	for (int i=0; i<1; i++) {
        improve(M,P,pp,F,A,0);
		redistribute_tf(M,P,pp,F,A,0);
	}
	print_hist("Unused fibers",5,histogram(A.unused_fbp(pp,F),5),false);
    //try assigning SF and SS before real time assignment
    for (int j=0;j<F.NUsedplate;++j){

        int js=A.suborder[j];
        assign_sf_ss(js,M,P,pp,F,A); // Assign SS and SF for each tile
        assign_unused(js,M,P,pp,F,A);
    }
    if(F.diagnose)diagnostic(M,Secret,F,A);
    init_time_at(time,"# Begin real time assignment",t);

	//Execute plan, updating targets at intervals
    for(int i=0;i<F.pass_intervals.size();i++)printf(" i=%d interval %d \n",i,F.pass_intervals[i]);
    std::vector <int> update_intervals=F.pass_intervals;
    update_intervals.push_back(F.NUsedplate);//to end intervals at last plate
    for(int i=0;i<update_intervals.size()-1;++i){//go plate by used plate
        int starter=update_intervals[i];
        printf(" before pass = %d  at %d  tiles  \n",i,starter);
        //display_results("doc/figs/",G,P,pp,F,A,true);
        //plan whole survey from this point out
        /*
        for (int jj=starter; jj<F.NUsedplate; jj++) {
            int js = A.suborder[jj];
            assign_sf_ss(js,M,P,pp,F,A); // Assign SS and SF
            assign_unused(js,M,P,pp,F,A);
        }
         */
        //update target information for interval i
    }
    /*
        for (int jj=starter; jj<update_intervals[i+1]; jj++) {

            // Update corrects all future occurrences of wrong QSOs etc and tries to observe something else
            if (0<=jj-F.Analysis) update_plan_from_one_obs(jj,Secret,M,P,pp,F,A); else printf("\n no update\n");
        }
        redistribute_tf(M,P,pp,F,A,starter);
        improve(M,P,pp,F,A,starter);
        redistribute_tf(M,P,pp,F,A,starter);
        //}

        if(F.diagnose)diagnostic(M,Secret,F,A);

    // check on SS and SF
/*
    for(int j=0;j<F.NUsedplate;++j){
        int js=A.suborder[j];
        printf("\n js = %d\n",js);
        for (int p=0;p<F.Npetal;++p){
            int count_SS=0;
            int count_SF=0;
            for (int k=0;k<F.Nfbp;++k){
                int kk=pp.fibers_of_sp[p][k];
                int g=A.TF[js][kk];
                if(g!=-1 && M[g].SS)count_SS++;
                if(g!=-1 && M[g].SF)count_SF++;

            }
            printf("  %d  %d   ",count_SS,count_SF);
        }
        printf("\n");
    }

    }
     */

	// Results -------------------------------------------------------
    if (F.PrintAscii) for (int j=0; j<F.NUsedplate; j++){
        write_FAtile_ascii(A.suborder[j],F.outDir,M,P,pp,F,A);
    }

    if (F.PrintFits) for (int j=0; j<F.NUsedplate; j++){
        fa_write(A.suborder[j],F.outDir,M,P,pp,F,A); // Write output
    }

	display_results("doc/figs/",Secret,M,P,pp,F,A,true);
	if (F.Verif) A.verif(P,M,pp,F); // Verification that the assignment is sane

	print_time(t,"# Finished !... in");

	return(0);

}
<commit_msg>diagnostic<commit_after>#include	<cstdlib>
#include	<cmath>
#include	<fstream>
#include	<sstream>
#include	<iostream>
#include	<iomanip>
#include	<string>
#include	<vector>
#include	<algorithm>
#include	<exception>
#include	<sys/time.h>
#include	"modules/htmTree.h"
#include	"modules/kdTree.h"
#include        "misc.h"
#include        "feat.h"
#include        "structs.h"
#include        "collision.h"
#include        "global.h"
//reduce redistributes, updates  07/02/15 rnc
int main(int argc, char **argv) {
	//// Initializations ---------------------------------------------
	srand48(1234); // Make sure we have reproducability
	check_args(argc);
	Time t, time; // t for global, time for local
	init_time(t);
	Feat F;
    MTL M;

	// Read parameters file //
	F.readInputFile(argv[1]);
	printFile(argv[1]);
	// Read Secretfile
    // Secret contains the identity of each target: QSO-Ly-a, QSO-tracers, LRG, ELG, fake QSO, fake LRG, SS, SF
    Gals Secret;
    printf("before read secretfile \n");
    init_time_at(time,"# read Secret file",t);

    Secret=read_Secretfile(F.Secretfile,F);
    printf("# Read %d galaxies from %s \n",Secret.size(),F.Secretfile.c_str());
	print_time(time,"# ... took :");
    std::vector<int> count;
    count=count_galaxies(Secret);
    printf(" Number of galaxies by type, QSO-Ly-a, QSO-tracers, LRG, ELG, fake QSO, fake LRG, SS, SF\n");
    for(int i=0;i<8;i++){printf (" type %d number  %d  \n",i, count[i]);}
    //read the three input files
    init_time_at(time,"# read target, SS, SF files",t);
    MTL Targ=read_MTLfile(F.Targfile,F,0,0);
    MTL SStars=read_MTLfile(F.SStarsfile,F,1,0);
    MTL SkyF=read_MTLfile(F.SkyFfile,F,0,1);
    print_time(time,"# ... took :");
    //combine the three input files

    M=Targ;

    printf(" M size %d \n",M.size());
    M.insert(M.end(),SStars.begin(),SStars.end());
    printf(" M size %d \n",M.size());
    M.insert(M.end(),SkyF.begin(),SkyF.end());
    printf(" M size %d \n",M.size());
    F.Ngal=M.size();
    assign_priority_class(M);

    //establish priority classes
    init_time_at(time,"# establish priority clasess",t);
    std::vector <int> count_class(M.priority_list.size(),0);
    for(int i;i<M.size();++i){
        if(!M[i].SS&&!M[i].SF){
        count_class[M[i].priority_class]+=1;
        }
    }
	print_time(time,"# ... took :");

    for(int i;i<M.priority_list.size();++i){
        printf("  class  %d  number  %d\n",i,count_class[i]);
    }

    PP pp;
	pp.read_fiber_positions(F);
	F.Nfiber = pp.fp.size()/2;
	F.Npetal = max(pp.spectrom)+1;
    F.Nfbp = (int) (F.Nfiber/F.Npetal);// fibers per petal = 500
	pp.get_neighbors(F); pp.compute_fibsofsp(F);

    //P is original list of plates
	Plates P = read_plate_centers(F);

    printf(" future plates %d\n",P.size());
    F.Nplate=P.size();
	printf("# Read %s plate centers from %s and %d fibers from %s\n",f(F.Nplate).c_str(),F.tileFile.c_str(),F.Nfiber,F.fibFile.c_str());

	// Computes geometries of cb and fh: pieces of positioner - used to determine possible collisions
	F.cb = create_cb(); // cb=central body
	F.fh = create_fh(); // fh=fiber holder

	//// Collect available galaxies <-> tilefibers --------------------
	// HTM Tree of galaxies
	const double MinTreeSize = 0.01;
	init_time_at(time,"# Start building HTM tree",t);
	htmTree<struct target> T(M,MinTreeSize);
	print_time(time,"# ... took :");//T.stats();
    init_time_at(time,"# collect galaxies at ",t);

	// For plates/fibers, collect available galaxies; done in parallel
    collect_galaxies_for_all(M,T,P,pp,F);
    print_time(time,"# ... took :");//T.stats();
    init_time_at(time,"# collect available tile-fibers at",t);
	// For each galaxy, computes available tilefibers  G[i].av_tfs = [(j1,k1),(j2,k2),..]
	collect_available_tilefibers(M,P,F);

	//results_on_inputs("doc/figs/",G,P,F,true);

	//// Assignment |||||||||||||||||||||||||||||||||||||||||||||||||||
    printf(" Nplate %d  Ngal %d   Nfiber %d \n", F.Nplate, F.Ngal, F.Nfiber);
    Assignment A(M,F);

	print_time(t,"# Start assignment at : ");

	// Make a plan ----------------------------------------------------
    // Plans whole survey without sky fibers, standard stars
    // assumes maximum number of observations needed for QSOs, LRGs

    simple_assign(M,P,pp,F,A);

    //check to see if there are tiles with no galaxies
    //need to keep mapping of old tile list to new tile list
    //and inverse map
    A.inv_order=initList(F.Nplate,0);
    for (int j=0;j<F.Nplate ;++j){
        bool not_done=true;
        for(int k=0;k<F.Nfiber && not_done;++k){
            if(A.TF[j][k]!=-1){
                A.suborder.push_back(j);
                not_done=false;

            }
        }
    }
    F.NUsedplate=A.suborder.size();
    printf(" Plates after screening %d \n",F.NUsedplate);

    //if(F.diagnose)diagnostic(M,G,F,A);

    print_hist("Unused fibers",5,histogram(A.unused_fbp(pp,F),5),false); // Hist of unused fibs

	// Smooth out distribution of free fibers, and increase the number of assignments

	for (int i=0; i<1; i++) redistribute_tf(M,P,pp,F,A,0);// more iterations will improve performance slightly
	for (int i=0; i<1; i++) {
        improve(M,P,pp,F,A,0);
		redistribute_tf(M,P,pp,F,A,0);
	}
	print_hist("Unused fibers",5,histogram(A.unused_fbp(pp,F),5),false);
    //try assigning SF and SS before real time assignment
    for (int j=0;j<F.NUsedplate;++j){

        int js=A.suborder[j];
        assign_sf_ss(js,M,P,pp,F,A); // Assign SS and SF for each tile
        assign_unused(js,M,P,pp,F,A);
    }
    if(F.diagnose)diagnostic(M,Secret,F,A);
    init_time_at(time,"# Begin real time assignment",t);

	//Execute plan, updating targets at intervals
    for(int i=0;i<F.pass_intervals.size();i++)printf(" i=%d interval %d \n",i,F.pass_intervals[i]);
    std::vector <int> update_intervals=F.pass_intervals;
    update_intervals.push_back(F.NUsedplate);//to end intervals at last plate
    for(int i=0;i<update_intervals.size()-1;++i){//go plate by used plate
        int starter=update_intervals[i];
        printf(" before pass = %d  at %d  tiles  \n",i,starter);
        //display_results("doc/figs/",G,P,pp,F,A,true);
        //plan whole survey from this point out
        /*
        for (int jj=starter; jj<F.NUsedplate; jj++) {
            int js = A.suborder[jj];
            assign_sf_ss(js,M,P,pp,F,A); // Assign SS and SF
            assign_unused(js,M,P,pp,F,A);
        }
         */
        //update target information for interval i

        for (int jj=starter; jj<update_intervals[i+1]; jj++) {

            // Update corrects all future occurrences of wrong QSOs etc and tries to observe something else
            if (0<=jj-F.Analysis) update_plan_from_one_obs(jj,Secret,M,P,pp,F,A); else printf("\n no update\n");
        }
    }
    /*
        redistribute_tf(M,P,pp,F,A,starter);
        improve(M,P,pp,F,A,starter);
        redistribute_tf(M,P,pp,F,A,starter);
        //}

        if(F.diagnose)diagnostic(M,Secret,F,A);

    // check on SS and SF
/*
    for(int j=0;j<F.NUsedplate;++j){
        int js=A.suborder[j];
        printf("\n js = %d\n",js);
        for (int p=0;p<F.Npetal;++p){
            int count_SS=0;
            int count_SF=0;
            for (int k=0;k<F.Nfbp;++k){
                int kk=pp.fibers_of_sp[p][k];
                int g=A.TF[js][kk];
                if(g!=-1 && M[g].SS)count_SS++;
                if(g!=-1 && M[g].SF)count_SF++;

            }
            printf("  %d  %d   ",count_SS,count_SF);
        }
        printf("\n");
    }

    }
     */

	// Results -------------------------------------------------------
    if (F.PrintAscii) for (int j=0; j<F.NUsedplate; j++){
        write_FAtile_ascii(A.suborder[j],F.outDir,M,P,pp,F,A);
    }

    if (F.PrintFits) for (int j=0; j<F.NUsedplate; j++){
        fa_write(A.suborder[j],F.outDir,M,P,pp,F,A); // Write output
    }

	display_results("doc/figs/",Secret,M,P,pp,F,A,true);
	if (F.Verif) A.verif(P,M,pp,F); // Verification that the assignment is sane

	print_time(t,"# Finished !... in");

	return(0);

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "perfetto/base/logging.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "src/base/test/test_task_runner.h"
#include "test/cts/utils.h"
#include "test/gtest_and_gmock.h"
#include "test/test_helper.h"

#include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
#include "protos/perfetto/trace/profiling/heap_graph.gen.h"
#include "protos/perfetto/trace/profiling/profile_common.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"

namespace perfetto {
namespace {

std::string RandomSessionName() {
  std::random_device rd;
  std::default_random_engine generator(rd());
  std::uniform_int_distribution<char> distribution('a', 'z');

  constexpr size_t kSessionNameLen = 20;
  std::string result(kSessionNameLen, '\0');
  for (size_t i = 0; i < kSessionNameLen; ++i)
    result[i] = distribution(generator);
  return result;
}

std::vector<protos::gen::TracePacket> ProfileRuntime(std::string app_name) {
  base::TestTaskRunner task_runner;

  // (re)start the target app's main activity
  if (IsAppRunning(app_name)) {
    StopApp(app_name, "old.app.stopped", &task_runner);
    task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
  }
  StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
                   /*delay_ms=*/100);
  task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
  // If we try to dump too early in app initialization, we sometimes deadlock.
  sleep(1);

  // set up tracing
  TestHelper helper(&task_runner);
  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(20 * 1024);
  trace_config.set_duration_ms(6000);
  trace_config.set_unique_session_name(RandomSessionName().c_str());

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.java_hprof");
  ds_config->set_target_buffer(0);

  protos::gen::JavaHprofConfig java_hprof_config;
  java_hprof_config.add_process_cmdline(app_name.c_str());
  ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());

  // start tracing
  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled(10000 /*ms*/);
  helper.ReadData();
  helper.WaitForReadData();
  PERFETTO_CHECK(IsAppRunning(app_name));
  StopApp(app_name, "new.app.stopped", &task_runner);
  task_runner.RunUntilCheckpoint("new.app.stopped", 1000 /*ms*/);
  return helper.trace();
}

void AssertGraphPresent(std::vector<protos::gen::TracePacket> packets) {
  ASSERT_GT(packets.size(), 0u);

  size_t objects = 0;
  size_t roots = 0;
  for (const auto& packet : packets) {
    objects += static_cast<size_t>(packet.heap_graph().objects_size());
    roots += static_cast<size_t>(packet.heap_graph().roots_size());
  }
  ASSERT_GT(objects, 0u);
  ASSERT_GT(roots, 0u);
}

void AssertNoProfileContents(std::vector<protos::gen::TracePacket> packets) {
  // If profile packets are present, they must be empty.
  for (const auto& packet : packets) {
    ASSERT_EQ(packet.heap_graph().roots_size(), 0);
    ASSERT_EQ(packet.heap_graph().objects_size(), 0);
    ASSERT_EQ(packet.heap_graph().type_names_size(), 0);
    ASSERT_EQ(packet.heap_graph().field_names_size(), 0);
  }
}

TEST(HeapprofdJavaCtsTest, DebuggableAppRuntime) {
  std::string app_name = "android.perfetto.cts.app.debuggable";
  const auto& packets = ProfileRuntime(app_name);
  AssertGraphPresent(packets);
}

TEST(HeapprofdJavaCtsTest, ProfileableAppRuntime) {
  std::string app_name = "android.perfetto.cts.app.profileable";
  const auto& packets = ProfileRuntime(app_name);
  AssertGraphPresent(packets);
}

TEST(HeapprofdJavaCtsTest, ReleaseAppRuntime) {
  std::string app_name = "android.perfetto.cts.app.release";
  const auto& packets = ProfileRuntime(app_name);

  if (IsDebuggableBuild())
    AssertGraphPresent(packets);
  else
    AssertNoProfileContents(packets);
}

}  // namespace
}  // namespace perfetto
<commit_msg>Increase buffer size for Java Heap Prof CTS. am: e8990636e0 am: d17b6a1b16 am: a7be53d5a2<commit_after>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "perfetto/base/logging.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "src/base/test/test_task_runner.h"
#include "test/cts/utils.h"
#include "test/gtest_and_gmock.h"
#include "test/test_helper.h"

#include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
#include "protos/perfetto/trace/profiling/heap_graph.gen.h"
#include "protos/perfetto/trace/profiling/profile_common.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"

namespace perfetto {
namespace {

std::string RandomSessionName() {
  std::random_device rd;
  std::default_random_engine generator(rd());
  std::uniform_int_distribution<char> distribution('a', 'z');

  constexpr size_t kSessionNameLen = 20;
  std::string result(kSessionNameLen, '\0');
  for (size_t i = 0; i < kSessionNameLen; ++i)
    result[i] = distribution(generator);
  return result;
}

std::vector<protos::gen::TracePacket> ProfileRuntime(std::string app_name) {
  base::TestTaskRunner task_runner;

  // (re)start the target app's main activity
  if (IsAppRunning(app_name)) {
    StopApp(app_name, "old.app.stopped", &task_runner);
    task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
  }
  StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
                   /*delay_ms=*/100);
  task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
  // If we try to dump too early in app initialization, we sometimes deadlock.
  sleep(1);

  // set up tracing
  TestHelper helper(&task_runner);
  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(40 * 1024);
  trace_config.set_duration_ms(6000);
  trace_config.set_unique_session_name(RandomSessionName().c_str());

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.java_hprof");
  ds_config->set_target_buffer(0);

  protos::gen::JavaHprofConfig java_hprof_config;
  java_hprof_config.add_process_cmdline(app_name.c_str());
  ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());

  // start tracing
  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled(10000 /*ms*/);
  helper.ReadData();
  helper.WaitForReadData();
  PERFETTO_CHECK(IsAppRunning(app_name));
  StopApp(app_name, "new.app.stopped", &task_runner);
  task_runner.RunUntilCheckpoint("new.app.stopped", 1000 /*ms*/);
  return helper.trace();
}

void AssertGraphPresent(std::vector<protos::gen::TracePacket> packets) {
  ASSERT_GT(packets.size(), 0u);

  size_t objects = 0;
  size_t roots = 0;
  for (const auto& packet : packets) {
    objects += static_cast<size_t>(packet.heap_graph().objects_size());
    roots += static_cast<size_t>(packet.heap_graph().roots_size());
  }
  ASSERT_GT(objects, 0u);
  ASSERT_GT(roots, 0u);
}

void AssertNoProfileContents(std::vector<protos::gen::TracePacket> packets) {
  // If profile packets are present, they must be empty.
  for (const auto& packet : packets) {
    ASSERT_EQ(packet.heap_graph().roots_size(), 0);
    ASSERT_EQ(packet.heap_graph().objects_size(), 0);
    ASSERT_EQ(packet.heap_graph().type_names_size(), 0);
    ASSERT_EQ(packet.heap_graph().field_names_size(), 0);
  }
}

TEST(HeapprofdJavaCtsTest, DebuggableAppRuntime) {
  std::string app_name = "android.perfetto.cts.app.debuggable";
  const auto& packets = ProfileRuntime(app_name);
  AssertGraphPresent(packets);
}

TEST(HeapprofdJavaCtsTest, ProfileableAppRuntime) {
  std::string app_name = "android.perfetto.cts.app.profileable";
  const auto& packets = ProfileRuntime(app_name);
  AssertGraphPresent(packets);
}

TEST(HeapprofdJavaCtsTest, ReleaseAppRuntime) {
  std::string app_name = "android.perfetto.cts.app.release";
  const auto& packets = ProfileRuntime(app_name);

  if (IsDebuggableBuild())
    AssertGraphPresent(packets);
  else
    AssertNoProfileContents(packets);
}

}  // namespace
}  // namespace perfetto
<|endoftext|>


<commit_before>// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "web_layer_impl.h"

#include "SkMatrix44.h"
#ifdef LOG
#undef LOG
#endif
#include "base/string_util.h"
#include "cc/active_animation.h"
#include "cc/layer.h"
#include "cc/region.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebFloatPoint.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebFloatRect.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebTransformationMatrix.h"
#include "web_animation_impl.h"

using cc::ActiveAnimation;
using cc::Layer;

namespace WebKit {

namespace {

WebTransformationMatrix transformationMatrixFromSkMatrix44(const SkMatrix44& matrix)
{
    double data[16];
    matrix.asColMajord(data);
    return WebTransformationMatrix(data[0], data[1], data[2], data[3],
                                   data[4], data[5], data[6], data[7],
                                   data[8], data[9], data[10], data[11],
                                   data[12], data[13], data[14], data[15]);
}

SkMatrix44 skMatrix44FromTransformationMatrix(const WebTransformationMatrix& matrix)
{
    SkMatrix44 skMatrix;
    skMatrix.set(0, 0, SkDoubleToMScalar(matrix.m11()));
    skMatrix.set(1, 0, SkDoubleToMScalar(matrix.m12()));
    skMatrix.set(2, 0, SkDoubleToMScalar(matrix.m13()));
    skMatrix.set(3, 0, SkDoubleToMScalar(matrix.m14()));
    skMatrix.set(0, 1, SkDoubleToMScalar(matrix.m21()));
    skMatrix.set(1, 1, SkDoubleToMScalar(matrix.m22()));
    skMatrix.set(2, 1, SkDoubleToMScalar(matrix.m23()));
    skMatrix.set(3, 1, SkDoubleToMScalar(matrix.m24()));
    skMatrix.set(0, 2, SkDoubleToMScalar(matrix.m31()));
    skMatrix.set(1, 2, SkDoubleToMScalar(matrix.m32()));
    skMatrix.set(2, 2, SkDoubleToMScalar(matrix.m33()));
    skMatrix.set(3, 2, SkDoubleToMScalar(matrix.m34()));
    skMatrix.set(0, 3, SkDoubleToMScalar(matrix.m41()));
    skMatrix.set(1, 3, SkDoubleToMScalar(matrix.m42()));
    skMatrix.set(2, 3, SkDoubleToMScalar(matrix.m43()));
    skMatrix.set(3, 3, SkDoubleToMScalar(matrix.m44()));
    return skMatrix;
}

}

WebLayer* WebLayer::create()
{
    return new WebLayerImpl();
}

WebLayerImpl::WebLayerImpl()
    : m_layer(Layer::create())
{
}

WebLayerImpl::WebLayerImpl(scoped_refptr<Layer> layer)
    : m_layer(layer)
{
}

WebLayerImpl::~WebLayerImpl()
{
    m_layer->clearRenderSurface();
    m_layer->setLayerAnimationDelegate(0);
}

int WebLayerImpl::id() const
{
    return m_layer->id();
}

void WebLayerImpl::invalidateRect(const WebFloatRect& rect)
{
    m_layer->setNeedsDisplayRect(rect);
}

void WebLayerImpl::invalidate()
{
    m_layer->setNeedsDisplay();
}

void WebLayerImpl::addChild(WebLayer* child)
{
    m_layer->addChild(static_cast<WebLayerImpl*>(child)->layer());
}

void WebLayerImpl::insertChild(WebLayer* child, size_t index)
{
    m_layer->insertChild(static_cast<WebLayerImpl*>(child)->layer(), index);
}

void WebLayerImpl::replaceChild(WebLayer* reference, WebLayer* newLayer)
{
    m_layer->replaceChild(static_cast<WebLayerImpl*>(reference)->layer(), static_cast<WebLayerImpl*>(newLayer)->layer());
}

void WebLayerImpl::removeFromParent()
{
    m_layer->removeFromParent();
}

void WebLayerImpl::removeAllChildren()
{
    m_layer->removeAllChildren();
}

void WebLayerImpl::setAnchorPoint(const WebFloatPoint& anchorPoint)
{
    m_layer->setAnchorPoint(anchorPoint);
}

WebFloatPoint WebLayerImpl::anchorPoint() const
{
    return m_layer->anchorPoint();
}

void WebLayerImpl::setAnchorPointZ(float anchorPointZ)
{
    m_layer->setAnchorPointZ(anchorPointZ);
}

float WebLayerImpl::anchorPointZ() const
{
    return m_layer->anchorPointZ();
}

void WebLayerImpl::setBounds(const WebSize& size)
{
    m_layer->setBounds(size);
}

WebSize WebLayerImpl::bounds() const
{
    return m_layer->bounds();
}

void WebLayerImpl::setMasksToBounds(bool masksToBounds)
{
    m_layer->setMasksToBounds(masksToBounds);
}

bool WebLayerImpl::masksToBounds() const
{
    return m_layer->masksToBounds();
}

void WebLayerImpl::setMaskLayer(WebLayer* maskLayer)
{
    m_layer->setMaskLayer(maskLayer ? static_cast<WebLayerImpl*>(maskLayer)->layer() : 0);
}

void WebLayerImpl::setReplicaLayer(WebLayer* replicaLayer)
{
    m_layer->setReplicaLayer(replicaLayer ? static_cast<WebLayerImpl*>(replicaLayer)->layer() : 0);
}

void WebLayerImpl::setOpacity(float opacity)
{
    m_layer->setOpacity(opacity);
}

float WebLayerImpl::opacity() const
{
    return m_layer->opacity();
}

void WebLayerImpl::setOpaque(bool opaque)
{
    m_layer->setContentsOpaque(opaque);
}

bool WebLayerImpl::opaque() const
{
    return m_layer->contentsOpaque();
}

void WebLayerImpl::setPosition(const WebFloatPoint& position)
{
    m_layer->setPosition(position);
}

WebFloatPoint WebLayerImpl::position() const
{
    return m_layer->position();
}

void WebLayerImpl::setSublayerTransform(const SkMatrix44& matrix)
{
    m_layer->setSublayerTransform(transformationMatrixFromSkMatrix44(matrix));
}

void WebLayerImpl::setSublayerTransform(const WebTransformationMatrix& matrix)
{
    m_layer->setSublayerTransform(matrix);
}

SkMatrix44 WebLayerImpl::sublayerTransform() const
{
    return skMatrix44FromTransformationMatrix(m_layer->sublayerTransform());
}

void WebLayerImpl::setTransform(const SkMatrix44& matrix)
{
    m_layer->setTransform(transformationMatrixFromSkMatrix44(matrix));
}

void WebLayerImpl::setTransform(const WebTransformationMatrix& matrix)
{
    m_layer->setTransform(matrix);
}

SkMatrix44 WebLayerImpl::transform() const
{
    return skMatrix44FromTransformationMatrix(m_layer->transform());
}

void WebLayerImpl::setDrawsContent(bool drawsContent)
{
    m_layer->setIsDrawable(drawsContent);
}

bool WebLayerImpl::drawsContent() const
{
    return m_layer->drawsContent();
}

void WebLayerImpl::setPreserves3D(bool preserve3D)
{
    m_layer->setPreserves3D(preserve3D);
}

void WebLayerImpl::setUseParentBackfaceVisibility(bool useParentBackfaceVisibility)
{
    m_layer->setUseParentBackfaceVisibility(useParentBackfaceVisibility);
}

void WebLayerImpl::setBackgroundColor(WebColor color)
{
    m_layer->setBackgroundColor(color);
}

WebColor WebLayerImpl::backgroundColor() const
{
    return m_layer->backgroundColor();
}

void WebLayerImpl::setFilters(const WebFilterOperations& filters)
{
    m_layer->setFilters(filters);
}

void WebLayerImpl::setBackgroundFilters(const WebFilterOperations& filters)
{
    m_layer->setBackgroundFilters(filters);
}

void WebLayerImpl::setFilter(SkImageFilter* filter)
{
    m_layer->setFilter(filter);
}

void WebLayerImpl::setDebugBorderColor(const WebColor& color)
{
    NOTREACHED();
}

void WebLayerImpl::setDebugBorderWidth(float width)
{
    NOTREACHED();
}

void WebLayerImpl::setDebugName(WebString name)
{
    m_layer->setDebugName(UTF16ToASCII(string16(name.data(), name.length())));
}

void WebLayerImpl::setAnimationDelegate(WebAnimationDelegate* delegate)
{
    m_layer->setLayerAnimationDelegate(delegate);
}

bool WebLayerImpl::addAnimation(WebAnimation* animation)
{
    return m_layer->addAnimation(static_cast<WebAnimationImpl*>(animation)->cloneToAnimation());
}

void WebLayerImpl::removeAnimation(int animationId)
{
    m_layer->removeAnimation(animationId);
}

void WebLayerImpl::removeAnimation(int animationId, WebAnimation::TargetProperty targetProperty)
{
    m_layer->layerAnimationController()->removeAnimation(animationId, static_cast<ActiveAnimation::TargetProperty>(targetProperty));
}

void WebLayerImpl::pauseAnimation(int animationId, double timeOffset)
{
    m_layer->pauseAnimation(animationId, timeOffset);
}

void WebLayerImpl::suspendAnimations(double monotonicTime)
{
    m_layer->suspendAnimations(monotonicTime);
}

void WebLayerImpl::resumeAnimations(double monotonicTime)
{
    m_layer->resumeAnimations(monotonicTime);
}

bool WebLayerImpl::hasActiveAnimation()
{
    return m_layer->hasActiveAnimation();
}

void WebLayerImpl::transferAnimationsTo(WebLayer* other)
{
    DCHECK(other);
    static_cast<WebLayerImpl*>(other)->m_layer->setLayerAnimationController(m_layer->releaseLayerAnimationController());
}

void WebLayerImpl::setForceRenderSurface(bool forceRenderSurface)
{
    m_layer->setForceRenderSurface(forceRenderSurface);
}

void WebLayerImpl::setScrollPosition(WebPoint position)
{
    m_layer->setScrollOffset(gfx::Point(position).OffsetFromOrigin());
}

WebPoint WebLayerImpl::scrollPosition() const
{
    return gfx::PointAtOffsetFromOrigin(m_layer->scrollOffset());
}

void WebLayerImpl::setMaxScrollPosition(WebSize maxScrollPosition)
{
    m_layer->setMaxScrollOffset(maxScrollPosition);
}

WebSize WebLayerImpl::maxScrollPosition() const
{
    return m_layer->maxScrollOffset();
}

void WebLayerImpl::setScrollable(bool scrollable)
{
    m_layer->setScrollable(scrollable);
}

bool WebLayerImpl::scrollable() const
{
    return m_layer->scrollable();
}

void WebLayerImpl::setHaveWheelEventHandlers(bool haveWheelEventHandlers)
{
    m_layer->setHaveWheelEventHandlers(haveWheelEventHandlers);
}

bool WebLayerImpl::haveWheelEventHandlers() const
{
    return m_layer->haveWheelEventHandlers();
}

void WebLayerImpl::setShouldScrollOnMainThread(bool shouldScrollOnMainThread)
{
    m_layer->setShouldScrollOnMainThread(shouldScrollOnMainThread);
}

bool WebLayerImpl::shouldScrollOnMainThread() const
{
    return m_layer->shouldScrollOnMainThread();
}

void WebLayerImpl::setNonFastScrollableRegion(const WebVector<WebRect>& rects)
{
    cc::Region region;
    for (size_t i = 0; i < rects.size(); ++i)
        region.Union(rects[i]);
    m_layer->setNonFastScrollableRegion(region);
}

WebVector<WebRect> WebLayerImpl::nonFastScrollableRegion() const
{
    size_t numRects = 0;
    for (cc::Region::Iterator regionRects(m_layer->nonFastScrollableRegion()); regionRects.has_rect(); regionRects.next())
        ++numRects;

    WebVector<WebRect> result(numRects);
    size_t i = 0;
    for (cc::Region::Iterator regionRects(m_layer->nonFastScrollableRegion()); regionRects.has_rect(); regionRects.next()) {
        result[i] = regionRects.rect();
        ++i;
    }
    return result;
}

void WebLayerImpl::setTouchEventHandlerRegion(const WebVector<WebRect>& rects)
{
    cc::Region region;
    for (size_t i = 0; i < rects.size(); ++i)
        region.Union(rects[i]);
    m_layer->setTouchEventHandlerRegion(region);
}

WebVector<WebRect> WebLayerImpl::touchEventHandlerRegion() const
{
    size_t numRects = 0;
    for (cc::Region::Iterator regionRects(m_layer->touchEventHandlerRegion()); regionRects.has_rect(); regionRects.next())
        ++numRects;

    WebVector<WebRect> result(numRects);
    size_t i = 0;
    for (cc::Region::Iterator regionRects(m_layer->touchEventHandlerRegion()); regionRects.has_rect(); regionRects.next()) {
        result[i] = regionRects.rect();
        ++i;
    }
    return result;
}

void WebLayerImpl::setIsContainerForFixedPositionLayers(bool enable)
{
    m_layer->setIsContainerForFixedPositionLayers(enable);
}

bool WebLayerImpl::isContainerForFixedPositionLayers() const
{
    return m_layer->isContainerForFixedPositionLayers();
}

void WebLayerImpl::setFixedToContainerLayer(bool enable)
{
    m_layer->setFixedToContainerLayer(enable);
}

bool WebLayerImpl::fixedToContainerLayer() const
{
    return m_layer->fixedToContainerLayer();
}

void WebLayerImpl::setScrollClient(WebLayerScrollClient* scrollClient)
{
    m_layer->setLayerScrollClient(scrollClient);
}

Layer* WebLayerImpl::layer() const
{
    return m_layer.get();
}

} // namespace WebKit
<commit_msg>cc: Remove some more unneeded asserts for methods that the UI compositor is using temporarily during transition to the new debug borders.<commit_after>// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "web_layer_impl.h"

#include "SkMatrix44.h"
#ifdef LOG
#undef LOG
#endif
#include "base/string_util.h"
#include "cc/active_animation.h"
#include "cc/layer.h"
#include "cc/region.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebFloatPoint.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebFloatRect.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebTransformationMatrix.h"
#include "web_animation_impl.h"

using cc::ActiveAnimation;
using cc::Layer;

namespace WebKit {

namespace {

WebTransformationMatrix transformationMatrixFromSkMatrix44(const SkMatrix44& matrix)
{
    double data[16];
    matrix.asColMajord(data);
    return WebTransformationMatrix(data[0], data[1], data[2], data[3],
                                   data[4], data[5], data[6], data[7],
                                   data[8], data[9], data[10], data[11],
                                   data[12], data[13], data[14], data[15]);
}

SkMatrix44 skMatrix44FromTransformationMatrix(const WebTransformationMatrix& matrix)
{
    SkMatrix44 skMatrix;
    skMatrix.set(0, 0, SkDoubleToMScalar(matrix.m11()));
    skMatrix.set(1, 0, SkDoubleToMScalar(matrix.m12()));
    skMatrix.set(2, 0, SkDoubleToMScalar(matrix.m13()));
    skMatrix.set(3, 0, SkDoubleToMScalar(matrix.m14()));
    skMatrix.set(0, 1, SkDoubleToMScalar(matrix.m21()));
    skMatrix.set(1, 1, SkDoubleToMScalar(matrix.m22()));
    skMatrix.set(2, 1, SkDoubleToMScalar(matrix.m23()));
    skMatrix.set(3, 1, SkDoubleToMScalar(matrix.m24()));
    skMatrix.set(0, 2, SkDoubleToMScalar(matrix.m31()));
    skMatrix.set(1, 2, SkDoubleToMScalar(matrix.m32()));
    skMatrix.set(2, 2, SkDoubleToMScalar(matrix.m33()));
    skMatrix.set(3, 2, SkDoubleToMScalar(matrix.m34()));
    skMatrix.set(0, 3, SkDoubleToMScalar(matrix.m41()));
    skMatrix.set(1, 3, SkDoubleToMScalar(matrix.m42()));
    skMatrix.set(2, 3, SkDoubleToMScalar(matrix.m43()));
    skMatrix.set(3, 3, SkDoubleToMScalar(matrix.m44()));
    return skMatrix;
}

}

WebLayer* WebLayer::create()
{
    return new WebLayerImpl();
}

WebLayerImpl::WebLayerImpl()
    : m_layer(Layer::create())
{
}

WebLayerImpl::WebLayerImpl(scoped_refptr<Layer> layer)
    : m_layer(layer)
{
}

WebLayerImpl::~WebLayerImpl()
{
    m_layer->clearRenderSurface();
    m_layer->setLayerAnimationDelegate(0);
}

int WebLayerImpl::id() const
{
    return m_layer->id();
}

void WebLayerImpl::invalidateRect(const WebFloatRect& rect)
{
    m_layer->setNeedsDisplayRect(rect);
}

void WebLayerImpl::invalidate()
{
    m_layer->setNeedsDisplay();
}

void WebLayerImpl::addChild(WebLayer* child)
{
    m_layer->addChild(static_cast<WebLayerImpl*>(child)->layer());
}

void WebLayerImpl::insertChild(WebLayer* child, size_t index)
{
    m_layer->insertChild(static_cast<WebLayerImpl*>(child)->layer(), index);
}

void WebLayerImpl::replaceChild(WebLayer* reference, WebLayer* newLayer)
{
    m_layer->replaceChild(static_cast<WebLayerImpl*>(reference)->layer(), static_cast<WebLayerImpl*>(newLayer)->layer());
}

void WebLayerImpl::removeFromParent()
{
    m_layer->removeFromParent();
}

void WebLayerImpl::removeAllChildren()
{
    m_layer->removeAllChildren();
}

void WebLayerImpl::setAnchorPoint(const WebFloatPoint& anchorPoint)
{
    m_layer->setAnchorPoint(anchorPoint);
}

WebFloatPoint WebLayerImpl::anchorPoint() const
{
    return m_layer->anchorPoint();
}

void WebLayerImpl::setAnchorPointZ(float anchorPointZ)
{
    m_layer->setAnchorPointZ(anchorPointZ);
}

float WebLayerImpl::anchorPointZ() const
{
    return m_layer->anchorPointZ();
}

void WebLayerImpl::setBounds(const WebSize& size)
{
    m_layer->setBounds(size);
}

WebSize WebLayerImpl::bounds() const
{
    return m_layer->bounds();
}

void WebLayerImpl::setMasksToBounds(bool masksToBounds)
{
    m_layer->setMasksToBounds(masksToBounds);
}

bool WebLayerImpl::masksToBounds() const
{
    return m_layer->masksToBounds();
}

void WebLayerImpl::setMaskLayer(WebLayer* maskLayer)
{
    m_layer->setMaskLayer(maskLayer ? static_cast<WebLayerImpl*>(maskLayer)->layer() : 0);
}

void WebLayerImpl::setReplicaLayer(WebLayer* replicaLayer)
{
    m_layer->setReplicaLayer(replicaLayer ? static_cast<WebLayerImpl*>(replicaLayer)->layer() : 0);
}

void WebLayerImpl::setOpacity(float opacity)
{
    m_layer->setOpacity(opacity);
}

float WebLayerImpl::opacity() const
{
    return m_layer->opacity();
}

void WebLayerImpl::setOpaque(bool opaque)
{
    m_layer->setContentsOpaque(opaque);
}

bool WebLayerImpl::opaque() const
{
    return m_layer->contentsOpaque();
}

void WebLayerImpl::setPosition(const WebFloatPoint& position)
{
    m_layer->setPosition(position);
}

WebFloatPoint WebLayerImpl::position() const
{
    return m_layer->position();
}

void WebLayerImpl::setSublayerTransform(const SkMatrix44& matrix)
{
    m_layer->setSublayerTransform(transformationMatrixFromSkMatrix44(matrix));
}

void WebLayerImpl::setSublayerTransform(const WebTransformationMatrix& matrix)
{
    m_layer->setSublayerTransform(matrix);
}

SkMatrix44 WebLayerImpl::sublayerTransform() const
{
    return skMatrix44FromTransformationMatrix(m_layer->sublayerTransform());
}

void WebLayerImpl::setTransform(const SkMatrix44& matrix)
{
    m_layer->setTransform(transformationMatrixFromSkMatrix44(matrix));
}

void WebLayerImpl::setTransform(const WebTransformationMatrix& matrix)
{
    m_layer->setTransform(matrix);
}

SkMatrix44 WebLayerImpl::transform() const
{
    return skMatrix44FromTransformationMatrix(m_layer->transform());
}

void WebLayerImpl::setDrawsContent(bool drawsContent)
{
    m_layer->setIsDrawable(drawsContent);
}

bool WebLayerImpl::drawsContent() const
{
    return m_layer->drawsContent();
}

void WebLayerImpl::setPreserves3D(bool preserve3D)
{
    m_layer->setPreserves3D(preserve3D);
}

void WebLayerImpl::setUseParentBackfaceVisibility(bool useParentBackfaceVisibility)
{
    m_layer->setUseParentBackfaceVisibility(useParentBackfaceVisibility);
}

void WebLayerImpl::setBackgroundColor(WebColor color)
{
    m_layer->setBackgroundColor(color);
}

WebColor WebLayerImpl::backgroundColor() const
{
    return m_layer->backgroundColor();
}

void WebLayerImpl::setFilters(const WebFilterOperations& filters)
{
    m_layer->setFilters(filters);
}

void WebLayerImpl::setBackgroundFilters(const WebFilterOperations& filters)
{
    m_layer->setBackgroundFilters(filters);
}

void WebLayerImpl::setFilter(SkImageFilter* filter)
{
    m_layer->setFilter(filter);
}

void WebLayerImpl::setDebugBorderColor(const WebColor& color)
{
}

void WebLayerImpl::setDebugBorderWidth(float width)
{
}

void WebLayerImpl::setDebugName(WebString name)
{
    m_layer->setDebugName(UTF16ToASCII(string16(name.data(), name.length())));
}

void WebLayerImpl::setAnimationDelegate(WebAnimationDelegate* delegate)
{
    m_layer->setLayerAnimationDelegate(delegate);
}

bool WebLayerImpl::addAnimation(WebAnimation* animation)
{
    return m_layer->addAnimation(static_cast<WebAnimationImpl*>(animation)->cloneToAnimation());
}

void WebLayerImpl::removeAnimation(int animationId)
{
    m_layer->removeAnimation(animationId);
}

void WebLayerImpl::removeAnimation(int animationId, WebAnimation::TargetProperty targetProperty)
{
    m_layer->layerAnimationController()->removeAnimation(animationId, static_cast<ActiveAnimation::TargetProperty>(targetProperty));
}

void WebLayerImpl::pauseAnimation(int animationId, double timeOffset)
{
    m_layer->pauseAnimation(animationId, timeOffset);
}

void WebLayerImpl::suspendAnimations(double monotonicTime)
{
    m_layer->suspendAnimations(monotonicTime);
}

void WebLayerImpl::resumeAnimations(double monotonicTime)
{
    m_layer->resumeAnimations(monotonicTime);
}

bool WebLayerImpl::hasActiveAnimation()
{
    return m_layer->hasActiveAnimation();
}

void WebLayerImpl::transferAnimationsTo(WebLayer* other)
{
    DCHECK(other);
    static_cast<WebLayerImpl*>(other)->m_layer->setLayerAnimationController(m_layer->releaseLayerAnimationController());
}

void WebLayerImpl::setForceRenderSurface(bool forceRenderSurface)
{
    m_layer->setForceRenderSurface(forceRenderSurface);
}

void WebLayerImpl::setScrollPosition(WebPoint position)
{
    m_layer->setScrollOffset(gfx::Point(position).OffsetFromOrigin());
}

WebPoint WebLayerImpl::scrollPosition() const
{
    return gfx::PointAtOffsetFromOrigin(m_layer->scrollOffset());
}

void WebLayerImpl::setMaxScrollPosition(WebSize maxScrollPosition)
{
    m_layer->setMaxScrollOffset(maxScrollPosition);
}

WebSize WebLayerImpl::maxScrollPosition() const
{
    return m_layer->maxScrollOffset();
}

void WebLayerImpl::setScrollable(bool scrollable)
{
    m_layer->setScrollable(scrollable);
}

bool WebLayerImpl::scrollable() const
{
    return m_layer->scrollable();
}

void WebLayerImpl::setHaveWheelEventHandlers(bool haveWheelEventHandlers)
{
    m_layer->setHaveWheelEventHandlers(haveWheelEventHandlers);
}

bool WebLayerImpl::haveWheelEventHandlers() const
{
    return m_layer->haveWheelEventHandlers();
}

void WebLayerImpl::setShouldScrollOnMainThread(bool shouldScrollOnMainThread)
{
    m_layer->setShouldScrollOnMainThread(shouldScrollOnMainThread);
}

bool WebLayerImpl::shouldScrollOnMainThread() const
{
    return m_layer->shouldScrollOnMainThread();
}

void WebLayerImpl::setNonFastScrollableRegion(const WebVector<WebRect>& rects)
{
    cc::Region region;
    for (size_t i = 0; i < rects.size(); ++i)
        region.Union(rects[i]);
    m_layer->setNonFastScrollableRegion(region);
}

WebVector<WebRect> WebLayerImpl::nonFastScrollableRegion() const
{
    size_t numRects = 0;
    for (cc::Region::Iterator regionRects(m_layer->nonFastScrollableRegion()); regionRects.has_rect(); regionRects.next())
        ++numRects;

    WebVector<WebRect> result(numRects);
    size_t i = 0;
    for (cc::Region::Iterator regionRects(m_layer->nonFastScrollableRegion()); regionRects.has_rect(); regionRects.next()) {
        result[i] = regionRects.rect();
        ++i;
    }
    return result;
}

void WebLayerImpl::setTouchEventHandlerRegion(const WebVector<WebRect>& rects)
{
    cc::Region region;
    for (size_t i = 0; i < rects.size(); ++i)
        region.Union(rects[i]);
    m_layer->setTouchEventHandlerRegion(region);
}

WebVector<WebRect> WebLayerImpl::touchEventHandlerRegion() const
{
    size_t numRects = 0;
    for (cc::Region::Iterator regionRects(m_layer->touchEventHandlerRegion()); regionRects.has_rect(); regionRects.next())
        ++numRects;

    WebVector<WebRect> result(numRects);
    size_t i = 0;
    for (cc::Region::Iterator regionRects(m_layer->touchEventHandlerRegion()); regionRects.has_rect(); regionRects.next()) {
        result[i] = regionRects.rect();
        ++i;
    }
    return result;
}

void WebLayerImpl::setIsContainerForFixedPositionLayers(bool enable)
{
    m_layer->setIsContainerForFixedPositionLayers(enable);
}

bool WebLayerImpl::isContainerForFixedPositionLayers() const
{
    return m_layer->isContainerForFixedPositionLayers();
}

void WebLayerImpl::setFixedToContainerLayer(bool enable)
{
    m_layer->setFixedToContainerLayer(enable);
}

bool WebLayerImpl::fixedToContainerLayer() const
{
    return m_layer->fixedToContainerLayer();
}

void WebLayerImpl::setScrollClient(WebLayerScrollClient* scrollClient)
{
    m_layer->setLayerScrollClient(scrollClient);
}

Layer* WebLayerImpl::layer() const
{
    return m_layer.get();
}

} // namespace WebKit
<|endoftext|>


<commit_before>//---------------------------------------------------------
// Copyright 2017 Ontario Institute for Cancer Research
// Written by Jared Simpson (jared.simpson@oicr.on.ca)
//---------------------------------------------------------
//
// nanopolish_phase_reads -- phase variants onto reads
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <inttypes.h>
#include <assert.h>
#include <cmath>
#include <sys/time.h>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <set>
#include <map>
#include <omp.h>
#include <getopt.h>
#include <cstddef>
#include "htslib/faidx.h"
#include "nanopolish_iupac.h"
#include "nanopolish_poremodel.h"
#include "nanopolish_transition_parameters.h"
#include "nanopolish_profile_hmm.h"
#include "nanopolish_pore_model_set.h"
#include "nanopolish_variant.h"
#include "nanopolish_haplotype.h"
#include "nanopolish_alignment_db.h"
#include "nanopolish_bam_processor.h"
#include "nanopolish_bam_utils.h"
#include "nanopolish_index.h"
#include "H5pubconf.h"
#include "profiler.h"
#include "progress.h"
#include "logger.hpp"

using namespace std::placeholders;

//
// structs
//

//
// Getopt
//
#define SUBPROGRAM "phase-reads"

static const char *PHASE_READS_VERSION_MESSAGE =
SUBPROGRAM " Version " PACKAGE_VERSION "\n"
"Written by Jared Simpson.\n"
"\n"
"Copyright 2017 Ontario Institute for Cancer Research\n";

static const char *PHASE_READS_USAGE_MESSAGE =
"Usage: " PACKAGE_NAME " " SUBPROGRAM " [OPTIONS] --reads reads.fa --bam alignments.bam --genome genome.fa variants.vcf\n"
"Train a duration model\n"
"\n"
"  -v, --verbose                        display verbose output\n"
"      --version                        display version\n"
"      --help                           display this help and exit\n"
"  -r, --reads=FILE                     the 2D ONT reads are in fasta FILE\n"
"  -b, --bam=FILE                       the reads aligned to the genome assembly are in bam FILE\n"
"  -g, --genome=FILE                    the reference genome is in FILE\n"
"  -w, --window=STR                     only phase reads in the window STR (format: ctg:start-end)\n"
"  -t, --threads=NUM                    use NUM threads (default: 1)\n"
"      --progress                       print out a progress message\n"
"\nReport bugs to " PACKAGE_BUGREPORT "\n\n";

namespace opt
{
    static unsigned int verbose;
    static std::string reads_file;
    static std::string bam_file;
    static std::string genome_file;
    static std::string variants_file;
    static std::string region;

    static unsigned progress = 0;
    static unsigned num_threads = 1;
    static unsigned batch_size = 128;
    static int min_flanking_sequence = 30;
}

static const char* shortopts = "r:b:g:t:w:v";

enum { OPT_HELP = 1,
       OPT_VERSION,
       OPT_PROGRESS,
       OPT_LOG_LEVEL
     };

static const struct option longopts[] = {
    { "verbose",            no_argument,       NULL, 'v' },
    { "reads",              required_argument, NULL, 'r' },
    { "bam",                required_argument, NULL, 'b' },
    { "genome",             required_argument, NULL, 'g' },
    { "threads",            required_argument, NULL, 't' },
    { "window",             required_argument, NULL, 'w' },
    { "progress",           no_argument,       NULL, OPT_PROGRESS },
    { "help",               no_argument,       NULL, OPT_HELP },
    { "version",            no_argument,       NULL, OPT_VERSION },
    { "log-level",          required_argument, NULL, OPT_LOG_LEVEL },
    { NULL, 0, NULL, 0 }
};

void parse_phase_reads_options(int argc, char** argv)
{
    bool die = false;
    for (char c; (c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1;) {
        std::istringstream arg(optarg != NULL ? optarg : "");
        switch (c) {
            case 'r': arg >> opt::reads_file; break;
            case 'g': arg >> opt::genome_file; break;
            case 'b': arg >> opt::bam_file; break;
            case 'w': arg >> opt::region; break;
            case '?': die = true; break;
            case 't': arg >> opt::num_threads; break;
            case 'v': opt::verbose++; break;
            case OPT_PROGRESS: opt::progress = true; break;
            case OPT_HELP:
                std::cout << PHASE_READS_USAGE_MESSAGE;
                exit(EXIT_SUCCESS);
            case OPT_VERSION:
                std::cout << PHASE_READS_VERSION_MESSAGE;
                exit(EXIT_SUCCESS);
            case OPT_LOG_LEVEL:
                logger::Logger::set_level_from_option(arg.str());
                break;
        }
    }

    if(argc - optind > 0) {
        opt::variants_file = argv[optind++];
    } else {
        fprintf(stderr, "Error, variants file is missing\n");
        die = true;
    }

    if (argc - optind > 0) {
        std::cerr << SUBPROGRAM ": too many arguments\n";
        die = true;
    }

    if(opt::num_threads <= 0) {
        std::cerr << SUBPROGRAM ": invalid number of threads: " << opt::num_threads << "\n";
        die = true;
    }

    if(opt::reads_file.empty()) {
        std::cerr << SUBPROGRAM ": a --reads file must be provided\n";
        die = true;
    }

    if(opt::genome_file.empty()) {
        std::cerr << SUBPROGRAM ": a --genome file must be provided\n";
        die = true;
    }

    if(opt::bam_file.empty()) {
        std::cerr << SUBPROGRAM ": a --bam file must be provided\n";
        die = true;
    }

    if (die) {
        std::cout << "\n" << PHASE_READS_USAGE_MESSAGE;
        exit(EXIT_FAILURE);
    }
}

void phase_single_read(const ReadDB& read_db,
                       const faidx_t* fai,
                       const std::vector<Variant>& variants,
                       samFile* sam_fp,
                       const bam_hdr_t* hdr,
                       const bam1_t* record,
                       size_t read_idx,
                       int region_start,
                       int region_end)
{
    const double MAX_Q_SCORE = 30;
    const double BAM_Q_OFFSET = 0;
    int tid = omp_get_thread_num();
    uint32_t alignment_flags = HAF_ALLOW_PRE_CLIP | HAF_ALLOW_POST_CLIP;

    // Load a squiggle read for the mapped read
    std::string read_name = bam_get_qname(record);

    // load read
    SquiggleRead sr(read_name, read_db);

    std::string ref_name = hdr->target_name[record->core.tid];
    int alignment_start_pos = record->core.pos;
    int alignment_end_pos = bam_endpos(record);

    // Search the variant collection for the index of the first/last variants to phase
    Variant lower_search;
    lower_search.ref_name = ref_name;
    lower_search.ref_position = alignment_start_pos;
    auto lower_iter = std::lower_bound(variants.begin(), variants.end(), lower_search, sortByPosition);

    Variant upper_search;
    upper_search.ref_name = ref_name;
    upper_search.ref_position = alignment_end_pos;
    auto upper_iter = std::upper_bound(variants.begin(), variants.end(), upper_search, sortByPosition);

    fprintf(stderr, "Phasing read %s %s:%u-%u %zu\n", read_name.c_str(), ref_name.c_str(), alignment_start_pos, alignment_end_pos, upper_iter - lower_iter);

    // no variants to phase?
    if(lower_iter == variants.end()) {
        return;
    }

    int fetched_len;
    std::string reference_seq = get_reference_region_ts(fai,
                                                        ref_name.c_str(),
                                                        alignment_start_pos,
                                                        alignment_end_pos,
                                                        &fetched_len);

    std::string read_outseq = reference_seq;
    std::string read_outqual(reference_seq.length(), MAX_Q_SCORE + BAM_Q_OFFSET);

    Haplotype reference_haplotype(ref_name, alignment_start_pos, reference_seq);
    for(size_t strand_idx = 0; strand_idx < NUM_STRANDS; ++strand_idx) {

        // skip if 1D reads and this is the wrong strand
        if(!sr.has_events_for_strand(strand_idx)) {
            continue;
        }

        // only phase using template strand
        if(strand_idx != 0) {
            continue;
        }

        SequenceAlignmentRecord seq_align_record(record);
        EventAlignmentRecord event_align_record(&sr, strand_idx, seq_align_record);

        //
        for(; lower_iter < upper_iter; ++lower_iter) {

            const Variant& v = *lower_iter;

            if(!v.is_snp()) {
                continue;
            }

            int calling_start = v.ref_position - opt::min_flanking_sequence;
            int calling_end = v.ref_position + opt::min_flanking_sequence;

            HMMInputData data;
            data.read = event_align_record.sr;
            data.strand = event_align_record.strand;
            data.rc = event_align_record.rc;
            data.event_stride = event_align_record.stride;
            data.pore_model = data.read->get_base_model(data.strand);

            int e1,e2;
            bool bounded = AlignmentDB::_find_by_ref_bounds(event_align_record.aligned_events,
                                                            calling_start,
                                                            calling_end,
                                                            e1,
                                                            e2);

            // The events of this read do not span the calling window, skip
            if(!bounded || fabs(e2 - e1) / (calling_start - calling_end) > MAX_EVENT_TO_BP_RATIO) {
                continue;
            }

            data.event_start_idx = e1;
            data.event_stop_idx = e2;

            Haplotype calling_haplotype =
                reference_haplotype.substr_by_reference(calling_start, calling_end);

            double ref_score = profile_hmm_score(calling_haplotype.get_sequence(), data, alignment_flags);
            bool good_haplotype = calling_haplotype.apply_variant(v);
            if(good_haplotype) {
                double alt_score = profile_hmm_score(calling_haplotype.get_sequence(), data, alignment_flags);
                double log_sum = add_logs(alt_score, ref_score);
                double log_p_ref = ref_score - log_sum;
                double log_p_alt = alt_score - log_sum;
                char call;
                double log_p_wrong;
                if(alt_score > ref_score) {
                     call = v.alt_seq[0];
                     log_p_wrong = log_p_ref;
                } else {
                    call = v.ref_seq[0];
                    log_p_wrong = log_p_alt;
                }

                double q_score = -10 * log_p_wrong / log(10);
                q_score = std::min(MAX_Q_SCORE, q_score);
                char q_char = (int)q_score + 33;
                //fprintf(stderr, "\t%s score: %.2lf %.2lf %c p_wrong: %.3lf Q: %d QC: %c\n", v.key().c_str(), ref_score, alt_score, call, log_p_wrong, (int)q_score, q_char);

                int out_position = v.ref_position - alignment_start_pos;
                assert(read_outseq[out_position] == v.ref_seq[0]);
                read_outseq[out_position] = call;
                read_outqual[out_position] = q_char;
            }
        }

        // Construct the output bam record
        bam1_t* out_record = bam_init1();

        // basic stats
        out_record->core.tid = record->core.tid;
        out_record->core.pos = alignment_start_pos;
        out_record->core.qual = record->core.qual;
        out_record->core.flag = record->core.flag;

        // no read pairs
        out_record->core.mtid = -1;
        out_record->core.mpos = -1;
        out_record->core.isize = 0;

        std::vector<uint32_t> cigar;
        uint32_t cigar_op = read_outseq.size() << BAM_CIGAR_SHIFT | BAM_CMATCH;
        cigar.push_back(cigar_op);
        write_bam_vardata(out_record, read_name, cigar, read_outseq, read_outqual);

        #pragma omp critical
        {
            sam_write1(sam_fp, hdr, out_record);
        }
        bam_destroy1(out_record); // automatically frees malloc'd segment

    } // for strand
}

int phase_reads_main(int argc, char** argv)
{
    parse_phase_reads_options(argc, argv);
    omp_set_num_threads(opt::num_threads);

    ReadDB read_db;
    read_db.load(opt::reads_file);

    // load reference fai file
    faidx_t *fai = fai_load(opt::genome_file.c_str());

    std::vector<Variant> variants;
    if(!opt::region.empty()) {
        std::string contig;
        int start_base;
        int end_base;
        parse_region_string(opt::region, contig, start_base, end_base);

        // Read the variants for this region
        variants = read_variants_for_region(opt::variants_file, contig, start_base, end_base);
    } else {
         variants = read_variants_from_file(opt::variants_file);
    }

    // Sort variants by reference coordinate
    std::sort(variants.begin(), variants.end(), sortByPosition);

    // remove hom reference
    auto new_end = std::remove_if(variants.begin(), variants.end(), [](Variant v) { return v.genotype == "0/0"; });
    variants.erase( new_end, variants.end());

    samFile* sam_out = sam_open("-", "w");

    // the BamProcessor framework calls the input function with the
    // bam record, read index, etc passed as parameters
    // bind the other parameters the worker function needs here
    auto f = std::bind(phase_single_read, std::ref(read_db), std::ref(fai), std::ref(variants), sam_out, _1, _2, _3, _4, _5);
    BamProcessor processor(opt::bam_file, opt::region, opt::num_threads);

    // Copy the bam header to std
    sam_hdr_write(sam_out, processor.get_bam_header());

    processor.parallel_run(f);

    fai_destroy(fai);
    sam_close(sam_out);

    return EXIT_SUCCESS;
}
<commit_msg>emit warning on unexpected reference base, do not print status unless verbose is set<commit_after>//---------------------------------------------------------
// Copyright 2017 Ontario Institute for Cancer Research
// Written by Jared Simpson (jared.simpson@oicr.on.ca)
//---------------------------------------------------------
//
// nanopolish_phase_reads -- phase variants onto reads
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <inttypes.h>
#include <assert.h>
#include <cmath>
#include <sys/time.h>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <set>
#include <map>
#include <omp.h>
#include <getopt.h>
#include <cstddef>
#include "htslib/faidx.h"
#include "nanopolish_iupac.h"
#include "nanopolish_poremodel.h"
#include "nanopolish_transition_parameters.h"
#include "nanopolish_profile_hmm.h"
#include "nanopolish_pore_model_set.h"
#include "nanopolish_variant.h"
#include "nanopolish_haplotype.h"
#include "nanopolish_alignment_db.h"
#include "nanopolish_bam_processor.h"
#include "nanopolish_bam_utils.h"
#include "nanopolish_index.h"
#include "H5pubconf.h"
#include "profiler.h"
#include "progress.h"
#include "logger.hpp"

using namespace std::placeholders;

//
// structs
//

//
// Getopt
//
#define SUBPROGRAM "phase-reads"

static const char *PHASE_READS_VERSION_MESSAGE =
SUBPROGRAM " Version " PACKAGE_VERSION "\n"
"Written by Jared Simpson.\n"
"\n"
"Copyright 2017 Ontario Institute for Cancer Research\n";

static const char *PHASE_READS_USAGE_MESSAGE =
"Usage: " PACKAGE_NAME " " SUBPROGRAM " [OPTIONS] --reads reads.fa --bam alignments.bam --genome genome.fa variants.vcf\n"
"Train a duration model\n"
"\n"
"  -v, --verbose                        display verbose output\n"
"      --version                        display version\n"
"      --help                           display this help and exit\n"
"  -r, --reads=FILE                     the 2D ONT reads are in fasta FILE\n"
"  -b, --bam=FILE                       the reads aligned to the genome assembly are in bam FILE\n"
"  -g, --genome=FILE                    the reference genome is in FILE\n"
"  -w, --window=STR                     only phase reads in the window STR (format: ctg:start-end)\n"
"  -t, --threads=NUM                    use NUM threads (default: 1)\n"
"      --progress                       print out a progress message\n"
"\nReport bugs to " PACKAGE_BUGREPORT "\n\n";

namespace opt
{
    static unsigned int verbose;
    static std::string reads_file;
    static std::string bam_file;
    static std::string genome_file;
    static std::string variants_file;
    static std::string region;

    static unsigned progress = 0;
    static unsigned num_threads = 1;
    static unsigned batch_size = 128;
    static int min_flanking_sequence = 30;
}

static const char* shortopts = "r:b:g:t:w:v";

enum { OPT_HELP = 1,
       OPT_VERSION,
       OPT_PROGRESS,
       OPT_LOG_LEVEL
     };

static const struct option longopts[] = {
    { "verbose",            no_argument,       NULL, 'v' },
    { "reads",              required_argument, NULL, 'r' },
    { "bam",                required_argument, NULL, 'b' },
    { "genome",             required_argument, NULL, 'g' },
    { "threads",            required_argument, NULL, 't' },
    { "window",             required_argument, NULL, 'w' },
    { "progress",           no_argument,       NULL, OPT_PROGRESS },
    { "help",               no_argument,       NULL, OPT_HELP },
    { "version",            no_argument,       NULL, OPT_VERSION },
    { "log-level",          required_argument, NULL, OPT_LOG_LEVEL },
    { NULL, 0, NULL, 0 }
};

void parse_phase_reads_options(int argc, char** argv)
{
    bool die = false;
    for (char c; (c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1;) {
        std::istringstream arg(optarg != NULL ? optarg : "");
        switch (c) {
            case 'r': arg >> opt::reads_file; break;
            case 'g': arg >> opt::genome_file; break;
            case 'b': arg >> opt::bam_file; break;
            case 'w': arg >> opt::region; break;
            case '?': die = true; break;
            case 't': arg >> opt::num_threads; break;
            case 'v': opt::verbose++; break;
            case OPT_PROGRESS: opt::progress = true; break;
            case OPT_HELP:
                std::cout << PHASE_READS_USAGE_MESSAGE;
                exit(EXIT_SUCCESS);
            case OPT_VERSION:
                std::cout << PHASE_READS_VERSION_MESSAGE;
                exit(EXIT_SUCCESS);
            case OPT_LOG_LEVEL:
                logger::Logger::set_level_from_option(arg.str());
                break;
        }
    }

    if(argc - optind > 0) {
        opt::variants_file = argv[optind++];
    } else {
        fprintf(stderr, "Error, variants file is missing\n");
        die = true;
    }

    if (argc - optind > 0) {
        std::cerr << SUBPROGRAM ": too many arguments\n";
        die = true;
    }

    if(opt::num_threads <= 0) {
        std::cerr << SUBPROGRAM ": invalid number of threads: " << opt::num_threads << "\n";
        die = true;
    }

    if(opt::reads_file.empty()) {
        std::cerr << SUBPROGRAM ": a --reads file must be provided\n";
        die = true;
    }

    if(opt::genome_file.empty()) {
        std::cerr << SUBPROGRAM ": a --genome file must be provided\n";
        die = true;
    }

    if(opt::bam_file.empty()) {
        std::cerr << SUBPROGRAM ": a --bam file must be provided\n";
        die = true;
    }

    if (die) {
        std::cout << "\n" << PHASE_READS_USAGE_MESSAGE;
        exit(EXIT_FAILURE);
    }
}

void phase_single_read(const ReadDB& read_db,
                       const faidx_t* fai,
                       const std::vector<Variant>& variants,
                       samFile* sam_fp,
                       const bam_hdr_t* hdr,
                       const bam1_t* record,
                       size_t read_idx,
                       int region_start,
                       int region_end)
{
    const double MAX_Q_SCORE = 30;
    const double BAM_Q_OFFSET = 0;
    int tid = omp_get_thread_num();
    uint32_t alignment_flags = HAF_ALLOW_PRE_CLIP | HAF_ALLOW_POST_CLIP;

    // Load a squiggle read for the mapped read
    std::string read_name = bam_get_qname(record);

    // load read
    SquiggleRead sr(read_name, read_db);

    std::string ref_name = hdr->target_name[record->core.tid];
    int alignment_start_pos = record->core.pos;
    int alignment_end_pos = bam_endpos(record);

    // Search the variant collection for the index of the first/last variants to phase
    Variant lower_search;
    lower_search.ref_name = ref_name;
    lower_search.ref_position = alignment_start_pos;
    auto lower_iter = std::lower_bound(variants.begin(), variants.end(), lower_search, sortByPosition);

    Variant upper_search;
    upper_search.ref_name = ref_name;
    upper_search.ref_position = alignment_end_pos;
    auto upper_iter = std::upper_bound(variants.begin(), variants.end(), upper_search, sortByPosition);

    if(opt::verbose >= 1) {
        fprintf(stderr, "Phasing read %s %s:%u-%u %zu\n", read_name.c_str(), ref_name.c_str(), alignment_start_pos, alignment_end_pos, upper_iter - lower_iter);
    }

    // no variants to phase?
    if(lower_iter == variants.end()) {
        return;
    }

    int fetched_len;
    std::string reference_seq = get_reference_region_ts(fai,
                                                        ref_name.c_str(),
                                                        alignment_start_pos,
                                                        alignment_end_pos,
                                                        &fetched_len);

    std::string read_outseq = reference_seq;
    std::string read_outqual(reference_seq.length(), MAX_Q_SCORE + BAM_Q_OFFSET);

    Haplotype reference_haplotype(ref_name, alignment_start_pos, reference_seq);
    for(size_t strand_idx = 0; strand_idx < NUM_STRANDS; ++strand_idx) {

        // skip if 1D reads and this is the wrong strand
        if(!sr.has_events_for_strand(strand_idx)) {
            continue;
        }

        // only phase using template strand
        if(strand_idx != 0) {
            continue;
        }

        SequenceAlignmentRecord seq_align_record(record);
        EventAlignmentRecord event_align_record(&sr, strand_idx, seq_align_record);

        //
        for(; lower_iter < upper_iter; ++lower_iter) {

            const Variant& v = *lower_iter;

            if(!v.is_snp()) {
                continue;
            }

            int calling_start = v.ref_position - opt::min_flanking_sequence;
            int calling_end = v.ref_position + opt::min_flanking_sequence;

            HMMInputData data;
            data.read = event_align_record.sr;
            data.strand = event_align_record.strand;
            data.rc = event_align_record.rc;
            data.event_stride = event_align_record.stride;
            data.pore_model = data.read->get_base_model(data.strand);

            int e1,e2;
            bool bounded = AlignmentDB::_find_by_ref_bounds(event_align_record.aligned_events,
                                                            calling_start,
                                                            calling_end,
                                                            e1,
                                                            e2);

            // The events of this read do not span the calling window, skip
            if(!bounded || fabs(e2 - e1) / (calling_start - calling_end) > MAX_EVENT_TO_BP_RATIO) {
                continue;
            }

            data.event_start_idx = e1;
            data.event_stop_idx = e2;

            Haplotype calling_haplotype =
                reference_haplotype.substr_by_reference(calling_start, calling_end);

            double ref_score = profile_hmm_score(calling_haplotype.get_sequence(), data, alignment_flags);
            bool good_haplotype = calling_haplotype.apply_variant(v);
            if(good_haplotype) {
                double alt_score = profile_hmm_score(calling_haplotype.get_sequence(), data, alignment_flags);
                double log_sum = add_logs(alt_score, ref_score);
                double log_p_ref = ref_score - log_sum;
                double log_p_alt = alt_score - log_sum;
                char call;
                double log_p_wrong;
                if(alt_score > ref_score) {
                     call = v.alt_seq[0];
                     log_p_wrong = log_p_ref;
                } else {
                    call = v.ref_seq[0];
                    log_p_wrong = log_p_alt;
                }

                double q_score = -10 * log_p_wrong / log(10);
                q_score = std::min(MAX_Q_SCORE, q_score);
                char q_char = (int)q_score + 33;
                //fprintf(stderr, "\t%s score: %.2lf %.2lf %c p_wrong: %.3lf Q: %d QC: %c\n", v.key().c_str(), ref_score, alt_score, call, log_p_wrong, (int)q_score, q_char);

                int out_position = v.ref_position - alignment_start_pos;
                if(read_outseq[out_position] != v.ref_seq[0]) {
                    fprintf(stderr, "warning: reference base at position %d does not match variant record (%c != %c)\n",
                        v.ref_position, v.ref_seq[0], read_outseq[out_position]);
                }
                read_outseq[out_position] = call;
                read_outqual[out_position] = q_char;
            }
        }

        // Construct the output bam record
        bam1_t* out_record = bam_init1();

        // basic stats
        out_record->core.tid = record->core.tid;
        out_record->core.pos = alignment_start_pos;
        out_record->core.qual = record->core.qual;
        out_record->core.flag = record->core.flag;

        // no read pairs
        out_record->core.mtid = -1;
        out_record->core.mpos = -1;
        out_record->core.isize = 0;

        std::vector<uint32_t> cigar;
        uint32_t cigar_op = read_outseq.size() << BAM_CIGAR_SHIFT | BAM_CMATCH;
        cigar.push_back(cigar_op);
        write_bam_vardata(out_record, read_name, cigar, read_outseq, read_outqual);

        #pragma omp critical
        {
            sam_write1(sam_fp, hdr, out_record);
        }
        bam_destroy1(out_record); // automatically frees malloc'd segment

    } // for strand
}

int phase_reads_main(int argc, char** argv)
{
    parse_phase_reads_options(argc, argv);
    omp_set_num_threads(opt::num_threads);

    ReadDB read_db;
    read_db.load(opt::reads_file);

    // load reference fai file
    faidx_t *fai = fai_load(opt::genome_file.c_str());

    std::vector<Variant> variants;
    if(!opt::region.empty()) {
        std::string contig;
        int start_base;
        int end_base;
        parse_region_string(opt::region, contig, start_base, end_base);

        // Read the variants for this region
        variants = read_variants_for_region(opt::variants_file, contig, start_base, end_base);
    } else {
         variants = read_variants_from_file(opt::variants_file);
    }

    // Sort variants by reference coordinate
    std::sort(variants.begin(), variants.end(), sortByPosition);

    // remove hom reference
    auto new_end = std::remove_if(variants.begin(), variants.end(), [](Variant v) { return v.genotype == "0/0"; });
    variants.erase( new_end, variants.end());

    samFile* sam_out = sam_open("-", "w");

    // the BamProcessor framework calls the input function with the
    // bam record, read index, etc passed as parameters
    // bind the other parameters the worker function needs here
    auto f = std::bind(phase_single_read, std::ref(read_db), std::ref(fai), std::ref(variants), sam_out, _1, _2, _3, _4, _5);
    BamProcessor processor(opt::bam_file, opt::region, opt::num_threads);

    // Copy the bam header to std
    sam_hdr_write(sam_out, processor.get_bam_header());

    processor.parallel_run(f);

    fai_destroy(fai);
    sam_close(sam_out);

    return EXIT_SUCCESS;
}
<|endoftext|>