QRコードジェネレーター・QRコード作成するアプリを作りたい

QRコードリーダーを作る前に、ジェネレーターを作りたい

今日びQRコードなんてそこら中にあるわけだけど、せっかくなら自分で作ったQRコードを読んでみたい。なのでまず入力した文字列をQRコードに変換するようなアプリを作りたい。Howtoではないです。やった後に見つけたんだけど、こっち

https://qiita.com/hoshiume11/items/0f496fe80df84875c132

https://github.com/journeyapps/zxing-android-embedded

のがいいと思いました。くそぅ。

QRコードの仕様を見て実装する

のは無理、死ぬ。なので出来合いのもの、ライブラリを探す。最初からやりたい人は、こういう

https://www.qrcode.com/about/standards.html

http://www.swetake.com/qrcode/qr1.html

のを見ながら作っていけばよいみたいです。すごいよなぁ、こういう人たち。

QR-Code-generatorライブラリを使いたい

使いたい。Githubを検索してたら、

https://github.com/nayuki/QR-Code-generator

https://www.nayuki.io/page/qr-code-generator-library

こちらを見つけた。Javaってなってるし、いけんだろと思った。この時は。

Gradleに追記する

implementation 'io.nayuki:qrcodegen:1.4.0'

mavencentralでjarが公開されてたので、こう書いてSyncしたら自動でやってくれた。mavenでの書き方が分からんかったけど、versionクリックして右の方にgradleでの書き方が載ってて助かった。

参考

https://search.maven.org/artifact/io.nayuki/qrcodegen/1.4.0/jar

https://teratail.com/questions/30337

サンプルコードを書いてみる→失敗

あっるぇー?いつもどおりAlt+Enterを押しまくったのだけど、赤いのが消えてくれない。なんでやろーと思ったら、AndroidではBufferedImageは使えないみたい。なんということでしょう。あれ?ていうことはこのライブラリ利用できないの?じゃあどうしてるの?と、ちょっこし調べてみると、先駆者はBufferedImageを書き換えてBitmapというものを利用しているようだ。似たようなもんだろうし、俺にもできんだろーと思ったのが沼の始まりであった。

参考

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10147763825

https://stackoverflow.com/questions/33210065/how-can-i-import-java-awt-image-bufferedimage-in-android-studio

http://pinmarch.sakura.ne.jp/blog/archives/2010/12/01_001698.php

https://web.archive.org/web/20160708194253/http://d.hatena.ne.jp/g1rolamo/20101015/1287101742

QR-Code-generatorライブラリをAndroid用に書き換えたい

BufferedImageで受けてるんだから、ライブラリの中にBufferedImageを使ってる部分があるんだろーなーと思い、中を見てみることに。

QR-Code-generatorの中を見る

左上をProjectにして、ExternalLibraries→Gradle:io.nayuki:qrcodegen:1.4.0@jar→qrcodegen-1.4.0.jar→io.nayuki.qrcodegenと進んでいき、中に入ってるclassファイルを開いてみると…、赤いとこあった!ここをAndroidのBitmapにしてけばいいんだろーなーと、書き換えようと思ったら

File is read-only

File is not Writable

の表示ががが…。あ、まじでこれできないやつだ、と思ったのだけれど、ソース公開されてんのに書き換えできないってどういうことだよと思ったら、自分で生のjavaファイルからjarを作成すればいけるっぽい。なんかどんどんやること増えてないか。ぐるぐる調べているうちに、このライブラリを速くしたぜ!ってなライブラリを見つけた。それもJavaオンリーやで。

https://github.com/nayuki/Fast-QR-Code-generator

https://www.nayuki.io/page/fast-qr-code-generator-library

どうせなので、こっちを書き換えてみたい。みたいって、できるのかな。

参考

https://ja.wikipedia.org/wiki/JAR_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)

http://sakebook.hatenablog.com/entry/2014/08/17/004540

java.awt.BufferedImageをandroid.graphics.Bitmapに置き換えたい

BufferedImageの方が何をやっているのかを見てきながら、Bitmapにそれっぽいのがないか探していく。

public BufferedImage toImage(int scale, int border) {
   if (scale <= 0 || border < 0)
      throw new IllegalArgumentException("Value out of range");
   if (border > Integer.MAX_VALUE / 2 || size + border * 2L > Integer.MAX_VALUE / scale)
      throw new IllegalArgumentException("Scale or border too large");
   
   BufferedImage result = new BufferedImage((size + border * 2) * scale, (size + border * 2) * scale, BufferedImage.TYPE_INT_RGB);
   for (int y = 0; y < result.getHeight(); y++) {
      for (int x = 0; x < result.getWidth(); x++) {
         boolean color = getModule(x / scale - border, y / scale - border);
         result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF);
      }
   }
   return result;
}

1行目

BufferedImageは、まぁ赤い。戻り値をBitmapにしたいから、ここはBitmapにする。

2-5行目

scaleとborderってなんだろうと思ったら、scaleは各セル(黒い点・白い点)を何個のピクセルで構成するかみたい。例えばscale=1だと、1セル=1ピクセルだし、scale=2だと、1セル=4ピクセルになる。じゃあborderはというと、外側の余白部分をどれくらい取るか、という値みたい。で、この行ではそれぞれについて、オーバーだったり足りなかったりしたときにエラーになるようにしてるようだ。ここは変えなくてもよさそう。多分。

7行目

BufferedImageのコンストラクタを読んでみる。引数が3つだから多分これだろう。縦横とカラースペースを指定してるのかな。Androidの方でいうと、BitmapFactoryかな?使ったことあるし。て、思ったんだけど違うみたい。これはリソースから読み込む用で、無から作り出すにはcreateBitmapというメソッドがあるようだ。引数3つでそれっぽいやつといったらこれかなぁと思ったので、書いてみる。

Bitmap result = Bitmap.createBitmap((size + border * 2) * scale, (size + border * 2) * scale, Bitmap.Config.ARGB_8888);

x,yはそのままで、問題はBitmap.Configなのだけど、元のTYPE_INT_RGBはアルファ無しの8ビットRGBのようでBitmap.Configにはそんなものは無い。アルファ付きで近いやつがARGB_8888なので、これでいってみよう。アルファっつーのは透明度のことらしい。

8-9行目

getHeight,getWidthはBitmapの方にもあった(getHeight,getWidth)。戻り値もintだし大丈夫だと思う。この行で全ピクセルを走査してるんだろうなぁ。多分。

11行目

setRGBは引数3つだから多分これだ。x,y座標のピクセルに第3引数のRGB値の色を設定しているんだろう。この書き方がよく分かんなくてググりまくった。3項演算子というらしい。Bitmapの方に無いぞ…、あった。setPixelって名前なのね。書いてみる。

result.setPixel(x, y, color ? Color.argb(255, 0, 0, 0) : Color.argb(255, 255, 255, 255));

Bitmapの方に

The color must be a non-premultiplied ARGB value in the sRGB color space.

とあり、BufferedImageの方は6桁16進数なのだけど、アルファをプラスして8桁16進数にしなきゃみたい。ただ書き方分かんねぇなと思ってたらColorにこう書け、という記述があったので、こうしてみた。0x表記のほうが速いのかな。まぁそうだよな、いちいち計算するより結果が書いてあったほうが早そうだもんな。いやでもそこらへんコンパイラが最適化とかしてくんないかな。してくれることを祈ろう。

書き換え後

import android.graphics.Bitmap;
import android.graphics.Color;
public Bitmap toImage(int scale, int border) {
    if (scale <= 0 || border < 0)
        throw new IllegalArgumentException("Value out of range");
    if (border > Integer.MAX_VALUE / 2 || size + border * 2L > Integer.MAX_VALUE / scale)
        throw new IllegalArgumentException("Scale or border too large");

    Bitmap result = Bitmap.createBitmap((size + border * 2) * scale, (size + border * 2) * scale, Bitmap.Config.ARGB_8888);
    for (int y = 0; y < result.getHeight(); y++) {
        for (int x = 0; x < result.getWidth(); x++) {
            boolean color = getModule(x / scale - border, y / scale - border);
            result.setPixel(x, y, color ? Color.argb(255, 0, 0, 0) : Color.argb(255, 255, 255, 255));
        }
    }
    return result;
}

不思議な点

これでなんでQRコードの画像の白黒ができるんだろ?と疑問が湧いてきて、10行目のgetModuleが怪しいと感じて見に行ったら

public boolean getModule(int x, int y) {
   return 0 <= x && x < size && 0 <= y && y < size && modules[y][x];
}

こんな感じだった。多分最後のmodules[y][x]に白黒どっちにするかのデータが入ってるのかな…。とにかく一応書き換えられたので、これでいってみよう。jarを作るのです。

書き換えたjavaファイルを元にjarファイルを作りたい→無理

書き換え終わったのでjarファイルを作りたい。流れとしてはjavaファイルをclassファイルにして、それをjarファイルにまとめるって感じみたい。コマンドプロンプトでやればいいみたい。意気揚々と

javac -encoding UTF-8 *.java

とやってみると、

QrCode.java:26: エラー: パッケージandroid.graphicsは存在しません
import android.graphics.Bitmap;
^
QrCode.java:27: エラー: パッケージandroid.graphicsは存在しません
import android.graphics.Color;
^
QrCode.java:294: エラー: シンボルを見つけられません
public Bitmap toImage(int scale, int border) {
^
シンボル: クラス Bitmap
場所: クラス QrCode
QrCode.java:300: エラー: シンボルを見つけられません
Bitmap result = Bitmap.createBitmap((size + border * 2)
* scale, (size + border * 2) * scale, Bitmap.Config.ARGB_8888);
^
シンボル: クラス Bitmap
場所: クラス QrCode
QrCode.java:300: エラー: パッケージBitmapは存在しません
Bitmap result = Bitmap.createBitmap((size + border * 2)
* scale, (size + border * 2) * scale, Bitmap.Config.ARGB_8888);

^
QrCode.java:300: エラー: シンボルを見つけられません
Bitmap result = Bitmap.createBitmap((size + border * 2)
* scale, (size + border * 2) * scale, Bitmap.Config.ARGB_8888);
^
シンボル: 変数 Bitmap
場所: クラス QrCode
QrCode.java:304: エラー: シンボルを見つけられません
result.setPixel(x, y, color ? Color.argb(255, 0,
0, 0) : Color.argb(255, 255, 255, 255));
^
シンボル: 変数 Color
場所: クラス QrCode
QrCode.java:304: エラー: シンボルを見つけられません
result.setPixel(x, y, color ? Color.argb(255, 0,
0, 0) : Color.argb(255, 255, 255, 255));

^
シンボル: 変数 Color
場所: クラス QrCode
エラー8個

なん…だと…。どうもandroid.graphics.Bitmapとandroid.graphics.Colorが存在してないからコンパイルできないみたい。classファイルができないまま、

jar -cvf fastqrcodegen.jar *.java
implementation files('libs/fastqrcodegen.jar')

とかやっても ちゃんとできない。おいマジか、ここまできて無理なのかーいとがっかりしたところ、Android用のライブラリとして書けばいいみたいな感じの記事を発見した。ただ全部ソースをコピペだ。

参考

https://www.sejuku.net/blog/64490

https://qiita.com/hiwm0126/items/1b3c008c21463d78337a

https://qiita.com/HachiwareWorks/items/2b23b180d328df597fe4

書き換えたjavaファイルを元にAndroidライブラリを作りたい

ここを参考に新しくModuleを作り、そこのjavaフォルダ内に元のjavaファイルと同じ名前のJavaClassを作り内容をコピペしていく。果たしてそんなことが許されるのか、ライセンス的にどうなんだという思いを置き去りに、作業を進めていった。

こうなりました。QrCodeGeneratorDemo.javaとpackage-info.javaは無くても大丈夫そうだったので、コピペしなかった。あとQrSegmentAdvanced.javaも、Call requires API level…が出てしまい、赤字の消し方が分からんかったのでコピペしなかった。最後にGradleに

implementation project(path: ':fastqrcodegen')

と追記してSyncすると使えるようになった。ただ、なんか、罪悪感があるんだけど、それはもうしょうがないよね。他のやり方がわからんし…。

Fast-QR-Code-generatorを試してみる

適当にImageViewを設置し、onCreateに

imageView = findViewById(R.id.imageView);

QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM);
Bitmap img = qr0.toImage(4, 10);

imageView.setImageBitmap(img);

結果がこちら

で、できた…、できたぞー!!感慨もひとしおである。ただ、アライメントがなーいのね。あら、そうなの。そうなのね。そっかー。ついてた。早とちり。データ量多くなると付くっぽい。ごめんなさい。

参考

https://www.javadrive.jp/android/imageview/index2.html

ZXing Android Embeddedを使ってみる

いやー、これ見つけた時はショックだったね。今までの俺の苦労はなんだったんだ、つって。

implementation 'com.journeyapps:zxing-android-embedded:3.6.0'

をgradleに追記。

<application android:hardwareAccelerated="true">

をAndroidManifest.xmlに追記。生成の場合はいらんようだ。

try {
    imageView = findViewById(R.id.imageView);
    BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
    Bitmap bitmap = barcodeEncoder.encodeBitmap("Hello, world!", BarcodeFormat.QR_CODE, 400, 400);
    imageView.setImageBitmap(bitmap);
} catch (WriterException e) {
    e.printStackTrace();
}

Githubのサンプルほぼまんま。

はいっ、できましたー。アライメントもついてまーす。上のにも付いてた。

所感

もっとちゃんとググる。

さぁ次はリーダーを作るぞー!

カテゴリー: したい タグ: , , , パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です