Помощь · Поиск · Пользователи · Календарь
Полная версия этой страницы: Программизмы
AnimeKazan Forum > Разное > Железо и программы
da-nie
В данной теме предполагается приводить решения в области разработки программного обеспечения.
--------------------------------------------------

Написание прокси-сервера

Начнём с прокси-сервера (proxy-server). Пишется такая штука с использованием, конечно же, сокетов. Ниже будет приведён исходник и программа простого однопоточного, но многоклиентского прокси-сервера. Сервер создаётся на порту 8080. Создаётся сокет основного сервера и сразу переводится в неблокирующий режим, а с помощью контейнеров stl организуется список клиентов. Затем с помощью функции select определяются события с сокетом основного сервера, с сокетами клиентов и с сокетами серверов, к которым желают подключиться клиенты.
Таковых событий обрабатывается два:
1) Read - сокет готов на чтение. Если такое событие случилось с сокетом основного сервера, то это значит, что к нам желает подключиться новый клиент. Для остальных сокетов это сигнал о поступлении данных в сокет.
2) Exeption - произошло какое-либо исключение. Трактуется как разрыв связи. (Кстати, не уверен, что в функции select на этой позиции находятся исключения - но я где-то видел что это они самые. Могу и ошибаться)

Собственно, подключив клиента, мы готовимся для начала принять заголовок его запроса. Для этого есть у нас класс работы с байтовыми последовательностями CLine. Из простоты не имеющий конструктора копий и операции присваивания. Ну да ничего. В общем, клиент пришлёт нам заголовок, мы прочтём адрес и подключимся к серверу, попутно отправив ему этот самый принятый и дешифрованный заголовок. Потом отправим следующие данные от клиента, примем ответ сервера и ... нет, отключаться не будем. Отключаемся мы от клиента и сервера только когда кто-то из них разрывает канал со своей стороны. Теперь о гадостях. Клиент может установить с прокси-сервером соединение, в которое он отправит последовательно по-порядку несколько разных запросов. То есть, цепочку запрос-ответ, запрос-ответ. И адрес может давать разный. Не разрывая соединения. Так вот, когда сервер нам ответит, мы должны выставить переменную Answer в true. И если клиент снова пришлёт запрос, мы должны проверить, в тот же хост отправляет клиент сообщение или нет. Если окажется, что в другой, то мы должны отключиться от сервера и начать собирать заголовок клиента заново. Вот, собственно, и все тонкости работы этого прокси-сервера.

Исходный код основного потока:

ThreadServer.h
Код
#ifndef THREAD_SERVER_H
#define THREAD_SERVER_H

#include "stdafx.h"
#include <string.h>
#include <winsock2.h>


#include <list>
using namespace std;

#include "cline.h"

struct SConnect
{
SOCKET Server;//сокет сервера
SOCKET Client;//сокет клиента
CLine cLine_Header;//здесь будет формироваться заголовок
CLine cLine_Host;//здесь хранится имя хоста
bool Answer;//true-сервер уже нам отвечал
bool Header;//true-заголовок собран
};

UINT ThreadServer(LPVOID pParam);//поток подключения

class CThreadServer
{
protected:  
  char *buffer;//буфер приёма/передачи
  list<SConnect*> list_SConnectPtr;//список клиентов
public:  
  //конструктор
  CThreadServer(void);
  //деструктор
  ~CThreadServer();
  void Processing(void);//запустить
protected:
  void ClientDisconnect(SConnect *sConnectPtr);//отключение от клиента и его сервера
  bool ClientReaden(SConnect* sConnectPtr);//принять данные клиента
  bool ServerReaden(SConnect* sConnectPtr);//принять данные сервера
  void Pause(int time);//пауза
  SOCKET ConnectAsServer(char *Host);//подключение к серверу
  bool SendPackage(SOCKET Socket,char *Package,int Size);//отправка сообщения  
  void AddLog(char *String);//добавить в log.txt строку
  void AddLog(char *String,int Size);//добавить в log.txt набор символов длины Size
  void AddLog(int Code);//добавить в log.txt значение
};

#endif


ThreadServer.cpp
Код
#include "threadserver.h"

CEvent cEvent_ThreadServerExit;//требование на выход из потока
CThreadServer cThreadServer;//класс потока подключения

UINT ThreadServer(LPVOID pParam)
{
cThreadServer.Processing();
return(0);
}
//функции класса потока передачи
CThreadServer::CThreadServer(void)
{
buffer=new char[16384];
}
CThreadServer::~CThreadServer()
{
delete(buffer);
}
void CThreadServer::Processing(void)
{
//настроим сокеты
WSADATA wsadata;
int error=WSAStartup(0x0202,&wsadata);
if (error!=0)
{
  MessageBox(NULL,"Ошибка инициализации сокетов","Ошибка",MB_OK);
  WSACleanup();
  return;
}
if (wsadata.wVersion!=0x0202)
{
  MessageBox(NULL,"Не та версия библиотеки сокетов","Ошибка",MB_OK);
  WSACleanup();
  return;
}
//ждём подключения
SOCKET server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//создаём сокет
struct sockaddr_in server_addr;
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(8080);//порт
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//ждать подключения с любых адресов (т.е. с любых сетевых карт)
//связываем адрес с сокетом
if (bind(server,(SOCKADDR*)&server_addr,sizeof(server_addr))==SOCKET_ERROR)
{
  MessageBox(NULL,"Не могу назначить порт сокету","Ошибка",MB_OK);
  WSACleanup();
  return;
}
//назначаем порт на прослушивание
if (listen(server,10)==SOCKET_ERROR)
{
  MessageBox(NULL,"Не могу поставить сокет на прослушивание","Ошибка",MB_OK);
  WSACleanup();
  return;
}
//переведём сокет сервера в неблокирующий режим
unsigned long nb=1;
if (ioctlsocket(server,FIONBIO,(unsigned long *)&nb)==SOCKET_ERROR)
{
  MessageBox(NULL,"Не могу перевести сокет прокси-сервера в неблокирующий режим","Ошибка",MB_OK);
  WSACleanup();
  return;
}
//очищаем список клиентов (который и так пустой)
list_SConnectPtr.clear();
//ждём соединения
while(1)
{
  if (WaitForSingleObject(cEvent_ThreadServerExit.m_hObject,0)==WAIT_OBJECT_0) break;//получен сигнал на выход
  fd_set Readen;
  FD_ZERO(&Readen);//обнуляем список
  FD_SET(server,&Readen);//добавляем сокет сервера
  fd_set Exeption;
  FD_ZERO(&Exeption);//обнуляем список
  FD_SET(server,&Exeption);//добавляем сокет сервера
  //добавляем список сокетов клиентов и подключённых к ним серверов
  list<SConnect*>::iterator iterator_begin=list_SConnectPtr.begin();
  list<SConnect*>::iterator iterator_end=list_SConnectPtr.end();
  list<SConnect*>::iterator iterator_current=iterator_begin;
  while(iterator_current!=iterator_end)
  {
   SConnect *sConnectPtr=*iterator_current;
   if (sConnectPtr->Client!=INVALID_SOCKET)
   {
    FD_SET(sConnectPtr->Client,&Readen);//добавляем сокет клиента
    FD_SET(sConnectPtr->Client,&Exeption);//добавляем сокет клиента
   }
   if (sConnectPtr->Server!=INVALID_SOCKET)
   {
    FD_SET(sConnectPtr->Server,&Readen);//добавляем сокет сервера
    FD_SET(sConnectPtr->Server,&Exeption);//добавляем сокет сервера
   }
   iterator_current++;
  }
  //ждём событий в системе
  timeval timeout;
  timeout.tv_sec=1;
  timeout.tv_usec=0;
  //спрашиваем, не случилось ли чего с сокетами?
  if (select(0,&Readen,0,&Exeption,&timeout)!=SOCKET_ERROR)
  {
   //не хочет ли подключиться новый клиент?
   if (FD_ISSET(server,&Readen))//новый клиент хочет подключиться к серверу
   {
    struct sockaddr client_addr;
    int addr_size=sizeof(sockaddr);
    SOCKET client=accept(server,(SOCKADDR*)&client_addr,&addr_size);
    if (client==INVALID_SOCKET) continue;
    AddLog("\r\nНовый клиент\r\n");
    //переведём сокет клиента в неблокирующий режим
    nb=1;
    if (ioctlsocket(client,FIONBIO,(unsigned long *)&nb)==INVALID_SOCKET)
    {
     shutdown(client,SD_BOTH);
     closesocket(client);
     AddLog("\r\nНе могу перевести сокет клиента в неблокирующий режим\r\n");
     continue;
    }
    //добавим клиента в список
    SConnect *sConnectPtr=new SConnect;
    sConnectPtr->Client=client;
    sConnectPtr->Server=INVALID_SOCKET;
    sConnectPtr->cLine_Header.Set(NULL,0);
    sConnectPtr->cLine_Host.Set(NULL,0);
    sConnectPtr->Answer=false;
    sConnectPtr->Header=false;
    list_SConnectPtr.push_back(sConnectPtr);
    continue;//на новый виток
   }
   //не произошло ли исключение на сокете сервера?
   if (FD_ISSET(server,&Exeption)) break;//это какой-то сбой
   //теперь посмотрим, как у нас дела с клиентами
   iterator_begin=list_SConnectPtr.begin();
   iterator_end=list_SConnectPtr.end();
   iterator_current=iterator_begin;
   while(iterator_current!=iterator_end)
   {
    SConnect *sConnectPtr=*iterator_current;
    //обработаем события клиента
    if (sConnectPtr->Client!=INVALID_SOCKET)
    {
     if (FD_ISSET(sConnectPtr->Client,&Exeption))//клиент отключился
     {
      AddLog("\r\nКлиент отключился\r\n");
      //отключаемся от сервера и от клиента
      ClientDisconnect(sConnectPtr);
      delete(sConnectPtr);
      //удаляем клиента из списка
      list<SConnect*>::iterator iterator_delete=iterator_current;
      iterator_current++;
      list_SConnectPtr.erase(iterator_delete,iterator_current);      
      break;//на новый виток
     }
     if (FD_ISSET(sConnectPtr->Client,&Readen))//клиент готов на чтение
     {
      if (sConnectPtr->Answer==true)//сервер уже ответил, закрываем подключение со старым сервером
      {
       AddLog("\r\nПовторный запрос от клиента по существующему сокету\r\n");      
       sConnectPtr->cLine_Header.Set(NULL,0);//убираем заголовок
       sConnectPtr->Answer=false;//сервер ещё не отвечал на этот запрос
       sConnectPtr->Header=false;//заголовок ещё не собран
      }
      if (ClientReaden(sConnectPtr)==false)//ошибка чтения
      {
       AddLog("\r\nКлиент отключился\r\n");
       //отключаемся от сервера и от клиента
       ClientDisconnect(sConnectPtr);
       delete(sConnectPtr);
       //удаляем клиента из списка
       list<SConnect*>::iterator iterator_delete=iterator_current;
       iterator_current++;
       list_SConnectPtr.erase(iterator_delete,iterator_current);      
      }
      break;//на новый виток
     }
    }
    //обработаем события сервера
    if (sConnectPtr->Server!=INVALID_SOCKET)
    {
     if (FD_ISSET(sConnectPtr->Server,&Exeption))//сервер отключился
     {
      AddLog("\r\nСервер отключился\r\n");
      //отключаемся от сервера и от клиента
      ClientDisconnect(sConnectPtr);
      delete(sConnectPtr);
      //удаляем клиента из списка
      list<SConnect*>::iterator iterator_delete=iterator_current;
      iterator_current++;
      list_SConnectPtr.erase(iterator_delete,iterator_current);      
      break;//на новый виток
     }
     if (FD_ISSET(sConnectPtr->Server,&Readen))//сервер готов на чтение
     {
      if (ServerReaden(sConnectPtr)==false)//ошибка чтения
      {
       AddLog("\r\nСервер отключился\r\n");
       //отключаемся от сервера и от клиента
       ClientDisconnect(sConnectPtr);
       delete(sConnectPtr);
       //удаляем клиента из списка
       list<SConnect*>::iterator iterator_delete=iterator_current;
       iterator_current++;
       list_SConnectPtr.erase(iterator_delete,iterator_current);      
      }
      break;//на новый виток
     }
    }
    iterator_current++;
   }
  }
}
//удаляем всех клиентов
list<SConnect*>::iterator iterator_begin=list_SConnectPtr.begin();
list<SConnect*>::iterator iterator_end=list_SConnectPtr.end();
list<SConnect*>::iterator iterator_current=iterator_begin;
while(iterator_current!=iterator_end)
{
  SConnect *sConnectPtr=*iterator_current;
  ClientDisconnect(sConnectPtr);
  delete(sConnectPtr);
  iterator_current++;
}
list_SConnectPtr.clear();
//выходим
WSACleanup();
}
void CThreadServer::ClientDisconnect(SConnect *sConnectPtr)
{
unsigned long nb;
//отключаем сервер
if (sConnectPtr->Server!=INVALID_SOCKET)
{
  //переведём сокет сервера в блокирующий режим
  nb=0;
  ioctlsocket(sConnectPtr->Server,FIONBIO,(unsigned long *)&nb);
  //отключаем сервер
  shutdown(sConnectPtr->Server,SD_BOTH);
  closesocket(sConnectPtr->Server);
}
//переведём сокет клиента в блокирующий режим
nb=0;
ioctlsocket(sConnectPtr->Client,FIONBIO,(unsigned long *)&nb);
//отключаем клиента
shutdown(sConnectPtr->Client,SD_BOTH);
closesocket(sConnectPtr->Client);
}
bool CThreadServer::ClientReaden(SConnect* sConnectPtr)
{
AddLog("\r\nКлиент прислал данные\r\n");
//принимаем данные от клиента
int receive=recv(sConnectPtr->Client,buffer,16384,0);
if (receive<=0) return(false);//разрыв соединения
//если к серверу уже подключились, то есть, заголовок собран
if (sConnectPtr->Header==true)
{
  if (SendPackage(sConnectPtr->Server,buffer,receive)==false) return(false);//отправляем данные серверу
}
else
{
  //добавляем данные в буфер
  sConnectPtr->cLine_Header.PushBack(buffer,receive);
  if (sConnectPtr->cLine_Header.FindString(0,"\r\n\r\n",4)>=0)//нашли конец заголовка
  {
   AddLog("\r\nСобран заголовок\r\n");
   sConnectPtr->Header=true;
   //выделяем имя хоста и подключаемся к этому хосту
   int pos1=sConnectPtr->cLine_Header.FindString(0,"Host: ",6);
   if (pos1>=0)
   {
    int pos2=sConnectPtr->cLine_Header.FindString(pos1,"\r\n ",2);
    if (pos2>=0)
    {
     int length=pos2-(pos1+6);
     char *host=sConnectPtr->cLine_Header.GetLineCopy(pos1+6,length);
     if (host!=NULL)
     {
      char *Host=new char[length+1];
      memcpy(Host,host,length);
      Host[length]=0;      
      delete(host);
      //если сокет сервера уже существует (то есть, это повторный запрос клиента по существующему сокету)
      if (sConnectPtr->Server!=INVALID_SOCKET)
      {  
       //выясним, не по старому ли адресу идёт запрос?
       if (sConnectPtr->cLine_Host.Compare(Host,length)==false)//адрес другой
       {
        AddLog("\r\nОтключаемся от сервера\r\n");
        //переведём сокет сервера в блокирующий режим
        unsigned long nb=0;
        ioctlsocket(sConnectPtr->Server,FIONBIO,(unsigned long *)&nb);
        //отключаем сервер
        shutdown(sConnectPtr->Server,SD_BOTH);
        closesocket(sConnectPtr->Server);
        sConnectPtr->Server=ConnectAsServer(Host);//подключаемся к серверу
       }
       //если же адрес старый, то ничего не делаем
      }
      else sConnectPtr->Server=ConnectAsServer(Host);//подключаемся к серверу
      //запоминаем адрес
      sConnectPtr->cLine_Host.Set(Host,length);
      delete[](Host);
      //передаём серверу заголовок
      if (sConnectPtr->Server!=INVALID_SOCKET)
      {
       if (SendPackage(sConnectPtr->Server,sConnectPtr->cLine_Header.GetLine(),sConnectPtr->cLine_Header.GetSize())==false) return(false);//отправляем данные серверу
      }
     }
    }  
    if (sConnectPtr->Server==INVALID_SOCKET) return(false);//если к серверу не подключились
   }
  }
}
AddLog("\r\nПринял данные от клиента\r\n");
return(true);
}
bool CThreadServer::ServerReaden(SConnect* sConnectPtr)
{
AddLog("\r\nПринимаю данные от сервера\r\n");
sConnectPtr->Answer=true;
//раз уже сервер начал отвечать, сообщим клиенту, что больше в этот сокет запросы не принимаются
//shutdown(sConnectPtr->Client,SD_RECEIVE);
int receive=recv(sConnectPtr->Server,buffer,16384,0);
if (receive<=0) return(false);
//отправляем данные клиенту
if (SendPackage(sConnectPtr->Client,buffer,receive)==false) return(false);
AddLog("\r\nПринял данные от сервера\r\n");
return(true);
}
void CThreadServer::Pause(int time)
{
CEvent cEvent;
cEvent.ResetEvent();
WaitForSingleObject(cEvent.m_hObject,time);
}
SOCKET CThreadServer::ConnectAsServer(char *Host)
{
AddLog("\r\nПодключаюсь:[");
AddLog(Host);
AddLog("]\r\n");
//подключаемся к серверу
SOCKET server=socket(AF_INET,SOCK_STREAM,0);//создаём сокет
sockaddr_in target;
target.sin_family=AF_INET;//семество адресов-интернет
target.sin_port=htons(80);//порт сервера
u_long addr=inet_addr(Host);//IP-сервера  
if (addr==INADDR_NONE)//это не IP
{
  hostent *host=gethostbyname(Host);
  if (host==NULL)
  {
   AddLog("\r\nХост не найден\r\n");
   return(INVALID_SOCKET);//ошибка  
  }
  addr=*((u_long*)host->h_addr_list[0]);
}
target.sin_addr.s_addr=addr;
//переведём сокет в неблокирующий режим
unsigned long nb=1;
if (ioctlsocket(server,FIONBIO,(unsigned long *)&nb)==SOCKET_ERROR)
{
  AddLog("\r\nНе могу перевести сокет сервера в неблокирующий режим\r\n");
  return(false);
}
//подключаемся к серверу
int counter=0;
while(1)
{
  if (WaitForSingleObject(cEvent_ThreadServerExit.m_hObject,0)==WAIT_OBJECT_0)
  {
   cEvent_ThreadServerExit.SetEvent();
   return(false);//получен сигнал на выход
  }
  if (connect(server,(LPSOCKADDR)&target,sizeof(target))==SOCKET_ERROR)
  {
   int error_code=WSAGetLastError();
   if (error_code==WSAEWOULDBLOCK) break;//надо подождать (раньше ждали, а сейчас лучше не будем)
   if (error_code==WSAEINPROGRESS) break;//операция выполняется
   if (error_code==WSAEISCONN) break;//сокет уже подключён
   AddLog("\r\nСбой\r\n");
   closesocket(server);
   return(INVALID_SOCKET);//ошибка
  }
  break;
}
AddLog("\r\nПодключился\r\n");
//подключились
return(server);
}
bool CThreadServer::SendPackage(SOCKET Socket,char *Package,int Size)
{
AddLog("\r\nПередаю:\r\n");
AddLog(Package,Size);

timeval timeout;
timeout.tv_sec=1;
timeout.tv_usec=0;
int offset=0;
while(Size>0)
{
  if (WaitForSingleObject(cEvent_ThreadServerExit.m_hObject,0)==WAIT_OBJECT_0)
  {
   cEvent_ThreadServerExit.SetEvent();
   return(false);//получен сигнал на выход
  }
  //узнаем как дела у наших сокетов
  fd_set Writen;
  FD_ZERO(&Writen);//обнуляем список
  FD_SET(Socket,&Writen);//добавляем сокет
  fd_set Exeption;
  FD_ZERO(&Exeption);//обнуляем список
  FD_SET(Socket,&Exeption);//добавляем сокет
  //спрашиваем, не готов ли сокет передавать данные
  if (select(0,0,&Writen,&Exeption,&timeout)!=SOCKET_ERROR)
  {
   if (FD_ISSET(Socket,&Exeption)) return(false);
   if (FD_ISSET(Socket,&Writen))
   {
    int l=send(Socket,Package+offset,Size,0);
    if (l==SOCKET_ERROR)
    {
     int error_code=WSAGetLastError();
     if (error_code==WSAEWOULDBLOCK)//надо подождать
     {
      AddLog("\r\nПауза\r\n");
      Pause(1);
      continue;
     }
     AddLog("\r\nСбой\r\n");
     return(false);
    }
    Size-=l;
    offset+=l;
    AddLog("\r\nПередаём\r\n");
   }
  }
  else
  {
   AddLog("\r\nОшибка сокета\r\n");
   return(false);
  }
}
AddLog("\r\nЗакончил передачу\r\n");
return(true);
}

void CThreadServer::AddLog(char *String)
{
return;//обойдёмся
FILE *file=fopen("log.txt","ab");
fprintf(file,String);
fclose(file);
}
void CThreadServer::AddLog(char *String,int Size)
{
return;//обойдёмся
FILE *file=fopen("log.txt","ab");
fwrite(String,1,Size,file);
fclose(file);
}
void CThreadServer::AddLog(int Code)
{
return;//обойдёмся
char string[255];
sprintf(string,"%i",Code);
AddLog(string);
}


Нажмите для просмотра прикрепленного файла
da-nie
Рисуем графики в MFC

Часто программистам требуется нарисовать в программе график. И если в Builder и Delphi такие компоненты есть, то в Microsoft Visual C такого, разумеется, нет. Нет такого компонента и в MFC. Задача же написания собственного компонента многим программистам представляется через-чур сложной. Однако, это не такая уж и сложная задача. Ниже будет приведена программа, демонстрирующая компонент рисования графиков в MFC.
Особенности:
1) Графики запрашиваются у компонента графиков командой AddGraphics. По этой команде вы получаете указатель на созданный класс данных. Учтите, удалять самим этот указатель НЕЛЬЗЯ. Он будет удалён деструктором класса графиков при завершении работы. Либо вы можете вызвать класс графиков с командой RemoteGraphic, передать ей указатель, а она сама удалит график из списка.
2) При большом количестве данных отображение графиков будет тормозить. Чтобы такого не было, используются уровни. Каждый уровень представляет собой список точек, который достаточен для отображения графиков в нужном масштабе экрана. Этот список нужно создавать вызовом команды класса данных графиков CreateLevel после того, как вы задаёте новые значения точкам графика. Вызывать этот метод нужно потому, что список точек для каждого уровня масштабирования может поменяться и графики будут рисоваться неправильно.
3) Заполняя класс данных графиков, вы всегда заполняете уровень 0.
4) Вы можете получить данные графиков любого уровня, но не можете их изменять самостоятельно
5) Вы можете менять цветовую настройку графиков и способ отображения как захотите.
6) Выделив область, вы можете по левой кнопке мыши её увеличить, по правой - вернуться в оригинальный масштаб
7) Для перемещения по графику зажмите и держите правую кнопку мыши. Способ перемещения (поле/голова) меняется командой SetMoveMode.
8) Вы можете менять количество точек в графиках и из значения не вызывая удаление графика и запрос нового. Вам всего лишь нужно полученному классу данных графика задавать новые значения точек или их количество. При этом если количество требуемых точек не совпадает с тем, что было ранее, старые точки удаляются и память выделяется заново.
9) Вы можете запрещать отображение каких-либо графиков.
10) Вы можете экспортировать вид графиков в метафайл.

Нажмите для просмотра прикрепленного файла

Нажмите для просмотра прикрепленного файла

Все вопросы по использованию компонента можно задавать здесь. smile.gif
da-nie
Кстати, должен сказать, что приведённый прокси-сервер некорректно работает с yandex.ru. Причина по-видимому в том, что у имени уandex несколько серверов с разными IP. А прокси подключается к первому из списка. Как на зло, первый сервер яндекса возвращает "нет такой страницы". По крайней мере, мне кажется, что причина в этом.
da-nie
Используем модели 3D Max в своей программе

Наверное, многих интересовало, каким образом можно вставить в свою программу модели из 3D Max. Ну вот я сейчас и покажу, каким именно способом можно этого добиться.

Итак, нам понадобится скрипт Ивана Дишленко (точнее, моя модификация этого скрипта):

Код
-------------------------------------------------------------------------------------
-- MEGA.ms
-- Exports entire scenes or selected objects with full mesh animation!!!
-- By Ivan Dishlenko  as@vc1.rgec.spb.su
-------------------------------------------------------------------------------------
utility MEGExport "MEGA"
(
local ostream, tabs = ""
group "Options"
(
  checkbox cb_exportSelOnly "Selected Only"
)
group "Frames"
(
  spinner sp_start "From:" range:[animationrange.start,animationrange.end,animationrange.start] type:#Integer
  spinner sp_end "To:" range:[animationrange.start,animationrange.end,animationrange.end] type:#Integer
  spinner sp_step "Step:" range:[animationrange.start + 1,animationrange.end,animationrange.start + 1] type:#Integer
)
button btn_export "Save As..." width:100
button btn_about "About" width:100
-------------------------------------------------------------------------------------
function ExportTarget obj =
(
  Format (tabs+"myobj.target = Targetobject()\n") to:ostream
  Format (tabs+"myobj.target.name = \"%\"\n") obj.target.name to:ostream
  Format (tabs+"myobj.target.transform = %\n") obj.target.transform to:ostream
  Format (tabs+"myobj.target.position += createPos\n") to:ostream
)
-------------------------------------------------------------------------------------
function ExportGeneric obj =
(
  Format (tabs+"%\n") (ClassOf obj) to:ostream
  for prop in GetPropNames obj do
  (
   local propname = prop as String
   local propval  = GetProperty obj prop
   if (ClassOf obj) == Hedra and prop == #vertices then continue
   if propval != undefined then Format (tabs+"\t% %\n") propname propval to:ostream
  )
  if obj.target != undefined then
  ExportTarget obj
  Format (tabs+"end %\n") (ClassOf obj) to:ostream
)
-------------------------------------------------------------------------------------
function ExportMesh meshObj =
(
  Format (tabs+"vertex_amount: %\nface_amount: %\ntexcoord_amount: %\n") meshObj.numverts meshObj.numfaces meshObj.numTVerts to:ostream
  -- Write vertices
  if meshObj.numVerts>0 then
  (
   Format (tabs+"vertex:[\n") to:ostream
   for i = 1 to meshObj.numVerts do
   (
    Format (tabs+"% % % ") ((GetVert meshObj i).x) ((GetVert meshObj i).y) ((GetVert meshObj i).z) to:ostream
    Format (tabs+"% % %\n") (GetNormal meshObj i).x (GetNormal meshObj i).y (GetNormal meshObj i).z to:ostream
   )
   Format (tabs+"]\n") to:ostream
  )
  -- Write faces
  if meshObj.numFaces>0 then
  (
   Format (tabs+"faces:[\n") to:ostream
   for i = 1 to meshObj.numFaces do
   (
    Format (tabs+"% % % ") ((GetFace meshObj i).x as Integer) ((GetFace meshObj i).y as integer) ((GetFace meshObj i).z as Integer) to:ostream
    if meshObj.numTVerts>0 then Format (tabs+"% % %\n") (GetTVFace meshObj i).x (GetTVFace meshObj i).y (GetTVFace meshObj i).z to:ostream
                           else Format (tabs+"\n") to:ostream
   )
   Format (tabs+"]\n") to:ostream
  )
  -- Write texcoord
  if meshObj.numTVerts>0 then
  (
   Format (tabs+"texcoord:[\n") to:ostream
   for i = 1 to meshObj.numTVerts do
   (
    Format (tabs+"% %\n") (GetTVert meshObj i).x (GetTVert meshObj i).y to:ostream     
   )
   Format (tabs+"]\n") to:ostream
  )
)
-------------------------------------------------------------------------------------
function ExportNode node =
(
  Format "frame\n" to:ostream
  if SuperClassOf node == GeometryClass and ClassOf node == Editable_mesh then ExportMesh node
  else if (not (ClassOf node == Targetobject and node.isTarget)) then ExportGeneric node
  else return false
  return true
)
-------------------------------------------------------------------------------------
function RecursiveExportNode node =
(
  if (ExportNode node) == false then return false
  -- Recurse children before writing this node
  for child in node.children do    RecursiveExportNode child
  Format "\n\n" to:ostream
)
-------------------------------------------------------------------------------------
function ExportGMS =
(
  if cb_exportSelOnly.checked then
  (
   for node in selection do ExportNode node
  )
  else
  (
   for node in rootnode.children do RecursiveExportNode node
  )
)
-------------------------------------------------------------------------------------
function GetSaveFileStream =
(
  fname = GetSaveFileName types:"Game Meshes Files (*.gms)|*.gms|All Files(*.*)|*.*|"
  if fname == undefined then return undefined
  ostream = CreateFile fname
  if ostream == undefined then
  (
   MessageBox "Couldn't open file for writing !"
   return undefined
  )
  return ostream
)
------------------------------------------------------------------------------------
on btn_about pressed do
(
  MessageBox "Meshes Export for Games and Animation v1.0\n Copyright о Ivan Dishlenko 2001 \n as@vc1.rgec.spb.su" title:"About MEGA"
) -------------------------------------------------------------------------------------
on btn_export pressed do
(
  ostream = GetSaveFileStream()
  if ostream != undefined then
  (
   i = sp_start.value
   while i <= sp_end.value do
   (
    slidertime = i
    ExportGMS()
    i += sp_step.value
   )
   Close ostream
  )
)
--------------------------------------------------------------------------------------
on sp_start changed value do
(
  slidertime = sp_start.value
)
) -- End MEGExport


Нажмите для просмотра прикрепленного файла

Если в 3D Max загрузить модель, конвертировать её в Editable Mesh, а затем применить к ней этот скрипт, указав с какого по какой кадры выводить модель, то результатом будет файл *.gms, в котором покадрово хранятся составные части модели. То есть, если у вас модель состоит из 5 кусков и вы запросили вывод 10 кадров, то в файле gms будет 50 последовательных записей о положении точек кусков сцены (10 кадров по 5 составных кусочков). Остаётся только этот gms-файл загрузить и, зная, каким кадрам что соответствует, вывести на экран. Учтите, выводится диапазон кадров от N до M включительно! То есть, если нужен один кадр анимации, то вводим от 0 до 0 с шагом 1. smile.gif

Нажмите для просмотра прикрепленного файла

Итак, в результате применения этого скрипта у вас получится файл *.gms вида:

Код
frame
vertex_amount: 38
face_amount: 72
texcoord_amount: 0
vertex:[
0.0 0.0 3.0 0.0 0.0 1.0
0.5 0.0 2.86603 0.536653 0.0819236 0.839817
0.25 0.433013 2.86603 0.197379 0.505717 0.839817
-0.25 0.433013 2.86603 -0.339274 0.423793 0.839817
-0.5 0.0 2.86603 -0.536653 -0.0819236 0.839817
-0.25 -0.433013 2.86603 -0.197379 -0.505717 0.839817
0.25 -0.433013 2.86603 0.339274 -0.423793 0.839817
0.866025 0.0 2.5 0.861905 0.0209195 0.506638
0.433013 0.75 2.5 0.412836 0.756891 0.506638
-0.433013 0.75 2.5 -0.449069 0.735972 0.506638
-0.866025 0.0 2.5 -0.861905 -0.0209195 0.506638
-0.433013 -0.75 2.5 -0.412835 -0.756891 0.506638
0.433013 -0.75 2.5 0.449069 -0.735972 0.506638
1.0 0.0 2.0 0.991369 0.00250149 0.131077
0.5 0.866025 2.0 0.493518 0.859802 0.131077
-0.5 0.866025 2.0 -0.497851 0.8573 0.131077
-1.0 0.0 2.0 -0.991369 -0.00250157 0.131077
-0.5 -0.866025 2.0 -0.493518 -0.859802 0.131077
0.5 -0.866025 2.0 0.497851 -0.8573 0.131077
1.0 0.0 1.0 0.991369 -0.00250145 -0.131077
0.5 0.866025 1.0 0.497851 0.8573 -0.131077
-0.5 0.866025 1.0 -0.493518 0.859801 -0.131077
-1.0 0.0 1.0 -0.991369 0.00250137 -0.131077
-0.5 -0.866025 1.0 -0.497851 -0.8573 -0.131077
0.5 -0.866025 1.0 0.493518 -0.859801 -0.131077
0.866025 0.0 0.5 0.861905 -0.0209194 -0.506638
0.433013 0.75 0.5 0.449069 0.735972 -0.506638
-0.433013 0.75 0.5 -0.412836 0.756891 -0.506638
-0.866025 0.0 0.5 -0.861905 0.0209194 -0.506638
-0.433013 -0.75 0.5 -0.449069 -0.735972 -0.506638
0.433013 -0.75 0.5 0.412836 -0.756891 -0.506638
0.5 0.0 0.133975 0.536653 -0.0819235 -0.839817
0.25 0.433013 0.133975 0.339274 0.423793 -0.839817
-0.25 0.433013 0.133975 -0.197379 0.505717 -0.839817
-0.5 0.0 0.133975 -0.536653 0.0819235 -0.839817
-0.25 -0.433013 0.133975 -0.339274 -0.423793 -0.839817
0.25 -0.433013 0.133975 0.197379 -0.505717 -0.839817
0.0 0.0 0.0 0.0 0.0 -1.0
]
faces:[
1 2 3
1 3 4
1 4 5
1 5 6
1 6 7
1 7 2
2 8 9
2 9 3
3 9 10
3 10 4
4 10 11
4 11 5
5 11 12
5 12 6
6 12 13
6 13 7
7 13 8
7 8 2
8 14 15
8 15 9
9 15 16
9 16 10
10 16 17
10 17 11
11 17 18
11 18 12
12 18 19
12 19 13
13 19 14
13 14 8
14 20 21
14 21 15
15 21 22
15 22 16
16 22 23
16 23 17
17 23 24
17 24 18
18 24 25
18 25 19
19 25 20
19 20 14
20 26 27
20 27 21
21 27 28
21 28 22
22 28 29
22 29 23
23 29 30
23 30 24
24 30 31
24 31 25
25 31 26
25 26 20
26 32 33
26 33 27
27 33 34
27 34 28
28 34 35
28 35 29
29 35 36
29 36 30
30 36 37
30 37 31
31 37 32
31 32 26
32 38 33
33 38 34
34 38 35
35 38 36
36 38 37
37 38 32
]



Здесь frame - означает новый объект; vertex_amount: - количество вершин; face_amount: - количество граней; texcoord_amount: - количество текстурных точек, vertex: - в скобках [ ] идут координаты x,y,z, и компоненты вектора нормали к вершине; faces: - в скобках [ ] перечисляются номера соединяемых вершин и (если есть текстурные точки) номера текстурных точек; texture: - в скобках [ ] идут данные текстурных точек (U и V координаты на текстуре).

Иногда в файл попадают кадры с нулевым количеством вершин, кадры с dummy, кадры с непреобразованными объектами (скажем, если вы забудете преобразовать сферу в Editable Mesh, то в файле будет просто строчка, требующая от 3D Max нарисовать сферу с нужными координатами и радиусом, но никак не вершины и грани сферы) - такие кадры нужно удалять вручную. Их быть в файле не должно!

Итак, теперь у вас есть файл gms. Загружает и выводит его мой класс CGMSModel.

cGMSModel.h
Код
#ifndef CGMS_H
#define CGMS_H

#include "stdafx.h"

#include <string.h>
#include "cdatasource.h"

class CGMSFrame
{
protected:
  int VertexAmount;//число вершин
  int TexCoordAmount;//число текстурных точек
  int FaceAmount;//число граней
  //массивы
  struct SVertex
  {
   float X;
   float Y;
   float Z;
   float NX;
   float NY;
   float NZ;
  } *sVertex;//вершины и нормали
  struct SFace
  {
   int V[3];//вершины
   int T[3];//текстура
  } *sFace;//грани
  struct STexCoord
  {
   float U;
   float V;
  } *sTexCoord;//текстура
public:
  CGMSFrame(void);//конструктор
  ~CGMSFrame();//деструктор
  void Release(void);//освободить память
  bool Load(CDataSource *cDataSourcePtr);//загрузить модель
  void Put(void);//вывести модель
  void PutScale(float s);//вывести кадр с масштабированием
  void GetBoxSize(float &xmin,float &ymin,float &zmin,float &xmax,float &ymax,float &zmax);//получить размер ограничивающего параллелепипеда
protected:
  void GetLexeme(CDataSource *cDataSourcePtr,char *Lexeme,int &LexemeLength,int LexemeMaxSize);//получить лексему
};
//----------------------------------------------------------------------------------------------------
class CGMSModel
{
protected:
  CGMSFrame **cGMSFramePtr;//массив указателей на GMSFrame
  int Frame;//количество кадров
public:
  CGMSModel(void);//конструктор
  ~CGMSModel();//деструктор
  void Release(void);//удалить кадры
  bool Load(char *FileName);//загрузить из файла
  bool Load(int ID);//загрузить из ресурсов
  bool Put(int frame);//вывести кадр
  bool PutScale(int frame,float s);//вывести кадр с масштабированием
  int GetFrame(void);//получить число кадров
  void GetBoxSize(int frame,float &xmin,float &ymin,float &zmin,float &xmax,float &ymax,float &zmax);//получить размер ограничивающего параллелепипеда
};

#endif


cGMSModel.cpp

Код
#include "cgmsmodel.h"

CGMSFrame::CGMSFrame(void)
{
VertexAmount=0;
TexCoordAmount=0;
FaceAmount=0;
sVertex=NULL;
sFace=NULL;
sTexCoord=NULL;
}
CGMSFrame::~CGMSFrame()
{
Release();
}
void CGMSFrame::Release(void)
{
VertexAmount=0;
TexCoordAmount=0;
FaceAmount=0;
if (sVertex!=NULL) delete(sVertex);
sVertex=NULL;
if (sFace!=NULL) delete(sFace);
sFace=NULL;
if (sTexCoord!=NULL) delete(sTexCoord);
sTexCoord=NULL;
}
bool CGMSFrame::Load(CDataSource *cDataSourcePtr)
{
char lexeme[256];
Release();
int mode=0;//текущий режим расшифровки данных
int counter;//счётчик элементов
int sub_counter=0;//счётчик элементов внутри строки
bool texcoord_loaded=false;
bool vertex_loaded=false;
bool face_loaded=false;
while(1)
{
  //читаем файл и выделяем лексемы
  int length;//размер лексемы
  GetLexeme(cDataSourcePtr,lexeme,length,255);
  if (length==0) break;//файл закончился
  //смотрим, что это за лексема
  if (strcmp(lexeme,"frame")==0)//новый кадр
  {
   if (sVertex!=NULL || sTexCoord!=NULL || sFace!=NULL) return(false);//ошибка:неожиданный конец кадра
   if (VertexAmount!=0 || TexCoordAmount!=0 || FaceAmount!=0) return(false);//ошибка:неожиданный конец кадра
   mode=1;//данные кадра найдены
   continue;
  }
  if (mode==0) continue;//пропускаем
  if (strcmp(lexeme,"vertex_amount:")==0)//число вершин
  {
   if (sVertex!=NULL)//ошибка: число вершин уже было задано
   {
    Release();
    return(false);//ошибка
   }
   mode=2;
   continue;
  }
  if (strcmp(lexeme,"face_amount:")==0)//число граней
  {
   if (sFace!=NULL)//ошибка: число граней уже было задано
   {
    Release();
    return(false);//ошибка
   }
   mode=3;
   continue;
  }
  if (strcmp(lexeme,"texcoord_amount:")==0)//число текстурных вершин
  {
   if (sTexCoord!=NULL)//ошибка: число текстурных вершин уже было задано
   {
    Release();
    return(false);//ошибка
   }
   mode=4;
   continue;
  }
  if (strcmp(lexeme,"vertex:[")==0)//данные вершин
  {
   if (sVertex==NULL)//ошибка: число вершин не задано
   {
    Release();
    return(false);//ошибка
   }
   mode=5;
   counter=0;
   sub_counter=0;
   continue;
  }
  if (strcmp(lexeme,"faces:[")==0)//данные граней
  {
   if (sFace==NULL)//ошибка: число граней не задано
   {
    Release();
    return(false);//ошибка
   }
   mode=6;
   counter=0;
   sub_counter=0;
   continue;
  }
  if (strcmp(lexeme,"texcoord:[")==0)//данные текстурных вершин
  {
   if (sTexCoord==NULL)//ошибка: число текстурных вершин не задано
   {
    Release();
    return(false);//ошибка
   }
   mode=7;
   counter=0;
   sub_counter=0;
   continue;
  }
  if (strcmp(lexeme,"]")==0)//конец блока
  {
   if (mode!=5 && mode!=6 && mode!=7)
   {
    Release();
    return(false);//ошибка
   }
   if (mode==5 && counter<VertexAmount)
   {
    Release();
    return(false);//ошибка
   }
   if (mode==6 && counter<FaceAmount)
   {
    Release();
    return(false);//ошибка
   }
   if (mode==7 && counter<TexCoordAmount)
   {
    Release();
    return(false);//ошибка
   }
   if (mode==5) vertex_loaded=true;
   if (mode==6) face_loaded=true;
   if (mode==7) texcoord_loaded=true;
   if (vertex_loaded==true && face_loaded==true)
   {
    if (texcoord_loaded==true) break;//все данные кадра загружены
    if (TexCoordAmount==0) break;//все данные кадра загружены
   }
   mode=1;
   continue;
  }
  //читаем вершины модели
  if (mode==5)
  {
   if (counter>=VertexAmount)
   {
    Release();
    return(false);//ошибка
   }
   float value=(float)(atof(lexeme));
   if (sub_counter==0) sVertex[counter].X=value;
   if (sub_counter==1) sVertex[counter].Z=-value;
   if (sub_counter==2) sVertex[counter].Y=value;
   if (sub_counter==3) sVertex[counter].NX=value;
   if (sub_counter==4) sVertex[counter].NZ=-value;
   if (sub_counter==5) sVertex[counter].NY=value;
   sub_counter++;
   if (sub_counter==6)
   {
    sub_counter=0;
    counter++;
   }
   continue;
  }
  //читаем грани модели
  if (mode==6)
  {
   if (counter>=FaceAmount)
   {
    Release();
    return(false);//ошибка
   }
   int value=atoi(lexeme)-1;
   if (sub_counter==0) sFace[counter].V[0]=value;
   if (sub_counter==1) sFace[counter].V[1]=value;
   if (sub_counter==2) sFace[counter].V[2]=value;
   if (TexCoordAmount>0)//если есть текстурные данные
   {
    if (sub_counter==3) sFace[counter].T[0]=value;
    if (sub_counter==4) sFace[counter].T[1]=value;
    if (sub_counter==5) sFace[counter].T[2]=value;
    sub_counter++;
    if (sub_counter==6)
    {
     sub_counter=0;
     counter++;
    }    
   }
   else
   {
    sub_counter++;
    if (sub_counter==3)
    {
     sub_counter=0;
     counter++;
    }
   }
   continue;
  }
  //читаем текстурные вершины модели
  if (mode==7)
  {
   if (counter>=TexCoordAmount)
   {
    Release();
    return(false);//ошибка
   }
   float value=(float)(atof(lexeme));
   if (sub_counter==0) sTexCoord[counter].U=value;
   if (sub_counter==1) sTexCoord[counter].V=1-value;
   sub_counter++;
   if (sub_counter==2)
   {
    sub_counter=0;
    counter++;
   }
   continue;
  }
  //читаем данные модели
  if (mode==2)//число вершин
  {
   VertexAmount=atoi(lexeme);
   sVertex=new SVertex[VertexAmount];
   mode=1;
   continue;
  }
  if (mode==3)//число граней
  {
   FaceAmount=atoi(lexeme);
   sFace=new SFace[FaceAmount];
   mode=1;
   continue;
  }
  if (mode==4)//число текстурных вершин
  {
   TexCoordAmount=atoi(lexeme);
   sTexCoord=new STexCoord[TexCoordAmount];
   mode=1;
   continue;
  }  
}
if (sTexCoord==NULL && TexCoordAmount!=0)
{
  Release();
  return(false);
}
if (sVertex==NULL && VertexAmount!=0)
{
  Release();
  return(false);
}
if (sFace==NULL && FaceAmount!=0)
{
  Release();
  return(false);
}
if (mode==0)
{
  Release();
  return(false);
}
return(true);
}
void CGMSFrame::Put(void)
{
glBegin(GL_TRIANGLES);
if (TexCoordAmount>0)//если есть текстура
{
  for(int f=0;f<FaceAmount;f++)//для каждой грани
  {
   for(int i=0;i<3;i++)
   {
    int v=sFace[f].V[i];  
    int t=sFace[f].T[i];
    glNormal3f(sVertex[v].NX,sVertex[v].NY,sVertex[v].NZ);
    glTexCoord2f(sTexCoord[t].U,sTexCoord[t].V);    
    glVertex3f(sVertex[v].X,sVertex[v].Y,sVertex[v].Z);
   }
  }
}
else//если нет текстуры
{
  for(int f=0;f<FaceAmount;f++)//для каждой грани
  {
   for(int i=0;i<3;i++)
   {
    int v=sFace[f].V[i];  
    glNormal3f(sVertex[v].NX,sVertex[v].NY,sVertex[v].NZ);
    glVertex3f(sVertex[v].X,sVertex[v].Y,sVertex[v].Z);
   }
  }
}
glEnd();
}
void CGMSFrame::PutScale(float s)
{
glBegin(GL_TRIANGLES);
if (TexCoordAmount>0)//если есть текстура
{
  for(int f=0;f<FaceAmount;f++)//для каждой грани
  {
   for(int i=0;i<3;i++)
   {
    int v=sFace[f].V[i];  
    int t=sFace[f].T[i];
    glNormal3f(sVertex[v].NX,sVertex[v].NY,sVertex[v].NZ);
    glTexCoord2f(sTexCoord[t].U,sTexCoord[t].V);    
    glVertex3f(sVertex[v].X*s,sVertex[v].Y*s,sVertex[v].Z*s);
   }
  }
}
else//если нет текстуры
{
  for(int f=0;f<FaceAmount;f++)//для каждой грани
  {
   for(int i=0;i<3;i++)
   {
    int v=sFace[f].V[i];  
    glNormal3f(sVertex[v].NX,sVertex[v].NY,sVertex[v].NZ);
    glVertex3f(sVertex[v].X*s,sVertex[v].Y*s,sVertex[v].Z*s);
   }
  }
}
glEnd();
}
void CGMSFrame::GetLexeme(CDataSource *cDataSourcePtr,char *Lexeme,int &LexemeLength,int LexemeMaxSize)
{
LexemeLength=0;
int mode=0;
while(1)
{
  unsigned char symbol;
  if (cDataSourcePtr->Read(symbol)==false) break;//файл закончился
  if (symbol>32 && mode==0)
  {
   mode=1;
   LexemeLength=0;
  }
  if (mode==1)
  {
   if (symbol<33 || LexemeLength>=LexemeMaxSize)//конец лексемы
   {
    Lexeme[LexemeLength]=0;//отмечаем конец строки
    return;
   }
   Lexeme[LexemeLength]=symbol;//помещаем символ в лексему
   LexemeLength++;
  }
}
Lexeme[LexemeLength]=0;//отмечаем конец строки
return;
}
void CGMSFrame::GetBoxSize(float &xmin,float &ymin,float &zmin,float &xmax,float &ymax,float &zmax)
{
xmin=0;
xmax=0;
ymin=0;
ymax=0;
zmin=0;
zmax=0;
for(int f=0;f<FaceAmount;f++)//для каждой грани
{
  for(int i=0;i<3;i++)
  {
   int v=sFace[f].V[i];
   float x=sVertex[v].X;
   float y=sVertex[v].Y;
   float z=sVertex[v].Z;
   if (f==0 && i==0)
   {
    xmin=x;
    xmax=x;
    ymin=y;
    ymax=y;
    zmin=z;
    zmax=z;
   }
   if (x<xmin) xmin=x;
   if (y<ymin) ymin=y;
   if (z<zmin) zmin=z;

   if (x>xmax) xmax=x;
   if (y>ymax) ymax=y;
   if (z>zmax) zmax=z;
  }
}
}
//----------------------------------------------------------------------------------------------------
CGMSModel::CGMSModel(void)
{
Frame=0;
cGMSFramePtr=NULL;
}
CGMSModel::~CGMSModel()
{
Release();
}
void CGMSModel::Release(void)
{
for(int n=0;n<Frame;n++) delete(cGMSFramePtr[n]);
if (cGMSFramePtr!=NULL) delete(cGMSFramePtr);
cGMSFramePtr=NULL;
Frame=0;
}
bool CGMSModel::Load(char *FileName)
{
Release();
CDataSource cDataSource(NULL);
if (cDataSource.Open(FileName)==false) return(false);
while(1)
{
  CGMSFrame *cGMSFrame_New=new CGMSFrame;
  if (cGMSFrame_New->Load(&cDataSource)==false) break;//кадры закончились
  CGMSFrame **cGMSFrame_List=new CGMSFrame*[Frame+1];
  for(int n=0;n<Frame;n++) cGMSFrame_List[n]=cGMSFramePtr[n];
  cGMSFrame_List[Frame]=cGMSFrame_New;
  if (cGMSFramePtr!=NULL) delete(cGMSFramePtr);
  cGMSFramePtr=cGMSFrame_List;
  Frame++;
}
cDataSource.Close();
return(true);
}
bool CGMSModel::Load(int ID)
{
Release();
CDataSource cDataSource(NULL);
if (cDataSource.Open(ID)==false) return(false);
while(1)
{
  CGMSFrame *cGMSFrame_New=new CGMSFrame;
  if (cGMSFrame_New->Load(&cDataSource)==false) break;//кадры закончились
  CGMSFrame **cGMSFrame_List=new CGMSFrame*[Frame+1];
  for(int n=0;n<Frame;n++) cGMSFrame_List[n]=cGMSFramePtr[n];
  cGMSFrame_List[Frame]=cGMSFrame_New;
  if (cGMSFramePtr!=NULL) delete(cGMSFramePtr);
  cGMSFramePtr=cGMSFrame_List;
  Frame++;
}
cDataSource.Close();
return(true);
}
bool CGMSModel::Put(int frame)
{
if (Frame<=frame) return(false);
if (frame<0) return(false);
cGMSFramePtr[frame]->Put();
return(true);
}
bool CGMSModel::PutScale(int frame,float s)
{
if (Frame<=frame) return(false);
if (frame<0) return(false);
cGMSFramePtr[frame]->PutScale(s);
return(true);
}
int CGMSModel::GetFrame(void)
{
return(Frame);
}
void CGMSModel::GetBoxSize(int frame,float &xmin,float &ymin,float &zmin,float &xmax,float &ymax,float &zmax)
{
xmin=0;
ymin=0;
zmin=0;
xmax=0;
ymax=0;
zmax=0;
if (Frame<=frame) return;;
if (frame<0) return;
cGMSFramePtr[frame]->GetBoxSize(xmin,ymin,zmin,xmax,ymax,zmax);
}


Нужен так же и класс чтения данных из файла или из ресурсов exe-шника.

CDataSource.h
Код
#ifndef CDATASOURCE_H
#define CDATASOURCE_H

#include <windows.h>
#include <math.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>

class CDataSource
{
protected:
  HMODULE hModule;
  HGLOBAL hGlobal;
  unsigned char *Data;
  int ResourceSize;
  int Offset;

  FILE *File;//открытый файл
public:
  CDataSource(HMODULE hmodule);//конструктор
  ~CDataSource();//деструктор
  bool Open(int ID);//открыть ресурс
  bool Open(char *FileName);//открыть имя файла
  bool Close(void);//закрыть открытый ресурс
  bool Seek(int Pos);//переместиться на позицию
  bool Read(unsigned char &Symbol);//считать символ и перейти к следующему
};
#endif


cDataSource.cpp
Код
#include "cdatasource.h"

CDataSource::CDataSource(HMODULE hmodule)
{
hModule=hmodule;
Data=NULL;
Offset=0;
ResourceSize=0;
hGlobal=NULL;
File=NULL;
}
CDataSource::~CDataSource()
{
Close();
}
bool CDataSource::Open(int ID)
{
Close();
HRSRC hRSRC=FindResource(hModule,(LPSTR)ID,RT_RCDATA);
if (hRSRC==NULL) return(false);
hGlobal=LoadResource(hModule,hRSRC);
if (hGlobal==NULL) return(false);
Data=(unsigned char*)LockResource(hGlobal);
ResourceSize=SizeofResource(hModule,hRSRC);
Offset=0;
return(true);
}
bool CDataSource::Open(char *FileName)
{
Close();
File=fopen(FileName,"rb");
if (File==NULL) return(false);
return(true);
}
bool CDataSource::Close(void)
{
if (File!=NULL) fclose(File);
File=NULL;
if (hGlobal!=NULL) GlobalUnlock(hGlobal);
hGlobal=NULL;
Data=NULL;
Offset=0;
return(true);
}
bool CDataSource::Seek(int Pos)
{
if (File!=NULL)
{
  if (fseek(File,Pos,SEEK_SET)!=0) return(false);
  return(true);
}
if (Data!=NULL)
{
  if (Pos>=ResourceSize) return(false);
  Offset=Pos;
  return(true);
}
return(false);
}
bool CDataSource::Read(unsigned char &Symbol)
{
if (File!=NULL)
{
  if (fread(&Symbol,1,1,File)<=0) return(false);
  return(true);
}
if (Data!=NULL)
{
  if (Offset+(int)sizeof(char)>=ResourceSize) return(false);
  unsigned char *Ptr=(unsigned char*)(Data+Offset);
  Symbol=*Ptr;
  Offset+=sizeof(char);
  return(true);
}
return(false);
}



Ну вот теперь можно вывести gms-файл на экран. Скажем, с помощью просмотрщика таких файлов (в файле находятся исходники всех классов и exe-шник): Нажмите для просмотра прикрепленного файла

Повороты объекта выполняются зажав правую кнопку мыши. Левой кнопкой переключаются кадры по кругу.

Ну и напоследок, несколько моделей gms:

Нажмите для просмотра прикрепленного файла

А вот пример использования моделей в программе:

Скринсейвер "Вращающаяся свинка": Нажмите для просмотра прикрепленного файла
da-nie
А вот пример использования модели с текстурами:

Скринсейвер "Тайский Барсик"

Нажмите для просмотра прикрепленного файла

Нажмите для просмотра прикрепленного файла
da-nie
Преобразование фурье/быстрое преобразование фурье (БПФ)

Иногда бывает очень нужно посчитать дискретное преобразование фурье. Для выборок являющихся степенью двойки, возможно использование быстрого преобразования фурье. Поскольку в целом преобразование фурье возможно и для комплексного сигнала, то оба приведённых ниже преобразования работают с массивами комплексных чисел. Для действительных чисел (а у вас скорее всего именно такие будут выборки) нужно просто задать комплексную часть числа нулём.

furje.h
Код
#ifndef FURJE_H
#define FURJE_H

#include <math.h>

#define M_PI 3.1415926535897932384626433832795

struct SComplex
{
double Real;
double Image;
double Norma;
};

void MakeFurje(int Samples,SComplex *sComplex_EntranceArray,SComplex *sComplex_TargetArray);
void MakeContrFurje(int Samples,SComplex *sComplex_EntranceArray,SComplex *sComplex_TargetArray);
void BaseFurje(int Pow2,SComplex *sComplex_SampleArray,SComplex *sComplex_TargetArray);
void MakeSpeedFurje(int Pow2,SComplex *sComplex_EntranceArray,SComplex *sComplex_TargetArray);
void MakeSpeedContrFurje(int Pow2,SComplex *EntranceArray,SComplex *TargetArray);

#endif


furje.cpp
Код
#include "furje.h"

void MakeFurje(int Samples,SComplex *sComplex_EntranceArray,SComplex *sComplex_TargetArray)
{
int n,m;
double normalize=(double)(1.0/(double)(Samples));
int SamplesDiv2=Samples>>1;
//создаём таблицу синусов и косинусов
double *WC=new double[Samples+1];
double *WS=new double[Samples+1];
double param=(double)(2.0*M_PI/(double)(Samples));
for(n=0;n<SamplesDiv2;n++)
{
  double phase=param*(double)(n);
  WC[n]=(double)(cos(phase));
  WS[n]=(double)(sin(phase));
  WC[n+SamplesDiv2]=-WC[n];
  WS[n+SamplesDiv2]=-WS[n];
}
for(m=0;m<Samples;m++)
{
  double real=0;
  double image=0;
  int pos=0;
  for(int i=0;i<Samples;i++,pos+=m)
  {
   if (pos>=Samples) pos-=Samples;
   real+=sComplex_EntranceArray[i].Real*WC[pos]+sComplex_EntranceArray[i].Image*WS[pos];
   image+=sComplex_EntranceArray[i].Real*WS[pos]-sComplex_EntranceArray[i].Image*WC[pos];
  }
  sComplex_TargetArray[m].Image=-image*normalize;
  sComplex_TargetArray[m].Real=real*normalize;
  sComplex_TargetArray[m].Norma=(double)(sqrt(real*real+image*image));
}
delete(WC);
delete(WS);
}
void MakeContrFurje(int Samples,SComplex *sComplex_EntranceArray,SComplex *sComplex_TargetArray)
{
int n,m;
int SamplesDiv2=Samples>>1;
SComplex *sComplex_SampleArray=new SComplex[Samples];
for(n=0;n<Samples;n++) sComplex_SampleArray[n]=sComplex_EntranceArray[n];
for(n=1;n<SamplesDiv2;n++)
{
  SComplex sComplex_Reserv=sComplex_SampleArray[n];
  sComplex_SampleArray[n]=sComplex_SampleArray[Samples-n];
  sComplex_SampleArray[Samples-n]=sComplex_Reserv;
}
//создаём таблицу синусов и косинусов
double *WC=new double[Samples+1];
double *WS=new double[Samples+1];
double param=(double)(2.0*M_PI/(double)(Samples));
for(n=0;n<SamplesDiv2;n++)
{
  double phase=param*(double)(n);
  WC[n]=(double)(cos(phase));
  WS[n]=(double)(sin(phase));
  WC[n+SamplesDiv2]=-WC[n];
  WS[n+SamplesDiv2]=-WS[n];
}
for(m=0;m<Samples;m++)
{
  double real=0;
  double image=0;
  int pos=0;
  for(int i=0;i<Samples;i++,pos+=m)
  {
   if (pos>=Samples) pos-=Samples;
   real+=sComplex_SampleArray[i].Real*WC[pos]+sComplex_SampleArray[i].Image*WS[pos];
   image+=sComplex_SampleArray[i].Real*WS[pos]-sComplex_SampleArray[i].Image*WC[pos];
  }
  sComplex_TargetArray[m].Image=-image;
  sComplex_TargetArray[m].Real=real;
  sComplex_TargetArray[m].Norma=(double)(sqrt(real*real+image*image));
}
delete(sComplex_SampleArray);
delete(WC);
delete(WS);



}




void BinRevers(SComplex *sComplex_Array,int Samples)
{
int j=0;
int k;
for(int i=0;i<Samples-1;i++)
{
  if (i<j)
  {
   SComplex sComplex_Reserv=sComplex_Array[j];
   sComplex_Array[j]=sComplex_Array[i];
   sComplex_Array[i]=sComplex_Reserv;
  }
  for(k=Samples/2;k<=j;k/=2) j-=k;
  j+=k;
}
}
void BaseFurje(int Pow2,SComplex *sComplex_SampleArray,SComplex *sComplex_TargetArray)
{
//спектр получится зеркальный относительно центральной точки!
//обычно нужна только одна половина (0-Samples/2), другая её повторяет
int Samples=(int)pow(2,Pow2);
//делаем бит-реверсивное преобразование
BinRevers(sComplex_SampleArray,Samples);
//вычисляем преобразование фурье
double ur,ui,wr,wi,tr,ti,ur2;
int i,j,l,le1,le2,ip;
le2=1;
for(l=1;l<=Pow2;l++)
{
  le1=le2;
  le2*=2;
  ur=1.0;
  ui=0.0;
  wr=cos(M_PI/le1);
  wi=-sin(M_PI/le1);
  for(j=0;j<le1;j++)
  {
   for(i=j;i<Samples;i+=le2)
   {
    ip=i+le1;
    tr=sComplex_SampleArray[ip].Real*ur-sComplex_SampleArray[ip].Image*ui;
    ti=sComplex_SampleArray[ip].Real*ui+sComplex_SampleArray[ip].Image*ur;
    sComplex_SampleArray[ip]=sComplex_SampleArray[i];
    sComplex_SampleArray[i].Real+=tr;
    sComplex_SampleArray[i].Image+=ti;
    sComplex_SampleArray[ip].Real-=tr;
    sComplex_SampleArray[ip].Image-=ti;
   }
   ur2=ur*wr-ui*wi;
   ui=ur*wi+ui*wr;
   ur=ur2;
  }
}
//заполним вектор выходных значений
for(int n=0;n<Samples;n++)
{
  sComplex_TargetArray[n]=sComplex_SampleArray[n];
  sComplex_TargetArray[n].Norma=sqrt(sComplex_TargetArray[n].Image*sComplex_TargetArray[n].Image+sComplex_TargetArray[n].Real*sComplex_TargetArray[n].Real);
}
}

void MakeSpeedFurje(int Pow2,SComplex *sComplex_EntranceArray,SComplex *sComplex_TargetArray)
{
//спектр получится зеркальный относительно центральной точки!
//обычно нужна только одна половина (0-Samples/2), другая её повторяет
int Samples=(int)pow(2,Pow2);
//Samples- равно степени 2
SComplex *sComplex_SampleArray=new SComplex[Samples];
for(int n=0;n<Samples;n++)
{
  sComplex_SampleArray[n].Image=sComplex_EntranceArray[n].Image/Samples;
  sComplex_SampleArray[n].Real=sComplex_EntranceArray[n].Real/Samples;
}
BaseFurje(Pow2,sComplex_SampleArray,sComplex_TargetArray);
delete(sComplex_SampleArray);
}
void MakeSpeedContrFurje(int Pow2,SComplex *EntranceArray,SComplex *TargetArray)
{
int n;
int Samples=(int)pow(2,Pow2);
SComplex *sComplex_SampleArray=new SComplex[Samples];
for(n=0;n<Samples;n++) sComplex_SampleArray[n]=EntranceArray[n];
for(n=1;n<Samples/2;n++)
{
  SComplex sComplex_Reserv=sComplex_SampleArray[n];
  sComplex_SampleArray[n]=sComplex_SampleArray[Samples-n];
  sComplex_SampleArray[Samples-n]=sComplex_Reserv;
}
BaseFurje(Pow2,sComplex_SampleArray,TargetArray);
delete(sComplex_SampleArray);
}


Нажмите для просмотра прикрепленного файла
da-nie
Книга "DirectShow и телевидение"

Нажмите для просмотра прикрепленного файла
da-nie
Хоть Direct Show я занялся буквально две недели назад, но в принципе, получение видео с камеры у меня заработало. Поэтому

Получение видео с видеокамер (и иных устройств видеозахвата) средствами Direct Show

В DS все процессы обработки медиаконтента производятся т.н. фильтрами. Фильтр - это по сути элемент обработки медиаданных. Для получения нужных результатов, фильтры соединяют в последовательности - графы. Есть фильтры-источники видео и звука, есть фильтры кодеков, есть фильтры разделителей контента на потоки (отдельно аудио и отдельно видео, например), есть фильтры вывода видео на экран.

Поскольку DS построен на COM, первое, что нужно сделать, это инициализировать COM:

Код
//инициализируем библиотеку COM
if (FAILED(CoInitializeEx(NULL,COINIT_APARTMENTTHREADED)))
{
  MessageBox("Не могу инициализировать библиотеку COM!","Ошибка",MB_OK);
  EndDialog(0);
  return(CDialog::OnInitDialog());
}


Ну и по завершению программы нужно деинициализировать COM:

Код
CoUninitialize();


После инициализации COM, можно начинать получать интерфейсы нужных нам объектво DS:

Код
//создаём менеджера графа фильтров
if (FAILED(CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void **)&pGraphBuilder)))
{
  MessageBox("Не могу создать менеджер графа фильтров!","Ошибка",MB_OK);
  EndDialog(0);
  return(CDialog::OnInitDialog());
}
//получаем интерфейс управления видео
if (FAILED(pGraphBuilder->QueryInterface(IID_IMediaControl,(void **)&pMediaControl)))
{
  MessageBox("Не могу получить интерфейс управления видео!","Ошибка",MB_OK);
  EndDialog(0);
  return(CDialog::OnInitDialog());
}
//создаем построитель графа захвата
if (FAILED(CoCreateInstance(CLSID_CaptureGraphBuilder2,NULL,CLSCTX_INPROC,IID_ICaptureGraphBuilder2,(void**)&pCaptureGraphBuilder2)))
{
  MessageBox("Не могу создать построитель графа захвата!","Ошибка",MB_OK);
  EndDialog(0);
  return(CDialog::OnInitDialog());
}
//получаем интерфейс управления параметрами отображения
if (FAILED(pGraphBuilder->QueryInterface(IID_IBasicVideo,(void **)&pBasicVideo)))
{
  MessageBox("Не могу получить интерфейс управления параметрами отображения!","Ошибка",MB_OK);
  EndDialog(0);
  return(CDialog::OnInitDialog());
}
//получаем интерфейс вывода
if (FAILED(pGraphBuilder->QueryInterface(IID_IVideoWindow,(void **)&pVideoWindow)))
{
  MessageBox("Не могу получить интерфейс вывода!","Ошибка",MB_OK);
  EndDialog(0);
  return(CDialog::OnInitDialog());
}


При этом, все интерфейсы объектов нужно ОБЯЗАТЕЛЬНО при завершении работы программы освободить:

Код
if (pMediaControl!=NULL) pMediaControl->Release();//интерфейс управления видео
if (pCaptureGraphBuilder2!=NULL) pCaptureGraphBuilder2->Release();//построитель графа захвата
if (pBasicVideo!=NULL) pBasicVideo->Release();//интерфейс управления параметрами отображения
if (pVideoWindow!=NULL) pVideoWindow->Release();//интерфейс вывода
if (pBaseFilter_PutVideo!=NULL) pBaseFilter_PutVideo->Release();//интерфейс фильтра вывода видео на экран
if (pBaseFilter_SourceVideo!=NULL) pBaseFilter_SourceVideo->Release();//интерфейс фильтра источника видео

if (pMoniker_SourceDevice!=NULL) pMoniker_SourceDevice->Release();//освобождаем моникер устройва видеозахвата


В процессе работы программы мы построим граф захвата видео используя фильтр устройства захвата видео, которое выберет пользователь из доступных в вашей системе. В граф входит устройство захвата и фильтр отображения видео на экране.

Код
//вызываем диалог запроса источника видео
CDialog_SelectDevice *cDialog_SelectDevice=new CDialog_SelectDevice((LPSTR)IDD_DIALOG_SELECT_DEVICE,NULL);
if (cDialog_SelectDevice->DoModal()!=0)//отменено
{
  DestroyGraph();
  delete(cDialog_SelectDevice);
  return;
}
if (pMoniker_SourceDevice!=NULL) pMoniker_SourceDevice->Release();//освобождаем моникер устройства, если ранее устройство было выбрано
pMoniker_SourceDevice=cDialog_SelectDevice->GetSelectedDeviceMoniker();
delete(cDialog_SelectDevice);
DestroyGraph();
//подключаемся к источнику видео и формируем граф
//получаем объект, идентифицируемый моникером
if (FAILED(pMoniker_SourceDevice->BindToObject(0,0,IID_IBaseFilter,(void**)&pBaseFilter_SourceVideo)))
{
  MessageBox("Не могу указать фильтр источника!","Ошибка",MB_OK);
  DestroyGraph();
  return;
}
//получаем интерфейс вывода видео на экран
if (FAILED(CoCreateInstance(CLSID_VideoRendererDefault,NULL,CLSCTX_INPROC,IID_IBaseFilter,(void **)&pBaseFilter_PutVideo)))
{
  MessageBox("Не могу получить фильтр вывода видео на экран!","Ошибка",MB_OK);
  DestroyGraph();
  return;
}
//указываем нашему графу захвата граф фильтров
pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);
WCHAR wFilterName[MAX_FILTER_NAME];
MultiByteToWideChar(CP_ACP,0,"SourceVideo",-1,wFilterName,MAX_FILTER_NAME);
pGraphBuilder->AddFilter(pBaseFilter_SourceVideo,wFilterName);
MultiByteToWideChar(CP_ACP,0,"PutVideo",-1,wFilterName,MAX_FILTER_NAME);
pGraphBuilder->AddFilter(pBaseFilter_PutVideo,wFilterName);
//получаем контакты фильтров
IPin *pPin_SourceOut;
if (pCaptureGraphBuilder2->FindPin(pBaseFilter_SourceVideo,PINDIR_OUTPUT,&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video,FALSE,0,&pPin_SourceOut)!=S_OK)
{
  MessageBox("Фильтр устройства не имеет нужных контактов!","Ошибка",MB_OK);
  DestroyGraph();
  return;
}
IPin *pPin_VideoIn;
//получим входной контакт любого типа фильтра воспроизведения видео
if (pCaptureGraphBuilder2->FindPin(pBaseFilter_PutVideo,PINDIR_INPUT,0,0,FALSE,0,&pPin_VideoIn)!=S_OK)
{
  MessageBox("Фильтр устройства не имеет нужных контактов!","Ошибка",MB_OK);
  DestroyGraph();
  return;
}
if (FAILED(pGraphBuilder->Connect(pPin_SourceOut,pPin_VideoIn)))
{
  MessageBox("Не могу соединить фильтры!","Ошибка",MB_OK);
  pPin_VideoIn->Release();
  pPin_SourceOut->Release();
  return;
}
pPin_VideoIn->Release();
pPin_SourceOut->Release();

CStatic *cStatic_VideoMap=(CStatic *)GetDlgItem(IDC_STATIC_MAIN_VIDEO_MAP);
cStatic_VideoMap->ModifyStyle(0,WS_CLIPCHILDREN,0);//чтобы окно не затирало окно воспроизведения видео
CRect cRect_Client;
cStatic_VideoMap->GetClientRect(&cRect_Client);
long video_height;
long video_width;
pBasicVideo->get_VideoHeight(&video_height);
pBasicVideo->get_VideoWidth(&video_width);
long width=cRect_Client.right-cRect_Client.left-10;
long height=cRect_Client.bottom-cRect_Client.top-30;
double k_width=(double)width/(double)video_width;
double k_height=(double)height/(double)video_height;
if (k_width>k_height) width=(long)(video_width*k_height);
                  else height=(long)(video_height*k_width);

pVideoWindow->SetWindowPosition(cRect_Client.left+5,cRect_Client.top+15,width,height);
pVideoWindow->put_Owner((OAHWND)cStatic_VideoMap->m_hWnd);
pVideoWindow->put_WindowStyle(WS_CHILD|WS_VISIBLE);
pVideoWindow->put_MessageDrain((OAHWND)cStatic_VideoMap->m_hWnd);
pVideoWindow->put_Visible(OATRUE);
pMediaControl->Run();//запускаем


Между собой фильтры соединятся т.н. контактами. Они бывают входные и выходные и могут иметь разные типы (скажем, контакт видео, контакт аудио, контакт телетекста и т.д.).

А вот так вызывается панель настройки фильтра видеозахвата:

Код
if (pBaseFilter_SourceVideo==NULL) return;//нет фильтра захвата видео
//получаем информацию о фильтре
FILTER_INFO FilterInfo;
if (pBaseFilter_SourceVideo->QueryFilterInfo(&FilterInfo)==S_OK)
{
  //узнаем настройки фильтра
  ISpecifyPropertyPages *pSpecifyPropertyPages;
  pBaseFilter_SourceVideo->QueryInterface(IID_ISpecifyPropertyPages,(void**)&pSpecifyPropertyPages);
  CAUUID cauuid={0};
  pSpecifyPropertyPages->GetPages(&cauuid);
  IUnknown *pUnk=0;
  pBaseFilter_SourceVideo->QueryInterface(&pUnk);
  //вызываем диалог настроек фильтра
  OleCreatePropertyFrame(m_hWnd,0,0,FilterInfo.achName,1,&pUnk,cauuid.cElems,cauuid.pElems,0,0,NULL);
  if (FilterInfo.pGraph!=NULL) FilterInfo.pGraph->Release();//обязательно нужно освободить
  CoTaskMemFree(cauuid.pElems);
  pSpecifyPropertyPages->Release();
}


А вот так перечисляются устройства видеозахвата в системе:

Код
//получаем элементы управления
cListBox_Sources=(CListBox *)GetDlgItem(IDC_LIST_DIALOG_SELECT_DEVICE_SOURCES);
cListBox_Sources->ResetContent();
//заполняем список фильтрами доступных устройств видеозахвата
ICreateDevEnum *pCreateDevEnum;//интерфейс нужен для создания объекта перечисления указанной категории устройств
if (CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_INPROC_SERVER,IID_ICreateDevEnum,(void **)&pCreateDevEnum)==S_OK)
{
  IEnumMoniker *pEnumMoniker=NULL;//используется для перечисления моникеров
  if (pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEnumMoniker,0)==S_OK)
  {
   //перечисляем видео устройства
   ULONG cFetched;
   IMoniker *pMoniker=NULL;//интерфейс позволяет получить указатель на объект, идентифицируемый моникером или получить доступ к хранилищу свойств объекта
   while(pEnumMoniker->Next(1,&pMoniker,&cFetched)==S_OK)
   {
    IPropertyBag *pPropertyBag;//предоставляет доступ к коллекции свойств объекта
    //узнаем параметры устройства, которое идентифицирует моникёр
    if (pMoniker->BindToStorage(0,0,IID_IPropertyBag,(void **)&pPropertyBag)==S_OK)
    {
     VARIANT varName;
     VariantInit(&varName);
     pPropertyBag->Read(L"FriendlyName",&varName,0);
     //добавляем видеоустройство в список
     char DeviceName[MAX_FILTER_NAME];
     if (WideCharToMultiByte(CP_ACP,0,varName.bstrVal,-1,DeviceName,MAX_FILTER_NAME,0,0)>0)
     {
      cListBox_Sources->AddString(DeviceName);
     }
     VariantClear(&varName);    
     pPropertyBag->Release();
    }
    pMoniker->Release();
   }
   pEnumMoniker->Release();
  }
  pCreateDevEnum->Release();
}


Перед завершение программы нужно разрушить граф фильтров:

Код
if (pMediaControl!=NULL) pMediaControl->Stop();//останавливаем воспроизведение видео
IFilterGraph2 *pFilterGraph2;//интерфейс менеджера графа фильтров
if (SUCCEEDED(pGraphBuilder->QueryInterface(&pFilterGraph2)))//получаем интерфейс графа фильтров
{
  IEnumFilters *pEnum;
  if (pFilterGraph2->EnumFilters(&pEnum)==S_OK)
  {
   IBaseFilter *pFilter;
   ULONG cFetched;
   while (S_OK==pEnum->Next(1,&pFilter,&cFetched))
   {
    //удалить фильтр.
    pFilterGraph2->RemoveFilter(pFilter);    
    //обязательно сбросить перечислитель, после удаления фильтра.
    pEnum->Reset();
    pFilter=NULL;
   }
   pEnum=NULL;
  }    
  pFilterGraph2->Release();
}
if (pBaseFilter_PutVideo!=NULL) pBaseFilter_PutVideo->Release();//интерфейс фильтра вывода видео на экран
if (pBaseFilter_SourceVideo!=NULL) pBaseFilter_SourceVideo->Release();//интерфейс фильтра захвата видео
pBaseFilter_PutVideo=NULL;
pBaseFilter_SourceVideo=NULL;


Если вы этого не сделаете, будете получать "Программа выполнила недопустимую операцию".

Картинку можно сохранить в bmp-файл:

Цитата
if (pBasicVideo==NULL) return(NULL);//нет интерфейса работы с видео
if (SUCCEEDED(pBasicVideo->GetCurrentImage(&Size,NULL)))
{
long *Image=new long[Size];//память под картинку
if (SUCCEEDED(pBasicVideo->GetCurrentImage(&Size,Image)))
{
pBasicVideo->GetVideoSize(&Width,&Height);//получаем картинку
return(Image);
}
}

....


//сохраним картинку
SYSTEMTIME st;
GetLocalTime(&st);

BITMAPFILEHEADER bmphdr;
BITMAPINFOHEADER bmpinfo;
DWORD nWritten;

memset(&bmphdr,0,sizeof(bmphdr));
memset(&bmpinfo,0,sizeof(bmpinfo));

bmphdr.bfType=('M'<<8|'B');
bmphdr.bfSize=sizeof(bmphdr)+sizeof(bmpinfo)+Size;
bmphdr.bfOffBits=sizeof(bmphdr)+sizeof(bmpinfo);
bmpinfo.biSize=sizeof(bmpinfo);
bmpinfo.biWidth=Width;
bmpinfo.biHeight=Height;
bmpinfo.biPlanes=1;
bmpinfo.biBitCount=24;
char filename[255];
sprintf(filename,"img[%02i.%02i.%04i %02i_%02i_%02i_%02i].bmp",st.wDay,st.wMonth,st.wYear,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds
);
HANDLE fh=CreateFile(filename,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,
NULL);
if (fh!=INVALID_HANDLE_VALUE)
{
WriteFile(fh,&bmphdr,sizeof(bmphdr),&nWritten,NULL);
WriteFile(fh,Image,Size,&nWritten,NULL);
CloseHandle(fh);
}


Ну, вот и всё из того, чему я научился за две недели. smile.gif

Исходный код и exe-файл программы получения картинки с устройств видеоввода:
Нажмите для просмотра прикрепленного файла

Иногда видео сбивается - его затирает окошко, в которое оно выводится. До конца эту проблему мне решить пока не удалось.
da-nie
Кстати, для работы с сетевыми камерами потребуется фильтр такой сетевой камеры! И далеко не все производители его дают.
Вот Axis даёт. Драйвер можно скачать с их сайта, но там надо регистрироваться. Поэтому всем, у кого камеры Axis или совместимые, вот:

Нажмите для просмотра прикрепленного файла
da-nie
Сжатие изображений с использованием дискретно-косинусного преобразования (ДКП)

Одна из интересных задач - сжатие изображений. Формат jpg использует для этой цели дискретно-косинусное преобразование. Попробуем создать некое подобие сжатия, использующегося в jpg. В интернете на эту тему находится очень много "левого" материала с неправильными формулами (потому что копируют одну неверную статью друг у друга). А вот как должно быть.

Итак, ДКП - это просто разложение по ортам, задаваемых функцией косинус. Для сжатия изображений мы используем двумерное ДКП. Итак, пусть у нас есть изображение, скажем, 640x480 точек в формате 32 бита RGB (т.е. по байту на цвет). Разобьём это изображение на квадраты по 8x8 точек. На самом деле, квадраты могут быть любого размера, но во-первых, принято 8x8, а во-вторых, для этого варианта у меня уже есть кое-какой оптимизированный алгоритм перемножения матриц ДКП. smile.gif

Нажмите для просмотра прикрепленного файла

Теперь каждый такой блок надо сжать. Для начала, RGB значения цветов переводят в цветовую систему YCrCb (она же YUV). Что это такое вы можете почитать в педивикии. smile.gif

Цитата
ПРАВИЛЬНЫЕ ФОРМУЛЫ ПЕРЕВОДА RGB->YUV:

Y=0.299*r+0.587*g+0.114*b

Cr=0.5*r-0.4187*g-0.0813*b

Cb=-0.1687*r-0.3313*g+0.5*b


И обратное преобразование:

Цитата
ПРАВИЛЬНЫЕ ФОРМУЛЫ ПЕРЕВОДА YUV->RGB:

R=Y+1.402*Cr

G=Y-0.34414*Cb-0.71414*Cr

B=Y+1.772*Cb


И не забудьте ограничить полученные R,G,B в диапазоне 0-255. Они легко могут выйти из этого диапазона вследствие ошибок округления или ДКП с потерями.


Между прочим, на этом этапе советую заменить все эти умножения на заранее собранную таблицу размера [0-255] слагаемых для каждого значения R,G,B.
Тогда RGB->YUV будет производиться вот как:

Код
//создаём таблицы цветовых преобразований

  //таблицы для быстрого RGB->YUV преобразования
  int Y_RItem[256];
  int Y_GItem[256];
  int Y_BItem[256];

  int Cr_RItem[256];
  int Cr_GItem[256];
  int Cr_BItem[256];

  int Cb_RItem[256];
  int Cb_GItem[256];
  int Cb_BItem[256];

  //таблицы для быстрого YUV->RGB преобразования
  int R_YItem[256];
  int R_CrItem[256];
  int R_CbItem[256];

  int G_YItem[256];
  int G_CrItem[256];
  int G_CbItem[256];

  int B_YItem[256];
  int B_CrItem[256];
  int B_CbItem[256];

...

for(int color=0;color<256;color++)
{
  //задаём таблицы быстрого RGB->YUV преобразования
  Y_RItem[color]=(int)((16.0f*0.299f*(float)(color)));
  Y_GItem[color]=(int)((16.0f*0.587f*(float)(color)));
  Y_BItem[color]=(int)((16.0f*0.114f*(float)(color)));

  Cr_RItem[color]=(int)((16.0f*0.5f*(float)(color)));
  Cr_GItem[color]=-(int)((16.0f*0.4187f*(float)(color)));
  Cr_BItem[color]=-(int)((16.0f*0.0813f*(float)(color)));

  Cb_RItem[color]=-(int)((16.0f*0.1687f*(float)(color)));
  Cb_GItem[color]=-(int)((16.0f*0.3313f*(float)(color)));
  Cb_BItem[color]=(int)((16.0f*0.5f*(float)(color)));
  //задаём таблицы быстрого YUV->RGB преобразования
  R_YItem[color]=color;
  R_CrItem[color]=(int)((1.402f*(float)(color-128)));
  R_CbItem[color]=0;

  G_YItem[color]=color;
  G_CrItem[color]=-(int)((0.71414f*(float)(color-128)));
  G_CbItem[color]=-(int)((0.34414f*(float)(color-128)));

  B_YItem[color]=color;
  B_CrItem[color]=0;
  B_CbItem[color]=(int)((1.772f*(float)(color-128)));
}
...
//в YUV
    Y=Y_RItem[r]+Y_GItem[g]+Y_BItem[b];
    Cr=(Cr_RItem[r]+Cr_GItem[g]+Cr_BItem[b]+2048);
    Cb=(Cb_RItem[r]+Cb_GItem[g]+Cb_BItem[b]+2048);

//в RGB

   Y=Y>>4;
   Cr=Cr>>4;
   Cb=Cb>>4;
    if (Y<0) Y=0;
    if (Y>255) Y=255;
    if (Cr<0) Cr=0;
    if (Cr>255) Cr=255;
    if (Cb<0) Cb=0;
    if (Cb>255) Cb=255;

    R=R_YItem[yu]+R_CrItem[cr]+R_CbItem[cb];
    G=G_YItem[yu]+G_CrItem[cr]+G_CbItem[cb];
    B=B_YItem[yu]+B_CrItem[cr]+B_CbItem[cb];

    if (R<0) R=0;
    if (R>255) R=255;
    if (G<0) G=0;
    if (G>255) G=255;
    if (B<0) B=0;
    if (B>255) B=255;


Обратите внимание на умножение на 16 при создании таблиц RGB->YUV и на деление на 16 при преобразовании цвета в RGB из YUV. Это формат числа с фиксированной точкой. Под значения после запятой мы отдали 4 бита. А сдвигом на 4 вправо (деление на 16) мы округляем числа.
da-nie
Самое время перейти к матрице ДКП:

Формула, по которой она считается для матрицы 8x8:

Цитата
Правильная формула ДКП:

for(int k=0;k<8;k++)
{
for(int n=0;n<8;n++)
{
float eps;
if (k==0) eps=1.0f/sqrtf(8.0f);
else eps=sqrtf(2.0f/8.0f);
DCP[k*8+n]=(float)(eps*cos(M_PI*(2.0*(float)(n)+1.0)*(float)(k)/(2.0*8)));
DCP_T[n*8+k]=DCP[k*8+n];
}
}


Здесь мы получаем DCP и DCP_T-транспонированную.

А само преобразование делается просто: Y=DCP*X*DCP_T - это прямое. И обратное X=DCP_T*Y*DCP.
Но в этом виде мы его использовать не будем.
Вот если значения этих матриц ДКП умножить на 1/sqrt(8), тогда, сделав умножения прямое или обратное, мы получим, что результат нужно будет поделить на 8. А давайте-ка вообще уйдём от чисел с плавающей точкой. Умножим эти матрицы на 16/sqrt(8) и округлим до целого (если округлить до чётного целого, тогда можно будет все умножения заменить очень просто сдвигами (с нечётным тоже на сдвиги заменяется, но их больше будет)). Тогда после операций перемножения матриц для преобразований нам надо будет результат поделить на (8*16*16). То есть, сделать сдвиг вправо на 11. И при этом нам нафиг не понадобится арифметика с плавающей точкой.

Итак, целочисленная матрица (кстати, в ней в нижеприведённой статье 20-ки заменили на 22 - это не повлияет на результат):

Код
int DCP[64]={16,16,16,16,16,16,16,16,22,18,12,4,-4,-12,-18,-22,22,8,-8,-20,-20,-8,8,22,18,-4,-22,-12,12,22,4,-18,16,-16,-16,16,
16,-16,-16, 16,12,-22,4,18,-18,-4,22,-12,8,-22,22,-8,-8,22,-22,8,4,-12,18,-22,22,-18,12,-4};


В работе Нажмите для просмотра прикрепленного файла показано, что умножения на эту матрицу (и на её транспонированную) можно заменить поэтапным умножением матриц, каждое их которых требует немного операций умножения и сложения. Итого, выигрыш в 2 раза по количеству операций.

Вот эти матрицы DCP=P=P1*P2*P3*P4:

Нажмите для просмотра прикрепленного файла

Ну а я заменил умножение на них справа и слева функциями. Громоздко, но работает (не пугайтесь, я не вручную эти формулы получал smile.gif ):

Код
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//умножение матрицы на матрицу ДКП P1;
void CImage::ProductP1xM(int *M_In,int *M_Out)
{
M_Out[0]=16*M_In[0];
M_Out[1]=16*M_In[1];
M_Out[2]=16*M_In[2];
M_Out[3]=16*M_In[3];
M_Out[4]=16*M_In[4];
M_Out[5]=16*M_In[5];
M_Out[6]=16*M_In[6];
M_Out[7]=16*M_In[7];

M_Out[8]=M_In[8];
M_Out[9]=M_In[9];
M_Out[10]=M_In[10];
M_Out[11]=M_In[11];
M_Out[12]=M_In[12];
M_Out[13]=M_In[13];
M_Out[14]=M_In[14];
M_Out[15]=M_In[15];

M_Out[16]=22*M_In[16]+8*M_In[48];
M_Out[17]=22*M_In[17]+8*M_In[49];
M_Out[18]=22*M_In[18]+8*M_In[50];
M_Out[19]=22*M_In[19]+8*M_In[51];
M_Out[20]=22*M_In[20]+8*M_In[52];
M_Out[21]=22*M_In[21]+8*M_In[53];
M_Out[22]=22*M_In[22]+8*M_In[54];
M_Out[23]=22*M_In[23]+8*M_In[55];

M_Out[24]=M_In[24];
M_Out[25]=M_In[25];
M_Out[26]=M_In[26];
M_Out[27]=M_In[27];
M_Out[28]=M_In[28];
M_Out[29]=M_In[29];
M_Out[30]=M_In[30];
M_Out[31]=M_In[31];

M_Out[32]=16*M_In[32];
M_Out[33]=16*M_In[33];
M_Out[34]=16*M_In[34];
M_Out[35]=16*M_In[35];
M_Out[36]=16*M_In[36];
M_Out[37]=16*M_In[37];
M_Out[38]=16*M_In[38];
M_Out[39]=16*M_In[39];

M_Out[40]=M_In[40];
M_Out[41]=M_In[41];
M_Out[42]=M_In[42];
M_Out[43]=M_In[43];
M_Out[44]=M_In[44];
M_Out[45]=M_In[45];
M_Out[46]=M_In[46];
M_Out[47]=M_In[47];

M_Out[48]=8*M_In[16]-22*M_In[48];
M_Out[49]=8*M_In[17]-22*M_In[49];
M_Out[50]=8*M_In[18]-22*M_In[50];
M_Out[51]=8*M_In[19]-22*M_In[51];
M_Out[52]=8*M_In[20]-22*M_In[52];
M_Out[53]=8*M_In[21]-22*M_In[53];
M_Out[54]=8*M_In[22]-22*M_In[54];
M_Out[55]=8*M_In[23]-22*M_In[55];

M_Out[56]=M_In[56];
M_Out[57]=M_In[57];
M_Out[58]=M_In[58];
M_Out[59]=M_In[59];
M_Out[60]=M_In[60];
M_Out[61]=M_In[61];
M_Out[62]=M_In[62];
M_Out[63]=M_In[63];
}

//умножение матрицы на матрицу ДКП P2
void CImage::ProductP2xM(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[32];
M_Out[1]=M_In[1]+M_In[33];
M_Out[2]=M_In[2]+M_In[34];
M_Out[3]=M_In[3]+M_In[35];
M_Out[4]=M_In[4]+M_In[36];
M_Out[5]=M_In[5]+M_In[37];
M_Out[6]=M_In[6]+M_In[38];
M_Out[7]=M_In[7]+M_In[39];

M_Out[8]=M_In[8];
M_Out[9]=M_In[9];
M_Out[10]=M_In[10];
M_Out[11]=M_In[11];
M_Out[12]=M_In[12];
M_Out[13]=M_In[13];
M_Out[14]=M_In[14];
M_Out[15]=M_In[15];

M_Out[16]=M_In[16];
M_Out[17]=M_In[17];
M_Out[18]=M_In[18];
M_Out[19]=M_In[19];
M_Out[20]=M_In[20];
M_Out[21]=M_In[21];
M_Out[22]=M_In[22];
M_Out[23]=M_In[23];

M_Out[24]=M_In[24];
M_Out[25]=M_In[25];
M_Out[26]=M_In[26];
M_Out[27]=M_In[27];
M_Out[28]=M_In[28];
M_Out[29]=M_In[29];
M_Out[30]=M_In[30];
M_Out[31]=M_In[31];

M_Out[32]=M_In[0]-M_In[32];
M_Out[33]=M_In[1]-M_In[33];
M_Out[34]=M_In[2]-M_In[34];
M_Out[35]=M_In[3]-M_In[35];
M_Out[36]=M_In[4]-M_In[36];
M_Out[37]=M_In[5]-M_In[37];
M_Out[38]=M_In[6]-M_In[38];
M_Out[39]=M_In[7]-M_In[39];

M_Out[40]=M_In[40];
M_Out[41]=M_In[41];
M_Out[42]=M_In[42];
M_Out[43]=M_In[43];
M_Out[44]=M_In[44];
M_Out[45]=M_In[45];
M_Out[46]=M_In[46];
M_Out[47]=M_In[47];

M_Out[48]=M_In[48];
M_Out[49]=M_In[49];
M_Out[50]=M_In[50];
M_Out[51]=M_In[51];
M_Out[52]=M_In[52];
M_Out[53]=M_In[53];
M_Out[54]=M_In[54];
M_Out[55]=M_In[55];

M_Out[56]=M_In[56];
M_Out[57]=M_In[57];
M_Out[58]=M_In[58];
M_Out[59]=M_In[59];
M_Out[60]=M_In[60];
M_Out[61]=M_In[61];
M_Out[62]=M_In[62];
M_Out[63]=M_In[63];
}
//умножение матрицы на матрицу ДКП P3
void CImage::ProductP3xM(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[48];
M_Out[1]=M_In[1]+M_In[49];
M_Out[2]=M_In[2]+M_In[50];
M_Out[3]=M_In[3]+M_In[51];
M_Out[4]=M_In[4]+M_In[52];
M_Out[5]=M_In[5]+M_In[53];
M_Out[6]=M_In[6]+M_In[54];
M_Out[7]=M_In[7]+M_In[55];

M_Out[8]=22*M_In[8]+18*M_In[24]+12*M_In[40]+4*M_In[56];
M_Out[9]=22*M_In[9]+18*M_In[25]+12*M_In[41]+4*M_In[57];
M_Out[10]=22*M_In[10]+18*M_In[26]+12*M_In[42]+4*M_In[58];
M_Out[11]=22*M_In[11]+18*M_In[27]+12*M_In[43]+4*M_In[59];
M_Out[12]=22*M_In[12]+18*M_In[28]+12*M_In[44]+4*M_In[60];
M_Out[13]=22*M_In[13]+18*M_In[29]+12*M_In[45]+4*M_In[61];
M_Out[14]=22*M_In[14]+18*M_In[30]+12*M_In[46]+4*M_In[62];
M_Out[15]=22*M_In[15]+18*M_In[31]+12*M_In[47]+4*M_In[63];

M_Out[16]=M_In[0]-M_In[48];
M_Out[17]=M_In[1]-M_In[49];
M_Out[18]=M_In[2]-M_In[50];
M_Out[19]=M_In[3]-M_In[51];
M_Out[20]=M_In[4]-M_In[52];
M_Out[21]=M_In[5]-M_In[53];
M_Out[22]=M_In[6]-M_In[54];
M_Out[23]=M_In[7]-M_In[55];

M_Out[24]=18*M_In[8]-4*M_In[24]-22*M_In[40]-12*M_In[56];
M_Out[25]=18*M_In[9]-4*M_In[25]-22*M_In[41]-12*M_In[57];
M_Out[26]=18*M_In[10]-4*M_In[26]-22*M_In[42]-12*M_In[58];
M_Out[27]=18*M_In[11]-4*M_In[27]-22*M_In[43]-12*M_In[59];
M_Out[28]=18*M_In[12]-4*M_In[28]-22*M_In[44]-12*M_In[60];
M_Out[29]=18*M_In[13]-4*M_In[29]-22*M_In[45]-12*M_In[61];
M_Out[30]=18*M_In[14]-4*M_In[30]-22*M_In[46]-12*M_In[62];
M_Out[31]=18*M_In[15]-4*M_In[31]-22*M_In[47]-12*M_In[63];

M_Out[32]=M_In[16]+M_In[32];
M_Out[33]=M_In[17]+M_In[33];
M_Out[34]=M_In[18]+M_In[34];
M_Out[35]=M_In[19]+M_In[35];
M_Out[36]=M_In[20]+M_In[36];
M_Out[37]=M_In[21]+M_In[37];
M_Out[38]=M_In[22]+M_In[38];
M_Out[39]=M_In[23]+M_In[39];

M_Out[40]=12*M_In[8]-22*M_In[24]+4*M_In[40]+18*M_In[56];
M_Out[41]=12*M_In[9]-22*M_In[25]+4*M_In[41]+18*M_In[57];
M_Out[42]=12*M_In[10]-22*M_In[26]+4*M_In[42]+18*M_In[58];
M_Out[43]=12*M_In[11]-22*M_In[27]+4*M_In[43]+18*M_In[59];
M_Out[44]=12*M_In[12]-22*M_In[28]+4*M_In[44]+18*M_In[60];
M_Out[45]=12*M_In[13]-22*M_In[29]+4*M_In[45]+18*M_In[61];
M_Out[46]=12*M_In[14]-22*M_In[30]+4*M_In[46]+18*M_In[62];
M_Out[47]=12*M_In[15]-22*M_In[31]+4*M_In[47]+18*M_In[63];

M_Out[48]=M_In[16]-M_In[32];
M_Out[49]=M_In[17]-M_In[33];
M_Out[50]=M_In[18]-M_In[34];
M_Out[51]=M_In[19]-M_In[35];
M_Out[52]=M_In[20]-M_In[36];
M_Out[53]=M_In[21]-M_In[37];
M_Out[54]=M_In[22]-M_In[38];
M_Out[55]=M_In[23]-M_In[39];

M_Out[56]=4*M_In[8]-12*M_In[24]+18*M_In[40]-22*M_In[56];
M_Out[57]=4*M_In[9]-12*M_In[25]+18*M_In[41]-22*M_In[57];
M_Out[58]=4*M_In[10]-12*M_In[26]+18*M_In[42]-22*M_In[58];
M_Out[59]=4*M_In[11]-12*M_In[27]+18*M_In[43]-22*M_In[59];
M_Out[60]=4*M_In[12]-12*M_In[28]+18*M_In[44]-22*M_In[60];
M_Out[61]=4*M_In[13]-12*M_In[29]+18*M_In[45]-22*M_In[61];
M_Out[62]=4*M_In[14]-12*M_In[30]+18*M_In[46]-22*M_In[62];
M_Out[63]=4*M_In[15]-12*M_In[31]+18*M_In[47]-22*M_In[63];
}
//умножение матрицы на матрицу ДКП P4
void CImage::ProductP4xM(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[56];
M_Out[1]=M_In[1]+M_In[57];
M_Out[2]=M_In[2]+M_In[58];
M_Out[3]=M_In[3]+M_In[59];
M_Out[4]=M_In[4]+M_In[60];
M_Out[5]=M_In[5]+M_In[61];
M_Out[6]=M_In[6]+M_In[62];
M_Out[7]=M_In[7]+M_In[63];

M_Out[8]=M_In[0]-M_In[56];
M_Out[9]=M_In[1]-M_In[57];
M_Out[10]=M_In[2]-M_In[58];
M_Out[11]=M_In[3]-M_In[59];
M_Out[12]=M_In[4]-M_In[60];
M_Out[13]=M_In[5]-M_In[61];
M_Out[14]=M_In[6]-M_In[62];
M_Out[15]=M_In[7]-M_In[63];

M_Out[16]=M_In[8]+M_In[48];
M_Out[17]=M_In[9]+M_In[49];
M_Out[18]=M_In[10]+M_In[50];
M_Out[19]=M_In[11]+M_In[51];
M_Out[20]=M_In[12]+M_In[52];
M_Out[21]=M_In[13]+M_In[53];
M_Out[22]=M_In[14]+M_In[54];
M_Out[23]=M_In[15]+M_In[55];

M_Out[24]=M_In[8]-M_In[48];
M_Out[25]=M_In[9]-M_In[49];
M_Out[26]=M_In[10]-M_In[50];
M_Out[27]=M_In[11]-M_In[51];
M_Out[28]=M_In[12]-M_In[52];
M_Out[29]=M_In[13]-M_In[53];
M_Out[30]=M_In[14]-M_In[54];
M_Out[31]=M_In[15]-M_In[55];

M_Out[32]=M_In[16]+M_In[40];
M_Out[33]=M_In[17]+M_In[41];
M_Out[34]=M_In[18]+M_In[42];
M_Out[35]=M_In[19]+M_In[43];
M_Out[36]=M_In[20]+M_In[44];
M_Out[37]=M_In[21]+M_In[45];
M_Out[38]=M_In[22]+M_In[46];
M_Out[39]=M_In[23]+M_In[47];

M_Out[40]=M_In[16]-M_In[40];
M_Out[41]=M_In[17]-M_In[41];
M_Out[42]=M_In[18]-M_In[42];
M_Out[43]=M_In[19]-M_In[43];
M_Out[44]=M_In[20]-M_In[44];
M_Out[45]=M_In[21]-M_In[45];
M_Out[46]=M_In[22]-M_In[46];
M_Out[47]=M_In[23]-M_In[47];

M_Out[48]=M_In[24]+M_In[32];
M_Out[49]=M_In[25]+M_In[33];
M_Out[50]=M_In[26]+M_In[34];
M_Out[51]=M_In[27]+M_In[35];
M_Out[52]=M_In[28]+M_In[36];
M_Out[53]=M_In[29]+M_In[37];
M_Out[54]=M_In[30]+M_In[38];
M_Out[55]=M_In[31]+M_In[39];

M_Out[56]=M_In[24]-M_In[32];
M_Out[57]=M_In[25]-M_In[33];
M_Out[58]=M_In[26]-M_In[34];
M_Out[59]=M_In[27]-M_In[35];
M_Out[60]=M_In[28]-M_In[36];
M_Out[61]=M_In[29]-M_In[37];
M_Out[62]=M_In[30]-M_In[38];
M_Out[63]=M_In[31]-M_In[39];
}
//умножение матрицы на матрицу ДКП слева
void CImage::ProductPxM(int *M_In,int *M_Out)
{
int M_Tmp[64];
ProductP4xM(M_In,M_Tmp);
ProductP3xM(M_Tmp,M_Out);
ProductP2xM(M_Out,M_Tmp);
ProductP1xM(M_Tmp,M_Out);
}

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//умножение матрицы на матрицу ДКП P3T;
void CImage::ProductP3TxM(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[16];
M_Out[1]=M_In[1]+M_In[17];
M_Out[2]=M_In[2]+M_In[18];
M_Out[3]=M_In[3]+M_In[19];
M_Out[4]=M_In[4]+M_In[20];
M_Out[5]=M_In[5]+M_In[21];
M_Out[6]=M_In[6]+M_In[22];
M_Out[7]=M_In[7]+M_In[23];

M_Out[8]=22*M_In[8]+18*M_In[24]+12*M_In[40]+4*M_In[56];
M_Out[9]=22*M_In[9]+18*M_In[25]+12*M_In[41]+4*M_In[57];
M_Out[10]=22*M_In[10]+18*M_In[26]+12*M_In[42]+4*M_In[58];
M_Out[11]=22*M_In[11]+18*M_In[27]+12*M_In[43]+4*M_In[59];
M_Out[12]=22*M_In[12]+18*M_In[28]+12*M_In[44]+4*M_In[60];
M_Out[13]=22*M_In[13]+18*M_In[29]+12*M_In[45]+4*M_In[61];
M_Out[14]=22*M_In[14]+18*M_In[30]+12*M_In[46]+4*M_In[62];
M_Out[15]=22*M_In[15]+18*M_In[31]+12*M_In[47]+4*M_In[63];

M_Out[16]=M_In[32]+M_In[48];
M_Out[17]=M_In[33]+M_In[49];
M_Out[18]=M_In[34]+M_In[50];
M_Out[19]=M_In[35]+M_In[51];
M_Out[20]=M_In[36]+M_In[52];
M_Out[21]=M_In[37]+M_In[53];
M_Out[22]=M_In[38]+M_In[54];
M_Out[23]=M_In[39]+M_In[55];

M_Out[24]=18*M_In[8]-4*M_In[24]-22*M_In[40]-12*M_In[56];
M_Out[25]=18*M_In[9]-4*M_In[25]-22*M_In[41]-12*M_In[57];
M_Out[26]=18*M_In[10]-4*M_In[26]-22*M_In[42]-12*M_In[58];
M_Out[27]=18*M_In[11]-4*M_In[27]-22*M_In[43]-12*M_In[59];
M_Out[28]=18*M_In[12]-4*M_In[28]-22*M_In[44]-12*M_In[60];
M_Out[29]=18*M_In[13]-4*M_In[29]-22*M_In[45]-12*M_In[61];
M_Out[30]=18*M_In[14]-4*M_In[30]-22*M_In[46]-12*M_In[62];
M_Out[31]=18*M_In[15]-4*M_In[31]-22*M_In[47]-12*M_In[63];

M_Out[32]=M_In[32]-M_In[48];
M_Out[33]=M_In[33]-M_In[49];
M_Out[34]=M_In[34]-M_In[50];
M_Out[35]=M_In[35]-M_In[51];
M_Out[36]=M_In[36]-M_In[52];
M_Out[37]=M_In[37]-M_In[53];
M_Out[38]=M_In[38]-M_In[54];
M_Out[39]=M_In[39]-M_In[55];

M_Out[40]=12*M_In[8]-22*M_In[24]+4*M_In[40]+18*M_In[56];
M_Out[41]=12*M_In[9]-22*M_In[25]+4*M_In[41]+18*M_In[57];
M_Out[42]=12*M_In[10]-22*M_In[26]+4*M_In[42]+18*M_In[58];
M_Out[43]=12*M_In[11]-22*M_In[27]+4*M_In[43]+18*M_In[59];
M_Out[44]=12*M_In[12]-22*M_In[28]+4*M_In[44]+18*M_In[60];
M_Out[45]=12*M_In[13]-22*M_In[29]+4*M_In[45]+18*M_In[61];
M_Out[46]=12*M_In[14]-22*M_In[30]+4*M_In[46]+18*M_In[62];
M_Out[47]=12*M_In[15]-22*M_In[31]+4*M_In[47]+18*M_In[63];

M_Out[48]=M_In[0]-M_In[16];
M_Out[49]=M_In[1]-M_In[17];
M_Out[50]=M_In[2]-M_In[18];
M_Out[51]=M_In[3]-M_In[19];
M_Out[52]=M_In[4]-M_In[20];
M_Out[53]=M_In[5]-M_In[21];
M_Out[54]=M_In[6]-M_In[22];
M_Out[55]=M_In[7]-M_In[23];

M_Out[56]=4*M_In[8]-12*M_In[24]+18*M_In[40]-22*M_In[56];
M_Out[57]=4*M_In[9]-12*M_In[25]+18*M_In[41]-22*M_In[57];
M_Out[58]=4*M_In[10]-12*M_In[26]+18*M_In[42]-22*M_In[58];
M_Out[59]=4*M_In[11]-12*M_In[27]+18*M_In[43]-22*M_In[59];
M_Out[60]=4*M_In[12]-12*M_In[28]+18*M_In[44]-22*M_In[60];
M_Out[61]=4*M_In[13]-12*M_In[29]+18*M_In[45]-22*M_In[61];
M_Out[62]=4*M_In[14]-12*M_In[30]+18*M_In[46]-22*M_In[62];
M_Out[63]=4*M_In[15]-12*M_In[31]+18*M_In[47]-22*M_In[63];
}
//умножение матрицы на матрицу ДКП P4T;
void CImage::ProductP4TxM(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[8];
M_Out[1]=M_In[1]+M_In[9];
M_Out[2]=M_In[2]+M_In[10];
M_Out[3]=M_In[3]+M_In[11];
M_Out[4]=M_In[4]+M_In[12];
M_Out[5]=M_In[5]+M_In[13];
M_Out[6]=M_In[6]+M_In[14];
M_Out[7]=M_In[7]+M_In[15];

M_Out[8]=M_In[16]+M_In[24];
M_Out[9]=M_In[17]+M_In[25];
M_Out[10]=M_In[18]+M_In[26];
M_Out[11]=M_In[19]+M_In[27];
M_Out[12]=M_In[20]+M_In[28];
M_Out[13]=M_In[21]+M_In[29];
M_Out[14]=M_In[22]+M_In[30];
M_Out[15]=M_In[23]+M_In[31];

M_Out[16]=M_In[32]+M_In[40];
M_Out[17]=M_In[33]+M_In[41];
M_Out[18]=M_In[34]+M_In[42];
M_Out[19]=M_In[35]+M_In[43];
M_Out[20]=M_In[36]+M_In[44];
M_Out[21]=M_In[37]+M_In[45];
M_Out[22]=M_In[38]+M_In[46];
M_Out[23]=M_In[39]+M_In[47];

M_Out[24]=M_In[48]+M_In[56];
M_Out[25]=M_In[49]+M_In[57];
M_Out[26]=M_In[50]+M_In[58];
M_Out[27]=M_In[51]+M_In[59];
M_Out[28]=M_In[52]+M_In[60];
M_Out[29]=M_In[53]+M_In[61];
M_Out[30]=M_In[54]+M_In[62];
M_Out[31]=M_In[55]+M_In[63];

M_Out[32]=M_In[48]-M_In[56];
M_Out[33]=M_In[49]-M_In[57];
M_Out[34]=M_In[50]-M_In[58];
M_Out[35]=M_In[51]-M_In[59];
M_Out[36]=M_In[52]-M_In[60];
M_Out[37]=M_In[53]-M_In[61];
M_Out[38]=M_In[54]-M_In[62];
M_Out[39]=M_In[55]-M_In[63];

M_Out[40]=M_In[32]-M_In[40];
M_Out[41]=M_In[33]-M_In[41];
M_Out[42]=M_In[34]-M_In[42];
M_Out[43]=M_In[35]-M_In[43];
M_Out[44]=M_In[36]-M_In[44];
M_Out[45]=M_In[37]-M_In[45];
M_Out[46]=M_In[38]-M_In[46];
M_Out[47]=M_In[39]-M_In[47];

M_Out[48]=M_In[16]-M_In[24];
M_Out[49]=M_In[17]-M_In[25];
M_Out[50]=M_In[18]-M_In[26];
M_Out[51]=M_In[19]-M_In[27];
M_Out[52]=M_In[20]-M_In[28];
M_Out[53]=M_In[21]-M_In[29];
M_Out[54]=M_In[22]-M_In[30];
M_Out[55]=M_In[23]-M_In[31];

M_Out[56]=M_In[0]-M_In[8];
M_Out[57]=M_In[1]-M_In[9];
M_Out[58]=M_In[2]-M_In[10];
M_Out[59]=M_In[3]-M_In[11];
M_Out[60]=M_In[4]-M_In[12];
M_Out[61]=M_In[5]-M_In[13];
M_Out[62]=M_In[6]-M_In[14];
M_Out[63]=M_In[7]-M_In[15];
}

//умножение матрицы на матрицу ДКП транспонированное слева
void CImage::ProductPTxM(int *M_In,int *M_Out)
{
int M_Tmp[64];
ProductP1xM(M_In,M_Tmp);//матрица P1 транспонированная одинакова с P1
ProductP2xM(M_Tmp,M_Out);//матрица P2 транспонированная одинакова с P12
ProductP3TxM(M_Out,M_Tmp);
ProductP4TxM(M_Tmp,M_Out);
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//умножение матрицы на матрицу ДКП P1;
void CImage::ProductMxP1(int *M_In,int *M_Out)
{
M_Out[0]=16*M_In[0];
M_Out[1]=M_In[1];
M_Out[2]=22*M_In[2]+8*M_In[6];
M_Out[3]=M_In[3];
M_Out[4]=16*M_In[4];
M_Out[5]=M_In[5];
M_Out[6]=8*M_In[2]-22*M_In[6];
M_Out[7]=M_In[7];

M_Out[8]=16*M_In[8];
M_Out[9]=M_In[9];
M_Out[10]=22*M_In[10]+8*M_In[14];
M_Out[11]=M_In[11];
M_Out[12]=16*M_In[12];
M_Out[13]=M_In[13];
M_Out[14]=8*M_In[10]-22*M_In[14];
M_Out[15]=M_In[15];

M_Out[16]=16*M_In[16];
M_Out[17]=M_In[17];
M_Out[18]=22*M_In[18]+8*M_In[22];
M_Out[19]=M_In[19];
M_Out[20]=16*M_In[20];
M_Out[21]=M_In[21];
M_Out[22]=8*M_In[18]-22*M_In[22];
M_Out[23]=M_In[23];

M_Out[24]=16*M_In[24];
M_Out[25]=M_In[25];
M_Out[26]=22*M_In[26]+8*M_In[30];
M_Out[27]=M_In[27];
M_Out[28]=16*M_In[28];
M_Out[29]=M_In[29];
M_Out[30]=8*M_In[26]-22*M_In[30];
M_Out[31]=M_In[31];

M_Out[32]=16*M_In[32];
M_Out[33]=M_In[33];
M_Out[34]=22*M_In[34]+8*M_In[38];
M_Out[35]=M_In[35];
M_Out[36]=16*M_In[36];
M_Out[37]=M_In[37];
M_Out[38]=8*M_In[34]-22*M_In[38];
M_Out[39]=M_In[39];

M_Out[40]=16*M_In[40];
M_Out[41]=M_In[41];
M_Out[42]=22*M_In[42]+8*M_In[46];
M_Out[43]=M_In[43];
M_Out[44]=16*M_In[44];
M_Out[45]=M_In[45];
M_Out[46]=8*M_In[42]-22*M_In[46];
M_Out[47]=M_In[47];

M_Out[48]=16*M_In[48];
M_Out[49]=M_In[49];
M_Out[50]=22*M_In[50]+8*M_In[54];
M_Out[51]=M_In[51];
M_Out[52]=16*M_In[52];
M_Out[53]=M_In[53];
M_Out[54]=8*M_In[50]-22*M_In[54];
M_Out[55]=M_In[55];

M_Out[56]=16*M_In[56];
M_Out[57]=M_In[57];
M_Out[58]=22*M_In[58]+8*M_In[62];
M_Out[59]=M_In[59];
M_Out[60]=16*M_In[60];
M_Out[61]=M_In[61];
M_Out[62]=8*M_In[58]-22*M_In[62];
M_Out[63]=M_In[63];
}
//умножение матрицы на матрицу ДКП P2
void CImage::ProductMxP2(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[4];
M_Out[1]=M_In[1];
M_Out[2]=M_In[2];
M_Out[3]=M_In[3];
M_Out[4]=M_In[0]-M_In[4];
M_Out[5]=M_In[5];
M_Out[6]=M_In[6];
M_Out[7]=M_In[7];

M_Out[8]=M_In[8]+M_In[12];
M_Out[9]=M_In[9];
M_Out[10]=M_In[10];
M_Out[11]=M_In[11];
M_Out[12]=M_In[8]-M_In[12];
M_Out[13]=M_In[13];
M_Out[14]=M_In[14];
M_Out[15]=M_In[15];

M_Out[16]=M_In[16]+M_In[20];
M_Out[17]=M_In[17];
M_Out[18]=M_In[18];
M_Out[19]=M_In[19];
M_Out[20]=M_In[16]-M_In[20];
M_Out[21]=M_In[21];
M_Out[22]=M_In[22];
M_Out[23]=M_In[23];

M_Out[24]=M_In[24]+M_In[28];
M_Out[25]=M_In[25];
M_Out[26]=M_In[26];
M_Out[27]=M_In[27];
M_Out[28]=M_In[24]-M_In[28];
M_Out[29]=M_In[29];
M_Out[30]=M_In[30];
M_Out[31]=M_In[31];

M_Out[32]=M_In[32]+M_In[36];
M_Out[33]=M_In[33];
M_Out[34]=M_In[34];
M_Out[35]=M_In[35];
M_Out[36]=M_In[32]-M_In[36];
M_Out[37]=M_In[37];
M_Out[38]=M_In[38];
M_Out[39]=M_In[39];

M_Out[40]=M_In[40]+M_In[44];
M_Out[41]=M_In[41];
M_Out[42]=M_In[42];
M_Out[43]=M_In[43];
M_Out[44]=M_In[40]-M_In[44];
M_Out[45]=M_In[45];
M_Out[46]=M_In[46];
M_Out[47]=M_In[47];

M_Out[48]=M_In[48]+M_In[52];
M_Out[49]=M_In[49];
M_Out[50]=M_In[50];
M_Out[51]=M_In[51];
M_Out[52]=M_In[48]-M_In[52];
M_Out[53]=M_In[53];
M_Out[54]=M_In[54];
M_Out[55]=M_In[55];

M_Out[56]=M_In[56]+M_In[60];
M_Out[57]=M_In[57];
M_Out[58]=M_In[58];
M_Out[59]=M_In[59];
M_Out[60]=M_In[56]-M_In[60];
M_Out[61]=M_In[61];
M_Out[62]=M_In[62];
M_Out[63]=M_In[63];
}

//умножение матрицы на матрицу ДКП P3
void CImage::ProductMxP3(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[2];
M_Out[1]=22*M_In[1]+18*M_In[3]+12*M_In[5]+4*M_In[7];
M_Out[2]=M_In[4]+M_In[6];
M_Out[3]=18*M_In[1]-4*M_In[3]-22*M_In[5]-12*M_In[7];
M_Out[4]=M_In[4]-M_In[6];
M_Out[5]=12*M_In[1]-22*M_In[3]+4*M_In[5]+18*M_In[7];
M_Out[6]=M_In[0]-M_In[2];
M_Out[7]=4*M_In[1]-12*M_In[3]+18*M_In[5]-22*M_In[7];

M_Out[8]=M_In[8]+M_In[10];
M_Out[9]=22*M_In[9]+18*M_In[11]+12*M_In[13]+4*M_In[15];
M_Out[10]=M_In[12]+M_In[14];
M_Out[11]=18*M_In[9]-4*M_In[11]-22*M_In[13]-12*M_In[15];
M_Out[12]=M_In[12]-M_In[14];
M_Out[13]=12*M_In[9]-22*M_In[11]+4*M_In[13]+18*M_In[15];
M_Out[14]=M_In[8]-M_In[10];
M_Out[15]=4*M_In[9]-12*M_In[11]+18*M_In[13]-22*M_In[15];

M_Out[16]=M_In[16]+M_In[18];
M_Out[17]=22*M_In[17]+18*M_In[19]+12*M_In[21]+4*M_In[23];
M_Out[18]=M_In[20]+M_In[22];
M_Out[19]=18*M_In[17]-4*M_In[19]-22*M_In[21]-12*M_In[23];
M_Out[20]=M_In[20]-M_In[22];
M_Out[21]=12*M_In[17]-22*M_In[19]+4*M_In[21]+18*M_In[23];
M_Out[22]=M_In[16]-M_In[18];
M_Out[23]=4*M_In[17]-12*M_In[19]+18*M_In[21]-22*M_In[23];

M_Out[24]=M_In[24]+M_In[26];
M_Out[25]=22*M_In[25]+18*M_In[27]+12*M_In[29]+4*M_In[31];
M_Out[26]=M_In[28]+M_In[30];
M_Out[27]=18*M_In[25]-4*M_In[27]-22*M_In[29]-12*M_In[31];
M_Out[28]=M_In[28]-M_In[30];
M_Out[29]=12*M_In[25]-22*M_In[27]+4*M_In[29]+18*M_In[31];
M_Out[30]=M_In[24]-M_In[26];
M_Out[31]=4*M_In[25]-12*M_In[27]+18*M_In[29]-22*M_In[31];

M_Out[32]=M_In[32]+M_In[34];
M_Out[33]=22*M_In[33]+18*M_In[35]+12*M_In[37]+4*M_In[39];
M_Out[34]=M_In[36]+M_In[38];
M_Out[35]=18*M_In[33]-4*M_In[35]-22*M_In[37]-12*M_In[39];
M_Out[36]=M_In[36]-M_In[38];
M_Out[37]=12*M_In[33]-22*M_In[35]+4*M_In[37]+18*M_In[39];
M_Out[38]=M_In[32]-M_In[34];
M_Out[39]=4*M_In[33]-12*M_In[35]+18*M_In[37]-22*M_In[39];

M_Out[40]=M_In[40]+M_In[42];
M_Out[41]=22*M_In[41]+18*M_In[43]+12*M_In[45]+4*M_In[47];
M_Out[42]=M_In[44]+M_In[46];
M_Out[43]=18*M_In[41]-4*M_In[43]-22*M_In[45]-12*M_In[47];
M_Out[44]=M_In[44]-M_In[46];
M_Out[45]=12*M_In[41]-22*M_In[43]+4*M_In[45]+18*M_In[47];
M_Out[46]=M_In[40]-M_In[42];
M_Out[47]=4*M_In[41]-12*M_In[43]+18*M_In[45]-22*M_In[47];

M_Out[48]=M_In[48]+M_In[50];
M_Out[49]=22*M_In[49]+18*M_In[51]+12*M_In[53]+4*M_In[55];
M_Out[50]=M_In[52]+M_In[54];
M_Out[51]=18*M_In[49]-4*M_In[51]-22*M_In[53]-12*M_In[55];
M_Out[52]=M_In[52]-M_In[54];
M_Out[53]=12*M_In[49]-22*M_In[51]+4*M_In[53]+18*M_In[55];
M_Out[54]=M_In[48]-M_In[50];
M_Out[55]=4*M_In[49]-12*M_In[51]+18*M_In[53]-22*M_In[55];

M_Out[56]=M_In[56]+M_In[58];
M_Out[57]=22*M_In[57]+18*M_In[59]+12*M_In[61]+4*M_In[63];
M_Out[58]=M_In[60]+M_In[62];
M_Out[59]=18*M_In[57]-4*M_In[59]-22*M_In[61]-12*M_In[63];
M_Out[60]=M_In[60]-M_In[62];
M_Out[61]=12*M_In[57]-22*M_In[59]+4*M_In[61]+18*M_In[63];
M_Out[62]=M_In[56]-M_In[58];
M_Out[63]=4*M_In[57]-12*M_In[59]+18*M_In[61]-22*M_In[63];
}
//умножение матрицы на матрицу ДКП P4
void CImage::ProductMxP4(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[1];
M_Out[1]=M_In[2]+M_In[3];
M_Out[2]=M_In[4]+M_In[5];
M_Out[3]=M_In[6]+M_In[7];
M_Out[4]=M_In[6]-M_In[7];
M_Out[5]=M_In[4]-M_In[5];
M_Out[6]=M_In[2]-M_In[3];
M_Out[7]=M_In[0]-M_In[1];

M_Out[8]=M_In[8]+M_In[9];
M_Out[9]=M_In[10]+M_In[11];
M_Out[10]=M_In[12]+M_In[13];
M_Out[11]=M_In[14]+M_In[15];
M_Out[12]=M_In[14]-M_In[15];
M_Out[13]=M_In[12]-M_In[13];
M_Out[14]=M_In[10]-M_In[11];
M_Out[15]=M_In[8]-M_In[9];

M_Out[16]=M_In[16]+M_In[17];
M_Out[17]=M_In[18]+M_In[19];
M_Out[18]=M_In[20]+M_In[21];
M_Out[19]=M_In[22]+M_In[23];
M_Out[20]=M_In[22]-M_In[23];
M_Out[21]=M_In[20]-M_In[21];
M_Out[22]=M_In[18]-M_In[19];
M_Out[23]=M_In[16]-M_In[17];

M_Out[24]=M_In[24]+M_In[25];
M_Out[25]=M_In[26]+M_In[27];
M_Out[26]=M_In[28]+M_In[29];
M_Out[27]=M_In[30]+M_In[31];
M_Out[28]=M_In[30]-M_In[31];
M_Out[29]=M_In[28]-M_In[29];
M_Out[30]=M_In[26]-M_In[27];
M_Out[31]=M_In[24]-M_In[25];

M_Out[32]=M_In[32]+M_In[33];
M_Out[33]=M_In[34]+M_In[35];
M_Out[34]=M_In[36]+M_In[37];
M_Out[35]=M_In[38]+M_In[39];
M_Out[36]=M_In[38]-M_In[39];
M_Out[37]=M_In[36]-M_In[37];
M_Out[38]=M_In[34]-M_In[35];
M_Out[39]=M_In[32]-M_In[33];

M_Out[40]=M_In[40]+M_In[41];
M_Out[41]=M_In[42]+M_In[43];
M_Out[42]=M_In[44]+M_In[45];
M_Out[43]=M_In[46]+M_In[47];
M_Out[44]=M_In[46]-M_In[47];
M_Out[45]=M_In[44]-M_In[45];
M_Out[46]=M_In[42]-M_In[43];
M_Out[47]=M_In[40]-M_In[41];

M_Out[48]=M_In[48]+M_In[49];
M_Out[49]=M_In[50]+M_In[51];
M_Out[50]=M_In[52]+M_In[53];
M_Out[51]=M_In[54]+M_In[55];
M_Out[52]=M_In[54]-M_In[55];
M_Out[53]=M_In[52]-M_In[53];
M_Out[54]=M_In[50]-M_In[51];
M_Out[55]=M_In[48]-M_In[49];

M_Out[56]=M_In[56]+M_In[57];
M_Out[57]=M_In[58]+M_In[59];
M_Out[58]=M_In[60]+M_In[61];
M_Out[59]=M_In[62]+M_In[63];
M_Out[60]=M_In[62]-M_In[63];
M_Out[61]=M_In[60]-M_In[61];
M_Out[62]=M_In[58]-M_In[59];
M_Out[63]=M_In[56]-M_In[57];
}

//умножение матрицы на матрицу ДКП справа
void CImage::ProductMxP(int *M_In,int *M_Out)
{
int M_Tmp[64];
ProductMxP1(M_In,M_Tmp);
ProductMxP2(M_Tmp,M_Out);
ProductMxP3(M_Out,M_Tmp);
ProductMxP4(M_Tmp,M_Out);
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//умножение матрицы на матрицу ДКП P3T;
void CImage::ProductMxP3T(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[6];
M_Out[1]=22*M_In[1]+18*M_In[3]+12*M_In[5]+4*M_In[7];
M_Out[2]=M_In[0]-M_In[6];
M_Out[3]=18*M_In[1]-4*M_In[3]-22*M_In[5]-12*M_In[7];
M_Out[4]=M_In[2]+M_In[4];
M_Out[5]=12*M_In[1]-22*M_In[3]+4*M_In[5]+18*M_In[7];
M_Out[6]=M_In[2]-M_In[4];
M_Out[7]=4*M_In[1]-12*M_In[3]+18*M_In[5]-22*M_In[7];

M_Out[8]=M_In[8]+M_In[14];
M_Out[9]=22*M_In[9]+18*M_In[11]+12*M_In[13]+4*M_In[15];
M_Out[10]=M_In[8]-M_In[14];
M_Out[11]=18*M_In[9]-4*M_In[11]-22*M_In[13]-12*M_In[15];
M_Out[12]=M_In[10]+M_In[12];
M_Out[13]=12*M_In[9]-22*M_In[11]+4*M_In[13]+18*M_In[15];
M_Out[14]=M_In[10]-M_In[12];
M_Out[15]=4*M_In[9]-12*M_In[11]+18*M_In[13]-22*M_In[15];

M_Out[16]=M_In[16]+M_In[22];
M_Out[17]=22*M_In[17]+18*M_In[19]+12*M_In[21]+4*M_In[23];
M_Out[18]=M_In[16]-M_In[22];
M_Out[19]=18*M_In[17]-4*M_In[19]-22*M_In[21]-12*M_In[23];
M_Out[20]=M_In[18]+M_In[20];
M_Out[21]=12*M_In[17]-22*M_In[19]+4*M_In[21]+18*M_In[23];
M_Out[22]=M_In[18]-M_In[20];
M_Out[23]=4*M_In[17]-12*M_In[19]+18*M_In[21]-22*M_In[23];

M_Out[24]=M_In[24]+M_In[30];
M_Out[25]=22*M_In[25]+18*M_In[27]+12*M_In[29]+4*M_In[31];
M_Out[26]=M_In[24]-M_In[30];
M_Out[27]=18*M_In[25]-4*M_In[27]-22*M_In[29]-12*M_In[31];
M_Out[28]=M_In[26]+M_In[28];
M_Out[29]=12*M_In[25]-22*M_In[27]+4*M_In[29]+18*M_In[31];
M_Out[30]=M_In[26]-M_In[28];
M_Out[31]=4*M_In[25]-12*M_In[27]+18*M_In[29]-22*M_In[31];

M_Out[32]=M_In[32]+M_In[38];
M_Out[33]=22*M_In[33]+18*M_In[35]+12*M_In[37]+4*M_In[39];
M_Out[34]=M_In[32]-M_In[38];
M_Out[35]=18*M_In[33]-4*M_In[35]-22*M_In[37]-12*M_In[39];
M_Out[36]=M_In[34]+M_In[36];
M_Out[37]=12*M_In[33]-22*M_In[35]+4*M_In[37]+18*M_In[39];
M_Out[38]=M_In[34]-M_In[36];
M_Out[39]=4*M_In[33]-12*M_In[35]+18*M_In[37]-22*M_In[39];

M_Out[40]=M_In[40]+M_In[46];
M_Out[41]=22*M_In[41]+18*M_In[43]+12*M_In[45]+4*M_In[47];
M_Out[42]=M_In[40]-M_In[46];
M_Out[43]=18*M_In[41]-4*M_In[43]-22*M_In[45]-12*M_In[47];
M_Out[44]=M_In[42]+M_In[44];
M_Out[45]=12*M_In[41]-22*M_In[43]+4*M_In[45]+18*M_In[47];
M_Out[46]=M_In[42]-M_In[44];
M_Out[47]=4*M_In[41]-12*M_In[43]+18*M_In[45]-22*M_In[47];

M_Out[48]=M_In[48]+M_In[54];
M_Out[49]=22*M_In[49]+18*M_In[51]+12*M_In[53]+4*M_In[55];
M_Out[50]=M_In[48]-M_In[54];
M_Out[51]=18*M_In[49]-4*M_In[51]-22*M_In[53]-12*M_In[55];
M_Out[52]=M_In[50]+M_In[52];
M_Out[53]=12*M_In[49]-22*M_In[51]+4*M_In[53]+18*M_In[55];
M_Out[54]=M_In[50]-M_In[52];
M_Out[55]=4*M_In[49]-12*M_In[51]+18*M_In[53]-22*M_In[55];

M_Out[56]=M_In[56]+M_In[62];
M_Out[57]=22*M_In[57]+18*M_In[59]+12*M_In[61]+4*M_In[63];
M_Out[58]=M_In[56]-M_In[62];
M_Out[59]=18*M_In[57]-4*M_In[59]-22*M_In[61]-12*M_In[63];
M_Out[60]=M_In[58]+M_In[60];
M_Out[61]=12*M_In[57]-22*M_In[59]+4*M_In[61]+18*M_In[63];
M_Out[62]=M_In[58]-M_In[60];
M_Out[63]=4*M_In[57]-12*M_In[59]+18*M_In[61]-22*M_In[63];
}

//умножение матрицы на матрицу ДКП P4T;
void CImage::ProductMxP4T(int *M_In,int *M_Out)
{
M_Out[0]=M_In[0]+M_In[7];
M_Out[1]=M_In[0]-M_In[7];
M_Out[2]=M_In[1]+M_In[6];
M_Out[3]=M_In[1]-M_In[6];
M_Out[4]=M_In[2]+M_In[5];
M_Out[5]=M_In[2]-M_In[5];
M_Out[6]=M_In[3]+M_In[4];
M_Out[7]=M_In[3]-M_In[4];

M_Out[8]=M_In[8]+M_In[15];
M_Out[9]=M_In[8]-M_In[15];
M_Out[10]=M_In[9]+M_In[14];
M_Out[11]=M_In[9]-M_In[14];
M_Out[12]=M_In[10]+M_In[13];
M_Out[13]=M_In[10]-M_In[13];
M_Out[14]=M_In[11]+M_In[12];
M_Out[15]=M_In[11]-M_In[12];

M_Out[16]=M_In[16]+M_In[23];
M_Out[17]=M_In[16]-M_In[23];
M_Out[18]=M_In[17]+M_In[22];
M_Out[19]=M_In[17]-M_In[22];
M_Out[20]=M_In[18]+M_In[21];
M_Out[21]=M_In[18]-M_In[21];
M_Out[22]=M_In[19]+M_In[20];
M_Out[23]=M_In[19]-M_In[20];

M_Out[24]=M_In[24]+M_In[31];
M_Out[25]=M_In[24]-M_In[31];
M_Out[26]=M_In[25]+M_In[30];
M_Out[27]=M_In[25]-M_In[30];
M_Out[28]=M_In[26]+M_In[29];
M_Out[29]=M_In[26]-M_In[29];
M_Out[30]=M_In[27]+M_In[28];
M_Out[31]=M_In[27]-M_In[28];

M_Out[32]=M_In[32]+M_In[39];
M_Out[33]=M_In[32]-M_In[39];
M_Out[34]=M_In[33]+M_In[38];
M_Out[35]=M_In[33]-M_In[38];
M_Out[36]=M_In[34]+M_In[37];
M_Out[37]=M_In[34]-M_In[37];
M_Out[38]=M_In[35]+M_In[36];
M_Out[39]=M_In[35]-M_In[36];

M_Out[40]=M_In[40]+M_In[47];
M_Out[41]=M_In[40]-M_In[47];
M_Out[42]=M_In[41]+M_In[46];
M_Out[43]=M_In[41]-M_In[46];
M_Out[44]=M_In[42]+M_In[45];
M_Out[45]=M_In[42]-M_In[45];
M_Out[46]=M_In[43]+M_In[44];
M_Out[47]=M_In[43]-M_In[44];

M_Out[48]=M_In[48]+M_In[55];
M_Out[49]=M_In[48]-M_In[55];
M_Out[50]=M_In[49]+M_In[54];
M_Out[51]=M_In[49]-M_In[54];
M_Out[52]=M_In[50]+M_In[53];
M_Out[53]=M_In[50]-M_In[53];
M_Out[54]=M_In[51]+M_In[52];
M_Out[55]=M_In[51]-M_In[52];

M_Out[56]=M_In[56]+M_In[63];
M_Out[57]=M_In[56]-M_In[63];
M_Out[58]=M_In[57]+M_In[62];
M_Out[59]=M_In[57]-M_In[62];
M_Out[60]=M_In[58]+M_In[61];
M_Out[61]=M_In[58]-M_In[61];
M_Out[62]=M_In[59]+M_In[60];
M_Out[63]=M_In[59]-M_In[60];
}

//умножение матрицы на матрицу ДКП транспонированное справа
void CImage::ProductMxPT(int *M_In,int *M_Out)
{
int M_Tmp[64];
ProductMxP4T(M_In,M_Tmp);
ProductMxP3T(M_Tmp,M_Out);
ProductMxP2(M_Out,M_Tmp);//матрица P2 транспонированная одинакова с P2
ProductMxP1(M_Tmp,M_Out);//матрица P1 транспонированная одинакова с P1
}


Тогда прямое ДКП для компонентов YCrCb делается так:

Код
  
   //выполняем ДКП
   int Tmp[64];
   int dcp_Y[64];
   int dcp_Cr[64];
   int dcp_Cb[64];
   ProductMxPT(Y,Tmp);
   ProductPxM(Tmp,dcp_Y);

   ProductMxPT(Cr,Tmp);
   ProductPxM(Tmp,dcp_Cr);

   ProductMxPT(Cb,Tmp);
   ProductPxM(Tmp,dcp_Cb);

   for(n=0;n<64;n++)
   {
    dcp_Y[n]>>=11;
    dcp_Cr[n]>>=11;
    dcp_Cb[n]>>=11;
   }


А обратное:

Код
//делаем обратное преобразование
  ProductMxP(dcp_Y,Tmp);
   ProductPTxM(Tmp,Y);

   ProductMxP(dcp_Cr,Tmp);
   ProductPTxM(Tmp,Cr);

   ProductMxP(dcp_Cb,Tmp);
   ProductPTxM(Tmp,Cb);

   for(n=0;n<64;n++)
   {
    Y[n]>>=11;
    Cr[n]>>=11;
    Cb[n]>>=11;
   }
da-nie
А теперь посмотрим, на чём основано сжатие. Если из матриц Y=DCP*X*DCP_T выбросить часть компонентов, то по оставшимся можно тоже восстановить исходные цвета более-менее приближённо к тем, которые были изначально. А какие компоненты можно выбросить? А об этом нам расскажет матрица квантования.
Скажем, такая:

Код
     | 3  5  7  9 11 13 15 17|
     | 5  7  9 11 13 15 17 19|
     | 7  9 11 13 15 17 19 21|
     | 9 11 13 15 17 19 21 23|
     |11 13 15 17 19 21 23 25|
     |13 15 17 19 21 23 25 27|
     |15 17 19 21 23 25 27 29|
     |17 19 21 23 25 27 29 31|


Надо почленно поделить компоненты матрицы Y на соответствующие элементы матрицы квантования. Где ноль получится - то и выкинем. smile.gif
Делается эта матрица просто: q=1+((1+x+y)*k), где k-коэффициент качества. k от 0 и до сколько хотите (только начиная с некоторого значения вы просто матрицу Y обнулите всю и всё). smile.gif
Ну а для обратного ДКП надо полученные компоненты умножить на это q. Там где ноль выйдет после деления (у нас же целые числа - значения после запятой обнуляются), там сколько ни умножай кроме нуля ничего и не будет. Теперь эти нули просто надо считать зигзагом и сжать хоть методом rle (кодирование повторов).

Нажмите для просмотра прикрепленного файла

Как получить зигзаг?

Ну, я получил так:

Код
//создаём зигзаг для обхода матрицы
int ZigZag[64];
int index=0;
//левая верхняя половина
for(int n=0;n<8;n++)
{
  int x=0;
  int y=0;
  if (n%2==0)
  {
   x=0;
   y=n;
   for(int m=0;m<=n;m++)
   {
    ZigZag[index]=x+y*8;
    index++;
    x++;
    y--;
   }
  }
  else
  {
   x=n;
   y=0;
   for(int m=0;m<=n;m++)
   {
    ZigZag[index]=x+y*8;
    index++;
    x--;
    y++;
   }
  }
}
//правая нижняя половина
for(n=1;n<8;n++)
{
  int x=0;
  int y=0;
  if (n%2==1)
  {
   x=n;
   y=7;
   for(int m=0;m<=7-n;m++)
   {
    ZigZag[index]=x+y*8;
    index++;
    x++;
    y--;
   }
  }
  else
  {
   x=7;
   y=n;
   for(int m=0;m<=7-n;m++)
   {
    ZigZag[index]=x+y*8;
    index++;
    x--;
    y++;
   }
  }
}


В результате в массиве ZigZag[64] будут индексы элементов матрицы. Ах да, у меня матрицы хранятся как смещение=y*8+x.

Ну вот и всё. Сжимайте полученные нули матриц Y, Cr, Cb после ДКП любым способом. smile.gif
da-nie
Быстрое Дискретное Косинусное Преобразование (ДКП) AA&N

Выше я приводил для быстрого дискретного косинусного преобразования большущую развёртку матриц.
Однако, эти матрицы можно уместить в махонький кусочек кода, всего лишь воспользовавшись соотношениями, приведёнными в статье, откуда я эти матрицы взял, и собрав вычисления в цикл. В результате мы придём, как я понимаю, к алгоритму AA&N (Y.Arai,T.Agui,M.Nakajima):

Код
//быстрое ДКП
void FastDCT(int *M_I,int *M_DCT)
{
int i;
int matrix[64];
int *m_i_ptr=M_I;
int *m_ptr=matrix;
for(i=0;i<8;i++,m_i_ptr++,m_ptr++)
{
  int *m_i_local_ptr=m_i_ptr;
  int x0=*m_i_local_ptr;m_i_local_ptr+=8;
  int x1=*m_i_local_ptr;m_i_local_ptr+=8;
  int x2=*m_i_local_ptr;m_i_local_ptr+=8;
  int x3=*m_i_local_ptr;m_i_local_ptr+=8;
  int x4=*m_i_local_ptr;m_i_local_ptr+=8;
  int x5=*m_i_local_ptr;m_i_local_ptr+=8;
  int x6=*m_i_local_ptr;m_i_local_ptr+=8;
  int x7=*m_i_local_ptr;m_i_local_ptr+=8;

  int tmp0=x0+x7;
  int tmp1=x0-x7;
  int tmp2=x1+x6;
  int tmp3=x1-x6;
  int tmp4=x2+x5;
  int tmp5=x2-x5;
  int tmp6=x3+x4;
  int tmp7=x3-x4;

  int tmp10=tmp0+tmp6;
  int tmp11=22*tmp1+18*tmp3+12*tmp5+4*tmp7;
  int tmp12=tmp0-tmp6;
  int tmp13=18*tmp1-4*tmp3-22*tmp5-12*tmp7;
  int tmp14=tmp2+tmp4;
  int tmp15=12*tmp1-22*tmp3+4*tmp5+18*tmp7;
  int tmp16=tmp2-tmp4;
  int tmp17=4*tmp1-12*tmp3+18*tmp5-22*tmp7;

  int tmp20=tmp10+tmp14;
  int tmp21=tmp11;
  int tmp22=tmp12;
  int tmp23=tmp13;
  int tmp24=tmp10-tmp14;
  int tmp25=tmp15;
  int tmp26=tmp16;
  int tmp27=tmp17;

  int tmp30=16*tmp20;
  int tmp31=tmp21;
  int tmp32=22*tmp22+8*tmp26;
  int tmp33=tmp23;
  int tmp34=16*tmp24;
  int tmp35=tmp25;
  int tmp36=8*tmp22-22*tmp26;
  int tmp37=tmp27;
  
  int *m_local_ptr=m_ptr;

  *m_local_ptr=tmp30;m_local_ptr+=8;
  *m_local_ptr=tmp31;m_local_ptr+=8;
  *m_local_ptr=tmp32;m_local_ptr+=8;
  *m_local_ptr=tmp33;m_local_ptr+=8;
  *m_local_ptr=tmp34;m_local_ptr+=8;
  *m_local_ptr=tmp35;m_local_ptr+=8;
  *m_local_ptr=tmp36;m_local_ptr+=8;
  *m_local_ptr=tmp37;m_local_ptr+=8;
}
m_ptr=matrix;
int *m_dct_ptr=M_DCT;
for(i=0;i<8;i++)
{
  int x0=*m_ptr;m_ptr++;
  int x1=*m_ptr;m_ptr++;
  int x2=*m_ptr;m_ptr++;
  int x3=*m_ptr;m_ptr++;
  int x4=*m_ptr;m_ptr++;
  int x5=*m_ptr;m_ptr++;
  int x6=*m_ptr;m_ptr++;
  int x7=*m_ptr;m_ptr++;

  int tmp0=x0+x7;
  int tmp1=x0-x7;
  int tmp2=x1+x6;
  int tmp3=x1-x6;
  int tmp4=x2+x5;
  int tmp5=x2-x5;
  int tmp6=x3+x4;
  int tmp7=x3-x4;

  int tmp10=tmp0+tmp6;
  int tmp11=22*tmp1+18*tmp3+12*tmp5+4*tmp7;
  int tmp12=tmp0-tmp6;
  int tmp13=18*tmp1-4*tmp3-22*tmp5-12*tmp7;
  int tmp14=tmp2+tmp4;
  int tmp15=12*tmp1-22*tmp3+4*tmp5+18*tmp7;
  int tmp16=tmp2-tmp4;
  int tmp17=4*tmp1-12*tmp3+18*tmp5-22*tmp7;

  int tmp20=tmp10+tmp14;
  int tmp21=tmp11;
  int tmp22=tmp12;
  int tmp23=tmp13;
  int tmp24=tmp10-tmp14;
  int tmp25=tmp15;
  int tmp26=tmp16;
  int tmp27=tmp17;

  int tmp30=16*tmp20;
  int tmp31=tmp21;
  int tmp32=22*tmp22+8*tmp26;
  int tmp33=tmp23;
  int tmp34=16*tmp24;
  int tmp35=tmp25;
  int tmp36=8*tmp22-22*tmp26;
  int tmp37=tmp27;
  
  *m_dct_ptr=tmp30>>11;m_dct_ptr++;
  *m_dct_ptr=tmp31>>11;m_dct_ptr++;
  *m_dct_ptr=tmp32>>11;m_dct_ptr++;
  *m_dct_ptr=tmp33>>11;m_dct_ptr++;
  *m_dct_ptr=tmp34>>11;m_dct_ptr++;
  *m_dct_ptr=tmp35>>11;m_dct_ptr++;
  *m_dct_ptr=tmp36>>11;m_dct_ptr++;
  *m_dct_ptr=tmp37>>11;m_dct_ptr++;
}
}

//Быстрое обратное ДКП
void FastIDCT(int *M_DCT,int *M_I)
{
int i;
int matrix[64];
int *dct_ptr=M_DCT;
int *m_ptr=matrix;
for(i=0;i<8;i++,dct_ptr++,m_ptr++)
{
  int *dct_local_ptr=dct_ptr;
  int x0=*dct_local_ptr;dct_local_ptr+=8;
  int x1=*dct_local_ptr;dct_local_ptr+=8;
  int x2=*dct_local_ptr;dct_local_ptr+=8;
  int x3=*dct_local_ptr;dct_local_ptr+=8;
  int x4=*dct_local_ptr;dct_local_ptr+=8;
  int x5=*dct_local_ptr;dct_local_ptr+=8;
  int x6=*dct_local_ptr;dct_local_ptr+=8;
  int x7=*dct_local_ptr;dct_local_ptr+=8;

  int tmp0=16*x0;
  int tmp1=x1;
  int tmp2=22*x2+8*x6;
  int tmp3=x3;
  int tmp4=16*x4;
  int tmp5=x5;
  int tmp6=8*x2-22*x6;
  int tmp7=x7;

  int tmp10=tmp0+tmp4;
  int tmp11=x1;
  int tmp12=tmp2;
  int tmp13=x3;
  int tmp14=tmp0-tmp4;
  int tmp15=x5;
  int tmp16=tmp6;
  int tmp17=x7;

  int tmp20=tmp10+tmp12;
  int tmp21=22*tmp11+18*tmp13+12*tmp15+4*tmp17;
  int tmp22=tmp14+tmp16;
  int tmp23=18*tmp11-4*tmp13-22*tmp15-12*tmp17;
  int tmp24=tmp14-tmp16;
  int tmp25=12*tmp11-22*tmp13+4*tmp15+18*tmp17;
  int tmp26=tmp10-tmp12;
  int tmp27=4*tmp11-12*tmp13+18*tmp15-22*tmp17;

  int tmp30=tmp20+tmp21;
  int tmp31=tmp22+tmp23;
  int tmp32=tmp24+tmp25;
  int tmp33=tmp26+tmp27;
  int tmp34=tmp26-tmp27;
  int tmp35=tmp24-tmp25;
  int tmp36=tmp22-tmp23;
  int tmp37=tmp20-tmp21;

  int *m_local_ptr=m_ptr;
  *m_local_ptr=tmp30;m_local_ptr+=8;
  *m_local_ptr=tmp31;m_local_ptr+=8;
  *m_local_ptr=tmp32;m_local_ptr+=8;
  *m_local_ptr=tmp33;m_local_ptr+=8;
  *m_local_ptr=tmp34;m_local_ptr+=8;
  *m_local_ptr=tmp35;m_local_ptr+=8;
  *m_local_ptr=tmp36;m_local_ptr+=8;
  *m_local_ptr=tmp37;m_local_ptr+=8;
}
m_ptr=matrix;
int *m_i_ptr=M_I;
for(i=0;i<8;i++)
{
  int x0=*m_ptr;m_ptr++;
  int x1=*m_ptr;m_ptr++;
  int x2=*m_ptr;m_ptr++;
  int x3=*m_ptr;m_ptr++;
  int x4=*m_ptr;m_ptr++;
  int x5=*m_ptr;m_ptr++;
  int x6=*m_ptr;m_ptr++;
  int x7=*m_ptr;m_ptr++;

  int tmp0=16*x0;
  int tmp1=x1;
  int tmp2=22*x2+8*x6;
  int tmp3=x3;
  int tmp4=16*x4;
  int tmp5=x5;
  int tmp6=8*x2-22*x6;
  int tmp7=x7;

  int tmp10=tmp0+tmp4;
  int tmp11=x1;
  int tmp12=tmp2;
  int tmp13=x3;
  int tmp14=tmp0-tmp4;
  int tmp15=x5;
  int tmp16=tmp6;
  int tmp17=x7;

  int tmp20=tmp10+tmp12;
  int tmp21=22*tmp11+18*tmp13+12*tmp15+4*tmp17;
  int tmp22=tmp14+tmp16;
  int tmp23=18*tmp11-4*tmp13-22*tmp15-12*tmp17;
  int tmp24=tmp14-tmp16;
  int tmp25=12*tmp11-22*tmp13+4*tmp15+18*tmp17;
  int tmp26=tmp10-tmp12;
  int tmp27=4*tmp11-12*tmp13+18*tmp15-22*tmp17;

  int tmp30=tmp20+tmp21;
  int tmp31=tmp22+tmp23;
  int tmp32=tmp24+tmp25;
  int tmp33=tmp26+tmp27;
  int tmp34=tmp26-tmp27;
  int tmp35=tmp24-tmp25;
  int tmp36=tmp22-tmp23;
  int tmp37=tmp20-tmp21;
  
  *m_i_ptr=tmp30>>11;m_i_ptr++;
  *m_i_ptr=tmp31>>11;m_i_ptr++;
  *m_i_ptr=tmp32>>11;m_i_ptr++;
  *m_i_ptr=tmp33>>11;m_i_ptr++;
  *m_i_ptr=tmp34>>11;m_i_ptr++;
  *m_i_ptr=tmp35>>11;m_i_ptr++;
  *m_i_ptr=tmp36>>11;m_i_ptr++;
  *m_i_ptr=tmp37>>11;m_i_ptr++;
}
}


Нажмите для просмотра прикрепленного файла

По сути, я просто собрал вычисления в матрицах в цикл по строкам и столбцам, а потом объединил их, используя то, что Y=P*((P*Xтранспонир.)транспонир.), а X=Pтранспонир.*((Pтранспонир.*X)транспонир.). Таким образом, циклов два. По числу умножений матриц. Вот и всё.
da-nie
Кстати, хочу предупредить. Я нашёл ошибку в ProxyServer.

Вместо:
Цитата
if (select(0,&Readen,0,&Exeption,&timeout)!=SOCKET_ERROR)


Надо писать
Цитата
if (select(0,&Readen,0,&Exeption,&timeout)>0)


Ведь функция select возвращает ошибку (<0), выход по истечении времени (=0), или количество сработавших сокетов (>0).
da-nie
Пишем некоторый аналог Remote Administrator

Наверное, многим хотелось написать программу, которая показывала экран другого компьютера, включенного в сеть. Вот сейчас такой программой мы и займёмся. smile.gif Итак, давайте посмотрим, в чём проблема в передаче по сети изображения экрана компьютер. Пусть экран 1024x768 точек и 32 бита цвет (на самом деле, там четвёртый байт не используется - он нужен для выравнивания до 4-х байт - так процессору копировать легче и быстрее smile.gif ). Тогда памяти он займёт 3145728 байт. 3 мегабайта! А 100 мегабитная сеть, дай Боже, 8 мегабайт в секунду передаёт. Значит, надо сжимать. Но вот незадача - сжатие процесс не быстрый! Очень даже не быстрый. И со 100% загрузкой процессора. Придётся искать компромисс. Сделаем так - заставим сжимать картинку в jpg такую штуку, как GDI+. И распаковывать этот самый jpg тоже у нас будет GDI+. А вот на экран выводить будем с помощью Direct Draw 7 и только в 32-х битном видеорежиме. На самом деле, DD7 вовсе не обязателен - GDI тоже с этим неплохо справится. Просто у меня изначально заготовка была с Direct Draw 7 и я решил от него не отказываться.

Вот как получается в GDI+ захват и сжатие изображения рабочего стола:

Код
...
   GdiplusStartupInput input;
   GdiplusStartupOutput output;
   ULONG dwToken;
   //подключаемся к GDI+
   if (GdiplusStartup(&dwToken,&input,&output)==Gdiplus::Ok)
   {
    //захватываем экран    
    //определяем разрешение экрана
    HDC hDC_Screen=GetDC(NULL);
    int width_screen=GetDeviceCaps(hDC_Screen,HORZRES);
    int height_screen=GetDeviceCaps(hDC_Screen,VERTRES);
    //создаём картинку
    HDC hDC_Memory=CreateCompatibleDC(hDC_Screen);
    HBITMAP hBitmap=CreateCompatibleBitmap(hDC_Screen,width_screen,height_screen);
    SelectObject(hDC_Memory,hBitmap);

    //копируем экран
    BitBlt(hDC_Memory,0,0,width_screen,height_screen,hDC_Screen,0,0,SRCCOPY);
    //создаём объекты GDI+
    Bitmap *bitmap=new Bitmap(hBitmap,NULL);    
    Image  *image=bitmap->GetThumbnailImage(width_screen/scale,height_screen/scale);
    EncoderParameters encoderParams;//параметры кодирования
    int quality = 65;
    encoderParams.Count=1;
    encoderParams.Parameter[0].Guid=EncoderQuality;
    encoderParams.Parameter[0].Type=4;
    encoderParams.Parameter[0].NumberOfValues=1;
    encoderParams.Parameter[0].Value=&quality;
    //ищем кодировщик jpg
    
    CLSID imgClsid;
    if (GetEncoderClsid("image/jpeg",&imgClsid)>=0)
    {
     //создаём поток
     IStream *pStream=NULL;
     if (SUCCEEDED(CreateStreamOnHGlobal(NULL,true,&pStream)))
     {
      if (image->Save(pStream,&imgClsid,&encoderParams)==Gdiplus::Ok)//отправляем картинку в поток
      {
       ULARGE_INTEGER StreamSize;
       LARGE_INTEGER zero;
       zero.QuadPart=0;      
       //узнаем размер картинки
       pStream->Seek(zero,STREAM_SEEK_END,&StreamSize);
       pStream->Seek(zero,STREAM_SEEK_SET,0);
       int size=StreamSize.LowPart;
       unsigned char *buffer=new unsigned char[size];
       pStream->Read(buffer,StreamSize.LowPart,0);
       cImage_CaptureFrame.CreateImage(buffer,StreamSize.LowPart);
       delete(buffer);
       cEvent_ThreadCaptureGetFrameOk.SetEvent();//кадр захвачен
      
      }
      pStream->Release();
     }    
    }
    delete(image);
    
    delete(bitmap);
    DeleteObject(hBitmap);
    ReleaseDC(NULL,hDC_Memory);
    DeleteDC(hDC_Memory);
        ReleaseDC(NULL,hDC_Screen);
    DeleteDC(hDC_Screen);
    //отключаемся от GDI+
       GdiplusShutdown(dwToken);
...


Здесь используется то, что объекты GDI+ способны писать в поток данные (и в файл они тоже писать могут), а мы способны этот поток прочесть и передать по сети, не связываясь с файлами.

А вот так мы найдём GLSID кодека jpg (он ведь COM-объект):

Код
//поиск GUID кодировщиков
int GetEncoderClsid(char *format, CLSID* pClsid)
{
UINT num=0;
UINT size=0;
ImageCodecInfo* pImageCodecInfo;
GetImageEncodersSize(&num, &size);
if (size==0) return(-1);
pImageCodecInfo=(ImageCodecInfo*)LocalAlloc(LPTR,size);
if(pImageCodecInfo==NULL) return(-1);
GetImageEncoders(num,size,pImageCodecInfo);
for(UINT j=0;j<num;++j)
{
  char FormatName[1024];
  if (WideCharToMultiByte(CP_ACP,0,pImageCodecInfo[j].MimeType,-1,FormatName,1024,0,0)>0)
  {
   if (strcmp(format,FormatName)==0)
   {    
    *pClsid=pImageCodecInfo[j].Clsid;
    LocalFree(pImageCodecInfo);
    return(j);
   }
  }
}
LocalFree(pImageCodecInfo);
return(-1);
}


А вот так распаковывается принятое изображение jpg:

Код
GdiplusStartupInput input;
GdiplusStartupOutput output;
ULONG dwToken;
//подключаемся к GDI+
if (GdiplusStartup(&dwToken,&input,&output)!=Gdiplus::Ok) return;
//создаём поток
IStream *pStream=NULL;
if (SUCCEEDED(CreateStreamOnHGlobal(NULL,true,&pStream)))
{
  pStream->Write(Data,Size,0);//записываем в поток наши данные
  Bitmap SrcBitmap(pStream,0);//создаём объект  
  Rect rect(0,0,SrcBitmap.GetWidth(),SrcBitmap.GetHeight());
  BitmapData SrcBitmapData;  
  PixelFormat SrcPixelFormat=SrcBitmap.GetPixelFormat();
  SrcBitmap.LockBits(&rect,ImageLockModeRead,SrcPixelFormat,&SrcBitmapData);
  if (SrcPixelFormat==PixelFormat24bppRGB)
  {
   cImage_Frame.SetImage(SrcBitmapData.Width,SrcBitmapData.Height,SrcBitmapData.Stride,(unsigned char*)SrcBitmapData.Scan0);
  }
  SrcBitmap.UnlockBits(&SrcBitmapData);
  pStream->Release();
}
//отключаемся от GDI+
GdiplusShutdown(dwToken);


Здесь мы загружаем из потока изображение в Bitmap и просим его дать нам указатель на данные изображения. А затем забираем кадр.
Класс CImage как раз и занимается у меня хранением изображений в потоках (не в смысле Stream, а в смысле Thread. smile.gif Путаница тут получается некоторая. smile.gif ).

Итак, получится две программы:

ScreenCaptureServer - захватывает изображение. Состоит из основного потока и двух дополнительных. Первый дополнительный поток осуществляет захват экрана как только сработает событие, требующее захватить экран (threadcapture.cpp). Второй дополнительный поток обрабатывает запросы по сети и устанавливает событие необходимости захвата экрана (threadserver.cpp). Класс CImage хранит сжатое изображение.

Monitor - показывает изображение в окне. Программа использует технологию Документ-Вид. Состоит из основного потока и дополнительного. Дополнительный поток (threadclient.cpp) обеспечивает по событию запрос изображения и его получение, распаковку методами GDI+ и помещение распакованного изображения в класс CImage. Вывод изображение на экран средствами Direct Draw 7 осуществляет основной поток в функции обработки таймера. Там же производится и команда дополнительному потоку на запрос изображения. Адрес сервера задаётся в функции:
Код
//запрос и получение захваченного кадра
ErrorCode CThreadClient::CaptureFrame(void)
{
AddLog("Запрашиваем данные.\r\n");
//посылаем запрос
char Host[255];
sprintf(Host,"127.0.0.1");


Вот файлы GDI+ для VC6: Нажмите для просмотра прикрепленного файла
Раскидайте их по каталогам Include и Lib. Dll скорее всего, вам не нужен - он уже в системе есть.

Исходники программ: Нажмите для просмотра прикрепленного файла
da-nie
Перечисление доступных локальному компьютеру IP установленных сетевых карт

Для ряда задач бывает нужно узнать, какие именно IP-адреса имеют сетевые карты компьютера.
Делается это вот так вот:

Код
INTERFACE_INFO iflist[100];
DWORD dwBytes;
SOCKET s_enum=socket(AF_INET,SOCK_STREAM,0);
if (s_enum!=INVALID_SOCKET)
{
  if (WSAIoctl(s_enum,SIO_GET_INTERFACE_LIST,NULL,0,&iflist,sizeof(iflist),&dwBytes,NULL,NULL)!=SOCKET_ERROR)
  {
   int amount=dwBytes/sizeof(INTERFACE_INFO);//количество сетевых подключений
   for(int i=0;i<amount;i++)
   {
    MessageBox(NULL,inet_ntoa(iflist[i].iiAddress.AddressIn.sin_addr),"",MB_OK);
   }
  }
  closesocket(s_enum);
}


Только не удивляйтесь, если у вас сетевая карта есть, IP у неё задан, а эта программа его не показывает и создать сервер на этом IP нельзя.
Такое вот:
Код
SOCKET server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//создаём сокет
struct sockaddr_in server_addr;
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(8080);//порт
server_addr.sin_addr.s_addr=inet_addr("192.168.1.30");//ждать подключения с адреса
//связываем адрес с сокетом
bind(server,(SOCKADDR*)&server_addr,sizeof(server_addr))


работает только когда сеть включена. Если вы сетевую карту не подключили хотя бы к роутеру, компьютер вам создать на ней сокет не даст и в списке вышеприведённой программы перечисления, как я понимаю, не покажет эту сетевую карту.
da-nie
Программирование платы КМ-104 от Каскод

Есть такое устройство - плата KM 104 производства фирмы Каскод. Отличается редким уродством при программировании, ошибками в описании (например, регистр заполненности буферов АЦП 12 бит имеет несколько другую структуру, нежели указанная в описании), сильный (до пол-вольта!) шумом АЦП (если не посадить принудительно входы на землю резистором на 1 кОм), офигенной ценой. Далее рассматривается плата KM-104 с шиной ISA (точнее, в формате PC-104). Все исходники даны для MS-DOS и компилятора Watcom C. Переделать хоть под QNX проблем не составит.

Итак:

km104.h
Код
#if !defined(__KM104__)
#define __KM104__
/*****************************************************************************
  Km104Var - Библиотека переменных модуля Km-104
  (c) Cascode Ltd, S-Peterburg, 2001

  Compiler: Watcom v11.0
*****************************************************************************/
#define SizeBufADC          127

#define SizeBuf             128

/* Внутренние регистры для обращения к IDT*/
#define REG_IRQ     2   // смещение адреса (относительно базового) для установки прерывания Km104.
#define REG_SSTRT1  4  // смещение адреса (относительно базового) сигнала SSTRT1.
#define REG_SSTRT2  6  // смещение адреса (относительно базового) сигнала SSTRT2.
#define REG_CNTEN   8  // смещение адреса (относительно базового) сигнала CNTEN.
#define REG_RST     10 // смещение адреса (относительно базового) сигнала RST.
#define RESET_KM    12 // смещение адреса (относительно базового) сигнала RST.
#define ADDR_SLD    14 // адрес регистра адреса IDT (обычный т.е не триггерный).

//********************************************************************************
***********************
// Карта распределения памяти IDT.
//********************************************************************************
***********************
#define RegComm                        0        // Регистр команд.
#define Mode_P2P6      1      // Режим порта P2 и P6 (P6.4, P6.5, P6.6).
#define Mode_P3P4      2       // Режим порта P3 и P4 (P4.4, P4.7).
#define Mode_P7P8      3       // Режим порта P7 и P8.

#define Wr_P2P6        4        // Установить (записать) значение P2 и P6 (P6.4, P6.5, P6.6).
#define Wr_P3P4        5       // Установить (записать) значение P3 и P4 (P4.4, P4.7).
#define Wr_P7P8        6       // Установить (записать) значение P7 и P8.

#define Rd_P2P6        7       // Считать значение P2 и P6 (P6.4, P6.5, P6.6).
#define Rd_P3P4        8       // Считать значение P3 и P4 (P4.4, P4.7).
#define Rd_P5          9        // Считать значение P5.
#define Rd_P7P8       10      // Считать значение P7 и P8.

#define T01mode       11       // Режим работы таймеров 0/1.
#define T78mode       12       // Режим работы таймеров 7/8.

#define T2mode        13      // Режим работы таймера T2.
#define T3mode        14       // Режим работы таймера T3.
#define T4mode        15       // Режим работы таймера T4.
#define T5mode        16       // Режим работы таймера T5.
#define T6mode        17      // Режим работы таймера T6.

#define Timer0        18      // Период работы таймера T0.
#define Timer1        19      // Период работы таймера T1.
#define Timer2        20      // Период работы таймера T2.
#define Timer3        21      // Период работы таймера T3.
#define Timer4        22      // Период работы таймера T4.
#define Timer5        23       // Период работы таймера T5.
#define Timer6        24       // Период работы таймера T6.
#define Timer7        25      // Период работы таймера T7.
#define Timer8        26     // Период работы таймера T8.

#define PWM0_3        27      // Режим работы каналов 0-3  PWM.
#define PWM4_7        28      // Режим работы каналов 4-7  PWM.
#define PWM8_11       29       // Режим работы каналов 8-11 PWM.
#define PWM16_19      30      // Режим работы каналов 16-19 PWM.
#define PWM20_23      31       // Режим работы каналов 20-23 PWM.
#define PWM28_31      32      // Режим работы каналов 28-31  PWM.

                                                                                                                                        //  Длительность импульса каналов медленного ШИМ.
#define LongImpSlowPWM0        33          //  Канал 0.
#define LongImpSlowPWM1   34 //  Канал 1.
#define LongImpSlowPWM2   35 //  Канал 2.
#define LongImpSlowPWM3   36 //  Канал 3.
#define LongImpSlowPWM4   37 //  Канал 4.
#define LongImpSlowPWM5   38 //  Канал 5.
#define LongImpSlowPWM6   39 //  Канал 6.
#define LongImpSlowPWM7   40 //  Канал 7.
#define LongImpSlowPWM8   41 //  Канал 8.
#define LongImpSlowPWM9   42 //  Канал 9.
#define LongImpSlowPWM10  43 //  Канал 10.
#define LongImpSlowPWM11  44 //  Канал 11.

#define LongImpSlowPWM16  45//  Канал 16.
#define LongImpSlowPWM17  46 //  Канал 17.
#define LongImpSlowPWM18  47 //  Канал 18.
#define LongImpSlowPWM19  48 //  Канал 19.
#define LongImpSlowPWM20  49 //  Канал 20.
#define LongImpSlowPWM21  50 //  Канал 21.
#define LongImpSlowPWM22  51 //  Канал 22.
#define LongImpSlowPWM23  52 //  Канал 23.

#define LongImpSlowPWM28  53 //  Канал 20.
#define LongImpSlowPWM29  54 //  Канал 21.
#define LongImpSlowPWM30  55 //  Канал 22.
#define LongImpSlowPWM31  56 //  Канал 23.


#define StatFastPWM0      57 // Режим работы каналов быстрого ШИМ.
#define StatFastPWM1      58 // Режим работы каналов быстрого ШИМ.

#define FaseFastPWM0      59 // Фаза быстрого ШИМ канала 0.
#define FaseFastPWM1      60 // Фаза быстрого ШИМ канала 1.
#define FaseFastPWM2      61 // Фаза быстрого ШИМ канала 2.
#define FaseFastPWM3      62 // Фаза быстрого ШИМ канала 3.

#define PeriodFastPWM0    63 // Период быстрого ШИМ канала 0 (P7.0)
#define PeriodFastPWM1    64 // Период быстрого ШИМ канала 1 (P7.1)
#define PeriodFastPWM2    65 // Период быстрого ШИМ канала 2 (P7.2)
#define PeriodFastPWM3    66 // Период быстрого ШИМ канала 3 (P7.3)

#define LongImpFastPWM0   67 // Длительность импульса быстрого ШИМ канала 0.
#define LongImpFastPWM1   68 // Длительность импульса быстрого ШИМ канала 1.
#define LongImpFastPWM2   69 // Длительность импульса быстрого ШИМ канала 2.
#define LongImpFastPWM3   70 // Длительность импульса быстрого ШИМ канала 3.

#define ModeTimerExtADC   71 // Режим работы таймера T2 канала A ЦАП.
#define SampleExtADC      72 // Отсчеты (семплы) работы  12-разрядного АЦП.
#define ModeExtADC        73// Режим работы каналов 12-разрядного АЦП.
                                                                                                                                        // Младший байт - Номер канала (в групповом режиме-работа каналов от заданного до 0)
                                                                                                                                        // Старший байт - Флаг режима работы: 0-останов 1-одиночный 2-групповой.
#define StatBufExtADC     74 // Статус готовности буферов 12-разрядного АЦП в IDT.

#define ModeIntADC        75// Режим работы каналов 10-разрядного АЦП.
#define StatBufIntADC0    76 // Статус готовности буферов 10-разрядногов АЦП в IDT.
#define StatBufIntADC1    77 // Статус готовности буферов 10-разрядного АЦП в IDT.
#define ParamFillADC      78 // Параметр заполнения буферов АЦП константой.

#define ModeDAC           79 // Режим работы каналов  ЦАП.

#define CountT2low        80 // Счетчик импульсов (таймер) T2.
#define CountT2high       81 // Счетчик импульсов (таймер) T2.
#define CountT3low        82 // Счетчик импульсов (таймер) T3.
#define CountT3high       83 // Счетчик импульсов (таймер) T3.
#define CountT4low        84 // Счетчик импульсов (таймер) T4.
#define CountT4high       85 // Счетчик импульсов (таймер) T4.
#define CountT5low        86 // Счетчик импульсов (таймер) T5.
#define CountT5high       87 // Счетчик импульсов (таймер) T5.


#define StartBufExtADC0_0   88               //  Начало буфера 0 канала 0 12-разрядного АЦП.
#define StartBufExtADC0_1   (StartBufExtADC0_0 + SizeBuf/2) //  Начало буфера 1 канала 0 12-разрядного АЦП.

#define StartBufExtADC1_0   (StartBufExtADC0_1 + SizeBuf/2) //  Начало буфера 0 канала 1 12-разрядного АЦП.
#define StartBufExtADC1_1   (StartBufExtADC1_0 + SizeBuf/2) //  Начало буфера 1 канала 1 12-разрядного АЦП.

#define StartBufExtADC2_0   (StartBufExtADC1_1 + SizeBuf/2) //  Начало буфера 0 канала 2 12-разрядного АЦП.
#define StartBufExtADC2_1   (StartBufExtADC2_0 + SizeBuf/2) //  Начало буфера 1 канала 2 12-разрядного АЦП.

#define StartBufExtADC3_0   (StartBufExtADC2_1 + SizeBuf/2) //  Начало буфера 0 канала 3 12-разрядного АЦП.
#define StartBufExtADC3_1  (StartBufExtADC3_0 + SizeBuf/2)  //  Начало буфера 0 канала 3 12-разрядного АЦП.

#define StartBufExtADC4_0   (StartBufExtADC3_1 + SizeBuf/2) //  Начало буфера 0 канала 4 12-разрядного АЦП.
#define StartBufExtADC4_1   (StartBufExtADC4_0 + SizeBuf/2) //  Начало буфера 1 канала 4 12-разрядного АЦП.

#define StartBufExtADC5_0   (StartBufExtADC4_1 + SizeBuf/2) //  Начало буфера 0 канала 5 12-разрядного АЦП.
#define StartBufExtADC5_1   (StartBufExtADC5_0 + SizeBuf/2) //  Начало буфера 1 канала 5 12-разрядного АЦП.

#define StartBufExtADC6_0   (StartBufExtADC5_1 + SizeBuf/2) //  Начало буфера 0 канала 6 12-разрядного АЦП.
#define StartBufExtADC6_1   (StartBufExtADC6_0 + SizeBuf/2) //  Начало буфера 1 канала 6 12-разрядного АЦП.

#define StartBufExtADC7_0   (StartBufExtADC6_1 + SizeBuf/2) //  Начало буфера 0 канала 7 12-разрядного АЦП.
#define StartBufExtADC7_1   (StartBufExtADC7_0 + SizeBuf/2) //  Начало буфера 1 канала 7 12-разрядного АЦП.

#define StartBufIntADC0_0   1112  //  Начало буфера 1 канала 0 10-разрядного АЦП.
#define StartBufIntADC0_1   (StartBufIntADC0_0 + SizeBuf/2)  //  Начало буфера 1 канала 0 10-разрядного АЦП.

#define StartBufIntADC1_0   (StartBufIntADC0_1 + SizeBuf/2)  //  Начало буфера 0 канала 1 10-разрядного АЦП.
#define StartBufIntADC1_1   (StartBufIntADC1_0 + SizeBuf/2)  //  Начало буфера 1 канала 1 10-разрядного АЦП.

#define StartBufIntADC2_0   (StartBufIntADC1_1 + SizeBuf/2)  //  Начало буфера 0 канала 2 10-разрядного АЦП.
#define StartBufIntADC2_1   (StartBufIntADC2_0 + SizeBuf/2)  //  Начало буфера 1 канала 2 10-разрядного АЦП.

#define StartBufIntADC3_0   (StartBufIntADC2_1 + SizeBuf/2)  //  Начало буфера 0 канала 3 10-разрядного АЦП.
#define StartBufIntADC3_1   (StartBufIntADC3_0 + SizeBuf/2)  //  Начало буфера 1 канала 3 10-разрядного АЦП.

#define StartBufIntADC4_0   (StartBufIntADC3_1 + SizeBuf/2)  //  Начало буфера 0 канала 4 10-разрядного АЦП.
#define StartBufIntADC4_1   (StartBufIntADC4_0 + SizeBuf/2)  //  Начало буфера 1 канала 4 10-разрядного АЦП.

#define StartBufIntADC5_0   (StartBufIntADC4_1 + SizeBuf/2)  //  Начало буфера 0 канала 5 10-разрядного АЦП.
#define StartBufIntADC5_1   (StartBufIntADC5_0 + SizeBuf/2)  //  Начало буфера 1 канала 5 10-разрядного АЦП.

#define StartBufIntADC6_0   (StartBufIntADC5_1 + SizeBuf/2)  //  Начало буфера 0 канала 6 10-разрядного АЦП.
#define StartBufIntADC6_1   (StartBufIntADC6_0 + SizeBuf/2)  //  Начало буфера 1 канала 6 10-разрядного АЦП.

#define StartBufIntADC7_0   (StartBufIntADC6_1 + SizeBuf/2)  //  Начало буфера 0 канала 7 10-разрядного АЦП.
#define StartBufIntADC7_1   (StartBufIntADC7_0 + SizeBuf/2)  //  Начало буфера 1 канала 7 10-разрядного АЦП.

#define StartBufIntADC8_0   (StartBufIntADC7_1 + SizeBuf/2)  //  Начало буфера 0 канала 8 10-разрядного АЦП.
#define StartBufIntADC8_1   (StartBufIntADC8_0 + SizeBuf/2)  //  Начало буфера 1 канала 8 10-разрядного АЦП.

#define StartBufIntADC9_0   (StartBufIntADC8_1 + SizeBuf/2)  //  Начало буфера 0 канала 9 10-разрядного АЦП.
#define StartBufIntADC9_1   (StartBufIntADC9_0 + SizeBuf/2)  //  Начало буфера 1 канала 9 10-разрядного АЦП.

#define StartBufIntADC10_0  (StartBufIntADC9_1 + SizeBuf/2)  //  Начало буфера 0 канала 10 10-разрядного АЦП.
#define StartBufIntADC10_1  (StartBufIntADC10_0 + SizeBuf/2) //  Начало буфера 1 канала 10 10-разрядного АЦП.

#define StartBufIntADC11_0  (StartBufIntADC10_1 + SizeBuf/2) //  Начало буфера 0 канала 10 10-разрядного АЦП.
#define StartBufIntADC11_1  (StartBufIntADC11_0 + SizeBuf/2) //  Начало буфера 1 канала 10 10-разрядного АЦП.

#define StartBufIntADC12_0  (StartBufIntADC11_1 + SizeBuf/2) //  Начало буфера 0 канала 12 10-разрядного АЦП.
#define StartBufIntADC12_1  (StartBufIntADC12_0 + SizeBuf/2) //  Начало буфера 1 канала 12 10-разрядного АЦП.

#define StartBufIntADC13_0  (StartBufIntADC12_1 + SizeBuf/2) //  Начало буфера 0 канала 13 10-разрядного АЦП.
#define StartBufIntADC13_1  (StartBufIntADC13_0 + SizeBuf/2) //  Начало буфера 1 канала 13 10-разрядного АЦП.

#define StartBufIntADC14_0  (StartBufIntADC13_1 + SizeBuf/2) //  Начало буфера 0 канала 14 10-разрядного АЦП.
#define StartBufIntADC14_1  (StartBufIntADC14_0 + SizeBuf/2) //  Начало буфера 1 канала 14 10-разрядного АЦП.

#define StartBufIntADC15_0  (StartBufIntADC14_1 + SizeBuf/2) //  Начало буфера 0 канала 15 10-разрядного АЦП.
#define StartBufIntADC15_1  (StartBufIntADC15_0 + SizeBuf/2) //  Начало буфера 1 канала 15 10-разрядного АЦП.

#define StartBufDACA        (StartBufIntADC15_1 + SizeBuf/2) //  Начало буфера канала A ЦАП.
#define StartBufDACB        (StartBufDACA       + SizeBuf ) //  Начало буфера канала B ЦАП.
#define StartBufDACC        (StartBufDACB       + SizeBuf ) //  Начало буфера канала B ЦАП.
#define StartBufDACD        (StartBufDACC       + SizeBuf ) //  Начало буфера канала B ЦАП.


//********************************************************************************
***********************
// Команды управления KM104
//********************************************************************************
***********************
#define SetModeP2             0x80                     // Режим порта P2.
#define SetModeP6            (SetModeP2            +1) // Режим порта P6 (P6.4, P6.5, P6.6).
#define SetModeP2P6          (SetModeP6            +1) // Режим порта P2 и P6 (P6.4, P6.5, P6.6).
#define WrP2                 (SetModeP2P6          +1) // Установить выходные биты порта P2.
#define WrP6                 (WrP2                 +1) // Установить выходные биты порта P6 (P6.4, P6.5, P6.6).
#define WrP2P6               (WrP6                 +1) // Установить выходные биты порта P2 и P6 (P6.4, P6.5, P6.6).
#define RdP2P6               (WrP2P6               +1) // Считать значение битов порта P2 и P6 (P6.4, P6.5, P6.6).

#define SetModeP3            (RdP2P6               +1) // Режим порта P3.
#define SetModeP4            (SetModeP3            +1) // Режим порта P4 (P4.4, P4.7).
#define SetModeP3P4          (SetModeP4            +1) // Режим порта P3 и P4 (P4.4, P4.7).
#define WrP3                 (SetModeP3P4          +1) // Установить выходные биты порта P3.
#define WrP4                 (WrP3                 +1) // Установить выходные биты порта P4 (P4.4, P4.7).
#define WrP3P4               (WrP4                 +1) // Установить выходные биты порта P3 и P4 (P4.4, P4.7).
#define RdP3P4               (WrP3P4               +1) // Считать значение битов порта P3 и P4 (P4.4, P4.7).

#define RdP5                 (RdP3P4               +1) // Считать значение битов порта P5.

#define SetModeP7            (RdP5                 +1) // Режим порта P7.
#define SetModeP8            (SetModeP7            +1) // Режим порта P8.
#define SetModeP7P8          (SetModeP8            +1) // Режим порта P7 и P8.
#define WrP7                 (SetModeP7P8          +1) // Установить выходные биты порта P7.
#define WrP8                 (WrP7                 +1) // Установить выходные биты порта P8.
#define WrP7P8               (WrP8                 +1) // Установить выходные биты порта P7 и P8.
#define RdP7P8               (WrP7P8               +1) // Считать значение битов порта P7 и P8.

#define SetModeTimerT01      (RdP7P8               +1) // Установить параметров таймеров T0 и T1.
#define SetModeTimerT78      (SetModeTimerT01      +1) // Установить параметров таймеров T7 и T8.
#define SetModeTimerT0178    (SetModeTimerT78      +1) // Установить параметров таймеров T0, T1, T7, T8.
#define StartTimerT0         (SetModeTimerT0178    +1) // Запустить таймер T0.
#define StopTimerT0          (StartTimerT0         +1) // Остановить таймер T0.
#define StartTimerT1         (StopTimerT0          +1) // Запустить таймер T1.
#define StopTimerT1          (StartTimerT1         +1) // Остановить таймер T1.
#define StartTimerT7         (StopTimerT1          +1) // Запустить таймер T7.
#define StopTimerT7          (StartTimerT7         +1) // Остановить таймер T8.
#define StartTimerT8         (StopTimerT7          +1) // Запустить таймер T7.
#define StopTimerT8          (StartTimerT8         +1) // Остановить таймер T8.

#define SetParamSlowPWM      (StopTimerT8          +1) // Установить параметры медленного ШИМ.
#define SetParamSlowPWM0_3   (SetParamSlowPWM      +1) // Установить параметры медленного ШИМ каналов 0-3.
#define SetParamSlowPWM4_7   (SetParamSlowPWM0_3   +1) // Установить параметры медленного ШИМ каналов 4-7.
#define SetParamSlowPWM8_11  (SetParamSlowPWM4_7   +1) // Установить параметры медленного ШИМ каналов 8-11.
#define SetParamSlowPWM16_19 (SetParamSlowPWM8_11  +1) // Установить параметры медленного ШИМ каналов 16-19.
#define SetParamSlowPWM20_23 (SetParamSlowPWM16_19 +1) // Установить параметры медленного ШИМ каналов 20-23
#define SetParamSlowPWM28_31 (SetParamSlowPWM20_23 +1) // Установить параметры медленного ШИМ каналов 28-31.

#define SetLongImpSPWM0      (SetParamSlowPWM28_31 +1) // Установить длительность импульса канала 0.
#define SetLongImpSPWM1      (SetLongImpSPWM0      +1) // Установить длительность импульса канала 1.
#define SetLongImpSPWM2      (SetLongImpSPWM1      +1) // Установить длительность импульса канала 2.
#define SetLongImpSPWM3      (SetLongImpSPWM2      +1) // Установить длительность импульса канала 3.
#define SetLongImpSPWM4      (SetLongImpSPWM3      +1) // Установить длительность импульса канала 4.
#define SetLongImpSPWM5      (SetLongImpSPWM4      +1) // Установить длительность импульса канала 5.
#define SetLongImpSPWM6      (SetLongImpSPWM5      +1) // Установить длительность импульса канала 6.
#define SetLongImpSPWM7      (SetLongImpSPWM6      +1) // Установить длительность импульса канала 7.
#define SetLongImpSPWM8      (SetLongImpSPWM7      +1) // Установить длительность импульса канала 8.
#define SetLongImpSPWM9      (SetLongImpSPWM8      +1) // Установить длительность импульса канала 9.
#define SetLongImpSPWM10     (SetLongImpSPWM9      +1) // Установить длительность импульса канала 10.
#define SetLongImpSPWM11     (SetLongImpSPWM10     +1) // Установить длительность импульса канала 11.

#define SetLongImpSPWM16     (SetLongImpSPWM11     +1) // Установить длительность импульса канала 16.
#define SetLongImpSPWM17     (SetLongImpSPWM16     +1) // Установить длительность импульса канала 17.
#define SetLongImpSPWM18     (SetLongImpSPWM17     +1) // Установить длительность импульса канала 18.
#define SetLongImpSPWM19     (SetLongImpSPWM18     +1) // Установить длительность импульса канала 19.
#define SetLongImpSPWM20     (SetLongImpSPWM19     +1) // Установить длительность импульса канала 20.
#define SetLongImpSPWM21     (SetLongImpSPWM20     +1) // Установить длительность импульса канала 21.
#define SetLongImpSPWM22     (SetLongImpSPWM21     +1) // Установить длительность импульса канала 22.
#define SetLongImpSPWM23     (SetLongImpSPWM22     +1) // Установить длительность импульса канала 23.

#define SetLongImpSPWM28     (SetLongImpSPWM23     +1) // Установить длительность импульса канала 28.
#define SetLongImpSPWM29     (SetLongImpSPWM28     +1) // Установить длительность импульса канала 29.
#define SetLongImpSPWM30     (SetLongImpSPWM29     +1) // Установить длительность импульса канала 30.
#define SetLongImpSPWM31     (SetLongImpSPWM30     +1) // Установить длительность импульса канала 31.


#define SetFaseFPWM          (SetLongImpSPWM31     +1) // Установить начальную фазу быстрого ШИМ.
#define SetPeriodFPWM        (SetFaseFPWM          +1) // Установить период быстрого ШИМ.
#define SetLongImpFPWM       (SetPeriodFPWM        +1) // Установить длительность импульса быстрого ШИМ.
#define SetPeriodLongFPWM    (SetLongImpFPWM       +1) // Установить период и длительность импульса быстрого ШИМ.
#define SetModeFPWM          (SetPeriodLongFPWM    +1) // Установить режим работы быстрого ШИМ.
#define SetParamFPWM         (SetModeFPWM          +1) // Установить период, длительность импульса и режим быстрого ШИМ.

#define SetModeIntADC        0xC6                                                        // Установить режим работы 10-разрядного АЦП.
#define StartIntADC          (SetModeIntADC        +1) // Запуск  10-разрядного АЦП.
#define StopIntADC           (StartIntADC          +1) // Останов 10-разрядного АЦП.
#define FillBufIntADC0       (StopIntADC           +1) // Заполнить константой буфер 10-разрядного АЦП канала 0 (см. ячейку ParamFillADC).
#define FillBufIntADC1       (FillBufIntADC0       +1) // Заполнить константой буфер 10-разрядного АЦП канала 1 (см. ячейку ParamFillADC).
#define FillBufIntADC2       (FillBufIntADC1       +1) // Заполнить константой буфер 10-разрядного АЦП канала 2 (см. ячейку ParamFillADC).
#define FillBufIntADC3       (FillBufIntADC2       +1) // Заполнить константой буфер 10-разрядного АЦП канала 3 (см. ячейку ParamFillADC).
#define FillBufIntADC4       (FillBufIntADC3       +1) // Заполнить константой буфер 10-разрядного АЦП канала 4 (см. ячейку ParamFillADC).
#define FillBufIntADC5       (FillBufIntADC4       +1) // Заполнить константой буфер 10-разрядного АЦП канала 5 (см. ячейку ParamFillADC).
#define FillBufIntADC6       (FillBufIntADC5       +1) // Заполнить константой буфер 10-разрядного АЦП канала 6 (см. ячейку ParamFillADC).
#define FillBufIntADC7       (FillBufIntADC6       +1) // Заполнить константой буфер 10-разрядного АЦП канала 7 (см. ячейку ParamFillADC).
#define FillBufIntADC8       (FillBufIntADC7       +1) // Заполнить константой буфер 10-разрядного АЦП канала 8 (см. ячейку ParamFillADC).
#define FillBufIntADC9       (FillBufIntADC8       +1) // Заполнить константой буфер 10-разрядного АЦП канала 9 (см. ячейку ParamFillADC).
#define FillBufIntADC10      (FillBufIntADC9       +1) // Заполнить константой буфер 10-разрядного АЦП канала 10 (см. ячейку ParamFillADC).
#define FillBufIntADC11      (FillBufIntADC10      +1) // Заполнить константой буфер 10-разрядного АЦП канала 11 (см. ячейку ParamFillADC).
#define FillBufIntADC12      (FillBufIntADC11      +1) // Заполнить константой буфер 10-разрядного АЦП канала 12 (см. ячейку ParamFillADC).
#define FillBufIntADC13      (FillBufIntADC12      +1) // Заполнить константой буфер 10-разрядного АЦП канала 13 (см. ячейку ParamFillADC).
#define FillBufIntADC14      (FillBufIntADC13      +1) // Заполнить константой буфер 10-разрядного АЦП канала 14 (см. ячейку ParamFillADC).
#define FillBufIntADC15      (FillBufIntADC14      +1) // Заполнить константой буфер 10-разрядного АЦП канала 15 (см. ячейку ParamFillADC).

//**************************************************************************
#define SetParamTimerExtADC  (FillBufIntADC15      +1) // Установить парметры (режим и семплы) 12-разрядного АЦП.
#define SetSampleExtADC      (SetParamTimerExtADC  +1) // Установить семплы 12-разрядного АЦП.
#define SetModeExtADC        (SetSampleExtADC      +1) // Установить режим работы 12-разрядного АЦП.
#define StartExtADC          (SetModeExtADC        +1) // Запуск 12-разрядного АЦП.
#define StopExtADC           (StartExtADC          +1) // Останов 12-разрядного АЦП.
#define FillBufExtADC0       (StopExtADC           +1) // Заполнить константой буфер 12-разрядного АЦП канала 0 (см. ячейку ParamFillADC).
#define FillBufExtADC1       (FillBufExtADC0       +1) // Заполнить константой буфер 12-разрядного АЦП канала 1 (см. ячейку ParamFillADC).
#define FillBufExtADC2       (FillBufExtADC1       +1) // Заполнить константой буфер 12-разрядного АЦП канала 2 (см. ячейку ParamFillADC).
#define FillBufExtADC3       (FillBufExtADC2       +1) // Заполнить константой буфер 12-разрядного АЦП канала 3 (см. ячейку ParamFillADC).
#define FillBufExtADC4       (FillBufExtADC3       +1) // Заполнить константой буфер 12-разрядного АЦП канала 4 (см. ячейку ParamFillADC).
#define FillBufExtADC5       (FillBufExtADC4       +1) // Заполнить константой буфер 12-разрядного АЦП канала 5 (см. ячейку ParamFillADC).
#define FillBufExtADC6       (FillBufExtADC5       +1) // Заполнить константой буфер 12-разрядного АЦП канала 6 (см. ячейку ParamFillADC).
#define FillBufExtADC7       (FillBufExtADC6       +1) // Заполнить константой буфер 12-разрядного АЦП канала 7 (см. ячейку ParamFillADC).

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


#define SetModeTimer2        (FillBufExtADC7       +1) // Установить параметров таймера T2.
#define SetModeTimer3        (SetModeTimer2        +1) // Установить параметров таймера T3.
#define SetModeTimer4        (SetModeTimer3        +1) // Установить параметров таймера T4
#define SetModeTimer5        (SetModeTimer4        +1) // Установить параметров таймера T5
#define StartTimerT2         (SetModeTimer5        +1) // Запустить  таймер T2.
#define StartTimerT3         (StartTimerT2         +1) // Запустить  таймер T3.
#define StartTimerT4         (StartTimerT3         +1) // Запустить  таймер T4.
#define StartTimerT5         (StartTimerT4         +1) // Запустить  таймер T5.
#define StopTimerT2          (StartTimerT5         +1) // Остановить таймер T2.
#define StopTimerT3          (StopTimerT2          +1) // Остановить таймер T3.
#define StopTimerT4          (StopTimerT3          +1) // Остановить таймер T4.
#define StopTimerT5          (StopTimerT4          +1) // Остановить таймер T5.

#define CopyDACA             (StopTimerT5          +1) // Заполнить буфер ЦАП канала A.
#define CopyDACB             (CopyDACA             +1) // Заполнить буфер ЦАП канала B.
#define CopyDACC             (CopyDACB             +1) // Заполнить буфер ЦАП канала C.
#define CopyDACD             (CopyDACC             +1) // Заполнить буфер ЦАП канала D.

#define ReadCountT2low       (CopyDACD             +1) // Считать значени таймера T2(младшую часть-старшая находттся в ячейке CountT2high).
#define ReadCountT3low       (ReadCountT2low       +1) // Считать значени таймера T3(младшую часть-старшая находттся в ячейке CountT3high).
#define ReadCountT4low       (ReadCountT3low       +1) // Считать значени таймера T4(младшую часть-старшая находттся в ячейке CountT4high).
#define ReadCountT5low       (ReadCountT4low       +1) // Считать значени таймера T5(младшую часть-старшая находттся в ячейке CountT5high).
/*
#define SaveConfig           (ReadCountT5low       +1) // Сохранить конфигурацию.
#define LoadConfig           (SaveConfig           +1) // Восстановить конфигурацию.
*/
#define ResetOk               0x15C
#define CommOk                0x15A                     // Ответ на принятую команду(положительное подтверждение).
#define CommError            (CommOK               +1)  // Ответ на принятую команд(отрицательное подтверждение).

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

//  Записать значение в IDT (без инкремента).
#define         WriteData(data)                         outpw(KM104Base, data)

//  Считать значение из IDT (с инкрементом).
#define ReadDatainc()                                   inpw(KM104Base+REG_CNTEN)

//  Записать значение в IDT (с инкрементом).
#define WriteDatainc(data)                      outpw(KM104Base+REG_CNTEN, data)

// Записать адрес необходимой ячейки в счетчик адреса IDT (0-4095).
#define SelectReg(AddrIDT)                      outpw(KM104Base+ADDR_SLD, AddrIDT)

//  Считать значение из ячейки IDT по стартовому адресу буфера1
// (стартовый адрес буфера 1 записывается со стороны контроллера).
#define RdSSTRT1()                                              inpw(KM104Base+REG_SSTRT1)

//  Записать значение в ячейку IDT по стартовому адресу буфера1
// (стартовый адрес буфера 1 записывается со стороны контроллера).
#define WrSSTRT1(data)                                  outpw(KM104Base+REG_SSTRT1, data)

//  Считать значение из ячейки IDT по стартовому адресу буфера2
// (стартовый адрес буфера 2 записывается со стороны контроллера).
#define RdSSTRT2()                                              inpw(KM104Base+REG_SSTRT2)

// Записать значение в ячейку IDT по стартовому адресу буфера2
// (стартовый адрес буфера 2 записывается со стороны контроллера).
#define WrSSTRT2(data)                                  outpw(KM104Base+REG_SSTRT2, data)
#endif


Это штатный файл.
А вот мой класс, для работы с некоторыми функциями этого чуда природы.

cdevice.h
Код
#ifndef CDEVICE_H
#define CDEVICE_H

#include "km104.h"
#include <conio.h>
#include <time.h>
#include <sys/timeb.h>
#include <i86.h>
//----------------------------------------------------------------------------------------------------
#define BIN16(b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0) ((b15<<15)|(b14<<14)|(b13<<13)|(b12<<12)|(b11<<11)|(b10<<10)|(b9<<9)|(b8<<8)|(b7<<7)|(b6<<6)|(b5<<5)|(b4<<4)|(b3<<3)|(b2<<2)|(b1<<1)|b0)
//----------------------------------------------------------------------------------------------------
//класс платы KM104
class CDevice_KM104
{
protected:
  //переменные класса
  unsigned int KM104Base;//базовый адрес
  int TimeOut;//предел времени на текущую операцию в миллисекундах (если используется этой операцией)
  double ADC12_LimitValue;//значение половины интервала для АЦП 12 бит (=5 для +-5 вольт, =10 для +-10 вольт]
public:
  //конструктор
  CDevice_KM104(void);
  //методы класса
  bool SetTimeOut(unsigned int timeout);//установить время ожидания команды
  bool SetBaseAddress(unsigned int baseaddress);//установить базовый адрес платы
  bool WaitReady(unsigned int code);//подождать готовности платы
  bool Poke(unsigned int address,unsigned int value);//записать значение value по адресу adress внутренней памяти платы
  bool ReadValue(unsigned int *value);//считать данные из базового адреса платы (перед этим нужно эти данные запросить)
  bool WriteValue(unsigned int value);//записать данные в базовый адрес платы (перед этим нужно эти данные запросить)
  bool Peek(unsigned int address,unsigned int *value);//считать содержимое ячейки с адресом addresss внутренней памяти платы
  bool Reset(void);//сброс платы
  //работа с АЦП 12 бит
  bool ADC12_Start(void);//запустить АЦП
  bool ADC12_Stop(void);//остановить АЦП
  bool ADC12_GetValue(double value_array[8][SizeBuf]);//получить данные преобразования
  bool ADC12_SetLimitValue(double limitvalue);//установить значение половины интервала
  //работа с ЦАП 12 бит
  bool DAC12_Start(int channel);//запустить ЦАП канала channel
  bool DAC12_Stop(int channel);//остановить ЦАП канала channel
  bool DAC12_SetValue(int channel,unsigned int length,unsigned short *value);//установить значение в буфере канала channel
};
#endif


cdevice.cpp
Код
#include "cdevice.h"

//----------------------------------------------------------------------------------------------------
CDevice_KM104::CDevice_KM104(void)
{
TimeOut=100;//100 миллисекунд на команду
ADC12_LimitValue=10;
}
//установить время ожидания команды
bool CDevice_KM104::SetTimeOut(unsigned int timeout)
{
TimeOut=timeout;
return(true);
}
//установить базовый адрес
bool CDevice_KM104::SetBaseAddress(unsigned int baseaddress)
{
KM104Base=baseaddress;
return(true);
}
//подождать готовности платы
bool CDevice_KM104::WaitReady(unsigned int code)
{
unsigned int res;
SelectReg(RegComm);//выбрать регистр команд
timeb current_time;
timeb start_time;
ftime(&start_time);
while(1)
{
  ReadValue(&res);//считать данные с платы
  if (res==code) return(true);
  ftime(&current_time);
  long delta=(current_time.time-start_time.time)*1000+(current_time.millitm-start_time.millitm);
  if (delta<0)//особый случай, проще слегка увеличить задержку
  {
   start_time=current_time;
   continue;
  }
  if (delta>=TimeOut) break;//время истекло
}
return(false);
}
//записать значение value по адресу adress внутренней памяти платы
bool CDevice_KM104::Poke(unsigned int address,unsigned int value)
{
//выбираем ячейку для записи
SelectReg(address);
//заносим значение в ячейку для данных, которые будут помещены по этому адресу
WriteData(value);
//ждём готовности платы
if (WaitReady(CommOk)==false) return(false);
return(true);
}
//считать значение данных
bool CDevice_KM104::ReadValue(unsigned int *value)
{
//считывание производится несколько раз
//это связано с особенностями работы двухпортового ОЗУ
while(1)
{
  *value=inpw(KM104Base);
  if (inpw(KM104Base)==(unsigned short)*value) break;
}
return(true);
}
//записать значение данных
bool CDevice_KM104::WriteValue(unsigned int value)
{
//считывание производится несколько раз
//это связано с особенностями работы двухпортового ОЗУ
while(1)
{
  outpw(KM104Base,value);
  if (inpw(KM104Base)==value) break;
}
return(true);
}
bool CDevice_KM104::Peek(unsigned int address,unsigned int *value)
{
//выбираем ячейку для чтения
SelectReg(address);
//читаем данные
ReadValue(value);
//ждем готовности платы
if (WaitReady(CommOk)==false) return(false);
return(true);
}
//сброс платы
bool CDevice_KM104::Reset(void)
{
//сброс платы
outpw(KM104Base+RESET_KM,0);
delay(250);
if (WaitReady(ResetOk)==false) return(false);
//регистр управления
if (Poke(RegComm,CommOk)==false) return(false);
  //сброс двухпортового ОЗУ (инициализация ОЗУ IDT)
outpw(KM104Base+REG_RST,0);
if (WaitReady(CommOk)==false) return(false);
//стоп АЦП
if (Poke(RegComm,StopIntADC)==false) return(false);
if (Poke(RegComm,StopExtADC)==false) return(false);
//очистка флагов заполнения буфера для всех 16-ти каналов
if (Poke(StatBufIntADC0,0)==false) return(false);
if (Poke(StatBufIntADC1,0)==false) return(false);
if (Poke(StatBufExtADC,0)==false) return(false);
//останавливаем все каналы ЦАП
if (Poke(ModeDAC,BIN16(0,0,0,0, 1,1,1,1, 0,0,0,0, 0,0,0,0))==false) return(false);
return(true);
}
//----------------------------------------------------------------------------------------------------
//АЦП 12 бит
//----------------------------------------------------------------------------------------------------
//запустить АЦП
bool CDevice_KM104::ADC12_Start(void)
{
unsigned int ModeExt_ADC;
//стоп АЦП
if (Poke(RegComm,StopExtADC)==false) return(false);
//повторяющееся преобразование для всех каналов
ModeExt_ADC=BIN16(0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1);
if (Poke(ModeExtADC,ModeExt_ADC)==false) return(false);
if (Poke(RegComm,SetModeExtADC)==false) return(false);
if (Poke(T6mode,BIN16(1, 0,0,0,0, 1,0,0,0,1, 0,0,0, 0,0,0))==false) return(false);
if (Poke(Timer6,0)==false) return(false);
if (Poke(SampleExtADC,40)==false) return(false);
if (Poke(StatBufExtADC,0)==false) return(false);
if (Poke(ParamFillADC,2047)==false) return(false);
//старт АЦП
if (Poke(RegComm,StartExtADC)==false) return(false);
return(true);
}
//остановить АЦП
bool CDevice_KM104::ADC12_Stop(void)
{
if (Poke(RegComm,StopExtADC)==false) return(false);
return(true);
}
//получить данные преобразования
bool CDevice_KM104::ADC12_GetValue(double value_array[8][SizeBuf])
{
int n,m;
unsigned short ADC12Value[8][SizeBuf];//буфер данных АЦП 12 бит
ADC12_Start();
/*
//ждем срабатывания всех каналов АЦП
FILE *file=fopen("a.txt","wb");
while(1)
{
  unsigned int state;
  if (Peek(StatBufExtADC,&state)==false) return(false);
  fprintf(file,"%016b\r\n",state);
  if (state==0xffff) break;
}
fclose(file);
ADC12_Stop();*/

//ждем срабатывания всех каналов АЦП
while(1)
{
  unsigned int state;
  if (Peek(StatBufExtADC,&state)==false) return(false);
  if (state&0x5555) break;
}
//читаем первую половину буфера
for(n=0;n<8;n++)
{
  SelectReg(StartBufExtADC0_0+SizeBuf*n);
  for(m=0;m<SizeBuf/2;m++) ADC12Value[n][m]=ReadDatainc();
}

//ждем срабатывания всех каналов АЦП
while(1)
{
  unsigned int state;
  if (Peek(StatBufExtADC,&state)==false) return(false);
  if (state&0xaaaa) break;
}
//читаем вторую половину буфера
for(n=0;n<8;n++)
{
  SelectReg(StartBufExtADC0_1+SizeBuf*n);
  for(m=SizeBuf/2;m<SizeBuf;m++) ADC12Value[n][m]=ReadDatainc();
}

ADC12_Stop();
//преобразуем значения
double scale=(double)(ADC12_LimitValue)/2047.0;//константа перехода к вольтам
for(n=0;n<6;n++)
{
  for(int m=0;m<SizeBuf;m++)
  {
   int adc=ADC12Value[n][m];
   adc&=4095;
   adc-=2048;//вычтем половину интервала (т.к. значение двуполярное)
   value_array[n][m]=scale*((double)(adc));
  }
}
return(true);
}

//установить значение половины интервала
bool CDevice_KM104::ADC12_SetLimitValue(double limitvalue)
{
ADC12_LimitValue=limitvalue;
return(true);
}
//----------------------------------------------------------------------------------------------------
//ЦАП 12 бит
//----------------------------------------------------------------------------------------------------
//запустить ЦАП канала channel
bool CDevice_KM104::DAC12_Start(int channel)
{
if (channel<0 || channel>=4) return(false);//такого канала нет
unsigned int Mode_DAC=0;
//повторяющееся преобразование для канала
if (Peek(ModeDAC,&Mode_DAC)==false) return(false);
unsigned short DAC_Mask=1<<channel;
Mode_DAC|=DAC_Mask;//запускаем канал
Mode_DAC|=(DAC_Mask<<4);//привязываем канал к таймеру Т2
if (Poke(ModeDAC,Mode_DAC)==false) return(false);
//старт ЦАП
if (Poke(RegComm,StopTimerT2)==false) return(false);
if (Poke(T2mode,BIN16(0,0,0,0,0,0,0, 0,0,1, 0,0,0, 0,0,0))==false) return(false);
if (Poke(Timer2,2)==false) return(false);
if (Poke(RegComm,StartTimerT2)==false) return(false);
return(true);
}
//остановить ЦАП
bool CDevice_KM104::DAC12_Stop(int channel)
{
if (channel<0 || channel>=4) return(false);//такого канала нет
unsigned int Mode_DAC=0;
//останавливаем канал
if (Peek(ModeDAC,&Mode_DAC)==false) return(false);
unsigned short DAC_Mask=0xFF^(1<<channel);
Mode_DAC&=DAC_Mask;//останавливаем канал
Mode_DAC&=(DAC_Mask<<4);//отвязываем канал от таймера Т2
if (Poke(ModeDAC,Mode_DAC)==false) return(false);
return(true);
}
//установить значения в буфере
bool CDevice_KM104::DAC12_SetValue(int channel,unsigned int length,unsigned short *value)
{
if (length>4096) return(false);//слишком много
if (channel<0 || channel>=4) return(false);//такого канала нет
unsigned int index=0;
unsigned int Reg=StartBufDACA;
unsigned int Command=CopyDACA;
if (channel==0)
{
  Reg=StartBufDACA;
  Command=CopyDACA;
}
if (channel==1)
{
  Reg=StartBufDACB;
  Command=CopyDACB;
}
if (channel==2)
{
  Reg=StartBufDACC;
  Command=CopyDACC;
}
if (channel==3)
{
  Reg=StartBufDACD;
  Command=CopyDACD;
}
while(index<length)
{
  for(int n=0;n<128 && index<length;n++,index++)
  {
   unsigned short val=value[index];
   if (index==length-1) val|=BIN16(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
   SelectReg(Reg+n);
   WriteData(val);
  }
  //переносим в ЦАП
  if (Poke(RegComm,Command)==false) return(false);
  //ждём подтверждения готовности
  if (WaitReady(CommOk)==false) return(false);
}
return(true);
}
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------


А вот так плата инициализируется:


Код
bool Km104Initialize(void)
{
printf("Выполняется инициализация платы КМ-104.\n");
//устанавливаем параметры
if (cDevice_KM104.SetBaseAddress(0x300)==false)
{
  printf("Ошибка установки базового адреса!\n");
  return(false);
}
if (cDevice_KM104.SetTimeOut(100)==false)
{
  printf("Ошибка установки предельного времени ожидания!\n");
  return(false);
}
if (cDevice_KM104.Reset()==false)
{
  printf("Ошибка выполнения команды сброс платы!\n");
  return(false);
}
if (cDevice_KM104.ADC12_SetLimitValue(10)==false)
{
  printf("Ошибка установки режима АЦП!\n");
  return(false);
}
printf("Успешно.\n");
return(true);
}


Ну, вот и всё. Может, кому-то из разработчиков это поможет начать работу с этой платой.

Исходники: Нажмите для просмотра прикрепленного файла
da-nie
Описание платы КМ-104.

ВНИМАНИЕ! Это описание называется RM-104, а не KM-104, так как выходные разъёмы распаяны на разъёмы РПММ (потому и R в названии). Соответственно, номера контактов на разъёме РПММ не совпадают с номерами контактов на плате! Всё остальное верное.

Нажмите для просмотра прикрепленного файла

Кстати, если будете использовать ЦАП 12 бит, подключайте его вот так вот к вашей схеме:

Нажмите для просмотра прикрепленного файла

Всё дело в сильном шуме от этого ЦАП. Причём, шум не случайный! Он всегда одинаковый.
da-nie
Фейерверк на OpenGL

Фейерверк делается довольно просто. Вот красивый фейерверк сделать не так просто. smile.gif У меня получился фейерверк так себе, но "с пивом потянет". smile.gif
Что же нужно для фейерверка? Всего одна штука - обработчик частиц. Ведь вся эта красота - просто светящиеся частички, летящие по своим траекториям.
Опишем частицу следующим образом:

Код
//частица
struct SParticle
{
//тип частицы
int Type;
//координаты частицы
float X;
float Y;
float Z;
//цвет частицы
float R;
float G;
float B;
//перемещение частицы
float dX;
float dY;
float dZ;
//размер частицы
float Size;
//масса частицы
float Mass;
//сопротивление воздуха
float Resistance;
//время жизни
int LifeCounter;
};


Тип частицы будет указывать нам на действия, которые необходимо произвести после уничтожения частицы, как только время жизни частицы станет равным нулю.
Зачем это нужно? Например, запуская ракеты фейерверка, мы запускаем одну частицу, которая после уничтожения должна создать множество новых частиц, а они в свою очередь порождают другие частицы.

Вот код процессора частиц:

cparticle.h
Код
#ifndef CPARTICLE_H
#define CPARTICLE_H

#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>

#define M_PI 3.14159265358

#define PARTICLE_GRAVITY 0.01f

#include <list>
using namespace std;
//типы частиц
#define PARTICLE_TYPE_SIMPLY 1
#define PARTICLE_TYPE_EXPLOSION1 2
#define PARTICLE_TYPE_EXPLOSION2 3
#define PARTICLE_TYPE_4 4

//частица
struct SParticle
{
//тип частицы
int Type;
//координаты частицы
float X;
float Y;
float Z;
//цвет частицы
float R;
float G;
float B;
//перемещение частицы
float dX;
float dY;
float dZ;
//размер частицы
float Size;
//масса частицы
float Mass;
//сопротивление воздуха
float Resistance;
//время жизни
int LifeCounter;
};
class CParticle
{
protected:
  list<SParticle*> list_sParticlePtr;//список частиц
public:
  CParticle(void);
  ~CParticle(void);
  void Delete(void);//удалить все частицы
  void AddNewParticle(SParticle sParticle);//добавить частицу
  void Processing(void);//процесс обработки частиц
  void Draw(void);//отрисовка частицы
  void ParticleOut(SParticle sParticle);//уничтожение частицы
};

float randf(void);

#endif


cparticle.cpp
Код
#include "cparticle.h"

CParticle cParticle;

CParticle::CParticle(void)
{
list_sParticlePtr.clear();
}
CParticle::~CParticle(void)
{
Delete();
}
void CParticle::Delete(void)
{
list<SParticle*>::iterator iterator_begin=list_sParticlePtr.begin();
list<SParticle*>::iterator iterator_end=list_sParticlePtr.end();
list<SParticle*>::iterator iterator_current=iterator_begin;
while(iterator_current!=iterator_end)
{
  delete(*iterator_current);
  iterator_current++;
}
list_sParticlePtr.clear();
}
void CParticle::AddNewParticle(SParticle sParticle)
{
int size=list_sParticlePtr.size();
if (size>20000) return;//слишком много частиц
SParticle *sParticlePtr=new SParticle;
*sParticlePtr=sParticle;
list_sParticlePtr.push_back(sParticlePtr);
}
void CParticle::Processing(void)
{
list<SParticle*>::iterator iterator_begin=list_sParticlePtr.begin();
list<SParticle*>::iterator iterator_end=list_sParticlePtr.end();
list<SParticle*>::iterator iterator_current=iterator_begin;
while(iterator_current!=iterator_end)
{
  SParticle *sParticlePtr=*iterator_current;
  //перемещаем частицу
  sParticlePtr->X+=sParticlePtr->dX;
  sParticlePtr->Y+=sParticlePtr->dY;
  sParticlePtr->Z+=sParticlePtr->dZ;
  //учитываем споротивление воздуха
  sParticlePtr->dX*=(1.0f-sParticlePtr->Resistance);
  sParticlePtr->dY*=(1.0f-sParticlePtr->Resistance);
  sParticlePtr->dZ*=(1.0f-sParticlePtr->Resistance);
  //гравитация
  sParticlePtr->dY-=(PARTICLE_GRAVITY*sParticlePtr->Mass);
  //время жизни
  sParticlePtr->LifeCounter--;
  //обработка частицы
  if (sParticlePtr->LifeCounter==0)//частица потухла - надо удалить
  {
   //вызываем обработчик уничтожения частицы
   ParticleOut(*sParticlePtr);
   delete(sParticlePtr);
   list<SParticle*>::iterator iterator_delete=iterator_current;
   iterator_current++;
   list_sParticlePtr.erase(iterator_delete,iterator_current);
   iterator_end=list_sParticlePtr.end();
   continue;
  }
  iterator_current++;
}
}
void CParticle::Draw(void)
{
list<SParticle*>::iterator iterator_begin=list_sParticlePtr.begin();
list<SParticle*>::iterator iterator_end=list_sParticlePtr.end();
list<SParticle*>::iterator iterator_current=iterator_begin;

while(iterator_current!=iterator_end)
{
  SParticle *sParticlePtr=*iterator_current;
  glPointSize(sParticlePtr->Size);
  glBegin(GL_POINTS);
  glColor3f(sParticlePtr->R,sParticlePtr->G,sParticlePtr->B);
  glVertex3f(sParticlePtr->X,sParticlePtr->Y,sParticlePtr->Z);
  glEnd();
  iterator_current++;
}
}
void CParticle::ParticleOut(SParticle sParticle)
{
int n;
if (sParticle.Type==PARTICLE_TYPE_SIMPLY) return;//частица просто уничтожается
if (sParticle.Type==PARTICLE_TYPE_EXPLOSION1)//взрыв частицы порождает новые частицы
{
  SParticle sParticleNew;
  for(n=0;n<300;n++)
  {
   float xa=2.0f*M_PI*randf();
   float ya=M_PI*randf();
   float sp=0.1*(3.0f+2.0f*randf());  
   sParticleNew.X=sParticle.X;
   sParticleNew.Y=sParticle.Y;
   sParticleNew.Z=sParticle.Z;
   float c=(1.0-randf());
   sParticleNew.R=sParticle.R*c;
   sParticleNew.G=sParticle.G*c;
   sParticleNew.B=sParticle.B*c;
   sParticleNew.dX=sp*sin(ya)*cos(xa);
   sParticleNew.dY=sp*cos(ya);
   sParticleNew.dZ=sp*sin(ya)*sin(xa);
   sParticleNew.Size=0.6f*5;
   sParticleNew.Resistance=0.013f;
   sParticleNew.LifeCounter=30+rand()%10;
   sParticleNew.Mass=0.04f;
   sParticleNew.Type=PARTICLE_TYPE_EXPLOSION2;
   AddNewParticle(sParticleNew);
  }
}
if (sParticle.Type==PARTICLE_TYPE_EXPLOSION2)//взрыв частицы порождает новые частицы
{
  SParticle sParticleNew;
  for(n=0;n<20;n++)
  {
   float xa=360.0f*randf();
   float ya=180.0f*randf();
   float sp=0.1*(3.0f+2.0f*randf());  
   sParticleNew.X=sParticle.X;
   sParticleNew.Y=sParticle.Y;
   sParticleNew.Z=sParticle.Z;
   float c=(1.0-randf());
   sParticleNew.R=sParticle.R*c;
   sParticleNew.G=sParticle.G*c;
   sParticleNew.B=sParticle.B*c;

   sParticleNew.dX=sp*sin(ya)*cos(xa);
   sParticleNew.dY=sp*cos(ya);
   sParticleNew.dZ=sp*sin(ya)*sin(xa);
   sParticleNew.Size=0.4f*5;
   sParticleNew.Resistance=0.13f;
   sParticleNew.LifeCounter=5+rand()%10;
   sParticleNew.Mass=0.01f;
   sParticleNew.Type=PARTICLE_TYPE_SIMPLY;
   AddNewParticle(sParticleNew);
  }
}
}

float randf(void)
{
float r=rand();
float m=RAND_MAX;
return(r/m);
}


А запуск ракеты можно будет тогда сделать так:

Код
float R[6]={1,0,0,1,0,1};
float G[6]={0,1,0,1,1,0};
float B[6]={0,0,1,0,1,1};

if (randf()<0.02)//создадим новую ракету
{
  SParticle sParticle;
  int color=((int)(randf()*100))%6;

  sParticle.X=70*(randf()-0.5);
  sParticle.Y=20*(randf()-0.5);
  sParticle.Z=randf()-0.5;
  //угол на сфере
  float angle_a=M_PI/2+(randf()-0.5)*(M_PI/180.0*30.0);
  float angle_b=randf()*2.0f*M_PI;
  float speed=randf()*0.5+0.5;

  sParticle.dX=speed*cos(angle_a)*cos(angle_b);
  sParticle.dY=speed*sin(angle_a);
  sParticle.dZ=speed*cos(angle_a)*sin(angle_b);

  sParticle.Size=2+randf();
  sParticle.Mass=0.1f;
  sParticle.Resistance=0.02f;

  sParticle.LifeCounter=randf()*20+30;
  sParticle.Type=PARTICLE_TYPE_EXPLOSION1;

  sParticle.R=R[color];
  sParticle.G=G[color];
  sParticle.B=B[color];

  cParticle.AddNewParticle(sParticle);

  if (randf()>0.5)
  {
   color=((int)(randf()*100))%6;

   sParticle.R=R[color];
   sParticle.G=G[color];
   sParticle.B=B[color];

   cParticle.AddNewParticle(sParticle);
  }
}


Частицы надо в каждой итерации заставлять перемещаться, поэтому нужно вызывать по таймеру функцию cParticle.Processing();

Для того, чтобы за частицами тянулся шлейф, я использую буфер-накопитель:

Код
//читаем буфер-накопитель
glAccum(GL_RETURN,0.8f);
//текстура
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
//система координат
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//выводим салют
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glTranslatef(0,0,-70);
cParticle.Draw();
glPopMatrix();
glEnable(GL_DEPTH_TEST);
//загружаем буфер-накопитель
glAccum(GL_LOAD,1);


В этом случае старое изображение салюта в буфере постепенно пропадает и объединяется с новым изображением. Однако, работа с буфером-накопителем способна встроенные видеокарты (или старые) привести в состояние невероятных тормозов - разработчики не всегда оптимизируют все функции OpenGL. Так что имейте это в виду.

Вот как выглядит фейерверк:

Нажмите для просмотра прикрепленного файла

Исходный код и исполняемый файл программы фейерверка:

Нажмите для просмотра прикрепленного файла

Модифицировав машину частиц в части взрывов ракет, можно сделать фейерверки в виде любых объектов. Скажем, букв, контуров и прочего. Надо лишь запустить частицы по нужным контурам и с нужной скоростью.
da-nie
Давно я тут ничего не выкладывал. Пусть на этот раз тут будет сниффер. smile.gif

Что делает сниффер, думаю, все знают. smile.gif Желающие могут посмотреть, как внутри устроен их траффик интернета. smile.gif

Нажмите для просмотра прикрепленного файла
Sanyok774
Работает! )))
Shaman Anime
Даже не качая, могу сказать из чего устроен мой трафик - сплошной зашифрованный поток vpn biggrin.gif
Kaigen
А вот кто-нибудь умеет пользоваться тором по существу? Дать намеки как "гуглить" торовский поисковик или хотя бы как на него выйти? cd.gif
Shaman Anime
тором сейчас уже рисковано пользоваться- много развелось фейковых нодов. Правда один умный чел меня научил, как забанить целиком страну, но ни что не мешает сделать фейки в иной стране. А если банить целые регионы (тот же exUSSR), то нестабильно работает из-за снижения числа доступных подключений, да и скорость уже не айс. Куда интереснее P2P, но пока руки не доходят до него. Как то попробовал- ни фига не работало, видимо надо конкретно ковырять настройки.
da-nie
Вот уже лет 12 лежит у меня интересная программка. Сделана она на OpenGL и всё, что умеет - показывать сцену с двумя лампочками, плоскостью и пирамидой надо плоскостью. В чём же её интерес? В рассчёте освещённости с тенями от этих ламп (а их может быть до 8 - по числу источников света OpenGL).
Вот эта программа:

Нажмите для просмотра прикрепленного файла

Внутри есть файл shadow.h и shadow.cpp. В этих файлах находится функция построения тени одного выпуклого многоугольника на другой от одного источника света. Так же внутри есть недописанная статья с описанием метода рассчёта освещённости с помощью этого самого модуля построения теней. Статья пишется уже очень давно и я вряд ли её завершу. smile.gif
Как пользоваться функцией построения тени из файла shadow.h будет понятно из основной программы. В принципе, суть-то простая: для всех полигонов находим тени от всех источников. Далее, включая и выключая источники выводим эти кусочки полигонов. Вот и всё.
da-nie
А вот движок с использованием этой технологии создания теней. Он ровестник вышеприведённой программы. smile.gif В архиве есть движок с картой под сокращённый набор текстур и редактор карт под полный набор текстур. Увы, но карты после конвертации исказились и потеряли источники света. В редакторе важны настройки рендера, правильные такие, как у карты doom.map. В редакторе для удаления выбранных блоков используется клавиша delete. Для редактирования выбранных блоков клавиша enter.

Нажмите для просмотра прикрепленного файла

Нажмите для просмотра прикрепленного файла

Нажмите для просмотра прикрепленного файла

Ну вот и всё, что можно рассказать про эти две забавные программы. smile.gif
da-nie
Запустил под QNX плату CAN Adlink PCI-7841. Может быть, кому-нибудь пригодится.
Программа для работы с платой PCI-7841 (репозиторий будет обновляться).

За основу взят драйвер для Linux. Все магические числа успешно сохранены (я всё равно не в курсе, что большая часть из них значит).
Как драйвер оформлять не стал - лет 15 назад я такую глупость сделал для платы CAN527D (драйвер физического CAN->драйвер виртуального CAN (чтобы много устройств работало одновременно с одним CAN)->драйвер работы с устройством->основная программа) и наелся с задержками (у меня счёт на части миллисекунды идёт - относительно плотный обмен) и необходимостью следить за соединением с драйвером (вдруг он уже упал).

В данный момент осталось понять, как детектировать Buss-off и что после него сделать для восстановления канала (на CAN527D я полностью переинициализирую канал).


Ну и ещё переписал пасьянс под адаптивные настройки к размерам спрайтов.
Русская версия IP.Board © 2001-2019 IPS, Inc.