画像
image_load(.pimg/.bmp/.jpg/.png) / image_create / image_target / image_size / draw_image / draw_image_part / image_free
pico.image_load
.pimg・.bmp・.jpg(JPEG)・.png(PNG)を読み込み、画像のハンドル(整数。WidgetIdとは別物)を返します。形式はファイルの中身で見分けます(拡張子は見ません)。
BMPは無圧縮(と BI_BITFIELDS)の 1/4/8/16/24/32bpp を読めます(RLE圧縮は読めません)。画面は16色なので、
読むときにOSのパレットの一番近い色へ寄せます。opts で寄せ方を選べます:
| キー | 既定 | 意味 |
|---|---|---|
dither | フルカラー(16bpp以上)なら true | 4x4の組織的ディザを掛ける |
raw | false | 4bpp以下のBMPの番号を、そのままパレットの番号として使う(OS向けに16色で描いた絵。pico.set_palette で色を変えても番号が保たれる) |
JPEG はベースライン(普通の保存形式)だけを読めます。プログレッシブJPEGは読めません(nil)。読むときに 1/2・1/4・1/8 に縮められます(8x8の中を平均するので、大きな写真を小さく見せても汚くなりません)。
| キー | 既定 | 意味 |
|---|---|---|
dither | true | 4x4の組織的ディザを掛ける |
scale | (自動) | 1 / 2 / 4 / 8 で、その分の1に縮める。それ以外はエラー |
max_w / max_h | 画面の大きさ(240 / 320) | scale を指定しないとき、この大きさに収まる一番大きい縮め方を選ぶ。画像用メモリの残りに収まらなければさらに縮める |
PNG は色の種類を問わず読めます(グレー・パレット・RGB・アルファ付き、1〜16bit、インターレース)。JPEGと同じく 1/2・1/4・1/8 に縮められ(四角の中を平均。インターレースの絵は間引き)、scale / max_w / max_h も同じ意味です。
| キー | 既定 | 意味 |
|---|---|---|
dither | true | 4x4の組織的ディザを掛ける |
transparent | アルファの列があれば true | 透過の画像にする(アルファが96未満の画素が透過=0番になり、draw_image で描かない。不透明な画素は0番以外の色へ寄せる)。パレットの tRNS で透過を指定したPNGは true を渡す。false なら半透明の画素を背景の色と混ぜる |
scale / max_w / max_h | JPEGと同じ |
ディザはJPEG・PNGとも、パレットの色にほぼ等しい画素には掛けません(16色で描いたドット絵のPNGをそのまま読んでも汚れません)。
PNGを解く間だけ約45KBの作業場所を使います(展開の辞書32KBなど。読み終えたら返します)。
フルカラーで描く: 画面のバッファは16色ですが、full_color = true を付けて読んだ JPEG・PNG・フルカラーのBMP(8bpp以上)は、
Canvas の render の中で pico.draw_image すると、16色の絵を描いた上にSDから解きながら液晶へそのままの色で直接描きます
(フルカラーの絵はメモリに持ちません。持つのは今までどおり16色の絵だけ)。
| キー | 既定 | 意味 |
|---|---|---|
full_color | false | pico.draw_image でフルカラーでも描く。.pimg・4bpp以下のBMPは16色の絵そのものなので、付けても変わりません |
background | 白(15) | 透過にしない画像の半透明の画素を混ぜる色(パレットの番号)。透過のPNGも、フルカラーで描く分は透過せずこの色と混ぜます |
- フルカラーで描くのは
pico.draw_imageだけです(draw_image_part/draw_image_ex/pico.gameのスプライトは16色)。 - 描き直すたびにSDから解き直すので、画像にかからない所だけを描き直すときは解き直しません(
pico.mark_dirtyで変わった所だけを描き直すと速い)。 画像に少しでもかかると、画像全体を描き直します。 - 液晶へ直接描けるのは画面に1枚だけです。2枚目は16色の絵のまま見えます。フルカラーで覚えておけるのは同時に2枚まで(超えても読めて、16色で描きます)。
Canvasの上に部品・ダイアログ・トーストが重なっている間は、16色の絵が見えます(直描きは上に載ったものを塗りつぶしてしまうため。どけば戻ります)。pico.image_targetで描き足したりpico.image_clearしたりした画像は、ファイルの絵と違うので16色だけになります。
-- 640x640のジャケット画像を、80x80に収めて読む(1/8)。Canvas の render で draw_image するとフルカラーで見える
local art = pico.image_load(pico.app_dir() .. "/cover.jpg", { max_w = 80, max_h = 80, full_color = true })
失敗すると nil を返します(エラーにはなりません)。失敗する条件:
- SDカードが使用不可
sd_outside_app_dir権限が無く、app_dirの外を指している- 同時に保持できる画像数の上限(8枚)に達している
- 画像用メモリ合計の上限(96KiB)を超える
- ファイルが存在しない、または
.pimg/.bmp/.jpg/.pngとして不正(プログレッシブJPEG・途中で切れたファイルを含む) - PNGの作業場所(約45KB)をまとめて取れない
pico.image_size
無効なハンドル(未発行・解放済み)を渡すとエラーになります。
pico.draw_image
画像を描画します。他の pico.draw_* と同様、Canvas の render コールバックの中で使うこと(直接描画 参照)。無効なハンドルを渡すとエラーになります。
full_color = true で読んだ写真は、液晶へフルカラーでも描きます(上の pico.image_load の「フルカラーで描く」)。
pico.draw_image_part
画像のうち (sx, sy) から幅 w・高さ h の部分だけを (x, y) へ描きます。同じ大きさの絵を1枚に並べた画像(スプライトシート)から1つずつ切り出すためのもので、画像は8枚までしか持てないので、部品の多い絵はまとめて1枚にしてこれで描き分けます。画像の外にはみ出す分は描きません。draw_image と同じく Canvas の render コールバックの中で使い、無効なハンドルはエラーです。
-- 12x12のタイルを横に並べた画像から、n番目(1始まり)を描く
pico.draw_image_part(img, x, y, (n - 1) * 12, 0, 12, 12)
flip_x / flip_y が真なら左右/上下を反転して描きます(キャラクターの向きを変えるのに。反転は1画素ずつ描くので、しないときより遅い)。
実例は「テトリス」(/lua/apps/テトリス/。ミノの絵を1枚の blocks.pimg に並べ、盤面のタイルマップ・スプライト・HOLD/NEXTの絵の全部で同じ画像を使う)。タイルマップをまとめて描く pico.draw_tilemap は ゲームエンジン にあります。
pico.draw_image_ex
Love2D の love.graphics.draw(image, x, y, r, sx, sy, ox, oy) と同じ並びで、画像を拡大縮小・回転・反転して描きます。
- 画像の
(ox, oy)(元画像のピクセル座標、既定0, 0)を画面の(x, y)に置く。 r: 回転(ラジアン、時計回り、既定0)。回転の中心も(ox, oy)。sx,sy: 倍率(既定1)。syを省くとsxと同じ。負にすると反転(sx = -1で左右反転)。倍率は±64まで、0は何もしない。- 最近傍で、補間はしません。透過指定の画像は index 0 を描きません。
- 描くのは今のクリップ(
set_draw_area/renderの中なら今回描き直す範囲)の内側だけです。
-- 画像の中心を軸に、2倍で45度回して描く
local w, h = pico.image_size(img)
pico.draw_image_ex(img, 120, 160, math.pi / 4, 2, 2, w / 2, h / 2)
draw_image_ex は描くたびに1画素ずつ回すので、回転しない描画より重くなります。毎フレームたくさん回して描くなら、下の image_rotate + draw_rotated を使ってください。
pico.image_rotate
画像を frames 通りの角度(既定16 = 22.5度刻み、1〜64)に回した絵を先に作っておき、1枚の画像(コマを格子状に並べたもの)として返します。setup() などで1回だけ呼び、描くときは pico.draw_rotated を使います。
opts.sx,opts.sy: 倍率(既定1、syを省くとsxと同じ。負で反転、8倍まで)。opts.ox,opts.oy: 回転の中心(元画像のピクセル座標、既定は画像の中央)。opts.start: 最初のコマの角度(ラジアン、既定0)。- 戻り値の
cellはコマの一辺(px)。回転の中心はコマの真ん中に来ます。 - 新しい画像のスロットを1つ使い、メモリは
cell × cell × frames ÷ 2バイトほど食います(48x24の画像・16コマで約25KB、1.5倍なら約56KB)。画像の上限(8枚・合計96KiB)に収まらなければnil。 - 出来た画像は 0番の色が透過になります(元が透過でない画像の0番の色=黒も抜けます)。
- 使い終わったら
pico.image_free(sheet)で解放できます。
pico.draw_rotated
image_rotate で作った画像から、角度 r(ラジアン、時計回り)に一番近いコマを選び、回転の中心が (x, y) に来るように描きます。角度は frames 段階に丸まります(品質より速さを取る版)。
1画素ずつ回す処理が無く、コマを1枚写すだけなので、回転しない draw_image とほぼ同じ速さで描けます。
local img = pico.image_load("/lua/apps/x/ship.pimg")
local ship = pico.image_rotate(img, 32) -- 11.25度刻み
pico.image_free(img) -- 元の画像はもう要らなければ解放してよい
pico.on(canvas, "render", function()
pico.draw_rotated(ship, px, py, angle)
end)
image_rotate で作った画像以外を渡すとエラーです。
pico.image_free
画像を明示的に解放します。無効なハンドル・解放済みハンドルを渡してもエラーにはならず黙って無視されます(pico.destroy() と同じ二重解放の扱い)。
呼ばなくても、アプリを閉じれば全部解放されます。
オフスクリーン画像
画面ではなく画像に描いておき、後でまとめて画面へ貼れます。盤面のように重い絵を一度だけ描いて使い回したいときに使います。画像は image_load したものと同じスロット(8枚・合計96KiB)を使い、draw_image / draw_image_part / draw_image_ex で画面へ描けます。
pico.image_create
w×h(最大480×640)の空の画像を作ります。中身は白(transparent = true なら0番色=透過)。枚数や容量の上限を超えると nil です。大きさが不正ならエラー。
pico.image_target
以降の pico.draw_*(fill_rect draw_text draw_image など)の描き先をその画像に切り替えます。nil で画面へ戻します。座標は画像の左上が(0,0)です。コールバックを抜けると自動で画面へ戻ります(戻し忘れても他の描画が画像に吸い込まれません)。画像へ描いている間は、画面は描き直されません。
pico.image_clear
画像を1色で塗りつぶします(既定は白、透過画像は0番色)。
local cache = pico.image_create(120, 120)
pico.image_target(cache)
pico.fill_rect(0, 0, 120, 120, 7)
pico.draw_text(8, 8, "キャッシュ", 0)
pico.image_target(nil)
-- Canvasのrenderの中で
pico.draw_image(cache, 10, 40)