Posts Tagged ‘ActionScript 3.0’

Operações binárias

Posted in ASP.NET, ActionScript 3.0, Arduino, C++, Electronics, Flash, PHP on maio 25th, 2009 by Bruno Soares – 7 Comments

Ultimamente tenho me deparado com muitos trechos de códigos que utilizam operações binárias, como chaveamento de multiplexador, extração de RGB a partir de um inteiro ou hexadecimal, bitshift para controlar LED Matrix, etc… E finalmente dei aquela estuda, agora vai ai um post sobre o que resultou o estudo.

Obs.: Os trechos de códigos deste post foram escritos em ActionScript, mas pode ser aplicado a C, C++, Java, Processing, PHP, entre outras linguagens.


Introdução
• Bit Shift
   • Operador >> (bitwise right shift)
   • Operador << (bitwise left shift)
• Operações Bitwise
   • Operador & (bitwise AND)
   • Operador | (bitwise OR)
   • Operador ^ (bitwise XOR)
   • Operador ~ (bitwise NOT)
• Exemplos
   • Extraindo o RGB de uma cor
   • Chaveando multiplexador 4051
 


Introdução
Um operador binário, como o nome sugere, é um operador que trabalha com a representação binária do número, e como normalmente não sabemos a representação binária dos números de cabeça, vamos utilizar a tabela abaixo:

 -----------------------
|      BIN |  DEC | HEX |
|-----------------------|
|        1 |    1 |   1 |
|       10 |    2 |   2 |
|       11 |    3 |   3 |
|      100 |    4 |   4 |
|      101 |    5 |   5 |
|      110 |    6 |   6 |
|      111 |    7 |   7 |
|     1000 |    8 |   8 |
|     1001 |    9 |   9 |
|     1010 |   10 |   A |
|     1011 |   11 |   B |
|     1100 |   12 |   C |
|     1101 |   13 |   D |
|     1110 |   14 |   E |
|     1111 |   15 |   F |
|    10000 |   16 |  10 |
|    10001 |   17 |  11 |
|    10010 |   18 |  12 |
|    10011 |   19 |  13 |
|    10100 |   20 |  14 |
|-----------------------|
| 11111111 |  255 |  FF |
 ----------------------- 

A tabela lista os números de 1 à 20 e 255 em três bases diferentes:
Binário (BIN)
• Decimal (DEC)
Hexadecimal (HEX)

Analisando a tabela podemos concluir que 3d = 11b, 19d = 10011b (as letras d e b significam decimal e binário respectivamente). Lembrando que pode ser utilizada uma calculadora que opere em binário (como a do windows) ou uma alternativa de conversão de bases on-line como está: “Conversão de número binário”.

Então vamos deslocar, escorregar, escovar alguns bits para entender melhor.


Operador >>
Deslocamento de bits para a direita (bitwise right shift)

1
2
3
4
trace(8 >> 1); // 4
trace(8 >> 2); // 2
trace(8 >> 3); // 1
trace(8 >> 4); // 0

Olhando os números na base decimal faz pouco sentido, ou talvez nenhum sentido, então passamos os números corretos para a base binária e tudo fica mais claro:
8d = 1000b (8 decimal é igual a 1000 em binário), então:
1000b >> 1 (deslocando uma casa para direita) temos o número:
100b que em decimal é 4(dê uma olhada na tabela).

Agora ficou fácil não? Vamos deslocar o número 13:

1
trace(13 >> 1); // 6

13 em binário é 1101, deslocando uma casa para a direita (ou removendo 1 bit), fica 110, e 110 é igual a 6 em decimal.


Operador <<
Deslocamento de bits para a esquerda (bitwise left shift)

1
2
3
trace(2 << 1); // 4
trace(2 << 2); // 8
trace(2 << 3); // 16

Agora é só seguir o mesmo raciocino já utilizando anteriormente.
Se 2 em base binária é igual a 10 e deslocarmos um bit para esquerda, vamos ganhar mais um zero, ficando com 100 que é igual a 4 em decimal.


Operador &
AND binário (bitwise AND)
O operador & compara bit a bit os números a sua direita e esquerda, por exemplo o resultado de 10 & 11 é 10:

  1010
& 1011
------
  1010

A comparação bit-a-bit somente retorna True (1) quando os bits comparados são iguais a 1, caso contrário retorna False (0). Formando assim um novo número.
Mais alguns exemplos para fortalecer:

|14 &  9|13 & 11|20 &  9|14 & 10|89 &  112|45  &  77|255  &  13|112 &  255|
|       |       |       |       |         |         |          |          |
|  1110 |  1101 |  10100|  1110 |  1011001|   101101|  11111111|   1110000|
|& 1001 |& 1011 |&  1001|& 1010 |& 1110000|& 1001101|&     1101|& 11111111|
|  ---- |  ---- |  -----|  ---- |  -------|  -------|  --------|  --------|
|  1000 |  1001 |      0|  1010 |  1010000|     1101|      1101|   1110000|
|    8d |    9d |     0d|   10d |      80d|      13d|       13d|      112d|


Operador |
OR binário (bitwise OR)
O operador | tem a mesma função do operador OR comum (||) só que atua bit-a-bit, assim como os outros operadores binários. Vejamos um exemplo:

  1010
| 1011
------
  1011

Se um dos bits comparados forem iguais a 1 a expressão retornará 1, caso os dois bits comparados forem iguais a 0, a expressão retorna 0. Agora vamos refazer o exemplo anterior trocando apenas o operador & (and binário) por | (or binário):

|14 |  9|13 | 11|20 |  9|14 | 10|89 |  112|45  |  77|255  |  13|112 |  255|
|       |       |       |       |         |         |          |          |
|  1110 |  1101 |  10100|  1110 |  1011001|   101101|  11111111|   1110000|
|| 1001 || 1011 ||  1001|| 1010 || 1110000|| 1001101||     1101|| 11111111|
|  ---- |  ---- |  -----|  ---- |  -------|  -------|  --------|  --------|
|  1111 |  1111 |  11101|  1110 |  1111001|  1101101|  11111111|  11111111|
|   15d |   15d |    29d|   14d |     121d|     109d|      255d|      255d|


Operador ^
OU exclusivo (bitwise XOR)
A letra X na frente do OR significa Exclusive (Exclusive OR). Isso quer dizer que este operador faz a comparação binária de dois números e resulta os bits que são diferentes. Por exemplos, quais são os bits diferentes entre os números 10 e 11?

  1010
^ 1011
------
     1

Vamos novamente trocar o operador do exemplo anterior para analisar os resultados:

|14 ^  9|13 ^ 11|20 ^  9|14 ^ 10|89 ^  112|45  ^  77|255  ^  13|112 ^  255|
|       |       |       |       |         |         |          |          |
|  1110 |  1101 |  10100|  1110 |  1011001|   101101|  11111111|   1110000|
|^ 1001 |^ 1011 |^  1001|^ 1010 |^ 1110000|^ 1001101|^     1101|^ 11111111|
|  ---- |  ---- |  -----|  ---- |  -------|  -------|  --------|  --------|
|   111 |   110 |  11101|   100 |   101001|  1100000|  11110010|  10001111|
|    7d |    6d |    29d|    4d |      41d|      96d|      242d|      143d|


Operador ~
Negação (bitwise NOT)
O operador NOT inverte o sinal e complementa em um.
Negando o número 168 (~168) teremos -169.

Alguns exemplos:

1
2
3
4
5
6
trace(~7);   // -8
trace(~-7);  // 6
trace(~14);  // -15
trace(~13);  // -14
trace(~255); // -256
trace(~112); // -113

 


Extraindo o RGB de uma cor
Sabendo que uma cor no formato RGB utiliza dois dígitos hexadecimais para definir quanto existe de Vermelho, Verde e Azul (respectivamente), formando cores como: Vermelho (FF0000), Cinza (C0C0C0), Laranja (FF9900), etc. Temos ai a possibilidade de gerar 16.581.375 de cores com este código, é só fazer a conta para conferir: 255 * 255 * 255 ou FF * FF * FF.
Vamos desmembrar um tom de azul (#347BB7) para saber quanto esta cor tem de Vermelho, Verde e Azul (o valor dos canais RGB).

1
2
3
4
5
6
7
8
9
10
// DEC: 3439543
// BIN: 1101000111101110110111
var color:uint = 0x347BB7;

var r:uint = (color >> 16) & 0xFF;
var g:uint = (color >>  8) & 0xFF;
var b:uint =  color        & 0xFF;

trace("Red:", r, "Green:", g, "Blue:", b);
// Red: 52 Green: 123 Blue: 183

Linha 5) Deslocando 16 bits para a direita temos:

  1101000111101110110111 >> 16
=                 110100 (DEC: 52)

Para o caso do vermelho não precisamos continuar a expressão (& 0xFF),
pois deslocando 16 bits para a direita já temos o resultado do vermelho,
mas se a cor estivesse no formato ARGB (Alpha Red Green Blue), seria necessário.

Linha 6) Deslocando 8 bits para conseguir o verde:

  1101000111101110110111 >> 8
=         11010001111011 (DEC: 13435)

Só com o valor do deslocamentos não vamos conseguir a cor verde, então utilizamos o
operador & (AND) com o valor 255 (0xFF) para extrair a parte binária que nos interessa:

  11010001111011 (DEC: 13435)
&       11111111 (DEC: 255, HEX: 0xFF)
  --------------
        01111011 (DEC: 123)

Linha 7) Para extrair o azul não precisamos deslocar bits e sim pegar os últimos 8 bits:

  1101000111101110110111
&               11111111 (DEC: 255, HEX: 0xFF)
  ----------------------
                10110111 (DEC: 183)

Agora voltando para o hexadecimal:

1
2
3
4
5
6
7
var r:uint = 52;
var g:uint = 123;
var b:uint = 183;
var color:uint = (r << 16) | (g << 8) | b;

trace(color.toString(16));
// 347bb7

Linha 4) Deslocando 16 bits para a esquerda do número 52 (Vermelho):

  110100 << 16
= 1101000000000000000000

Deslocando 8 bits para a esquerda do número 123 (Verde):

  1111011 << 8
= 111101100000000

Efetuando o OR (|) com o resultado das duas operações ((r << 16) | (g << 8)):

  1101000000000000000000
|        111101100000000
  ----------------------
  1101000111101100000000

Efetuando a última operação, o OR com o Azul (183)

  1101000111101100000000
| 0000000000000010110111
  ----------------------
  1101000111101110110111

O resultado agora ficou claro. O número 1101000111101110110111 (binário) é igual a 3439543 (decimal) e 347BB7 (hexadecimal).

 


Chaveando multiplexador 4051
A tarefa de chavear um Multiplexador / Demultiplexador (MUX / DEMUX) 4051 é muito parecida com a extração dos canais RGB de uma cor. Você só precisa Ligar ou Desligar três pinos de seleção (select pins) para que o circuito interprete o valor gerado e transmita a voltagem da entrada desejada.
Existe um gif animado do RogerCom muito didático que demonstra o funcionamento do CI 4051, gif animado CI 4051 aqui.
Por exemplo, para ler a entrada 3, precisamos desligar o pino de seleção 0, ligar o 1 e o 2, formando assim o número 011 (binário) que é igual a 3 em decimal. Veja no código (Escrito em Arduino / C++):

1
2
3
4
5
6
7
8
9
10
11
12
// Entrada desejada
int count = 3;

// Extração dos bits ativos
byte s0 =  count       & 0x1;
byte s1 = (count >> 1) & 0x1;
byte s2 = (count >> 2) & 0x1;

// Ligando ou desligando os pinos de seleção
digitalWrite(2, s0);
digitalWrite(3, s1);
digitalWrite(4, s2);

Conteúdo relacionado:
Bitwise operation on Wikipedia
 

Vector em AS3 para Flash Player 10

Posted in ActionScript 3.0, Flash on maio 25th, 2009 by Bruno Soares – 2 Comments

Para aqueles que já estão usando Flash Player 10, trocar o Array por Vector é uma boa opção, pois você ganha em performance e organização de código (Vector é um array tipado ou seja, mais organizado).
O que mudou mais é a forma de construir o objeto, a sintaxe funciona como o Generics do C# ou Java. Ressaltando que não existe generics em AS3, somente o Vector que implementa uma sintaxe parecida com a sintaxe de um generics.

Muitos blogueiros já comentaram sobre o Vector logo quando saiu a versão beta do Flash Player 10. Por este motivo não vou falar muito sobre, vou apenas exemplificar e dar links para os outros blog.

Criando um Vector:

1
var vetor:Vector.<T> = new Vector.<T>();

(Substitua o T pelo tipo desejado)

Veja como é parecido com um Array:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package
{
    import __AS3__.vec.Vector;
   
    import flash.display.Sprite;
    import flash.utils.getTimer;
   
    [SWF(framerate="30", width="500", height="500")]
    public class Main extends Sprite
    {
        private var _vector:Vector.<String> = new Vector.<String>();
        private var _timer:Number;
       
        public function Main()
        {
            _vector.push("a");
            _vector.push("b");
            trace(_vector.length);
            _vector.push("c");
            trace(_vector.length);
            _vector[_vector.length] = "d";
            trace(_vector.length);
           
            for each (var s:String in _vector)
            {
                trace("FOR: " + s);
            }
           
            trace("Timer Vector:");
            testVector();
           
            trace("Timer Array:");
            testArray();
        }
       
        private function testArray():void
        {
            _timer = getTimer();
            var array:Array = new Array();
            for(var i:uint = 0; i < 1000000; i++)
            {
                array[i] = i;
            }
            trace(getTimer() - _timer);
        }
       
        private function testVector():void
        {
            _timer = getTimer();
            var vector:Vector.<uint> = new Vector.<uint>();
            for(var i:uint = 0; i < 1000000; i++)
            {
                vector[i] = i;
            }
            trace(getTimer() - _timer);
        }
    }
}

(O código acima é a Main Class de um projeto Flex / ActionScript)

Teste de performance entre arrays, vector e outros:
http://businessintelligence.me/projects/performance_tester/performanceTester.html

Veja na imagem abaixo como é “gritante” a diferença de performance entre um Array comum e um Vector:
as3-vector-array-performance-tester

Conteúdo relacionado:
Documentação do Vector
Teste de performance
Post sobre Vector por: Mike Chambers
Post sobre Vector por: DaveOnCode
Post sobre Vector por: The Back Button

Twitter Hardware

Posted in Adobe AIR, Arduino, Electronics, Projects on abril 18th, 2009 by Bruno Soares – 4 Comments

Twitter Hardware é um projeto que utiliza a placa Arduino e um Display LCD para exibir as mensagens do Twitter. Para que a Arduino tenha as mensagens escrevi um software em Adobe AIR / Flex, que se conecta ao Serial Proxy enviando o conteúdo para a Arduino.

Arduino:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/**
 * Twitter Hardware
 *
 * @author   Bruno Soares
 * @link     http://www.bsoares.com.br
 * @language Arduino / C++
 */


// Includes
#include <LiquidCrystal.h>

// Defines
#define DEBUG_PIN                 13

#define COMMAND_RIGHT             0
#define COMMAND_LEFT              1
#define QUANTITY_COMMANDS         2

#define ANALOG_PIN_COMMAND_RIGHT  0
#define ANALOG_PIN_COMMAND_LEFT   1
#define ANALOG_PIN_SPEED          2

#define MESSAGE_START             94  //  94 = ^
#define MESSAGE_END               126 // 126 = ~

#define SERIAL_BOUND              9600

#define LCD_LINES                 2
#define LCD_COLUMNS               16

#define ANIMATE_CMD_WIDTH         4

// Global variables
unsigned long time;

// LiquidCrystal display with:
// rs on pin 12
// rw on pin 11
// enable on pin 2
// dbs 3, 4, 5, 6, 7, 8, 9, 10
LiquidCrystal lcd(12, 11, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Analog configuration
unsigned int analogCmd[QUANTITY_COMMANDS];
unsigned int analogSpeed = 0;

// Control commands
boolean changingSpeed = true;
boolean readingSerial = false;
boolean pressedCmd[QUANTITY_COMMANDS];
unsigned int lastSpeed = 0;

// Massages
char currentMessage[400] = "No messages.";
unsigned int currentMessageLenght;

// ------------------------------------------------------------------------ \\
// Program
void setup() {
  Serial.begin(SERIAL_BOUND);
  pinMode(DEBUG_PIN, OUTPUT);
 
  smartDelay(10);
  lastSpeed = analogSpeed;
 
  presentation();
  changingSpeed = false;
}

void loop() {
 
  // Show current message
  clearLcd();
  writeLongTextInLcd(currentMessage);
  smartDelay(applySpeed(7000));
 
}

void presentation() {
  lcd.clear();
  writeLongTextInLcd("Twitter Hardwareby Bruno Soares");
  smartDelay(3000);
}

void refreshAnalogVars() {
  analogCmd[COMMAND_RIGHT] = analogRead(ANALOG_PIN_COMMAND_RIGHT);
  analogCmd[COMMAND_LEFT]  = analogRead(ANALOG_PIN_COMMAND_LEFT);
  analogSpeed              = analogRead(ANALOG_PIN_SPEED);
}

// ------------------------------------------------------------------------ \\
// [INI] Commands
void detectCommand() {
  unsigned int i = 0;
  while (i < QUANTITY_COMMANDS) {
    if (analogCmd[i] > 500 && !pressedCmd[i]) {
      executeCommand(i);
    }
    i++;
  }
}

void executeCommand(int command) {
  pressedCmd[command] = true;
  digitalWrite(DEBUG_PIN, 1);
 
  if (command == 0) {
    animateNext();
  } else {
    animatePrevious();
  }
 
  currentMessage[0] = 'L';
  currentMessage[1] = 'o';
  currentMessage[2] = 'a';
  currentMessage[3] = 'd';
  currentMessage[4] = 'i';
  currentMessage[5] = 'n';
  currentMessage[6] = 'g';
  currentMessage[7] = '\0';
  currentMessageLenght = 7;
 
  Serial.print(command);
 
  while (analogCmd[command] > 500) {
    smartDelay(4);
  }
 
  pressedCmd[command] = false;
  digitalWrite(DEBUG_PIN, 0);
}

void animateNext()  {
  for (unsigned int i = 0; i < LCD_COLUMNS + ANIMATE_CMD_WIDTH; i++) {
    if (i < LCD_COLUMNS) {
      lcd.setCursor(i, 0);
      lcd.print(">");
      lcd.setCursor(i, 1);
      lcd.print(">");
    }
    delay(5);
    if (i - ANIMATE_CMD_WIDTH >= 0) {
      lcd.setCursor(i - ANIMATE_CMD_WIDTH, 0);
      lcd.print(" ");
      lcd.setCursor(i - ANIMATE_CMD_WIDTH, 1);
      lcd.print(" ");
    }
    delay(30);
  }
}

void animatePrevious() {
  for (int i = LCD_COLUMNS; i >= -ANIMATE_CMD_WIDTH; i--) {
    if (i >= 0 && i < LCD_COLUMNS) {
      lcd.setCursor(i, 0);
      lcd.print("<");
      lcd.setCursor(i, 1);
      lcd.print("<");
    }
    delay(5);
    if (i + ANIMATE_CMD_WIDTH < LCD_COLUMNS && i + ANIMATE_CMD_WIDTH >= 0) {
      lcd.setCursor(i + ANIMATE_CMD_WIDTH, 0);
      lcd.print(" ");
      lcd.setCursor(i + ANIMATE_CMD_WIDTH, 1);
      lcd.print(" ");
    }
    delay(30);
  }
}
// [END] Commands

// ------------------------------------------------------------------------ \\
// [INI] Messages
void detectSerialMessage() {
  if (Serial.available() > 0 && !readingSerial) {
    if (Serial.read() == MESSAGE_START) {
      serialReadMessage();
    }
  }
}

void serialReadMessage() {
  digitalWrite(DEBUG_PIN, 1);
  readingSerial = true;
  currentMessageLenght = 0;
 
  iniReading:
  if (Serial.available() > 0) {
    unsigned int _char = Serial.read();
    if (_char == MESSAGE_END) {
      goto endReading;
    } else {
      currentMessage[currentMessageLenght++] = _char;
      delay(2);
      goto iniReading;
    }
  }
  goto iniReading;
 
  endReading:
  currentMessage[currentMessageLenght] = '\0';
  digitalWrite(DEBUG_PIN, 0);
  readingSerial = false;
}
// [END] Messages

// ------------------------------------------------------------------------ \\
// [INI] LCD manipulation
void writeLongTextInLcd(char text[]) {
  writeInit:
  clearLcd();
  unsigned int cml = currentMessageLenght;
  int loops = -1;
  unsigned int chars = 1;
  while (text[loops++ + 1] != 0) {
    if (chars == 17) {
      lcd.setCursor(0, 1);
      smartDelay(applySpeed(600));
      if (cml != currentMessageLenght) goto writeInit;
    } else if (chars == 33) {
      smartDelay(applySpeed(3000));
      clearLcd();
      if (cml != currentMessageLenght) goto writeInit;
      chars = 1;
    }
    lcd.print(text[loops]);
    smartDelay(applySpeed(60));
    if (cml != currentMessageLenght) goto writeInit;
    chars++;
  }
  Serial.print(2);
}

void writeInLcd(char text[], int quantity) {
  for (unsigned int i = 0; i < quantity; i++) {
    if (text[i] == 0) {
      do {
        lcd.print(" ");
      } while (i++ < quantity);
      break;
    } else {
      lcd.print(text[i]);
    }
  }
  delay(2);
  //smartDelay(5);
}

void clearLcd() {
  lcd.setCursor(0, 0);
  smartDelay(5);
  lcd.print("                ");
  smartDelay(applySpeed(400));
  lcd.setCursor(0, 1);
  smartDelay(5);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  smartDelay(applySpeed(0));
}
// [END] LCD manipulation

// ------------------------------------------------------------------------ \\
// [INI] Speed
void detectChangeSpeed() {
  if (changingSpeed) return;
  if (!(lastSpeed < analogSpeed + 6 && lastSpeed > analogSpeed - 6)) {
    showGraderSpeed();
  }
}

void showGraderSpeed() {
  changingSpeed = true;
  digitalWrite(DEBUG_PIN, 1);
  lcd.clear();
  writeInLcd("Speed:", LCD_COLUMNS);
 
  // Create display velocity
  showSpeed:
  for (unsigned int i = 0; i < 10; i++) {
    int charsSpeed = map(analogSpeed, 0, 1023, 0, LCD_COLUMNS);
    int percentSpeed = map(analogSpeed, 0, 1023, 0, 100);
    char* percentString;
    itoa(100 - percentSpeed, percentString, 10);
    percentString = strcat(percentString, "%");
    lcd.setCursor(7, 0);
    writeInLcd(percentString, 4);
    charsSpeed = LCD_COLUMNS - charsSpeed;
    lcd.setCursor(0, 1);
    for (unsigned int x = 1; x < LCD_COLUMNS + 1; x++) {
      if (x <= charsSpeed) {
        lcd.print(">");
      } else {
        lcd.print(" ");
      }
    }
    lastSpeed = analogSpeed;
    smartDelay(40);
  }
 
  // Verify
  for (unsigned int i = 0; i < 10; i++) {
    if (!(lastSpeed < analogSpeed + 6 && lastSpeed > analogSpeed - 6)) {
      goto showSpeed;
    }
    smartDelay(40);
  }
 
  // Finalize
  lcd.clear();
  changingSpeed = false;
  digitalWrite(DEBUG_PIN, 0);
}

long applySpeed (unsigned int value) {
  return map(analogSpeed, 0, 1023, 0, value);
}

// [END] Speed

// ------------------------------------------------------------------------ \\
// [INI] Milliseconds controller
void smartDelay(int milliseconds) {
  if (milliseconds < 2) {
    noDelayFunctions();
    delay(milliseconds);
    return;
  }
  do {
    delay(1);
    milliseconds -= noDelayFunctions();
    milliseconds -= 1;
  } while (milliseconds > 0);
}

int noDelayFunctions() {
  time = millis();
 
  // [INI] No delay functions here
  refreshAnalogVars();
  detectChangeSpeed();
  detectCommand();
  detectSerialMessage();
  // [END] No delay functions here
 
  return millis() - time;
}
// [END] Milliseconds controller

ActionScript (usando Serialproxy):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package br.com.bsoares.air.airtwitterhardware
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.net.Socket;
   
    /**
     * Message Control
     * Control messages between the Arduino and in AIR
     * Application using Serial Proxy.
     *
     * @author Bruno Soares
     * @link   http://www.bsoares.com.br
     */

    public class MessageControl extends EventDispatcher
    {
        // Properties
        private var _host:String;
        private var _port:uint;
        private var _socket:Socket;
        private var _messages:Array;
        private var _currentMessage:uint;
        private static var _instance:MessageControl;
        private static var _allowInstantiation:Boolean = false;
       
        // Constructor
        public function MessageControl()
        {
            if (!_allowInstantiation)
                throw new Error("Use instance property (this is a Singleton Class).");
            init();
        }
       
        // Logic
        private function init():void
        {
            _host = "127.0.0.1";
            // COM2
            _port = 5332;
            _messages = [ ];
            _currentMessage = 0;
            socketConnect();
        }
       
        private function socketConnect():void
        {
            _socket = new Socket();
            _socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
            _socket.addEventListener(Event.CLOSE, onSocketClose);
            _socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
            _socket.connect(_host, _port);
        }
       
        private function processData(data:String):void
        {
            trace("Arduino command:", data);
            switch (true)
            {
                // Send next message
                case data == "2" || data == "0":
                {
                    sendNextMessage();
                    break;
                }
                // Send previous message
                case data == "1":
                {
                    sendPreviousMessage();
                    break;
                }
            }
        }
       
        private function sendMessage(message:String):void
        {
            if (message == null || message == "")
                return;
           
            trace("Message:", message);
           
            // MESSAGE_START
            _socket.writeUTFBytes("^");
            _socket.flush();
           
            // MESSAGE
            for (var i:uint = 0; i < message.length; i++)
            {
                _socket.writeUTFBytes(message.charAt(i));
            }
            _socket.flush();
           
            // MESSAGE_END
            _socket.writeUTFBytes("~");
            _socket.flush();
           
            dispatchEvent(new Event(Event.CHANGE));
        }
       
        private function sendNextMessage():void
        {
            _currentMessage = _currentMessage == _messages.length - 1 ? 0 : _currentMessage + 1;
            sendMessage(_messages[_currentMessage]);
        }
       
        private function sendPreviousMessage():void
        {
            _currentMessage = _currentMessage == 0 ? _messages.length - 1 : _currentMessage - 1;
            sendMessage(_messages[_currentMessage]);
        }
       
        // Events
        private function onSocketData(event:ProgressEvent):void
        {
            while (_socket.bytesAvailable > 0)
                processData(_socket.readUTFBytes(1));
        }
       
        private function onSocketClose(event:Event):void
        {
            socketConnect();
        }
       
        private function onSocketIoError(event:IOErrorEvent):void
        {
            trace(event.text);
        }
       
        // Getters and Setters
        public static function get instance():MessageControl
        {
            if (_instance == null) {
                _allowInstantiation = true;
                _instance = new MessageControl();
                _allowInstantiation = false;
            }
            return _instance;
        }
       
        public function set messages(value:Array):void
        {
            _currentMessage = 0;
            _messages = value;
            if (_messages.length > 0)
                sendMessage(_messages[0]);
        }
       
        public function get messages():Array
        {
            return _messages;
        }
       
        public function get currentMessage():uint
        {
            return _currentMessage;
        }
    }
}

Esquemático:
twitterhardware-schematic

Baixe o código fonte aqui.

Conteúdo relacionado:
Mais fotos: http://www.flickr.com/photos/bsoares/sets/72157616923436133/
Fritzing: http://fritzing.org/
Arduino: http://arduino.cc/
Adobe AIR: http://www.adobe.com/products/air/
Adobe Flex: http://www.adobe.com/products/flex/
LiquidCrystal Library: http://arduino.cc/en/Reference/LiquidCrystal?from=Tutorial.LCDLibrary
Twitter: http://twitter.com/

JPGEncoder (AS3) com AMFPHP

Posted in ActionScript 3.0, Flash, PHP, Remoting on abril 7th, 2009 by Bruno Soares – 3 Comments

Tenho notado pelo Google Analytics que pessoas chegam ao blog procurando por AMFPHP, encode de imagens criadas no flash, salvar imagem com Flash + AMFPHP e outros critérios de busca. E por isso me sinto na obrigação de escrever algo sobre isto.

Exemplo:

This movie requires Flash Player 9

Vou demonstrar exatamente o que o título do post propõe (Criar imagens no Flash com ActionScript 3, encodar essas imagens com a classe JPGEncoder presente na biblioteca as3corelib e salvar como um arquivo .jpg utilizando o AMFPHP). Já escrevi aqui como fazer isso em FluorineFx (ASP.NET Flash Remoting Gateway).

Suponho que quem esteja interessado em rodar o que está descrito neste tutorial tenha o Apache com PHP instalado, ou algum servidor com suporte. Caso você não tenha recomendo a instalação do XAMPP (é de fácil instalação e tem tudo que um programador precisa).

Configuração do AMFPHP:
A versão que utilizo neste tutorial é a 1.9 beta, mas versões posteriores devem funcionar perfeitamente.
• Baixe o AMFPHP do seguinte link: http://www.amfphp.org/
• Copie o conteúdo do ZIP para o diretório onde você está criando o projeto, recomendo a estrutura de diretórios como mostrada na imagem abaixo:
folders

• Para testar o funcionamento é só acessar o diretório browser do navegador (http://127.0.0.1/www/amf/browser/).

Fique entendido que a responsabilidade de gerar a imagem é do Flash, e o servidor deve apenas receber os binários para gravar em disco. Para gerarmos o código da imagem (binário), vamos utilizar a classe JPGEncoder.
Exemplo:

1
2
3
4
5
6
7
8
import flash.display.BitmapData;
import flash.utils.ByteArray;
import com.adobe.images.JPGEncoder;

var bmpData:BitmapData = new BitmapData(width, height);
bmpData.draw(MEU_MOVIECLIP);
var objJPGEncoder:JPGEncoder = new JPGEncoder(QUALIDADE);
var dadosEncode:ByteArray = objJPGEncoder.encode(bmpData);

Muito simples não? O método draw da classe BitmapData obtém a imagem atual do clip, criamos uma instância da JPGEncoder já passando a qualidade (0 à 100) e por fim “encodamos” o BitmapData utilizando o método encode da nossa instância da JPGEncoder, ele nos retorna um Array de Bytes (flash.utils.ByteArray).

Vamos a parte do actionscript que interessa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private function encode():void
{
    lblMessage.text = "Codificando dados (JPGEncoder.encode)";
   
    var bmpData:BitmapData = new BitmapData(hit.width, hit.height);
    bmpData.draw(target);
    var objJPGEncoder:JPGEncoder = new JPGEncoder(sliderQuality.value);
    var dadosEncode:ByteArray = objJPGEncoder.encode(bmpData);
   
    sendToAmf(dadosEncode);
}

private function sendToAmf(data:ByteArray):void
{
    lblMessage.text = "Enviando dados para o AMF...";
   
    _objService = new NetConnection();
    _objResponder = new Responder(onResultEvent, onStatusEvent);
    _objService.connect(_amfGateway);
    _objService.call("br.com.bsoares.Image.saveDataToFile", _objResponder, data);
}

private function onResultEvent(result:Object):void
{
    lblMessage.text = "Abrindo imagem";
    navigateToURL(new URLRequest("http://blog.bsoares.com.br/articles/jpgencoder_amfphp/generated_images/image.jpg"), "_blank");
}

private function onStatusEvent(event:Event):void
{
    lblMessage.text = "Erro";
}

Agora a classe Image do PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class Image
{
    var $imagePath;
   
    public function __construct ()
    {
        $this->imagePath = "../../../../../generated_images/image.jpg";
    }

    function saveDataToFile($byteArray)
    {
        file_put_contents($this->imagePath, $byteArray->data);
        return $this->imagePath;
    }
}
?>

O PHP só precisa pegar o ByteArray e salvar em um arquivo.

Dica: Para verificar os request usem o Charles Web Debugging Proxy.

É isso ai, qualquer dúvida só postar um comentário.

Conteúdo relacionado:
Código fonte do exemplo: http://blog.bsoares.com.br/articles/jpgencoder_amfphp/jpgencoder-amfphp.zip
AMFPHP: http://www.amfphp.org/
AS3CoreLib: http://code.google.com/p/as3corelib/
Charles: http://www.charlesproxy.com/

Enjoy

Flickr – Buscar fotos por Tag com ActionScript

Posted in ActionScript 3.0, Flash on abril 4th, 2009 by Bruno Soares – 2 Comments

Vamos a um exemplo de uso da API do Flickr, bem simples pois a API do Flickr é realmente simples (isso não quer dizer que ela não é poderoza).

Aqui está o resultado de pouco código:

This movie requires Flash Player 9

Em primeiro lugar você vai precisar de uma api_key e pode conseguir neste link “Solicitar uma nova chave API“. Com sua KEY em mão vamos chamar a api buscando uma tag:
http://api.flickr.com/services/rest/?api_key=[SUA-API-KEY]&method=flickr.photos.search&tags=[TAG]

O resultado esperado é este:

1
2
3
4
5
6
7
<rsp stat="ok">
<photos page="1" pages="10" perpage="100" total="1000">
    <photo id="3411384625" owner="[email protected]" secret="74167a8895" server="3374" farm="4" title="_MG_0611" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3411401933" owner="[email protected]" secret="46c095f827" server="3585" farm="4" title="_MG_0641" ispublic="1" isfriend="0" isfamily="0" />
    ...
</photos>
</rsp>

Vamos entender o link, rest é o formato que você deseja receber a resposta, atualmente o Flickr suporta 5 formatos (REST, XML-RPC, SOAP, JSON e PHP), usamos o rest porque o ActionScript trabalha muito bem com ele (XML). api_key é a chave que você solicitou acima. method, é o método de busca (você pode encontrar muitos no link da API, http://www.flickr.com/services/api/). E por fim tags que é um parâmetro pelo qual você busca.

Agora que temos o XML de resposta podemos construir links para as fotos, para a página de galeria do usuário encontrado, para o perfil do usuário entro outros. O link que eu acho mais importante é o da imagem e para consegui-lo basta concatenar alguns dados presentes no XML dessa forma:

http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg

http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg

http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)

O que é mstb?
s) quadrado pequeno 75×75.
t) miniatura, 100 no lado mais longo.
m) pequeno, 240 no lado mais longo.
-) médio, 500 no lado mais longo.
b) grande, 1.024 no lado mais longo (existe apenas para imagens originais muito grandes).
o) imagem original, jpg, gif ou png, dependendo do formato de origem.
Na página URLs da origem da foto você encontra tudo bem explicado.

Veja no exemplo abaixo o link (do primeio nó ‘photo’) para a imagem:
http://farm4.static.flickr.com/3374/3411384625_74167a8895.jpg

Agora tudo isso com ActionScript (Load do XML e parse para gerar o link):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var apiKey:String = "[SUA-API-KEY]";
var tag:String = "arduino";

var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE, onComplete);
var url:String = "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=" + apiKey + "&tags=" + tag;
trace("URL: ", url);
xmlLoader.load(new URLRequest(url));

function onComplete(event:Event):void
{
    var xmlData:XML = new XML(event.target.data);
    if (xmlData.@stat != "ok")
    {
        trace("ERROR: " + xmlData.err.@msg);
        return;
    }
   
    for (var i:uint = 0; i < xmlData.photos.photo.length(); i++)
    {
        var photo:Object = {
            id:xmlData.photos.photo[i].@id,
            owner:xmlData.photos.photo[i].@owner,
            secret:xmlData.photos.photo[i].@secret,
            server:xmlData.photos.photo[i].@server,
            farm:xmlData.photos.photo[i].@farm,
            title:xmlData.photos.photo[i].@title
        };
        photo.url = "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" + photo.id + "_" + photo.secret + ".jpg";
        trace("Imagem: ", photo.url);
    }
}

Repare que você pode adicionar dois parâmetros no link para controlar a paginação das fotos (page e perpage).

Conteúdo relacionado:
Flickr API: http://www.flickr.com/services/api/
XML ActionScript 3.0: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/XML.html

AIR Install Badge com Google Analytics

Posted in ActionScript 3.0, Adobe AIR on abril 2nd, 2009 by Bruno Soares – Be the first to comment

Já a algum tempo procurei um Air Install Badge com o Google Analytics integrado e acabei encontrando o do Marc’s Musings, mas me pareceu meio “travado” uma vez que é necessário compilar o flash com as informações do seu aplicativo AIR. Sendo assim modifiquei o original para receber via FlashVars o nome da aplicação, versão, endereço do .air, e é claro o código AnalyticsTracker.

O Google Translation Plus (uma aplicação que estou desenvolvendo) já está utilizando o Install Badge modificado, verifique.

Como é mostrado o relatório no Google Analytics:
googleanalytics


Principal trecho de código do Install Badge alterado:

1
2
3
4
// Google Analytics Tracking
if (tracker != null) {
    tracker.trackPageview("/" + appName + "/" + action);
}

Atenção:
Existem muitas variáreis para serem alteradas no arquivo AirAppPage.html, como por exemplo: analyticstracker, airversion, appname, appurl, appid, appversion, etc…

Download do AIR Install Badge com Google Analytics.

Conteúdo relacionado:
AIR Install Badge: http://www.adobe.com/devnet/air/articles/badge_for_air.html
gaforflash: Google Analytics Tracking For Adobe Flash
swfobject: http://code.google.com/p/swfobject/

Regra de três simplificada (Map do Processing)

Posted in ActionScript 3.0, Flash on março 30th, 2009 by Bruno Soares – 3 Comments

Se tem uma coisa que programador Flash faz muito é regra de três, esta pequena formula matemática é executada diversas vezes dentro de um projeto, e ela é usada para transferir um valor dentro de um intervalo de números para o valor equivalente em um outro intervalo de números. Pro exemplo, no loading de uma imagem você sabe quantos bytes tem a imagem e quantos bytes já foram carregados, agora como calcular o percentual de carregamento? Vamos supor que a imagem tenha 300 bytes e já foram carregados 150 então temos 50% carregado, para chegar a este resultado de uma forma fácil e padronizada:

1
trace(NumberUtils.map(150, 0, 300, 0, 100));

Converti o script escrito originalmente em Processing e Arduino para ActionScript 3.0, agora é só usar.

Mais exemplos de uso:

1
2
3
4
5
6
7
8
9
10
11
12
13
import br.com.bsoares.utils.NumberUtils;

trace(NumberUtils.map(50, 0, 100, 0, 10));
// 5

trace(NumberUtils.map(90, 0, 100, 0, 10));
// 9

trace(NumberUtils.map(90, 100, 0, 0, 10));
// 1

trace(NumberUtils.map(-90, -100, 100, 0, 10));
// 0.5

Classe completa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * NumberUtils
 *
 * @author Bruno Soares
 * @link http://www.bsoares.com.br
 */


package br.com.bsoares.utils
{
    public class NumberUtils
    {
        public function NumberUtils() { }

        /**
         * Transfere um valor de um intervalo para outro.
         * Versão original escrita em Processing (http://processing.org/reference/map_.html).
         *
         * @param value Valor a ser transferido
         * @param inMin Menor valor do primeiro intervalo
         * @param inMax Maior valor do primeiro intervalo
         * @param outMin Menor valor do segundo intervalo
         * @param outMax Maior valor do segundo intervalo
         *
         * @return Valor calculado
         */

        public static function map(value:Number, inMin:Number, inMax:Number, outMin:Number, outMax:Number):Number
        {
            return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
        }
    }
}

Links relacionados:
Processing: http://processing.org/reference/map_.html
Arduino: http://arduino.cc/en/Reference/Map
Regra de Três: http://pt.wikipedia.org/wiki/Regra_de_tr%C3%AAs, http://www.somatematica.com.br/fundam/regra3s.php

Enjoy

JPEG Encoder (AS3) + FluorineFx .NET Flash Remoting Gateway

Posted in ASP.NET, ActionScript 3.0, Flash, Remoting on fevereiro 18th, 2008 by Bruno Soares – 4 Comments
Vamos entender neste post como o flash cria um jpeg e o envia através do FluorineFx para o servidor para que o servidor possa trabalhar o dado binário e salvar como um arquivo .JPG.

Não vamos entrar em detalhes da instalação e nem da configuração básica de um site com FluorineFx, pois no próprio site do FluorineFx existe um ótimo tutorial para esta configuração inicial (FluorineFx Visual Studio 2005 Wizard).

Em primeiro lugar, que fique entendido que a responsabilidade de gerar a imagem é do Flash, e o servidor deve apenas receber os binários para gravar em disco. Para gerarmos o código da imagem (binário), vamos utilizar a classe JPEGEncoder.
Exemplo:

1
2
3
4
5
6
7
8
import flash.display.BitmapData;
import flash.utils.ByteArray;
import JPEGEncoder;

var bmpData : BitmapData = new BitmapData(width, height);
bmpData.draw(MEU_MOVIECLIP);
var objJPEGEncoder : JPEGEncoder = new JPEGEncoder(QUALIDADE);
var dadosEncode : ByteArray = objJPEGEncoder.encode(bmpData);

Muito simples não? O método draw da classe BitmapData obtém a imagem atual do clip, criamos uma instância da JPEGEncoder já passando a qualidade (0 à 100) e por fim “encodamos” o BitmapData utilizando o método encode da nossa instância da JPEGEncoder, ele nos retorna um Array de Bytes (flash.utils.ByteArray).

Agora ficou simples, temos em mãos o array de bytes, podemos enviar ele para o servidor para que o mesmo possa fazer sua parte, que nada mais é que salvar estes bytes em um arquivo. Utilizando o FluorineFx fica fácil, veja o código abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Web;
using System.Drawing;
using System.IO;
using FluorineFx;
using FluorineFx.AMF3;

namespace ServiceLibrary.Imagem
{
   [RemotingService("Comentário da classe")]
   public class JpegEncoder
   {
       public void Salvar(ByteArray byteArray)
       {
           // Transfere de ByteArray para MemoryStream
           uint length = byteArray.Length;
           byte[] bytes = new byte[length];
           byteArray.ReadBytes(bytes, 0, length);
           MemoryStream stream = new MemoryStream(bytes);

           // Cria a imagem
           Image image = Bitmap.FromStream(stream);

           // Salva a imagem
           image.Save(
           HttpContext.Current.Server.MapPath("_upload/JpegEncoder.jpg"));

           // Libera o espaço na memória
           stream.Dispose();
           image.Dispose();
       }
   }
}

Este código deve ser inserido dentro da ServiceLibrary que você criou para a sua Solution.

Repare na classe ByteArray, é uma implementação do ByteArray do Flash no servidor, provida pelo FluorineFx.AMF3.

Sua Solution Explorer deve se parecer um pouco com esta:

Deixei selecionadas as referências para System.Drawing e Web propositalmente, pois são referências necessárias para o funcionamento da nossa classe.

Agora vamos a parte do envio do ByteArray criado no Flash apartir da JPEGEncoder para a nossa classe la no FluorineFx, a ServiceLibrary.Imagem.JpegEncoder. No exemplo anexo ao post (mais ao fim do post tem um link para download) utilizei uma classe que criei a pouco tempo para trabalhar com AMF no geral (br.com.bsoares.net.Amf), ela vai tomar muito espaço no post, portanto vou demonstrar somente a utilização dela:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* Função de callback para JpegEncoder.Salvar
* @param resposta
*/

private function baixarSusseco (resposta : Object) : void {
   UtilNet.navegar(Config.URL_UPLOAD + "JpegEncoder.jpg", "_blank");
}

/**
* Função de callback para JpegEncoder.Salvar
* @param resposta
*/

private function baixarErro (resposta : Object) : void {
   trace("--&amp;lt; ERRO &amp;gt;----------------------");
   for each (var o : Object in resposta) {
       trace(o);
   }
   trace("--------------------------------");
}

/**
* Retorna o ByteArray gerado pela JPEGEncoder
* @return Byte Array
*/

private function obterByteArray () : ByteArray {
   var bmpData : BitmapData = new BitmapData(_flvPlayer.msk.width, _flvPlayer.msk.height);
   bmpData.draw(_alvoCaptura);
   var objJPEGEncoder : JPEGEncoder = new JPEGEncoder(_qualidade.value);
   var dadosEncode : ByteArray = objJPEGEncoder.encode(bmpData);
   return dadosEncode;
}

/**
* Chama o método Salvar da Classe JpegEncoder utilizando o FluorineFx.
* Neste exemplo, criei uma classe chamada AMF para tratar a comunicação
* com o FluorineFx (serve também para AMFPHP)
* @param Evento
*/

private function onBaixarClick (e : MouseEvent) : void {
   var objAmf : Amf = new Amf();
   objAmf.URL = Config.URL_AMF_GATEWAY;
   objAmf.onSusseco = baixarSusseco;
   objAmf.onErro = baixarErro;
   objAmf.executar("ServiceLibrary.Imagem.JpegEncoder.Salvar", obterByteArray());
}

Pelo comentário de cada método já podemos perceber como funciona o envio do Array de bytes.

Dicas:
• Neste projeto utilizei o FlashDevelop, que na minha opinião é um ótimo editor de Action Script.
• Observe a estrutura de classes, para quem ainda não entende muito de POO (Programação Orientada a Objetos) este post é um bom exemplo de organização de Packages e Nomenclatura de Variáveis, Classes e Métodos. Vou tentar em um próximo post escrever mais sobre Programação Orientada a Objetos (POO) de uma forma mais avançada abrangendo Encapsulamento, Herança e Polimorfismo, o qual não é o objetivo deste post).

Observações de Configuração:
Observe na imagem abaixo o projeto aberto no FlashDevelop:

Clique com o botão direito do mouse sobre JPEG_Encoder (AS3), vá em Properties, repare que na área Project Classpaths deve ser adicionado os dois diretórios (_Biblioteca_ e Classes).

Conclusão:
Vimos como é possível o Flash gerar uma imagem (JPEG) utilizando a classe JPEGEncoder, como enviamos os binários do JPEG para o ASP.NET utilizando o FluorineFx e como o FluorineFx salva os binários em disco.

Por hoje é isso pessoal.
Vocês podem fazer o download do projeto aqui.

Links relacionados:
• FluorineFx: http://www.fluorinefx.com/
• FluorineFx download: http://www.fluorinefx.com/download.html
• FluorineFx Visual Studio: http://www.fluorinefx.com/docs/fluorine/vswizardnet20.html
• Live JPEG Encoder: http://www.bytearray.org/?p=26
• PNG Encoder in AS3: http://www.kaourantin.net/2005/10/png-encoder-in-as3.html
• FlashDevelop: http://osflash.org/flashdevelop

Fluorine, uma alternativa de Flash Remoting com ASP.NET

Posted in ASP.NET, ActionScript 3.0, Flash, Remoting on fevereiro 2nd, 2008 by Bruno Soares – 9 Comments

Nos tempos em que o Flash ainda era da Macromedia, foi criada a especificação AMF (ActionScript Message Format), para facilitar a comunicação entre o ActionScript e as demais linguagens Server-side (Gateway, mais a diante vamos ver que o que controla a comunicação entre o ActionScript e o ASP.NET é com.TheSilentGroup.Fluorine.FluorineGateway). Temos hoje várias linguagem com alguma biblioteca que implemente AMF como por exemplo o PHP, Java, ColdFusion e também o ASP.NET. Existe uma alternativa de remoting muito boa para quem trabalha com PHP que é o AMFPHP, para quem o conhece será fácil entender o funcionamento do Fluorine. E para quem não o conhece tenho certeza de que vão encontrar muito material sobre ele na Internet.

Hoje em dia já estamos na versão 3 da AMF e a Fluorine também implementa bibliotecas para uso desta versão de AMF.

Vamos ao tutorial.

Nosso primeiro passo será o download do Fluorine no link a seguir: http://fluorine.thesilentgroup.com/fluorine/download.html
Não vou dar detalhes da instalação porque é sempre a mesma coisa de Next, Next e Finish.

Após realizada a instalação do Fluorine, vamos abrir o Visual Studio e criar uma nova aplicação web File -> New -> Web Site, selecione Fluorine ASP.NET Web Application, como mostrado na figura seguinte:

Sua Solution Explorer deve se parecer com esta imagem:

Abra o arquivo Sample.cs (dentro de App_Code), vamos utiliza-lo em nosso primeiro teste do Fluorine.
Observe que a classe Sample está dentro do namespace www e que possui um método chamado Echo que retorna uma string e precisa de um parâmetro. O método pega a string do parâmetro (text), concatena com “Gateway echo: ” e retorna.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using com.TheSilentGroup.Fluorine;

namespace www
{
    [RemotingService("Fluorine sample service")]
    public class Sample
    {
        public Sample() { }

        public string Echo(string text)
        {
            return "Gateway echo: " + text;
        }
    }
}

Pressione F5 para rodar a aplicação, clique sob o arquivo Console.aspx, note que você foi redirecionado para Fluorine.aspx, o Service Browser do Fluorine, deve se parecer com a imagem seguinte:

Você pode clicar no link “• www.Sample” para ver a especificação dos métodos e também obter um exemplo de código em ActionScript 1 e 2 que já roda os métodos da classes Sample.

Para dizer ao Fluorine que você quer que determinada classe seja acessada por Flash é só você importar “com.TheSilentGroup.Fluorine” (using com.TheSilentGroup.Fluorine;), e colocar o atributo “[RemotingService("Descrição")]” na classe. Fique atento que feito isso, o Fluorine só vai mostrar no Service Browser os métodos publicos da classe, é claro que sem contar o construtor.
Veja como o Service Browser detalha os métodos das classes e já deixa um exemplo de Script para utilizarmos no Flash:

Visto que o Service Browser já gera o código correspondente em ActionScript 1 e 2, vamos deixar o nosso tutorial mais interessante e mostrar uma forma de fazer as devidas chamadas em ActionScript 3.0. Abra o Flash, e cria um arquivo novo, no primeiro frame do Flash insira o código a seguir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import flash.net.*;

function onResult(responds:Object):void {
    trace("onResult: " + responds);
}

function onFault(responds:Object):void {
    trace("fault " + responds.toString());
}

var responder : Responder = new Responder(onResult, onFault);

var gateway : NetConnection = new NetConnection();
gateway.connect("http://localhost:2471/www/Gateway.aspx");
gateway.call("www.Sample.Echo", responder, "Primeiro teste do Fluorine!");

Fique atento quanto ao endereço do Gateway.aspx, pois no nosso exemplo estou utilizando o endereço que o próprio Visual Studio cria quando rodamos a aplicação, por isto que temos esta portar “maluca” (http://localhost:2471).

Depois que você rodar a aplicação no Visual Studio, pegar o endereço gerado por ele, colar o endereço dentro de ‘gateway.connect(“ENDEREÇO“);’ e exportar o flash, você deve receber a mensagem “onResult: Gateway echo: Primeiro teste do Fluorine!”. Se foi exatamente isso que aconteceu, uueebá! Você acabou de fazer o seu primeiro teste de Flash Remoting com ASP.NET utilizando Fluorine!!! Fácil não?

Agora vamos entender cada linha de código desse nosso ActionScript 3.0:
1ª – Importamos todas as classes que nos ajudam a efetuar conexões do Flash com o “Mundo Externo”.
3ª à 9ª – Criamos as funções de callback que vão receber as respostas da nossa requisição.
11ª – Criamos um Responder para armazenar as nossas funções de respostas (onResult e onFault).
13ª – Criamos o nosso gateway, responsável pela comunicação com o Fluorine.
14ª – Conectamos o nosso gateway ao Serviço do Fluorine.
15ª – Efetuamos a chamada para o método Echo da classe Sample que se encontra no namespace www, passamos como segundo parâmetro o Responder (criado na linha 11) e como 3º parâmetro passamos o uma string, pois o método Echo precisa de uma string para funcionar.

É isso ai pessoal, espero ter ajudado aqueles que precisam da combinação Flash + ASP.NET, como foi o meu caso, pois já conhecia o AMFPHP mas no meu trabalho atual utilizamos ASP.NET.

No próximo post vou mostrar como transferir um bitmap do Flash para o Fluorine, agora a coisa vai ficar boa :)

Link para download do projeto:
http://blog.bsoares.com.br/wp-content/uploads/2009/03/20080202_projeto.zip

Links relacionados:
• Fluorine: http://fluorine.thesilentgroup.com/fluorine
• Fluorine download: http://fluorine.thesilentgroup.com/fluorine/download.html
• Flash Remoting: http://www.adobe.com/devnet/flashremoting/
• AMF3: http://osflash.org/documentation/amf3
• AMFPHP: http://www.amfphp.org
• Gateway: http://pt.wikipedia.org/wiki/Gateway