











序章
GIF 画像形式は、小型 TFT または OLED ポイントに表示される写真を保存するのに適しています。 多くの形式の写真を効率的に圧縮する LZW 圧縮アルゴリズムを使用しており、LZW 圧縮された写真をデコードするコードは、間違いなく比較的コンパクトになります。 というか、画像編集者がスマートにサポートしてくれます
GIFポイントが欲しかった実行するためのルーチンに移動する必要があり、比較的少量の RAM を備えたマイクロコントローラー向けの GIF デコーダーを保護できない可能性があるため、1 つを書き留めるように努力する戦略を立てました。 近い結果は、適切な 120k バイト以上を使用するデコーダであり、この理由から、AVR128DA28 や ATmega1284P などのように 160k バイトまでの RAM を使用しない AVR プロセッサにとっては、このため楽しいものです。 これは、ATSAMD21 や ATSAMD51 などの 32 ビット プロセッサでさらにダッシュする可能性があり、主に完全に Adafruit PyBadge ベースの ATSAMD51 で動作するインスタンスを含めます。
これらのプロセッサは大量のフラッシュを保持しているため、圧縮されたさまざまな GIF 写真をフラッシュに保存し、デコーダを使用してそれらを指定することができます。スタンド・オン・マイ・ソーンター – 既存の実行。 または、おそらく SD カードから GIF 画像を読み込んで、それらを指定することもできます。
LZW圧縮 [Table[rest] GIF 形式は、画像をピクセルの配色値の線形モザイクとして扱います。 使用する LZW 圧縮は、既に遭遇した一連のピクセルを検出することで機能し、すべてのシーケンスを 3 ~ 12 ビットの可変サイズ コードとしてエンコードします。 これらのコードは、ビットの正確な mosey として出力されます。
GIF をデコードするには、デコーダーを使用して、入力された mosey を実際のビット数を含むコードに分割します。 これらのコードは、4096 アスペクト デスクで考慮され、そのコードで表される 1 つ以上のピクセルの実際のシーケンスに変換されます。
デスク内のすべてのコードと同様に、完全なピクセル シーケンスを保存する必要があるとしたら、非常に思い出深いものになるでしょう。 幸いなことに、すべてのシーケンスは以前のシーケンスと同等であり、1 ピクセルの追加によって延長されます。 この真実の理由から、シーケンスを、デスク内の以前のシーケンスへのポインタと追加のピクセルとして表すことができます。 この理由から、ルックアップ デスク内のすべてのエントリは、デスク内の以前のエントリへの 12 ビット ポインタと、シーケンス内の追加ピクセルの色付け用の 8 ビット バイトを格納することを望んでいます。
LZW 圧縮のすばらしい点は、ルックアップ デスクがうまく組み込まれていない可能性があることです。 GIF 画像を使用すると、コード値がエンター mosey から読み込まれるため、叙事詩的に動的に再構築されることは間違いありません。 GIF 形式がどのように機能するかについての詳細な説明については、ウィキペディアのページ 。 また、Joshua Davies [2] [3] およびラリー金融機関 [4]
.
カラーデスク
GIF 画像は最大 256 色の配色デスクをぶら下げることができるため、さらに、この配色デスクを格納するために 256 バイトの配列が必要です。 すべての配色は、配色の R、G、および B 成分を示す 3 バイトで概説されます。 この GIF デコーダーは、5-6-5 配色コンドミニアムの TFT ディスプレイに写真を表示することを想定しているため、配色デスク内のすべてのエントリを 16 ビットに下げることができます。 回想要件
完全な RAM 要件は、この理由から 4096 x 3 + 256 バイト、または 12K バイト以上が適切です。 より多くのプログラミングを行って RAM の要件を減らすという提案がありますが、デコーダーを簡単に差し控えるために、私はこれらを控えました。 見る エクストラストラテジー[Table[rest] これらがどのように機能する可能性があるかのミニチュアプリント用.
圧縮デスクと配色デスクはマウントされた配列として割り当てられ、プログラムは動的記憶割り当てのために malloc を実行しません。
制限
このデコーダーは、古い GIF87a 形式、魅力的な GIF、ネイティブの配色テーブル、透明度、インターレース GIF、またはさまざまな拡張子を強化しません。 不明なフォーマットに遭遇すると、LED が点滅してエラー コードが表示されます。 しかし、変動する画像編集者によって作成された古いGIFと同じように穏やかに動作し、さまざまな編集者の写真で調べました.
回路
Minimal GIF Decoder を再現するために私が削除した回路は次のとおりです:
回路消滅AVR128DA28 の最小 GIF デコーダーを表示します。
いくつかの小さなブレッドボードで作成しました [5]。 プロセッサに関しては、PDIP パッケージの AVR128DA28 を削除しましたが、AVR128DB28 も同様に楽しいものになる可能性があります。
Adafruit 160×120 1.8″ coloration TFT を消す目的で [5], しかし, それは間違いなく, 任意のポイントで穏やかに動作する可能性があります. このシステムには, グラフィックライブラリでサポートされているさまざまなディスプレイの設定が必要です. 実行したいディスプレイのコメントを外してください. LITE ピンへのこのポイントは、バックライトが機能するために Vcc にリンクする必要があります。
さらに、LED_BUILTIN 出力に LED を組み込んで、プログラムがエラーを通知できるようにしました
このシステム
TFTポイントをドライブするには、私は私のCompact TFT Graphics Library、同じ古い Arduino SPI 呼び出しを使用します。 Arduino ポイントは SD カード ソケットを提供します。また、同じ SPI インターフェイスを使用して SD カードから GIF 写真を読み取ることもできます。 ライブラリからルーチンを削除しましたが、これはおそらくこのアプリケーションでは不要になっている可能性があります。
あるいは、あなたは多分また私の運動をするかもしれません Shrimp TFT Graphics Library 2。これは、AVR128DA28 ポートで Snort ビット操作を使用して、目的をできるだけ迅速に実行します。間違いない。 どちらの TFT ライブラリも、ディスプレイの大きな変動をサポートしています。 実行したい目的のパラメータのコメントを外してください。
GIFデコーダー
GIF デコーダによって消去された圧縮デスク内の側面は、構造体 によって概説されます。 cell_t
typedef struct { int16_t 緩和; uint8_t 最終; } cell_t;>
すべての机のエントリで リラクゼーション は以前のエントリへのポインタであり、
最後の は、このデスク エントリによってエンコードされたシーケンス内の最後のピクセルのピクセル カラーです。[256] コンプレッション デスク アレイと GIF カラーレーション デスクの定義は次のとおりです:
cell_t デスク[256]; uint16_t ColorTable[256];ビットモジーの読み方[Table[rest]
入力ファイルから可変サイズのコードを調べることのマイナス面は、ルーチン GetNBits():を返しますん
int GetNBits (int n) { while (Nbuf> n; Nbuf=Nbuf - n; 結果を返す; }> ![]()
への呼び出しGetNBits(n)
は次の- ファイルからのビット コード. これにより、グローバル変数内にバッファが維持されます。 Buf 、 と Nbuf は、 でジェントル アクセス可能な有効ビット数を指定します。 Buf . 未満の場合ん バッファ内のビット ReadByte() は、もう 1 つの 8 ビットを読み取り、それらをバッファーの末尾に追加するように名前が付けられています
さらに複雑なのは、ビット mosey が最大 255 バイトのブロックに分割され、各ブロックの前に size バイトが続くことです。 グローバル変数
Block 販売店 最終バイト数焼けるようなブロックの中で。 これがゼロになると、もう 1 つのサイズのバイトで学習してリセットされます![]()
シーケンスの先頭文字の取得
コンプレッション デスクの側面を考えると c
ポインターに従って検索しますそれが表すシーケンス内の最初のピクセルの配色を取り出します:>uint8_t FirstPixel (int c) { uint8_t final; 制定 { 最終=デスク[c].最終; c=デ sk[c].リラクゼーション; (c !=-1); } ながら (c !=-1); ファイナルを返します。 }
シーケンスの出力
ルーチン プロットシーケンス()圧縮机の側面 で表される一連のピクセルをプロットしますc.![]()
プロットシーケンス() は、次のように再帰的に概説される可能性もあります:
void PlotSequence (int c) { int リラクゼーション=デスク[c].relaxation; if (緩和 !=-1) PlotSequence(緩和); fore=ColourTable[テーブル[c].last]; PlotPoint (Pixel%Width, ysize - ピクセル/幅 - 1); ピクセル++; }
これは、シーケンス内の最初のピクセルを効果的に見つけます。ポインターをたどって、-1 ポインターに到達するまで手を貸してから、シーケンス内の次のピクセルを見つけます。 4 番目、最後のピクセル で終わる*)デスク[c].ファイナル.
![]()
このバージョンは完全に機能しましたが、長いシーケンスでは適度にいくつかのスタックコンドミニアムが必要になる可能性があるため、私はそれを使用することにかなり不機嫌でした. 幸いなことに、私は再帰の必要性を完全に回避する 1 つの描画に遭遇しました。 )
void PlotSequence ( int c) { // バックオフを測定 int i=0、緩和=c; while (リラクゼーション !=-1) { リラクゼーション=デスク[rest].リラクゼーション; i++; } // スペースを後方へ Pixel=Pixel + i - 1; リラクゼーション=c; while (リラクゼーション !=-1) { fore=ColourTable[Table[rest].last]; PlotPoint (Pixel%Width, ysize - ピクセル/幅 - 1); ピクセル - ; リラクゼーション=デスク[Table[rest].リラクゼーション; } ピクセル=ピクセル + i + 1; }これは最初に、シーケンスの長さを で測定します。 私、-1 ポインターに到達するまでのステップ数をカウントすることによって。 次に、最後のピクセルから開始して逆露光でピクセルをプロットすることで、再帰の必要性を回避します。
バイト飛ばし
ルーチン SkipNBytes() は、入力ファイル内の一連の不要なバイトをスキップするように命名されています:
void SkipNBytes (int n) { for (int i=0; i