旨辛チキンおいしい

よくある備忘録みたいな奴です。

Android で色差を簡単に求める

色差

色差とは、色の距離のことで、簡単にいうと2つの色がどれくらい近いかということを表します。 しかしこの「どれくらい近いか」というのは、何を基準に考えるかによって答えも変わってきます。

色の距離はある色空間上でのユークリッド距離として定義されることが多いようです1

Lab 色空間

コンピュータのディスプレイは色を表現するのに RGB 色空間を用いていますが、 RGB 色空間上でのユークリッド距離は人間の感じる色差とは大きく異なります。これは、 RGB 表色系が人間の目の視覚特性を考慮していないためです。

そこで人の知覚により合った色空間が必要となり、 国際照明委員会 (CIE) が策定したのが Lab 色空間です。 Lab 色空間は "知覚的均等性を重視しており、L成分値は人間の明度の知覚と極めて近い" とされています2

簡単に言えば、 Lab 色空間上での距離は RGB 色空間での距離よりも人間の視覚特性に一致しているということです。 色差を求めるには、 Lab 色空間上での距離を計算する必要がありそうです。

ColorUtils

Android で Lab 色空間上の距離を計算したかったのですが、軽く調べただけでは Java のライブラリも含め簡単に扱えるものがあまり見つかりませんでした。 Qiita のこちらの記事 はかなり詳しく紹介されていてサンプルコードも掲載されているのですが、ソースコードのライセンスなどが不明なので今回は利用しませんでした。

その後いろいろ調べているうちに、 Android のサポートライブラリに ColorUtils というクラスがあることを発見したのですが、なんと ColorUtils に distanceEuclidean() というメソッドがありました。 このメッソドはその名の通りユークリッド距離を求めるメソッドで、引数に2つの Lab 色空間で表された色を取ります。

また、 ColorUtils には色空間を変換するメソッドも多数用意されており、 ColorUtils.RGBtoLAB()ColorUtils.colorToLAB() メソッドで RGB 色空間で表される色を簡単に Lab 色空間に変換できます。

ColorUtils で色差を求める

ということで、2つの色から色差 (Lab 色空間上でのユークリッド距離) を求めるには次のようにします:

val labArray1 = DoubleArray(3).also {
  ColorUtils.RGBtoLAB(r1, g1, b1, it)
}
val labArray2 = DoubleArray(3).also {
  ColorUtils.RGBtoLAB(r2, g2, b2, it)
}
val distance = ColorUtils.distanceEuclidean(labArray1, labArray2)

または、 0xFF0088FF などの @ColorInt Int を用いて

val labArray1 = DoubleArray(3).also {
  ColorUtils.colorToLAB(colorInt1, it)
}
val labArray2 = DoubleArray(3).also {
  ColorUtils.colorToLAB(colorInt2, it)
}
val distance = ColorUtils.distanceEuclidean(labArray1, labArray2)

ちなみに、 Kotlin では 0xFF0088FFLong 型とみなされるので、明示的に 0xFF0088FF.toInt() のように Int 型に変換する必要があります3

データ層は全てのデータに責任を負う層ではないし、ドメイン層は全てのロジックに責任を負う層ではない

Clean Architecture ではしばしば Presentation 層、 Domain 層、 Data 層の三層にレイヤーが分割されます。

これはそれぞれ表現、ロジック、データに関して責任を負う層なのですが、そのままの意味で捉えると危険です。

とある件で Android Clean Architecture のリポジトリを眺めていると、こんなイシューを見つけました。

AutoLoadImageView breaking the rules? · Issue #64 · android10/Android-CleanArchitecture · GitHub

このイシューでは、 AutoLoadImageView のような、指定した URL から画像をフェッチしてきてキャッシュして画面に表示するようなロジックを持ったコンポーネントを Presentation 層内で完結させるのは、 Clean Architecture のルールを侵害しているのではないか?という疑問が示されています。

この質問者と全く同じ考えかどうかはわかりませんが、自分も以前同じような疑問を抱いていたのを思い出しました。

例えば、ユーザーの情報を画面に表示するアプリがあるとします。 ユーザー情報にはユーザー名と簡単なプロフィール情報の他に、ユーザーの画像が含まれているとします。 ユーザー情報を表示する画面では、 GetUserProfileUseCase のようなユースケースクラスを経由して、 UserProfileRepository から UserProfile エンティティを取得します。 UserProfile エンティティを取得したあとはそこに含まれる情報を画面に描画しますが、ユーザー画像は Picasso を用いてインターネットから取得して、角丸に加工してから画面に表示します。

このとき、 Picasso が Presentation 層の中で URL をもとにインターネットから画像を取得してキャッシュし、それを角丸に加工してから画面に表示するという動作は、Clean Architecture のコアとなるルール、表現に関することは Presentation 層、ビジネスルールやそのロジックに関することは Domain 層、データの入出力に関することは Data 層に分けて記述するというルールを侵害しているのではないか?このルールを侵害しないようにするためには、画像を管理するためのリポジトリと、それを取得したり加工したりするためのユースケースドメイン層に定義し、画像の入出力の実装についてはデータ層で行わなければならないのではないか?という疑問です。

結論から言えば、 AutoLoadImageView や Picasso を用いたこれらの方法は、 Clean Architecture のルールを侵害しません。

先程のイシューでは次のように議論されています1

I think that you should not look at it as "oh, it accesses the disk so it must be in the data layer". The "data" is not about files, bitmaps or objects, but about the entities that your app uses.

雑に意訳すると、 「お、これはディスクにアクセスしているな。これはデータ層に違いない。」と考えるべきではない。「データ」とは、ファイルやビットマップやオブジェクトのことではなく、あなたのアプリが使用するエンティティのことである。 という感じでしょうか。

こんなふうにも書かれています2

You have an issue with the AutoLoadImageView component because you see that it caches bitmaps to disk, and think "this belongs in the data layer" because of that. But the data layer is not about "all stuff that comes from network or disk". But you have to think about data in more of an abstract way. "Data" in this example is "users". From an application perspective, bitmaps are not "data", they are just bytearrays that are only shown by the UI. There is no demand that states that these bytearrays are important to the application and can be used in other parts of the application.

この引用内では AutoLoadImageView で議論されていますが、 Picasso の場合も同じです(イシュー内では Picasso についても触れられています)。

ここで気付かされたのは、ディスクやインターネットにアクセスしているからといって、必ずしもそれがデータ層の関心であるということではないということです。 今回例示したアプリでは画像のキャッシュをローカルのディスクから取得したり、画像をインターネットからダウンロードしたりしていますが、だからといって、すぐにそれがデータ層の関心事だということにはならないということです。

同じ理由で、画像を角丸に加工するというロジックはドメイン層の関心事ではありません。ドメイン層はあくまでアプリケーションが実現したい機能を実現するためのロジックやビジネスルールを記述するところであって、アプリケーションに含まれる全てのロジックに対して責任を持つ層ではないのです。

このアプリがもし画像を加工するアプリなのであれば、画像はそのアプリケーションを実現するために必要不可欠なモデルなので、ドメイン層で定義されその入出力のためのリポジトリとロジックのためのユースケースが存在するべきです。 しかし今回の例のように、ユーザー情報を表示する上でプロフィール画像を画面に表示するという程度の場合、そのプロフィール画像はアプリケーションに必要不可欠なモデルではないでしょう。

したがって、そのような場合は AutoLoadImageView や Picasso が Presentation 層に存在し、それらがファイルの入出力やネットワーク接続、画像の加工などを行っても Clean Architecture のルールを侵害することにはなりません。

まとめ

何でもかんでも、表現に関することだから Presentation 層、ロジックやルールに関することだからドメイン層、ディスクアクセスやネットワークアクセスを行うから Data 層、のように単純に考えず、今作ろうとしているアプリケーションが達成したいことは何か、中心的で必要不可欠なモデル(エンティティ)は何か、ということを常に考え、そのエンティティを中心にユースケースリポジトリを設計していく必要があるなぁと思いました。

何もやる気が起きないときにやる気を出すためにしていること

平日の仕事終わりはすごくやる気に満ち溢れているのに、ご飯作ったりお風呂入ったりしてたら時間がなくなって、まあ週末でいいかと思う。

そして週末になるとなんともやる気が出ず、まあ土曜日は平日の疲れを癒せばいいかと思って日曜日に託す。

しかし日曜日になってもやる気なんて出るはずもなく、だらだらと過ごすうちに夕方に..

ということはみなさん経験されていると思いますが、そういう時に「これではだめだ!」と思い立ってやる気を出すために自分がしていることをメモ的に書いておきます。

来週末の自分に読んでほしい。

(※この記事は以下を実践してなんとかやる気を出した日曜の夜に書いてます)

やる気はやらないと出ない

そもそも、やる気というのは沸き起こってくるものではないらしいです。

やるからやる気がでるという、なんとも逆説的な説明が多く見受けられます。

でも実際そうなんじゃないかと経験的にも思います。やる気が出るのを待っていてもやる気が出たことなんてありません。

何もやる気がない状態から脱する

とはいえ、「何もやる気がない状態」だとなにかをやりはじめることは困難です。

そんな時にこうすればその状態を脱せるという、経験則をメモしておきます。

あくまで自分の場合なので、みなさんの参考になるかはわかりませんが。

部屋を掃除する

部屋が散らかっていたり机の上に物が散乱していると、なんだかなにをやるにも億劫になる気がします。

なのでまず散らかっている状態を片付けます。

ただやる気がない状態でこれをやるのはしんどいので、見えている範囲に散らかっているものをそれなりの丁寧さでタンスや物置に片付けるだけの場合が多いです。

部屋を明るくする

部屋が暗いと眠くなるし、やる気も出ないのでとりあえず部屋を明るくします。

明るさが変えられるシーリングライトなら全灯にして最大限明るくします。

カーテンは開けられるだけ開けて、外光を取り入れます。

部屋を換気する

空気が淀んでいるとぼーっとしてしまうので、窓を開けて、換気扇を回します。

あんまり空気が流れないようなら玄関のドアも半開きにしてとりあえず外気を取り込みます。

着替える、顔を洗う、寝癖を治す

外に出かけられるような状態を作って目を覚まします。だいたい平日の朝にしているようなことをすれば目が覚めてくる気がします。

コーヒーを買いに行く

一番大事。 外に出られる状態になったら、多少歩くくらいのところにコーヒーを買いに行きます。片道5分くらい歩けたほうが目が覚めます。

このとき、ケチらずに欲しいものを買うようにしています。アイスやお菓子を買ったり、多少高いコーヒーを買っても数百円くらいの出費で済むはずなので、やる気が出るのであれば安いもんです。

パソコンを開く

アイスコーヒーなんかを飲みながら帰ると、明るくてキレイで空気の澄んだ部屋になっています。

その状態で机に向かってとりあえずパソコンを開けば、なんとなく起きたときよりはやる気が出ているはずです。

まとめ

こんなに長々と書くつもり無かったのに、ちょっとエモめな自己啓発記事みたいになってしまいました。

とりあえず、身の回りを綺麗にして好きな飲物を買いに行けばそれなりにやる気が出るなーと前から思っていたので、やる気が出なくて困っている人は真似してみてください。

一番いいのは自分がやる気が出るパターンを見つけることだとおもいますが。

はてなブログのシンタックスハイライトの色をカスタマイズするためのツールを作った

TL;DR

作ったものはこれです。

はてなブログシンタックスハイライト CSS ジェネレータ

GitHub で公開しているのでご自由にお使いください。

github.com

動機

はてなブログではコードブロックに対してシンタックスハイライトを適用できますが、 highlight.js や prism.js などの有名なシンタックスハイライターは使用されていないようで、シンタックスハイライトのカラーテーマをカスタマイズするのは簡単ではありません。

Google で検索してみると、みなさんそれぞれ自分の好きなカラーテーマの色を参考に、はてなブログシンタックスハイライトの色を変更する CSS を作成して使用しているようでした (参考サイト参照)。

私は Dracula テーマを愛用しているので、はてなブログシンタックスハイライトにも Dracula を使用したかったのですが、既存のものは見つけられませんでした。 こうなれば自分で CSS を作るしかありません。

しかしいざ CSS をカスタマイズしようとすると、次のような問題にぶちあたりました。

  • どのクラスがどこの色に対応しているのかがよくわからない
  • 作った CSS での見た目を確認するのが面倒くさい
  • リアルタイムで確認したい

ということで、リアルタイムで見た目を確認しながらカスタム CSS を出力できるツールを作ることにしました。

作ったもの

その名も はてなブログシンタックスハイライト CSS ジェネレータ です (長い)

こちらから利用できます。

はてなブログシンタックスハイライト CSS ジェネレータ

f:id:kawmra:20180331233156p:plain

主な機能や特徴:

  • 色を変更するとリアルタイムにプレビューと CSS が確認できる
  • ブラウザを閉じてしまっても、もう一度開けば直前の状態に復元される
  • インポート/エクスポート機能

右側のパレットに色を入力すると、リアルタイムに左側のプレビューが更新されます。それと同時に、下のデザイン CSS もリアルタイムに生成されます。

How it works

今回は練習も兼ねて React で実装しました。 React を本格的に使用したのははじめてですが、かなり直感的に、簡単に作れました。これからも使っていきたいです。

このツールの仕組みは簡単で、右側のパレットの Value が更新されたタイミングでプレビューとデザイン CSSコンポーネントの props を更新して再描画しているだけです。 React っぽいですね。

ちなみにこのツールのソースコードは全て GitHub に公開しています。

React 初学者なので間違っているところや非効率的なところなどあるかと思いますが、同じく初学者にとって参考になれば幸いです。

識者の方は、ここがおかしい!とかお気軽にコメントなり Issue を送ってもらえると嬉しいです。(PR を投げて頂けるともっと嬉しいです 👏)

課題

サンプルコードが固定

現状、最初から表示されている Kotlin のサンプルコードでしかプレビューを確認できません。

幾つかの言語でサンプルコードを用意しておいて、それを切り替えられるようにしたいと思ってます (PR 待ってます)。

はてなブログシンタックスハイライトに使われているツールが不明

実ははてなブログシンタックスハイライトがどのようにコードの役割 (Constant とか Type とか) を判断しているのかよくわかっていません。

はてなさんのお手製のツールが使用されているのでしょうか?

もしこれがわかれば (そしてそのツールが利用できれば) 、ユーザが任意の言語の任意のコードを貼り付けてプレビューが確認できるようになるのですが、現状できていません。

ちなみに今回は、はてなブログのプレビューなどで実際にはてなブログによって生成された HTML コードを React で使用しているコンポーネント ( CodeParts ) に自動的に変換するためのツールを作ってそれを使用しました。気になる方は、 Generator コンポーネントを読んでみてください。

参考

OkHttp が Android のプロキシ設定を無視していた話

OkHttp の 3.4.2 以下のバージョンでは、一度リクエストに失敗した URL に対してはプロキシを使用しなくなるということがありました。 仕事でこの問題に当たり、まさかプロキシが回避されているとは思わなかったので原因究明に時間がかかってしまいました。

Option to force use of the system proxy · Issue #2525 · square/okhttp · GitHub

調べるとこれは既に Issue に投稿されており、この PR でマージされていました。

そのため、 3.5.0 からは修正されています。

React で親の props を特定の子にだけ伝えたい

※ React 初心者ですのでその点ご留意ください。

props.children

<Parent>
  <Child />
  <Child />
</Parent>

このように親子関係がある場合、 Parent がその子である複数の Child を描画するには、 props.children を使用します。

const Parent = (props) => {
  return(
    <div>
      { props.children }
    </div>
  )
}

簡単ですね。

親の props を伝える

親の props の値を props.children に伝えるには、 React.Children.map 関数と React.cloneElement 関数を使用して、 props を追加した Child のコピーを使用するようにします。

<Parent foo={"bar"}>
  <Child />
  <Child />
</Parent>
const Parent = (props) => {
  const children = React.Children.map(props.children, (child) => {
    return React.cloneElement(child, {foo: props.foo})
  })
  return(
    <div>
      { children }
    </div>
  )
}
const Child = (props) => {
  return(
    <p>foo: {props.foo}</p>
  )
}

親の props を特定の子にだけ伝える

上の例では、 Parent の子要素として Child 以外の要素があった場合、余計な props をその要素にも渡してしまうことになります。

<Parent foo={"bar"}>
  <Child />
  <Child />
  <SomeElement />
  <Child />
</Parent>

この場合、 SomeElement にも props.foo が渡されてしまいます。 SomeElement には渡さず、 Child にだけ渡したいです。

React.Children.map 関数の第二引数に指定する関数の引数に渡される childtype 属性を見ることによって、その子要素がどの要素であるかを判別することができました。

const Parent = (props) => {
  const children = React.Children.map(props.children, (child) => {
    if (child.type === Child) {
      return React.cloneElement(child, {foo: params.foo})
    } else {
      return child
    }
  })
  return(
    <div>
      { children }
    </div>
  )
}
const Child = (props) => {
  return(
    <p>foo: {props.foo}</p>
  )
}

参考

Adobe Illustrator 用のスクリプトを作ろうとした

社内のデザイナが使用していた Illustrator 用のスクリプトに少し機能を追加してほしいとのことで、(あまり頼まれてないけど)おもしろそうなので検索してみました。 が、情報が少ない…

デザイナが使用していたスクリプトQuick-exporter というもので、 iOSAndroid アプリ向けの書き出し作業が格段に楽になる素晴らしいスクリプトです。ソースコードGitHub で管理されているようです。

これまで Photochop や Illustrator でこのようなスクリプトを利用できることは知っていましたが、実際に中身を見たのは初めてです。

中を見てみると、どうやら JavaScript みたいです。しかも拡張子が jsx (!)

まさか React か!?なんて思いましたが(思ってない)、そんなことはもちろんなく、 XML が埋め込めるというただそれだけの普通の JSX でした。

見た限り、 View が XML で記述されている様子もなく、 Java の Swing や他の多くの GUI フレームワークと同じように、コードで View を記述するような感じに見えました。

palette = new Window("palette", "Quick exporter");
var pnlMain = palette.add("group { orientation: 'column', alignment :'left', alignChildren :'right'}");
var grExtension = pnlMain.add("group");
var rbtnPNG24 = grExtension.add("radiobutton", undefined, "PNG24");
var rbtnPNG8 = grExtension.add("radiobutton", undefined, "PNG8");
// さらに続く..

View がどのように作られているのかわかったので、リファレンスで Window とその引数である palette、またはパーツの名前だと思われる groupradiobutton について調べよう、と思ったのですが…

全然情報が出てきません。

Adobe Forums にもこんな質問こんな質問が立てられていますが、結局公式リファレンスのしっかりとしたものはなさそうでした。

公式のページ が一応あるのですが、 Scripting Guide も JavaScript Reference も PDF です…

いやそれは最悪いいとして、 Scripting Guide にも JavaScript Reference にも、 Windowpatellealert() もどこにもない! その後結構調べましたが、公式でちゃんとしたドキュメントを見つけることはできませんでした。

また、スクリプトExtendscript Toolkit を使用して作ると作りやすいらしいのですが、 macOS 10.11.5 以前しか対応していない1とのことでした..

と諦め掛けていたのですが、 Windowdialog のことを ScriptUI と呼ぶことを知り、それで検索していると以下のサイトを見つけました。

ScriptUI for dummies | Peter Kahrel

このページの下部にある、 click here リンクをたどると、非公式ですが ScriptUI の Guide があります 👏

Windowpalette が具体的なコード例と一緒に説明されています。他にも edittextbutton などの Control 要素についても説明がされています。

ということで、まだ Quick-exporter のコードに手を加えて View を少しいじった程度のことしかできていませんが、もう少しいじってみようかと思います。

余談

会社の Illustrator CC 2018 で alert() をすると Illustrator がフリーズするんですが、環境のせいでしょうか…?