Блог

    Caché (Кашэ́) — промышленная высокопроизводительная, объектная система управления базами данных, интегрированная с технологией разработки веб-приложений. Единая архитектура данных Caché позволяет разработчикам использовать одновременно объектный, реляционный (SQL) и прямой (NoSQL) доступ к одним и тем же данным, хранение которых обеспечивается ориентированным на транзакции многомерным ядром СУБД.

    http://www.intersystems.ru/cache/

Последние записи


Теги

Информация

Пример работы с компонентом <canvas> и функцией getUserMedia по мотивам HTML5 Exploding Camera

добавлено: 17 июл 12
понравилось:0
просмотров: 2933
комментов: 0

теги:

Автор: servit

Картинка с другого сайта.

Этот же пост доступен и на хабре.

Начиная с версии 2012.1, в СУБД Caché появился встроенный ZEN-компонент <canvas>.
Примечание
Скачать однопользовательскую версию СУБД Caché можно отсюда.

А в последнем релизе Opera 12 появилась встроенная поддержка функции getUserMedia (WebRTC 1.0: Real-time Communication Between Browsers), которая даёт возможность обращаться к устройствам, генерирующим медиапоток, например к веб-камере.
Примечание
Сводная таблица поддержки getUserMedia/Stream API в настольных и мобильных браузерах.

Для демонстрации обеих этих возможностей, используя встроенный в СУБД Caché фреймворк ZEN, за основу было выбрано демо: HTML5 Exploding Camera Demo.
Примечание
Оригинальное демо в онлайне.

Также в код были дополнительно добавлены четыре фильтра на выводимое видео с веб-камеры для демонстрации работы с отдельными пикселями компонента <canvas>.

Код тестировался на:

  1) Caché 2012.2 Release Candidate (Unicode, x64);
  2) Opera 12.00 Release;
  3) Chrome 21 beta.

Примечание
WebRTC можно включить в браузере Chrome 18.0.1008 и его более поздних версиях на странице about:flags или chrome://flags

Картинка с другого сайта.

+ Код класса ZEN-страницы
Class demo.camcanvas Extends %ZEN.Component.page
{

Parameter JSINCLUDES As STRING = "zenCSLM.js";

/// Этот блок Style содержит определение CSS-стиля страницы.
XData 
Style
{
<
style type="text/css">
* {
  margin:0;
  padding:0;
}
a {
  color:#99f;
}
#page {
  background:#000;
  color:#fff;
  font-family:'Helvetica Neue', 'Free Sans', 'Deja Vu Sans', Arial, Helvetica, sans-serif;
  margin:0px;
  padding:0px;
}
#main {
  color:#fff;
  border:solid 2px #c00;
  border-radius:1em;
  line-height:1.5;
  margin:2em auto;
  padding:1em;
  width:50%;
}
#rs {
  background:#3af;
  color:#777;
}
</
style>
}

XData Contents [ XMLNamespace "http://www.intersystems.com/zen" ]
{
<
page xmlns="http://www.intersystems.com/zen" title="HTML5 Демо &quot;Взрывающаяся Камера&quot;" id="page">
  <
timer
    
id="timer"
    
timeout="0"
    
ontimeout="zenPage.processFrame();"
  
/>
  <
html
    
id="main"
    
align="center"
    
hidden="true">
    <
video
      
id="sourcevid"
      
autoplay="autoplay"
      
hidden="true"
    
>Извините, Ваш браузер не поддерживает тег &lt;video&gt;. Пожалуйста, попробуйте <href="http://ru.opera.com/download/">Opera</a>.</video>
  </
html>
  <
radioSet
    
id="rs"
    
align="center"
    
hidden="true"
    
displayList="Обычный,Рельефный,Красный,Инверсный,Монохромный"
    
valueList="filterNormal,filterEmboss,filterRed,filterInversed,filterGrayscale"
    
value="filterNormal"
    
onchange="zenPage.changeFilter(zenThis.getValue());"
  
/>
  <
canvas id="output" hidden="true"/>
  <
canvas id="sourcecopy" hidden="true"/>
</
page>
}

ClientMethod changeFilter(strFunction) [ Language = javascript ]
{
  eval(
"filter = 'this." strFunction "();'");
}

ClientMethod filterNormal() [ Language = javascript ]
{
}

ClientMethod filterInversed() [ Language = javascript ]
{
 
  
var imageData copy.getImageData(0,0,copycanvas.width,copycanvas.height);
  
var data=imageData.data;

  
var media (data[0]+data[1]+data[2])/3>>0

  data[
0]=media;
  data[
1]=media;
  data[
2]=media;

  
for(var i=4,len=data.length;i<len;i+=4) { 
    media 
= 255-(data[i]+data[i+1]+data[i+2])/3>>0

    data[i]
=media;
    data[i
+1]=media;
    data[i
+2]=media;
  } 
  copy.putImageData(imageData,
0,0);
}

ClientMethod filterGrayscale() [ Language = javascript ]
{
 
  
var imageData copy.getImageData(0,0,copycanvas.width,copycanvas.height);
  
var data=imageData.data;

  
for(var i=0,len=data.length;i<len;i+=4) { 
    
var media (data[i]+data[i+1]+data[i+2])/3>>0;

    data[i]
=media;
    data[i
+1]=media;
    data[i
+2]=media;
  } 
  copy.putImageData(imageData,
0,0);
}

ClientMethod filterRed() [ Language = javascript ]
{
 
  
var imageData copy.getImageData(0,0,copycanvas.width,copycanvas.height);
  
var data=imageData.data;

  
for(var i=0,len=data.length;i<len;i+=4) { 
    data[i
+1]=0;
    data[i
+2]=0;
  } 
  copy.putImageData(imageData,
0,0);
}

ClientMethod filterEmboss() [ Language = javascript ]
{
  
var imageData copy.getImageData(0,0,copycanvas.width,copycanvas.height);
  
var data=imageData.data;
  
  
var media (data[0]+data[1]+data[2])/3>>0;
  data[
0]=media;
  data[
1]=media;
  data[
2]=media;

  media 
(data[4]+data[5]+data[6])/3>>0;
  data[
4]=media;
  data[
5]=media;
  data[
6]=media;

  
for(var i=8,len=data.length;i<len;i+=4) { 
    media 
(data[i]+data[i+1]+data[i+2])/3>>0;

    data[i]
=media;
    data[i
+1]=media;
    data[i
+2]=media;

    data[i
-8]=(data[i-8]+255-media)/2>>0;
    data[i
-7]=(data[i-7]+255-media)/2>>0;
    data[i
-6]=(data[i-6]+255-media)/2>>0;
    
  } 
  copy.putImageData(imageData,
0,0);
}

ClientMethod processFrame() [ Language = javascript ]
{
  
if(!isNaN(video.duration)){
    
if(SOURCERECT.width == 0){
      SOURCERECT 
{x:0,y:0,width:video.videoWidth,height:video.videoHeight};
      copycanvas.width 
video.videoWidth;
      copycanvas.height 
video.videoHeight;

      
      TILE_WIDTH 
copycanvas.width / 16;
      TILE_HEIGHT 
copycanvas.height / 16;
      TILE_CENTER_WIDTH 
TILE_WIDTH / 2 >> 0;
      TILE_CENTER_HEIGHT 
TILE_HEIGHT / 2 >> 0;

      
this.createTiles();
      zenSetProp(
'output','hidden',false);
      zenSetProp(
'rs','hidden',false);
    }
  }
  
//копирование плитки
  
copy.drawImage(video, 00);
  eval(filter);
  draw.clearRect(PAINTX, PAINTY,PAINTWIDTH,PAINTHEIGHT);

  
for(var i=0, len tiles.length; i<len; i++){
    
var tile tiles[i];
    
if(tile.force 0.0001){
      
//расширение
      
var force tile.force;
      tile.moveX 
*= force;
      tile.moveY 
*= force;
      tile.moveRotation 
*= force;
      tile.currentX 
+= tile.moveX;
      tile.currentY 
+= tile.moveY;
      tile.rotation 
+= tile.moveRotation;
      tile.rotation 
%= 360;
      tile.force 
*= 0.9;
      
if(tile.currentX <= 0 || tile.currentX >= PAINTWIDTH){
        tile.moveX 
*= -1;
      }
      
if(tile.currentY <= 0 || tile.currentY >= PAINTHEIGHT){
        tile.moveY 
*= -1;
      }
    }
else if(tile.rotation != 0 || tile.currentX != tile.originX || tile.currentY != tile.originY){
      
//схлопывание
      
var diffx (tile.originX-tile.currentX)*0.2;
      
var diffy (tile.originY-tile.currentY)*0.2;
      
var diffRot (0-tile.rotation)*0.2;

      
if(this.absolute(diffx) 0.5){
        tile.currentX 
tile.originX;
      }
else{
        tile.currentX 
+= diffx;
      }
      
if(this.absolute(diffy) 0.5){
        tile.currentY 
tile.originY;
      }
else{
        tile.currentY 
+= diffy;
      }
      
if(this.absolute(diffRot) 0.5){
        tile.rotation 
= 0;
      }
else{
        tile.rotation 
+= diffRot;
      }
    }
else{
      tile.force 
= 0;
    }
    draw.save();
    draw.translate(tile.currentX, tile.currentY);
    draw.rotate(tile.rotation
*RAD);
    draw.drawImage(copycanvas, tile.videoX, tile.videoY, TILE_WIDTH, TILE_HEIGHT, 
-TILE_CENTER_WIDTH, -TILE_CENTER_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
    draw.restore();
  }
  zen(
'timer').startTimer();
}

ClientMethod successCallback(stream) [ Language = javascript ]
{
  
// Замена источника видеоэлемента потоком с камеры
  
video.src window.URL.createObjectURL(stream) || stream;
  video.play();
}

ClientMethod errorCallback(error) [ Language = javascript ]
{
}

/// Конструктор для отдельных плиток
ClientMethod 
Tile() [ Language = javascript ]
{
  
this.originX = 0;
  
this.originY = 0;
  
this.currentX = 0;
  
this.currentY = 0;
  
this.rotation = 0;
  
this.force = 0;
  
this.z = 0;
  
this.moveX= 0;
  
this.moveY= 0;
  
this.moveRotation = 0;
  
this.videoX = 0;
  
this.videoY = 0;
}

/// Быстрее, чем Math.abs
ClientMethod 
absolute(x) [ Language = javascript ]
{
  
return (x < 0 ? -x);
}

ClientMethod zindexSort(
  
a,
  
b) [ Language = javascript ]
{
  
return (a.force-b.force);
}

/// Получить координаты нажатия/мыши для взрыва полотна
ClientMethod 
dropBomb(
  
event,
  
obj) [ Language = javascript ]
{
  event.preventDefault();
  
var posx = 0;
  
var posy = 0;
  
var event || window.event;

  
if (e.touches) {
    posx 
event.touches[0].pageX;
    posy 
event.touches[0].pageY;
  } 
else if (e.pageX || e.pageY) {
    posx 
e.pageX;
    posy 
e.pageY;
  } 
else if (e.clientX || e.clientY) {
    posx 
e.clientX ZLM.getPageXOffset() document.documentElement.scrollLeft;
    posy 
e.clientY ZLM.getPageYOffset() document.documentElement.scrollTop;
  }
  
var canvasX posx-obj.offsetLeft;
  
var canvasY posy-obj.offsetTop;
  
this.explode(canvasX, canvasY);
}

ClientMethod explode(
  
x,
  
y) [ Language = javascript ]
{
  
for(var i=0, len tiles.length; i<len; i++){
    
var tile tiles[i];

    
var xdiff tile.currentX-x;
    
var ydiff tile.currentY-y;
    
var dist Math.sqrt(xdiff*xdiff ydiff*ydiff);
    
var rnd Math.random();

    
var randRange = 180+(rnd*10);
    
var range randRange-dist;
    
var force = 3*(range/randRange);
    
if(force tile.force){
      tile.force 
force;
      
var radians Math.atan2(ydiff, xdiff);
      tile.moveX 
Math.cos(radians);
      tile.moveY 
Math.sin(radians);
      tile.moveRotation 
0.5-rnd;
    }
  }
  tiles.sort(zindexSort);
  
this.processFrame();
}

ClientMethod createTiles() [ Language = javascript ]
{
  
var offsetX (TILE_CENTER_WIDTH+(PAINTWIDTH-SOURCERECT.width)/2 >> 0);
  
var offsetY (TILE_CENTER_HEIGHT+(PAINTHEIGHT-SOURCERECT.height)/2 >> 0);
  
var y=0;
  
while(y SOURCERECT.height){
    
var x=0;
    
while(x SOURCERECT.width){
      
var tile new this.Tile();
      tile.videoX 
x;
      tile.videoY 
y;
      tile.originX 
offsetX+x;
      tile.originY 
offsetY+y;
      tile.currentX 
tile.originX;
      tile.currentY 
tile.originY;
      tiles.push(tile);
      x
+=TILE_WIDTH;
    }
    y
+=TILE_HEIGHT;
  }
}

ClientMethod onloadHandler() [ Language = javascript ]
{
  TILE_WIDTH 
= 32;
  TILE_HEIGHT 
= 24;
  TILE_CENTER_WIDTH 
TILE_WIDTH / 2;
  TILE_CENTER_HEIGHT 
TILE_HEIGHT / 2;
  SOURCERECT 
{x:0, y:0, width:0, height:0};
  PAINTX 
= 0;
  PAINTY 
= 0;
  PAINTWIDTH 
ZLM.getViewportWidth();
  PAINTHEIGHT 
ZLM.getViewportHeight();
  
  RAD 
Math.PI/180;

  tiles 
[];
  filter 
'this.filterNormal();';
  video 
document.getElementById('sourcevid');
  copycanvas 
zen('sourcecopy').findElement('canvas');
  copy 
zen('sourcecopy').getContext();

  
var outputcanvas zen('output').findElement('canvas');
  draw 
zen('output').getContext();
  outputcanvas.width 
PAINTWIDTH;
  outputcanvas.height 
PAINTHEIGHT-20;
  
var mouse_down ('createTouch' in document 'ontouchstart' 'onmousedown');
  outputcanvas[mouse_down] 
function(event) {
    zenPage.dropBomb(event, 
this);
  };
  
  
// Получить поток с камеры, используя функцию getUserMedia
  
navigator.getUserMedia navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
  window.URL 
window.URL || window.webkitURL || window.mozURL || window.msURL;
  
if (navigator.getUserMedia) {
    
// This beautiful hack for the options is from @kanasansoft:
    // http://www.kanasansoft.com/weblab/2012/06/arguments_of_getusermedia.html
    
var gumOptions {video: true, toString: function(){return 'video';}};
    navigator.getUserMedia(gumOptions, 
this.successCallback, this.errorCallback);

    zen(
'timer').setProperty('timeout',33);
    zen(
'timer').startTimer();
  } 
else {
    zenSetProp(
'main','content','Ой, кажется, Ваш браузер не поддерживает функцию getUserMedia.<br>Пожалуйста, попробуйте <a href="http://ru.opera.com/download/">браузер, который имеет такую поддержку</a>.');
    zenSetProp(
'main','hidden',false);
  }
}

}

Исходники класса demo.camcanvas.

Импорт исходного кода, его компиляция и запуск примера

Импорт исходного кода (проекта, классов, данных и др.) можно осуществить с помощью:
Все эти инструменты доступны из меню Caché Launcher.

Для удобства запуска примера воспользуемся Caché Studio.
Примечание
Все вышеперечисленные инструменты поддерживают русский интерфейс, но далее при описании пунктов меню будет использоваться их английский вариант.

Итак:

  1) откройте Caché Studio;
  2) выберите область "USER": File–>Change Namespace или (F4);
  3) запустите мастер импорта файла: Tools–>Import Local или (Ctrl+I);
  4) выберите файл "sources.xml";
  5) установите галочку Compile Imported Items и нажмите OK;
  6) откройте исходный код нашего класса demo.camcanvas из дерева классов;

Картинка с другого сайта.

  7) откройте веб-страницу: View->Web Page или (F5).

По умолчанию ссылка на нашу страницу будет иметь следующий вид:
http://localhost:xxxx/csp/user/demo.camcanvas.cls
, где xxxx - это номер порта, который мы указали при инсталляции СУБД Caché, на котором будет работать встроенный веб-сервер Apache.

PS: класс demo.camcanvas не составит труда переделать под технологию CSP (Caché Server Pages).

Комментарии




Необходимо войти на сайт, чтобы оставлять комментарии