<commit_before>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 512
#define ACK 0
#define NAK 1
#define BUFSIZE 505
#define FILENAME "Testfile"
#define TIMEOUT 150 //in ms
#define WIN_SIZE 16

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
void loadWindow();
bool sendPacket();
bool getGet();

struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
Packet window[16];
int length;
bool dropPck;
bool delayPck;
int toms;
unsigned char b[BUFSIZE];
int base;

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  sendFile();

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  struct timeval timeout;
  timeout.tv_usec = TIMEOUT * 1000;
  toms = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  base = 0;
  dropPck = false;
  calen = sizeof(ca);

  getGet();

  return true;
}

bool getGet(){
	unsigned char packet[PAKSIZE + 1];
	rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
	return (rlen > 0) ? true : false;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == 0) return false; //doesn't matter, only for the old version (netwark)
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        //seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p (false, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

void loadWindow(){
	for(int i = base; i < base + WIN_SIZE; i++) {
		window[i-base] = createPacket(i);
		if(strlen(window[i-base].getDataBuffer()) < BUFSIZE && window[i-base].chksm()) {
			cout << "In loadWindow's secret base, index is: " << i-base << endl;
			for(++i; i < base + WIN_SIZE; i++){
				window[i-base].loadDataBuffer("\0");
			}

		}
		/*cout << "window[i-base] seq. num: " << window[i-base].getSequenceNum() << endl;*/
	}

		/*cout << "packet " << base + 1 << ": " << window[1].str() << endl;
		cout << "packet " << base + 2 << ": " << window[2].str() << endl;*/
}

bool sendFile() {
	/*Currently causes the program to only send the first 16 packets of file out
		requires additional code later to sendFile again with updated window*/
	fd_set stReadFDS;
	struct timeval stTimeOut;

	FD_ZERO(&stReadFDS);
	stTimeOut.tv_sec = 1;
	stTimeOut.tv_usec = 1000 * TIMEOUT;
	FD_SET(0, &stReadFDS);

	base = 0;
	int finale = -1;
	bool hasRead = false;
	while(base * BUFSIZE < length) {
		loadWindow();

		if(p.str()[0] == '\0') finale = p.getSequenceNum();
		for(int x = 0; x < WIN_SIZE; x++) {
			p = window[x];
			if(!sendPacket()) continue;
		}
		for(int x = 0; x < WIN_SIZE; x++) {
			FD_ZERO(&stReadFDS);
			stTimeOut.tv_sec = 1;
			stTimeOut.tv_usec = 1000 * TIMEOUT;
			FD_SET(0, &stReadFDS);

			cout << "begin loop no. " << x << endl;
			int t = select(1, &stReadFDS, NULL, NULL, &stTimeOut);
			if (t != 0) {
				if(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
					cout << "=== ACK TIMEOUT (recvfrom)" << endl;
				} else hasRead = true;
			} else {
				cout << "=== ACK TIMEOUT (select)" << endl;
			}
			if(!hasRead) continue;
			if(isAck()) {
				handleAck();
			} else {
				handleAck();
				//handleNak(x);
			}
			if(finale > 0 && base == finale) break;
			memset(b, 0, BUFSIZE);
		}

	}

	if(sendto(s, "\0", BUFSIZE, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Final package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
	}
	return true;
}

Packet createPacket(int index){
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
		cout << "Null terminated mstr." << endl;
		mstr[length - (index * BUFSIZE)] = '\0';
    }

	cout << "index: " << index << endl;
    return Packet (index, mstr.c_str());
}

bool sendPacket(){
	cout << endl;
    cout << "=== TRANSMISSION START" << endl;
	cout << "Sequence number before gremlin function: " << p.getSequenceNum() << endl;
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	if (dropPck == true) return false;
	if (delayPck == true) p.setAckNack(1);

    if(sendto(s, p.str(), BUFSIZE + 8, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}
bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {
	int ack = boost::lexical_cast<int>(b);
	if(base < ack) base = ack;
	cout << "Window base: " << base << endl;
}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){

  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  packStatus[0] = false;
  packStatus[1] = false;

  if(r <= (lossProb)){
	packStatus[0] = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  packStatus[1] = true;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
	cout << "Seq. num (pre-gremlin): " << pack->getSequenceNum() << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  //cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<commit_msg>okay done with that melodrama<commit_after>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 512
#define ACK 0
#define NAK 1
#define BUFSIZE 505
#define FILENAME "Testfile"
#define TIMEOUT 150 //in ms
#define WIN_SIZE 16

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
void loadWindow();
bool sendPacket();
bool getGet();

struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
Packet window[16];
int length;
bool dropPck;
bool delayPck;
int toms;
unsigned char b[BUFSIZE];
int base;

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  sendFile();

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  struct timeval timeout;
  timeout.tv_usec = TIMEOUT * 1000;
  toms = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  base = 0;
  dropPck = false;
  calen = sizeof(ca);

  getGet();

  return true;
}

bool getGet(){
	unsigned char packet[PAKSIZE + 1];
	rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
	return (rlen > 0) ? true : false;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == 0) return false; //doesn't matter, only for the old version (netwark)
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        //seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p (false, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

void loadWindow(){
	for(int i = base; i < base + WIN_SIZE; i++) {
		window[i-base] = createPacket(i);
		if(strlen(window[i-base].getDataBuffer()) < BUFSIZE && window[i-base].chksm()) {
			cout << "In loadWindow's secret base, index is: " << i-base << endl;
			for(++i; i < base + WIN_SIZE; i++){
				window[i-base].loadDataBuffer("\0");
			}

		}
		/*cout << "window[i-base] seq. num: " << window[i-base].getSequenceNum() << endl;*/
	}

		/*cout << "packet " << base + 1 << ": " << window[1].str() << endl;
		cout << "packet " << base + 2 << ": " << window[2].str() << endl;*/
}

bool sendFile() {
	/*Currently causes the program to only send the first 16 packets of file out
		requires additional code later to sendFile again with updated window*/
	fd_set stReadFDS;
	struct timeval stTimeOut;

	FD_ZERO(&stReadFDS);
	stTimeOut.tv_sec = 1;
	stTimeOut.tv_usec = 1000 * TIMEOUT;
	FD_SET(0, &stReadFDS);

	base = 0;
	int finale = -1;
	bool hasRead = false;
	while(base * BUFSIZE < length) {
		loadWindow();

		if(p.str()[0] == '\0') finale = p.getSequenceNum();
		for(int x = 0; x < WIN_SIZE; x++) {
			p = window[x];
			if(!sendPacket()) continue;
		}
		for(int x = 0; x < WIN_SIZE; x++) {
			FD_ZERO(&stReadFDS);
			stTimeOut.tv_sec = 1;
			stTimeOut.tv_usec = 1000 * TIMEOUT;
			FD_SET(s, &stReadFDS);

			cout << "begin loop no. " << x << endl;
			int t = select(1, &stReadFDS, NULL, NULL, &stTimeOut);
			if (t != 0) {
				if(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
					cout << "=== ACK TIMEOUT (recvfrom)" << endl;
				} else hasRead = true;
			} else {
				cout << "=== ACK TIMEOUT (select)" << endl;
			}
			if(!hasRead) continue;
			if(isAck()) {
				handleAck();
			} else {
				handleAck();
				//handleNak(x);
			}
			if(finale > 0 && base == finale) break;
			memset(b, 0, BUFSIZE);
		}

	}

	if(sendto(s, "\0", BUFSIZE, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Final package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
	}
	return true;
}

Packet createPacket(int index){
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
		cout << "Null terminated mstr." << endl;
		mstr[length - (index * BUFSIZE)] = '\0';
    }

	cout << "index: " << index << endl;
    return Packet (index, mstr.c_str());
}

bool sendPacket(){
	cout << endl;
    cout << "=== TRANSMISSION START" << endl;
	cout << "Sequence number before gremlin function: " << p.getSequenceNum() << endl;
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	if (dropPck == true) return false;
	if (delayPck == true) p.setAckNack(1);

    if(sendto(s, p.str(), BUFSIZE + 8, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}
bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {
	int ack = boost::lexical_cast<int>(b);
	if(base < ack) base = ack;
	cout << "Window base: " << base << endl;
}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){

  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  packStatus[0] = false;
  packStatus[1] = false;

  if(r <= (lossProb)){
	packStatus[0] = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  packStatus[1] = true;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
	cout << "Seq. num (pre-gremlin): " << pack->getSequenceNum() << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  //cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<|endoftext|>


<commit_before>/*
  pvsops.c: pvs and other spectral-based opcodes

  Copyright (C) 2017 Victor Lazzarini
  This file is part of Csound.

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

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

  You should have received a copy of the GNU Lesser General Public
  License along with Csound; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  02111-1307 USA
*/
#include <algorithm>
#include <plugin.h>

struct PVTrace : csnd::FPlugin<1, 2> {
  csnd::AuxMem<float> amps;
  static constexpr char const *otypes = "f";
  static constexpr char const *itypes = "fk";

  int init() {
    if (inargs.fsig_data(0).isSliding())
      return csound->init_error(Str("sliding not supported"));

    if (inargs.fsig_data(0).fsig_format() != csnd::fsig_format::pvs &&
        inargs.fsig_data(0).fsig_format() != csnd::fsig_format::polar)
      return csound->init_error(Str("fsig format not supported"));

    amps.allocate(csound, inargs.fsig_data(0).nbins());
    csnd::Fsig &fout = outargs.fsig_data(0);
    fout.init(csound, inargs.fsig_data(0));
    framecount = 0;
    return OK;
  }

  int kperf() {
    csnd::pv_frame &fin = inargs.fsig_data(0);
    csnd::pv_frame &fout = outargs.fsig_data(0);

    if (framecount < fin.count()) {
      int n = fin.len() - (int)inargs[1];
      float thrsh;
      std::transform(fin.begin(), fin.end(), amps.begin(),
                     [](csnd::pv_bin f) { return f.amp(); });
      std::nth_element(amps.begin(), amps.begin() + n, amps.end());
      thrsh = amps[n];
      std::transform(fin.begin(), fin.end(), fout.begin(),
                     [thrsh](csnd::pv_bin f) {
                       return f.amp() >= thrsh ? f : csnd::pv_bin();
                     });
      framecount = fout.count(fin.count());
    }
    return OK;
  }
};

struct TVConv : csnd::Plugin<1, 6> {
  csnd::AuxMem<MYFLT> ir;
  csnd::AuxMem<MYFLT> in;
  csnd::AuxMem<MYFLT> insp;
  csnd::AuxMem<MYFLT> irsp;
  csnd::AuxMem<MYFLT> out;
  csnd::AuxMem<MYFLT> saved;
  uint32_t n;
  uint32_t fils;
  uint32_t pars;
  uint32_t ffts;
  uint32_t nparts;
  uint32_t pp;
  csnd::fftp fwd, inv;
  typedef std::complex<MYFLT> cmplx;

  uint32_t rpow2(uint32_t n) {
    uint32_t v = 2;
    while (v <= n)
      v <<= 1;
    if ((n - (v >> 1)) < (v - n))
      return v >> 1;
    else
      return v;
  }

  cmplx *to_cmplx(MYFLT *f) { return reinterpret_cast<cmplx *>(f); }

  cmplx real_prod(cmplx &a, cmplx &b) {
    return cmplx(a.real() * b.real(), a.imag() * b.imag());
  }

  int init() {
    pars = inargs[4];
    fils = inargs[5];
    if (pars > fils)
      std::swap(pars, fils);
    if (pars > 1) {
      pars = rpow2(pars);
      fils = rpow2(fils);
      ffts = pars * 2;
      fwd = csound->fft_setup(ffts, FFT_FWD);
      inv = csound->fft_setup(ffts, FFT_INV);
      out.allocate(csound, 2 * pars);
      insp.allocate(csound, 2 * fils);
      irsp.allocate(csound, 2 * fils);
      saved.allocate(csound, pars);
      ir.allocate(csound, 2 * fils);
      in.allocate(csound, 2 * fils);
      nparts = fils / pars;
      fils *= 2;
    } else {
      ir.allocate(csound, fils);
      in.allocate(csound, fils);
    }
    n = 0;
    pp = 0;
    return OK;
  }

  int pconv() {
    csnd::AudioSig insig(this, inargs(0));
    csnd::AudioSig irsig(this, inargs(1));
    csnd::AudioSig outsig(this, outargs(0));
    auto irp = irsig.begin();
    auto inp = insig.begin();
    MYFLT *frz1 = inargs(2);
    MYFLT *frz2 = inargs(3);
    bool inc1 = csound->is_asig(frz1);
    bool inc2 = csound->is_asig(frz2);

    for (auto &s : outsig) {
      if(*frz1 > 0) in[pp + n] = *inp;
      if(*frz2 > 0) ir[pp + n] = *irp;

      s = out[n] + saved[n];
      saved[n] = out[n + pars];
      if (++n == pars) {
        cmplx *ins, *irs, *ous = to_cmplx(out.data());
        std::copy(in.begin() + pp, in.begin() + pp + ffts, insp.begin() + pp);
        std::copy(ir.begin() + pp, ir.begin() + pp + ffts, irsp.begin() + pp);
        std::fill(out.begin(), out.end(), 0.);
        // FFT
        csound->rfft(fwd, insp.data() + pp);
        csound->rfft(fwd, irsp.data() + pp);
        pp += ffts;
        if (pp == fils)
          pp = 0;
        // spectral delay line
        for (uint32_t k = 0, kp = pp; k < nparts; k++, kp += ffts) {
          if (kp == fils)
            kp = 0;
          ins = to_cmplx(insp.data() + kp);
          irs = to_cmplx(irsp.data() + (nparts - k - 1) * ffts);
          // spectral product
          for (uint32_t i = 1; i < pars; i++)
            ous[i] += ins[i] * irs[i];
          ous[0] += real_prod(ins[0], irs[0]);
        }
        // IFFT
        csound->rfft(inv, out.data());
        n = 0;
      }
      frz1 += inc1;
      frz2 += inc2;
      irp++;
      inp++;
    }
    return OK;
  }

  int pconv_ols() {
    csnd::AudioSig insig(this, inargs(0));
    csnd::AudioSig irsig(this, inargs(1));
    csnd::AudioSig outsig(this, outargs(0));
    auto irp = irsig.begin();
    auto inp = insig.begin();
    MYFLT *frz1 = inargs(2);
    MYFLT *frz2 = inargs(3);
    bool inc1 = csound->is_asig(frz1);
    bool inc2 = csound->is_asig(frz2);

    for (auto &s : outsig) {
      if(*frz1 > 0)
	in[pp + n + pars] = *inp;
      if(*frz2 > 0)
       ir[pp + n] = *irp;

      s = out[n+pars];

      if (++n == pars) {
        cmplx *ins, *irs, *ous = to_cmplx(out.data());
	uint32_t po = pp;
        std::copy(in.begin() + pp, in.begin() + pp + ffts, insp.begin() + pp);
	std::copy(ir.begin() + pp, ir.begin() + pp + ffts, irsp.begin() + pp);
        std::fill(out.begin(), out.end(), 0.);
        // FFT
        csound->rfft(fwd, insp.data() + pp);
	csound->rfft(fwd, irsp.data() + pp);
        pp += ffts;
	if (pp == fils) pp = 0;
        // spectral delay line
        for (uint32_t k = 0, kp = pp; k < nparts; k++, kp += ffts) {
	 if (kp == fils) kp = 0;
         ins = to_cmplx(insp.data() + kp);
         irs = to_cmplx(irsp.data() + (nparts - k - 1) * ffts);
          // spectral product
          for (uint32_t i = 1; i < pars; i++)
            ous[i] += ins[i] * irs[i];
          ous[0] += real_prod(ins[0], irs[0]);
	 }
        // IFFT
        csound->rfft(inv, out.data());
	std::copy(in.begin() + po + pars, in.begin() + po + ffts, in.begin() + pp);
        n = 0;
      }
      frz1 += inc1;
      frz2 += inc2;
      irp++;
      inp++;
    }
    return OK;
  }

  int dconv() {
    csnd::AudioSig insig(this, inargs(0));
    csnd::AudioSig irsig(this, inargs(1));
    csnd::AudioSig outsig(this, outargs(0));
    auto irp = irsig.begin();
    auto inp = insig.begin();
    MYFLT *frz1 = inargs(2);
    MYFLT *frz2 = inargs(3);
    bool inc1 = csound->is_asig(frz1);
    bool inc2 = csound->is_asig(frz2);
    for (auto &s : outsig) {
      if(*frz1 > 0) in[pp] = *inp;
      if(*frz2 > 0) ir[pp] = *irp;
      pp = pp != fils - 1 ? pp + 1 : 0;
      s = 0.;
      for (uint32_t k = 0, kp = pp; k < fils; k++, kp++) {
        if (kp == fils)
          kp = 0;
        s += in[kp] * ir[fils - k - 1];
      }
      frz1 += inc1;
      frz2 += inc2;
      inp++;
      irp++;
    }
    return OK;
  }

  int aperf() {
    if (pars > 1)
      return pconv();
    else
      return dconv();
  }
};

/*
class PrintThread : public csnd::Thread {
  std::atomic_bool splock;
  std::atomic_bool on;
  std::string message;

  void lock() {
    bool tmp = false;
    while(!splock.compare_exchange_weak(tmp,true))
      tmp = false;
  }

  void unlock() {
    splock = false;
  }

  uintptr_t run() {
    std::string old;
    while(on) {
      lock();
      if(old.compare(message)) {
       csound->message(message.c_str());
       old = message;
      }
      unlock();
    }
    return 0;
  }

public:
  PrintThread(csnd::Csound *csound)
    : Thread(csound), splock(false), on(true), message("") {};

  ~PrintThread(){
    on = false;
    join();
  }

  void set_message(const char *m) {
    lock();
    message = m;
    unlock();
  }

};

struct TPrint : csnd::Plugin<0, 1> {
  static constexpr char const *otypes = "";
  static constexpr char const *itypes = "S";
  PrintThread t;

  int init() {
    csound->plugin_deinit(this);
    csnd::constr(&t, csound);
    return OK;
  }

  int deinit() {
    csnd::destr(&t);
    return OK;
  }

  int kperf() {
    t.set_message(inargs.str_data(0).data);
    return OK;
  }
};
*/

#include <modload.h>
void csnd::on_load(Csound *csound) {
  csnd::plugin<PVTrace>(csound, "pvstrace", csnd::thread::ik);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aakkii", csnd::thread::ia);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aaakii", csnd::thread::ia);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aakaii", csnd::thread::ia);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aaaaii", csnd::thread::ia);
}
<commit_msg>tvconv possible fix<commit_after>/*
  pvsops.c: pvs and other spectral-based opcodes

  Copyright (C) 2017 Victor Lazzarini
  This file is part of Csound.

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

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

  You should have received a copy of the GNU Lesser General Public
  License along with Csound; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  02111-1307 USA
*/
#include <algorithm>
#include <plugin.h>

struct PVTrace : csnd::FPlugin<1, 2> {
  csnd::AuxMem<float> amps;
  static constexpr char const *otypes = "f";
  static constexpr char const *itypes = "fk";

  int init() {
    if (inargs.fsig_data(0).isSliding())
      return csound->init_error(Str("sliding not supported"));

    if (inargs.fsig_data(0).fsig_format() != csnd::fsig_format::pvs &&
        inargs.fsig_data(0).fsig_format() != csnd::fsig_format::polar)
      return csound->init_error(Str("fsig format not supported"));

    amps.allocate(csound, inargs.fsig_data(0).nbins());
    csnd::Fsig &fout = outargs.fsig_data(0);
    fout.init(csound, inargs.fsig_data(0));
    framecount = 0;
    return OK;
  }

  int kperf() {
    csnd::pv_frame &fin = inargs.fsig_data(0);
    csnd::pv_frame &fout = outargs.fsig_data(0);

    if (framecount < fin.count()) {
      int n = fin.len() - (int)inargs[1];
      float thrsh;
      std::transform(fin.begin(), fin.end(), amps.begin(),
                     [](csnd::pv_bin f) { return f.amp(); });
      std::nth_element(amps.begin(), amps.begin() + n, amps.end());
      thrsh = amps[n];
      std::transform(fin.begin(), fin.end(), fout.begin(),
                     [thrsh](csnd::pv_bin f) {
                       return f.amp() >= thrsh ? f : csnd::pv_bin();
                     });
      framecount = fout.count(fin.count());
    }
    return OK;
  }
};

struct TVConv : csnd::Plugin<1, 6> {
  csnd::AuxMem<MYFLT> ir;
  csnd::AuxMem<MYFLT> in;
  csnd::AuxMem<MYFLT> insp;
  csnd::AuxMem<MYFLT> irsp;
  csnd::AuxMem<MYFLT> out;
  csnd::AuxMem<MYFLT> saved;
  uint32_t n;
  uint32_t fils;
  uint32_t pars;
  uint32_t ffts;
  uint32_t nparts;
  uint32_t pp;
  csnd::fftp fwd, inv;
  typedef std::complex<MYFLT> cmplx;

  uint32_t rpow2(uint32_t n) {
    uint32_t v = 2;
    while (v <= n)
      v <<= 1;
    if ((n - (v >> 1)) < (v - n))
      return v >> 1;
    else
      return v;
  }

  cmplx *to_cmplx(MYFLT *f) { return reinterpret_cast<cmplx *>(f); }

  cmplx real_prod(cmplx &a, cmplx &b) {
    return cmplx(a.real() * b.real(), a.imag() * b.imag());
  }

  int init() {
    pars = inargs[4];
    fils = inargs[5];
    if (pars > fils)
      std::swap(pars, fils);
    if (pars > 1) {
      pars = rpow2(pars);
      fils = rpow2(fils);
      std::cout << "fils: " << fils << " pars: " << pars << std::endl;
      ffts = pars * 2;
      fwd = csound->fft_setup(ffts, FFT_FWD);
      inv = csound->fft_setup(ffts, FFT_INV);
      out.allocate(csound, 2 * pars);
      insp.allocate(csound, 2 * fils);
      irsp.allocate(csound, 2 * fils);
      saved.allocate(csound, pars);
      ir.allocate(csound, 2 * fils);
      in.allocate(csound, 2 * fils);
      nparts = fils / pars;
      fils *= 2;
    } else {
      ir.allocate(csound, fils);
      in.allocate(csound, fils);
    }
    n = 0;
    pp = 0;
    return OK;
  }

  int pconv() {
    csnd::AudioSig insig(this, inargs(0));
    csnd::AudioSig irsig(this, inargs(1));
    csnd::AudioSig outsig(this, outargs(0));
    auto irp = irsig.begin();
    auto inp = insig.begin();
    MYFLT *frz1 = inargs(2);
    MYFLT *frz2 = inargs(3);
    bool inc1 = csound->is_asig(frz1);
    bool inc2 = csound->is_asig(frz2);

    for (auto &s : outsig) {
      if(*frz1 > 0) in[pp + n] = *inp;
      if(*frz2 > 0) ir[pp + n] = *irp;

      s = out[n] + saved[n];
      saved[n] = out[n + pars];
      if (++n == pars) {
        cmplx *ins, *irs, *ous = to_cmplx(out.data());
        std::copy(in.begin() + pp, in.begin() + pp + ffts, insp.begin() + pp);
        std::copy(ir.begin() + pp, ir.begin() + pp + ffts, irsp.begin() + pp);
        std::fill(out.begin(), out.end(), 0.);
        // FFT
        csound->rfft(fwd, insp.data() + pp);
        csound->rfft(fwd, irsp.data() + pp);
        pp += ffts;
        if (pp == fils)
          pp = 0;
        // spectral delay line
        for (uint32_t k = 0, kp = pp; k < nparts; k++, kp += ffts) {
          if (kp == fils)
            kp = 0;
          ins = to_cmplx(insp.data() + kp);
          irs = to_cmplx(irsp.data() + (nparts - k - 1) * ffts);
          // spectral product
          for (uint32_t i = 1; i < pars/2; i++)
            ous[i] += ins[i] * irs[i];
          ous[0] += real_prod(ins[0], irs[0]);
        }
        // IFFT
        csound->rfft(inv, out.data());
        n = 0;
      }
      frz1 += inc1;
      frz2 += inc2;
      irp++;
      inp++;
    }
    return OK;
  }

  int pconv_ols() {
    csnd::AudioSig insig(this, inargs(0));
    csnd::AudioSig irsig(this, inargs(1));
    csnd::AudioSig outsig(this, outargs(0));
    auto irp = irsig.begin();
    auto inp = insig.begin();
    MYFLT *frz1 = inargs(2);
    MYFLT *frz2 = inargs(3);
    bool inc1 = csound->is_asig(frz1);
    bool inc2 = csound->is_asig(frz2);

    for (auto &s : outsig) {
      if(*frz1 > 0)
	in[pp + n + pars] = *inp;
      if(*frz2 > 0)
       ir[pp + n] = *irp;

      s = out[n+pars];

      if (++n == pars) {
        cmplx *ins, *irs, *ous = to_cmplx(out.data());
	uint32_t po = pp;
        std::copy(in.begin() + pp, in.begin() + pp + ffts, insp.begin() + pp);
	std::copy(ir.begin() + pp, ir.begin() + pp + ffts, irsp.begin() + pp);
        std::fill(out.begin(), out.end(), 0.);
        // FFT
        csound->rfft(fwd, insp.data() + pp);
	csound->rfft(fwd, irsp.data() + pp);
        pp += ffts;
	if (pp == fils) pp = 0;
        // spectral delay line
        for (uint32_t k = 0, kp = pp; k < nparts; k++, kp += ffts) {
	 if (kp == fils) kp = 0;
         ins = to_cmplx(insp.data() + kp);
         irs = to_cmplx(irsp.data() + (nparts - k - 1) * ffts);
          // spectral product
          for (uint32_t i = 1; i < pars; i++)
            ous[i] += ins[i] * irs[i];
          ous[0] += real_prod(ins[0], irs[0]);
	 }
        // IFFT
        csound->rfft(inv, out.data());
	std::copy(in.begin() + po + pars, in.begin() + po + ffts, in.begin() + pp);
        n = 0;
      }
      frz1 += inc1;
      frz2 += inc2;
      irp++;
      inp++;
    }
    return OK;
  }

  int dconv() {
    csnd::AudioSig insig(this, inargs(0));
    csnd::AudioSig irsig(this, inargs(1));
    csnd::AudioSig outsig(this, outargs(0));
    auto irp = irsig.begin();
    auto inp = insig.begin();
    MYFLT *frz1 = inargs(2);
    MYFLT *frz2 = inargs(3);
    bool inc1 = csound->is_asig(frz1);
    bool inc2 = csound->is_asig(frz2);
    for (auto &s : outsig) {
      if(*frz1 > 0) in[pp] = *inp;
      if(*frz2 > 0) ir[pp] = *irp;
      pp = pp != fils - 1 ? pp + 1 : 0;
      s = 0.;
      for (uint32_t k = 0, kp = pp; k < fils; k++, kp++) {
        if (kp == fils)
          kp = 0;
        s += in[kp] * ir[fils - k - 1];
      }
      frz1 += inc1;
      frz2 += inc2;
      inp++;
      irp++;
    }
    return OK;
  }

  int aperf() {
    if (pars > 1)
      return pconv();
    else
      return dconv();
  }
};

/*
class PrintThread : public csnd::Thread {
  std::atomic_bool splock;
  std::atomic_bool on;
  std::string message;

  void lock() {
    bool tmp = false;
    while(!splock.compare_exchange_weak(tmp,true))
      tmp = false;
  }

  void unlock() {
    splock = false;
  }

  uintptr_t run() {
    std::string old;
    while(on) {
      lock();
      if(old.compare(message)) {
       csound->message(message.c_str());
       old = message;
      }
      unlock();
    }
    return 0;
  }

public:
  PrintThread(csnd::Csound *csound)
    : Thread(csound), splock(false), on(true), message("") {};

  ~PrintThread(){
    on = false;
    join();
  }

  void set_message(const char *m) {
    lock();
    message = m;
    unlock();
  }

};

struct TPrint : csnd::Plugin<0, 1> {
  static constexpr char const *otypes = "";
  static constexpr char const *itypes = "S";
  PrintThread t;

  int init() {
    csound->plugin_deinit(this);
    csnd::constr(&t, csound);
    return OK;
  }

  int deinit() {
    csnd::destr(&t);
    return OK;
  }

  int kperf() {
    t.set_message(inargs.str_data(0).data);
    return OK;
  }
};
*/

#include <modload.h>
void csnd::on_load(Csound *csound) {
  csnd::plugin<PVTrace>(csound, "pvstrace", csnd::thread::ik);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aakkii", csnd::thread::ia);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aaakii", csnd::thread::ia);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aakaii", csnd::thread::ia);
  csnd::plugin<TVConv>(csound, "tvconv", "a", "aaaaii", csnd::thread::ia);
}
<|endoftext|>


<commit_before>// @(#)root/gui:$Name:  $:$Id: TGResourcePool.cxx,v 1.1 2003/05/28 11:55:31 rdm Exp $
// Author: Fons Rademakers   19/5/2003

/*************************************************************************
 * Copyright (C) 1995-2003, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/
/**************************************************************************

    This source is based on Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.

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

**************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGResourcePool                                                       //
//                                                                      //
// This class implements a pool for the default GUI resource set,       //
// like GC's, colors, fonts, etc..                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifdef HAVE_CONFIG
#include "config.h"
#endif

#include "TGResourcePool.h"
#include "TGClient.h"
#include "TGWindow.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TEnv.h"
#include "TVirtualX.h"
#include "TGGC.h"
#include "TGPicture.h"
#include "TGFont.h"
#include "TGMimeTypes.h"

const int gray_width  = 8;
const int gray_height = 8;
static unsigned char gray_bits[] = {
   0x55, 0xaa, 0x55, 0xaa,
   0x55, 0xaa, 0x55, 0xaa
};

ClassImp(TGResourcePool)

//______________________________________________________________________________
TGResourcePool::TGResourcePool(TGClient *client)
{
   // Create the global GUI resource pool manager.

   fClient = client;
   fDefaultColormap = gVirtualX->GetColormap();

   // Get GUI defaults from [system].rootrc
   char default_font[256];
   char menu_font[256];
   char menu_hi_font[256];
   char doc_fixed_font[256];
   char doc_prop_font[256];
   char icon_font[256];
   char status_font[256];
   char backcolor[256];
   char forecolor[256];
   char selbackcolor[256];
   char selforecolor[256];
   char docbackcolor[256];
   char docforecolor[256];
   char tipbackcolor[256];
   char tipforecolor[256];
   char framebgpixmap[2048], docbgpixmap[2048];
   char icon_path[2048], mime_file[256], line[2048];

#ifndef GDK_WIN32
   strcpy(default_font,   gEnv->GetValue("Gui.DefaultFont",  "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(menu_font,      gEnv->GetValue("Gui.MenuFont",     "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(menu_hi_font,   gEnv->GetValue("Gui.MenuHiFont",   "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(doc_fixed_font, gEnv->GetValue("Gui.DocFixedFont", "-adobe-courier-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(doc_prop_font,  gEnv->GetValue("Gui.DocPropFont",  "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(icon_font,      gEnv->GetValue("Gui.IconFont",     "-adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-iso8859-1"));
   strcpy(status_font,    gEnv->GetValue("Gui.StatusFont",   "-adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-iso8859-1"));
#else
   strcpy(default_font,   gEnv->GetValue("Gui.DefaultFont",  "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(menu_font,      gEnv->GetValue("Gui.MenuFont",     "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(menu_hi_font,   gEnv->GetValue("Gui.MenuHiFont",   "-adobe-helvetica-bold-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(doc_fixed_font, gEnv->GetValue("Gui.DocFixedFont", "-adobe-courier-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(doc_prop_font,  gEnv->GetValue("Gui.DocPropFont",  "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(icon_font,      gEnv->GetValue("Gui.IconFont",     "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(status_font,    gEnv->GetValue("Gui.StatusFont",   "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
#endif
   strcpy(backcolor,    gEnv->GetValue("Gui.BackgroundColor",         "#c0c0c0"));
   strcpy(forecolor,    gEnv->GetValue("Gui.ForegroundColor",         "black"));
   strcpy(selbackcolor, gEnv->GetValue("Gui.SelectBackgroundColor",   "#000080"));
   strcpy(selforecolor, gEnv->GetValue("Gui.SelectForegroundColor",   "white"));
   strcpy(docbackcolor, gEnv->GetValue("Gui.DocumentBackgroundColor", "white"));
   strcpy(docforecolor, gEnv->GetValue("Gui.DocumentForegroundColor", "black"));
   strcpy(tipbackcolor, gEnv->GetValue("Gui.TooltipBackgroundColor",  "LightYellow"));
   strcpy(tipforecolor, gEnv->GetValue("Gui.TooltipForegroundColor",  "black"));

   strcpy(framebgpixmap, gEnv->GetValue("Gui.FrameBackgroundPixmap", ""));
   strcpy(docbgpixmap,   gEnv->GetValue("Gui.DocumentBackgroundPixmap", ""));
#ifndef GDK_WIN32
#ifndef R__VMS
# ifdef ROOTICONPATH
   sprintf(icon_path, "%s/icons:%s:.:",
           gSystem->Getenv("HOME"),
           ROOTICONPATH);
#  ifdef EXTRAICONPATH
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", EXTRAICONPATH));
#  else
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", ""));
#  endif
# else
   sprintf(icon_path, "%s/icons:%s/icons:.:", gSystem->Getenv("HOME"),
                                              gSystem->Getenv("ROOTSYS"));
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", ""));
# endif
   sprintf(line, "%s/.root.mimes", gSystem->Getenv("HOME"));
#else
   sprintf(line,"[%s.ICONS]",gSystem->Getenv("ROOTSYS"));
   strcpy(icon_path, gEnv->GetValue("Gui.IconPath",line));
   sprintf(line,"%sroot.mimes",gSystem->Getenv("HOME"));
#endif

   strcpy(mime_file, gEnv->GetValue("Gui.MimeTypeFile", line));
   char *mf = gSystem->ExpandPathName(mime_file);
   if (mf) {
      strcpy(mime_file, mf);
      delete [] mf;
   }
   if (gSystem->AccessPathName(mime_file, kReadPermission))
#ifdef R__VMS
      sprintf(mime_file,"[%s.ETC]root.mimes",gSystem->Getenv("ROOTSYS"));
#else
# ifdef ROOTETCDIR
      sprintf(mime_file, "%s/root.mimes", ROOTETCDIR);
# else
      sprintf(mime_file, "%s/etc/root.mimes", gSystem->Getenv("ROOTSYS"));
# endif
#endif
#else // GDK_WIN32
   sprintf(icon_path, "%s\\icons:.\\", gSystem->Getenv("ROOTSYS"));
   sprintf(line, "%s\\root.mimes", gSystem->Getenv("HOME"));
   strcpy(mime_file, gEnv->GetValue("Gui.MimeTypeFile", line));
   if (gSystem->AccessPathName(mime_file, kReadPermission))
       sprintf(mime_file, "%s\\etc\\root.mimes", gSystem->Getenv("ROOTSYS"));
#endif // GDK_WIN32

   // Setup colors...
   fClient->GetColorByName("white", fWhite);  // white and black always exist
   fClient->GetColorByName("black", fBlack);
   if (!fClient->GetColorByName(backcolor, fBackColor))
      fBackColor = fWhite;
   if (!fClient->GetColorByName(forecolor, fForeColor))
      fForeColor = fBlack;
   fHilite = fClient->GetHilite(fBackColor);
   fShadow = fClient->GetShadow(fBackColor);
   if (!fClient->GetColorByName(selbackcolor, fSelBackColor))
      fSelBackColor = fBlack;
   if (!fClient->GetColorByName(selforecolor, fSelForeColor))
      fSelForeColor = fWhite;
   if (!fClient->GetColorByName(docbackcolor, fDocBackColor))
      fDocBackColor = fWhite;
   if (!fClient->GetColorByName(docforecolor, fDocForeColor))
      fDocForeColor = fBlack;
   if (!fClient->GetColorByName(tipbackcolor, fTipBackColor))
      fTipBackColor = fWhite;
   if (!fClient->GetColorByName(tipforecolor, fTipForeColor))
      fTipForeColor = fBlack;

   // Setup checkered pix/bit-maps...
   fCheckered = gVirtualX->CreatePixmap(fClient->GetRoot()->GetId(),
                                        (const char *)gray_bits,
                                        gray_width, gray_height,
                                        fBackColor, fWhite /*fHilite*/,
                                        gVirtualX->GetDepth());
   fCheckeredBitmap = gVirtualX->CreatePixmap(fClient->GetRoot()->GetId(),
                                              (const char *)gray_bits,
                                              gray_width, gray_height,
                                              1, 0, 1);

   // Create picture pool, GC pool, font pool, mime type list, etc.

   // Create picture pool and pre-load some pictures...
   fPicturePool = new TGPicturePool(fClient, icon_path);

   fDefaultBackPicture    = 0;
   fDefaultDocBackPicture = 0;
   if (strlen(framebgpixmap))
      fDefaultBackPicture = fPicturePool->GetPicture(framebgpixmap);
   if (strlen(docbgpixmap))
      fDefaultDocBackPicture = fPicturePool->GetPicture(docbgpixmap);

   // Create font pool and pre-load some fonts...
   fFontPool = new TGFontPool(fClient);
   fDefaultFont  = fFontPool->GetFont(default_font);
   fMenuFont     = fFontPool->GetFont(menu_font);
   fMenuHiFont   = fFontPool->GetFont(menu_hi_font);
   fDocFixedFont = fFontPool->GetFont(doc_fixed_font);
   fDocPropFont  = fFontPool->GetFont(doc_prop_font);
   fIconFont     = fFontPool->GetFont(icon_font);
   fStatusFont   = fFontPool->GetFont(status_font);

   // Create GC pool and pre-load some GCs...
   fGCPool = new TGGCPool(fClient);

   GCValues_t gval;
   gval.fMask = kGCForeground | kGCBackground | kGCFont |
                kGCFillStyle | kGCGraphicsExposures;
   gval.fGraphicsExposures = kFALSE;
   gval.fFillStyle  = kFillSolid;
   gval.fFont       = fDefaultFont->GetFontHandle();
   gval.fBackground = fBackColor;
   gval.fForeground = fBlack;
   fBlackGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fWhite;
   fWhiteGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fHilite;
   fHiliteGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fShadow;
   fShadowGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fBackColor;
   fBckgndGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fForeColor;
   fFrameGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fSelBackColor;
   fSelbgndGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fSelForeColor;
   gval.fBackground = fSelBackColor;
   fSelGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fFont       = fDocPropFont->GetFontHandle();
   gval.fForeground = fDocForeColor;
   gval.fBackground = fDocBackColor;
   fDocGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fDocBackColor;
   fDocbgndGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fFont       = fStatusFont->GetFontHandle();
   gval.fForeground = fTipForeColor;
   gval.fBackground = fTipBackColor;
   fTipGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fMask = kGCForeground | kGCBackground | kGCFillStyle |
                kGCLineWidth  | kGCSubwindowMode |
                kGCGraphicsExposures | kGCStipple;
   gval.fForeground = fBlack;
   gval.fBackground = fSelBackColor;
   gval.fLineWidth  = 0;
   gval.fFillStyle  = kFillStippled;
   gval.fSubwindowMode = kIncludeInferiors;
   gval.fGraphicsExposures = kFALSE;
   gval.fStipple = fCheckeredBitmap;
   fFocusGC = fGCPool->GetGC(&gval, kTRUE);

   // Setup cursors...
   fDefaultCursor = kNone;
   fGrabCursor    = gVirtualX->CreateCursor(kArrowRight);
   fTextCursor    = gVirtualX->CreateCursor(kCaret);
   fWaitCursor    = gVirtualX->CreateCursor(kWatch);

   // Read in mime type...
   fMimeTypeList  = new TGMimeTypes(fClient, mime_file);

   // Clipboard handle...
#ifndef GDK_WIN32
   fClipboardAtom = gVirtualX->InternAtom("_ROOT_CLIPBOARD", kFALSE);
#else
   fClipboardAtom = gVirtualX->InternAtom("CLIPBOARD", kFALSE);
#endif
}

//______________________________________________________________________________
TGResourcePool::~TGResourcePool()
{
   // Cleanup the resource pool...

   delete fPicturePool;
   delete fFontPool;
   delete fGCPool;
   delete fMimeTypeList;

   gVirtualX->DeletePixmap(fCheckered);
   gVirtualX->DeletePixmap(fCheckeredBitmap);
}
<commit_msg>From valeriy Onuchin:<commit_after>// @(#)root/gui:$Name:  $:$Id: TGResourcePool.cxx,v 1.2 2003/12/15 08:54:29 brun Exp $
// Author: Fons Rademakers   19/5/2003

/*************************************************************************
 * Copyright (C) 1995-2003, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/
/**************************************************************************

    This source is based on Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Ricky Ralston, Hector Peraza.

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

**************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGResourcePool                                                       //
//                                                                      //
// This class implements a pool for the default GUI resource set,       //
// like GC's, colors, fonts, etc..                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifdef HAVE_CONFIG
#include "config.h"
#endif

#include "TGResourcePool.h"
#include "TGClient.h"
#include "TGWindow.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TEnv.h"
#include "TVirtualX.h"
#include "TGGC.h"
#include "TGPicture.h"
#include "TGFont.h"
#include "TGMimeTypes.h"

const int gray_width  = 8;
const int gray_height = 8;
static unsigned char gray_bits[] = {
   0x55, 0xaa, 0x55, 0xaa,
   0x55, 0xaa, 0x55, 0xaa
};

ClassImp(TGResourcePool)

//______________________________________________________________________________
TGResourcePool::TGResourcePool(TGClient *client)
{
   // Create the global GUI resource pool manager.

   fClient = client;
   fDefaultColormap = gVirtualX->GetColormap();

   // Get GUI defaults from [system].rootrc
   char default_font[256];
   char menu_font[256];
   char menu_hi_font[256];
   char doc_fixed_font[256];
   char doc_prop_font[256];
   char icon_font[256];
   char status_font[256];
   char backcolor[256];
   char forecolor[256];
   char selbackcolor[256];
   char selforecolor[256];
   char docbackcolor[256];
   char docforecolor[256];
   char tipbackcolor[256];
   char tipforecolor[256];
   char framebgpixmap[2048], docbgpixmap[2048];
   char icon_path[2048], mime_file[256], line[2048];

#ifndef GDK_WIN32
   strcpy(default_font,   gEnv->GetValue("Gui.DefaultFont",  "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(menu_font,      gEnv->GetValue("Gui.MenuFont",     "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(menu_hi_font,   gEnv->GetValue("Gui.MenuHiFont",   "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(doc_fixed_font, gEnv->GetValue("Gui.DocFixedFont", "-adobe-courier-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(doc_prop_font,  gEnv->GetValue("Gui.DocPropFont",  "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"));
   strcpy(icon_font,      gEnv->GetValue("Gui.IconFont",     "-adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-iso8859-1"));
   strcpy(status_font,    gEnv->GetValue("Gui.StatusFont",   "-adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-iso8859-1"));
#else
   strcpy(default_font,   gEnv->GetValue("Gui.DefaultFont",  "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(menu_font,      gEnv->GetValue("Gui.MenuFont",     "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(menu_hi_font,   gEnv->GetValue("Gui.MenuHiFont",   "-adobe-helvetica-bold-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(doc_fixed_font, gEnv->GetValue("Gui.DocFixedFont", "-adobe-courier-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(doc_prop_font,  gEnv->GetValue("Gui.DocPropFont",  "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(icon_font,      gEnv->GetValue("Gui.IconFont",     "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
   strcpy(status_font,    gEnv->GetValue("Gui.StatusFont",   "-adobe-helvetica-medium-r-*-*-15-*-*-*-*-*-iso8859-1"));
#endif
   strcpy(backcolor,    gEnv->GetValue("Gui.BackgroundColor",         "#c0c0c0"));
   strcpy(forecolor,    gEnv->GetValue("Gui.ForegroundColor",         "black"));
   strcpy(selbackcolor, gEnv->GetValue("Gui.SelectBackgroundColor",   "#000080"));
   strcpy(selforecolor, gEnv->GetValue("Gui.SelectForegroundColor",   "white"));
   strcpy(docbackcolor, gEnv->GetValue("Gui.DocumentBackgroundColor", "white"));
   strcpy(docforecolor, gEnv->GetValue("Gui.DocumentForegroundColor", "black"));
   strcpy(tipbackcolor, gEnv->GetValue("Gui.TooltipBackgroundColor",  "LightYellow"));
   strcpy(tipforecolor, gEnv->GetValue("Gui.TooltipForegroundColor",  "black"));

   strcpy(framebgpixmap, gEnv->GetValue("Gui.FrameBackgroundPixmap", ""));
   strcpy(docbgpixmap,   gEnv->GetValue("Gui.DocumentBackgroundPixmap", ""));
#ifndef GDK_WIN32
#ifndef R__VMS
# ifdef ROOTICONPATH
   sprintf(icon_path, "%s/icons:%s:.:",
           gSystem->Getenv("HOME"),
           ROOTICONPATH);
#  ifdef EXTRAICONPATH
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", EXTRAICONPATH));
#  else
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", ""));
#  endif
# else
   sprintf(icon_path, "%s/icons:%s/icons:.:", gSystem->Getenv("HOME"),
                                              gSystem->Getenv("ROOTSYS"));
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", ""));
# endif
   sprintf(line, "%s/.root.mimes", gSystem->Getenv("HOME"));
#else
   sprintf(line,"[%s.ICONS]",gSystem->Getenv("ROOTSYS"));
   strcpy(icon_path, gEnv->GetValue("Gui.IconPath",line));
   sprintf(line,"%sroot.mimes",gSystem->Getenv("HOME"));
#endif

   strcpy(mime_file, gEnv->GetValue("Gui.MimeTypeFile", line));
   char *mf = gSystem->ExpandPathName(mime_file);
   if (mf) {
      strcpy(mime_file, mf);
      delete [] mf;
   }
   if (gSystem->AccessPathName(mime_file, kReadPermission))
#ifdef R__VMS
      sprintf(mime_file,"[%s.ETC]root.mimes",gSystem->Getenv("ROOTSYS"));
#else
# ifdef ROOTETCDIR
      sprintf(mime_file, "%s/root.mimes", ROOTETCDIR);
# else
      sprintf(mime_file, "%s/etc/root.mimes", gSystem->Getenv("ROOTSYS"));
# endif
#endif
#else // GDK_WIN32
   sprintf(icon_path, "%s\\icons:.:\\", gSystem->Getenv("ROOTSYS"));
   strcat(icon_path, gEnv->GetValue("Gui.IconPath", ""));
   sprintf(line, "%s\\root.mimes", gSystem->Getenv("HOME"));
   strcpy(mime_file, gEnv->GetValue("Gui.MimeTypeFile", line));
   if (gSystem->AccessPathName(mime_file, kReadPermission))
       sprintf(mime_file, "%s\\etc\\root.mimes", gSystem->Getenv("ROOTSYS"));
#endif // GDK_WIN32

   // Setup colors...
   fClient->GetColorByName("white", fWhite);  // white and black always exist
   fClient->GetColorByName("black", fBlack);
   if (!fClient->GetColorByName(backcolor, fBackColor))
      fBackColor = fWhite;
   if (!fClient->GetColorByName(forecolor, fForeColor))
      fForeColor = fBlack;
   fHilite = fClient->GetHilite(fBackColor);
   fShadow = fClient->GetShadow(fBackColor);
   if (!fClient->GetColorByName(selbackcolor, fSelBackColor))
      fSelBackColor = fBlack;
   if (!fClient->GetColorByName(selforecolor, fSelForeColor))
      fSelForeColor = fWhite;
   if (!fClient->GetColorByName(docbackcolor, fDocBackColor))
      fDocBackColor = fWhite;
   if (!fClient->GetColorByName(docforecolor, fDocForeColor))
      fDocForeColor = fBlack;
   if (!fClient->GetColorByName(tipbackcolor, fTipBackColor))
      fTipBackColor = fWhite;
   if (!fClient->GetColorByName(tipforecolor, fTipForeColor))
      fTipForeColor = fBlack;

   // Setup checkered pix/bit-maps...
   fCheckered = gVirtualX->CreatePixmap(fClient->GetRoot()->GetId(),
                                        (const char *)gray_bits,
                                        gray_width, gray_height,
                                        fBackColor, fWhite /*fHilite*/,
                                        gVirtualX->GetDepth());
   fCheckeredBitmap = gVirtualX->CreatePixmap(fClient->GetRoot()->GetId(),
                                              (const char *)gray_bits,
                                              gray_width, gray_height,
                                              1, 0, 1);

   // Create picture pool, GC pool, font pool, mime type list, etc.

   // Create picture pool and pre-load some pictures...
   fPicturePool = new TGPicturePool(fClient, icon_path);

   fDefaultBackPicture    = 0;
   fDefaultDocBackPicture = 0;
   if (strlen(framebgpixmap))
      fDefaultBackPicture = fPicturePool->GetPicture(framebgpixmap);
   if (strlen(docbgpixmap))
      fDefaultDocBackPicture = fPicturePool->GetPicture(docbgpixmap);

   // Create font pool and pre-load some fonts...
   fFontPool = new TGFontPool(fClient);
   fDefaultFont  = fFontPool->GetFont(default_font);
   fMenuFont     = fFontPool->GetFont(menu_font);
   fMenuHiFont   = fFontPool->GetFont(menu_hi_font);
   fDocFixedFont = fFontPool->GetFont(doc_fixed_font);
   fDocPropFont  = fFontPool->GetFont(doc_prop_font);
   fIconFont     = fFontPool->GetFont(icon_font);
   fStatusFont   = fFontPool->GetFont(status_font);

   // Create GC pool and pre-load some GCs...
   fGCPool = new TGGCPool(fClient);

   GCValues_t gval;
   gval.fMask = kGCForeground | kGCBackground | kGCFont |
                kGCFillStyle | kGCGraphicsExposures;
   gval.fGraphicsExposures = kFALSE;
   gval.fFillStyle  = kFillSolid;
   gval.fFont       = fDefaultFont->GetFontHandle();
   gval.fBackground = fBackColor;
   gval.fForeground = fBlack;
   fBlackGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fWhite;
   fWhiteGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fHilite;
   fHiliteGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fShadow;
   fShadowGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fBackColor;
   fBckgndGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fForeColor;
   fFrameGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fSelBackColor;
   fSelbgndGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fSelForeColor;
   gval.fBackground = fSelBackColor;
   fSelGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fFont       = fDocPropFont->GetFontHandle();
   gval.fForeground = fDocForeColor;
   gval.fBackground = fDocBackColor;
   fDocGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fForeground = fDocBackColor;
   fDocbgndGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fFont       = fStatusFont->GetFontHandle();
   gval.fForeground = fTipForeColor;
   gval.fBackground = fTipBackColor;
   fTipGC = fGCPool->GetGC(&gval, kTRUE);

   gval.fMask = kGCForeground | kGCBackground | kGCFillStyle |
                kGCLineWidth  | kGCSubwindowMode |
                kGCGraphicsExposures | kGCStipple;
   gval.fForeground = fBlack;
   gval.fBackground = fSelBackColor;
   gval.fLineWidth  = 0;
   gval.fFillStyle  = kFillStippled;
   gval.fSubwindowMode = kIncludeInferiors;
   gval.fGraphicsExposures = kFALSE;
   gval.fStipple = fCheckeredBitmap;
   fFocusGC = fGCPool->GetGC(&gval, kTRUE);

   // Setup cursors...
   fDefaultCursor = kNone;
   fGrabCursor    = gVirtualX->CreateCursor(kArrowRight);
   fTextCursor    = gVirtualX->CreateCursor(kCaret);
   fWaitCursor    = gVirtualX->CreateCursor(kWatch);

   // Read in mime type...
   fMimeTypeList  = new TGMimeTypes(fClient, mime_file);

   // Clipboard handle...
#ifndef GDK_WIN32
   fClipboardAtom = gVirtualX->InternAtom("_ROOT_CLIPBOARD", kFALSE);
#else
   fClipboardAtom = gVirtualX->InternAtom("CLIPBOARD", kFALSE);
#endif
}

//______________________________________________________________________________
TGResourcePool::~TGResourcePool()
{
   // Cleanup the resource pool...

   delete fPicturePool;
   delete fFontPool;
   delete fGCPool;
   delete fMimeTypeList;

   gVirtualX->DeletePixmap(fCheckered);
   gVirtualX->DeletePixmap(fCheckeredBitmap);
}
<|endoftext|>


<commit_before>/*
 * util.cpp
 *
 *  Created on: 2009-11-20
 *      Author: fasiondog
 */
#include <cmath>
#include "util.h"

#if defined(BOOST_WINDOWS)
#include <windows.h>
#else
#include <iconv.h>
#include <string.h>
#include <iostream>
#endif

namespace hku {

double HKU_API roundEx(double number, int ndigits){
    double f;
    int i;
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0){
        f = f*10.0;
    }

    if (ndigits < 0){
        number /= f;
    }else{
        number *= f;
    }

    if (number >= 0.0){
        number = std::floor(number + 0.5);
    }else{
        number = std::ceil(number - 0.5);
    }

    if (ndigits < 0){
        number *= f;
    }else{
        number /= f;
    }

    return number;
}

double HKU_API roundUp(double number, int ndigits) {
    double f;
    int i;
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0){
        f = f*10.0;
    }

    if (ndigits < 0){
        number /= f;
    }else{
        number *= f;
    }

    if (number >= 0.0){
        number = std::floor(number + 1.0);
    }else{
        number = std::ceil(number - 1.0);
    }

    if (ndigits < 0){
        number *= f;
    }else{
        number /= f;
    }

    return number;
}

double HKU_API roundDown(double number, int ndigits) {
    double f;
    int i;
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0){
        f = f*10.0;
    }

    if (ndigits < 0){
        number /= f;
    }else{
        number *= f;
    }

    if (number >= 0.0){
        number = std::floor(number);
    }else{
        number = std::ceil(number);
    }

    if (ndigits < 0){
        number *= f;
    }else{
        number /= f;
    }

    return number;
}

#if defined(BOOST_WINDOWS)
/**
 * 将UTF8编码的字符串转换为GB2312编码的字符串
 * @param szinput 待转换的原始UTF8编码的字符串
 * @return 以GB2312编码的字符串
 * @note 仅在Windows平台下生效
 */
string HKU_API utf8_to_gb(const char* szinput) {
    wchar_t *strSrc;
    char *szRes;
    string nullStr;
    if (!szinput) {
        return nullStr;
    }

    int i = MultiByteToWideChar(CP_UTF8, 0, szinput, -1, NULL, 0);
    if (i == 0) {
        return nullStr;
    }
    strSrc = new wchar_t[i+1];
    if (!MultiByteToWideChar(CP_UTF8, 0, szinput, -1, strSrc, i)) {
        delete[] strSrc;
        return nullStr;
    }

    i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
    if (i == 0) {
        return nullStr;
    }

    szRes = new char[i+1];
    if (!WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL)) {
        delete[] szRes;
        return nullStr;
    }

    string result(szRes);

    delete []strSrc;
    delete []szRes;
    return result;
}

string HKU_API utf8_to_gb(const string& szinput) {
    return utf8_to_gb(szinput.c_str());
}

/**
 * 将GB2312编码的字符串转换为UTF8编码的字符串
 * @param szinput 待转换的原始GB2312编码的字符串
 * @return 以UTF8编码的字符串
 * @note 仅在Windows平台下生效
 */
string HKU_API gb_to_utf8(const char * szinput) {
    wchar_t *strSrc;
    char *szRes;
    string nullstr;
    if (!szinput) {
        return nullstr;
    }

    int i = MultiByteToWideChar(CP_ACP, 0, szinput, -1, NULL, 0);
    if (0 == i) {
        return nullstr;
    }

    strSrc = new wchar_t[i+1];
    if (!MultiByteToWideChar(CP_ACP, 0, szinput, -1, strSrc, i)) {
        delete[] strSrc;
        return nullstr;
    }

    i = WideCharToMultiByte(CP_UTF8, 0, strSrc, -1, NULL, 0, NULL, NULL);
    if (0 == i) {
        return nullstr;
    }

    szRes = new char[i + 1];
    if (!WideCharToMultiByte(CP_UTF8, 0, strSrc, -1, szRes, i, NULL, NULL)) {
        delete[] szRes;
        return nullstr;
    }

    string result(szRes);

    delete []strSrc;
    delete []szRes;
    return result;
}

string HKU_API gb_to_utf8(const string& szinput) {
    return gb_to_utf8(szinput.c_str());
}

#else /* else for defined(BOOST_WINDOWS) */
string HKU_API utf8_to_gb(const string& szinput) {
    char *inbuf=const_cast<char*>(szinput.c_str());
    size_t inlen = strlen(inbuf);
    size_t outlen = inlen;
    char *outbuf = (char *)malloc(outlen);
    memset(outbuf, 0, outlen);
    char *in = inbuf;
    char *out = outbuf;
    iconv_t cd=iconv_open("gbk","utf-8");
    iconv(cd, &in, &inlen, &out, &outlen);
    iconv_close(cd);
    string result(outbuf);
    free(oufbuf);
    return outbuf;
}

string HKU_API gb_to_utf8(const string& szinput) {
    char *inbuf=const_cast<char*>(szinput.c_str());
    size_t inlen = strlen(inbuf);
    size_t outlen = inlen * 2;
    char *outbuf = (char *)malloc(outlen);
    memset(outbuf, 0, outlen);
    char *in = inbuf;
    char *out = outbuf;
    iconv_t cd=iconv_open("utf-8","gbk");
    iconv(cd, &in, &inlen, &out, &outlen);
    iconv_close(cd);
    string result(outbuf);
    free(outbuf);
    return result;
}

#endif /* defined(BOOST_WINDOWS) */

} /*namespace*/
<commit_msg>update<commit_after>/*
 * util.cpp
 *
 *  Created on: 2009-11-20
 *      Author: fasiondog
 */
#include <cmath>
#include "util.h"

#if defined(BOOST_WINDOWS)
#include <windows.h>
#else
#include <iconv.h>
#include <string.h>
#include <iostream>
#endif

namespace hku {

double HKU_API roundEx(double number, int ndigits){
    double f;
    int i;
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0){
        f = f*10.0;
    }

    if (ndigits < 0){
        number /= f;
    }else{
        number *= f;
    }

    if (number >= 0.0){
        number = std::floor(number + 0.5);
    }else{
        number = std::ceil(number - 0.5);
    }

    if (ndigits < 0){
        number *= f;
    }else{
        number /= f;
    }

    return number;
}

double HKU_API roundUp(double number, int ndigits) {
    double f;
    int i;
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0){
        f = f*10.0;
    }

    if (ndigits < 0){
        number /= f;
    }else{
        number *= f;
    }

    if (number >= 0.0){
        number = std::floor(number + 1.0);
    }else{
        number = std::ceil(number - 1.0);
    }

    if (ndigits < 0){
        number *= f;
    }else{
        number /= f;
    }

    return number;
}

double HKU_API roundDown(double number, int ndigits) {
    double f;
    int i;
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0){
        f = f*10.0;
    }

    if (ndigits < 0){
        number /= f;
    }else{
        number *= f;
    }

    if (number >= 0.0){
        number = std::floor(number);
    }else{
        number = std::ceil(number);
    }

    if (ndigits < 0){
        number *= f;
    }else{
        number /= f;
    }

    return number;
}

#if defined(BOOST_WINDOWS)
/**
 * 将UTF8编码的字符串转换为GB2312编码的字符串
 * @param szinput 待转换的原始UTF8编码的字符串
 * @return 以GB2312编码的字符串
 * @note 仅在Windows平台下生效
 */
string HKU_API utf8_to_gb(const char* szinput) {
    wchar_t *strSrc;
    char *szRes;
    string nullStr;
    if (!szinput) {
        return nullStr;
    }

    int i = MultiByteToWideChar(CP_UTF8, 0, szinput, -1, NULL, 0);
    if (i == 0) {
        return nullStr;
    }
    strSrc = new wchar_t[i+1];
    if (!MultiByteToWideChar(CP_UTF8, 0, szinput, -1, strSrc, i)) {
        delete[] strSrc;
        return nullStr;
    }

    i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
    if (i == 0) {
        return nullStr;
    }

    szRes = new char[i+1];
    if (!WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL)) {
        delete[] szRes;
        return nullStr;
    }

    string result(szRes);

    delete []strSrc;
    delete []szRes;
    return result;
}

string HKU_API utf8_to_gb(const string& szinput) {
    return utf8_to_gb(szinput.c_str());
}

/**
 * 将GB2312编码的字符串转换为UTF8编码的字符串
 * @param szinput 待转换的原始GB2312编码的字符串
 * @return 以UTF8编码的字符串
 * @note 仅在Windows平台下生效
 */
string HKU_API gb_to_utf8(const char * szinput) {
    wchar_t *strSrc;
    char *szRes;
    string nullstr;
    if (!szinput) {
        return nullstr;
    }

    int i = MultiByteToWideChar(CP_ACP, 0, szinput, -1, NULL, 0);
    if (0 == i) {
        return nullstr;
    }

    strSrc = new wchar_t[i+1];
    if (!MultiByteToWideChar(CP_ACP, 0, szinput, -1, strSrc, i)) {
        delete[] strSrc;
        return nullstr;
    }

    i = WideCharToMultiByte(CP_UTF8, 0, strSrc, -1, NULL, 0, NULL, NULL);
    if (0 == i) {
        return nullstr;
    }

    szRes = new char[i + 1];
    if (!WideCharToMultiByte(CP_UTF8, 0, strSrc, -1, szRes, i, NULL, NULL)) {
        delete[] szRes;
        return nullstr;
    }

    string result(szRes);

    delete []strSrc;
    delete []szRes;
    return result;
}

string HKU_API gb_to_utf8(const string& szinput) {
    return gb_to_utf8(szinput.c_str());
}

#else /* else for defined(BOOST_WINDOWS) */
string HKU_API utf8_to_gb(const string& szinput) {
    char *inbuf=const_cast<char*>(szinput.c_str());
    size_t inlen = strlen(inbuf);
    size_t outlen = inlen;
    char *outbuf = (char *)malloc(outlen);
    memset(outbuf, 0, outlen);
    char *in = inbuf;
    char *out = outbuf;
    iconv_t cd=iconv_open("gbk","utf-8");
    iconv(cd, &in, &inlen, &out, &outlen);
    iconv_close(cd);
    string result(outbuf);
    free(outbuf);
    return result;
}

string HKU_API gb_to_utf8(const string& szinput) {
    char *inbuf=const_cast<char*>(szinput.c_str());
    size_t inlen = strlen(inbuf);
    size_t outlen = inlen * 2;
    char *outbuf = (char *)malloc(outlen);
    memset(outbuf, 0, outlen);
    char *in = inbuf;
    char *out = outbuf;
    iconv_t cd=iconv_open("utf-8","gbk");
    iconv(cd, &in, &inlen, &out, &outlen);
    iconv_close(cd);
    string result(outbuf);
    free(outbuf);
    return result;
}

#endif /* defined(BOOST_WINDOWS) */

} /*namespace*/
<|endoftext|>


<commit_before>#include "PathTracer.hpp"
#include "system/System.hpp"
#include "system/Random.hpp"
#include <chrono>

PathTracer::PathTracer(const std::shared_ptr<Scene> & scene) {
	// Add all scene objects to the raycaster.
	for(const auto & obj : scene->objects) {
		if(obj.mesh()->tangents.empty()){
			Log::Error() << "The path tracer requires local tangent frames for all meshes." << std::endl;
		}
		_raycaster.addMesh(*obj.mesh(), obj.model());
	}
	_raycaster.updateHierarchy();
	_scene = scene;
}

glm::vec3 F(glm::vec3 F0, float VdotH){
	return F0 + std::pow(1.0f - VdotH, 5.0f) * (glm::vec3(1.0f) - F0);
}

float D(float NdotH, float alpha){
	const float halfDenum = NdotH * NdotH * (alpha * alpha - 1.0f) + 1.0f;
	const float halfTerm = alpha / std::max(0.0001f, halfDenum);
	return halfTerm * halfTerm * glm::one_over_pi<float>();
}

float V(float NdotL, float NdotV, float alpha){
	// Correct version.
	const float alpha2 = alpha * alpha;
	const float visL = NdotV * std::sqrt((-NdotL * alpha2 + NdotL) * NdotL + alpha2);
	const float visV = NdotL * std::sqrt((-NdotV * alpha2 + NdotV) * NdotV + alpha2);
	return 0.5f / std::max(0.0001f, visV + visL);
}

glm::vec3 brdf(const glm::vec3 & wo, const glm::vec3 & baseColor, float roughness, float metallic, glm::vec3 & wi, float & pdf){
	const float probaSpecular = glm::mix(1.0f / (glm::dot(baseColor, glm::vec3(1.0f)) / 3.0f + 1.0f), 1.0f,  metallic);

	const float roughClamp = std::max(0.045f, roughness);
	const float alpha = std::max(0.0001f, roughClamp*roughClamp);

	if(Random::Float() < probaSpecular){
		// Sample specular lobe.
		const float a2 = alpha * alpha;
		const float x = Random::Float();
		// for dielectrics, Walter et al. have a roughness rescaling hack.
		// alpha * (1.2f - 0.2f * std::sqrt(std::abs(wi.z)));
		const float phiH = Random::Float() * glm::two_pi<float>();
		const float cosThetaHSqr = std::min((1.0f - x) / ((a2 - 1.0f) * x + 1.0f), 1.0f);
		const float cosThetaH = std::sqrt(cosThetaHSqr);
		const float sinThetaH = std::sqrt(1.0f - cosThetaHSqr);
		const glm::vec3 lh(sinThetaH * cos(phiH), sinThetaH * sin(phiH), cosThetaH);
		// wi is outgoing here. wo is outgoing also.
		wi = 2.0f * glm::dot(wo, lh) * lh - wo;
	} else {
		// Else sample diffuse lobe.
		wi = Random::sampleCosineHemisphere();
		if(wo.z < 0.0f){
			wi.z *= -1.0f;
		}
	}
	if(wi.z < 0.0f){
		pdf = 0.0f;
		return glm::vec3(0.0f);
	}
	const glm::vec3 h = glm::normalize(wi + wo);
	const float NdotH = std::max(h.z, 0.0f);
	const float VdotH = std::max(glm::dot(wi,h), 0.0f);
	const float NdotL = std::max(wo.z, 0.0f);
	const float NdotV = std::max(wi.z, 0.0f);

	// Evaluate D(h)
	const float Dh = D(NdotH, alpha);
	// Evaluate the total PDF.
	const float hPdf = Dh * NdotH;
	pdf = glm::mix(glm::one_over_pi<float>() * NdotV, hPdf / (4.0f * std::max(0.0001f, VdotH)), probaSpecular);
	if(pdf == 0.0f){
		return glm::vec3(0.0f);
	}
	// Evaluate the total BRDF and weight it.
	const glm::vec3 F0 = glm::mix(glm::vec3(0.04f), baseColor, metallic);
	const glm::vec3 spec = Dh * V(NdotL, NdotV, alpha) * F(F0, VdotH);
	const glm::vec3 diff = (1.0f - metallic) * glm::one_over_pi<float>() * baseColor * (glm::vec3(1.0f) - F0);
	const glm::vec3 brdf = (diff + spec) * NdotV;
	return brdf / pdf;
}

void PathTracer::render(const Camera & camera, size_t samples, size_t depth, Image & render) {

	// Safety checks.
	if(!_scene) {
		Log::Error() << "[PathTracer] No scene available." << std::endl;
		return;
	}
	if(render.components != 3) {
		Log::Warning() << "[PathTracer] Expected a RGB image." << std::endl;
	}
	const size_t samplesOld = samples;
	samples					= size_t(std::pow(2, std::round(std::log2(float(samplesOld)))));
	if(samplesOld != samples) {
		Log::Warning() << "[PathTracer] Non power-of-2 samples count. Using " << samples << " instead." << std::endl;
	}

	// Compute incremental pixel shifts.
	glm::vec3 corner, dx, dy;
	camera.pixelShifts(corner, dx, dy);

	// Prepare the stratified grid.
	// We know that we have 2^k samples.
	const int k = int(std::floor(std::log2(samples)));
	// If even, just use k samples on each side.
	glm::ivec2 stratesCount(int(std::pow(2, k / 2)));
	if(k % 2 == 1) {
		//  Else dispatch the extraneous factor of 2 on the horizontal axis.
		stratesCount[0] = int(std::pow(2, (k + 1) / 2));
		stratesCount[1] = int(std::pow(2, (k - 1) / 2));
	}
	const glm::vec2 stratesSize = 1.0f / glm::vec2(stratesCount);

	// Start chrono.
	const auto start = std::chrono::steady_clock::now();

	// Parallelize on each row of the image.
	System::forParallel(0, size_t(render.height), [&render, samples, stratesCount, &stratesSize, &corner, &dx, &dy, &camera, depth, this](size_t y) {
		for(size_t x = 0; x < size_t(render.width); ++x) {
			for(size_t sid = 0; sid < samples; ++sid) {
				// Find the grid location.
				const int sidy = int(sid) / stratesCount.x;
				const int sidx = int(sid) % stratesCount.x;

				// Draw random shift in [0.0,1.0f) for jittering.
				const float jx = Random::Float();
				const float jy = Random::Float();
				// Compute position in the stratification grid.
				const glm::vec2 gridPos = glm::vec2(float(sidx) + jx, float(sidy) + jy);
				// Position in screen space.
				const glm::vec2 screenPos = gridPos * stratesSize + glm::vec2(x, y);

				// Derive a position on the image plane from the pixel.
				const glm::vec2 ndcPos = screenPos / glm::vec2(render.width, render.height);
				// Place the point on the near plane in clip space.
				const glm::vec3 worldPos = corner + ndcPos.x * dx + ndcPos.y * dy;

				glm::vec3 rayPos = camera.position();
				glm::vec3 rayDir = glm::normalize(worldPos - camera.position());

				glm::vec3 sampleColor(0.0f);
				glm::vec3 attenuation(1.0f);

				for(size_t did = 0; did < depth; ++did) {
					// Query closest intersection.
					const Raycaster::RayHit hit = _raycaster.intersects(rayPos, rayDir);

					// If no hit, background.
					if(!hit.hit) {
						const Scene::Background mode = _scene->backgroundMode;

						// If direct background hit, produce the correct color without attenuation.
						if(did == 0) {
							if(mode == Scene::Background::IMAGE) {
								const Image & image = _scene->background->textures()[0]->images[0];
								sampleColor			= image.rgbl(ndcPos.x, ndcPos.y);
							} else if(mode == Scene::Background::SKYBOX) {
								const Texture * tex = _scene->background->textures()[0];
								sampleColor			= tex->sampleCubemap(glm::normalize(rayDir));
							} else {
								sampleColor = _scene->backgroundColor;
							}
							// \todo Support sampling atmospheric simulation.
							break;
						}

						// Else, only environment maps contribute to indirect illumination (for now).
						if(mode == Scene::Background::SKYBOX) {
							const Texture * tex = _scene->background->textures()[0];
							sampleColor += attenuation * tex->sampleCubemap(glm::normalize(rayDir));
						}
						break;
					}

					// Fetch geometry infos...
					const Object & obj = _scene->objects[hit.meshId];
					const Mesh & mesh  = *obj.mesh();
					const glm::vec3 p  = rayPos + hit.dist * rayDir;
					const glm::mat4 invtp = glm::inverse(glm::transpose(obj.model()));
					glm::vec3 n = glm::normalize(Raycaster::interpolateAttribute(hit, mesh, mesh.normals));
					glm::vec3 t = glm::normalize(Raycaster::interpolateAttribute(hit, mesh, mesh.tangents));
					// Convert to world frame.
					n = glm::normalize(glm::vec3(invtp * glm::vec4(n, 0.0f)));
					t = glm::normalize(glm::vec3(invtp * glm::vec4(t, 0.0f)));
					// Enforce orthogonality.
					const glm::vec3 b = glm::normalize(glm::cross(n,t));
					t = glm::normalize(glm::cross(b,n));

					const glm::mat3 tbn(t, b, n);
					const glm::mat3 itbn = glm::transpose(tbn);
					const glm::vec2 uv = Raycaster::interpolateAttribute(hit, mesh, mesh.texcoords);

					// Fetch base color from texture.
					const Image & image  = obj.textures()[0]->images[0];
					const glm::vec4 bCol = image.rgbal(uv.x, uv.y);
					const Image & imageRMAO  = obj.textures()[2]->images[0];
					const glm::vec4 rmao = imageRMAO.rgbal(uv.x, uv.y);
					// In case of alpha cut-out, just update the position to the intersection and keep casting.
					// The 'mini' margin will ensures that we don't reintersect the same surface.
					if(bCol.a < 0.01f) {
						rayPos = p;
						continue;
					}

					// When sampling and evaluating the BRDF, work in the local (t,b,n) frame
					/// \todo Create a frame for meshes with no UVs.
					float pdf = 0.0f;
					const glm::vec3 wo = glm::normalize(itbn * (-rayDir));
					const glm::vec3 baseColor = glm::pow(glm::vec3(bCol), glm::vec3(2.2f));
					glm::vec3 wi;
					glm::vec3 eval = brdf(wo, baseColor, rmao.r, rmao.g, wi, pdf);
					const glm::vec3 nextRayDir = glm::normalize(tbn * wi);
					// Bounce decay.
					attenuation *= eval;

					// Compute lighting.
					// Cast a ray toward one of the lights, at random.
					if(!_scene->lights.empty()){
						// Light sampling.
						glm::vec3 illumination(0.0f);
						// No support for geometric emitters for now.
						const unsigned int lid = Random::Int(0, _scene->lights.size()-1);
						const auto & light = _scene->lights[lid];
						glm::vec3 direction;
						float falloff;
						if(light->visible(p+0.001f*n, _raycaster, direction, falloff)) {
							const float diffuse = glm::max(glm::dot(n, direction), 0.0f);
							illumination += falloff * diffuse * light->intensity();
						}
						// Because we only sample analytical lights, we can't hit an emitter via the raycaster, so no double-hit case to consider for now.
						sampleColor += attenuation * illumination;
					}

					// Update position and ray direction.
					if(did < depth - 1) {
						rayPos = p;
						rayDir = glm::normalize(nextRayDir);
					}
				}
				// Modulate and store.
				render.rgb(int(x), int(y)) += sampleColor;
			}
		}
	});

	// Normalize and gamma correction.
	System::forParallel(0, size_t(render.height), [&render, &samples](size_t y) {
		for(size_t x = 0; x < (render.width); ++x) {
			const glm::vec3 color	  = render.rgb(int(x), int(y)) / float(samples);
			render.rgb(int(x), int(y)) = glm::pow(color, glm::vec3(1.0f / 2.2f));
		}
	});

	// Display duration.
	const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
	Log::Info() << "[PathTracer] Rendering took " << duration.count() / 1000.0f << "s at " << render.width << "x" << render.height << "." << std::endl;
}
<commit_msg>PathTracer: clamping, multiscattering hack.<commit_after>#include "PathTracer.hpp"
#include "system/System.hpp"
#include "system/Random.hpp"
#include <chrono>

PathTracer::PathTracer(const std::shared_ptr<Scene> & scene) {
	// Add all scene objects to the raycaster.
	for(const auto & obj : scene->objects) {
		if(obj.mesh()->tangents.empty()){
			Log::Error() << "The path tracer requires local tangent frames for all meshes." << std::endl;
		}
		_raycaster.addMesh(*obj.mesh(), obj.model());
	}
	_raycaster.updateHierarchy();
	_scene = scene;
}

glm::vec3 F(glm::vec3 F0, float VdotH){
	return F0 + std::pow(1.0f - VdotH, 5.0f) * (glm::vec3(1.0f) - F0);
}

float D(float NdotH, float alpha){
	const float halfDenum = NdotH * NdotH * (alpha * alpha - 1.0f) + 1.0f;
	const float halfTerm = alpha / std::max(0.0001f, halfDenum);
	return halfTerm * halfTerm * glm::one_over_pi<float>();
}

float V(float NdotL, float NdotV, float alpha){
	// Correct version.
	const float alpha2 = alpha * alpha;
	const float visL = NdotV * std::sqrt((-NdotL * alpha2 + NdotL) * NdotL + alpha2);
	const float visV = NdotL * std::sqrt((-NdotV * alpha2 + NdotV) * NdotV + alpha2);
	return 0.5f / std::max(0.0001f, visV + visL);
}

glm::vec3 brdf(const glm::vec3 & wo, const glm::vec3 & baseColor, float roughness, float metallic, glm::vec3 & wi, float & pdf){
	const float probaSpecular = glm::mix(1.0f / (glm::dot(baseColor, glm::vec3(1.0f)) / 3.0f + 1.0f), 1.0f,  metallic);

	const float roughClamp = std::max(0.045f, roughness);
	const float alpha = std::max(0.0001f, roughClamp*roughClamp);

	if(Random::Float() < probaSpecular){
		// Sample specular lobe.
		const float a2 = alpha * alpha;
		const float x = Random::Float();
		// for dielectrics, Walter et al. have a roughness rescaling hack.
		// alpha * (1.2f - 0.2f * std::sqrt(std::abs(wi.z)));
		const float phiH = Random::Float() * glm::two_pi<float>();
		const float cosThetaHSqr = std::min((1.0f - x) / ((a2 - 1.0f) * x + 1.0f), 1.0f);
		const float cosThetaH = std::sqrt(cosThetaHSqr);
		const float sinThetaH = std::sqrt(1.0f - cosThetaHSqr);
		const glm::vec3 lh(sinThetaH * cos(phiH), sinThetaH * sin(phiH), cosThetaH);
		// wi is outgoing here. wo is outgoing also.
		wi = 2.0f * glm::dot(wo, lh) * lh - wo;
	} else {
		// Else sample diffuse lobe.
		wi = Random::sampleCosineHemisphere();
		if(wo.z < 0.0f){
			wi.z *= -1.0f;
		}
	}
	if(wi.z < 0.0f){
		pdf = 0.0f;
		return glm::vec3(0.0f);
	}
	const glm::vec3 h = glm::normalize(wi + wo);
	const float NdotH = std::max(h.z, 0.0f);
	const float VdotH = std::max(glm::dot(wi,h), 0.0f);
	const float NdotL = std::max(wo.z, 0.0f);
	const float NdotV = std::max(wi.z, 0.0f);

	// Evaluate D(h)
	const float Dh = D(NdotH, alpha);
	// Evaluate the total PDF.
	const float hPdf = Dh * NdotH;
	pdf = glm::mix(glm::one_over_pi<float>() * NdotV, hPdf / (4.0f * std::max(0.0001f, VdotH)), probaSpecular);
	if(pdf == 0.0f){
		return glm::vec3(0.0f);
	}
	// Evaluate the total BRDF and weight it.
	const glm::vec3 F0 = glm::mix(glm::vec3(0.04f), baseColor, metallic);
	const glm::vec3 specular = Dh * V(NdotL, NdotV, alpha) * F(F0, VdotH);

	const glm::vec3 diffuse = (1.0f - metallic) * glm::one_over_pi<float>() * baseColor * (1.0f - F0);
	// Multi scattering adjustment hack.
	const glm::vec3 multiAdj = glm::vec3(1.0f) + (2.0f * alpha * alpha * NdotV) * F0;
	const glm::vec3 brdf = (diffuse + specular * multiAdj) * NdotV;
	return brdf / pdf;
}

void PathTracer::render(const Camera & camera, size_t samples, size_t depth, Image & render) {

	// Safety checks.
	if(!_scene) {
		Log::Error() << "[PathTracer] No scene available." << std::endl;
		return;
	}
	if(render.components != 3) {
		Log::Warning() << "[PathTracer] Expected a RGB image." << std::endl;
	}
	const size_t samplesOld = samples;
	samples					= size_t(std::pow(2, std::round(std::log2(float(samplesOld)))));
	if(samplesOld != samples) {
		Log::Warning() << "[PathTracer] Non power-of-2 samples count. Using " << samples << " instead." << std::endl;
	}

	// Compute incremental pixel shifts.
	glm::vec3 corner, dx, dy;
	camera.pixelShifts(corner, dx, dy);

	// Prepare the stratified grid.
	// We know that we have 2^k samples.
	const int k = int(std::floor(std::log2(samples)));
	// If even, just use k samples on each side.
	glm::ivec2 stratesCount(int(std::pow(2, k / 2)));
	if(k % 2 == 1) {
		//  Else dispatch the extraneous factor of 2 on the horizontal axis.
		stratesCount[0] = int(std::pow(2, (k + 1) / 2));
		stratesCount[1] = int(std::pow(2, (k - 1) / 2));
	}
	const glm::vec2 stratesSize = 1.0f / glm::vec2(stratesCount);

	// Start chrono.
	const auto start = std::chrono::steady_clock::now();

	// Parallelize on each row of the image.
	System::forParallel(0, size_t(render.height), [&render, samples, stratesCount, &stratesSize, &corner, &dx, &dy, &camera, depth, this](size_t y) {
		for(size_t x = 0; x < size_t(render.width); ++x) {
			for(size_t sid = 0; sid < samples; ++sid) {
				// Find the grid location.
				const int sidy = int(sid) / stratesCount.x;
				const int sidx = int(sid) % stratesCount.x;

				// Draw random shift in [0.0,1.0f) for jittering.
				const float jx = Random::Float();
				const float jy = Random::Float();
				// Compute position in the stratification grid.
				const glm::vec2 gridPos = glm::vec2(float(sidx) + jx, float(sidy) + jy);
				// Position in screen space.
				const glm::vec2 screenPos = gridPos * stratesSize + glm::vec2(x, y);

				// Derive a position on the image plane from the pixel.
				const glm::vec2 ndcPos = screenPos / glm::vec2(render.width, render.height);
				// Place the point on the near plane in clip space.
				const glm::vec3 worldPos = corner + ndcPos.x * dx + ndcPos.y * dy;

				glm::vec3 rayPos = camera.position();
				glm::vec3 rayDir = glm::normalize(worldPos - camera.position());

				glm::vec3 sampleColor(0.0f);
				glm::vec3 attenuation(1.0f);

				for(size_t did = 0; did < depth; ++did) {
					// Query closest intersection.
					const Raycaster::RayHit hit = _raycaster.intersects(rayPos, rayDir);

					// If no hit, background.
					if(!hit.hit) {
						const Scene::Background mode = _scene->backgroundMode;

						// If direct background hit, produce the correct color without attenuation.
						if(did == 0) {
							if(mode == Scene::Background::IMAGE) {
								const Image & image = _scene->background->textures()[0]->images[0];
								sampleColor			= image.rgbl(ndcPos.x, ndcPos.y);
							} else if(mode == Scene::Background::SKYBOX) {
								const Texture * tex = _scene->background->textures()[0];
								sampleColor			= tex->sampleCubemap(glm::normalize(rayDir));
							} else {
								sampleColor = _scene->backgroundColor;
							}
							// \todo Support sampling atmospheric simulation.
							break;
						}

						// Else, only environment maps contribute to indirect illumination (for now).
						if(mode == Scene::Background::SKYBOX) {
							const Texture * tex = _scene->background->textures()[0];
							sampleColor += attenuation * tex->sampleCubemap(glm::normalize(rayDir));
						}
						break;
					}

					// Fetch geometry infos...
					const Object & obj = _scene->objects[hit.meshId];
					const Mesh & mesh  = *obj.mesh();
					const glm::vec3 p  = rayPos + hit.dist * rayDir;
					const glm::mat4 invtp = glm::inverse(glm::transpose(obj.model()));
					glm::vec3 n = glm::normalize(Raycaster::interpolateAttribute(hit, mesh, mesh.normals));
					glm::vec3 t = glm::normalize(Raycaster::interpolateAttribute(hit, mesh, mesh.tangents));
					// Convert to world frame.
					n = glm::normalize(glm::vec3(invtp * glm::vec4(n, 0.0f)));
					t = glm::normalize(glm::vec3(invtp * glm::vec4(t, 0.0f)));
					// Enforce orthogonality.
					const glm::vec3 b = glm::normalize(glm::cross(n,t));
					t = glm::normalize(glm::cross(b,n));

					const glm::mat3 tbn(t, b, n);
					const glm::mat3 itbn = glm::transpose(tbn);
					const glm::vec2 uv = Raycaster::interpolateAttribute(hit, mesh, mesh.texcoords);

					// Fetch base color from texture.
					const Image & image  = obj.textures()[0]->images[0];
					const glm::vec4 bCol = image.rgbal(uv.x, uv.y);
					const Image & imageRMAO  = obj.textures()[2]->images[0];
					const glm::vec4 rmao = imageRMAO.rgbal(uv.x, uv.y);
					// In case of alpha cut-out, just update the position to the intersection and keep casting.
					// The 'mini' margin will ensures that we don't reintersect the same surface.
					if(bCol.a < 0.01f) {
						rayPos = p;
						continue;
					}

					// When sampling and evaluating the BRDF, work in the local (t,b,n) frame
					/// \todo Create a frame for meshes with no UVs.
					float pdf = 0.0f;
					const glm::vec3 wo = glm::normalize(itbn * (-rayDir));
					const glm::vec3 baseColor = glm::pow(glm::vec3(bCol), glm::vec3(2.2f));
					glm::vec3 wi;
					glm::vec3 eval = brdf(wo, baseColor, rmao.r, rmao.g, wi, pdf);
					const glm::vec3 nextRayDir = glm::normalize(tbn * wi);
					// Bounce decay.
					attenuation *= eval;

					// Compute lighting.
					// Cast a ray toward one of the lights, at random.
					if(!_scene->lights.empty()){
						// Light sampling.
						glm::vec3 illumination(0.0f);
						// No support for geometric emitters for now.
						const unsigned int lid = Random::Int(0, _scene->lights.size()-1);
						const auto & light = _scene->lights[lid];
						glm::vec3 direction;
						float falloff;
						if(light->visible(p+0.001f*n, _raycaster, direction, falloff)) {
							const float diffuse = glm::max(glm::dot(n, direction), 0.0f);
							illumination += falloff * diffuse * light->intensity();
						}
						// Because we only sample analytical lights, we can't hit an emitter via the raycaster, so no double-hit case to consider for now.
						sampleColor += attenuation * illumination;
					}

					// Update position and ray direction.
					if(did < depth - 1) {
						rayPos = p;
						rayDir = glm::normalize(nextRayDir);
					}
				}
				// Clamp and store.
				render.rgb(int(x), int(y)) += glm::min(sampleColor, 5.0f);
			}
		}
	});

	// Normalize and gamma correction.
	System::forParallel(0, size_t(render.height), [&render, &samples](size_t y) {
		for(size_t x = 0; x < (render.width); ++x) {
			const glm::vec3 color	  = render.rgb(int(x), int(y)) / float(samples);
			render.rgb(int(x), int(y)) = glm::pow(color, glm::vec3(1.0f / 2.2f));
		}
	});

	// Display duration.
	const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
	Log::Info() << "[PathTracer] Rendering took " << duration.count() / 1000.0f << "s at " << render.width << "x" << render.height << "." << std::endl;
}
<|endoftext|>