root/as3/BMPDecoder/src/com/voidelement/images/BMPDecoder.as

リビジョン 243, 16.0 kB (コミッタ: munegon, コミット時期: 4 年 前)

演算子の優先順ミスってたのを修正

  • svn:mime-type 属性の設定値: text/plain;charset=utf8
Line 
1 /**
2  * com.voidelement.images.BMPDecoder  Class for ActionScript 3.0
3  * 
4  * @author       Copyright (c) 2007 munegon
5  * @version      1.0
6  * 
7  * @link         http://www.voidelement.com/
8  * @link         http://void.heteml.jp/blog/
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  * 
14  * http://www.apache.org/licenses/LICENSE-2.0
15  * 
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
19  * either express or implied. See the License for the specific language
20  * governing permissions and limitations under the License.
21  */
22
23
24
25 package com.voidelement.images {
26         import flash.display.BitmapData;
27         import flash.errors.IOError;
28         import flash.utils.ByteArray;
29         import flash.utils.Endian;
30        
31         public class BMPDecoder {
32                 //___________________________________________________________ const
33                
34                 private const BITMAP_HEADER_TYPE:String = "BM";
35                
36                 private const BITMAP_FILE_HEADER_SIZE:int = 14;
37                 private const BITMAP_CORE_HEADER_SIZE:int = 12;
38                 private const BITMAP_INFO_HEADER_SIZE:int = 40;
39                
40                 private const COMP_RGB      :int = 0;
41                 private const COMP_RLE8     :int = 1;
42                 private const COMP_RLE4     :int = 2;
43                 private const COMP_BITFIELDS:int = 3;
44                
45                 private const BIT1 :int = 1;
46                 private const BIT4 :int = 4;
47                 private const BIT8 :int = 8;
48                 private const BIT16:int = 16;
49                 private const BIT24:int = 24;
50                 private const BIT32:int = 32;
51                
52                
53                 //___________________________________________________________ vars
54                
55                 private var bytes:ByteArray;
56                 private var palette:Array;
57                 private var bd:BitmapData;
58        
59                 private var nFileSize:uint;
60                 private var nReserved1:uint;
61                 private var nReserved2:uint;
62                 private var nOffbits:uint;
63                
64                 private var nInfoSize:uint;
65                 private var nWidth:int;
66                 private var nHeight:int;
67                 private var nPlains:uint;
68                 private var nBitsPerPixel:uint;
69                 private var nCompression:uint;
70                 private var nSizeImage:uint;
71                 private var nXPixPerMeter:int;
72                 private var nYPixPerMeter:int;
73                 private var nColorUsed:uint;
74                 private var nColorImportant:uint;
75                
76                 private var nRMask:uint;
77                 private var nGMask:uint;
78                 private var nBMask:uint;
79                 private var nRPos:uint;
80                 private var nGPos:uint;
81                 private var nBPos:uint;
82                 private var nRMax:uint;
83                 private var nGMax:uint;
84                 private var nBMax:uint;
85                
86                
87                 /**
88                  * コンストラクタ
89                  */
90                 public function BMPDecoder() {
91                         nRPos = 0;
92                         nGPos = 0;
93                         nBPos = 0;
94                 }
95                
96                
97                 /**
98                  * デコード
99                  *
100                  * @param デコードしたいBMPファイルのバイナリデータ
101                  */
102                 public function decode( data:ByteArray ):BitmapData {
103                         bytes = data;
104                         bytes.endian = Endian.LITTLE_ENDIAN;
105                         bytes.position = 0;
106                        
107                         readFileHeader();
108                        
109                         nInfoSize = bytes.readUnsignedInt();
110                        
111                         switch ( nInfoSize ) {
112                                 case BITMAP_CORE_HEADER_SIZE:
113                                         readCoreHeader();
114                                         break;
115                                 case BITMAP_INFO_HEADER_SIZE:
116                                         readInfoHeader();
117                                         break;
118                                 default:
119                                         readExtendedInfoHeader();
120                                         break;
121                         }
122                        
123                         bd = new BitmapData( nWidth, nHeight );
124                        
125                         switch ( nBitsPerPixel ){
126                                 case BIT1:
127                                         readColorPalette();
128                                         decode1BitBMP();
129                                         break;
130                                 case BIT4:
131                                         readColorPalette();
132                                         if ( nCompression == COMP_RLE4 ){
133                                                 decode4bitRLE();
134                                         } else {
135                                                 decode4BitBMP();
136                                         }
137                                         break;
138                                 case BIT8:
139                                         readColorPalette();
140                                         if ( nCompression == COMP_RLE8 ){
141                                                 decode8BitRLE();
142                                         } else {
143                                                 decode8BitBMP();
144                                         }
145                                         break;
146                                 case BIT16:
147                                         readBitFields();
148                                         checkColorMask();
149                                         decode16BitBMP();
150                                         break;
151                                 case BIT24:
152                                         decode24BitBMP();
153                                         break;
154                                 case BIT32:
155                                         readBitFields();
156                                         checkColorMask();
157                                         decode32BitBMP();
158                                         break;
159                                 default:
160                                         throw new VerifyError("invalid bits per pixel : " + nBitsPerPixel );
161                         }
162                        
163                         return bd;
164                 }
165                
166                
167                 /**
168                  * BITMAP FILE HEADER 読み込み
169                  */
170                 private function readFileHeader():void {
171                         var fileHeader:ByteArray = new ByteArray();
172                         fileHeader.endian = Endian.LITTLE_ENDIAN;
173                        
174                         try {
175                                 bytes.readBytes( fileHeader, 0, BITMAP_FILE_HEADER_SIZE );
176                                
177                                 if ( fileHeader.readUTFBytes( 2 ) != BITMAP_HEADER_TYPE ){
178                                         throw new VerifyError("invalid bitmap header type");
179                                 }
180                                
181                                 nFileSize  = fileHeader.readUnsignedInt();
182                                 nReserved1 = fileHeader.readUnsignedShort();
183                                 nReserved2 = fileHeader.readUnsignedShort();
184                                 nOffbits   = fileHeader.readUnsignedInt();
185                         } catch ( e:IOError ) {
186                                 throw new VerifyError("invalid file header");
187                         }
188                 }
189                
190                
191                 /**
192                  * BITMAP CORE HEADER 読み込み
193                  */
194                 private function readCoreHeader():void {
195                         var coreHeader:ByteArray = new ByteArray();
196                         coreHeader.endian = Endian.LITTLE_ENDIAN;
197                        
198                         try {
199                                 bytes.readBytes( coreHeader, 0, BITMAP_CORE_HEADER_SIZE - 4 );
200                                
201                                 nWidth  = coreHeader.readShort();
202                                 nHeight = coreHeader.readShort();
203                                 nPlains = coreHeader.readUnsignedShort();
204                                 nBitsPerPixel = coreHeader.readUnsignedShort();
205                         } catch ( e:IOError ) {
206                                 throw new VerifyError("invalid core header");
207                         }
208                 }
209                
210                
211                 /**
212                  * BITMAP INFO HEADER 読み込み
213                  */
214                 private function readInfoHeader():void {
215                         var infoHeader:ByteArray = new ByteArray();
216                         infoHeader.endian = Endian.LITTLE_ENDIAN;
217                        
218                         try {
219                                 bytes.readBytes( infoHeader, 0, BITMAP_INFO_HEADER_SIZE - 4 );
220                                
221                                 nWidth  = infoHeader.readInt();
222                                 nHeight = infoHeader.readInt();
223                                 nPlains = infoHeader.readUnsignedShort();
224                                 nBitsPerPixel = infoHeader.readUnsignedShort();
225                                
226                                 nCompression = infoHeader.readUnsignedInt();
227                                 nSizeImage = infoHeader.readUnsignedInt();
228                                 nXPixPerMeter = infoHeader.readInt();
229                                 nYPixPerMeter = infoHeader.readInt();
230                                 nColorUsed = infoHeader.readUnsignedInt();
231                                 nColorImportant = infoHeader.readUnsignedInt();
232                         } catch ( e:IOError ) {
233                                 throw new VerifyError("invalid info header");
234                         }
235                 }
236                
237                 /**
238                  * 拡張 BITMAP INFO HEADER 読み込み
239                  */
240                 private function readExtendedInfoHeader():void {
241                         var infoHeader:ByteArray = new ByteArray();
242                         infoHeader.endian = Endian.LITTLE_ENDIAN;
243                        
244                         try {
245                                 bytes.readBytes( infoHeader, 0, nInfoSize - 4 );
246                                
247                                 nWidth  = infoHeader.readInt();
248                                 nHeight = infoHeader.readInt();
249                                 nPlains = infoHeader.readUnsignedShort();
250                                 nBitsPerPixel = infoHeader.readUnsignedShort();
251                                
252                                 nCompression = infoHeader.readUnsignedInt();
253                                 nSizeImage = infoHeader.readUnsignedInt();
254                                 nXPixPerMeter = infoHeader.readInt();
255                                 nYPixPerMeter = infoHeader.readInt();
256                                 nColorUsed = infoHeader.readUnsignedInt();
257                                 nColorImportant = infoHeader.readUnsignedInt();
258                                
259                                 if ( infoHeader.bytesAvailable >= 4 ) nRMask = infoHeader.readUnsignedInt();
260                                 if ( infoHeader.bytesAvailable >= 4 ) nGMask = infoHeader.readUnsignedInt();
261                                 if ( infoHeader.bytesAvailable >= 4 ) nBMask = infoHeader.readUnsignedInt();
262                         } catch ( e:IOError ) {
263                                 throw new VerifyError("invalid info header");
264                         }
265                 }
266                
267                
268                 /**
269                  * ビットフィールド読み込み
270                  */
271                 private function readBitFields():void {
272                         if ( nCompression == COMP_RGB ){
273                                 if ( nBitsPerPixel == BIT16 ){
274                                         // RGB555
275                                         nRMask = 0x00007c00;
276                                         nGMask = 0x000003e0;
277                                         nBMask = 0x0000001f;
278                                 } else {
279                                         //RGB888;
280                                         nRMask = 0x00ff0000;
281                                         nGMask = 0x0000ff00;
282                                         nBMask = 0x000000ff;
283                                 }
284                         } else if ( ( nCompression == COMP_BITFIELDS ) && ( nInfoSize < 52 ) ){
285                                 try {
286                                         nRMask = bytes.readUnsignedInt();
287                                         nGMask = bytes.readUnsignedInt();
288                                         nBMask = bytes.readUnsignedInt();
289                                 } catch ( e:IOError ) {
290                                         throw new VerifyError("invalid bit fields");
291                                 }
292                         }
293                 }
294                
295                
296                 /**
297                  * カラーパレット読み込み
298                  */
299                 private function readColorPalette():void {
300                         var i:int;
301                         var len:int = ( nColorUsed > 0 ) ? nColorUsed : Math.pow( 2, nBitsPerPixel );
302                         palette = new Array( len );
303                        
304                         for ( i = 0; i < len; ++i ){
305                                 palette[ i ] = bytes.readUnsignedInt();
306                         }
307                 }
308                
309                
310                 /**
311                  * 1bitのBMPデコード
312                  */
313                 private function decode1BitBMP():void {
314                         var x:int;
315                         var y:int;
316                         var i:int;
317                         var col:int;
318                         var buf:ByteArray = new ByteArray();
319                         var line:int = nWidth / 8;
320                        
321                         if ( line % 4 > 0 ){
322                                 line = ( ( line / 4 | 0 ) + 1 ) * 4;
323                         }
324                        
325                         try {
326                                 for ( y = nHeight - 1; y >= 0; --y ){
327                                         buf.length = 0;
328                                         bytes.readBytes( buf, 0, line );
329                                        
330                                         for ( x = 0; x < nWidth; x += 8 ){
331                                                 col = buf.readUnsignedByte();
332                                                
333                                                 for ( i = 0; i < 8; ++i ){
334                                                         bd.setPixel( x + i, y, palette[ col >> ( 7 - i ) & 0x01 ] );
335                                                 }
336                                         }
337                                 }
338                         } catch ( e:IOError ) {
339                                 throw new VerifyError("invalid image data");
340                         }
341                 }
342                
343                
344                 /**
345                  * 4bitのRLE圧縮BMPデコード
346                  */
347                 private function decode4bitRLE():void {
348                         var x:int;
349                         var y:int;
350                         var i:int;
351                         var n:int;
352                         var col:int;
353                         var data:uint;
354                         var buf:ByteArray = new ByteArray();
355                        
356                         try {
357                                 for ( y = nHeight - 1; y >= 0; --y ){
358                                         buf.length = 0;
359                                        
360                                         while ( bytes.bytesAvailable > 0 ){
361                                                 n = bytes.readUnsignedByte();
362                                                
363                                                 if ( n > 0 ){
364                                                         // エンコードデータ
365                                                         data = bytes.readUnsignedByte();
366                                                         for ( i = 0; i < n/2; ++i ){
367                                                                 buf.writeByte( data );
368                                                         }
369                                                 } else {
370                                                         n = bytes.readUnsignedByte();
371                                                        
372                                                         if ( n > 0 ){
373                                                                 // 絶対モードデータ
374                                                                 bytes.readBytes( buf, buf.length, n/2 );
375                                                                 buf.position += n/2;
376                                                                
377                                                                 if ( n/2 + 1 >> 1 << 1 != n/2 ){
378                                                                         bytes.readUnsignedByte();
379                                                                 }
380                                                         } else {
381                                                                 // EOL
382                                                                 break;
383                                                         }
384                                                 }
385                                         }
386                                        
387                                         buf.position = 0;
388                                        
389                                         for ( x = 0; x < nWidth; x += 2 ){
390                                                 col = buf.readUnsignedByte();
391                                                
392                                                 bd.setPixel( x, y, palette[ col >> 4 ] );
393                                                 bd.setPixel( x + 1, y, palette[ col & 0x0f ] );
394                                         }
395                                 }
396                         } catch ( e:IOError ) {
397                                 throw new VerifyError("invalid image data");
398                         }
399                 }
400                
401                
402                 /**
403                  * 4bitの非圧縮BMPデコード
404                  */
405                 private function decode4BitBMP():void {
406                         var x:int;
407                         var y:int;
408                         var i:int;
409                         var col:int;
410                         var buf:ByteArray = new ByteArray();
411                         var line:int = nWidth / 2;
412                        
413                         if ( line % 4 > 0 ){
414                                 line = ( ( line / 4 | 0 ) + 1 ) * 4;
415                         }
416                        
417                         try {
418                                 for ( y = nHeight - 1; y >= 0; --y ){
419                                         buf.length = 0;
420                                         bytes.readBytes( buf, 0, line );
421                                        
422                                         for ( x = 0; x < nWidth; x += 2 ){
423                                                 col = buf.readUnsignedByte();
424                                                
425                                                 bd.setPixel( x, y, palette[ col >> 4 ] );
426                                                 bd.setPixel( x + 1, y, palette[ col & 0x0f ] );
427                                         }
428                                 }
429                         } catch ( e:IOError ) {
430                                 throw new VerifyError("invalid image data");
431                         }
432                 }
433                
434                
435                 /**
436                  * 8bitのRLE圧縮BMPデコード
437                  */
438                 private function decode8BitRLE():void {
439                         var x:int;
440                         var y:int;
441                         var i:int;
442                         var n:int;
443                         var col:int;
444                         var data:uint;
445                         var buf:ByteArray = new ByteArray();
446                        
447                         try {
448                                 for ( y = nHeight - 1; y >= 0; --y ){
449                                         buf.length = 0;
450                                        
451                                         while ( bytes.bytesAvailable > 0 ){
452                                                 n = bytes.readUnsignedByte();
453                                                
454                                                 if ( n > 0 ){
455                                                         // エンコードデータ
456                                                         data = bytes.readUnsignedByte();
457                                                         for ( i = 0; i < n; ++i ){
458                                                                 buf.writeByte( data );
459                                                         }
460                                                 } else {
461                                                         n = bytes.readUnsignedByte();
462                                                        
463                                                         if ( n > 0 ){
464                                                                 // 絶対モードデータ
465                                                                 bytes.readBytes( buf, buf.length, n );
466                                                                 buf.position += n;
467                                                                 if ( n + 1 >> 1 << 1 != n ){
468                                                                         bytes.readUnsignedByte();
469                                                                 }
470                                                         } else {
471                                                                 // EOL
472                                                                 break;
473                                                         }
474                                                 }
475                                         }
476                                        
477                                         buf.position = 0;
478                                        
479                                         for ( x = 0; x < nWidth; ++x ){
480                                                 bd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );
481                                         }
482                                 }
483                         } catch ( e:IOError ) {
484                                 throw new VerifyError("invalid image data");
485                         }
486                 }
487                
488                 /**
489                  * 8bitの非圧縮BMPデコード
490                  */
491                 private function decode8BitBMP():void {
492                         var x:int;
493                         var y:int;
494                         var i:int;
495                         var col:int;
496                         var buf:ByteArray = new ByteArray();
497                         var line:int = nWidth;
498                        
499                         if ( line % 4 > 0 ){
500                                 line = ( ( line / 4 | 0 ) + 1 ) * 4;
501                         }
502                        
503                         try {
504                                 for ( y = nHeight - 1; y >= 0; --y ){
505                                         buf.length = 0;
506                                         bytes.readBytes( buf, 0, line );
507                                        
508                                         for ( x = 0; x < nWidth; ++x ){
509                                                 bd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );
510                                         }
511                                 }
512                         } catch ( e:IOError ) {
513                                 throw new VerifyError("invalid image data");
514                         }
515                 }
516                
517                 /**
518                  * 16bitのBMPデコード
519                  */
520                 private function decode16BitBMP():void {
521                         var x:int;
522                         var y:int;
523                         var col:int;
524                        
525                         try {
526                                 for ( y = nHeight - 1; y >= 0; --y ){
527                                         for ( x = 0; x < nWidth; ++x ){
528                                                 col = bytes.readUnsignedShort();
529                                                 bd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );
530                                         }
531                                 }
532                         } catch ( e:IOError ) {
533                                 throw new VerifyError("invalid image data");
534                         }
535                 }
536                
537                 /**
538                  * 24bitのBMPデコード
539                  */
540                 private function decode24BitBMP():void {
541                         var x:int;
542                         var y:int;
543                         var col:int;
544                         var buf:ByteArray = new ByteArray();
545                         var line:int = nWidth * 3;
546                        
547                         if ( line % 4 > 0 ){
548                                 line = ( ( line / 4 | 0 ) + 1 ) * 4;
549                         }
550                        
551                         try {
552                                 for ( y = nHeight - 1; y >= 0; --y ){
553                                         buf.length = 0;
554                                         bytes.readBytes( buf, 0, line );
555                                        
556                                         for ( x = 0; x < nWidth; ++x ){
557                                                 bd.setPixel( x, y, buf.readUnsignedByte() + ( buf.readUnsignedByte() << 8 ) + ( buf.readUnsignedByte() << 16 ) );
558                                         }
559                                 }
560                         } catch ( e:IOError ) {
561                                 throw new VerifyError("invalid image data");
562                         }
563                 }
564                
565                 /**
566                  * 32bitのBMPデコード
567                  */
568                 private function decode32BitBMP():void {
569                         var x:int;
570                         var y:int;
571                         var col:int;
572                        
573                         try {
574                                 for ( y = nHeight - 1; y >= 0; --y ){
575                                         for ( x = 0; x < nWidth; ++x ){
576                                                 col = bytes.readUnsignedInt();
577                                                 bd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );
578                                         }
579                                 }
580                         } catch ( e:IOError ) {
581                                 throw new VerifyError("invalid image data");
582                         }
583                 }
584                
585                
586                 /**
587                  * カラーマスクチェック
588                  */
589                 private function checkColorMask():void {
590                         if ( ( nRMask & nGMask ) | ( nGMask & nBMask ) | ( nBMask & nRMask ) ){
591                                 throw new VerifyError("invalid bit fields");
592                         }
593                        
594                         while ( ( ( nRMask >> nRPos ) & 0x00000001 ) == 0 ){
595                                 nRPos++;
596                         }
597                         while ( ( ( nGMask >> nGPos ) & 0x00000001 ) == 0 ){
598                                 nGPos++;
599                         }
600                         while ( ( ( nBMask >> nBPos ) & 0x00000001 ) == 0 ){
601                                 nBPos++;
602                         }
603                        
604                         nRMax = nRMask >> nRPos;
605                         nGMax = nGMask >> nGPos;
606                         nBMax = nBMask >> nBPos;
607                 }
608                
609                
610                 /**
611                  * 情報出力
612                  */
613                 public function traceInfo():void {
614                         trace("---- FILE HEADER ----");
615                         trace("nFileSize: " + nFileSize );
616                         trace("nReserved1: " + nReserved1 );
617                         trace("nReserved2: " + nReserved2 );
618                         trace("nOffbits: " + nOffbits );
619                        
620                         trace("---- INFO HEADER ----");
621                         trace("nWidth: " + nWidth );
622                         trace("nHeight: " + nHeight );
623                         trace("nPlains: " + nPlains );
624                         trace("nBitsPerPixel: " + nBitsPerPixel );
625                        
626                         if ( nInfoSize >= 40 ){
627                                 trace("nCompression: " + nCompression );
628                                 trace("nSizeImage: " + nSizeImage );
629                                 trace("nXPixPerMeter: " + nXPixPerMeter );
630                                 trace("nYPixPerMeter: " + nYPixPerMeter );
631                                 trace("nColorUsed: " + nColorUsed );
632                                 trace("nColorUsed: " + nColorImportant );
633                         }
634                        
635                         if ( nInfoSize >= 52 ){
636                                 trace("nRMask: " + nRMask.toString( 2 ) );
637                                 trace("nGMask: " + nGMask.toString( 2 ) );
638                                 trace("nBMask: " + nBMask.toString( 2 ) );
639                         }
640                 }
641         }
642 }
643
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。