// forked from RobotHacker's QR Code sample from http://www.libspark.org/svn/as3/QRCodeReader/trunk/sample/ReadQrCodeSample.as /************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.GradientType; import flash.display.SimpleButton; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.filters.BlurFilter; import flash.filters.DropShadowFilter; import flash.geom.Matrix; import flash.geom.Point; import flash.media.Camera; import flash.media.Video; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.utils.Timer; /** * QRコード解析クラスの使用例です * @author Kenichi UENO */ public class ReadQrCodeSample extends Sprite { private const SRC_SIZE:int = 320; private const STAGE_SIZE:int = 350; private var getQRimage:GetQRimage; private var qrDecode:QRdecode = new QRdecode(); private var errorView:Sprite; private var errorText:TextField = new TextField(); private var startView:Sprite; private var cameraView:Sprite; private var camera:Camera; private var video:Video = new Video(SRC_SIZE, SRC_SIZE); private var freezeImage:Bitmap; private var blue:Sprite = new Sprite(); private var red:Sprite = new Sprite(); private var blurFilter:BlurFilter = new BlurFilter(); private var resultView:Sprite; private var textArea:TextField = new TextField(); private var cameraTimer:Timer = new Timer(2000); private var textArray:Array = ["", "", ""]; /** * コンストラクタ */ public function ReadQrCodeSample():void { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; errorView = buildErrorView(); cameraTimer.addEventListener(TimerEvent.TIMER, getCamera); cameraTimer.start(); getCamera(); } /** * カメラの接続をチェックします */ private function getCamera(e:TimerEvent = null):void{ camera = Camera.getCamera(); this.graphics.clear(); if ( camera == null ) { this.addChild( errorView ); } else { cameraTimer.stop(); if ( errorView.parent == this ) { this.removeChild(errorView); } start(); } } /** * スタートボタンを表示 */ private function start():void { startView = buildStartView(); this.addChild( startView ); startView.addEventListener(MouseEvent.CLICK, onStart); } /** * 画像解析クラスにカメラ画像を渡し、解析完了イベントを監視します */ private function onStart(e:MouseEvent):void { cameraView = buildCameraView(); resultView = buildResultView(); this.addChild( cameraView ); this.addChild( resultView ); this.removeChild( startView ); resultView.visible = false; getQRimage = new GetQRimage(video); getQRimage.addEventListener(QRreaderEvent.QR_IMAGE_READ_COMPLETE, onQrImageReadComplete); qrDecode.addEventListener(QRdecoderEvent.QR_DECODE_COMPLETE, onQrDecodeComplete); redTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onRedTimer ); this.addEventListener(Event.ENTER_FRAME, onEnterFrame); } /** * エラー画面を作成 */ private function buildErrorView():Sprite { var sprite:Sprite = new Sprite(); errorText.autoSize = TextFieldAutoSize.LEFT; errorText.text = "no camera detected."; errorText.x = 0.5 * (STAGE_SIZE - errorText.width); errorText.y = 0.5 * (STAGE_SIZE - errorText.height); errorText.border = true; errorText.background = true; sprite.graphics.lineStyle(0); sprite.graphics.drawPath(Vector.<int>([1, 2, 2, 2, 2, 2, 1, 2]), Vector.<Number>([5, 5, STAGE_SIZE-5, 5, STAGE_SIZE-5, STAGE_SIZE-5, 5, STAGE_SIZE-5, 5, 5, STAGE_SIZE-5, STAGE_SIZE-5, 5, STAGE_SIZE-5, STAGE_SIZE-5, 5])); sprite.addChild(errorText); return sprite; } /** * 開始ボタンを作成 */ private function buildStartView():Sprite { var sprite:Sprite = new Sprite(); sprite.graphics.beginGradientFill(GradientType.LINEAR, [0xCCCCCC, 0xAAAAAA], [1.0, 1.0], [0, 255], new Matrix(0, 0.03, -0.03, 0, 0, 15)); sprite.graphics.lineStyle(2); sprite.graphics.drawRoundRect(0, 0, 200, 30, 5); var btnText:TextField = new TextField(); btnText.autoSize = TextFieldAutoSize.LEFT; btnText.text = "Click here to start!"; btnText.setTextFormat(new TextFormat(null, 16, null, true)); btnText.selectable = false; btnText.x = 0.5 * (sprite.width - btnText.width); btnText.y = 0.5 * (sprite.height - btnText.height); sprite.addChild(btnText); sprite.mouseChildren = false; sprite.buttonMode = true; sprite.x = 0.5 * (STAGE_SIZE - sprite.width); sprite.y = 0.5 * (STAGE_SIZE - sprite.height); return sprite; } /** * カメラの表示部分を作成 */ private function buildCameraView():Sprite { camera.setQuality(0, 100); camera.setMode(SRC_SIZE, SRC_SIZE, 24, true ); video.attachCamera( camera ); var sprite:Sprite = new Sprite(); sprite.graphics.beginGradientFill(GradientType.LINEAR, [0xCCCCCC, 0x999999], [1.0, 1.0], [0, 255], new Matrix(0, 0.1, -0.1, 0, 0, 150)); sprite.graphics.drawRoundRect(0, 0, SRC_SIZE+30, SRC_SIZE+30, 20); var videoHolder:Sprite = new Sprite(); videoHolder.addChild( video ); videoHolder.x = videoHolder.y = 15; freezeImage = new Bitmap(new BitmapData(SRC_SIZE, SRC_SIZE)); videoHolder.addChild( freezeImage ); freezeImage.visible = false; red.graphics.lineStyle(2, 0xFF0000); red.graphics.drawPath(Vector.<int>([1,2,2,1,2,2,1,2,2,1,2,2]), Vector.<Number>([30,60,30,30,60,30,290,60,290,30,260,30,30,260,30,290,60,290,290,260,290,290,260,290])); blue.graphics.lineStyle(2, 0x0000FF); blue.graphics.drawPath(Vector.<int>([1,2,2,1,2,2,1,2,2,1,2,2]), Vector.<Number>([30,60,30,30,60,30,290,60,290,30,260,30,30,260,30,290,60,290,290,260,290,290,260,290])); sprite.addChild( videoHolder ); sprite.addChild( red ); sprite.addChild( blue ); blue.alpha = 0; red.x = red.y = 15; blue.x = blue.y = 15; return sprite; } /** * 結果表示用Sprite作成 */ private function buildResultView():Sprite { var sprite:Sprite = new Sprite(); sprite.graphics.beginGradientFill(GradientType.LINEAR, [0xDDDDEE, 0xBBBBCC], [0.9, 0.9], [0, 255], new Matrix(0, 0.1, -0.1, 0, 0, 150)); sprite.graphics.drawRoundRect(0, 0, 280, 280, 20); sprite.addChild( textArea ); textArea.width = 250; textArea.height = 200; textArea.wordWrap = true; textArea.multiline = true; textArea.border = true; textArea.background = true; textArea.backgroundColor = 0xFFFFFF; textArea.x = textArea.y = 15; var btnText:TextField = new TextField(); btnText.autoSize = TextFieldAutoSize.LEFT; btnText.text = "CLOSE"; btnText.selectable = false; var btnSprite:Sprite = new Sprite(); btnSprite.addChild(btnText); btnSprite.graphics.lineStyle(1); btnSprite.graphics.beginGradientFill(GradientType.LINEAR, [0xEEEEEE, 0xCCCCCC], [0.9, 0.9], [0, 255], new Matrix(0, 0.01, -0.01, 0, 0, 10)); btnSprite.graphics.drawRoundRect(0, 0, 80, 20, 8); btnText.x = 0.5 * (btnSprite.width - btnText.width); btnText.y = 0.5 * (btnSprite.height - btnText.height); btnSprite.x = 0.5 * ( 280 - 80 ); btnSprite.y = 240; btnSprite.buttonMode = true; btnSprite.mouseChildren = false; btnSprite.addEventListener(MouseEvent.CLICK, onClose); sprite.addChild( btnSprite ); sprite.addChild( textArea ); sprite.x = sprite.y = 35; sprite.filters = [new DropShadowFilter(4.0,45,0,0.875)]; return sprite; } /** * 解析を毎フレーム行う */ private function onEnterFrame(e: Event):void{ if( camera.currentFPS > 0 ){ getQRimage.process(); } } /** * QRコードを発見したらデコーダーに渡す */ private function onQrImageReadComplete(e: QRreaderEvent):void{ qrDecode.setQR(e.data); // QRreaderEvent.data: QRコード配列 qrDecode.startDecode(); // デコード開始 } /** * デコードが完了したら結果テキストを表示する */ private function onQrDecodeComplete(e: QRdecoderEvent):void { blue.alpha = 1.0; redTimer.reset(); redTimer.start(); textArray.shift(); textArray.push( e.data ); // QRdecoderEvent.data: 解析文字列 if ( textArray[0] == textArray[1] && textArray[1] == textArray[2] ) { textArea.htmlText = e.data; cameraView.filters = [blurFilter]; redTimer.stop(); freezeImage.bitmapData.draw(video); freezeImage.visible = true; this.removeEventListener(Event.ENTER_FRAME, onEnterFrame); resultView.visible = true; } } /** * 結果を削除 */ private function onClose(e: MouseEvent):void { textArray = ["", "", ""]; freezeImage.visible = false; redTimer.start(); this.addEventListener(Event.ENTER_FRAME, onEnterFrame); cameraView.filters = []; resultView.visible = false; } private var redTimer:Timer = new Timer(400, 1); /** * ガイドの色を戻す */ private function onRedTimer(e:TimerEvent):void { blue.alpha = 0; } } } //足りない定義をコピペした // > svn co http://www.libspark.org/svn/as3/QRCodeReader/trunk/src/com/logosware/ // > cd logosware // > ruby -ape "$_.gsub!(/^package.*$/, '/* \\0 */') or $_.gsub!(/^(\s*)public class/, '\\1/* public */ internal class') or $_.gsub!(/^(\s*)(import com.logosware.*)$/, '\\1/* \\2 */')" event\*.as utils\*.as utils\QRcode\*.as > hoge.txt /************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.event */ { import flash.events.Event; /** * QRコードのデコード完了イベントを送出します */ /* public */ internal class QRdecoderEvent extends Event { // 定数( Class constants ) /** * デコード完了を通知します。 * @eventType QR_DECODE_COMPLETE **/ public static const QR_DECODE_COMPLETE:String = "QR_DECODE_COMPLETE"; // プロパティ( Proerties ) /** * 解析した結果の文字列が格納されます **/ public var data:String; /** * 解析に用いたコード配列が格納されます **/ public var checkArray:Array; // コンストラクタ( Constructor ) /** * コンストラクタ * @param type イベントタイプ * @param data 抽出文字列 * @param check 入力したコード **/ public function QRdecoderEvent(type:String, data:String, check:Array){ super(type); // 新しいプロパティを設定する this.data = data; this.checkArray = check; } // Eventからオーバーライドしたメソッド( Overridden Method: Event ) /** * @private **/ override public function clone():Event { return new QRdecoderEvent(type, data, checkArray); } } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.event */ { import flash.display.BitmapData; import flash.events.Event; /** * QRコード画像の抽出完了イベントを送出します */ /* public */ internal class QRreaderEvent extends Event { // 定数( Class constants ) /** * QRコードの抽出完了を通知します。 * @eventType QR_IMAGE_READ_COMPLETE **/ public static const QR_IMAGE_READ_COMPLETE:String = "QR_IMAGE_READ_COMPLETE"; // プロパティ( Proerties ) /** * 解析した結果のQRコード画像が格納されます **/ public var imageData:BitmapData; /** * 解析した結果のQRコード画像のビットパターン配列が格納されます **/ public var data:Array; /** * 入力した任意の配列が格納されます(デバッグ用) **/ public var checkArray:Array; // コンストラクタ( Constructor ) /** * コンストラクタ * @param type イベントタイプ * @param imageData 抽出したQRコード画像 * @param data 抽出したQRコードのビット列 * @param check 入力した任意のコード **/ public function QRreaderEvent(type:String, imageData:BitmapData, data:Array, checkArray:Array = null){ super(type); // 新しいプロパティを設定する this.imageData = imageData; this.data = data; this.checkArray = checkArray; } // Eventからオーバーライドしたメソッド( Overridden Method: Event ) /** * @private **/ override public function clone():Event { return new QRreaderEvent(type, imageData, data, checkArray); } } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils */ { import flash.display.BitmapData; import flash.geom.Point; import flash.geom.Rectangle; /** * ラベリングを行うクラスです */ /* public */ internal class LabelingClass { private var _bmp:BitmapData; private var _minSize:uint; private var _startColor:uint; private var _pickedRects:Array = []; private var _pickedColor:Array = []; /** * コンストラクタ * @param bmp 入力画像(0x0, 0xFFFFFFFFでニ値化されたもの) * @param minSize 画素として認める最低サイズ(ノイズ対策) * @param startColor 塗り開始色 * @param isChangeOriginal 入力画像を実際に塗るかどうか **/ public function Labeling(bmp:BitmapData, minSize:uint = 10, startColor:uint = 0xFFFFFFFE, isChangeOriginal:Boolean = true):void{ _minSize = minSize; _startColor = startColor; if( isChangeOriginal ){ _bmp = bmp; } else { _bmp = bmp.clone(); } _process(); } /** * ラベリングした結果得られた範囲の矩形情報を返します * @return 矩形の配列 **/ public function getRects():Array{ return _pickedRects; } /** * ラベリングした結果得られた範囲を塗った色情報を返します * @return 色の配列 **/ public function getColors():Array{ return _pickedColor; } /** * コア関数 **/ private function _process():void { var _fillColor:uint = _startColor; var _rect:Rectangle; while (_paintNextLabel( _bmp, 0xFF000000, _fillColor ) ){ _rect = _bmp.getColorBoundsRect( 0xFFFFFFFF, _fillColor ); if ( ( _rect.width > _minSize) && ( _rect.height > _minSize ) ) { var _tempRect:Rectangle = _rect.clone(); _pickedRects.push( _tempRect ); _pickedColor.push( _fillColor ); } _fillColor --; } } /** * 次のpickcolor色の領域をfillcolor色に塗る。pickcolorが見つからなければfalseを返す * @param bmp 画像 * @param pickcolor 次の色 * @param fillcolor 塗る色 * @return 目的の色があったかどうか **/ private function _paintNextLabel( bmp:BitmapData, pickcolor:uint, fillcolor:uint ):Boolean { var rect:Rectangle = bmp.getColorBoundsRect( 0xFFFFFFFF, pickcolor ); if( (rect.width > 0) && (rect.height> 0) ){ var tempBmp:BitmapData = new BitmapData( rect.width, 1 ); tempBmp.copyPixels( bmp, new Rectangle(rect.topLeft.x, rect.topLeft.y, rect.width, 1 ), new Point(0, 0) ); var rect2:Rectangle = tempBmp.getColorBoundsRect( 0xFFFFFFFF, pickcolor ); bmp.floodFill( rect2.topLeft.x + rect.topLeft.x, rect2.topLeft.y + rect.topLeft.y, fillcolor ); return true; } return false; } } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils.QRcode */ { /* import com.logosware.utils.QRcode.GFstatic; */ /** * GF(2^4)を扱うためのクラス **/ /* public */ internal class G4Num { private var _vector:uint; private var _power:int; /** * コンストラクタ * @param power 指数 **/ public function G4Num(power:int) { setPower( power ); } /** * 指数を指定する * @param power 指数 **/ public function setPower( power:int ):void { _power = power; if ( _power < 0 ) { _vector = 0; } else { _power %= 15; _vector = GFstatic._power2vector_4[_power]; } } /** * 整数値を指定する * @param vector 整数値 **/ public function setVector( vector:uint ):void { _vector = vector; _power = GFstatic._vector2power_4[_vector]; } /** * 整数値を取得する * @param 整数値 **/ public function getVector():uint { return _vector; } /** * 指数を取得する * @param 指数 **/ public function getPower():int { return _power; } /** * 足し算を行う。整数値同士のxorを取る。 * @param other 足す対象となるG4Numインスタンス * @param 計算結果 **/ public function plus( other:G4Num ):G4Num { var newVector:uint = _vector ^ other.getVector(); return new G4Num( GFstatic._vector2power_4[ newVector ] ); } /** * 乗算を行う。指数同士の足し算を行う。 * @param other かける対象となるG4Numインスタンス * @param 計算結果 **/ public function multiply( other:G4Num ):G4Num { if ( (_power == -1) || (other.getPower() == -1 ) ) { return new G4Num( -1 ); } else { return new G4Num( _power + other.getPower() ); } } } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils.QRcode */ { /* import com.logosware.utils.QRcode.GFstatic; */ /** * GF(2^8)を扱うためのクラス **/ /* public */ internal class G8Num { private var _vector:uint; private var _power:int; /** * コンストラクタ * @param power 指数 **/ public function G8Num(power:int) { setPower( power ); } /** * 指数を指定する * @param power 指数 **/ public function setPower( power:int ):void { _power = power; if ( _power < 0 ) { _vector = 0; } else { _power %= 255; _vector = GFstatic._power2vector_8[_power]; } } /** * 整数値を指定する * @param vector 整数値 **/ public function setVector( vector:uint ):void { _vector = vector; _power = GFstatic._vector2power_8[_vector]; } /** * 整数値を取得する * @param 整数値 **/ public function getVector():uint { return _vector; } /** * 指数を取得する * @param 指数 **/ public function getPower():int { return _power; } /** * 足し算を行う。整数値同士のxorを取る。 * @param other 足す対象となるG4Numインスタンス * @param 計算結果 **/ public function plus( other:G8Num ):G8Num { var newVector:uint = _vector ^ other.getVector(); return new G8Num( GFstatic._vector2power_8[ newVector ] ); } /** * 乗算を行う。指数同士の足し算を行う。 * @param other かける対象となるG4Numインスタンス * @param 計算結果 **/ public function multiply( other:G8Num ):G8Num { if ( (_power < 0) || (other.getPower() < 0 ) ) { return new G8Num( -1 ); } else { return new G8Num( _power + other.getPower() ); } } /** * 逆数を計算して得る。元のインスタンスは変化しない。 * @param 計算結果 **/ public function inverse():G8Num { return new G8Num( 255 - getPower() ); } } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils.QRcode */ { /* import com.logosware.event.QRreaderEvent; */ /* import com.logosware.utils.LabelingClass; */ import flash.display.*; import flash.events.EventDispatcher; import flash.filters.ColorMatrixFilter; import flash.filters.ConvolutionFilter; import flash.geom.*; import flash.utils.ByteArray; /** * 主にカメラ画像からQRコードを切り出すためのクラスです. * 画像上にあるVersion 1~10のQRコードを0,1からなる2次元配列に整形して返します * @author Kenichi UENO **/ /* public */ internal class GetQRimage extends EventDispatcher { private var _wid:uint = 320; private var _hgt:uint = 240; private var _minVersion:uint = 1; // サポートする最低バージョン private var _maxVersion:uint = 10; // サポートする最高バージョン private var _imageSource:DisplayObject = new Sprite(); private var _resultImage:BitmapData = new BitmapData(1, 1); private var _resultArray:Array = []; private var _results:Array = [ _resultImage, _resultArray ]; private const _origin:Point = new Point(0, 0); private var detecter:QRCodeDetecter; /** * コンストラクタ. * @param tempMC QRコード描画元のSpriteインスタンス **/ public function GetQRimage(source:DisplayObject) { _imageSource = source; detecter = new QRCodeDetecter(_imageSource); } /** * 読み取りを実行します * @eventType QRreaderEvent.QR_IMAGE_READ_COMPLETE */ public function process():void { var QRCodes:Array = detecter.detect(); for ( var i:int = 0; i < QRCodes.length; i++ ) { var bmpData:BitmapData = QRCodes[i].image; var colors:Array = QRCodes[i].borderColors; // バージョンの取得 var qrInfo:Object = _getVersion( bmpData, colors[0], colors[1], colors[2] ); if ( qrInfo.version > 0 ) { // グリッドの結果を取得 _results = _getGrid( bmpData, qrInfo ); _resultImage = _results[0]; _resultArray = _results[1]; // グリッド中でもマーカー確認 var checkBmp:BitmapData = new BitmapData( _resultImage.width, _resultImage.height ); checkBmp.applyFilter(_resultImage,_resultImage.rect,_origin,new ConvolutionFilter(7, 7, [1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,0,1,1,1,0,1,1,0,1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1],33)); if ( (checkBmp.getPixel(7, 7) == 0) && (checkBmp.getPixel(checkBmp.width - 8, 7) == 0) && (checkBmp.getPixel(7, checkBmp.height - 8) == 0) ) { dispatchEvent( new QRreaderEvent( QRreaderEvent.QR_IMAGE_READ_COMPLETE, _resultImage, _resultArray ) ); } else { } } } // process終了 } /** * QRコードのビット情報を二次元配列化する * @param bmpData 画像 * @param qrInfo QRコード情報オブジェクト * @param ビットパターン配列 */ private function _getGrid( bmpData:BitmapData, qrInfo:Object ):Array { var __resultBmp:BitmapData = new BitmapData( 8 + qrInfo.version * 4 + 17, 8 + qrInfo.version * 4 + 17 ); var __resultArray:Array = new Array( qrInfo.version * 4 + 17 ); var __i:uint; var __thisColor:uint; var __tlCenter:Object = { x:qrInfo.topLeftRect.topLeft.x + 0.5 * ( qrInfo.topLeftRect.width ), y:qrInfo.topLeftRect.topLeft.y + 0.5 * ( qrInfo.topLeftRect.height ) }; var __trCenter:Object = { x:qrInfo.topRightRect.topLeft.x + 0.5 * ( qrInfo.topRightRect.width ), y:qrInfo.topRightRect.topLeft.y + 0.5 * ( qrInfo.topRightRect.height ) }; var __blCenter:Object = { x:qrInfo.bottomLeftRect.topLeft.x + 0.5 * ( qrInfo.bottomLeftRect.width ), y:qrInfo.bottomLeftRect.topLeft.y + 0.5 * ( qrInfo.bottomLeftRect.height ) }; for( __i = 0; __i < (qrInfo.version*4+17); __i++ ){ __resultArray[__i] = new Array( qrInfo.version * 4 + 17 ); } __i = 0; __thisColor = 0; while ( __thisColor != 0xFFFFFF ) { __i++; __thisColor = bmpData.getPixel( qrInfo.topRightRect.topLeft.x + __i, qrInfo.bottomLeftRect.topLeft.y + __i ); } bmpData.floodFill( qrInfo.topRightRect.topLeft.x + __i, qrInfo.bottomLeftRect.topLeft.y + __i, 0xFFCCFFFF ); var __bottomRightRect:Rectangle = bmpData.getColorBoundsRect( 0xFFFFFFFF, 0xFFCCFFFF ); bmpData.floodFill( qrInfo.topRightRect.topLeft.x + __i, qrInfo.bottomLeftRect.topLeft.y + __i, 0xFFFFFFFF ); var __brCenter:Object = { x:__bottomRightRect.topLeft.x + 0.5 * ( __bottomRightRect.width ), y:__bottomRightRect.topLeft.y + 0.5 * ( __bottomRightRect.height ) }; if( qrInfo.version == 1 ){ __brCenter.x = __blCenter.x + (__trCenter.x - __tlCenter.x) * 11.0 / 14.0; __brCenter.y = __trCenter.y + (__blCenter.y - __tlCenter.y) * 11.0 / 14.0; } var __tempNum1:Number = ( qrInfo.version * 4.0 + 17 - 10 ); //QRコード上の、左上マーカー中心から右「下」マーカー中心までのx座標の差 (ver5なら27) var __tempNum2:Number = ( qrInfo.version * 4.0 + 17 - 7 ); //QRコード上の、左上マーカー中心から右「上」マーカー中心までのx座標の差 (ver5なら30) var __blTop:Object = { x: qrInfo.bottomLeftRect.topLeft.x + qrInfo.bottomLeftRect.width*0.5, y: qrInfo.bottomLeftRect.topLeft.y + qrInfo.bottomLeftRect.height/14.0 }; var __trLeft:Object = { x: qrInfo.topRightRect.topLeft.x + qrInfo.topRightRect.width / 14.0, y: qrInfo.topRightRect.topLeft.y + qrInfo.topRightRect.height * 0.5 }; var __sum:Number = 0.0; var __num:Number = 0.0; for ( __i = __blTop.y - qrInfo.cellSize; __i <= __blTop.y + qrInfo.cellSize; __i++ ) { if ( bmpData.getPixel( __blTop.x, __i ) != 0xFFFFFF ) { __sum += __i; __num++; } } __blTop.y = 0.5 * (__blTop.y + __sum / __num); var __a3:Number = ( __tlCenter.y - __trLeft.y) / ( __tlCenter.x - __trLeft.x); var __a30:Number = ( __blTop.y - __brCenter.y) / ( __blTop.x - __brCenter.x); var __b3:Number = __trLeft.y - __a3 * __trLeft.x; var __b30:Number = __brCenter.y - __a30 * __brCenter.x; var __startX3:Number = __tlCenter.x + ( __trLeft.x - __tlCenter.x ) * ( -3.0 / __tempNum1 ); var __startX30:Number = __blTop.x + ( __brCenter.x - __blTop.x ) * ( -3.0 / __tempNum1 ); var __startY3:Number = __tlCenter.y + ( __trLeft.y - __tlCenter.y ) * ( -3.0 / __tempNum1 ); var __startY30:Number = __blTop.y + ( __brCenter.y - __blTop.y ) * ( -3.0 / __tempNum1 ); var __end3:Number = __trLeft.x + ( __trLeft.x - __tlCenter.x ) * ( 6.0 / __tempNum1 ); var __end30:Number = __brCenter.x + ( __brCenter.x - __blTop.x ) * ( 6.0 / __tempNum1 ); var __loopConst:uint = (__resultBmp.width - 8 ); var __loopConst2:uint = __loopConst - 1; var __a:Array = new Array( __loopConst ); var __b:Array = new Array( __loopConst ); var __startX:Array = new Array( __loopConst ); var __startY:Array = new Array( __loopConst ); var __endX:Array = new Array( __loopConst ); for ( __i = 0; __i < __loopConst; __i++ ) { __a[__i] = (( __a30 - __a3 ) / __tempNum1) * ( __i - 3 ) + __a3; __startX[__i] = (( __startX30 - __startX3 ) / __tempNum1) * ( __i - 3 ) + __startX3; __startY[__i] = (( __startY30 - __startY3 ) / __tempNum1) * ( __i - 3 ) + __startY3; __endX[__i] = (( __end30 - __end3 ) / __tempNum1) * ( __i - 3 ) + __end3; __b[__i] = __startY[__i] - __a[__i] * __startX[__i]; } for ( var __y:Number = 0; __y < __loopConst; __y++ ) { var __y2:Number = __y - 3; for ( var __x:Number = 0; __x < __loopConst; __x++ ) { var __x2:Number = __x - 3; if ( (bmpData.getPixel( __startX[__y] + ( __endX[__y] - __startX[__y] ) * ( __x / __loopConst2 ), __a[__y] * (__startX[__y] + ( __endX[__y] - __startX[__y] ) * ( __x / __loopConst2 )) + __b[__y] ) & 0xFF0000) < 0xFF0000) { __resultBmp.setPixel( 4 + __x, 4 + __y, 0 ); __resultArray[__y][__x] = 1; } } } return [__resultBmp, __resultArray]; } /** * QRコードのバージョンを判別する * @param bmp 画像 * @param QRコード情報オブジェクト */ private function _getVersion( bmp:BitmapData, tlColor:uint, trColor:uint, blColor:uint ):Object { var i:uint; var thisColor:uint; bmp.lock(); var topLeftRect:Rectangle = bmp.getColorBoundsRect( 0xFFFFFFFF, tlColor ); var topRightRect:Rectangle = bmp.getColorBoundsRect( 0xFFFFFFFF, trColor ); var bottomLeftRect:Rectangle = bmp.getColorBoundsRect( 0xFFFFFFFF, blColor ); var startTopLeft:Point = new Point( 26, topLeftRect.topLeft.y + topLeftRect.height ); var numX:uint = 0; var tempX:uint; var oldP:uint; var whiteNum:uint = 0; for ( var j:int = -8; j <= 0; j++ ) { tempX = 0; var whiteArray:Array = []; var tempArray:ByteArray = bmp.getPixels( new Rectangle( startTopLeft.x, startTopLeft.y + j, bmp.width - 52, 1 ) ); var startColor:uint = tempArray[1]; var endColor:uint = tempArray[4*(bmp.width-26-1)+1]; if ( ( startColor != 0xFF ) && ( endColor != 0xFF ) ) { oldP = startColor; for ( i = 1; i < (bmp.width - 24); i++ ) { var tempColor:uint = tempArray[4*i+1]; if ( tempColor != oldP ) { tempX ++; oldP = tempColor; } if ( tempColor == 0xFF ) { whiteNum++; } else { if ( whiteNum > 0 ) { whiteArray.push( [whiteNum] ); whiteNum = 0; } } } var sum:Number = 0; // 妥当性のチェック 白いマスが全部同じくらいのサイズだったらOK for ( var k:uint = 0; k < whiteArray.length; k++ ) { sum += Number( whiteArray[k] ); } var average:Number = sum / whiteArray.length; var error:uint = 0; for ( k = 0; k < whiteArray.length; k++ ) { if ( ! ((whiteArray[k] > (average * 0.5)) && (whiteArray[k] < (average * 1.5)) ) ) { error++; } } if ( (numX < tempX) && (error == 0) ) { numX = tempX; } } } numX = Math.floor( ( ( numX - 3 ) - 6 ) * 0.25 ) + 1; startTopLeft = new Point( topLeftRect.topLeft.x + topLeftRect.width, 26 ); var numY:uint = 0; whiteNum = 0; for ( j = -8; j <= 0; j++ ) { tempX = 0; whiteArray = []; tempArray = bmp.getPixels( new Rectangle( startTopLeft.x + j, startTopLeft.y, 1, bmp.height - 52 ) ); startColor = tempArray[1]; endColor = tempArray[4*(bmp.height-26-1)+1]; if ( ( startColor != 0xFF ) && ( endColor != 0xFF ) ) { oldP = startColor; for ( i = 1; i < (bmp.height - 24); i++ ) { tempColor = tempArray[4*i+1]; if ( tempColor != oldP ) { tempX ++; oldP = tempColor; } if ( tempColor == 0xFF ) { whiteNum++; } else { if ( whiteNum > 0 ) { whiteArray.push( [whiteNum] ); whiteNum = 0; } } } sum = 0; // 妥当性のチェック 白いマスが全部同じくらいのサイズだったらOK for ( k = 0; k < whiteArray.length; k++ ) { sum += Number( whiteArray[k] ); } average = sum / whiteArray.length; error = 0; for ( k = 0; k < whiteArray.length; k++ ) { if ( ! ((whiteArray[k] > (average * 0.5)) && (whiteArray[k] < (average * 1.5)) ) ) { error++; } } if ( (numY < tempX) && (error == 0) ) { numY = tempX; } } } numY = Math.floor( ( ( numY - 3 ) - 6 ) * 0.25 ) + 1; if ( (numX == numY) && (numX >= _minVersion) && (numX <= _maxVersion ) ) { // trace("numX"); } else { numX = 0; } bmp.unlock(); return {cellSize:(topRightRect.x + topRightRect.width - topLeftRect.x) / (numX * 4 + 17), version:numX, topLeftRect: topLeftRect, topRightRect:topRightRect, bottomLeftRect: bottomLeftRect}; } /** * 画像中央付近の明るさを使って白と黒の閾値を計算する * @param bmp 画像 * @param 閾値 */ private function _getThreshold( bmp:BitmapData ):uint { var rect:Rectangle = new Rectangle( bmp.width * 0.5, 0, 1, bmp.height ); var bmp_check:BitmapData = new BitmapData( 1, bmp.height ); bmp_check.copyPixels(bmp, rect, new Point(0, 0)); bmp_check.lock(); var tempArray:ByteArray = bmp_check.getPixels( bmp_check.rect ); var sum:Number = 0.0; for ( var i:uint = 0; i < bmp.height; i++ ) { sum += tempArray[4*i+3]; // 緑成分で判定 } sum /= bmp.height; return uint(0xFF000000 + 0x00010101 * Math.round(sum)); } /** * 画像をグレースケール化する * @param bmp_src 元の画像 * @param bmp_dst 結果格納先の画像 * @param rect 適用範囲指定 * @param point 適用原点指定 * @param constnum 明るさ補正 **/ private function _toGray( bmp_src:BitmapData, bmp_dst:BitmapData, rect:Rectangle, point:Point, constnum:Number = 2.5 ):void { var conGray:Array = [constnum*0.3, constnum*0.59, constnum*0.11]; var cmfGray:ColorMatrixFilter = new ColorMatrixFilter( [conGray[0], conGray[1], conGray[2], 0, 0, conGray[0], conGray[1], conGray[2], 0, 0, conGray[0], conGray[1],conGray[2], 0, 0, 0, 0, 0, 0, 255] ); bmp_dst.applyFilter( bmp_src, rect, point, cmfGray ); } /** * 画像を2値化する * @param bmp 2値化する画像 * @param threshold 閾値 **/ private function _binalization( bmp:BitmapData, threshold:uint = 0xFFFFFFFF ):void { bmp.threshold(bmp, bmp.rect, new Point(0, 0), "<", threshold, 0xFF000000, 0xFFFFFFFF ); bmp.threshold(bmp, bmp.rect, new Point(0, 0), ">=", threshold, 0xFFFFFFFF, 0xFFFFFFFF ); } /** * 境界上の点をピックアップする * @param bmp 元画像 * @param rect 対象画像位置 * @param color 対象色 * @param devide 分割個数 * @param 点情報 **/ private function _getBorderPoints(bmp:BitmapData, rect:Rectangle, color:uint, divide:uint):Array { var tempX:uint; var tempY:uint; var tempBmpX:BitmapData = new BitmapData( rect.width, 1); var tempBmpY:BitmapData = new BitmapData( 1, rect.height ); var tempRect2:Rectangle; var tempPoint:Point; var loopCount:uint = divide; var borderPoints:Array = new Array(); for (var j:uint = 0; j <= loopCount; j++ ) { tempX = ( (rect.width-1) * j ) / loopCount + rect.topLeft.x; tempY = ( (rect.height-1) * j ) / loopCount + rect.topLeft.y; tempBmpX.copyPixels( bmp, new Rectangle(rect.topLeft.x, tempY, rect.width, 1), new Point(0,0) ); tempBmpY.copyPixels( bmp, new Rectangle(tempX, rect.topLeft.y, 1, rect.height), new Point(0,0) ); // 横線スキャン tempRect2 = tempBmpY.getColorBoundsRect( 0xFFFFFFFF, color ); tempPoint = new Point( tempX + tempRect2.topLeft.x, rect.topLeft.y + tempRect2.topLeft.y ); borderPoints.push( tempPoint ); tempPoint = new Point( tempX + tempRect2.topLeft.x + tempRect2.width, rect.topLeft.y + tempRect2.topLeft.y + tempRect2.height ); borderPoints.push( tempPoint ); // 縦線スキャン tempRect2 = tempBmpX.getColorBoundsRect( 0xFFFFFFFF, color ); tempPoint = new Point( rect.topLeft.x + tempRect2.topLeft.x, tempY + tempRect2.topLeft.y ); borderPoints.push( tempPoint ); tempPoint = new Point( rect.topLeft.x + tempRect2.topLeft.x + tempRect2.width, tempY + tempRect2.topLeft.y + tempRect2.height ); borderPoints.push( tempPoint ); } return borderPoints; } } } import flash.geom.Point; /************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils.QRcode */ { /** * ガロア体GF(2^w) w=4, 8 の計算に用いる定数 */ /* public */ internal class GFstatic { /** * GF(2^4)計算用定数. * _power2vector_4[指数] = 整数値 のように使います */ public static var _power2vector_4:Array = [ 1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15, 13, 9, 1 ]; /** * GF(2^4)計算用定数. * _power2vector_4[整数値] = 指数 のように使います */ public static var _vector2power_4:Array = [ -1, 0, 1, 4, 2, 8, 5, 10, 3, 14, 9, 7, 6, 13, 11, 12 ]; /** * GF(2^8)計算用定数. * _power2vector_8[指数] = 整数値 のように使います */ public static var _power2vector_8:Array = [ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 ]; /** * GF(2^8)計算用定数. * _power2vector_8[整数] = 指数 のように使います */ public static var _vector2power_8:Array = [ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 ]; } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils.QRcode */ { /* import com.logosware.utils.LabelingClass; */ import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.DisplayObject; import flash.display.Loader; import flash.display.Sprite; import flash.filters.BlurFilter; import flash.filters.ColorMatrixFilter; import flash.filters.ConvolutionFilter; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.getTimer; /** * @author UENO Kenichi */ /* public */ internal class QRCodeDetecter extends Sprite { private var image:DisplayObject; private var bd:BitmapData; private var bd2:BitmapData; private var threshold:uint = 0xFF888888; private var grayConst:Array = [ 0.3, 0.59, 0.11, 0, 0, 0.3, 0.59, 0.11, 0, 0, 0.3, 0.59, 0.11, 0, 0, 0, 0, 0, 0, 255 ]; /** * 画像からQRコードを見つけ出します * @param imageSource */ public function QRCodeDetecter(imageSource:DisplayObject) { image = imageSource; bd = new BitmapData(image.width, image.height, true, 0x0); bd2 = new BitmapData(image.width, image.height, true, 0x0); // debug code /* var bmp:Bitmap = new Bitmap( bd ); image.parent.addChild( bmp ); bmp.x = image.width; */ } /** * 見つかったQRコードの位置情報を返します * @return マーカー配列 * [ * { * image:BitmapData, * borderColors:[ * 0: uint color of topleft marker * 1: uint color of topright marker * 2: uint color of bottomleft marker * ], * originalLocation:[ * 0: Rectangle of topleft marker * 1: Rectangle of topright marker * 2: Rectangle of bottomleft marker * ] * } * ... * ] */ public function detect():Array { var ret:Array = []; bd.lock(); bd.draw(image); // グレー化 bd.applyFilter(bd, bd.rect, new Point(), new ColorMatrixFilter(grayConst)); bd.applyFilter(bd, bd.rect, new Point(), new ConvolutionFilter(5, 5, [ 0, -1, -1, -1, 0, -1, -1, -2, -1, -1, -1, -2, 25, -2, -1, -1, -1, -2, -1, -1, 0, -1, -1, -1, 0 ])); bd.applyFilter(bd, bd.rect, new Point(), new BlurFilter(3, 3)); // 二値化 bd.threshold(bd, bd.rect, new Point(), ">", threshold, 0xFFFFFFFF, 0x0000FF00); bd.threshold(bd, bd.rect, new Point(), "!=", 0xFFFFFFFF, 0xFF000000); // ラベリング var LabelingObj:LabelingClass = new LabelingClass(); LabelingObj.Labeling( bd, 10, 0xFF88FFFE, true ); // ラベリング実行 var pickedRects:Array = LabelingObj.getRects(); var pickedColor:Array = LabelingObj.getColors(); LabelingObj = null; // マーカー候補の矩形を取得 var borders:Array = _searchBorders( bd, pickedRects, pickedColor ); // 直角の位置にあるコードを検索 var codes:Array = _searchCode( borders ); // 適切な角度で切り抜き var images:Array = _clipCodes( bd, codes ); for ( var i:int = 0; i < images.length; i++ ) { ret.push( { image:images[i], borderColors:[codes[i][0].borderColor, codes[i][1].borderColor, codes[i][2].borderColor], originalLocation:[codes[i][0].borderRect, codes[i][1].borderRect, codes[i][2].borderRect] } ); } bd.unlock(); return ret; } private function _clipCodes( bd:BitmapData, codes:Array):Array { var ret:Array = []; for ( var i:int = 0; i < codes.length; i++ ) { var marker1:Rectangle = codes[i][0].borderRect; // top left var marker2:Rectangle = codes[i][1].borderRect; // top right var marker3:Rectangle = codes[i][2].borderRect; // bottom left var vector12:Point = marker2.topLeft.subtract( marker1.topLeft ); // vector: top left -> top right var vector13:Point = marker3.topLeft.subtract( marker1.topLeft ); // vector: top left -> bottom left var theta:Number = -Math.atan2( vector12.y, vector12.x ); // 平面状の回転角 var matrix:Matrix = new Matrix(); var d:Number = (0.5 * marker1.width) / (Math.abs(Math.cos( theta )) + Math.abs(Math.sin( theta ) ) ); // マーカーの一辺の長さの半分 matrix.translate( -(marker1.topLeft.x + marker1.width * 0.5), -(marker1.topLeft.y + marker1.height * 0.5) ); matrix.rotate( theta ); matrix.translate( 20 + d, 20 + d ); var matrix2:Matrix = new Matrix(); matrix2.rotate( theta ); var vector13r:Point = matrix2.transformPoint( vector13 ); matrix2 = new Matrix(1.0, 0, -vector13r.x/vector13r.y, vector12.length / vector13r.y ); matrix.concat( matrix2 ); var len:Number = ( vector12.length + 2 * d ); // QRコードの一辺の長さ var bd2:BitmapData = new BitmapData( 40 + len, 40 + len ); bd2.draw( bd, matrix ); ret.push( bd2 ); } return ret; } /** * マーカーの候補をピックアップする * @param bmp ラベリング済みの画像 * @param rectArray 矩形情報 * @param colorArray 矩形の色情報 * @return 候補の配列 */ private function _searchBorders(bmp:BitmapData, rectArray:Array, colorArray:Array):Array { function isMarker( ary:Array ):Boolean { var c:Number = 0.75; var ave:Number = (ary[0] + ary[1] + ary[2] + ary[3] + ary[4]) / 7; return( ary[0] > ((1.0-c)*ave) && ary[0] < ((1.0+c)*ave) && ary[1] > ((1.0-c)*ave) && ary[1] < ((1.0+c)*ave) && ary[2] > ((3.0-c)*ave) && ary[2] < ((3.0+c)*ave) && ary[3] > ((1.0-c)*ave) && ary[3] < ((1.0+c)*ave) && ary[4] > ((1.0-c) * ave) && ary[4] < ((1.0+c) * ave) ); } var retArray:Array = []; for ( var i:int = 0; i < rectArray.length; i++ ) { var count:int = 0; var target:Number = 0; var tempRect:Rectangle = rectArray[i];// 外側 if( colorArray[i] != bmp.getPixel( rectArray[i].topLeft.x + rectArray[i].width*0.5, rectArray[i].topLeft.y + rectArray[i].height*0.5) ){ var oldFlg:uint = 0; var tempFlg:uint = 0; var index:int = -1; var countArray:Array = [0.0, 0.0, 0.0, 0.0, 0.0]; var j:int; var constNum:Number; // 横方向 constNum = rectArray[i].topLeft.y + rectArray[i].height*0.5; for ( j = 0; j < rectArray[i].width; j++ ){ tempFlg = (bmp.getPixel( rectArray[i].topLeft.x + j, constNum ) == 0xFFFFFF)?0:1; if( (index == -1) && (tempFlg == 0) ){ //go next } else { if( tempFlg != oldFlg ){ index++; oldFlg = tempFlg; if( index >= 5 ){ break; } } countArray[index]++; } } if ( isMarker(countArray) ) { // 縦方向 countArray = [0.0, 0.0, 0.0, 0.0, 0.0]; oldFlg = tempFlg = 0; index = -1; constNum = rectArray[i].topLeft.x + rectArray[i].width*0.5; for ( j = 0; j < rectArray[i].width; j++ ) { tempFlg = (bmp.getPixel( constNum, rectArray[i].topLeft.y + j ) == 0xFFFFFF)?0:1; if( (index == -1) && (tempFlg == 0) ){ //go next } else { if( tempFlg != oldFlg ){ index++; oldFlg = tempFlg; if( index >= 5 ){ break; } } countArray[index]++; } } if ( isMarker(countArray) ) { retArray.push( {borderColor:colorArray[i], borderRect:rectArray[i]} ); } } } } return retArray; } /** * 直角関係にあるマーカーを探します * @param borders 候補の配列 * @return */ private function _searchCode( borders:Array ):Array { function isNear( p1:Point, p2:Point, d:Number ):Boolean { return( (p1.x + d) > p2.x && (p1.x - d) < p2.x && (p1.y + d) > p2.y && (p1.y - d) < p2.y ); } var ret:Array = []; var loop:int = borders.length; for ( var i:int = 0; i < (loop-2); i++ ) { for ( var j:int = i + 1; j < (loop-1); j++ ) { var vec:Point = borders[i].borderRect.topLeft.subtract( borders[j].borderRect.topLeft ); for ( var k:int = j + 1; k < loop; k++ ) { if( isNear( borders[k].borderRect.topLeft, new Point( borders[i].borderRect.topLeft.x + vec.y, borders[i].borderRect.topLeft.y - vec.x ), 0.125 * vec.length )) ret.push( [borders[i], borders[j], borders[k]] ); else if ( isNear( borders[k].borderRect.topLeft, new Point( borders[i].borderRect.topLeft.x - vec.y, borders[i].borderRect.topLeft.y + vec.x ), 0.125 * vec.length )) ret.push( [borders[i], borders[k], borders[j]] ); else if ( isNear( borders[k].borderRect.topLeft, new Point( borders[j].borderRect.topLeft.x + vec.y, borders[j].borderRect.topLeft.y - vec.x ), 0.125 * vec.length )) ret.push( [borders[j], borders[k], borders[i]] ); else if ( isNear( borders[k].borderRect.topLeft, new Point( borders[j].borderRect.topLeft.x - vec.y, borders[j].borderRect.topLeft.y + vec.x ), 0.125 * vec.length )) ret.push( [borders[j], borders[i], borders[k]] ); } } } return ret; } } }/************************************************************************** * LOGOSWARE Class Library. * * Copyright 2009 (c) LOGOSWARE (http://www.logosware.com) All rights reserved. * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * **************************************************************************/ /* package com.logosware.utils.QRcode */ { /* import com.logosware.event.QRdecoderEvent; */ import flash.events.EventDispatcher; import flash.system.System; import flash.text.TextField; import flash.utils.unescapeMultiByte; /** * QRコードをデコードして文字列を抽出するクラスです * @author Kenichi UENO **/ /* public */ internal class QRdecode extends EventDispatcher { private var _xorPattern:Array = [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0]; private var _fixed:Array; private var _qr:Array; // private var _textObj:TextField = new TextField(); private var _qrVersion:uint = 5; public function QRdecode() { } /** * 解析したいQRコードを格納する関数 * @param qr QRコードのビットパターン二次元配列 */ public function setQR( qr:Array ):void { _qr = qr; _qrVersion = (qr.length - 17) * 0.25; } /** * setQRで格納したQRコードのデコードを行う関数 * @param retObj 結果イベントに含ませたいオブジェクト * @eventType QRdecoderEvent.QR_DECODE_COMPLETE */ public function startDecode(retObj:Object = null):void { // 形式情報の読み出し var dataArray:Array; var unmaskedQR:Array; var wordArray:Array; var trueWordArray:Array; var result:Array; var resultFlg:uint; var resultStr:String; var qrSize:uint = _qrVersion * 4 + 17; dataArray = _decode15_5(); // マスク処理の解除 unmaskedQR = _unmask( dataArray ); // 機能領域の計算 _makeFixed(); // データ読み出し wordArray = _getWords( unmaskedQR ); // リードソロモン trueWordArray = _ReedSolomon( wordArray, dataArray ); //データコード語復号 result = _readData( trueWordArray ); resultFlg = result[0]; if ( resultFlg ) { resultStr = result[1]; // _textObj.appendText( "読み取り成功!\n" + resultStr ); dispatchEvent( new QRdecoderEvent( QRdecoderEvent.QR_DECODE_COMPLETE, resultStr, [retObj] ) ); } } /** * リードソロモンで8bitずつ読み取る関数 */ private function _RS8bit( __dataArray:Array, __codeNum:uint, __errorNum:uint, __snum:uint ):void { var __i:uint; var __j:uint; var __index:uint; var __dataLength:uint = __dataArray.length; var __Snum:uint = __errorNum; var __a:Array; // 誤り位置計算用変数 var __e:Array; // 誤り位置 var __S:Array = new Array(__Snum); // シンドローム var __s:Array = new Array(__snum); // 誤り位置変数 var __tempNum1:G8Num; var __tempNum2:G8Num; // シンドローム配列初期化 for ( __j = 0; __j < __Snum; __j++ ) { __S[__j] = new G8Num(-1); } for ( __i = 0; __i < __dataLength; __i++ ) { __tempNum1 = new G8Num(0); __tempNum1.setVector( __dataArray[__dataLength - 1 - __i] ); for ( __j = 0; __j < __Snum; __j++ ) { __S[__j] = __S[__j].plus( __tempNum1.multiply(new G8Num( __i * __j )) ); } } __j = 0; for ( __i = 0; __i < __Snum; __i++ ) { if( __S[__i].getPower() != -1 ){ __j++; } } if( __j == 0 ){ // 100%エラーなし return; } // エラーあるかも for ( __i = __snum; __i > 0; __i-- ){ if( _calcDet( __S, __i ) != 0 ){ break; } } __snum = __i; __a = new Array(__snum); // 誤り訂正位置変数の計算 for (__i = 0; __i < __snum; __i++) { __a[__i] = new Array(__snum+1); for (__j = 0; __j <= __snum; __j++ ) { __a[__i][__j] = new G8Num( __S[__i+__j].getPower() ); } } for ( __i = 0; __i < __snum; __i++ ){ _reduceToLU( __a, __i ); } for (__i = 0; __i < __snum; __i++) { for ( __j = 0; __j < __snum; __j++ ) { if (__a[__i][__j].getPower() != -1) { __s[__snum-1-__j] = __a[__i][__snum]; } } } //__aは再利用 __e = new Array( __snum ); __index = 0; for ( __i = 0; __i < __dataLength; __i++ ) { __tempNum1 = new G8Num( __i * __snum ); for ( __j = __snum - 1; __j >= 1; __j-- ) { __tempNum2 = new G8Num( __i * __j ); __tempNum1 = __tempNum1.plus( __tempNum2.multiply( __s[__snum-1-__j] ) ); } __tempNum1 = __tempNum1.plus( __s[__snum-1] ); if ( __tempNum1.getPower() < 0 ) { __e[__index] = __dataLength - 1 - __i; for( __j = 0; __j < __snum; __j++ ){ __a[__j][__index] = new G8Num(__i * __j); } __a[__index][__snum] = new G8Num( __S[__index].getPower() ); __index++; } } for ( __i = 0; __i < __snum; __i++ ){ _reduceToLU( __a, __i ); } for ( __i = 0; __i < __snum; __i++ ){ for ( __j = 0; __j < __snum; __j++ ){ if( __a[__i][__j].getPower() == 0 ){ __dataArray[__e[__j]] = __dataArray[__e[__j]] ^ (__a[__i][__snum].getVector() ); } } } } /** * 行列式を計算する * @param 行列 * @param 行列サイズ **/ private function _calcDet( __Dat:Array, __size:uint ):int { var __i:uint; var __j:uint; var __k:uint; var __doing:uint = 0; var __result:G8Num = new G8Num(0); var __tempNum:G8Num; var __todo:Array = new Array( __size ); var __temp:Array = new Array( __size ); for( __j = 0; __j < __size; __j++ ){ __todo[__j] = 1; __temp[__j] = new Array( __size ); for ( __i = 0; __i < __size; __i++ ){ __temp[__j][__i] = new G8Num( __Dat[__i+__j].getPower() ); } } //三角行列にする while( __doing < __size ){ // 一列ずつ、つぶす for( __i = 0; __i < __size; __i++ ){ if( __todo[__i] == 1 ){ if( __temp[__i][__doing].getPower() >= 0 ){ __result.multiply( __temp[__i][__doing] ); __tempNum = __temp[__i][__doing].inverse(); //自身の列の頭を1に for( __j = __doing; __j < __size; __j++ ){ __temp[__i][__j] = __temp[__i][__j].multiply( __tempNum ); } //その他の列を全部引き算 for( __k = 0; __k < __size; __k++ ){ if( (__k != __i) && (__todo[__k] == 1) && (__temp[__k][__doing].getPower() >= 0) ){ __tempNum = new G8Num(__temp[__k][__doing].getPower() ); for( __j = __doing; __j < __size; __j++ ){ __temp[__k][__j] = __temp[__k][__j].plus( __tempNum.multiply( __temp[__i][__j] ) ); } } } __todo[__i] = 0; break; } } } if( __i == __size ){ return 0; } __doing++; } return __result.getVector(); } /** * 下三角行列を作る */ private function _reduceToLU(__a:Array, __num:uint ):void { var __i:uint; var __j:uint; var __it:uint; var __flg:uint; var __snum:uint = __a.length; var __tempNum:G8Num; for ( __i = 0; __i < __snum; __i++ ) { __flg = 0; if ( __a[__i][__num].getPower() != -1 ) { __flg = 1; for ( __j = 0; __j < __num; __j++ ) { if ( __a[__i][__j].getPower() != -1 ) { __flg = 0; } } } if ( __flg ) { __it = __i; __i = __snum; } } __tempNum = __a[__it][__num].inverse(); for ( __j = __num; __j <= __snum; __j++ ) { __a[__it][__j] = __a[__it][__j].multiply( __tempNum ); } for ( __i = 0; __i < __snum; __i++ ) { if ( (__i != __it) && (__a[__i][__num].getPower != -1 ) ) { __tempNum = new G8Num( __a[__i][__num].getPower() ); for ( __j = __num; __j <= __snum; __j++ ) { __a[__i][__j] = __a[__i][__j].plus( __a[__it][__j].multiply( __tempNum ) ); } } } } /** * バイナリを文字列に変換する * @param バイナリデータ */ private function _readData ( __dataCode:Array ):Array { var __num2alphabet:Array = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", " ", "$", "%", "*", "+", "-", ".", "/", ":" ]; var __verMode:uint; var __stringBits:Array = [[10,9,8,8],[12,11,16,10],[14,13,16,12]]; var __result:String = ""; var __dataBin:Array; var __i:uint; var __mode:String; var __num:uint; var __tempNum:uint; var __tempNum2:uint; var __tempStr:String; var __isSuccess:uint = 1; if( _qrVersion < 10 ){ __verMode = 0; } else if( _qrVersion < 27 ) { __verMode = 1; } else if( _qrVersion < 41 ) { __verMode = 2; } __dataBin = _Hex2Bin( __dataCode ); //どんどん読み取り while( __dataBin.length > 0 ){ __mode = _readNstr( __dataBin, 4 ); switch( __mode ) { case "0001": // 数字 __num = _readNnumber( __dataBin, __stringBits[__verMode][0] ); // 10: バージョンに依存 for ( __i = 0; __i < __num; __i += 3 ) { if ( (__num - __i) == 2 ) { __tempNum = _readNnumber( __dataBin, 7 ); __result += String("00"+__tempNum).substr(-2,2); } else if( (__num - __i) == 1 ) { __tempNum = _readNnumber( __dataBin, 4 ); __result += String("0"+__tempNum).substr(-1,1); } else { __tempNum = _readNnumber( __dataBin, 10 ); __result += String("000"+__tempNum).substr(-3,3); } } break; case "0010": // 英数字 __num = _readNnumber( __dataBin, __stringBits[__verMode][1] ); // 9: バージョンに依存 for ( __i = 0; __i < __num; __i += 2 ) { if ( (__num - __i) > 1 ) { __tempNum = _readNnumber( __dataBin, 11 ); __result += __num2alphabet[ Math.floor(__tempNum / 45) ] + __num2alphabet[ __tempNum % 45 ]; } else { __tempNum = _readNnumber( __dataBin, 6 ); __result += __num2alphabet[ __tempNum ]; } } break; case "0100": // 8 bit Byte __num = _readNnumber( __dataBin, __stringBits[__verMode][2] ); // 8: バージョンに依存 __tempStr = ""; for ( __i = 0; __i < __num; __i ++ ) { __tempNum = _readNnumber( __dataBin, 8 ); // __result += String.fromCharCode(__tempNum); __tempStr += "%"+_Hex2String(__tempNum); } System.useCodePage = true; __result += unescapeMultiByte( __tempStr ); break; case "1000": // 漢字 __num = _readNnumber( __dataBin, __stringBits[__verMode][3] ); // 8: バージョンに依存 for ( __i = 0; __i < __num; __i ++ ) { __tempNum = _readNnumber( __dataBin, 13 ); __tempNum2 = Math.floor(__tempNum / 0xC0 ); __tempNum2 += ( __tempNum2 <= 0x1E )?0x81:0xC1; __tempNum %= 0xC0; __tempNum += 0x40; System.useCodePage = true; __result += unescapeMultiByte( "%"+_Hex2String(__tempNum2)+"%"+_Hex2String(__tempNum) ); // __result += "("+_Hex2String(__tempNum2)+_Hex2String(__tempNum)+")"; } break; case "0000": case "000": case "00": case "0": __tempNum = _readNnumber( __dataBin, __dataBin.length ); //正常終了 break; default: //未対応 __isSuccess = 0; __result += "***未対応の形式を検出しました。"; continue; break; } } return [__isSuccess, __result]; } /** * 32ビットのデータを文字に直す * @param バイナリデータ */ private function _Hex2String( __hex:uint ):String { var __tempNum:uint = __hex >> 4; var __tempNum2:uint = __hex & 0xF; return String.fromCharCode( __tempNum+48 + uint(__tempNum>9)*7 )+String.fromCharCode( __tempNum2+48 + uint(__tempNum2>9)*7 ); } /** * N文字分の文字列を読み込む * @param バイナリデータ * @param データ長 */ private function _readNstr( __bin:Array, __length:uint ):String { var __i:uint; var __retStr:String = ""; if ( __bin.length < __length ) { __length = __bin.length; } for ( __i = 0; __i < __length; __i++ ) { __retStr += __bin[0]? "1":"0"; __bin.shift(); } return __retStr; } /** * N文字分の数字を読み込む * @param バイナリデータ * @param データ長 */ private function _readNnumber( __bin:Array, __length:uint ):uint { var __i:uint; var __retNum:uint = 0; for ( __i = 0; __i < __length; __i++ ) { __retNum <<= 1; __retNum += __bin[0]; __bin.shift(); } return __retNum; } /** * 16進数の配列を2進数の配列に直す * @param 16進数パターン */ private function _Hex2Bin( __hex:Array ):Array { var __i:uint; var __index:uint; var __loopCount:uint = __hex.length; var __retArray:Array = new Array( __loopCount * 8 ); for ( __i = 0; __i < __loopCount; __i++ ) { __retArray[__index++] = (__hex[__i]>>7) & 1; __retArray[__index++] = (__hex[__i]>>6) & 1; __retArray[__index++] = (__hex[__i]>>5) & 1; __retArray[__index++] = (__hex[__i]>>4) & 1; __retArray[__index++] = (__hex[__i]>>3) & 1; __retArray[__index++] = (__hex[__i]>>2) & 1; __retArray[__index++] = (__hex[__i]>>1) & 1; __retArray[__index++] = (__hex[__i]>>0) & 1; } return __retArray; } /** * リードソロモン解析 * @param 解析データ * @param 形式情報 */ private function _ReedSolomon( __data:Array, __type:Array ):Array { var __RSblock:Array; var __retArray:Array = []; var __dataNum:Array; var __errorNum:Array; var __i:uint; var __loopCount:uint; var __j:uint; var __loopCount2:uint; var __index:uint = 0; var __correctNum:uint = 0; switch( _qrVersion ) { case 1: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(1); __dataNum = [16]; __errorNum = [10]; break; case 1: // L __RSblock = new Array(1); __dataNum = [19]; __errorNum = [7]; break; case 2: // H __RSblock = new Array(1); __dataNum = [9]; __errorNum = [17]; break; case 3: // Q __RSblock = new Array(1); __dataNum = [13]; __errorNum = [13]; break; } break; case 2: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(1); __dataNum = [28]; __errorNum = [16]; break; case 1: // L __RSblock = new Array(1); __dataNum = [34]; __errorNum = [10]; break; case 2: // H __RSblock = new Array(1); __dataNum = [16]; __errorNum = [28]; break; case 3: // Q __RSblock = new Array(1); __dataNum = [22]; __errorNum = [22]; break; } break; case 3: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(1); __dataNum = [44]; __errorNum = [26]; break; case 1: // L __RSblock = new Array(1); __dataNum = [55]; __errorNum = [15]; break; case 2: // H __RSblock = new Array(2); __dataNum = [13,13]; __errorNum = [22,22]; break; case 3: // Q __RSblock = new Array(2); __dataNum = [17,17]; __errorNum = [18,18]; break; } break; case 4: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(2); __dataNum = [32,32]; __errorNum = [18,18]; break; case 1: // L __RSblock = new Array(1); __dataNum = [80]; __errorNum = [20]; break; case 2: // H __RSblock = new Array(4); __dataNum = [9,9,9,9]; __errorNum = [16,16,16,16]; break; case 3: // Q __RSblock = new Array(2); __dataNum = [24,24]; __errorNum = [26,26]; break; } break; case 5: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(2); __dataNum = [43, 43]; __errorNum = [24, 24]; break; case 1: // L __RSblock = new Array(1); __dataNum = [108]; __errorNum = [26]; break; case 2: // H __RSblock = new Array(4); __dataNum = [11, 11, 12, 12]; __errorNum = [22, 22, 22, 22]; break; case 3: // Q __RSblock = new Array(4); __dataNum = [15, 15, 16, 16]; __errorNum = [18, 18,18,18]; break; } break; case 6: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(4); __dataNum = [27, 27, 27, 27]; __errorNum = [16, 16, 16, 16]; break; case 1: // L __RSblock = new Array(2); __dataNum = [68, 68]; __errorNum = [18, 18]; break; case 2: // H __RSblock = new Array(4); __dataNum = [15, 15, 15, 15]; __errorNum = [28, 28, 28, 28]; break; case 3: // Q __RSblock = new Array(4); __dataNum = [19, 19, 19, 19]; __errorNum = [24, 24, 24, 24]; break; } break; case 7: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(4); __dataNum = [31, 31, 31, 31]; __errorNum = [18, 18, 18, 18]; break; case 1: // L __RSblock = new Array(2); __dataNum = [78, 78]; __errorNum = [20, 20]; break; case 2: // H __RSblock = new Array(5); __dataNum = [13,13,13,13,14]; __errorNum = [26,26,26,26,26]; break; case 3: // Q __RSblock = new Array(6); __dataNum = [14,14,15,15,15,15]; __errorNum = [18,18,18,18,18,18]; break; } break; case 8: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(4); __dataNum = [38, 38, 39, 39]; __errorNum = [22, 22, 22, 22]; break; case 1: // L __RSblock = new Array(2); __dataNum = [97, 97]; __errorNum = [24, 24]; break; case 2: // H __RSblock = new Array(6); __dataNum = [14, 14, 14, 14, 15, 15]; __errorNum = [26, 26, 26, 26, 26, 26]; break; case 3: // Q __RSblock = new Array(6); __dataNum = [18, 18, 18, 18, 19, 19]; __errorNum = [22, 22, 22, 22, 22, 22]; break; } break; case 9: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(5); __dataNum = [36, 36, 36, 37, 37]; __errorNum = [22, 22, 22, 22, 22]; break; case 1: // L __RSblock = new Array(2); __dataNum = [116, 116]; __errorNum = [30, 30]; break; case 2: // H __RSblock = new Array(8); __dataNum = [12, 12, 12, 12, 13, 13, 13, 13]; __errorNum = [24, 24, 24, 24, 24, 24, 24, 24]; break; case 3: // Q __RSblock = new Array(8); __dataNum = [16, 16, 16, 16, 17, 17, 17, 17]; __errorNum = [20, 20, 20, 20, 20, 20, 20, 20]; break; } break; case 10: switch( (__type[0] << 1) + (__type[1] << 0) ) { case 0: // M __RSblock = new Array(5); __dataNum = [43, 43, 43, 43, 44]; __errorNum = [26, 26, 26, 26, 26]; break; case 1: // L __RSblock = new Array(4); __dataNum = [68, 68, 69, 69]; __errorNum = [18, 18, 18, 18]; break; case 2: // H __RSblock = new Array(8); __dataNum = [15, 15, 15, 15, 15, 15, 16, 16]; __errorNum = [28, 28, 28, 28, 28, 28, 28, 28]; break; case 3: // Q __RSblock = new Array(8); __dataNum = [19, 19, 19, 19, 19, 19, 20, 20]; __errorNum = [24, 24, 24, 24, 24, 24, 24, 24]; break; } break; default: //trace( _qrVersion + ", " + (__type[0] << 1) + (__type[1] << 0) ); return []; break } __loopCount = __RSblock.length; for ( __i = 0; __i < __loopCount; __i++) { __RSblock[__i] = new Array(); } __loopCount2 = __dataNum[__loopCount-1]; for ( __j = 0; __j < __loopCount2; __j++) { for ( __i = 0; __i < __loopCount; __i++) { // ここに条件をいれないといけない気がする。 j < datanum とか if( __j < __dataNum[__i] ){ __RSblock[__i].push( [ _readByteData(__data[__index++]) ] ); } } } __correctNum = __errorNum[0] * 0.5; if( _qrVersion == 1 ){ switch( (__type[0] << 1) + (__type[1] << 0) ){ case 0: __correctNum = 4; break; case 1: __correctNum = 2; break; case 2: __correctNum = 8; break; case 3: __correctNum = 6; break; } } if( (_qrVersion == 2) && ( ( (__type[0] << 1) + (__type[1] << 0) ) == 1 ) ){ __correctNum = 4; } if( (_qrVersion == 3) && ( ( (__type[0] << 1) + (__type[1] << 0) ) == 1 ) ){ __correctNum = 7; } __loopCount2 = __errorNum[0]; // 全部同じなので[0] for ( __j = 0; __j < __loopCount2; __j++) { for ( __i = 0; __i < __loopCount; __i++) { __RSblock[__i].push( [ _readByteData(__data[__index++]) ] ); } } //誤り訂正 for ( __i = 0; __i < __loopCount; __i++ ) { _RS8bit( __RSblock[__i], __dataNum[__i], __errorNum[__i], __correctNum ); } //データ再配置 for ( __i = 0; __i < __loopCount; __i++) { __loopCount2 = __dataNum[__i]; for ( __j = 0; __j < __loopCount2; __j++) { __retArray.push(__RSblock[__i][__j]); } } return __retArray; } /** * 1バイト分の情報を読み込む * @param 情報ビット列 */ private function _readByteData( __byte:Array ):uint { return (__byte[0] << 7) + (__byte[1] << 6) + (__byte[2] << 5) + (__byte[3] << 4) + (__byte[4] << 3) + (__byte[5] << 2) + (__byte[6] << 1) + (__byte[7] << 0) ; } /** * 情報のバイト列を読み取る * @param QRコードビットパターン */ private function _getWords( __qr:Array ):Array { var __checkArray:Array = []; var __cordNum:Array = [ 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706 ]; // バージョン1~40までの総コード語数 var __retArray:Array = new Array( __cordNum[_qrVersion - 1] ); var __i:uint; // var __j:uint; var __loopCount:uint = __retArray.length; var __x:uint = _qrVersion*4 + 16; var __y:uint = _qrVersion*4 + 16; var __len:uint; var __toUp:uint = 1; var __toLeft:uint = 1; var __index:uint = 0; for ( __i = 0; __i < __loopCount; __i++ ) { __retArray[__i] = new Array(8); __checkArray[__i] = new Array(8); // for( __j = 0; __j < 8; __j++ ){ // __checkArray[__i][__j] = new Array(2); // } } for ( __i = 0; __i < __loopCount; __i++ ) { for ( __len = 0; __len < 8; __len++ ) { while ( _isFixed(__x, __y) ) { if ( __x == 6 ){ __x--; } if ( __toLeft ) { __x--; __toLeft = 0; } else { __toLeft = 1; if ( __toUp ) { if ( __y == 0 ) { __x--; __toUp = 0; } else { __x++; __y--; } } else { if ( __y == (_qrVersion*4+16) ) { __x--; __toUp = 1; } else { __x++; __y++; } } } } _fixed[__y][__x] = 1; __retArray[__index][__len] = __qr[__y][__x]; __checkArray[__index][__len] = [__x, __y]; } __index++; } return __retArray; } /** * QRコードのマスクを解除する関数 * @param 形式情報 */ private function _unmask( __typeData:Array ):Array { var __qrSize:uint = _qrVersion * 4 + 17; var __retArray:Array = new Array(__qrSize); var __i:uint; var __j:uint; for ( __j = 0; __j < __qrSize; __j++ ) { __retArray[__j] = new Array(__qrSize); } switch( (__typeData[2]<<2)+(__typeData[3]<<1)+(__typeData[4]) ) { case 0: //(i+j)mod2==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( (__i + __j) % 2) == 0 ); } } break; case 1: // i mod2==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( __i % 2) == 0 ); } } break; case 2: //j mod3==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( __j % 3) == 0 ); } } break; case 3: //(i+j)mod3==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( (__i + __j) % 3) == 0 ); } } break; case 4: //((idiv2)+(jdiv3))mod2==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( (Math.floor(__i*0.5) + Math.floor(__j/3.0)) % 2) == 0 ); } } break; case 5: //((ij)mod2+(ij)mod3)==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( ( (__i*__j) % 2 ) + ((__i*__j) % 3 ) ) == 0 ); } } break; case 6: //((ij)mod2+(ij)mod3)mod2==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( ( ( (__i*__j) % 2 ) + ((__i*__j) % 3 ) ) % 2 ) == 0 ); } } break; case 7: //((i+j)mod2+(ij)mod3)mod2==0 for ( __j = 0; __j < __qrSize; __j++ ) { for ( __i = 0; __i < __qrSize; __i++ ) { __retArray[__i][__j] = _getQR(__j, __i) ^ int( ( ( ( (__i+__j) % 2 ) + ((__i*__j) % 3 ) ) % 2 ) == 0 ); } } break; } return __retArray; } /** * 機能パターンの範囲をバージョン別に指定する関数 */ private function _makeFixed():void { var __i:int; var __j:int; var __k:int; var __l:int; _fixed = new Array( _qrVersion * 4 + 17 ); for ( __i = 0; __i < (_qrVersion * 4 + 17); __i++) { _fixed[__i] = new Array( _qrVersion * 4 + 17 ); } switch( _qrVersion ) { case 1: for ( __i = 0; __i < 8; __i++) { for ( __j = 0; __j < 8; __j++) { _fixed[__j][__i] = _fixed[__j][_qrVersion*4 + 9 + __i] = _fixed[_qrVersion*4 + 9 + __j][__i] = 1; } } for (__i = 0; __i < 8; __i++) { _fixed[8][__i] = _fixed[__i][8] = _fixed[_qrVersion * 4 + 9+__i][8] = _fixed[8][_qrVersion * 4 + 9+__i] = 1; } _fixed[8][8] = 1; for ( __i = 9; __i < _qrVersion * 4 + 9; __i++ ) { _fixed[6][__i] = _fixed[__i][6] = 1; } break; case 2: case 3: case 4: case 5: case 6: for ( __i = 0; __i < 8; __i++) { for ( __j = 0; __j < 8; __j++) { _fixed[__j][__i] = _fixed[__j][_qrVersion*4 + 9 + __i] = _fixed[_qrVersion*4 + 9 + __j][__i] = 1; } } for (__i = 0; __i < 8; __i++) { _fixed[8][__i] = _fixed[__i][8] = _fixed[_qrVersion * 4 + 9+__i][8] = _fixed[8][_qrVersion * 4 + 9+__i] = 1; } _fixed[8][8] = 1; for ( __i = -2; __i <= 2; __i++ ) { for ( __j = -2; __j <= 2; __j++ ) { _fixed[_qrVersion * 4 + 10 + __j][_qrVersion * 4 + 10 + __i] = 1; } } for ( __i = 9; __i < _qrVersion * 4 + 9; __i++ ) { _fixed[6][__i] = _fixed[__i][6] = 1; } break; case 7: case 8: case 9: case 10: case 12: case 13: for ( __i = 0; __i < 3; __i++) { for ( __j = 0; __j < 6; __j++) { _fixed[__j][_qrVersion*4 + 6 + __i] = _fixed[_qrVersion*4 + 6 + __i][__j] = 1; } } for ( __i = 0; __i < 8; __i++) { for ( __j = 0; __j < 8; __j++) { _fixed[__j][__i] = _fixed[__j][_qrVersion*4 + 9 + __i] = _fixed[_qrVersion*4 + 9 + __j][__i] = 1; } } for (__i = 0; __i < 8; __i++) { _fixed[8][__i] = _fixed[__i][8] = _fixed[_qrVersion * 4 + 9+__i][8] = _fixed[8][_qrVersion * 4 + 9+__i] = 1; } _fixed[8][8] = 1; for( __k = 6; __k <= (_qrVersion*4 + 10); __k += (_qrVersion*2 + 2)){ for( __l = 6; __l <= (_qrVersion*4 + 10); __l += (_qrVersion*2 + 2)){ if( !((__k == 6) && (__l == (_qrVersion*4 + 10))) && !((__l == 6) && (__k == (_qrVersion*4 + 10))) ){ for ( __i = -2; __i <= 2; __i++ ) { for ( __j = -2; __j <= 2; __j++ ) { _fixed[__k + __j][__l + __i] = 1; } } } } } for ( __i = 9; __i < _qrVersion * 4 + 9; __i++ ) { _fixed[6][__i] = _fixed[__i][6] = 1; } break; } } /** * 座標(x,y)のビットパターンを返す関数 */ private function _getQR(x:uint, y:uint):uint { return _qr[y][x]; } /** * 機能パターン情報を返す関数 */ private function _isFixed(x:uint, y:uint):uint { return _fixed[y][x]; } /** * 形式情報をデコードする関数 */ private function _decode15_5():Array { var __str1:Array = new Array(15); var __str2:Array = new Array(15); var __i:uint; var __j:uint; var __S:Array = new Array(5); // シンドローム var __s:Array = new Array(3); // 誤り位置変数 var __tempNum1:G4Num; var __tempNum2:G4Num; var __retArray:Array = new Array(5); // データ部 var __checkPattern:Array = new Array(15); //マスク解除後の形式情報 // シンドローム配列初期化 for ( __j = 0; __j < 5; __j++ ) { __S[__j] = new G4Num(-1); } // 形式情報を取得 for ( __i = 0; __i <= 5; __i++ ) { __str1[__i] = _getQR(8, __i); } __str1[6] = _getQR(8, 7); __str1[7] = _getQR(8, 8); __str1[8] = _getQR(7, 8); for ( __i = 0; __i <= 5; __i++ ) { __str1[__i + 9] = _getQR(5 - __i, 8); } for ( __i = 0; __i <= 7; __i++ ) { __str2[__i] = _getQR(_qrVersion * 4 + 16 - __i, 8); } for ( __i = 0; __i <= 6; __i++ ) { __str2[8+__i] = _getQR(8, _qrVersion * 4 + 10 + __i); } // マスク解除 for ( __i = 0; __i < 15; __i++ ) { __checkPattern[__i] = __str1[14 - __i] ^ _xorPattern[__i]; } for ( __i = 0; __i < 15; __i++ ) { if ( __checkPattern[__i] ) { for ( __j = 0; __j < 5; __j+=2 ) { __S[__j] = __S[__j].plus( new G4Num( __i * (__j + 1) ) ); } } } __S[1] = __S[0].multiply( __S[0] ); __S[3] = __S[1].multiply( __S[1] ); __s[0] = new G4Num( __S[0].getPower() ); __tempNum1 = __S[4].plus( __S[1].multiply( __S[2] ) ); __tempNum2 = __S[2].plus( __S[0].multiply( __S[1] ) ); if ( (__tempNum1.getPower() < 0) || (__tempNum2.getPower() < 0) ) { __s[1] = new G4Num( -1 ); } else { __s[1] = new G4Num( __tempNum1.getPower() - __tempNum2.getPower() + 15 ); } __tempNum1 = __S[1].multiply( __s[0] ); __tempNum2 = __S[0].multiply( __s[1] ); __s[2] = __S[2].plus( __tempNum1.plus( __tempNum2 ) ); for ( __i = 0; __i < 5; __i++ ) { __tempNum1 = new G4Num( (__i) * 3 ); __tempNum2 = new G4Num( (__i) * 2 ); __tempNum1 = __tempNum1.plus( __tempNum2.multiply( __s[0] ) ); __tempNum2 = new G4Num( (__i) ); __tempNum1 = __tempNum1.plus( __tempNum2.multiply( __s[1] ) ); __tempNum1 = __tempNum1.plus( __s[2] ); if ( __tempNum1.getPower() < 0 ) { __retArray[__i] = __checkPattern[__i] ^ 1; } else { __retArray[__i] = __checkPattern[__i]; } } return __retArray; } } } forked from: QR Code sample from http://www.libspark.org/svn/as3/QRCodeReader/trunk/sample/ReadQrCodeSample.as